[Webfunds-commits] java/webfunds/client/utils UpgradesManager.java

Ian Grigg iang@cypherpunks.ai
Thu, 9 Nov 2000 09:28:21 -0400 (AST)


iang        00/11/09 09:28:21

  Modified:    webfunds/client AccountBrowser.java AccountBrowserImpl.java
               webfunds/client/plugins PluginManager.java
               webfunds/client/sox SOXWallet.java WalletException.java
               webfunds/client/utils UpgradesManager.java
  Added:       webfunds/client/sox/gui PaymentFrame.java
  Log:
  1.  checnged the plugins model so that SOXWallet could add as well as Trader
  2.  moved and added Payment Frame as a plugin into SOXWallet
  3.  added Token and Rollover payments to above.  Untested, don't use.

Revision  Changes    Path
1.8       +3 -3      java/webfunds/client/AccountBrowser.java

Index: AccountBrowser.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/AccountBrowser.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- AccountBrowser.java	2000/06/05 03:48:15	1.7
+++ AccountBrowser.java	2000/11/09 13:28:19	1.8
@@ -1,5 +1,5 @@
 /*
- * $Id: AccountBrowser.java,v 1.7 2000/06/05 03:48:15 gelderen Exp $
+ * $Id: AccountBrowser.java,v 1.8 2000/11/09 13:28:19 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -17,6 +17,6 @@
     void setWallets(WalletInterface[] wallets);
     
     void setPlugins(PluginInfo[] plugins);
-    void setPlugins(PluginInfo[] plugins, WalletInterface wi);
-    
+    void startPlugins(PluginInfo[] plugins, WalletInterface wi);
+    void addPlugins(PluginInfo[] plugins, WalletInterface wi);
 }



1.81      +36 -35    java/webfunds/client/AccountBrowserImpl.java

Index: AccountBrowserImpl.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/AccountBrowserImpl.java,v
retrieving revision 1.80
retrieving revision 1.81
diff -u -r1.80 -r1.81
--- AccountBrowserImpl.java	2000/10/09 17:30:01	1.80
+++ AccountBrowserImpl.java	2000/11/09 13:28:19	1.81
@@ -1,5 +1,5 @@
 /*
- * $Id: AccountBrowserImpl.java,v 1.80 2000/10/09 17:30:01 iang Exp $
+ * $Id: AccountBrowserImpl.java,v 1.81 2000/11/09 13:28:19 iang Exp $
  *
  * Copyright (c) Systemics Inc 1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -17,6 +17,7 @@
 import java.util.*;
 
 import webfunds.utils.Debug;
+import webfunds.utils.Panic;
 import webfunds.client.sun.ExampleFileFilter;
 import webfunds.sox.*;
 import webfunds.client.sox.*;
@@ -1115,7 +1116,6 @@
         popup.add(contractInfoAction);
         popup.add(contractHelp);
 
-        //popup.add(new JPopupMenu.Separator());  // now in plugged call
         return popup ;
     }
 
@@ -1130,7 +1130,6 @@
         popup.add(accountInfoAction);
         popup.add(accountHelp);
 
-        //popup.add(new JPopupMenu.Separator());
         return popup ;
     }
 
@@ -1142,7 +1141,6 @@
         popup.add(walletInfo);
         popup.add(walletHelp);
 
-        //popup.add(new JPopupMenu.Separator());
         return popup ;
     }
 
@@ -1197,29 +1195,47 @@
     }
 
     /**
-     * Set a plugin for this wallet only.
+     * Start the plugins for this wallet only.
      * Should be called after all common plugins above are set,
      * as the already set ones are copied.
+     * May be called multiple times, ignored after first time.
+     *
+     * A concession to extended wallets that can't figure out who
+     * called what first...
      */
-    public void setPlugins(PluginInfo[] plugins, WalletInterface wi)
+    public void startPlugins(PluginInfo[] plugins, WalletInterface wi)
     {
-        JPopupMenu menu;
+        if (contractPopups.get(wi) != null)
+            return ;                        // already called
 
         //
         //  Need the standard plugin block first.
         //  If not there, must be the first time, so create a
         //  new default popup for this wallet.
         //
-        if (contractPopups.get(wi) == null)
-        {
+            JPopupMenu menu;
+
             menu = newContractPopup();
             contractPopups.put(wi, menu);
             menu = newAccountPopup();
             accountPopups.put(wi, menu);
             menu = newWalletPopup();
             walletPopups.put(wi, menu);
-        }
 
+            addPlugins(plugins, wi);
+
+    }
+
+    /**
+     * Add plugins to this wallet.
+     * Should be called after startPlugins() above, else will crash.
+     * May be called multiple times, each call inserts a line in the menu.
+     */
+    public void addPlugins(PluginInfo[] plugins, WalletInterface wi)
+    {
+        if (contractPopups.get(wi) == null)
+            throw new Panic("must call startPlugins first");
+
         int len = plugins.length;
         logmsg("adding " + len + " plugins");
 
@@ -1228,13 +1244,15 @@
 
         for (int i = 0; i < len; i++)
         {
-            if (plugins[i].getContractAction() != null)
+            PluginInfo plugin = plugins[i];
+
+            if (plugin.getContractAction() != null)
             {
                 JMenuItem item = new AccountBrowserMenuItem(
-                                     plugins[i].getContractAction(),
-                                     Plugin.CONTRACTMODE, plugins[i]);
+                                     plugin.getContractAction(),
+                                     Plugin.CONTRACTMODE, plugin);
                 item.addActionListener(this);
-                item.setActionCommand(plugins[i].getClassname());
+                item.setActionCommand(plugin.getClassname());
 
                 if (conMenu == null)    // signal for first time, add line
                 {
@@ -1243,13 +1261,13 @@
                 }
                 conMenu.add(item);
             }
-            if (plugins[i].getAccountAction() != null)
+            if (plugin.getAccountAction() != null)
             {
                 JMenuItem item = new AccountBrowserMenuItem(
-                                     plugins[i].getAccountAction(),
-                                     Plugin.ACCOUNTMODE, plugins[i]);
+                                     plugin.getAccountAction(),
+                                     Plugin.ACCOUNTMODE, plugin);
                 item.addActionListener(this);
-                item.setActionCommand(plugins[i].getClassname());
+                item.setActionCommand(plugin.getClassname());
 
                 if (accMenu == null)    // signal for first time, add line
                 {
@@ -1258,19 +1276,6 @@
                 }
                 accMenu.add(item);
             }
