dogecoin/src/qt/paymentserver.cpp
Alan Westbrook f62bb24297 Fix some URI handling weirdness in Qt
remove whack paths like file:dogecoin:
change out dogecoin:/D for dogecoin:D
Add a local URI handler so it doesn’t have to go through the system if
we try a local url.
2014-02-06 22:23:46 -08:00

176 lines
4.7 KiB
C++

// 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>
#include <QDesktopServices>
#if QT_VERSION < 0x050000
#include <QUrl>
#endif
using namespace boost;
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
const QString BITCOIN_IPC_PREFIX("dogecoin:");
//
// 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 dogecoin: click-to-pay handler");
else
connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
QDesktopServices::setUrlHandler("dogecoin", this, SLOT(handleDogeURI));
}
void PaymentServer::handleDogeURI(const QUrl &url)
{
emit receivedURI(url.toString());
}
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())
{
// WTF Qt?
QString url = fileEvent->url().toString();
url.replace("file:dogecoin", "dogecoin");
url.replace(":/D", ":D");
if (saveURIs) // Before main window is ready:
savedPaymentRequests.append(url);
else
emit receivedURI(url);
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);
}