PRCYCoin  2.0.0.7rc1
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Copyright (c) 2014-2015 The Dash developers
3 // Copyright (c) 2015-2018 The PIVX developers
4 // Copyright (c) 2018-2020 The DAPS Project developers
5 // Distributed under the MIT software license, see the accompanying
6 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 
8 #include "paymentserver.h"
9 
10 #include "bitcoinunits.h"
11 #include "guiutil.h"
12 #include "optionsmodel.h"
13 
14 #include "base58.h"
15 #include "chainparams.h"
16 #include "guiinterface.h"
17 #include "util.h"
18 #include "wallet/wallet.h"
19 
20 #include <cstdlib>
21 
22 #include <openssl/x509_vfy.h>
23 
24 #include <QApplication>
25 #include <QByteArray>
26 #include <QDataStream>
27 #include <QDateTime>
28 #include <QDebug>
29 #include <QFile>
30 #include <QFileOpenEvent>
31 #include <QHash>
32 #include <QList>
33 #include <QLocalServer>
34 #include <QLocalSocket>
35 #include <QStringList>
36 #include <QUrlQuery>
37 
38 
39 const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
40 const QString BITCOIN_IPC_PREFIX("prcycoin:");
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("PRCYQt");
50 
51  // Append a simple hash of the datadir
52  // Note that GetDataDir(true) returns a different path
53  // for -testnet versus main net
54  QString ddir(QString::fromStdString(GetDataDir(true).string()));
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 QList<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  QString arg(argv[i]);
80  if (arg.startsWith("-"))
81  continue;
82 
83  // If the prcycoin: URI contains a payment request, we are not able to detect the
84  // network as that would require fetching and parsing the payment request.
85  // That means clicking such an URI which contains a testnet payment request
86  // will start a mainnet instance and throw a "wrong network" error.
87  if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // prcycoin: URI
88  {
89  savedPaymentRequests.append(arg);
90 
92  if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) {
93  CBitcoinAddress address(r.address.toStdString());
94 
95  if (address.IsValid(Params(CBaseChainParams::MAIN))) {
97  } else if (address.IsValid(Params(CBaseChainParams::TESTNET))) {
99  }
100  }
101  }
102  }
103 }
104 
105 //
106 // Sending to the server is done synchronously, at startup.
107 // If the server isn't already running, startup continues,
108 // and the items in savedPaymentRequest will be handled
109 // when uiReady() is called.
110 //
112 {
113  bool fResult = false;
114  Q_FOREACH (const QString& r, savedPaymentRequests) {
115  QLocalSocket* socket = new QLocalSocket();
116  socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
117  if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) {
118  delete socket;
119  socket = NULL;
120  return false;
121  }
122 
123  QByteArray block;
124  QDataStream out(&block, QIODevice::WriteOnly);
125  out.setVersion(QDataStream::Qt_4_0);
126  out << r;
127  out.device()->seek(0);
128 
129  socket->write(block);
130  socket->flush();
131  socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
132  socket->disconnectFromServer();
133 
134  delete socket;
135  socket = NULL;
136  fResult = true;
137  }
138 
139  return fResult;
140 }
141 
142 PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : QObject(parent),
143  saveURIs(true),
144  uriServer(0),
145  optionsModel(0)
146 {
147  // Install global event filter to catch QFileOpenEvents
148  // on Mac: sent when you click prcycoin: links
149  // other OSes: helpful when dealing with payment request files (in the future)
150  if (parent)
151  parent->installEventFilter(this);
152 
153  QString name = ipcServerName();
154 
155  // Clean up old socket leftover from a crash:
156  QLocalServer::removeServer(name);
157 
158  if (startLocalServer) {
159  uriServer = new QLocalServer(this);
160 
161  if (!uriServer->listen(name)) {
162  // constructor is called early in init, so don't use "Q_EMIT message()" here
163  QMessageBox::critical(0, tr("Payment request error"),
164  tr("Cannot start prcycoin: click-to-pay handler"));
165  } else {
166  connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
167  }
168  }
169 }
170 
172 {
173 }
174 
175 //
176 // OSX-specific way of handling prcycoin: URIs
177 //
178 bool PaymentServer::eventFilter(QObject* object, QEvent* event)
179 {
180  // clicking on prcycoin: URIs creates FileOpen events on the Mac
181  if (event->type() == QEvent::FileOpen) {
182  QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
183  if (!fileEvent->file().isEmpty())
184  handleURIOrFile(fileEvent->file());
185  else if (!fileEvent->url().isEmpty())
186  handleURIOrFile(fileEvent->url().toString());
187 
188  return true;
189  }
190 
191  return QObject::eventFilter(object, event);
192 }
193 
195 {
196  saveURIs = false;
197  Q_FOREACH (const QString& s, savedPaymentRequests) {
198  handleURIOrFile(s);
199  }
200  savedPaymentRequests.clear();
201 }
202 
203 void PaymentServer::handleURIOrFile(const QString& s)
204 {
205  if (saveURIs) {
206  savedPaymentRequests.append(s);
207  return;
208  }
209 
210  if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // prcycoin: URI
211  {
212  QUrlQuery uri((QUrl(s)));
213  // normal URI
214  {
215  SendCoinsRecipient recipient;
216  if (GUIUtil::parseBitcoinURI(s, &recipient)) {
217  CBitcoinAddress address(recipient.address.toStdString());
218  if (!address.IsValid()) {
219  Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
221  } else
222  Q_EMIT receivedPaymentRequest(recipient);
223  } else
224  Q_EMIT message(tr("URI handling"),
225  tr("URI cannot be parsed! This can be caused by an invalid PRCY address or malformed URI parameters."),
227 
228  return;
229  }
230  }
231 }
232 
234 {
235  QLocalSocket* clientConnection = uriServer->nextPendingConnection();
236 
237  while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
238  clientConnection->waitForReadyRead();
239 
240  connect(clientConnection, SIGNAL(disconnected()),
241  clientConnection, SLOT(deleteLater()));
242 
243  QDataStream in(clientConnection);
244  in.setVersion(QDataStream::Qt_4_0);
245  if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
246  return;
247  }
248  QString msg;
249  in >> msg;
250 
251  handleURIOrFile(msg);
252 }
253 
255 {
256  this->optionsModel = optionsModel;
257 }
BITCOIN_IPC_PREFIX
const QString BITCOIN_IPC_PREFIX("prcycoin:")
PaymentServer::optionsModel
OptionsModel * optionsModel
Definition: paymentserver.h:100
PaymentServer::saveURIs
bool saveURIs
Definition: paymentserver.h:98
PaymentServer::message
void message(const QString &title, const QString &message, unsigned int style)
CBitcoinAddress
base58-encoded PRCY addresses.
Definition: base58.h:109
CClientUIInterface::ICON_WARNING
@ ICON_WARNING
Definition: guiinterface.h:34
PaymentServer::~PaymentServer
~PaymentServer()
Definition: paymentserver.cpp:171
PaymentServer::setOptionsModel
void setOptionsModel(OptionsModel *optionsModel)
Definition: paymentserver.cpp:254
wallet.h
PaymentServer::handleURIConnection
void handleURIConnection()
Definition: paymentserver.cpp:233
PaymentServer::eventFilter
bool eventFilter(QObject *object, QEvent *event)
Definition: paymentserver.cpp:178
GUIUtil::parseBitcoinURI
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:104
PaymentServer::ipcParseCommandLine
static void ipcParseCommandLine(int argc, char *argv[])
Definition: paymentserver.cpp:76
chainparams.h
guiinterface.h
PaymentServer::uiReady
void uiReady()
Definition: paymentserver.cpp:194
r
void const uint64_t uint64_t * r
Definition: field_5x52_asm_impl.h:10
PaymentServer::ipcSendCommandLine
static bool ipcSendCommandLine()
Definition: paymentserver.cpp:111
SendCoinsRecipient
Definition: walletmodel.h:46
OptionsModel
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:18
PaymentServer::receivedPaymentRequest
void receivedPaymentRequest(SendCoinsRecipient)
CBaseChainParams::MAIN
@ MAIN
Definition: chainparamsbase.h:19
guiutil.h
CBaseChainParams::TESTNET
@ TESTNET
Definition: chainparamsbase.h:20
SelectParams
void SelectParams(CBaseChainParams::Network network)
Sets the params returned by Params() to those for the given network.
Definition: chainparams.cpp:484
name
const char * name
Definition: rest.cpp:34
CClientUIInterface::MSG_ERROR
@ MSG_ERROR
Definition: guiinterface.h:76
SendCoinsRecipient::address
QString address
Definition: walletmodel.h:57
GetDataDir
const fs::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:349
Params
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:463
bitcoinunits.h
base58.h
PaymentServer::uriServer
QLocalServer * uriServer
Definition: paymentserver.h:99
PaymentServer::handleURIOrFile
void handleURIOrFile(const QString &s)
Definition: paymentserver.cpp:203
optionsmodel.h
BITCOIN_IPC_CONNECT_TIMEOUT
const int BITCOIN_IPC_CONNECT_TIMEOUT
Definition: paymentserver.cpp:39
CBitcoinAddress::IsValid
bool IsValid() const
Definition: base58.cpp:254
PaymentServer::PaymentServer
PaymentServer(QObject *parent, bool startLocalServer=true)
Definition: paymentserver.cpp:142
paymentserver.h