/** * obs-websocket * Copyright (C) 2016-2017 Stéphane Lepin * Copyright (C) 2017 Mikhail Swift * * 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, see */ #include #include "Config.h" #include "Utils.h" #include "WSRequestHandler.h" QHash WSRequestHandler::messageMap{ { "GetVersion", WSRequestHandler::HandleGetVersion }, { "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired }, { "Authenticate", WSRequestHandler::HandleAuthenticate }, { "GetStats", WSRequestHandler::HandleGetStats }, { "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat }, { "GetVideoInfo", WSRequestHandler::HandleGetVideoInfo }, { "OpenProjector", WSRequestHandler::HandleOpenProjector }, { "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting }, { "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting }, { "BroadcastCustomMessage", WSRequestHandler::HandleBroadcastCustomMessage }, { "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene }, { "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene }, { "GetSceneList", WSRequestHandler::HandleGetSceneList }, { "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat { "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender }, { "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition }, { "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform }, { "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop }, { "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties }, { "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties }, { "ResetSceneItem", WSRequestHandler::HandleResetSceneItem }, { "DeleteSceneItem", WSRequestHandler::HandleDeleteSceneItem }, { "DuplicateSceneItem", WSRequestHandler::HandleDuplicateSceneItem }, { "ReorderSceneItems", WSRequestHandler::HandleReorderSceneItems }, { "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus }, { "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming }, { "StartStopRecording", WSRequestHandler::HandleStartStopRecording }, { "StartStreaming", WSRequestHandler::HandleStartStreaming }, { "StopStreaming", WSRequestHandler::HandleStopStreaming }, { "StartRecording", WSRequestHandler::HandleStartRecording }, { "StopRecording", WSRequestHandler::HandleStopRecording }, { "PauseRecording", WSRequestHandler::HandlePauseRecording }, { "ResumeRecording", WSRequestHandler::HandleResumeRecording }, { "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer }, { "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer }, { "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer }, { "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer }, { "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder }, { "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder }, { "GetTransitionList", WSRequestHandler::HandleGetTransitionList }, { "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition }, { "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition }, { "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration }, { "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration }, { "SetVolume", WSRequestHandler::HandleSetVolume }, { "GetVolume", WSRequestHandler::HandleGetVolume }, { "ToggleMute", WSRequestHandler::HandleToggleMute }, { "SetMute", WSRequestHandler::HandleSetMute }, { "GetMute", WSRequestHandler::HandleGetMute }, { "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset }, { "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset }, { "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources }, { "GetSourcesList", WSRequestHandler::HandleGetSourcesList }, { "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList }, { "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings }, { "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings }, { "TakeSourceScreenshot", WSRequestHandler::HandleTakeSourceScreenshot }, { "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters }, { "GetSourceFilterInfo", WSRequestHandler::HandleGetSourceFilterInfo }, { "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource }, { "RemoveFilterFromSource", WSRequestHandler::HandleRemoveFilterFromSource }, { "ReorderSourceFilter", WSRequestHandler::HandleReorderSourceFilter }, { "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter }, { "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings }, { "SetSourceFilterVisibility", WSRequestHandler::HandleSetSourceFilterVisibility }, { "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection }, { "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection }, { "ListSceneCollections", WSRequestHandler::HandleListSceneCollections }, { "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile }, { "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile }, { "ListProfiles", WSRequestHandler::HandleListProfiles }, { "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings }, { "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings }, { "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings }, #if BUILD_CAPTIONS { "SendCaptions", WSRequestHandler::HandleSendCaptions }, #endif { "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus }, { "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene }, { "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene }, { "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram }, { "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode }, { "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode }, { "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode }, { "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties }, { "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties }, { "SetTextFreetype2Properties", WSRequestHandler::HandleSetTextFreetype2Properties }, { "GetTextFreetype2Properties", WSRequestHandler::HandleGetTextFreetype2Properties }, { "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties }, { "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties }, { "ListOutputs", WSRequestHandler::HandleListOutputs }, { "GetOutputInfo", WSRequestHandler::HandleGetOutputInfo }, { "StartOutput", WSRequestHandler::HandleStartOutput }, { "StopOutput", WSRequestHandler::HandleStopOutput } }; QSet WSRequestHandler::authNotRequired { "GetVersion", "GetAuthRequired", "Authenticate" }; WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) : _messageId(0), _requestType(""), data(nullptr), _connProperties(connProperties) { } std::string WSRequestHandler::processIncomingMessage(std::string& textMessage) { if (GetConfig()->DebugEnabled) { blog(LOG_INFO, "Request >> '%s'", textMessage.c_str()); } OBSDataAutoRelease responseData = processRequest(textMessage); std::string response = obs_data_get_json(responseData); if (GetConfig()->DebugEnabled) { blog(LOG_INFO, "Response << '%s'", response.c_str()); } return response; } HandlerResponse WSRequestHandler::processRequest(std::string& textMessage){ std::string msgContainer(textMessage); const char* msg = msgContainer.c_str(); data = obs_data_create_from_json(msg); if (!data) { blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg); return SendErrorResponse("invalid JSON payload"); } if (!hasField("request-type") || !hasField("message-id")) { return SendErrorResponse("missing request parameters"); } _requestType = obs_data_get_string(data, "request-type"); _messageId = obs_data_get_string(data, "message-id"); if (GetConfig()->AuthRequired && (!authNotRequired.contains(_requestType)) && (!_connProperties.isAuthenticated())) { return SendErrorResponse("Not Authenticated"); } HandlerResponse (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]); if (!handlerFunc) { return SendErrorResponse("invalid request type"); } return handlerFunc(this); } WSRequestHandler::~WSRequestHandler() { } HandlerResponse WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) { return SendResponse("ok", additionalFields); } HandlerResponse WSRequestHandler::SendErrorResponse(QString errorMessage) { OBSDataAutoRelease fields = obs_data_create(); obs_data_set_string(fields, "error", errorMessage.toUtf8().constData()); return SendResponse("error", fields); } HandlerResponse WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) { return SendResponse("error", additionalFields); } HandlerResponse WSRequestHandler::SendResponse(const char* status, obs_data_t* fields) { obs_data_t* response = obs_data_create(); obs_data_set_string(response, "message-id", _messageId); obs_data_set_string(response, "status", status); if (fields) { obs_data_apply(response, fields); } return response; } bool WSRequestHandler::hasField(QString name, obs_data_type expectedFieldType, obs_data_number_type expectedNumberType) { if (!data || name.isEmpty() || name.isNull()) { return false; } OBSDataItemAutoRelease dataItem = obs_data_item_byname(data, name.toUtf8()); if (!dataItem) { return false; } if (expectedFieldType != OBS_DATA_NULL) { obs_data_type fieldType = obs_data_item_gettype(dataItem); if (fieldType != expectedFieldType) { return false; } if (fieldType == OBS_DATA_NUMBER && expectedNumberType != OBS_DATA_NUM_INVALID) { obs_data_number_type numberType = obs_data_item_numtype(dataItem); if (numberType != expectedNumberType) { return false; } } } return true; } bool WSRequestHandler::hasBool(QString fieldName) { return this->hasField(fieldName, OBS_DATA_BOOLEAN); } bool WSRequestHandler::hasString(QString fieldName) { return this->hasField(fieldName, OBS_DATA_STRING); } bool WSRequestHandler::hasNumber(QString fieldName, obs_data_number_type expectedNumberType) { return this->hasField(fieldName, OBS_DATA_NUMBER, expectedNumberType); } bool WSRequestHandler::hasInteger(QString fieldName) { return this->hasNumber(fieldName, OBS_DATA_NUM_INT); } bool WSRequestHandler::hasDouble(QString fieldName) { return this->hasNumber(fieldName, OBS_DATA_NUM_DOUBLE); } bool WSRequestHandler::hasArray(QString fieldName) { return this->hasField(fieldName, OBS_DATA_ARRAY); } bool WSRequestHandler::hasObject(QString fieldName) { return this->hasField(fieldName, OBS_DATA_OBJECT); }