Changeset View
Changeset View
Standalone View
Standalone View
src/qt/paymentserver.cpp
Show First 20 Lines • Show All 195 Lines • ▼ Show 20 Lines | #endif | ||||
// Issues to consider: | // Issues to consider: | ||||
// performance (start a thread to fetch in background?) | // performance (start a thread to fetch in background?) | ||||
// privacy (fetch through tor/proxy so IP address isn't revealed) | // privacy (fetch through tor/proxy so IP address isn't revealed) | ||||
// would it be easier to just use a compiled-in blacklist? | // would it be easier to just use a compiled-in blacklist? | ||||
// or use Qt's blacklist? | // or use Qt's blacklist? | ||||
// "certificate stapling" with server-side caching is more efficient | // "certificate stapling" with server-side caching is more efficient | ||||
} | } | ||||
static std::string ipcParseURI(const QString &arg, const CChainParams ¶ms, | |||||
bool useCashAddr) { | |||||
const QString scheme = GUIUtil::bitcoinURIScheme(params, useCashAddr); | |||||
if (!arg.startsWith(scheme + ":", Qt::CaseInsensitive)) { | |||||
return {}; | |||||
} | |||||
SendCoinsRecipient r; | |||||
if (!GUIUtil::parseBitcoinURI(scheme, arg, &r)) { | |||||
return {}; | |||||
} | |||||
return r.address.toStdString(); | |||||
} | |||||
static bool ipcCanParseCashAddrURI(const QString &arg, | |||||
const std::string &network) { | |||||
const CChainParams ¶ms(Params(network)); | |||||
std::string addr = ipcParseURI(arg, params, true); | |||||
return IsValidDestinationString(addr, params); | |||||
} | |||||
static bool ipcCanParseLegacyURI(const QString &arg, | |||||
const std::string &network) { | |||||
const CChainParams ¶ms(Params(network)); | |||||
std::string addr = ipcParseURI(arg, params, false); | |||||
return IsValidDestinationString(addr, params); | |||||
} | |||||
// | // | ||||
// Sending to the server is done synchronously, at startup. | // Sending to the server is done synchronously, at startup. | ||||
// If the server isn't already running, startup continues, and the items in | // If the server isn't already running, startup continues, and the items in | ||||
// savedPaymentRequest will be handled when uiReady() is called. | // savedPaymentRequest will be handled when uiReady() is called. | ||||
// | // | ||||
// Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT | // Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT | ||||
// message()", but "QMessageBox::"! | // message()", but "QMessageBox::"! | ||||
// | // | ||||
void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) { | void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) { | ||||
std::array<const std::string *, 3> networks = {&CBaseChainParams::MAIN, | |||||
&CBaseChainParams::TESTNET, | |||||
&CBaseChainParams::REGTEST}; | |||||
const std::string *chosenNetwork = nullptr; | |||||
for (int i = 1; i < argc; i++) { | for (int i = 1; i < argc; i++) { | ||||
QString arg(argv[i]); | QString arg(argv[i]); | ||||
if (arg.startsWith("-")) { | if (arg.startsWith("-")) { | ||||
continue; | continue; | ||||
} | } | ||||
QString scheme = GUIUtil::bitcoinURIScheme(Params(), false); | const std::string *itemNetwork = nullptr; | ||||
// If the bitcoincash: URI contains a payment request, we are not able | |||||
// to detect the network as that would require fetching and parsing the | |||||
// payment request. That means clicking such an URI which contains a | |||||
// testnet payment request will start a mainnet instance and throw a | |||||
// "wrong network" error. | |||||
if (arg.startsWith(scheme + ":", | |||||
Qt::CaseInsensitive)) // bitcoincash: URI | |||||
{ | |||||
savedPaymentRequests.append(arg); | |||||
SendCoinsRecipient r; | // Try to parse as a URI | ||||
if (GUIUtil::parseBitcoinURI(scheme, arg, &r) && | for (auto net : networks) { | ||||
!r.address.isEmpty()) { | if (ipcCanParseCashAddrURI(arg, *net)) { | ||||
if (IsValidDestinationString(r.address.toStdString(), | itemNetwork = net; | ||||
Params(CBaseChainParams::MAIN))) { | break; | ||||
SelectParams(CBaseChainParams::MAIN); | } | ||||
} else if (IsValidDestinationString( | |||||
r.address.toStdString(), | if (ipcCanParseLegacyURI(arg, *net)) { | ||||
Params(CBaseChainParams::TESTNET))) { | itemNetwork = net; | ||||
SelectParams(CBaseChainParams::TESTNET); | break; | ||||
} | } | ||||
} | } | ||||
} else if (QFile::exists(arg)) { | |||||
// Filename | |||||
savedPaymentRequests.append(arg); | |||||
if (!itemNetwork && QFile::exists(arg)) { | |||||
// Filename | |||||
PaymentRequestPlus request; | PaymentRequestPlus request; | ||||
if (readPaymentRequestFromFile(arg, request)) { | if (readPaymentRequestFromFile(arg, request)) { | ||||
if (request.getDetails().network() == "main") { | for (auto net : networks) { | ||||
SelectParams(CBaseChainParams::MAIN); | if (*net == request.getDetails().network()) { | ||||
} else if (request.getDetails().network() == "test") { | itemNetwork = net; | ||||
SelectParams(CBaseChainParams::TESTNET); | |||||
} | } | ||||
} | } | ||||
} else { | } | ||||
} | |||||
if (itemNetwork == nullptr) { | |||||
// Printing to debug.log is about the best we can do here, the GUI | // Printing to debug.log is about the best we can do here, the GUI | ||||
// hasn't started yet so we can't pop up a message box. | // hasn't started yet so we can't pop up a message box. | ||||
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | ||||
"file does not exist: " | "file or URI does not exist or is invalid: " | ||||
<< arg; | << arg; | ||||
continue; | |||||
} | } | ||||
if (chosenNetwork && chosenNetwork != itemNetwork) { | |||||
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " | |||||
"from network " | |||||
<< QString(itemNetwork->c_str()) | |||||
<< " does not match already chosen network " | |||||
<< QString(chosenNetwork->c_str()); | |||||
continue; | |||||
} | |||||
savedPaymentRequests.append(arg); | |||||
chosenNetwork = itemNetwork; | |||||
} | |||||
if (chosenNetwork) { | |||||
SelectParams(*chosenNetwork); | |||||
} | } | ||||
} | } | ||||
// | // | ||||
// Sending to the server is done synchronously, at startup. | // Sending to the server is done synchronously, at startup. | ||||
// If the server isn't already running, startup continues, and the items in | // If the server isn't already running, startup continues, and the items in | ||||
// savedPaymentRequest will be handled when uiReady() is called. | // savedPaymentRequest will be handled when uiReady() is called. | ||||
// | // | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | void PaymentServer::uiReady() { | ||||
saveURIs = false; | saveURIs = false; | ||||
for (const QString &s : savedPaymentRequests) { | for (const QString &s : savedPaymentRequests) { | ||||
handleURIOrFile(s); | handleURIOrFile(s); | ||||
} | } | ||||
savedPaymentRequests.clear(); | savedPaymentRequests.clear(); | ||||
} | } | ||||
void PaymentServer::handleURIOrFile(const QString &s) { | bool PaymentServer::handleURI(const QString &scheme, const QString &s) { | ||||
if (saveURIs) { | if (!s.startsWith(scheme + ":", Qt::CaseInsensitive)) { | ||||
savedPaymentRequests.append(s); | return false; | ||||
return; | |||||
} | } | ||||
// bitcoincash: URI | |||||
QString scheme = GUIUtil::bitcoinURIScheme(Params(), false); | |||||
if (s.startsWith(scheme + ":", Qt::CaseInsensitive)) { | |||||
#if QT_VERSION < 0x050000 | #if QT_VERSION < 0x050000 | ||||
QUrl uri(s); | QUrl uri(s); | ||||
#else | #else | ||||
QUrlQuery uri((QUrl(s))); | QUrlQuery uri((QUrl(s))); | ||||
#endif | #endif | ||||
if (uri.hasQueryItem("r")) { | if (uri.hasQueryItem("r")) { | ||||
// payment request URI | // payment request URI | ||||
QByteArray temp; | QByteArray temp; | ||||
temp.append(uri.queryItemValue("r")); | temp.append(uri.queryItemValue("r")); | ||||
QString decoded = QUrl::fromPercentEncoding(temp); | QString decoded = QUrl::fromPercentEncoding(temp); | ||||
QUrl fetchUrl(decoded, QUrl::StrictMode); | QUrl fetchUrl(decoded, QUrl::StrictMode); | ||||
if (fetchUrl.isValid()) { | if (fetchUrl.isValid()) { | ||||
qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" | qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" | ||||
<< fetchUrl << ")"; | << fetchUrl << ")"; | ||||
fetchRequest(fetchUrl); | fetchRequest(fetchUrl); | ||||
} else { | } else { | ||||
qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " | qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " | ||||
<< fetchUrl; | << fetchUrl; | ||||
Q_EMIT message(tr("URI handling"), | Q_EMIT message(tr("URI handling"), | ||||
tr("Payment request fetch URL is invalid: %1") | tr("Payment request fetch URL is invalid: %1") | ||||
.arg(fetchUrl.toString()), | .arg(fetchUrl.toString()), | ||||
CClientUIInterface::ICON_WARNING); | CClientUIInterface::ICON_WARNING); | ||||
} | } | ||||
return; | return true; | ||||
} else { | } | ||||
// normal URI | // normal URI | ||||
SendCoinsRecipient recipient; | SendCoinsRecipient recipient; | ||||
if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) { | if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) { | ||||
if (!IsValidDestinationString( | if (!IsValidDestinationString(recipient.address.toStdString())) { | ||||
recipient.address.toStdString())) { | |||||
Q_EMIT message( | Q_EMIT message( | ||||
tr("URI handling"), | tr("URI handling"), | ||||
tr("Invalid payment address %1").arg(recipient.address), | tr("Invalid payment address %1").arg(recipient.address), | ||||
CClientUIInterface::MSG_ERROR); | CClientUIInterface::MSG_ERROR); | ||||
} else { | } else { | ||||
Q_EMIT receivedPaymentRequest(recipient); | Q_EMIT receivedPaymentRequest(recipient); | ||||
} | } | ||||
} else { | } else { | ||||
Q_EMIT message( | Q_EMIT message( | ||||
tr("URI handling"), | tr("URI handling"), | ||||
tr("URI cannot be parsed! This can be caused by an invalid " | tr("URI cannot be parsed! This can be caused by an invalid " | ||||
"Bitcoin address or malformed URI parameters."), | "Bitcoin address or malformed URI parameters."), | ||||
CClientUIInterface::ICON_WARNING); | CClientUIInterface::ICON_WARNING); | ||||
} | } | ||||
return true; | |||||
} | |||||
void PaymentServer::handleURIOrFile(const QString &s) { | |||||
if (saveURIs) { | |||||
savedPaymentRequests.append(s); | |||||
return; | |||||
} | |||||
// bitcoincash: CashAddr URI | |||||
QString schemeCash = GUIUtil::bitcoinURIScheme(Params(), true); | |||||
if (handleURI(schemeCash, s)) { | |||||
return; | return; | ||||
} | } | ||||
// bitcoincash: Legacy URI | |||||
QString schemeLegacy = GUIUtil::bitcoinURIScheme(Params(), false); | |||||
if (handleURI(schemeLegacy, s)) { | |||||
return; | |||||
} | } | ||||
// payment request file | // payment request file | ||||
if (QFile::exists(s)) { | if (QFile::exists(s)) { | ||||
PaymentRequestPlus request; | PaymentRequestPlus request; | ||||
SendCoinsRecipient recipient; | SendCoinsRecipient recipient; | ||||
if (!readPaymentRequestFromFile(s, request)) { | if (!readPaymentRequestFromFile(s, request)) { | ||||
Q_EMIT message(tr("Payment request file handling"), | Q_EMIT message(tr("Payment request file handling"), | ||||
▲ Show 20 Lines • Show All 377 Lines • Show Last 20 Lines |