]> Git Repo - VerusCoin.git/blame - src/bitcoinrpc.cpp
Merge pull request #2157 from Diapolo/Qt_addrlist
[VerusCoin.git] / src / bitcoinrpc.cpp
CommitLineData
69d605f4 1// Copyright (c) 2010 Satoshi Nakamoto
88216419 2// Copyright (c) 2009-2012 The Bitcoin developers
69d605f4 3// Distributed under the MIT/X11 software license, see the accompanying
3a25a2b9 4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
69d605f4 5
0eeb4f5d 6#include "init.h"
e3bc5698
JG
7#include "util.h"
8#include "sync.h"
ed6d0b5f 9#include "ui_interface.h"
10254401 10#include "base58.h"
e46704dd 11#include "bitcoinrpc.h"
3731f578 12#include "db.h"
ed6d0b5f 13
69d605f4 14#include <boost/asio.hpp>
c1ecab81 15#include <boost/asio/ip/v6_only.hpp>
914dc012 16#include <boost/bind.hpp>
95d888a6 17#include <boost/filesystem.hpp>
a0780ba0 18#include <boost/foreach.hpp>
69d605f4
WL
19#include <boost/iostreams/concepts.hpp>
20#include <boost/iostreams/stream.hpp>
0eeb4f5d 21#include <boost/algorithm/string.hpp>
30ab2c9c 22#include <boost/lexical_cast.hpp>
9247134e 23#include <boost/asio/ssl.hpp>
0eeb4f5d 24#include <boost/filesystem/fstream.hpp>
914dc012 25#include <boost/shared_ptr.hpp>
a0780ba0 26#include <list>
5ce4c2a2 27
69d605f4
WL
28using namespace std;
29using namespace boost;
30using namespace boost::asio;
31using namespace json_spirit;
32
33void ThreadRPCServer2(void* parg);
69d605f4 34
f81ce5bd
GA
35static std::string strRPCUserColonPass;
36
74335bd3
LD
37const Object emptyobj;
38
e9205293
DJS
39void ThreadRPCServer3(void* parg);
40
b202d430 41static inline unsigned short GetDefaultRPCPort()
42{
43 return GetBoolArg("-testnet", false) ? 18332 : 8332;
44}
45
69d605f4
WL
46Object JSONRPCError(int code, const string& message)
47{
48 Object error;
49 error.push_back(Pair("code", code));
50 error.push_back(Pair("message", message));
51 return error;
52}
53
899d373b 54void RPCTypeCheck(const Array& params,
cc6dfd1f
GA
55 const list<Value_type>& typesExpected,
56 bool fAllowNull)
899d373b 57{
dab9fa7f 58 unsigned int i = 0;
899d373b
GA
59 BOOST_FOREACH(Value_type t, typesExpected)
60 {
61 if (params.size() <= i)
62 break;
63
cc6dfd1f
GA
64 const Value& v = params[i];
65 if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
899d373b
GA
66 {
67 string err = strprintf("Expected type %s, got %s",
68 Value_type_name[t], Value_type_name[v.type()]);
738835d7 69 throw JSONRPCError(RPC_TYPE_ERROR, err);
899d373b
GA
70 }
71 i++;
72 }
73}
74
75void RPCTypeCheck(const Object& o,
cc6dfd1f
GA
76 const map<string, Value_type>& typesExpected,
77 bool fAllowNull)
899d373b
GA
78{
79 BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
80 {
81 const Value& v = find_value(o, t.first);
cc6dfd1f 82 if (!fAllowNull && v.type() == null_type)
738835d7 83 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str()));
cc6dfd1f
GA
84
85 if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
899d373b
GA
86 {
87 string err = strprintf("Expected type %s for %s, got %s",
88 Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
738835d7 89 throw JSONRPCError(RPC_TYPE_ERROR, err);
899d373b
GA
90 }
91 }
92}
93
bde280b9 94int64 AmountFromValue(const Value& value)
69d605f4
WL
95{
96 double dAmount = value.get_real();
97 if (dAmount <= 0.0 || dAmount > 21000000.0)
738835d7 98 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
bde280b9 99 int64 nAmount = roundint64(dAmount * COIN);
69d605f4 100 if (!MoneyRange(nAmount))
738835d7 101 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
69d605f4
WL
102 return nAmount;
103}
104
bde280b9 105Value ValueFromAmount(int64 amount)
69d605f4
WL
106{
107 return (double)amount / (double)COIN;
108}
109
bdab0cf5 110std::string HexBits(unsigned int nBits)
34f87889
LD
111{
112 union {
113 int32_t nBits;
114 char cBits[4];
115 } uBits;
116 uBits.nBits = htonl((int32_t)nBits);
117 return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
118}
119
69d605f4 120
74335bd3 121
69d605f4
WL
122///
123/// Note: This interface may still be subject to change.
124///
125
9862229d 126string CRPCTable::help(string strCommand) const
69d605f4 127{
69d605f4
WL
128 string strRet;
129 set<rpcfn_type> setDone;
9862229d 130 for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
69d605f4 131 {
9862229d 132 const CRPCCommand *pcmd = mi->second;
dc42bf52 133 string strMethod = mi->first;
69d605f4 134 // We already filter duplicates, but these deprecated screw up the sort order
47f48a65 135 if (strMethod.find("label") != string::npos)
69d605f4
WL
136 continue;
137 if (strCommand != "" && strMethod != strCommand)
138 continue;
139 try
140 {
141 Array params;
dc42bf52 142 rpcfn_type pfn = pcmd->actor;
69d605f4
WL
143 if (setDone.insert(pfn).second)
144 (*pfn)(params, true);
145 }
146 catch (std::exception& e)
147 {
148 // Help text is returned in an exception
149 string strHelp = string(e.what());
150 if (strCommand == "")
ab9dc75a 151 if (strHelp.find('\n') != string::npos)
69d605f4
WL
152 strHelp = strHelp.substr(0, strHelp.find('\n'));
153 strRet += strHelp + "\n";
154 }
155 }
156 if (strRet == "")
157 strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
158 strRet = strRet.substr(0,strRet.size()-1);
159 return strRet;
160}
161
9862229d
PW
162Value help(const Array& params, bool fHelp)
163{
164 if (fHelp || params.size() > 1)
165 throw runtime_error(
166 "help [command]\n"
167 "List commands, or get help for a command.");
168
169 string strCommand;
170 if (params.size() > 0)
171 strCommand = params[0].get_str();
172
173 return tableRPC.help(strCommand);
174}
175
69d605f4
WL
176
177Value stop(const Array& params, bool fHelp)
178{
92467073 179 // Accept the deprecated and ignored 'detach´ boolean argument
3731f578 180 if (fHelp || params.size() > 1)
69d605f4 181 throw runtime_error(
92467073
PW
182 "stop\n"
183 "Stop Bitcoin server.");
69d605f4 184 // Shutdown will take long enough that the response should get back
9247134e 185 StartShutdown();
ff0ee876 186 return "Bitcoin server stopping";
69d605f4
WL
187}
188
189
69d605f4
WL
190
191//
192// Call Table
193//
194
dc42bf52 195
e46704dd 196static const CRPCCommand vRPCCommands[] =
0e1d3551
JG
197{ // name function safemd unlocked
198 // ------------------------ ----------------------- ------ --------
199 { "help", &help, true, true },
200 { "stop", &stop, true, true },
201 { "getblockcount", &getblockcount, true, false },
202 { "getconnectioncount", &getconnectioncount, true, false },
203 { "getpeerinfo", &getpeerinfo, true, false },
204 { "getdifficulty", &getdifficulty, true, false },
205 { "getgenerate", &getgenerate, true, false },
206 { "setgenerate", &setgenerate, true, false },
207 { "gethashespersec", &gethashespersec, true, false },
208 { "getinfo", &getinfo, true, false },
209 { "getmininginfo", &getmininginfo, true, false },
210 { "getnewaddress", &getnewaddress, true, false },
211 { "getaccountaddress", &getaccountaddress, true, false },
212 { "setaccount", &setaccount, true, false },
213 { "getaccount", &getaccount, false, false },
214 { "getaddressesbyaccount", &getaddressesbyaccount, true, false },
215 { "sendtoaddress", &sendtoaddress, false, false },
216 { "getreceivedbyaddress", &getreceivedbyaddress, false, false },
217 { "getreceivedbyaccount", &getreceivedbyaccount, false, false },
218 { "listreceivedbyaddress", &listreceivedbyaddress, false, false },
219 { "listreceivedbyaccount", &listreceivedbyaccount, false, false },
220 { "backupwallet", &backupwallet, true, false },
221 { "keypoolrefill", &keypoolrefill, true, false },
222 { "walletpassphrase", &walletpassphrase, true, false },
223 { "walletpassphrasechange", &walletpassphrasechange, false, false },
224 { "walletlock", &walletlock, true, false },
225 { "encryptwallet", &encryptwallet, false, false },
226 { "validateaddress", &validateaddress, true, false },
227 { "getbalance", &getbalance, false, false },
228 { "move", &movecmd, false, false },
229 { "sendfrom", &sendfrom, false, false },
230 { "sendmany", &sendmany, false, false },
231 { "addmultisigaddress", &addmultisigaddress, false, false },
34226be7 232 { "createmultisig", &createmultisig, true, true },
0e1d3551
JG
233 { "getrawmempool", &getrawmempool, true, false },
234 { "getblock", &getblock, false, false },
235 { "getblockhash", &getblockhash, false, false },
236 { "gettransaction", &gettransaction, false, false },
237 { "listtransactions", &listtransactions, false, false },
22dfd735 238 { "listaddressgroupings", &listaddressgroupings, false, false },
0e1d3551
JG
239 { "signmessage", &signmessage, false, false },
240 { "verifymessage", &verifymessage, false, false },
241 { "getwork", &getwork, true, false },
242 { "listaccounts", &listaccounts, false, false },
243 { "settxfee", &settxfee, false, false },
244 { "getblocktemplate", &getblocktemplate, true, false },
245 { "submitblock", &submitblock, false, false },
246 { "listsinceblock", &listsinceblock, false, false },
247 { "dumpprivkey", &dumpprivkey, false, false },
248 { "importprivkey", &importprivkey, false, false },
249 { "listunspent", &listunspent, false, false },
250 { "getrawtransaction", &getrawtransaction, false, false },
251 { "createrawtransaction", &createrawtransaction, false, false },
252 { "decoderawtransaction", &decoderawtransaction, false, false },
253 { "signrawtransaction", &signrawtransaction, false, false },
254 { "sendrawtransaction", &sendrawtransaction, false, false },
beeb5761
PW
255 { "gettxoutsetinfo", &gettxoutsetinfo, true, false },
256 { "gettxout", &gettxout, true, false },
fdbb537d
JG
257 { "lockunspent", &lockunspent, false, false },
258 { "listlockunspent", &listlockunspent, false, false },
69d605f4 259};
69d605f4 260
9862229d 261CRPCTable::CRPCTable()
dc42bf52 262{
dc42bf52
JG
263 unsigned int vcidx;
264 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
265 {
e46704dd 266 const CRPCCommand *pcmd;
69d605f4 267
dc42bf52
JG
268 pcmd = &vRPCCommands[vcidx];
269 mapCommands[pcmd->name] = pcmd;
270 }
271}
69d605f4 272
9862229d
PW
273const CRPCCommand *CRPCTable::operator[](string name) const
274{
275 map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
276 if (it == mapCommands.end())
277 return NULL;
278 return (*it).second;
279}
69d605f4
WL
280
281//
282// HTTP protocol
283//
284// This ain't Apache. We're just using HTTP header for the length field
285// and to be compatible with other JSON-RPC implementations.
286//
287
288string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
289{
290 ostringstream s;
291 s << "POST / HTTP/1.1\r\n"
292 << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
293 << "Host: 127.0.0.1\r\n"
294 << "Content-Type: application/json\r\n"
295 << "Content-Length: " << strMsg.size() << "\r\n"
3552497a 296 << "Connection: close\r\n"
69d605f4
WL
297 << "Accept: application/json\r\n";
298 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
299 s << item.first << ": " << item.second << "\r\n";
300 s << "\r\n" << strMsg;
301
302 return s.str();
303}
304
305string rfc1123Time()
306{
307 char buffer[64];
308 time_t now;
309 time(&now);
310 struct tm* now_gmt = gmtime(&now);
311 string locale(setlocale(LC_TIME, NULL));
814efd6f 312 setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
69d605f4
WL
313 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
314 setlocale(LC_TIME, locale.c_str());
315 return string(buffer);
316}
317
96c52695 318static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
69d605f4 319{
285746d3 320 if (nStatus == HTTP_UNAUTHORIZED)
69d605f4
WL
321 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
322 "Date: %s\r\n"
323 "Server: bitcoin-json-rpc/%s\r\n"
324 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
325 "Content-Type: text/html\r\n"
326 "Content-Length: 296\r\n"
327 "\r\n"
328 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
329 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
330 "<HTML>\r\n"
331 "<HEAD>\r\n"
332 "<TITLE>Error</TITLE>\r\n"
333 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
334 "</HEAD>\r\n"
335 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
336 "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
ae81b82f 337 const char *cStatus;
285746d3
WL
338 if (nStatus == HTTP_OK) cStatus = "OK";
339 else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
340 else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
341 else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
342 else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
ae81b82f 343 else cStatus = "";
69d605f4
WL
344 return strprintf(
345 "HTTP/1.1 %d %s\r\n"
346 "Date: %s\r\n"
96c52695 347 "Connection: %s\r\n"
d210f4f5 348 "Content-Length: %"PRIszu"\r\n"
69d605f4
WL
349 "Content-Type: application/json\r\n"
350 "Server: bitcoin-json-rpc/%s\r\n"
351 "\r\n"
352 "%s",
353 nStatus,
ae81b82f 354 cStatus,
69d605f4 355 rfc1123Time().c_str(),
96c52695 356 keepalive ? "keep-alive" : "close",
69d605f4
WL
357 strMsg.size(),
358 FormatFullVersion().c_str(),
359 strMsg.c_str());
360}
361
fcf234fc
JG
362bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
363 string& http_method, string& http_uri)
364{
365 string str;
366 getline(stream, str);
367
368 // HTTP request line is space-delimited
369 vector<string> vWords;
370 boost::split(vWords, str, boost::is_any_of(" "));
371 if (vWords.size() < 2)
372 return false;
373
374 // HTTP methods permitted: GET, POST
375 http_method = vWords[0];
376 if (http_method != "GET" && http_method != "POST")
377 return false;
378
379 // HTTP URI must be an absolute path, relative to current host
380 http_uri = vWords[1];
381 if (http_uri.size() == 0 || http_uri[0] != '/')
382 return false;
383
384 // parse proto, if present
385 string strProto = "";
386 if (vWords.size() > 2)
387 strProto = vWords[2];
388
389 proto = 0;
390 const char *ver = strstr(strProto.c_str(), "HTTP/1.");
391 if (ver != NULL)
392 proto = atoi(ver+7);
393
394 return true;
395}
396
96c52695 397int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
69d605f4
WL
398{
399 string str;
400 getline(stream, str);
401 vector<string> vWords;
402 boost::split(vWords, str, boost::is_any_of(" "));
403 if (vWords.size() < 2)
285746d3 404 return HTTP_INTERNAL_SERVER_ERROR;
96c52695
DJS
405 proto = 0;
406 const char *ver = strstr(str.c_str(), "HTTP/1.");
407 if (ver != NULL)
408 proto = atoi(ver+7);
69d605f4
WL
409 return atoi(vWords[1].c_str());
410}
411
2306dc4b 412int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
69d605f4
WL
413{
414 int nLen = 0;
415 loop
416 {
417 string str;
418 std::getline(stream, str);
419 if (str.empty() || str == "\r")
420 break;
421 string::size_type nColon = str.find(":");
422 if (nColon != string::npos)
423 {
424 string strHeader = str.substr(0, nColon);
425 boost::trim(strHeader);
426 boost::to_lower(strHeader);
427 string strValue = str.substr(nColon+1);
428 boost::trim(strValue);
429 mapHeadersRet[strHeader] = strValue;
430 if (strHeader == "content-length")
431 nLen = atoi(strValue.c_str());
432 }
433 }
434 return nLen;
435}
436
2306dc4b
JG
437int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
438 string>& mapHeadersRet, string& strMessageRet,
439 int nProto)
69d605f4
WL
440{
441 mapHeadersRet.clear();
442 strMessageRet = "";
443
69d605f4 444 // Read header
2306dc4b 445 int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
1d8c7a95 446 if (nLen < 0 || nLen > (int)MAX_SIZE)
285746d3 447 return HTTP_INTERNAL_SERVER_ERROR;
69d605f4
WL
448
449 // Read message
450 if (nLen > 0)
451 {
452 vector<char> vch(nLen);
453 stream.read(&vch[0], nLen);
454 strMessageRet = string(vch.begin(), vch.end());
455 }
456
96c52695
DJS
457 string sConHdr = mapHeadersRet["connection"];
458
459 if ((sConHdr != "close") && (sConHdr != "keep-alive"))
460 {
461 if (nProto >= 1)
462 mapHeadersRet["connection"] = "keep-alive";
463 else
464 mapHeadersRet["connection"] = "close";
465 }
466
2306dc4b 467 return HTTP_OK;
69d605f4
WL
468}
469
69d605f4
WL
470bool HTTPAuthorized(map<string, string>& mapHeaders)
471{
472 string strAuth = mapHeaders["authorization"];
473 if (strAuth.substr(0,6) != "Basic ")
474 return false;
475 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
476 string strUserPass = DecodeBase64(strUserPass64);
f81ce5bd 477 return strUserPass == strRPCUserColonPass;
69d605f4
WL
478}
479
480//
481// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
482// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
483// unspecified (HTTP errors and contents of 'error').
484//
485// 1.0 spec: http://json-rpc.org/wiki/specification
486// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
487// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
488//
489
490string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
491{
492 Object request;
493 request.push_back(Pair("method", strMethod));
494 request.push_back(Pair("params", params));
495 request.push_back(Pair("id", id));
496 return write_string(Value(request), false) + "\n";
497}
498
c6494d82 499Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
69d605f4
WL
500{
501 Object reply;
502 if (error.type() != null_type)
503 reply.push_back(Pair("result", Value::null));
504 else
505 reply.push_back(Pair("result", result));
506 reply.push_back(Pair("error", error));
507 reply.push_back(Pair("id", id));
c6494d82
JG
508 return reply;
509}
510
511string JSONRPCReply(const Value& result, const Value& error, const Value& id)
512{
513 Object reply = JSONRPCReplyObj(result, error, id);
69d605f4
WL
514 return write_string(Value(reply), false) + "\n";
515}
516
517void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
518{
519 // Send error reply from json-rpc error object
285746d3 520 int nStatus = HTTP_INTERNAL_SERVER_ERROR;
69d605f4 521 int code = find_value(objError, "code").get_int();
285746d3
WL
522 if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
523 else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
69d605f4 524 string strReply = JSONRPCReply(Value::null, objError, id);
96c52695 525 stream << HTTPReply(nStatus, strReply, false) << std::flush;
69d605f4
WL
526}
527
43b6dafa 528bool ClientAllowed(const boost::asio::ip::address& address)
69d605f4 529{
43b6dafa
GS
530 // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
531 if (address.is_v6()
532 && (address.to_v6().is_v4_compatible()
533 || address.to_v6().is_v4_mapped()))
534 return ClientAllowed(address.to_v6().to_v4());
535
536 if (address == asio::ip::address_v4::loopback()
7cc2ceae
GS
537 || address == asio::ip::address_v6::loopback()
538 || (address.is_v4()
814efd6f 539 // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
7cc2ceae 540 && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
69d605f4 541 return true;
43b6dafa
GS
542
543 const string strAddress = address.to_string();
69d605f4
WL
544 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
545 BOOST_FOREACH(string strAllow, vAllow)
546 if (WildcardMatch(strAddress, strAllow))
547 return true;
548 return false;
549}
550
69d605f4
WL
551//
552// IOStream device that speaks SSL but can also speak non-SSL
553//
a0780ba0 554template <typename Protocol>
69d605f4
WL
555class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
556public:
a0780ba0 557 SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
69d605f4
WL
558 {
559 fUseSSL = fUseSSLIn;
560 fNeedHandshake = fUseSSLIn;
561 }
562
563 void handshake(ssl::stream_base::handshake_type role)
564 {
565 if (!fNeedHandshake) return;
566 fNeedHandshake = false;
567 stream.handshake(role);
568 }
569 std::streamsize read(char* s, std::streamsize n)
570 {
571 handshake(ssl::stream_base::server); // HTTPS servers read first
572 if (fUseSSL) return stream.read_some(asio::buffer(s, n));
573 return stream.next_layer().read_some(asio::buffer(s, n));
574 }
575 std::streamsize write(const char* s, std::streamsize n)
576 {
577 handshake(ssl::stream_base::client); // HTTPS clients write first
578 if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
579 return asio::write(stream.next_layer(), asio::buffer(s, n));
580 }
581 bool connect(const std::string& server, const std::string& port)
582 {
583 ip::tcp::resolver resolver(stream.get_io_service());
584 ip::tcp::resolver::query query(server.c_str(), port.c_str());
585 ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
586 ip::tcp::resolver::iterator end;
587 boost::system::error_code error = asio::error::host_not_found;
588 while (error && endpoint_iterator != end)
589 {
590 stream.lowest_layer().close();
591 stream.lowest_layer().connect(*endpoint_iterator++, error);
592 }
593 if (error)
594 return false;
595 return true;
596 }
597
598private:
599 bool fNeedHandshake;
600 bool fUseSSL;
a0780ba0 601 asio::ssl::stream<typename Protocol::socket>& stream;
69d605f4 602};
69d605f4 603
e9205293
DJS
604class AcceptedConnection
605{
a0780ba0
GS
606public:
607 virtual ~AcceptedConnection() {}
608
609 virtual std::iostream& stream() = 0;
610 virtual std::string peer_address_to_string() const = 0;
611 virtual void close() = 0;
612};
e9205293 613
a0780ba0
GS
614template <typename Protocol>
615class AcceptedConnectionImpl : public AcceptedConnection
616{
617public:
618 AcceptedConnectionImpl(
619 asio::io_service& io_service,
620 ssl::context &context,
621 bool fUseSSL) :
622 sslStream(io_service, context),
623 _d(sslStream, fUseSSL),
624 _stream(_d)
625 {
626 }
627
628 virtual std::iostream& stream()
629 {
630 return _stream;
631 }
632
633 virtual std::string peer_address_to_string() const
634 {
635 return peer.address().to_string();
636 }
e9205293 637
a0780ba0
GS
638 virtual void close()
639 {
640 _stream.close();
641 }
642
643 typename Protocol::endpoint peer;
644 asio::ssl::stream<typename Protocol::socket> sslStream;
645
646private:
647 SSLIOStreamDevice<Protocol> _d;
648 iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
e9205293
DJS
649};
650
69d605f4
WL
651void ThreadRPCServer(void* parg)
652{
96931d6f
GS
653 // Make this thread recognisable as the RPC listener
654 RenameThread("bitcoin-rpclist");
655
69d605f4
WL
656 try
657 {
e9205293 658 vnThreadsRunning[THREAD_RPCLISTENER]++;
69d605f4 659 ThreadRPCServer2(parg);
e9205293 660 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
661 }
662 catch (std::exception& e) {
e9205293 663 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
664 PrintException(&e, "ThreadRPCServer()");
665 } catch (...) {
e9205293 666 vnThreadsRunning[THREAD_RPCLISTENER]--;
69d605f4
WL
667 PrintException(NULL, "ThreadRPCServer()");
668 }
1d764d63 669 printf("ThreadRPCServer exited\n");
69d605f4
WL
670}
671
914dc012 672// Forward declaration required for RPCListen
a0780ba0
GS
673template <typename Protocol, typename SocketAcceptorService>
674static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
675 ssl::context& context,
676 bool fUseSSL,
677 AcceptedConnection* conn,
678 const boost::system::error_code& error);
679
680/**
681 * Sets up I/O resources to accept and handle a new connection.
682 */
a0780ba0
GS
683template <typename Protocol, typename SocketAcceptorService>
684static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
685 ssl::context& context,
686 const bool fUseSSL)
687{
914dc012 688 // Accept connection
a0780ba0 689 AcceptedConnectionImpl<Protocol>* conn = new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL);
914dc012
GS
690
691 acceptor->async_accept(
692 conn->sslStream.lowest_layer(),
693 conn->peer,
a0780ba0 694 boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
914dc012
GS
695 acceptor,
696 boost::ref(context),
697 fUseSSL,
698 conn,
699 boost::asio::placeholders::error));
700}
701
702/**
703 * Accept and handle incoming connection.
704 */
a0780ba0
GS
705template <typename Protocol, typename SocketAcceptorService>
706static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
707 ssl::context& context,
708 const bool fUseSSL,
709 AcceptedConnection* conn,
710 const boost::system::error_code& error)
711{
712 vnThreadsRunning[THREAD_RPCLISTENER]++;
713
814efd6f 714 // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
460d8786 715 if (error != asio::error::operation_aborted
ad25804f
GS
716 && acceptor->is_open())
717 RPCListen(acceptor, context, fUseSSL);
914dc012 718
a0780ba0
GS
719 AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
720
914dc012
GS
721 // TODO: Actually handle errors
722 if (error)
723 {
724 delete conn;
725 }
726
727 // Restrict callers by IP. It is important to
728 // do this before starting client thread, to filter out
729 // certain DoS and misbehaving clients.
a0780ba0
GS
730 else if (tcp_conn
731 && !ClientAllowed(tcp_conn->peer.address()))
914dc012
GS
732 {
733 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
734 if (!fUseSSL)
285746d3 735 conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
914dc012
GS
736 delete conn;
737 }
738
739 // start HTTP client thread
4d1d94c5 740 else if (!NewThread(ThreadRPCServer3, conn)) {
914dc012
GS
741 printf("Failed to create RPC server client thread\n");
742 delete conn;
743 }
744
745 vnThreadsRunning[THREAD_RPCLISTENER]--;
746}
747
69d605f4
WL
748void ThreadRPCServer2(void* parg)
749{
750 printf("ThreadRPCServer started\n");
751
f81ce5bd 752 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
7e1610d5
JG
753 if ((mapArgs["-rpcpassword"] == "") ||
754 (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"]))
69d605f4 755 {
b04f301c
GM
756 unsigned char rand_pwd[32];
757 RAND_bytes(rand_pwd, 32);
69d605f4
WL
758 string strWhatAmI = "To use bitcoind";
759 if (mapArgs.count("-server"))
760 strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
761 else if (mapArgs.count("-daemon"))
762 strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
ab1b288f 763 uiInterface.ThreadSafeMessageBox(strprintf(
5a60b66a
WL
764 _("%s, you must set a rpcpassword in the configuration file:\n %s\n"
765 "It is recommended you use the following random password:\n"
766 "rpcuser=bitcoinrpc\n"
767 "rpcpassword=%s\n"
768 "(you do not need to remember this password)\n"
7e1610d5 769 "The username and password MUST NOT be the same.\n"
5a60b66a 770 "If the file does not exist, create it with owner-readable-only file permissions.\n"),
69d605f4 771 strWhatAmI.c_str(),
ee12c3d6 772 GetConfigFile().string().c_str(),
5a60b66a 773 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
5350ea41 774 "", CClientUIInterface::MSG_ERROR);
9247134e 775 StartShutdown();
69d605f4
WL
776 return;
777 }
778
914dc012 779 const bool fUseSSL = GetBoolArg("-rpcssl");
69d605f4
WL
780
781 asio::io_service io_service;
fbf9df2e 782
69d605f4
WL
783 ssl::context context(io_service, ssl::context::sslv23);
784 if (fUseSSL)
785 {
786 context.set_options(ssl::context::no_sslv2);
93fb7489
PK
787
788 filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
789 if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
ee12c3d6 790 if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
93fb7489
PK
791 else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
792
793 filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
794 if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
ee12c3d6 795 if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
93fb7489
PK
796 else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
797
798 string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
799 SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
69d605f4 800 }
69d605f4 801
c1ecab81
GS
802 // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
803 const bool loopback = !mapArgs.count("-rpcallowip");
804 asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
b202d430 805 ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort()));
c1d79812
PW
806 boost::system::error_code v6_only_error;
807 boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service));
c1ecab81 808
7cf3d2cc
MC
809 boost::signals2::signal<void ()> StopRequests;
810
c1d79812
PW
811 bool fListening = false;
812 std::string strerr;
c1ecab81
GS
813 try
814 {
ad25804f
GS
815 acceptor->open(endpoint.protocol());
816 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
c1ecab81
GS
817
818 // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
ad25804f 819 acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
c1ecab81 820
ad25804f
GS
821 acceptor->bind(endpoint);
822 acceptor->listen(socket_base::max_connections);
c1ecab81 823
ad25804f
GS
824 RPCListen(acceptor, context, fUseSSL);
825 // Cancel outstanding listen-requests for this acceptor when shutting down
7cf3d2cc 826 StopRequests.connect(signals2::slot<void ()>(
ad25804f
GS
827 static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
828 .track(acceptor));
c1ecab81 829
c1d79812
PW
830 fListening = true;
831 }
832 catch(boost::system::system_error &e)
833 {
95e625d2 834 strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what());
c1d79812
PW
835 }
836
837 try {
c1ecab81 838 // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
c1d79812 839 if (!fListening || loopback || v6_only_error)
c1ecab81
GS
840 {
841 bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
842 endpoint.address(bindAddress);
843
ad25804f
GS
844 acceptor.reset(new ip::tcp::acceptor(io_service));
845 acceptor->open(endpoint.protocol());
846 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
847 acceptor->bind(endpoint);
848 acceptor->listen(socket_base::max_connections);
849
850 RPCListen(acceptor, context, fUseSSL);
851 // Cancel outstanding listen-requests for this acceptor when shutting down
7cf3d2cc 852 StopRequests.connect(signals2::slot<void ()>(
ad25804f
GS
853 static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
854 .track(acceptor));
c1d79812
PW
855
856 fListening = true;
c1ecab81
GS
857 }
858 }
859 catch(boost::system::system_error &e)
860 {
463a1cab 861 strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what());
c1d79812
PW
862 }
863
864 if (!fListening) {
5350ea41 865 uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
07368a9e 866 StartShutdown();
c1ecab81
GS
867 return;
868 }
69d605f4 869
914dc012 870 vnThreadsRunning[THREAD_RPCLISTENER]--;
18c4beb0
MC
871 while (!fShutdown)
872 io_service.run_one();
914dc012 873 vnThreadsRunning[THREAD_RPCLISTENER]++;
7cf3d2cc 874 StopRequests();
e9205293
DJS
875}
876
c6494d82
JG
877class JSONRequest
878{
879public:
880 Value id;
881 string strMethod;
882 Array params;
883
884 JSONRequest() { id = Value::null; }
885 void parse(const Value& valRequest);
886};
887
888void JSONRequest::parse(const Value& valRequest)
889{
890 // Parse request
891 if (valRequest.type() != obj_type)
738835d7 892 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
c6494d82
JG
893 const Object& request = valRequest.get_obj();
894
895 // Parse id now so errors from here on will have the id
896 id = find_value(request, "id");
897
898 // Parse method
899 Value valMethod = find_value(request, "method");
900 if (valMethod.type() == null_type)
738835d7 901 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
c6494d82 902 if (valMethod.type() != str_type)
738835d7 903 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
c6494d82 904 strMethod = valMethod.get_str();
44427fa8 905 if (strMethod != "getwork" && strMethod != "getblocktemplate")
c6494d82
JG
906 printf("ThreadRPCServer method=%s\n", strMethod.c_str());
907
908 // Parse params
909 Value valParams = find_value(request, "params");
910 if (valParams.type() == array_type)
911 params = valParams.get_array();
912 else if (valParams.type() == null_type)
913 params = Array();
914 else
738835d7 915 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
c6494d82
JG
916}
917
61338901
JG
918static Object JSONRPCExecOne(const Value& req)
919{
920 Object rpc_result;
921
922 JSONRequest jreq;
923 try {
924 jreq.parse(req);
925
926 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
927 rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
928 }
929 catch (Object& objError)
930 {
931 rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
932 }
933 catch (std::exception& e)
934 {
935 rpc_result = JSONRPCReplyObj(Value::null,
738835d7 936 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
61338901
JG
937 }
938
939 return rpc_result;
940}
941
942static string JSONRPCExecBatch(const Array& vReq)
943{
944 Array ret;
945 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
946 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
947
948 return write_string(Value(ret), false) + "\n";
949}
950
4e97a9d9
MC
951static CCriticalSection cs_THREAD_RPCHANDLER;
952
e9205293
DJS
953void ThreadRPCServer3(void* parg)
954{
96931d6f
GS
955 // Make this thread recognisable as the RPC handler
956 RenameThread("bitcoin-rpchand");
957
4e97a9d9
MC
958 {
959 LOCK(cs_THREAD_RPCHANDLER);
960 vnThreadsRunning[THREAD_RPCHANDLER]++;
961 }
e9205293 962 AcceptedConnection *conn = (AcceptedConnection *) parg;
69d605f4 963
96c52695
DJS
964 bool fRun = true;
965 loop {
966 if (fShutdown || !fRun)
967 {
a0780ba0 968 conn->close();
96c52695 969 delete conn;
4e97a9d9
MC
970 {
971 LOCK(cs_THREAD_RPCHANDLER);
972 --vnThreadsRunning[THREAD_RPCHANDLER];
973 }
96c52695
DJS
974 return;
975 }
69d605f4 976
2306dc4b 977 int nProto = 0;
69d605f4 978 map<string, string> mapHeaders;
fcf234fc
JG
979 string strRequest, strMethod, strURI;
980
981 // Read HTTP request line
982 if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI))
983 break;
69d605f4 984
2306dc4b
JG
985 // Read HTTP message headers and body
986 ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto);
69d605f4
WL
987
988 // Check authorization
989 if (mapHeaders.count("authorization") == 0)
990 {
285746d3 991 conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
e9205293 992 break;
69d605f4
WL
993 }
994 if (!HTTPAuthorized(mapHeaders))
995 {
a0780ba0 996 printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str());
b04f301c
GM
997 /* Deter brute-forcing short passwords.
998 If this results in a DOS the user really
999 shouldn't have their RPC port exposed.*/
1000 if (mapArgs["-rpcpassword"].size() < 20)
1001 Sleep(250);
69d605f4 1002
285746d3 1003 conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
e9205293 1004 break;
69d605f4 1005 }
96c52695
DJS
1006 if (mapHeaders["connection"] == "close")
1007 fRun = false;
69d605f4 1008
c6494d82 1009 JSONRequest jreq;
69d605f4
WL
1010 try
1011 {
1012 // Parse request
1013 Value valRequest;
61338901 1014 if (!read_string(strRequest, valRequest))
738835d7 1015 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
69d605f4 1016
61338901
JG
1017 string strReply;
1018
1019 // singleton request
1020 if (valRequest.type() == obj_type) {
1021 jreq.parse(valRequest);
c6494d82 1022
61338901 1023 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
460c51fd 1024
61338901
JG
1025 // Send reply
1026 strReply = JSONRPCReply(result, Value::null, jreq.id);
1027
1028 // array of requests
1029 } else if (valRequest.type() == array_type)
1030 strReply = JSONRPCExecBatch(valRequest.get_array());
1031 else
738835d7 1032 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
ea0796bd 1033
285746d3 1034 conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush;
69d605f4
WL
1035 }
1036 catch (Object& objError)
1037 {
c6494d82 1038 ErrorReply(conn->stream(), objError, jreq.id);
e9205293 1039 break;
69d605f4
WL
1040 }
1041 catch (std::exception& e)
1042 {
738835d7 1043 ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
e9205293 1044 break;
69d605f4
WL
1045 }
1046 }
96c52695 1047
e9205293 1048 delete conn;
4e97a9d9
MC
1049 {
1050 LOCK(cs_THREAD_RPCHANDLER);
1051 vnThreadsRunning[THREAD_RPCHANDLER]--;
1052 }
69d605f4
WL
1053}
1054
460c51fd
WL
1055json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
1056{
1057 // Find method
1058 const CRPCCommand *pcmd = tableRPC[strMethod];
1059 if (!pcmd)
738835d7 1060 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
69d605f4 1061
460c51fd
WL
1062 // Observe safe mode
1063 string strWarning = GetWarnings("rpc");
1064 if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
1065 !pcmd->okSafeMode)
738835d7 1066 throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
460c51fd
WL
1067
1068 try
1069 {
1070 // Execute
1071 Value result;
1072 {
0e1d3551
JG
1073 if (pcmd->unlocked)
1074 result = pcmd->actor(params, false);
1075 else {
1076 LOCK2(cs_main, pwalletMain->cs_wallet);
1077 result = pcmd->actor(params, false);
1078 }
460c51fd
WL
1079 }
1080 return result;
1081 }
1082 catch (std::exception& e)
1083 {
738835d7 1084 throw JSONRPCError(RPC_MISC_ERROR, e.what());
460c51fd
WL
1085 }
1086}
69d605f4
WL
1087
1088
1089Object CallRPC(const string& strMethod, const Array& params)
1090{
1091 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
1092 throw runtime_error(strprintf(
1093 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
1094 "If the file does not exist, create it with owner-readable-only file permissions."),
ee12c3d6 1095 GetConfigFile().string().c_str()));
69d605f4
WL
1096
1097 // Connect to localhost
1098 bool fUseSSL = GetBoolArg("-rpcssl");
69d605f4
WL
1099 asio::io_service io_service;
1100 ssl::context context(io_service, ssl::context::sslv23);
1101 context.set_options(ssl::context::no_sslv2);
a0780ba0
GS
1102 asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
1103 SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
1104 iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
b202d430 1105 if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort()))))
69d605f4 1106 throw runtime_error("couldn't connect to server");
69d605f4
WL
1107
1108 // HTTP basic authentication
1109 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
1110 map<string, string> mapRequestHeaders;
1111 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
1112
1113 // Send request
1114 string strRequest = JSONRPCRequest(strMethod, params, 1);
1115 string strPost = HTTPPost(strRequest, mapRequestHeaders);
1116 stream << strPost << std::flush;
1117
2306dc4b
JG
1118 // Receive HTTP reply status
1119 int nProto = 0;
1120 int nStatus = ReadHTTPStatus(stream, nProto);
1121
1122 // Receive HTTP reply message headers and body
69d605f4
WL
1123 map<string, string> mapHeaders;
1124 string strReply;
2306dc4b
JG
1125 ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
1126
285746d3 1127 if (nStatus == HTTP_UNAUTHORIZED)
69d605f4 1128 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
285746d3 1129 else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
69d605f4
WL
1130 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
1131 else if (strReply.empty())
1132 throw runtime_error("no response from server");
1133
1134 // Parse reply
1135 Value valReply;
1136 if (!read_string(strReply, valReply))
1137 throw runtime_error("couldn't parse reply from server");
1138 const Object& reply = valReply.get_obj();
1139 if (reply.empty())
1140 throw runtime_error("expected reply to have result, error and id properties");
1141
1142 return reply;
1143}
1144
1145
1146
1147
1148template<typename T>
cc6dfd1f 1149void ConvertTo(Value& value, bool fAllowNull=false)
69d605f4 1150{
cc6dfd1f
GA
1151 if (fAllowNull && value.type() == null_type)
1152 return;
69d605f4
WL
1153 if (value.type() == str_type)
1154 {
1155 // reinterpret string as unquoted json value
1156 Value value2;
18871d47
GA
1157 string strJSON = value.get_str();
1158 if (!read_string(strJSON, value2))
1159 throw runtime_error(string("Error parsing JSON:")+strJSON);
cc6dfd1f
GA
1160 ConvertTo<T>(value2, fAllowNull);
1161 value = value2;
69d605f4
WL
1162 }
1163 else
1164 {
1165 value = value.get_value<T>();
1166 }
1167}
1168
460c51fd
WL
1169// Convert strings to command-specific RPC representation
1170Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
1171{
1172 Array params;
1173 BOOST_FOREACH(const std::string &param, strParams)
1174 params.push_back(param);
1175
1176 int n = params.size();
1177
1178 //
1179 // Special case non-string parameter types
1180 //
3731f578 1181 if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]);
460c51fd
WL
1182 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
1183 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1184 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
1185 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
1186 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1187 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1188 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1189 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
1190 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1191 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
1192 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1193 if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1194 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
1195 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
1196 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
1197 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
1198 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1199 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
1200 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1201 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
44427fa8 1202 if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
460c51fd 1203 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
98474d3d
GA
1204 if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
1205 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
1206 if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1207 if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
34226be7
GA
1208 if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1209 if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
a2709fad
GA
1210 if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
1211 if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
92735bca 1212 if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
a2709fad
GA
1213 if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1214 if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
1215 if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
cc6dfd1f
GA
1216 if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
1217 if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
beeb5761
PW
1218 if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
1219 if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
fdbb537d
JG
1220 if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
1221 if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
5dfbd4ff 1222 if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
98474d3d 1223
460c51fd
WL
1224 return params;
1225}
1226
69d605f4
WL
1227int CommandLineRPC(int argc, char *argv[])
1228{
1229 string strPrint;
1230 int nRet = 0;
1231 try
1232 {
1233 // Skip switches
1234 while (argc > 1 && IsSwitchChar(argv[1][0]))
1235 {
1236 argc--;
1237 argv++;
1238 }
1239
1240 // Method
1241 if (argc < 2)
1242 throw runtime_error("too few parameters");
1243 string strMethod = argv[1];
1244
1245 // Parameters default to strings
460c51fd
WL
1246 std::vector<std::string> strParams(&argv[2], &argv[argc]);
1247 Array params = RPCConvertValues(strMethod, strParams);
69d605f4
WL
1248
1249 // Execute
1250 Object reply = CallRPC(strMethod, params);
1251
1252 // Parse reply
1253 const Value& result = find_value(reply, "result");
1254 const Value& error = find_value(reply, "error");
69d605f4
WL
1255
1256 if (error.type() != null_type)
1257 {
1258 // Error
1259 strPrint = "error: " + write_string(error, false);
1260 int code = find_value(error.get_obj(), "code").get_int();
1261 nRet = abs(code);
1262 }
1263 else
1264 {
1265 // Result
1266 if (result.type() == null_type)
1267 strPrint = "";
1268 else if (result.type() == str_type)
1269 strPrint = result.get_str();
1270 else
1271 strPrint = write_string(result, true);
1272 }
1273 }
1274 catch (std::exception& e)
1275 {
1276 strPrint = string("error: ") + e.what();
1277 nRet = 87;
1278 }
1279 catch (...)
1280 {
1281 PrintException(NULL, "CommandLineRPC()");
1282 }
1283
1284 if (strPrint != "")
1285 {
69d605f4 1286 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
69d605f4
WL
1287 }
1288 return nRet;
1289}
1290
1291
1292
1293
1294#ifdef TEST
1295int main(int argc, char *argv[])
1296{
1297#ifdef _MSC_VER
814efd6f 1298 // Turn off Microsoft heap dump noise
69d605f4
WL
1299 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1300 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
1301#endif
1302 setbuf(stdin, NULL);
1303 setbuf(stdout, NULL);
1304 setbuf(stderr, NULL);
1305
1306 try
1307 {
1308 if (argc >= 2 && string(argv[1]) == "-server")
1309 {
1310 printf("server ready\n");
1311 ThreadRPCServer(NULL);
1312 }
1313 else
1314 {
1315 return CommandLineRPC(argc, argv);
1316 }
1317 }
1318 catch (std::exception& e) {
1319 PrintException(&e, "main()");
1320 } catch (...) {
1321 PrintException(NULL, "main()");
1322 }
1323 return 0;
1324}
1325#endif
e46704dd
PW
1326
1327const CRPCTable tableRPC;
This page took 0.407483 seconds and 4 git commands to generate.