Package com.moneychanger.core.util

Source Code of com.moneychanger.core.util.Helpers

/************************************************************
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

*                 M O N E Y C H A N G E R
*
*  Open Transactions:
*       Financial Cryptography and Digital Cash
*       Library, Protocol, API, Server, CLI, GUI
*   
*       -- Anonymous Numbered Accounts.
*       -- Untraceable Digital Cash.
*       -- Triple-Signed Receipts.
*       -- Cheques, Vouchers, Transfers, Inboxes.
*       -- Basket Currencies, Markets, Payment Plans.
*       -- Signed, XML, Ricardian-style Contracts.
*       -- Scripted smart contracts.
*   
*  Copyright (C) 2010-2013 by "Fellow Traveler" (A pseudonym)
*
*  EMAIL:
*  FellowTraveler@rayservers.net --- SEE PGP KEY BELOW.
*  F3llowTraveler@gmail.com --- (not preferred.)
*  FINGERPRINT:
*  9DD5 90EB 9292 4B48 0484  7910 0308 00ED F951 BB8E
*
*  BITCOIN:  1NtTPVVjDsUfDWybS4BwvHpG2pdS9RnYyQ
*
*  OFFICIAL PROJECT WIKI:
*  https://github.com/FellowTraveler/Moneychanger
*  https://github.com/FellowTraveler/Open-Transactions/wiki
*
*  WEBSITE:
*  http://www.OpenTransactions.org/
*   
*  Components and licensing:
*   -- Moneychanger..A Java client GUI.....LICENSE:.....GPLv3
*   -- otlib.........A class library.......LICENSE:...LAGPLv3
*   -- otapi.........A client API..........LICENSE:...LAGPLv3
*   -- opentxs/ot....Command-line client...LICENSE:...LAGPLv3
*   -- otserver......Server Application....LICENSE:....AGPLv3
*  Github.com/FellowTraveler/Open-Transactions/wiki/Components
*
*  All of the above OT components were designed and written by
*  Fellow Traveler, with the exception of Moneychanger, which
*  was contracted out to Vicky C (bitcointrader4@gmail.com).
*  The open-source community has since actively contributed.
*
*  -----------------------------------------------------
*
*   LICENSE:
*   This program is free software: you can redistribute it
*   and/or modify it under the terms of the GNU General
*   Public License as published by the Free Software
*   Foundation, either version 3 of the License, or (at your
*   option) any later version.
*
*   You should have received a copy of the GNU General
*   Public License along with this program.  If not, see:
*   http://www.gnu.org/licenses/
*
*   If you would like to use this software outside of the free
*   software license, please contact FellowTraveler.
*   (Unfortunately many will run anonymously and untraceably,
*   so who could really stop them?)
*  
*   DISCLAIMER:
*   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 for more
*   details.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Darwin)

iQIcBAEBAgAGBQJRSsfMAAoJEAMIAO35UbuOCTQQAJWUR6l+IbUGYPfudumDBZep
XWu5aUVXPt+HTetWobTT3VrSEoQTV+t3Qk10EHzIIQNIvDNkA3cfQod5xMk3CZgm
MuA1PTDZdmrcm1lf92rVULaiB2tHYuPKYryFfkmNcidoWaJAI0ny/AE4gSdopKuU
MCLhB+fWpPv+nK9Qjb8duvRyFjYeLxrnwjfy05zNIY5Fj4HsCzmf6G6xqbUOZzBA
Zc4RjfKeg9MVJl4ObIKhDfpicCTVZkgFPVGeok/KtmiPRgV1HtaHqib4RiN9VMkr
YKbOUOb931pukRJQv+z5fT1EQkSVBDO5Th2q7Nls5idMgtR2BPXxVhs+e8OM4IJK
W+1V0WHWHuE+6SRKQrPU4hAmXrtmGRtu474TTmPlW6dCqFgvLWBuxeTRCPf4l29T
ImEOxdjFwlMVoxsazE3KE/YMbX7IiZqgLx/C4OTPlz7BVLhphzPRXA7KhhRFi1Df
jie5oRhJ4zbTFQI8SKsjbx0H/4VpB+Vtjx6fOxnLUpjZAE7G6ZL2zOEK8rtiGeiH
0AWK2rKWP8oOMnQwBMP838WRtxFmaOIhvVqAngjynSmVouf1RKKU7y7/YQ0iVDcN
WqAhaZhvszQ6UCDAEi11rfdC2qt29Jds9lS41YGqvYaan+b50lr5u59Uknz2LC94
HS4/gWtdVEVnXDda0wk9
=CbRB
-----END PGP SIGNATURE-----
**************************************************************/


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.moneychanger.core.util;


import com.moneychanger.core.Account;
import com.moneychanger.core.OpenTransactionAccount;
import com.moneychanger.core.dataobjects.OTDetails;
import com.moneychanger.ui.MainPage;
import com.moneychanger.ui.model.AccountTableModel;
import com.moneychanger.ui.panels.OpenTransactionAccountBottomPanel;
import com.moneychanger.ui.panels.OpenTransactionAccountTopPanel;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.LookAndFeel;
import org.opentransactions.otapi.AddressBook;
import org.opentransactions.otapi.MarketList;
import org.opentransactions.otapi.OfferListMarket;
import org.opentransactions.otapi.OfferListNym;
import org.opentransactions.otapi.Storable;
import org.opentransactions.otapi.StoredObjectType;
import org.opentransactions.otapi.TradeListMarket;
import org.opentransactions.otapi.TradeListNym;
import org.opentransactions.otapi.WalletData;
import org.opentransactions.otapi.otapi;
import org.opentransactions.otapi.otapiJNI;
import org.opentransactions.otjavalib.util.Utility;
import org.opentransactions.otjavalib.util.Utility.OTBool;
import org.opentransactions.otjavalib.util.Utility.OTInteger;

/**
*
* @author Vicky C and Cameron
*/
public class Helpers {

// ------------------------------------
    private static Object settingsObj;
    private static String m_nymID, lastReplyReceived;
    private static List basketExistingAssets = new ArrayList();
   
    public static List getBasketExistingAssets() {
        return basketExistingAssets;
    }

    public static String getLastReplyReceived() {
        return lastReplyReceived;
    }

    public static void setLastReplyReceived(String strReply) {
        lastReplyReceived = strReply;
    }

    public static void addBasketExistingAssets(String assetID) {
        Helpers.basketExistingAssets.add(assetID);
    }

    public static void clearBasketExistingAssets() {
        Helpers.basketExistingAssets.clear();
    }

    public static void setBasketExistingAssets(List basketExistingAssets) {
        Helpers.basketExistingAssets = basketExistingAssets;
    }

    public static String getMinTransfer() {
        return minTransfer;
    }

    public static void setMinTransfer(String minTransfer) {
        Helpers.minTransfer = minTransfer;
    }

    public static String getSubCurrency() {
        return subCurrency;
    }

    public static void setSubCurrency(String subCurrency) {
        Helpers.subCurrency = subCurrency;
    }

    public static boolean getCancelBasket() {
        return cancelBasket;
    }

    public static void setCancelBasket(boolean cancelBasket) {
        Helpers.cancelBasket = cancelBasket;
    }
    private static String minTransfer;
    private static String subCurrency;
    private static boolean cancelBasket = false;

    public static String getNymID() {
        return m_nymID;
    }

    public static void setNymID(String nymID) {
        Helpers.m_nymID = nymID;
    }

    public static Object getSettingsObj() {
        return settingsObj;
    }

    public static void setSettingsObj(Object settingsObj) {
        Helpers.settingsObj = settingsObj;
    }
    private static String dataFolder;

    public static String getDataFolder() {
        return Helpers.dataFolder;
    }

    public static void setDataFolder(String dataFolder) {
        Helpers.dataFolder = dataFolder;
    }

    public static Object obj;

    public static Object getObj() {
        return obj;
    }

    public static void setObj(Object obj) {
        Helpers.obj = obj;
    }

    // OT Helpers
    public static void reloadOTDetails(String accountID) {
        Account account = new OpenTransactionAccount();
        Object details = account.getAccountDetails(accountID);
        OTDetails otDetails = (OTDetails) details;
        populateOTDetails(otDetails);

        JTable table = MainPage.getAccountTable();
        ((AccountTableModel) table.getModel()).setValueAt(otDetails.getBalance(), table.getSelectedRow(), 1);
    }

    public static void populateOTDetails(OTDetails otDetails) {
        if (otDetails == null) {
            return;
        }
        OpenTransactionAccountTopPanel.populateOTDetails(otDetails);
        OpenTransactionAccountBottomPanel.populateOTDetails(otDetails);

        System.out.println("IN populateOTDetails");

    }
    private static String basketXAcct;
    private static boolean basketXCancelled;

    public static boolean isBasketXCancelled() {
        return basketXCancelled;
    }

    public static void setBasketXCancelled(boolean basketXCancelled) {
        Helpers.basketXCancelled = basketXCancelled;
    }

    public static String getBasketXAcct() {
        return basketXAcct;
    }

    public static void setBasketXAcct(String basketXAcct) {
        Helpers.basketXAcct = basketXAcct;
    }
    private static boolean loadNymTrades = false;

    public static boolean isLoadNymTrades() {
        return loadNymTrades;
    }

    public static void setLoadNymTrades(boolean loadNymTrades) {
        Helpers.loadNymTrades = loadNymTrades;
    }

    public static Object otDepositCash;

    public static Object getOtDepositCash() {
        return otDepositCash;
    }

    public static void setOtDepositCash(Object otDepositCash) {
        Helpers.otDepositCash = otDepositCash;
    }

    // Get Helpers:
    public static String getKey(Map map, String value) {

        if ("All".equalsIgnoreCase(value)) {
            return "ALL";
        }

        if (!Utility.VerifyStringVal(value)) {
            return null;
        }

        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
            String key = (String) i.next();
            if (map.get(key).equals(value)) {
                return key;
            }
        }
        return null;
    }

    // ------------------------------------------------------
    public static int getNymboxLowLevel(String serverID, String nymID) {
        OTBool bWasSent = new OTBool(false);

        return Helpers.getNymboxLowLevel(serverID, nymID, bWasSent);
    }

    // This returns -1 if error, or a positive request number if it was sent.
    // (It cannot return 0.)
    // Called by getAndProcessNymbox.  
    // DONE
    public static int getNymboxLowLevel(String serverID, String nymID, OTBool bWasSent) {
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
        bWasSent.setBooleanValue(false);
        // --------------------------------------------------------------------
        final int nRequestNum = otapiJNI.OTAPI_Basic_getNymbox(serverID, nymID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getNymboxLowLevel: ERROR, not supported. (-2 was returned.)");
                return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getNymboxLowLevel: Failed to send getNymbox message due to error.");
                return (-1);
            case (0):
                System.out.println("Utility.getNymboxLowLevel: Unexpectedly returned 0. Didn't send getNymbox message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return (-1); // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getNymbox. Therefore I treat it as an error.
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getNymboxLowLevel: Unexpected request number: " + nRequestNum);
                    return (-1);
                }
                // -------------------------
                break; // SUCCESS!
        }
        bWasSent.setBooleanValue(true);
        // ***************************************************
        //
        final int nResult = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getNymboxLowLevel");
//        System.out.println("IN Utility.getNymboxLowLevel " + Utility.getLastReplyReceived());

        // BY this point, we definitely have the request number in nResult, which means
        // the message was actually SENT. (At least.) This also means we can use nResult
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //
        // THE REMOVE SENT MESSAGE BELOW FAILS, LIKE IT'S ALREADY GONE.
        //
        // THIS MUST BE DUE TO THE PROCESS SERVER REPLY THAT OCCURS **IMMEDIATELY** after the message was originally sent!
        // (The reply came in and was sent to OT's "ProcessServerReply", INSIDE the call to getNymbox.)
        // Our subsequent "receive" (above) is nothing of the sort, but actually pops the incoming message buffer where
        // the server's reply was ALREADY SITTING, since it was put there in OT's "ProcessServerReply", WHICH REMOVED THE
        // SENT MESSAGE ALREADY (that's why the below call to RemoveSentMessage fails.)
        //
        // RETHINK any logic that doesn't take this into account,.
        // Either we REMOVE this call wherever this happens, OR... we call Get first and make sure whether it's
        // there, THEN remove it. But we can't be lumping "Failure because it's gone" versus "Error state" by mixing
        // 0 and -1 here. We need to differentiate.
        //
        // Bottom line: if the reply WAS received, then the original sent message has ALREADY been removed
        // from the sent buffer. Whereas if the reply was NOT received, then the sent message is still there,
        // but in that case, we do NOT want to remove it -- we want it to STAY in the sent buffer, so that
        // when we get the Nymbox later and we DO have the reply from that, THEN we can remove the sent msg from
        // the sent buffer. Until then, we don't want OT to think it's already been processed (which it will, if
        // it's already been removed from the sent buffer. So we leave it there for now.)
        //
        //
        //

//        final int nRemovedSentMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        if (nRemovedSentMsg < 1)
//        {
//            System.out.println("Utility.getNymboxLowLevel: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedSentMsg);
//        }
        // ----------------------------------------------

        if (1 == nResult) {
            return nRequestNum;
        }
        return nResult;
    }

    // GET NYMBOX
    //
    // Note: The caller needs to call OT_API_RemoveSentMessage after
    // calling this function.
    // Correction: I spoke too soon. GetNymboxLowLevel ALREADY does
    // these things:
    //    receiveReplySuccessLowLevel
    //    OT_API_RemoveSentMessage(Integer.toString(nRequestNum));
    //
    public static int getNymbox(String serverID, String nymID) {
        return Helpers.getNymbox(serverID, nymID, false); // bForceDownload=false
    }

    public static int getNymbox(String serverID, String nymID, boolean bForceDownload) {
        //-------------------------------------------------                       
        final String strRecentHash = otapiJNI.OTAPI_Basic_GetNym_RecentHash(serverID, nymID);
        final boolean bRecentHash = Helpers.isValid(strRecentHash);
        if (!bRecentHash) {
            System.out.println("Utility.getNymbox(): Warning: Unable to retrieve recent cached copy of server-side "
                    + "NymboxHash from client-side nym (perhaps he's never downloaded it before?)\n\n");
        }
        //-------------------------------------------------               
        String strLocalHash = otapiJNI.OTAPI_Basic_GetNym_NymboxHash(serverID, nymID);
        boolean bLocalHash = Helpers.isValid(strLocalHash);
        if (!bLocalHash) {
            System.out.println("Utility.getNymbox(): Warning: Unable to retrieve client-side NymboxHash "
                    + "for:\n serverID: " + serverID + "\n nymID: " + nymID);
        }
        //-------------------------------------------------
        if (!bForceDownload) {
            if (bLocalHash
                    && bRecentHash
                    && strRecentHash.equals(strLocalHash)) // the hashes match -- no need to download anything.
            {
                System.out.println("Utility.getNymbox(): The hashes already match (skipping Nymbox download.)");
                return 1;
            }
        }

        //-------------------------------------------------
        // -- SECTION 1: "GET NYMBOX"
        //
        OTBool bWasMsgSent = new OTBool(false);

        int nGetNymbox = Helpers.getNymboxLowLevel(serverID, nymID, bWasMsgSent); // bWasMsgSent is output from this call.       

        if (bWasMsgSent.getBooleanValue()) {
            System.out.println("Utility.getNymbox(): FYI: Utility.getNymboxLowLevel apparently SENT the request. nGetNymbox is: " + nGetNymbox);
        }
        // -----------------------------------------------------------
        if ((false == bWasMsgSent.getBooleanValue()) || // UPDATE: This might've been a bug:  Removing this for now, since I still want to give it one last chance with the getRequest call, just below.
                ((nGetNymbox <= 0) && ((-1) != nGetNymbox))) {
            System.out.println("Utility.getNymbox(): Failure: Utility.getNymboxLowLevel returned unexpected value: " + nGetNymbox);
            return (-1);
        }   // NOTE: for getNymbox, there is no '0' return value, which is why you don't see me handling it here.
        // -----------------------------------------------------------
        if ((-1) == nGetNymbox) // we'll try re-syncing the request number, then try again.
        {
            System.out.println("Utility.getNymbox(): FYI: Utility.getNymboxLowLevel returned -1. (Re-trying...)");

            final int nGetRequest = Helpers.getRequestNumber(serverID, nymID);

            if (1 != nGetRequest) {
                System.out.println("Utility.getNymbox(): Failure: Utility.getNymboxLowLevel failed, then I tried to resync with getRequestNumber and then that failed too. (I give up.)");
                return (-1);
            }
            // ---------------------------------

            final String strLastReplyReceived = Helpers.getLastReplyReceived();
            // I had to do this bit because getRequestNumber doesn't return the actual
            // reply itself. But in this case, I needed it.
            if (!Helpers.isValid(strLastReplyReceived)) // THIS SHOULD NEVER HAPPEN.
            {
                System.out.println("Utility.getNymbox(): "
                        + "ERROR in Utility.getLastReplyReceived(): why was this string not set, when Utility.getRequestNumber was otherwise an apparent success?");
                return (-1); // (SHOULD NEVER HAPPEN. This string is set in the getRequestNumber function.)
            }
            //-------------------------------------------------

            // BY THIS POINT, we have received a server reply:  @getRequest
            // (Unless it is malformed.) It's definitely not null, nor empty.

            //-------------------------------------------------

            // Grab the NymboxHash on the @getRequest reply, and also the one I
            // already had on my client-side Nym... (So we can compare them.)
            //
            //      If the hashes do NOT match, then I DO need to download nymbox and box receipts.
            /*
             *      ===> If the NymboxHash is changed from what I expected, then I need to re-download the
             *      nymbox (and any box receipts I don't already have.)
             *
             *      Then I need to process the Nymbox. But first, see if my missing server reply is in there.
             *      If it is, then I have the server reply! (As if we had succeeded in the first place!!)
             *      Next, process the Nymbox (which processes that reply) and then return strReply like normal.
             *
             *      (Clearly this is just going to be a normal part of the getRequest syncronization.)
             *
             *      By the time that much is done, I will KNOW the request number, the nymbox, the box receipts,
             *      etc are ALL syncronized properly, and that I THEN processed the Nymbox successfully.
             *
             *
             *      NOTICE: In this example I do NOT want to pull out my sent message from the outbuffer (using
             *      the request number) and try to harvest all the transaction numbers. Why not? Because possibly the
             *      server DID reply! And if I processed that reply properly, it would sync my transaction numbers
             *      properly just from that! ===>
             *
             *      ===> Therefore, I need to see FIRST if the old message has a reply WAITING in the Nymbox. THEN
             *      I need to process the Nymbox. ONLY if the reply wasn't there, can I THEN pull out the message
             *      from my outbuffer and harvest it. (Which I am reticent to do, until I am SURE the server
             *      really never saw that message in the first place.)
             *
             *      However, as long as my NymboxHash hasn't changed, then I'm safe! But if it HAS changed,
             *      then I HAVE to A. download it B. SEE if the reply is there for the request number, then
             *      C. process it. ... If the reply wasn't there, THEN Harvest the transaction #s (for transaction
             *      messages) and then re-try.
             */

            //-------------------------------------------------      
            // Grabbing again in case it's changed.
            //
            final String strServerHash = otapiJNI.OTAPI_Basic_Message_GetNymboxHash(strLastReplyReceived);
            final boolean bServerHash = Helpers.isValid(strServerHash);
            if (!bServerHash) {
                System.out.println("Utility.getNymbox(): Warning: Unable to retrieve server-side "
                        + "NymboxHash from server @getRequest reply:\n\n"
                        + strLastReplyReceived);
            }
            //-------------------------------------------------               
            strLocalHash = otapiJNI.OTAPI_Basic_GetNym_NymboxHash(serverID, nymID);
            bLocalHash = Helpers.isValid(strLocalHash);

            if (!bLocalHash) {
                System.out.println("Utility.getNymbox(2): Warning: Unable to retrieve client-side NymboxHash "
                        + "for:\n serverID: " + serverID + "\n nymID: " + nymID);
            }
            //-------------------------------------------------

            if (bForceDownload
                    || !bLocalHash
                    || !bServerHash
                    || (bServerHash && bLocalHash
                    && !strServerHash.equals(strLocalHash))) // the hashes don't match -- so let's definitely re-try to download the latest nymbox.
            {
                // the getRequest worked, and the server hashes don't match,
                // so let's try the call again...
                //
                nGetNymbox = Helpers.getNymboxLowLevel(serverID, nymID, bWasMsgSent);

                if ((false == bWasMsgSent.getBooleanValue())
                        || ((nGetNymbox <= 0) && ((-1) != nGetNymbox))) {
                    System.out.println("Utility.getNymbox(2): Failure: Utility.getNymboxLowLevel returned unexpected value: " + nGetNymbox);
                    return (-1);
                } else if ((-1) == nGetNymbox) // we'll try re-syncing the request number, then try again.
                {
                    System.out.println("Utility.getNymbox(): Failure: Utility.getNymboxLowLevel returned -1, even after syncing the request number successfully. (Giving up.)");
                    return (-1);
                }
            }
        }

        // By this point, we DEFINITELY know that the Nymbox was retrieved successfully.
        // (With request number nGetNymbox.) This is because the getNymboxLowLevel() call
        // also tries to receive the reply, so we already know by now whether the reply
        // was successfully received.
        //
        return nGetNymbox;
    }

