summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/json-rpc/JSONRPC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/interfaces/json-rpc/JSONRPC.cpp')
-rw-r--r--xbmc/interfaces/json-rpc/JSONRPC.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/xbmc/interfaces/json-rpc/JSONRPC.cpp b/xbmc/interfaces/json-rpc/JSONRPC.cpp
new file mode 100644
index 0000000..4b86188
--- /dev/null
+++ b/xbmc/interfaces/json-rpc/JSONRPC.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "JSONRPC.h"
+
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "ServiceDescription.h"
+#include "TextureDatabase.h"
+#include "addons/Addon.h"
+#include "addons/IAddon.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "dbwrappers/DatabaseQuery.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/WindowTranslator.h"
+#include "input/actions/ActionTranslator.h"
+#include "interfaces/AnnouncementManager.h"
+#include "playlists/SmartPlayList.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <string.h>
+
+using namespace JSONRPC;
+
+bool CJSONRPC::m_initialized = false;
+
+void CJSONRPC::Initialize()
+{
+ if (m_initialized)
+ return;
+
+ // Add some types/enums at runtime
+ std::vector<std::string> enumList;
+ for (int addonType = static_cast<int>(ADDON::AddonType::UNKNOWN);
+ addonType < static_cast<int>(ADDON::AddonType::MAX_TYPES); addonType++)
+ enumList.push_back(
+ ADDON::CAddonInfo::TranslateType(static_cast<ADDON::AddonType>(addonType), false));
+ CJSONServiceDescription::AddEnum("Addon.Types", enumList);
+
+ enumList.clear();
+ CActionTranslator::GetActions(enumList);
+ CJSONServiceDescription::AddEnum("Input.Action", enumList);
+
+ enumList.clear();
+ CWindowTranslator::GetWindows(enumList);
+ CJSONServiceDescription::AddEnum("GUI.Window", enumList);
+
+ // filter-related enums
+ std::vector<std::string> smartplaylistList;
+ CDatabaseQueryRule::GetAvailableOperators(smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Operators", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("movies", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Movies", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("tvshows", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.TVShows", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("episodes", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Episodes", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("musicvideos", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.MusicVideos", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("artists", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Artists", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("albums", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Albums", smartplaylistList);
+
+ smartplaylistList.clear();
+ CSmartPlaylist::GetAvailableFields("songs", smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Songs", smartplaylistList);
+
+ smartplaylistList.clear();
+ CTextureRule::GetAvailableFields(smartplaylistList);
+ CJSONServiceDescription::AddEnum("List.Filter.Fields.Textures", smartplaylistList);
+
+ unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
+
+ for (unsigned int index = 0; index < size; index++)
+ CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
+
+ size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
+
+ for (unsigned int index = 0; index < size; index++)
+ CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
+
+ size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
+
+ for (unsigned int index = 0; index < size; index++)
+ CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
+
+ CJSONServiceDescription::ResolveReferences();
+
+ m_initialized = true;
+ CLog::Log(LOGINFO, "JSONRPC v{}: Successfully initialized",
+ CJSONServiceDescription::GetVersion());
+}
+
+void CJSONRPC::Cleanup()
+{
+ CJSONServiceDescription::Cleanup();
+ m_initialized = false;
+}
+
+JSONRPC_STATUS CJSONRPC::Introspect(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ return CJSONServiceDescription::Print(result, transport, client,
+ parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(),
+ parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean());
+}
+
+JSONRPC_STATUS CJSONRPC::Version(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ result["version"]["major"] = 0;
+ result["version"]["minor"] = 0;
+ result["version"]["patch"] = 0;
+
+ const char* version = CJSONServiceDescription::GetVersion();
+ if (version != NULL)
+ {
+ std::vector<std::string> parts = StringUtils::Split(version, ".");
+ if (!parts.empty())
+ result["version"]["major"] = (int)strtol(parts[0].c_str(), NULL, 10);
+ if (parts.size() > 1)
+ result["version"]["minor"] = (int)strtol(parts[1].c_str(), NULL, 10);
+ if (parts.size() > 2)
+ result["version"]["patch"] = (int)strtol(parts[2].c_str(), NULL, 10);
+ }
+
+ return OK;
+}
+
+JSONRPC_STATUS CJSONRPC::Permission(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ int flags = client->GetPermissionFlags();
+
+ for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
+ result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
+
+ return OK;
+}
+
+JSONRPC_STATUS CJSONRPC::Ping(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ CVariant temp = "pong";
+ result.swap(temp);
+ return OK;
+}
+
+JSONRPC_STATUS CJSONRPC::GetConfiguration(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ int flags = client->GetAnnouncementFlags();
+
+ for (int i = 1; i <= ANNOUNCEMENT::ANNOUNCE_ALL; i *= 2)
+ result["notifications"][AnnouncementFlagToString((ANNOUNCEMENT::AnnouncementFlag)i)] = (flags & i) == i;
+
+ return OK;
+}
+
+JSONRPC_STATUS CJSONRPC::SetConfiguration(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ int flags = 0;
+ int oldFlags = client->GetAnnouncementFlags();
+
+ if (parameterObject.isMember("notifications"))
+ {
+ CVariant notifications = parameterObject["notifications"];
+ if ((notifications["Player"].isNull() && (oldFlags & ANNOUNCEMENT::Player)) ||
+ (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
+ flags |= ANNOUNCEMENT::Player;
+ if ((notifications["Playlist"].isNull() && (oldFlags & ANNOUNCEMENT::Playlist)) ||
+ (notifications["Playlist"].isBoolean() && notifications["Playlist"].asBoolean()))
+ flags |= ANNOUNCEMENT::Playlist;
+ if ((notifications["GUI"].isNull() && (oldFlags & ANNOUNCEMENT::GUI)) ||
+ (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
+ flags |= ANNOUNCEMENT::GUI;
+ if ((notifications["System"].isNull() && (oldFlags & ANNOUNCEMENT::System)) ||
+ (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
+ flags |= ANNOUNCEMENT::System;
+ if ((notifications["VideoLibrary"].isNull() && (oldFlags & ANNOUNCEMENT::VideoLibrary)) ||
+ (notifications["VideoLibrary"].isBoolean() && notifications["VideoLibrary"].asBoolean()))
+ flags |= ANNOUNCEMENT::VideoLibrary;
+ if ((notifications["AudioLibrary"].isNull() && (oldFlags & ANNOUNCEMENT::AudioLibrary)) ||
+ (notifications["AudioLibrary"].isBoolean() && notifications["AudioLibrary"].asBoolean()))
+ flags |= ANNOUNCEMENT::AudioLibrary;
+ if ((notifications["Application"].isNull() && (oldFlags & ANNOUNCEMENT::Other)) ||
+ (notifications["Application"].isBoolean() && notifications["Application"].asBoolean()))
+ flags |= ANNOUNCEMENT::Application;
+ if ((notifications["Input"].isNull() && (oldFlags & ANNOUNCEMENT::Input)) ||
+ (notifications["Input"].isBoolean() && notifications["Input"].asBoolean()))
+ flags |= ANNOUNCEMENT::Input;
+ if ((notifications["Other"].isNull() && (oldFlags & ANNOUNCEMENT::Other)) ||
+ (notifications["Other"].isBoolean() && notifications["Other"].asBoolean()))
+ flags |= ANNOUNCEMENT::Other;
+ }
+
+ if (!client->SetAnnouncementFlags(flags))
+ return BadPermission;
+
+ return GetConfiguration(method, transport, client, parameterObject, result);
+}
+
+JSONRPC_STATUS CJSONRPC::NotifyAll(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
+{
+ if (parameterObject["data"].isNull())
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Other,
+ parameterObject["sender"].asString(),
+ parameterObject["message"].asString());
+ else
+ {
+ CVariant data = parameterObject["data"];
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Other,
+ parameterObject["sender"].asString(),
+ parameterObject["message"].asString(), data);
+ }
+
+ return ACK;
+}
+
+std::string CJSONRPC::MethodCall(const std::string &inputString, ITransportLayer *transport, IClient *client)
+{
+ CVariant inputroot, outputroot, result;
+ bool hasResponse = false;
+
+ CLog::Log(LOGDEBUG, LOGJSONRPC, "JSONRPC: Incoming request: {}", inputString);
+
+ if (CJSONVariantParser::Parse(inputString, inputroot) && !inputroot.isNull())
+ {
+ if (inputroot.isArray())
+ {
+ if (inputroot.size() <= 0)
+ {
+ CLog::Log(LOGERROR, "JSONRPC: Empty batch call");
+ BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
+ hasResponse = true;
+ }
+ else
+ {
+ for (CVariant::const_iterator_array itr = inputroot.begin_array();
+ itr != inputroot.end_array(); ++itr)
+ {
+ CVariant response;
+ if (HandleMethodCall(*itr, response, transport, client))
+ {
+ outputroot.append(response);
+ hasResponse = true;
+ }
+ }
+ }
+ }
+ else
+ hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "JSONRPC: Failed to parse '{}'", inputString);
+ BuildResponse(inputroot, ParseError, CVariant(), outputroot);
+ hasResponse = true;
+ }
+
+ std::string str;
+ if (hasResponse)
+ CJSONVariantWriter::Write(outputroot, str, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonOutputCompact);
+
+ return str;
+}
+
+bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
+{
+ JSONRPC_STATUS errorCode = OK;
+ CVariant result;
+ bool isNotification = false;
+
+ if (IsProperJSONRPC(request))
+ {
+ isNotification = !request.isMember("id");
+
+ std::string methodName = request["method"].asString();
+ StringUtils::ToLower(methodName);
+
+ JSONRPC::MethodCall method;
+ CVariant params;
+
+ if ((errorCode = CJSONServiceDescription::CheckCall(methodName.c_str(), request["params"], transport, client, isNotification, method, params)) == OK)
+ errorCode = method(methodName, transport, client, params, result);
+ else
+ result = params;
+ }
+ else
+ {
+ std::string str;
+ CJSONVariantWriter::Write(request, str, true);
+
+ CLog::Log(LOGERROR, "JSONRPC: Failed to parse '{}'", str);
+ errorCode = InvalidRequest;
+ }
+
+ BuildResponse(request, errorCode, result, response);
+
+ return !isNotification;
+}
+
+inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
+{
+ return inputroot.isMember("jsonrpc") && inputroot["jsonrpc"].isString() && inputroot["jsonrpc"] == CVariant("2.0") && inputroot.isMember("method") && inputroot["method"].isString() && (!inputroot.isMember("params") || inputroot["params"].isArray() || inputroot["params"].isObject());
+}
+
+inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
+{
+ response["jsonrpc"] = "2.0";
+ response["id"] = request.isMember("id") ? request["id"] : CVariant();
+
+ switch (code)
+ {
+ case OK:
+ response["result"] = result;
+ break;
+ case ACK:
+ response["result"] = "OK";
+ break;
+ case InvalidRequest:
+ response["error"]["code"] = InvalidRequest;
+ response["error"]["message"] = "Invalid request.";
+ break;
+ case InvalidParams:
+ response["error"]["code"] = InvalidParams;
+ response["error"]["message"] = "Invalid params.";
+ if (!result.isNull())
+ response["error"]["data"] = result;
+ break;
+ case MethodNotFound:
+ response["error"]["code"] = MethodNotFound;
+ response["error"]["message"] = "Method not found.";
+ break;
+ case ParseError:
+ response["error"]["code"] = ParseError;
+ response["error"]["message"] = "Parse error.";
+ break;
+ case BadPermission:
+ response["error"]["code"] = BadPermission;
+ response["error"]["message"] = "Bad client permission.";
+ break;
+ case FailedToExecute:
+ response["error"]["code"] = FailedToExecute;
+ response["error"]["message"] = "Failed to execute method.";
+ break;
+ default:
+ response["error"]["code"] = InternalError;
+ response["error"]["message"] = "Internal error.";
+ break;
+ }
+}
+
+void CJSONRPCUtils::NotifyItemUpdated()
+{
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE,
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message);
+}
+
+void CJSONRPCUtils::NotifyItemUpdated(const CVideoInfoTag& info,
+ const std::map<std::string, std::string>& artwork)
+{
+ CFileItemPtr msgItem(new CFileItem(info));
+ if (!artwork.empty())
+ msgItem->SetArt(artwork);
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL,
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0,
+ GUI_MSG_UPDATE_ITEM, 0, msgItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message);
+}