You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

paymentserver.cpp 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. // Copyright (c) 2011-2013 The Bitcoin developers
  2. // Distributed under the MIT/X11 software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "paymentserver.h"
  5. #include "bitcoinunits.h"
  6. #include "guiconstants.h"
  7. #include "guiutil.h"
  8. #include "optionsmodel.h"
  9. #include "base58.h"
  10. #include "ui_interface.h"
  11. #include "wallet.h"
  12. #include <cstdlib>
  13. #include <openssl/x509.h>
  14. #include <openssl/x509_vfy.h>
  15. #include <QApplication>
  16. #include <QByteArray>
  17. #include <QDataStream>
  18. #include <QDateTime>
  19. #include <QDebug>
  20. #include <QFile>
  21. #include <QFileOpenEvent>
  22. #include <QHash>
  23. #include <QList>
  24. #include <QLocalServer>
  25. #include <QLocalSocket>
  26. #include <QNetworkAccessManager>
  27. #include <QNetworkProxy>
  28. #include <QNetworkReply>
  29. #include <QNetworkRequest>
  30. #include <QSslCertificate>
  31. #include <QSslError>
  32. #include <QSslSocket>
  33. #include <QStringList>
  34. #include <QTextDocument>
  35. #if QT_VERSION < 0x050000
  36. #include <QUrl>
  37. #else
  38. #include <QUrlQuery>
  39. #endif
  40. using namespace boost;
  41. const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
  42. const QString BITCOIN_IPC_PREFIX("bitcoin:");
  43. const char* BITCOIN_REQUEST_MIMETYPE = "application/bitcoin-paymentrequest";
  44. const char* BITCOIN_PAYMENTACK_MIMETYPE = "application/bitcoin-paymentack";
  45. const char* BITCOIN_PAYMENTACK_CONTENTTYPE = "application/bitcoin-payment";
  46. X509_STORE* PaymentServer::certStore = NULL;
  47. void PaymentServer::freeCertStore()
  48. {
  49. if (PaymentServer::certStore != NULL)
  50. {
  51. X509_STORE_free(PaymentServer::certStore);
  52. PaymentServer::certStore = NULL;
  53. }
  54. }
  55. //
  56. // Create a name that is unique for:
  57. // testnet / non-testnet
  58. // data directory
  59. //
  60. static QString ipcServerName()
  61. {
  62. QString name("BitcoinQt");
  63. // Append a simple hash of the datadir
  64. // Note that GetDataDir(true) returns a different path
  65. // for -testnet versus main net
  66. QString ddir(QString::fromStdString(GetDataDir(true).string()));
  67. name.append(QString::number(qHash(ddir)));
  68. return name;
  69. }
  70. //
  71. // We store payment URIs and requests received before
  72. // the main GUI window is up and ready to ask the user
  73. // to send payment.
  74. static QList<QString> savedPaymentRequests;
  75. static void ReportInvalidCertificate(const QSslCertificate& cert)
  76. {
  77. qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName);
  78. }
  79. //
  80. // Load OpenSSL's list of root certificate authorities
  81. //
  82. void PaymentServer::LoadRootCAs(X509_STORE* _store)
  83. {
  84. if (PaymentServer::certStore == NULL)
  85. atexit(PaymentServer::freeCertStore);
  86. else
  87. freeCertStore();
  88. // Unit tests mostly use this, to pass in fake root CAs:
  89. if (_store)
  90. {
  91. PaymentServer::certStore = _store;
  92. return;
  93. }
  94. // Normal execution, use either -rootcertificates or system certs:
  95. PaymentServer::certStore = X509_STORE_new();
  96. // Note: use "-system-" default here so that users can pass -rootcertificates=""
  97. // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
  98. QString certFile = QString::fromStdString(GetArg("-rootcertificates", "-system-"));
  99. if (certFile.isEmpty())
  100. return; // Empty store
  101. QList<QSslCertificate> certList;
  102. if (certFile != "-system-")
  103. {
  104. certList = QSslCertificate::fromPath(certFile);
  105. // Use those certificates when fetching payment requests, too:
  106. QSslSocket::setDefaultCaCertificates(certList);
  107. }
  108. else
  109. certList = QSslSocket::systemCaCertificates ();
  110. int nRootCerts = 0;
  111. const QDateTime currentTime = QDateTime::currentDateTime();
  112. foreach (const QSslCertificate& cert, certList)
  113. {
  114. if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
  115. ReportInvalidCertificate(cert);
  116. continue;
  117. }
  118. #if QT_VERSION >= 0x050000
  119. if (cert.isBlacklisted()) {
  120. ReportInvalidCertificate(cert);
  121. continue;
  122. }
  123. #endif
  124. QByteArray certData = cert.toDer();
  125. const unsigned char *data = (const unsigned char *)certData.data();
  126. X509* x509 = d2i_X509(0, &data, certData.size());
  127. if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509))
  128. {
  129. // Note: X509_STORE_free will free the X509* objects when
  130. // the PaymentServer is destroyed
  131. ++nRootCerts;
  132. }
  133. else
  134. {
  135. ReportInvalidCertificate(cert);
  136. continue;
  137. }
  138. }
  139. qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates";
  140. // Project for another day:
  141. // Fetch certificate revocation lists, and add them to certStore.
  142. // Issues to consider:
  143. // performance (start a thread to fetch in background?)
  144. // privacy (fetch through tor/proxy so IP address isn't revealed)
  145. // would it be easier to just use a compiled-in blacklist?
  146. // or use Qt's blacklist?
  147. // "certificate stapling" with server-side caching is more efficient
  148. }
  149. //
  150. // Sending to the server is done synchronously, at startup.
  151. // If the server isn't already running, startup continues,
  152. // and the items in savedPaymentRequest will be handled
  153. // when uiReady() is called.
  154. //
  155. // Warning: ipcSendCommandLine() is called early in init,
  156. // so don't use "emit message()", but "QMessageBox::"!
  157. //
  158. bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
  159. {
  160. for (int i = 1; i < argc; i++)
  161. {
  162. QString arg(argv[i]);
  163. if (arg.startsWith("-"))
  164. continue;
  165. if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
  166. {
  167. savedPaymentRequests.append(arg);
  168. SendCoinsRecipient r;
  169. if (GUIUtil::parseBitcoinURI(arg, &r))
  170. {
  171. CBitcoinAddress address(r.address.toStdString());
  172. SelectParams(CChainParams::MAIN);
  173. if (!address.IsValid())
  174. {
  175. SelectParams(CChainParams::TESTNET);
  176. }
  177. }
  178. }
  179. else if (QFile::exists(arg)) // Filename
  180. {
  181. savedPaymentRequests.append(arg);
  182. PaymentRequestPlus request;
  183. if (readPaymentRequest(arg, request))
  184. {
  185. if (request.getDetails().network() == "main")
  186. SelectParams(CChainParams::MAIN);
  187. else
  188. SelectParams(CChainParams::TESTNET);
  189. }
  190. }
  191. else
  192. {
  193. // Printing to debug.log is about the best we can do here, the
  194. // GUI hasn't started yet so we can't pop up a message box.
  195. qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg;
  196. }
  197. }
  198. return true;
  199. }
  200. //
  201. // Sending to the server is done synchronously, at startup.
  202. // If the server isn't already running, startup continues,
  203. // and the items in savedPaymentRequest will be handled
  204. // when uiReady() is called.
  205. //
  206. bool PaymentServer::ipcSendCommandLine()
  207. {
  208. bool fResult = false;
  209. foreach (const QString& r, savedPaymentRequests)
  210. {
  211. QLocalSocket* socket = new QLocalSocket();
  212. socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
  213. if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
  214. {
  215. delete socket;
  216. return false;
  217. }
  218. QByteArray block;
  219. QDataStream out(&block, QIODevice::WriteOnly);
  220. out.setVersion(QDataStream::Qt_4_0);
  221. out << r;
  222. out.device()->seek(0);
  223. socket->write(block);
  224. socket->flush();
  225. socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
  226. socket->disconnectFromServer();
  227. delete socket;
  228. fResult = true;
  229. }
  230. return fResult;
  231. }
  232. PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
  233. QObject(parent),
  234. saveURIs(true),
  235. uriServer(0),
  236. netManager(0),
  237. optionsModel(0)
  238. {
  239. // Verify that the version of the library that we linked against is
  240. // compatible with the version of the headers we compiled against.
  241. GOOGLE_PROTOBUF_VERIFY_VERSION;
  242. // Install global event filter to catch QFileOpenEvents
  243. // on Mac: sent when you click bitcoin: links
  244. // other OSes: helpful when dealing with payment request files (in the future)
  245. if (parent)
  246. parent->installEventFilter(this);
  247. QString name = ipcServerName();
  248. // Clean up old socket leftover from a crash:
  249. QLocalServer::removeServer(name);
  250. if (startLocalServer)
  251. {
  252. uriServer = new QLocalServer(this);
  253. if (!uriServer->listen(name)) {
  254. // constructor is called early in init, so don't use "emit message()" here
  255. QMessageBox::critical(0, tr("Payment request error"),
  256. tr("Cannot start bitcoin: click-to-pay handler"));
  257. }
  258. else {
  259. connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
  260. connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString)));
  261. }
  262. }
  263. }
  264. PaymentServer::~PaymentServer()
  265. {
  266. google::protobuf::ShutdownProtobufLibrary();
  267. }
  268. //
  269. // OSX-specific way of handling bitcoin: URIs and
  270. // PaymentRequest mime types
  271. //
  272. bool PaymentServer::eventFilter(QObject *object, QEvent *event)
  273. {
  274. // clicking on bitcoin: URIs creates FileOpen events on the Mac
  275. if (event->type() == QEvent::FileOpen)
  276. {
  277. QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
  278. if (!fileEvent->file().isEmpty())
  279. handleURIOrFile(fileEvent->file());
  280. else if (!fileEvent->url().isEmpty())
  281. handleURIOrFile(fileEvent->url().toString());
  282. return true;
  283. }
  284. return QObject::eventFilter(object, event);
  285. }
  286. void PaymentServer::initNetManager()
  287. {
  288. if (!optionsModel)
  289. return;
  290. if (netManager != NULL)
  291. delete netManager;
  292. // netManager is used to fetch paymentrequests given in bitcoin: URIs
  293. netManager = new QNetworkAccessManager(this);
  294. QNetworkProxy proxy;
  295. // Query active proxy (fails if no SOCKS5 proxy)
  296. if (optionsModel->getProxySettings(proxy)) {
  297. if (proxy.type() == QNetworkProxy::Socks5Proxy) {
  298. netManager->setProxy(proxy);
  299. qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
  300. }
  301. else
  302. qDebug() << "PaymentServer::initNetManager : No active proxy server found.";
  303. }
  304. else
  305. emit message(tr("Net manager warning"),
  306. tr("Your active proxy doesn't support SOCKS5, which is required for payment requests via proxy."),
  307. CClientUIInterface::MSG_WARNING);
  308. connect(netManager, SIGNAL(finished(QNetworkReply*)),
  309. this, SLOT(netRequestFinished(QNetworkReply*)));
  310. connect(netManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)),
  311. this, SLOT(reportSslErrors(QNetworkReply*, const QList<QSslError> &)));
  312. }
  313. void PaymentServer::uiReady()
  314. {
  315. initNetManager();
  316. saveURIs = false;
  317. foreach (const QString& s, savedPaymentRequests)
  318. {
  319. handleURIOrFile(s);
  320. }
  321. savedPaymentRequests.clear();
  322. }
  323. void PaymentServer::handleURIOrFile(const QString& s)
  324. {
  325. if (saveURIs)
  326. {
  327. savedPaymentRequests.append(s);
  328. return;
  329. }
  330. if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
  331. {
  332. #if QT_VERSION < 0x050000
  333. QUrl uri(s);
  334. #else
  335. QUrlQuery uri((QUrl(s)));
  336. #endif
  337. if (uri.hasQueryItem("r")) // payment request URI
  338. {
  339. QByteArray temp;
  340. temp.append(uri.queryItemValue("r"));
  341. QString decoded = QUrl::fromPercentEncoding(temp);
  342. QUrl fetchUrl(decoded, QUrl::StrictMode);
  343. if (fetchUrl.isValid())
  344. {
  345. qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")";
  346. fetchRequest(fetchUrl);
  347. }
  348. else
  349. {
  350. qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl;
  351. emit message(tr("URI handling"),
  352. tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
  353. CClientUIInterface::ICON_WARNING);
  354. }
  355. return;
  356. }
  357. else // normal URI
  358. {
  359. SendCoinsRecipient recipient;
  360. if (GUIUtil::parseBitcoinURI(s, &recipient))
  361. {
  362. CBitcoinAddress address(recipient.address.toStdString());
  363. if (!address.IsValid()) {
  364. emit message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
  365. CClientUIInterface::MSG_ERROR);
  366. }
  367. else
  368. emit receivedPaymentRequest(recipient);
  369. }
  370. else
  371. emit message(tr("URI handling"),
  372. tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
  373. CClientUIInterface::ICON_WARNING);
  374. return;
  375. }
  376. }
  377. if (QFile::exists(s)) // payment request file
  378. {
  379. PaymentRequestPlus request;
  380. SendCoinsRecipient recipient;
  381. if (!readPaymentRequest(s, request))
  382. {
  383. emit message(tr("Payment request file handling"),
  384. tr("Payment request file can not be read! This can be caused by an invalid payment request file."),
  385. CClientUIInterface::ICON_WARNING);
  386. }
  387. else if (processPaymentRequest(request, recipient))
  388. emit receivedPaymentRequest(recipient);
  389. return;
  390. }
  391. }
  392. void PaymentServer::handleURIConnection()
  393. {
  394. QLocalSocket *clientConnection = uriServer->nextPendingConnection();
  395. while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
  396. clientConnection->waitForReadyRead();
  397. connect(clientConnection, SIGNAL(disconnected()),
  398. clientConnection, SLOT(deleteLater()));
  399. QDataStream in(clientConnection);
  400. in.setVersion(QDataStream::Qt_4_0);
  401. if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
  402. return;
  403. }
  404. QString msg;
  405. in >> msg;
  406. handleURIOrFile(msg);
  407. }
  408. bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPlus& request)
  409. {
  410. QFile f(filename);
  411. if (!f.open(QIODevice::ReadOnly))
  412. {
  413. qDebug() << "PaymentServer::readPaymentRequest : Failed to open " << filename;
  414. return false;
  415. }
  416. if (f.size() > MAX_PAYMENT_REQUEST_SIZE)
  417. {
  418. qDebug() << "PaymentServer::readPaymentRequest : " << filename << " too large";
  419. return false;
  420. }
  421. QByteArray data = f.readAll();
  422. return request.parse(data);
  423. }
  424. bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient)
  425. {
  426. if (!optionsModel)
  427. return false;
  428. if (request.IsInitialized()) {
  429. const payments::PaymentDetails& details = request.getDetails();
  430. // Payment request network matches client network?
  431. if (details.network() != Params().NetworkIDString())
  432. {
  433. emit message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."),
  434. CClientUIInterface::MSG_ERROR);
  435. return false;
  436. }
  437. // Expired payment request?
  438. if (details.has_expires() && (int64_t)details.expires() < GetTime())
  439. {
  440. emit message(tr("Payment request rejected"), tr("Payment request has expired."),
  441. CClientUIInterface::MSG_ERROR);
  442. return false;
  443. }
  444. }
  445. else {
  446. emit message(tr("Payment request error"), tr("Payment request is not initialized."),
  447. CClientUIInterface::MSG_ERROR);
  448. return false;
  449. }
  450. recipient.paymentRequest = request;
  451. recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo());
  452. request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant);
  453. QList<std::pair<CScript, qint64> > sendingTos = request.getPayTo();
  454. QStringList addresses;
  455. foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) {
  456. // Extract and check destination addresses
  457. CTxDestination dest;
  458. if (ExtractDestination(sendingTo.first, dest)) {
  459. // Append destination address
  460. addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString()));
  461. }
  462. else if (!recipient.authenticatedMerchant.isEmpty()) {
  463. // Insecure payments to custom bitcoin addresses are not supported
  464. // (there is no good way to tell the user where they are paying in a way
  465. // they'd have a chance of understanding).
  466. emit message(tr("Payment request rejected"),
  467. tr("Unverified payment requests to custom payment scripts are unsupported."),
  468. CClientUIInterface::MSG_ERROR);
  469. return false;
  470. }
  471. // Extract and check amounts
  472. CTxOut txOut(sendingTo.second, sendingTo.first);
  473. if (txOut.IsDust(CTransaction::minRelayTxFee)) {
  474. emit message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
  475. .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
  476. CClientUIInterface::MSG_ERROR);
  477. return false;
  478. }
  479. recipient.amount += sendingTo.second;
  480. }
  481. // Store addresses and format them to fit nicely into the GUI
  482. recipient.address = addresses.join("<br />");
  483. if (!recipient.authenticatedMerchant.isEmpty()) {
  484. qDebug() << "PaymentServer::processPaymentRequest : Secure payment request from " << recipient.authenticatedMerchant;
  485. }
  486. else {
  487. qDebug() << "PaymentServer::processPaymentRequest : Insecure payment request to " << addresses.join(", ");
  488. }
  489. return true;
  490. }
  491. void PaymentServer::fetchRequest(const QUrl& url)
  492. {
  493. QNetworkRequest netRequest;
  494. netRequest.setAttribute(QNetworkRequest::User, "PaymentRequest");
  495. netRequest.setUrl(url);
  496. netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
  497. netRequest.setRawHeader("Accept", BITCOIN_REQUEST_MIMETYPE);
  498. netManager->get(netRequest);
  499. }
  500. void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction)
  501. {
  502. const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
  503. if (!details.has_payment_url())
  504. return;
  505. QNetworkRequest netRequest;
  506. netRequest.setAttribute(QNetworkRequest::User, "PaymentACK");
  507. netRequest.setUrl(QString::fromStdString(details.payment_url()));
  508. netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE);
  509. netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
  510. netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE);
  511. payments::Payment payment;
  512. payment.set_merchant_data(details.merchant_data());
  513. payment.add_transactions(transaction.data(), transaction.size());
  514. // Create a new refund address, or re-use:
  515. QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
  516. std::string strAccount = account.toStdString();
  517. set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount);
  518. if (!refundAddresses.empty()) {
  519. CScript s; s.SetDestination(*refundAddresses.begin());
  520. payments::Output* refund_to = payment.add_refund_to();
  521. refund_to->set_script(&s[0], s.size());
  522. }
  523. else {
  524. CPubKey newKey;
  525. if (wallet->GetKeyFromPool(newKey)) {
  526. LOCK(wallet->cs_wallet); // SetAddressBook
  527. CKeyID keyID = newKey.GetID();
  528. wallet->SetAddressBook(keyID, strAccount, "refund");
  529. CScript s; s.SetDestination(keyID);
  530. payments::Output* refund_to = payment.add_refund_to();
  531. refund_to->set_script(&s[0], s.size());
  532. }
  533. else {
  534. // This should never happen, because sending coins should have
  535. // just unlocked the wallet and refilled the keypool.
  536. qDebug() << "PaymentServer::fetchPaymentACK : Error getting refund key, refund_to not set";
  537. }
  538. }
  539. int length = payment.ByteSize();
  540. netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
  541. QByteArray serData(length, '\0');
  542. if (payment.SerializeToArray(serData.data(), length)) {
  543. netManager->post(netRequest, serData);
  544. }
  545. else {
  546. // This should never happen, either.
  547. qDebug() << "PaymentServer::fetchPaymentACK : Error serializing payment message";
  548. }
  549. }
  550. void PaymentServer::netRequestFinished(QNetworkReply* reply)
  551. {
  552. reply->deleteLater();
  553. if (reply->error() != QNetworkReply::NoError)
  554. {
  555. QString msg = tr("Error communicating with %1: %2")
  556. .arg(reply->request().url().toString())
  557. .arg(reply->errorString());
  558. qDebug() << "PaymentServer::netRequestFinished : " << msg;
  559. emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR);
  560. return;
  561. }
  562. QByteArray data = reply->readAll();
  563. QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
  564. if (requestType == "PaymentRequest")
  565. {
  566. PaymentRequestPlus request;
  567. SendCoinsRecipient recipient;
  568. if (!request.parse(data))
  569. {
  570. qDebug() << "PaymentServer::netRequestFinished : Error parsing payment request";
  571. emit message(tr("Payment request error"),
  572. tr("Payment request can not be parsed!"),
  573. CClientUIInterface::MSG_ERROR);
  574. }
  575. else if (processPaymentRequest(request, recipient))
  576. emit receivedPaymentRequest(recipient);
  577. return;
  578. }
  579. else if (requestType == "PaymentACK")
  580. {
  581. payments::PaymentACK paymentACK;
  582. if (!paymentACK.ParseFromArray(data.data(), data.size()))
  583. {
  584. QString msg = tr("Bad response from server %1")
  585. .arg(reply->request().url().toString());
  586. qDebug() << "PaymentServer::netRequestFinished : " << msg;
  587. emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR);
  588. }
  589. else
  590. {
  591. emit receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo()));
  592. }
  593. }
  594. }
  595. void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs)
  596. {
  597. Q_UNUSED(reply);
  598. QString errString;
  599. foreach (const QSslError& err, errs) {
  600. qDebug() << "PaymentServer::reportSslErrors : " << err;
  601. errString += err.errorString() + "\n";
  602. }
  603. emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
  604. }
  605. void PaymentServer::setOptionsModel(OptionsModel *optionsModel)
  606. {
  607. this->optionsModel = optionsModel;
  608. }
  609. void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
  610. {
  611. // currently we don't futher process or store the paymentACK message
  612. emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL);
  613. }