strip added smb:// shares of their user/pass when adding, and instead store that...
[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 <string.h>
23
24 #include "JSONRPC.h"
25 #include "ServiceDescription.h"
26 #include "interfaces/AnnouncementManager.h"
27 #include "settings/AdvancedSettings.h"
28 #include "utils/log.h"
29 #include "utils/Variant.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   CLog::Log(LOGINFO, "JSONRPC: Sucessfully initialized");
59 }
60
61 JSONRPC_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
62 {
63   return CJSONServiceDescription::Print(result, transport, client,
64     parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(),
65     parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean());
66 }
67
68 JSONRPC_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
69 {
70   result["version"] = CJSONServiceDescription::GetVersion();
71
72   return OK;
73 }
74
75 JSONRPC_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
76 {
77   int flags = client->GetPermissionFlags();
78
79   for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
80     result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
81
82   return OK;
83 }
84
85 JSONRPC_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
86 {
87   CVariant temp = "pong";
88   result.swap(temp);
89   return OK;
90 }
91
92 JSONRPC_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
93 {
94   int flags = client->GetAnnouncementFlags();
95
96   for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
97     result["notifications"][AnnouncementFlagToString((AnnouncementFlag)i)] = (flags & i) == i;
98
99   return OK;
100 }
101
102 JSONRPC_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
103 {
104   int flags = 0;
105   int oldFlags = client->GetAnnouncementFlags();
106
107   if (parameterObject.isMember("notifications"))
108   {
109     CVariant notifications = parameterObject["notifications"];
110     if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
111         (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
112       flags |= Player;
113     if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
114         (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
115       flags |= GUI;
116     if ((notifications["System"].isNull() && (oldFlags & System)) ||
117         (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
118       flags |= System;
119     if ((notifications["VideoLibrary"].isNull() && (oldFlags & VideoLibrary)) ||
120         (notifications["VideoLibrary"].isBoolean() && notifications["VideoLibrary"].asBoolean()))
121       flags |= VideoLibrary;
122     if ((notifications["AudioLibrary"].isNull() && (oldFlags & AudioLibrary)) ||
123         (notifications["AudioLibrary"].isBoolean() && notifications["AudioLibrary"].asBoolean()))
124       flags |= AudioLibrary;
125     if ((notifications["Application"].isNull() && (oldFlags & Other)) ||
126         (notifications["Application"].isBoolean() && notifications["Application"].asBoolean()))
127       flags |= Application;
128     if ((notifications["Other"].isNull() && (oldFlags & Other)) ||
129         (notifications["Other"].isBoolean() && notifications["Other"].asBoolean()))
130       flags |= Other;
131   }
132
133   if (!client->SetAnnouncementFlags(flags))
134     return BadPermission;
135
136   return GetConfiguration(method, transport, client, parameterObject, result);
137 }
138
139 JSONRPC_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
140 {
141   if (parameterObject["data"].isNull())
142     CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),  
143       parameterObject["message"].asString().c_str());
144   else
145   {
146     CVariant data = parameterObject["data"];
147     CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),  
148       parameterObject["message"].asString().c_str(), data);
149   }
150
151   return ACK;
152 }
153
154 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
155 {
156   CVariant inputroot, outputroot, result;
157   bool hasResponse = false;
158
159   CLog::Log(LOGDEBUG, "JSONRPC: Incoming request: %s", inputString.c_str());
160   inputroot = CJSONVariantParser::Parse((unsigned char *)inputString.c_str(), inputString.length());
161   if (!inputroot.isNull())
162   {
163     if (inputroot.isArray())
164     {
165       if (inputroot.size() <= 0)
166       {
167         CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
168         BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
169         hasResponse = true;
170       }
171       else
172       {
173         for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
174         {
175           CVariant response;
176           if (HandleMethodCall(*itr, response, transport, client))
177           {
178             outputroot.append(response);
179             hasResponse = true;
180           }
181         }
182       }
183     }
184     else
185       hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
186   }
187   else
188   {
189     CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
190     BuildResponse(inputroot, ParseError, CVariant(), outputroot);
191     hasResponse = true;
192   }
193
194   CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
195   return str;
196 }
197
198 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
199 {
200   JSONRPC_STATUS errorCode = OK;
201   CVariant result;
202   bool isNotification = false;
203
204   if (IsProperJSONRPC(request))
205   {
206     isNotification = !request.isMember("id");
207
208     CStdString methodName = request["method"].asString();
209     methodName = methodName.ToLower();
210
211     JSONRPC::MethodCall method;
212     CVariant params;
213
214     CLog::Log(LOGDEBUG, "JSONRPC: Calling %s", methodName.c_str());
215     if ((errorCode = CJSONServiceDescription::CheckCall(methodName, request["params"], transport, client, isNotification, method, params)) == OK)
216       errorCode = method(methodName, transport, client, params, result);
217     else
218       result = params;
219   }
220   else
221   {
222     CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
223     errorCode = InvalidRequest;
224   }
225
226   BuildResponse(request, errorCode, result, response);
227
228   return !isNotification;
229 }
230
231 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
232 {
233   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());
234 }
235
236 inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
237 {
238   response["jsonrpc"] = "2.0";
239   response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
240
241   switch (code)
242   {
243     case OK:
244       response["result"] = result;
245       break;
246     case ACK:
247       response["result"] = "OK";
248       break;
249     case InvalidRequest:
250       response["error"]["code"] = InvalidRequest;
251       response["error"]["message"] = "Invalid request.";
252       break;
253     case InvalidParams:
254       response["error"]["code"] = InvalidParams;
255       response["error"]["message"] = "Invalid params.";
256       if (!result.isNull())
257         response["error"]["data"] = result;
258       break;
259     case MethodNotFound:
260       response["error"]["code"] = MethodNotFound;
261       response["error"]["message"] = "Method not found.";
262       break;
263     case ParseError:
264       response["error"]["code"] = ParseError;
265       response["error"]["message"] = "Parse error.";
266       break;
267     case BadPermission:
268       response["error"]["code"] = BadPermission;
269       response["error"]["message"] = "Bad client permission.";
270       break;
271     case FailedToExecute:
272       response["error"]["code"] = FailedToExecute;
273       response["error"]["message"] = "Failed to execute method.";
274       break;
275     default:
276       response["error"]["code"] = InternalError;
277       response["error"]["message"] = "Internal error.";
278       break;
279   }
280 }