-//            if (plugins[i].getGeneralAction() != null)
-//            {
-//                JMenuItem item = new AccountBrowserMenuItem(
-//                                     plugins[i].getGeneralAction(),
-//                                     Plugin.GENERALMODE, plugins[i]);
-//                item.addActionListener(this);
-//                item.setActionCommand(plugins[i].getClassname());
-//
-//// I don't understand this.  Is GeneralAction the same as WalletPopup????
-//                menu = (JPopupMenu)walletPopups.get(wi);
-//                menu.add(item);
-//                //pluginmenu.add(item);
-//            }
         }
 
     }
@@ -1332,13 +1337,9 @@
         JPopupMenu popup;
         if (obj instanceof Contract || obj instanceof ItemId)
         {
-//logmsg("getting popup");
             popup = (JPopupMenu)contractPopups.get(wi);
-//if (popup != null) logmsg("wi popup " + popup.getClass());
             if (popup == null)               // wallet has no special plugins
                 popup = contractMainPopup;
-//logmsg("use popup " + popup);
-//if (popup != null) logmsg("use popup " + popup.getClass());
             popup.show(treetable, evt.getX(), evt.getY());
         }
         else if (obj instanceof AccountInfo)



1.18      +39 -23    java/webfunds/client/plugins/PluginManager.java

Index: PluginManager.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/plugins/PluginManager.java,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- PluginManager.java	2000/09/26 03:19:07	1.17
+++ PluginManager.java	2000/11/09 13:28:19	1.18
@@ -1,5 +1,5 @@
 /* 
- * $Id: PluginManager.java,v 1.17 2000/09/26 03:19:07 iang Exp $
+ * $Id: PluginManager.java,v 1.18 2000/11/09 13:28:19 iang Exp $
  *
  * Copyright (c) 1995-2000 Systemics Inc on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -56,27 +56,40 @@
     {
 
         PluginInfo[] array;
-        int pugs;
+        int len;
+        AccountBrowser ab = core.getAccountBrowser(this);
 
         //
         //  The "official" WebFunds plugins.
         //
-        pugs = plugins.size();
-        array = new PluginInfo[pugs];
-        for (int i = 0; i < pugs; i++)
-            array[i] = (PluginInfo)plugins.elementAt(i);
-        core.getAccountBrowser(this).setPlugins(array, wi);
+        len = plugins.size();
+        array = new PluginInfo[len];
+        // for (int i = 0; i < len; i++)
+        //     array[i] = (PluginInfo)plugins.elementAt(i);
+        plugins.copyInto(array);
+        ab.startPlugins(array, wi);          // ignored if already done
 
         //
         //  The additional plugins added by the wallet itself.
         //
         Vector wp = getPluginVector(wi);
-        pugs = wp.size();
-        array = new PluginInfo[pugs];
-        for (int i = 0; i < pugs; i++)
-            array[i] = (PluginInfo)wp.elementAt(i);
-        core.getAccountBrowser(this).setPlugins(array, wi);
+        len = wp.size();
+        logmsg("plugged(" + wi.getClass() + ") with " + len + " plugins");
+        array = new PluginInfo[len];
+        // for (int i = 0; i < len; i++)
+        //     array[i] = (PluginInfo)wp.elementAt(i);
+        wp.copyInto(array);
+        ab.addPlugins(array, wi);
+
+    }
 
+    /*
+     *  Reset the wallet list so that extending wallets can start again.
+     */
+    public void unplug(Object obj)
+    {
+        logmsg("unplug(" + obj.getClass() + ")");
+        walletPlugins.remove(obj);
     }
 
     public void plugged()
@@ -92,6 +105,20 @@
         core.getAccountBrowser(this).setPlugins(array);
     }
 
+    public Vector getPluginVector(Object obj)
+    {
+        Vector v = (Vector) walletPlugins.get(obj);
+        if (v == null)
+        {
+            logmsg("new " + obj.getClass() + "");
+            v = new Vector();
+            walletPlugins.put(obj, v);
+        }
+        return v ;
+    }
+
+
+
     protected void updateAccountBrowser()
     {
         logmsg("updateAccountBrowser()");
@@ -183,17 +210,6 @@
         Vector plugins = getPluginVector(wi);
         prepare(plugin, plugins);
         //updateAccountBrowser();
-    }
-
-    public Vector getPluginVector(Object obj)
-    {
-        Vector v = (Vector) walletPlugins.get(obj);
-        if (v == null)
-        {
-            v = new Vector();
-            walletPlugins.put(obj, v);
-        }
-        return v ;
     }
 
     static final String spec  = "plugin.ini";



1.138     +414 -43   java/webfunds/client/sox/SOXWallet.java

