<commit_before>// Copyright (c) 2009-2012 Bitcoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iostream>
#include <fstream>

#include "init.h" // for pwalletMain
#include "rpcserver.h"
#include "rpcprotocol.h"
#include "ui_interface.h"
#include "base58.h"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

using namespace std;

void EnsureWalletIsUnlocked();

namespace bt = boost::posix_time;

// Extended DecodeDumpTime implementation, see this page for details:
// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost
const std::locale formats[] = {
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};

const size_t formats_n = sizeof(formats)/sizeof(formats[0]);

std::time_t pt_to_time_t(const bt::ptime& pt)
{
    bt::ptime timet_start(boost::gregorian::date(1970,1,1));
    bt::time_duration diff = pt - timet_start;
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}

int64_t DecodeDumpTime(const std::string& s)
{
    bt::ptime pt;

    for(size_t i=0; i<formats_n; ++i)
    {
        std::istringstream is(s);
        is.imbue(formats[i]);
        is >> pt;
        if(pt != bt::ptime()) break;
    }

    return pt_to_time_t(pt);
}

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned char c : str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

class CTxDump
{
public:
    CBlockIndex *pindex;
    int64_t nValue;
    bool fSpent;
    CWalletTx* ptx;
    int nOut;
    CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
    {
        pindex = NULL;
        nValue = 0;
        fSpent = false;
        this->ptx = ptx;
        this->nOut = nOut;
    }
};

UniValue importprivkey(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
        "importprivkey <gridcoinprivkey> [label] [bool:rescan]\n"
        "\n"
        "[label] -------> Optional; Label for imported address\n"
        "[bool:rescan] -> Optional; Default true\n"
        "WARNING: if true rescan of blockchain will occur. This could take up to 20 minutes.\n"
        "\n"
        "Adds a private key (as returned by dumpprivkey) to your wallet\n");

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

     // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    CKey key;
    bool fGood = vchSecret.SetString(strSecret);

    // If CBitcoinSecret key decode failed, try to decode the key as hex
    if(!fGood)
    {
        auto vecsecret = ParseHex(strSecret);
        if(!key.SetPrivKey(CPrivKey(vecsecret.begin(),vecsecret.end())))
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
    }
    else
    {
    bool fCompressed;
    CSecret secret = vchSecret.GetSecret(fCompressed);
    key.SetSecret(secret, fCompressed);
    }

    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    CKeyID vchAddress = key.GetPubKey().GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return NullUniValue;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKey(key))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
        pwalletMain->SetAddressBookName(vchAddress, strLabel);

        if (fRescan)
        {
            pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return NullUniValue;
}

UniValue importwallet(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet <filename>\n"
            "\n"
            "<filename> -> filename of the wallet to import\n"
            "\n"
            "Imports keys from a wallet dump file (see dumpwallet)\n");

    ifstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    LOCK2(cs_main, pwalletMain->cs_wallet);

    EnsureWalletIsUnlocked();

    int64_t nTimeBegin = pindexBest->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;

        bool fCompressed;
        CKey key;
        CSecret secret = vchSecret.GetSecret(fCompressed);
        key.SetSecret(secret, fCompressed);
        CKeyID keyid = key.GetPubKey().GetID();

        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKey(key)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBookName(keyid, strLabel);
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = pindexBest;
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
        pwalletMain->nTimeFirstKey = nTimeBegin;

    LogPrintf("Rescanning last %i blocks", pindexBest->nHeight - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return NullUniValue;
}

UniValue dumpprivkey(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey <gridcoinaddress>\n"
            "<gridcoinaddress> -> Address of requested key\n"
            "\n"
            "Reveals the private key corresponding to <gridcoinaddress>\n");

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Gridcoin address");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    LOCK2(cs_main, pwalletMain->cs_wallet);

    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CSecret vchSecret;
    bool fCompressed;
    if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret, fCompressed).ToString();
}

UniValue dumpwallet(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet <filename>\n"
            "\n"
            "<filename> -> filename to dump wallet to\n"
            "\n"
            "Dumps all wallet keys in a human-readable format into the specified file.\n"
            "If a path is not specified in the filename, the data directory is used.");

    EnsureWalletIsUnlocked();

    boost::filesystem::path PathForDump = boost::filesystem::path(params[0].get_str());
    boost::filesystem::path DefaultPathDataDir = GetDataDir();

    // If provided filename does not have a path, then append parent path, otherwise leave alone.
    if (PathForDump.parent_path().empty())
        PathForDump = DefaultPathDataDir / PathForDump;

    ofstream file;
    file.open(PathForDump.string().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;

    std::set<CKeyID> setKeyPool;

    LOCK2(cs_main, pwalletMain->cs_wallet);

    pwalletMain->GetKeyBirthTimes(mapKeyBirth);

    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by Gridcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(pindexBest->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();
        bool IsCompressed;

        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid]), strAddr);
            } else if (setKeyPool.count(keyid)) {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, strAddr);
            } else {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return NullUniValue;
}
<commit_msg>Have importwallet file path default to datadir<commit_after>// Copyright (c) 2009-2012 Bitcoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iostream>
#include <fstream>

#include "init.h" // for pwalletMain
#include "rpcserver.h"
#include "rpcprotocol.h"
#include "ui_interface.h"
#include "base58.h"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

using namespace std;

void EnsureWalletIsUnlocked();

namespace bt = boost::posix_time;

// Extended DecodeDumpTime implementation, see this page for details:
// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost
const std::locale formats[] = {
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};

const size_t formats_n = sizeof(formats)/sizeof(formats[0]);

std::time_t pt_to_time_t(const bt::ptime& pt)
{
    bt::ptime timet_start(boost::gregorian::date(1970,1,1));
    bt::time_duration diff = pt - timet_start;
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}

int64_t DecodeDumpTime(const std::string& s)
{
    bt::ptime pt;

    for(size_t i=0; i<formats_n; ++i)
    {
        std::istringstream is(s);
        is.imbue(formats[i]);
        is >> pt;
        if(pt != bt::ptime()) break;
    }

    return pt_to_time_t(pt);
}

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned char c : str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

class CTxDump
{
public:
    CBlockIndex *pindex;
    int64_t nValue;
    bool fSpent;
    CWalletTx* ptx;
    int nOut;
    CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
    {
        pindex = NULL;
        nValue = 0;
        fSpent = false;
        this->ptx = ptx;
        this->nOut = nOut;
    }
};

UniValue importprivkey(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
        "importprivkey <gridcoinprivkey> [label] [bool:rescan]\n"
        "\n"
        "[label] -------> Optional; Label for imported address\n"
        "[bool:rescan] -> Optional; Default true\n"
        "WARNING: if true rescan of blockchain will occur. This could take up to 20 minutes.\n"
        "\n"
        "Adds a private key (as returned by dumpprivkey) to your wallet\n");

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

     // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    CKey key;
    bool fGood = vchSecret.SetString(strSecret);

    // If CBitcoinSecret key decode failed, try to decode the key as hex
    if(!fGood)
    {
        auto vecsecret = ParseHex(strSecret);
        if(!key.SetPrivKey(CPrivKey(vecsecret.begin(),vecsecret.end())))
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
    }
    else
    {
    bool fCompressed;
    CSecret secret = vchSecret.GetSecret(fCompressed);
    key.SetSecret(secret, fCompressed);
    }

    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    CKeyID vchAddress = key.GetPubKey().GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return NullUniValue;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKey(key))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
        pwalletMain->SetAddressBookName(vchAddress, strLabel);

        if (fRescan)
        {
            pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return NullUniValue;
}

