Changeset View
Changeset View
Standalone View
Standalone View
src/qt/paymentserver.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // 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/paymentserver.h> | ||||
#include <cashaddrenc.h> | #include <cashaddrenc.h> | ||||
#include <chainparams.h> | #include <chainparams.h> | ||||
#include <interfaces/node.h> | #include <interfaces/node.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <qt/bitcoinunits.h> | #include <qt/bitcoinunits.h> | ||||
Show All 26 Lines | |||||
#include <QStringList> | #include <QStringList> | ||||
#include <QTextDocument> | #include <QTextDocument> | ||||
#include <QUrlQuery> | #include <QUrlQuery> | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <memory> | #include <memory> | ||||
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds | const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds | ||||
#ifdef ENABLE_BIP70 | |||||
// BIP70 payment protocol messages | // BIP70 payment protocol messages | ||||
const char *BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; | const char *BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; | ||||
const char *BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; | const char *BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; | ||||
// BIP71 payment protocol media types | // BIP71 payment protocol media types | ||||
const char *BIP71_MIMETYPE_PAYMENT = "application/bitcoincash-payment"; | const char *BIP71_MIMETYPE_PAYMENT = "application/bitcoincash-payment"; | ||||
const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack"; | const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack"; | ||||
const char *BIP71_MIMETYPE_PAYMENTREQUEST = | const char *BIP71_MIMETYPE_PAYMENTREQUEST = | ||||
"application/bitcoincash-paymentrequest"; | "application/bitcoincash-paymentrequest"; | ||||
#endif | |||||
// | // | ||||
// Create a name that is unique for: | // Create a name that is unique for: | ||||
// testnet / non-testnet | // testnet / non-testnet | ||||
// data directory | // data directory | ||||
// | // | ||||
static QString ipcServerName() { | static QString ipcServerName() { | ||||
QString name("BitcoinQt"); | QString name("BitcoinQt"); | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | for (int i = 1; i < argc; i++) { | ||||
} | } | ||||
if (ipcCanParseLegacyURI(arg, *net)) { | if (ipcCanParseLegacyURI(arg, *net)) { | ||||
itemNetwork = net; | itemNetwork = net; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
#ifdef ENABLE_BIP70 | |||||
if (!itemNetwork && QFile::exists(arg)) { | if (!itemNetwork && QFile::exists(arg)) { | ||||
// Filename | // Filename | ||||
PaymentRequestPlus request; | PaymentRequestPlus request; | ||||
if (readPaymentRequestFromFile(arg, request)) { | if (readPaymentRequestFromFile(arg, request)) { | ||||
for (auto net : networks) { | for (auto net : networks) { | ||||
if (*net == request.getDetails().network()) { | if (*net == request.getDetails().network()) { | ||||
itemNetwork = net; | itemNetwork = net; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#endif | |||||
if (itemNetwork == nullptr) { | if (itemNetwork == nullptr) { | ||||
// Printing to debug.log is about the best we can do here, the GUI | // 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. | // hasn't started yet so we can't pop up a message box. | ||||
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | ||||
"file or URI does not exist or is invalid: " | "file or URI does not exist or is invalid: " | ||||
<< arg; | << arg; | ||||
continue; | continue; | ||||
} | } | ||||
#ifdef ENABLE_BIP70 | |||||
if (chosenNetwork && chosenNetwork != itemNetwork) { | if (chosenNetwork && chosenNetwork != itemNetwork) { | ||||
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | ||||
"from network " | "from network " | ||||
<< QString(itemNetwork->c_str()) | << QString(itemNetwork->c_str()) | ||||
<< " does not match already chosen network " | << " does not match already chosen network " | ||||
<< QString(chosenNetwork->c_str()); | << QString(chosenNetwork->c_str()); | ||||
continue; | continue; | ||||
} | } | ||||
savedPaymentRequests.append(arg); | savedPaymentRequests.append(arg); | ||||
#endif | |||||
chosenNetwork = itemNetwork; | chosenNetwork = itemNetwork; | ||||
} | } | ||||
if (chosenNetwork) { | if (chosenNetwork) { | ||||
node.selectParams(*chosenNetwork); | node.selectParams(*chosenNetwork); | ||||
} | } | ||||
} | } | ||||
Show All 28 Lines | for (const QString &r : savedPaymentRequests) { | ||||
socket = nullptr; | socket = nullptr; | ||||
fResult = true; | fResult = true; | ||||
} | } | ||||
return fResult; | return fResult; | ||||
} | } | ||||
PaymentServer::PaymentServer(QObject *parent, bool startLocalServer) | PaymentServer::PaymentServer(QObject *parent, bool startLocalServer) | ||||
: QObject(parent), saveURIs(true), uriServer(0), optionsModel(0), | : QObject(parent), saveURIs(true), uriServer(0), optionsModel(0) | ||||
netManager(0) { | // clang-format off | ||||
#ifdef ENABLE_BIP70 | |||||
,netManager(0) | |||||
#endif | |||||
// clang-format on | |||||
{ | |||||
#ifdef ENABLE_BIP70 | |||||
// Verify that the version of the library that we linked against is | // Verify that the version of the library that we linked against is | ||||
// compatible with the version of the headers we compiled against. | // compatible with the version of the headers we compiled against. | ||||
GOOGLE_PROTOBUF_VERIFY_VERSION; | GOOGLE_PROTOBUF_VERIFY_VERSION; | ||||
#endif | |||||
// Install global event filter to catch QFileOpenEvents | // Install global event filter to catch QFileOpenEvents | ||||
// on Mac: sent when you click bitcoincash: links | // on Mac: sent when you click bitcoincash: links | ||||
// other OSes: helpful when dealing with payment request files | // other OSes: helpful when dealing with payment request files | ||||
if (parent) { | if (parent) { | ||||
parent->installEventFilter(this); | parent->installEventFilter(this); | ||||
} | } | ||||
QString name = ipcServerName(); | QString name = ipcServerName(); | ||||
// Clean up old socket leftover from a crash: | // Clean up old socket leftover from a crash: | ||||
QLocalServer::removeServer(name); | QLocalServer::removeServer(name); | ||||
if (startLocalServer) { | if (startLocalServer) { | ||||
uriServer = new QLocalServer(this); | uriServer = new QLocalServer(this); | ||||
if (!uriServer->listen(name)) { | if (!uriServer->listen(name)) { | ||||
// constructor is called early in init, so don't use "Q_EMIT | // constructor is called early in init, so don't use "Q_EMIT | ||||
// message()" here | // message()" here | ||||
QMessageBox::critical(0, tr("Payment request error"), | QMessageBox::critical(0, tr("Payment request error"), | ||||
tr("Cannot start click-to-pay handler")); | tr("Cannot start click-to-pay handler")); | ||||
} else { | } else { | ||||
connect(uriServer, &QLocalServer::newConnection, this, | connect(uriServer, &QLocalServer::newConnection, this, | ||||
&PaymentServer::handleURIConnection); | &PaymentServer::handleURIConnection); | ||||
#ifdef ENABLE_BIP70 | |||||
connect(this, &PaymentServer::receivedPaymentACK, this, | connect(this, &PaymentServer::receivedPaymentACK, this, | ||||
&PaymentServer::handlePaymentACK); | &PaymentServer::handlePaymentACK); | ||||
#endif | |||||
} | } | ||||
} | } | ||||
} | } | ||||
PaymentServer::~PaymentServer() { | PaymentServer::~PaymentServer() { | ||||
#ifdef ENABLE_BIP70 | |||||
google::protobuf::ShutdownProtobufLibrary(); | google::protobuf::ShutdownProtobufLibrary(); | ||||
#endif | |||||
} | } | ||||
// | // | ||||
// OSX-specific way of handling bitcoincash: URIs and PaymentRequest mime types. | // OSX-specific way of handling bitcoincash: URIs and PaymentRequest mime types. | ||||
// Also used by paymentservertests.cpp and when opening a payment request file | // Also used by paymentservertests.cpp and when opening a payment request file | ||||
// via "Open URI..." menu entry. | // via "Open URI..." menu entry. | ||||
// | // | ||||
bool PaymentServer::eventFilter(QObject *object, QEvent *event) { | bool PaymentServer::eventFilter(QObject *object, QEvent *event) { | ||||
if (event->type() == QEvent::FileOpen) { | if (event->type() == QEvent::FileOpen) { | ||||
QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent *>(event); | QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent *>(event); | ||||
if (!fileEvent->file().isEmpty()) { | if (!fileEvent->file().isEmpty()) { | ||||
handleURIOrFile(fileEvent->file()); | handleURIOrFile(fileEvent->file()); | ||||
} else if (!fileEvent->url().isEmpty()) { | } else if (!fileEvent->url().isEmpty()) { | ||||
handleURIOrFile(fileEvent->url().toString()); | handleURIOrFile(fileEvent->url().toString()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
return QObject::eventFilter(object, event); | return QObject::eventFilter(object, event); | ||||
} | } | ||||
void PaymentServer::uiReady() { | void PaymentServer::uiReady() { | ||||
#ifdef ENABLE_BIP70 | |||||
initNetManager(); | initNetManager(); | ||||
#endif | |||||
saveURIs = false; | saveURIs = false; | ||||
for (const QString &s : savedPaymentRequests) { | for (const QString &s : savedPaymentRequests) { | ||||
handleURIOrFile(s); | handleURIOrFile(s); | ||||
} | } | ||||
savedPaymentRequests.clear(); | savedPaymentRequests.clear(); | ||||
} | } | ||||
bool PaymentServer::handleURI(const CChainParams ¶ms, const QString &s) { | bool PaymentServer::handleURI(const CChainParams ¶ms, const QString &s) { | ||||
const QString scheme = QString::fromStdString(params.CashAddrPrefix()); | const QString scheme = QString::fromStdString(params.CashAddrPrefix()); | ||||
if (!s.startsWith(scheme + ":", Qt::CaseInsensitive)) { | if (!s.startsWith(scheme + ":", Qt::CaseInsensitive)) { | ||||
return false; | return false; | ||||
} | } | ||||
QUrlQuery uri((QUrl(s))); | QUrlQuery uri((QUrl(s))); | ||||
if (uri.hasQueryItem("r")) { | |||||
// payment request URI | // payment request URI | ||||
if (uri.hasQueryItem("r")) { | |||||
#ifdef ENABLE_BIP70 | |||||
QByteArray temp; | QByteArray temp; | ||||
temp.append(uri.queryItemValue("r")); | temp.append(uri.queryItemValue("r")); | ||||
QString decoded = QUrl::fromPercentEncoding(temp); | QString decoded = QUrl::fromPercentEncoding(temp); | ||||
QUrl fetchUrl(decoded, QUrl::StrictMode); | QUrl fetchUrl(decoded, QUrl::StrictMode); | ||||
if (fetchUrl.isValid()) { | if (fetchUrl.isValid()) { | ||||
qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" | qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" | ||||
<< fetchUrl << ")"; | << fetchUrl << ")"; | ||||
fetchRequest(fetchUrl); | fetchRequest(fetchUrl); | ||||
} else { | } else { | ||||
qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " | qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " | ||||
<< fetchUrl; | << fetchUrl; | ||||
Q_EMIT message(tr("URI handling"), | Q_EMIT message(tr("URI handling"), | ||||
tr("Payment request fetch URL is invalid: %1") | tr("Payment request fetch URL is invalid: %1") | ||||
.arg(fetchUrl.toString()), | .arg(fetchUrl.toString()), | ||||
CClientUIInterface::ICON_WARNING); | CClientUIInterface::ICON_WARNING); | ||||
} | } | ||||
#else | |||||
Q_EMIT message(tr("URI handling"), | |||||
tr("Cannot process payment request because BIP70 " | |||||
"support was not compiled in."), | |||||
CClientUIInterface::ICON_WARNING); | |||||
#endif | |||||
return true; | return true; | ||||
} | } | ||||
// normal URI | // normal URI | ||||
SendCoinsRecipient recipient; | SendCoinsRecipient recipient; | ||||
if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) { | if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) { | ||||
if (!IsValidDestinationString(recipient.address.toStdString(), | if (!IsValidDestinationString(recipient.address.toStdString(), | ||||
params)) { | params)) { | ||||
Show All 21 Lines | if (saveURIs) { | ||||
return; | return; | ||||
} | } | ||||
// bitcoincash: CashAddr URI | // bitcoincash: CashAddr URI | ||||
if (handleURI(Params(), s)) { | if (handleURI(Params(), s)) { | ||||
return; | return; | ||||
} | } | ||||
#ifdef ENABLE_BIP70 | |||||
// payment request file | // payment request file | ||||
if (QFile::exists(s)) { | if (QFile::exists(s)) { | ||||
PaymentRequestPlus request; | PaymentRequestPlus request; | ||||
SendCoinsRecipient recipient; | SendCoinsRecipient recipient; | ||||
if (!readPaymentRequestFromFile(s, request)) { | if (!readPaymentRequestFromFile(s, request)) { | ||||
Q_EMIT message(tr("Payment request file handling"), | Q_EMIT message(tr("Payment request file handling"), | ||||
tr("Payment request file cannot be read! This can " | tr("Payment request file cannot be read! This can " | ||||
"be caused by an invalid payment request file."), | "be caused by an invalid payment request file."), | ||||
CClientUIInterface::ICON_WARNING); | CClientUIInterface::ICON_WARNING); | ||||
} else if (processPaymentRequest(request, recipient)) { | } else if (processPaymentRequest(request, recipient)) { | ||||
Q_EMIT receivedPaymentRequest(recipient); | Q_EMIT receivedPaymentRequest(recipient); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
#endif | |||||
} | } | ||||
void PaymentServer::handleURIConnection() { | void PaymentServer::handleURIConnection() { | ||||
QLocalSocket *clientConnection = uriServer->nextPendingConnection(); | QLocalSocket *clientConnection = uriServer->nextPendingConnection(); | ||||
while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) { | while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) { | ||||
clientConnection->waitForReadyRead(); | clientConnection->waitForReadyRead(); | ||||
} | } | ||||
Show All 11 Lines | void PaymentServer::handleURIConnection() { | ||||
handleURIOrFile(msg); | handleURIOrFile(msg); | ||||
} | } | ||||
void PaymentServer::setOptionsModel(OptionsModel *_optionsModel) { | void PaymentServer::setOptionsModel(OptionsModel *_optionsModel) { | ||||
this->optionsModel = _optionsModel; | this->optionsModel = _optionsModel; | ||||
} | } | ||||
#ifdef ENABLE_BIP70 | |||||
struct X509StoreDeleter { | struct X509StoreDeleter { | ||||
void operator()(X509_STORE *b) { X509_STORE_free(b); } | void operator()(X509_STORE *b) { X509_STORE_free(b); } | ||||
}; | }; | ||||
struct X509Deleter { | struct X509Deleter { | ||||
void operator()(X509 *b) { X509_free(b); } | void operator()(X509 *b) { X509_free(b); } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 471 Lines • ▼ Show 20 Lines | if (!fVerified) { | ||||
.arg(MAX_MONEY / SATOSHI); | .arg(MAX_MONEY / SATOSHI); | ||||
} | } | ||||
return fVerified; | return fVerified; | ||||
} | } | ||||
X509_STORE *PaymentServer::getCertStore() { | X509_STORE *PaymentServer::getCertStore() { | ||||
return certStore.get(); | return certStore.get(); | ||||
} | } | ||||
#endif |