diff --git a/RetroShare.pro b/RetroShare.pro index 110e14747..27d7915a9 100644 --- a/RetroShare.pro +++ b/RetroShare.pro @@ -7,7 +7,7 @@ SUBDIRS += \ supportlibs/pegmarkdown/pegmarkdown.pro \ libbitdht/src/libbitdht.pro \ libretroshare/src/libretroshare.pro \ - retroshare-gui/src/retroshare-gui.pro \ libresapi/src/libresapi.pro \ + retroshare-gui/src/retroshare-gui.pro \ retroshare-nogui/src/retroshare-nogui.pro \ plugins/plugins.pro diff --git a/libresapi/src/api/ApiServerMHD.cpp b/libresapi/src/api/ApiServerMHD.cpp index 441cb351d..d62bc4db7 100644 --- a/libresapi/src/api/ApiServerMHD.cpp +++ b/libresapi/src/api/ApiServerMHD.cpp @@ -8,6 +8,9 @@ // for filestreamer #include +// to determine default docroot +#include + #include "JsonStream.h" #include "ApiServer.h" @@ -58,6 +61,11 @@ struct MHD_Response * MHD_create_response_from_fd(size_t size, int fd) namespace resource_api{ +std::string getDefaultDocroot() +{ + return RsAccounts::DataDirectory() + "/webui"; +} + const char* API_ENTRY_PATH = "/api/v2"; const char* FILESTREAMER_ENTRY_PATH = "/fstream/"; const char* STATIC_FILES_ENTRY_PATH = "/static/"; @@ -509,11 +517,9 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection, FILE* fd = fopen(filename.c_str(), "rb"); if(fd == 0) { - const char *error = "

Error: can't open the requested file.

"; - struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1); - MHD_add_response_header(resp, "Content-Type", "text/html"); - secure_queue_response(connection, MHD_HTTP_NOT_FOUND, resp); - MHD_destroy_response(resp); +#warning sending untrusted string to the browser + std::string msg = "

Error: can't open the requested file. Path is ""+filename+""

