Updated locale variables names + WIP Dynamic Server Settings

This commit is contained in:
Palakis 2017-02-23 21:01:24 +01:00
parent f8b1cae0c9
commit 3d68b7c9e5
12 changed files with 224 additions and 93 deletions

View File

@ -19,22 +19,41 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <mbedtls/base64.h> #include <mbedtls/base64.h>
#include <mbedtls/sha256.h> #include <mbedtls/sha256.h>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <util/config-file.h>
#include "Config.h" #include "Config.h"
#define CONFIG_SECTION_NAME "obs-websocket" #define SECTION_NAME "obs-websocket"
#define CONFIG_PARAM_SECRET "auth_hash" #define PARAM_ENABLE "server_enabled"
#define CONFIG_PARAM_SALT "auth_salt" #define PARAM_PORT "server_port"
#define CONFIG_PARAM_AUTHREQUIRED "auth_required" #define PARAM_SECRET "auth_hash"
#define PARAM_SALT "auth_salt"
#define PARAM_AUTHREQUIRED "auth_required"
Config *Config::_instance = new Config(); Config *Config::_instance = new Config();
Config::Config() { Config::Config()
{
// Default settings // Default settings
ServerEnabled = true;
ServerPort = 4444;
AuthRequired = false; AuthRequired = false;
Secret = ""; Secret = "";
Salt = ""; Salt = "";
SettingsLoaded = false; SettingsLoaded = false;
// OBS Config defaults
config_t* obs_config = obs_frontend_get_global_config();
if (obs_config)
{
config_set_default_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_default_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_string(obs_config, SECTION_NAME, PARAM_SECRET, Secret);
config_set_default_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
}
mbedtls_entropy_init(&entropy); mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&rng); mbedtls_ctr_drbg_init(&rng);
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0); mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
@ -43,12 +62,40 @@ Config::Config() {
SessionChallenge = GenerateSalt(); SessionChallenge = GenerateSalt();
} }
Config::~Config() { Config::~Config()
{
mbedtls_ctr_drbg_free(&rng); mbedtls_ctr_drbg_free(&rng);
mbedtls_entropy_free(&entropy); mbedtls_entropy_free(&entropy);
} }
const char* Config::GenerateSalt() { void Config::Load()
{
config_t* obs_config = obs_frontend_get_global_config();
ServerEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ENABLE);
ServerPort = config_get_uint(obs_config, SECTION_NAME, PARAM_PORT);
AuthRequired = config_get_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED);
Secret = config_get_string(obs_config, SECTION_NAME, PARAM_SECRET);
Salt = config_get_string(obs_config, SECTION_NAME, PARAM_SALT);
}
void Config::Save()
{
config_t* obs_config = obs_frontend_get_global_config();
config_set_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obs_config, SECTION_NAME, PARAM_SECRET, Secret);
config_set_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
config_save(obs_config);
}
const char* Config::GenerateSalt()
{
// Generate 32 random chars // Generate 32 random chars
unsigned char *random_chars = (unsigned char *)bzalloc(32); unsigned char *random_chars = (unsigned char *)bzalloc(32);
mbedtls_ctr_drbg_random(&rng, random_chars, 32); mbedtls_ctr_drbg_random(&rng, random_chars, 32);
@ -63,7 +110,8 @@ const char* Config::GenerateSalt() {
return (char *)salt; return (char *)salt;
} }
const char* Config::GenerateSecret(const char *password, const char *salt) { const char* Config::GenerateSecret(const char *password, const char *salt)
{
size_t passwordLength = strlen(password); size_t passwordLength = strlen(password);
size_t saltLength = strlen(salt); size_t saltLength = strlen(salt);
@ -88,7 +136,8 @@ const char* Config::GenerateSecret(const char *password, const char *salt) {
return (char*)challenge; return (char*)challenge;
} }
void Config::SetPassword(const char *password) { void Config::SetPassword(const char *password)
{
const char *new_salt = GenerateSalt(); const char *new_salt = GenerateSalt();
const char *new_challenge = GenerateSecret(password, new_salt); const char *new_challenge = GenerateSecret(password, new_salt);
@ -96,7 +145,8 @@ void Config::SetPassword(const char *password) {
this->Secret = new_challenge; this->Secret = new_challenge;
} }
bool Config::CheckAuth(const char *response) { bool Config::CheckAuth(const char *response)
{
size_t secretLength = strlen(this->Secret); size_t secretLength = strlen(this->Secret);
size_t sessChallengeLength = strlen(this->SessionChallenge); size_t sessChallengeLength = strlen(this->SessionChallenge);
@ -125,29 +175,7 @@ bool Config::CheckAuth(const char *response) {
} }
} }
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) { Config* Config::Current()
Config *conf = static_cast<Config *>(private_data); {
if (saving) {
obs_data_t *settings = obs_data_create();
obs_data_set_bool(settings, CONFIG_PARAM_AUTHREQUIRED, conf->AuthRequired);
obs_data_set_string(settings, CONFIG_PARAM_SECRET, conf->Secret);
obs_data_set_string(settings, CONFIG_PARAM_SALT, conf->Salt);
obs_data_set_obj(save_data, CONFIG_SECTION_NAME, settings);
}
else {
obs_data_t *settings = obs_data_get_obj(save_data, CONFIG_SECTION_NAME);
if (settings) {
conf->AuthRequired = obs_data_get_bool(settings, CONFIG_PARAM_AUTHREQUIRED);
conf->Secret = obs_data_get_string(settings, CONFIG_PARAM_SECRET);
conf->Salt = obs_data_get_string(settings, CONFIG_PARAM_SALT);
conf->SettingsLoaded = true;
}
}
}
Config* Config::Current() {
return _instance; return _instance;
} }

