You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.4 KiB
162 lines
4.4 KiB
// 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); |
|
}
|
|
|