2018-01-02 18:12:05 +01:00
// Copyright (c) 2011-2017 The Bitcoin Core developers
2014-12-05 09:40:58 +01:00
// Distributed under the MIT software license, see the accompanying
2013-02-12 00:52:30 +01:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-10 01:57:53 +01:00
# include <qt/paymentserver.h>
# include <qt/bitcoinunits.h>
# include <qt/guiutil.h>
# include <qt/optionsmodel.h>
# include <base58.h>
# include <chainparams.h>
# include <policy/policy.h>
# include <ui_interface.h>
# include <util.h>
# include <wallet/wallet.h>
2013-04-13 07:13:08 +02:00
# include <cstdlib>
# include <openssl/x509_vfy.h>
2014-11-05 11:47:57 +01:00
2013-07-23 08:52:24 +02:00
# include <QApplication>
2013-02-12 00:52:30 +01:00
# include <QByteArray>
# include <QDataStream>
2013-07-22 08:50:39 +02:00
# include <QDateTime>
2013-02-12 00:52:30 +01:00
# include <QDebug>
2013-07-22 08:50:39 +02:00
# include <QFile>
2013-02-12 00:52:30 +01:00
# include <QFileOpenEvent>
# include <QHash>
2013-07-22 08:50:39 +02:00
# include <QList>
2013-02-12 00:52:30 +01:00
# include <QLocalServer>
# include <QLocalSocket>
2013-07-22 08:50:39 +02:00
# include <QNetworkAccessManager>
# include <QNetworkProxy>
# include <QNetworkReply>
# include <QNetworkRequest>
# include <QSslCertificate>
# include <QSslError>
# include <QSslSocket>
2013-04-13 07:13:08 +02:00
# include <QStringList>
# include <QTextDocument>
2013-05-31 14:02:24 +02:00
# if QT_VERSION < 0x050000
2013-02-12 00:52:30 +01:00
# include <QUrl>
2013-07-22 08:50:39 +02:00
# else
# include <QUrlQuery>
2013-05-31 14:02:24 +02:00
# endif
2013-02-12 00:52:30 +01:00
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000 ; // milliseconds
const QString BITCOIN_IPC_PREFIX ( " bitcoin: " ) ;
2014-11-05 11:47:57 +01:00
// BIP70 payment protocol messages
const char * BIP70_MESSAGE_PAYMENTACK = " PaymentACK " ;
const char * BIP70_MESSAGE_PAYMENTREQUEST = " PaymentRequest " ;
// BIP71 payment protocol media types
const char * BIP71_MIMETYPE_PAYMENT = " application/bitcoin-payment " ;
const char * BIP71_MIMETYPE_PAYMENTACK = " application/bitcoin-paymentack " ;
const char * BIP71_MIMETYPE_PAYMENTREQUEST = " application/bitcoin-paymentrequest " ;
2013-02-12 00:52:30 +01:00
2016-11-19 16:12:23 +01:00
struct X509StoreDeleter {
void operator ( ) ( X509_STORE * b ) {
X509_STORE_free ( b ) ;
}
} ;
struct X509Deleter {
void operator ( ) ( X509 * b ) { X509_free ( b ) ; }
} ;
namespace // Anon namespace
2013-07-22 08:50:39 +02:00
{
2016-11-19 16:12:23 +01:00
std : : unique_ptr < X509_STORE , X509StoreDeleter > certStore ;
2013-07-22 08:50:39 +02:00
}
2013-02-12 00:52:30 +01:00
//
// Create a name that is unique for:
// testnet / non-testnet
// data directory
//
static QString ipcServerName ( )
{
QString name ( " BitcoinQt " ) ;
// Append a simple hash of the datadir
// Note that GetDataDir(true) returns a different path
// for -testnet versus main net
2016-11-07 12:11:59 +01:00
QString ddir ( GUIUtil : : boostPathToQString ( GetDataDir ( true ) ) ) ;
2013-02-12 00:52:30 +01:00
name . append ( QString : : number ( qHash ( ddir ) ) ) ;
return name ;
}
//
2013-08-31 01:11:12 +02:00
// We store payment URIs and requests received before
2013-02-12 00:52:30 +01:00
// the main GUI window is up and ready to ask the user
// to send payment.
2013-07-22 08:50:39 +02:00
static QList < QString > savedPaymentRequests ;
static void ReportInvalidCertificate ( const QSslCertificate & cert )
{
2014-12-12 10:17:56 +01:00
# if QT_VERSION < 0x050000
qDebug ( ) < < QString ( " %1: Payment server found an invalid certificate: " ) . arg ( __func__ ) < < cert . serialNumber ( ) < < cert . subjectInfo ( QSslCertificate : : CommonName ) < < cert . subjectInfo ( QSslCertificate : : OrganizationalUnitName ) ;
# else
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 ) ;
# endif
2013-07-22 08:50:39 +02:00
}
//
2013-08-24 15:07:17 +02:00
// Load OpenSSL's list of root certificate authorities
2013-02-12 00:52:30 +01:00
//
2013-07-22 08:50:39 +02:00
void PaymentServer : : LoadRootCAs ( X509_STORE * _store )
{
// Unit tests mostly use this, to pass in fake root CAs:
if ( _store )
{
2016-11-19 16:12:23 +01:00
certStore . reset ( _store ) ;
2013-07-22 08:50:39 +02:00
return ;
}
// Normal execution, use either -rootcertificates or system certs:
2016-11-19 16:12:23 +01:00
certStore . reset ( X509_STORE_new ( ) ) ;
2013-07-22 08:50:39 +02:00
// Note: use "-system-" default here so that users can pass -rootcertificates=""
// and get 'I don't like X.509 certificates, don't trust anybody' behavior:
2017-08-01 21:17:40 +02:00
QString certFile = QString : : fromStdString ( gArgs . GetArg ( " -rootcertificates " , " -system- " ) ) ;
2013-07-22 08:50:39 +02:00
2015-01-15 14:30:44 +01:00
// Empty store
if ( certFile . isEmpty ( ) ) {
qDebug ( ) < < QString ( " PaymentServer::%1: Payment request authentication via X.509 certificates disabled. " ) . arg ( __func__ ) ;
return ;
}
2013-07-22 08:50:39 +02:00
QList < QSslCertificate > certList ;
2015-01-15 14:30:44 +01:00
if ( certFile ! = " -system- " ) {
qDebug ( ) < < QString ( " PaymentServer::%1: Using \" %2 \" as trusted root certificate. " ) . arg ( __func__ ) . arg ( certFile ) ;
2013-07-22 08:50:39 +02:00
certList = QSslCertificate : : fromPath ( certFile ) ;
// Use those certificates when fetching payment requests, too:
QSslSocket : : setDefaultCaCertificates ( certList ) ;
2015-01-15 14:30:44 +01:00
} else
certList = QSslSocket : : systemCaCertificates ( ) ;
2013-07-22 08:50:39 +02:00
int nRootCerts = 0 ;
const QDateTime currentTime = QDateTime : : currentDateTime ( ) ;
2014-12-12 10:15:34 +01:00
2017-06-02 03:25:02 +02:00
for ( const QSslCertificate & cert : certList ) {
2014-12-12 10:15:34 +01:00
// Don't log NULL certificates
if ( cert . isNull ( ) )
continue ;
// Not yet active/valid, or expired certificate
2013-07-22 08:50:39 +02:00
if ( currentTime < cert . effectiveDate ( ) | | currentTime > cert . expiryDate ( ) ) {
ReportInvalidCertificate ( cert ) ;
continue ;
}
2014-12-12 10:15:34 +01:00
2013-07-22 08:50:39 +02:00
# if QT_VERSION >= 0x050000
2014-12-12 10:15:34 +01:00
// Blacklisted certificate
2013-07-22 08:50:39 +02:00
if ( cert . isBlacklisted ( ) ) {
ReportInvalidCertificate ( cert ) ;
continue ;
}
# endif
QByteArray certData = cert . toDer ( ) ;
const unsigned char * data = ( const unsigned char * ) certData . data ( ) ;
2016-11-19 16:12:23 +01:00
std : : unique_ptr < X509 , X509Deleter > x509 ( d2i_X509 ( 0 , & data , certData . size ( ) ) ) ;
if ( x509 & & X509_STORE_add_cert ( certStore . get ( ) , x509 . get ( ) ) )
2013-07-22 08:50:39 +02:00
{
2016-11-19 16:12:23 +01:00
// Note: X509_STORE increases the reference count to the X509 object,
// we still have to release our reference to it.
2013-07-22 08:50:39 +02:00
+ + nRootCerts ;
}
else
{
ReportInvalidCertificate ( cert ) ;
continue ;
}
}
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::LoadRootCAs: Loaded " < < nRootCerts < < " root certificates " ;
2013-07-22 08:50:39 +02:00
// Project for another day:
// Fetch certificate revocation lists, and add them to certStore.
// Issues to consider:
// performance (start a thread to fetch in background?)
// privacy (fetch through tor/proxy so IP address isn't revealed)
// would it be easier to just use a compiled-in blacklist?
// or use Qt's blacklist?
// "certificate stapling" with server-side caching is more efficient
}
2013-02-12 00:52:30 +01:00
//
// 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.
//
2013-11-16 01:54:29 +01:00
// Warning: ipcSendCommandLine() is called early in init,
2015-07-14 13:59:05 +02:00
// so don't use "Q_EMIT message()", but "QMessageBox::"!
2013-11-16 01:54:29 +01:00
//
2014-11-05 11:42:51 +01:00
void PaymentServer : : ipcParseCommandLine ( int argc , char * argv [ ] )
2013-02-12 00:52:30 +01:00
{
2013-07-22 08:50:39 +02:00
for ( int i = 1 ; i < argc ; i + + )
2013-02-12 00:52:30 +01:00
{
2013-07-22 08:50:39 +02:00
QString arg ( argv [ i ] ) ;
if ( arg . startsWith ( " - " ) )
2013-02-12 00:52:30 +01:00
continue ;
2013-07-22 08:50:39 +02:00
2014-11-05 11:42:51 +01:00
// 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.
2013-10-18 11:44:05 +02:00
if ( arg . startsWith ( BITCOIN_IPC_PREFIX , Qt : : CaseInsensitive ) ) // bitcoin: URI
2013-07-22 08:50:39 +02:00
{
savedPaymentRequests . append ( arg ) ;
SendCoinsRecipient r ;
2014-07-02 09:42:15 +02:00
if ( GUIUtil : : parseBitcoinURI ( arg , & r ) & & ! r . address . isEmpty ( ) )
2013-07-22 08:50:39 +02:00
{
2015-11-28 15:04:35 +01:00
auto tempChainParams = CreateChainParams ( CBaseChainParams : : MAIN ) ;
2013-07-22 08:50:39 +02:00
2017-08-23 03:02:33 +02:00
if ( IsValidDestinationString ( r . address . toStdString ( ) , * tempChainParams ) ) {
2014-08-02 20:54:57 +02:00
SelectParams ( CBaseChainParams : : MAIN ) ;
2017-08-23 03:02:33 +02:00
} else {
2015-11-28 15:04:35 +01:00
tempChainParams = CreateChainParams ( CBaseChainParams : : TESTNET ) ;
2017-08-23 03:02:33 +02:00
if ( IsValidDestinationString ( r . address . toStdString ( ) , * tempChainParams ) ) {
2015-11-28 15:04:35 +01:00
SelectParams ( CBaseChainParams : : TESTNET ) ;
2017-08-23 03:02:33 +02:00
}
2013-07-22 08:50:39 +02:00
}
}
}
else if ( QFile : : exists ( arg ) ) // Filename
{
savedPaymentRequests . append ( arg ) ;
PaymentRequestPlus request ;
2014-11-19 12:53:57 +01:00
if ( readPaymentRequestFromFile ( arg , request ) )
2013-07-22 08:50:39 +02:00
{
if ( request . getDetails ( ) . network ( ) = = " main " )
2014-06-02 23:05:35 +02:00
{
2014-06-19 15:10:04 +02:00
SelectParams ( CBaseChainParams : : MAIN ) ;
2014-06-02 23:05:35 +02:00
}
else if ( request . getDetails ( ) . network ( ) = = " test " )
{
2014-06-19 15:10:04 +02:00
SelectParams ( CBaseChainParams : : TESTNET ) ;
2014-06-02 23:05:35 +02:00
}
2013-07-22 08:50:39 +02:00
}
}
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.
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::ipcSendCommandLine: Payment request file does not exist: " < < arg ;
2013-07-22 08:50:39 +02:00
}
2013-02-12 00:52:30 +01:00
}
2014-01-05 12:37:51 +01:00
}
2013-02-12 00:52:30 +01:00
2014-01-05 12:37:51 +01:00
//
// 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 ;
2017-06-02 03:25:02 +02:00
for ( const QString & r : savedPaymentRequests )
2013-02-12 00:52:30 +01:00
{
QLocalSocket * socket = new QLocalSocket ( ) ;
socket - > connectToServer ( ipcServerName ( ) , QIODevice : : WriteOnly ) ;
if ( ! socket - > waitForConnected ( BITCOIN_IPC_CONNECT_TIMEOUT ) )
2013-11-14 19:21:16 +01:00
{
delete socket ;
2017-08-07 07:36:37 +02:00
socket = nullptr ;
2013-02-12 00:52:30 +01:00
return false ;
2013-11-14 19:21:16 +01:00
}
2013-02-12 00:52:30 +01:00
QByteArray block ;
QDataStream out ( & block , QIODevice : : WriteOnly ) ;
out . setVersion ( QDataStream : : Qt_4_0 ) ;
2013-07-22 08:50:39 +02:00
out < < r ;
2013-02-12 00:52:30 +01:00
out . device ( ) - > seek ( 0 ) ;
2014-11-05 12:05:29 +01:00
2013-02-12 00:52:30 +01:00
socket - > write ( block ) ;
socket - > flush ( ) ;
socket - > waitForBytesWritten ( BITCOIN_IPC_CONNECT_TIMEOUT ) ;
socket - > disconnectFromServer ( ) ;
2014-11-05 12:05:29 +01:00
2013-02-12 00:52:30 +01:00
delete socket ;
2017-08-07 07:36:37 +02:00
socket = nullptr ;
2013-02-12 00:52:30 +01:00
fResult = true ;
}
2013-10-18 11:44:05 +02:00
2013-02-12 00:52:30 +01:00
return fResult ;
}
2013-10-24 16:16:39 +02:00
PaymentServer : : PaymentServer ( QObject * parent , bool startLocalServer ) :
QObject ( parent ) ,
saveURIs ( true ) ,
uriServer ( 0 ) ,
netManager ( 0 ) ,
optionsModel ( 0 )
2013-02-12 00:52:30 +01:00
{
2013-07-22 08:50:39 +02:00
// 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 ;
2013-10-18 11:44:05 +02:00
// Install global event filter to catch QFileOpenEvents
// on Mac: sent when you click bitcoin: links
2015-01-12 14:26:52 +01:00
// other OSes: helpful when dealing with payment request files
2013-07-22 08:50:39 +02:00
if ( parent )
parent - > installEventFilter ( this ) ;
2013-02-12 00:52:30 +01:00
QString name = ipcServerName ( ) ;
// Clean up old socket leftover from a crash:
QLocalServer : : removeServer ( name ) ;
2013-07-22 08:50:39 +02:00
if ( startLocalServer )
{
uriServer = new QLocalServer ( this ) ;
2013-02-12 00:52:30 +01:00
2013-11-14 19:21:16 +01:00
if ( ! uriServer - > listen ( name ) ) {
2015-07-14 13:59:05 +02:00
// constructor is called early in init, so don't use "Q_EMIT message()" here
2013-11-14 19:21:16 +01:00
QMessageBox : : critical ( 0 , tr ( " Payment request error " ) ,
tr ( " Cannot start bitcoin: click-to-pay handler " ) ) ;
}
2013-10-22 21:27:24 +02:00
else {
2013-07-22 08:50:39 +02:00
connect ( uriServer , SIGNAL ( newConnection ( ) ) , this , SLOT ( handleURIConnection ( ) ) ) ;
2013-10-22 21:27:24 +02:00
connect ( this , SIGNAL ( receivedPaymentACK ( QString ) ) , this , SLOT ( handlePaymentACK ( QString ) ) ) ;
}
2013-07-22 08:50:39 +02:00
}
2013-02-12 00:52:30 +01:00
}
2013-07-22 08:50:39 +02:00
PaymentServer : : ~ PaymentServer ( )
{
google : : protobuf : : ShutdownProtobufLibrary ( ) ;
}
//
2015-01-12 14:26:52 +01:00
// 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.
2013-07-22 08:50:39 +02:00
//
2013-11-14 19:21:16 +01:00
bool PaymentServer : : eventFilter ( QObject * object , QEvent * event )
2013-02-12 00:52:30 +01:00
{
2015-01-12 14:26:52 +01:00
if ( event - > type ( ) = = QEvent : : FileOpen ) {
2013-11-14 19:21:16 +01:00
QFileOpenEvent * fileEvent = static_cast < QFileOpenEvent * > ( event ) ;
2013-07-22 08:50:39 +02:00
if ( ! fileEvent - > file ( ) . isEmpty ( ) )
handleURIOrFile ( fileEvent - > file ( ) ) ;
else if ( ! fileEvent - > url ( ) . isEmpty ( ) )
handleURIOrFile ( fileEvent - > url ( ) . toString ( ) ) ;
return true ;
2013-02-12 00:52:30 +01:00
}
2013-11-14 19:21:16 +01:00
return QObject : : eventFilter ( object , event ) ;
2013-02-12 00:52:30 +01:00
}
2013-08-24 15:07:17 +02:00
void PaymentServer : : initNetManager ( )
2013-07-22 08:50:39 +02:00
{
2013-08-24 15:07:17 +02:00
if ( ! optionsModel )
return ;
2017-06-28 23:14:48 +02:00
delete netManager ;
2013-07-22 08:50:39 +02:00
2013-10-18 11:44:05 +02:00
// netManager is used to fetch paymentrequests given in bitcoin: URIs
2013-07-22 08:50:39 +02:00
netManager = new QNetworkAccessManager ( this ) ;
2013-12-20 18:47:49 +01:00
QNetworkProxy proxy ;
2014-06-11 13:20:59 +02:00
// Query active SOCKS5 proxy
2013-12-20 18:47:49 +01:00
if ( optionsModel - > getProxySettings ( proxy ) ) {
2014-06-11 13:20:59 +02:00
netManager - > setProxy ( proxy ) ;
2013-12-20 18:47:49 +01:00
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " PaymentServer::initNetManager: Using SOCKS5 proxy " < < proxy . hostName ( ) < < " : " < < proxy . port ( ) ;
2013-07-22 08:50:39 +02:00
}
2013-12-20 18:47:49 +01:00
else
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " PaymentServer::initNetManager: No active proxy server found. " ;
2013-07-22 08:50:39 +02:00
connect ( netManager , SIGNAL ( finished ( QNetworkReply * ) ) ,
this , SLOT ( netRequestFinished ( QNetworkReply * ) ) ) ;
connect ( netManager , SIGNAL ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ,
this , SLOT ( reportSslErrors ( QNetworkReply * , const QList < QSslError > & ) ) ) ;
}
2013-02-12 00:52:30 +01:00
void PaymentServer : : uiReady ( )
{
2013-10-30 11:30:53 +01:00
initNetManager ( ) ;
2013-07-22 08:50:39 +02:00
2013-02-12 00:52:30 +01:00
saveURIs = false ;
2017-06-02 03:25:02 +02:00
for ( const QString & s : savedPaymentRequests )
2013-07-22 08:50:39 +02:00
{
handleURIOrFile ( s ) ;
}
2013-02-12 00:52:30 +01:00
savedPaymentRequests . clear ( ) ;
}
2013-07-22 08:50:39 +02:00
void PaymentServer : : handleURIOrFile ( const QString & s )
{
if ( saveURIs )
{
savedPaymentRequests . append ( s ) ;
return ;
}
2013-11-14 19:21:16 +01:00
if ( s . startsWith ( BITCOIN_IPC_PREFIX , Qt : : CaseInsensitive ) ) // bitcoin: URI
2013-07-22 08:50:39 +02:00
{
2013-04-13 07:13:08 +02:00
# if QT_VERSION < 0x050000
2013-08-31 01:11:12 +02:00
QUrl uri ( s ) ;
2013-04-13 07:13:08 +02:00
# else
QUrlQuery uri ( ( QUrl ( s ) ) ) ;
2013-07-22 08:50:39 +02:00
# endif
2013-12-09 10:48:14 +01:00
if ( uri . hasQueryItem ( " r " ) ) // payment request URI
2013-07-22 08:50:39 +02:00
{
2013-10-18 11:44:05 +02:00
QByteArray temp ;
2013-12-04 04:18:09 +01:00
temp . append ( uri . queryItemValue ( " r " ) ) ;
2013-07-22 08:50:39 +02:00
QString decoded = QUrl : : fromPercentEncoding ( temp ) ;
QUrl fetchUrl ( decoded , QUrl : : StrictMode ) ;
if ( fetchUrl . isValid ( ) )
2013-12-09 10:48:14 +01:00
{
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " PaymentServer::handleURIOrFile: fetchRequest( " < < fetchUrl < < " ) " ;
2013-07-22 08:50:39 +02:00
fetchRequest ( fetchUrl ) ;
2013-12-09 10:48:14 +01:00
}
2013-07-22 08:50:39 +02:00
else
2013-12-09 10:48:14 +01:00
{
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::handleURIOrFile: Invalid URL: " < < fetchUrl ;
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " URI handling " ) ,
2013-12-09 10:48:14 +01:00
tr ( " Payment request fetch URL is invalid: %1 " ) . arg ( fetchUrl . toString ( ) ) ,
CClientUIInterface : : ICON_WARNING ) ;
}
2013-10-28 13:29:13 +01:00
2013-07-22 08:50:39 +02:00
return ;
}
2013-12-09 10:48:14 +01:00
else // normal URI
{
SendCoinsRecipient recipient ;
if ( GUIUtil : : parseBitcoinURI ( s , & recipient ) )
2013-11-16 01:54:29 +01:00
{
2017-08-23 03:02:33 +02:00
if ( ! IsValidDestinationString ( recipient . address . toStdString ( ) ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " URI handling " ) , tr ( " Invalid payment address %1 " ) . arg ( recipient . address ) ,
2013-11-16 01:54:29 +01:00
CClientUIInterface : : MSG_ERROR ) ;
}
else
2015-07-14 13:59:05 +02:00
Q_EMIT receivedPaymentRequest ( recipient ) ;
2013-11-16 01:54:29 +01:00
}
2013-12-09 10:48:14 +01:00
else
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " URI handling " ) ,
2014-07-31 16:56:14 +02:00
tr ( " URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters. " ) ,
2013-12-09 10:48:14 +01:00
CClientUIInterface : : ICON_WARNING ) ;
2013-07-22 08:50:39 +02:00
2013-12-09 10:48:14 +01:00
return ;
}
2013-07-22 08:50:39 +02:00
}
2013-12-09 10:48:14 +01:00
if ( QFile : : exists ( s ) ) // payment request file
2013-07-22 08:50:39 +02:00
{
PaymentRequestPlus request ;
2013-10-28 13:29:13 +01:00
SendCoinsRecipient recipient ;
2014-11-19 12:53:57 +01:00
if ( ! readPaymentRequestFromFile ( s , request ) )
2013-11-16 01:54:29 +01:00
{
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request file handling " ) ,
2014-07-31 16:56:14 +02:00
tr ( " Payment request file cannot be read! This can be caused by an invalid payment request file. " ) ,
2013-12-09 10:48:14 +01:00
CClientUIInterface : : ICON_WARNING ) ;
2013-11-16 01:54:29 +01:00
}
else if ( processPaymentRequest ( request , recipient ) )
2015-07-14 13:59:05 +02:00
Q_EMIT receivedPaymentRequest ( recipient ) ;
2013-10-28 13:29:13 +01:00
2013-07-22 08:50:39 +02:00
return ;
}
}
2013-02-12 00:52:30 +01:00
void PaymentServer : : handleURIConnection ( )
{
QLocalSocket * clientConnection = uriServer - > nextPendingConnection ( ) ;
while ( clientConnection - > bytesAvailable ( ) < ( int ) sizeof ( quint32 ) )
clientConnection - > waitForReadyRead ( ) ;
connect ( clientConnection , SIGNAL ( disconnected ( ) ) ,
clientConnection , SLOT ( deleteLater ( ) ) ) ;
QDataStream in ( clientConnection ) ;
in . setVersion ( QDataStream : : Qt_4_0 ) ;
if ( clientConnection - > bytesAvailable ( ) < ( int ) sizeof ( quint16 ) ) {
return ;
}
2013-10-18 11:44:05 +02:00
QString msg ;
in > > msg ;
2013-02-12 00:52:30 +01:00
2013-10-18 11:44:05 +02:00
handleURIOrFile ( msg ) ;
2013-07-22 08:50:39 +02:00
}
2014-11-19 12:53:57 +01:00
//
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
2015-07-14 13:59:05 +02:00
// so don't use "Q_EMIT message()", but "QMessageBox::"!
2014-11-19 12:53:57 +01:00
//
bool PaymentServer : : readPaymentRequestFromFile ( const QString & filename , PaymentRequestPlus & request )
2013-07-22 08:50:39 +02:00
{
QFile f ( filename ) ;
2014-11-19 12:53:57 +01:00
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Failed to open %2 " ) . arg ( __func__ ) . arg ( filename ) ;
2013-07-22 08:50:39 +02:00
return false ;
}
2014-11-19 12:53:57 +01:00
// BIP70 DoS protection
2015-01-15 09:13:32 +01:00
if ( ! verifySize ( f . size ( ) ) ) {
2013-07-22 08:50:39 +02:00
return false ;
}
QByteArray data = f . readAll ( ) ;
return request . parse ( data ) ;
}
2015-01-12 14:24:01 +01:00
bool PaymentServer : : processPaymentRequest ( const PaymentRequestPlus & request , SendCoinsRecipient & recipient )
2013-07-22 08:50:39 +02:00
{
2013-08-24 15:07:17 +02:00
if ( ! optionsModel )
return false ;
2013-11-16 01:54:29 +01:00
if ( request . IsInitialized ( ) ) {
// Payment request network matches client network?
2015-01-12 08:43:08 +01:00
if ( ! verifyNetwork ( request . getDetails ( ) ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request rejected " ) , tr ( " Payment request network doesn't match client network. " ) ,
2013-11-16 01:54:29 +01:00
CClientUIInterface : : MSG_ERROR ) ;
return false ;
}
2015-01-08 14:42:04 +01:00
// Make sure any payment requests involved are still valid.
// This is re-checked just before sending coins in WalletModel::sendCoins().
if ( verifyExpired ( request . getDetails ( ) ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request rejected " ) , tr ( " Payment request expired. " ) ,
2013-11-16 01:54:29 +01:00
CClientUIInterface : : MSG_ERROR ) ;
return false ;
}
2015-01-08 14:42:04 +01:00
} else {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request error " ) , tr ( " Payment request is not initialized. " ) ,
2013-11-16 01:54:29 +01:00
CClientUIInterface : : MSG_ERROR ) ;
return false ;
}
2013-10-28 13:29:13 +01:00
recipient . paymentRequest = request ;
recipient . message = GUIUtil : : HtmlEscape ( request . getDetails ( ) . memo ( ) ) ;
2013-10-24 16:02:39 +02:00
2016-11-19 16:12:23 +01:00
request . getMerchant ( certStore . get ( ) , recipient . authenticatedMerchant ) ;
2013-10-24 16:02:39 +02:00
2014-04-23 00:46:19 +02:00
QList < std : : pair < CScript , CAmount > > sendingTos = request . getPayTo ( ) ;
2013-10-30 11:26:44 +01:00
QStringList addresses ;
2013-10-24 16:02:39 +02:00
2017-06-02 03:28:42 +02:00
for ( const std : : pair < CScript , CAmount > & sendingTo : sendingTos ) {
2013-10-24 16:02:39 +02:00
// Extract and check destination addresses
CTxDestination dest ;
if ( ExtractDestination ( sendingTo . first , dest ) ) {
2013-10-30 11:26:44 +01:00
// Append destination address
2017-08-23 03:02:33 +02:00
addresses . append ( QString : : fromStdString ( EncodeDestination ( dest ) ) ) ;
2013-10-24 16:02:39 +02:00
}
2013-11-16 01:54:29 +01:00
else if ( ! recipient . authenticatedMerchant . isEmpty ( ) ) {
2015-01-12 14:26:52 +01:00
// Unauthenticated payment requests 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).
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request rejected " ) ,
2013-10-24 16:02:39 +02:00
tr ( " Unverified payment requests to custom payment scripts are unsupported. " ) ,
CClientUIInterface : : MSG_ERROR ) ;
return false ;
}
2015-01-09 14:25:43 +01:00
// Bitcoin amounts are stored as (optional) uint64 in the protobuf messages (see paymentrequest.proto),
// but CAmount is defined as int64_t. Because of that we need to verify that amounts are in a valid range
// and no overflow has happened.
if ( ! verifyAmount ( sendingTo . second ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request rejected " ) , tr ( " Invalid payment request. " ) , CClientUIInterface : : MSG_ERROR ) ;
2015-01-09 14:25:43 +01:00
return false ;
}
2013-10-24 16:02:39 +02:00
// Extract and check amounts
2013-07-22 08:50:39 +02:00
CTxOut txOut ( sendingTo . second , sendingTo . first ) ;
2016-10-14 17:49:05 +02:00
if ( IsDust ( txOut , : : dustRelayFee ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request error " ) , tr ( " Requested payment amount of %1 is too small (considered dust). " )
2013-11-16 01:54:29 +01:00
. arg ( BitcoinUnits : : formatWithUnit ( optionsModel - > getDisplayUnit ( ) , sendingTo . second ) ) ,
CClientUIInterface : : MSG_ERROR ) ;
2013-09-04 11:52:45 +02:00
2013-07-22 08:50:39 +02:00
return false ;
}
2013-10-28 13:29:13 +01:00
recipient . amount + = sendingTo . second ;
2015-01-09 14:25:43 +01:00
// Also verify that the final amount is still in a valid range after adding additional amounts.
if ( ! verifyAmount ( recipient . amount ) ) {
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request rejected " ) , tr ( " Invalid payment request. " ) , CClientUIInterface : : MSG_ERROR ) ;
2015-01-09 14:25:43 +01:00
return false ;
}
2013-07-22 08:50:39 +02:00
}
2013-10-24 16:02:39 +02:00
// Store addresses and format them to fit nicely into the GUI
recipient . address = addresses . join ( " <br /> " ) ;
2013-07-22 08:50:39 +02:00
2013-10-24 16:02:39 +02:00
if ( ! recipient . authenticatedMerchant . isEmpty ( ) ) {
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " PaymentServer::processPaymentRequest: Secure payment request from " < < recipient . authenticatedMerchant ;
2013-07-22 08:50:39 +02:00
}
else {
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " PaymentServer::processPaymentRequest: Insecure payment request to " < < addresses . join ( " , " ) ;
2013-07-22 08:50:39 +02:00
}
return true ;
}
2013-08-31 01:11:12 +02:00
void PaymentServer : : fetchRequest ( const QUrl & url )
2013-07-22 08:50:39 +02:00
{
QNetworkRequest netRequest ;
2014-11-05 11:47:57 +01:00
netRequest . setAttribute ( QNetworkRequest : : User , BIP70_MESSAGE_PAYMENTREQUEST ) ;
2013-07-22 08:50:39 +02:00
netRequest . setUrl ( url ) ;
netRequest . setRawHeader ( " User-Agent " , CLIENT_NAME . c_str ( ) ) ;
2014-11-05 11:47:57 +01:00
netRequest . setRawHeader ( " Accept " , BIP71_MIMETYPE_PAYMENTREQUEST ) ;
2013-07-22 08:50:39 +02:00
netManager - > get ( netRequest ) ;
}
2017-08-01 23:40:19 +02:00
void PaymentServer : : fetchPaymentACK ( CWallet * wallet , const SendCoinsRecipient & recipient , QByteArray transaction )
2013-07-22 08:50:39 +02:00
{
const payments : : PaymentDetails & details = recipient . paymentRequest . getDetails ( ) ;
if ( ! details . has_payment_url ( ) )
return ;
QNetworkRequest netRequest ;
2014-11-05 11:47:57 +01:00
netRequest . setAttribute ( QNetworkRequest : : User , BIP70_MESSAGE_PAYMENTACK ) ;
2013-07-22 08:50:39 +02:00
netRequest . setUrl ( QString : : fromStdString ( details . payment_url ( ) ) ) ;
2014-11-05 11:47:57 +01:00
netRequest . setHeader ( QNetworkRequest : : ContentTypeHeader , BIP71_MIMETYPE_PAYMENT ) ;
2013-07-22 08:50:39 +02:00
netRequest . setRawHeader ( " User-Agent " , CLIENT_NAME . c_str ( ) ) ;
2014-11-05 11:47:57 +01:00
netRequest . setRawHeader ( " Accept " , BIP71_MIMETYPE_PAYMENTACK ) ;
2013-07-22 08:50:39 +02:00
payments : : Payment payment ;
payment . set_merchant_data ( details . merchant_data ( ) ) ;
payment . add_transactions ( transaction . data ( ) , transaction . size ( ) ) ;
// Create a new refund address, or re-use:
2013-10-22 21:27:24 +02:00
QString account = tr ( " Refund from %1 " ) . arg ( recipient . authenticatedMerchant ) ;
2013-07-22 08:50:39 +02:00
std : : string strAccount = account . toStdString ( ) ;
2017-12-01 01:49:11 +01:00
CPubKey newKey ;
if ( wallet - > GetKeyFromPool ( newKey ) ) {
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
// types supported by the receiver. As a result, we choose the address format we also
// use for change. Despite an actual payment and not change, this is a close match:
// it's the output type we use subject to privacy issues, but not restricted by what
// other software supports.
2018-01-23 17:56:15 +01:00
const OutputType change_type = g_change_type ! = OUTPUT_TYPE_NONE ? g_change_type : g_address_type ;
wallet - > LearnRelatedScripts ( newKey , change_type ) ;
CTxDestination dest = GetDestinationForKey ( newKey , change_type ) ;
2017-12-01 01:49:11 +01:00
wallet - > SetAddressBook ( dest , strAccount , " refund " ) ;
CScript s = GetScriptForDestination ( dest ) ;
2013-07-22 08:50:39 +02:00
payments : : Output * refund_to = payment . add_refund_to ( ) ;
refund_to - > set_script ( & s [ 0 ] , s . size ( ) ) ;
2017-12-01 01:49:11 +01:00
} else {
// This should never happen, because sending coins should have
// just unlocked the wallet and refilled the keypool.
qWarning ( ) < < " PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set " ;
2013-07-22 08:50:39 +02:00
}
int length = payment . ByteSize ( ) ;
netRequest . setHeader ( QNetworkRequest : : ContentLengthHeader , length ) ;
QByteArray serData ( length , ' \0 ' ) ;
if ( payment . SerializeToArray ( serData . data ( ) , length ) ) {
netManager - > post ( netRequest , serData ) ;
}
else {
2013-11-16 01:54:29 +01:00
// This should never happen, either.
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::fetchPaymentACK: Error serializing payment message " ;
2013-07-22 08:50:39 +02:00
}
}
2013-08-31 01:11:12 +02:00
void PaymentServer : : netRequestFinished ( QNetworkReply * reply )
2013-07-22 08:50:39 +02:00
{
reply - > deleteLater ( ) ;
2014-11-19 12:53:57 +01:00
// BIP70 DoS protection
2015-01-15 09:13:32 +01:00
if ( ! verifySize ( reply - > size ( ) ) ) {
Q_EMIT message ( tr ( " Payment request rejected " ) ,
tr ( " Payment request %1 is too large (%2 bytes, allowed %3 bytes). " )
. arg ( reply - > request ( ) . url ( ) . toString ( ) )
. arg ( reply - > size ( ) )
. arg ( BIP70_MAX_PAYMENTREQUEST_SIZE ) ,
CClientUIInterface : : MSG_ERROR ) ;
2014-11-19 12:53:57 +01:00
return ;
}
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) {
2013-10-22 21:27:24 +02:00
QString msg = tr ( " Error communicating with %1: %2 " )
2013-07-22 08:50:39 +02:00
. arg ( reply - > request ( ) . url ( ) . toString ( ) )
. arg ( reply - > errorString ( ) ) ;
2013-10-22 21:27:24 +02:00
2014-11-19 12:53:57 +01:00
qWarning ( ) < < " PaymentServer::netRequestFinished: " < < msg ;
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request error " ) , msg , CClientUIInterface : : MSG_ERROR ) ;
2013-07-22 08:50:39 +02:00
return ;
}
QByteArray data = reply - > readAll ( ) ;
QString requestType = reply - > request ( ) . attribute ( QNetworkRequest : : User ) . toString ( ) ;
2014-11-05 11:47:57 +01:00
if ( requestType = = BIP70_MESSAGE_PAYMENTREQUEST )
2013-07-22 08:50:39 +02:00
{
PaymentRequestPlus request ;
2013-10-28 13:29:13 +01:00
SendCoinsRecipient recipient ;
2013-11-16 01:54:29 +01:00
if ( ! request . parse ( data ) )
2013-12-09 10:48:14 +01:00
{
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::netRequestFinished: Error parsing payment request " ;
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request error " ) ,
2014-07-31 16:56:14 +02:00
tr ( " Payment request cannot be parsed! " ) ,
2013-12-09 10:48:14 +01:00
CClientUIInterface : : MSG_ERROR ) ;
}
2013-11-16 01:54:29 +01:00
else if ( processPaymentRequest ( request , recipient ) )
2015-07-14 13:59:05 +02:00
Q_EMIT receivedPaymentRequest ( recipient ) ;
2013-10-22 21:27:24 +02:00
2013-07-22 08:50:39 +02:00
return ;
}
2014-11-05 11:47:57 +01:00
else if ( requestType = = BIP70_MESSAGE_PAYMENTACK )
2013-07-22 08:50:39 +02:00
{
payments : : PaymentACK paymentACK ;
if ( ! paymentACK . ParseFromArray ( data . data ( ) , data . size ( ) ) )
{
2013-10-22 21:27:24 +02:00
QString msg = tr ( " Bad response from server %1 " )
2013-07-22 08:50:39 +02:00
. arg ( reply - > request ( ) . url ( ) . toString ( ) ) ;
2013-10-22 21:27:24 +02:00
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::netRequestFinished: " < < msg ;
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment request error " ) , msg , CClientUIInterface : : MSG_ERROR ) ;
2013-07-22 08:50:39 +02:00
}
2013-12-09 10:48:14 +01:00
else
{
2015-07-14 13:59:05 +02:00
Q_EMIT receivedPaymentACK ( GUIUtil : : HtmlEscape ( paymentACK . memo ( ) ) ) ;
2013-07-22 08:50:39 +02:00
}
}
}
2013-08-31 01:11:12 +02:00
void PaymentServer : : reportSslErrors ( QNetworkReply * reply , const QList < QSslError > & errs )
2013-07-22 08:50:39 +02:00
{
2013-08-31 01:11:12 +02:00
Q_UNUSED ( reply ) ;
2013-07-22 08:50:39 +02:00
QString errString ;
2017-06-02 03:25:02 +02:00
for ( const QSslError & err : errs ) {
2015-01-08 11:44:25 +01:00
qWarning ( ) < < " PaymentServer::reportSslErrors: " < < err ;
2013-07-22 08:50:39 +02:00
errString + = err . errorString ( ) + " \n " ;
}
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Network request error " ) , errString , CClientUIInterface : : MSG_ERROR ) ;
2013-02-12 00:52:30 +01:00
}
2013-08-24 15:07:17 +02:00
2016-09-09 13:43:29 +02:00
void PaymentServer : : setOptionsModel ( OptionsModel * _optionsModel )
2013-08-24 15:07:17 +02:00
{
2016-09-09 13:43:29 +02:00
this - > optionsModel = _optionsModel ;
2013-08-24 15:07:17 +02:00
}
2013-10-22 21:27:24 +02:00
void PaymentServer : : handlePaymentACK ( const QString & paymentACKMsg )
{
2015-08-09 01:17:27 +02:00
// currently we don't further process or store the paymentACK message
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Payment acknowledged " ) , paymentACKMsg , CClientUIInterface : : ICON_INFORMATION | CClientUIInterface : : MODAL ) ;
2013-10-22 21:27:24 +02:00
}
2015-01-12 08:43:08 +01:00
bool PaymentServer : : verifyNetwork ( const payments : : PaymentDetails & requestDetails )
{
bool fVerified = requestDetails . network ( ) = = Params ( ) . NetworkIDString ( ) ;
if ( ! fVerified ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Payment request network \" %2 \" doesn't match client network \" %3 \" . " )
. arg ( __func__ )
. arg ( QString : : fromStdString ( requestDetails . network ( ) ) )
. arg ( QString : : fromStdString ( Params ( ) . NetworkIDString ( ) ) ) ;
}
return fVerified ;
}
2015-01-08 14:42:04 +01:00
bool PaymentServer : : verifyExpired ( const payments : : PaymentDetails & requestDetails )
{
bool fVerified = ( requestDetails . has_expires ( ) & & ( int64_t ) requestDetails . expires ( ) < GetTime ( ) ) ;
if ( fVerified ) {
const QString requestExpires = QString : : fromStdString ( DateTimeStrFormat ( " %Y-%m-%d %H:%M:%S " , ( int64_t ) requestDetails . expires ( ) ) ) ;
qWarning ( ) < < QString ( " PaymentServer::%1: Payment request expired \" %2 \" . " )
. arg ( __func__ )
. arg ( requestExpires ) ;
}
return fVerified ;
}
2015-01-09 14:25:43 +01:00
2015-01-15 09:13:32 +01:00
bool PaymentServer : : verifySize ( qint64 requestSize )
{
bool fVerified = ( requestSize < = BIP70_MAX_PAYMENTREQUEST_SIZE ) ;
if ( ! fVerified ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes). " )
. arg ( __func__ )
. arg ( requestSize )
. arg ( BIP70_MAX_PAYMENTREQUEST_SIZE ) ;
}
return fVerified ;
}
2015-01-09 14:25:43 +01:00
bool PaymentServer : : verifyAmount ( const CAmount & requestAmount )
{
bool fVerified = MoneyRange ( requestAmount ) ;
if ( ! fVerified ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3). " )
. arg ( __func__ )
. arg ( requestAmount )
. arg ( MAX_MONEY ) ;
}
return fVerified ;
}
2016-11-19 16:12:23 +01:00
X509_STORE * PaymentServer : : getCertStore ( )
{
return certStore . get ( ) ;
}