<|start_filename|>src/keepass.cpp<|end_filename|>
// Copyright (c) 2014-2017 The Dash Core developers
// Copyright (c) 2018 The Documentchain developers

// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "keepass.h"

#include "wallet/crypter.h"
#include "clientversion.h"
#include "protocol.h"
#include "random.h"
#include "rpc/protocol.h"

// Necessary to prevent compile errors due to forward declaration of
//CScript in serialize.h (included from crypter.h)
#include "script/script.h"
#include "script/standard.h"

#include "util.h"
#include "utilstrencodings.h"

#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include "support/cleanse.h" // for OPENSSL_cleanse()

const char* CKeePassIntegrator::KEEPASS_HTTP_HOST = "localhost";

CKeePassIntegrator keePassInt;

// Base64 decoding with secure memory allocation
SecureString DecodeBase64Secure(const SecureString& sInput)
{
    SecureString output;

    // Init openssl BIO with base64 filter and memory input
    BIO *b64, *mem;
    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
    mem = BIO_new_mem_buf((void *) &sInput[0], sInput.size());
    BIO_push(b64, mem);

    // Prepare buffer to receive decoded data
    if(sInput.size() % 4 != 0) {
        throw std::runtime_error("Input length should be a multiple of 4");
    }
    size_t nMaxLen = sInput.size() / 4 * 3; // upper bound, guaranteed divisible by 4
    output.resize(nMaxLen);

    // Decode the string
    size_t nLen;
    nLen = BIO_read(b64, (void *) &output[0], sInput.size());
    output.resize(nLen);

    // Free memory
    BIO_free_all(b64);
    return output;
}

// Base64 encoding with secure memory allocation
SecureString EncodeBase64Secure(const SecureString& sInput)
{
    // Init openssl BIO with base64 filter and memory output
    BIO *b64, *mem;
    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); // No newlines in output
    mem = BIO_new(BIO_s_mem());
    BIO_push(b64, mem);

    // Decode the string
    BIO_write(b64, &sInput[0], sInput.size());
    (void) BIO_flush(b64);

    // Create output variable from buffer mem ptr
    BUF_MEM *bptr;
    BIO_get_mem_ptr(b64, &bptr);
    SecureString output(bptr->data, bptr->length);

    // Cleanse secure data buffer from memory
    memory_cleanse((void *) bptr->data, bptr->length);

    // Free memory
    BIO_free_all(b64);
    return output;
}

CKeePassIntegrator::CKeePassIntegrator()
    :sKeyBase64(" "), sKey(" "), sUrl(" ") // Prevent LockedPageManagerBase complaints
{
    sKeyBase64.clear(); // Prevent LockedPageManagerBase complaints
    sKey.clear(); // Prevent LockedPageManagerBase complaints
    sUrl.clear(); // Prevent LockedPageManagerBase complaints
    bIsActive = false;
    nPort = DEFAULT_KEEPASS_HTTP_PORT;
}

// Initialze from application context
void CKeePassIntegrator::init()
{
    bIsActive = GetBoolArg("-keepass", false);
    nPort = GetArg("-keepassport", DEFAULT_KEEPASS_HTTP_PORT);
    sKeyBase64 = SecureString(GetArg("-keepasskey", "").c_str());
    strKeePassId = GetArg("-keepassid", "");
    strKeePassEntryName = GetArg("-keepassname", "");
    // Convert key if available
    if(sKeyBase64.size() > 0)
    {
        sKey = DecodeBase64Secure(sKeyBase64);
    }
    // Construct url if available
    if(strKeePassEntryName.size() > 0)
    {
        sUrl = SecureString("http://");
        sUrl += SecureString(strKeePassEntryName.c_str());
        sUrl += SecureString("/");
        //sSubmitUrl = "http://";
        //sSubmitUrl += SecureString(strKeePassEntryName.c_str());
    }
}

void CKeePassIntegrator::CKeePassRequest::addStrParameter(const std::string& strName, const std::string& strValue)
{
    requestObj.push_back(Pair(strName, strValue));
}

void CKeePassIntegrator::CKeePassRequest::addStrParameter(const std::string& strName, const SecureString& sValue)
{
    std::string sCipherValue;

    if(!EncryptAES256(sKey, sValue, strIV, sCipherValue))
    {
        throw std::runtime_error("Unable to encrypt Verifier");
    }

    addStrParameter(strName, EncodeBase64(sCipherValue));
}

std::string CKeePassIntegrator::CKeePassRequest::getJson()
{
    return requestObj.write();
}

void CKeePassIntegrator::CKeePassRequest::init()
{
    SecureString sIVSecure = generateRandomKey(KEEPASS_CRYPTO_BLOCK_SIZE);
    strIV = std::string(&sIVSecure[0], sIVSecure.size());
    // Generate Nonce, Verifier and RequestType
    SecureString sNonceBase64Secure = EncodeBase64Secure(sIVSecure);
    addStrParameter("Nonce", std::string(&sNonceBase64Secure[0], sNonceBase64Secure.size())); // Plain
    addStrParameter("Verifier", sNonceBase64Secure); // Encoded
    addStrParameter("RequestType", strType);
}

