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