"; + sendMessage(connection, MHD_HTTP_NOT_FOUND, msg); return MHD_YES; } diff --git a/libresapi/src/api/ApiServerMHD.h b/libresapi/src/api/ApiServerMHD.h index 412dcceb9..ec0897c95 100644 --- a/libresapi/src/api/ApiServerMHD.h +++ b/libresapi/src/api/ApiServerMHD.h @@ -14,6 +14,10 @@ namespace resource_api{ class ApiServer; +// returns the default docroot path +// (it is differen on different operating systems) +std::string getDefaultDocroot(); + class ApiServerMHD { public: diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index cf0780fa6..01709db39 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -132,7 +132,7 @@ namespace RsAccounts { // Directories. std::string ConfigDirectory(); // aka Base Directory. (normally ~/.retroshare) - std::string DataDirectory(); + std::string DataDirectory(); // you can call this method even before initialisation (you can't with the other methods) std::string PGPDirectory(); std::string AccountDirectory(); diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index e63699379..0f9a66b80 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -717,7 +717,7 @@ static bool checkAccount(std::string accountdir, AccountDetails &account,std::ma //#include #endif -std::string RsAccountsDetail::PathDataDirectory() +/*static*/ std::string RsAccountsDetail::PathDataDirectory() { std::string dataDirectory; @@ -1227,7 +1227,7 @@ bool RsInit::LoadPassword(const std::string& id, const std::string& inPwd) // Directories. std::string RsAccounts::ConfigDirectory() { return rsAccounts->PathBaseDirectory(); } -std::string RsAccounts::DataDirectory() { return rsAccounts->PathDataDirectory(); } +std::string RsAccounts::DataDirectory() { return RsAccountsDetail::PathDataDirectory(); } std::string RsAccounts::PGPDirectory() { return rsAccounts->PathPGPDirectory(); } std::string RsAccounts::AccountDirectory() { return rsAccounts->PathAccountDirectory(); } diff --git a/libretroshare/src/rsserver/rsaccounts.h b/libretroshare/src/rsserver/rsaccounts.h index d62404b21..351d900c4 100644 --- a/libretroshare/src/rsserver/rsaccounts.h +++ b/libretroshare/src/rsserver/rsaccounts.h @@ -73,7 +73,7 @@ class RsAccountsDetail void unlockPreferredAccount(); // Paths. - std::string PathDataDirectory(); + static std::string PathDataDirectory(); std::string PathBaseDirectory(); // PGP Path is only dependent on BaseDirectory. diff --git a/retroshare-gui/src/gui/images.qrc b/retroshare-gui/src/gui/images.qrc index 70376b5dc..5f1f9da8d 100644 --- a/retroshare-gui/src/gui/images.qrc +++ b/retroshare-gui/src/gui/images.qrc @@ -719,5 +719,6 @@ images/btn_red.png images/view-feeds.png images/view-files.png + images/emblem-web.png diff --git a/retroshare-gui/src/gui/images/emblem-web.png b/retroshare-gui/src/gui/images/emblem-web.png new file mode 100644 index 000000000..cdf16fabd Binary files /dev/null and b/retroshare-gui/src/gui/images/emblem-web.png differ diff --git a/retroshare-gui/src/gui/settings/WebuiPage.cpp b/retroshare-gui/src/gui/settings/WebuiPage.cpp new file mode 100644 index 000000000..963ec9c08 --- /dev/null +++ b/retroshare-gui/src/gui/settings/WebuiPage.cpp @@ -0,0 +1,136 @@ +#include "WebuiPage.h" + +#include +#include +#include +#include + +#include "api/ApiServer.h" +#include "api/ApiServerMHD.h" +#include "api/RsControlModule.h" +#include "api/GetPluginInterfaces.h" + +#include "rsharesettings.h" + +resource_api::ApiServer* WebuiPage::apiServer = 0; +resource_api::ApiServerMHD* WebuiPage::apiServerMHD = 0; +resource_api::RsControlModule* WebuiPage::controlModule = 0; + +WebuiPage::WebuiPage(QWidget */*parent*/, Qt::WindowFlags /*flags*/) +{ + ui.setupUi(this); + connect(ui.enableWebUI_CB, SIGNAL(clicked(bool)), this, SLOT(onEnableCBClicked(bool))); + connect(ui.applyStartBrowser_PB, SIGNAL(clicked()), this, SLOT(onApplyClicked())); +} + +WebuiPage::~WebuiPage() +{ + +} + +bool WebuiPage::save(QString &errmsg) +{ + std::cerr << "WebuiPage::save()" << std::endl; + bool ok = true; + bool changed = false; + if(ui.enableWebUI_CB->isChecked() != Settings->getWebinterfaceEnabled()) + changed = true; + if(ui.port_SB->value() != Settings->getWebinterfacePort()) + changed = true; + if(ui.allIp_CB->isChecked() != Settings->getWebinterfaceAllowAllIps()) + changed = true; + if(changed) + { + // store config + Settings->setWebinterfaceEnabled(ui.enableWebUI_CB->isChecked()); + Settings->setWebinterfacePort(ui.port_SB->value()); + Settings->setWebinterfaceAllowAllIps(ui.allIp_CB->isChecked()); + + // apply config + checkShutdownWebui(); + ok = checkStartWebui(); + } + if(!ok) + errmsg = "Could not start webinterface."; + return ok; +} + +void WebuiPage::load() +{ + std::cerr << "WebuiPage::load()" << std::endl; + ui.enableWebUI_CB->setChecked(Settings->getWebinterfaceEnabled()); + onEnableCBClicked(Settings->getWebinterfaceEnabled()); + ui.port_SB->setValue(Settings->getWebinterfacePort()); + ui.allIp_CB->setChecked(Settings->getWebinterfaceAllowAllIps()); +} + +QString WebuiPage::helpText() const +{ + return tr("

  Webinterface

\ +

The webinterface allows to control Retroshare from the browser. Multiple devices can share control over one Retroshare instance. So you could start a conversation on a tablet omputer and later use a desktop computer to continue it.

\ +

Warning: don't expose the webinterface to the internet, because there is no access control and no encryption. If you want to use the webinterface over the internet, use a SSH tunnel or a proxy to secure the connection.

