IPC and custom protocol handler for monero://

This commit is contained in:
dsc
2019-03-22 21:02:08 +01:00
committed by xmrdsc
parent ff6ce6294b
commit 18f2accc7f
13 changed files with 354 additions and 15 deletions

106
src/qt/ipc.cpp Normal file
View File

@@ -0,0 +1,106 @@
#include <QCoreApplication>
#include <QLocalSocket>
#include <QLocalServer>
#include <QtNetwork>
#include <QDebug>
#include "ipc.h"
#include "utils.h"
// Start listening for incoming IPC commands on UDS (Unix) or named pipe (Windows)
void IPC::bind(){
QString path = QString(this->m_socketFile.absoluteFilePath());
qDebug() << path;
this->m_server = new QLocalServer(this);
this->m_server->setSocketOptions(QLocalServer::UserAccessOption);
bool restarted = false;
if(!this->m_server->listen(path)){
// On Unix if the server crashes without closing listen will fail with AddressInUseError.
// To create a new server the file should be removed. On Windows two local servers can listen
// to the same pipe at the same time, but any connections will go to one of the server.
#ifdef Q_OS_UNIX
qDebug() << QString("Unable to start IPC server in \"%1\": \"%2\". Retrying.").arg(path).arg(this->m_server->errorString());
if(this->m_socketFile.exists()){
QFile file(path);
file.remove();
if(this->m_server->listen(path)){
restarted = true;
}
}
#endif
if(!restarted)
qDebug() << QString("Unable to start IPC server in \"%1\": \"%2\".").arg(path).arg(this->m_server->errorString());
}
connect(this->m_server, &QLocalServer::newConnection, this, &IPC::handleConnection);
}
// Process incoming IPC command. First check if monero-wallet-gui is
// already running. If it is, send it to that instance instead, if not,
// queue the command for later use inside our QML engine. Returns true
// when queued, false if sent to another instance, at which point we can
// kill the current process.
bool IPC::saveCommand(QString cmdString){
qDebug() << QString("saveCommand called: %1").arg(cmdString);
QLocalSocket ls;
QByteArray buffer;
buffer = buffer.append(cmdString);
QString socketFilePath = this->socketFile().filePath();
ls.connectToServer(socketFilePath, QIODevice::WriteOnly);
if(ls.waitForConnected(1000)){
ls.write(buffer);
if (!ls.waitForBytesWritten(1000)){
qDebug() << QString("Could not send command \"%1\" over IPC %2: \"%3\"").arg(cmdString, socketFilePath, ls.errorString());
return false;
}
qDebug() << QString("Sent command \"%1\" over IPC \"%2\"").arg(cmdString, socketFilePath);
return false;
}
if(ls.isOpen())
ls.disconnectFromServer();
// Queue for later
this->SetQueuedCmd(cmdString);
return true;
}
bool IPC::saveCommand(const QUrl &url){;
this->saveCommand(url.toString());
}
void IPC::handleConnection(){
QLocalSocket *clientConnection = this->m_server->nextPendingConnection();
connect(clientConnection, &QLocalSocket::disconnected,
clientConnection, &QLocalSocket::deleteLater);
clientConnection->waitForReadyRead(2);
QByteArray cmdArray = clientConnection->readAll();
QString cmdString = QTextCodec::codecForMib(106)->toUnicode(cmdArray); // UTF-8
qDebug() << cmdString;
this->parseCommand(cmdString);
clientConnection->close();
delete clientConnection;
}
void IPC::parseCommand(const QUrl &url){
this->parseCommand(url.toString());
}
void IPC::parseCommand(QString cmdString){
if(cmdString.contains(reURI)){
this->emitUriHandler(cmdString);
}
}
void IPC::emitUriHandler(QString uriString){
emit uriHandler(uriString);
}

35
src/qt/ipc.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef IPC_H
#define IPC_H
#include <QtCore>
#include <QLocalServer>
#include <qt/utils.h>
class IPC : public QObject
{
Q_OBJECT
public:
IPC(QObject *parent = 0) : QObject(parent) {}
QFileInfo socketFile() const { return m_socketFile; }
Q_INVOKABLE QString queuedCmd() { return m_queuedCmd; }
void SetQueuedCmd(const QString cmdString) { m_queuedCmd = cmdString; }
public slots:
void bind();
void handleConnection();
bool saveCommand(QString cmdString);
bool saveCommand(const QUrl &url);
void parseCommand(QString cmdString);
void parseCommand(const QUrl &url);
void emitUriHandler(QString uriString);
signals:
void uriHandler(QString uriString);
private:
QLocalServer *m_server;
QString m_queuedCmd;
QFileInfo m_socketFile = QFileInfo(QString(QDir::tempPath() + "/xmr-gui_%2.sock").arg(getAccountName()));
};
#endif // IPC_H

42
src/qt/mime.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include <QtCore>
#include <QApplication>
#include <QFile>
#include <QTextStream>
#include "mime.h"
#include "utils.h"
void registerXdgMime(QApplication &app){
// MacOS handled via Info.plist
// Windows handled in the installer by rbrunner7
QString xdg = QString(
"[Desktop Entry]\n"
"Name=Monero GUI\n"
"GenericName=Monero-GUI\n"
"X-GNOME-FullName=Monero-GUI\n"
"Comment=Monero GUI\n"
"Keywords=Monero;\n"
"Exec=%1 %u\n"
"Terminal=false\n"
"Type=Application\n"
"Icon=monero\n"
"Categories=Network;GNOME;Qt;\n"
"MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n"
"StartupNotify=true\n"
"X-GNOME-Bugzilla-Bugzilla=GNOME\n"
"X-GNOME-UsesNotifications=true\n"
).arg(app.applicationFilePath());
QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
QString filePath = QString("%1/monero-gui.desktop").arg(appPath);
qDebug() << QString("Writing %1").arg(filePath);
QFile file(filePath);
if(file.open(QIODevice::WriteOnly)){
QTextStream out(&file); out << xdg << endl;
file.close();
}
else
file.close();
}

8
src/qt/mime.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef MIME_H
#define MIME_H
#include <QApplication>
void registerXdgMime(QApplication &app);
#endif // MIME_H

20
src/qt/utils.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include <QtCore>
#include "utils.h"
bool fileExists(QString path) {
QFileInfo check_file(path);
if (check_file.exists() && check_file.isFile())
return true;
else
return false;
}
QString getAccountName(){
QString accountName = qgetenv("USER"); // mac/linux
if (accountName.isEmpty())
accountName = qgetenv("USERNAME"); // Windows
if (accountName.isEmpty())
accountName = "My monero Account";
return accountName;
}

11
src/qt/utils.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef UTILS_H
#define UTILS_H
#include <QtCore>
#include <QRegExp>
bool fileExists(QString path);
QString getAccountName();
const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)");
#endif // UTILS_H