Bitcoin Core  24.99.0
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2021 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 <interfaces/node.h>
17 #include <key_io.h>
18 #include <node/interface_ui.h>
19 #include <policy/policy.h>
20 #include <util/system.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  saveURIs(true),
132  uriServer(nullptr),
133  optionsModel(nullptr)
134 {
135  // Install global event filter to catch QFileOpenEvents
136  // on Mac: sent when you click bitcoin: links
137  // other OSes: helpful when dealing with payment request files
138  if (parent)
139  parent->installEventFilter(this);
140 
141  QString name = ipcServerName();
142 
143  // Clean up old socket leftover from a crash:
144  QLocalServer::removeServer(name);
145 
146  if (startLocalServer)
147  {
148  uriServer = new QLocalServer(this);
149 
150  if (!uriServer->listen(name)) {
151  // constructor is called early in init, so don't use "Q_EMIT message()" here
152  QMessageBox::critical(nullptr, tr("Payment request error"),
153  tr("Cannot start bitcoin: click-to-pay handler"));
154  }
155  else {
156  connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
157  }
158  }
159 }
160 
162 
163 //
164 // OSX-specific way of handling bitcoin: URIs
165 //
166 bool PaymentServer::eventFilter(QObject *object, QEvent *event)
167 {
168  if (event->type() == QEvent::FileOpen) {
169  QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
170  if (!fileEvent->file().isEmpty())
171  handleURIOrFile(fileEvent->file());
172  else if (!fileEvent->url().isEmpty())
173  handleURIOrFile(fileEvent->url().toString());
174 
175  return true;
176  }
177 
178  return QObject::eventFilter(object, event);
179 }
180 
182 {
183  saveURIs = false;
184  for (const QString& s : savedPaymentRequests)
185  {
186  handleURIOrFile(s);
187  }
188  savedPaymentRequests.clear();
189 }
190 
191 void PaymentServer::handleURIOrFile(const QString& s)
192 {
193  if (saveURIs)
194  {
195  savedPaymentRequests.insert(s);
196  return;
197  }
198 
199  if (s.startsWith("bitcoin://", Qt::CaseInsensitive))
200  {
201  Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."),
203  }
204  else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
205  {
206  QUrlQuery uri((QUrl(s)));
207  // normal URI
208  {
209  SendCoinsRecipient recipient;
210  if (GUIUtil::parseBitcoinURI(s, &recipient))
211  {
212  std::string error_msg;
213  const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg);
214 
215  if (!IsValidDestination(dest)) {
216  if (uri.hasQueryItem("r")) { // payment request
217  Q_EMIT message(tr("URI handling"),
218  tr("Cannot process payment request because BIP70 is not supported.\n"
219  "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
220  "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
222  }
223  Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg),
225  }
226  else
227  Q_EMIT receivedPaymentRequest(recipient);
228  }
229  else
230  Q_EMIT message(tr("URI handling"),
231  tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
233 
234  return;
235  }
236  }
237 
238  if (QFile::exists(s)) // payment request file
239  {
240  Q_EMIT message(tr("Payment request file handling"),
241  tr("Cannot process payment request because BIP70 is not supported.\n"
242  "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
243  "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
245  }
246 }
247 
249 {
250  QLocalSocket *clientConnection = uriServer->nextPendingConnection();
251 
252  while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
253  clientConnection->waitForReadyRead();
254 
255  connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
256 
257  QDataStream in(clientConnection);
258  in.setVersion(QDataStream::Qt_4_0);
259  if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
260  return;
261  }
262  QString msg;
263  in >> msg;
264 
265  handleURIOrFile(msg);
266 }
267 
269 {
270  this->optionsModel = _optionsModel;
271 }
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:303
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:281
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:665
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:140
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:46
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
ArgsManager gArgs
Definition: system.cpp:86