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 72 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
80 static QString ipcServerName()
82 QString name(
"RavenQt");
88 name.append(QString::number(qHash(ddir)));
98 static QList<QString> savedPaymentRequests;
100 static void ReportInvalidCertificate(
const QSslCertificate& cert)
102 #if QT_VERSION < 0x050000 103 qDebug() << QString(
"%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
105 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);
117 certStore.reset(_store);
122 certStore.reset(X509_STORE_new());
126 QString certFile = QString::fromStdString(
gArgs.
GetArg(
"-rootcertificates",
"-system-"));
129 if (certFile.isEmpty()) {
130 qDebug() << QString(
"PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
134 QList<QSslCertificate> certList;
136 if (certFile !=
"-system-") {
137 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
139 certList = QSslCertificate::fromPath(certFile);
141 QSslSocket::setDefaultCaCertificates(certList);
143 certList = QSslSocket::systemCaCertificates();
146 const QDateTime currentTime = QDateTime::currentDateTime();
148 for (
const QSslCertificate& cert : certList) {
154 if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
155 ReportInvalidCertificate(cert);
159 #if QT_VERSION >= 0x050000 161 if (cert.isBlacklisted()) {
162 ReportInvalidCertificate(cert);
166 QByteArray certData = cert.toDer();
167 const unsigned char *data = (
const unsigned char *)certData.data();
169 std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
170 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
178 ReportInvalidCertificate(cert);
182 qWarning() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts <<
" root certificates";
205 for (
int i = 1; i < argc; i++)
207 QString arg(argv[i]);
208 if (arg.startsWith(
"-"))
217 savedPaymentRequests.append(arg);
234 else if (QFile::exists(arg))
236 savedPaymentRequests.append(arg);
239 if (readPaymentRequestFromFile(arg, request))
245 else if (request.
getDetails().network() ==
"test")
255 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
268 bool fResult =
false;
269 for (
const QString& r : savedPaymentRequests)
271 QLocalSocket* socket =
new QLocalSocket();
272 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
281 QDataStream out(&block, QIODevice::WriteOnly);
282 out.setVersion(QDataStream::Qt_4_0);
284 out.device()->seek(0);
286 socket->write(block);
289 socket->disconnectFromServer();
308 GOOGLE_PROTOBUF_VERIFY_VERSION;
314 parent->installEventFilter(
this);
316 QString name = ipcServerName();
319 QLocalServer::removeServer(name);
321 if (startLocalServer)
327 QMessageBox::critical(0, tr(
"Payment request error"),
328 tr(
"Cannot start raven: click-to-pay handler"));
339 google::protobuf::ShutdownProtobufLibrary();
349 if (event->type() == QEvent::FileOpen) {
350 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent*
>(event);
351 if (!fileEvent->file().isEmpty())
353 else if (!fileEvent->url().isEmpty())
359 return QObject::eventFilter(
object, event);
378 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() <<
":" << proxy.port();
381 qDebug() <<
"PaymentServer::initNetManager: No active proxy server found.";
383 connect(
netManager, SIGNAL(finished(QNetworkReply*)),
385 connect(
netManager, SIGNAL(sslErrors(QNetworkReply*,
const QList<QSslError> &)),
386 this, SLOT(
reportSslErrors(QNetworkReply*,
const QList<QSslError> &)));
394 for (
const QString& s : savedPaymentRequests)
398 savedPaymentRequests.clear();
405 savedPaymentRequests.append(s);
411 #if QT_VERSION < 0x050000 414 QUrlQuery uri((QUrl(s)));
416 if (uri.hasQueryItem(
"r"))
419 temp.append(uri.queryItemValue(
"r"));
420 QString decoded = QUrl::fromPercentEncoding(temp);
421 QUrl fetchUrl(decoded, QUrl::StrictMode);
423 if (fetchUrl.isValid())
425 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl <<
")";
430 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
431 Q_EMIT
message(tr(
"URI handling"),
432 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 Raven 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;
556 for (
const std::pair<CScript, CAmount>& sendingTo : sendingTos) {
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 for (
const QSslError& err : errs) {
744 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
745 errString += err.errorString() +
"\n";
765 qWarning() << QString(
"PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
767 .arg(QString::fromStdString(requestDetails.network()))
768 .arg(QString::fromStdString(
Params().NetworkIDString()));
775 bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() <
GetTime());
777 const QString requestExpires = QString::fromStdString(
DateTimeStrFormat(
"%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires()));
778 qWarning() << QString(
"PaymentServer::%1: Payment request expired \"%2\".")
780 .arg(requestExpires);
787 bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
789 qWarning() << QString(
"PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
792 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE);
801 qWarning() << QString(
"PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
811 return certStore.get();
static void LoadRootCAs(X509_STORE *store=nullptr)
std::string NetworkIDString() const
Return the BIP70 network string (main, test or regtest)
static bool verifyAmount(const CAmount &requestAmount)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
PaymentRequestPlus paymentRequest
const char * BIP71_MIMETYPE_PAYMENT
void message(const QString &title, const QString &message, unsigned int style)
static bool verifyNetwork(const payments::PaymentDetails &requestDetails)
void operator()(X509_STORE *b)
bool getProxySettings(QNetworkProxy &proxy) const
void setOptionsModel(OptionsModel *optionsModel)
bool parseRavenURI(const QUrl &uri, SendCoinsRecipient *out)
bool processPaymentRequest(const PaymentRequestPlus &request, SendCoinsRecipient &recipient)
bool eventFilter(QObject *object, QEvent *event)
void handlePaymentACK(const QString &paymentACKMsg)
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)
void receivedPaymentACK(const QString &paymentACKMsg)
bool MoneyRange(const CAmount &nValue)
void receivedPaymentRequest(SendCoinsRecipient)
QString HtmlEscape(const QString &str, bool fMultiLine)
bool IsInitialized() const
static void ipcParseCommandLine(int argc, char *argv[])
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
int getDisplayUnit() const
void handleURIOrFile(const QString &s)
Force blocking, modal message box dialog (not just OS notification)
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
static bool ipcSendCommandLine()
int64_t CAmount
Amount in corbies (Can be negative)
bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose)
const int RAVEN_IPC_CONNECT_TIMEOUT
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms)
const char * BIP71_MIMETYPE_PAYMENTACK
void netRequestFinished(QNetworkReply *)
bool getMerchant(X509_STORE *certStore, QString &merchant) const
std::unique_ptr< CChainParams > CreateChainParams(const std::string &chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
static bool readPaymentRequestFromFile(const QString &filename, PaymentRequestPlus &request)
void fetchRequest(const QUrl &url)
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
const char * BIP70_MESSAGE_PAYMENTREQUEST
An encapsulated public key.
const payments::PaymentDetails & getDetails() const
static bool verifyExpired(const payments::PaymentDetails &requestDetails)
QList< std::pair< CScript, CAmount > > getPayTo() const
An output of a transaction.
PaymentServer(QObject *parent, bool startLocalServer=true)
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Raven scriptPubKey for the given CTxDestination.
static X509_STORE * getCertStore()
QNetworkAccessManager * netManager
const std::string CLIENT_NAME
bool parse(const QByteArray &data)
void fetchPaymentACK(CWallet *wallet, const SendCoinsRecipient &recipient, QByteArray transaction)
const char * BIP71_MIMETYPE_PAYMENTREQUEST
const char * BIP70_MESSAGE_PAYMENTACK
void reportSslErrors(QNetworkReply *, const QList< QSslError > &)
std::set< CTxDestination > GetAccountAddresses(const std::string &strAccount) const
const QString RAVEN_IPC_PREFIX("raven:")
std::string EncodeDestination(const CTxDestination &dest)
Interface from Qt to configuration data structure for Raven client.
const CChainParams & Params()
Return the currently selected parameters.
Serialized script, used inside transaction inputs and outputs.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
A reference to a CKey: the Hash160 of its serialized public key.
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
void handleURIConnection()
static const std::string TESTNET
bool GetKeyFromPool(CPubKey &key, bool internal=false)
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
static bool verifySize(qint64 requestSize)
OptionsModel * optionsModel
const fs::path & GetDataDir(bool fNetSpecific)
int64_t GetTime()
GetTimeMicros() and GetTimeMillis() both return the system time, but in different units...
QString boostPathToQString(const fs::path &path)
QString authenticatedMerchant