void CKeePassIntegrator::CKeePassResponse::parseResponse(const std::string& strResponse)
{
    UniValue responseValue;
    if(!responseValue.read(strResponse))
    {
        throw std::runtime_error("Unable to parse KeePassHttp response");
    }

    responseObj = responseValue;

    // retrieve main values
    bSuccess = responseObj["Success"].get_bool();
    strType = getStr("RequestType");
    strIV = DecodeBase64(getStr("Nonce"));
}

std::string CKeePassIntegrator::CKeePassResponse::getStr(const std::string& strName)
{
    return responseObj[strName].get_str();
}

SecureString CKeePassIntegrator::CKeePassResponse::getSecureStr(const std::string& strName)
{
    std::string strValueBase64Encrypted(responseObj[strName].get_str());
    SecureString sValue;
    try
    {
        sValue = decrypt(strValueBase64Encrypted);
    }
    catch (std::exception &e)
    {
        std::string strError = "Exception occured while decrypting ";
        strError += strName + ": " + e.what();
        throw std::runtime_error(strError);
    }
    return sValue;
}

SecureString CKeePassIntegrator::CKeePassResponse::decrypt(const std::string& strValueBase64Encrypted)
{
    std::string strValueEncrypted = DecodeBase64(strValueBase64Encrypted);
    SecureString sValue;
    if(!DecryptAES256(sKey, strValueEncrypted, strIV, sValue))
    {
      throw std::runtime_error("Unable to decrypt value.");
    }
    return sValue;
}

std::vector<CKeePassIntegrator::CKeePassEntry> CKeePassIntegrator::CKeePassResponse::getEntries()
{

    std::vector<CKeePassEntry> vEntries;

    UniValue aEntries = responseObj["Entries"].get_array();
    for(size_t i = 0; i < aEntries.size(); i++)
    {
        SecureString sEntryUuid(decrypt(aEntries[i]["Uuid"].get_str().c_str()));
        SecureString sEntryName(decrypt(aEntries[i]["Name"].get_str().c_str()));
        SecureString sEntryLogin(decrypt(aEntries[i]["Login"].get_str().c_str()));
        SecureString sEntryPassword(decrypt(aEntries[i]["Password"].get_str().c_str()));
        CKeePassEntry entry(sEntryUuid, sEntryName, sEntryLogin, sEntryPassword);
        vEntries.push_back(entry);
    }

    return vEntries;

}

SecureString CKeePassIntegrator::generateRandomKey(size_t nSize)
{
    // Generates random key
    SecureString sKey;
    sKey.resize(nSize);

    GetStrongRandBytes((unsigned char *) &sKey[0], nSize);

    return sKey;
}

// Construct POST body for RPC JSON call
std::string CKeePassIntegrator::constructHTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders)
{
    std::ostringstream streamOut;
    streamOut << "POST / HTTP/1.1\r\n"
      << "User-Agent: dms-json-rpc/" << FormatFullVersion() << "\r\n"
      << "Host: localhost\r\n"
      << "Content-Type: application/json\r\n"
      << "Content-Length: " << strMsg.size() << "\r\n"
      << "Connection: close\r\n"
      << "Accept: application/json\r\n";
    for (const auto& item : mapRequestHeaders)
        streamOut << item.first << ": " << item.second << "\r\n";
    streamOut << "\r\n" << strMsg;

    return streamOut.str();
}

/** Reply structure for request_done to fill in */
struct HTTPReply
{
    int nStatus;
    std::string strBody;
};

static void http_request_done(struct evhttp_request *req, void *ctx)
{
    HTTPReply *reply = static_cast<HTTPReply*>(ctx);

    if (req == NULL) {
        /* If req is NULL, it means an error occurred while connecting, but
         * I'm not sure how to find out which one. We also don't really care.
         */
        reply->nStatus = 0;
        return;
    }

    reply->nStatus = evhttp_request_get_response_code(req);

    struct evbuffer *buf = evhttp_request_get_input_buffer(req);
    if (buf)
    {
        size_t size = evbuffer_get_length(buf);
        const char *data = (const char*)evbuffer_pullup(buf, size);
        if (data)
            reply->strBody = std::string(data, size);
        evbuffer_drain(buf, size);
    }
}

