/* * 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 */