diff options
Diffstat (limited to 'src/qt/paymentserver.cpp')
-rw-r--r-- | src/qt/paymentserver.cpp | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp new file mode 100644 index 0000000000..0b0bce55bb --- /dev/null +++ b/src/qt/paymentserver.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <QApplication> + +#include "paymentserver.h" + +#include "guiconstants.h" +#include "ui_interface.h" +#include "util.h" + +#include <QByteArray> +#include <QDataStream> +#include <QDebug> +#include <QFileOpenEvent> +#include <QHash> +#include <QLocalServer> +#include <QLocalSocket> +#include <QStringList> +#if QT_VERSION < 0x050000 +#include <QUrl> +#endif + +using namespace boost; + +const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds +const QString BITCOIN_IPC_PREFIX("bitcoin:"); + +// +// Create a name that is unique for: +// testnet / non-testnet +// data directory +// +static QString ipcServerName() +{ + QString name("BitcoinQt"); + + // Append a simple hash of the datadir + // Note that GetDataDir(true) returns a different path + // for -testnet versus main net + QString ddir(GetDataDir(true).string().c_str()); + name.append(QString::number(qHash(ddir))); + + return name; +} + +// +// This stores payment requests received before +// the main GUI window is up and ready to ask the user +// to send payment. +// +static QStringList savedPaymentRequests; + +// +// Sending to the server is done synchronously, at startup. +// If the server isn't already running, startup continues, +// and the items in savedPaymentRequest will be handled +// when uiReady() is called. +// +bool PaymentServer::ipcSendCommandLine() +{ + bool fResult = false; + + const QStringList& args = qApp->arguments(); + for (int i = 1; i < args.size(); i++) + { + if (!args[i].startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) + continue; + savedPaymentRequests.append(args[i]); + } + + foreach (const QString& arg, savedPaymentRequests) + { + QLocalSocket* socket = new QLocalSocket(); + socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); + if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) + return false; + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + out << arg; + out.device()->seek(0); + socket->write(block); + socket->flush(); + + socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT); + socket->disconnectFromServer(); + delete socket; + fResult = true; + } + return fResult; +} + +PaymentServer::PaymentServer(QApplication* parent) : QObject(parent), saveURIs(true) +{ + // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + parent->installEventFilter(this); + + QString name = ipcServerName(); + + // Clean up old socket leftover from a crash: + QLocalServer::removeServer(name); + + uriServer = new QLocalServer(this); + + if (!uriServer->listen(name)) + qDebug() << tr("Cannot start bitcoin: click-to-pay handler"); + else + connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); +} + +bool PaymentServer::eventFilter(QObject *object, QEvent *event) +{ + // clicking on bitcoin: URLs creates FileOpen events on the Mac: + if (event->type() == QEvent::FileOpen) + { + QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); + if (!fileEvent->url().isEmpty()) + { + if (saveURIs) // Before main window is ready: + savedPaymentRequests.append(fileEvent->url().toString()); + else + emit receivedURI(fileEvent->url().toString()); + return true; + } + } + return false; +} + +void PaymentServer::uiReady() +{ + saveURIs = false; + foreach (const QString& s, savedPaymentRequests) + emit receivedURI(s); + savedPaymentRequests.clear(); +} + +void PaymentServer::handleURIConnection() +{ + QLocalSocket *clientConnection = uriServer->nextPendingConnection(); + + while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) + clientConnection->waitForReadyRead(); + + connect(clientConnection, SIGNAL(disconnected()), + clientConnection, SLOT(deleteLater())); + + QDataStream in(clientConnection); + in.setVersion(QDataStream::Qt_4_0); + if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { + return; + } + QString message; + in >> message; + + if (saveURIs) + savedPaymentRequests.append(message); + else + emit receivedURI(message); +} |