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
25 #include "ServiceDescription.h"
26 #include "interfaces/AnnouncementManager.h"
27 #include "settings/AdvancedSettings.h"
28 #include "utils/log.h"
29 #include "utils/Variant.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]);
58 CLog::Log(LOGINFO, "JSONRPC: Sucessfully initialized");
61 JSONRPC_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
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());
68 JSONRPC_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
70 result["version"] = CJSONServiceDescription::GetVersion();
75 JSONRPC_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
77 int flags = client->GetPermissionFlags();
79 for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
80 result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
85 JSONRPC_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
87 CVariant temp = "pong";
92 JSONRPC_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
94 int flags = client->GetAnnouncementFlags();
96 for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
97 result["notifications"][AnnouncementFlagToString((AnnouncementFlag)i)] = (flags & i) == i;
102 JSONRPC_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
105 int oldFlags = client->GetAnnouncementFlags();
107 if (parameterObject.isMember("notifications"))
109 CVariant notifications = parameterObject["notifications"];
110 if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
111 (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
113 if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
114 (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
116 if ((notifications["System"].isNull() && (oldFlags & System)) ||
117 (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
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()))
133 if (!client->SetAnnouncementFlags(flags))
134 return BadPermission;
136 return GetConfiguration(method, transport, client, parameterObject, result);
139 JSONRPC_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
141 if (parameterObject["data"].isNull())
142 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
143 parameterObject["message"].asString().c_str());
146 CVariant data = parameterObject["data"];
147 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
148 parameterObject["message"].asString().c_str(), data);
154 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
156 CVariant inputroot, outputroot, result;
157 bool hasResponse = false;
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())
163 if (inputroot.isArray())
165 if (inputroot.size() <= 0)
167 CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
168 BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
173 for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
176 if (HandleMethodCall(*itr, response, transport, client))
178 outputroot.append(response);
185 hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
189 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
190 BuildResponse(inputroot, ParseError, CVariant(), outputroot);
194 CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
198 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
200 JSONRPC_STATUS errorCode = OK;
202 bool isNotification = false;
204 if (IsProperJSONRPC(request))
206 isNotification = !request.isMember("id");
208 CStdString methodName = request["method"].asString();
209 methodName = methodName.ToLower();
211 JSONRPC::MethodCall method;
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);
222 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
223 errorCode = InvalidRequest;
226 BuildResponse(request, errorCode, result, response);
228 return !isNotification;
231 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
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());
236 inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
238 response["jsonrpc"] = "2.0";
239 response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
244 response["result"] = result;
247 response["result"] = "OK";
250 response["error"]["code"] = InvalidRequest;
251 response["error"]["message"] = "Invalid request.";
254 response["error"]["code"] = InvalidParams;
255 response["error"]["message"] = "Invalid params.";
256 if (!result.isNull())
257 response["error"]["data"] = result;
260 response["error"]["code"] = MethodNotFound;
261 response["error"]["message"] = "Method not found.";
264 response["error"]["code"] = ParseError;
265 response["error"]["message"] = "Parse error.";
268 response["error"]["code"] = BadPermission;
269 response["error"]["message"] = "Bad client permission.";
271 case FailedToExecute:
272 response["error"]["code"] = FailedToExecute;
273 response["error"]["message"] = "Failed to execute method.";
276 response["error"]["code"] = InternalError;
277 response["error"]["message"] = "Internal error.";