Index: SOXWallet.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/sox/SOXWallet.java,v
retrieving revision 1.137
retrieving revision 1.138
diff -u -r1.137 -r1.138
--- SOXWallet.java	2000/10/14 02:20:20	1.137
+++ SOXWallet.java	2000/11/09 13:28:20	1.138
@@ -1,4 +1,4 @@
-/* $Id: SOXWallet.java,v 1.137 2000/10/14 02:20:20 iang Exp $
+/* $Id: SOXWallet.java,v 1.138 2000/11/09 13:28:20 iang Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * The WebFunds Development Team.  All Rights Reserved.
@@ -36,7 +36,14 @@
 import webfunds.sox.ItemId;
 import webfunds.sox.MailId;
 import webfunds.sox.MailItem;
+
+import webfunds.sox.AbstractPayment;
+import webfunds.sox.PaymentFactory;
 import webfunds.sox.Payment;
+import webfunds.sox.TokenPayment;
+import webfunds.sox.Token;
+import webfunds.sox.RandomToken;
+
 import webfunds.sox.Receipt;
 import webfunds.sox.SOXAccountException;
 import webfunds.sox.SOXArgsException;
@@ -53,6 +60,9 @@
 import webfunds.client.Addressbook;
 import webfunds.client.Transaction;
 import webfunds.client.UInterface;
+import webfunds.client.plugins.PluginManager;
+import webfunds.client.plugins.Plugin;
+import webfunds.client.plugins.PluginException;
 import webfunds.client.plugins.WalletContext;
 import webfunds.client.WalletInterface;
 import webfunds.client.sox.StoreAccountStore;
@@ -156,8 +166,58 @@
             logmsg(e);
             throw new InternalError(e);
         }
+
+        addPlugins(soxFrames);
+    }
+
+    /**
+     *  As a Wallet, I can add additional plugins.
+     */
+    public final static String[] soxFrames = {
+                                 "webfunds.client.sox.gui.PaymentFrame",
+                                 };
+
+    protected void addPlugins(String[] frames)
+    {
+        logmsg("PM");
+        PluginManager tap;
+        try {
+            tap = context.getPluginManager((WalletInterface)this);
+        } catch (Throwable ex) {
+            return ;            // must be low level wallet :(
+        }
+
+        tap.unplug(this);
+        for (int i = 0; i < frames.length; i++)
+        {
+            String frame = frames[i];
+            logmsg("pugging " + frame + ":");
+            Plugin plugin;
+            try
+            {
+                Class clas = Class.forName(frame);
+                logmsg("W Class is " + clas);
+                plugin = (Plugin)clas.newInstance();
+            }
+            catch (Throwable e)
+            {
+                e.printStackTrace(err());
+                error("W Plugin " + frame + " failed to invoke: " + e);
+                continue ;
+            }
+        
+            tap.addPlugin(plugin, this);
+
+            // try {
+            // } catch (PluginException ex) {
+            //     error("Plugin " + frame + " no good: " + ex);
+            //     continue ;
+            // }
+        }
+        tap.plugged(this);
     }
 
+
     /**
      * Shortname is used for the stores.  Can be set by child.
      */
@@ -470,10 +530,16 @@
         return items;
     }
 
+
+
+///////////  Payments  //////////////////////////////////////////
+
     /**
      *  Make an ASCII-armoured payment, a la PGP armouring.
      *  Also updates the addressbook.
      *  Should targetinfo be an AccountInfo?  We don't really know of it.
+     *
+     *  Client Wallet Interface
      */
     public byte[] makePayment(ItemId contractid, AccountInfo source,
                               AccountInfo targetinfo,
@@ -489,26 +555,18 @@
         if (pay == null)
             return null ;
 
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        try {
-            pay.encode(bos);
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            error("Payment no good: " + ex);
-            return null ;
-        }
-
-        String retval = Armoury.encode("SOX MESSAGE", bos.toByteArray());
-
         Addressbook ab = context.getAddressbook(this);
         ab.updateInfo(source);
         if (targetinfo != null)
             ab.updateInfo(targetinfo);
 
-        return retval.getBytes();
+        return asciiArmourWithError(pay);
     }
 
 
+
+
+
     /**
      *  Conversion between high level and low level.
      */
@@ -539,8 +597,8 @@
     }
 
     private boolean gotEnough(AccountId src,
-                                          ItemId contractid,
-                                          long amount)
+                              ItemId contractid,
+                              long amount)
     {
         long pend = getValue(src, contractid, true);
         long confirmed = getValue(src, contractid, false);
@@ -670,25 +728,11 @@
             return null ;
         }
 
-        // this disappears when we get new better StateReceipts....
-        AccountInfo targetinfo = new AccountInfo(tgt.getId(), null, null);
-        AccountInfo sourceinfo = new AccountInfo(src.getId(), null, null);
-
-        //
-        //  Have to create and store the PendingReceipt securely
-        //  before returning the payment to the caller.  Once
-        //  returned, we have lost control of it, so must have the
-        //  PendingReceipt receipt there for matching or cancelling.
-        //
-        PendingReceipt pending = new PendingReceipt(pay.getId(),
-                                          contractid,
-                                          sourceinfo, targetinfo,
-                                          amount, desc, new Date());
         try {
-            receiptStore.addPendingReceipt(pending, src);
-        } catch (StoreException ex) {
+            savePaymentAsPending(src, pay);
+        } catch (PaymentException ex) {
             ex.printStackTrace();
-            error("Error saving receipt");
+            error("Payment not saved: " + ex);
             return null ;
         }
 
@@ -787,10 +831,22 @@
                                        "Payment no good: " + ex);
         }
 
-        // this disappears when we get new better StateReceipts....
-        AccountInfo targetinfo = new AccountInfo(tgt.getId(), null, null);
-        AccountInfo sourceinfo = new AccountInfo(src.getId(), null, null);
+        savePaymentAsPending(src, pay);
 
