14 #include "validation.h" 21 #include <openssl/x509_vfy.h> 23 #include <QApplication> 25 #include <QDataStream> 29 #include <QFileOpenEvent> 32 #include <QLocalServer> 33 #include <QLocalSocket> 34 #include <QNetworkAccessManager> 35 #include <QNetworkProxy> 36 #include <QNetworkReply> 37 #include <QNetworkRequest> 38 #include <QSslCertificate> 41 #include <QStringList> 42 #include <QTextDocument> 44 #if QT_VERSION < 0x050000 74 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
84 QString
name(
"DashQt");
89 QString ddir(QString::fromStdString(
GetDataDir(
true).
string()));
90 name.append(QString::number(qHash(ddir)));
104 #if QT_VERSION < 0x050000 105 qDebug() << QString(
"%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
107 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);
119 certStore.reset(_store);
124 certStore.reset(X509_STORE_new());
128 QString certFile = QString::fromStdString(
GetArg(
"-rootcertificates",
"-system-"));
131 if (certFile.isEmpty()) {
132 qDebug() << QString(
"PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
136 QList<QSslCertificate> certList;
138 if (certFile !=
"-system-") {
139 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
141 certList = QSslCertificate::fromPath(certFile);
143 QSslSocket::setDefaultCaCertificates(certList);
145 certList = QSslSocket::systemCaCertificates();
148 const QDateTime currentTime = QDateTime::currentDateTime();
150 Q_FOREACH (
const QSslCertificate& cert, certList) {
156 if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
161 #if QT_VERSION >= 0x050000 163 if (cert.isBlacklisted()) {
168 QByteArray certData = cert.toDer();
169 const unsigned char *
data = (
const unsigned char *)certData.data();
171 std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &
data, certData.size()));
172 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
184 qWarning() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts <<
" root certificates";
207 for (
int i = 1; i < argc; i++)
209 QString arg(argv[i]);
210 if (arg.startsWith(
"-"))
236 else if (QFile::exists(arg))
257 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
270 bool fResult =
false;
273 QLocalSocket* socket =
new QLocalSocket();
274 socket->connectToServer(
ipcServerName(), QIODevice::WriteOnly);
283 QDataStream
out(&block, QIODevice::WriteOnly);
284 out.setVersion(QDataStream::Qt_4_0);
286 out.device()->seek(0);
288 socket->write(block);
291 socket->disconnectFromServer();
310 GOOGLE_PROTOBUF_VERIFY_VERSION;
316 parent->installEventFilter(
this);
321 QLocalServer::removeServer(
name);
323 if (startLocalServer)
329 QMessageBox::critical(0, tr(
"Payment request error"),
330 tr(
"Cannot start dash: click-to-pay handler"));
341 google::protobuf::ShutdownProtobufLibrary();
351 if (event->type() == QEvent::FileOpen) {
352 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent*
>(event);
353 if (!fileEvent->file().isEmpty())
355 else if (!fileEvent->url().isEmpty())
361 return QObject::eventFilter(
object, event);
380 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() <<
":" << proxy.port();
383 qDebug() <<
"PaymentServer::initNetManager: No active proxy server found.";
385 connect(
netManager, SIGNAL(finished(QNetworkReply*)),
387 connect(
netManager, SIGNAL(sslErrors(QNetworkReply*,
const QList<QSslError> &)),
388 this, SLOT(
reportSslErrors(QNetworkReply*,
const QList<QSslError> &)));
413 #if QT_VERSION < 0x050000 416 QUrlQuery uri((QUrl(s)));
418 if (uri.hasQueryItem(
"r"))
421 temp.append(uri.queryItemValue(
"r"));
422 QString decoded = QUrl::fromPercentEncoding(temp);
423 QUrl fetchUrl(decoded, QUrl::StrictMode);
425 if (fetchUrl.isValid())
427 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl <<
")";
432 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
433 Q_EMIT
message(tr(
"URI handling"),
434 tr(
"Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
446 if (!address.IsValid()) {
447 Q_EMIT
message(tr(
"URI handling"), tr(
"Invalid payment address %1").arg(recipient.
address),
454 Q_EMIT
message(tr(
"URI handling"),
455 tr(
"URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters."),
462 if (QFile::exists(s))
468 Q_EMIT
message(tr(
"Payment request file handling"),
469 tr(
"Payment request file cannot be read! This can be caused by an invalid payment request file."),
481 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
483 while (clientConnection->bytesAvailable() < (int)
sizeof(quint32))
484 clientConnection->waitForReadyRead();
486 connect(clientConnection, SIGNAL(disconnected()),
487 clientConnection, SLOT(deleteLater()));
489 QDataStream in(clientConnection);
490 in.setVersion(QDataStream::Qt_4_0);
491 if (clientConnection->bytesAvailable() < (int)
sizeof(quint16)) {
507 if (!
f.open(QIODevice::ReadOnly)) {
508 qWarning() << QString(
"PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename);
517 QByteArray
data =
f.readAll();
530 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request network doesn't match client network."),
539 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request expired."),
545 Q_EMIT
message(tr(
"Payment request error"), tr(
"Payment request is not initialized."),
556 QList<std::pair<CScript, CAmount> > sendingTos = request.
getPayTo();
557 QStringList addresses;
564 addresses.append(QString::fromStdString(
CBitcoinAddress(dest).ToString()));
570 Q_EMIT
message(tr(
"Payment request rejected"),
571 tr(
"Unverified payment requests to custom payment scripts are unsupported."),
585 CTxOut txOut(sendingTo.second, sendingTo.first);
587 Q_EMIT
message(tr(
"Payment request error"), tr(
"Requested payment amount of %1 is too small (considered dust).")
594 recipient.
amount += sendingTo.second;
602 recipient.
address = addresses.join(
"<br />");
605 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment request from " << recipient.
authenticatedMerchant;
608 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(
", ");
616 QNetworkRequest netRequest;
618 netRequest.setUrl(
url);
619 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
630 QNetworkRequest netRequest;
632 netRequest.setUrl(QString::fromStdString(details.
payment_url()));
634 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
643 std::string strAccount = account.toStdString();
644 std::set<CTxDestination> refundAddresses =
wallet->GetAccountAddresses(strAccount);
645 if (!refundAddresses.empty()) {
652 if (
wallet->GetKeyFromPool(newKey,
false)) {
654 wallet->SetAddressBook(keyID, strAccount,
"refund");
663 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
668 netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
669 QByteArray serData(length,
'\0');
670 if (payment.SerializeToArray(serData.data(), length)) {
675 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing payment message";
681 reply->deleteLater();
685 Q_EMIT
message(tr(
"Payment request rejected"),
686 tr(
"Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
687 .arg(reply->request().url().toString())
694 if (reply->error() != QNetworkReply::NoError) {
695 QString
msg = tr(
"Error communicating with %1: %2")
696 .arg(reply->request().url().toString())
697 .arg(reply->errorString());
699 qWarning() <<
"PaymentServer::netRequestFinished: " <<
msg;
704 QByteArray
data = reply->readAll();
713 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing payment request";
714 Q_EMIT
message(tr(
"Payment request error"),
715 tr(
"Payment request cannot be parsed!"),
726 if (!paymentACK.ParseFromArray(
data.data(),
data.size()))
728 QString
msg = tr(
"Bad response from server %1")
729 .arg(reply->request().url().toString());
731 qWarning() <<
"PaymentServer::netRequestFinished: " <<
msg;
746 Q_FOREACH (
const QSslError&
err, errs) {
747 qWarning() <<
"PaymentServer::reportSslErrors: " <<
err;
748 errString +=
err.errorString() +
"\n";
768 qWarning() << QString(
"PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
770 .arg(QString::fromStdString(requestDetails.
network()))
771 .arg(QString::fromStdString(
Params().NetworkIDString()));
780 const QString requestExpires = QString::fromStdString(
DateTimeStrFormat(
"%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.
expires()));
781 qWarning() << QString(
"PaymentServer::%1: Payment request expired \"%2\".")
783 .arg(requestExpires);
792 qWarning() << QString(
"PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
804 qWarning() << QString(
"PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
814 return certStore.get();
void netRequestFinished(QNetworkReply *)
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
void handlePaymentACK(const QString &paymentACKMsg)
void receivedPaymentACK(const QString &paymentACKMsg)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
bool eventFilter(QObject *object, QEvent *event)
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
const ::std::string & payment_url() const
bool processPaymentRequest(const PaymentRequestPlus &request, SendCoinsRecipient &recipient)
const char * BIP71_MIMETYPE_PAYMENT
std::string NetworkIDString() const
void set_script(const ::std::string &value)
static const CAmount MAX_MONEY
static bool verifyNetwork(const payments::PaymentDetails &requestDetails)
void handleURIConnection()
PaymentServer(QObject *parent, bool startLocalServer=true)
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)
static bool verifySize(qint64 requestSize)
bool MoneyRange(const CAmount &nValue)
static bool verifyExpired(const payments::PaymentDetails &requestDetails)
bool parse(const QByteArray &data)
const ::std::string & merchant_data() const
void setOptionsModel(OptionsModel *optionsModel)
static const std::string TESTNET
bool IsInitialized() const
static bool readPaymentRequestFromFile(const QString &filename, PaymentRequestPlus &request)
static X509_STORE * getCertStore()
const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE
void handleURIOrFile(const QString &s)
OptionsModel * optionsModel
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
static bool ipcSendCommandLine()
inline ::std::string * add_transactions()
static bool verifyAmount(const CAmount &requestAmount)
static void ReportInvalidCertificate(const QSslCertificate &cert)
QNetworkAccessManager * netManager
void fetchPaymentACK(CWallet *wallet, SendCoinsRecipient recipient, QByteArray transaction)
const char * BIP71_MIMETYPE_PAYMENTACK
bool getMerchant(X509_STORE *certStore, QString &merchant) const
const int BITCOIN_IPC_CONNECT_TIMEOUT
bool getProxySettings(QNetworkProxy &proxy) const
void SelectParams(const std::string &network)
const char * BIP70_MESSAGE_PAYMENTREQUEST
inline ::google::protobuf::uint64 expires() const
QString HtmlEscape(const QString &str, bool fMultiLine)
void receivedPaymentRequest(SendCoinsRecipient)
CScript GetScriptForDestination(const CTxDestination &dest)
const std::string CLIENT_NAME
static QList< QString > savedPaymentRequests
void set_merchant_data(const ::std::string &value)
void message(const QString &title, const QString &message, unsigned int style)
PaymentRequestPlus paymentRequest
const char * BIP71_MIMETYPE_PAYMENTREQUEST
static void LoadRootCAs(X509_STORE *store=NULL)
const char * BIP70_MESSAGE_PAYMENTACK
bool IsDust(const CFeeRate &minRelayTxFee) const
const ::std::string & memo() const
void fetchRequest(const QUrl &url)
static void ipcParseCommandLine(int argc, char *argv[])
const CChainParams & Params()
QString authenticatedMerchant
bool has_payment_url() const
const QString BITCOIN_IPC_PREFIX("dash:")
static QString ipcServerName()
const ::std::string & network() const
static const std::string MAIN
std::string GetArg(const std::string &strArg, const std::string &strDefault)
QList< std::pair< CScript, CAmount > > getPayTo() const
int64_t GetTime()
For unit testing.
inline ::payments::Output * add_refund_to()
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
void reportSslErrors(QNetworkReply *, const QList< QSslError > &)
void operator()(X509_STORE *b)
const ::std::string & memo() const
const payments::PaymentDetails & getDetails() const