@ -1,11 +1,10 @@
@@ -1,11 +1,10 @@
// Copyright (c) 2011-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT 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"
@ -19,6 +18,7 @@
@@ -19,6 +18,7 @@
# include <openssl/x509.h>
# include <openssl/x509_vfy.h>
# include <QApplication>
# include <QByteArray>
# include <QDataStream>
@ -46,14 +46,20 @@
@@ -46,14 +46,20 @@
# include <QUrlQuery>
# endif
using namespace std ;
using namespace boost ;
using namespace std ;
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 " ;
// 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 " ;
// BIP70 max payment request size in bytes (DoS protection)
const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000 ;
X509_STORE * PaymentServer : : certStore = NULL ;
void PaymentServer : : freeCertStore ( )
@ -184,7 +190,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
@@ -184,7 +190,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
// Warning: ipcSendCommandLine() is called early in init,
// so don't use "emit message()", but "QMessageBox::"!
//
bool PaymentServer : : ipcParseCommandLine ( int argc , char * argv [ ] )
void PaymentServer : : ipcParseCommandLine ( int argc , char * argv [ ] )
{
for ( int i = 1 ; i < argc ; i + + )
{
@ -192,6 +198,10 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
@@ -192,6 +198,10 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
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 ) ;
@ -216,7 +226,7 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
@@ -216,7 +226,7 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
savedPaymentRequests . append ( arg ) ;
PaymentRequestPlus request ;
if ( readPaymentRequest ( arg , request ) )
if ( readPaymentRequestFromFile ( arg , request ) )
{
if ( request . getDetails ( ) . network ( ) = = " main " )
{
@ -235,7 +245,6 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
@@ -235,7 +245,6 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
qWarning ( ) < < " PaymentServer::ipcSendCommandLine : Payment request file does not exist: " < < arg ;
}
}
return true ;
}
//
@ -254,6 +263,7 @@ bool PaymentServer::ipcSendCommandLine()
@@ -254,6 +263,7 @@ bool PaymentServer::ipcSendCommandLine()
if ( ! socket - > waitForConnected ( BITCOIN_IPC_CONNECT_TIMEOUT ) )
{
delete socket ;
socket = NULL ;
return false ;
}
@ -262,12 +272,14 @@ bool PaymentServer::ipcSendCommandLine()
@@ -262,12 +272,14 @@ bool PaymentServer::ipcSendCommandLine()
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 = NULL ;
fResult = true ;
}
@ -440,7 +452,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
@@ -440,7 +452,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
{
PaymentRequestPlus request ;
SendCoinsRecipient recipient ;
if ( ! readPaymentRequest ( s , request ) )
if ( ! readPaymentRequestFromFile ( s , request ) )
{
emit message ( tr ( " Payment request file handling " ) ,
tr ( " Payment request file cannot be read! This can be caused by an invalid payment request file. " ) ,
@ -474,18 +486,25 @@ void PaymentServer::handleURIConnection()
@@ -474,18 +486,25 @@ void PaymentServer::handleURIConnection()
handleURIOrFile ( msg ) ;
}
bool PaymentServer : : readPaymentRequest ( const QString & filename , PaymentRequestPlus & request )
//
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
// so don't use "emit message()", but "QMessageBox::"!
//
bool PaymentServer : : readPaymentRequestFromFile ( const QString & filename , PaymentRequestPlus & request )
{
QFile f ( filename ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) )
{
qWarning ( ) < < " PaymentServer::readPaymentRequest : Failed to open " < < filename ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Failed to open %2 " ) . arg ( __func__ ) . arg ( filename ) ;
return false ;
}
if ( f . size ( ) > MAX_PAYMENT_REQUEST_SIZE )
{
qWarning ( ) < < " PaymentServer::readPaymentRequest : " < < filename < < " too large " ;
// BIP70 DoS protection
if ( f . size ( ) > BIP70_MAX_PAYMENTREQUEST_SIZE ) {
qWarning ( ) < < QString ( " PaymentServer::%1: Payment request %2 is too large (%3 bytes, allowed %4 bytes). " )
. arg ( __func__ )
. arg ( filename )
. arg ( f . size ( ) )
. arg ( BIP70_MAX_PAYMENTREQUEST_SIZE ) ;
return false ;
}
@ -580,10 +599,10 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
@@ -580,10 +599,10 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
void PaymentServer : : fetchRequest ( const QUrl & url )
{
QNetworkRequest netRequest ;
netRequest . setAttribute ( QNetworkRequest : : User , " PaymentRequest " ) ;
netRequest . setAttribute ( QNetworkRequest : : User , BIP70_MESSAGE_PAYMENTREQUEST ) ;
netRequest . setUrl ( url ) ;
netRequest . setRawHeader ( " User-Agent " , CLIENT_NAME . c_str ( ) ) ;
netRequest . setRawHeader ( " Accept " , BITCOIN_REQUEST_MIMETYPE ) ;
netRequest . setRawHeader ( " Accept " , BIP71_MIMETYPE_PAYMENTREQUEST ) ;
netManager - > get ( netRequest ) ;
}
@ -594,11 +613,11 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
@@ -594,11 +613,11 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
return ;
QNetworkRequest netRequest ;
netRequest . setAttribute ( QNetworkRequest : : User , " PaymentACK " ) ;
netRequest . setAttribute ( QNetworkRequest : : User , BIP70_MESSAGE_PAYMENTACK ) ;
netRequest . setUrl ( QString : : fromStdString ( details . payment_url ( ) ) ) ;
netRequest . setHeader ( QNetworkRequest : : ContentTypeHeader , BITCOIN_PAYMENTACK_CONTENTTYPE ) ;
netRequest . setHeader ( QNetworkRequest : : ContentTypeHeader , BIP71_MIMETYPE_PAYMENT ) ;
netRequest . setRawHeader ( " User-Agent " , CLIENT_NAME . c_str ( ) ) ;
netRequest . setRawHeader ( " Accept " , BITCOIN_PAYMENTACK_MIMETYPE ) ;
netRequest . setRawHeader ( " Accept " , BIP71_MIMETYPE_PAYMENTACK ) ;
payments : : Payment payment ;
payment . set_merchant_data ( details . merchant_data ( ) ) ;
@ -616,7 +635,6 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
@@ -616,7 +635,6 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
else {
CPubKey newKey ;
if ( wallet - > GetKeyFromPool ( newKey ) ) {
LOCK ( wallet - > cs_wallet ) ; // SetAddressBook
CKeyID keyID = newKey . GetID ( ) ;
wallet - > SetAddressBook ( keyID , strAccount , " refund " ) ;
@ -646,13 +664,26 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
@@ -646,13 +664,26 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
void PaymentServer : : netRequestFinished ( QNetworkReply * reply )
{
reply - > deleteLater ( ) ;
if ( reply - > error ( ) ! = QNetworkReply : : NoError )
{
// BIP70 DoS protection
if ( reply - > size ( ) > BIP70_MAX_PAYMENTREQUEST_SIZE ) {
QString msg = tr ( " Payment request %2 is too large (%3 bytes, allowed %4 bytes). " )
. arg ( __func__ )
. arg ( reply - > request ( ) . url ( ) . toString ( ) )
. arg ( reply - > size ( ) )
. arg ( BIP70_MAX_PAYMENTREQUEST_SIZE ) ;
qWarning ( ) < < QString ( " PaymentServer::%1: " ) . arg ( __func__ ) < < msg ;
emit message ( tr ( " Payment request DoS protection " ) , msg , CClientUIInterface : : MSG_ERROR ) ;
return ;
}
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) {
QString msg = tr ( " Error communicating with %1: %2 " )
. arg ( reply - > request ( ) . url ( ) . toString ( ) )
. arg ( reply - > errorString ( ) ) ;
qWarning ( ) < < " PaymentServer::netRequestFinished : " < < msg ;
qWarning ( ) < < " PaymentServer::netRequestFinished: " < < msg ;
emit message ( tr ( " Payment request error " ) , msg , CClientUIInterface : : MSG_ERROR ) ;
return ;
}
@ -660,7 +691,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
@@ -660,7 +691,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
QByteArray data = reply - > readAll ( ) ;
QString requestType = reply - > request ( ) . attribute ( QNetworkRequest : : User ) . toString ( ) ;
if ( requestType = = " PaymentRequest " )
if ( requestType = = BIP70_MESSAGE_PAYMENTREQUEST )
{
PaymentRequestPlus request ;
SendCoinsRecipient recipient ;
@ -676,7 +707,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
@@ -676,7 +707,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
return ;
}
else if ( requestType = = " PaymentACK " )
else if ( requestType = = BIP70_MESSAGE_PAYMENTACK )
{
payments : : PaymentACK paymentACK ;
if ( ! paymentACK . ParseFromArray ( data . data ( ) , data . size ( ) ) )