+        return pay ;
+    }
+
+
+    /**
+     *  Save a payment made as Pending.
+     *
+     * @param pay the Payment written out but not as yet saved
+     *  @except PaymentException if the save could not be effected
+     */
+    protected void savePaymentAsPending(AccountId src, AbstractPayment pay)
+        throws PaymentException
+    {
+
         //
         //  Have to create and store the PendingReceipt securely
         //  before returning the payment to the caller.  Once
@@ -798,9 +854,13 @@
         //  PendingReceipt receipt there for matching or cancelling.
         //
         PendingReceipt pending = new PendingReceipt(pay.getId(),
-                                          contractid,
-                                          sourceinfo, targetinfo,
-                                          amount, desc, new Date());
+                                          pay.getItem(),
+                        // disappears when we get new better StateReceipts....
+                        new AccountInfo(src.getId(),             null, null),
+                        new AccountInfo(pay.getTarget().getId(), null, null),
+                                          pay.getQty(),
+                                          pay.getDesc(),
+                                          new Date());
         try {
             receiptStore.addPendingReceipt(pending, src);
         } catch (StoreException ex) {
@@ -808,13 +868,314 @@
             throw new PaymentException(PaymentException.UNKNOWN,
                                        "Error saving Pending: " + ex);
         }
+    }
 
+
+    /**
+     *  Make a Rollover payment.
+     *  This should be used by low level clients.  It checks:
+     *     * access to subaccounts,
+     *     * saves the pending payment for later reconciliation.
+     *  and delivers the payment back.
+     *
+     *  It does not check balances (by definition).
+     *  It is thread safe.  Why?
+     *
+     * @param tgt the target account, should not be bearer, where funds go
+     * @param src the source account (to be frozen) where funds are 
+     * @param contractid is the name of the contract, but this is only used
+     *        to identify the issuer, all contracts at that issuer effected
+     * @param desc a description of what this payment is for (optional)
+     * @param boolean whether or not the payment is a rollover payment
+     * @param from the time from which the payment is valid
+     * @param till the time at which the payment will expire
+     */
+    public synchronized Payment makeRollover(AccountId src, AccountId tgt,
+                              ItemId contractid,
+                              byte[] desc,
+                              long till)
+        throws PaymentException
+    {
+        if (isClosed())
+            throw new PaymentException(WalletException.CLOSED, closeReason());
+
+        Account acc;
+        try {
+            acc = getAccount(src);
+        } catch (StoreException ex) {
+            ex.printStackTrace();
+            throw new PaymentException(PaymentException.UNKNOWN_AC,
+                                       "Error getting account: " + ex);
+        }
+
+        /*
+         *  There is always "enough" for a rollover...
+         */
+
+        ValueAccount sub;
+        Payment pay;
+        try {
+            sub = (ValueAccount) acc.getSub(contractid);
+            if (sub == null)
+            {
+                throw new PaymentException(PaymentException.UNKNOWN_SUB,
+                                       "unknown: " + contractid);
+            }
+            pay = sub.createPayment(tgt,
+                                    0,     /* value doesn't matter */
+                                    desc,
+                                    true,  /* the flag that makes it Rollover */
+                                    System.currentTimeMillis(),
+                                    till,
+                                    null
+                                    );
+        } catch (SOXException ex) {
+            ex.printStackTrace();
+            throw new PaymentException(PaymentException.UNKNOWN,
+                                       "Payment no good: " + ex);
+        }
+
+        savePaymentAsPending(src, pay);
+
         return pay ;
     }
 
+
     /**
-     * Smart decoder.
+     *  Make a Token payment.
+     *  This should be used by all automatic clients, it does:
+     *     * access to subaccounts,
+     *     * checks balances, and
+     *     * saves the pending payment for later reconciliation.
+     *  and delivers the payment back.  It is thread safe.
+     *
+     * @param tgt the target account, may be bearer
+     * @param src the source account where funds are written from
+     * @param contractid is the name of the contract
+     * @param amount the (contract) qty of items of which the payment is for
+     * @param desc a description of what this payment is for (optional)
+     * @param boolean whether or not the payment is a rollover payment
+     * @param from the time from which the payment is valid
+     * @param till the time at which the payment will expire
+     * @param type the type of token method from PaymentFactory
      */
