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