retroshare-gui now handle JsonApiServer too

This commit is contained in:
Gioacchino Mazzurco 2018-09-21 01:58:38 +02:00
parent 1c7f02655e
commit ccabf82e60
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051
10 changed files with 461 additions and 26 deletions

View File

@ -0,0 +1,173 @@
/*
* RetroShare JSON API
* Copyright (C) 2018 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "JsonApiPage.h"
#include "rsharesettings.h"
#include "jsonapi/jsonapi.h"
#include "util/misc.h"
#include <QTimer>
#include <QStringListModel>
#include <QProgressDialog>
JsonApiPage::JsonApiPage(QWidget */*parent*/, Qt::WindowFlags /*flags*/)
{
ui.setupUi(this);
connect( ui.addTokenPushButton, &QPushButton::clicked,
this, &JsonApiPage::addTokenClicked);
connect( ui.removeTokenPushButton, &QPushButton::clicked,
this, &JsonApiPage::removeTokenClicked );
connect( ui.tokensListView, &QListView::clicked,
this, &JsonApiPage::tokenClicked );
connect( ui.applyConfigPushButton, &QPushButton::clicked,
this, &JsonApiPage::onApplyClicked );
}
bool JsonApiPage::updateParams(QString &errmsg)
{
bool ok = true;
bool changed = false;
bool enabled = ui.enableCheckBox->isChecked();
if( enabled != Settings->getJsonApiEnabled())
{
Settings->setJsonApiEnabled(enabled);
changed = true;
}
uint16_t port = ui.portSpinBox->value();
if(port != Settings->getJsonApiPort())
{
Settings->setJsonApiPort(port);
changed = true;
}
QString listenAddress = ui.listenAddressLineEdit->text();
if(listenAddress != Settings->getJsonApiListenAddress())
{
Settings->setJsonApiListenAddress(listenAddress);
changed = true;
}
if(changed)
{
checkShutdownJsonApi();
ok = checkStartJsonApi();
}
if(!ok) errmsg = "Could not start JSON API Server!";
return ok;
}
void JsonApiPage::load()
{
whileBlocking(ui.enableCheckBox)->setChecked(Settings->getJsonApiEnabled());
whileBlocking(ui.portSpinBox)->setValue(Settings->getJsonApiPort());
whileBlocking(ui.listenAddressLineEdit)->setText(Settings->getJsonApiListenAddress());
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(Settings->getJsonApiAuthTokens()));
}
QString JsonApiPage::helpText() const { return ""; }
/*static*/ bool JsonApiPage::checkStartJsonApi()
{
checkShutdownJsonApi();
if(Settings->getJsonApiEnabled())
{
jsonApiServer = new JsonApiServer(
Settings->getJsonApiPort(),
Settings->getJsonApiListenAddress().toStdString() );
jsonApiServer->start("jsonApiServer");
for(const QString& token : Settings->getJsonApiAuthTokens())
jsonApiServer->authorizeToken(token.toStdString());
}
return true;
}
/*static*/ void JsonApiPage::checkShutdownJsonApi()
{
if(jsonApiServer)
{
/* It is important to make a copy of +jsonApiServer+ pointer so the old
* object can be deleted later, while the original pointer is
* reassigned */
JsonApiServer* oldJsonApiServer = jsonApiServer;
jsonApiServer = nullptr;
oldJsonApiServer->shutdown();
QProgressDialog* pd = new QProgressDialog(
"Stopping JSON API Server", QString(), 0, 3000);
QTimer* prtm = new QTimer;
prtm->setInterval(16); // 60 FPS
connect( prtm, &QTimer::timeout,
pd, [=](){pd->setValue(pd->value()+16);} );
pd->show();
prtm->start();
/* Must wait for deletion because stopping of the server is async.
* It is important to capture a copy so it "survive" after
* safeStopJsonApiServer returns */
QTimer::singleShot(3*1000, [=]()
{
delete oldJsonApiServer;
prtm->stop();
pd->close();
prtm->deleteLater();
pd->deleteLater();
});
}
}
void JsonApiPage::onApplyClicked(bool)
{
QString errmsg;
updateParams(errmsg);
}
void JsonApiPage::addTokenClicked(bool)
{
QString token(ui.tokenLineEdit->text());
if(jsonApiServer) jsonApiServer->authorizeToken(token.toStdString());
QStringList newTk(Settings->getJsonApiAuthTokens());
newTk.removeAll(token);
newTk.append(token);
Settings->setJsonApiAuthTokens(newTk);
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
}
void JsonApiPage::removeTokenClicked(bool)
{
QString token(ui.tokenLineEdit->text());
if(jsonApiServer) jsonApiServer->revokeAuthToken(token.toStdString());
QStringList newTk(Settings->getJsonApiAuthTokens());
newTk.removeAll(token);
Settings->setJsonApiAuthTokens(newTk);
whileBlocking(ui.tokensListView)->setModel(
new QStringListModel(Settings->getJsonApiAuthTokens()) );
}
void JsonApiPage::tokenClicked(const QModelIndex& index)
{
ui.tokenLineEdit->setText(ui.tokensListView->model()->data(index).toString());
}

