git / moonbite-core / src/qt/paymentserver.cpp · raw

// Copyright (c) 2011-2019 The Bitcoin Core developers// Distributed under the MIT software license, see the accompanying// file COPYING or http://www.opensource.org/licenses/mit-license.php.#if defined(HAVE_CONFIG_H)#include <config/bitcoin-config.h>#endif#include <qt/paymentserver.h>#include <qt/bitcoinunits.h>#include <qt/guiutil.h>#include <qt/optionsmodel.h>#include <chainparams.h>#include <interfaces/node.h>#include <key_io.h>#include <node/ui_interface.h>#include <policy/policy.h>#include <util/system.h>#include <wallet/wallet.h>#include <cstdlib>#include <memory>#include <QApplication>#include <QByteArray>#include <QDataStream>#include <QDateTime>#include <QDebug>#include <QFile>#include <QFileOpenEvent>#include <QHash>#include <QList>#include <QLocalServer>#include <QLocalSocket>#include <QStringList>#include <QUrlQuery>const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // millisecondsconst QString BITCOIN_IPC_PREFIX("moonbite:");//// Create a name that is unique for://  testnet / non-testnet//  data directory//static QString ipcServerName(){    QString name("LitecoinQt");    // Append a simple hash of the datadir    // Note that GetDataDir(true) returns a different path    // for -testnet versus main net    QString ddir(GUIUtil::boostPathToQString(GetDataDir(true)));    name.append(QString::number(qHash(ddir)));    return name;}//// We store payment URIs and requests received before// the main GUI window is up and ready to ask the user// to send payment.static QSet<QString> savedPaymentRequests;//// Sending to the server is done synchronously, at startup.// If the server isn't already running, startup continues,// and the items in savedPaymentRequest will be handled// when uiReady() is called.//// Warning: ipcSendCommandLine() is called early in init,// so don't use "Q_EMIT message()", but "QMessageBox::"!//void PaymentServer::ipcParseCommandLine(int argc, char* argv[]){    for (int i = 1; i < argc; i++)    {        QString arg(argv[i]);        if (arg.startsWith("-"))            continue;        // If the bitcoin: URI contains a payment request, we are not able to detect the        // network as that would require fetching and parsing the payment request.        // That means clicking such an URI which contains a testnet payment request        // will start a mainnet instance and throw a "wrong network" error.        if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI        {            if (savedPaymentRequests.contains(arg)) continue;            savedPaymentRequests.insert(arg);            SendCoinsRecipient r;            if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())            {                auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);                if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {                    SelectParams(CBaseChainParams::MAIN);                } else {                    tempChainParams = CreateChainParams(gArgs, CBaseChainParams::TESTNET);                    if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {                        SelectParams(CBaseChainParams::TESTNET);                    }                }            }        }    }}//// Sending to the server is done synchronously, at startup.// If the server isn't already running, startup continues,// and the items in savedPaymentRequest will be handled// when uiReady() is called.//bool PaymentServer::ipcSendCommandLine(){    bool fResult = false;    for (const QString& r : savedPaymentRequests)    {        QLocalSocket* socket = new QLocalSocket();        socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);        if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))        {            delete socket;            socket = nullptr;            return false;        }        QByteArray block;        QDataStream out(&block, QIODevice::WriteOnly);        out.setVersion(QDataStream::Qt_4_0);        out << r;        out.device()->seek(0);        socket->write(block);        socket->flush();        socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);        socket->disconnectFromServer();        delete socket;        socket = nullptr;        fResult = true;    }    return fResult;}PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :    QObject(parent),    saveURIs(true),    uriServer(nullptr),    optionsModel(nullptr){    // Install global event filter to catch QFileOpenEvents    // on Mac: sent when you click bitcoin: links    // other OSes: helpful when dealing with payment request files    if (parent)        parent->installEventFilter(this);    QString name = ipcServerName();    // Clean up old socket leftover from a crash:    QLocalServer::removeServer(name);    if (startLocalServer)    {        uriServer = new QLocalServer(this);        if (!uriServer->listen(name)) {            // constructor is called early in init, so don't use "Q_EMIT message()" here            QMessageBox::critical(nullptr, tr("Payment request error"),                tr("Cannot start moonbite: click-to-pay handler"));        }        else {            connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);        }    }}PaymentServer::~PaymentServer(){}//// OSX-specific way of handling bitcoin: URIs//bool PaymentServer::eventFilter(QObject *object, QEvent *event){    if (event->type() == QEvent::FileOpen) {        QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);        if (!fileEvent->file().isEmpty())            handleURIOrFile(fileEvent->file());        else if (!fileEvent->url().isEmpty())            handleURIOrFile(fileEvent->url().toString());        return true;    }    return QObject::eventFilter(object, event);}void PaymentServer::uiReady(){    saveURIs = false;    for (const QString& s : savedPaymentRequests)    {        handleURIOrFile(s);    }    savedPaymentRequests.clear();}void PaymentServer::handleURIOrFile(const QString& s){    if (saveURIs)    {        savedPaymentRequests.insert(s);        return;    }    if (s.startsWith("moonbite://", Qt::CaseInsensitive))    {        Q_EMIT message(tr("URI handling"), tr("'moonbite://' is not a valid URI. Use 'moonbite:' instead."),            CClientUIInterface::MSG_ERROR);    }    else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI    {        QUrlQuery uri((QUrl(s)));        // normal URI        {            SendCoinsRecipient recipient;            if (GUIUtil::parseBitcoinURI(s, &recipient))            {                if (!IsValidDestinationString(recipient.address.toStdString())) {                    if (uri.hasQueryItem("r")) {  // payment request                        Q_EMIT message(tr("URI handling"),                            tr("Cannot process payment request because BIP70 is not supported.")+                            tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+                            tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),                            CClientUIInterface::ICON_WARNING);                    }                    Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),                        CClientUIInterface::MSG_ERROR);                }                else                    Q_EMIT receivedPaymentRequest(recipient);            }            else                Q_EMIT message(tr("URI handling"),                    tr("URI cannot be parsed! This can be caused by an invalid Litecoin address or malformed URI parameters."),                    CClientUIInterface::ICON_WARNING);            return;        }    }    if (QFile::exists(s)) // payment request file    {        Q_EMIT message(tr("Payment request file handling"),            tr("Cannot process payment request because BIP70 is not supported.")+            tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+            tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),            CClientUIInterface::ICON_WARNING);    }}void PaymentServer::handleURIConnection(){    QLocalSocket *clientConnection = uriServer->nextPendingConnection();    while (clientConnection->bytesAvailable() < (int)sizeof(quint32))        clientConnection->waitForReadyRead();    connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);    QDataStream in(clientConnection);    in.setVersion(QDataStream::Qt_4_0);    if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {        return;    }    QString msg;    in >> msg;    handleURIOrFile(msg);}void PaymentServer::setOptionsModel(OptionsModel *_optionsModel){    this->optionsModel = _optionsModel;}