UniValue importwallet(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet <filename>\n"
            "\n"
            "<filename> -> filename of the wallet to import\n"
            "\n"
            "Imports keys from a wallet dump file (see dumpwallet)\n"
            "If a path is not specified in the filename, the data directory is used.");

    boost::filesystem::path PathForImport = boost::filesystem::path(params[0].get_str());
    boost::filesystem::path DefaultPathDataDir = GetDataDir();

    // If provided filename does not have a path, then append parent path, otherwise leave alone.
    if (PathForImport.parent_path().empty())
        PathForImport = DefaultPathDataDir / PathForImport;

    ifstream file;
    file.open(PathForImport.string().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    LOCK2(cs_main, pwalletMain->cs_wallet);

    EnsureWalletIsUnlocked();

    int64_t nTimeBegin = pindexBest->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;

        bool fCompressed;
        CKey key;
        CSecret secret = vchSecret.GetSecret(fCompressed);
        key.SetSecret(secret, fCompressed);
        CKeyID keyid = key.GetPubKey().GetID();

        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKey(key)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBookName(keyid, strLabel);
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = pindexBest;
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
        pwalletMain->nTimeFirstKey = nTimeBegin;

    LogPrintf("Rescanning last %i blocks", pindexBest->nHeight - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return NullUniValue;
}

UniValue dumpprivkey(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey <gridcoinaddress>\n"
            "<gridcoinaddress> -> Address of requested key\n"
            "\n"
            "Reveals the private key corresponding to <gridcoinaddress>\n");

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Gridcoin address");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    LOCK2(cs_main, pwalletMain->cs_wallet);

    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CSecret vchSecret;
    bool fCompressed;
    if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret, fCompressed).ToString();
}

UniValue dumpwallet(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet <filename>\n"
            "\n"
            "<filename> -> filename to dump wallet to\n"
            "\n"
            "Dumps all wallet keys in a human-readable format into the specified file.\n"
            "If a path is not specified in the filename, the data directory is used.");

    EnsureWalletIsUnlocked();

    boost::filesystem::path PathForDump = boost::filesystem::path(params[0].get_str());
    boost::filesystem::path DefaultPathDataDir = GetDataDir();

    // If provided filename does not have a path, then append parent path, otherwise leave alone.
    if (PathForDump.parent_path().empty())
        PathForDump = DefaultPathDataDir / PathForDump;

    ofstream file;
    file.open(PathForDump.string().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;

    std::set<CKeyID> setKeyPool;

    LOCK2(cs_main, pwalletMain->cs_wallet);

    pwalletMain->GetKeyBirthTimes(mapKeyBirth);

    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by Gridcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(pindexBest->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();
        bool IsCompressed;

        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid]), strAddr);
            } else if (setKeyPool.count(keyid)) {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, strAddr);
            } else {
                CSecret secret = key.GetSecret(IsCompressed);
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(secret, IsCompressed).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return NullUniValue;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2012 Bitcoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iostream>
#include <fstream>

#include "init.h" // for pwalletMain
#include "rpcserver.h"
#include "ui_interface.h"
#include "base58.h"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string.hpp>

using namespace json_spirit;
using namespace std;

void EnsureWalletIsUnlocked();

namespace bt = boost::posix_time;

// Extended DecodeDumpTime implementation, see this page for details:
// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost
const std::locale formats[] = {
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};

const size_t formats_n = sizeof(formats)/sizeof(formats[0]);

std::time_t pt_to_time_t(const bt::ptime& pt)
{
    bt::ptime timet_start(boost::gregorian::date(1970,1,1));
    bt::time_duration diff = pt - timet_start;
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}

int64_t DecodeDumpTime(const std::string& s)
{
    bt::ptime pt;

    for(size_t i=0; i<formats_n; ++i)
    {
        std::istringstream is(s);
        is.imbue(formats[i]);
        is >> pt;
        if(pt != bt::ptime()) break;
    }

    return pt_to_time_t(pt);
}

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    BOOST_FOREACH(unsigned char c, str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

class CTxDump
{
public:
    CBlockIndex *pindex;
    int64_t nValue;
    bool fSpent;
    CWalletTx* ptx;
    int nOut;
    CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
    {
        pindex = NULL;
        nValue = 0;
        fSpent = false;
        this->ptx = ptx;
        this->nOut = nOut;
    }
};

Value importprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
            "importprivkey <iadixcoinprivkey> [label] [rescan=true]\n"
            "Adds a private key (as returned by dumpprivkey) to your wallet.");

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

    // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    bool fGood = vchSecret.SetString(strSecret);

    if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    CKey key = vchSecret.GetKey();
    CPubKey pubkey = key.GetPubKey();
    CKeyID vchAddress = pubkey.GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();
        pwalletMain->SetAddressBookName(vchAddress, strLabel);

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return Value::null;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKeyPubKey(key, pubkey))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'

        if (fRescan) {
            pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return Value::null;
}

Value importwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet <filename>\n"
            "Imports keys from a wallet dump file (see dumpwallet).");

    EnsureWalletIsUnlocked();

    ifstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    int64_t nTimeBegin = pindexBest->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;
        CKey key = vchSecret.GetKey();
        CPubKey pubkey = key.GetPubKey();
        CKeyID keyid = pubkey.GetID();
        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKey(key)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBookName(keyid, strLabel);
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = pindexBest;
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
        pwalletMain->nTimeFirstKey = nTimeBegin;

    LogPrintf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return Value::null;
}
Value exportwallet(const Array& params, bool fHelp)
{
	CKey			newKey;
	CKeyID			keyID;
	CKey			vchSecret;
	Value			ret;
	string			strAddress;
	CBitcoinAddress address;
	CWalletDB		*cdb;
	CWallet			*pwalletNew;
	DBErrors		nLoadWalletRet;

	if (fHelp || params.size() != 3)
		throw runtime_error(
		"exportwallet <address> <filename> <secret>\n"
		"Export a wallet keys in a wallet file.");

	EnsureWalletIsUnlocked();

	strAddress = params[0].get_str();

	if (!address.SetString(strAddress))
		throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IadixCoin address");
	if (fWalletUnlockStakingOnly)
		throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
	if (!address.GetKeyID(keyID))
		throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
	if (!pwalletMain->GetKey(keyID, vchSecret))
		throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
	newKey			=	CBitcoinSecret(vchSecret).GetKey();
	pwalletNew		= new  CWallet			  (params[1].get_str());
	cdb				= new  CWalletDB		  (params[1].get_str(), "cr+");
	nLoadWalletRet	= cdb->LoadWallet		  (pwalletNew);

	if (nLoadWalletRet != DB_LOAD_OK)
	{
		throw JSONRPCError(RPC_WALLET_ERROR, "Error creating new wallet");
		return Value::null;
	}
	pwalletNew->AddKey					  (newKey);
	pwalletNew->ScanForWalletTransactions (pindexGenesisBlock, true);
	bitdb.dbenv.dbrename				  (NULL, params[1].get_str().c_str(), NULL, "wallet.dat", DB_AUTO_COMMIT);
	cdb->Close							  ();
	//pwalletNew->SetDefaultKey			  (newKey.GetPubKey());
	pwalletNew->EncryptWallet			  (params[2].get_str().c_str());

	bitdb.CloseDb						  (params[1].get_str());
	delete cdb;
	delete pwalletNew;

	ret = (GetDataDir() / params[1].get_str()).string();

	boost::filesystem::permissions(ret.get_str(), boost::filesystem::add_perms | boost::filesystem::others_read | boost::filesystem::others_write | boost::filesystem::group_read | boost::filesystem::group_write);
	return ret;
}

Value dumpprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey <iadixcoinaddress>\n"
            "Reveals the private key corresponding to <iadixcoinaddress>.");

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IadixCoin address");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CKey vchSecret;
    if (!pwalletMain->GetKey(keyID, vchSecret))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret).ToString();
}

Value dumpwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet <filename>\n"
            "Dumps all wallet keys in a human-readable format.");

    EnsureWalletIsUnlocked();

    ofstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;

    std::set<CKeyID> setKeyPool;

    pwalletMain->GetKeyBirthTimes(mapKeyBirth);

    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by IadixCoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(pindexBest->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();

        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid]), strAddr);
            } else if (setKeyPool.count(keyid)) {
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            } else {
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return Value::null;
}
<commit_msg>fix export<commit_after>// Copyright (c) 2009-2012 Bitcoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iostream>
#include <fstream>

