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(
"FabcoinQt");
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(
gArgs.
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 for (
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);
224 if (
address.IsValid(*tempChainParams))
230 if (
address.IsValid(*tempChainParams))
235 else if (QFile::exists(arg))
237 savedPaymentRequests.append(arg);
240 if (readPaymentRequestFromFile(arg, request))
256 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
269 bool fResult =
false;
270 for (
const QString& r : savedPaymentRequests)
272 QLocalSocket* socket =
new QLocalSocket();
273 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
282 QDataStream out(&block, QIODevice::WriteOnly);
283 out.setVersion(QDataStream::Qt_4_0);
285 out.device()->seek(0);
287 socket->write(block);
290 socket->disconnectFromServer();
309 GOOGLE_PROTOBUF_VERIFY_VERSION;
315 parent->installEventFilter(
this);
317 QString
name = ipcServerName();
320 QLocalServer::removeServer(name);
322 if (startLocalServer)
328 QMessageBox::critical(0, tr(
"Payment request error"),
329 tr(
"Cannot start fabcoin: click-to-pay handler"));
340 google::protobuf::ShutdownProtobufLibrary();
350 if (event->type() == QEvent::FileOpen) {
351 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent*
>(event);
352 if (!fileEvent->file().isEmpty())
354 else if (!fileEvent->url().isEmpty())
360 return QObject::eventFilter(
object, event);
379 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() <<
":" << proxy.port();
382 qDebug() <<
"PaymentServer::initNetManager: No active proxy server found.";
384 connect(
netManager, SIGNAL(finished(QNetworkReply*)),
386 connect(
netManager, SIGNAL(sslErrors(QNetworkReply*,
const QList<QSslError> &)),
387 this, SLOT(
reportSslErrors(QNetworkReply*,
const QList<QSslError> &)));
395 for (
const QString& s : savedPaymentRequests)
399 savedPaymentRequests.clear();
406 savedPaymentRequests.append(s);
412 #if QT_VERSION < 0x050000 415 QUrlQuery uri((QUrl(s)));
417 if (uri.hasQueryItem(
"r"))
420 temp.append(uri.queryItemValue(
"r"));
421 QString decoded = QUrl::fromPercentEncoding(temp);
422 QUrl fetchUrl(decoded, QUrl::StrictMode);
424 if (fetchUrl.isValid())
426 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl <<
")";
431 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
432 Q_EMIT
message(tr(
"URI handling"),
433 tr(
"Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
446 Q_EMIT
message(tr(
"URI handling"), tr(
"Invalid payment address %1").arg(recipient.
address),
453 Q_EMIT
message(tr(
"URI handling"),
454 tr(
"URI cannot be parsed! This can be caused by an invalid Fabcoin address or malformed URI parameters."),
461 if (QFile::exists(s))
467 Q_EMIT
message(tr(
"Payment request file handling"),
468 tr(
"Payment request file cannot be read! This can be caused by an invalid payment request file."),
480 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
482 while (clientConnection->bytesAvailable() < (int)
sizeof(quint32))
483 clientConnection->waitForReadyRead();
485 connect(clientConnection, SIGNAL(disconnected()),
486 clientConnection, SLOT(deleteLater()));
488 QDataStream in(clientConnection);
489 in.setVersion(QDataStream::Qt_4_0);
490 if (clientConnection->bytesAvailable() < (int)
sizeof(quint16)) {
506 if (!f.open(QIODevice::ReadOnly)) {
507 qWarning() << QString(
"PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename);
516 QByteArray
data = f.readAll();
518 return request.
parse(data);
529 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request network doesn't match client network."),
538 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request expired."),
544 Q_EMIT
message(tr(
"Payment request error"), tr(
"Payment request is not initialized."),
555 QList<std::pair<CScript, CAmount> > sendingTos = request.
getPayTo();
556 QStringList addresses;
558 for (
const std::pair<CScript, CAmount>& sendingTo : sendingTos) {
563 addresses.append(QString::fromStdString(
CFabcoinAddress(dest).ToString()));
569 Q_EMIT
message(tr(
"Payment request rejected"),
570 tr(
"Unverified payment requests to custom payment scripts are unsupported."),
584 CTxOut txOut(sendingTo.second, sendingTo.first);
586 Q_EMIT
message(tr(
"Payment request error"), tr(
"Requested payment amount of %1 is too small (considered dust).")
593 recipient.
amount += sendingTo.second;
601 recipient.
address = addresses.join(
"<br />");
604 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment request from " << recipient.
authenticatedMerchant;
607 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(
", ");
615 QNetworkRequest netRequest;
617 netRequest.setUrl(url);
618 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
629 QNetworkRequest netRequest;
631 netRequest.setUrl(QString::fromStdString(details.
payment_url()));
633 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
642 std::string strAccount = account.toStdString();
644 if (!refundAddresses.empty()) {
647 refund_to->set_script(&s[0], s.
size());
662 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
667 netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
668 QByteArray serData(length,
'\0');
669 if (payment.SerializeToArray(serData.data(), length)) {
674 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing payment message";
680 reply->deleteLater();
684 Q_EMIT
message(tr(
"Payment request rejected"),
685 tr(
"Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
686 .arg(reply->request().url().toString())
688 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE),
693 if (reply->error() != QNetworkReply::NoError) {
694 QString msg = tr(
"Error communicating with %1: %2")
695 .arg(reply->request().url().toString())
696 .arg(reply->errorString());
698 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
703 QByteArray
data = reply->readAll();
705 QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
710 if (!request.
parse(data))
712 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing payment request";
713 Q_EMIT
message(tr(
"Payment request error"),
714 tr(
"Payment request cannot be parsed!"),
725 if (!paymentACK.ParseFromArray(data.data(), data.size()))
727 QString msg = tr(
"Bad response from server %1")
728 .arg(reply->request().url().toString());
730 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
745 for (
const QSslError& err : errs) {
746 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
747 errString += err.errorString() +
"\n";
767 qWarning() << QString(
"PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
769 .arg(QString::fromStdString(requestDetails.
network()))
770 .arg(QString::fromStdString(
Params().NetworkIDString()));
779 const QString requestExpires = QString::fromStdString(
DateTimeStrFormat(
"%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.
expires()));
780 qWarning() << QString(
"PaymentServer::%1: Payment request expired \"%2\".")
782 .arg(requestExpires);
789 bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
791 qWarning() << QString(
"PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
794 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE);
803 qWarning() << QString(
"PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
813 return certStore.get();
static void LoadRootCAs(X509_STORE *store=nullptr)
static bool verifyAmount(const CAmount &requestAmount)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
PaymentRequestPlus paymentRequest
bool IsInitialized() const
const char * BIP71_MIMETYPE_PAYMENT
void message(const QString &title, const QString &message, unsigned int style)
static bool verifyNetwork(const payments::PaymentDetails &requestDetails)
void set_script(const ::std::string &value)
void operator()(X509_STORE *b)
void setOptionsModel(OptionsModel *optionsModel)
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)
bool getMerchant(X509_STORE *certStore, QString &merchant) const
void receivedPaymentACK(const QString &paymentACKMsg)
bool has_payment_url() const
bool MoneyRange(const CAmount &nValue)
base58-encoded Fabcoin addresses.
void set_merchant_data(const ::std::string &value)
inline::google::protobuf::uint64 expires() const
void receivedPaymentRequest(SendCoinsRecipient)
QString HtmlEscape(const QString &str, bool fMultiLine)
const QString FABCOIN_IPC_PREFIX("fabcoin:")
inline::std::string * add_transactions()
static void ipcParseCommandLine(int argc, char *argv[])
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 QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
const ::std::string & payment_url() const
std::set< CTxDestination > GetAccountAddresses(const std::string &strAccount) const
static bool ipcSendCommandLine()
int64_t CAmount
Amount in lius (Can be negative)
bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose)
std::string NetworkIDString() const
Return the BIP70 network string (main, test or regtest)
const char * BIP71_MIMETYPE_PAYMENTACK
void netRequestFinished(QNetworkReply *)
const ::std::string & memo() const
bool parseFabcoinURI(const QUrl &uri, SendCoinsRecipient *out)
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.
bool getProxySettings(QNetworkProxy &proxy) const
const char * BIP70_MESSAGE_PAYMENTREQUEST
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet, txnouttype *typeRet)
An encapsulated public key.
const int FABCOIN_IPC_CONNECT_TIMEOUT
static bool verifyExpired(const payments::PaymentDetails &requestDetails)
const ::std::string & network() const
const payments::PaymentDetails & getDetails() const
An output of a transaction.
PaymentServer(QObject *parent, bool startLocalServer=true)
CScript GetScriptForDestination(const CTxDestination &dest)
static X509_STORE * getCertStore()
QNetworkAccessManager * netManager
const std::string CLIENT_NAME
bool parse(const QByteArray &data)
const char * BIP71_MIMETYPE_PAYMENTREQUEST
QList< std::pair< CScript, CAmount > > getPayTo() const
const char * BIP70_MESSAGE_PAYMENTACK
void reportSslErrors(QNetworkReply *, const QList< QSslError > &)
void fetchPaymentACK(CWallet *wallet, SendCoinsRecipient recipient, QByteArray transaction)
Interface from Qt to configuration data structure for Fabcoin client.
const CChainParams & Params()
Return the currently selected parameters.
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Serialized script, used inside transaction inputs and outputs.
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)
inline::payments::Output * add_refund_to()
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
const ::std::string & memo() const
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...
struct evm_uint160be address(struct evm_env *env)
QString boostPathToQString(const fs::path &path)
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
const ::std::string & merchant_data() const
QString authenticatedMerchant