Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / interfaces / json-rpc / JSONRPC.cpp
1 /*
2  *      Copyright (C) 2005-2010 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "JSONRPC.h"
23 #include "settings/AdvancedSettings.h"
24 #include "interfaces/AnnouncementManager.h"
25 #include "interfaces/AnnouncementUtils.h"
26 #include "utils/log.h"
27 #include "utils/Variant.h"
28 #include <string.h>
29 #include "ServiceDescription.h"
30
31 using namespace ANNOUNCEMENT;
32 using namespace JSONRPC;
33 using namespace std;
34
35 bool CJSONRPC::m_initialized = false;
36
37 void CJSONRPC::Initialize()
38 {
39   if (m_initialized)
40     return;
41
42   unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
43
44   for (unsigned int index = 0; index < size; index++)
45     CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
46
47   size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
48
49   for (unsigned int index = 0; index < size; index++)
50     CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
51
52   size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
53
54   for (unsigned int index = 0; index < size; index++)
55     CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
56   
57   m_initialized = true;
58 }
59
60 JSON_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
61 {
62   return CJSONServiceDescription::Print(result, transport, client,
63     parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(),
64     parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean());
65 }
66
67 JSON_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
68 {
69   result["version"] = CJSONServiceDescription::GetVersion();
70
71   return OK;
72 }
73
74 JSON_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
75 {
76   int flags = client->GetPermissionFlags();
77
78   for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
79     result[PermissionToString((OperationPermission)i)] = (flags & i) > 0;
80
81   return OK;
82 }
83
84 JSON_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
85 {
86   CVariant temp = "pong";
87   result.swap(temp);
88   return OK;
89 }
90
91 JSON_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
92 {
93   int flags = client->GetAnnouncementFlags();
94
95   for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
96     result["notifications"][CAnnouncementUtils::AnnouncementFlagToString((EAnnouncementFlag)i)] = (flags & i) > 0;
97
98   return OK;
99 }
100
101 JSON_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
102 {
103   int flags = 0;
104   int oldFlags = client->GetAnnouncementFlags();
105
106   if (parameterObject.isMember("notifications"))
107   {
108     CVariant notifications = parameterObject["notifications"];
109     if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
110         (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
111       flags |= Player;
112     if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
113         (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
114       flags |= GUI;
115     if ((notifications["System"].isNull() && (oldFlags & System)) ||
116         (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
117       flags |= System;
118     if ((notifications["VideoLibrary"].isNull() && (oldFlags & VideoLibrary)) ||
119         (notifications["VideoLibrary"].isBoolean() && notifications["VideoLibrary"].asBoolean()))
120       flags |= VideoLibrary;
121     if ((notifications["AudioLibrary"].isNull() && (oldFlags & AudioLibrary)) ||
122         (notifications["AudioLibrary"].isBoolean() && notifications["AudioLibrary"].asBoolean()))
123       flags |= AudioLibrary;
124     if ((notifications["Other"].isNull() && (oldFlags & Other)) ||
125         (notifications["Other"].isBoolean() && notifications["Other"].asBoolean()))
126       flags |= Other;
127   }
128
129   if (!client->SetAnnouncementFlags(flags))
130     return BadPermission;
131
132   return GetConfiguration(method, transport, client, parameterObject, result);
133 }
134
135 JSON_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
136 {
137   if (parameterObject["data"].isNull())
138     CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),  
139       parameterObject["message"].asString().c_str());
140   else
141   {
142     CVariant data(parameterObject["data"].asString());
143     CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),  
144       parameterObject["message"].asString().c_str(), data);
145   }
146
147   return ACK;
148 }
149
150 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
151 {
152   CVariant inputroot, outputroot, result;
153   bool hasResponse = false;
154
155   inputroot = CJSONVariantParser::Parse((unsigned char *)inputString.c_str(), inputString.length());
156   if (!inputroot.isNull())
157   {
158     if (inputroot.isArray())
159     {
160       if (inputroot.size() <= 0)
161       {
162         CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
163         BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
164         hasResponse = true;
165       }
166       else
167       {
168         for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
169         {
170           CVariant response;
171           if (HandleMethodCall(*itr, response, transport, client))
172           {
173             outputroot.append(response);
174             hasResponse = true;
175           }
176         }
177       }
178     }
179     else
180       hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
181   }
182   else
183   {
184     CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
185     BuildResponse(inputroot, ParseError, CVariant(), outputroot);
186     hasResponse = true;
187   }
188
189   CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
190   return str;
191 }
192
193 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
194 {
195   JSON_STATUS errorCode = OK;
196   CVariant result;
197   bool isNotification = false;
198
199   if (IsProperJSONRPC(request))
200   {
201     isNotification = !request.isMember("id");
202
203     CStdString methodName = request["method"].asString();
204     methodName = methodName.ToLower();
205
206     JSONRPC::MethodCall method;
207     CVariant params;
208
209     if ((errorCode = CJSONServiceDescription::CheckCall(methodName, request["params"], transport, client, isNotification, method, params)) == OK)
210       errorCode = method(methodName, transport, client, params, result);
211     else
212       result = params;
213   }
214   else
215   {
216     CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
217     errorCode = InvalidRequest;
218   }
219
220   BuildResponse(request, errorCode, result, response);
221
222   return !isNotification;
223 }
224
225 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
226 {
227   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());
228 }
229
230 inline void CJSONRPC::BuildResponse(const CVariant& request, JSON_STATUS code, const CVariant& result, CVariant& response)
231 {
232   response["jsonrpc"] = "2.0";
233   response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
234
235   switch (code)
236   {
237     case OK:
238       response["result"] = result;
239       break;
240     case ACK:
241       response["result"] = "OK";
242       break;
243     case InvalidRequest:
244       response["error"]["code"] = InvalidRequest;
245       response["error"]["message"] = "Invalid request.";
246       break;
247     case InvalidParams:
248       response["error"]["code"] = InvalidParams;
249       response["error"]["message"] = "Invalid params.";
250       if (!result.isNull())
251         response["error"]["data"] = result;
252       break;
253     case MethodNotFound:
254       response["error"]["code"] = MethodNotFound;
255       response["error"]["message"] = "Method not found.";
256       break;
257     case ParseError:
258       response["error"]["code"] = ParseError;
259       response["error"]["message"] = "Parse error.";
260       break;
261     case BadPermission:
262       response["error"]["code"] = BadPermission;
263       response["error"]["message"] = "Bad client permission.";
264       break;
265     case FailedToExecute:
266       response["error"]["code"] = FailedToExecute;
267       response["error"]["message"] = "Failed to execute method.";
268       break;
269     default:
270       response["error"]["code"] = InternalError;
271       response["error"]["message"] = "Internal error.";
272       break;
273   }
274 }