#include "init.h" // for pwalletMain
#include "rpcserver.h"
#include "ui_interface.h"
#include "base58.h"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string.hpp>

using namespace json_spirit;
using namespace std;

void EnsureWalletIsUnlocked();

namespace bt = boost::posix_time;

// Extended DecodeDumpTime implementation, see this page for details:
// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost
const std::locale formats[] = {
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};

const size_t formats_n = sizeof(formats)/sizeof(formats[0]);

std::time_t pt_to_time_t(const bt::ptime& pt)
{
    bt::ptime timet_start(boost::gregorian::date(1970,1,1));
    bt::time_duration diff = pt - timet_start;
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}

int64_t DecodeDumpTime(const std::string& s)
{
    bt::ptime pt;

    for(size_t i=0; i<formats_n; ++i)
    {
        std::istringstream is(s);
        is.imbue(formats[i]);
        is >> pt;
        if(pt != bt::ptime()) break;
    }

    return pt_to_time_t(pt);
}

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    BOOST_FOREACH(unsigned char c, str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

class CTxDump
{
public:
    CBlockIndex *pindex;
    int64_t nValue;
    bool fSpent;
    CWalletTx* ptx;
    int nOut;
    CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
    {
        pindex = NULL;
        nValue = 0;
        fSpent = false;
        this->ptx = ptx;
        this->nOut = nOut;
    }
};

Value importprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
            "importprivkey <iadixcoinprivkey> [label] [rescan=true]\n"
            "Adds a private key (as returned by dumpprivkey) to your wallet.");

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

    // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    bool fGood = vchSecret.SetString(strSecret);

    if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");

    CKey key = vchSecret.GetKey();
    CPubKey pubkey = key.GetPubKey();
    CKeyID vchAddress = pubkey.GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();
        pwalletMain->SetAddressBookName(vchAddress, strLabel);

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return Value::null;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKeyPubKey(key, pubkey))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'

        if (fRescan) {
            pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return Value::null;
}

Value importwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet <filename>\n"
            "Imports keys from a wallet dump file (see dumpwallet).");

    EnsureWalletIsUnlocked();

    ifstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    int64_t nTimeBegin = pindexBest->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;
        CKey key = vchSecret.GetKey();
        CPubKey pubkey = key.GetPubKey();
        CKeyID keyid = pubkey.GetID();
        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKey(key)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBookName(keyid, strLabel);
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = pindexBest;
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
        pwalletMain->nTimeFirstKey = nTimeBegin;

    LogPrintf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return Value::null;
}
Value exportwallet(const Array& params, bool fHelp)
{
	CKey			newKey;
	CKeyID			keyID;
	CKey			vchSecret;
	Value			ret;
	string			strAddress;
	CBitcoinAddress address;
	CWalletDB		*cdb;
	CWallet			*pwalletNew;
	DBErrors		nLoadWalletRet;

	if (fHelp || params.size() != 3)
		throw runtime_error(
		"exportwallet <address> <filename> <secret>\n"
		"Export a wallet keys in a wallet file.");

	EnsureWalletIsUnlocked();

	strAddress = params[0].get_str();

	if (!address.SetString(strAddress))
		throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IadixCoin address");
	if (fWalletUnlockStakingOnly)
		throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
	if (!address.GetKeyID(keyID))
		throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
	if (!pwalletMain->GetKey(keyID, vchSecret))
		throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
	newKey			=	CBitcoinSecret(vchSecret).GetKey();
	pwalletNew		= new  CWallet			  (params[1].get_str());
	cdb				= new  CWalletDB		  (params[1].get_str(), "cr+");
	nLoadWalletRet	= cdb->LoadWallet		  (pwalletNew);

	if (nLoadWalletRet != DB_LOAD_OK)
	{
		throw JSONRPCError(RPC_WALLET_ERROR, "Error creating new wallet");
		return Value::null;
	}
	pwalletNew->AddKey					  (newKey);
	pwalletNew->ScanForWalletTransactions (pindexGenesisBlock, true);
	//bitdb.dbenv.dbrename				  (NULL, params[1].get_str().c_str(), NULL, "wallet.dat", DB_AUTO_COMMIT);
	cdb->Close							  ();
	//pwalletNew->SetDefaultKey			  (newKey.GetPubKey());
	pwalletNew->EncryptWallet			  (params[2].get_str().c_str());

	//bitdb.CloseDb						  (params[1].get_str());
	delete cdb;
	delete pwalletNew;

	ret = (GetDataDir() / params[1].get_str()).string();

	boost::filesystem::permissions(ret.get_str(), boost::filesystem::add_perms | boost::filesystem::others_read | boost::filesystem::others_write | boost::filesystem::group_read | boost::filesystem::group_write);
	return ret;
}

Value dumpprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey <iadixcoinaddress>\n"
            "Reveals the private key corresponding to <iadixcoinaddress>.");

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IadixCoin address");
    if (fWalletUnlockStakingOnly)
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for staking only.");
    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CKey vchSecret;
    if (!pwalletMain->GetKey(keyID, vchSecret))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret).ToString();
}

Value dumpwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet <filename>\n"
            "Dumps all wallet keys in a human-readable format.");

    EnsureWalletIsUnlocked();

    ofstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;

    std::set<CKeyID> setKeyPool;

    pwalletMain->GetKeyBirthTimes(mapKeyBirth);

    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by IadixCoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(pindexBest->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();

        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid]), strAddr);
            } else if (setKeyPool.count(keyid)) {
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            } else {
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return Value::null;
}
<|endoftext|>


<commit_before>/*
  Copyright © 2012, 2015  Vinícius dos Santos Oliveira

  This file is part of BlowThemAll.

  BlowThemAll is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  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 Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */

#include "priv/rpcnode.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>

using namespace Tufao;

RpcNode::RpcNode(QObject *parent) :
    QObject(parent),
    priv(new Priv)
{}

RpcNode::RpcNode(AbstractMessageSocket *socket, QObject *parent) :
    QObject(parent),
    priv(new Priv(socket))
{
    if (priv->socket) {
        connect(priv->socket, &AbstractMessageSocket::newMessage,
                this, &RpcNode::handleMessage);
    }
}

RpcNode::~RpcNode()
{
    delete priv;
}

QObject *RpcNode::methods()
{
    return priv->methods;
}

void RpcNode::setMethods(QObject *object)
{
    priv->methods = object;
}

AbstractMessageSocket *RpcNode::messageSocket()
{
    return priv->socket;
}

void RpcNode::setMessageSocket(AbstractMessageSocket *socket)
{
    if (priv->socket) {
        disconnect(priv->socket, &AbstractMessageSocket::newMessage,
                   this, &RpcNode::handleMessage);
    }

    priv->socket = socket;

    if (priv->socket) {
        connect(priv->socket, &AbstractMessageSocket::newMessage,
                this, &RpcNode::handleMessage);
    }
}

QJsonValue RpcNode::callWith(const QString &remoteMethod, const QJsonArray &args,
                          std::function<void(QJsonValue)> handler)
{
    return priv->callWith(remoteMethod, args, handler);
}

QJsonValue RpcNode::callWith(const QString &remoteMethod, const QJsonObject &args,
                          std::function<void(QJsonValue)> handler)
{
    return priv->callWith(remoteMethod, args, handler);
}

void RpcNode::call(const QString &remoteMethod, const QJsonArray &args)
{
    priv->call(remoteMethod, args);
}

void RpcNode::call(const QString &remoteMethod, const QJsonObject &args)
{
    priv->call(remoteMethod, args);
}