// Send RPC message to KeePassHttp
void CKeePassIntegrator::doHTTPPost(const std::string& sRequest, int& nStatusRet, std::string& strResponseRet)
{
//    // Prepare communication
//    boost::asio::io_service io_service;

//    // Get a list of endpoints corresponding to the server name.
//    tcp::resolver resolver(io_service);
//    tcp::resolver::query query(KEEPASS_HTTP_HOST, boost::lexical_cast<std::string>(nPort));
//    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
//    tcp::resolver::iterator end;

//    // Try each endpoint until we successfully establish a connection.
//    tcp::socket socket(io_service);
//    boost::system::error_code error = boost::asio::error::host_not_found;
//    while (error && endpoint_iterator != end)
//    {
//      socket.close();
//      socket.connect(*endpoint_iterator++, error);
//    }

//    if(error)
//    {
//        throw boost::system::system_error(error);
//    }
    // Create event base
    struct event_base *base = event_base_new(); // TODO RAII
    if (!base)
        throw std::runtime_error("cannot create event_base");

    // Synchronously look up hostname
    struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, KEEPASS_HTTP_HOST, DEFAULT_KEEPASS_HTTP_PORT); // TODO RAII
    if (evcon == NULL)
        throw std::runtime_error("create connection failed");
    evhttp_connection_set_timeout(evcon, KEEPASS_HTTP_CONNECT_TIMEOUT);

    // Form the request.
//    std::map<std::string, std::string> mapRequestHeaders;
//    std::string strPost = constructHTTPPost(sRequest, mapRequestHeaders);

    HTTPReply response;
    struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
    if (req == NULL)
        throw std::runtime_error("create http request failed");

    struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
    assert(output_headers);
//    s << "POST / HTTP/1.1\r\n"
    evhttp_add_header(output_headers, "User-Agent", ("dms-json-rpc/" + FormatFullVersion()).c_str());
    evhttp_add_header(output_headers, "Host", KEEPASS_HTTP_HOST);
    evhttp_add_header(output_headers, "Accept", "application/json");
    evhttp_add_header(output_headers, "Content-Type", "application/json");
//    evhttp_add_header(output_headers, "Content-Length", itostr(strMsg.size()).c_str());
    evhttp_add_header(output_headers, "Connection", "close");

    // Logging of actual post data disabled as to not write passphrase in debug.log. Only enable temporarily when needed
    //LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- send POST data: %s\n", strPost);
    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- send POST data\n");

//    boost::asio::streambuf request;
//    std::ostream request_stream(&request);
//    request_stream << strPost;

//    // Send the request.
//    boost::asio::write(socket, request);

//    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- request written\n");

//    // Read the response status line. The response streambuf will automatically
//    // grow to accommodate the entire line. The growth may be limited by passing
//    // a maximum size to the streambuf constructor.
//    boost::asio::streambuf response;
//    boost::asio::read_until(socket, response, "\r\n");

//    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- request status line read\n");

//    // Receive HTTP reply status
//    int nProto = 0;
//    std::istream response_stream(&response);
//    nStatus = ReadHTTPStatus(response_stream, nProto);

    // Attach request data
//    std::string sRequest = JSONRPCRequest(strMethod, params, 1);
    struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
    assert(output_buffer);
    evbuffer_add(output_buffer, sRequest.data(), sRequest.size());

    int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
    if (r != 0) {
        evhttp_connection_free(evcon);
        event_base_free(base);
        throw std::runtime_error("send http request failed");
    }

    event_base_dispatch(base);
    evhttp_connection_free(evcon);
    event_base_free(base);

//    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- reading response body start\n");
//    // Read until EOF, writing data to output as we go.
//    while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
//    {
//        if (error != boost::asio::error::eof)
//        {
//            if (error != 0)
//            { // 0 is success
//                throw boost::system::system_error(error);
//            }
//        }
//    }
//    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- reading response body end\n");
//
//    // Receive HTTP reply message headers and body
//    std::map<std::string, std::string> mapHeaders;
//    ReadHTTPMessage(response_stream, mapHeaders, strResponse, nProto, std::numeric_limits<size_t>::max());
//    LogPrint("keepass", "CKeePassIntegrator::doHTTPPost -- Processed body\n");

    nStatusRet = response.nStatus;
    if (response.nStatus == 0)
        throw std::runtime_error("couldn't connect to server");
    else if (response.nStatus >= 400 && response.nStatus != HTTP_BAD_REQUEST && response.nStatus != HTTP_NOT_FOUND && response.nStatus != HTTP_INTERNAL_SERVER_ERROR)
        throw std::runtime_error(strprintf("server returned HTTP error %d", response.nStatus));
    else if (response.strBody.empty())
        throw std::runtime_error("no response from server");

    // Parse reply
    UniValue valReply(UniValue::VSTR);
    if (!valReply.read(response.strBody))
         throw std::runtime_error("couldn't parse reply from server");
    const UniValue& reply = valReply.get_obj();
    if (reply.empty())
        throw std::runtime_error("expected reply to have result, error and id properties");

    strResponseRet = valReply.get_str();
}

