20 #include <openssl/x509_vfy.h>
22 #include <QApplication>
24 #include <QDataStream>
28 #include <QFileOpenEvent>
31 #include <QLocalServer>
32 #include <QLocalSocket>
33 #include <QNetworkAccessManager>
34 #include <QNetworkProxy>
35 #include <QNetworkReply>
36 #include <QNetworkRequest>
37 #include <QSslCertificate>
40 #include <QStringList>
41 #include <QTextDocument>
43 #if QT_VERSION < 0x050000
71 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
79 static QString ipcServerName()
81 QString name(
"DogecoinQt");
87 name.append(QString::number(qHash(ddir)));
97 static QList<QString> savedPaymentRequests;
99 static void ReportInvalidCertificate(
const QSslCertificate& cert)
101 #if QT_VERSION < 0x050000
102 qDebug() << QString(
"%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
104 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);
116 certStore.reset(_store);
121 certStore.reset(X509_STORE_new());
125 QString certFile = QString::fromStdString(
GetArg(
"-rootcertificates",
"-system-"));
128 if (certFile.isEmpty()) {
129 qDebug() << QString(
"PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
133 QList<QSslCertificate> certList;
135 if (certFile !=
"-system-") {
136 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
138 certList = QSslCertificate::fromPath(certFile);
140 QSslSocket::setDefaultCaCertificates(certList);
142 certList = QSslSocket::systemCaCertificates();
145 const QDateTime currentTime = QDateTime::currentDateTime();
147 Q_FOREACH (
const QSslCertificate& cert, certList) {
153 if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
154 ReportInvalidCertificate(cert);
158 #if QT_VERSION >= 0x050000
160 if (cert.isBlacklisted()) {
161 ReportInvalidCertificate(cert);
165 QByteArray certData = cert.toDer();
166 const unsigned char *data = (
const unsigned char *)certData.data();
168 std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
169 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
177 ReportInvalidCertificate(cert);
181 qWarning() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts <<
" root certificates";
204 for (
int i = 1; i < argc; i++)
206 QString arg(argv[i]);
207 if (arg.startsWith(
"-"))
216 savedPaymentRequests.append(arg);
233 else if (QFile::exists(arg))
235 savedPaymentRequests.append(arg);
240 if (request.
getDetails().genesis() ==
"1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691")
244 else if (request.
getDetails().genesis() ==
"bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e")
254 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
267 bool fResult =
false;
268 Q_FOREACH (
const QString& r, savedPaymentRequests)
270 QLocalSocket* socket =
new QLocalSocket();
271 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
280 QDataStream out(&block, QIODevice::WriteOnly);
281 out.setVersion(QDataStream::Qt_4_0);
283 out.device()->seek(0);
285 socket->write(block);
288 socket->disconnectFromServer();
307 GOOGLE_PROTOBUF_VERIFY_VERSION;
313 parent->installEventFilter(
this);
315 QString name = ipcServerName();
318 QLocalServer::removeServer(name);
320 if (startLocalServer)
326 QMessageBox::critical(0, tr(
"Payment request error"),
327 tr(
"Cannot start dogecoin: click-to-pay handler"));
338 google::protobuf::ShutdownProtobufLibrary();
348 if (event->type() == QEvent::FileOpen) {
349 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent*
>(event);
350 if (!fileEvent->file().isEmpty())
352 else if (!fileEvent->url().isEmpty())
358 return QObject::eventFilter(
object, event);
377 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() <<
":" << proxy.port();
380 qDebug() <<
"PaymentServer::initNetManager: No active proxy server found.";
382 connect(
netManager, SIGNAL(finished(QNetworkReply*)),
384 connect(
netManager, SIGNAL(sslErrors(QNetworkReply*,
const QList<QSslError> &)),
385 this, SLOT(
reportSslErrors(QNetworkReply*,
const QList<QSslError> &)));
393 Q_FOREACH (
const QString& s, savedPaymentRequests)
397 savedPaymentRequests.clear();
404 savedPaymentRequests.append(s);
410 #if QT_VERSION < 0x050000
413 QUrlQuery uri((QUrl(s)));
415 if (uri.hasQueryItem(
"r"))
418 temp.append(uri.queryItemValue(
"r"));
419 QString decoded = QUrl::fromPercentEncoding(temp);
420 QUrl fetchUrl(decoded, QUrl::StrictMode);
422 if (fetchUrl.isValid())
424 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl <<
")";
429 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
430 Q_EMIT
message(tr(
"URI handling"),
431 tr(
"Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
444 Q_EMIT
message(tr(
"URI handling"), tr(
"Invalid payment address %1").arg(recipient.
address),
451 Q_EMIT
message(tr(
"URI handling"),
452 tr(
"URI cannot be parsed! This can be caused by an invalid Dogecoin address or malformed URI parameters."),
459 if (QFile::exists(s))
465 Q_EMIT
message(tr(
"Payment request file handling"),
466 tr(
"Payment request file cannot be read! This can be caused by an invalid payment request file."),
478 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
480 while (clientConnection->bytesAvailable() < (
int)
sizeof(quint32))
481 clientConnection->waitForReadyRead();
483 connect(clientConnection, SIGNAL(disconnected()),
484 clientConnection, SLOT(deleteLater()));
486 QDataStream in(clientConnection);
487 in.setVersion(QDataStream::Qt_4_0);
488 if (clientConnection->bytesAvailable() < (
int)
sizeof(quint16)) {
504 if (!f.open(QIODevice::ReadOnly)) {
505 qWarning() << QString(
"PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename);
514 QByteArray data = f.readAll();
516 return request.
parse(data);
527 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request network doesn't match client network."),
536 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request expired."),
542 Q_EMIT
message(tr(
"Payment request error"), tr(
"Payment request is not initialized."),
553 QList<std::pair<CScript, CAmount> > sendingTos = request.
getPayTo();
554 QStringList addresses;
561 addresses.append(QString::fromStdString(
CBitcoinAddress(dest).ToString()));
567 Q_EMIT
message(tr(
"Payment request rejected"),
568 tr(
"Unverified payment requests to custom payment scripts are unsupported."),
582 CTxOut txOut(sendingTo.second, sendingTo.first);
584 Q_EMIT
message(tr(
"Payment request error"), tr(
"Requested payment amount of %1 is too small (considered dust).")
591 recipient.
amount += sendingTo.second;
599 recipient.
address = addresses.join(
"<br />");
602 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment request from " << recipient.
authenticatedMerchant;
605 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(
", ");
613 QNetworkRequest netRequest;
615 netRequest.setUrl(
url);
616 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
624 if (!details.has_payment_url())
627 QNetworkRequest netRequest;
629 netRequest.setUrl(QString::fromStdString(details.payment_url()));
631 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
634 payments::Payment payment;
635 payment.set_merchant_data(details.merchant_data());
636 payment.add_transactions(transaction.data(), transaction.size());
640 std::string strAccount = account.toStdString();
642 if (!refundAddresses.empty()) {
644 payments::Output* refund_to = payment.add_refund_to();
645 refund_to->set_script(&s[0], s.
size());
654 payments::Output* refund_to = payment.add_refund_to();
655 refund_to->set_script(&s[0], s.
size());
660 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
664 int length = payment.ByteSize();
665 netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
666 QByteArray serData(length,
'\0');
667 if (payment.SerializeToArray(serData.data(), length)) {
672 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing payment message";
678 reply->deleteLater();
682 Q_EMIT
message(tr(
"Payment request rejected"),
683 tr(
"Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
684 .arg(reply->request().url().toString())
686 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE),
691 if (reply->error() != QNetworkReply::NoError) {
692 QString msg = tr(
"Error communicating with %1: %2")
693 .arg(reply->request().url().toString())
694 .arg(reply->errorString());
696 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
701 QByteArray data = reply->readAll();
703 QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
708 if (!request.
parse(data))
710 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing payment request";
711 Q_EMIT
message(tr(
"Payment request error"),
712 tr(
"Payment request cannot be parsed!"),
722 payments::PaymentACK paymentACK;
723 if (!paymentACK.ParseFromArray(data.data(), data.size()))
725 QString msg = tr(
"Bad response from server %1")
726 .arg(reply->request().url().toString());
728 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
743 Q_FOREACH (
const QSslError& err, errs) {
744 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
745 errString += err.errorString() +
"\n";
766 qWarning() << QString(
"PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
768 .arg(QString::fromStdString(requestDetails.genesis()))
776 bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() <
GetTime());
778 const QString requestExpires = QString::fromStdString(
DateTimeStrFormat(
"%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires()));
779 qWarning() << QString(
"PaymentServer::%1: Payment request expired \"%2\".")
781 .arg(requestExpires);
788 bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
790 qWarning() << QString(
"PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
793 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE);
802 qWarning() << QString(
"PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
812 return certStore.get();
bool MoneyRange(const CAmount &nValue)
int64_t CAmount
Amount in satoshis (Can be negative)
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
const CChainParams & Params()
Return the currently selected parameters.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
static const std::string TESTNET
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
base58-encoded Bitcoin addresses.
const Consensus::Params & GetConsensus(uint32_t nTargetHeight) const
@ MODAL
Force blocking, modal message box dialog (not just OS notification)
A reference to a CKey: the Hash160 of its serialized public key.
An encapsulated public key.
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Serialized script, used inside transaction inputs and outputs.
An output of a transaction.
bool IsDust(const CFeeRate &minRelayTxFee) const
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,...
Interface from Qt to configuration data structure for Bitcoin client.
bool getProxySettings(QNetworkProxy &proxy) const
bool getMerchant(X509_STORE *certStore, QString &merchant) const
QList< std::pair< CScript, CAmount > > getPayTo() const
bool IsInitialized() const
bool parse(const QByteArray &data)
const payments::PaymentDetails & getDetails() const
bool processPaymentRequest(const PaymentRequestPlus &request, SendCoinsRecipient &recipient)
static bool ipcSendCommandLine()
static bool verifyNetwork(const payments::PaymentDetails &requestDetails)
static bool verifyExpired(const payments::PaymentDetails &requestDetails)
void receivedPaymentACK(const QString &paymentACKMsg)
static bool verifyAmount(const CAmount &requestAmount)
void setOptionsModel(OptionsModel *optionsModel)
PaymentServer(QObject *parent, bool startLocalServer=true)
void netRequestFinished(QNetworkReply *)
void message(const QString &title, const QString &message, unsigned int style)
static bool readPaymentRequestFromFile(const QString &filename, PaymentRequestPlus &request)
void handleURIConnection()
static void ipcParseCommandLine(int argc, char *argv[])
static void LoadRootCAs(X509_STORE *store=NULL)
static bool verifySize(qint64 requestSize)
void reportSslErrors(QNetworkReply *, const QList< QSslError > &)
void receivedPaymentRequest(SendCoinsRecipient)
QNetworkAccessManager * netManager
void handlePaymentACK(const QString &paymentACKMsg)
void fetchPaymentACK(CWallet *wallet, SendCoinsRecipient recipient, QByteArray transaction)
static X509_STORE * getCertStore()
void handleURIOrFile(const QString &s)
void fetchRequest(const QUrl &url)
bool eventFilter(QObject *object, QEvent *event)
OptionsModel * optionsModel
PaymentRequestPlus paymentRequest
QString authenticatedMerchant
std::string GetHex() const
const std::string CLIENT_NAME
bool GetKeyFromPool(CPubKey &key)
bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose)
std::set< CTxDestination > GetAccountAddresses(const std::string &strAccount) const
QString HtmlEscape(const QString &str, bool fMultiLine)
QString boostPathToQString(const boost::filesystem::path &path)
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
const QString BITCOIN_IPC_PREFIX("dogecoin:")
const char * BIP71_MIMETYPE_PAYMENTREQUEST
const int BITCOIN_IPC_CONNECT_TIMEOUT
const char * BIP71_MIMETYPE_PAYMENTACK
const char * BIP70_MESSAGE_PAYMENTREQUEST
const char * BIP70_MESSAGE_PAYMENTACK
const char * BIP71_MIMETYPE_PAYMENT
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
CScript GetScriptForDestination(const CTxDestination &dest)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Parameters that influence chain consensus.
void operator()(X509_STORE *b)
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
#define PAIRTYPE(t1, t2)
This is needed because the foreach macro can't get over the comma in pair<t1, t2>
int64_t GetTime()
GetTimeMicros() and GetTimeMillis() both return the system time, but in different units.
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)