//  public static int getNymboxLowLevel(String serverID, String nymID)
//  public static int receiveNymboxLowLevel(String serverID, String nymID, final int nRequestNum)
//  public static int processNymboxLowLevel(String serverID, String nymID) {
    public static int getAndProcessNymbox(String serverID, String nymID, OTBool bWasMsgSent) {
        return Helpers.getAndProcessNymbox(serverID, nymID, bWasMsgSent, false); // bForceDownload=false
    }

    public static int getAndProcessNymbox(String serverID, String nymID, OTBool bWasMsgSent, boolean bForceDownload) {
        final int nRequestNumber = 0;

        OTBool bFoundNymboxItem = new OTBool(false); // bFoundNymboxItem is output bool, telling caller whether it was found.

        return Helpers.getAndProcessNymbox(serverID, nymID, bWasMsgSent, bForceDownload, nRequestNumber, bFoundNymboxItem,
                false, false, false, false, false);
    }

    // NOTE, TODO: if the Nymbox simply WON'T process, OR on a regular basis,
    // need to iterate through the sent messages and make sure to harvest whichever
    // ones are not already in the Nymbox (no need to harvest those--they're already
    // in the system.)
    //
    // Returns:
    //   -1 ERROR.
    //    0 Nymbox was empty -- nothing done. (bWasMsgSent = false)
    //    0 Transaction status == server reply received (bWasMsgSent = true), but
    //      the server reply has status == FAILED. (All harvesting was subsequently successful for processNymbox).
    //    1 If the ProcessNymbox Transaction status (from the server reply) is SUCCESS,
    //      then this function returns 1.
    //   >1 If the ProcessNymbox Transaction status (from the server reply) is >1, then this function returns the
    //      REQUEST NUMBER from when it was originally sent. (Harvesting was NOT performed, which is why the request
    //      number is being returned, so the caller can choose what to do next.)
    public static int getAndProcessNymbox(String serverID, String nymID, OTBool bWasMsgSent, boolean bForceDownload,
            final int nRequestNumber, // nRequestNumber refers to a PREVIOUS msg (like a cash withdrawal) that had an error and then called this while trying to resync. (The caller will want to know whether it was found in the Nymbox.)
            OTBool bFoundNymboxItem, // bFoundNymboxItem is output bool, telling caller whether it was found.
            boolean bHarvestingForRetry, // bHarvestingForRetry is INPUT, in the case nRequestNumber needs to be harvested before a flush occurs.
            boolean bMsgReplySuccess, // bMsgReplySuccess is INPUT, and is in case nRequestNumber needs to be HARVESTED before a FLUSH happens.
            boolean bMsgReplyFailure, // bMsgReplyFailure is INPUT, and is in case nRequestNumber needs to be HARVESTED before a FLUSH happens.
            boolean bMsgTransSuccess, // bMsgTransSuccess is INPUT, and is in case nRequestNumber needs to be HARVESTED before a FLUSH happens.
            boolean bMsgTransFailure) // bMsgTransFailure is INPUT, and is in case nRequestNumber needs to be HARVESTED before a FLUSH happens.
    {
        if (1 == nRequestNumber) {
            System.out.println("getAndProcessNymbox: WARNING: Request Num of '1' was just passed in here.");
        }

        // This should NEVER happen (need an assert here.)
        //
        if ((null == bWasMsgSent) || (null == bFoundNymboxItem)) {
            System.out.println("getAndProcessNymbox: SHOULD NEVER HAPPEN!!! ASSERT!! ERROR!! FAILURE!!! PROBLEM!!!!!");
            return -1;
        }

        bWasMsgSent.setBooleanValue(false);

        // what is nRequestNumber?
        //
        // Let's say a message, say for a cash withdrawal with request number 5, has FAILED.
        // Since the message failed, perhaps the request number was out of sync, or Nymbox hash
        // was old? So, let's say that it then sent a getRequest message, in order to resync,
        // and discovered that the Nymbox hash has changed. Therefore the Nymbox is now being
        // re-downloaded and processed, so that the cash withdrawal can be attempted again.
        //
        // HOWEVER, before we PROCESS the Nymbox, we need to see if the withdrawal reply is already
        // sitting in it. Why, you ask, if the withdrawal failed, would I expect a reply to be in
        // the Nymbox? In case 1, the message was dropped, so I don't know if the reply is there
        // until I check the Nymbox. In case 2, the message may have failed OR SUCCEEDED, with the
        // successful message containing a FAILED TRANSACTION.
        // Thus, we just want to check the Nymbox for nRequestNumber, and make sure whether it's
        // there or not, before we PROCESS the nymbox, because once we do THAT, it will be empty.
        //
        // We will return a bool parameter to the caller, so that he can know whether the reply
        // was already in the Nymbox. We can also harvest the transaction numbers from the reply
        // message, if it's a transaction, so that everything is set for the re-try. (Possibly pass
        // a bool parameter dictating whether the harvesting is being done for a re-try or not.)
        //


        // -- SECTION 1: "GET NYMBOX"
        //
        // This call is sufficiently high-level enough that it already has re-tries
        // built into it. That's why you don't see me re-trying the getNymbox if it
        // fails.
        //
        int nGetNymbox = Helpers.getNymbox(serverID, nymID, bForceDownload);

        if (nGetNymbox < 1) {
            System.out.println("Utility.getAndProcessNymbox(): Failure: Utility.getNymbox returned: " + nGetNymbox);
            return (-1);
        }

        // By this point, we DEFINITELY know that the Nymbox was retrieved successfully.
        // (With request number nGetNymbox.) This is because the getNymboxLowLevel() call
        // also tries to receive the reply, so we already know by now whether the reply
        // was successfully received.
        //
        /*
         *  FYI: nRequestNumber is the request number, if >0, for whatever command
         * is causing this getAndProcessNymbox to occur (like a cash withdrawal, or
         * a cheque deposit, etc.) We pass it in here so we can verify whether it's on
         * the Nymbox, before we process it out (so the caller knows whether to clawback.)
         *
         * FYI: nGetNymbox is the request number from getNymboxLowLevel() (above.) We
         * know for a FACT, by this point, this number is >0.
         *
         * FYI: nProcess (just below) is the request number for the PROCESS NYMBOX message.
         * Below the switch block just down there, we know for a fact this number is >0.
         */
        // ******************************************************************************

        // -- SECTION 2: "SEND PROCESS NYMBOX"
        //

        // Next, we have to make sure we have all the BOX RECEIPTS downloaded
        // for this Nymbox.

        //-------------------------------------------------------------------------------

        if (Helpers.insureHaveAllBoxReceipts(serverID, nymID, nymID, 0, // nBoxType = 0 aka nymbox
                nRequestNumber, bFoundNymboxItem)) // If the caller wanted to know whether a certain reply (by request number) was in the Nymbox, then bFoundNymboxItem
        {                                           // will be set true in this call, if it was there. That way he can Harvest his own msg if he needs to. (Just like I
            // harvest my own processNymbox call below, if necessary.)

            // If the caller was on about a specific request number...
            //
            if (nRequestNumber > 0) {
                // And if we DID NOT find that number in the Nymbox,
                // then harvest it!!
                // (If we HAD found it, then we'd know it didn't NEED harvesting,
                // since the server clearly REPLIED to it already.)
                //
                if (bFoundNymboxItem.getBooleanValue()) // FOUND it in the nymbox! Therefore we can remove without harvesting. (Server definitely processed it, so nothing to harvest.)
                {
                    // Notice, if the above call to insureHaveAllBoxReceipts had any network hiccups, then
                    // it may have had to get and processNymbox, meaning the below Remove would fail, since
                    // the sent message was already removed. Therefore, might want to update this to call GetSent
                    // FIRST, before trying to remove.
                    // (Might want different messages in either case.)
                    //
                    final boolean bRemovedMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNumber), serverID, nymID);
                    System.out.println("Utility.getAndProcessNymbox(): OT_API_RemoveSentMessage: " + bRemovedMsg);
                }
                else // Didn't find it in the nymbox, so we can harvest it:
                {

                    // NOTE: This may always fail,

                    System.out.println("Utility.getAndProcessNymbox(): FYI: Calling OT_API_GetSentMessage...");

                    final String strSentMsg = otapiJNI.OTAPI_Basic_GetSentMessage(Integer.toString(nRequestNumber), serverID, nymID);

                    if (!Helpers.isValid(strSentMsg)) {
                        System.out.println("Utility.getAndProcessNymbox(): ERROR: (SHOULD NEVER HAPPEN 1) Expected OT_API_GetSentMessage to return the sent message (for clawback) but couldn't find it. (Expected it--I JUST supposedly sent it!) Request number: " + nRequestNumber);
                    } else // OT_API_GetSentMessage success.
                    {
                        System.out.println("Utility.getAndProcessNymbox(): FYI: Harvesting transaction numbers from failed Msg attempt...");
                        // ------------------------------------
                        final boolean bHarvested = otapiJNI.OTAPI_Basic_Msg_HarvestTransactionNumbers(strSentMsg, nymID,
                                bHarvestingForRetry, // bHarvestingForRetry.
                                bMsgReplySuccess, // bReplyWasSuccess,       // RECEIVED server reply: explicit success.
                                bMsgReplyFailure, // bReplyWasFailure,       // RECEIVED server reply: explicit failure.
                                bMsgTransSuccess, // bTransactionWasSuccess, // MESSAGE success, Transaction success. (Explicit.)
                                bMsgTransFailure);   // bTransactionWasFailure  // MESSAGE success, Transaction failure. (Explicit.)
                        System.out.println("Utility.getAndProcessNymbox(): OT_API_Msg_HarvestTransactionNumbers: " + bHarvested);

                        final boolean bRemovedMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNumber), serverID, nymID);
                        System.out.println("Utility.getAndProcessNymbox(): OT_API_RemoveSentMessage: " + bRemovedMsg);

                    } // strSentMsg NOT null!
                }
            }
            // ------------------------------------------------------------------

            // (flush): LOOP THROUGH ALL "SENT" messages, and see if ANY of them has a reply
            // sitting in my Nymbox. If so, REMOVE IT from "Sent" queue, (since clearly the server
            // DID respond already.) And if it's NOT in my nymbox, that means I DEFINITELY need to
            // harvest it back since the server definitely rejected it or never received it.
            //
            // The Nym actually SAVES the sent messages PER SERVER, so that this
            // will continue to work in every possible case!!
            // NOTE: Also now storing, on the client nym, a copy of the server's latest nymbox hash
            // for that nym, in addition to the nym's copy (which only updates when he gets his Nymbox.)
            // That way the Nym, even before syncing the nymboxes, and even before sending a new message
            // to find out if they are out of sync, ALREADY KNOWS if they're in sync or not. (That's why
            // all those other server messages send a copy of that hash back, not just the getNymbox msg.)
            //
            //