void CKeePassIntegrator::rpcTestAssociation(bool bTriggerUnlock)
{
    CKeePassRequest request(sKey, "test-associate");
    request.addStrParameter("TriggerUnlock", std::string(bTriggerUnlock ? "true" : "false"));

    int nStatus;
    std::string strResponse;

    doHTTPPost(request.getJson(), nStatus, strResponse);

    LogPrint("keepass", "CKeePassIntegrator::rpcTestAssociation -- send result: status: %d response: %s\n", nStatus, strResponse);
}

std::vector<CKeePassIntegrator::CKeePassEntry> CKeePassIntegrator::rpcGetLogins()
{

    // Convert key format
    SecureString sKey = DecodeBase64Secure(sKeyBase64);

    CKeePassRequest request(sKey, "get-logins");
    request.addStrParameter("addStrParameter", std::string("true"));
    request.addStrParameter("TriggerUnlock", std::string("true"));
    request.addStrParameter("Id", strKeePassId);
    request.addStrParameter("Url", sUrl);

    int nStatus;
    std::string strResponse;

    doHTTPPost(request.getJson(), nStatus, strResponse);

    // Logging of actual response data disabled as to not write passphrase in debug.log. Only enable temporarily when needed
    //LogPrint("keepass", "CKeePassIntegrator::rpcGetLogins -- send result: status: %d response: %s\n", nStatus, strResponse);
    LogPrint("keepass", "CKeePassIntegrator::rpcGetLogins -- send result: status: %d\n", nStatus);

    if(nStatus != 200)
    {
        std::string strError = "Error returned by KeePassHttp: HTTP code ";
        strError += itostr(nStatus);
        strError += " - Response: ";
        strError += " response: [";
        strError += strResponse;
        strError += "]";
        throw std::runtime_error(strError);
    }

    // Parse the response
    CKeePassResponse response(sKey, strResponse);

    if(!response.getSuccess())
    {
        std::string strError = "KeePassHttp returned failure status";
        throw std::runtime_error(strError);
    }

    return response.getEntries();
}

void CKeePassIntegrator::rpcSetLogin(const SecureString& sWalletPass, const SecureString& sEntryId)
{

    // Convert key format
    SecureString sKey = DecodeBase64Secure(sKeyBase64);

    CKeePassRequest request(sKey, "set-login");
    request.addStrParameter("Id", strKeePassId);
    request.addStrParameter("Url", sUrl);

    LogPrint("keepass", "CKeePassIntegrator::rpcSetLogin -- send Url: %s\n", sUrl);

    //request.addStrParameter("SubmitUrl", sSubmitUrl); // Is used to construct the entry title
    request.addStrParameter("Login", SecureString("dms"));
    request.addStrParameter("Password", sWallet<PASSWORD>);
    if(sEntryId.size() != 0)
    {
        request.addStrParameter("Uuid", sEntryId); // Update existing
    }

    int nStatus;
    std::string strResponse;

    doHTTPPost(request.getJson(), nStatus, strResponse);


    LogPrint("keepass", "CKeePassIntegrator::rpcSetLogin -- send result: status: %d response: %s\n", nStatus, strResponse);

    if(nStatus != 200)
    {
        std::string strError = "Error returned: HTTP code ";
        strError += itostr(nStatus);
        strError += " - Response: ";
        strError += " response: [";
        strError += strResponse;
        strError += "]";
        throw std::runtime_error(strError);
    }

    // Parse the response
    CKeePassResponse response(sKey, strResponse);

    if(!response.getSuccess())
    {
        throw std::runtime_error("KeePassHttp returned failure status");
    }
}


SecureString CKeePassIntegrator::generateKeePassKey()
{
    SecureString sKey = generateRandomKey(KEEPASS_CRYPTO_KEY_SIZE);
    SecureString sKeyBase64 = EncodeBase64Secure(sKey);
    return sKeyBase64;
}

void CKeePassIntegrator::rpcAssociate(std::string& strIdRet, SecureString& sKeyBase64Ret)
{
    sKey = generateRandomKey(KEEPASS_CRYPTO_KEY_SIZE);
    CKeePassRequest request(sKey, "associate");

    sKeyBase64Ret = EncodeBase64Secure(sKey);
    request.addStrParameter("Key", std::string(&sKeyBase64Ret[0], sKeyBase64Ret.size()));

    int nStatus;
    std::string strResponse;

    doHTTPPost(request.getJson(), nStatus, strResponse);

    LogPrint("keepass", "CKeePassIntegrator::rpcAssociate -- send result: status: %d response: %s\n", nStatus, strResponse);

    if(nStatus != 200)
    {
        std::string strError = "Error returned: HTTP code ";
        strError += itostr(nStatus);
        strError += " - Response: ";
        strError += " response: [";
        strError += strResponse;
        strError += "]";
        throw std::runtime_error(strError);
    }

    // Parse the response
    CKeePassResponse response(sKey, strResponse);

    if(!response.getSuccess())
    {
        throw std::runtime_error("KeePassHttp returned failure status");
    }

    // If we got here, we were successful. Return the information
    strIdRet = response.getStr("Id");
}

