]> Git Repo - VerusCoin.git/blobdiff - src/rpcprotocol.cpp
Fix exception
[VerusCoin.git] / src / rpcprotocol.cpp
index c4d6f06b12c1060c574750c2ac495423be40ba27..ac26bbdaa045f81ab78e8304cb7c095b5ea94b3c 100644 (file)
@@ -5,7 +5,7 @@
 
 #include "rpcprotocol.h"
 
-#include "clientversion.h"
+#include "random.h"
 #include "tinyformat.h"
 #include "util.h"
 #include "utilstrencodings.h"
 #include "version.h"
 
 #include <stdint.h>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/asio.hpp>
-#include <boost/asio/ssl.hpp>
-#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/foreach.hpp>
-#include <boost/iostreams/concepts.hpp>
-#include <boost/iostreams/stream.hpp>
-#include <boost/shared_ptr.hpp>
-#include "json/json_spirit_writer_template.h"
+#include <fstream>
 
 using namespace std;
-using namespace boost;
-using namespace boost::asio;
-using namespace json_spirit;
-
-//! Number of bytes to allocate and read at most at once in post data
-const size_t POST_READ_SIZE = 256 * 1024;
 
 /**
- * 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.
+ * 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://jsonrpc.org/historical/json-rpc-over-http.html
  */
 
-string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
+string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id)
 {
-    ostringstream s;
-    s << "POST / HTTP/1.1\r\n"
-      << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
-      << "Host: 127.0.0.1\r\n"
-      << "Content-Type: application/json\r\n"
-      << "Content-Length: " << strMsg.size() << "\r\n"
-      << "Connection: close\r\n"
-      << "Accept: application/json\r\n";
-    BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
-        s << item.first << ": " << item.second << "\r\n";
-    s << "\r\n" << strMsg;
-
-    return s.str();
+    UniValue request(UniValue::VOBJ);
+    request.push_back(Pair("method", strMethod));
+    request.push_back(Pair("params", params));
+    request.push_back(Pair("id", id));
+    return request.write() + "\n";
 }
 
-static string rfc1123Time()
+UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
 {
-    return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
+    UniValue reply(UniValue::VOBJ);
+    if (!error.isNull())
+        reply.push_back(Pair("result", NullUniValue));
+    else
+        reply.push_back(Pair("result", result));
+    reply.push_back(Pair("error", error));
+    reply.push_back(Pair("id", id));
+    return reply;
 }
 
-static const char *httpStatusDescription(int nStatus)
+string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id)
 {
-    switch (nStatus) {
-        case HTTP_OK: return "OK";
-        case HTTP_BAD_REQUEST: return "Bad Request";
-        case HTTP_FORBIDDEN: return "Forbidden";
-        case HTTP_NOT_FOUND: return "Not Found";
-        case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
-        default: return "";
-    }
+    UniValue reply = JSONRPCReplyObj(result, error, id);
+    return reply.write() + "\n";
 }
 
-string HTTPError(int nStatus, bool keepalive, bool headersOnly)
+UniValue JSONRPCError(int code, const string& message)
 {
-    if (nStatus == HTTP_UNAUTHORIZED)
-        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"
-            "<!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(), FormatFullVersion());
-
-    return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
-                     headersOnly, "text/plain");
+    UniValue error(UniValue::VOBJ);
+    error.push_back(Pair("code", code));
+    error.push_back(Pair("message", message));
+    return error;
 }
 
-string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char *contentType)
-{
-    return strprintf(
-            "HTTP/1.1 %d %s\r\n"
-            "Date: %s\r\n"
-            "Connection: %s\r\n"
-            "Content-Length: %u\r\n"
-            "Content-Type: %s\r\n"
-            "Server: bitcoin-json-rpc/%s\r\n"
-            "\r\n",
-        nStatus,
-        httpStatusDescription(nStatus),
-        rfc1123Time(),
-        keepalive ? "keep-alive" : "close",
-        contentLength,
-        contentType,
-        FormatFullVersion());
-}
+/** Username used when cookie authentication is in use (arbitrary, only for
+ * recognizability in debugging/logging purposes)
+ */
+static const std::string COOKIEAUTH_USER = "__cookie__";
+/** Default name for auth cookie file */
+static const std::string COOKIEAUTH_FILE = ".cookie";
 
-string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
-                 bool headersOnly, const char *contentType)
+boost::filesystem::path GetAuthCookieFile()
 {
-    if (headersOnly)
-    {
-        return HTTPReplyHeader(nStatus, keepalive, 0, contentType);
-    } else {
-        return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg;
-    }
+    boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
+    if (!path.is_complete()) path = GetDataDir() / path;
+    return path;
 }
 
-bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
-                         string& http_method, string& http_uri)
+bool GenerateAuthCookie(std::string *cookie_out)
 {
-    string str;
-    getline(stream, str);
-
-    // HTTP request line is space-delimited
-    vector<string> vWords;
-    boost::split(vWords, str, boost::is_any_of(" "));
-    if (vWords.size() < 2)
+    unsigned char rand_pwd[32];
+    GetRandBytes(rand_pwd, 32);
+    std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
+
+    /** the umask determines what permissions are used to create this file -
+     * these are set to 077 in init.cpp unless overridden with -sysperms.
+     */
+    std::ofstream file;
+    boost::filesystem::path filepath = GetAuthCookieFile();
+    file.open(filepath.string().c_str());
+    if (!file.is_open()) {
+        LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
         return false;
+    }
+    file << cookie;
+    file.close();
+    LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
 
-    // HTTP methods permitted: GET, POST
-    http_method = vWords[0];
-    if (http_method != "GET" && http_method != "POST")
-        return false;
-
-    // HTTP URI must be an absolute path, relative to current host
-    http_uri = vWords[1];
-    if (http_uri.size() == 0 || http_uri[0] != '/')
-        return false;
-
-    // parse proto, if present
-    string strProto = "";
-    if (vWords.size() > 2)
-        strProto = vWords[2];
-
-    proto = 0;
-    const char *ver = strstr(strProto.c_str(), "HTTP/1.");
-    if (ver != NULL)
-        proto = atoi(ver+7);
-
+    if (cookie_out)
+        *cookie_out = cookie;
     return true;
 }
 
-int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
+bool GetAuthCookie(std::string *cookie_out)
 {
-    string str;
-    getline(stream, str);
-    vector<string> vWords;
-    boost::split(vWords, str, boost::is_any_of(" "));
-    if (vWords.size() < 2)
-        return HTTP_INTERNAL_SERVER_ERROR;
-    proto = 0;
-    const char *ver = strstr(str.c_str(), "HTTP/1.");
-    if (ver != NULL)
-        proto = atoi(ver+7);
-    return atoi(vWords[1].c_str());
-}
+    std::ifstream file;
+    std::string cookie;
+    boost::filesystem::path filepath = GetAuthCookieFile();
+    file.open(filepath.string().c_str());
+    if (!file.is_open())
+        return false;
+    std::getline(file, cookie);
+    file.close();
 
-int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
-{
-    int nLen = 0;
-    while (true)
-    {
-        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;
+    if (cookie_out)
+        *cookie_out = cookie;
+    return true;
 }
 
-
-int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
-                    string>& mapHeadersRet, string& strMessageRet,
-                    int nProto, size_t max_size)
+void DeleteAuthCookie()
 {
-    mapHeadersRet.clear();
-    strMessageRet = "";
-
-    // Read header
-    int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
-    if (nLen < 0 || (size_t)nLen > max_size)
-        return HTTP_INTERNAL_SERVER_ERROR;
-
-    // Read message
-    if (nLen > 0)
-    {
-        vector<char> vch;
-        size_t ptr = 0;
-        while (ptr < (size_t)nLen)
-        {
-            size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
-            vch.resize(ptr + bytes_to_read);
-            stream.read(&vch[ptr], bytes_to_read);
-            if (!stream) // Connection lost while reading
-                return HTTP_INTERNAL_SERVER_ERROR;
-            ptr += bytes_to_read;
-        }
-        strMessageRet = string(vch.begin(), vch.end());
-    }
-
-    string sConHdr = mapHeadersRet["connection"];
-
-    if ((sConHdr != "close") && (sConHdr != "keep-alive"))
-    {
-        if (nProto >= 1)
-            mapHeadersRet["connection"] = "keep-alive";
-        else
-            mapHeadersRet["connection"] = "close";
+    try {
+        boost::filesystem::remove(GetAuthCookieFile());
+    } catch (const boost::filesystem::filesystem_error& e) {
+        LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
     }
-
-    return HTTP_OK;
 }
 
-/**
- * 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://jsonrpc.org/historical/json-rpc-over-http.html
- * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
- */
-
-string JSONRPCRequest(const string& strMethod, const Array& params, const 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";
-}
-
-Object JSONRPCReplyObj(const Value& result, const Value& error, const 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 reply;
-}
-
-string JSONRPCReply(const Value& result, const Value& error, const Value& id)
-{
-    Object reply = JSONRPCReplyObj(result, error, id);
-    return write_string(Value(reply), false) + "\n";
-}
-
-Object JSONRPCError(int code, const string& message)
-{
-    Object error;
-    error.push_back(Pair("code", code));
-    error.push_back(Pair("message", message));
-    return error;
-}
This page took 0.046809 seconds and 4 git commands to generate.