From 20fa00c40ef9e9388404573adddb04a3b0c5b1ce Mon Sep 17 00:00:00 2001 From: thunder2 Date: Thu, 14 Apr 2011 21:59:51 +0000 Subject: [PATCH] Added handling of RetroShare protocol under Windows. Added new command line parameter "-r retroshare://..." for adding links to the registered running RetroShare. Recompile of the GUI needed. git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@4156 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/retroshare/rsinit.h | 5 +- libretroshare/src/rsserver/rsinit.cc | 22 ++- retroshare-gui/src/RetroShare.pro | 2 + .../src/gui/settings/GeneralPage.cpp | 7 + .../src/gui/settings/GeneralPage.ui | 7 + .../src/gui/settings/rsharesettings.cpp | 52 ++++- .../src/gui/settings/rsharesettings.h | 5 + retroshare-gui/src/lang/retroshare_de.qm | Bin 325648 -> 326254 bytes retroshare-gui/src/lang/retroshare_de.ts | 31 ++- retroshare-gui/src/main.cpp | 26 ++- retroshare-gui/src/util/EventReceiver.cpp | 178 ++++++++++++++++++ retroshare-gui/src/util/EventReceiver.h | 61 ++++++ 12 files changed, 380 insertions(+), 16 deletions(-) create mode 100644 retroshare-gui/src/util/EventReceiver.cpp create mode 100644 retroshare-gui/src/util/EventReceiver.h diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index c071d4caf..a7cb95b22 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -127,8 +127,9 @@ class RsInit static std::string RsConfigDirectory(); static std::string RsConfigKeysDirectory(); - static std::string RsProfileConfigDirectory(); - static bool setStartMinimised() ; + static std::string RsProfileConfigDirectory(); + static bool getStartMinimised() ; + static std::string getRetroShareLink(); static int getSslPwdLen(); static bool getAutoLogin(); diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index f924e8605..4cb8df716 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -127,6 +127,8 @@ class RsInitConfig static std::string load_trustedpeer_file; static bool udpListenerOnly; + + static std::string RetroShareLink; }; @@ -160,6 +162,7 @@ std::string RsInitConfig::passwd; bool RsInitConfig::autoLogin; /* autoLogin allowed */ bool RsInitConfig::startMinimised; /* Icon or Full Window */ +std::string RsInitConfig::RetroShareLink; /* Win/Unix Differences */ char RsInitConfig::dirSeperator; @@ -396,7 +399,7 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck /* getopt info: every availiable option is listed here. if it is followed by a ':' it needs an argument. If it is followed by a '::' the argument is optional. */ - while((c = getopt(argc, argv,"hesamui:p:c:w:l:d:U:")) != -1) + while((c = getopt(argc, argv,"hesamui:p:c:w:l:d:U:r:")) != -1) { switch (c) { @@ -466,6 +469,11 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck std::cerr << "Opt for User Id "; std::cerr << std::endl; break; + case 'r': + RsInitConfig::RetroShareLink = optarg; + std::cerr << "Opt for RetroShare link"; + std::cerr << std::endl; + break; case 'h': std::cerr << "Help: " << std::endl; std::cerr << "The commandline options are for retroshare-nogui, a headless server in a shell, or systems without QT." << std::endl << std::endl; @@ -481,14 +489,15 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck std::cerr << "-u Only listen to UDP" << std::endl; std::cerr << "-e Use a forwarded external Port" << std::endl ; std::cerr << "-U [User Name/GPG id/SSL id] Sets Account to Use, Useful when Autologin is enabled." << std::endl; + std::cerr << "-r link Use RetroShare link." << std::endl; exit(1); break; default: - if (strictCheck) { + if (strictCheck) { std::cerr << "Unknown Option!" << std::endl; std::cerr << "Use '-h' for help." << std::endl; exit(1); - } + } } } @@ -1657,11 +1666,16 @@ std::string RsInit::RsProfileConfigDirectory() return dir; } -bool RsInit::setStartMinimised() +bool RsInit::getStartMinimised() { return RsInitConfig::startMinimised; } +std::string RsInit::getRetroShareLink() +{ + return RsInitConfig::RetroShareLink; +} + int RsInit::getSslPwdLen(){ return SSLPWD_LEN; } diff --git a/retroshare-gui/src/RetroShare.pro b/retroshare-gui/src/RetroShare.pro index 1f47e1316..058f0d815 100644 --- a/retroshare-gui/src/RetroShare.pro +++ b/retroshare-gui/src/RetroShare.pro @@ -246,6 +246,7 @@ HEADERS += rshare.h \ util/PixmapMerging.h \ util/MouseEventFilter.h \ util/EventFilter.h \ + util/EventReceiver.h \ util/Widget.h \ util/rsversion.h \ util/RsAction.h \ @@ -475,6 +476,7 @@ SOURCES += main.cpp \ util/PixmapMerging.cpp \ util/MouseEventFilter.cpp \ util/EventFilter.cpp \ + util/EventReceiver.cpp \ util/Widget.cpp \ util/RsAction.cpp \ util/rsversion.cpp \ diff --git a/retroshare-gui/src/gui/settings/GeneralPage.cpp b/retroshare-gui/src/gui/settings/GeneralPage.cpp index 81300ed86..ffea345d1 100755 --- a/retroshare-gui/src/gui/settings/GeneralPage.cpp +++ b/retroshare-gui/src/gui/settings/GeneralPage.cpp @@ -38,6 +38,7 @@ GeneralPage::GeneralPage(QWidget * parent, Qt::WFlags flags) #ifndef Q_WS_WIN ui.chkRunRetroshareAtSystemStartup->setVisible(false); ui.chkRunRetroshareAtSystemStartupMinimized->setVisible(false); + ui.enableRetroShareProtocol->setVisible(false); #endif } @@ -55,6 +56,10 @@ bool GeneralPage::save(QString &errmsg) #ifdef Q_WS_WIN Settings->setRunRetroshareOnBoot(ui.chkRunRetroshareAtSystemStartup->isChecked(), ui.chkRunRetroshareAtSystemStartupMinimized->isChecked()); + + if (ui.enableRetroShareProtocol->isChecked() != Settings->getRetroShareProtocol()) { + Settings->setRetroShareProtocol(ui.enableRetroShareProtocol->isChecked()); + } #endif Settings->setMaxTimeBeforeIdle(ui.spinBox->value()); @@ -71,6 +76,8 @@ void GeneralPage::load() bool minimized; ui.chkRunRetroshareAtSystemStartup->setChecked(Settings->runRetroshareOnBoot(minimized)); ui.chkRunRetroshareAtSystemStartupMinimized->setChecked(minimized); + + ui.enableRetroShareProtocol->setChecked(Settings->getRetroShareProtocol()); #endif ui.checkStartMinimized->setChecked(Settings->getStartMinimized()); diff --git a/retroshare-gui/src/gui/settings/GeneralPage.ui b/retroshare-gui/src/gui/settings/GeneralPage.ui index 87abfdac9..2d677f9bc 100755 --- a/retroshare-gui/src/gui/settings/GeneralPage.ui +++ b/retroshare-gui/src/gui/settings/GeneralPage.ui @@ -583,6 +583,13 @@ + + + + Register retroshare:// as url protocol (Restart required) + + + diff --git a/retroshare-gui/src/gui/settings/rsharesettings.cpp b/retroshare-gui/src/gui/settings/rsharesettings.cpp index b0131e899..f13f2a182 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.cpp +++ b/retroshare-gui/src/gui/settings/rsharesettings.cpp @@ -37,6 +37,7 @@ #endif // MINIMAL_RSGUI #include +#include #if defined(Q_WS_WIN) #include @@ -472,7 +473,56 @@ RshareSettings::setRunRetroshareOnBoot(bool run, bool minimized) #else /* Platforms othe rthan windows aren't supported yet */ Q_UNUSED(run); - return; + Q_UNUSED(minimized); +#endif +} + +static QString getAppPathForProtocol() +{ + return "\"" + QDir::convertSeparators(QCoreApplication::applicationFilePath()) + "\" -r \"%1\""; +} + +/** Returns true if retroshare:// is registered as protocol */ +bool RshareSettings::getRetroShareProtocol() +{ +#if defined(Q_WS_WIN) + /* Check key */ + QSettings retroshare("HKEY_CLASSES_ROOT\\retroshare", QSettings::NativeFormat); + if (retroshare.contains("Default")) { + /* Check profile */ + if (retroshare.value("Profile").toString().toStdString() == rsPeers->getOwnId()) { + /* Check app path */ + QSettings command("HKEY_CLASSES_ROOT\\retroshare\\shell\\open\\command", QSettings::NativeFormat); + if (command.value("Default").toString() == getAppPathForProtocol()) { + return true; + } + } + } +#else + /* Platforms other than windows aren't supported yet */ + return false; +#endif +} + +/** Register retroshare:// as protocl */ +void RshareSettings::setRetroShareProtocol(bool value) +{ +#if defined(Q_WS_WIN) + if (value) { + QSettings retroshare("HKEY_CLASSES_ROOT\\retroshare", QSettings::NativeFormat); + retroshare.setValue("Default", "URL: RetroShare protocol"); + retroshare.setValue("URL Protocol", ""); + retroshare.setValue("Profile", QString::fromStdString(rsPeers->getOwnId())); + + QSettings command("HKEY_CLASSES_ROOT\\retroshare\\shell\\open\\command", QSettings::NativeFormat); + command.setValue("Default", getAppPathForProtocol()); + } else { + QSettings retroshare("HKEY_CLASSES_ROOT", QSettings::NativeFormat); + retroshare.remove("retroshare"); + } +#else + /* Platforms othe rthan windows aren't supported yet */ + Q_UNUSED(value); #endif } diff --git a/retroshare-gui/src/gui/settings/rsharesettings.h b/retroshare-gui/src/gui/settings/rsharesettings.h index be103d4c7..a6bc6393b 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.h +++ b/retroshare-gui/src/gui/settings/rsharesettings.h @@ -109,6 +109,11 @@ public: /** Set whether to run RetroShare on system boot. */ void setRunRetroshareOnBoot(bool run, bool minimized); + /** Returns true if retroshare:// is registered as protocol */ + bool getRetroShareProtocol(); + /** Register retroshare:// as protocl */ + void setRetroShareProtocol(bool value); + /* Get the destination log file. */ QString getLogFile(); /** Set the destination log file. */ diff --git a/retroshare-gui/src/lang/retroshare_de.qm b/retroshare-gui/src/lang/retroshare_de.qm index 2609f8a54c38e3ee856acff67a1426a40cf7490b..00654f76f265481debe9438b08d9783f34048fd7 100644 GIT binary patch delta 14768 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 delta 14306 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 in den Systemabschnitt minimieren - + seconds Sekunden - + Start minimized Minimiert starten @@ -4335,7 +4335,12 @@ Gib Dein GPG Passwort wenn Du gefragt wirst ein, um Deinen neuen Schlüssel zu u Starte RetroShare mit dem System - + + Register retroshare:// as url protocol (Restart required) + Registriere retroshare:// als Protokoll (Neustart erforderlich) + + + Idle Untätig @@ -8828,19 +8833,19 @@ p, li { white-space: pre-wrap; } QObject - - + + RetroShare RetroShare - + Inititialize failed. Wrong or missing installation of gpg. Initialisierung fehlgeschlagen. GPG fehlt oder es ist eine falsche Version installiert. - + An unexpected error occured. Please report 'RsInit::InitRetroShare unexpected return code %1'. Ein unerwarteter Fehler ist aufgetreten. Bitte melde 'RsInit::InitRetroShare unexpected return code %1'. @@ -8971,6 +8976,16 @@ Lockdatei: Peer details Nachbar Details + + + No running instance of RetroShare found. + Kein laufendes RetroShare gefunden. + + + + Start with a RetroShare link is only supported for Windows. + Der Start mit einem RetroShare Link wird nur unter Windows unterstützt. + QuickStartWizard @@ -11073,7 +11088,7 @@ p, li { white-space: pre-wrap; } Definiere Farben - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index 66dd28926..4d1b6f1c5 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -42,6 +42,7 @@ #include "gui/connect/ConfCertDialog.h" #include "idle/idle.h" #include "gui/common/Emoticons.h" +#include "util/EventReceiver.h" /*** WINDOWS DON'T LIKE THIS - REDEFINES VER numbers. #include @@ -107,6 +108,14 @@ int main(int argc, char *argv[]) Rshare rshare(args, argc, argv, QString::fromStdString(RsInit::RsConfigDirectory())); + std::string link = RsInit::getRetroShareLink(); + if (!link.empty()) { + /* start with RetroShare link */ + EventReceiver eventReceiver; + eventReceiver.sendRetroShareLink(QString::fromStdString(link)); + return 0; + } + QSplashScreen splashScreen(QPixmap(":/images/splash.png")/* , Qt::WindowStaysOnTopHint*/); switch (initResult) { @@ -229,6 +238,15 @@ int main(int argc, char *argv[]) MainWindow *w = MainWindow::Create (); splashScreen.finish(w); + EventReceiver *eventReceiver = NULL; + if (Settings->getRetroShareProtocol()) { + /* Create event receiver */ + eventReceiver = new EventReceiver; + if (eventReceiver->start()) { + QObject::connect(eventReceiver, SIGNAL(linkReceived(const QUrl&)), w, SLOT(linkActivated(const QUrl&))); + } + } + // I'm using a signal to transfer the hashing info to the mainwindow, because Qt schedules signals properly to // avoid clashes between infos from threads. // @@ -268,7 +286,7 @@ int main(int argc, char *argv[]) w->installGroupChatNotifier(); /* only show window, if not startMinimized */ - if (RsInit::setStartMinimised() || Settings->getStartMinimized()) + if (RsInit::getStartMinimised() || Settings->getStartMinimized()) { splashScreen.close(); } else { @@ -286,6 +304,12 @@ int main(int argc, char *argv[]) #ifndef MINIMAL_RSGUI delete w ; + if (eventReceiver) { + /* Destroy event receiver */ + delete eventReceiver; + eventReceiver = NULL; + } + /* cleanup */ PopupChatDialog::cleanupChat(); #endif // MINIMAL_RSGUI diff --git a/retroshare-gui/src/util/EventReceiver.cpp b/retroshare-gui/src/util/EventReceiver.cpp new file mode 100644 index 000000000..225ca32c5 --- /dev/null +++ b/retroshare-gui/src/util/EventReceiver.cpp @@ -0,0 +1,178 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2006, 2007 crypton + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include +#include +#include +#include + +#include + +#include "EventReceiver.h" +#include "gui/RetroShareLink.h" + +#ifdef WINDOWS_SYS +#include +#define OP_RETROSHARELINK 12000 +#endif + +struct SharedMemoryInfo +{ +#ifdef WINDOWS_SYS + /* Store handle of the event window */ + WId wid; +#else + long dummy; +#endif +}; + +EventReceiver::EventReceiver() +{ +#ifdef WINDOWS_SYS + setWindowTitle("RetroShare EventReceiver"); +#endif + + /* Build unique name for the running instance */ + QString name = QString("RetroShare-%1::EventReceiver").arg(QCoreApplication::applicationDirPath()); + sharedMMemory.setKey(name); +} + +EventReceiver::~EventReceiver() +{ + sharedMMemory.detach(); +} + +bool EventReceiver::start() +{ + if (!sharedMMemory.create(sizeof(SharedMemoryInfo))) { + std::cerr << "EventReceiver::start() Cannot create shared memory !" << sharedMMemory.errorString().toStdString() << std::endl; + return false; + } + + bool result = true; + + if (sharedMMemory.lock()) { + SharedMemoryInfo *info = (SharedMemoryInfo*) sharedMMemory.data(); + if (info) { +#ifdef WINDOWS_SYS + info->wid = winId(); +#else + info->dummy = 0; +#endif + } else { + result = false; + std::cerr << "EventReceiver::start() Shared memory returns a NULL pointer!" << std::endl; + } + + sharedMMemory.unlock(); + } else { + result = false; + std::cerr << "EventReceiver::start() Cannot lock shared memory !" << std::endl; + } + + return result; +} + +void EventReceiver::showNoRunningInstanceFound() +{ + QMessageBox mb(QMessageBox::Critical, "RetroShare", QObject::tr("No running instance of RetroShare found."), QMessageBox::Ok); + mb.setWindowIcon(QIcon(":/images/rstray3.png")); + mb.exec(); +} + +bool EventReceiver::sendRetroShareLink(const QString& link) +{ + if (!sharedMMemory.attach()) { + /* No running instance found */ + showNoRunningInstanceFound(); + return false; + } + + bool result = true; + + if (sharedMMemory.lock()) { + SharedMemoryInfo *info = (SharedMemoryInfo*) sharedMMemory.data(); + if (info) { +#ifdef WINDOWS_SYS + if (info->wid) { + QByteArray linkData = link.toAscii(); + + COPYDATASTRUCT send; + send.cbData = (link.length() + 1) * sizeof(char); + send.dwData = OP_RETROSHARELINK; + send.lpData = (void*) linkData.constData(); + + SendMessage((HWND) info->wid, WM_COPYDATA, (WPARAM) 0, (LPARAM) (PCOPYDATASTRUCT) &send); + } else { + showNoRunningInstanceFound(); + result = false; + } +#else + QMessageBox mb(QMessageBox::Critical, "RetroShare", QObject::tr("Start with a RetroShare link is only supported for Windows."), QMessageBox::Ok); + mb.setWindowIcon(QIcon(":/images/rstray3.png")); + mb.exec(); + + result = false; +#endif + } else { + result = false; + std::cerr << "EventReceiver::sendRetroShareLink() Cannot lock shared memory !" << std::endl; + } + + sharedMMemory.unlock(); + } else { + result = false; + std::cerr << "EventReceiver::start() Cannot lock shared memory !" << std::endl; + } + + sharedMMemory.detach(); + + return result; +} + +#ifdef WINDOWS_SYS +bool EventReceiver::winEvent(MSG* message, long* result) +{ + if (message->message == WM_COPYDATA ) { + /* Extract the struct from lParam */ + COPYDATASTRUCT *data = (COPYDATASTRUCT*) message->lParam; + + if (data && data->dwData == OP_RETROSHARELINK) { + received(QString::fromAscii((const char*) data->lpData, data->cbData)); + + /* Keep the event from Qt */ + *result = 0; + return true; + } + } + + /* Give the event to Qt */ + return false; +} +#endif + +void EventReceiver::received(const QString &url) +{ + RetroShareLink link(url); + if (link.valid()) { + emit linkReceived(link.toUrl()); + } +} diff --git a/retroshare-gui/src/util/EventReceiver.h b/retroshare-gui/src/util/EventReceiver.h new file mode 100644 index 000000000..410abba7a --- /dev/null +++ b/retroshare-gui/src/util/EventReceiver.h @@ -0,0 +1,61 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2006,2007 crypton + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef _EVENTRECEIVER_H +#define _EVENTRECEIVER_H + +#include +#include + +class QUrl; + +class EventReceiver : public +#ifdef WINDOWS_SYS + QWidget +#else + QObject +#endif +{ + Q_OBJECT + +public: + EventReceiver(); + ~EventReceiver(); + + bool start(); + bool sendRetroShareLink(const QString& link); + +signals: + void linkReceived(const QUrl& url); + +private: + void showNoRunningInstanceFound(); + void received(const QString& url); + +#ifdef WINDOWS_SYS + /* Extend QWidget with a class that will capture the WM_COPYDATA messages */ + bool winEvent (MSG* message, long* result); +#endif // WINDOWS_SYS + + QSharedMemory sharedMMemory; +}; + +#endif