2 * Copyright (C) 2005-2013 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, see
17 * <http://www.gnu.org/licenses/>.
24 #include "ServiceDescription.h"
25 #include "dbwrappers/DatabaseQuery.h"
26 #include "input/ButtonTranslator.h"
27 #include "interfaces/AnnouncementManager.h"
28 #include "playlists/SmartPlayList.h"
29 #include "settings/AdvancedSettings.h"
30 #include "utils/log.h"
31 #include "utils/StringUtils.h"
32 #include "utils/Variant.h"
33 #include "TextureDatabase.h"
35 using namespace ANNOUNCEMENT;
36 using namespace JSONRPC;
39 bool CJSONRPC::m_initialized = false;
41 void CJSONRPC::Initialize()
46 // Add some types/enums at runtime
47 vector<string> enumList;
48 CButtonTranslator::GetActions(enumList);
49 CJSONServiceDescription::AddEnum("Input.Action", enumList);
52 CButtonTranslator::GetWindows(enumList);
53 CJSONServiceDescription::AddEnum("GUI.Window", enumList);
55 // filter-related enums
56 vector<string> smartplaylistList;
57 CDatabaseQueryRule::GetAvailableOperators(smartplaylistList);
58 CJSONServiceDescription::AddEnum("List.Filter.Operators", smartplaylistList);
60 smartplaylistList.clear();
61 CSmartPlaylist::GetAvailableFields("movies", smartplaylistList);
62 CJSONServiceDescription::AddEnum("List.Filter.Fields.Movies", smartplaylistList);
64 smartplaylistList.clear();
65 CSmartPlaylist::GetAvailableFields("tvshows", smartplaylistList);
66 CJSONServiceDescription::AddEnum("List.Filter.Fields.TVShows", smartplaylistList);
68 smartplaylistList.clear();
69 CSmartPlaylist::GetAvailableFields("episodes", smartplaylistList);
70 CJSONServiceDescription::AddEnum("List.Filter.Fields.Episodes", smartplaylistList);
72 smartplaylistList.clear();
73 CSmartPlaylist::GetAvailableFields("musicvideos", smartplaylistList);
74 CJSONServiceDescription::AddEnum("List.Filter.Fields.MusicVideos", smartplaylistList);
76 smartplaylistList.clear();
77 CSmartPlaylist::GetAvailableFields("artists", smartplaylistList);
78 CJSONServiceDescription::AddEnum("List.Filter.Fields.Artists", smartplaylistList);
80 smartplaylistList.clear();
81 CSmartPlaylist::GetAvailableFields("albums", smartplaylistList);
82 CJSONServiceDescription::AddEnum("List.Filter.Fields.Albums", smartplaylistList);
84 smartplaylistList.clear();
85 CSmartPlaylist::GetAvailableFields("songs", smartplaylistList);
86 CJSONServiceDescription::AddEnum("List.Filter.Fields.Songs", smartplaylistList);
88 smartplaylistList.clear();
89 CTextureRule::GetAvailableFields(smartplaylistList);
90 CJSONServiceDescription::AddEnum("List.Filter.Fields.Textures", smartplaylistList);
92 unsigned int size = sizeof(JSONRPC_SERVICE_TYPES) / sizeof(char*);
94 for (unsigned int index = 0; index < size; index++)
95 CJSONServiceDescription::AddType(JSONRPC_SERVICE_TYPES[index]);
97 size = sizeof(JSONRPC_SERVICE_METHODS) / sizeof(char*);
99 for (unsigned int index = 0; index < size; index++)
100 CJSONServiceDescription::AddBuiltinMethod(JSONRPC_SERVICE_METHODS[index]);
102 size = sizeof(JSONRPC_SERVICE_NOTIFICATIONS) / sizeof(char*);
104 for (unsigned int index = 0; index < size; index++)
105 CJSONServiceDescription::AddNotification(JSONRPC_SERVICE_NOTIFICATIONS[index]);
107 m_initialized = true;
108 CLog::Log(LOGINFO, "JSONRPC v%s: Successfully initialized", CJSONServiceDescription::GetVersion());
111 void CJSONRPC::Cleanup()
113 CJSONServiceDescription::Cleanup();
114 m_initialized = false;
117 JSONRPC_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
119 return CJSONServiceDescription::Print(result, transport, client,
120 parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(),
121 parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean());
124 JSONRPC_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
126 result["version"]["major"] = 0;
127 result["version"]["minor"] = 0;
128 result["version"]["patch"] = 0;
130 const char* version = CJSONServiceDescription::GetVersion();
133 vector<string> parts = StringUtils::Split(version, ".");
134 if (parts.size() > 0)
135 result["version"]["major"] = (int)strtol(parts[0].c_str(), NULL, 10);
136 if (parts.size() > 1)
137 result["version"]["minor"] = (int)strtol(parts[1].c_str(), NULL, 10);
138 if (parts.size() > 2)
139 result["version"]["patch"] = (int)strtol(parts[2].c_str(), NULL, 10);
145 JSONRPC_STATUS CJSONRPC::Permission(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
147 int flags = client->GetPermissionFlags();
149 for (int i = 1; i <= OPERATION_PERMISSION_ALL; i *= 2)
150 result[PermissionToString((OperationPermission)i)] = (flags & i) == i;
155 JSONRPC_STATUS CJSONRPC::Ping(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
157 CVariant temp = "pong";
162 JSONRPC_STATUS CJSONRPC::GetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
164 int flags = client->GetAnnouncementFlags();
166 for (int i = 1; i <= ANNOUNCE_ALL; i *= 2)
167 result["notifications"][AnnouncementFlagToString((AnnouncementFlag)i)] = (flags & i) == i;
172 JSONRPC_STATUS CJSONRPC::SetConfiguration(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
175 int oldFlags = client->GetAnnouncementFlags();
177 if (parameterObject.isMember("notifications"))
179 CVariant notifications = parameterObject["notifications"];
180 if ((notifications["Player"].isNull() && (oldFlags & Player)) ||
181 (notifications["Player"].isBoolean() && notifications["Player"].asBoolean()))
183 if ((notifications["Playlist"].isNull() && (oldFlags & Playlist)) ||
184 (notifications["Playlist"].isBoolean() && notifications["Playlist"].asBoolean()))
186 if ((notifications["GUI"].isNull() && (oldFlags & GUI)) ||
187 (notifications["GUI"].isBoolean() && notifications["GUI"].asBoolean()))
189 if ((notifications["System"].isNull() && (oldFlags & System)) ||
190 (notifications["System"].isBoolean() && notifications["System"].asBoolean()))
192 if ((notifications["VideoLibrary"].isNull() && (oldFlags & VideoLibrary)) ||
193 (notifications["VideoLibrary"].isBoolean() && notifications["VideoLibrary"].asBoolean()))
194 flags |= VideoLibrary;
195 if ((notifications["AudioLibrary"].isNull() && (oldFlags & AudioLibrary)) ||
196 (notifications["AudioLibrary"].isBoolean() && notifications["AudioLibrary"].asBoolean()))
197 flags |= AudioLibrary;
198 if ((notifications["Application"].isNull() && (oldFlags & Other)) ||
199 (notifications["Application"].isBoolean() && notifications["Application"].asBoolean()))
200 flags |= Application;
201 if ((notifications["Input"].isNull() && (oldFlags & Input)) ||
202 (notifications["Input"].isBoolean() && notifications["Input"].asBoolean()))
204 if ((notifications["Other"].isNull() && (oldFlags & Other)) ||
205 (notifications["Other"].isBoolean() && notifications["Other"].asBoolean()))
209 if (!client->SetAnnouncementFlags(flags))
210 return BadPermission;
212 return GetConfiguration(method, transport, client, parameterObject, result);
215 JSONRPC_STATUS CJSONRPC::NotifyAll(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result)
217 if (parameterObject["data"].isNull())
218 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
219 parameterObject["message"].asString().c_str());
222 CVariant data = parameterObject["data"];
223 CAnnouncementManager::Announce(Other, parameterObject["sender"].asString().c_str(),
224 parameterObject["message"].asString().c_str(), data);
230 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
232 CVariant inputroot, outputroot, result;
233 bool hasResponse = false;
235 CLog::Log(LOGDEBUG, "JSONRPC: Incoming request: %s", inputString.c_str());
236 inputroot = CJSONVariantParser::Parse((unsigned char *)inputString.c_str(), inputString.length());
237 if (!inputroot.isNull())
239 if (inputroot.isArray())
241 if (inputroot.size() <= 0)
243 CLog::Log(LOGERROR, "JSONRPC: Empty batch call\n");
244 BuildResponse(inputroot, InvalidRequest, CVariant(), outputroot);
249 for (CVariant::const_iterator_array itr = inputroot.begin_array(); itr != inputroot.end_array(); itr++)
252 if (HandleMethodCall(*itr, response, transport, client))
254 outputroot.append(response);
261 hasResponse = HandleMethodCall(inputroot, outputroot, transport, client);
265 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", inputString.c_str());
266 BuildResponse(inputroot, ParseError, CVariant(), outputroot);
270 CStdString str = hasResponse ? CJSONVariantWriter::Write(outputroot, g_advancedSettings.m_jsonOutputCompact) : "";
274 bool CJSONRPC::HandleMethodCall(const CVariant& request, CVariant& response, ITransportLayer *transport, IClient *client)
276 JSONRPC_STATUS errorCode = OK;
278 bool isNotification = false;
280 if (IsProperJSONRPC(request))
282 isNotification = !request.isMember("id");
284 CStdString methodName = request["method"].asString();
285 methodName = methodName.ToLower();
287 JSONRPC::MethodCall method;
290 if ((errorCode = CJSONServiceDescription::CheckCall(methodName, request["params"], transport, client, isNotification, method, params)) == OK)
291 errorCode = method(methodName, transport, client, params, result);
297 CLog::Log(LOGERROR, "JSONRPC: Failed to parse '%s'\n", CJSONVariantWriter::Write(request, true).c_str());
298 errorCode = InvalidRequest;
301 BuildResponse(request, errorCode, result, response);
303 return !isNotification;
306 inline bool CJSONRPC::IsProperJSONRPC(const CVariant& inputroot)
308 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());
311 inline void CJSONRPC::BuildResponse(const CVariant& request, JSONRPC_STATUS code, const CVariant& result, CVariant& response)
313 response["jsonrpc"] = "2.0";
314 response["id"] = request.isObject() && request.isMember("id") ? request["id"] : CVariant();
319 response["result"] = result;
322 response["result"] = "OK";
325 response["error"]["code"] = InvalidRequest;
326 response["error"]["message"] = "Invalid request.";
329 response["error"]["code"] = InvalidParams;
330 response["error"]["message"] = "Invalid params.";
331 if (!result.isNull())
332 response["error"]["data"] = result;
335 response["error"]["code"] = MethodNotFound;
336 response["error"]["message"] = "Method not found.";
339 response["error"]["code"] = ParseError;
340 response["error"]["message"] = "Parse error.";
343 response["error"]["code"] = BadPermission;
344 response["error"]["message"] = "Bad client permission.";
346 case FailedToExecute:
347 response["error"]["code"] = FailedToExecute;
348 response["error"]["message"] = "Failed to execute method.";
351 response["error"]["code"] = InternalError;
352 response["error"]["message"] = "Internal error.";