//            void OT_API_FlushSentMessages(const int bHarvestingForRetry, // bHarvestingForRetry is actually OT_BOOL
//                              const char * SERVER_ID,
//                              const char * USER_ID,
//                              const char * THE_NYMBOX);
            // NoVerify means don't load up all the box receipts.
            // Especially in this case--we only care about whether a box receipt is THERE, not
            // what it contains. FlushSentMessages will work just fine WITHOUT loading those
            // box receipts (because the Nymbox contains enough of an abbreviated record already
            // for each one, that we will have the info we need already.)
            //
            String strNymbox = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);      // FLUSH SENT MESSAGES!!!!  (AND HARVEST.)

            // *******************************************************
            if (Helpers.isValid(strNymbox)) // ---------------------------
            {
                otapiJNI.OTAPI_Basic_FlushSentMessages(false, //harvesting for retry == false. None of the things are being re-tried by the time they are being flushed.  They were already old news.
                        serverID,
                        nymID,
                        strNymbox);
            } // Flushing removes all the messages from the "sent messages" queue,
            // and harvests any transaction numbers to be had. How do I know for sure
            // that I can get away with this? How do I know whether the server has
            // processed those messages or not? How can I harvest them as though they
            // were dropped on the network somewhere? The answer is because I JUST
            // called GetNymbox and downloaded the latest one. I can SEE which replies
            // are in there -- and which ones aren't. I pass that Nymbox into the flush
            // call, so that flush can be careful to remove all sent messages that have
            // nymbox replies, and only harvest the others.
            // *******************************************************
            else {
                System.out.println("Utility.getAndProcessNymbox(): Error while trying to flush sent messages: Failed loading Nymbox for nym: " + nymID);
            }
            // -------------------------------------------------
            OTInteger nMsgSentRequestNumOut = new OTInteger(-1);
            OTInteger nReplySuccessOut = new OTInteger(-1);
            OTInteger nBalanceSuccessOut = new OTInteger(-1);
            OTInteger nTransSuccessOut = new OTInteger(-1);
            // -------------------------------------------------

            // PROCESS NYMBOX
            //
            // Returns:
            // -1 Error.
            //  0 Nymbox was empty -- nothing done. (bWasMsgSent = false)
            //  0 Transaction status == server reply received (bWasMsgSent = true),
            //    but the server reply says status==failed.
            // >0 If the Transaction status (from the server reply) is SUCCESS, then this function
            //    returns the REQUEST NUMBER from when it was originally sent.

            int nProcess = Helpers.processNymbox(serverID, nymID,
                    bWasMsgSent,
                    // ---------------
                    nMsgSentRequestNumOut,
                    nReplySuccessOut,
                    nBalanceSuccessOut,
                    nTransSuccessOut);
            switch (nProcess) {
                case (-1):
                    // Todo: might want to remove the sent message here, IF bMsgWasSent is true.
                    // (Just like case 0.)
                    //
                    System.out.println("Utility.getAndProcessNymbox(): Failure: Utility.processNymbox: error (-1). (It couldn't send. I give up.)");
                    return (-1); // (It didn't even send.)
                case (0):   // Nymbox was empty. (So we didn't send any process message because there was nothing to process.)
                    if (false == bWasMsgSent.getBooleanValue()) {
                        return 0; // success. done. (box was empty already.)
                    }                    // else: the message WAS sent, (the Nymbox was NOT empty)
                //       and then the server replied "success==FALSE"
                //       in its REPLY to that message! Thus we continue...
                default:
                    if (nProcess < 0) {
                        System.out.println("Utility.getAndProcessNymbox(): Failure:  unexpected: " + nProcess + ", when calling Utility.processNymbox. (I give up.)");
                        return (-1);
                    }
                    // (else break)
                    break; // Success!

            } // switch

            // bWasMsgSent.setBooleanValue(true);  // unnecessary -- set already by processNymbox call above.
            // ------------------------------------------ 
            // By this point, we definitely have a >0 request number from the sendProcessNymbox()
            // call, stored in nProcess (meaning the message WAS sent.) (Except in case of 0, see next line which fixes this:)
            //

            nProcess = nMsgSentRequestNumOut.getIntegerValue(); // Sometimes this could be 0 still, so we fix it here.
            final int nReplySuccess = nReplySuccessOut.getIntegerValue();
            final int nTransSuccess = nTransSuccessOut.getIntegerValue();
            final int nBalanceSuccess = nBalanceSuccessOut.getIntegerValue();

            // ------------------------------------------ 
            /*
            const
            char *    OT_API_GetSentMessage(const char * REQUEST_NUMBER)
            OT_BOOL   OT_API_RemoveSentMessage(const char * REQUEST_NUMBER)
           
             */
            // All of these booleans (except "error") represent RECEIVED ANSWERS from the server.
            // In other words, "false" does not mean "failed to find message."
            // Rather, it means "DEFINITELY got a reply, and that reply says success==false."

            // ---------------------------------
            // SHOULD NEVER HAPPEN (processNymbox call just above was successful,
            // therefore the sent message SHOULD be here in my cache.)
            //
            final String strReplyProcess = Helpers.getLastReplyReceived();
            // I had to do this bit because getRequestNumber doesn't return the actual
            // reply itself. But in this case, I needed it.
            if (!Helpers.isValid(strReplyProcess)) // THIS SHOULD NEVER HAPPEN.
            {
                System.out.println("Utility.getAndProcessNymbox(): "
                        + "ERROR in Utility.getLastReplyReceived(): why was this string not set, when Utility.getRequestNumber was otherwise an apparent success?");
                return (-1); // (SHOULD NEVER HAPPEN. This string is set in the getRequestNumber function.)
            }
            //-------------------------------------------------

            final boolean bProcessNymboxReplyError = (!Helpers.isValid(strReplyProcess) || (nReplySuccess < 0));
            final boolean bProcessNymboxBalanceError = (!Helpers.isValid(strReplyProcess) || (nBalanceSuccess < 0));
            final boolean bProcessNymboxTransError = (!Helpers.isValid(strReplyProcess) || (nTransSuccess < 0));
            // -----------------------------------------------------------------------------------------------------
            final boolean bProcessNymboxReplySuccess = (!bProcessNymboxReplyError && (nReplySuccess > 0));
            final boolean bProcessNymboxReplyFailure = (!bProcessNymboxReplyError && (nReplySuccess == 0));
            // -----------------------------------------------------------------------------------------------------
            final boolean bProcessNymboxBalanceSuccess = (!bProcessNymboxReplyError && !bProcessNymboxBalanceError && (nBalanceSuccess > 0));
            final boolean bProcessNymboxBalanceFailure = (!bProcessNymboxReplyError && !bProcessNymboxBalanceError && (nBalanceSuccess == 0));
            // -----------------------------------------------------------------------------------------------------
            final boolean bProcessNymboxTransSuccess = (!bProcessNymboxReplyError && !bProcessNymboxBalanceError && !bProcessNymboxTransError && (nTransSuccess > 0));
            final boolean bProcessNymboxTransFailure = (!bProcessNymboxReplyError && !bProcessNymboxBalanceError && !bProcessNymboxTransError && (nTransSuccess == 0));
            // -----------------------------------------------------------------------------------------------------
            final boolean bProcessAnyError = (bProcessNymboxReplyError || bProcessNymboxBalanceError || bProcessNymboxTransError);
            final boolean bProcessAnyFailure = (bProcessNymboxReplyFailure || bProcessNymboxBalanceFailure || bProcessNymboxTransFailure);
            final boolean bProcessAllSuccess = (bProcessNymboxReplySuccess && bProcessNymboxBalanceSuccess && bProcessNymboxTransSuccess);
            // -----------------------------------------------------------------------------------------------------


            // Note: we LEAVE the sent message in the "sent queue" until we are certain that it processed.
            // If we are NOT certain that it processed, then we try to download the Nymbox and see if there's
            // a reply there (for the sent message.) If we are able to confirm THAT, AFTER SUCCESSFULLY downloading
            // the Nymbox, then then we don't have to do anything because we know for sure it was processed.
            // Similarly, if we DEFINITELY download the Nymbox and do NOT find the reply, then we know the server
            // DEFINITELY did not receive (or at least process) that message, which is what allows us to HARVEST
            // the transaction numbers back from the sent message, and remove the sent message from the sent queue.
            //
            // However, if we are NOT able to verify any of these things, NOR are we able to download the Nymbox to
            // see, then we DO leave the message in the sent queue. This is deliberate, since it gives us the opportunity
            // in the future to clear those sent messages NEXT time we successfully DO download the Nymbox, and in the
            // meantime, it allows us to store a record of EXACTLY which messages were MISSED.
            //

            boolean bHarvested = false;

            if (bProcessAllSuccess) {
                // the processNymbox was a complete success, including the message
                // AND the transaction AND the transaction statement.
                // Therefore, there's DEFINITELY nothing to clawback.
                //
                // (Thus I RemoveSentMessage for the processNymbox message, since
                // I'm totally done with it now.)
                //
//                final int nRemoved = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nProcess), serverID, nymID);
                // NOTE: The above call is unnecessary, since a successful process means
                // we already received the successful server reply, and OT's "ProcessServerReply"
                // already removed the sent message from the sent buffer (so no need to do that here.)
                //
            } else if (bProcessAnyError || bProcessAnyFailure) // let's resync, and clawback whatever transaction numbers we might have used on the processNymbox request...
            {
                nGetNymbox = Helpers.getNymbox(serverID, nymID, true); // bForceDownload=true - NOTE: could maybe change this to false and have it still work.

                if (nGetNymbox < 1) {
                    System.out.println("Utility.getAndProcessNymbox(): Failure: Utility.getNymbox returned: " + nGetNymbox);
                    return (-1);
                }

                OTBool bWasFound = new OTBool(false);

                if (Helpers.insureHaveAllBoxReceipts(serverID, nymID, nymID, 0, // nBoxType = 0 aka nymbox
                        nProcess, bWasFound)) // This will tell us whether the processNymbox reply was found in the Nymbox
                {
                    // we FOUND the processNymbox reply in the Nymbox!
                    //
                    if (bWasFound.getBooleanValue()) {
                        // Thus, no need to clawback any transaction numbers,
                        // since the server clearly already processed this processNymbox
                        // transaction, since I have a reply to it already sitting in my Nymbox.
                        //
//                        final int nRemoved = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nProcess), serverID, nymID);
                        //
                        // NOTE: The above call is unnecessary, since a successful process means
                        // we already received the successful server reply, and OT's "ProcessServerReply"
                        // already removed the sent message from the sent buffer (so no need to do that here.)

                        System.out.println("Utility.getAndProcessNymbox(): FYI: I *did* find the @processNymbox reply in my Nymbox, so NO NEED to clawback any transaction numbers.");
                    }
                    else // was NOT found... we need to clawback.
                    {
                        // This means the server's reply was definitely NOT found in the
                        // Nymbox, even after successfully DOWNLOADING that Nymbox. Which
                        // means the server never got it in the first place, or rejected it
                        // at the message level before the transaction portion had a chance
                        // to run. Either way, we need to claw back any relevant transaction
                        // numbers...

                        // HARVEST the processNymbox message from outgoing messages.

                        System.out.println("Utility.getAndProcessNymbox 2: FYI: Calling OT_API_GetSentMessage...");

                        final String strSentProcessNymboxMsg = otapiJNI.OTAPI_Basic_GetSentMessage(Integer.toString(nProcess), serverID, nymID);

                        if (!Helpers.isValid(strSentProcessNymboxMsg)) {
                            System.out.println("Utility.getAndProcessNymbox(): ERROR: (SHOULD NEVER HAPPEN 2) Expected OT_API_GetSentMessage to return the sent processNymbox message (for clawback) but couldn't find it. (Expected it--I JUST sent it!)");
                        } else // strSentProcessNymboxMsg NOT null!
                        {
                            System.out.println("Utility.getAndProcessNymbox(): FYI: Harvesting transaction numbers from failed processNymbox attempt...");
                            // ------------------------------------
                            bHarvested = otapiJNI.OTAPI_Basic_Msg_HarvestTransactionNumbers(strSentProcessNymboxMsg, nymID,
                                    false, // bHarvestingForRetry == false
                                    bProcessNymboxReplySuccess, // bReplyWasSuccess,       // RECEIVED server reply: explicit success.
                                    bProcessNymboxReplyFailure, // bReplyWasFailure,       // RECEIVED server reply: explicit failure.
                                    bProcessNymboxTransSuccess, // bTransactionWasSuccess, // MESSAGE success, Transaction success. (Explicit.)
                                    bProcessNymboxTransFailure)// bTransactionWasFailure  // MESSAGE success, Transaction failure. (Explicit.)

                            System.out.println("Utility.getAndProcessNymbox(): OT_API_Msg_HarvestTransactionNumbers: " + bHarvested);

                            final boolean bRemovedProcessNymboxMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nProcess), serverID, nymID);

                            System.out.println("Utility.getAndProcessNymbox(): OT_API_RemoveSentMessage: " + bRemovedProcessNymboxMsg);

                        } // strSentProcessNymboxMsg NOT null!
                    } // a specific receipt was not found in the nymbox (need to clawback the transaction numbers on that receipt.)
                    // ----------------------------------------------------------------

                    strNymbox = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);      // FLUSH SENT MESSAGES!!!!  (AND HARVEST.)

                    // *******************************************************
                    if (Helpers.isValid(strNymbox)) {
                        otapiJNI.OTAPI_Basic_FlushSentMessages(false, //harvesting for retry == false
                                serverID,
                                nymID,
                                strNymbox);
                    } // Flushing removes all the messages from the "sent messages" queue,
                    // and harvests any transaction numbers to be had. How do I know for sure
                    // that I can get away with this? How do I know whether the server has
                    // processed those messages or not? How can I harvest them as though they
                    // were dropped on the network somewhere? The answer is because I JUST
                    // called GetNymbox and downloaded the latest one. I can SEE which replies
                    // are in there -- and which ones aren't. I pass that Nymbox into the flush
                    // call, so that flush can be careful to remove all sent messages that have
                    // nymbox replies, and only harvest the others.
                    // *******************************************************
                    else {
                        System.out.println("Utility.getAndProcessNymbox(): Error while trying to flush sent messages: Failed loading Nymbox for nym: " + nymID);
                    }
                    // -------------------------------------------------
                } // if insureHaveAllBoxReceipts()
                else // we do NOT have all the box receipts.
                {
                    System.out.println("Utility.getAndProcessNymbox(): Error: Utility.insureHaveAllBoxReceipts failed. (I give up.)");
                    return (-1);
                }
            } // else if (bProcessAnyError || bProcessAnyFailure)
            // --------------------------------------------------------
            // Return the request number, if potentially needed by caller.
            // If explicit success, the request number is returned as a positive
            // number (though already removed from sent queue.) Whereas if explicit
            // failure (reply status=failed) then we harvest the numbers
            //
            if (bProcessAllSuccess) //              return Utility.getNymbox(serverID, nymID, true); // bForceDownload=true. Since we DID process it successfully, then we grab it again.
            {
                return 1// We don't need the sent message after this, and we've already removed it from sent queue.
            }
            if (bProcessAnyFailure || bProcessAnyError) {
                if (false == bHarvested) // If the message failed, and the harvesting failed, then we return the request
                {
                    return nProcess; // number for the process nymbox, so the caller has a choice of what to do next.
                }
                if (bProcessAnyFailure) {
                    return 0;        // by this point, we've definitely harvested, AND removed sent message from sent queue. So we just return 0 so the caller can see the server said FAILED.
                }
            }

            return (-1); // must've been an error.
        } // if Utility.insureAllBoxReceipts()
        else {
            System.out.println("Utility.getAndProcessNymbox(): Utility.insureHaveAllBoxReceipts failed. (I give up.)");
        }

        return (-1);
    }

    // ---------------------------------------------------------
    // PROCESS NYMBOX
    //
    // Returns:
    // -1 Error.
    //  0 Nymbox was empty -- nothing done. (bWasMsgSent = false)
    //  0 server reply received, but it says success==false on that msg. (bWasMsgSent = true)
    // >0 If the Transaction status (from the server reply) is SUCCESS, then this function
    //    returns the REQUEST NUMBER from when it was originally sent.
    //
    public static int processNymbox(String serverID, String nymID,
            OTBool bWasMsgSent,
            // --------------------------------
            OTInteger nMsgSentRequestNumOut,
            OTInteger nReplySuccessOut,
            OTInteger nBalanceSuccessOut,
            OTInteger nTransSuccessOut) {
        bWasMsgSent.setBooleanValue(false);

        // ----------------------------------

        if ((null == bWasMsgSent) || (null == nReplySuccessOut) || (null == nBalanceSuccessOut) || (null == nTransSuccessOut)) {
            System.out.println("SHOULD NEVER HAPPEN: processNymbox has null values passed in...");
            return -1;
        }

        nMsgSentRequestNumOut.setIntegerValue(-1);
        nReplySuccessOut.setIntegerValue(-1);
        nBalanceSuccessOut.setIntegerValue(-1);
        nTransSuccessOut.setIntegerValue(-1);

        // -- SECTION 2: "SEND PROCESS NYMBOX"
        //

        // Next, we have to make sure we have all the BOX RECEIPTS downloaded
        // for this Nymbox.       
        //-------------------------------------------------------------------------------

        //
        final int nProcess = Helpers.sendProcessNymboxLowLevel(serverID, nymID); // <===================== SEND PROCESS NYMBOX!!

        switch (nProcess) {
            case (-1):
                System.out.println("Utility.processNymbox(): Failure: error (-1), when calling sendProcessNymboxLowLevel. (It couldn't send. I give up.)");
                return (-1); // (It didn't even send.)
            case (0):   // Nymbox was empty. (So we didn't send any process message because there was nothing to process.)
                return 0; // success. done.
            default:
                if (nProcess < 0) {
                    System.out.println("Utility.processNymbox(): Failure: unexpected: " + nProcess + ", when calling sendProcessNymboxLowLevel. (I give up.)");
                    return (-1);
                }
                // (else break)
                break; // Success!

        } // switch

        bWasMsgSent.setBooleanValue(true);
        nMsgSentRequestNumOut.setIntegerValue(nProcess);
        // ------------------------------------------ 
        // By this point, we definitely have a >0 request number from the sendProcessNymbox()
        // call, stored in  ** nProcess ** (meaning the message WAS sent.)
        //
        // But was it received?
        //
        final String strReplyProcess =
                Helpers.ReceiveReplyLowLevel(serverID, nymID, nProcess,
                "Utility.processNymbox / sendProcessNymboxLowLevel / ReceiveReplyLowLevel"); // <=============== Here we RECEIVE the REPLY...

        // -----------------------------------------------       
        // Utility.getLastReplyReceived() will also contain the same as strReplyProcess.
        // So if the CALLER of this function (that we're in, receiveNymboxLowLevel)
        // wants to see the contents, he can.
        // ------------------------------------------
        // ReceiveReplyLowLevel returns null unless there was a string returned.
        // So we can directly check it for success...

        final int nReplySuccess = Helpers.getMessageSuccess(strReplyProcess); // sendProcessNymboxLowLevel
        final int nTransSuccess, nBalanceSuccess;

        if (nReplySuccess > 0) // If message was success, then let's see if the transaction was, too.
        {
            nBalanceSuccess = otapiJNI.OTAPI_Basic_Message_GetBalanceAgreementSuccess(serverID, nymID, nymID, strReplyProcess); // the processNymbox transaction.

            if (nBalanceSuccess > 0) {
                nTransSuccess = otapiJNI.OTAPI_Basic_Message_GetTransactionSuccess(serverID, nymID, nymID, strReplyProcess); // the processNymbox transaction.
            } else {
                nTransSuccess = (-1);
            }
        } else {
            nBalanceSuccess = -1;
            nTransSuccess = -1;
        }

        nReplySuccessOut.setIntegerValue(nReplySuccess);
        nBalanceSuccessOut.setIntegerValue(nBalanceSuccess);
        nTransSuccessOut.setIntegerValue(nTransSuccess);

        // ------------------------------------------ 
        // NOTE: The caller MUST have a call to OT_API_RemoveSentMessage
        // to correspond to THIS function's call of sendProcessNymboxLowLevel().
        //
        if (nTransSuccess > 0) {
            return nProcess;  // <=========================
        } else {
            return nTransSuccess;
        }
    }

    // No need to deal with getRequest here when failure, since the calling
    // function already goes through that crap before we get here.
    // Returns: the request number for the process Nymbox request.
    // OR returns 0 if the Nymbox was empty (and no message was sent.)
    // OR returns -1 if there was an error.
    //
    // DONE
    public static int sendProcessNymboxLowLevel(String serverID, String nymID) // bWasSent is an output param allowing to return whether the request was even sent.
    {
        // ------------------------------------------
        // Send message..
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();

        int nRequestNum = otapiJNI.OTAPI_Basic_processNymbox(serverID, nymID);

        // ------------------------------------------
        if ((-1) == nRequestNum) {
            otapiJNI.OTAPI_Basic_Output(0, " Utility.sendProcessNymboxLowLevel: Failure sending. OT_API_processNymbox() returned -1. \n");
            return (-1); // no need to check for any reply.
        } // ------------------------------------------
        else if (nRequestNum < 0) {
            otapiJNI.OTAPI_Basic_Output(0, " Utility.sendProcessNymboxLowLevel: Failure: OT_API_processNymbox() returned unexpected value: " + nRequestNum);
            otapiJNI.OTAPI_Basic_Output(0, "\n");
            return (-1); // no need to check for any reply.
        } // ------------------------------------------
        else if (0 == nRequestNum) {
            otapiJNI.OTAPI_Basic_Output(0, " Utility.sendProcessNymboxLowLevel: Nymbox was empty; no need to process it. \n");
            return 0// Nymbox is empty, thus no need to process it.
        }

        // Note: I do NOT call RemoveSentMessage for processNymbox, at least, not here.
        // Instead, the place that CALLS this function, will actually use that because
        // it has to be able to harvest the transaction numbers in certain failure cases.

        return nRequestNum;
    }

    // ------------------------------------------
    // after calling Utility.sendProcessNymboxLowLevel, then
    // call this:
    // int receiveReplySuccessLowLevel(String IN_FUNC)
    /*
     * What might happen "in reply" when you send a message to the OT server,
     * successfully receive the request number from the send call, and then you
     * pop the message buffer to check for the actual server reply?
     *
     *    1. Returns null.
     *    2. Returns an empty string. (Just being redundant, really.)
     *    3. Returns the proper message, failed.
     *    4. Returns the proper message, succeeded.
     * TRANSACTIONS ONLY:
     *    5. Returns the proper message, succeeded: BUT Balance Agreement failed!
     *    6. Returns the proper message, succeeded: AND Balance Agreement success, but TRANSACTION failed.
     *    7. Returns the proper message, succeeded: AND Balance Agreement success, AND TRANSACTION succeeded.
     */
    public static boolean isValid(String strInput) {
        if (strInput == null || strInput.length() < 1) {
            return false;
        }
        return true;
    }

    // Returns:
    //    -1 if error, such as failure to load the message
    // and 0 if the message is failure
    // and 1 if the message is a sucess
    //
    public static int getMessageSuccess(String strInput) {
        if (!Helpers.isValid(strInput)) {
            System.out.println("Utility.getMessageSuccess(): null or empty input string. (Returning error condition of -1.)");
            return (-1);
        }
        // --------------------------------------------------- 

        int nSuccess = otapiJNI.OTAPI_Basic_Message_GetSuccess(strInput); // <==================

        // ---------------------------------------------------

        switch (nSuccess) {
            case (-1):
                System.out.println("Utility.getMessageSuccess(): Error calling OT_API_Message_GetSuccess, for message:\n\n" + strInput);
                break;
            case (0):
                System.out.println("Utility.getMessageSuccess(): Reply received: success == FALSE. Reply message:\n\n" + strInput);
                break;
            case (1):
                System.out.println("Utility.getMessageSuccess(): Reply received: success == TRUE.");
                break;
            default:
                System.out.println("Utility.getMessageSuccess(): Error. (This should never happen!) Input: " + strInput);
                nSuccess = (-1);
                break;
        }

        return nSuccess;
    }
    // ------------------------------------------------------

    // returns:
    // -1 for error,
    //  0 for server reply of failure,
    //  1 for server reply of success
    //
    public static int receiveReplySuccessLowLevel(String serverID, String nymID, int nRequestNumber, String IN_FUNC) {
        final String strReply = Helpers.ReceiveReplyLowLevel(serverID, nymID, nRequestNumber,
                "Utility.receiveReplySuccessLowLevel: " + IN_FUNC); // <=============== Here we RECEIVE the REPLY...

        // -----------------------------------------------       
        // Utility.getLastReplyReceived() will also contain the same as strReply.
        // So if the CALLER of this function (that we're in, receiveNymboxLowLevel)
        // wants to see the contents, he can.
        // ------------------------------------------
        // ReceiveReplyLowLevel returns null unless there was a string returned.
        // So we can directly check it for success...

        return Helpers.getMessageSuccess(strReply);
    }
    // ------------------------------------------------------

    // Tries to receive a server reply
    // (for a message that you presumably just sent.)
    // If successful, returns the server reply. Otherwise returns null.
    // (Successful meaning, a valid-formed message was received. Whether that is a
    // "success=true" or "success=false" message, the caller will have to figure
    // that out for himself.)
    //
    public static String ReceiveReplyLowLevel(String serverID, String nymID, int nRequestNumber, String IN_FUNCTION) {
        Helpers.delay();
        Helpers.setLastReplyReceived(null);
        // --------------------------------------------------------------------
        final String strResponseMessage = otapiJNI.OTAPI_Basic_PopMessageBuffer(Integer.toString(nRequestNumber),
                serverID, nymID);

        if (!Helpers.isValid(strResponseMessage)) {
            System.out.println("Utility.ReceiveReplyLowLevel (" + IN_FUNCTION + "): null server reply!");
            return null;
        }
        Helpers.setLastReplyReceived(strResponseMessage);
        // --------------------------------------------------------------------
        return strResponseMessage;
    }

    // ---------------------------------------------------
    public static int getRequestNumber(String serverID, String nymID) {
        OTBool bWasSent = new OTBool(false);

        return Helpers.getRequestNumber(serverID, nymID, bWasSent);
    }

    // -1 == error (couldn't send, or couldn't receive)
    //  0 == success false (received reply from server)
    //  1 == success true  (received reply from server)
    //
    // To distinguish between error where message wasn't sent,
    // and error where message WAS sent, but reply never received,
    // bWasSent will be set to TRUE once this function is sure that
    // it was sent out. (which you only care about if (-1) was the
    // return value, since otherwise you already KNOW you had a
    // server reply, AND its status.
    // DONE
    public static int getRequestNumber(String serverID, String nymID, OTBool bWasSent) // bWasSent is an output param allowing to return whether the request was even sent.
    {
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();

        final int nResult = otapiJNI.OTAPI_Basic_getRequest(serverID, nymID);

        if (nResult == (-1)) // if error (-1), that means it DIDN'T SEND (error)
        {
            System.out.println("Utility.getRequestNumber: Failed to send getRequest message due to error.");
            return (-1);
        } else if (nResult == 0) // if 0 is returned, that also means it DIDN'T SEND (but there was NO error...)
        {                       // I don't know if this case can actually even HAPPEN... but if it does, I'll log it.
            System.out.println("Utility.getRequestNumber: Didn't send this getRequest message, but NO error occurred, either. (Should never happen.)");
            return (-1); // Since the 0 case should never happen, I'm returning it as an ERROR (-1).
            // Note: I could never return 0 here, because that would imply that we RECEIEVED a SERVER REPLY,
            // and that the server's reply said "success == 0". But that's not what happened here. Here,
            // we couldn't even SEND our request, which is an error
        }
        //
        // else it's >0  ==  successfully sent! (I BELIEVE this is 1, in this case, every time, since you don't NEED a request number to CALL getRequestNum since you are only calling it in the first place because it must have gotten out of sync.)
        //
        bWasSent.setBooleanValue(true);
        // ***************************************************
        //
        final int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nResult, "Utility.getRequestNumber");