"); +} + +/*static*/ bool WebuiPage::checkStartWebui() +{ + if(!Settings->getWebinterfaceEnabled()) + return true; + if(apiServer || apiServerMHD || controlModule) + return true; + + apiServer = new resource_api::ApiServer(); + controlModule = new resource_api::RsControlModule(0, 0, apiServer->getStateTokenServer(), apiServer, false); + apiServer->addResourceHandler("control", dynamic_cast(controlModule), &resource_api::RsControlModule::handleRequest); + + RsPlugInInterfaces ifaces; + resource_api::getPluginInterfaces(ifaces); + apiServer->loadMainModules(ifaces); + + apiServerMHD = new resource_api::ApiServerMHD(apiServer); + bool ok = apiServerMHD->configure(resource_api::getDefaultDocroot(), + Settings->getWebinterfacePort(), + "", + Settings->getWebinterfaceAllowAllIps()); + apiServerMHD->start(); + return ok; +} + +/*static*/ void WebuiPage::checkShutdownWebui() +{ + if(apiServer || apiServerMHD) + { + apiServerMHD->stop(); + delete apiServerMHD; + apiServerMHD = 0; + delete apiServer; + apiServer = 0; + delete controlModule; + controlModule = 0; + } +} + +void WebuiPage::onEnableCBClicked(bool checked) +{ + if(checked) + { + ui.params_GB->setEnabled(true); + ui.applyStartBrowser_PB->setEnabled(true); + } + else + { + ui.params_GB->setEnabled(false); + ui.applyStartBrowser_PB->setEnabled(false); + } +} + +void WebuiPage::onApplyClicked() +{ + QString errmsg; + bool ok = save(errmsg); + if(!ok) + { + QMessageBox::warning(0, tr("failed to start Webinterface"), "Failed to start the webinterface."); + return; + } + QDesktopServices::openUrl(QUrl(QString("http://localhost:")+QString::number(ui.port_SB->value()))); +} diff --git a/retroshare-gui/src/gui/settings/WebuiPage.h b/retroshare-gui/src/gui/settings/WebuiPage.h new file mode 100644 index 000000000..e88960f1e --- /dev/null +++ b/retroshare-gui/src/gui/settings/WebuiPage.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "ui_WebuiPage.h" + +namespace resource_api{ + class ApiServer; + class ApiServerMHD; + class RsControlModule; +} + +class WebuiPage : public ConfigPage +{ + Q_OBJECT + +public: + /** Default Constructor */ + WebuiPage(QWidget * parent = 0, Qt::WindowFlags flags = 0); + /** Default Destructor */ + ~WebuiPage(); + + /** Saves the changes on this page */ + virtual bool save(QString &errmsg); + /** Loads the settings for this page */ + virtual void load(); + + virtual QPixmap iconPixmap() const { return QPixmap(":/images/emblem-web.png") ; } + virtual QString pageName() const { return tr("Webinterface") ; } + virtual QString helpText() const; + + // call this after start of libretroshare/Retroshare + // checks the settings and starts the webinterface if required + static bool checkStartWebui(); + // call this before shutdown of libretroshare + // it stops the webinterface if its running + static void checkShutdownWebui(); + +public slots: + void onEnableCBClicked(bool checked); + void onApplyClicked(); + +private: + /** Qt Designer generated object */ + Ui::WebuiPage ui; + + static resource_api::ApiServer* apiServer; + static resource_api::ApiServerMHD* apiServerMHD; + static resource_api::RsControlModule* controlModule; +}; diff --git a/retroshare-gui/src/gui/settings/WebuiPage.ui b/retroshare-gui/src/gui/settings/WebuiPage.ui new file mode 100644 index 000000000..dd2401904 --- /dev/null +++ b/retroshare-gui/src/gui/settings/WebuiPage.ui @@ -0,0 +1,94 @@ + + + WebuiPage + + + + 0 + 0 + 442 + 404 + + + + Form + + + + + + Enable Retroshare WEB Interface + + + + + + + true + + + Web parameters + + + + + + 1024 + + + 65535 + + + + + + + Port : + + + + + + + allow access from all IP adresses (Default: localhost only) + + + + + + + + + + apply setting and start browser + + + + + + + Note: these settings do not affect retroshare-nogui. retroshare-nogui has a command line switch to active the webinterface. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/retroshare-gui/src/gui/settings/rsettingswin.cpp b/retroshare-gui/src/gui/settings/rsettingswin.cpp index fc089f530..4c533109e 100644 --- a/retroshare-gui/src/gui/settings/rsettingswin.cpp +++ b/retroshare-gui/src/gui/settings/rsettingswin.cpp @@ -41,6 +41,7 @@ #include "PostedPage.h" #include "PluginsPage.h" #include "ServicePermissionsPage.h" +#include "WebuiPage.h" #include "rsharesettings.h" #include "gui/notifyqt.h" #include "gui/common/FloatingHelpBrowser.h" @@ -149,6 +150,7 @@ RSettingsWin::initStackedWidget() addPage(new AppearancePage()); addPage(new SoundPage() ); addPage(new ServicePermissionsPage() ); + addPage(new WebuiPage() ); // add widgets from plugins diff --git a/retroshare-gui/src/gui/settings/rsharesettings.cpp b/retroshare-gui/src/gui/settings/rsharesettings.cpp index e775c410a..4f00046df 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.cpp +++ b/retroshare-gui/src/gui/settings/rsharesettings.cpp @@ -1040,3 +1040,33 @@ void RshareSettings::setMaxTimeBeforeIdle(uint nValue) m_maxTimeBeforeIdle = nValue; setValue("maxTimeBeforeIdle", nValue); } + +bool RshareSettings::getWebinterfaceEnabled() +{ + return valueFromGroup("Webinterface", "enabled", false).toBool(); +} + +void RshareSettings::setWebinterfaceEnabled(bool enabled) +{ + setValueToGroup("Webinterface", "enabled", enabled); +} + +uint16_t RshareSettings::getWebinterfacePort() +{ + return valueFromGroup("Webinterface", "port", 9090).toUInt(); +} + +void RshareSettings::setWebinterfacePort(uint16_t port) +{ + setValueToGroup("Webinterface", "port", port); +} + +bool RshareSettings::getWebinterfaceAllowAllIps() +{ + return valueFromGroup("Webinterface", "allowAllIps", false).toBool(); +} + +void RshareSettings::setWebinterfaceAllowAllIps(bool allow_all) +{ + setValueToGroup("Webinterface", "allowAllIps", allow_all); +} diff --git a/retroshare-gui/src/gui/settings/rsharesettings.h b/retroshare-gui/src/gui/settings/rsharesettings.h index d00e20e8c..d4f9c95c2 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.h +++ b/retroshare-gui/src/gui/settings/rsharesettings.h @@ -202,9 +202,9 @@ public: bool getChatSendMessageWithCtrlReturn(); void setChatSendMessageWithCtrlReturn(bool bValue); - bool getChatSearchShowBarByDefault(); - void setChatSearchShowBarByDefault(bool bValue); - + bool getChatSearchShowBarByDefault(); + void setChatSearchShowBarByDefault(bool bValue); + void setChatSearchCharToStartSearch(int iValue); int getChatSearchCharToStartSearch(); @@ -304,6 +304,16 @@ public: uint getMaxTimeBeforeIdle(); void setMaxTimeBeforeIdle(uint value); + // webinterface + bool getWebinterfaceEnabled(); + void setWebinterfaceEnabled(bool enabled); + + uint16_t getWebinterfacePort(); + void setWebinterfacePort(uint16_t port); + + bool getWebinterfaceAllowAllIps(); + void setWebinterfaceAllowAllIps(bool allow_all); + protected: /** Default constructor. */ RshareSettings(); diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index b0843273c..536c04f41 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -46,6 +46,7 @@ #include "gui/NetworkView.h" #include "lang/languagesupport.h" #include "util/RsGxsUpdateBroadcast.h" +#include "gui/settings/WebuiPage.h" #if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0) #ifdef WINDOWS_SYS @@ -415,10 +416,14 @@ int main(int argc, char *argv[]) notify->enable() ; // enable notification system after GUI creation, to avoid data races in Qt. + WebuiPage::checkStartWebui(); + /* dive into the endless loop */ int ti = rshare.exec(); delete w ; + WebuiPage::checkShutdownWebui(); + if (eventReceiver) { /* Destroy event receiver */ delete eventReceiver; diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index bc5b2c1bc..8dbaf5c75 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -210,6 +210,9 @@ win32 { DEPENDPATH += . INCLUDEPATH += . + DEPENDPATH += $$LIBS_DIR/include + INCLUDEPATH += $$LIBS_DIR/include + greaterThan(QT_MAJOR_VERSION, 4) { # Qt 5 RC_INCLUDEPATH += $$_PRO_FILE_PWD_/../../libretroshare/src @@ -304,6 +307,12 @@ win32 { DEPENDPATH += . ../../libretroshare/src/ INCLUDEPATH += ../../libretroshare/src/ +# webinterface +DEPENDPATH += ../../libresapi/src +INCLUDEPATH += ../../libresapi/src +PRE_TARGETDEPS *= ../../libresapi/src/lib/libresapi.a +LIBS += ../../libresapi/src/lib/libresapi.a -lmicrohttpd + # Input HEADERS += rshare.h \ retroshare-gui/configpage.h \ @@ -530,6 +539,7 @@ HEADERS += rshare.h \ gui/statistics/BwCtrlWindow.h \ gui/statistics/RttStatistics.h \ gui/statistics/OutQueueStatistics.h \ + gui/settings/WebuiPage.h # gui/ForumsDialog.h \ # gui/forums/ForumDetails.h \ @@ -642,6 +652,7 @@ FORMS += gui/StartDialog.ui \ gui/statistics/BwCtrlWindow.ui \ gui/statistics/RttStatistics.ui \ gui/GetStartedDialog.ui \ + gui/settings/WebuiPage.ui # gui/ForumsDialog.ui \ # gui/forums/CreateForum.ui \ @@ -865,6 +876,7 @@ SOURCES += main.cpp \ gui/statistics/StatisticsWindow.cpp \ gui/statistics/BwCtrlWindow.cpp \ gui/statistics/RttStatistics.cpp \ + gui/settings/WebuiPage.cpp # gui/ForumsDialog.cpp \ # gui/forums/ForumDetails.cpp \