diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index b18a3c212c..6cf4ffa9a8 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -503,6 +503,10 @@ int commandLineRPC(const std::vector& vCmd) RPCParser rpParser; Json::Value jvRpcParams(Json::arrayValue); + if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty()) + 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."); + if (vCmd.empty()) return 1; // 1 = print usage. for (int i = 1; i != vCmd.size(); i++) @@ -529,6 +533,10 @@ int commandLineRPC(const std::vector& vCmd) jvParams.append(jvRequest); jvOutput = callRPC( + theConfig.RPC_IP, + theConfig.RPC_PORT, + theConfig.RPC_USER, + theConfig.RPC_PASSWORD, jvRequest.isMember("method") // Allow parser to rewrite method. ? jvRequest["method"].asString() : vCmd[0], @@ -589,25 +597,21 @@ int commandLineRPC(const std::vector& vCmd) return nRet; } -Json::Value callRPC(const std::string& strMethod, const Json::Value& params) +Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, 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. " - "If the file does not exist, create it with owner-readable-only file permissions."); - // Connect to localhost 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); + endpoint(boost::asio::ip::address::from_string(strIp), iPort); boost::asio::ip::tcp::iostream stream; stream.connect(endpoint); if (stream.fail()) throw std::runtime_error("couldn't connect to server"); // HTTP basic authentication - std::string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD); + std::string strUserPass64 = EncodeBase64(strUsername + ":" + strPassword); std::map mapRequestHeaders; mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64; diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index e03572caf1..f17b5b576e 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -42,7 +42,7 @@ public: }; extern int commandLineRPC(const std::vector& vCmd); -extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params); +extern Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strMethod, const Json::Value& params); #endif diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 2707798830..007bcd6b8c 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1502,4 +1502,15 @@ bool NetworkOPs::unsubRTTransactions(InfoSub* ispListener) return !!mSubTransactions.erase(ispListener); } +RPCSub* NetworkOPs::findRpcSub(const std::string& strRpc) +{ + return (RPCSub*)(0); +} + +RPCSub* NetworkOPs::addRpcSub(const std::string& strRpc, RPCSub* rspEntry) +{ + return rspEntry; +} + + // vim:ts=4 diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index b3bd97f405..dfc1ea374d 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -22,6 +22,8 @@ class LedgerConsensus; DEFINE_INSTANCE(InfoSub); +class RPCSub; + class InfoSub : public IS_INSTANCE(InfoSub) { public: @@ -34,12 +36,12 @@ protected: boost::unordered_set mSubAccountInfo; boost::unordered_set mSubAccountTransaction; - boost::mutex mLock; + boost::mutex mLockInfo; public: void insertSubAccountInfo(RippleAddress addr) { - boost::mutex::scoped_lock sl(mLock); + boost::mutex::scoped_lock sl(mLockInfo); mSubAccountInfo.insert(addr); } }; @@ -68,6 +70,8 @@ protected: typedef boost::unordered_map > subSubmitMapType; + typedef boost::unordered_map subRpcMapType; + OperatingMode mMode; bool mNeedNetworkLedger; boost::posix_time::ptime mConnectTime; @@ -97,12 +101,13 @@ protected: subInfoMapType mSubRTAccount; subSubmitMapType mSubmitMap; // TODO: probably dump this + subRpcMapType mRpcSubMap; + boost::unordered_set mSubLedger; // accepted ledgers boost::unordered_set mSubServer; // when server changes connectivity state boost::unordered_set mSubTransactions; // all accepted transactions boost::unordered_set mSubRTTransactions; // all proposed and accepted transactions - void setMode(OperatingMode); Json::Value transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType); @@ -270,6 +275,9 @@ public: bool subRTTransactions(InfoSub* ispListener); bool unsubRTTransactions(InfoSub* ispListener); + + RPCSub* findRpcSub(const std::string& strRpc); + RPCSub* addRpcSub(const std::string& strRpc, RPCSub* rspEntry); }; #endif diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 25deb2cfa8..884d03f49e 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1,5 +1,5 @@ // -// carries out the RPC +// Carries out the RPC. // #include @@ -11,6 +11,7 @@ #include "Log.h" #include "NetworkOPs.h" #include "RPCHandler.h" +#include "RPCSub.h" #include "Application.h" #include "AccountItems.h" #include "Wallet.h" @@ -22,19 +23,18 @@ #include "InstanceCounter.h" #include "Offer.h" - SETUP_LOG(); RPCHandler::RPCHandler(NetworkOPs* netOps) { - mNetOps=netOps; - mInfoSub=NULL; + mNetOps = netOps; + mInfoSub = NULL; } RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub* infoSub) { - mNetOps=netOps; - mInfoSub=infoSub; + mNetOps = netOps; + mInfoSub = infoSub; } // Look up the master public generator for a regular seed so we may index source accounts ids. @@ -2138,8 +2138,45 @@ rt_accounts */ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) { + InfoSub* ispSub; Json::Value jvResult(Json::objectValue); + if (!mInfoSub && !jvRequest.isMember("url")) + { + // Must be a JSON-RPC call. + return rpcError(rpcINVALID_PARAMS); + } + + if (jvRequest.isMember("url")) + { + if (mRole != ADMIN) + return rpcError(rpcNO_PERMISSION); + + std::string strUrl = jvRequest["url"].asString(); + std::string strUsername = jvRequest.isMember("username") ? jvRequest["username"].asString() : ""; + std::string strPassword = jvRequest.isMember("password") ? jvRequest["password"].asString() : ""; + + RPCSub *rspSub = mNetOps->findRpcSub(strUrl); + if (!rspSub) + { + rspSub = mNetOps->addRpcSub(strUrl, new RPCSub(strUrl, strUsername, strPassword)); + } + else + { + if (jvRequest.isMember("username")) + rspSub->setUsername(strUsername); + + if (jvRequest.isMember("password")) + rspSub->setPassword(strPassword); + } + + ispSub = rspSub; + } + else + { + ispSub = mInfoSub; + } + if (jvRequest.isMember("streams")) { for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) @@ -2148,26 +2185,31 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) { std::string streamName=(*it).asString(); - if(streamName=="server") + if (streamName=="server") { - mNetOps->subServer(mInfoSub, jvResult); + mNetOps->subServer(ispSub, jvResult); - } else if(streamName=="ledger") - { - mNetOps->subLedger(mInfoSub, jvResult); - - } else if(streamName=="transactions") - { - mNetOps->subTransactions(mInfoSub); - - } else if(streamName=="rt_transactions") - { - mNetOps->subRTTransactions(mInfoSub); } - else { + else if (streamName=="ledger") + { + mNetOps->subLedger(ispSub, jvResult); + + } + else if (streamName=="transactions") + { + mNetOps->subTransactions(ispSub); + + } + else if (streamName=="rt_transactions") + { + mNetOps->subRTTransactions(ispSub); + } + else + { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); } - } else + } + else { jvResult["error"] = "malformedSteam"; } @@ -2181,14 +2223,15 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) if (usnaAccoundIds.empty()) { jvResult["error"] = "malformedAccount"; - }else + } + else { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - mInfoSub->insertSubAccountInfo(naAccountID); + ispSub->insertSubAccountInfo(naAccountID); } - mNetOps->subAccount(mInfoSub, usnaAccoundIds, true); + mNetOps->subAccount(ispSub, usnaAccoundIds, true); } } @@ -2199,24 +2242,51 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) if (usnaAccoundIds.empty()) { jvResult["error"] = "malformedAccount"; - }else + } + else { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - mInfoSub->insertSubAccountInfo(naAccountID); + ispSub->insertSubAccountInfo(naAccountID); } - mNetOps->subAccount(mInfoSub, usnaAccoundIds, false); + mNetOps->subAccount(ispSub, usnaAccoundIds, false); } } return jvResult; } +// This leaks RPCSub objects for JSON-RPC. Shouldn't matter for anyone sane. Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) { + InfoSub* ispSub; Json::Value jvResult(Json::objectValue); + if (!mInfoSub && !jvRequest.isMember("url")) + { + // Must be a JSON-RPC call. + return rpcError(rpcINVALID_PARAMS); + } + + if (jvRequest.isMember("url")) + { + if (mRole != ADMIN) + return rpcError(rpcNO_PERMISSION); + + std::string strUrl = jvRequest["url"].asString(); + + RPCSub *rspSub = mNetOps->findRpcSub(strUrl); + if (!rspSub) + return jvResult; + + ispSub = rspSub; + } + else + { + ispSub = mInfoSub; + } + if (jvRequest.isMember("streams")) { for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) @@ -2225,23 +2295,28 @@ Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) { std::string streamName=(*it).asString(); - if(streamName=="server") + if (streamName == "server") { - mNetOps->unsubServer(mInfoSub); - }else if(streamName=="ledger") + mNetOps->unsubServer(ispSub); + } + else if (streamName == "ledger") { - mNetOps->unsubLedger(mInfoSub); - }else if(streamName=="transactions") + mNetOps->unsubLedger(ispSub); + } + else if (streamName == "transactions") { - mNetOps->unsubTransactions(mInfoSub); - }else if(streamName=="rt_transactions") + mNetOps->unsubTransactions(ispSub); + } + else if (streamName == "rt_transactions") { - mNetOps->unsubRTTransactions(mInfoSub); - }else + mNetOps->unsubRTTransactions(ispSub); + } + else { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); } - }else + } + else { jvResult["error"] = "malformedSteam"; } @@ -2255,14 +2330,15 @@ Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) if (usnaAccoundIds.empty()) { jvResult["error"] = "malformedAccount"; - }else + } + else { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - mInfoSub->insertSubAccountInfo(naAccountID); + ispSub->insertSubAccountInfo(naAccountID); } - mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,true); + mNetOps->unsubAccount(ispSub, usnaAccoundIds,true); } } @@ -2273,14 +2349,15 @@ Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) if (usnaAccoundIds.empty()) { jvResult["error"] = "malformedAccount"; - }else + } + else { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - mInfoSub->insertSubAccountInfo(naAccountID); + ispSub->insertSubAccountInfo(naAccountID); } - mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,false); + mNetOps->unsubAccount(ispSub, usnaAccoundIds,false); } } diff --git a/src/cpp/ripple/RPCSub.cpp b/src/cpp/ripple/RPCSub.cpp new file mode 100644 index 0000000000..633feaaa77 --- /dev/null +++ b/src/cpp/ripple/RPCSub.cpp @@ -0,0 +1,12 @@ +#include "RPCSub.h" + +RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const std::string& strPassword) + : mUrl(strUrl), mUsername(strUsername), mPassword(strPassword) +{ + +} + +void RPCSub::send(const Json::Value& jvObj) +{ + +} diff --git a/src/cpp/ripple/RPCSub.h b/src/cpp/ripple/RPCSub.h new file mode 100644 index 0000000000..7971ee1211 --- /dev/null +++ b/src/cpp/ripple/RPCSub.h @@ -0,0 +1,37 @@ +#ifndef __RPCSUB__ +#define __RPCSUB__ + +#include "NetworkOPs.h" + +// Subscription object for JSON-RPC +class RPCSub : public InfoSub +{ + std::string mUrl; + std::string mIp; + int mPort; + std::string mUsername; + std::string mPassword; + +public: + RPCSub(const std::string& strUrl, const std::string& strUsername, const std::string& strPassword); + + // Implement overridden functions from base class: + void send(const Json::Value& jvObj); + + 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; + } +}; + +#endif +// vim:ts=4