diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index a8337198e4..436f9c824a 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -13,11 +13,16 @@ #include "../json/value.h" #include "../json/reader.h" -#include "CallRPC.h" #include "RPC.h" +#include "Log.h" +#include "RPCErr.h" #include "Config.h" #include "BitcoinUtil.h" +#include "CallRPC.h" + +SETUP_LOG(); + static inline bool isSwitchChar(char c) { #ifdef __WXMSW__ @@ -46,73 +51,175 @@ std::string EncodeBase64(const std::string& s) return result; } +Json::Value RPCParser::parseAsIs(Json::Value jvReq, const Json::Value ¶ms) +{ + return jvReq; +} + +// Convert a command plus args to a json request. +Json::Value RPCParser::parseCommand(Json::Value jvRequest) +{ + std::string strCommand = jvRequest["method"].asString(); + Json::Value jvParams = jvRequest["params"]; + + cLog(lsTRACE) << "RPC:" << strCommand; + cLog(lsTRACE) << "RPC params:" << jvParams; + + static struct { + const char* pCommand; + parseFuncPtr pfpFunc; + int iMinParams; + int iMaxParams; + } commandsA[] = { + // Request-response methods + { "accept_ledger", &RPCParser::parseAsIs, 0, 0 }, +#if 0 + { "account_info", &RPCParser::doAccountInfo, 1, 2, false, false, optCurrent }, + { "account_tx", &RPCParser::doAccountTransactions, 2, 3, false, false, optNetwork }, + { "connect", &RPCParser::doConnect, 1, 2, true, false, optNone }, + { "data_delete", &RPCParser::doDataDelete, 1, 1, true, false, optNone }, + { "data_fetch", &RPCParser::doDataFetch, 1, 1, true, false, optNone }, + { "data_store", &RPCParser::doDataStore, 2, 2, true, false, optNone }, + { "get_counts", &RPCParser::doGetCounts, 0, 1, true, false, optNone }, + { "ledger", &RPCParser::doLedger, 0, 2, false, false, optNetwork }, + { "ledger_accept", &RPCParser::doLedgerAccept, 0, 0, true, false, optCurrent }, + { "ledger_closed", &RPCParser::doLedgerClosed, 0, 0, false, false, optClosed }, + { "ledger_current", &RPCParser::doLedgerCurrent, 0, 0, false, false, optCurrent }, + { "ledger_entry", &RPCParser::doLedgerEntry, -1, -1, false, false, optCurrent }, + { "ledger_header", &RPCParser::doLedgerHeader, -1, -1, false, false, optCurrent }, + { "log_level", &RPCParser::doLogLevel, 0, 2, true, false, optNone }, + { "logrotate", &RPCParser::doLogRotate, 0, 0, true, false, optNone }, + { "nickname_info", &RPCParser::doNicknameInfo, 1, 1, false, false, optCurrent }, + { "owner_info", &RPCParser::doOwnerInfo, 1, 2, false, false, optCurrent }, + { "peers", &RPCParser::doPeers, 0, 0, true, false, optNone }, + { "profile", &RPCParser::doProfile, 1, 9, false, false, optCurrent }, + { "ripple_lines_get", &RPCParser::doRippleLinesGet, 1, 2, false, false, optCurrent }, + { "ripple_path_find", &RPCParser::doRipplePathFind, -1, -1, false, false, optCurrent }, + { "submit", &RPCParser::doSubmit, 2, 2, false, false, optCurrent }, + { "submit_json", &RPCParser::doSubmitJson, -1, -1, false, false, optCurrent }, + { "server_info", &RPCParser::doServerInfo, 0, 0, true, false, optNone }, + { "stop", &RPCParser::doStop, 0, 0, true, false, optNone }, + { "transaction_entry", &RPCParser::doTransactionEntry, -1, -1, false, false, optCurrent }, + { "tx", &RPCParser::doTx, 1, 1, true, false, optNone }, + { "tx_history", &RPCParser::doTxHistory, 1, 1, false, false, optNone }, + + { "unl_add", &RPCParser::doUnlAdd, 1, 2, true, false, optNone }, + { "unl_delete", &RPCParser::doUnlDelete, 1, 1, true, false, optNone }, + { "unl_list", &RPCParser::doUnlList, 0, 0, true, false, optNone }, + { "unl_load", &RPCParser::doUnlLoad, 0, 0, true, false, optNone }, + { "unl_network", &RPCParser::doUnlNetwork, 0, 0, true, false, optNone }, + { "unl_reset", &RPCParser::doUnlReset, 0, 0, true, false, optNone }, + { "unl_score", &RPCParser::doUnlScore, 0, 0, true, false, optNone }, + + { "validation_create", &RPCParser::doValidationCreate, 0, 1, false, false, optNone }, + { "validation_seed", &RPCParser::doValidationSeed, 0, 1, false, false, optNone }, + + { "wallet_accounts", &RPCParser::doWalletAccounts, 1, 1, false, false, optCurrent }, + { "wallet_propose", &RPCParser::doWalletPropose, 0, 1, false, false, optNone }, + { "wallet_seed", &RPCParser::doWalletSeed, 0, 1, false, false, optNone }, + + { "login", &RPCParser::doLogin, 2, 2, true, false, optNone }, + + // Evented methods + { "subscribe", &RPCParser::doSubscribe, -1, -1, false, true, optNone }, + { "unsubscribe", &RPCParser::doUnsubscribe, -1, -1, false, true, optNone }, +#endif + }; + + int i = NUMBER(commandsA); + + while (i-- && strCommand != commandsA[i].pCommand) + ; + + if (i < 0) + { + return rpcError(rpcBAD_SYNTAX, jvRequest); + } + else if (commandsA[i].iMinParams >= 0 + ? commandsA[i].iMaxParams + ? (jvParams.size() < commandsA[i].iMinParams + || (commandsA[i].iMaxParams >= 0 && jvParams.size() > commandsA[i].iMaxParams)) + : false + : jvParams.isArray()) + { + return rpcError(rpcBAD_SYNTAX, jvRequest); + } + + return (this->*(commandsA[i].pfpFunc))(jvRequest, jvParams); +} + int commandLineRPC(const std::vector& vCmd) { - std::string strPrint; - int nRet = 0; + Json::Value jvOutput; + int nRet = 0; + Json::Value jvRequest(Json::objectValue); + try { - if (vCmd.empty()) return 1; + RPCParser rpParser; + Json::Value jvCliParams(Json::arrayValue); - std::string strMethod = vCmd[0]; + if (vCmd.empty()) return 1; // 1 = print usage. + + jvRequest["method"] = vCmd[0]; - // Parameters default to strings - Json::Value params(Json::arrayValue); for (int i = 1; i != vCmd.size(); i++) - params.append(vCmd[i]); + jvCliParams.append(vCmd[i]); - // Execute - Json::Value reply = callRPC(strMethod, params); + jvRequest["params"] = jvCliParams; - // Parse reply - Json::Value result = reply.get("result", Json::Value()); - Json::Value error = reply.get("error", Json::Value()); + jvRequest = rpParser.parseCommand(jvRequest); - if (result.isString() && (result.asString() == "unknown command")) - nRet=1; + // std::cerr << "Request: " << jvRequest << std::endl; - if (!error.isNull()) - { // Error - strPrint = "error: " + error.toStyledString(); - int code = error["code"].asInt(); - nRet = abs(code); - } - else - { // Result - if (result.isNull()) - strPrint = ""; - else if (result.isString()) - strPrint = result.asString(); - else - strPrint = result.toStyledString(); + Json::Value jvParams(Json::arrayValue); + + jvParams.append(jvRequest); + + Json::Value jvResult; + + jvResult = jvRequest.isMember("error") + ? jvRequest // Parse failed, return error. + : callRPC(jvRequest["method"].asString(), jvParams); // Parsed, execute. + + if (jvResult.isMember("error")) + { + nRet = jvResult.isMember("error_code") + ? lexical_cast_s(jvResult["error_code"].asString()) + : 1; } + + jvOutput = jvResult; } catch (std::exception& e) { - strPrint = std::string("error: ") + e.what(); - nRet = 87; + jvRequest["error_what"] = e.what(); + + jvOutput = rpcError(rpcINTERNAL, jvRequest); + nRet = rpcINTERNAL; } catch (...) { - std::cout << "Exception CommandLineRPC()" << std::endl; + jvRequest["error_what"] = "exception"; + + jvOutput = rpcError(rpcINTERNAL, jvRequest); + nRet = rpcINTERNAL; } - if (strPrint != "") - { - std::cout << strPrint << std::endl; - } + std::cout << jvOutput.toStyledString(); + return nRet; } - Json::Value callRPC(const std::string& strMethod, const Json::Value& params) { if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty()) - throw std::runtime_error("You must set rpcpassword= in the configuration file" + throw std::runtime_error("You must set rpcpassword= in the configuration file. " "If the file does not exist, create it with owner-readable-only file permissions."); // Connect to localhost - std::cout << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl; + if (!theConfig.QUIET) + std::cerr << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT); @@ -128,11 +235,11 @@ Json::Value callRPC(const std::string& strMethod, const Json::Value& params) // Send request std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1)); - std::cout << "send request " << strMethod << " : " << strRequest << std::endl; + cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl; std::string strPost = createHTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // std::cout << "post " << strPost << std::endl; + // std::cerr << "post " << strPost << std::endl; // Receive reply std::map mapHeaders; @@ -146,11 +253,14 @@ Json::Value callRPC(const std::string& strMethod, const Json::Value& params) throw std::runtime_error("no response from server"); // Parse reply - std::cout << "RPC reply: " << strReply << std::endl; + cLog(lsDEBUG) << "RPC reply: " << strReply << std::endl; + Json::Reader reader; Json::Value valReply; + if (!reader.parse(strReply, valReply)) throw std::runtime_error("couldn't parse reply from server"); + if (valReply.isNull()) throw std::runtime_error("expected reply to have result, error and id properties"); diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index ac6fc3dae2..cbe288c185 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -1,7 +1,25 @@ +#ifndef __CALLRPC__ +#define __CALLRPC__ + #include #include "../json/value.h" +class RPCParser +{ +protected: + typedef Json::Value (RPCParser::*parseFuncPtr)(Json::Value jvRet, const Json::Value &jvParams); + + Json::Value parseAsIs(Json::Value jvRet, const Json::Value &jvParams); + +public: + Json::Value parseCommand(Json::Value jvRequest); +}; + extern int commandLineRPC(const std::vector& vCmd); extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params); + +#endif + +// vim:ts=4