// 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 #include "paymentserver.h" #include "guiconstants.h" #include "ui_interface.h" #include "util.h" #include #include #include #include #include #include #include #include #if QT_VERSION < 0x050000 #include #endif using namespace boost; const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("litecoin:"); // // 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 litecoin: 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(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); }