Better session data persistence

This commit is contained in:
Stéphane Lepin 2016-11-11 18:13:23 +01:00
parent 1faccf8ee3
commit 338776ccd9
8 changed files with 206 additions and 37 deletions

49
Config.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "Config.h"
Config *Config::_instance = new Config();
Config::Config() {
AuthRequired = false;
Challenge = "";
Salt = "";
SettingsLoaded = false;
}
void Config::SetPassword(const char *password) {
}
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) {
Config *conf = static_cast<Config *>(private_data);
if (saving) {
obs_data_t *settings = obs_data_create();
obs_data_set_bool(settings, "auth_required", conf->AuthRequired);
obs_data_set_string(settings, "auth_hash", conf->Challenge);
obs_data_set_string(settings, "auth_salt", conf->Salt);
obs_data_set_obj(save_data, "obs-websocket", settings);
obs_data_release(settings);
}
else {
obs_data_t *settings = obs_data_get_obj(save_data, "obs-websocket");
if (!settings) {
settings = obs_data_create();
obs_data_set_bool(settings, "auth_required", conf->AuthRequired);
obs_data_set_string(settings, "auth_hash", conf->Challenge);
obs_data_set_string(settings, "auth_salt", conf->Salt);
}
conf->AuthRequired = obs_data_get_bool(settings, "auth_required");
conf->Challenge = obs_data_get_string(settings, "auth_hash");
conf->Salt = obs_data_get_string(settings, "auth_salt");
conf->SettingsLoaded = true;
obs_data_release(settings);
}
}
Config* Config::Current() {
return _instance;
}

22
Config.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <obs-module.h>
class Config {
public:
Config();
void SetPassword(const char *password);
bool AuthRequired;
const char *Challenge;
const char *Salt;
bool SettingsLoaded;
static void OBSSaveCallback(obs_data_t *save_data, bool saving, void *);
static Config* Current();
private:
static Config *_instance;
};
#endif // CONFIG_H

View File

@ -50,6 +50,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private
owner->OnRecordingStopped();
}
else if (event == OBS_FRONTEND_EVENT_EXIT) {
obs_frontend_save();
owner->OnExit();
}
}

View File

