diff options
Diffstat (limited to 'src/qt/paymentserver.cpp')
-rw-r--r-- | src/qt/paymentserver.cpp | 258 |
1 files changed, 130 insertions, 128 deletions
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6c10d8a04d..34da0f90ec 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,7 +1,24 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "paymentserver.h" + +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "paymentserver.h" +#include "walletmodel.h" + +#include "base58.h" +#include "ui_interface.h" +#include "wallet.h" + +#include <cstdlib> + +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> #include <QApplication> #include <QByteArray> #include <QDataStream> @@ -13,8 +30,6 @@ #include <QList> #include <QLocalServer> #include <QLocalSocket> -#include <QStringList> -#include <QTextDocument> #include <QNetworkAccessManager> #include <QNetworkProxy> #include <QNetworkReply> @@ -22,34 +37,22 @@ #include <QSslCertificate> #include <QSslError> #include <QSslSocket> +#include <QStringList> +#include <QTextDocument> + #if QT_VERSION < 0x050000 #include <QUrl> #else #include <QUrlQuery> #endif -#include <cstdlib> - -#include <openssl/x509.h> -#include <openssl/x509_vfy.h> - -#include "base58.h" -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "paymentserver.h" -#include "ui_interface.h" -#include "util.h" -#include "wallet.h" -#include "walletmodel.h" - using namespace boost; const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("bitcoin:"); const char* BITCOIN_REQUEST_MIMETYPE = "application/bitcoin-paymentrequest"; const char* BITCOIN_PAYMENTACK_MIMETYPE = "application/bitcoin-paymentack"; +const char* BITCOIN_PAYMENTACK_CONTENTTYPE = "application/bitcoin-payment"; X509_STORE* PaymentServer::certStore = NULL; void PaymentServer::freeCertStore() @@ -73,14 +76,14 @@ static QString ipcServerName() // Append a simple hash of the datadir // Note that GetDataDir(true) returns a different path // for -testnet versus main net - QString ddir(GetDataDir(true).string().c_str()); + QString ddir(QString::fromStdString(GetDataDir(true).string())); name.append(QString::number(qHash(ddir))); return name; } // -// We store payment URLs and requests received before +// We store payment URIs and requests received before // the main GUI window is up and ready to ask the user // to send payment. @@ -88,9 +91,7 @@ static QList<QString> savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate& cert) { - if (fDebug) { - qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); - } + qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); } // @@ -161,8 +162,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) continue; } } - if (fDebug) - qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; + qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; // Project for another day: // Fetch certificate revocation lists, and add them to certStore. @@ -190,7 +190,7 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) if (arg.startsWith("-")) continue; - if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: + if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { savedPaymentRequests.append(arg); @@ -221,9 +221,9 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) } else { - qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << argv[i]; // Printing to debug.log is about the best we can do here, the // GUI hasn't started yet so we can't pop up a message box. + qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg; } } @@ -247,17 +247,24 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) delete socket; fResult = true; } + return fResult; } -PaymentServer::PaymentServer(QObject* parent, - bool startLocalServer) : QObject(parent), saveURIs(true) +PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : + QObject(parent), + saveURIs(true), + uriServer(0), + netManager(0), + optionsModel(0) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; - // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + // Install global event filter to catch QFileOpenEvents + // on Mac: sent when you click bitcoin: links + // other OSes: helpful when dealing with payment request files (in the future) if (parent) parent->installEventFilter(this); @@ -272,12 +279,11 @@ PaymentServer::PaymentServer(QObject* parent, if (!uriServer->listen(name)) qDebug() << "PaymentServer::PaymentServer : Cannot start bitcoin: click-to-pay handler"; - else + else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); + connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); + } } - - // netManager is null until uiReady() is called - netManager = NULL; } PaymentServer::~PaymentServer() @@ -286,12 +292,12 @@ PaymentServer::~PaymentServer() } // -// OSX-specific way of handling bitcoin uris and +// OSX-specific way of handling bitcoin: URIs and // PaymentRequest mime types // bool PaymentServer::eventFilter(QObject *, QEvent *event) { - // clicking on bitcoin: URLs creates FileOpen events on the Mac: + // clicking on bitcoin: URIs creates FileOpen events on the Mac: if (event->type() == QEvent::FileOpen) { QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); @@ -312,10 +318,10 @@ void PaymentServer::initNetManager() if (netManager != NULL) delete netManager; - // netManager is used to fetch paymentrequests given in bitcoin: URI's + // netManager is used to fetch paymentrequests given in bitcoin: URIs netManager = new QNetworkAccessManager(this); - // Use proxy settings from optionsModel: + // Use proxy settings from optionsModel QString proxyIP; quint16 proxyPort; if (optionsModel->getProxySettings(proxyIP, proxyPort)) @@ -335,7 +341,7 @@ void PaymentServer::initNetManager() void PaymentServer::uiReady() { - assert(netManager != NULL); // Must call initNetManager before uiReady() + initNetManager(); saveURIs = false; foreach (const QString& s, savedPaymentRequests) @@ -355,42 +361,46 @@ void PaymentServer::handleURIOrFile(const QString& s) if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: { -#if QT_VERSION >= 0x050000 - QUrlQuery url((QUrl(s))); +#if QT_VERSION < 0x050000 + QUrl uri(s); #else - QUrl url(s); + QUrlQuery uri((QUrl(s))); #endif - if (url.hasQueryItem("request")) + if (uri.hasQueryItem("request")) { - QByteArray temp; temp.append(url.queryItemValue("request")); + QByteArray temp; + temp.append(uri.queryItemValue("request")); QString decoded = QUrl::fromPercentEncoding(temp); QUrl fetchUrl(decoded, QUrl::StrictMode); - if (fDebug) - qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; + qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; if (fetchUrl.isValid()) fetchRequest(fetchUrl); else - qDebug() << "PaymentServer::handleURIOrFile : Invalid url: " << fetchUrl; + qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; + return; } SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) emit receivedPaymentRequest(recipient); + else + emit message(tr("URI handling"), + tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); + return; } if (QFile::exists(s)) { PaymentRequestPlus request; - QList<SendCoinsRecipient> recipients; - if (readPaymentRequest(s, request) && processPaymentRequest(request, recipients)) { - foreach (const SendCoinsRecipient& recipient, recipients){ - emit receivedPaymentRequest(recipient); - } - } + SendCoinsRecipient recipient; + if (readPaymentRequest(s, request) && processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); + return; } } @@ -410,10 +420,10 @@ void PaymentServer::handleURIConnection() if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { return; } - QString message; - in >> message; + QString msg; + in >> msg; - handleURIOrFile(message); + handleURIOrFile(msg); } bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPlus& request) @@ -436,76 +446,63 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl return request.parse(data); } -bool -PaymentServer::processPaymentRequest(PaymentRequestPlus& request, - QList<SendCoinsRecipient>& recipients) +bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient) { if (!optionsModel) return false; - QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo(); - qint64 totalAmount = 0; + recipient.paymentRequest = request; + recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo()); + + request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant); + + QList<std::pair<CScript, qint64> > sendingTos = request.getPayTo(); + QStringList addresses; + foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) { + // Extract and check destination addresses + CTxDestination dest; + if (ExtractDestination(sendingTo.first, dest)) { + // Append destination address + addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); + } + else if (!recipient.authenticatedMerchant.isEmpty()){ + // Insecure payments to custom bitcoin addresses are not supported + // (there is no good way to tell the user where they are paying in a way + // they'd have a chance of understanding). + emit message(tr("Payment request error"), + tr("Unverified payment requests to custom payment scripts are unsupported."), + CClientUIInterface::MSG_ERROR); + return false; + } + + // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { - QString message = QObject::tr("Requested payment amount (%1) too small") + QString msg = tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)); - qDebug() << "PaymentServer::processPaymentRequest : " << message; - emit reportError(tr("Payment request error"), message, CClientUIInterface::MODAL); + qDebug() << "PaymentServer::processPaymentRequest : " << msg; + emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); return false; } - totalAmount += sendingTo.second; + recipient.amount += sendingTo.second; } + // Store addresses and format them to fit nicely into the GUI + recipient.address = addresses.join("<br />"); - recipients.append(SendCoinsRecipient()); - - if (request.getMerchant(PaymentServer::certStore, recipients[0].authenticatedMerchant)) { - recipients[0].paymentRequest = request; - recipients[0].amount = totalAmount; - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant; + if (!recipient.authenticatedMerchant.isEmpty()) { + qDebug() << "PaymentServer::processPaymentRequest : Secure payment request from " << recipient.authenticatedMerchant; } else { - recipients.clear(); - // Insecure payment requests may turn into more than one recipient if - // the merchant is requesting payment to more than one address. - for (int i = 0; i < sendingTos.size(); i++) { - std::pair<CScript, qint64>& sendingTo = sendingTos[i]; - recipients.append(SendCoinsRecipient()); - recipients[i].amount = sendingTo.second; - QString memo = QString::fromStdString(request.getDetails().memo()); -#if QT_VERSION < 0x050000 - recipients[i].label = Qt::escape(memo); -#else - recipients[i].label = memo.toHtmlEscaped(); -#endif - CTxDestination dest; - if (ExtractDestination(sendingTo.first, dest)) { - if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs - recipients[i].paymentRequest = request; - recipients[i].address = QString::fromStdString(CBitcoinAddress(dest).ToString()); - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address; - } - else { - // Insecure payments to custom bitcoin addresses are not supported - // (there is no good way to tell the user where they are paying in a way - // they'd have a chance of understanding). - emit reportError(tr("Payment request error"), - tr("Insecure requests to custom payment scripts unsupported"), - CClientUIInterface::MODAL); - return false; - } - } + qDebug() << "PaymentServer::processPaymentRequest : Insecure payment request to " << addresses.join(", "); } return true; } -void -PaymentServer::fetchRequest(const QUrl& url) +void PaymentServer::fetchRequest(const QUrl& url) { QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentRequest"); @@ -515,8 +512,7 @@ PaymentServer::fetchRequest(const QUrl& url) netManager->get(netRequest); } -void -PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) +void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) { const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); if (!details.has_payment_url()) @@ -525,7 +521,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentACK"); netRequest.setUrl(QString::fromStdString(details.payment_url())); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/bitcoin-payment"); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE); netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE); @@ -534,7 +530,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB payment.add_transactions(transaction.data(), transaction.size()); // Create a new refund address, or re-use: - QString account = tr("Refund from") + QString(" ") + recipient.authenticatedMerchant; + QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant); std::string strAccount = account.toStdString(); set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount); if (!refundAddresses.empty()) { @@ -571,17 +567,17 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB } } -void -PaymentServer::netRequestFinished(QNetworkReply* reply) +void PaymentServer::netRequestFinished(QNetworkReply* reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - QString message = QObject::tr("Error communicating with %1: %2") + QString msg = tr("Error communicating with %1: %2") .arg(reply->request().url().toString()) .arg(reply->errorString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); return; } @@ -591,14 +587,12 @@ PaymentServer::netRequestFinished(QNetworkReply* reply) if (requestType == "PaymentRequest") { PaymentRequestPlus request; - QList<SendCoinsRecipient> recipients; - if (request.parse(data) && processPaymentRequest(request, recipients)) { - foreach (const SendCoinsRecipient& recipient, recipients){ - emit receivedPaymentRequest(recipient); - } - } + SendCoinsRecipient recipient; + if (request.parse(data) && processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); else qDebug() << "PaymentServer::netRequestFinished : Error processing payment request"; + return; } else if (requestType == "PaymentACK") @@ -606,29 +600,37 @@ PaymentServer::netRequestFinished(QNetworkReply* reply) payments::PaymentACK paymentACK; if (!paymentACK.ParseFromArray(data.data(), data.size())) { - QString message = QObject::tr("Bad response from server %1") + QString msg = tr("Bad response from server %1") .arg(reply->request().url().toString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); } else { - emit receivedPaymentACK(QString::fromStdString(paymentACK.memo())); + emit receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo())); } } } -void -PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs) +void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs) { + Q_UNUSED(reply); + QString errString; foreach (const QSslError& err, errs) { qDebug() << "PaymentServer::reportSslErrors : " << err; errString += err.errorString() + "\n"; } - emit reportError(tr("Network request error"), errString, CClientUIInterface::MODAL); + emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR); } void PaymentServer::setOptionsModel(OptionsModel *optionsModel) { this->optionsModel = optionsModel; } + +void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) +{ + // currently we don't futher process or store the paymentACK message + emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); +} |