aboutsummaryrefslogtreecommitdiff
path: root/src/qt/paymentserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt/paymentserver.cpp')
-rw-r--r--src/qt/paymentserver.cpp258
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);
+}