mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'develop' of github.com:ripple/rippled into develop
This commit is contained in:
1064
modules/ripple_app/rpc/CallRPC.cpp
Normal file
1064
modules/ripple_app/rpc/CallRPC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
73
modules/ripple_app/rpc/CallRPC.h
Normal file
73
modules/ripple_app/rpc/CallRPC.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __CALLRPC__
|
||||
#define __CALLRPC__
|
||||
|
||||
class RPCParser
|
||||
{
|
||||
public:
|
||||
Json::Value parseCommand (std::string strMethod, Json::Value jvParams);
|
||||
|
||||
private:
|
||||
typedef Json::Value (RPCParser::*parseFuncPtr) (const Json::Value& jvParams);
|
||||
|
||||
Json::Value parseAccountRaw (const Json::Value& jvParams, bool bPeer);
|
||||
|
||||
Json::Value parseAccountItems (const Json::Value& jvParams);
|
||||
Json::Value parseAccountLines (const Json::Value& jvParams);
|
||||
Json::Value parseAccountTransactions (const Json::Value& jvParams);
|
||||
Json::Value parseAsIs (const Json::Value& jvParams);
|
||||
Json::Value parseBookOffers (const Json::Value& jvParams);
|
||||
Json::Value parseConnect (const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseDataDelete (const Json::Value& jvParams);
|
||||
Json::Value parseDataFetch (const Json::Value& jvParams);
|
||||
Json::Value parseDataStore (const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseEvented (const Json::Value& jvParams);
|
||||
Json::Value parseFeature (const Json::Value& jvParams);
|
||||
Json::Value parseFetchInfo (const Json::Value& jvParams);
|
||||
Json::Value parseGetCounts (const Json::Value& jvParams);
|
||||
Json::Value parseInternal (const Json::Value& jvParams);
|
||||
Json::Value parseJson (const Json::Value& jvParams);
|
||||
Json::Value parseLedger (const Json::Value& jvParams);
|
||||
Json::Value parseLedgerId (const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseLogin (const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseLogLevel (const Json::Value& jvParams);
|
||||
Json::Value parseOwnerInfo (const Json::Value& jvParams);
|
||||
Json::Value parseProofCreate (const Json::Value& jvParams);
|
||||
Json::Value parseProofSolve (const Json::Value& jvParams);
|
||||
Json::Value parseProofVerify (const Json::Value& jvParams);
|
||||
Json::Value parseRandom (const Json::Value& jvParams);
|
||||
Json::Value parseRipplePathFind (const Json::Value& jvParams);
|
||||
Json::Value parseSMS (const Json::Value& jvParams);
|
||||
Json::Value parseSignSubmit (const Json::Value& jvParams);
|
||||
Json::Value parseTx (const Json::Value& jvParams);
|
||||
Json::Value parseTxHistory (const Json::Value& jvParams);
|
||||
Json::Value parseUnlAdd (const Json::Value& jvParams);
|
||||
Json::Value parseUnlDelete (const Json::Value& jvParams);
|
||||
Json::Value parseValidationCreate (const Json::Value& jvParams);
|
||||
Json::Value parseValidationSeed (const Json::Value& jvParams);
|
||||
Json::Value parseWalletAccounts (const Json::Value& jvParams);
|
||||
Json::Value parseWalletPropose (const Json::Value& jvParams);
|
||||
Json::Value parseWalletSeed (const Json::Value& jvParams);
|
||||
};
|
||||
|
||||
extern int commandLineRPC (const std::vector<std::string>& vCmd);
|
||||
|
||||
extern void callRPC (
|
||||
boost::asio::io_service& io_service,
|
||||
const std::string& strIp, const int iPort,
|
||||
const std::string& strUsername, const std::string& strPassword,
|
||||
const std::string& strPath, const std::string& strMethod,
|
||||
const Json::Value& jvParams, const bool bSSL,
|
||||
FUNCTION_TYPE<void (const Json::Value& jvInput)> callbackFuncP = FUNCTION_TYPE<void (const Json::Value& jvInput)> ());
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
36
modules/ripple_app/rpc/RPC.h
Normal file
36
modules/ripple_app/rpc/RPC.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RPC_H_INCLUDED
|
||||
#define RIPPLE_RPC_H_INCLUDED
|
||||
|
||||
// VFALCO TODO Wrap these up into a class. It looks like they just do some
|
||||
// convenience packaging of JSON data from the pieces. It looks
|
||||
// Ripple client protocol-specific.
|
||||
//
|
||||
extern std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params,
|
||||
const Json::Value& id);
|
||||
|
||||
extern std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id);
|
||||
|
||||
extern Json::Value JSONRPCError (int code, const std::string& message);
|
||||
|
||||
extern std::string createHTTPPost (const std::string& strHost, const std::string& strPath, const std::string& strMsg,
|
||||
const std::map<std::string, std::string>& mapRequestHeaders);
|
||||
|
||||
extern std::string HTTPReply (int nStatus, const std::string& strMsg);
|
||||
|
||||
// VFALCO TODO Create a HTTPHeaders class with a nice interface instead of the std::map
|
||||
//
|
||||
extern bool HTTPAuthorized (std::map <std::string, std::string> const& mapHeaders);
|
||||
|
||||
// VFALCO NOTE This one looks like it does some sort of stream i/o
|
||||
//
|
||||
extern int ReadHTTP (std::basic_istream<char>& stream,
|
||||
std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet);
|
||||
|
||||
#endif
|
||||
101
modules/ripple_app/rpc/RPCDoor.cpp
Normal file
101
modules/ripple_app/rpc/RPCDoor.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (RPCDoor)
|
||||
|
||||
RPCDoor::RPCDoor (boost::asio::io_service& io_service, RPCServer::Handler& handler)
|
||||
: m_rpcServerHandler (handler)
|
||||
, mAcceptor (io_service,
|
||||
boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string (theConfig.getRpcIP ()), theConfig.getRpcPort ()))
|
||||
, mDelayTimer (io_service)
|
||||
, mSSLContext (boost::asio::ssl::context::sslv23)
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) << "RPC port: " << theConfig.getRpcAddress().toRawUTF8() << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
|
||||
if (theConfig.RPC_SECURE != 0)
|
||||
{
|
||||
// VFALCO TODO This could be a method of theConfig
|
||||
//
|
||||
basio::SslContext::initializeFromFile (
|
||||
mSSLContext,
|
||||
theConfig.RPC_SSL_KEY,
|
||||
theConfig.RPC_SSL_CERT,
|
||||
theConfig.RPC_SSL_CHAIN);
|
||||
}
|
||||
|
||||
startListening ();
|
||||
}
|
||||
|
||||
RPCDoor::~RPCDoor ()
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) << "RPC port: " << theConfig.getRpcAddress().toRawUTF8() << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
}
|
||||
|
||||
void RPCDoor::startListening ()
|
||||
{
|
||||
RPCServer::pointer new_connection = RPCServer::New (mAcceptor.get_io_service (), mSSLContext, m_rpcServerHandler);
|
||||
mAcceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
|
||||
|
||||
mAcceptor.async_accept (new_connection->getRawSocket (),
|
||||
boost::bind (&RPCDoor::handleConnect, this, new_connection,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
bool RPCDoor::isClientAllowed (const std::string& ip)
|
||||
{
|
||||
if (theConfig.RPC_ALLOW_REMOTE)
|
||||
return true;
|
||||
|
||||
// VFALCO TODO Represent ip addresses as a structure. Use isLoopback() member here
|
||||
//
|
||||
if (ip == "127.0.0.1")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RPCDoor::handleConnect (RPCServer::pointer new_connection, const boost::system::error_code& error)
|
||||
{
|
||||
bool delay = false;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Restrict callers by IP
|
||||
try
|
||||
{
|
||||
if (! isClientAllowed (new_connection->getRemoteAddressText ()))
|
||||
{
|
||||
startListening ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// client may have disconnected
|
||||
startListening ();
|
||||
return;
|
||||
}
|
||||
|
||||
new_connection->getSocket ().async_handshake (AutoSocket::ssl_socket::server,
|
||||
boost::bind (&RPCServer::connected, new_connection));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error == boost::system::errc::too_many_files_open)
|
||||
delay = true;
|
||||
|
||||
WriteLog (lsINFO, RPCDoor) << "RPCDoor::handleConnect Error: " << error;
|
||||
}
|
||||
|
||||
if (delay)
|
||||
{
|
||||
mDelayTimer.expires_from_now (boost::posix_time::milliseconds (1000));
|
||||
mDelayTimer.async_wait (boost::bind (&RPCDoor::startListening, this));
|
||||
}
|
||||
else
|
||||
startListening ();
|
||||
}
|
||||
// vim:ts=4
|
||||
35
modules/ripple_app/rpc/RPCDoor.h
Normal file
35
modules/ripple_app/rpc/RPCDoor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RPCDOOR_H
|
||||
#define RIPPLE_RPCDOOR_H
|
||||
|
||||
/*
|
||||
Handles incoming connections from people making RPC Requests
|
||||
*/
|
||||
|
||||
class RPCDoor : LeakChecked <RPCDoor>
|
||||
{
|
||||
public:
|
||||
explicit RPCDoor (
|
||||
boost::asio::io_service& io_service,
|
||||
RPCServer::Handler& handler);
|
||||
~RPCDoor ();
|
||||
|
||||
private:
|
||||
RPCServer::Handler& m_rpcServerHandler;
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::asio::deadline_timer mDelayTimer;
|
||||
boost::asio::ssl::context mSSLContext;
|
||||
|
||||
void startListening ();
|
||||
void handleConnect (RPCServer::pointer new_connection,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
bool isClientAllowed (const std::string& ip);
|
||||
};
|
||||
|
||||
#endif
|
||||
106
modules/ripple_app/rpc/RPCErr.cpp
Normal file
106
modules/ripple_app/rpc/RPCErr.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
struct RPCErr; // for Log
|
||||
|
||||
SETUP_LOG (RPCErr)
|
||||
|
||||
Json::Value rpcError (int iError, Json::Value jvResult)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
int iError;
|
||||
const char* pToken;
|
||||
const char* pMessage;
|
||||
} errorInfoA[] =
|
||||
{
|
||||
{ rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address." },
|
||||
{ rpcACT_EXISTS, "actExists", "Account already exists." },
|
||||
{ rpcACT_MALFORMED, "actMalformed", "Account malformed." },
|
||||
{ rpcACT_NOT_FOUND, "actNotFound", "Account not found." },
|
||||
{ rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." },
|
||||
{ rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid." },
|
||||
{ rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." },
|
||||
{ rpcBAD_MARKET, "badMarket", "No such market." },
|
||||
{ rpcBAD_SECRET, "badSecret", "Secret does not match account." },
|
||||
{ rpcBAD_SEED, "badSeed", "Disallowed seed." },
|
||||
{ rpcBAD_SYNTAX, "badSyntax", "Syntax error." },
|
||||
{ rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." },
|
||||
{ rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." },
|
||||
{ rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." },
|
||||
{ rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." },
|
||||
{ rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." },
|
||||
{ rpcFORBIDDEN, "forbidden", "Bad credentials." },
|
||||
{ rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." },
|
||||
{ rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." },
|
||||
{ rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." },
|
||||
{ rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." },
|
||||
{ rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." },
|
||||
{ rpcINTERNAL, "internal", "Internal error." },
|
||||
{ rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." },
|
||||
{ rpcJSON_RPC, "json_rpc", "JSON-RPC transport error." },
|
||||
{ rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." },
|
||||
{ rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." },
|
||||
{ rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." },
|
||||
{ rpcNICKNAME_MALFORMED, "nicknameMalformed", "Nickname is malformed." },
|
||||
{ rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." },
|
||||
{ rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." },
|
||||
{ rpcNOT_IMPL, "notImpl", "Not implemented." },
|
||||
{ rpcNO_ACCOUNT, "noAccount", "No such account." },
|
||||
{ rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." },
|
||||
{ rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." },
|
||||
{ rpcNO_EVENTS, "noEvents", "Current transport does not support events." },
|
||||
{ rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." },
|
||||
{ rpcNO_NETWORK, "noNetwork", "Network not available." },
|
||||
{ rpcNO_PATH, "noPath", "Unable to find a ripple path." },
|
||||
{ rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." },
|
||||
{ rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress." },
|
||||
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
|
||||
{ rpcNOT_SUPPORTED, "notSupported", "Operation not supported." },
|
||||
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
|
||||
{ rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." },
|
||||
{ rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." },
|
||||
{ rpcPORT_MALFORMED, "portMalformed", "Port is malformed." },
|
||||
{ rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." },
|
||||
{ rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." },
|
||||
{ rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." },
|
||||
{ rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." },
|
||||
{ rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." },
|
||||
{ rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." },
|
||||
{ rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." },
|
||||
{ rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." },
|
||||
{ rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." },
|
||||
{ rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." },
|
||||
{ rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method." },
|
||||
{ rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." },
|
||||
{ rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now." },
|
||||
{ rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server." },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
for (i = NUMBER (errorInfoA); i-- && errorInfoA[i].iError != iError;)
|
||||
;
|
||||
|
||||
jvResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i (iError);
|
||||
jvResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i (iError);
|
||||
jvResult["error_code"] = iError;
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
WriteLog (lsDEBUG, RPCErr) << "rpcError: "
|
||||
<< errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl;
|
||||
}
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
bool isRpcError (Json::Value jvResult)
|
||||
{
|
||||
return jvResult.isObject () && jvResult.isMember ("error");
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
97
modules/ripple_app/rpc/RPCErr.h
Normal file
97
modules/ripple_app/rpc/RPCErr.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCERR__
|
||||
#define __RPCERR__
|
||||
|
||||
enum
|
||||
{
|
||||
rpcSUCCESS = 0,
|
||||
rpcBAD_SYNTAX, // Must be 1 to print usage to command line.
|
||||
rpcJSON_RPC,
|
||||
rpcFORBIDDEN,
|
||||
|
||||
// Error numbers beyond this line are not stable between versions.
|
||||
// Programs should use error tokens.
|
||||
|
||||
// Misc failure
|
||||
rpcLOAD_FAILED,
|
||||
rpcNO_PERMISSION,
|
||||
rpcNO_EVENTS,
|
||||
rpcNOT_STANDALONE,
|
||||
rpcTOO_BUSY,
|
||||
rpcSLOW_DOWN,
|
||||
|
||||
// Networking
|
||||
rpcNO_CLOSED,
|
||||
rpcNO_CURRENT,
|
||||
rpcNO_NETWORK,
|
||||
|
||||
// Ledger state
|
||||
rpcACT_EXISTS,
|
||||
rpcACT_NOT_FOUND,
|
||||
rpcINSUF_FUNDS,
|
||||
rpcLGR_NOT_FOUND,
|
||||
rpcNICKNAME_MISSING,
|
||||
rpcNO_ACCOUNT,
|
||||
rpcNO_PATH,
|
||||
rpcPASSWD_CHANGED,
|
||||
rpcSRC_MISSING,
|
||||
rpcSRC_UNCLAIMED,
|
||||
rpcTXN_NOT_FOUND,
|
||||
rpcWRONG_SEED,
|
||||
|
||||
// Malformed command
|
||||
rpcINVALID_PARAMS,
|
||||
rpcUNKNOWN_COMMAND,
|
||||
rpcNO_PF_REQUEST,
|
||||
|
||||
// Bad parameter
|
||||
rpcACT_BITCOIN,
|
||||
rpcACT_MALFORMED,
|
||||
rpcQUALITY_MALFORMED,
|
||||
rpcBAD_BLOB,
|
||||
rpcBAD_FEATURE,
|
||||
rpcBAD_ISSUER,
|
||||
rpcBAD_MARKET,
|
||||
rpcBAD_SECRET,
|
||||
rpcBAD_SEED,
|
||||
rpcCOMMAND_MISSING,
|
||||
rpcDST_ACT_MALFORMED,
|
||||
rpcDST_ACT_MISSING,
|
||||
rpcDST_AMT_MALFORMED,
|
||||
rpcDST_ISR_MALFORMED,
|
||||
rpcGETS_ACT_MALFORMED,
|
||||
rpcGETS_AMT_MALFORMED,
|
||||
rpcHOST_IP_MALFORMED,
|
||||
rpcLGR_IDXS_INVALID,
|
||||
rpcLGR_IDX_MALFORMED,
|
||||
rpcNICKNAME_MALFORMED,
|
||||
rpcNICKNAME_PERM,
|
||||
rpcPAYS_ACT_MALFORMED,
|
||||
rpcPAYS_AMT_MALFORMED,
|
||||
rpcPORT_MALFORMED,
|
||||
rpcPUBLIC_MALFORMED,
|
||||
rpcSRC_ACT_MALFORMED,
|
||||
rpcSRC_ACT_MISSING,
|
||||
rpcSRC_ACT_NOT_FOUND,
|
||||
rpcSRC_AMT_MALFORMED,
|
||||
rpcSRC_CUR_MALFORMED,
|
||||
rpcSRC_ISR_MALFORMED,
|
||||
|
||||
// Internal error (should never happen)
|
||||
rpcINTERNAL, // Generic internal error.
|
||||
rpcFAIL_GEN_DECRPYT,
|
||||
rpcNOT_IMPL,
|
||||
rpcNOT_SUPPORTED,
|
||||
rpcNO_GEN_DECRPYT,
|
||||
};
|
||||
|
||||
bool isRpcError (Json::Value jvResult);
|
||||
Json::Value rpcError (int iError, Json::Value jvResult = Json::Value (Json::objectValue));
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
3778
modules/ripple_app/rpc/RPCHandler.cpp
Normal file
3778
modules/ripple_app/rpc/RPCHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
191
modules/ripple_app/rpc/RPCHandler.h
Normal file
191
modules/ripple_app/rpc/RPCHandler.h
Normal file
@@ -0,0 +1,191 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCHANDLER__
|
||||
#define __RPCHANDLER__
|
||||
|
||||
#define LEDGER_CURRENT -1
|
||||
#define LEDGER_CLOSED -2
|
||||
#define LEDGER_VALIDATED -3
|
||||
|
||||
// used by the RPCServer or WSDoor to carry out these RPC commands
|
||||
class NetworkOPs;
|
||||
class InfoSub;
|
||||
|
||||
// VFALCO TODO Refactor to abstract interface IRPCHandler
|
||||
//
|
||||
class RPCHandler
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
GUEST,
|
||||
USER,
|
||||
ADMIN,
|
||||
FORBID
|
||||
};
|
||||
|
||||
explicit RPCHandler (NetworkOPs* netOps);
|
||||
|
||||
RPCHandler (NetworkOPs* netOps, InfoSub::pointer infoSub);
|
||||
|
||||
Json::Value doCommand (const Json::Value& jvRequest, int role, LoadType* loadType);
|
||||
|
||||
Json::Value doRpcCommand (const std::string& strCommand, Json::Value const& jvParams, int iRole, LoadType* loadType);
|
||||
|
||||
private:
|
||||
typedef Json::Value (RPCHandler::*doFuncPtr) (
|
||||
Json::Value params,
|
||||
LoadType* loadType,
|
||||
Application::ScopedLockType& MasterLockHolder);
|
||||
|
||||
// VFALCO TODO Document these and give the enumeration a label.
|
||||
enum
|
||||
{
|
||||
optNone = 0,
|
||||
optNetwork = 1, // Need network
|
||||
optCurrent = 2 + optNetwork, // Need current ledger
|
||||
optClosed = 4 + optNetwork, // Need closed ledger
|
||||
};
|
||||
|
||||
// Utilities
|
||||
|
||||
void addSubmitPath (Json::Value& txJSON);
|
||||
|
||||
boost::unordered_set <RippleAddress> parseAccountIds (const Json::Value& jvArray);
|
||||
|
||||
Json::Value transactionSign (Json::Value jvRequest, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh);
|
||||
|
||||
Json::Value lookupLedger (Json::Value jvRequest, Ledger::pointer& lpLedger);
|
||||
|
||||
Json::Value getMasterGenerator (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naRegularSeed,
|
||||
RippleAddress& naMasterGenerator);
|
||||
|
||||
Json::Value authorize (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naRegularSeed,
|
||||
const RippleAddress& naSrcAccountID,
|
||||
RippleAddress& naAccountPublic,
|
||||
RippleAddress& naAccountPrivate,
|
||||
STAmount& saSrcBalance,
|
||||
const STAmount& saFee,
|
||||
AccountState::pointer& asSrc,
|
||||
const RippleAddress& naVerifyGenerator);
|
||||
|
||||
Json::Value accounts (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naMasterGenerator);
|
||||
|
||||
Json::Value accountFromString (
|
||||
Ledger::ref lrLedger,
|
||||
RippleAddress& naAccount,
|
||||
bool& bIndex,
|
||||
const std::string& strIdent,
|
||||
const int iIndex,
|
||||
const bool bStrict);
|
||||
|
||||
Json::Value doAccountInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountLines (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountOffers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountTransactions (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doBookOffers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doConnect (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doConsensusInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doFeature (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doFetchInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doGetCounts (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doInternal (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedger (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerAccept (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerClosed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerCurrent (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerEntry (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerHeader (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogLevel (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogRotate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doNicknameInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doOwnerInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPathFind (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPeers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPing (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProfile (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofCreate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofSolve (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofVerify (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doRandom (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doRipplePathFind (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSMS (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doServerInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh); // for humans
|
||||
Json::Value doServerState (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh); // for machines
|
||||
Json::Value doSessionClose (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSessionOpen (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSign (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doStop (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSubmit (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSubscribe (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTransactionEntry (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTx (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTxHistory (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlAdd (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlDelete (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlFetch (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlList (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlLoad (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlNetwork (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlReset (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlScore (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnsubscribe (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doValidationCreate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doValidationSeed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletAccounts (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletLock (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletPropose (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletSeed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletUnlock (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletVerify (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value doDataDelete (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doDataFetch (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doDataStore (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogin (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NetworkOPs* mNetOps;
|
||||
InfoSub::pointer mInfoSub;
|
||||
|
||||
// VFALCO TODO Create an enumeration for this.
|
||||
int mRole;
|
||||
};
|
||||
|
||||
class RPCInternalHandler
|
||||
{
|
||||
public:
|
||||
typedef Json::Value (*handler_t) (const Json::Value&);
|
||||
|
||||
public:
|
||||
RPCInternalHandler (const std::string& name, handler_t handler);
|
||||
static Json::Value runHandler (const std::string& name, const Json::Value& params);
|
||||
|
||||
private:
|
||||
// VFALCO TODO Replace with a singleton with a well defined interface and
|
||||
// a lock free stack (if necessary).
|
||||
//
|
||||
static RPCInternalHandler* sHeadHandler;
|
||||
|
||||
RPCInternalHandler* mNextHandler;
|
||||
std::string mName;
|
||||
handler_t mHandler;
|
||||
};
|
||||
|
||||
// VFALCO TODO tidy up this loose function
|
||||
int iAdminGet (const Json::Value& jvRequest, const std::string& strRemoteIp);
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
119
modules/ripple_app/rpc/RPCSub.cpp
Normal file
119
modules/ripple_app/rpc/RPCSub.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (RPCSub)
|
||||
|
||||
RPCSub::RPCSub (const std::string& strUrl, const std::string& strUsername, const std::string& strPassword)
|
||||
: mUrl (strUrl), mSSL (false), mUsername (strUsername), mPassword (strPassword), mSending (false)
|
||||
{
|
||||
std::string strScheme;
|
||||
|
||||
if (!parseUrl (strUrl, strScheme, mIp, mPort, mPath))
|
||||
{
|
||||
throw std::runtime_error ("Failed to parse url.");
|
||||
}
|
||||
else if (strScheme == "https")
|
||||
{
|
||||
mSSL = true;
|
||||
}
|
||||
else if (strScheme != "http")
|
||||
{
|
||||
throw std::runtime_error ("Only http and https is supported.");
|
||||
}
|
||||
|
||||
mSeq = 1;
|
||||
|
||||
if (mPort < 0)
|
||||
mPort = mSSL ? 443 : 80;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC sub: ip='%s' port=%d ssl=%d path='%s'")
|
||||
% mIp
|
||||
% mPort
|
||||
% mSSL
|
||||
% mPath);
|
||||
}
|
||||
|
||||
// XXX Could probably create a bunch of send jobs in a single get of the lock.
|
||||
void RPCSub::sendThread ()
|
||||
{
|
||||
Json::Value jvEvent;
|
||||
bool bSend;
|
||||
|
||||
do
|
||||
{
|
||||
{
|
||||
// Obtain the lock to manipulate the queue and change sending.
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
if (mDeque.empty ())
|
||||
{
|
||||
mSending = false;
|
||||
bSend = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::pair<int, Json::Value> pEvent = mDeque.front ();
|
||||
|
||||
mDeque.pop_front ();
|
||||
|
||||
jvEvent = pEvent.second;
|
||||
jvEvent["seq"] = pEvent.first;
|
||||
|
||||
bSend = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Send outside of the lock.
|
||||
if (bSend)
|
||||
{
|
||||
// XXX Might not need this in a try.
|
||||
try
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC calling: %s") % mIp);
|
||||
|
||||
callRPC (
|
||||
getApp().getIOService (),
|
||||
mIp, mPort,
|
||||
mUsername, mPassword,
|
||||
mPath, "event",
|
||||
jvEvent,
|
||||
mSSL);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC exception: %s") % e.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
while (bSend);
|
||||
}
|
||||
|
||||
void RPCSub::send (const Json::Value& jvObj, bool broadcast)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
if (RPC_EVENT_QUEUE_MAX == mDeque.size ())
|
||||
{
|
||||
// Drop the previous event.
|
||||
WriteLog (lsWARNING, RPCSub) << boost::str (boost::format ("callRPC drop"));
|
||||
mDeque.pop_back ();
|
||||
}
|
||||
|
||||
WriteLog (broadcast ? lsDEBUG : lsINFO, RPCSub) << boost::str (boost::format ("callRPC push: %s") % jvObj);
|
||||
|
||||
mDeque.push_back (std::make_pair (mSeq++, jvObj));
|
||||
|
||||
if (!mSending)
|
||||
{
|
||||
// Start a sending thread.
|
||||
mSending = true;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC start"));
|
||||
boost::thread (BIND_TYPE (&RPCSub::sendThread, this)).detach ();
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
65
modules/ripple_app/rpc/RPCSub.h
Normal file
65
modules/ripple_app/rpc/RPCSub.h
Normal file
@@ -0,0 +1,65 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCSUB__
|
||||
#define __RPCSUB__
|
||||
|
||||
#define RPC_EVENT_QUEUE_MAX 32
|
||||
|
||||
// Subscription object for JSON-RPC
|
||||
class RPCSub
|
||||
: public InfoSub
|
||||
, LeakChecked <RPCSub>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<RPCSub> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
RPCSub (const std::string& strUrl, const std::string& strUsername, const std::string& strPassword);
|
||||
|
||||
virtual ~RPCSub ()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Implement overridden functions from base class:
|
||||
void send (const Json::Value& jvObj, bool broadcast);
|
||||
|
||||
void setUsername (const std::string& strUsername)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
mUsername = strUsername;
|
||||
}
|
||||
|
||||
void setPassword (const std::string& strPassword)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
mPassword = strPassword;
|
||||
}
|
||||
|
||||
protected:
|
||||
void sendThread ();
|
||||
|
||||
private:
|
||||
std::string mUrl;
|
||||
std::string mIp;
|
||||
int mPort;
|
||||
bool mSSL;
|
||||
std::string mUsername;
|
||||
std::string mPassword;
|
||||
std::string mPath;
|
||||
|
||||
int mSeq; // Next id to allocate.
|
||||
|
||||
bool mSending; // Sending threead is active.
|
||||
|
||||
std::deque<std::pair<int, Json::Value> > mDeque;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
281
modules/ripple_app/rpc/rpc.cpp
Normal file
281
modules/ripple_app/rpc/rpc.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// Used for logging
|
||||
struct RPC;
|
||||
|
||||
SETUP_LOG (RPC)
|
||||
|
||||
unsigned int const gMaxHTTPHeaderSize = 0x02000000;
|
||||
|
||||
std::string gFormatStr ("v1");
|
||||
|
||||
// VFALCO TODO clean up this nonsense
|
||||
std::string FormatFullVersion ()
|
||||
{
|
||||
return (gFormatStr);
|
||||
}
|
||||
|
||||
|
||||
Json::Value JSONRPCError (int code, const std::string& message)
|
||||
{
|
||||
Json::Value error (Json::objectValue);
|
||||
|
||||
error["code"] = Json::Value (code);
|
||||
error["message"] = Json::Value (message);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP protocol
|
||||
//
|
||||
// This ain't Apache. We're just using HTTP header for the length field
|
||||
// and to be compatible with other JSON-RPC implementations.
|
||||
//
|
||||
|
||||
std::string createHTTPPost (const std::string& strHost, const std::string& strPath, const std::string& strMsg, const std::map<std::string, std::string>& mapRequestHeaders)
|
||||
{
|
||||
std::ostringstream s;
|
||||
|
||||
s << "POST "
|
||||
<< (strPath.empty () ? "/" : strPath)
|
||||
<< " HTTP/1.0\r\n"
|
||||
<< "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion () << "\r\n"
|
||||
<< "Host: " << strHost << "\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "Content-Length: " << strMsg.size () << "\r\n"
|
||||
<< "Accept: application/json\r\n";
|
||||
|
||||
typedef std::map<std::string, std::string>::value_type HeaderType;
|
||||
|
||||
BOOST_FOREACH (const HeaderType & item, mapRequestHeaders)
|
||||
s << item.first << ": " << item.second << "\r\n";
|
||||
|
||||
s << "\r\n" << strMsg;
|
||||
|
||||
return s.str ();
|
||||
}
|
||||
|
||||
std::string rfc1123Time ()
|
||||
{
|
||||
char buffer[64];
|
||||
time_t now;
|
||||
time (&now);
|
||||
struct tm* now_gmt = gmtime (&now);
|
||||
std::string locale (setlocale (LC_TIME, NULL));
|
||||
setlocale (LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
|
||||
strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
|
||||
setlocale (LC_TIME, locale.c_str ());
|
||||
return std::string (buffer);
|
||||
}
|
||||
|
||||
std::string HTTPReply (int nStatus, const std::string& strMsg)
|
||||
{
|
||||
WriteLog (lsTRACE, RPC) << "HTTP Reply " << nStatus << " " << strMsg;
|
||||
|
||||
if (nStatus == 401)
|
||||
return strprintf ("HTTP/1.0 401 Authorization Required\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Server: " SYSTEM_NAME "-json-rpc/%s\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: 296\r\n"
|
||||
"\r\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
||||
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
||||
"<HTML>\r\n"
|
||||
"<HEAD>\r\n"
|
||||
"<TITLE>Error</TITLE>\r\n"
|
||||
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
||||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
||||
"</HTML>\r\n", rfc1123Time ().c_str (), FormatFullVersion ().c_str ());
|
||||
|
||||
std::string strStatus;
|
||||
|
||||
if (nStatus == 200) strStatus = "OK";
|
||||
else if (nStatus == 400) strStatus = "Bad Request";
|
||||
else if (nStatus == 403) strStatus = "Forbidden";
|
||||
else if (nStatus == 404) strStatus = "Not Found";
|
||||
else if (nStatus == 500) strStatus = "Internal Server Error";
|
||||
|
||||
std::string access;
|
||||
|
||||
if (theConfig.RPC_ALLOW_REMOTE) access = "Access-Control-Allow-Origin: *\r\n";
|
||||
else access = "";
|
||||
|
||||
return strprintf (
|
||||
"HTTP/1.1 %d %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"%s"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: application/json; charset=UTF-8\r\n"
|
||||
"Server: " SYSTEM_NAME "-json-rpc/%s\r\n"
|
||||
"\r\n"
|
||||
"%s\r\n",
|
||||
nStatus,
|
||||
strStatus.c_str (),
|
||||
rfc1123Time ().c_str (),
|
||||
access.c_str (),
|
||||
strMsg.size () + 2,
|
||||
SERVER_VERSION,
|
||||
strMsg.c_str ());
|
||||
}
|
||||
|
||||
int ReadHTTPStatus (std::basic_istream<char>& stream)
|
||||
{
|
||||
std::string str;
|
||||
getline (stream, str);
|
||||
std::vector<std::string> vWords;
|
||||
boost::split (vWords, str, boost::is_any_of (" "));
|
||||
|
||||
if (vWords.size () < 2)
|
||||
return 500;
|
||||
|
||||
return atoi (vWords[1].c_str ());
|
||||
}
|
||||
|
||||
int ReadHTTPHeader (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet)
|
||||
{
|
||||
int nLen = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::string str;
|
||||
std::getline (stream, str);
|
||||
|
||||
if (str.empty () || str == "\r")
|
||||
break;
|
||||
|
||||
std::string::size_type nColon = str.find (":");
|
||||
|
||||
if (nColon != std::string::npos)
|
||||
{
|
||||
std::string strHeader = str.substr (0, nColon);
|
||||
boost::trim (strHeader);
|
||||
boost::to_lower (strHeader);
|
||||
std::string strValue = str.substr (nColon + 1);
|
||||
boost::trim (strValue);
|
||||
mapHeadersRet[strHeader] = strValue;
|
||||
|
||||
if (strHeader == "content-length")
|
||||
nLen = atoi (strValue.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
return nLen;
|
||||
}
|
||||
|
||||
int ReadHTTP (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet)
|
||||
{
|
||||
mapHeadersRet.clear ();
|
||||
strMessageRet = "";
|
||||
|
||||
// Read status
|
||||
int nStatus = ReadHTTPStatus (stream);
|
||||
|
||||
// Read header
|
||||
int nLen = ReadHTTPHeader (stream, mapHeadersRet);
|
||||
|
||||
if (nLen < 0 || nLen > gMaxHTTPHeaderSize)
|
||||
return 500;
|
||||
|
||||
// Read message
|
||||
if (nLen > 0)
|
||||
{
|
||||
std::vector<char> vch (nLen);
|
||||
stream.read (&vch[0], nLen);
|
||||
strMessageRet = std::string (vch.begin (), vch.end ());
|
||||
}
|
||||
|
||||
return nStatus;
|
||||
}
|
||||
|
||||
std::string DecodeBase64 (std::string s)
|
||||
{
|
||||
// FIXME: This performs badly
|
||||
BIO* b64, *bmem;
|
||||
|
||||
char* buffer = static_cast<char*> (calloc (s.size (), sizeof (char)));
|
||||
|
||||
b64 = BIO_new (BIO_f_base64 ());
|
||||
BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
bmem = BIO_new_mem_buf (const_cast<char*> (s.data ()), s.size ());
|
||||
bmem = BIO_push (b64, bmem);
|
||||
BIO_read (bmem, buffer, s.size ());
|
||||
BIO_free_all (bmem);
|
||||
|
||||
std::string result (buffer);
|
||||
free (buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HTTPAuthorized (const std::map<std::string, std::string>& mapHeaders)
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it = mapHeaders.find ("authorization");
|
||||
|
||||
if ((it == mapHeaders.end ()) || (it->second.substr (0, 6) != "Basic "))
|
||||
return theConfig.RPC_USER.empty () && theConfig.RPC_PASSWORD.empty ();
|
||||
|
||||
std::string strUserPass64 = it->second.substr (6);
|
||||
boost::trim (strUserPass64);
|
||||
std::string strUserPass = DecodeBase64 (strUserPass64);
|
||||
std::string::size_type nColon = strUserPass.find (":");
|
||||
|
||||
if (nColon == std::string::npos)
|
||||
return false;
|
||||
|
||||
std::string strUser = strUserPass.substr (0, nColon);
|
||||
std::string strPassword = strUserPass.substr (nColon + 1);
|
||||
return (strUser == theConfig.RPC_USER) && (strPassword == theConfig.RPC_PASSWORD);
|
||||
}
|
||||
|
||||
//
|
||||
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
// unspecified (HTTP errors and contents of 'error').
|
||||
//
|
||||
// 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
||||
//
|
||||
|
||||
std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params, const Json::Value& id)
|
||||
{
|
||||
Json::Value request;
|
||||
request["method"] = strMethod;
|
||||
request["params"] = params;
|
||||
request["id"] = id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (request) + "\n";
|
||||
}
|
||||
|
||||
std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id)
|
||||
{
|
||||
Json::Value reply (Json::objectValue);
|
||||
reply["result"] = result;
|
||||
//reply["error"]=error;
|
||||
//reply["id"]=id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (reply) + "\n";
|
||||
}
|
||||
|
||||
void ErrorReply (std::ostream& stream, const Json::Value& objError, const Json::Value& id)
|
||||
{
|
||||
// Send error reply from json-rpc error object
|
||||
int nStatus = 500;
|
||||
int code = objError["code"].asInt ();
|
||||
|
||||
if (code == -32600) nStatus = 400;
|
||||
else if (code == -32601) nStatus = 404;
|
||||
|
||||
std::string strReply = JSONRPCReply (Json::Value (), objError, id);
|
||||
stream << HTTPReply (nStatus, strReply) << std::flush;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
Reference in New Issue
Block a user