View File

@ -0,0 +1,58 @@
/*
* RetroShare JSON API
* Copyright (C) 2018 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <retroshare-gui/configpage.h>
#include "ui_JsonApiPage.h"
class JsonApiPage : public ConfigPage
{
Q_OBJECT
public:
JsonApiPage(QWidget * parent = nullptr, Qt::WindowFlags flags = 0);
~JsonApiPage() {}
/** Loads the settings for this page */
virtual void load();
virtual QPixmap iconPixmap() const
{ return QPixmap(":/icons/svg/empty-circle.svg"); }
virtual QString pageName() const { return tr("JSON API"); }
virtual QString helpText() const;
/** Call this after start of libretroshare/Retroshare
* checks the settings and starts JSON API if required */
static bool checkStartJsonApi();
/** call this before shutdown of libretroshare
* it stops the JSON API if its running */
static void checkShutdownJsonApi();
public slots:
void onApplyClicked(bool);
void addTokenClicked(bool);
void removeTokenClicked(bool);
void tokenClicked(const QModelIndex& index);
private:
Ui::JsonApiPage ui; /// Qt Designer generated object
bool updateParams(QString &errmsg);
};

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JsonApiPage</class>
<widget class="QWidget" name="JsonApiPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>393</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="WebuiPageVLayout">
<item>
<widget class="QGroupBox" name="jsonApiGroupBox">
<property name="minimumSize">
<size>
<width>274</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>JSON API Server</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QCheckBox" name="enableCheckBox">
<property name="text">
<string>Enable RetroShare JSON API Server</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="portSpinBox">
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>9092</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Listen Address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="listenAddressLineEdit">
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Token:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="tokenLineEdit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ApiUser:ApiPassword&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addTokenPushButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeTokenPushButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Authenticated Tokens</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="tokensListView"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="applyConfigPushButton">
<property name="text">
<string>Restart JSON API Server to apply settings</string>
</property>
</widget>
</item>
<item>
<spacer name="mainVSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>17</width>
<height>632</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -23,11 +23,6 @@ resource_api::ApiServerLocal* WebuiPage::apiServerLocal = 0;
#endif
resource_api::RsControlModule* WebuiPage::controlModule = 0;
#ifdef RS_JSONAPI
# include <csignal>
JsonApiServer* WebuiPage::jsonApiServer = nullptr;
#endif // ifdef RS_JSONAPI
WebuiPage::WebuiPage(QWidget */*parent*/, Qt::WindowFlags /*flags*/)
{
@ -113,15 +108,6 @@ QString WebuiPage::helpText() const
apiServerLocal = new resource_api::ApiServerLocal(apiServer, resource_api::ApiServerLocal::serverPath());
#endif
#ifdef RS_JSONAPI
// Use same port of libresapi + 2
jsonApiServer = new JsonApiServer(
Settings->getWebinterfacePort() + 2,
Settings->getWebinterfaceAllowAllIps() ? "::" : "127.0.0.1",
[](int /*ec*/) { std::raise(SIGTERM); } );
jsonApiServer->start("WebuiPage::jsonApiServer");
#endif // ifdef RS_JSONAPI
return ok;
}
@ -142,10 +128,6 @@ QString WebuiPage::helpText() const
delete controlModule;
controlModule = 0;
}
#ifdef RS_JSONAPI
delete jsonApiServer;
jsonApiServer = nullptr;
#endif
}
/*static*/ void WebuiPage::showWebui()

View File

@ -3,10 +3,6 @@
#include <retroshare-gui/configpage.h>
#include "ui_WebuiPage.h"
#ifdef RS_JSONAPI
# include "jsonapi/jsonapi.h"
#endif
namespace resource_api{
class ApiServer;
class ApiServerMHD;
@ -59,7 +55,4 @@ private:
static resource_api::ApiServerLocal* apiServerLocal;
#endif
static resource_api::RsControlModule* controlModule;
#ifdef RS_JSONAPI
static JsonApiServer* jsonApiServer;
#endif
};

