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 "input/ButtonTranslator.h"
27 #include "interfaces/AnnouncementManager.h"
28 #include "settings/AdvancedSettings.h"
29 #include "utils/log.h"
30 #include "utils/Variant.h"
32 using namespace ANNOUNCEMENT;
33 using namespace JSONRPC;
36 bool CJSONRPC::m_initialized = false;
38 void CJSONRPC::Initialize()
43 // Add some types/enums at runtime
44 vector<string> inputActions;
45 CButtonTranslator::GetActions(inputActions);
46 CJSONServiceDescription::AddEnum("Input.Action", inputActions);
48 unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
50 for (unsigned int index = 0; index < size; index++)
51 CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
53 size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
55 for (unsigned int index = 0; index < size; index++)
56 CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
58 size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
60 for (unsigned int index = 0; index < size; index++)
61 CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
64 CLog::Log(LOGINFO, "JSONRPC: Sucessfully initialized");
67 JSONRPC_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
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());
74 JSONRPC_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
76 result["version"] = CJSONServiceDescription::GetVersion();
81 JSONRPC_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
83 int flags = client->GetPermissionFlags();
85 for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
86 result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
91 JSONRPC_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
93 CVariant temp = "pong";
98 JSONRPC_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
100 int flags = client->GetAnnouncementFlags();
102 for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
103 result["notifications"][AnnouncementFlagToString((AnnouncementFlag)i)] = (flags & i) == i;
108 JSONRPC_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
111 int oldFlags = client->GetAnnouncementFlags();
113 if (parameterObject.isMember("notifications"))
115 CVariant notifications = parameterObject["notifications"];
116 if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
117 (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
119 if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
120 (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
122 if ((notifications["System"].isNull() && (oldFlags & System)) ||
123 (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
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()))
139 if (!client->SetAnnouncementFlags(flags))
140 return BadPermission;
142 return GetConfiguration(method, transport, client, parameterObject, result);
145 JSONRPC_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
147 if (parameterObject["data"].isNull())
148 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
149 parameterObject["message"].asString().c_str());
152 CVariant data = parameterObject["data"];
153 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
154 parameterObject["message"].asString().c_str(), data);
160 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
162 CVariant inputroot, outputroot, result;
163 bool hasResponse = false;
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())
169 if (inputroot.isArray())
171 if (inputroot.size() <= 0)
173 CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
174 BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
179 for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
182 if (HandleMethodCall(*itr, response, transport, client))
184 outputroot.append(response);
191 hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
195 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
196 BuildResponse(inputroot, ParseError, CVariant(), outputroot);
200 CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
204 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
206 JSONRPC_STATUS errorCode = OK;
208 bool isNotification = false;
210 if (IsProperJSONRPC(request))
212 isNotification = !request.isMember("id");
214 CStdString methodName = request["method"].asString();
215 methodName = methodName.ToLower();
217 JSONRPC::MethodCall method;
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);
228 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
229 errorCode = InvalidRequest;
232 BuildResponse(request, errorCode, result, response);
234 return !isNotification;
237 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
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());
242 inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
244 response["jsonrpc"] = "2.0";
245 response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
250 response["result"] = result;
253 response["result"] = "OK";
256 response["error"]["code"] = InvalidRequest;
257 response["error"]["message"] = "Invalid request.";
260 response["error"]["code"] = InvalidParams;
261 response["error"]["message"] = "Invalid params.";
262 if (!result.isNull())
263 response["error"]["data"] = result;
266 response["error"]["code"] = MethodNotFound;
267 response["error"]["message"] = "Method not found.";
270 response["error"]["code"] = ParseError;
271 response["error"]["message"] = "Parse error.";
274 response["error"]["code"] = BadPermission;
275 response["error"]["message"] = "Bad client permission.";
277 case FailedToExecute:
278 response["error"]["code"] = FailedToExecute;
279 response["error"]["message"] = "Failed to execute method.";
282 response["error"]["code"] = InternalError;
283 response["error"]["message"] = "Internal error.";