// Retrieve wallet passphrase from KeePass
SecureString CKeePassIntegrator::retrievePassphrase()
{

    // Check we have all required information
    if(sKey.size() == 0)
    {
        throw std::runtime_error("keepasskey parameter is not defined. Please specify the configuration parameter.");
    }
    if(strKeePassId.size() == 0)
    {
        throw std::runtime_error("keepassid parameter is not defined. Please specify the configuration parameter.");
    }
    if(strKeePassEntryName == "")
    {
        throw std::runtime_error("keepassname parameter is not defined. Please specify the configuration parameter.");
    }

    // Retrieve matching logins from KeePass
    std::vector<CKeePassIntegrator::CKeePassEntry> vecEntries = rpcGetLogins();

    // Only accept one unique match
    if(vecEntries.size() == 0)
    {
        throw std::runtime_error("KeePassHttp returned 0 matches, please verify the keepassurl setting.");
    }
    if(vecEntries.size() > 1)
    {
        throw std::runtime_error("KeePassHttp returned multiple matches, bailing out.");
    }

    return vecEntries[0].getPassword();
}

// Update wallet passphrase in keepass
void CKeePassIntegrator::updatePassphrase(const SecureString& sWalletPassphrase)
{
    // Check we have all required information
    if(sKey.size() == 0)
    {
        throw std::runtime_error("keepasskey parameter is not defined. Please specify the configuration parameter.");
    }
    if(strKeePassId.size() == 0)
    {
        throw std::runtime_error("keepassid parameter is not defined. Please specify the configuration parameter.");
    }
    if(strKeePassEntryName == "")
    {
        throw std::runtime_error("keepassname parameter is not defined. Please specify the configuration parameter.");
    }

    SecureString sEntryId("");

    std::string strError;

    // Lookup existing entry
    std::vector<CKeePassIntegrator::CKeePassEntry> vecEntries = rpcGetLogins();

    if(vecEntries.size() > 1)
    {
        throw std::runtime_error("KeePassHttp returned multiple matches, bailing out.");
    }

    if(vecEntries.size() == 1)
    {
        sEntryId = vecEntries[0].getUuid();
    }

    // Update wallet passphrase in KeePass
    rpcSetLogin(sWalletPassphrase, sEntryId);
}


<|start_filename|>src/primitives/document.cpp<|end_filename|>
// Copyright (c) 2020-2021 The Documentchain developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "primitives/document.h"
#include "tinyformat.h"

/** CDocumentHash
 */
CDocumentHash::CDocumentHash()
{
    SetNull();
}

CDocumentHash::CDocumentHash(int aalgo, std::string ahash)
{
    algo = aalgo;
    hash = ahash;
}

void CDocumentHash::SetNull()
{
    algo = -1;
    hash = "";
}

bool CDocumentHash::SameHash(CDocumentHash comparewith, bool allowempty/*=false*/)
{
    if (!allowempty && (hash.empty() || algo < 0))
        return false;

    return (hash == comparewith.hash && algo == comparewith.algo);
}

std::string CDocumentHash::AlgoName()
{
    switch (algo) {
        case  0: return "MD5"; // GUID/MD5
        case  1: return "SHA1";
        case 21: return "SHA2-224";
        case 22: return "SHA2-256";
        case 23: return "SHA2-384";
        case 25: return "SHA2-512";
        case 31: return "SHA3-224";
        case 32: return "SHA3-256";
        case 33: return "SHA3-384";
        case 35: return "SHA3-512";
        default: return "";
    }
}

/** CDocument
 */
CDocument::CDocument() {
    SetNull();
}

CDocument::CDocument(std::string rawDoc)
{
    SetNull();
    rawDocument = rawDoc; // "444d24..."
    std::transform(rawDocument.begin(), rawDocument.end(), rawDocument.begin(), ::toupper);
    if (rawDocument.length() < 42 || rawDocument.substr(0, 6) != "444D24")
        return;

    std::string sVersion = rawDocument.substr(6, 4);
    if (sVersion == "0001") {
        version = 1;
        guidcompr = rawDocument.substr(14, 32);
        filehash = CDocumentHash(0, rawDocument.substr(46, 32));
        indexhash = filehash;
        attrhash = CDocumentHash(0, rawDocument.substr(78, 32));
        if (rawDocument.length() == 142)
            ownerhash = CDocumentHash(0, rawDocument.substr(110, 32));
    }
    else if (sVersion == "0002") {
        version = 2;
        int idx = 14;
        int i = 0;
        while (idx < rawDocument.length()) {
            i++; // wrong data could cause an endless loop
            if (i > 19)
                throw("Invalid hash data");
            char htype = rawDocument[idx];
            int halgo = std::stoi(rawDocument.substr(idx+2, 2));
            std::string hash = rawDocument.substr(idx+4, HashHexLength(halgo));
            idx += 4 + HashHexLength(halgo);

            switch (htype) {
                case '0':
                    guidcompr = hash;
                    break;
                case 'F':
                    if (halgo == 0)
                        indexhash = CDocumentHash(halgo, hash);
                    else
                        filehash = CDocumentHash(halgo, hash);
                    break;
                case 'A':
                    attrhash = CDocumentHash(halgo, hash);
                    break;
                case 'B':
                    ownerhash = CDocumentHash(halgo, hash);
            }
        }
    }

    if (!guidcompr.empty())
        guid = strprintf("{%s-%s-%s-%s-%s}", guidcompr.substr(0, 8), guidcompr.substr(8, 4), 
                  guidcompr.substr(12, 4), guidcompr.substr(16, 4), guidcompr.substr(20, 12));
    isvalid = true;
}

