Bitcoin Core  27.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 
35 const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
36 const QString BITCOIN_IPC_PREFIX("bitcoin:");
37 
38 //
39 // Create a name that is unique for:
40 // testnet / non-testnet
41 // data directory
42 //
43 static 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 
61 static 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 //
72 void 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 
125 PaymentServer::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 //
159 bool 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  {
179  handleURIOrFile(s);
180  }
181  savedPaymentRequests.clear();
182 }
183 
184 void PaymentServer::handleURIOrFile(const QString& s)
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, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:131
ArgsManager gArgs
Definition: args.cpp:41
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:232
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:292
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:674
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:148
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:50