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