+    public synchronized TokenPayment makeTokenPayment(
+                              AccountId src, AccountId tgt,
+                              ItemId contractid, long amount,
+                              byte[] desc,
+                              long from, long till,
+                              int type)
+        throws PaymentException
+    {
+        if (isClosed())
+            throw new PaymentException(WalletException.CLOSED, closeReason());
+
+        if (amount < 0)
+            throw new IllegalArgumentException("amount " + amount + " < 0 !");
+
+        Account ac;
+        try {
+            ac = getAccount(src);
+        } catch (StoreException ex) {
+            ex.printStackTrace();
+            throw new PaymentException(PaymentException.UNKNOWN_AC,
+                                       "Error getting account: " + ex);
+        }
+
+        /*
+         *  Check if there is enough.
+         *  (There is always "enough" if the src is equal to the target
+         *  or if the amount is zero...  This is a judgement call of course.)
+         */
+        if ((amount > 0) &&
+            !gotEnough(src, contractid, amount))
+        {
+            throw new PaymentException(PaymentException.NOT_ENUF_FUNDS,
+                                       "amount " + amount + " not available");
+        }
+
+        ValueAccount sub;
+            sub = (ValueAccount) ac.getSub(contractid);
+            if (sub == null)
+            {
+                throw new PaymentException(PaymentException.UNKNOWN_SUB,
+                                       "unknown: " + contractid);
+            }
+
+        if (desc == null)
+            desc = new byte[0];
+
+        /*
+         *  We need two payments: one is the proto token payment,
+         *  the other is the cash to pay for it.  Both need to be
+         *  written, then saved.  Then, we can attempt a deposit
+         *  of the primary payment in order to pay for the proto.
+         *
+         *  We need to write a payment to someone who can cash these
+         *  payments ...  it could be bearer, but here, let's make it
+         *  "someone" and see what happens.
+        AccountId bearerFloat = new AccountId();
+        byte[] f = webfunds.sox.Crypto.digest("Random".getBytes());
+        bearerFloat.setId(f);
+         */
+
+        Payment pay;
+        try {
+            pay = sub.createPayment(new AccountId(), amount,
+                                    desc, false,
+                                    from, till,
+                                    null);
+        } catch (SOXException ex) {
+            ex.printStackTrace();
+            throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
+        }
+
+        Token[] tokens;
+        if (type == PaymentFactory.RANDOM_TOKEN)
+            tokens = RandomToken.getProtoTokens(amount);
+        else
+        {
+            throw new PaymentException(PaymentException.UNKNOWN_TYPE,
+                                       "Token Type " + type + " not supported");
+        }
+
+        TokenPayment proto;
+        String pid = pay.getId();
+        try {
+            proto = sub.createTokenPayment(tokens, desc, pid + "+");
+        } catch (SOXException ex) {
+            ex.printStackTrace();
+            throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
+        }
+
+        savePaymentAsPending(src, pay);
+// actually, what is the point of saving a proto payment?
+//        savePaymentAsPending(src, proto);
+
+
+        //
+        //  Beyond this point, failure leaves a dangling payment,
+        //  cancel manually for now.
+        //
+        MailItem[] mails;
+        try {
+            mails = sub.deposit(pay, new String(desc), null);
+        } catch (SOXLaterException ex) {
+            throw new PaymentException(ex.getNumber(), "Later: " + ex);
+        } catch (SOXSubAccountException ex) {
+            throw new PaymentException(ex.getNumber(), "SOXSAEx: " + ex);
+        } catch (SOXException ex) {
+            throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
+        }
+
+        logmsg("received mails: " + mails.length);
+        
+        //
+        //  By convention, if mails are returned, 1st one is
+        //  the one for this deposit.
+        //  Others are for the (sub?) account.
+        //  (Actually, as implemented, there is only one mail.)
+        //
+        if (mails == null)
+        {
+            throw new PaymentException(PaymentException.UNKNOWN,
+                  "Withdraw partial (no mail returned) try an update ?");
+        }
+
+        /*
+         *  As we have no clue who the payment went to, extract it
+         *  out of the receipt so we can ... check it against the
+         *  receipt.
+         */
+        byte[] b = mails[0].getMessage();
+        Receipt receipt;
+        try {
+            receipt = new Receipt(b);
+        } catch (SOXPacketException ex) {
+            throw new PaymentException(ex.getNumber(),
+                              "SOXPEx on receipt: " + ex);
+        }
+        AccountId ptt = receipt.getTarget();
+
+        String s = checkReceipt(mails[0], src, ptt, contractid, pay.getQty());
+        if (s != null)
+        {
+            throw new PaymentException(PaymentException.UNKNOWN,
+                  "cancel receipt is not good: " + s);
+        }
+
+        internalUpdate(sub, mails);
+
+        AbstractPayment pp = receipt.getExchangePayment();
+        if (pp == null && src.equals(receipt.getTarget()))
+        {
+            throw new PaymentException(PaymentException.UNKNOWN_TYPE,
+                      "no Payment returned in Withdrawal"+
+                      "\n\n(Issuer does not support this type of Payment)");
+        }
+        if ( !(pp instanceof TokenPayment) )
+        {
+            throw new PaymentException(PaymentException.UNKNOWN,
+                      "AB is not TP? : " + pp.getClass() +
+                      "\n\n" + pp.toString());
+        }
+        TokenPayment tp = (TokenPayment)pp;
+
+        if (!tp.isSigned())
+        {
+            throw new PaymentException(PaymentException.UNKNOWN,
+                      "TP not signed? : " + tp.getClass() +
+                      "\n\n" + pp.toString());
+        }
+        return tp;
+    }
+
+
+    public final static String SOX_MESSAGE = "SOX MESSAGE";
+
+    /**
+     *  Make an ASCII-armoured message from a payment, a la PGP armouring.
+     *  Low level wallet.
+     */
+    public byte[] asciiArmour(AbstractPayment pay)
+        throws PaymentException
+    {
+        if (pay == null)
+            throw new PaymentException("null");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try {
+            pay.encode(bos);
+        } catch (IOException ex) {
+            ex.printStackTrace();
+            throw new PaymentException("Payment no good: " + ex);
+        }
+
+        String s = Armoury.encode(SOX_MESSAGE, bos.toByteArray());
+
+        return s.getBytes();
+    }
+
+    /**
+     *  Make an ASCII-armoured message from a payment, a la PGP armouring.
+     *  High level wallet.
+     */
+    public byte[] asciiArmourWithError(AbstractPayment pay)
+    {
+        byte[] b;
+        try {
+            b = asciiArmour(pay);
+        } catch (PaymentException ex) {
+            error("Payment no good: " + ex);
+            return null ;
+        }
+
+        return b;
+    }
+
+    /**
+     *  Smart decoder of SOX armoured messages.
+     */
     public byte[] decodeSOXPayment(byte[] payment)
     {
         byte[] thing;
@@ -831,7 +1192,7 @@
         logmsg("failed on Armoury.decode, trying decodeByteArray");
 
         try {
-            thing = Armoury.decodeByteArray("SOX MESSAGE", pay);
+            thing = Armoury.decodeByteArray(SOX_MESSAGE, pay);
             return thing;
         } catch (Exception ex) { }
 
@@ -841,6 +1202,9 @@
     }
 
 