void CDocument::SetNull()
{
    isvalid = false;
    version = 0;
    guidcompr = "";
    guid = "";
    indexhash.SetNull();
    filehash.SetNull();
    attrhash.SetNull();
    ownerhash.SetNull();
}

int CDocument::HashHexLength(int algo)
{
    switch (algo) {
        case  0: return 32;  // GUID/MD5
        case  1: return 40;  // SHA1
        case 21: return 56;  // SHA2-224
        case 22: return 64;  // SHA2-256
        case 23: return 96;  // SHA2-384
        case 25: return 128; // SHA2-512
        case 31: return 56;  // SHA3-224
        case 32: return 64;  // SHA3-256
        case 33: return 96;  // SHA3-384
        case 35: return 128; // SHA3-512
        default: return 0;
    }
}


<|start_filename|>src/chainparamsseeds.h<|end_filename|>
#ifndef DMS_CHAINPARAMSSEEDS_H
#define DMS_CHAINPARAMSSEEDS_H
/**
 * List of fixed seed nodes for the documentchain network
 * AUTOGENERATED by contrib/seeds/generate-seeds.py
 *
 * Each line contains a 16-byte IPv6 address and a port.
 * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.
 */
static SeedSpec6 pnSeed6_main[] = {
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x2d,0x69,0x81}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0x5e,0x69,0x81}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x23,0x51,0xc8}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x54,0x00,0x7d}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x56,0x44,0x4f}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0xd0,0xa0,0x26}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x49,0x42,0xe5}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0xd7,0xa8}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xdf,0x19,0x68}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd6,0x41,0x3c}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0x8f,0x0d}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0xae,0xcb,0x61}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0xaf,0x1f,0xad}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0xee,0x1f}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xc4,0x03,0x9b}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x03,0x50,0x3a}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x24,0xed,0x68}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x80,0x65,0xe7}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0xc9,0xe2,0x80}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd2,0x10,0x78,0x66}, 41319},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x3b,0x7b,0x8e}, 41319}

};

static SeedSpec6 pnSeed6_test[] = {
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0x5e,0x69,0x81}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xfc,0xbf,0x73}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0xd7,0xa8}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0x76,0xf5,0xc5}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd6,0x41,0x3c}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0x8f,0x0d}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xc4,0x03,0x05}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x24,0xed,0x68}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0xc9,0xe2,0x80}, 41419},
    {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x3b,0x7b,0x8e}, 41419}
};
#endif // DMS_CHAINPARAMSSEEDS_H


<|start_filename|>src/primitives/document.h<|end_filename|>
// Copyright (c) 2020-2021 The Documentchain developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_PRIMITIVES_DOCUMENT_H
#define BITCOIN_PRIMITIVES_DOCUMENT_H

#include <iostream>

class CDocumentHash
{
private:
public:
    int algo;
    std::string hash;

    CDocumentHash();
    CDocumentHash(int aalgo, std::string ahash);

    void SetNull();
    bool SameHash(CDocumentHash comparewith, bool allowempty = false);
    std::string AlgoName();
};

class CDocument
{
private:
    void SetNull();
    int HashHexLength(int algo);
public:
    int version;
    bool isvalid;
    std::string rawDocument;
    std::string guidcompr;
    std::string guid;
    CDocumentHash indexhash;
    CDocumentHash filehash;
    CDocumentHash attrhash;
    CDocumentHash ownerhash;

    CDocument();
    CDocument(std::string rawDoc);
};

#endif // BITCOIN_PRIMITIVES_DOCUMENT_H


<|start_filename|>src/test/Makefile<|end_filename|>
all:
	$(MAKE) -C .. dms_test
clean:
	$(MAKE) -C .. dms_test_clean
check:
	$(MAKE) -C .. dms_test_check


<|start_filename|>src/clientversion.h<|end_filename|>
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2018-2021 The Documentchain developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CLIENTVERSION_H
#define BITCOIN_CLIENTVERSION_H

#if defined(HAVE_CONFIG_H)
#include "config/dms-config.h"
#else

/**
 * client versioning and copyright year
 */