void RpcNode::handleMessage(const QByteArray &msg)
{
    QJsonParseError err;
    auto object = QJsonDocument::fromJson(msg, &err);

    if (err.error != QJsonParseError::NoError) {
        static const char data[] = "{\"jsonrpc\": \"2.0\", \"error\": "
                                   "{\"code\": -32700,"
                                   " \"message\": \"Parse error\"}, "
                                   "\"id\": null}";
        auto invalidJson = QByteArray::fromRawData(data, sizeof(data) - 1);
        priv->socket->sendMessage(invalidJson);
        return;
    }

    if (isRequestMessage(object)) {
        handleRequest(object);
    } else if (isResponseMessage(object)) {
        handleResponse(object.object());
    } else {
        static QByteArray invalidJsonRpc = "{\"jsonrpc\": \"2.0\", \"error\": "
                "{\"code\": -32600, \"message\": \"Invalid JSON-RPC.\"}, "
                "\"id\": null}";
        priv->socket->sendMessage(invalidJsonRpc);
    }
}

inline void RpcNode::handleRequest(const QJsonDocument &object)
{
    if (object.isObject()) {
        QJsonObject response = processRequest(object.object());
        if (!response.isEmpty()) {
            priv->socket->sendMessage(QJsonDocument{response}
                                      .toJson(QJsonDocument::Compact));
        }
    } else if (object.isArray()) {
        QJsonArray batch = object.array();
        if (batch.isEmpty()) {
            priv->socket->sendMessage("{\"jsonrpc\": \"2.0\", \"error\": "
                                      "{\"code\": -32600, "
                                      "\"message\": \"Invalid JSON-RPC.\"}, "
                                      "\"id\": null}");
            return;
        }

        QJsonArray response;
        for (const QJsonValue &i: batch) {
            QJsonObject singleResponse = processRequest(i.toObject());
            if (!singleResponse.isEmpty())
                response.push_back(singleResponse);
        }
        priv->socket->sendMessage(QJsonDocument{response}
                                  .toJson(QJsonDocument::Compact));
    } else {
        priv->socket->sendMessage("{\"jsonrpc\": \"2.0\", \"error\": "
                                  "{\"code\": -32600, "
                                  "\"message\": \"Invalid JSON-RPC.\"}, "
                                  "\"id\": null}");
    }
}

inline void RpcNode::handleResponse(const QJsonObject &object)
{
    if (!object.contains("id"))
        return;

    if (object.contains("error")) {
        QJsonValue error = object["error"];
        if (!error.isObject())
            return;

        emit this->error(object["id"],
                         QJsonDocument{error.toObject()}
                         .toJson(QJsonDocument::Compact));
        return;
    }

    QJsonValue key = object["id"];
    if (!object.contains("result") || !priv->calls.contains(key))
        return;

    priv->calls[key](object["result"]);
    priv->calls.remove(key);
}

QJsonObject RpcNode::processRequest(const QJsonObject &request)
{
    QJsonObject reply({{QString("jsonrpc"), QJsonValue("2.0")}});

    if (!request.contains("method")) {
        reply["error"] = QJsonObject{{{"code", -32600},
                                      {"message", "Invalid Request."}}};
        reply["id"] = QJsonValue{};
        return reply;
    }

    QJsonValue method = request["method"];

    if (!method.isString()) {
                reply["error"] = QJsonObject{{{"code", -32600},
                                             {"message", "Invalid Request."}}};
                reply["id"] = QJsonValue{};
                return reply;
    }

    if (request.contains("params")) {
        QJsonValue params = request["params"];
        if (!params.isArray() && !params.isObject() && !params.isNull()) {
                    reply["error"] = QJsonObject{{
                        {"code", -32600},
                        {"message", "Invalid Request."}
                    }};
                    reply["id"] = QJsonValue{};
                    return reply;
        }
    }

    if (request.contains("id")) {
        QJsonValue id = request["id"];
        if (!id.isString() && !id.isDouble() && !id.isNull()) {
            reply["error"] = QJsonObject{{{"code", -32600},
                                         {"message", "Invalid Request."}}};
            reply["id"] = QJsonValue{};
            return reply;
        }
        reply["id"] = id;
    }

    QPair<QJsonValue, QJsonObject> result
        = processReply(method.toString(), request.value("params"));
    if (!reply.contains("id"))
        return {};

    if (!result.second.isEmpty()) {
        reply["error"] = result.second;
        return reply;
    }

    reply["result"] = result.first;
    return reply;
}

inline QPair<QJsonValue, QJsonObject> RpcNode::processReply(const QString &method,
                                                         const QJsonValue &args)
{
    if (!priv->methods
            || (!args.isArray() && !args.isUndefined())) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    QJsonArray params = args.toArray();

    // 10 is the max number of args supported by moc/qobject/...
    if (params.size() > 10) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    const QMetaObject *metaObject = priv->methods->metaObject();
    int i = indexOfMethod(metaObject, method, params);

    if (i == -1) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    QMetaMethod metaMethod = metaObject->method(i);

    {
        int size = params.size();
        QVector<ArgumentHandler> argsHandler;
        QVector<QGenericArgument> args;

        argsHandler.resize(size);
        args.reserve(size);

        for (int i = 0;i != size;++i)
            args.push_back(argsHandler[i].convert(params[i]));

        ReturnHandler retHandler;
        QGenericReturnArgument ret = retHandler.convert(metaMethod.typeName());

        metaMethod.invoke(priv->methods, Qt::DirectConnection, ret,
                          (0 < size) ? args[0] : QGenericArgument(),
                          (1 < size) ? args[1] : QGenericArgument(),
                          (2 < size) ? args[2] : QGenericArgument(),
                          (3 < size) ? args[3] : QGenericArgument(),
                          (4 < size) ? args[4] : QGenericArgument(),
                          (5 < size) ? args[5] : QGenericArgument(),
                          (6 < size) ? args[6] : QGenericArgument(),
                          (7 < size) ? args[7] : QGenericArgument(),
                          (8 < size) ? args[8] : QGenericArgument(),
                          (9 < size) ? args[9] : QGenericArgument());

        return qMakePair(QJsonValue{retHandler}, QJsonObject{});
    }
}
<commit_msg>fix: build issue with clang<commit_after>/*
  Copyright © 2012, 2015  Vinícius dos Santos Oliveira

  This file is part of BlowThemAll.

  BlowThemAll is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  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 Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */

#include "priv/rpcnode.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>

using namespace Tufao;

RpcNode::RpcNode(QObject *parent) :
    QObject(parent),
    priv(new Priv)
{}

RpcNode::RpcNode(AbstractMessageSocket *socket, QObject *parent) :
    QObject(parent),
    priv(new Priv(socket))
{
    if (priv->socket) {
        connect(priv->socket, &AbstractMessageSocket::newMessage,
                this, &RpcNode::handleMessage);
    }
}

RpcNode::~RpcNode()
{
    delete priv;
}

QObject *RpcNode::methods()
{
    return priv->methods;
}

void RpcNode::setMethods(QObject *object)
{
    priv->methods = object;
}

AbstractMessageSocket *RpcNode::messageSocket()
{
    return priv->socket;
}

void RpcNode::setMessageSocket(AbstractMessageSocket *socket)
{
    if (priv->socket) {
        disconnect(priv->socket, &AbstractMessageSocket::newMessage,
                   this, &RpcNode::handleMessage);
    }

    priv->socket = socket;

    if (priv->socket) {
        connect(priv->socket, &AbstractMessageSocket::newMessage,
                this, &RpcNode::handleMessage);
    }
}

QJsonValue RpcNode::callWith(const QString &remoteMethod, const QJsonArray &args,
                          std::function<void(QJsonValue)> handler)
{
    return priv->callWith(remoteMethod, args, handler);
}

QJsonValue RpcNode::callWith(const QString &remoteMethod, const QJsonObject &args,
                          std::function<void(QJsonValue)> handler)
{
    return priv->callWith(remoteMethod, args, handler);
}

void RpcNode::call(const QString &remoteMethod, const QJsonArray &args)
{
    priv->call(remoteMethod, args);
}

void RpcNode::call(const QString &remoteMethod, const QJsonObject &args)
{
    priv->call(remoteMethod, args);
}