+
+///////////  Deposit  //////////////////////////////////////////
+
     public void makeDeposit(byte[] payment, AccountInfo callerAc, byte[] desc)
     {
         if (isClosed())
@@ -1404,6 +1768,8 @@
 
 
 
+///////////  Cancel  ////////////////////////
+
     /**
      *  Cancel many Transactions.  Provides a chance to batch
      *  up the transactions.  All details should be the same
@@ -1748,6 +2114,7 @@
     }
 
 
+
 ///////////  Account / Contract manipulations ////////////////////////
 
     /**
@@ -2260,9 +2627,12 @@
         //  Would often be two cycles, might be 3 if
         //  something arrived during the process.
         //  Too many cycles would probably indicate a bug
-        //  somewhere.
+        //  somewhere.  Although, if someone does a whole
+        //  bunch of transactions, this can cause a steady
+        //  series of cycles.  Better to back off in that
+        //  case.  (Like if WebFunds does a hundred cancels...)
         //
-        for (int i = 0; i < 4; i++)
+        for (int i = 0; i < 10; i++)
         {
             mails = tryUpdate(sub, confirms);
             if (mails == null || (mails.length == 0))
@@ -2281,7 +2651,8 @@
                 return ;
         }
 
-        throw new Panic("too many: mail appears to be spinning");
+        logmsg("too many: mail appears to be spinning");
+        return ;
     }
 
 



1.7       +3 -2      java/webfunds/client/sox/WalletException.java

Index: WalletException.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/sox/WalletException.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- WalletException.java	2000/09/30 18:50:14	1.6
+++ WalletException.java	2000/11/09 13:28:20	1.7
@@ -1,5 +1,5 @@
 /*
- * $Id: WalletException.java,v 1.6 2000/09/30 18:50:14 iang Exp $
+ * $Id: WalletException.java,v 1.7 2000/11/09 13:28:20 iang Exp $
  *
  * Copyright (c) 2000 Systemics Inc on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -37,8 +37,9 @@
                             NOT_ENUF_FUNDS = 108, // asking for too much!
                             PID_IN_USE     = 109, // requested pid already used
                             CLOSED         = 110, // wallet has been shut down
+                            UNKNOWN_TYPE   = 111, // what type of payment?
 
-                            LAST_WALLET_ERRNO = 110;
+                            LAST_WALLET_ERRNO = 111;
 
     /**
      *  Use these rather than worry about which number...



1.1                  java/webfunds/client/sox/gui/PaymentFrame.java

Index: PaymentFrame.java
===================================================================
/*
 * $Id: PaymentFrame.java,v 1.1 2000/11/09 13:28:20 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1995-1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.client.sox.gui;

import java.awt.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
import java.util.Date;

import webfunds.utils.Hex;
import webfunds.sox.*;
import webfunds.ricardian.Contract;

import webfunds.client.WalletInterface;
import webfunds.client.AccountInfo;
import webfunds.client.plugins.Plugin;
import webfunds.client.plugins.ModeNotSupportedException;

import webfunds.client.sox.PaymentException;
import webfunds.client.sox.SOXWallet;

/**
 *  This is a specialist SOX payment gui dialog, that knows all
 *  about the non-generic payments such as rollover and token.
 */
public class PaymentFrame
    extends Plugin implements ActionListener
{

    protected String name           = "PaymentFrame";
    protected String contractaction = "Pay";

    public String getPluginName()     { return name; }
    public String getContractAction() { return contractaction; }

    JFrame  frame;
    TextField amount;
    TextField target1;
    JTextField validFrom;
    JTextField validTill;
    TextArea   paybox;
    SOXWallet wallet;
    AccountInfo source;
    AccountId src;
    JTextArea description;
    JComboBox target2;
    JComboBox typeChoice;

    ItemId contractid;
    Contract contract;
    String contractName;

    String title;

    protected final static String TEXT_PAY = "Make Payment",
                                  DIRECT_TFR = "Direct Deposit";

    protected final static String SOX = "SOX", FRZ = "Rollover",
                                  BRN = "Big Random Numbers", DAW = "Wagner";
    protected final static String[] types = { SOX, FRZ, BRN, DAW };
    
    public PaymentFrame() { }
    
    public void init(WalletInterface wi, AccountInfo source, ItemId contractId)
        throws ModeNotSupportedException
    {
//System.err.println("PluginManager: " + pm);
        if ( !(wi instanceof SOXWallet) )
            throw new ModeNotSupportedException("wallet is not SOX: " + wi);

        wallet           = (SOXWallet)wi;
        this.source      = source;
        this.contractid  = contractId;

        if (source == null)
            throw new IllegalArgumentException("source == null");
        src = new AccountId();
        src.setId(source.getId());

        // might be null
        contract = cs.getContract(contractid);
        contractName = (contract==null) ? contractid.fp() : contract.getName();

        title = "Payment in " + contractName +
                " from account: " + source.toString();


        try
        {

            AccountInfo[] accounts = pm.getAddressbook(this).getAccounts();
            DefaultComboBoxModel acList;
            acList = new DefaultComboBoxModel(accounts);
            AccountInfo bearer = new AccountInfo();
            acList.insertElementAt(bearer, 0);
            acList.setSelectedItem(bearer);

            DefaultComboBoxModel typeList;
            typeList = new DefaultComboBoxModel(types);
            typeList.setSelectedItem(SOX);

            amount      = new TextField(15);
            target1      = new TextField(15);
            target2     = new JComboBox(acList);
            validFrom   = new JTextField(15);
            validTill   = new JTextField(15);
            typeChoice  = new JComboBox(typeList);
            paybox     = new TextArea(20, 55);
            description = new JTextArea(5, 15);
            frame       = new JFrame(title);
            JPanel pane = (JPanel)frame.getContentPane();
            
            JLabel amountlbl        = new JLabel("Amount:");
            JLabel target1lbl       = new JLabel("Target:");
            JLabel target2lbl       = new JLabel("Target:");
            JLabel validFromlbl     = new JLabel("Valid from:");
            JLabel validTilllbl     = new JLabel("Valid till:");
            JLabel typelbl          = new JLabel("Type:");
            JLabel desclbl          = new JLabel("Description:");
            JButton ok              = new JButton(TEXT_PAY);
            JButton directdeposit   = new JButton(DIRECT_TFR);
            JButton close           = new JButton("Close");
            JPanel butpanel         = new JPanel();
            JPanel textpanel        = new JPanel();

            JMenuBar menu           = new JMenuBar();
            JMenu filemenu          = new JMenu("File");
            JMenuItem exititem      = new JMenuItem("Close");
            JMenuItem configureitem = new JMenuItem("Configure");
            
            // why won't the menu show?
            frame.setJMenuBar(menu);
            menu.add(filemenu);
            filemenu.add(configureitem);
            filemenu.add(new JSeparator());
            filemenu.add(exititem);

            ok.setToolTipText("Write a Cut & Pastable Payment into Box");
            directdeposit.setToolTipText("Write Payment and Deposit to Target");
            close.setToolTipText("Close Dialog");
            
            exititem.addActionListener(this);
            configureitem.addActionListener(this);
            ok.addActionListener(this);
            close.addActionListener(this);
            directdeposit.addActionListener(this);
    
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            //description.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
            //paybox.setEnabled(false);
            
            filemenu.setMnemonic('f');
            exititem.setMnemonic('x');
            configureitem.setMnemonic('n');
            ok.setMnemonic('m');
            close.setMnemonic('c');
    
            amountlbl.setLabelFor(amount);
            target1lbl.setLabelFor(target1);
            target2lbl.setLabelFor(target2);
            validFromlbl.setLabelFor(validFrom);
            validTilllbl.setLabelFor(validTill);
            typelbl.setLabelFor(typeChoice);
            desclbl.setLabelFor(description);
    
            textpanel.setLayout(new GridBagLayout());
            
            int x = 0;
            int y = 0;
            GridBagConstraints c = new GridBagConstraints();
            
            //
            //    Column 0: Labels
            //
            c.gridx = x++;
            c.gridy = y++;
            c.gridwidth = GridBagConstraints.RELATIVE;
            c.gridheight = 1;
            c.weightx = 0;
            c.weighty = 0;
            c.anchor = GridBagConstraints.NORTHWEST;
            c.fill = GridBagConstraints.NONE;
            c.insets = new Insets(10, 3, 3, 3);
            textpanel.add(amountlbl, c);
            
            c.gridy = y++;
            c.insets = new Insets(3, 3, 3, 3);
            textpanel.add(target1lbl, c);
            
            c.gridy = y++;
            textpanel.add(target2lbl, c);
            
            c.gridy = y++;
            textpanel.add(validFromlbl, c);
            
            c.gridy = y++;
            textpanel.add(validTilllbl, c);
            
            c.gridy = y++;
            textpanel.add(typelbl, c);
            
            c.gridy = y++;
            c.weighty = 1;
            textpanel.add(desclbl, c);
            
            y = 0;
            
            //
            //    Column 1: Boxes
            //
            c.gridx = x++;
            c.gridy = y++;
            c.weighty = 0;
            c.weightx = 1;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.insets = new Insets(10, 3, 3, 3);
            textpanel.add(amount, c);
    
            c.gridy = y++;
            c.insets = new Insets(3, 3, 3, 3);
            textpanel.add(target1, c);
            
            c.gridy = y++;
            textpanel.add(target2, c);

            c.gridy = y++;
            textpanel.add(validFrom, c);
            
            c.gridy = y++;
            textpanel.add(validTill, c);
            
            c.gridy = y++;
            textpanel.add(typeChoice, c);
            
            c.gridy = y++;
            c.weighty = 1;
            textpanel.add(description,c);

            /*
             *  Build the Pane:  add the options panel (textpanel).
             */
            pane.setLayout(new GridBagLayout());
            
            GridBagConstraints ct = new GridBagConstraints();
            ct.gridx = 1;
            ct.gridy = 0;
            ct.gridwidth = GridBagConstraints.REMAINDER;
            ct.gridheight = GridBagConstraints.RELATIVE;
            ct.fill = GridBagConstraints.HORIZONTAL;
            ct.insets = new Insets(3, 3, 3, 3);
            ct.anchor = GridBagConstraints.NORTHWEST;
            ct.weightx = 0.7;
            ct.weighty = 1;
            pane.add(textpanel,ct);
            
            /*
             *  Add the buttons panel at bottom.
             */
            butpanel.add(ok);
            butpanel.add(directdeposit);
            butpanel.add(close);
            
            GridBagConstraints cb = new GridBagConstraints();
            cb.gridx = 0;
            cb.gridy = 1;
            cb.gridwidth = GridBagConstraints.REMAINDER;
            cb.gridheight = GridBagConstraints.REMAINDER;
            cb.fill = GridBagConstraints.BOTH;
            cb.insets = new Insets(3, 3, 3, 3);
            cb.weightx = 0;
            cb.weighty = 0;
            pane.add(butpanel, cb);
    
            /*
             *  Add the payments panel at left.
             */
            GridBagConstraints cp = new GridBagConstraints();
            cp.gridx = 0;
            cp.gridy = 0;
            cp.gridwidth = GridBagConstraints.RELATIVE;
            cp.gridheight = GridBagConstraints.RELATIVE;
            cp.anchor = GridBagConstraints.NORTHWEST;
            cp.weightx = 0.3;
            cp.weighty = 0;
            cp.fill = GridBagConstraints.BOTH;
            cp.insets = new Insets(3, 3, 3, 3);
            pane.add(paybox, cp);
            
            frame.pack();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        
    }
    
    public void run()
    {
        if (frame != null)
        {
            frame.show();
        }
    }

    public void actionPerformed(ActionEvent evt)
    {
        String command = evt.getActionCommand();

        //
        // dispose of the easy cases first
        //
        if (command.equals("Close"))
        {
            frame.dispose();
            this.stop();
            return ;
        }
        else if (command.equals("Configure"))
        {
            System.out.println("Should bring up config screen");
            return ;
        }

        //
        // Now we have to do a payment.  Sort out standard details first.
        //

        // amount in units of contract
        double dub;
        try {
            dub = new Double(amount.getText()).doubleValue();
        } catch (NumberFormatException ex) {
            String e = "Please specify an amount of " + contractName;
            pm.getUInterface(this).errorMessage(e);
            return ;
        }
        long longamount = contract.getUnitsOfContract(dub);

        //
        //  Get the target.  Text box of KHID overrides address book list.
        //  Default is bearer.
        //
        AccountInfo target = (AccountInfo)target2.getSelectedItem();
        String tgtName = target1.getText();

        if (!"".equals(tgtName))             // text box is (also?) set, use it
        {
            byte[] bytes = Hex.hex2data(tgtName);
            target = new AccountInfo(bytes, null, null);
        }
        else if (target.isBearer())             // what does target return ?
            tgtName = "BEARER";
        else
            tgtName = target.getName() + " (" + Hex.data2hex(target.getId()) + ")";

        // expiry date and earliest date
        Date validFrom = new Date(0);
        long future = 5 * 24 * 60 * 60 * 1000;
        Date validTill = new Date(System.currentTimeMillis() + future);

        AccountId tgt = new AccountId();
        if (target != null)               // target probably bearer
            tgt.setId(target.getId());
         

        //
        //  Extract the payment from the wallet.  What sort?
        //
        String typ = (String)typeChoice.getSelectedItem();
        byte[] pay = null;
        byte[] desc = description.getText().getBytes();
        AbstractPayment payment = null;
        long from = validFrom.getTime();
        long till = validTill.getTime();
        String typeString = typ;

        if (SOX.equals(typ))
        {
            // the payment itself
            pay = wallet.makePayment(contractid, source, target,
                             longamount, desc,
                             validFrom, validTill);
            if (pay == null)     // wallet encountered error, already reported
                return ;
        }
        if (FRZ.equals(typ))
        {
            // a rollover payment causes account to freeze and transfer funds
            try {
                payment = wallet.makeRollover(src, tgt,
                             contractid,
                             desc,
                             till);
            } catch (PaymentException ex) {
                String e = "Failed: " + ex;
                wallet.error(e);
                return ;
            }

            String q = "This experimental ROLLOVER payment is highly dangerous"+
                       " and could lose your value." +
                       "\n\nAre you absolutely sure?";
            if ( !wallet.ask(q) )
                return;
            pay = wallet.asciiArmourWithError(payment);
            if (pay == null)
                return;
            typeString = "Rollover";
        }
        else if (BRN.equals(typ))
        {
            // the payment itself
            try {
                payment = wallet.makeTokenPayment(src, tgt,
                             contractid, longamount,
                             desc,
                             from, till,
                             webfunds.sox.PaymentFactory.RANDOM_TOKEN);
            } catch (PaymentException ex) {
                String e = "didn't work: " + ex;
                wallet.error(e);
                return ;
            }
            if (payment == null)
                return ;

            String q = "This experimental TOKEN payment is highly dangerous "+
                       "and could lose your value." +
                       "\n\nAre you absolutely sure?";
            if ( !wallet.ask(q) )
                return;
            pay = wallet.asciiArmourWithError(payment);
            if (pay == null)
                return;
        }
        else if (DAW.equals(typ))
        {
            String e = "Sorry, this payment type not yet available.";
            wallet.error(e);
            return ;
        }


        //
        // Two sorts of delivery - direct transfer or make payment for email.
        //
        if (TEXT_PAY.equals(command))
        {
            double am = contract.getUnitsOfAccount(longamount);
            String tla = contract.getField("currency", "currency_tla");
            String nam = "";
            if (tla == null)
            {
                nam = " " + contractName;
                tla = "";
            }
            else
                tla = tla + " ";

            String msg = "\nThis is a " + typeString +
                         " payment for " + tla + am + nam +
                         "\nfrom " + source.getName() +
                                 " (" + Hex.data2hex(source.getId()) + ")" +
                         "\nto   " + tgtName +
                         "\nCut and Paste the following lines into an email:" +
                         "\n";

                         // " from wallet " + wallet.getShortName() +

            paybox.append(msg);;
            paybox.append(new String(pay)); 

            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Clipboard clipboard = toolkit.getSystemClipboard();
            StringSelection string = new StringSelection(paybox.getText());
            clipboard.setContents(string, string);
        }

        else if (DIRECT_TFR.equals(command))
        {
            if (target == null || target.isBearer())
            {
                String e = "You have to specify a target";
                pm.getUInterface(this).errorMessage(e);
                return ;
            }

            wallet.makeDeposit(pay, target, "Deposit".getBytes());
            // frame.dispose();
            // this.stop();
            // pm.getUInterface(this).infoMessage("Transfer done!");
        }
    }
        

}



1.2       +1 -3      java/webfunds/client/utils/UpgradesManager.java

Index: UpgradesManager.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/client/utils/UpgradesManager.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- UpgradesManager.java	2000/10/07 01:12:45	1.1
+++ UpgradesManager.java	2000/11/09 13:28:21	1.2
@@ -1,5 +1,5 @@
 /*
- * $Id: UpgradesManager.java,v 1.1 2000/10/07 01:12:45 iang Exp $
+ * $Id: UpgradesManager.java,v 1.2 2000/11/09 13:28:21 iang Exp $
  *
  * Copyright (c) 2000 Systemics Inc on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -37,11 +37,9 @@
 
     protected String name           = "UpgradesManager";
     protected String generalaction  = "UpgradesManager";
-    protected String contractaction = "View Upgrades";
 
     public String getPluginName()     { return name; }
     public String getGeneralAction()  { return generalaction; }
-    public String getContractAction() { return contractaction; }
 
     public static final String INSTALL_BUTTON = "Install Upgrade";
     public static final String GO_BUTTON      = "Go!";