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