View File

@ -50,6 +50,10 @@
# include "WebuiPage.h"
#endif
#ifdef RS_JSONAPI
# include "JsonApiPage.h"
#endif
#define IMAGE_GENERAL ":/images/kcmsystem24.png"
#define ITEM_SPACING 2
@ -165,8 +169,12 @@ SettingsPage::initStackedWidget()
#ifdef ENABLE_WEBUI
addPage(new WebuiPage() );
#endif // ENABLE_WEBUI
// add widgets from plugins
#ifdef RS_JSONAPI
addPage(new JsonApiPage());
#endif
// add widgets from plugins
for(int i=0;i<rsPlugins->nbPlugins();++i)
{
RsPlugin *pl = rsPlugins->plugin(i) ;

View File

@ -1171,3 +1171,45 @@ void RshareSettings::setPageAlreadyDisplayed(const QString& page_name,bool b)
{
return setValueToGroup("PageAlreadyDisplayed",page_name,b);
}
#ifdef RS_JSONAPI
bool RshareSettings::getJsonApiEnabled()
{
return valueFromGroup("JsonApi", "enabled", false).toBool();
}
void RshareSettings::setJsonApiEnabled(bool enabled)
{
setValueToGroup("JsonApi", "enabled", enabled);
}
uint16_t RshareSettings::getJsonApiPort()
{
return valueFromGroup("JsonApi", "port", 9092).toUInt();
}
void RshareSettings::setJsonApiPort(uint16_t port)
{
setValueToGroup("JsonApi", "port", port);
}
QString RshareSettings::getJsonApiListenAddress()
{
return valueFromGroup("JsonApi", "listenAddress", "127.0.0.1").toString();
}
void RshareSettings::setJsonApiListenAddress(const QString& listenAddress)
{
setValueToGroup("JsonApi", "listenAddress", listenAddress);
}
QStringList RshareSettings::getJsonApiAuthTokens()
{
return valueFromGroup("JsonApi", "authTokens", QStringList()).toStringList();
}
void RshareSettings::setJsonApiAuthTokens(const QStringList& authTokens)
{
setValueToGroup("JsonApi", "authTokens", authTokens);
}
#endif // RS_JSONAPI

View File

@ -341,6 +341,20 @@ public:
bool getPageAlreadyDisplayed(const QString& page_code) ;
void setPageAlreadyDisplayed(const QString& page_code,bool b) ;
#ifdef RS_JSONAPI
bool getJsonApiEnabled();
void setJsonApiEnabled(bool enabled);
uint16_t getJsonApiPort();
void setJsonApiPort(uint16_t port);
QString getJsonApiListenAddress();
void setJsonApiListenAddress(const QString& listenAddress);
QStringList getJsonApiAuthTokens();
void setJsonApiAuthTokens(const QStringList& authTokens);
#endif // ifdef RS_JSONAPI
protected:
/** Default constructor. */
RshareSettings();

View File

@ -51,6 +51,10 @@
# include "gui/settings/WebuiPage.h"
#endif
#ifdef RS_JSONAPI
# include "gui/settings/JsonApiPage.h"
#endif // RS_JSONAPI
#include "TorControl/TorManager.h"
#include "TorControl/TorControlWindow.h"
@ -536,6 +540,10 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
WebuiPage::checkStartWebui();
#endif // ENABLE_WEBUI
#ifdef RS_JSONAPI
JsonApiPage::checkStartJsonApi();
#endif // RS_JSONAPI
// This is done using a timer, because the passphrase request from notify is asynchrouneous and therefore clearing the
// passphrase here makes it request for a passphrase when creating the default chat identity.
@ -545,6 +553,10 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
int ti = rshare.exec();
delete w ;
#ifdef RS_JSONAPI
JsonApiPage::checkShutdownJsonApi();
#endif // RS_JSONAPI
#ifdef ENABLE_WEBUI
WebuiPage::checkShutdownWebui();
#endif // ENABLE_WEBUI

View File

@ -17,6 +17,12 @@ libresapihttpserver {
FORMS *= gui/settings/WebuiPage.ui
}
rs_jsonapi {
HEADERS *= gui/settings/JsonApiPage.h
SOURCES *= gui/settings/JsonApiPage.cc
FORMS *= gui/settings/JsonApiPage.ui
}
!include("../../libretroshare/src/use_libretroshare.pri"):error("Including")
FORMS += TorControl/TorControlWindow.ui