Bitcoin Core 28.99.0
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2022 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <qt/paymentserver.h>
6
7#include <qt/bitcoinunits.h>
8#include <qt/guiutil.h>
9#include <qt/optionsmodel.h>
10
11#include <chainparams.h>
12#include <common/args.h>
13#include <interfaces/node.h>
14#include <key_io.h>
15#include <node/interface_ui.h>
16#include <policy/policy.h>
17#include <wallet/wallet.h>
18
19#include <cstdlib>
20#include <memory>
21
22#include <QApplication>
23#include <QByteArray>
24#include <QDataStream>
25#include <QDebug>
26#include <QFile>
27#include <QFileOpenEvent>
28#include <QHash>
29#include <QList>
30#include <QLocalServer>
31#include <QLocalSocket>
32#include <QStringList>
33#include <QUrlQuery>
34
35const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
36const QString BITCOIN_IPC_PREFIX("bitcoin:");
37
38//
39// Create a name that is unique for:
40// testnet / non-testnet
41// data directory
42//
43static QString ipcServerName()
44{
45 QString name("BitcoinQt");
46
47 // Append a simple hash of the datadir
48 // Note that gArgs.GetDataDirNet() returns a different path
49 // for -testnet versus main net
51 name.append(QString::number(qHash(ddir)));
52
53 return name;
54}
55
56//
57// We store payment URIs and requests received before
58// the main GUI window is up and ready to ask the user
59// to send payment.
60
61static QSet<QString> savedPaymentRequests;
62
63//
64// Sending to the server is done synchronously, at startup.
65// If the server isn't already running, startup continues,
66// and the items in savedPaymentRequest will be handled
67// when uiReady() is called.
68//
69// Warning: ipcSendCommandLine() is called early in init,
70// so don't use "Q_EMIT message()", but "QMessageBox::"!
71//
72void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
73{
74 for (int i = 1; i < argc; i++)
75 {
76 QString arg(argv[i]);
77 if (arg.startsWith("-")) continue;
78
79 if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
80 {
81 savedPaymentRequests.insert(arg);
82 }
83 }
84}
85
86//
87// Sending to the server is done synchronously, at startup.
88// If the server isn't already running, startup continues,
89// and the items in savedPaymentRequest will be handled
90// when uiReady() is called.
91//
93{
94 bool fResult = false;
95 for (const QString& r : savedPaymentRequests)
96 {
97 QLocalSocket* socket = new QLocalSocket();
98 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
99 if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
100 {
101 delete socket;
102 socket = nullptr;
103 return false;
104 }
105
106 QByteArray block;
107 QDataStream out(&block, QIODevice::WriteOnly);
108 out.setVersion(QDataStream::Qt_4_0);
109 out << r;
110 out.device()->seek(0);
111
112 socket->write(block);
113 socket->flush();
114 socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
115 socket->disconnectFromServer();
116
117 delete socket;
118 socket = nullptr;
119 fResult = true;
120 }
121
122 return fResult;
123}
124
125PaymentServer::PaymentServer(QObject* parent, bool startLocalServer)
126 : QObject(parent)
127{
128 // Install global event filter to catch QFileOpenEvents
129 // on Mac: sent when you click bitcoin: links
130 // other OSes: helpful when dealing with payment request files
131 if (parent)
132 parent->installEventFilter(this);
133
134 QString name = ipcServerName();
135
136 // Clean up old socket leftover from a crash:
137 QLocalServer::removeServer(name);
138
139 if (startLocalServer)
140 {
141 uriServer = new QLocalServer(this);
142
143 if (!uriServer->listen(name)) {
144 // constructor is called early in init, so don't use "Q_EMIT message()" here
145 QMessageBox::critical(nullptr, tr("Payment request error"),
146 tr("Cannot start bitcoin: click-to-pay handler"));
147 }
148 else {
149 connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
150 }
151 }
152}
153
155
156//
157// OSX-specific way of handling bitcoin: URIs
158//
159bool PaymentServer::eventFilter(QObject *object, QEvent *event)
160{
161 if (event->type() == QEvent::FileOpen) {
162 QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
163 if (!fileEvent->file().isEmpty())
164 handleURIOrFile(fileEvent->file());
165 else if (!fileEvent->url().isEmpty())
166 handleURIOrFile(fileEvent->url().toString());
167
168 return true;
169 }
170
171 return QObject::eventFilter(object, event);
172}
173
175{
176 saveURIs = false;
177 for (const QString& s : savedPaymentRequests)
178 {
180 }
181 savedPaymentRequests.clear();
182}
183
185{
186 if (saveURIs)
187 {
188 savedPaymentRequests.insert(s);
189 return;
190 }
191
192 if (s.startsWith("bitcoin://", Qt::CaseInsensitive))
193 {
194 Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."),
196 }
197 else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
198 {
199 QUrlQuery uri((QUrl(s)));
200 // normal URI
201 {
202 SendCoinsRecipient recipient;
203 if (GUIUtil::parseBitcoinURI(s, &recipient))
204 {
205 std::string error_msg;
206 const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg);
207
208 if (!IsValidDestination(dest)) {
209 if (uri.hasQueryItem("r")) { // payment request
210 Q_EMIT message(tr("URI handling"),
211 tr("Cannot process payment request because BIP70 is not supported.\n"
212 "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
213 "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
215 }
216 Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg),
218 }
219 else
220 Q_EMIT receivedPaymentRequest(recipient);
221 }
222 else
223 Q_EMIT message(tr("URI handling"),
224 tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
226
227 return;
228 }
229 }
230
231 if (QFile::exists(s)) // payment request file
232 {
233 Q_EMIT message(tr("Payment request file handling"),
234 tr("Cannot process payment request because BIP70 is not supported.\n"
235 "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
236 "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
238 }
239}
240
242{
243 QLocalSocket *clientConnection = uriServer->nextPendingConnection();
244
245 while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
246 clientConnection->waitForReadyRead();
247
248 connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
249
250 QDataStream in(clientConnection);
251 in.setVersion(QDataStream::Qt_4_0);
252 if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
253 return;
254 }
255 QString msg;
256 in >> msg;
257
259}
260
262{
263 this->optionsModel = _optionsModel;
264}
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:140
ArgsManager gArgs
Definition: args.cpp:42
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:234
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:43
static bool ipcSendCommandLine()
void setOptionsModel(OptionsModel *optionsModel)
PaymentServer(QObject *parent, bool startLocalServer=true)
void message(const QString &title, const QString &message, unsigned int style)
void handleURIConnection()
static void ipcParseCommandLine(int argc, char *argv[])
QLocalServer * uriServer
void receivedPaymentRequest(SendCoinsRecipient)
bool eventFilter(QObject *object, QEvent *event) override
void handleURIOrFile(const QString &s)
OptionsModel * optionsModel
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:299
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:682
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:149
static bool exists(const path &p)
Definition: fs.h:89
static QString ipcServerName()
const int BITCOIN_IPC_CONNECT_TIMEOUT
const QString BITCOIN_IPC_PREFIX("bitcoin:")
static QSet< QString > savedPaymentRequests
const char * name
Definition: rest.cpp:49