/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.j2me.payment; import javax.microedition.midlet.MIDlet; import com.sun.midp.midlet.*; import com.sun.midp.midletsuite.*; import java.io.IOException; import java.util.Random; import javax.microedition.payment.TransactionListener; import javax.microedition.payment.TransactionRecord; import javax.microedition.payment.TransactionModuleException; import javax.microedition.payment.TransactionFeatureException; import javax.microedition.payment.TransactionListenerException; import javax.microedition.payment.TransactionPayloadException; import com.sun.midp.security.Permissions; import com.sun.midp.util.Properties; import com.sun.midp.midletsuite.MIDletSuiteStorage; import java.io.InputStream; import javax.microedition.rms.RecordStoreException; import com.sun.midp.log.Logging; import java.io.ByteArrayInputStream; import javax.microedition.rms.RecordStoreNotFoundException; import javax.microedition.rms.RecordStore; import com.sun.midp.installer.JadProperties; import com.sun.midp.log.LogChannels; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; /** * This class implements parts of <code>TransactionModuleImpl</code> which * are dependent on the CLDC. It's created by a factory method in the payment * module. * * @version * @see CldcPaymentModule#createTransactionModule */ public class CldcTransactionModuleImpl extends TransactionModuleImpl { /** MDIletSuite being run */ private MIDletSuite midletSuite; /** * The name of the file where the Midlet Payment Update Info is stored. */ private static final String PAYMENT_UPDATE_FILE_NAME = "payment_update"; /** * MIDlet property for the Payment Version. */ public static final String PAY_VERSION_PROP = "Pay-Version"; /** MIDletSuite payment info */ private PaymentInfo paymentInfo = null; /** Describes what is wrong with provisioning data */ private String provisioningDataError = null; /** * Creates a new instance of <code>CldcTransactionModuleImpl</code>. * Requires a reference to the application MIDlet which initiated the * payment. * <p> * Handles the <code>Pay-Debug-FailInitialize</code> debug mode. * * @param object the caller MIDlet * @throws TransactionModuleException indicates an error preventing the * MIDlet from using the payment API * @see javax.microedition.payment.TransactionModule#TransactionModule */ protected CldcTransactionModuleImpl(Object object) throws TransactionModuleException { super(object); PaymentInfo paymentInfo = getPaymentInfo(); if (paymentInfo == null) { throw new TransactionModuleException( provisioningDataError == null ? "Missing provisioning information" : "Provisioning data is corrupt or incomplete. " + provisioningDataError); } /* Check if MIDletSuite is Trusted */ // Removed - SecurityException is thrown if untrusted MIDlet tries to use // restricted permission, so no additional check is required // if (!paymentInfo.isDemoMode() && !getMIDletSuite().isTrusted()) { // throw new TransactionModuleException("MidletSuite is untrusted"); // } // === DEBUG MODE === // (PaymentModule.random.nextInt(192) < 64) = approx. one in three will // fail in this way if (paymentInfo.isDemoMode() && ( paymentInfo.getDbgFailInitialize() || paymentInfo.getDbgRandomTests() && (CldcPaymentModule.random.nextInt(192) < 64))) { throw new TransactionModuleException("Debug mode"); } // === DEBUG MODE === } /** * Returns the MIDlet suite of the MIDlet. * * @return the MIDlet suite */ private MIDletSuite getMIDletSuite() { if (midletSuite == null) { midletSuite = (MIDletSuite) Scheduler.getScheduler().getMIDletSuite(); } return midletSuite; } /** * Ensures that the MIDlet will have required privileges to do a protected * operation. * * @param permission the required permission * @param name an additional info string * @throws SecurityException if the permission is not granted * @throws InterruptedException if the thread waiting for the permission * is interrupted */ protected void checkForPermission(String permission, String name) throws InterruptedException { getMIDletSuite().checkForPermission(permission, name); } /** * Helper class that allows PaymentInfo to be created from MIDletSuiet * properies */ class PropertiesWraper extends Properties { /** Provision info source */ private MIDletSuite midletSuite; /** * PropertiesWraper constructor * * @param suite property storage */ public PropertiesWraper(MIDletSuite suite) { midletSuite = suite; } /** * Returns property from given MIDletSuite * * @param key property name * @return property value or null */ public String getProperty(String key) { return midletSuite.getProperty(key); } } /** * Returns the payment provisioning information associated with the MIDlet. * * @return the payment provisioning information */ protected PaymentInfo getPaymentInfo() { if (paymentInfo == null) { provisioningDataError = null; if (getMIDletSuite().getProperty(PAY_VERSION_PROP) == null) { // quick check return null; } RecordStore store; PropertiesWraper props = new PropertiesWraper(getMIDletSuite()); // try to read updated provision info try { store = RecordStore.openRecordStore(PAYMENT_UPDATE_FILE_NAME, false); try { byte[] data = new byte[store.getRecordSize(1)]; data = store.getRecord(1); InputStream bis = new ByteArrayInputStream(data); JadProperties payProps = new JadProperties(); payProps.load(bis); bis.close(); paymentInfo = PaymentInfo.createFromProperties( props, payProps); } finally { store.closeRecordStore(); } } catch (RecordStoreNotFoundException ex) { // if the is no update file try to read payment info from // suite properties try { paymentInfo = PaymentInfo.createFromProperties( props, props); } catch (PaymentException e) { if (Logging.REPORT_LEVEL <= Logging.ERROR) { Logging.report(Logging.ERROR, LogChannels.LC_AMS, "getPaymentInfo threw an PaymentException: " + e.getMessage()); } provisioningDataError = e.getMessage(); } } catch (RecordStoreException ex) { if (Logging.REPORT_LEVEL <= Logging.ERROR) { Logging.report(Logging.ERROR, LogChannels.LC_AMS, "getPaymentInfo threw an RecordStoreException: " + ex.getMessage()); } } catch (PaymentException ex) { if (Logging.REPORT_LEVEL <= Logging.ERROR) { Logging.report(Logging.ERROR, LogChannels.LC_AMS, "getPaymentInfo threw an PaymentException: " + ex.getMessage()); } provisioningDataError = ex.getMessage(); } catch (IOException ex) { if (Logging.REPORT_LEVEL <= Logging.ERROR) { Logging.report(Logging.ERROR, LogChannels.LC_AMS, "getPaymentInfo threw an IOException: " + ex.getMessage()); } } if (paymentInfo != null) { // initialize the transaction store for this MIDletSuite only // for once ((CldcPaymentModule) PaymentModule.getInstance()) .initializeTransactionStore(paymentInfo, midletSuite.getID(), midletSuite.getProperty(MIDletSuiteImpl.SUITE_NAME_PROP)); } } return paymentInfo; } /** * Stores the payment provisioning information associated with the MIDlet. * * @throws IOException indicates an output error */ protected void savePaymentInfo() throws IOException { PaymentInfo pInfo = getPaymentInfo(); if (pInfo != null) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); OutputStreamWriter os = new OutputStreamWriter(bos, "UTF-8"); byte[] data; try { pInfo.export(os); data = bos.toByteArray(); } finally { os.close(); } RecordStore store = null; try { store = RecordStore.openRecordStore(PAYMENT_UPDATE_FILE_NAME, true); try { if (store.getNumRecords() == 0) { /* Record Store is EMPTY - add new record */ store.addRecord(data, 0, data.length); } else { /* Replace first record */ store.setRecord(1, data, 0, data.length); } } finally { store.closeRecordStore(); } } catch (RecordStoreException e) { throw new IOException( "Storage Failure: Can not store Payment Info"); } } } /** * Returns the MIDlet payment ID that can be used to store transaction * records for the MIDlet initiated transactions into the transaction store. * * @return the ID */ protected int getApplicationID() { return ((CldcPaymentModule) PaymentModule.getInstance()) .getPaymentID(getMIDletSuite().getID()); } /** * Returns an array of the missed transactions associated with the MIDlet * suite. * <p> * It handles the <code>Pay-Debug-MissedTransactions</code> debug mode. * * @return an array of the missed transactions */ protected TransactionRecord[] getMissedTransactions() { // === DEBUG MODE === PaymentInfo paymentInfo = getPaymentInfo(); if (paymentInfo.isDemoMode() && paymentInfo.getDbgMissedTransactions() >= 0) { int applicationID = getApplicationID(); CldcPaymentModule paymentModule = (CldcPaymentModule) PaymentModule.getInstance(); CldcTransactionStoreImpl transactionStore = (CldcTransactionStoreImpl) paymentModule.getTransactionStore(); TransactionRecord[] fakeMissed = null; try { fakeMissed = transactionStore.getMissedTransactions( applicationID, true); } catch (IOException e) { } return fakeMissed; } // === DEBUG MODE === return super.getMissedTransactions(); } }