//! These need to be macros, as clientversion.cpp's and dms*-res.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 0
#define CLIENT_VERSION_MINOR 13
#define CLIENT_VERSION_REVISION 3
#define CLIENT_VERSION_BUILD 1

//! Set to true for release, false for prerelease or test build
#define CLIENT_VERSION_IS_RELEASE true

/**
 * Copyright year (2009-this)
 * Todo: update this when changing our copyright comments in the source
 */
#define COPYRIGHT_YEAR 2021

#endif //HAVE_CONFIG_H

/**
 Release code names 
  1. Alice  0.12.9   26 Aug 2018
  2. Bob    0.12.10  13 Nov 2018
  3. Carol  0.12.11   4 Jan 2019
  4. Dave   0.12.12  15 Apr 2019
  5. Erin   0.12.13  16 Aug 2019
  6. Faythe 0.12.14  29 Nov 2019
  7. Grace  0.12.15  17 Apr 2020
  8. Heidi  0.12.16   8 Oct 2020
  9. Ivan   0.13.3    3 May 2021
*/
#define RELEASE_CODE_NAME "Ivan"

/**
 * Converts the parameter X to a string after macro replacement on X has been performed.
 * Don't merge these into one macro!
 */
#define STRINGIZE(X) DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X

//! Copyright string used in Windows .rc files
#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin Core Developers, 2014-" STRINGIZE(COPYRIGHT_YEAR) " The Dash Core Developers, 2018-" STRINGIZE(COPYRIGHT_YEAR) " " COPYRIGHT_HOLDERS_FINAL

/**
 * dmsd-res.rc includes this file, but it cannot cope with real c++ code.
 * WINDRES_PREPROC is defined to indicate that its pre-processor is running.
 * Anything other than a define should be guarded below.
 */

#if !defined(WINDRES_PREPROC)

#include <string>
#include <vector>

static const int CLIENT_VERSION =
                           1000000 * CLIENT_VERSION_MAJOR
                         +   10000 * CLIENT_VERSION_MINOR
                         +     100 * CLIENT_VERSION_REVISION
                         +       1 * CLIENT_VERSION_BUILD;

extern const std::string CLIENT_NAME;
extern const std::string CLIENT_BUILD;

std::string FormatVersion(int nVersion);
std::string FormatFullVersion();
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);

#endif // WINDRES_PREPROC

#endif // BITCOIN_CLIENTVERSION_H


<|start_filename|>src/qt/information.h<|end_filename|>
// Copyright (c) 2021 The Documentchain developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef INFORMATION_H
#define INFORMATION_H

#include "platformstyle.h"
#include "walletmodel.h"

#include <QStringListModel>
#include <QWidget>
#include <QNetworkReply>
#include <QTextCharFormat>

namespace Ui {
    class Information;
}

class ClientModel;
class WalletModel;

class Information : public QWidget
{
    Q_OBJECT

public:
    explicit Information(const PlatformStyle *_platformStyle, QWidget *parent = nullptr);
    ~Information();
    const PlatformStyle *platformStyle;

    void setClientModel(ClientModel *clientModel);
    void setWalletModel(WalletModel *model);

private:
    Ui::Information *ui;
    ClientModel *clientModel;
    WalletModel *walletModel;
    QStringListModel *categoryModel;

    QString downloadURL;
    QString filenameUserNotes;
    QTextCharFormat defaultCharFormat;
    bool categoryChanging;
    bool userNotesModified;
    void downloadFile(QString fileName);
    void setCategories(const QString &cats);
    void showErrorPage();
    void saveUserNotes();
    bool isUserNotesSelected(const QModelIndex &current);

private Q_SLOTS:
    void finishedDownloadFile(QNetworkReply* reply);
    void onlistViewCategoryChanged(const QModelIndex &current, const QModelIndex &previous);
    void on_textBrowserInfo_textChanged();
    void on_textBrowserInfo_anchorClicked(const QUrl &arg1);
};

#endif // INFORMATION_H


<|start_filename|>src/qt/test/Makefile<|end_filename|>
all:
	$(MAKE) -C ../../ test_dms_qt
clean:
	$(MAKE) -C ../../ test_dms_qt_clean
check:
	$(MAKE) -C ../../ test_dms_qt_check


<|start_filename|>src/qt/information.cpp<|end_filename|>
// Copyright (c) 2021 The Documentchain developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "information.h"
#include "ui_information.h"

#include "clientmodel.h"
#include "walletmodel.h"
#include "guiutil.h"

#include <QUrl>
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkRequest>

/** local functions
*/
namespace {

const QString downloadRootURL = "https://documentchain.org/files/dms-core-integrations/information/";
    //QString downloadRootURL = "https://raw.githubusercontent.com/Krekeler/dms-core-integrations/main/information/";
      QString indexFileName = "categories.txt";
      std::string userNotesFileName = "mynotes.txt";

} // namespace


/** Information
*/