void RpcNode::handleMessage(const QByteArray &msg)
{
    QJsonParseError err;
    auto object = QJsonDocument::fromJson(msg, &err);

    if (err.error != QJsonParseError::NoError) {
        static const char data[] = "{\"jsonrpc\": \"2.0\", \"error\": "
                                   "{\"code\": -32700,"
                                   " \"message\": \"Parse error\"}, "
                                   "\"id\": null}";
        auto invalidJson = QByteArray::fromRawData(data, sizeof(data) - 1);
        priv->socket->sendMessage(invalidJson);
        return;
    }

    if (isRequestMessage(object)) {
        handleRequest(object);
    } else if (isResponseMessage(object)) {
        handleResponse(object.object());
    } else {
        static QByteArray invalidJsonRpc = "{\"jsonrpc\": \"2.0\", \"error\": "
                "{\"code\": -32600, \"message\": \"Invalid JSON-RPC.\"}, "
                "\"id\": null}";
        priv->socket->sendMessage(invalidJsonRpc);
    }
}

inline void RpcNode::handleRequest(const QJsonDocument &object)
{
    if (object.isObject()) {
        QJsonObject response = processRequest(object.object());
        if (!response.isEmpty()) {
            priv->socket->sendMessage(QJsonDocument{response}
                                      .toJson(QJsonDocument::Compact));
        }
    } else if (object.isArray()) {
        QJsonArray batch = object.array();
        if (batch.isEmpty()) {
            priv->socket->sendMessage("{\"jsonrpc\": \"2.0\", \"error\": "
                                      "{\"code\": -32600, "
                                      "\"message\": \"Invalid JSON-RPC.\"}, "
                                      "\"id\": null}");
            return;
        }

        QJsonArray response;
        for (const QJsonValue &i: batch) {
            QJsonObject singleResponse = processRequest(i.toObject());
            if (!singleResponse.isEmpty())
                response.push_back(singleResponse);
        }
        priv->socket->sendMessage(QJsonDocument{response}
                                  .toJson(QJsonDocument::Compact));
    } else {
        priv->socket->sendMessage("{\"jsonrpc\": \"2.0\", \"error\": "
                                  "{\"code\": -32600, "
                                  "\"message\": \"Invalid JSON-RPC.\"}, "
                                  "\"id\": null}");
    }
}

inline void RpcNode::handleResponse(const QJsonObject &object)
{
    if (!object.contains("id"))
        return;

    if (object.contains("error")) {
        QJsonValue error = object["error"];
        if (!error.isObject())
            return;

        emit this->error(object["id"],
                         QJsonDocument{error.toObject()}
                         .toJson(QJsonDocument::Compact));
        return;
    }

    QJsonValue key = object["id"];
    if (!object.contains("result") || !priv->calls.contains(key))
        return;

    priv->calls[key](object["result"]);
    priv->calls.remove(key);
}

QJsonObject RpcNode::processRequest(const QJsonObject &request)
{
    QJsonObject reply({{QString("jsonrpc"), QJsonValue("2.0")}});

    if (!request.contains("method")) {
        reply["error"] = QJsonObject{{{"code", -32600},
                                      {"message", "Invalid Request."}}};
        reply["id"] = QJsonValue{};
        return reply;
    }

    QJsonValue method = request["method"];

    if (!method.isString()) {
                reply["error"] = QJsonObject{{{"code", -32600},
                                             {"message", "Invalid Request."}}};
                reply["id"] = QJsonValue{};
                return reply;
    }

    if (request.contains("params")) {
        QJsonValue params = request["params"];
        if (!params.isArray() && !params.isObject() && !params.isNull()) {
                    reply["error"] = QJsonObject{{
                        {"code", -32600},
                        {"message", "Invalid Request."}
                    }};
                    reply["id"] = QJsonValue{};
                    return reply;
        }
    }

    if (request.contains("id")) {
        QJsonValue id = request["id"];
        if (!id.isString() && !id.isDouble() && !id.isNull()) {
            reply["error"] = QJsonObject{{{"code", -32600},
                                         {"message", "Invalid Request."}}};
            reply["id"] = QJsonValue{};
            return reply;
        }
        reply["id"] = id;
    }

    QPair<QJsonValue, QJsonObject> result
        = processReply(method.toString(), request.value("params"));
    if (!reply.contains("id"))
        return {};

    if (!result.second.isEmpty()) {
        reply["error"] = result.second;
        return reply;
    }

    reply["result"] = result.first;
    return reply;
}

inline QPair<QJsonValue, QJsonObject> RpcNode::processReply(const QString &method,
                                                         const QJsonValue &args)
{
    if (!priv->methods
            || (!args.isArray() && !args.isUndefined())) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    QJsonArray params = args.toArray();

    // 10 is the max number of args supported by moc/qobject/...
    if (params.size() > 10) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    const QMetaObject *metaObject = priv->methods->metaObject();
    int i = indexOfMethod(metaObject, method, params);

    if (i == -1) {
        return qMakePair(QJsonValue{},
                         QJsonObject{{{"code", METHOD_NOT_FOUND},
                                      {"message", "Method not found"}}});
    }

    QMetaMethod metaMethod = metaObject->method(i);

    {
        int size = params.size();
        QVector<ArgumentHandler> argsHandler;
        QVector<QGenericArgument> args;

        argsHandler.resize(size);
        args.reserve(size);

        for (int i = 0;i != size;++i)
            args.push_back(argsHandler[i].convert(params[i]));

        ReturnHandler retHandler;
        QGenericReturnArgument ret = retHandler.convert(metaMethod.typeName());

        metaMethod.invoke(priv->methods, Qt::DirectConnection, ret,
                          (0 < size) ? args[0] : QGenericArgument(),
                          (1 < size) ? args[1] : QGenericArgument(),
                          (2 < size) ? args[2] : QGenericArgument(),
                          (3 < size) ? args[3] : QGenericArgument(),
                          (4 < size) ? args[4] : QGenericArgument(),
                          (5 < size) ? args[5] : QGenericArgument(),
                          (6 < size) ? args[6] : QGenericArgument(),
                          (7 < size) ? args[7] : QGenericArgument(),
                          (8 < size) ? args[8] : QGenericArgument(),
                          (9 < size) ? args[9] : QGenericArgument());

        return qMakePair(static_cast<QJsonValue>(retHandler), QJsonObject{});
    }
}
<|endoftext|>


<commit_before>/* Copyright 1989, 1990, 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

/* Forward declarations in SegmentLdr.h (DO NOT DELETE THIS LINE) */

#include <base/common.h>

#include <FileMgr.h>
#include <SegmentLdr.h>
#include <MemoryMgr.h>
#include <ResourceMgr.h>
#include <StdFilePkg.h>
#include <SysErr.h>
#include <FontMgr.h>
#include <OSEvent.h>
#include <WindowMgr.h>

#include <hfs/hfs.h>
#include <base/cpu.h>
#include <wind/wind.h>
#include <rsys/segment.h>
#include <vdriver/vdriver.h>
#include <rsys/executor.h>
#include <commandline/flags.h>
#include <prefs/prefs.h>
#include <osevent/osevent.h>
#include <quickdraw/cquick.h>
#include <rsys/desk.h>
#include <hfs/dcache.h>
#include <rsys/launch.h>
#include <rsys/paths.h>

#include <ctype.h>
#include <algorithm>

#if defined(CYGWIN32)

#include "winfs.h"

/*
 * NOTE:  I've looked at MINGW32 0.1.3 and the io.h #defines
 *        X_OK, which we use below.  I think Sam uses 0.1.3, even though
 *	  ARDI is currently at 0.1.2.
 */

#if !defined(OLD_MINGWIN32)
#include <io.h> /* needed for X_OK */
#else
#define X_OK 4 /* this is really just to get segment.c to compile */
#endif

#endif

namespace Executor
{
char *ROMlib_errorstring;
bool ROMlib_exit = false;

typedef finderinfo *finderinfoptr;

typedef GUEST<finderinfoptr> *finderinfohand;
}

using namespace Executor;

