diff --git a/configure.ac b/configure.ac index 72bd785e2..7141a9a03 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,11 @@ AC_ARG_ENABLE([zmq], [disable ZMQ notifications])], [use_zmq=$enableval], [use_zmq=yes]) +AC_ARG_ENABLE([bip70], + [AS_HELP_STRING([--disable-bip70], + [disable BIP70 (payment protocol) support in GUI (enabled by default)])], + [enable_bip70=$enableval], + [enable_bip70=yes]) AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) @@ -1082,7 +1087,9 @@ if test x$use_pkgconfig = xyes; then [ PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) - BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) + if test x$enable_bip70 != xno; then + BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) + fi if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi @@ -1142,7 +1149,9 @@ else esac fi - BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) + if test x$enable_bip70 != xno; then + BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) + fi if test x$use_qr != xno; then BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])]) BITCOIN_QT_CHECK([AC_CHECK_HEADER([qrencode.h],, have_qrencode=no)]) @@ -1220,7 +1229,9 @@ AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) +if test x$enable_bip70 != xno; then BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) +fi AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) @@ -1338,6 +1349,15 @@ if test x$bitcoin_enable_qt != xno; then else AC_MSG_RESULT([no]) fi + + AC_MSG_CHECKING([whether to build BIP70 support]) + if test x$enable_bip70 != xno; then + AC_DEFINE([ENABLE_BIP70],[1],[Define if BIP70 support should be compiled in]) + enable_bip70=yes + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi fi AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"]) @@ -1369,6 +1389,7 @@ AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes]) AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes]) +AM_CONDITIONAL([ENABLE_BIP70],[test x$enable_bip70 = xyes]) AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) @@ -1503,6 +1524,7 @@ echo "Options used to compile and link:" echo " with wallet = $enable_wallet" echo " with gui / qt = $bitcoin_enable_qt" if test x$bitcoin_enable_qt != xno; then + echo " with bip70 = $enable_bip70" echo " with qr = $use_qr" fi echo " with zmq = $use_zmq" diff --git a/src/Makefile.am b/src/Makefile.am index 7a2e9fa5e..614191900 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -587,9 +587,11 @@ if HARDEN $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif +if ENABLE_BIP70 %.pb.cc %.pb.h: %.proto @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(setCurrentWallet(walletModel->getWalletName()); } +#ifdef ENABLE_BIP70 connect(walletModel, &WalletModel::coinsSent, paymentServer, &PaymentServer::fetchPaymentACK); +#endif connect(walletModel, &WalletModel::unload, this, &BitcoinApplication::removeWallet); m_wallet_models.push_back(walletModel); @@ -467,7 +469,9 @@ void BitcoinApplication::initializeResult(bool success) // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete qWarning() << "Platform customization:" << platformStyle->getName(); #ifdef ENABLE_WALLET +#ifdef ENABLE_BIP70 PaymentServer::LoadRootCAs(); +#endif paymentServer->setOptionsModel(optionsModel); #endif @@ -536,7 +540,7 @@ WId BitcoinApplication::getMainWinId() const static void SetupUIArgs() { -#ifdef ENABLE_WALLET +#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70) gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI); #endif gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 68330c51f..ea970c0bc 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -2,10 +2,15 @@ // 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 +#endif + #include #include #include +#include #include #include #include diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index bcafc8f85..3f118e37f 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -2,6 +2,10 @@ // 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 +#endif + #include #include @@ -45,6 +49,7 @@ const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("bitcoin:"); +#ifdef ENABLE_BIP70 // BIP70 payment protocol messages const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; @@ -52,21 +57,7 @@ const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment"; const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack"; const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest"; - -struct X509StoreDeleter { - void operator()(X509_STORE* b) { - X509_STORE_free(b); - } -}; - -struct X509Deleter { - void operator()(X509* b) { X509_free(b); } -}; - -namespace // Anon namespace -{ - std::unique_ptr certStore; -} +#endif // // Create a name that is unique for: @@ -93,6 +84,325 @@ static QString ipcServerName() static QList 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(interfaces::Node& node, 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 + { + savedPaymentRequests.append(arg); + + SendCoinsRecipient r; + if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) + { + auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); + + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { + node.selectParams(CBaseChainParams::MAIN); + } else { + tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { + node.selectParams(CBaseChainParams::TESTNET); + } + } + } + } +#ifdef ENABLE_BIP70 + else if (QFile::exists(arg)) // Filename + { + savedPaymentRequests.append(arg); + + PaymentRequestPlus request; + if (readPaymentRequestFromFile(arg, request)) + { + if (request.getDetails().network() == "main") + { + node.selectParams(CBaseChainParams::MAIN); + } + else if (request.getDetails().network() == "test") + { + node.selectParams(CBaseChainParams::TESTNET); + } + } + } + else + { + // 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. + qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg; + } +#endif + } +} + +// +// 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(0), + optionsModel(0) +#ifdef ENABLE_BIP70 + ,netManager(0) +#endif +{ +#ifdef ENABLE_BIP70 + // 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; +#endif + + // 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(0, tr("Payment request error"), + tr("Cannot start bitcoin: click-to-pay handler")); + } + else { + connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection); +#ifdef ENABLE_BIP70 + connect(this, &PaymentServer::receivedPaymentACK, this, &PaymentServer::handlePaymentACK); +#endif + } + } +} + +PaymentServer::~PaymentServer() +{ +#ifdef ENABLE_BIP70 + google::protobuf::ShutdownProtobufLibrary(); +#endif +} + +// +// OSX-specific way of handling bitcoin: URIs and PaymentRequest mime types. +// Also used by paymentservertests.cpp and when opening a payment request file +// via "Open URI..." menu entry. +// +bool PaymentServer::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent *fileEvent = static_cast(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() +{ +#ifdef ENABLE_BIP70 + initNetManager(); +#endif + + saveURIs = false; + for (const QString& s : savedPaymentRequests) + { + handleURIOrFile(s); + } + savedPaymentRequests.clear(); +} + +void PaymentServer::handleURIOrFile(const QString& s) +{ + if (saveURIs) + { + savedPaymentRequests.append(s); + return; + } + + if (s.startsWith("bitcoin://", Qt::CaseInsensitive)) + { + Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."), + CClientUIInterface::MSG_ERROR); + } + else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI + { + QUrlQuery uri((QUrl(s))); + if (uri.hasQueryItem("r")) // payment request URI + { +#ifdef ENABLE_BIP70 + Q_EMIT message(tr("URI handling"), + tr("You are using a BIP70 URL which will be unsupported in the future."), + CClientUIInterface::ICON_WARNING); + QByteArray temp; + temp.append(uri.queryItemValue("r")); + QString decoded = QUrl::fromPercentEncoding(temp); + QUrl fetchUrl(decoded, QUrl::StrictMode); + + if (fetchUrl.isValid()) + { + qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")"; + fetchRequest(fetchUrl); + } + else + { + qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl; + Q_EMIT message(tr("URI handling"), + tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()), + 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; + } + else // normal URI + { + SendCoinsRecipient recipient; + if (GUIUtil::parseBitcoinURI(s, &recipient)) + { + if (!IsValidDestinationString(recipient.address.toStdString())) { + 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 Bitcoin address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); + + return; + } + } + +#ifdef ENABLE_BIP70 + if (QFile::exists(s)) // payment request file + { + PaymentRequestPlus request; + SendCoinsRecipient recipient; + if (!readPaymentRequestFromFile(s, request)) + { + Q_EMIT message(tr("Payment request file handling"), + tr("Payment request file cannot be read! This can be caused by an invalid payment request file."), + CClientUIInterface::ICON_WARNING); + } + else if (processPaymentRequest(request, recipient)) + Q_EMIT receivedPaymentRequest(recipient); + + return; + } +#endif +} + +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; +} + +#ifdef ENABLE_BIP70 +struct X509StoreDeleter { + void operator()(X509_STORE* b) { + X509_STORE_free(b); + } +}; + +struct X509Deleter { + void operator()(X509* b) { X509_free(b); } +}; + +namespace // Anon namespace +{ + std::unique_ptr certStore; +} + static void ReportInvalidCertificate(const QSslCertificate& cert) { qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName); @@ -153,6 +463,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) ReportInvalidCertificate(cert); continue; } + QByteArray certData = cert.toDer(); const unsigned char *data = (const unsigned char *)certData.data(); @@ -181,174 +492,6 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) // "certificate stapling" with server-side caching is more efficient } -// -// 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(interfaces::Node& node, 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 - { - savedPaymentRequests.append(arg); - - SendCoinsRecipient r; - if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) - { - auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); - - if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { - node.selectParams(CBaseChainParams::MAIN); - } else { - tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); - if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { - node.selectParams(CBaseChainParams::TESTNET); - } - } - } - } - else if (QFile::exists(arg)) // Filename - { - savedPaymentRequests.append(arg); - - PaymentRequestPlus request; - if (readPaymentRequestFromFile(arg, request)) - { - if (request.getDetails().network() == "main") - { - node.selectParams(CBaseChainParams::MAIN); - } - else if (request.getDetails().network() == "test") - { - node.selectParams(CBaseChainParams::TESTNET); - } - } - } - else - { - // 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. - qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg; - } - } -} - -// -// 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(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 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(0, tr("Payment request error"), - tr("Cannot start bitcoin: click-to-pay handler")); - } - else { - connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection); - connect(this, &PaymentServer::receivedPaymentACK, this, &PaymentServer::handlePaymentACK); - } - } -} - -PaymentServer::~PaymentServer() -{ - google::protobuf::ShutdownProtobufLibrary(); -} - -// -// OSX-specific way of handling bitcoin: URIs and PaymentRequest mime types. -// Also used by paymentservertests.cpp and when opening a payment request file -// via "Open URI..." menu entry. -// -bool PaymentServer::eventFilter(QObject *object, QEvent *event) -{ - if (event->type() == QEvent::FileOpen) { - QFileOpenEvent *fileEvent = static_cast(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::initNetManager() { if (!optionsModel) @@ -373,114 +516,6 @@ void PaymentServer::initNetManager() connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors); } -void PaymentServer::uiReady() -{ - initNetManager(); - - saveURIs = false; - for (const QString& s : savedPaymentRequests) - { - handleURIOrFile(s); - } - savedPaymentRequests.clear(); -} - -void PaymentServer::handleURIOrFile(const QString& s) -{ - if (saveURIs) - { - savedPaymentRequests.append(s); - return; - } - - if (s.startsWith("bitcoin://", Qt::CaseInsensitive)) - { - Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."), - CClientUIInterface::MSG_ERROR); - } - else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI - { - QUrlQuery uri((QUrl(s))); - if (uri.hasQueryItem("r")) // payment request URI - { - QByteArray temp; - temp.append(uri.queryItemValue("r")); - QString decoded = QUrl::fromPercentEncoding(temp); - QUrl fetchUrl(decoded, QUrl::StrictMode); - - if (fetchUrl.isValid()) - { - qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")"; - fetchRequest(fetchUrl); - } - else - { - qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl; - Q_EMIT message(tr("URI handling"), - tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()), - CClientUIInterface::ICON_WARNING); - } - - return; - } - else // normal URI - { - SendCoinsRecipient recipient; - if (GUIUtil::parseBitcoinURI(s, &recipient)) - { - if (!IsValidDestinationString(recipient.address.toStdString())) { - 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 Bitcoin address or malformed URI parameters."), - CClientUIInterface::ICON_WARNING); - - return; - } - } - - if (QFile::exists(s)) // payment request file - { - PaymentRequestPlus request; - SendCoinsRecipient recipient; - if (!readPaymentRequestFromFile(s, request)) - { - Q_EMIT message(tr("Payment request file handling"), - tr("Payment request file cannot be read! This can be caused by an invalid payment request file."), - CClientUIInterface::ICON_WARNING); - } - else if (processPaymentRequest(request, recipient)) - Q_EMIT receivedPaymentRequest(recipient); - - return; - } -} - -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); -} - // // Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine() // so don't use "Q_EMIT message()", but "QMessageBox::"! @@ -731,11 +766,6 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList Q_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 further process or store the paymentACK message @@ -794,3 +824,4 @@ X509_STORE* PaymentServer::getCertStore() { return certStore.get(); } +#endif diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index d335db9c8..30b5bc3b6 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -32,7 +32,13 @@ // sends them to the server. // +#if defined(HAVE_CONFIG_H) +#include +#endif + +#ifdef ENABLE_BIP70 #include +#endif #include #include @@ -73,6 +79,10 @@ public: explicit PaymentServer(QObject* parent, bool startLocalServer = true); ~PaymentServer(); + // OptionsModel is used for getting proxy settings and display unit + void setOptionsModel(OptionsModel *optionsModel); + +#ifdef ENABLE_BIP70 // Load root certificate authorities. Pass nullptr (default) // to read from the file specified in the -rootcertificates setting, // or, if that's not set, to use the system default root certificates. @@ -83,9 +93,6 @@ public: // Return certificate store static X509_STORE* getCertStore(); - // OptionsModel is used for getting proxy settings and display unit - void setOptionsModel(OptionsModel *optionsModel); - // Verify that the payment request network matches the client network static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails); // Verify if the payment request is expired @@ -94,33 +101,40 @@ public: static bool verifySize(qint64 requestSize); // Verify the payment request amount is valid static bool verifyAmount(const CAmount& requestAmount); +#endif Q_SIGNALS: // Fired when a valid payment request is received void receivedPaymentRequest(SendCoinsRecipient); - // Fired when a valid PaymentACK is received - void receivedPaymentACK(const QString &paymentACKMsg); - // Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); +#ifdef ENABLE_BIP70 + // Fired when a valid PaymentACK is received + void receivedPaymentACK(const QString &paymentACKMsg); +#endif + public Q_SLOTS: // Signal this when the main window's UI is ready // to display payment requests to the user void uiReady(); - // Submit Payment message to a merchant, get back PaymentACK: - void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction); - // Handle an incoming URI, URI with local file scheme or file void handleURIOrFile(const QString& s); +#ifdef ENABLE_BIP70 + // Submit Payment message to a merchant, get back PaymentACK: + void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction); +#endif + private Q_SLOTS: void handleURIConnection(); +#ifdef ENABLE_BIP70 void netRequestFinished(QNetworkReply*); void reportSslErrors(QNetworkReply*, const QList &); void handlePaymentACK(const QString& paymentACKMsg); +#endif protected: // Constructor registers this on the parent QApplication to @@ -128,19 +142,19 @@ protected: bool eventFilter(QObject *object, QEvent *event); private: + bool saveURIs; // true during startup + QLocalServer* uriServer; + OptionsModel *optionsModel; + +#ifdef ENABLE_BIP70 static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request); bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient); void fetchRequest(const QUrl& url); // Setup networking void initNetManager(); - - bool saveURIs; // true during startup - QLocalServer* uriServer; - QNetworkAccessManager* netManager; // Used to fetch payment requests - - OptionsModel *optionsModel; +#endif }; #endif // BITCOIN_QT_PAYMENTSERVER_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 6f66bc19e..858128f9f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -2,6 +2,10 @@ // 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 +#endif + #include #include @@ -290,7 +294,9 @@ void SendCoinsDialog::on_sendButton_clicked() QString recipientElement; recipientElement = "
"; +#ifdef ENABLE_BIP70 if (!rcp.paymentRequest.IsInitialized()) // normal payment +#endif { if(rcp.label.length() > 0) // label with address { @@ -302,6 +308,7 @@ void SendCoinsDialog::on_sendButton_clicked() recipientElement.append(tr("%1 to %2").arg(amount, address)); } } +#ifdef ENABLE_BIP70 else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request { recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant))); @@ -310,6 +317,7 @@ void SendCoinsDialog::on_sendButton_clicked() { recipientElement.append(tr("%1 to %2").arg(amount, address)); } +#endif formatted.append(recipientElement); } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index b394ff315..76c942c8b 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -2,6 +2,10 @@ // 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 +#endif + #include #include @@ -133,9 +137,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node) // Check input validity bool retval = true; +#ifdef ENABLE_BIP70 // Skip checks for payment request if (recipient.paymentRequest.IsInitialized()) return retval; +#endif if (!model->validateAddress(ui->payTo->text())) { @@ -166,9 +172,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node) SendCoinsRecipient SendCoinsEntry::getValue() { +#ifdef ENABLE_BIP70 // Payment request if (recipient.paymentRequest.IsInitialized()) return recipient; +#endif // Normal payment recipient.address = ui->payTo->text(); @@ -196,6 +204,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; +#ifdef ENABLE_BIP70 if (recipient.paymentRequest.IsInitialized()) // payment request { if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated @@ -216,6 +225,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) } } else // normal payment +#endif { // message ui->messageTextLabel->setText(recipient.message); diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp index af5c69ea9..6750c543d 100644 --- a/src/qt/test/compattests.cpp +++ b/src/qt/test/compattests.cpp @@ -2,7 +2,13 @@ // 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 +#endif + +#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70) #include // this includes protobuf's port.h which defines its own bswap macos +#endif #include diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index df65a85fb..28df4ebf2 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -14,9 +14,11 @@ #ifdef ENABLE_WALLET #include +#ifdef ENABLE_BIP70 #include +#endif // ENABLE_BIP70 #include -#endif +#endif // ENABLE_WALLET #include #include @@ -74,7 +76,7 @@ int main(int argc, char *argv[]) if (QTest::qExec(&test1) != 0) { fInvalid = true; } -#ifdef ENABLE_WALLET +#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70) PaymentServerTests test2; if (QTest::qExec(&test2) != 0) { fInvalid = true; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 9ca0e7c67..fcc5806b8 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 59332be75..3c5617bfa 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include @@ -257,6 +261,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall if (r.first == "Message") strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(r.second, true) + "
"; +#ifdef ENABLE_BIP70 // // PaymentRequest info: // @@ -271,6 +276,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall strHTML += "" + tr("Merchant") + ": " + GUIUtil::HtmlEscape(merchant) + "
"; } } +#endif if (wtx.is_coinbase) { diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 7d903b3b1..faeed87ec 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -14,7 +14,9 @@ #include #include #include +#ifdef ENABLE_BIP70 #include +#endif #include #include diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f7cc94ae3..71b2d321e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -2,6 +2,10 @@ // 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 +#endif + #include #include @@ -142,6 +146,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; +#ifdef ENABLE_BIP70 if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; @@ -164,6 +169,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact total += subtotal; } else +#endif { // User-entered bitcoin address / amount: if(!validateAddress(rcp.address)) { @@ -235,6 +241,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran std::vector> vOrderForm; for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { +#ifdef ENABLE_BIP70 if (rcp.paymentRequest.IsInitialized()) { // Make sure any payment requests involved are still valid. @@ -247,7 +254,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran rcp.paymentRequest.SerializeToString(&value); vOrderForm.emplace_back("PaymentRequest", std::move(value)); } - else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) + else +#endif + if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) vOrderForm.emplace_back("Message", rcp.message.toStdString()); } @@ -266,7 +275,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { // Don't touch the address book when we have a payment request +#ifdef ENABLE_BIP70 if (!rcp.paymentRequest.IsInitialized()) +#endif { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = DecodeDestination(strAddress); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index b22728c69..ec4c5a2a6 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -10,7 +10,13 @@ #include #include