2 * Copyright (C) 2005-2010 Team XBMC
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)
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.
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
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"
29 #include "ServiceDescription.h"
31 using namespace ANNOUNCEMENT;
32 using namespace JSONRPC;
35 bool CJSONRPC::m_initialized = false;
37 void CJSONRPC::Initialize()
42 unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
44 for (unsigned int index = 0; index < size; index++)
45 CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
47 size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
49 for (unsigned int index = 0; index < size; index++)
50 CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
52 size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
54 for (unsigned int index = 0; index < size; index++)
55 CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
60 JSON_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
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());
67 JSON_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
69 result["version"] = CJSONServiceDescription::GetVersion();
74 JSON_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
76 int flags = client->GetPermissionFlags();
78 for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
79 result[PermissionToString((OperationPermission)i)] = (flags & i) > 0;
84 JSON_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
86 CVariant temp = "pong";
91 JSON_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
93 int flags = client->GetAnnouncementFlags();
95 for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
96 result["notifications"][CAnnouncementUtils::AnnouncementFlagToString((EAnnouncementFlag)i)] = (flags & i) > 0;
101 JSON_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
104 int oldFlags = client->GetAnnouncementFlags();
106 if (parameterObject.isMember("notifications"))
108 CVariant notifications = parameterObject["notifications"];
109 if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
110 (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
112 if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
113 (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
115 if ((notifications["System"].isNull() && (oldFlags & System)) ||
116 (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
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()))
129 if (!client->SetAnnouncementFlags(flags))
130 return BadPermission;
132 return GetConfiguration(method, transport, client, parameterObject, result);
135 JSON_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
137 if (parameterObject["data"].isNull())
138 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
139 parameterObject["message"].asString().c_str());
142 CVariant data(parameterObject["data"].asString());
143 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
144 parameterObject["message"].asString().c_str(), data);
150 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
152 CVariant inputroot, outputroot, result;
153 bool hasResponse = false;
155 inputroot = CJSONVariantParser::Parse((unsigned char *)inputString.c_str(), inputString.length());
156 if (!inputroot.isNull())
158 if (inputroot.isArray())
160 if (inputroot.size() <= 0)
162 CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
163 BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
168 for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
171 if (HandleMethodCall(*itr, response, transport, client))
173 outputroot.append(response);
180 hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
184 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
185 BuildResponse(inputroot, ParseError, CVariant(), outputroot);
189 CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
193 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
195 JSON_STATUS errorCode = OK;
197 bool isNotification = false;
199 if (IsProperJSONRPC(request))
201 isNotification = !request.isMember("id");
203 CStdString methodName = request["method"].asString();
204 methodName = methodName.ToLower();
206 JSONRPC::MethodCall method;
209 if ((errorCode = CJSONServiceDescription::CheckCall(methodName, request["params"], transport, client, isNotification, method, params)) == OK)
210 errorCode = method(methodName, transport, client, params, result);
216 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
217 errorCode = InvalidRequest;
220 BuildResponse(request, errorCode, result, response);
222 return !isNotification;
225 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
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());
230 inline void CJSONRPC::BuildResponse(const CVariant& request, JSON_STATUS code, const CVariant& result, CVariant& response)
232 response["jsonrpc"] = "2.0";
233 response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
238 response["result"] = result;
241 response["result"] = "OK";
244 response["error"]["code"] = InvalidRequest;
245 response["error"]["message"] = "Invalid request.";
248 response["error"]["code"] = InvalidParams;
249 response["error"]["message"] = "Invalid params.";
250 if (!result.isNull())
251 response["error"]["data"] = result;
254 response["error"]["code"] = MethodNotFound;
255 response["error"]["message"] = "Method not found.";
258 response["error"]["code"] = ParseError;
259 response["error"]["message"] = "Parse error.";
262 response["error"]["code"] = BadPermission;
263 response["error"]["message"] = "Bad client permission.";
265 case FailedToExecute:
266 response["error"]["code"] = FailedToExecute;
267 response["error"]["message"] = "Failed to execute method.";
270 response["error"]["code"] = InternalError;
271 response["error"]["message"] = "Internal error.";