static BOOLEAN argv_to_appfile(const char *uname, AppFile *ap)
{
    auto spec = cmdlinePathToFSSpec(uname);

    if(!spec)
        return false;

    CInfoPBRec cinfo;
    cinfo.hFileInfo.ioVRefNum = spec->vRefNum;
    cinfo.hFileInfo.ioDirID = spec->parID;
    cinfo.hFileInfo.ioFDirIndex = 0;
    cinfo.hFileInfo.ioNamePtr = spec->name;

    if(PBGetCatInfo(&cinfo, false) != noErr)
    {
        warning_unexpected("%s: unable to get info on `%s'\n", ROMlib_appname.c_str(),
                           uname);
        return false;
    }

    ap->fType = cinfo.hFileInfo.ioFlFndrInfo.fdType;
    ap->versNum = 0;
    str255assign(ap->fName, spec->name);

    WDPBRec wpb;
    wpb.ioNamePtr = spec->name;
    wpb.ioVRefNum = spec->vRefNum;
    wpb.ioWDProcID = TICK("unix");
    wpb.ioWDDirID = spec->parID;
    if(PBOpenWD(&wpb, false) == noErr)
        ap->vRefNum = wpb.ioVRefNum;
    else
        ap->vRefNum = spec->vRefNum;

    return true;
}

void Executor::InitAppFiles(int argc, char **argv)
{
    LM(CurApRefNum) = -1;
    assignPString(LM(CurApName), toMacRomanFilename(ROMlib_appname), sizeof(LM(CurApName)) - 1);

    GUEST<THz> saveZone = LM(TheZone);
    LM(TheZone) = LM(SysZone);
    finderinfohand fh = (finderinfohand)
        NewHandle((Size)sizeof(finderinfo) - sizeof(AppFile));
    LM(TheZone) = saveZone;

    LM(AppParmHandle) = (Handle)fh;
    (*fh)->count = 0;
    (*fh)->message = ROMlib_print ? appPrint : appOpen;

    for(int i = 1; i < argc; i++)
    {
        AppFile appFile;
        if(argv_to_appfile(argv[i], &appFile))
        {
            ROMlib_exit = true;
            INTEGER newcount = (*fh)->count + 1;
            (*fh)->count = newcount;
            SetHandleSize((Handle)fh,
                            (char *)&(*fh)->files[newcount] - (char *)*fh);
            (*fh)->files[(*fh)->count - 1] = appFile;
        }
    }
}

void Executor::CountAppFiles(GUEST<INTEGER> *messagep,
                             GUEST<INTEGER> *countp) /* IMII-57 */
{
    if(LM(AppParmHandle))
    {
        if(messagep)
            *messagep = (*(finderinfohand)LM(AppParmHandle))->message;
        if(countp)
            *countp = (*(finderinfohand)LM(AppParmHandle))->count;
    }
    else
        *countp = 0;
}

void Executor::GetAppFiles(INTEGER index, AppFile *filep) /* IMII-58 */
{
    *filep = (*(finderinfohand)LM(AppParmHandle))->files[index - 1];
}

void Executor::ClrAppFiles(INTEGER index) /* IMII-58 */
{
    if((*(finderinfohand)LM(AppParmHandle))->files[index - 1].fType)
    {
        (*(finderinfohand)LM(AppParmHandle))->files[index - 1].fType = 0;
        (*(finderinfohand)LM(AppParmHandle))->count = (*(finderinfohand)LM(AppParmHandle))->count - 1;
    }
}

void Executor::C_GetAppParms(StringPtr namep, GUEST<INTEGER> *rnp,
                             GUEST<Handle> *aphandp) /* IMII-58 */
{
    str255assign(namep, LM(CurApName));
    *rnp = LM(CurApRefNum);
    *aphandp = LM(AppParmHandle);
}

static BOOLEAN valid_browser(void)
{
    OSErr err;
    FInfo finfo;

    err = GetFInfo(LM(FinderName), LM(BootDrive), &finfo);
    return !ROMlib_nobrowser && err == noErr && finfo.fdType == TICK("APPL");
}

static void launch_browser(void)
{
    /* Set the depth to what was specified on the command line;
   * if nothing was specified there, set the depth to the maximum
   * supported bits per pixel.
   */
    SetDepth(LM(MainDevice),
             (flag_bpp
                  ? std::min(flag_bpp, vdriver->maxBpp())
                  : vdriver->maxBpp()),
             0, 0);
    Launch(LM(FinderName), LM(BootDrive));
}

void Executor::C_ExitToShell()
{
    static char beenhere = false;
    ALLOCABEGIN

#if 1

    Point pt;
    static GUEST<SFTypeList> applonly = { FOURCC('A', 'P', 'P', 'L') };
    SFReply reply;
    struct
    {
        char quickbytes[grafSize];
        GUEST<GrafPtr> qdthePort;
        GUEST<LONGINT> tmpA5;
    } a5space;
    WindowPeek t_w;
    int i;

    if(ROMlib_mods & optionKey)
        ROMlib_exit = true;

    /* NOTE: closing drivers is a bad thing in the long run, but for now
     we're doing it.  We actually do it here *and* in reinitialize_things().
     We have to do it here since we want the window taken out of the
     windowlist properly, before we hack it out ourself, but we have to
     do it in reinitializethings because launch calls that, but doesn't
     call exittoshell */

    for(i = DESK_ACC_MIN; i <= DESK_ACC_MAX; ++i)
        CloseDriver(-i - 1);

    empty_timer_queues();

    if(LM(WWExist) == EXIST_YES)
    {
        /* remove global datastructures associated with each window
           remaining in the application's window list */
        for(t_w = LM(WindowList); t_w; t_w = WINDOW_NEXT_WINDOW(t_w))
            pm_window_closed((WindowPtr)t_w);
    }
    LM(WindowList) = 0;

    if(!ROMlib_exit
       && (!beenhere
           || strncmp((char *)LM(CurApName) + 1,
                      BROWSER_NAME, LM(CurApName)[0])
               != 0))
    {
        beenhere = true;

        /* if we call `InitWindows ()', we don't want it shouting
	   about 32bit uncleanliness, &c */
        size_info.application_p = false;

        if(LM(QDExist) == EXIST_NO)
        {
            EM_A5 = US_TO_SYN68K(&a5space.tmpA5);
            LM(CurrentA5) = guest_cast<Ptr>(EM_A5);
            InitGraf((Ptr)&a5space.qdthePort);
        }
        InitFonts();
        FlushEvents(everyEvent, 0);
        if(LM(WWExist) == EXIST_NO)
            InitWindows();
        if(LM(TEScrpHandle) == guest_cast<Handle>(-1) || LM(TEScrpHandle) == nullptr)
            TEInit();
        if(LM(DlgFont) == 0 || LM(DlgFont) == -1)
            InitDialogs((ProcPtr)0);
        InitCursor();

        if(valid_browser())
        {
            /* NOTE: much of the initialization done above isn't really
	             needed here, but I'd prefer to have the same environment
		     when we auto-launch browser as when we choose an app
		     using stdfile */
            launch_browser();
        }
        else
        {
            pt.h = 100;
            pt.v = 100;
            SFGetFile(pt, (StringPtr) "", nullptr, 1,
                      applonly, nullptr, &reply);

            if(!reply.good)
                ROMlib_exit = true;
            else
            {
                LM(CurApName)[0] = std::min<uint8_t>(reply.fName[0], 31);
                BlockMoveData((Ptr)reply.fName + 1, (Ptr)LM(CurApName) + 1,
                              (Size)LM(CurApName)[0]);
                Launch(LM(CurApName), reply.vRefNum);
            }
        }
    }

#endif

    CloseResFile(0);
    ROMlib_OurClose();

    dcache_invalidate_all(true);

    if(ROMlib_errorstring)
    {
        write(2, ROMlib_errorstring, strlen(ROMlib_errorstring));
        gui_abort();
    }

    exit(0);
    ALLOCAEND /* yeah, right, if exit fails... */
}

#define JMPLINSTR 0x4EF9
#define MOVESPINSTR 0x3F3C
#define LOADSEGTRAP 0xA9F0