Information::Information(const PlatformStyle *_platformStyle, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Information),
    clientModel(0),
    walletModel(0),
    platformStyle(_platformStyle)
{
    ui->setupUi(this);
    categoryModel = new QStringListModel(this);
    ui->listViewCategory->setModel(categoryModel);

    filenameUserNotes = GUIUtil::boostPathToQString(GetDataDir() / userNotesFileName);
    categoryChanging = false;
    userNotesModified = false;
    defaultCharFormat = ui->textBrowserInfo->currentCharFormat();
    downloadURL = downloadRootURL + tr("en/");
    downloadFile(indexFileName); // one download at startup
}

void Information::setClientModel(ClientModel *model)
{
    this->clientModel = model;
}

void Information::setWalletModel(WalletModel *model)
{
    this->walletModel = model;
}

Information::~Information()
{
    if (userNotesModified)
        saveUserNotes();

    delete ui;
}

void Information::downloadFile(QString fileName)
{
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished,
            this, &Information::finishedDownloadFile);
    manager->get(QNetworkRequest(QUrl(downloadURL + fileName)));
}

void Information::finishedDownloadFile(QNetworkReply *reply)
{
    QString url = reply->url().toString();

    // do not overwrite mynotes if the user changes faster than the download takes
    if (isUserNotesSelected(ui->listViewCategory->currentIndex()))
        return;

    if (reply->error() == QNetworkReply::NoError) {
        QByteArray bts = reply->readAll();
        QString str(bts);
        if (url.endsWith("/" + indexFileName)) {
            setCategories(str);
        }
        else
            ui->textBrowserInfo->setHtml(str);
    }
    else {
        showErrorPage();
        if (url.endsWith("/" + indexFileName))
            setCategories("");
    }

    reply->deleteLater();
}

void Information::setCategories(const QString &cats)
{
    QStringList catlist = cats.split(QRegExp("(\\r\\n)|(\\n\\r)|\\r|\\n"), QString::SkipEmptyParts);
    catlist << tr("My Notes");
    categoryModel->setStringList(catlist);

    ui->listViewCategory->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(ui->listViewCategory->selectionModel(),
        SIGNAL(currentChanged(const QModelIndex& , const QModelIndex&)), this,
        SLOT(onlistViewCategoryChanged(const QModelIndex&, const QModelIndex&)),
        Qt::QueuedConnection);
}

void Information::showErrorPage()
{
    QString errorText = "<p>" + 
        tr("The recent information pages have not yet been loaded from %1").arg(downloadURL) +
        "</p><p>" +
        tr("You can find information on the project website at %1").arg("<a href=\"https://documentchain.org/\">documentchain.org</a>") +
        "</p>";
    ui->textBrowserInfo->setHtml(errorText);
}

void Information::saveUserNotes()
{
    QFile file(filenameUserNotes);
    if (file.open(QFile::WriteOnly | QFile::Text)) {
        QTextStream output(&file);
        output << ui->textBrowserInfo->toPlainText();
        userNotesModified = false;
    }
    else
        QMessageBox::critical(NULL, tr("My Notes"), tr("Notes could not be saved to file."));
}

bool Information::isUserNotesSelected(const QModelIndex &selected)
{
    if (!selected.isValid())
        return false;
    return (categoryModel->stringList().at(selected.row()) == tr("My Notes"));
}

void Information::onlistViewCategoryChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if (!current.isValid())
        return;

    categoryChanging = true;
    if (isUserNotesSelected(current)) {
        ui->textBrowserInfo->clear();
        ui->textBrowserInfo->setAcceptRichText(false);
        ui->textBrowserInfo->setCurrentCharFormat(defaultCharFormat);
        QFile file(filenameUserNotes);
        if (file.open(QFile::ReadOnly | QFile::Text)) {
            QTextStream input(&file);
            ui->textBrowserInfo->setPlainText(input.readAll());
        }
        else
            ui->textBrowserInfo->setPlainText(tr("Here you can enter your own notes..."));
        userNotesModified = false;
        ui->textBrowserInfo->setReadOnly(false);
    }
    else {
        if (isUserNotesSelected(previous) && userNotesModified)
            saveUserNotes(); // save not only on exit

        ui->textBrowserInfo->setReadOnly(true);
        QString selcat = categoryModel->stringList().at(current.row());
        selcat = selcat.replace(" ", "-").toLower();
        downloadFile(selcat + ".html"); // setMarkdown requires Qt 5.14
    }
    categoryChanging = false;
}

void Information::on_textBrowserInfo_textChanged()
{
    if (isUserNotesSelected(ui->listViewCategory->currentIndex()) && !categoryChanging)
        userNotesModified = true;
}

void Information::on_textBrowserInfo_anchorClicked(const QUrl &arg1)
{
  /*QString cmd = arg1.toString();

    if (cmd == "internal-link")
        QMessageBox::information(NULL, tr("Link"), cmd);
    else*/
        QDesktopServices::openUrl(arg1);
}