@ -1,8 +1,14 @@
#include "WSRequestHandler.h"
#include "obs-websocket.h"
#include "Config.h"
#include "Utils.h"
WSRequestHandler::WSRequestHandler(QWebSocket *client) {
WSRequestHandler::WSRequestHandler(QWebSocket *client) :
_authenticated(false),
_messageId(0),
_requestType(""),
_requestData(nullptr)
{
_client = client;
messageMap["GetVersion"] = WSRequestHandler::HandleGetVersion;
@ -21,12 +27,28 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) {
messageMap["ToggleMute"] = WSRequestHandler::ErrNotImplemented;
messageMap["GetVolumes"] = WSRequestHandler::ErrNotImplemented;
messageMap["SetVolume"] = WSRequestHandler::ErrNotImplemented;
messageMap["GetTransitionList"] = WSRequestHandler::HandleGetTransitionList;
messageMap["GetCurrentTransition"] = WSRequestHandler::HandleGetCurrentTransition;
messageMap["SetCurrentTransition"] = WSRequestHandler::HandleSetCurrentTransition;
authNotRequired.insert("GetVersion");
authNotRequired.insert("GetAuthRequired");
authNotRequired.insert("Authenticate");
blog(LOG_INFO, "[obs-websockets] new client connected from %s:%d", _client->peerAddress().toString().toLocal8Bit(), _client->peerPort());
connect(_client, &QWebSocket::textMessageReceived, this, &WSRequestHandler::processTextMessage);
connect(_client, &QWebSocket::disconnected, this, &WSRequestHandler::socketDisconnected);
}
void WSRequestHandler::handleMessage(const char *message) {
_requestData = obs_data_create_from_json(message);
void WSRequestHandler::processTextMessage(QString textMessage) {
QByteArray msgData = textMessage.toLocal8Bit();
const char *msg = msgData;
_requestData = obs_data_create_from_json(msg);
if (!_requestData) {
blog(LOG_ERROR, "[obs-websockets] invalid JSON payload for '%s'", message);
blog(LOG_ERROR, "[obs-websockets] invalid JSON payload for '%s'", msg);
SendErrorResponse("invalid JSON payload");
return;
}
@ -34,6 +56,14 @@ void WSRequestHandler::handleMessage(const char *message) {
_requestType = obs_data_get_string(_requestData, "request-type");
_messageId = obs_data_get_int(_requestData, "message-id");
if (Config::Current()->AuthRequired
&& !_authenticated
&& authNotRequired.find(_requestType) == authNotRequired.end())
{
SendErrorResponse("Not Authenticated");
return;
}
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
if (handlerFunc != NULL) {
@ -44,6 +74,17 @@ void WSRequestHandler::handleMessage(const char *message) {
}
}
void WSRequestHandler::socketDisconnected() {
blog(LOG_INFO, "[obs-websockets] client %s:%d disconnected", _client->peerAddress().toString().toStdString(), _client->peerPort());
_client->deleteLater();
emit disconnected();
}
void WSRequestHandler::sendTextMessage(QString textMessage) {
_client->sendTextMessage(textMessage);
}
WSRequestHandler::~WSRequestHandler() {
if (_requestData != NULL) {
obs_data_release(_requestData);
@ -78,21 +119,20 @@ void WSRequestHandler::SendErrorResponse(const char *errorMessage) {
void WSRequestHandler::HandleGetVersion(WSRequestHandler *owner) {
obs_data_t *data = obs_data_create();
obs_data_set_double(data, "version", OBS_WEBSOCKET_VERSION);
owner->SendOKResponse(data);
obs_data_release(data);
}
void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *owner) {
bool authRequired = false; // Auth isn't implemented yet
bool authRequired = Config::Current()->AuthRequired;
obs_data_t *data = obs_data_create();
obs_data_set_bool(data, "authRequired", authRequired);
if (authRequired) {
// Just here for protocol doc
obs_data_set_string(data, "challenge", "");
obs_data_set_string(data, "salt", "");
obs_data_set_string(data, "challenge", Config::Current()->Challenge);
obs_data_set_string(data, "salt", Config::Current()->Salt);
}
owner->SendOKResponse(data);
@ -102,11 +142,14 @@ void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *owner) {
void WSRequestHandler::HandleAuthenticate(WSRequestHandler *owner) {
const char *auth = obs_data_get_string(owner->_requestData, "auth");
if (!auth) {
if (!auth || strlen(auth) < 1) {
owner->SendErrorResponse("auth not specified!");
return;
}
// TODO : Implement auth here
owner->_authenticated = true;
owner->SendOKResponse();
}
@ -203,6 +246,52 @@ void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner) {
owner->SendOKResponse();
}
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) {
obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList);
obs_data_array_t* transitions = obs_data_array_create();
for (size_t i = 0; i < (&transitionList)->sources.num; i++) {
obs_source_t* transition = (&transitionList)->sources.array[i];
obs_data_t *obj = obs_data_create();
obs_data_set_string(obj, "name", obs_source_get_name(transition));
obs_data_array_push_back(transitions, obj);
}
obs_frontend_source_list_free(&transitionList);
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "current-transition", obs_source_get_name(obs_frontend_get_current_transition()));
obs_data_set_array(response, "transitions", transitions);
owner->SendOKResponse(response);
obs_data_release(response);
}
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) {
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "name", obs_source_get_name(obs_frontend_get_current_transition()));
owner->SendOKResponse(response);
obs_data_release(response);
}
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) {
const char *name = obs_data_get_string(owner->_requestData, "transition-name");
obs_source_t *transition = obs_get_source_by_name(name);
if (transition) {
obs_frontend_set_current_transition(transition);
owner->SendOKResponse();
obs_source_release(transition);
}
else {
owner->SendErrorResponse("requested transition does not exist");
}
}
void WSRequestHandler::ErrNotImplemented(WSRequestHandler *owner) {
owner->SendErrorResponse("not implemented");
}

View File

@ -2,23 +2,35 @@
#define WSREQUESTHANDLER_H
#include <map>
#include <set>
#include <QtWebSockets/QWebSocket>
#include <obs-frontend-api.h>
class WSRequestHandler
class WSRequestHandler : public QObject
{
Q_OBJECT
public:
explicit WSRequestHandler(QWebSocket *client);
~WSRequestHandler();
void handleMessage(const char* message);
void sendTextMessage(QString textMessage);
private Q_SLOTS:
void processTextMessage(QString textMessage);
void socketDisconnected();
Q_SIGNALS:
void disconnected();
private:
QWebSocket *_client;
long _messageId;
bool _authenticated;
unsigned long _messageId;
const char *_requestType;
obs_data_t *_requestData;
std::map<std::string, void(*)(WSRequestHandler*)> messageMap;
std::set<std::string> authNotRequired;
void SendOKResponse(obs_data_t *additionalFields = NULL);
void SendErrorResponse(const char *errorMessage);
@ -27,13 +39,19 @@ class WSRequestHandler
static void HandleGetVersion(WSRequestHandler *owner);
static void HandleGetAuthRequired(WSRequestHandler *owner);
static void HandleAuthenticate(WSRequestHandler *owner);
static void HandleSetCurrentScene(WSRequestHandler *owner);
static void HandleGetCurrentScene(WSRequestHandler *owner);
static void HandleGetSceneList(WSRequestHandler *owner);
static void HandleSetSourceRender(WSRequestHandler *owner);
static void HandleGetStreamingStatus(WSRequestHandler *owner);
static void HandleStartStopStreaming(WSRequestHandler *owner);
static void HandleStartStopRecording(WSRequestHandler *owner);
static void HandleGetTransitionList(WSRequestHandler *owner);
static void HandleGetCurrentTransition(WSRequestHandler *owner);
static void HandleSetCurrentTransition(WSRequestHandler *owner);
};
#endif // WSPROTOCOL_H

View File

@ -31,7 +31,7 @@ WSServer::~WSServer()
void WSServer::broadcast(QString message)
{
Q_FOREACH(QWebSocket *pClient, _clients) {
Q_FOREACH(WSRequestHandler *pClient, _clients) {
pClient->sendTextMessage(message);
}
}
@ -40,31 +40,17 @@ void WSServer::onNewConnection()
{
QWebSocket *pSocket = _wsServer->nextPendingConnection();
blog(LOG_INFO, "[obs-websockets] new client connected from %s:%d", pSocket->peerAddress().toString().toStdString(), pSocket->peerPort());
if (pSocket) {
WSRequestHandler *pHandler = new WSRequestHandler(pSocket);
connect(pSocket, &QWebSocket::textMessageReceived, this, &WSServer::processTextMessage);
connect(pSocket, &QWebSocket::disconnected, this, &WSServer::socketDisconnected);
_clients << pSocket;
}
void WSServer::processTextMessage(QString textMessage) {
QWebSocket *pSender = qobject_cast<QWebSocket *>(sender());
if (pSender) {
const char *msg = textMessage.toLocal8Bit();
blog(LOG_INFO, "[obs-websockets] new message : %s", msg);
WSRequestHandler *handler = new WSRequestHandler(pSender);
handler->handleMessage(msg);
delete handler;
connect(pHandler, &WSRequestHandler::disconnected, this, &WSServer::socketDisconnected);
_clients << pHandler;
}
}
void WSServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
blog(LOG_INFO, "[obs-websockets] client %s:%d disconnected", pClient->peerAddress().toString().toStdString(), pClient->peerPort());
WSRequestHandler *pClient = qobject_cast<WSRequestHandler *>(sender());
if (pClient) {
_clients.removeAll(pClient);

View File

@ -4,6 +4,7 @@
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QByteArray>
#include "WSRequestHandler.h"
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
@ -19,12 +20,11 @@ class WSServer : public QObject
private Q_SLOTS:
void onNewConnection();
void processTextMessage(QString textMessage);
void socketDisconnected();
private:
QWebSocketServer *_wsServer;
QList<QWebSocket *> _clients;
QList<WSRequestHandler *> _clients;
};
#endif // WSSERVER_H

View File

@ -1,8 +1,10 @@
#include <obs-module.h>
#include <obs-frontend-api.h>
#include "obs-websocket.h"
#include "WSEvents.h"
#include "WSServer.h"
#include "Config.h"
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
@ -12,11 +14,13 @@ WSServer *server;
bool obs_module_load(void)
{
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %f)", OBS_WEBSOCKET_VERSION);
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %.2f)", OBS_WEBSOCKET_VERSION);
server = new WSServer(4444);
eventHandler = new WSEvents(server);
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current());
return true;
}