void Executor::C_LoadSeg(INTEGER segno)
{
    Handle newcode;
    unsigned short offbytes;
    INTEGER taboff, nentries, savenentries;
    GUEST<int16_t> *ptr, *saveptr;

    LM(ResLoad) = -1; /* CricketDraw III's behaviour suggested this */
    newcode = GetResource(TICK("CODE"), segno);
    HLock(newcode);
    taboff = ((GUEST<INTEGER> *)*newcode)[0];
    if((uint16_t)taboff == 0xA89F) /* magic compressed resource signature */
    {
        /* We are totally dead here.  We almost certainly can't use
	 * `system_error' to inform the user because the window
	 * manager probably is not yet running.
	 */

        ROMlib_launch_failure = (system_version >= 0x700 ? launch_compressed_ge7 : launch_compressed_lt7);
        C_ExitToShell();
    }
    savenentries = nentries = ((GUEST<INTEGER> *)*newcode)[1];

    saveptr = ptr = (GUEST<int16_t> *)((char *)SYN68K_TO_US(EM_A5) + taboff + LM(CurJTOffset));
    while(--nentries >= 0)
    {
        if(ptr[1] != JMPLINSTR)
        {
            offbytes = *ptr;
            *ptr++ = segno;
            *ptr++ = JMPLINSTR;
            *(GUEST<LONGINT> *)ptr = US_TO_SYN68K(*newcode) + offbytes + 4;
            ptr += 2;
        }
        else
            ptr += 4;
    }
    ROMlib_destroy_blocks(US_TO_SYN68K(saveptr), 8L * savenentries,
                          true);
}

#define SEGNOOFP(p) (((GUEST<INTEGER> *)p)[-1])

static void unpatch(Ptr segstart, Ptr p)
{
    GUEST<INTEGER> *ip;
    Ptr firstpc;

    ip = (GUEST<INTEGER> *)p;

    firstpc = *(GUEST<Ptr> *)(p + 2);
    ip[1] = ip[-1]; /* the segment number */
    ip[-1] = firstpc - segstart - 4;
    ip[0] = MOVESPINSTR;
    ip[2] = LOADSEGTRAP;
}

void Executor::C_UnloadSeg(Ptr addr)
{
    Ptr p, segstart;
    char *top, *bottom;

    Handle h;
    INTEGER segno;

    if(*(GUEST<INTEGER> *)addr == JMPLINSTR)
    {
        segno = SEGNOOFP(addr);
        h = GetResource(TICK("CODE"), segno);
        if(!*h)
            LoadResource(h);
        segstart = *h;
        for(p = addr; SEGNOOFP(p) == segno; p += 8)
            unpatch(segstart, p);

        bottom = (char *)p;

        for(p = addr - 8; SEGNOOFP(p) == segno; p -= 8)
            unpatch(segstart, p);

        top = (char *)p + 6; /* +8 that we didn't zap, -2 that we went
					overboard on unpatch (see above) */
        ROMlib_destroy_blocks(US_TO_SYN68K(top),
                              bottom - top, false);

        HUnlock(h);
        HPurge(h);
    }
}

<commit_msg>add missing #include<commit_after>/* Copyright 1989, 1990, 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

/* Forward declarations in SegmentLdr.h (DO NOT DELETE THIS LINE) */

#include <base/common.h>

#include <FileMgr.h>
#include <SegmentLdr.h>
#include <MemoryMgr.h>
#include <ResourceMgr.h>
#include <StdFilePkg.h>
#include <SysErr.h>
#include <FontMgr.h>
#include <OSEvent.h>
#include <WindowMgr.h>

#include <hfs/hfs.h>
#include <base/cpu.h>
#include <wind/wind.h>
#include <rsys/segment.h>
#include <vdriver/vdriver.h>
#include <rsys/executor.h>
#include <commandline/flags.h>
#include <prefs/prefs.h>
#include <osevent/osevent.h>
#include <quickdraw/cquick.h>
#include <rsys/desk.h>
#include <hfs/dcache.h>
#include <rsys/launch.h>
#include <rsys/paths.h>
#include <rsys/macstrings.h>

#include <ctype.h>
#include <algorithm>

#if defined(CYGWIN32)

#include "winfs.h"

/*
 * NOTE:  I've looked at MINGW32 0.1.3 and the io.h #defines
 *        X_OK, which we use below.  I think Sam uses 0.1.3, even though
 *	  ARDI is currently at 0.1.2.
 */

#if !defined(OLD_MINGWIN32)
#include <io.h> /* needed for X_OK */
#else
#define X_OK 4 /* this is really just to get segment.c to compile */
#endif

#endif

namespace Executor
{
char *ROMlib_errorstring;
bool ROMlib_exit = false;

typedef finderinfo *finderinfoptr;

typedef GUEST<finderinfoptr> *finderinfohand;
}

using namespace Executor;

static BOOLEAN argv_to_appfile(const char *uname, AppFile *ap)
{
    auto spec = cmdlinePathToFSSpec(uname);

    if(!spec)
        return false;

    CInfoPBRec cinfo;
    cinfo.hFileInfo.ioVRefNum = spec->vRefNum;
    cinfo.hFileInfo.ioDirID = spec->parID;
    cinfo.hFileInfo.ioFDirIndex = 0;
    cinfo.hFileInfo.ioNamePtr = spec->name;

    if(PBGetCatInfo(&cinfo, false) != noErr)
    {
        warning_unexpected("%s: unable to get info on `%s'\n", ROMlib_appname.c_str(),
                           uname);
        return false;
    }

    ap->fType = cinfo.hFileInfo.ioFlFndrInfo.fdType;
    ap->versNum = 0;
    str255assign(ap->fName, spec->name);

    WDPBRec wpb;
    wpb.ioNamePtr = spec->name;
    wpb.ioVRefNum = spec->vRefNum;
    wpb.ioWDProcID = TICK("unix");
    wpb.ioWDDirID = spec->parID;
    if(PBOpenWD(&wpb, false) == noErr)
        ap->vRefNum = wpb.ioVRefNum;
    else
        ap->vRefNum = spec->vRefNum;

    return true;
}

void Executor::InitAppFiles(int argc, char **argv)
{
    LM(CurApRefNum) = -1;
    assignPString(LM(CurApName), toMacRomanFilename(ROMlib_appname), sizeof(LM(CurApName)) - 1);

    GUEST<THz> saveZone = LM(TheZone);
    LM(TheZone) = LM(SysZone);
    finderinfohand fh = (finderinfohand)
        NewHandle((Size)sizeof(finderinfo) - sizeof(AppFile));
    LM(TheZone) = saveZone;

    LM(AppParmHandle) = (Handle)fh;
    (*fh)->count = 0;
    (*fh)->message = ROMlib_print ? appPrint : appOpen;

    for(int i = 1; i < argc; i++)
    {
        AppFile appFile;
        if(argv_to_appfile(argv[i], &appFile))
        {
            ROMlib_exit = true;
            INTEGER newcount = (*fh)->count + 1;
            (*fh)->count = newcount;
            SetHandleSize((Handle)fh,
                            (char *)&(*fh)->files[newcount] - (char *)*fh);
            (*fh)->files[(*fh)->count - 1] = appFile;
        }
    }
}

void Executor::CountAppFiles(GUEST<INTEGER> *messagep,
                             GUEST<INTEGER> *countp) /* IMII-57 */
{
    if(LM(AppParmHandle))
    {
        if(messagep)
            *messagep = (*(finderinfohand)LM(AppParmHandle))->message;
        if(countp)
            *countp = (*(finderinfohand)LM(AppParmHandle))->count;
    }
    else
        *countp = 0;
}

void Executor::GetAppFiles(INTEGER index, AppFile *filep) /* IMII-58 */
{
    *filep = (*(finderinfohand)LM(AppParmHandle))->files[index - 1];
}

