1 /*
2 * Copyright (C) 2011-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "AddonsOperations.h"
10
11 #include "JSONUtils.h"
12 #include "ServiceBroker.h"
13 #include "TextureCache.h"
14 #include "addons/AddonDatabase.h"
15 #include "addons/AddonManager.h"
16 #include "addons/PluginSource.h"
17 #include "filesystem/File.h"
18 #include "messaging/ApplicationMessenger.h"
19 #include "utils/StringUtils.h"
20 #include "utils/Variant.h"
21
22 using namespace JSONRPC;
23 using namespace ADDON;
24 using namespace XFILE;
25 using namespace KODI::MESSAGING;
26
GetAddons(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)27 JSONRPC_STATUS CAddonsOperations::GetAddons(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
28 {
29 std::vector<TYPE> addonTypes;
30 TYPE addonType = CAddonInfo::TranslateType(parameterObject["type"].asString());
31 CPluginSource::Content content = CPluginSource::Translate(parameterObject["content"].asString());
32 CVariant enabled = parameterObject["enabled"];
33 CVariant installed = parameterObject["installed"];
34
35 // ignore the "content" parameter if the type is specified but not a plugin or script
36 if (addonType != ADDON_UNKNOWN && addonType != ADDON_PLUGIN && addonType != ADDON_SCRIPT)
37 content = CPluginSource::UNKNOWN;
38
39 if (addonType >= ADDON_VIDEO && addonType <= ADDON_EXECUTABLE)
40 {
41 addonTypes.push_back(ADDON_PLUGIN);
42 addonTypes.push_back(ADDON_SCRIPT);
43
44 switch (addonType)
45 {
46 case ADDON_VIDEO:
47 content = CPluginSource::VIDEO;
48 break;
49 case ADDON_AUDIO:
50 content = CPluginSource::AUDIO;
51 break;
52 case ADDON_IMAGE:
53 content = CPluginSource::IMAGE;
54 break;
55 case ADDON_GAME:
56 content = CPluginSource::GAME;
57 break;
58 case ADDON_EXECUTABLE:
59 content = CPluginSource::EXECUTABLE;
60 break;
61
62 default:
63 break;
64 }
65 }
66 else
67 addonTypes.push_back(addonType);
68
69 VECADDONS addons;
70 for (const auto& typeIt : addonTypes)
71 {
72 VECADDONS typeAddons;
73 if (typeIt == ADDON_UNKNOWN)
74 {
75 if (!enabled.isBoolean()) //All
76 {
77 if (!installed.isBoolean() || installed.asBoolean())
78 CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons);
79 if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean()))
80 CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons);
81 }
82 else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled
83 CServiceBroker::GetAddonMgr().GetAddons(typeAddons);
84 else if (!installed.isBoolean() || installed.asBoolean())
85 CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons);
86 }
87 else
88 {
89 if (!enabled.isBoolean()) //All
90 {
91 if (!installed.isBoolean() || installed.asBoolean())
92 CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons, typeIt);
93 if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean()))
94 CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons, typeIt);
95 }
96 else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled
97 CServiceBroker::GetAddonMgr().GetAddons(typeAddons, typeIt);
98 else if (!installed.isBoolean() || installed.asBoolean())
99 CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons, typeIt);
100 }
101
102 addons.insert(addons.end(), typeAddons.begin(), typeAddons.end());
103 }
104
105 // remove library addons
106 for (int index = 0; index < (int)addons.size(); index++)
107 {
108 PluginPtr plugin;
109 if (content != CPluginSource::UNKNOWN)
110 plugin = std::dynamic_pointer_cast<CPluginSource>(addons.at(index));
111
112 if ((addons.at(index)->Type() <= ADDON_UNKNOWN || addons.at(index)->Type() >= ADDON_MAX) ||
113 ((content != CPluginSource::UNKNOWN && plugin == NULL) || (plugin != NULL && !plugin->Provides(content))))
114 {
115 addons.erase(addons.begin() + index);
116 index--;
117 }
118 }
119
120 int start, end;
121 HandleLimits(parameterObject, result, addons.size(), start, end);
122
123 CAddonDatabase addondb;
124 for (int index = start; index < end; index++)
125 FillDetails(addons.at(index), parameterObject["properties"], result["addons"], addondb, true);
126
127 return OK;
128 }
129
GetAddonDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)130 JSONRPC_STATUS CAddonsOperations::GetAddonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
131 {
132 std::string id = parameterObject["addonid"].asString();
133 AddonPtr addon;
134 if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, ADDON::ADDON_UNKNOWN, OnlyEnabled::NO) ||
135 addon.get() == NULL || addon->Type() <= ADDON_UNKNOWN || addon->Type() >= ADDON_MAX)
136 return InvalidParams;
137
138 CAddonDatabase addondb;
139 FillDetails(addon, parameterObject["properties"], result["addon"], addondb);
140
141 return OK;
142 }
143
SetAddonEnabled(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)144 JSONRPC_STATUS CAddonsOperations::SetAddonEnabled(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
145 {
146 std::string id = parameterObject["addonid"].asString();
147 AddonPtr addon;
148 if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, ADDON::ADDON_UNKNOWN, OnlyEnabled::NO) ||
149 addon == nullptr || addon->Type() <= ADDON_UNKNOWN || addon->Type() >= ADDON_MAX)
150 return InvalidParams;
151
152 bool disabled = false;
153 if (parameterObject["enabled"].isBoolean())
154 {
155 disabled = !parameterObject["enabled"].asBoolean();
156 }
157 // we need to toggle the current disabled state of the addon
158 else if (parameterObject["enabled"].isString())
159 {
160 disabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(id);
161 }
162 else
163 {
164 return InvalidParams;
165 }
166
167 bool success = disabled
168 ? CServiceBroker::GetAddonMgr().DisableAddon(id, AddonDisabledReason::USER)
169 : CServiceBroker::GetAddonMgr().EnableAddon(id);
170
171 return success ? ACK : InvalidParams;
172 }
173
ExecuteAddon(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)174 JSONRPC_STATUS CAddonsOperations::ExecuteAddon(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
175 {
176 std::string id = parameterObject["addonid"].asString();
177 AddonPtr addon;
178 if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, ADDON_UNKNOWN, OnlyEnabled::YES) ||
179 addon.get() == NULL || addon->Type() < ADDON_VIZ || addon->Type() >= ADDON_MAX)
180 return InvalidParams;
181
182 std::string argv;
183 CVariant params = parameterObject["params"];
184 if (params.isObject())
185 {
186 for (CVariant::const_iterator_map it = params.begin_map(); it != params.end_map(); it++)
187 {
188 if (it != params.begin_map())
189 argv += ",";
190 argv += it->first + "=" + it->second.asString();
191 }
192 }
193 else if (params.isArray())
194 {
195 for (CVariant::const_iterator_array it = params.begin_array(); it != params.end_array(); it++)
196 {
197 if (it != params.begin_array())
198 argv += ",";
199 argv += StringUtils::Paramify(it->asString());
200 }
201 }
202 else if (params.isString())
203 {
204 if (!params.empty())
205 argv = StringUtils::Paramify(params.asString());
206 }
207
208 std::string cmd;
209 if (params.empty())
210 cmd = StringUtils::Format("RunAddon(%s)", id.c_str());
211 else
212 cmd = StringUtils::Format("RunAddon(%s, %s)", id.c_str(), argv.c_str());
213
214 if (params["wait"].asBoolean())
215 CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
216 else
217 CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
218
219 return ACK;
220 }
221
Serialize(const AddonPtr & addon)222 static CVariant Serialize(const AddonPtr& addon)
223 {
224 CVariant variant;
225 variant["addonid"] = addon->ID();
226 variant["type"] = CAddonInfo::TranslateType(addon->Type(), false);
227 variant["name"] = addon->Name();
228 variant["version"] = addon->Version().asString();
229 variant["summary"] = addon->Summary();
230 variant["description"] = addon->Description();
231 variant["path"] = addon->Path();
232 variant["author"] = addon->Author();
233 variant["thumbnail"] = addon->Icon();
234 variant["disclaimer"] = addon->Disclaimer();
235 variant["fanart"] = addon->FanArt();
236
237 variant["dependencies"] = CVariant(CVariant::VariantTypeArray);
238 for (const auto& dep : addon->GetDependencies())
239 {
240 CVariant info(CVariant::VariantTypeObject);
241 info["addonid"] = dep.id;
242 info["minversion"] = dep.versionMin.asString();
243 info["version"] = dep.version.asString();
244 info["optional"] = dep.optional;
245 variant["dependencies"].push_back(std::move(info));
246 }
247 if (addon->LifecycleState() == AddonLifecycleState::BROKEN)
248 variant["broken"] = addon->LifecycleStateDescription();
249 else
250 variant["broken"] = false;
251 if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED)
252 variant["deprecated"] = addon->LifecycleStateDescription();
253 else
254 variant["deprecated"] = false;
255 variant["extrainfo"] = CVariant(CVariant::VariantTypeArray);
256 for (const auto& kv : addon->ExtraInfo())
257 {
258 CVariant info(CVariant::VariantTypeObject);
259 info["key"] = kv.first;
260 info["value"] = kv.second;
261 variant["extrainfo"].push_back(std::move(info));
262 }
263 variant["rating"] = -1;
264 return variant;
265 }
266
FillDetails(const AddonPtr & addon,const CVariant & fields,CVariant & result,CAddonDatabase & addondb,bool append)267 void CAddonsOperations::FillDetails(const AddonPtr& addon,
268 const CVariant& fields,
269 CVariant& result,
270 CAddonDatabase& addondb,
271 bool append /* = false */)
272 {
273 if (addon.get() == NULL)
274 return;
275
276 CVariant addonInfo = Serialize(addon);
277
278 CVariant object;
279 object["addonid"] = addonInfo["addonid"];
280 object["type"] = addonInfo["type"];
281
282 for (unsigned int index = 0; index < fields.size(); index++)
283 {
284 std::string field = fields[index].asString();
285
286 // we need to manually retrieve the enabled / installed state of every addon
287 // from the addon database because it can't be read from addon.xml
288 if (field == "enabled")
289 {
290 object[field] = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID());
291 }
292 else if (field == "installed")
293 {
294 object[field] = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID());
295 }
296 else if (field == "fanart" || field == "thumbnail")
297 {
298 std::string url = addonInfo[field].asString();
299 // We need to check the existence of fanart and thumbnails as the addon simply
300 // holds where the art will be, not whether it exists.
301 bool needsRecaching;
302 std::string image = CTextureCache::GetInstance().CheckCachedImage(url, needsRecaching);
303 if (!image.empty() || CFile::Exists(url))
304 object[field] = CTextureUtils::GetWrappedImageURL(url);
305 else
306 object[field] = "";
307 }
308 else if (addonInfo.isMember(field))
309 object[field] = addonInfo[field];
310 }
311
312 if (append)
313 result.append(object);
314 else
315 result = object;
316 }
317