1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "JSONRPC.h"
10
11 #include "ServiceBroker.h"
12 #include "ServiceDescription.h"
13 #include "TextureDatabase.h"
14 #include "addons/Addon.h"
15 #include "addons/IAddon.h"
16 #include "dbwrappers/DatabaseQuery.h"
17 #include "input/WindowTranslator.h"
18 #include "input/actions/ActionTranslator.h"
19 #include "interfaces/AnnouncementManager.h"
20 #include "playlists/SmartPlayList.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/SettingsComponent.h"
23 #include "utils/StringUtils.h"
24 #include "utils/Variant.h"
25 #include "utils/log.h"
26
27 #include <string.h>
28
29 using namespace JSONRPC;
30
31 bool CJSONRPC::m_initialized = false;
32
Initialize()33 void CJSONRPC::Initialize()
34 {
35 if (m_initialized)
36 return;
37
38 // Add some types/enums at runtime
39 std::vector<std::string> enumList;
40 for (int addonType = ADDON::ADDON_UNKNOWN; addonType < ADDON::ADDON_MAX; addonType++)
41 enumList.push_back(ADDON::CAddonInfo::TranslateType(static_cast<ADDON::TYPE>(addonType), false));
42 CJSONServiceDescription::AddEnum("Addon.Types", enumList);
43
44 enumList.clear();
45 CActionTranslator::GetActions(enumList);
46 CJSONServiceDescription::AddEnum("Input.Action", enumList);
47
48 enumList.clear();
49 CWindowTranslator::GetWindows(enumList);
50 CJSONServiceDescription::AddEnum("GUI.Window", enumList);
51
52 // filter-related enums
53 std::vector<std::string> smartplaylistList;
54 CDatabaseQueryRule::GetAvailableOperators(smartplaylistList);
55 CJSONServiceDescription::AddEnum("List.Filter.Operators", smartplaylistList);
56
57 smartplaylistList.clear();
58 CSmartPlaylist::GetAvailableFields("movies", smartplaylistList);
59 CJSONServiceDescription::AddEnum("List.Filter.Fields.Movies", smartplaylistList);
60
61 smartplaylistList.clear();
62 CSmartPlaylist::GetAvailableFields("tvshows", smartplaylistList);
63 CJSONServiceDescription::AddEnum("List.Filter.Fields.TVShows", smartplaylistList);
64
65 smartplaylistList.clear();
66 CSmartPlaylist::GetAvailableFields("episodes", smartplaylistList);
67 CJSONServiceDescription::AddEnum("List.Filter.Fields.Episodes", smartplaylistList);
68
69 smartplaylistList.clear();
70 CSmartPlaylist::GetAvailableFields("musicvideos", smartplaylistList);
71 CJSONServiceDescription::AddEnum("List.Filter.Fields.MusicVideos", smartplaylistList);
72
73 smartplaylistList.clear();
74 CSmartPlaylist::GetAvailableFields("artists", smartplaylistList);
75 CJSONServiceDescription::AddEnum("List.Filter.Fields.Artists", smartplaylistList);
76
77 smartplaylistList.clear();
78 CSmartPlaylist::GetAvailableFields("albums", smartplaylistList);
79 CJSONServiceDescription::AddEnum("List.Filter.Fields.Albums", smartplaylistList);
80
81 smartplaylistList.clear();
82 CSmartPlaylist::GetAvailableFields("songs", smartplaylistList);
83 CJSONServiceDescription::AddEnum("List.Filter.Fields.Songs", smartplaylistList);
84
85 smartplaylistList.clear();
86 CTextureRule::GetAvailableFields(smartplaylistList);
87 CJSONServiceDescription::AddEnum("List.Filter.Fields.Textures", smartplaylistList);
88
89 unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
90
91 for (unsigned int index = 0; index < size; index++)
92 CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
93
94 size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
95
96 for (unsigned int index = 0; index < size; index++)
97 CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
98
99 size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
100
101 for (unsigned int index = 0; index < size; index++)
102 CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
103
104 CJSONServiceDescription::ResolveReferences();
105
106 m_initialized = true;
107 CLog::Log(LOGINFO, "JSONRPC v%s: Successfully initialized", CJSONServiceDescription::GetVersion());
108 }
109
Cleanup()110 void CJSONRPC::Cleanup()
111 {
112 CJSONServiceDescription::Cleanup();
113 m_initialized = false;
114 }
115
Introspect(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)116 JSONRPC_STATUS CJSONRPC::Introspect(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
117 {
118 return CJSONServiceDescription::Print(result, transport, client,
119 parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(),
120 parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean());
121 }
122
Version(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)123 JSONRPC_STATUS CJSONRPC::Version(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
124 {
125 result["version"]["major"] = 0;
126 result["version"]["minor"] = 0;
127 result["version"]["patch"] = 0;
128
129 const char* version = CJSONServiceDescription::GetVersion();
130 if (version != NULL)
131 {
132 std::vector<std::string> parts = StringUtils::Split(version, ".");
133 if (!parts.empty())
134 result["version"]["major"] = (int)strtol(parts[0].c_str(), NULL, 10);
135 if (parts.size() > 1)
136 result["version"]["minor"] = (int)strtol(parts[1].c_str(), NULL, 10);
137 if (parts.size() > 2)
138 result["version"]["patch"] = (int)strtol(parts[2].c_str(), NULL, 10);
139 }
140
141 return OK;
142 }
143
Permission(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)144 JSONRPC_STATUS CJSONRPC::Permission(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
145 {
146 int flags = client->GetPermissionFlags();
147
148 for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
149 result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
150
151 return OK;
152 }
153
Ping(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)154 JSONRPC_STATUS CJSONRPC::Ping(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
155 {
156 CVariant temp = "pong";
157 result.swap(temp);
158 return OK;
159 }
160
GetConfiguration(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)161 JSONRPC_STATUS CJSONRPC::GetConfiguration(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
162 {
163 int flags = client->GetAnnouncementFlags();
164
165 for (int i = 1; i <= ANNOUNCEMENT::ANNOUNCE_ALL; i *= 2)
166 result["notifications"][AnnouncementFlagToString((ANNOUNCEMENT::AnnouncementFlag)i)] = (flags & i) == i;
167
168 return OK;
169 }
170
SetConfiguration(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)171 JSONRPC_STATUS CJSONRPC::SetConfiguration(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
172 {
173 int flags = 0;
174 int oldFlags = client->GetAnnouncementFlags();
175
176 if (parameterObject.isMember("notifications"))
177 {
178 CVariant notifications = parameterObject["notifications"];
179 if ((notifications["Player"].isNull() && (oldFlags & ANNOUNCEMENT::Player)) ||
180 (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
181 flags |= ANNOUNCEMENT::Player;
182 if ((notifications["Playlist"].isNull() && (oldFlags & ANNOUNCEMENT::Playlist)) ||
183 (notifications["Playlist"].isBoolean() && notifications["Playlist"].asBoolean()))
184 flags |= ANNOUNCEMENT::Playlist;
185 if ((notifications["GUI"].isNull() && (oldFlags & ANNOUNCEMENT::GUI)) ||
186 (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
187 flags |= ANNOUNCEMENT::GUI;
188 if ((notifications["System"].isNull() && (oldFlags & ANNOUNCEMENT::System)) ||
189 (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
190 flags |= ANNOUNCEMENT::System;
191 if ((notifications["VideoLibrary"].isNull() && (oldFlags & ANNOUNCEMENT::VideoLibrary)) ||
192 (notifications["VideoLibrary"].isBoolean() && notifications["VideoLibrary"].asBoolean()))
193 flags |= ANNOUNCEMENT::VideoLibrary;
194 if ((notifications["AudioLibrary"].isNull() && (oldFlags & ANNOUNCEMENT::AudioLibrary)) ||
195 (notifications["AudioLibrary"].isBoolean() && notifications["AudioLibrary"].asBoolean()))
196 flags |= ANNOUNCEMENT::AudioLibrary;
197 if ((notifications["Application"].isNull() && (oldFlags & ANNOUNCEMENT::Other)) ||
198 (notifications["Application"].isBoolean() && notifications["Application"].asBoolean()))
199 flags |= ANNOUNCEMENT::Application;
200 if ((notifications["Input"].isNull() && (oldFlags & ANNOUNCEMENT::Input)) ||
201 (notifications["Input"].isBoolean() && notifications["Input"].asBoolean()))
202 flags |= ANNOUNCEMENT::Input;
203 if ((notifications["Other"].isNull() && (oldFlags & ANNOUNCEMENT::Other)) ||
204 (notifications["Other"].isBoolean() && notifications["Other"].asBoolean()))
205 flags |= ANNOUNCEMENT::Other;
206 }
207
208 if (!client->SetAnnouncementFlags(flags))
209 return BadPermission;
210
211 return GetConfiguration(method, transport, client, parameterObject, result);
212 }
213
NotifyAll(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)214 JSONRPC_STATUS CJSONRPC::NotifyAll(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
215 {
216 if (parameterObject["data"].isNull())
217 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Other,
218 parameterObject["sender"].asString(),
219 parameterObject["message"].asString());
220 else
221 {
222 CVariant data = parameterObject["data"];
223 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Other,
224 parameterObject["sender"].asString(),
225 parameterObject["message"].asString(), data);
226 }
227
228 return ACK;
229 }
230
MethodCall(const std::string & inputString,ITransportLayer * transport,IClient * client)231 std::string CJSONRPC::MethodCall(const std::string &inputString, ITransportLayer *transport, IClient *client)
232 {
233 CVariant inputroot, outputroot, result;
234 bool hasResponse = false;
235
236 CLog::Log(LOGDEBUG, LOGJSONRPC, "JSONRPC: Incoming request: %s", inputString.c_str());
237
238 if (CJSONVariantParser::Parse(inputString, inputroot) && !inputroot.isNull())
239 {
240 if (inputroot.isArray())
241 {
242 if (inputroot.size() <= 0)
243 {
244 CLog::Log(LOGERROR, "JSONRPC: Empty batch call");
245 BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
246 hasResponse = true;
247 }
248 else
249 {
250 for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
251 {
252 CVariant response;
253 if (HandleMethodCall(*itr, response, transport, client))
254 {
255 outputroot.append(response);
256 hasResponse = true;
257 }
258 }
259 }
260 }
261 else
262 hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
263 }
264 else
265 {
266 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'", inputString.c_str());
267 BuildResponse(inputroot, ParseError, CVariant(), outputroot);
268 hasResponse = true;
269 }
270
271 std::string str;
272 if (hasResponse)
273 CJSONVariantWriter::Write(outputroot, str, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonOutputCompact);
274
275 return str;
276 }
277
HandleMethodCall(const CVariant & request,CVariant & response,ITransportLayer * transport,IClient * client)278 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
279 {
280 JSONRPC_STATUS errorCode = OK;
281 CVariant result;
282 bool isNotification = false;
283
284 if (IsProperJSONRPC(request))
285 {
286 isNotification = !request.isMember("id");
287
288 std::string methodName = request["method"].asString();
289 StringUtils::ToLower(methodName);
290
291 JSONRPC::MethodCall method;
292 CVariant params;
293
294 if ((errorCode = CJSONServiceDescription::CheckCall(methodName.c_str(), request["params"], transport, client, isNotification, method, params)) == OK)
295 errorCode = method(methodName, transport, client, params, result);
296 else
297 result = params;
298 }
299 else
300 {
301 std::string str;
302 CJSONVariantWriter::Write(request, str, true);
303
304 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'", str.c_str());
305 errorCode = InvalidRequest;
306 }
307
308 BuildResponse(request, errorCode, result, response);
309
310 return !isNotification;
311 }
312
IsProperJSONRPC(const CVariant & inputroot)313 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
314 {
315 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());
316 }
317
BuildResponse(const CVariant & request,JSONRPC_STATUS code,const CVariant & result,CVariant & response)318 inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
319 {
320 response["jsonrpc"] = "2.0";
321 response["id"] = request.isMember("id") ? request["id"] : CVariant();
322
323 switch (code)
324 {
325 case OK:
326 response["result"] = result;
327 break;
328 case ACK:
329 response["result"] = "OK";
330 break;
331 case InvalidRequest:
332 response["error"]["code"] = InvalidRequest;
333 response["error"]["message"] = "Invalid request.";
334 break;
335 case InvalidParams:
336 response["error"]["code"] = InvalidParams;
337 response["error"]["message"] = "Invalid params.";
338 if (!result.isNull())
339 response["error"]["data"] = result;
340 break;
341 case MethodNotFound:
342 response["error"]["code"] = MethodNotFound;
343 response["error"]["message"] = "Method not found.";
344 break;
345 case ParseError:
346 response["error"]["code"] = ParseError;
347 response["error"]["message"] = "Parse error.";
348 break;
349 case BadPermission:
350 response["error"]["code"] = BadPermission;
351 response["error"]["message"] = "Bad client permission.";
352 break;
353 case FailedToExecute:
354 response["error"]["code"] = FailedToExecute;
355 response["error"]["message"] = "Failed to execute method.";
356 break;
357 default:
358 response["error"]["code"] = InternalError;
359 response["error"]["message"] = "Internal error.";
360 break;
361 }
362 }
363