View File

@ -23,15 +23,21 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <mbedtls/entropy.h> #include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
class Config { class Config
{
public: public:
Config(); Config();
~Config(); ~Config();
void Load();
void Save();
void SetPassword(const char *password); void SetPassword(const char *password);
bool CheckAuth(const char *userChallenge); bool CheckAuth(const char *userChallenge);
const char* GenerateSalt(); const char* GenerateSalt();
static const char* GenerateSecret(const char *password, const char *salt); static const char* GenerateSecret(const char *password, const char *salt);
static void OBSSaveCallback(obs_data_t *save_data, bool saving, void *);
bool ServerEnabled;
uint64_t ServerPort;
bool AuthRequired; bool AuthRequired;
const char *Secret; const char *Secret;

View File

@ -19,8 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "WSEvents.h" #include "WSEvents.h"
WSEvents::WSEvents(WSServer *server) { WSEvents::WSEvents(WSServer *srv) {
_srv = server; _srv = srv;
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this); obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
QTimer *statusTimer = new QTimer(); QTimer *statusTimer = new QTimer();
@ -39,6 +39,9 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private
{ {
WSEvents *owner = static_cast<WSEvents *>(private_data); WSEvents *owner = static_cast<WSEvents *>(private_data);
if (!owner->_srv)
return;
// TODO : implement SourceChanged, SourceOrderChanged and RepopulateSources // TODO : implement SourceChanged, SourceOrderChanged and RepopulateSources
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) { if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) {

View File

@ -31,7 +31,7 @@ class WSEvents : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit WSEvents(WSServer *server); explicit WSEvents(WSServer *srv);
~WSEvents(); ~WSEvents();
static void FrontendEventHandler(enum obs_frontend_event event, void *private_data); static void FrontendEventHandler(enum obs_frontend_event event, void *private_data);

View File

@ -29,7 +29,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
QT_USE_NAMESPACE QT_USE_NAMESPACE
WSServer::WSServer(quint16 port, QObject *parent) : WSServer* WSServer::Instance = new WSServer();
WSServer::WSServer(QObject *parent) :
QObject(parent), QObject(parent),
_wsServer(Q_NULLPTR), _wsServer(Q_NULLPTR),
_clients(), _clients(),
@ -44,6 +46,22 @@ WSServer::WSServer(quint16 port, QObject *parent) :
_wsServer->moveToThread(_serverThread); _wsServer->moveToThread(_serverThread);
_serverThread->start(); _serverThread->start();
}
WSServer::~WSServer()
{
Stop();
delete _serverThread;
}
void WSServer::Start(quint16 port)
{
if (port == _wsServer->serverPort())
return;
if(_wsServer->isListening())
Stop();
bool serverStarted = _wsServer->listen(QHostAddress::Any, port); bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
if (serverStarted) if (serverStarted)
@ -52,15 +70,18 @@ WSServer::WSServer(quint16 port, QObject *parent) :
} }
} }
WSServer::~WSServer() void WSServer::Stop()
{ {
_wsServer->close();
_clMutex.lock(); _clMutex.lock();
Q_FOREACH(QWebSocket *pClient, _clients)
{
pClient->close();
}
qDeleteAll(_clients.begin(), _clients.end()); qDeleteAll(_clients.begin(), _clients.end());
_clMutex.unlock(); _clMutex.unlock();
delete _serverThread; _wsServer->close();
} }
void WSServer::broadcast(QString message) void WSServer::broadcast(QString message)

View File

@ -34,9 +34,12 @@ class WSServer : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit WSServer(quint16 port, QObject *parent = Q_NULLPTR); explicit WSServer(QObject *parent = Q_NULLPTR);
virtual ~WSServer(); virtual ~WSServer();
void Start(quint16 port);
void Stop();
void broadcast(QString message); void broadcast(QString message);
static WSServer* Instance;
private Q_SLOTS: private Q_SLOTS:
void onNewConnection(); void onNewConnection();

View File

@ -1,4 +1,4 @@
Menu.SettingsItem="Websocket-Server Einstellungen" OBSWebsocket.Menu.SettingsItem="Websocket-Server Einstellungen"
Settings.DialogTitle="Websocket-Server Einstellungen" OBSWebsocket.Settings.DialogTitle="Websocket-Server Einstellungen"
Settings.AuthRequired="Authentifizierung erforderlich" OBSWebsocket.Settings.AuthRequired="Authentifizierung erforderlich"
Settings.Password="Passwort" OBSWebsocket.Settings.Password="Passwort"

View File

@ -1,4 +1,6 @@
Menu.SettingsItem="Websocket server settings" OBSWebsocket.Menu.SettingsItem="Websocket server settings"
Settings.DialogTitle="obs-websocket" OBSWebsocket.Settings.DialogTitle="obs-websocket"
Settings.AuthRequired="Enable authentication" OBSWebsocket.Settings.ServerEnable="Enable Websocket server"
Settings.Password="Password" OBSWebsocket.Settings.ServerPort="Server Port"
OBSWebsocket.Settings.AuthRequired="Enable authentication"
OBSWebsocket.Settings.Password="Password"

View File

@ -1,4 +1,6 @@
Menu.SettingsItem="Paramètres du serveur Websocket" OBSWebsocket.Menu.SettingsItem="Paramètres du serveur Websocket"
Settings.DialogTitle="obs-websocket" OBSWebsocket.Settings.DialogTitle="obs-websocket"
Settings.AuthRequired="Activer l'authentification" OBSWebsocket.Settings.ServerEnable="Activer le serveur Websockets"
Settings.Password="Mot de passe" OBSWebsocket.Settings.ServerPort="Port du serveur"
OBSWebsocket.Settings.AuthRequired="Activer l'authentification"
OBSWebsocket.Settings.Password="Mot de passe"

View File

@ -17,9 +17,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include "obs-websocket.h"
#include "Config.h"
#include "WSServer.h"
#include "settings-dialog.h" #include "settings-dialog.h"
#include "ui_settings-dialog.h" #include "ui_settings-dialog.h"
#include "Config.h"
#define CHANGE_ME "changeme" #define CHANGE_ME "changeme"
@ -35,12 +38,19 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
AuthCheckboxChanged(); AuthCheckboxChanged();
} }
void SettingsDialog::showEvent(QShowEvent *event) { void SettingsDialog::showEvent(QShowEvent *event)
ui->authRequired->setChecked(Config::Current()->AuthRequired); {
Config* conf = Config::Current();
ui->serverEnabled->setChecked(conf->ServerEnabled);
ui->serverPort->setValue(conf->ServerPort);
ui->authRequired->setChecked(conf->AuthRequired);
ui->password->setText(CHANGE_ME); ui->password->setText(CHANGE_ME);
} }
void SettingsDialog::ToggleShowHide() { void SettingsDialog::ToggleShowHide()
{
if (!isVisible()) { if (!isVisible()) {
setVisible(true); setVisible(true);
} }
@ -49,7 +59,8 @@ void SettingsDialog::ToggleShowHide() {
} }
} }
void SettingsDialog::AuthCheckboxChanged() { void SettingsDialog::AuthCheckboxChanged()
{
if (ui->authRequired->isChecked()) { if (ui->authRequired->isChecked()) {
ui->password->setEnabled(true); ui->password->setEnabled(true);
} }
@ -58,28 +69,48 @@ void SettingsDialog::AuthCheckboxChanged() {
} }
} }
void SettingsDialog::FormAccepted() { void SettingsDialog::FormAccepted()
if (ui->authRequired->isChecked()) { {
if (ui->password->text() != CHANGE_ME) { Config* conf = Config::Current();
conf->ServerEnabled = ui->serverEnabled->isChecked();
conf->ServerPort = ui->serverPort->value();
if (ui->authRequired->isChecked())
{
if (ui->password->text() != CHANGE_ME)
{
QByteArray pwd = ui->password->text().toLocal8Bit(); QByteArray pwd = ui->password->text().toLocal8Bit();
const char *new_password = pwd; const char *new_password = pwd;
blog(LOG_INFO, "new password : %s", new_password); blog(LOG_INFO, "new password : %s", new_password);
Config::Current()->SetPassword(new_password); conf->SetPassword(new_password);
} }
if (strcmp(Config::Current()->Secret, "") != 0) { if (strcmp(Config::Current()->Secret, "") != 0)
Config::Current()->AuthRequired = true; {
conf->AuthRequired = true;
} }
else { else
Config::Current()->AuthRequired = false; {
conf->AuthRequired = false;
} }
} }
else { else
Config::Current()->AuthRequired = false; {
conf->AuthRequired = false;
} }
obs_frontend_save(); conf->Save();
if (conf->ServerEnabled)
{
WSServer::Instance->Start(conf->ServerPort);
}
else
{
WSServer::Instance->Stop();
}
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>354</width> <width>407</width>
<height>110</height> <height>155</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -17,7 +17,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Settings.DialogTitle</string> <string>OBSWebsocket.Settings.DialogTitle</string>
</property> </property>
<property name="sizeGripEnabled"> <property name="sizeGripEnabled">
<bool>false</bool> <bool>false</bool>
@ -28,24 +28,54 @@
</property> </property>
<item> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="3" column="0"> <item row="3" column="1">
<widget class="QLabel" name="lbl_password"> <widget class="QCheckBox" name="authRequired">
<property name="text"> <property name="text">
<string>Settings.Password</string> <string>OBSWebsocket.Settings.AuthRequired</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="0">
<widget class="QLabel" name="lbl_password">
<property name="text">
<string>OBSWebsocket.Settings.Password</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="password"> <widget class="QLineEdit" name="password">
<property name="echoMode"> <property name="echoMode">
<enum>QLineEdit::Password</enum> <enum>QLineEdit::Password</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="authRequired"> <widget class="QCheckBox" name="serverEnabled">
<property name="text"> <property name="text">
<string>Settings.AuthRequired</string> <string>OBSWebsocket.Settings.ServerEnable</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_serverPort">
<property name="text">
<string>OBSWebsocket.Settings.ServerPort</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="serverPort">
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>4444</number>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -21,8 +21,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QAction> #include <QAction>
#include "obs-websocket.h" #include "obs-websocket.h"
#include "WSEvents.h"
#include "WSServer.h" #include "WSServer.h"
#include "WSEvents.h"
#include "Config.h" #include "Config.h"
#include "forms/settings-dialog.h" #include "forms/settings-dialog.h"
@ -30,19 +30,21 @@ OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
WSEvents *eventHandler; WSEvents *eventHandler;
WSServer *server;
SettingsDialog *settings_dialog; SettingsDialog *settings_dialog;
bool obs_module_load(void) bool obs_module_load(void)
{ {
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); // Core setup
Config* config = Config::Current();
config->Load();
server = new WSServer(4444); if (config->ServerEnabled)
eventHandler = new WSEvents(server); WSServer::Instance->Start(config->ServerPort);
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current()); eventHandler = new WSEvents(WSServer::Instance);
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(obs_module_text("Menu.SettingsItem")); // UI setup
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(obs_module_text("OBSWebsocket.Menu.SettingsItem"));
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
settings_dialog = new SettingsDialog(); settings_dialog = new SettingsDialog();
@ -53,6 +55,9 @@ bool obs_module_load(void)
}; };
menu_action->connect(menu_action, &QAction::triggered, menu_cb); menu_action->connect(menu_action, &QAction::triggered, menu_cb);
// Loading finished
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
return true; return true;
} }