//        System.out.println("IN Utility.getRequestNumber " + Utility.getLastReplyReceived());

        // BY this point, we definitely have the request number in nResult, which means
        // the message was actually SENT. (At least.) This also means we can use nResult
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //
        //final int nRemovedGetRequest = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nResult), serverID, nymID);

        // NOTE: The above call is unnecessary, since a successful reply means
        // we already received the successful server reply, and OT's "ProcessServerReply"
        // already removed the sent message from the sent buffer (so no need to do that here.)

//        if (nRemovedGetRequest < 1)
//        {
//            System.out.println("Utility.getRequestNumber: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedGetRequest);
//        }
        // ----------------------------------------------

        return nReturn;
    }
    // --------------------------------------------------------------

    /*
    // This returns -1 if error, or a positive request number if it was sent.
    // (It cannot return 0.)
    // Called by getAndProcessNymbox.
    public static int getNymboxLowLevel(String serverID, String nymID)
    {
    otapiJNI.OTAPI_Basic_FlushMessageBuffer();
    // --------------------------------------------------------------------
    final int nRequestNum = otapiJNI.OTAPI_Basic_getNymbox(serverID, nymID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...
   
    switch(nRequestNum)
    {
    case (-2):
    System.out.println("Utility.getNymboxLowLevel: ERROR, not supported. (-2 was returned.)");
    return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
    case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
    System.out.println("Utility.getNymboxLowLevel: Failed to send getNymbox message due to error.");
    return (-1);
    case (0):
    System.out.println("Utility.getNymboxLowLevel: Didn't send getNymbox message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
    return (-1); // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getNymbox. Therefore I treat it as an error.
    default:
    if (nRequestNum < 0) {
    System.out.println("Utility.getNymboxLowLevel: Unexpected request number: " + nRequestNum);
    return (-1);
    }
    // -------------------------
    break; // SUCCESS!
    }
    // --------------------------
    //
    // BY this point, we definitely have the request number, which means the
    // message was actually SENT. (At least.) This also means we can use nRequestNum
    // later to query for a copy of that sent message.
    //
    // ----------------------------------------------
   
    return nRequestNum;
    }    
     */
    // called by getBoxReceiptWithErrorCorrection   DONE
    public static boolean getBoxReceiptLowLevel(String serverID, String nymID, String accountID, int nBoxType, String strTransactionNum, OTBool bWasSent) {
        bWasSent.setBooleanValue(false);

        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
        // --------------------------------------------------------------------
        final int nRequestNum = otapiJNI.OTAPI_Basic_getBoxReceipt(serverID, nymID, accountID, nBoxType, strTransactionNum); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getBoxReceiptLowLevel: ERROR, not supported. (-2 was returned.)");
                return false; // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getBoxReceiptLowLevel: Failed to send getNymbox message due to error.");
                return false;
            case (0):
                System.out.println("Utility.getBoxReceiptLowLevel: Didn't send getNymbox message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return false; // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getNymbox. Therefore I treat it as an error.
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getBoxReceiptLowLevel: Unexpected request number: " + nRequestNum);
                    return false;
                }
                // -------------------------
                break; // SUCCESS!
        }
        // --------------------------
        //
        bWasSent.setBooleanValue(true);
        //
        // BY this point, we definitely have the request number, which means the
        // message was actually SENT. (At least.) This also means we can use nRequestNum
        // later to query for a copy of that sent message.
        //
        // ***************************************************
        //
        int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getBoxReceiptLowLevel");

        System.out.println("IN Utility.getBoxReceiptLowLevel: nRequestNum: " + nRequestNum + " /  nReturn: " + nReturn);

        // ---------------------------------------------