void Executor::ClrAppFiles(INTEGER index) /* IMII-58 */
{
    if((*(finderinfohand)LM(AppParmHandle))->files[index - 1].fType)
    {
        (*(finderinfohand)LM(AppParmHandle))->files[index - 1].fType = 0;
        (*(finderinfohand)LM(AppParmHandle))->count = (*(finderinfohand)LM(AppParmHandle))->count - 1;
    }
}

void Executor::C_GetAppParms(StringPtr namep, GUEST<INTEGER> *rnp,
                             GUEST<Handle> *aphandp) /* IMII-58 */
{
    str255assign(namep, LM(CurApName));
    *rnp = LM(CurApRefNum);
    *aphandp = LM(AppParmHandle);
}

static BOOLEAN valid_browser(void)
{
    OSErr err;
    FInfo finfo;

    err = GetFInfo(LM(FinderName), LM(BootDrive), &finfo);
    return !ROMlib_nobrowser && err == noErr && finfo.fdType == TICK("APPL");
}

static void launch_browser(void)
{
    /* Set the depth to what was specified on the command line;
   * if nothing was specified there, set the depth to the maximum
   * supported bits per pixel.
   */
    SetDepth(LM(MainDevice),
             (flag_bpp
                  ? std::min(flag_bpp, vdriver->maxBpp())
                  : vdriver->maxBpp()),
             0, 0);
    Launch(LM(FinderName), LM(BootDrive));
}

void Executor::C_ExitToShell()
{
    static char beenhere = false;
    ALLOCABEGIN

#if 1

    Point pt;
    static GUEST<SFTypeList> applonly = { FOURCC('A', 'P', 'P', 'L') };
    SFReply reply;
    struct
    {
        char quickbytes[grafSize];
        GUEST<GrafPtr> qdthePort;
        GUEST<LONGINT> tmpA5;
    } a5space;
    WindowPeek t_w;
    int i;

    if(ROMlib_mods & optionKey)
        ROMlib_exit = true;

    /* NOTE: closing drivers is a bad thing in the long run, but for now
     we're doing it.  We actually do it here *and* in reinitialize_things().
     We have to do it here since we want the window taken out of the
     windowlist properly, before we hack it out ourself, but we have to
     do it in reinitializethings because launch calls that, but doesn't
     call exittoshell */

    for(i = DESK_ACC_MIN; i <= DESK_ACC_MAX; ++i)
        CloseDriver(-i - 1);

    empty_timer_queues();

    if(LM(WWExist) == EXIST_YES)
    {
        /* remove global datastructures associated with each window
           remaining in the application's window list */
        for(t_w = LM(WindowList); t_w; t_w = WINDOW_NEXT_WINDOW(t_w))
            pm_window_closed((WindowPtr)t_w);
    }
    LM(WindowList) = 0;

    if(!ROMlib_exit
       && (!beenhere
           || strncmp((char *)LM(CurApName) + 1,
                      BROWSER_NAME, LM(CurApName)[0])
               != 0))
    {
        beenhere = true;

        /* if we call `InitWindows ()', we don't want it shouting
	   about 32bit uncleanliness, &c */
        size_info.application_p = false;

        if(LM(QDExist) == EXIST_NO)
        {
            EM_A5 = US_TO_SYN68K(&a5space.tmpA5);
            LM(CurrentA5) = guest_cast<Ptr>(EM_A5);
            InitGraf((Ptr)&a5space.qdthePort);
        }
        InitFonts();
        FlushEvents(everyEvent, 0);
        if(LM(WWExist) == EXIST_NO)
            InitWindows();
        if(LM(TEScrpHandle) == guest_cast<Handle>(-1) || LM(TEScrpHandle) == nullptr)
            TEInit();
        if(LM(DlgFont) == 0 || LM(DlgFont) == -1)
            InitDialogs((ProcPtr)0);
        InitCursor();

        if(valid_browser())
        {
            /* NOTE: much of the initialization done above isn't really
	             needed here, but I'd prefer to have the same environment
		     when we auto-launch browser as when we choose an app
		     using stdfile */
            launch_browser();
        }
        else
        {
            pt.h = 100;
            pt.v = 100;
            SFGetFile(pt, (StringPtr) "", nullptr, 1,
                      applonly, nullptr, &reply);

            if(!reply.good)
                ROMlib_exit = true;
            else
            {
                LM(CurApName)[0] = std::min<uint8_t>(reply.fName[0], 31);
                BlockMoveData((Ptr)reply.fName + 1, (Ptr)LM(CurApName) + 1,
                              (Size)LM(CurApName)[0]);
                Launch(LM(CurApName), reply.vRefNum);
            }
        }
    }

#endif

    CloseResFile(0);
    ROMlib_OurClose();

    dcache_invalidate_all(true);

    if(ROMlib_errorstring)
    {
        write(2, ROMlib_errorstring, strlen(ROMlib_errorstring));
        gui_abort();
    }

    exit(0);
    ALLOCAEND /* yeah, right, if exit fails... */
}

#define JMPLINSTR 0x4EF9
#define MOVESPINSTR 0x3F3C
#define LOADSEGTRAP 0xA9F0

void Executor::C_LoadSeg(INTEGER segno)
{
    Handle newcode;
    unsigned short offbytes;
    INTEGER taboff, nentries, savenentries;
    GUEST<int16_t> *ptr, *saveptr;

    LM(ResLoad) = -1; /* CricketDraw III's behaviour suggested this */
    newcode = GetResource(TICK("CODE"), segno);
    HLock(newcode);
    taboff = ((GUEST<INTEGER> *)*newcode)[0];
    if((uint16_t)taboff == 0xA89F) /* magic compressed resource signature */
    {
        /* We are totally dead here.  We almost certainly can't use
	 * `system_error' to inform the user because the window
	 * manager probably is not yet running.
	 */

        ROMlib_launch_failure = (system_version >= 0x700 ? launch_compressed_ge7 : launch_compressed_lt7);
        C_ExitToShell();
    }
    savenentries = nentries = ((GUEST<INTEGER> *)*newcode)[1];

    saveptr = ptr = (GUEST<int16_t> *)((char *)SYN68K_TO_US(EM_A5) + taboff + LM(CurJTOffset));
    while(--nentries >= 0)
    {
        if(ptr[1] != JMPLINSTR)
        {
            offbytes = *ptr;
            *ptr++ = segno;
            *ptr++ = JMPLINSTR;
            *(GUEST<LONGINT> *)ptr = US_TO_SYN68K(*newcode) + offbytes + 4;
            ptr += 2;
        }
        else
            ptr += 4;
    }
    ROMlib_destroy_blocks(US_TO_SYN68K(saveptr), 8L * savenentries,
                          true);
}

#define SEGNOOFP(p) (((GUEST<INTEGER> *)p)[-1])

static void unpatch(Ptr segstart, Ptr p)
{
    GUEST<INTEGER> *ip;
    Ptr firstpc;

    ip = (GUEST<INTEGER> *)p;

    firstpc = *(GUEST<Ptr> *)(p + 2);
    ip[1] = ip[-1]; /* the segment number */
    ip[-1] = firstpc - segstart - 4;
    ip[0] = MOVESPINSTR;
    ip[2] = LOADSEGTRAP;
}

void Executor::C_UnloadSeg(Ptr addr)
{
    Ptr p, segstart;
    char *top, *bottom;

    Handle h;
    INTEGER segno;

    if(*(GUEST<INTEGER> *)addr == JMPLINSTR)
    {
        segno = SEGNOOFP(addr);
        h = GetResource(TICK("CODE"), segno);
        if(!*h)
            LoadResource(h);
        segstart = *h;
        for(p = addr; SEGNOOFP(p) == segno; p += 8)
            unpatch(segstart, p);

        bottom = (char *)p;

        for(p = addr - 8; SEGNOOFP(p) == segno; p -= 8)
            unpatch(segstart, p);

        top = (char *)p + 6; /* +8 that we didn't zap, -2 that we went
					overboard on unpatch (see above) */
        ROMlib_destroy_blocks(US_TO_SYN68K(top),
                              bottom - top, false);

        HUnlock(h);
        HPurge(h);
    }
}

<|endoftext|>