Bitcoin Core  22.99.0
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2020 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/ui_interface.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("-"))
82  continue;
83 
84  // If the bitcoin: URI contains a payment request, we are not able to detect the
85  // network as that would require fetching and parsing the payment request.
86  // That means clicking such an URI which contains a testnet payment request
87  // will start a mainnet instance and throw a "wrong network" error.
88  if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
89  {
90  if (savedPaymentRequests.contains(arg)) continue;
91  savedPaymentRequests.insert(arg);
92 
94  if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
95  {
96  auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);
97 
98  if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
100  } else {
102  if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
104  }
105  }
106  }
107  }
108  }
109 }
110 
111 //
112 // Sending to the server is done synchronously, at startup.
113 // If the server isn't already running, startup continues,
114 // and the items in savedPaymentRequest will be handled
115 // when uiReady() is called.
116 //
118 {
119  bool fResult = false;
120  for (const QString& r : savedPaymentRequests)
121  {
122  QLocalSocket* socket = new QLocalSocket();
123  socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
124  if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
125  {
126  delete socket;
127  socket = nullptr;
128  return false;
129  }
130 
131  QByteArray block;
132  QDataStream out(&block, QIODevice::WriteOnly);
133  out.setVersion(QDataStream::Qt_4_0);
134  out << r;
135  out.device()->seek(0);
136 
137  socket->write(block);
138  socket->flush();
139  socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
140  socket->disconnectFromServer();
141 
142  delete socket;
143  socket = nullptr;
144  fResult = true;
145  }
146 
147  return fResult;
148 }
149 
150 PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
151  QObject(parent),
152  saveURIs(true),
153  uriServer(nullptr),
154  optionsModel(nullptr)
155 {
156  // Install global event filter to catch QFileOpenEvents
157  // on Mac: sent when you click bitcoin: links
158  // other OSes: helpful when dealing with payment request files
159  if (parent)
160  parent->installEventFilter(this);
161 
162  QString name = ipcServerName();
163 
164  // Clean up old socket leftover from a crash:
165  QLocalServer::removeServer(name);
166 
167  if (startLocalServer)
168  {
169  uriServer = new QLocalServer(this);
170 
171  if (!uriServer->listen(name)) {
172  // constructor is called early in init, so don't use "Q_EMIT message()" here
173  QMessageBox::critical(nullptr, tr("Payment request error"),
174  tr("Cannot start bitcoin: click-to-pay handler"));
175  }
176  else {
177  connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
178  }
179  }
180 }
181 
183 {
184 }
185 
186 //
187 // OSX-specific way of handling bitcoin: URIs
188 //
189 bool PaymentServer::eventFilter(QObject *object, QEvent *event)
190 {
191  if (event->type() == QEvent::FileOpen) {
192  QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
193  if (!fileEvent->file().isEmpty())
194  handleURIOrFile(fileEvent->file());
195  else if (!fileEvent->url().isEmpty())
196  handleURIOrFile(fileEvent->url().toString());
197 
198  return true;
199  }
200 
201  return QObject::eventFilter(object, event);
202 }
203 
205 {
206  saveURIs = false;
207  for (const QString& s : savedPaymentRequests)
208  {
209  handleURIOrFile(s);
210  }
211  savedPaymentRequests.clear();
212 }
213 
214 void PaymentServer::handleURIOrFile(const QString& s)
215 {
216  if (saveURIs)
217  {
218  savedPaymentRequests.insert(s);
219  return;
220  }
221 
222  if (s.startsWith("bitcoin://", Qt::CaseInsensitive))
223  {
224  Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."),
226  }
227  else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
228  {
229  QUrlQuery uri((QUrl(s)));
230  // normal URI
231  {
232  SendCoinsRecipient recipient;
233  if (GUIUtil::parseBitcoinURI(s, &recipient))
234  {
235  std::string error_msg;
236  const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg);
237 
238  if (!IsValidDestination(dest)) {
239  if (uri.hasQueryItem("r")) { // payment request
240  Q_EMIT message(tr("URI 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  Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg),
248  }
249  else
250  Q_EMIT receivedPaymentRequest(recipient);
251  }
252  else
253  Q_EMIT message(tr("URI handling"),
254  tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
256 
257  return;
258  }
259  }
260 
261  if (QFile::exists(s)) // payment request file
262  {
263  Q_EMIT message(tr("Payment request file handling"),
264  tr("Cannot process payment request because BIP70 is not supported.\n"
265  "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
266  "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
268  }
269 }
270 
272 {
273  QLocalSocket *clientConnection = uriServer->nextPendingConnection();
274 
275  while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
276  clientConnection->waitForReadyRead();
277 
278  connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
279 
280  QDataStream in(clientConnection);
281  in.setVersion(QDataStream::Qt_4_0);
282  if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
283  return;
284  }
285  QString msg;
286  in >> msg;
287 
288  handleURIOrFile(msg);
289 }
290 
292 {
293  this->optionsModel = _optionsModel;
294 }
policy.h
GUIUtil::boostPathToQString
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:652
PaymentServer::optionsModel
OptionsModel * optionsModel
Definition: paymentserver.h:106
wallet.h
ArgsManager::GetDataDirNet
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:282
key_io.h
PaymentServer::saveURIs
bool saveURIs
Definition: paymentserver.h:104
PaymentServer::message
void message(const QString &title, const QString &message, unsigned int style)
BITCOIN_IPC_PREFIX
const QString BITCOIN_IPC_PREFIX("bitcoin:")
CClientUIInterface::ICON_WARNING
@ ICON_WARNING
Definition: ui_interface.h:31
PaymentServer::~PaymentServer
~PaymentServer()
Definition: paymentserver.cpp:182
PaymentServer::setOptionsModel
void setOptionsModel(OptionsModel *optionsModel)
Definition: paymentserver.cpp:291
CBaseChainParams::TESTNET
static const std::string TESTNET
Definition: chainparamsbase.h:23
PaymentServer::handleURIConnection
void handleURIConnection()
Definition: paymentserver.cpp:271
GUIUtil::parseBitcoinURI
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:131
PaymentServer::ipcParseCommandLine
static void ipcParseCommandLine(int argc, char *argv[])
Definition: paymentserver.cpp:76
bitcoin-config.h
chainparams.h
PaymentServer::uiReady
void uiReady()
Definition: paymentserver.cpp:204
PaymentServer::ipcSendCommandLine
static bool ipcSendCommandLine()
Definition: paymentserver.cpp:117
SendCoinsRecipient
Definition: sendcoinsrecipient.h:19
OptionsModel
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:39
ipcServerName
static QString ipcServerName()
Definition: paymentserver.cpp:47
CTxDestination
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
IsValidDestination
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:373
PaymentServer::receivedPaymentRequest
void receivedPaymentRequest(SendCoinsRecipient)
node.h
guiutil.h
SelectParams
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given chain name.
Definition: chainparams.cpp:555
DecodeDestination
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg)
Definition: key_io.cpp:261
gArgs
ArgsManager gArgs
Definition: system.cpp:84
savedPaymentRequests
static QSet< QString > savedPaymentRequests
Definition: paymentserver.cpp:65
CBaseChainParams::MAIN
static const std::string MAIN
Chain name strings.
Definition: chainparamsbase.h:22
name
const char * name
Definition: rest.cpp:43
ui_interface.h
system.h
CClientUIInterface::MSG_ERROR
@ MSG_ERROR
Definition: ui_interface.h:68
SendCoinsRecipient::address
QString address
Definition: sendcoinsrecipient.h:31
bitcoinunits.h
IsValidDestinationString
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: key_io.cpp:272
PaymentServer::uriServer
QLocalServer * uriServer
Definition: paymentserver.h:105
PaymentServer::handleURIOrFile
void handleURIOrFile(const QString &s)
Definition: paymentserver.cpp:214
optionsmodel.h
BITCOIN_IPC_CONNECT_TIMEOUT
const int BITCOIN_IPC_CONNECT_TIMEOUT
Definition: paymentserver.cpp:39
PaymentServer::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Definition: paymentserver.cpp:189
PaymentServer::PaymentServer
PaymentServer(QObject *parent, bool startLocalServer=true)
Definition: paymentserver.cpp:150
CreateChainParams
std::unique_ptr< const CChainParams > CreateChainParams(const ArgsManager &args, const std::string &chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
Definition: chainparams.cpp:541
paymentserver.h