//        final int nRemovedGetBoxReceipt = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        // NOTE: The above call is unnecessary, since a successful reply means
//        // we already received the successful server reply, and OT's "ProcessServerReply"
//        // already removed the sent message from the sent buffer (so no need to do that here.)
//       
//        if (nRemovedGetBoxReceipt < 1)
//        {
//            System.out.println("Utility.getBoxReceiptLowLevel: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedGetBoxReceipt);
//        }
        // ---------------------

        if (nReturn > 0) {
            return true;
        }

        System.out.println("Failure in Utility.getBoxReceiptLowLevel : Response from server: " + Helpers.getLastReplyReceived());

        return false;
    }

    // called by insureHaveAllBoxReceipts     DONE
    public static boolean getBoxReceiptWithErrorCorrection(String serverID, String nymID, String accountID, int nBoxType, String strTransactionNum) {
        OTBool bWasSent = new OTBool(false), bWasRequestSent = new OTBool(false);
        // ------------------------------------------
        if (Helpers.getBoxReceiptLowLevel(serverID, nymID, accountID, nBoxType, strTransactionNum, bWasSent)) {
            return true;
        } else if (bWasSent.getBooleanValue() && (1 == Helpers.getRequestNumber(serverID, nymID, bWasRequestSent))) // this might be out of sync, if it failed... we'll re-sync, and re-try.
        {
            if (bWasRequestSent.getBooleanValue()
                    && Helpers.getBoxReceiptLowLevel(serverID, nymID, accountID, nBoxType, strTransactionNum, bWasSent)) {
                return true;
            } else {
                System.out.println("Utility.getBoxReceiptWithErrorCorrection(): Utility.getBoxReceiptLowLevel failed, then Utility.getRequestNumber succeeded, then Utility.getBoxReceiptLowLevel failed again. (I give up.)");
            }
        } else {
            System.out.println("Utility.getBoxReceiptWithErrorCorrection(): Utility.getBoxReceiptLowLevel failed, then Utility.getRequestNumber failed. (I give up.) Was getRequest message sent: " + bWasRequestSent.getBooleanValue());
        }
        return false;
    }

    // This function assumes you just downloaded the latest version of the box (inbox, outbox, or nymbox)
    // and its job is to make sure all the related box receipts are downloaded as well and available, though
    // not necessarily loaded into memory. (Yet.)
    // DONE
    public static boolean insureHaveAllBoxReceipts(String serverID, String nymID, String accountID, int nBoxType) {
        OTBool bFoundIt = new OTBool(false);

        final int nRequestSeeking = 0;

        return insureHaveAllBoxReceipts(serverID, nymID, accountID, nBoxType, nRequestSeeking, bFoundIt);
    }

    // ---------------------------------------------------------------
    public static boolean insureHaveAllBoxReceipts(String serverID, String nymID, String accountID, int nBoxType, int nRequestSeeking, OTBool bFoundIt) {
        // -------------------

        String ledger = null;

        // -------------------
        switch (nBoxType) {
            // The "Verify" versions of these load functions actually tries to
            // load all the box receipts. Therefore I use the "NoVerify" version,
            // which stops at loading the abbreviations. That way I can iterate
            // through them and download the box receipt for each, as necessary.
            //
            case 0:
                ledger = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);
                break;
            case 1:
                ledger = otapiJNI.OTAPI_Basic_LoadInboxNoVerify(serverID, nymID, accountID);
                break;
            case 2:
                ledger = otapiJNI.OTAPI_Basic_LoadOutboxNoVerify(serverID, nymID, accountID);
                break;
            default:
                System.out.println("Utility.insureHaveAllBoxReceipts(): Error. Expected nBoxType of 0,1,2 (nymbox, inbox, or outbox.)");
                return false;
        }

        // ----------------------------------------------
        // Unable to load or verify inbox/outbox/nymbox
        // Notice I don't call VerifyAccount() here (not that the API even exposes
        // that method) but why not? Because that method tries to load up all the
        // box receipts, in addition to verifying the signature. Therefore I call
        // "Load XXXX NoVerify()", avoiding all that, then I verify the Signature
        // itself. That's because this function's whole point is to find out what
        // the box receipts are, and download them from the server. No point trying
        // to load them before that time, when I know it will fail.
        //
        if (!Helpers.isValid(ledger) || (!otapiJNI.OTAPI_Basic_VerifySignature(nymID, ledger))) {
            System.out.println("Utility.insureHaveAllBoxReceipts(): Unable to load or verify signature on ledger. (Failure.) Contents: " + ledger);
            return false;
        }
        // ----------------------------------------------
        // At this point, the box is definitely loaded.
        // Next we'll iterate the receipts
        // within, and for each, verify that the Box Receipt already exists. If not,
        // then we'll download it using getBoxReceiptLowLevel(). If any download fails,
        // then we break out of the loop (without continuing on to try the rest.)
        //
        boolean bReturnValue = true; // Assuming an empty box, we return success by default.

        final int nReceiptCount = otapiJNI.OTAPI_Basic_Ledger_GetCount(serverID, nymID, accountID, ledger);

        if (nReceiptCount > 0) {
            for (int i = 0; i < nReceiptCount; i++) {
                final String strTransactionNum = otapiJNI.OTAPI_Basic_Ledger_GetTransactionIDByIndex(serverID, nymID, accountID, ledger, i);

                if (Helpers.isValid(strTransactionNum) && !strTransactionNum.equals("-1")) {
                    final Long lTransactionNum = Long.valueOf(strTransactionNum);

                    if (lTransactionNum > 0) {
                        final String strTransaction = otapiJNI.OTAPI_Basic_Ledger_GetTransactionByID(serverID, nymID, accountID, ledger, strTransactionNum);

                        if (!Utility.VerifyStringVal(strTransaction)) {
                            System.out.println("Utility.insureHaveAllBoxReceipts(): Error: Null transaction somehow returned, even though I had a good ID for this index: " + i);
                            return false;
                        } else {
                            final String strTransType = otapiJNI.OTAPI_Basic_Transaction_GetType(serverID, nymID, accountID, strTransaction);
                            final boolean bIsReplyNotice = (Utility.VerifyStringVal(strTransType) && strTransType.equals("replyNotice"));
                            final String strRequestNum;

                            if (bIsReplyNotice) {
                                strRequestNum = otapiJNI.OTAPI_Basic_ReplyNotice_GetRequestNum(serverID, nymID, strTransaction);
                            } else {
                                strRequestNum = null;
                            }
                            // ----------------------------------------

                            final boolean bShouldDownload = (!bIsReplyNotice
                                    || (bIsReplyNotice && Utility.VerifyStringVal(strRequestNum) && (!otapiJNI.OTAPI_Basic_HaveAlreadySeenReply(serverID, nymID, strRequestNum))));
                            // ----------------------------------------
                            if (bShouldDownload) // This block executes if we should download it (assuming we haven't already, which it also checks for.)
                            {
                                final boolean bHaveBoxReceipt =
                                        otapiJNI.OTAPI_Basic_DoesBoxReceiptExist(serverID, nymID, accountID, nBoxType, strTransactionNum);

                                if (!bHaveBoxReceipt) {
                                    System.out.println("Utility.insureHaveAllBoxReceipts(): Downloading box receipt to add to my collection...");

                                    final boolean bDownloaded = Helpers.getBoxReceiptWithErrorCorrection(serverID, nymID, accountID, nBoxType, strTransactionNum);

                                    if (!bDownloaded) {
                                        System.out.println("Utility.insureHaveAllBoxReceipts(): Failed downloading box receipt. (Skipping any others.) Transaction number: " + strTransactionNum);
                                        bReturnValue = false;
                                        break;
                                        // No point continuing to loop and fail 500 times, when getBoxReceiptWithErrorCorrection() already failed
                                        // even doing the getRequest() trick and everything, and whatever retries are inside OT, before it finally
                                        // gave up.
                                    }
                                    // else (Download success.)
                                } // if (!bHaveBoxReceipt)
                            }
                            // else we already have the box receipt, no need to download again.                           
                        }
                    } // if (lTransactionNum > 0)
                    else {
                        System.out.println("Utility.insureHaveAllBoxReceipts(): Error: TransactionNum less-than-or-equal-to 0.");
                    }
                } else {
                    System.out.println("Utility.insureHaveAllBoxReceipts(): Error: TransactionNum was null, when trying to read it based on the index (within bounds, too.)");
                }
            } // for
        } // if (nReceiptCount > 0)
        // ---------------------------------------------
        //
        // if nRequestSeeking is >0, that means the caller wants to know if there is a receipt present for that request number.
        // (which is only a valid option if nBoxType == 0 for Nymbox.)
        // IF the receipt is found, then bFoundIt is set to true.
        //
        if ((nRequestSeeking > 0) && (0 == nBoxType)) {
            // NOTE: the below call to OT_API_Nymbox_GetReplyNotice will succeed even if
            // only the abbreviated receipt is available, because the caller mainly just
            // wants to know if it is there.
            // Technically the full receipt SHOULD always be there, with the above loop,
            // but since the above loop can break in case of error, it's still possible that
            // box receipts haven't been downloaded by the time you reach this code.
            // Nevertheless, we will see if the reply is there for the appropriate request
            // number, whether abbreviated or not.
            //
            // UPDATE: I am now adding specific cases where the replyNotice is NOT downloaded.
            // You still use it, through its abbreviated version -- and the actual version
            // IS still available for download through the server's API. But with a replyNotice,
            // just knowing that it exists is usually enough for the client, who probably still
            // has a cached copy of the original sent message anyway. Only in cases where he
            // doesn't, would he need to download it. (Why? So he can process the server's reply.)
            // Therefore the cached sent message is useless, since it doesn't contain the server's
            // reply! Hmm. So I need that reply BUT ONLY IN CASES where I didn't already receive it
            // as a normal part of processing (and that is MOST of the time, meaning most cases can
            // thus entirely eliminate the download.)
            //
            // PROTOCOL FOR NOT DOWNLOADING MOST OF THE BOX RECEIPTS
            //
            // Solution: User messages should contain a list of the last X number of request numbers
            // that they have DEFINITELY seen the response to. The server, meanwhile, since the user
            // has DEFINITELY seen the response, can now safely remove the replyNotice from the Nymbox.
            // The only reason it was there in the first place was to make sure the user got the reply.
            // Since the user is straight-up acknowledging that he received it, the server no longer
            // needs to "make sure" and thus it can remove that reply from the Nymbox, and mark the
            // box receipt for deletion. This will be MOST REPLIES! We'll eliminate the step of having
            // to download the box receipt.
            // The above call to getBoxReceiptWithErrorCorrection should also be smart enough not to
            // bother downloading any replyNotice Box Receipts if their request number appears on that
            // list. Again: the list means I DEFINITELY already responded to it--if the request # is on
            // that list, then NO NEED downloading the Box Receipt -- I DEFINITELY already got that reply!
            //
            // Therefore, Something like OT_API_HaveAlreadySeenReply(serverID, nymID, requestNum);
            //
            // Perhaps also, on the server side, send a list of request numbers for that Nym that the
            // server KNOWS the Nym saw the reply to. This way, the Nym can remove the number from his
            // list, and thus won't be continually causing the server to load up the Nymbox and try
            // to remove the replyNotice (since it's already been removed.)
            //
            // The Nym doesn't have to keep a list of ALL request numbers he's seen the reply to.
            // Rather, just the past X number of them, and with the number explicitly removed once
            // he sees the server acknowledgment. (ServerAckOfAlreadyGotReply.)
            //
            // The server, meanwhile, is free to remove the ACK for any request # once he sees that
            // the client has as well. Server also only needs to store a list of the past X request #s.
            // Also: since both sides REMOVE the number, there need not necessarily be a limit on the
            // size of the list, since it grows and shrinks as needed.
            //
            // Whenever Wallet processes a server reply, just see if it's on that "replied to already"
            // list already on client side. If so, discard the reply. OTClient::ProcessServerReply probably
            // best place to do this. (We replied to it already, so discard it.)
            // Also, for any server reply, look at the list of numbers on it. The server is acknowledging
            // to us that it KNOWS we got those replies, and that it ALREADY has removed them from the
            // Nymbox as a result. Therefore we can remove ALL of those numbers from our own list
            // as well. No need for an API call to do this, since it will happen internally to OT.
            //
            // On the server side, any numbers on its own list were only there to acknowledge numbers
            // that had been on the client side list. Therefore, when those numbers disappear from the
            // client side list, the server simply removes them. Again: ANY NUMBERS on the server list,
            // which do NOT appear on the client list, are REMOVED From the server list. After all, the
            // client has clearly now removed them, so the server doesn't have to keep them around either.
            //
            // These are useful for synchronization but also there's a long term benefit, if we include
            // them in the signed receipt (which they will be already, if the receipt contains the entire
            // message and not just the transaction.) That benefit is that we can prove receipt of notice.
            // At least, notice of server replies. But for other notice types, such as notice of upcoming
            // meeting. Or notice of upcoming election. Or notice of election outcome. Or notice of petition
            // to put X issue on the next ballot, or to nominate Y Nym for some corporate role. Sometimes
            // you want to be able to PROVE that notice was received. Does this prove that?
            // Hmm, not necessarily. Currently I'm using this as an optimization scheme, which is useful
            // even if not provable. How to make it provable?
            //
            // Back from tangent: Wait a sec! If I notice the server that I saw the reply, the server will
            // remove that reply from my Nymbox -- but it's still in my Nymbox on the client side! Until
            // I download the latest Nymbox. Thus if I try to PROCESS MY NYMBOX, I will be attempting to
            // accept a receipt that's already gone! (And the processNymbox will therefore FAIL!)
            // Solution: be smart enough, when processing Nymbox, to IGNORE any replyNotices when the request
            // Number appears on the client's list! As the wallet processes the Nymbox it should already
            // know to skip the ones that were already replied-to.
            // Meanwhile the server side will deliberately NOT update the Nymbox hash just because the receipt
            // was removed. Otherwise it could trigger an unnecessary download of the Nymbox, when the whole
            // point of this exercise was to prevent unnecessary downloads. It only updates the Nymbox hash
            // when it WANTS me to download the Nymbox, and that certainly does NOT apply to cases where the
            // only change involved the removal of some old receipt I already acknowledged. (No need to force
            // any downloads based on THAT case, after all.)
            //
            final String strReplyNotice = otapiJNI.OTAPI_Basic_Nymbox_GetReplyNotice(serverID, nymID, Integer.toString(nRequestSeeking));

            if (Helpers.isValid(strReplyNotice)) {
                bFoundIt.setBooleanValue(true);
            }
        }

        // ---------------------------------------------

        return bReturnValue;
    }

    /*
    static void getBoxReceipt(  const std::string  SERVER_ID,
    const std::string  USER_ID,
    const std::string  ACCT_ID,  // If for Nymbox (vs inbox/outbox) then pass USER_ID in this field also.
    const int    nBoxType,  // 0/nymbox, 1/inbox, 2/outbox
    const std::string  TRANSACTION_NUMBER);
   
    static bool DoesBoxReceiptExist(const std::string  SERVER_ID,
    const std::string  USER_ID,
    const std::string  ACCT_ID,  // If for Nymbox (vs inbox/outbox) then pass USER_ID in this field also.
    const int    nBoxType,  // 0/nymbox, 1/inbox, 2/outbox
    const std::string  TRANSACTION_NUMBER);
     */
    // If the transaction number requests fail, this function will try to resync
    // the request number. But you still have to call getRequest() yourself if
    // you have a failure in your own function, since you might already have
    // enough transaction numbers, and thus this function will never get called,
    // even if your request number IS out of sync. Sorry :-)
    //
    public static int getTransactionNumLowLevel(String serverID, String nymID, OTBool bWasSent) {
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
        bWasSent.setBooleanValue(false);
        // --------------------------------------------------------------------
        final int nRequestNum = otapiJNI.OTAPI_Basic_getTransactionNumber(serverID, nymID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getTransactionNum: ERROR, not supported. (-2 was returned.)");
                return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getTransactionNum: Failed to send getNymbox message due to error.");
                return (-1);
            case (0):
                System.out.println("Utility.getTransactionNum: Unexpectedly returned 0. Didn't send getTransactionNum message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return (-1); // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getTransactionNum. Therefore I treat it as an error.
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getTransactionNum: Unexpected request number: " + nRequestNum);
                    return (-1);
                }
                // -------------------------
                break; // SUCCESS!
        }
        bWasSent.setBooleanValue(true);
        // ***************************************************
        //
        final int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getTransactionNum");
//        System.out.println("IN Utility.getTransactionNum " + Utility.getLastReplyReceived());

        // BY this point, we definitely have the request number in nResult, which means
        // the message was actually SENT. (At least.) This also means we can use nResult
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //

        // THE REMOVE SENT MESSAGE BELOW FAILS, LIKE IT'S ALREADY GONE.
        //
        // THIS MUST BE DUE TO THE PROCESS SERVER REPLY THAT OCCURS **IMMEDIATELY** after the message was originally sent!
        // (The reply came in and was sent to OT's "ProcessServerReply", INSIDE the call to OT_API_getTransactionNumber.)
        // Our subsequent "receive" (above) is nothing of the sort, but actually pops the incoming message buffer where
        // the server's reply was ALREADY SITTING, since it was put there in OT's "ProcessServerReply", WHICH REMOVED THE
        // SENT MESSAGE ALREADY (that's why the below call to RemoveSentMessage fails.)
        //
        // RETHINK any logic that doesn't take this into account,.
        // Either we REMOVE this call wherever this happens, OR... we call Get first and make sure whether it's
        // there, THEN remove it. But we can't be lumping "Failure because it's gone" versus "Error state" by mixing
        // 0 and -1 here. We need to differentiate.
        //
        // Bottom line: if the reply WAS received, then the original sent message has ALREADY been removed
        // from the sent buffer. Whereas if the reply was NOT received, then the sent message is still there,
        // but in that case, we do NOT want to remove it -- we want it to STAY in the sent buffer, so that
        // when we get the Nymbox later and we DO have the reply from that, THEN we can remove the sent msg from
        // the sent buffer. Until then, we don't want OT to think it's already been processed (which it will, if
        // it's already been removed from the sent buffer. So we leave it there for now.)
        //
        //
        //
//        final int nRemovedSentMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        if (nRemovedSentMsg < 1)
//        {
//            System.out.println("Utility.getTransactionNum: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedSentMsg);
//        }
        // ----------------------------------------------

        if (1 == nReturn) {
            return nRequestNum;
        }
        return nReturn;
    }

    // DONE
    public static boolean getTransactionNumbers(String serverID, String nymID) {
        return Helpers.getTransactionNumbers(serverID, nymID, true); // bForceFirstCall == true (by default) but in special cases you can override it and set it to false.
    }

    public static boolean getTransactionNumbers(String serverID, String nymID, boolean bForceFirstCall) // defaults to true.
    {
// System.out.println("DEBUGGING -- 1.");
        //
        OTBool bWasSent = new OTBool(false);
        int nGetNumbers = -1;

        if (bForceFirstCall) {
            nGetNumbers = Helpers.getTransactionNumLowLevel(serverID, nymID, bWasSent);   // <============ FIRST TRY
        } else {
            nGetNumbers = -1;
        }

// System.out.println("DEBUGGING -- 2.");

        if (!bForceFirstCall || // if the first call didn't happen, due to bForceFirstCall being false, that means the caller wants the rest of this to happen as though it did. 
                (bWasSent.getBooleanValue() && (nGetNumbers >= 1))
                || (!bWasSent.getBooleanValue() && (nGetNumbers == 0))) {
            // System.out.println("DEBUGGING -- 3.");

            // Because it was successful, we have to now SIGN FOR those numbers we requested.
            //
            int nProcess = Helpers.getAndProcessNymbox(serverID, nymID, bWasSent, true); // bForceDownload=true

            // System.out.println("DEBUGGING -- 4.");

            if ((bWasSent.getBooleanValue() && (1 == nProcess))
                    || (!bWasSent.getBooleanValue() && (0 == nProcess))) {
                // System.out.println("DEBUGGING -- 5.");
                return true;
            }
        } else if ((nGetNumbers < (-1)) || // If value is LESS THAN (-1) (which is an unexpected value)
                !bWasSent.getBooleanValue()) // or if the getTransactionNum message WASN'T EVEN SENT, then return.
        {
            // System.out.println("DEBUGGING -- 6.");

            System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel returned unexpected value: " + nGetNumbers);
            return false;
        } // -----------------------------------------------------------------
        // Below this point, the message WAS sent.  -1 is error, 0 is failure, >0 is success.
        // Now it's just about whether a reply was successful, or was even received.
        //
        else if (((-1) == nGetNumbers) || // Message sent, but then error receiving or loading the reply.
                ((0) == nGetNumbers)) // Received a reply, but status == failure on that reply.
        {
// System.out.println("DEBUGGING -- 7.");

            if ((-1) == nGetNumbers) {
                System.out.println("Utility.getTransactionNumbers: FYI: Utility.getTransactionNumLowLevel did send, but returned error (-1). (Re-trying...)");
            } else if ((0) == nGetNumbers) {
                System.out.println("Utility.getTransactionNumbers: FYI: Utility.getTransactionNumLowLevel did send, but returned failure (0). (Re-trying...)");
            }

            // ---------------------------------
            final int nGetRequest = Helpers.getRequestNumber(serverID, nymID);

// System.out.println("DEBUGGING -- 8.");

            if (1 != nGetRequest) {
                System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel failed, then "
                        + "I tried to resync with getRequestNumber and then that failed too. (I give up.)");
                return false;
            }
            // ---------------------------------

// System.out.println("DEBUGGING -- 9.");

            OTBool bWasProcessSent = new OTBool(false);
            OTBool bFoundNymboxItem = new OTBool(false);

            final int nProcessNymbox = Helpers.getAndProcessNymbox(serverID, nymID, bWasProcessSent, true);   //boolean bForceDownload=true

// System.out.println("DEBUGGING -- 10.");

            if ((!bWasProcessSent.getBooleanValue() && ((nProcessNymbox < 0) || (nProcessNymbox > 1)))
                    || (bWasProcessSent.getBooleanValue() && (nProcessNymbox != 1))) // -1 error, 0 failed (harvesting success), 1 success, >1 failed (harvesting NOT done) RequestNum is returned.
            {
                // System.out.println("DEBUGGING -- 11.");

                // todo: if request num is returned probably don't have to do anything with it.
                // Why not?  Because future processNymbox will iterate Nymbox and search for all found
                // items in the sent message buffer, and REMOVE them from it (as clearly they will be
                // processed already.)
                // The ones left over in the sent buffer, after this? Must be harvested!
                // Hmm, solution: Use the "Flush Sent Messages" function, which is already
                // there. Have it be smart enough to harvest all sent messages before flushing,
                //
                //
                if (bWasProcessSent.getBooleanValue() && nProcessNymbox > 1) {
                    // System.out.println("DEBUGGING -- 12.");

                    String strNymbox = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);      // FLUSH SENT MESSAGES!!!!  (AND HARVEST.)

                    // System.out.println("DEBUGGING -- 13.");

                    // *******************************************************
                    if (Helpers.isValid(strNymbox)) {
                        otapiJNI.OTAPI_Basic_FlushSentMessages(false, //harvesting for retry == false
                                serverID,
                                nymID,
                                strNymbox);
                    }
                    // System.out.println("DEBUGGING -- 14.");
                }

                System.out.println("Utility.getTransactionNumbers: Failure: Utility.getAndProcessNymbox. Returned value: " + nProcessNymbox);
                return false;
            }

