/* * Copyright (C) 2011-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "AddonsOperations.h" #include "JSONUtils.h" #include "ServiceBroker.h" #include "TextureCache.h" #include "addons/AddonDatabase.h" #include "addons/AddonManager.h" #include "addons/PluginSource.h" #include "addons/addoninfo/AddonInfo.h" #include "addons/addoninfo/AddonType.h" #include "messaging/ApplicationMessenger.h" #include "utils/FileUtils.h" #include "utils/StringUtils.h" #include "utils/Variant.h" using namespace JSONRPC; using namespace ADDON; JSONRPC_STATUS CAddonsOperations::GetAddons(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { std::vector addonTypes; AddonType addonType = CAddonInfo::TranslateType(parameterObject["type"].asString()); CPluginSource::Content content = CPluginSource::Translate(parameterObject["content"].asString()); CVariant enabled = parameterObject["enabled"]; CVariant installed = parameterObject["installed"]; // ignore the "content" parameter if the type is specified but not a plugin or script if (addonType != AddonType::UNKNOWN && addonType != AddonType::PLUGIN && addonType != AddonType::SCRIPT) content = CPluginSource::UNKNOWN; if (addonType >= AddonType::VIDEO && addonType <= AddonType::EXECUTABLE) { addonTypes.push_back(AddonType::PLUGIN); addonTypes.push_back(AddonType::SCRIPT); switch (addonType) { case AddonType::VIDEO: content = CPluginSource::VIDEO; break; case AddonType::AUDIO: content = CPluginSource::AUDIO; break; case AddonType::IMAGE: content = CPluginSource::IMAGE; break; case AddonType::GAME: content = CPluginSource::GAME; break; case AddonType::EXECUTABLE: content = CPluginSource::EXECUTABLE; break; default: break; } } else addonTypes.push_back(addonType); VECADDONS addons; for (const auto& typeIt : addonTypes) { VECADDONS typeAddons; if (typeIt == AddonType::UNKNOWN) { if (!enabled.isBoolean()) //All { if (!installed.isBoolean() || installed.asBoolean()) CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons); if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean())) CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons); } else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled CServiceBroker::GetAddonMgr().GetAddons(typeAddons); else if (!installed.isBoolean() || installed.asBoolean()) CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons); } else { if (!enabled.isBoolean()) //All { if (!installed.isBoolean() || installed.asBoolean()) CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons, typeIt); if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean())) CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons, typeIt); } else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled CServiceBroker::GetAddonMgr().GetAddons(typeAddons, typeIt); else if (!installed.isBoolean() || installed.asBoolean()) CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons, typeIt); } addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); } // remove library addons for (int index = 0; index < (int)addons.size(); index++) { std::shared_ptr plugin; if (content != CPluginSource::UNKNOWN) plugin = std::dynamic_pointer_cast(addons.at(index)); if ((addons.at(index)->Type() <= AddonType::UNKNOWN || addons.at(index)->Type() >= AddonType::MAX_TYPES) || ((content != CPluginSource::UNKNOWN && plugin == NULL) || (plugin != NULL && !plugin->Provides(content)))) { addons.erase(addons.begin() + index); index--; } } int start, end; HandleLimits(parameterObject, result, addons.size(), start, end); CAddonDatabase addondb; for (int index = start; index < end; index++) FillDetails(addons.at(index), parameterObject["properties"], result["addons"], addondb, true); return OK; } JSONRPC_STATUS CAddonsOperations::GetAddonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { std::string id = parameterObject["addonid"].asString(); AddonPtr addon; if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_NO) || addon.get() == NULL || addon->Type() <= AddonType::UNKNOWN || addon->Type() >= AddonType::MAX_TYPES) return InvalidParams; CAddonDatabase addondb; FillDetails(addon, parameterObject["properties"], result["addon"], addondb); return OK; } JSONRPC_STATUS CAddonsOperations::SetAddonEnabled(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { std::string id = parameterObject["addonid"].asString(); AddonPtr addon; if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_NO) || addon == nullptr || addon->Type() <= AddonType::UNKNOWN || addon->Type() >= AddonType::MAX_TYPES) return InvalidParams; bool disabled = false; if (parameterObject["enabled"].isBoolean()) { disabled = !parameterObject["enabled"].asBoolean(); } // we need to toggle the current disabled state of the addon else if (parameterObject["enabled"].isString()) { disabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(id); } else { return InvalidParams; } bool success = disabled ? CServiceBroker::GetAddonMgr().DisableAddon(id, AddonDisabledReason::USER) : CServiceBroker::GetAddonMgr().EnableAddon(id); return success ? ACK : InvalidParams; } JSONRPC_STATUS CAddonsOperations::ExecuteAddon(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { std::string id = parameterObject["addonid"].asString(); AddonPtr addon; if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_YES) || addon.get() == NULL || addon->Type() < AddonType::VISUALIZATION || addon->Type() >= AddonType::MAX_TYPES) return InvalidParams; std::string argv; CVariant params = parameterObject["params"]; if (params.isObject()) { for (CVariant::const_iterator_map it = params.begin_map(); it != params.end_map(); ++it) { if (it != params.begin_map()) argv += ","; argv += it->first + "=" + it->second.asString(); } } else if (params.isArray()) { for (CVariant::const_iterator_array it = params.begin_array(); it != params.end_array(); ++it) { if (it != params.begin_array()) argv += ","; argv += StringUtils::Paramify(it->asString()); } } else if (params.isString()) { if (!params.empty()) argv = StringUtils::Paramify(params.asString()); } std::string cmd; if (params.empty()) cmd = StringUtils::Format("RunAddon({})", id); else cmd = StringUtils::Format("RunAddon({}, {})", id, argv); if (params["wait"].asBoolean()) CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); else CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); return ACK; } static CVariant Serialize(const AddonPtr& addon) { CVariant variant; variant["addonid"] = addon->ID(); variant["type"] = CAddonInfo::TranslateType(addon->Type(), false); variant["name"] = addon->Name(); variant["version"] = addon->Version().asString(); variant["summary"] = addon->Summary(); variant["description"] = addon->Description(); variant["path"] = addon->Path(); variant["author"] = addon->Author(); variant["thumbnail"] = addon->Icon(); variant["disclaimer"] = addon->Disclaimer(); variant["fanart"] = addon->FanArt(); variant["dependencies"] = CVariant(CVariant::VariantTypeArray); for (const auto& dep : addon->GetDependencies()) { CVariant info(CVariant::VariantTypeObject); info["addonid"] = dep.id; info["minversion"] = dep.versionMin.asString(); info["version"] = dep.version.asString(); info["optional"] = dep.optional; variant["dependencies"].push_back(std::move(info)); } if (addon->LifecycleState() == AddonLifecycleState::BROKEN) variant["broken"] = addon->LifecycleStateDescription(); else variant["broken"] = false; if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED) variant["deprecated"] = addon->LifecycleStateDescription(); else variant["deprecated"] = false; variant["extrainfo"] = CVariant(CVariant::VariantTypeArray); for (const auto& kv : addon->ExtraInfo()) { CVariant info(CVariant::VariantTypeObject); info["key"] = kv.first; info["value"] = kv.second; variant["extrainfo"].push_back(std::move(info)); } variant["rating"] = -1; return variant; } void CAddonsOperations::FillDetails(const std::shared_ptr& addon, const CVariant& fields, CVariant& result, CAddonDatabase& addondb, bool append /* = false */) { if (addon.get() == NULL) return; CVariant addonInfo = Serialize(addon); CVariant object; object["addonid"] = addonInfo["addonid"]; object["type"] = addonInfo["type"]; for (unsigned int index = 0; index < fields.size(); index++) { std::string field = fields[index].asString(); // we need to manually retrieve the enabled / installed state of every addon // from the addon database because it can't be read from addon.xml if (field == "enabled") { object[field] = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID()); } else if (field == "installed") { object[field] = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID()); } else if (field == "fanart" || field == "thumbnail") { std::string url = addonInfo[field].asString(); // We need to check the existence of fanart and thumbnails as the addon simply // holds where the art will be, not whether it exists. bool needsRecaching; std::string image = CServiceBroker::GetTextureCache()->CheckCachedImage(url, needsRecaching); if (!image.empty() || CFileUtils::Exists(url)) object[field] = CTextureUtils::GetWrappedImageURL(url); else object[field] = ""; } else if (addonInfo.isMember(field)) object[field] = addonInfo[field]; } if (append) result.append(object); else result = object; }