diff --git a/CallRPC.cpp b/CallRPC.cpp index cc72821757..9d6fdfd49f 100644 --- a/CallRPC.cpp +++ b/CallRPC.cpp @@ -1,25 +1,22 @@ -#include "CallRPC.h" -#include "RPC.h" -#include "Config.h" -#include "BitcoinUtil.h" +#include +#include #include #include #include #include -#include -#include "string.h" -#include - #include -//#include #include -//#include -//#include -//#include -using namespace std; +#include "json/value.h" +#include "json/reader.h" + +#include "CallRPC.h" +#include "RPC.h" +#include "Config.h" +#include "BitcoinUtil.h" + using namespace boost::asio; inline bool isSwitchChar(char c) @@ -31,8 +28,8 @@ inline bool isSwitchChar(char c) #endif } -string EncodeBase64(string s) -{ +std::string EncodeBase64(std::string s) +{ // FIXME: This performs terribly BIO *b64, *bmem; BUF_MEM *bptr; @@ -44,16 +41,15 @@ string EncodeBase64(string s) BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); - string result(bptr->data, bptr->length); + std::string result(bptr->data, bptr->length); BIO_free_all(b64); return result; } -#if 0 int commandLineRPC(int argc, char *argv[]) { - string strPrint; + std::string strPrint; int nRet = 0; try { @@ -66,106 +62,106 @@ int commandLineRPC(int argc, char *argv[]) if(argc < 2) return(0); - string strMethod = argv[1]; + std::string strMethod = argv[1]; // Parameters default to strings - json_spirit::Array params; + Json::Value params(Json::arrayValue); for (int i = 2; i < argc; i++) - params.push_back(argv[i]); + params.append(argv[i]); // Execute - json_spirit::Object reply = callRPC(strMethod, params); + Json::Value reply = callRPC(strMethod, params); // Parse reply - const json_spirit::Value& result = find_value(reply, "result"); - const json_spirit::Value& error = find_value(reply, "error"); + Json::Value result=reply.get("result", Json::Value()); + Json::Value error=reply.get("error", Json::Value()); - if(error.type() != json_spirit::null_type) + if(!error.isNull()) { // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); + strPrint = "error: " + error.toStyledString(); + int code = error["code"].asInt(); nRet = abs(code); - }else + } + else { // Result - if (result.type() == json_spirit::null_type) + if (result.isNull()) strPrint = ""; - else if (result.type() == json_spirit::str_type) - strPrint = result.get_str(); + else if (result.isString()) + strPrint = result.asString(); else - strPrint = write_string(result, true); + strPrint = result.toStyledString(); } } catch (std::exception& e) { - strPrint = string("error: ") + e.what(); + strPrint = std::string("error: ") + e.what(); nRet = 87; } catch (...) { - cout << "Exception CommandLineRPC()" << endl; + std::cout << "Exception CommandLineRPC()" << std::endl; } if(strPrint != "") { - cout << strPrint << endl; + std::cout << strPrint << std::endl; } return nRet; } -json_spirit::Object callRPC(const string& strMethod, const json_spirit::Array& params) +Json::Value callRPC(const std::string& strMethod, const Json::Value& params) { if(theConfig.RPC_USER == "" && theConfig.RPC_PASSWORD == "") - throw 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 - cout << "Connecting to port:" << theConfig.RPC_PORT << endl; + std::cout << "Connecting to port:" << theConfig.RPC_PORT << std::endl; ip::tcp::endpoint endpoint( ip::address::from_string("127.0.0.1"), theConfig.RPC_PORT); ip::tcp::iostream stream; stream.connect(endpoint); if(stream.fail()) - throw runtime_error("couldn't connect to server"); + throw std::runtime_error("couldn't connect to server"); // HTTP basic authentication - string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + std::string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD); + std::map mapRequestHeaders; + mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64; // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - cout << "send request " << strMethod << " : " << strRequest << endl; - string strPost = createHTTPPost(strRequest, mapRequestHeaders); + std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1)); + std::cout << "send request " << strMethod << " : " << strRequest << std::endl; + std::string strPost = createHTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - cout << "post " << strPost << endl; + std::cout << "post " << strPost << std::endl; // Receive reply - map mapHeaders; - string strReply; + std::map mapHeaders; + std::string strReply; int nStatus = ReadHTTP(stream, mapHeaders, strReply); if (nStatus == 401) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + throw std::runtime_error(strprintf("server returned HTTP error %d", nStatus)); else if (strReply.empty()) - throw runtime_error("no response from server"); + throw std::runtime_error("no response from server"); // Parse reply - json_spirit::Value valReply; - if (!json_spirit::read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const json_spirit::Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); + 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"); - return reply; + return valReply; } -#endif diff --git a/CallRPC.h b/CallRPC.h index f1d4c497e2..fa30b28deb 100644 --- a/CallRPC.h +++ b/CallRPC.h @@ -1,4 +1,4 @@ -#include "json/json_spirit_value.h" +#include "json/value.h" extern int commandLineRPC(int argc, char *argv[]); -extern json_spirit::Object callRPC(const std::string& strMethod, const json_spirit::Array& params); \ No newline at end of file +extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params); \ No newline at end of file diff --git a/Makefile b/Makefile index 912e844d10..4126b72994 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Distributed under the MIT/X11 software license, see the accompanying # file license.txt or http://www.opensource.org/licenses/mit-license.php. -CXX=g++ -I/packages/openssl-1.0.0/include +CXX=g++ -I/packages/openssl-1.0.0/include -Wall -Wno-sign-compare -Wno-char-subscripts DEFS= @@ -72,12 +72,14 @@ SRCS= keystore.cpp BitcoinUtil.cpp \ Application.cpp TimingService.cpp KnownNodeList.cpp ConnectionPool.cpp Peer.cpp \ PeerDoor.cpp RPCDoor.cpp RPCServer.cpp rpc.cpp Conversion.cpp RequestParser.cpp HashedObject.cpp \ UniqueNodeList.cpp PubKeyCache.cpp SHAMapDiff.cpp DeterministicKeys.cpp LedgerMaster.cpp \ - LedgerHistory.cpp NetworkOPs.cpp + LedgerHistory.cpp NetworkOPs.cpp CallRPC.cpp DBSRCS= SqliteDatabase.cpp database.cpp UTILSRCS= pugixml.cpp +JSONSRCS= json_reader.cpp json_value.cpp json_writer.cpp + # Application.cpp HttpReply.cpp main.cpp RPCCommands.cpp \ # BitcoinUtil.cpp keystore.cpp NewcoinAddress.cpp rpc.cpp UniqueNodeList.cpp \ # CallRPC.cpp KnownNodeList.cpp PackedMessage.cpp RPCDoor.cpp ValidationCollection.cpp \ @@ -88,6 +90,7 @@ UTILSRCS= pugixml.cpp # database/linux/mysqldatabase.cpp database/database.cpp database/SqliteDatabase.cpp OBJS= $(SRCS:%.cpp=%.o) $(DBSRCS:%.cpp=database/%.o) $(UTILSRCS:%.cpp=util/%.o) newcoin.pb.o +OBJS+= $(JSONSRCS:%.cpp=json/%.o) #cryptopp/obj/sha.o cryptopp/obj/cpu.o all: newcoind @@ -107,8 +110,9 @@ newcoind: $(OBJS) clean: -rm -f newcoind - -rm -f *.o + -rm -f *.o database/*.o util/*.o -rm -f headers.h.gch -rm -f newcoin.pb.* + -rm -f .dep include .dep diff --git a/RPC.h b/RPC.h index 3ef3d91888..051bd70975 100644 --- a/RPC.h +++ b/RPC.h @@ -1,3 +1,7 @@ +#include +#include + +#include "json/value.h" enum http_status_type { @@ -19,11 +23,17 @@ enum http_status_type service_unavailable = 503 }; -#if 0 -extern std::string JSONRPCRequest(const std::string& strMethod, const json_spirit::Array& params, const json_spirit::Value& id); -extern std::string createHTTPPost(const std::string& strMsg, const std::map& mapRequestHeaders); -extern int ReadHTTP(std::basic_istream& stream, std::map& mapHeadersRet, std::string& strMessageRet); +extern std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params, + const Json::Value& id); + +extern std::string createHTTPPost(const std::string& strMsg, + const std::map& mapRequestHeaders); + +extern int ReadHTTP(std::basic_istream& stream, + std::map& mapHeadersRet, std::string& strMessageRet); + extern std::string HTTPReply(int nStatus, const std::string& strMsg); -extern std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id); -extern json_spirit::Object JSONRPCError(int code, const std::string& message); -#endif + +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); diff --git a/rpc.cpp b/rpc.cpp index ab8cb5fac1..e5c185423d 100644 --- a/rpc.cpp +++ b/rpc.cpp @@ -1,6 +1,3 @@ -#include "RPC.h" -#include "BitcoinUtil.h" -#include "Config.h" #include #include @@ -11,7 +8,13 @@ #include #include -using namespace std; +#include "json/value.h" +#include "json/writer.h" + +#include "RPC.h" +#include "BitcoinUtil.h" +#include "Config.h" + using namespace boost; using namespace boost::asio; @@ -19,13 +22,12 @@ using namespace boost::asio; -#if 0 -Object JSONRPCError(int code, const string& message) +Json::Value JSONRPCError(int code, const std::string& message) { - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; + Json::Value error(Json::arrayValue); + error["code"]=Json::Value(code); + error["message"]=Json::Value(message); + return error; } @@ -36,173 +38,174 @@ Object JSONRPCError(int code, const string& message) // and to be compatible with other JSON-RPC implementations. // -string createHTTPPost(const string& strMsg, const map& mapRequestHeaders) +std::string createHTTPPost(const std::string& strMsg, const std::map& mapRequestHeaders) { - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: coin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Accept: application/json\r\n"; + std::ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: coin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; - typedef const pair HeaderType; - BOOST_FOREACH(HeaderType& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; + typedef const std::pair HeaderType; + BOOST_FOREACH(HeaderType& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; - return s.str(); + return s.str(); } -string rfc1123Time() +std::string rfc1123Time() { - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - 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 string(buffer); + 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); } -string HTTPReply(int nStatus, const string& strMsg) +std::string HTTPReply(int nStatus, const std::string& strMsg) { - cout << "HTTP Reply " << nStatus << " " << strMsg << endl; + std::cout << "HTTP Reply " << nStatus << " " << strMsg << std::endl; - if (nStatus == 401) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: bitcoin-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" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - 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"; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: close\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - strStatus.c_str(), - rfc1123Time().c_str(), - strMsg.size(), - theConfig.VERSION_STR.c_str(), - strMsg.c_str()); + if (nStatus == 401) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: coin-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" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\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"; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Server: coin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + strStatus.c_str(), + rfc1123Time().c_str(), + strMsg.size(), + theConfig.VERSION_STR.c_str(), + strMsg.c_str()); } int ReadHTTPStatus(std::basic_istream& stream) { - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return 500; - return atoi(vWords[1].c_str()); + std::string str; + getline(stream, str); + std::vector 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& stream, map& mapHeadersRet) +int ReadHTTPHeader(std::basic_istream& stream, std::map& mapHeadersRet) { - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; + int nLen = 0; + loop + { + 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& stream, map& mapHeadersRet, string& strMessageRet) +int ReadHTTP(std::basic_istream& stream, std::map& mapHeadersRet, + std::string& strMessageRet) { - mapHeadersRet.clear(); - strMessageRet = ""; + mapHeadersRet.clear(); + strMessageRet = ""; - // Read status - int nStatus = ReadHTTPStatus(stream); + // Read status + int nStatus = ReadHTTPStatus(stream); - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > MAX_SIZE) - return 500; + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > MAX_SIZE) + return 500; - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } + // Read message + if (nLen > 0) + { + std::vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = std::string(vch.begin(), vch.end()); + } - return nStatus; + return nStatus; } -string DecodeBase64(string s) -{ - BIO *b64, *bmem; +std::string DecodeBase64(std::string s) +{ // FIXME: This performs badly + BIO *b64, *bmem; - char* buffer = static_cast(calloc(s.size(), sizeof(char))); + char* buffer = static_cast(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(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); - string result(buffer); - free(buffer); - return result; + std::string result(buffer); + free(buffer); + return result; } /* -bool HTTPAuthorized(map& mapHeaders) +bool HTTPAuthorized(map& mapHeaders) { - string strAuth = mapHeaders["authorization"]; - if (strAuth.substr(0,6) != "Basic ") - return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - string::size_type nColon = strUserPass.find(":"); - if (nColon == string::npos) - return false; - string strUser = strUserPass.substr(0, nColon); - string strPassword = strUserPass.substr(nColon+1); - return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); + std::string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + std::string strUserPass64 = strAuth.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 == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); }*/ // @@ -215,62 +218,36 @@ bool HTTPAuthorized(map& mapHeaders) // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx // -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params, const Json::Value& id) { - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; + Json::Value request; + request["method"]=strMethod; + request["params"]=params; + request["id"]=id; + Json::FastWriter writer; + return writer.write(request) + "\n"; } -string JSONRPCReply(const Value& result, const Value& error, const Value& id) +std::string JSONRPCReply(const Json::Value& result, const Json::Value& error, const Json::Value& id) { - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return write_string(Value(reply), false) + "\n"; + Json::Value reply; + if (!error.isNull()) + reply["result"]=Json::Value(); + else + reply["result"]=result; + reply["error"]=error; + reply["id"]=id; + Json::FastWriter writer; + return writer.write(reply) + "\n"; } -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +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 = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; + // 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; } - - - - - - - -template -void ConvertTo(Value& value) -{ - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - if (!read_string(value.get_str(), value2)) - throw runtime_error("type mismatch"); - value = value2.get_value(); - } - else - { - value = value.get_value(); - } -} - - - - -#endif