// System.out.println("DEBUGGING -- 15.");

            // -----------------------------------------------------------------

            nGetNumbers = Helpers.getTransactionNumLowLevel(serverID, nymID, bWasSent)// <================= SECOND TRY

// System.out.println("DEBUGGING -- 16.");

            // -----------------------------------------------------------------

            if ((bWasSent.getBooleanValue() && (nGetNumbers >= 1)) || // if message was sent, and was a success.
                    (!bWasSent.getBooleanValue() && (nGetNumbers == 0))) // Or if message wasn't sent due to "you already signed out too many numbers--you need to process your Nymbox..."
            {
// System.out.println("DEBUGGING -- 17.");

                int nProcess = Helpers.getAndProcessNymbox(serverID, nymID, bWasSent, true); // bForceDownload=true

// System.out.println("DEBUGGING -- 18.");

                if ((bWasSent.getBooleanValue() && (1 == nProcess))
                        || (!bWasSent.getBooleanValue() && (0 == nProcess))) {
// System.out.println("DEBUGGING -- 19.");

                    return true;
                }
            } else if ((nGetNumbers < (-1))
                    || (!bWasSent.getBooleanValue() && nGetNumbers != 0)) {
// System.out.println("DEBUGGING -- 20.");

                System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel returned unexpected value: " + nGetNumbers);
                return false;
            } else if (((-1) == nGetNumbers)
                    || ((0) == nGetNumbers)) {
                // System.out.println("DEBUGGING -- 21.");

                if ((-1) == nGetNumbers) {
                    System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel did send, but returned error (-1), "
                            + "even after syncing the request number successfully. (Giving up.)");
                } else if ((0) == nGetNumbers) {
                    System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel did send, but returned failure (0), "
                            + "even after syncing the request number successfully. (Giving up.)");
                }


                // System.out.println("DEBUGGING -- 22.");

                int nLast = Helpers.getAndProcessNymbox(serverID, nymID, bWasProcessSent, true);   //boolean bForceDownload=true
                if (((false == bWasProcessSent.getBooleanValue()) && ((nLast < 0) || (nLast > 1)))
                        || ((true == bWasProcessSent.getBooleanValue()) && (nLast != 1))) // -1 error, 0 failed (harvesting success), 1 success, >1 failed (harvesting NOT done) RequestNum is returned.
                {

                    // System.out.println("DEBUGGING -- 23.");

                    if (bWasProcessSent.getBooleanValue() && (nLast > 1)) {

                        // System.out.println("DEBUGGING -- 24.");

                        String strNymbox = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);      // FLUSH SENT MESSAGES!!!!  (AND HARVEST.)

                        // System.out.println("DEBUGGING -- 25.");

                        // *******************************************************
                        if (Helpers.isValid(strNymbox)) {
                            otapiJNI.OTAPI_Basic_FlushSentMessages(false, //harvesting for retry == false
                                    serverID,
                                    nymID,
                                    strNymbox);
                        }
                    }

                    System.out.println("Utility.getTransactionNumbers: Failure: Utility.getAndProcessNymbox. Returned value: " + nLast);
                    return false;
                }

                // System.out.println("DEBUGGING -- 27.");

                nGetNumbers = Helpers.getTransactionNumLowLevel(serverID, nymID, bWasSent);   // <============ FIRST TRY     

                // System.out.println("DEBUGGING -- 28.");

                if ((bWasSent.getBooleanValue() && (nGetNumbers >= 1))
                        || ((!bWasSent.getBooleanValue() && (nGetNumbers == 0)))) {
                    // System.out.println("DEBUGGING -- 29.");


                    int nProcess = Helpers.getAndProcessNymbox(serverID, nymID, bWasSent, true); // bForceDownload=true


                    // System.out.println("DEBUGGING -- 30.");


                    if ((bWasSent.getBooleanValue() && (1 == nProcess))
                            || (!bWasSent.getBooleanValue() && (0 == nProcess))) {
                        // System.out.println("DEBUGGING -- 31.");

                        return true;
                    }
                }
                if ((nGetNumbers < (-1)) || // If value is LESS THAN (-1) (which is an unexpected value)
                        !bWasSent.getBooleanValue()) // or if the getTransactionNum message WASN'T EVEN SENT, then return.
                {
                    // System.out.println("DEBUGGING -- 32.");

                    System.out.println("Utility.getTransactionNumbers: Failure: Utility.getTransactionNumLowLevel returned unexpected value: " + nGetNumbers);
                    return false;
                }
            }
        }

        // System.out.println("DEBUGGING -- 33.");

        // BY THIS POINT, we have SUCCESSFULLY sent the getTransactionNumLowLevel message,
        // and nGetNumbers contains its request number.
        // -----

        // No need to read the result, as getTransactionNumLowLevel() already read it,
        // and and it's available anytime via Utility.getLastReplyReceived()
        // -------------------------------------------------------------------------

        final String strLastReplyReceived = Helpers.getLastReplyReceived();

        if (!Helpers.isValid(strLastReplyReceived)) {
            System.out.println("Utility.getTransactionNumbers: "
                    + "ERROR in Utility.getLastReplyReceived(): why was this string not set, when Utility.getRequestNumber was otherwise an apparent success?");
            return false; // (SHOULD NEVER HAPPEN. This string is set in the getRequestNumber function.)
        }
        //-------------------------------------------------

        // BY THIS POINT, we have received a server reply:  @getTransactionNum
        // (Unless it is malformed.) It's definitely not null, nor empty.

        //-------------------------------------------------

        // Grab the NymboxHash on the @getTransactionNum reply, and also the one I
        // already had on my client-side Nym... (So we can compare them.)
        //
        final String strServerHash = otapiJNI.OTAPI_Basic_Message_GetNymboxHash(strLastReplyReceived);
        final boolean bServerhash = Helpers.isValid(strServerHash);
        if (!bServerhash) {
            System.out.println("Utility.getTransactionNumbers: Warning: Unable to retrieve server-side "
                    + "NymboxHash from OT, from server @getTransactionNum reply:\n\n"
                    + strLastReplyReceived);
//            return false;
        }
        //-------------------------------------------------               
        final String strLocalHash = otapiJNI.OTAPI_Basic_GetNym_NymboxHash(serverID, nymID);
        final boolean bLocalhash = Helpers.isValid(strLocalHash);
        if (!bLocalhash) {
            System.out.println("Utility.getTransactionNumbers: Warning: Unable to retrieve client-side NymboxHash from OT, "
                    + "for:\n serverID: " + serverID + "\n nymID: " + nymID);
//            return false;
        }
        //-------------------------------------------------

        if (!bServerhash || !bLocalhash
                || (bServerhash && bLocalhash
                && !strServerHash.equals(strLocalHash))) // the hashes don't match -- so let's definitely re-try to download the latest nymbox.
        {
            // the getRequest worked, and the server hashes don't match,
            // so let's get and process the Nymbox...
            //

            // System.out.println("DEBUGGING -- 34.");


            OTBool bWasProcessSent = new OTBool(false);
            OTBool bFoundNymboxItem = new OTBool(false);
            final int nGetNymbox = Helpers.getAndProcessNymbox(serverID, nymID, bWasProcessSent, true);   //boolean bForceDownload=true

            if (((false == bWasProcessSent.getBooleanValue()) && ((nGetNymbox < 0) || (nGetNymbox > 1)))
                    || ((true == bWasProcessSent.getBooleanValue()) && (nGetNymbox != 1))) // -1 error, 0 failed (harvesting success), 1 success, >1 failed (harvesting NOT done) RequestNum is returned.
            {
                if (nGetNymbox > 1) {
                    String strNymbox = otapiJNI.OTAPI_Basic_LoadNymboxNoVerify(serverID, nymID);      // FLUSH SENT MESSAGES!!!!  (AND HARVEST.)

                    // *******************************************************
                    if (Helpers.isValid(strNymbox)) {
                        otapiJNI.OTAPI_Basic_FlushSentMessages(false, //harvesting for retry == false
                                serverID,
                                nymID,
                                strNymbox);
                    }
                }

                System.out.println("Utility.getTransactionNumbers: Failure: Utility.getAndProcessNymbox returned unexpected value: " + nGetNymbox);
                return false;
            } else if ((-1) == nGetNymbox) // we'll try re-syncing the request number, then try again.
            {
                System.out.println("Utility.getTransactionNumbers: Failure: Utility.getAndProcessNymbox returned -1, even after syncing the request number successfully. (Giving up.)");
                return false;
            }
        }

        // System.out.println("DEBUGGING -- 35.");

        return true;
    }
    // -------------------------------

    public static String getCreditsFile(String fileName) {
        return otapi.QueryPlainString(".",fileName);
    }

    public static Point getLocation(Dimension componentDimension) {

        Point center = new Point(0, 0);
        Dimension toolkitDimension = Toolkit.getDefaultToolkit().getScreenSize();
        center.x = center.x + (toolkitDimension.width - componentDimension.width) / 2;
        center.y = center.y + (toolkitDimension.height - componentDimension.height) / 2;

        return center;
    }

    public static void getKeyFromName(String name) {
    }

    public static WalletData getWalletData() {

        WalletData walletData = null;
        Storable storable = null;
        if (otapi.Exists("moneychanger", "gui_wallet.dat")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_WALLET_DATA, "moneychanger", "gui_wallet.dat");
            if (storable == null) {
                return null;
            }
            walletData = WalletData.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_WALLET_DATA);
            if (storable == null) {
                return null;
            }
            walletData = WalletData.ot_dynamic_cast(storable);
        }

        return walletData;
    }

    public static MarketList getMarketList(String serverID) {

        MarketList marketList = null;
        Storable storable = null;
        if (otapi.Exists("markets", serverID, "market_data.bin")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_MARKET_LIST, "markets", serverID, "market_data.bin");
            if (storable == null) {
                return null;
            }
            marketList = MarketList.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_MARKET_LIST);
            if (storable == null) {
                return null;
            }
            marketList = MarketList.ot_dynamic_cast(storable);
        }

        return marketList;
    }

    public static OfferListMarket getMarketOffer(String serverID, String marketID) {

        OfferListMarket offerListMarket = null;
        Storable storable = null;
        if (otapi.Exists("markets", serverID, "offers", marketID + ".bin")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_OFFER_LIST_MARKET, "markets", serverID, "offers", marketID + ".bin");
            if (storable == null) {
                return null;
            }
            offerListMarket = OfferListMarket.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_OFFER_LIST_MARKET);
            if (storable == null) {
                return null;
            }
            offerListMarket = OfferListMarket.ot_dynamic_cast(storable);
        }

        return offerListMarket;
    }

    public static TradeListNym getNYMTrades(String serverID, String nymID) {

        TradeListNym tradeListNym = null;
        Storable storable = null;
        if (otapi.Exists("nyms", "trades", serverID, nymID)) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_TRADE_LIST_NYM, "nyms", "trades", serverID, nymID);
            if (storable == null) {
                return null;
            }
            tradeListNym = TradeListNym.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_TRADE_LIST_NYM);
            if (storable == null) {
                return null;
            }
            tradeListNym = TradeListNym.ot_dynamic_cast(storable);
        }

        return tradeListNym;
    }

    public static OfferListNym getNYMOffer(String serverID, String nymID) {

        OfferListNym offerListNym = null;
        Storable storable = null;
        if (otapi.Exists("nyms", serverID, "offers", nymID + ".bin")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_OFFER_LIST_NYM, "nyms", serverID, "offers", nymID + ".bin");
            if (storable == null) {
                return null;
            }
            offerListNym = OfferListNym.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_OFFER_LIST_NYM);
            if (storable == null) {
                return null;
            }
            offerListNym = OfferListNym.ot_dynamic_cast(storable);
        }

        return offerListNym;
    }

    public static TradeListMarket getMarketTradeList(String serverID, String marketID) {

        TradeListMarket tradeListMarket = null;
        Storable storable = null;
        if (otapi.Exists("markets", serverID, "recent", marketID + ".bin")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_TRADE_LIST_MARKET, "markets", serverID, "recent", marketID + ".bin");
            if (storable == null) {
                return null;
            }
            tradeListMarket = TradeListMarket.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_TRADE_LIST_MARKET);
            if (storable == null) {
                return null;
            }
            tradeListMarket = TradeListMarket.ot_dynamic_cast(storable);
        }

        return tradeListMarket;
    }

    public static AddressBook getAddressBook() {

        AddressBook addressBook = null;
        Storable storable = null;
        if (otapi.Exists("moneychanger", "gui_contacts.dat")) {
            storable = otapi.QueryObject(StoredObjectType.STORED_OBJ_ADDRESS_BOOK, "moneychanger", "gui_contacts.dat");
            if (storable == null) {
                return null;
            }
            addressBook = AddressBook.ot_dynamic_cast(storable);
        } else {
            storable = otapi.CreateObject(StoredObjectType.STORED_OBJ_ADDRESS_BOOK);
            if (storable == null) {
                return null;
            }
            addressBook = AddressBook.ot_dynamic_cast(storable);
        }

        return addressBook;
    }
    private static LookAndFeel defautLAF;

    public static LookAndFeel getDefautLAF() {
        return defautLAF;
    }

    public static void setDefautLAF(LookAndFeel defautLAF) {
        Helpers.defautLAF = defautLAF;
    }

    public static String fileToString(File file) {
        String fileText = "";
        try {
            FileInputStream fis = new FileInputStream(file);
            StringBuffer sb = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(new java.io.BufferedInputStream(fis)));
            //read the stream
            int c = 0;
            while ((c = reader.read()) != -1) {
                sb.append((char) c);
            }
            fileText = sb.toString();

        } catch (IOException ex) {
            Logger.getLogger(Helpers.class.getName()).log(Level.SEVERE, null, ex);
        }
        return fileText;
    }

    public static void replaceToLower(List<String> strings) {
        ListIterator<String> iterator = strings.listIterator();
        while (iterator.hasNext()) {
            iterator.set(iterator.next().toLowerCase());
        }
    }

    public static double roundTwoDecimals(double d) {
        DecimalFormat twoDForm = new DecimalFormat("#.##");
        return Double.valueOf(twoDForm.format(d));
    }

    public static void populateCombo(Map data, JComboBox component) {

        int count = 1;

        while (component.getItemCount() > 1) {
            component.removeItemAt(count);

        }

        Set set = data.keySet();
        Iterator iterator = set.iterator();

        while (iterator.hasNext()) {
            Integer key = (Integer) iterator.next();
            component.addItem(new ComboObject(((String[]) data.get(key))[0]));
        }

        if (component instanceof com.moneychanger.ui.custom.SteppedComboBox) {

            Dimension d = component.getPreferredSize();
            component.setPreferredSize(new Dimension(100, d.height));
            ((com.moneychanger.ui.custom.SteppedComboBox) component).setPopupWidth(d.width);
        }
    }

    public static void populateComboWithoutAll(Map data, JComboBox component) {

        while (component.getItemCount() > 0) {
            component.removeItemAt(0);

        }

        Set set = data.keySet();
        Iterator iterator = set.iterator();

        while (iterator.hasNext()) {
            Integer key = (Integer) iterator.next();
            component.addItem(new ComboObject(((String[]) data.get(key))[0]));
        }

        if (component instanceof com.moneychanger.ui.custom.SteppedComboBox) {

            Dimension d = component.getPreferredSize();
            component.setPreferredSize(new Dimension(100, d.height));
            ((com.moneychanger.ui.custom.SteppedComboBox) component).setPopupWidth(d.width);
        }
    }

    public static boolean isValidDouble(String text) {
        try {
            Double.parseDouble(text);
        } catch (NumberFormatException nfe) {
            nfe.printStackTrace();
            return false;
        }
        return true;
    }

    public static void delay() {
        try { // SLEEP
            if (Configuration.getWaitTime() > 0) {
                Thread.sleep(Configuration.getWaitTime());
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    public static void longDelay() throws InterruptedException {
        try { // SLEEP
            if (Configuration.getWaitTime() > 0) {
                Thread.sleep(Configuration.getWaitTime() + 200);
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        return;
    }

    public static String generateID() {
        SecureRandom random = new SecureRandom();
        return new BigInteger(130, random).toString(32);
    }

    public static void main(String a[]) {
        System.out.println(generateID());
    }

    public static boolean getIntermediaryFiles(String serverID, String nymID, String accountID) {
        return Helpers.getIntermediaryFiles(serverID, nymID, accountID, false); //bForceDownload=false
    }

    public static boolean getIntermediaryFiles(String serverID, String nymID, String accountID, boolean bForceDownload) // bForceDownload=false
    {

        if (!Utility.VerifyStringVal(serverID)) {
            System.out.println("Utility.getIntermediaryFiles: invalid serverID: " + serverID);
            return false;
        }
        if (!Utility.VerifyStringVal(nymID)) {
            System.out.println("Utility.getIntermediaryFiles: invalid nymID: " + nymID);
            return false;
        }
        if (!Utility.VerifyStringVal(accountID)) {
            System.out.println("Utility.getIntermediaryFiles: invalid accountID: " + accountID);
            return false;
        }
        // -----------------------------------------------------
        OTBool bWasSentInbox = new OTBool(false),
                bWasSentAccount = new OTBool(false);
        // -----------------------------------------------------
        int nGetInboxAcct = Helpers.getInboxAccount(serverID, nymID, accountID, bWasSentInbox, bWasSentAccount, bForceDownload);

        if ( // if we received an error state, and the "getAccount" message wasn't even sent,
                // then no point doing a bunch of retries -- it failed.
                //
                ((-1) == nGetInboxAcct) && !bWasSentAccount.getBooleanValue()) {
            System.out.println("Utility.getIntermediaryFiles: Utility.getInboxAccount failed, without even sending getAccount. (Returning false.)");
            return false;
        } else if (!bWasSentInbox.getBooleanValue() && // If it wasn't sent, and 0 was returned, that means
                (0 == nGetInboxAcct)) // no error: we already have the latest inbox. (Nothing done.)
        {
            // we don't return true here because getOutbox has to happen also.
        } else if (1 != nGetInboxAcct) {
            System.out.println("Utility.getIntermediaryFiles: getInboxAccount failed. (Trying one more time...)");

            // --------------------------------------
            final int nGetRequest = Helpers.getRequestNumber(serverID, nymID);

            if (1 != nGetRequest) {
                System.out.println("Utility.getIntermediaryFiles(): Failure: Utility.getInboxAccount failed, then I tried to resync with getRequestNumber and then that failed too. (I give up.)");
                return false;
            }
            // --------------------------------------
            final int nSecondtry = Helpers.getInboxAccount(serverID, nymID, accountID, bWasSentInbox, bWasSentAccount, bForceDownload);

            if (((-1) == nSecondtry) && !bWasSentAccount.getBooleanValue()) {
                // if we received an error state, and the "getAccount" message wasn't even sent,
                // then no point doing a bunch of retries -- it failed.
                //
                System.out.println("Utility.getIntermediaryFiles: Utility.getInboxAccount failed a second time, without even sending getAccount. (Returning false.)");
                return false;
            }
            if (!bWasSentInbox.getBooleanValue() && // If it wasn't sent, and 0 was returned, that means
                    (0 == nSecondtry)) // no error: we already have the latest inbox. (Nothing done.)
            {
                // we don't return true here because getOutbox has to happen also.
            } else if (1 != nSecondtry) {
                System.out.println("Utility.getIntermediaryFiles: getInboxAccount re-try failed. (That's twice now--Returning false.) Value: " + nSecondtry);
                return false;
            }
            System.out.println("Utility.getIntermediaryFiles: getInboxAccount second call succeeded. (Continuing...)");
        }
        // *****************************************************************************

        OTBool bWasSentOutbox = new OTBool(false);

        int nGetOutbox = Helpers.getOutboxLowLevel(serverID, nymID, accountID, bWasSentOutbox, bForceDownload);

        if (((-1) == nGetOutbox) && !bWasSentOutbox.getBooleanValue()) {
            // if we received an error state, and the "getOutbox" message wasn't even sent,
            // then no point doing a bunch of retries -- it failed.
            //
            System.out.println("Utility.getIntermediaryFiles: Utility.getOutboxLowLevel failed, without even sending getOutbox. (Returning false.)");
            return false;
        } else if (!bWasSentOutbox.getBooleanValue() && // If it wasn't sent, and 0 was returned, that means the
                (0 == nGetOutbox)) // outbox we have is already the latest version.
        {
            return true;
        } else if (1 != nGetOutbox) {
            System.out.println("Utility.getIntermediaryFiles: getOutboxLowLevel failed. (Trying one more time...)");

            // --------------------------------------
            final int nGetRequest = Helpers.getRequestNumber(serverID, nymID);

            if (1 != nGetRequest) {
                System.out.println("Utility.getIntermediaryFiles(): Failure: Utility.getOutboxLowLevel failed, then I tried to resync with getRequestNumber and then that failed too. (I give up.)");
                return false;
            }
            // --------------------------------------
            final int nSecondtry = Helpers.getOutboxLowLevel(serverID, nymID, accountID, bWasSentOutbox, bForceDownload);

            if (((-1) == nSecondtry) && !bWasSentOutbox.getBooleanValue()) {
                // if we received an error state, and the "getOutbox" message wasn't even sent,
                // then no point doing a bunch of retries -- it failed.
                //
                System.out.println("Utility.getIntermediaryFiles: Utility.getOutboxLowLevel failed a second time, without even sending getOutbox. (Returning false.)");
                return false;
            }
            if (!bWasSentOutbox.getBooleanValue() && // If it wasn't sent, and 0 was returned, that means
                    (0 == nSecondtry)) // no error: we already have the latest outbox. (Nothing done.)
            {
                return true;
            } else if (1 != nSecondtry) {
                System.out.println("Utility.getIntermediaryFiles: getOutboxLowLevel re-try failed. (That's twice now--Returning false.) Value: " + nSecondtry);
                return false;
            }
            System.out.println("Utility.getIntermediaryFiles: getOutboxLowLevel second call succeeded. (Continuing...)");
        }

        return true;
    }

    // Same as the above function, except you only have to pass the accountID.
    // (instead of 3 IDs...)
    //
    public static boolean getInboxOutboxAccount(String accountID) {
        return Helpers.getInboxOutboxAccount(accountID, false); //bForceDownload=false
    }

    public static boolean getInboxOutboxAccount(String accountID, boolean bForceDownload) //bForceDownload=false
    {

        if (!Utility.VerifyStringVal(accountID)) {
            System.out.println("getInboxOutboxAccount: invalid accountID: " + accountID);
            return false;
        }
        // ------------------------------------------------------------------------
        String serverID = otapiJNI.OTAPI_Basic_GetAccountWallet_ServerID(accountID);
        String nymID = otapiJNI.OTAPI_Basic_GetAccountWallet_NymID(accountID);
        // ------------------------------------------------------------------------

        if (false == Helpers.getIntermediaryFiles(serverID, nymID, accountID, bForceDownload)) {
            System.out.println("getInboxOutboxAccount: getIntermediaryFiles failed. (Returning.)");
            return false;
        }
        return true;
    }

    // getInboxAccount()
    // Grabs the "Account", which is the intermediary file containing the current balance, verified against
    // last signed receipt. Server must have your signature on the last balance agreement plus, if applicable,
    // any inbox receipts (box receipts), also with your signature, in order to justify the current balance.
    // Any inbox receipts, further, are only valid if they each contain a transaction number that was previously
    // already signed out to you.
    // (As you can see, the "account" is not a list of transactions, as per the classical understanding in
    // double-entry accounting, but instead it's just a signed balance agreement, plus any as-yet-unclosed
    // transactions that have cleared since that balance was last signed, and are still waiting in the inbox
    // for the next balance agreement to be signed when they can be removed.)
    // ----------------
    // In addition to the "Account" there is also the Inbox itself, as well as all of its box receipts.
    // The box receipts are stored in abbreviated form in the Inbox itself, with the actual full
    // versions in separate files. These are retrieved individually from the server after the inbox itself
    // is, and then each is verified against a hash kept inside its abbreviated version.)
    // DONE
    public static int getInboxAccount(String serverID, String nymID, String accountID, OTBool bWasSentInbox, OTBool bWasSentAccount) {
        return Helpers.getInboxAccount(serverID, nymID, accountID, bWasSentInbox, bWasSentAccount, false); // bForceDownload = false
    }

    public static int getInboxAccount(String serverID, String nymID, String accountID, OTBool bWasSentInbox, OTBool bWasSentAccount, boolean bForceDownload) //bForceDownload=false
    {
        bWasSentAccount.setBooleanValue(false);
        bWasSentInbox.setBooleanValue(false);

        // ***************************************************
        //
        // (Success means both were downloaded, if necessary.)
        //
        // FIRST WE DO THE ACCOUNT...
        //
        // ***************************************************
        // GET ACCOUNT
        //
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
       
        System.out.println("otapiJNI.OTAPI_Basic_getAccount(serverID = "+serverID+", nymID = "+nymID+", accountID = " +accountID+")");

        final int nRequestNum = otapiJNI.OTAPI_Basic_getAccount(serverID, nymID, accountID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getInboxAccount: ERROR, not supported. (-2 was returned.)");
                return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getInboxAccount: Failed to send getAccount message due to error.");
                return (-1);
            case (0):
                System.out.println("Utility.getInboxAccount: Didn't send getAccount message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return (-1);
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getInboxAccount: Unexpected failure sending getAccount(). Request number: " + nRequestNum);
                    return (-1);
                }
                // -------------------------
                break; // SUCCESS!
        }
        bWasSentAccount.setBooleanValue(true);
        // ***************************************************
        // -1 for error
        //  0 for reply: failure
        //  1 for reply: success
        //
        final int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getInboxAccount"); // <============ RETURN VALUE
//        System.out.println("IN Utility.getInboxAccount " + Utility.getLastReplyReceived());

        final boolean bAccount = ((1 == nReturn) ? true : false);

        // BY this point, we definitely have the request number, which means the
        // message was actually SENT. (At least.) This also means we can use nRequestNum
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //
        // ----------------------------------------------
//        final int nRemovedSentMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        // NOTE: The above call is unnecessary, since a successful process means
//        // we already received the successful server reply, and OT's "ProcessServerReply"
//        // already removed the sent message from the sent buffer (so no need to do that here.)
//       
//        if (nRemovedSentMsg < 1) // (not success.)
//        {
//            System.out.println("Utility.getInboxAccount: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedSentMsg);
//        }

        if (nReturn < 0) // error
        {
            System.out.println("Utility.getInboxAccount: Error in getAccount: " + nReturn + ".  (I give up.)");
            return (-1);
        }
        // ***************************************************
        if (!bAccount) {
            System.out.println("Utility.getInboxAccount: getAccount failed, returning: " + nReturn);
            return nReturn;
        }
        // --------------------------

        final int nReturn2 = Helpers.getInboxLowLevel(serverID, nymID, accountID, bWasSentInbox, bForceDownload);

        if (!bWasSentInbox.getBooleanValue() && // If it wasn't sent, and 0 was returned, that means
                (0 == nReturn2)) // no error: we already have the latest inbox. (Nothing done.)
        {
            return 0;
        }

        if (1 != nReturn2) {
            System.out.println("Utility.getInboxAccount: getInboxLowLevel failed. Returning: " + nReturn2);
        }

        return nReturn2;
    }

    // -1 error
    //  0 Request NOT sent: But NO error, since hash hasn't changed.  (bWasSent=false)
    //  0 Request WAS sent, reply WAS received: success == false.  (bWasSent=true)
    //  1 reply received: success == true. 
    //  bWasSent gets set to TRUE once the message is confirmed as sent.
    //
    public static int getInboxLowLevel(String serverID, String nymID, String accountID, OTBool bWasSent) {
        return Helpers.getInboxLowLevel(serverID, nymID, accountID, bWasSent, false); // bForce defaults to FALSE
    }

    public static int getInboxLowLevel(String serverID, String nymID, String accountID, OTBool bWasSent, boolean bForce) // bForce defaults to FALSE
    {
        bWasSent.setBooleanValue(false);
        //-------------------------------------------------
        //
        // Use OT_API_GetAccountWallet_InboxHash(ACCT_ID) to see the server's most recent inbox hash (on the OTAccount for that box)

        final String strRecentHash = otapiJNI.OTAPI_Basic_GetAccountWallet_InboxHash(accountID);
        final boolean bRecentHash = Helpers.isValid(strRecentHash);
        if (!bRecentHash) {
            System.out.println("Utility.getInboxLowLevel: Warning: Unable to retrieve recent cached copy of server-side "
                    + "InboxHash from client-side nym (perhaps he's never downloaded it before?)\n\n");
        }
        //-------------------------------------------------
        //
        // Use OT_API_GetNym_InboxHash(ACCT_ID, NYM_ID) to see the client's copy of the inbox hash,
        // from whenever the client last actually downloaded the inbox.

        String strLocalHash = otapiJNI.OTAPI_Basic_GetNym_InboxHash(accountID, nymID);
        boolean bLocalHash = Helpers.isValid(strLocalHash);
        if (!bLocalHash) {
            System.out.println("Utility.getInboxLowLevel: Warning: Unable to retrieve client-side InboxHash "
                    + "for:\n accountID: " + accountID + "\n nymID: " + nymID);
        }
        //-------------------------------------------------
        if (!bForce) {
            if (bLocalHash
                    && bRecentHash
                    && strRecentHash.equals(strLocalHash)) // the hashes match -- no need to download anything.
            {
                System.out.println("Utility.getInboxLowLevel: The hashes already match (skipping Inbox download.)");
                return 0;
            }
        }
        // ************************************************************************************

        // Now that we dealt with the Inbox Hash, let's do the download!!
        //
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
        // --------------------------------------------------------------------
        final int nRequestNum = otapiJNI.OTAPI_Basic_getInbox(serverID, nymID, accountID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getInboxLowLevel: ERROR, not supported. (-2 was returned.)");
                return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getInboxLowLevel: Failed to send getInbox message due to error.");
                return (-1);
            case (0):
                System.out.println("Utility.getInboxLowLevel: Didn't send getInbox message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return (-1); // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getInbox. Therefore I treat it as an error.
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getInboxLowLevel: Unexpected request number: " + nRequestNum);
                    return (-1);
                }
                // -------------------------
                break; // SUCCESS!
        }
        bWasSent.setBooleanValue(true);
        // ***************************************************
        //
        //
        final int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getInboxLowLevel");
//        System.out.println("IN Utility.getInboxLowLevel " + Utility.getLastReplyReceived());

        final boolean bInbox = ((1 == nReturn) ? true : false);

        // BY this point, we definitely have the request number, which means the
        // message was actually SENT. (At least.) This also means we can use nRequestNum
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //
        // ----------------------------------------------
//        final int nRemovedSentMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        // NOTE: The above call is unnecessary, since a successful process means
//        // we already received the successful server reply, and OT's "ProcessServerReply"
//        // already removed the sent message from the sent buffer (so no need to do that here.)
//       
//        if (nRemovedSentMsg < 1)
//        {
//            System.out.println("Utility.getInboxLowLevel: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedSentMsg);
//        }
        // ----------------------------------------------

        // ***************************************************
        // Now let's make sure we have all the box receipts for this outbox.
        // (They will be needed when it is used for something.)
        //
        if (bInbox && !Helpers.insureHaveAllBoxReceipts(serverID, nymID, accountID, 1)) // <===== nBoxType = 1 aka INBOX
        {
            System.out.println("Utility.getInboxLowLevel: getInbox succeeded, but then insureHaveAllBoxReceipts failed. (I give up.)");
            return (-1);
        }
        // ***************************************************

        return nReturn;
    }

    // ************************************************************************
    // -1 error
    //  0 Request NOT sent: But NO error, since hash hasn't changed.  (bWasSent=false)
    //  0 Request WAS sent, reply WAS received: success == false.  (bWasSent=true)
    //  1 reply received: success == true. 
    //  bWasSent gets set to TRUE once the message is confirmed as sent.
    //
    public static int getOutboxLowLevel(String serverID, String nymID, String accountID, OTBool bWasSent) {
        return Helpers.getOutboxLowLevel(serverID, nymID, accountID, bWasSent, false); // bForce defaults to FALSE
    }

    public static int getOutboxLowLevel(String serverID, String nymID, String accountID, OTBool bWasSent, boolean bForce) // bForce defaults to FALSE
    {
        bWasSent.setBooleanValue(false);

        //-------------------------------------------------
        //
        // Use OT_API_GetAccountWallet_OutboxHash(ACCT_ID) to see the server's most recent outbox hash (on the OTAccount for that box)

        final String strRecentHash = otapiJNI.OTAPI_Basic_GetAccountWallet_OutboxHash(accountID);
        final boolean bRecentHash = Helpers.isValid(strRecentHash);
        if (!bRecentHash) {
            System.out.println("Utility.getOutboxLowLevel: Warning: Unable to retrieve recent cached copy of server-side "
                    + "OutboxHash from client-side nym (perhaps he's never downloaded it before?)\n\n");
        }
        //-------------------------------------------------
        //
        // Use OT_API_GetNym_OutboxHash(ACCT_ID, NYM_ID) to see the client's copy of the outbox hash,
        // from whenever the client last actually downloaded the outbox.

        String strLocalHash = otapiJNI.OTAPI_Basic_GetNym_OutboxHash(accountID, nymID);
        boolean bLocalHash = Helpers.isValid(strLocalHash);
        if (!bLocalHash) {
            System.out.println("Utility.getOutboxLowLevel: Warning: Unable to retrieve client-side OutboxHash "
                    + "for:\n accountID: " + accountID + "\n nymID: " + nymID);
        }
        //-------------------------------------------------
        if (!bForce) {
            if (bLocalHash
                    && bRecentHash
                    && strRecentHash.equals(strLocalHash)) // the hashes match -- no need to download anything.
            {
                System.out.println("Utility.getOutboxLowLevel: The hashes already match (skipping Outbox download.)");
                return 0;
            }
        }
        // ************************************************************************************

        // Now that we dealt with the Outbox Hash, let's do the download!!
        //
        otapiJNI.OTAPI_Basic_FlushMessageBuffer();
        // --------------------------------------------------------------------
        final int nRequestNum = otapiJNI.OTAPI_Basic_getOutbox(serverID, nymID, accountID); // <===== ATTEMPT TO SEND THE MESSAGE HERE...

        switch (nRequestNum) {
            case (-2):
                System.out.println("Utility.getOutboxLowLevel: ERROR, not supported. (-2 was returned.)");
                return (-1); // -2 is also possible at some future date. (If the request number won't fit in an int, this is returned and then you can retrieve the actual number via a separate call.)
            case (-1):      // if the requestNumber returned by the send-attempt is -1, that means it DIDN'T SEND (error)
                System.out.println("Utility.getOutboxLowLevel: Failed to send getOutbox message due to error.");
                return (-1);
            case (0):
                System.out.println("Utility.getOutboxLowLevel: Didn't send getOutbox message, but NO error occurred, either. (In this case, SHOULD NEVER HAPPEN. Treating as Error.)");
                return (-1); // Even though '0' MEANS "didn't send, but no error" by convention in many places, it is actually an impossible return value from getOutbox. Therefore I treat it as an error.
            default:
                if (nRequestNum < 0) {
                    System.out.println("Utility.getOutboxLowLevel: Unexpected request number: " + nRequestNum);
                    return (-1);
                }
                // -------------------------
                break; // SUCCESS!
        }
        bWasSent.setBooleanValue(true);
        // ***************************************************
        //
        //
        final int nReturn = Helpers.receiveReplySuccessLowLevel(serverID, nymID, nRequestNum, "Utility.getOutboxLowLevel");
//        System.out.println("IN Utility.getOutboxLowLevel " + Utility.getLastReplyReceived());

        final boolean bOutbox = ((1 == nReturn) ? true : false);

        // BY this point, we definitely have the request number, which means the
        // message was actually SENT. (At least.) This also means we can use nRequestNum
        // later to query for a copy of that sent message.
        // Let's go ahead, in this case, and remove that now:
        //
        // ----------------------------------------------
//        final int nRemovedSentMsg = otapiJNI.OTAPI_Basic_RemoveSentMessage(Integer.toString(nRequestNum), serverID, nymID);
//
//        // NOTE: The above call is unnecessary, since a successful process means
//        // we already received the successful server reply, and OT's "ProcessServerReply"
//        // already removed the sent message from the sent buffer (so no need to do that here.)
//       
//        if (nRemovedSentMsg < 1)
//        {
//            System.out.println("Utility.getOutboxLowLevel: ERROR: OT_API_RemoveSentMessage returned: " + nRemovedSentMsg);
//        }
        // ----------------------------------------------

        // ***************************************************
        // Now let's make sure we have all the box receipts for this outbox.
        // (They will be needed when it is used for something.)
        //
        if (bOutbox && !Helpers.insureHaveAllBoxReceipts(serverID, nymID, accountID, 2)) // <===== nBoxType = 2 aka OUTBOX
        {
            System.out.println("Utility.getOutboxLowLevel: getOutbox succeeded, but then insureHaveAllBoxReceipts failed. (I give up.)");
            return (-1);
        }
        // ***************************************************

        return nReturn;
    }

    public interface ReturnAction {

        public String getAction();

        public void returnAction(String returnValue);
    }
}
TOP

Related Classes of com.moneychanger.core.util.Helpers

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.