From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- xbmc/addons/AddonManager.cpp | 1030 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1030 insertions(+) create mode 100644 xbmc/addons/AddonManager.cpp (limited to 'xbmc/addons/AddonManager.cpp') diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp new file mode 100644 index 0000000..9ab5c31 --- /dev/null +++ b/xbmc/addons/AddonManager.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "AddonManager.h" +#include "Addon.h" +#include "AudioEncoder.h" +#include "DllLibCPluff.h" +#include "utils/StringUtils.h" +#include "utils/JobManager.h" +#include "threads/SingleLock.h" +#include "FileItem.h" +#include "LangInfo.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/XBMCTinyXML.h" +#ifdef HAS_VISUALISATION +#include "Visualisation.h" +#endif +#ifdef HAS_SCREENSAVER +#include "ScreenSaver.h" +#endif +#ifdef HAS_PVRCLIENTS +#include "DllPVRClient.h" +#include "pvr/addons/PVRClient.h" +#endif +//#ifdef HAS_SCRAPERS +#include "Scraper.h" +//#endif +#include "PluginSource.h" +#include "Repository.h" +#include "Skin.h" +#include "Service.h" +#include "ContextItemAddon.h" +#include "Util.h" +#include "addons/Webinterface.h" + +using namespace std; +using namespace XFILE; + +namespace ADDON +{ + +cp_log_severity_t clog_to_cp(int lvl); +void cp_fatalErrorHandler(const char *msg); +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data); + +/********************************************************** + * CAddonMgr + * + */ + +map CAddonMgr::m_managers; + +AddonPtr CAddonMgr::Factory(const cp_extension_t *props) +{ + if (!PlatformSupportsAddon(props->plugin)) + return AddonPtr(); + + /* Check if user directories need to be created */ + const cp_cfg_element_t *settings = GetExtElement(props->configuration, "settings"); + if (settings) + CheckUserDirs(settings); + + const TYPE type = TranslateType(props->ext_point_id); + switch (type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(props)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(props)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(props)); + case ADDON_SCRIPT_WEATHER: + { + // Eden (API v2.0) broke old weather add-ons + AddonPtr result(new CAddon(props)); + AddonVersion ver1 = result->GetDependencyVersion("xbmc.python"); + AddonVersion ver2 = AddonVersion("2.0"); + if (ver1 < ver2) + { + CLog::Log(LOGINFO,"%s: Weather add-ons for api < 2.0 unsupported (%s)",__FUNCTION__,result->ID().c_str()); + return AddonPtr(); + } + return result; + } + case ADDON_SERVICE: + return AddonPtr(new CService(props)); + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + return AddonPtr(new CScraper(props)); + case ADDON_VIZ: + case ADDON_SCREENSAVER: + case ADDON_PVRDLL: + case ADDON_AUDIOENCODER: + { // begin temporary platform handling for Dlls + // ideally platforms issues will be handled by C-Pluff + // this is not an attempt at a solution + std::string value; + if (type == ADDON_SCREENSAVER && 0 == strnicmp(props->plugin->identifier, "screensaver.xbmc.builtin.", 25)) + { // built in screensaver + return AddonPtr(new CAddon(props)); + } + if (type == ADDON_SCREENSAVER) + { // Python screensaver + std::string library = CAddonMgr::Get().GetExtValue(props->configuration, "@library"); + if (URIUtils::HasExtension(library, ".py")) + return AddonPtr(new CScreenSaver(props)); + } + if (type == ADDON_AUDIOENCODER && 0 == strncmp(props->plugin->identifier, + "audioencoder.xbmc.builtin.", 26)) + { // built in audio encoder + return AddonPtr(new CAudioEncoder(props)); + } + std::string tograb; +#if defined(TARGET_ANDROID) + tograb = "@library_android"; +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + tograb = "@library_linux"; +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + tograb = "@library_windx"; +#elif defined(TARGET_DARWIN) + tograb = "@library_osx"; +#endif + value = GetExtValue(props->plugin->extensions->configuration, tograb.c_str()); + if (value.empty()) + break; + if (type == ADDON_VIZ) + { +#if defined(HAS_VISUALISATION) + return AddonPtr(new CVisualisation(props)); +#endif + } + else if (type == ADDON_PVRDLL) + { +#ifdef HAS_PVRCLIENTS + return AddonPtr(new PVR::CPVRClient(props)); +#endif + } + else if (type == ADDON_AUDIOENCODER) + return AddonPtr(new CAudioEncoder(props)); + else + return AddonPtr(new CScreenSaver(props)); + } + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(props)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(props)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(props)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(props)); + default: + break; + } + return AddonPtr(); +} + +bool CAddonMgr::CheckUserDirs(const cp_cfg_element_t *settings) +{ + if (!settings) + return false; + + const cp_cfg_element_t *userdirs = GetExtElement((cp_cfg_element_t *)settings, "userdirs"); + if (!userdirs) + return false; + + ELEMENTS elements; + if (!GetExtElements((cp_cfg_element_t *)userdirs, "userdir", elements)) + return false; + + ELEMENTS::iterator itr = elements.begin(); + while (itr != elements.end()) + { + std::string path = GetExtValue(*itr++, "@path"); + if (!CDirectory::Exists(path)) + { + if (!CUtil::CreateDirectoryEx(path)) + { + CLog::Log(LOGERROR, "CAddonMgr::CheckUserDirs: Unable to create directory %s.", path.c_str()); + return false; + } + } + } + + return true; +} + +CAddonMgr::CAddonMgr() +{ + m_cpluff = NULL; +} + +CAddonMgr::~CAddonMgr() +{ + DeInit(); +} + +CAddonMgr &CAddonMgr::Get() +{ + static CAddonMgr sAddonMgr; + return sAddonMgr; +} + +IAddonMgrCallback* CAddonMgr::GetCallbackForType(TYPE type) +{ + if (m_managers.find(type) == m_managers.end()) + return NULL; + else + return m_managers[type]; +} + +bool CAddonMgr::RegisterAddonMgrCallback(const TYPE type, IAddonMgrCallback* cb) +{ + if (cb == NULL) + return false; + + m_managers.erase(type); + m_managers[type] = cb; + + return true; +} + +void CAddonMgr::UnregisterAddonMgrCallback(TYPE type) +{ + m_managers.erase(type); +} + +bool CAddonMgr::Init() +{ + m_cpluff = new DllLibCPluff; + m_cpluff->Load(); + + m_database.Open(); + + if (!m_cpluff->IsLoaded()) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, could not load libcpluff"); + return false; + } + + m_cpluff->set_fatal_error_handler(cp_fatalErrorHandler); + + cp_status_t status; + status = m_cpluff->init(); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_init() returned status: %i", status); + return false; + } + + //TODO could separate addons into different contexts + // would allow partial unloading of addon framework + m_cp_context = m_cpluff->create_context(&status); + assert(m_cp_context); + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://home/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmc/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_logger(m_cp_context, cp_logger, + &CAddonMgr::Get(), clog_to_cp(g_advancedSettings.m_logLevel)); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_logger() returned status: %i", status); + return false; + } + + FindAddons(); + + VECADDONS repos; + if (GetAddons(ADDON_REPOSITORY, repos)) + { + VECADDONS::iterator it = repos.begin(); + for (;it != repos.end(); ++it) + CLog::Log(LOGNOTICE, "ADDONS: Using repository %s", (*it)->ID().c_str()); + } + + return true; +} + +void CAddonMgr::DeInit() +{ + if (m_cpluff) + m_cpluff->destroy(); + delete m_cpluff; + m_cpluff = NULL; + m_database.Close(); + m_disabled.clear(); +} + +bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/) +{ + // TODO: This isn't particularly efficient as we create an addon type for each addon using the Factory, just so + // we can check addon dependencies in the addon constructor. + VECADDONS addons; + return GetAddons(type, addons, enabled); +} + +bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */) +{ + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + if (!allowRepos && ADDON_REPOSITORY == (TYPE)i) + continue; + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled)) + addons.insert(addons.end(), temp.begin(), temp.end()); + } + return !addons.empty(); +} + +void CAddonMgr::AddToUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + m_updateableAddons.push_back(pAddon); +} + +void CAddonMgr::RemoveFromUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find(m_updateableAddons.begin(), m_updateableAddons.end(), pAddon); + + if(it != m_updateableAddons.end()) + { + m_updateableAddons.erase(it); + } +} + +struct AddonIdFinder +{ + AddonIdFinder(const std::string& id) + : m_id(id) + {} + + bool operator()(const AddonPtr& addon) + { + return m_id == addon->ID(); + } + private: + std::string m_id; +}; + +bool CAddonMgr::ReloadSettings(const std::string &id) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find_if(m_updateableAddons.begin(), m_updateableAddons.end(), AddonIdFinder(id)); + + if( it != m_updateableAddons.end()) + { + return (*it)->ReloadSettings(); + } + return false; +} + +bool CAddonMgr::GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion /*= false*/) +{ + CSingleLock lock(m_critSection); + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, true)) + { + AddonPtr repoAddon; + for (unsigned int j = 0; j < temp.size(); j++) + { + // Ignore duplicates due to add-ons with multiple extension points + bool found = false; + for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); ++addonIt) + { + if ((*addonIt)->ID() == temp[j]->ID()) + found = true; + } + + if (found || !m_database.GetAddon(temp[j]->ID(), repoAddon)) + continue; + + if (temp[j]->Version() < repoAddon->Version() && + !m_database.IsAddonBlacklisted(temp[j]->ID(), + repoAddon->Version().asString().c_str())) + { + if (getLocalVersion) + repoAddon->Props().version = temp[j]->Version(); + addons.push_back(repoAddon); + } + } + } + } + return !addons.empty(); +} + +bool CAddonMgr::HasOutdatedAddons() +{ + VECADDONS dummy; + return GetAllOutdatedAddons(dummy); +} + +bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */) +{ + CSingleLock lock(m_critSection); + addons.clear(); + if (!m_cp_context) + return false; + cp_status_t status; + int num; + std::string ext_point(TranslateType(type)); + cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num); + for(int i=0; i plugin->identifier) != enabled) + { + AddonPtr addon(Factory(props)); + if (addon) + { + if (enabled) + { + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + addons.push_back(addon); + } + } + } + m_cpluff->release_info(m_cp_context, exts); + return addons.size() > 0; +} + +bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/) +{ + CSingleLock lock(m_critSection); + + cp_status_t status; + cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status); + if (status == CP_OK && cpaddon) + { + addon = GetAddonFromDescriptor(cpaddon, type==ADDON_UNKNOWN?"":TranslateType(type)); + m_cpluff->release_info(m_cp_context, cpaddon); + + if (addon) + { + if (enabledOnly && IsAddonDisabled(addon->ID())) + return false; + + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + return NULL != addon.get(); + } + if (cpaddon) + m_cpluff->release_info(m_cp_context, cpaddon); + + return false; +} + +//TODO handle all 'default' cases here, not just scrapers & vizs +bool CAddonMgr::GetDefault(const TYPE &type, AddonPtr &addon) +{ + std::string setting; + switch (type) + { + case ADDON_VIZ: + setting = CSettings::Get().GetString("musicplayer.visualisation"); + break; + case ADDON_SCREENSAVER: + setting = CSettings::Get().GetString("screensaver.mode"); + break; + case ADDON_SCRAPER_ALBUMS: + setting = CSettings::Get().GetString("musiclibrary.albumsscraper"); + break; + case ADDON_SCRAPER_ARTISTS: + setting = CSettings::Get().GetString("musiclibrary.artistsscraper"); + break; + case ADDON_SCRAPER_MOVIES: + setting = CSettings::Get().GetString("scrapers.moviesdefault"); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + setting = CSettings::Get().GetString("scrapers.musicvideosdefault"); + break; + case ADDON_SCRAPER_TVSHOWS: + setting = CSettings::Get().GetString("scrapers.tvshowsdefault"); + break; + case ADDON_WEB_INTERFACE: + setting = CSettings::Get().GetString("services.webskin"); + break; + default: + return false; + } + return GetAddon(setting, addon, type); +} + +bool CAddonMgr::SetDefault(const TYPE &type, const std::string &addonID) +{ + switch (type) + { + case ADDON_VIZ: + CSettings::Get().SetString("musicplayer.visualisation",addonID); + break; + case ADDON_SCREENSAVER: + CSettings::Get().SetString("screensaver.mode",addonID); + break; + case ADDON_SCRAPER_ALBUMS: + CSettings::Get().SetString("musiclibrary.albumsscraper",addonID); + break; + case ADDON_SCRAPER_ARTISTS: + CSettings::Get().SetString("musiclibrary.artistsscraper",addonID); + break; + case ADDON_SCRAPER_MOVIES: + CSettings::Get().SetString("scrapers.moviesdefault",addonID); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + CSettings::Get().SetString("scrapers.musicvideosdefault",addonID); + break; + case ADDON_SCRAPER_TVSHOWS: + CSettings::Get().SetString("scrapers.tvshowsdefault",addonID); + break; + default: + return false; + } + + return true; +} + +std::string CAddonMgr::GetString(const std::string &id, const int number) +{ + AddonPtr addon; + if (GetAddon(id, addon)) + return addon->GetString(number); + + return ""; +} + +void CAddonMgr::FindAddons() +{ + { + CSingleLock lock(m_critSection); + if (m_cpluff && m_cp_context) + { + m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE); + SetChanged(); + } + } + NotifyObservers(ObservableMessageAddons); +} + +void CAddonMgr::RemoveAddon(const std::string& ID) +{ + if (m_cpluff && m_cp_context) + { + m_cpluff->uninstall_plugin(m_cp_context,ID.c_str()); + SetChanged(); + NotifyObservers(ObservableMessageAddons); + } +} + +bool CAddonMgr::DisableAddon(const std::string& ID, bool disable) +{ + CSingleLock lock(m_critSection); + if (m_database.DisableAddon(ID, disable)) + { + m_disabled[ID] = disable; + return true; + } + + return false; +} + +bool CAddonMgr::IsAddonDisabled(const std::string& ID) +{ + CSingleLock lock(m_critSection); + std::map::const_iterator it = m_disabled.find(ID); + if (it != m_disabled.end()) + return it->second; + + bool ret = m_database.IsAddonDisabled(ID); + m_disabled.insert(pair(ID, ret)); + + return ret; +} + +bool CAddonMgr::CanAddonBeDisabled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + AddonPtr localAddon; + // can't disable an addon that isn't installed + if (!IsAddonInstalled(ID, localAddon)) + return false; + + // can't disable an addon that is in use + if (localAddon->IsInUse()) + return false; + + // installed PVR addons can always be disabled + if (localAddon->Type() == ADDON_PVRDLL) + return true; + + std::string systemAddonsPath = CSpecialProtocol::TranslatePath("special://xbmc/addons"); + // can't disable system addons + if (StringUtils::StartsWith(localAddon->Path(), systemAddonsPath)) + return false; + + return true; +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID) +{ + AddonPtr tmp; + return IsAddonInstalled(ID, tmp); +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID, AddonPtr& addon) +{ + return GetAddon(ID, addon, ADDON_UNKNOWN, false); +} + +bool CAddonMgr::CanAddonBeInstalled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(ID)) + return false; + + // can't install broken addons + if (!m_database.IsAddonBroken(ID).empty()) + return false; + + return true; +} + +bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon) +{ + if (addon == NULL) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(addon->ID())) + return false; + + // can't install broken addons + if (!addon->Props().broken.empty()) + return false; + + return true; +} + +std::string CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag) +{ + if (!root) + return ""; + + const cp_cfg_element_t *eng = NULL; + for (unsigned int i = 0; i < root->num_children; i++) + { + const cp_cfg_element_t &child = root->children[i]; + if (strcmp(tag, child.name) == 0) + { // see if we have a "lang" attribute + const char *lang = m_cpluff->lookup_cfg_value((cp_cfg_element_t*)&child, "@lang"); + if (lang && 0 == strcmp(lang,g_langInfo.GetLanguageLocale().c_str())) + return child.value ? child.value : ""; + if (!lang || 0 == strcmp(lang, "en")) + eng = &child; + } + } + return (eng && eng->value) ? eng->value : ""; +} + +AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps) +{ + switch (addonProps.type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(addonProps)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(addonProps)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(addonProps)); + case ADDON_SERVICE: + return AddonPtr(new CService(addonProps)); + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + return AddonPtr(new CScraper(addonProps)); + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(addonProps)); +#if defined(HAS_VISUALISATION) + case ADDON_VIZ: + return AddonPtr(new CVisualisation(addonProps)); +#endif + case ADDON_SCREENSAVER: + return AddonPtr(new CScreenSaver(addonProps)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(addonProps)); + case ADDON_PVRDLL: + return AddonPtr(new PVR::CPVRClient(addonProps)); + case ADDON_AUDIOENCODER: + return AddonPtr(new CAudioEncoder(addonProps)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(addonProps)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(addonProps)); + default: + break; + } + return AddonPtr(); +} + +/* + * libcpluff interaction + */ + +bool CAddonMgr::PlatformSupportsAddon(const cp_plugin_info_t *plugin) const +{ + if (!plugin || !plugin->num_extensions) + return false; + const cp_extension_t *metadata = GetExtension(plugin, "xbmc.addon.metadata"); // platforms; + if (CAddonMgr::Get().GetExtList(metadata->configuration, "platform", platforms)) + { + for (vector::const_iterator platform = platforms.begin(); platform != platforms.end(); ++platform) + { + if (*platform == "all") + return true; +#if defined(TARGET_ANDROID) + if (*platform == "android") +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + if (*platform == "linux") +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + if (*platform == "windx") +#elif defined(TARGET_DARWIN_OSX) +// Remove this after Frodo and add an architecture filter +// in addition to platform. +#if defined(__x86_64__) + if (*platform == "osx64" || *platform == "osx") +#else + if (*platform == "osx32" || *platform == "osx") +#endif +#elif defined(TARGET_DARWIN_IOS) + if (*platform == "ios") +#endif + return true; + } + return false; // no works for us + } + return true; // assume no is equivalent to all +} + +const cp_cfg_element_t *CAddonMgr::GetExtElement(cp_cfg_element_t *base, const char *path) +{ + const cp_cfg_element_t *element = NULL; + if (base) + element = m_cpluff->lookup_cfg_element(base, path); + return element; +} + +bool CAddonMgr::GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &elements) +{ + if (!base || !path) + return false; + + for (unsigned int i = 0; i < base->num_children; i++) + { + std::string temp = base->children[i].name; + if (!temp.compare(path)) + elements.push_back(&base->children[i]); + } + + return !elements.empty(); +} + +const cp_extension_t *CAddonMgr::GetExtension(const cp_plugin_info_t *props, const char *extension) const +{ + if (!props) + return NULL; + for (unsigned int i = 0; i < props->num_extensions; ++i) + { + if (0 == strcmp(props->extensions[i].ext_point_id, extension)) + return &props->extensions[i]; + } + return NULL; +} + +std::string CAddonMgr::GetExtValue(cp_cfg_element_t *base, const char *path) +{ + const char *value = ""; + if (base && (value = m_cpluff->lookup_cfg_value(base, path))) + return value; + else + return ""; +} + +bool CAddonMgr::GetExtList(cp_cfg_element_t *base, const char *path, vector &result) const +{ + result.clear(); + if (!base || !path) + return false; + const char *all = m_cpluff->lookup_cfg_value(base, path); + if (!all || *all == 0) + return false; + StringUtils::Tokenize(all, result, ' '); + return true; +} + +AddonPtr CAddonMgr::GetAddonFromDescriptor(const cp_plugin_info_t *info, + const std::string& type) +{ + if (!info) + return AddonPtr(); + + if (!info->extensions && type.empty()) + { // no extensions, so we need only the dep information + return AddonPtr(new CAddon(info)); + } + + // grab a relevant extension point, ignoring our kodi.addon.metadata extension point + for (unsigned int i = 0; i < info->num_extensions; ++i) + { + if (0 != strcmp("xbmc.addon.metadata" , info->extensions[i].ext_point_id) && //extensions[i].ext_point_id) && + (type.empty() || 0 == strcmp(type.c_str(), info->extensions[i].ext_point_id))) + { // note that Factory takes care of whether or not we have platform support + return Factory(&info->extensions[i]); + } + } + return AddonPtr(); +} + +// FIXME: This function may not be required +bool CAddonMgr::LoadAddonDescription(const std::string &path, AddonPtr &addon) +{ + cp_status_t status; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor(m_cp_context, CSpecialProtocol::TranslatePath(path).c_str(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(m_cp_context, info); + return NULL != addon.get(); + } + return false; +} + +bool CAddonMgr::AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // each addon XML should have a UTF-8 declaration + TiXmlDeclaration decl("1.0", "UTF-8", ""); + const TiXmlElement *element = root->FirstChildElement("addon"); + while (element) + { + // dump the XML back to text + std::string xml; + xml << decl; + xml << *element; + cp_status_t status; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + AddonPtr addon = GetAddonFromDescriptor(info); + if (addon.get()) + addons.push_back(addon); + m_cpluff->release_info(context, info); + } + element = element->NextSiblingElement("addon"); + } + m_cpluff->destroy_context(context); + return true; +} + +bool CAddonMgr::LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // dump the XML back to text + std::string xml; + xml << TiXmlDeclaration("1.0", "UTF-8", ""); + xml << *root; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(context, info); + } + m_cpluff->destroy_context(context); + return addon != NULL; +} + +bool CAddonMgr::StartServices(const bool beforelogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Starting service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return false; + + bool ret = true; + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (beforelogin && service->GetStartOption() == CService::STARTUP) + || (!beforelogin && service->GetStartOption() == CService::LOGIN) ) + ret &= service->Start(); + } + } + + return ret; +} + +void CAddonMgr::StopServices(const bool onlylogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Stopping service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return; + + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (onlylogin && service->GetStartOption() == CService::LOGIN) + || (!onlylogin) ) + service->Stop(); + } + } +} + +int cp_to_clog(cp_log_severity_t lvl) +{ + if (lvl >= CP_LOG_ERROR) + return LOGINFO; + return LOGDEBUG; +} + +cp_log_severity_t clog_to_cp(int lvl) +{ + if (lvl >= LOG_LEVEL_DEBUG) + return CP_LOG_INFO; + return CP_LOG_ERROR; +} + +void cp_fatalErrorHandler(const char *msg) +{ + CLog::Log(LOGERROR, "ADDONS: CPluffFatalError(%s)", msg); +} + +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data) +{ + if(!apid) + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s'", msg); + else + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg); +} + +} /* namespace ADDON */ + -- cgit v1.2.3