summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/AddonManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/AddonManager.cpp')
-rw-r--r--xbmc/addons/AddonManager.cpp1030
1 files changed, 1030 insertions, 0 deletions
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 @@
1/*
2 * Copyright (C) 2005-2013 Team XBMC
3 * http://xbmc.org
4 *
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)
8 * any later version.
9 *
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.
14 *
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/>.
18 *
19 */
20#include "AddonManager.h"
21#include "Addon.h"
22#include "AudioEncoder.h"
23#include "DllLibCPluff.h"
24#include "utils/StringUtils.h"
25#include "utils/JobManager.h"
26#include "threads/SingleLock.h"
27#include "FileItem.h"
28#include "LangInfo.h"
29#include "settings/AdvancedSettings.h"
30#include "settings/Settings.h"
31#include "utils/log.h"
32#include "utils/XBMCTinyXML.h"
33#ifdef HAS_VISUALISATION
34#include "Visualisation.h"
35#endif
36#ifdef HAS_SCREENSAVER
37#include "ScreenSaver.h"
38#endif
39#ifdef HAS_PVRCLIENTS
40#include "DllPVRClient.h"
41#include "pvr/addons/PVRClient.h"
42#endif
43//#ifdef HAS_SCRAPERS
44#include "Scraper.h"
45//#endif
46#include "PluginSource.h"
47#include "Repository.h"
48#include "Skin.h"
49#include "Service.h"
50#include "ContextItemAddon.h"
51#include "Util.h"
52#include "addons/Webinterface.h"
53
54using namespace std;
55using namespace XFILE;
56
57namespace ADDON
58{
59
60cp_log_severity_t clog_to_cp(int lvl);
61void cp_fatalErrorHandler(const char *msg);
62void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data);
63
64/**********************************************************
65 * CAddonMgr
66 *
67 */
68
69map<TYPE, IAddonMgrCallback*> CAddonMgr::m_managers;
70
71AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
72{
73 if (!PlatformSupportsAddon(props->plugin))
74 return AddonPtr();
75
76 /* Check if user directories need to be created */
77 const cp_cfg_element_t *settings = GetExtElement(props->configuration, "settings");
78 if (settings)
79 CheckUserDirs(settings);
80
81 const TYPE type = TranslateType(props->ext_point_id);
82 switch (type)
83 {
84 case ADDON_PLUGIN:
85 case ADDON_SCRIPT:
86 return AddonPtr(new CPluginSource(props));
87 case ADDON_SCRIPT_LIBRARY:
88 case ADDON_SCRIPT_LYRICS:
89 case ADDON_SCRIPT_MODULE:
90 case ADDON_SUBTITLE_MODULE:
91 return AddonPtr(new CAddon(props));
92 case ADDON_WEB_INTERFACE:
93 return AddonPtr(new CWebinterface(props));
94 case ADDON_SCRIPT_WEATHER:
95 {
96 // Eden (API v2.0) broke old weather add-ons
97 AddonPtr result(new CAddon(props));
98 AddonVersion ver1 = result->GetDependencyVersion("xbmc.python");
99 AddonVersion ver2 = AddonVersion("2.0");
100 if (ver1 < ver2)
101 {
102 CLog::Log(LOGINFO,"%s: Weather add-ons for api < 2.0 unsupported (%s)",__FUNCTION__,result->ID().c_str());
103 return AddonPtr();
104 }
105 return result;
106 }
107 case ADDON_SERVICE:
108 return AddonPtr(new CService(props));
109 case ADDON_SCRAPER_ALBUMS:
110 case ADDON_SCRAPER_ARTISTS:
111 case ADDON_SCRAPER_MOVIES:
112 case ADDON_SCRAPER_MUSICVIDEOS:
113 case ADDON_SCRAPER_TVSHOWS:
114 case ADDON_SCRAPER_LIBRARY:
115 return AddonPtr(new CScraper(props));
116 case ADDON_VIZ:
117 case ADDON_SCREENSAVER:
118 case ADDON_PVRDLL:
119 case ADDON_AUDIOENCODER:
120 { // begin temporary platform handling for Dlls
121 // ideally platforms issues will be handled by C-Pluff
122 // this is not an attempt at a solution
123 std::string value;
124 if (type == ADDON_SCREENSAVER && 0 == strnicmp(props->plugin->identifier, "screensaver.xbmc.builtin.", 25))
125 { // built in screensaver
126 return AddonPtr(new CAddon(props));
127 }
128 if (type == ADDON_SCREENSAVER)
129 { // Python screensaver
130 std::string library = CAddonMgr::Get().GetExtValue(props->configuration, "@library");
131 if (URIUtils::HasExtension(library, ".py"))
132 return AddonPtr(new CScreenSaver(props));
133 }
134 if (type == ADDON_AUDIOENCODER && 0 == strncmp(props->plugin->identifier,
135 "audioencoder.xbmc.builtin.", 26))
136 { // built in audio encoder
137 return AddonPtr(new CAudioEncoder(props));
138 }
139 std::string tograb;
140#if defined(TARGET_ANDROID)
141 tograb = "@library_android";
142#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
143 tograb = "@library_linux";
144#elif defined(TARGET_WINDOWS) && defined(HAS_DX)
145 tograb = "@library_windx";
146#elif defined(TARGET_DARWIN)
147 tograb = "@library_osx";
148#endif
149 value = GetExtValue(props->plugin->extensions->configuration, tograb.c_str());
150 if (value.empty())
151 break;
152 if (type == ADDON_VIZ)
153 {
154#if defined(HAS_VISUALISATION)
155 return AddonPtr(new CVisualisation(props));
156#endif
157 }
158 else if (type == ADDON_PVRDLL)
159 {
160#ifdef HAS_PVRCLIENTS
161 return AddonPtr(new PVR::CPVRClient(props));
162#endif
163 }
164 else if (type == ADDON_AUDIOENCODER)
165 return AddonPtr(new CAudioEncoder(props));
166 else
167 return AddonPtr(new CScreenSaver(props));
168 }
169 case ADDON_SKIN:
170 return AddonPtr(new CSkinInfo(props));
171 case ADDON_VIZ_LIBRARY:
172 return AddonPtr(new CAddonLibrary(props));
173 case ADDON_REPOSITORY:
174 return AddonPtr(new CRepository(props));
175 case ADDON_CONTEXT_ITEM:
176 return AddonPtr(new CContextItemAddon(props));
177 default:
178 break;
179 }
180 return AddonPtr();
181}
182
183bool CAddonMgr::CheckUserDirs(const cp_cfg_element_t *settings)
184{
185 if (!settings)
186 return false;
187
188 const cp_cfg_element_t *userdirs = GetExtElement((cp_cfg_element_t *)settings, "userdirs");
189 if (!userdirs)
190 return false;
191
192 ELEMENTS elements;
193 if (!GetExtElements((cp_cfg_element_t *)userdirs, "userdir", elements))
194 return false;
195
196 ELEMENTS::iterator itr = elements.begin();
197 while (itr != elements.end())
198 {
199 std::string path = GetExtValue(*itr++, "@path");
200 if (!CDirectory::Exists(path))
201 {
202 if (!CUtil::CreateDirectoryEx(path))
203 {
204 CLog::Log(LOGERROR, "CAddonMgr::CheckUserDirs: Unable to create directory %s.", path.c_str());
205 return false;
206 }
207 }
208 }
209
210 return true;
211}
212
213CAddonMgr::CAddonMgr()
214{
215 m_cpluff = NULL;
216}
217
218CAddonMgr::~CAddonMgr()
219{
220 DeInit();
221}
222
223CAddonMgr &CAddonMgr::Get()
224{
225 static CAddonMgr sAddonMgr;
226 return sAddonMgr;
227}
228
229IAddonMgrCallback* CAddonMgr::GetCallbackForType(TYPE type)
230{
231 if (m_managers.find(type) == m_managers.end())
232 return NULL;
233 else
234 return m_managers[type];
235}
236
237bool CAddonMgr::RegisterAddonMgrCallback(const TYPE type, IAddonMgrCallback* cb)
238{
239 if (cb == NULL)
240 return false;
241
242 m_managers.erase(type);
243 m_managers[type] = cb;
244
245 return true;
246}
247
248void CAddonMgr::UnregisterAddonMgrCallback(TYPE type)
249{
250 m_managers.erase(type);
251}
252
253bool CAddonMgr::Init()
254{
255 m_cpluff = new DllLibCPluff;
256 m_cpluff->Load();
257
258 m_database.Open();
259
260 if (!m_cpluff->IsLoaded())
261 {
262 CLog::Log(LOGERROR, "ADDONS: Fatal Error, could not load libcpluff");
263 return false;
264 }
265
266 m_cpluff->set_fatal_error_handler(cp_fatalErrorHandler);
267
268 cp_status_t status;
269 status = m_cpluff->init();
270 if (status != CP_OK)
271 {
272 CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_init() returned status: %i", status);
273 return false;
274 }
275
276 //TODO could separate addons into different contexts
277 // would allow partial unloading of addon framework
278 m_cp_context = m_cpluff->create_context(&status);
279 assert(m_cp_context);
280 status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://home/addons").c_str());
281 if (status != CP_OK)
282 {
283 CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status);
284 return false;
285 }
286
287 status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmc/addons").c_str());
288 if (status != CP_OK)
289 {
290 CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status);
291 return false;
292 }
293
294 status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str());
295 if (status != CP_OK)
296 {
297 CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status);
298 return false;
299 }
300
301 status = m_cpluff->register_logger(m_cp_context, cp_logger,
302 &CAddonMgr::Get(), clog_to_cp(g_advancedSettings.m_logLevel));
303 if (status != CP_OK)
304 {
305 CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_logger() returned status: %i", status);
306 return false;
307 }
308
309 FindAddons();
310
311 VECADDONS repos;
312 if (GetAddons(ADDON_REPOSITORY, repos))
313 {
314 VECADDONS::iterator it = repos.begin();
315 for (;it != repos.end(); ++it)
316 CLog::Log(LOGNOTICE, "ADDONS: Using repository %s", (*it)->ID().c_str());
317 }
318
319 return true;
320}
321
322void CAddonMgr::DeInit()
323{
324 if (m_cpluff)
325 m_cpluff->destroy();
326 delete m_cpluff;
327 m_cpluff = NULL;
328 m_database.Close();
329 m_disabled.clear();
330}
331
332bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/)
333{
334 // TODO: This isn't particularly efficient as we create an addon type for each addon using the Factory, just so
335 // we can check addon dependencies in the addon constructor.
336 VECADDONS addons;
337 return GetAddons(type, addons, enabled);
338}
339
340bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */)
341{
342 for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i)
343 {
344 if (!allowRepos && ADDON_REPOSITORY == (TYPE)i)
345 continue;
346 VECADDONS temp;
347 if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled))
348 addons.insert(addons.end(), temp.begin(), temp.end());
349 }
350 return !addons.empty();
351}
352
353void CAddonMgr::AddToUpdateableAddons(AddonPtr &pAddon)
354{
355 CSingleLock lock(m_critSection);
356 m_updateableAddons.push_back(pAddon);
357}
358
359void CAddonMgr::RemoveFromUpdateableAddons(AddonPtr &pAddon)
360{
361 CSingleLock lock(m_critSection);
362 VECADDONS::iterator it = std::find(m_updateableAddons.begin(), m_updateableAddons.end(), pAddon);
363
364 if(it != m_updateableAddons.end())
365 {
366 m_updateableAddons.erase(it);
367 }
368}
369
370struct AddonIdFinder
371{
372 AddonIdFinder(const std::string& id)
373 : m_id(id)
374 {}
375
376 bool operator()(const AddonPtr& addon)
377 {
378 return m_id == addon->ID();
379 }
380 private:
381 std::string m_id;
382};
383
384bool CAddonMgr::ReloadSettings(const std::string &id)
385{
386 CSingleLock lock(m_critSection);
387 VECADDONS::iterator it = std::find_if(m_updateableAddons.begin(), m_updateableAddons.end(), AddonIdFinder(id));
388
389 if( it != m_updateableAddons.end())
390 {
391 return (*it)->ReloadSettings();
392 }
393 return false;
394}
395
396bool CAddonMgr::GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion /*= false*/)
397{
398 CSingleLock lock(m_critSection);
399 for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i)
400 {
401 VECADDONS temp;
402 if (CAddonMgr::Get().GetAddons((TYPE)i, temp, true))
403 {
404 AddonPtr repoAddon;
405 for (unsigned int j = 0; j < temp.size(); j++)
406 {
407 // Ignore duplicates due to add-ons with multiple extension points
408 bool found = false;
409 for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); ++addonIt)
410 {
411 if ((*addonIt)->ID() == temp[j]->ID())
412 found = true;
413 }
414
415 if (found || !m_database.GetAddon(temp[j]->ID(), repoAddon))
416 continue;
417
418 if (temp[j]->Version() < repoAddon->Version() &&
419 !m_database.IsAddonBlacklisted(temp[j]->ID(),
420 repoAddon->Version().asString().c_str()))
421 {
422 if (getLocalVersion)
423 repoAddon->Props().version = temp[j]->Version();
424 addons.push_back(repoAddon);
425 }
426 }
427 }
428 }
429 return !addons.empty();
430}
431
432bool CAddonMgr::HasOutdatedAddons()
433{
434 VECADDONS dummy;
435 return GetAllOutdatedAddons(dummy);
436}
437
438bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */)
439{
440 CSingleLock lock(m_critSection);
441 addons.clear();
442 if (!m_cp_context)
443 return false;
444 cp_status_t status;
445 int num;
446 std::string ext_point(TranslateType(type));
447 cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num);
448 for(int i=0; i <num; i++)
449 {
450 const cp_extension_t *props = exts[i];
451 if (IsAddonDisabled(props->plugin->identifier) != enabled)
452 {
453 AddonPtr addon(Factory(props));
454 if (addon)
455 {
456 if (enabled)
457 {
458 // if the addon has a running instance, grab that
459 AddonPtr runningAddon = addon->GetRunningInstance();
460 if (runningAddon)
461 addon = runningAddon;
462 }
463 addons.push_back(addon);
464 }
465 }
466 }
467 m_cpluff->release_info(m_cp_context, exts);
468 return addons.size() > 0;
469}
470
471bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/)
472{
473 CSingleLock lock(m_critSection);
474
475 cp_status_t status;
476 cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
477 if (status == CP_OK && cpaddon)
478 {
479 addon = GetAddonFromDescriptor(cpaddon, type==ADDON_UNKNOWN?"":TranslateType(type));
480 m_cpluff->release_info(m_cp_context, cpaddon);
481
482 if (addon)
483 {
484 if (enabledOnly && IsAddonDisabled(addon->ID()))
485 return false;
486
487 // if the addon has a running instance, grab that
488 AddonPtr runningAddon = addon->GetRunningInstance();
489 if (runningAddon)
490 addon = runningAddon;
491 }
492 return NULL != addon.get();
493 }
494 if (cpaddon)
495 m_cpluff->release_info(m_cp_context, cpaddon);
496
497 return false;
498}
499
500//TODO handle all 'default' cases here, not just scrapers & vizs
501bool CAddonMgr::GetDefault(const TYPE &type, AddonPtr &addon)
502{
503 std::string setting;
504 switch (type)
505 {
506 case ADDON_VIZ:
507 setting = CSettings::Get().GetString("musicplayer.visualisation");
508 break;
509 case ADDON_SCREENSAVER:
510 setting = CSettings::Get().GetString("screensaver.mode");
511 break;
512 case ADDON_SCRAPER_ALBUMS:
513 setting = CSettings::Get().GetString("musiclibrary.albumsscraper");
514 break;
515 case ADDON_SCRAPER_ARTISTS:
516 setting = CSettings::Get().GetString("musiclibrary.artistsscraper");
517 break;
518 case ADDON_SCRAPER_MOVIES:
519 setting = CSettings::Get().GetString("scrapers.moviesdefault");
520 break;
521 case ADDON_SCRAPER_MUSICVIDEOS:
522 setting = CSettings::Get().GetString("scrapers.musicvideosdefault");
523 break;
524 case ADDON_SCRAPER_TVSHOWS:
525 setting = CSettings::Get().GetString("scrapers.tvshowsdefault");
526 break;
527 case ADDON_WEB_INTERFACE:
528 setting = CSettings::Get().GetString("services.webskin");
529 break;
530 default:
531 return false;
532 }
533 return GetAddon(setting, addon, type);
534}
535
536bool CAddonMgr::SetDefault(const TYPE &type, const std::string &addonID)
537{
538 switch (type)
539 {
540 case ADDON_VIZ:
541 CSettings::Get().SetString("musicplayer.visualisation",addonID);
542 break;
543 case ADDON_SCREENSAVER:
544 CSettings::Get().SetString("screensaver.mode",addonID);
545 break;
546 case ADDON_SCRAPER_ALBUMS:
547 CSettings::Get().SetString("musiclibrary.albumsscraper",addonID);
548 break;
549 case ADDON_SCRAPER_ARTISTS:
550 CSettings::Get().SetString("musiclibrary.artistsscraper",addonID);
551 break;
552 case ADDON_SCRAPER_MOVIES:
553 CSettings::Get().SetString("scrapers.moviesdefault",addonID);
554 break;
555 case ADDON_SCRAPER_MUSICVIDEOS:
556 CSettings::Get().SetString("scrapers.musicvideosdefault",addonID);
557 break;
558 case ADDON_SCRAPER_TVSHOWS:
559 CSettings::Get().SetString("scrapers.tvshowsdefault",addonID);
560 break;
561 default:
562 return false;
563 }
564
565 return true;
566}
567
568std::string CAddonMgr::GetString(const std::string &id, const int number)
569{
570 AddonPtr addon;
571 if (GetAddon(id, addon))
572 return addon->GetString(number);
573
574 return "";
575}
576
577void CAddonMgr::FindAddons()
578{
579 {
580 CSingleLock lock(m_critSection);
581 if (m_cpluff && m_cp_context)
582 {
583 m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE);
584 SetChanged();
585 }
586 }
587 NotifyObservers(ObservableMessageAddons);
588}
589
590void CAddonMgr::RemoveAddon(const std::string& ID)
591{
592 if (m_cpluff && m_cp_context)
593 {
594 m_cpluff->uninstall_plugin(m_cp_context,ID.c_str());
595 SetChanged();
596 NotifyObservers(ObservableMessageAddons);
597 }
598}
599
600bool CAddonMgr::DisableAddon(const std::string& ID, bool disable)
601{
602 CSingleLock lock(m_critSection);
603 if (m_database.DisableAddon(ID, disable))
604 {
605 m_disabled[ID] = disable;
606 return true;
607 }
608
609 return false;
610}
611
612bool CAddonMgr::IsAddonDisabled(const std::string& ID)
613{
614 CSingleLock lock(m_critSection);
615 std::map<std::string, bool>::const_iterator it = m_disabled.find(ID);
616 if (it != m_disabled.end())
617 return it->second;
618
619 bool ret = m_database.IsAddonDisabled(ID);
620 m_disabled.insert(pair<std::string, bool>(ID, ret));
621
622 return ret;
623}
624
625bool CAddonMgr::CanAddonBeDisabled(const std::string& ID)
626{
627 if (ID.empty())
628 return false;
629
630 CSingleLock lock(m_critSection);
631 AddonPtr localAddon;
632 // can't disable an addon that isn't installed
633 if (!IsAddonInstalled(ID, localAddon))
634 return false;
635
636 // can't disable an addon that is in use
637 if (localAddon->IsInUse())
638 return false;
639
640 // installed PVR addons can always be disabled
641 if (localAddon->Type() == ADDON_PVRDLL)
642 return true;
643
644 std::string systemAddonsPath = CSpecialProtocol::TranslatePath("special://xbmc/addons");
645 // can't disable system addons
646 if (StringUtils::StartsWith(localAddon->Path(), systemAddonsPath))
647 return false;
648
649 return true;
650}
651
652bool CAddonMgr::IsAddonInstalled(const std::string& ID)
653{
654 AddonPtr tmp;
655 return IsAddonInstalled(ID, tmp);
656}
657
658bool CAddonMgr::IsAddonInstalled(const std::string& ID, AddonPtr& addon)
659{
660 return GetAddon(ID, addon, ADDON_UNKNOWN, false);
661}
662
663bool CAddonMgr::CanAddonBeInstalled(const std::string& ID)
664{
665 if (ID.empty())
666 return false;
667
668 CSingleLock lock(m_critSection);
669 // can't install already installed addon
670 if (IsAddonInstalled(ID))
671 return false;
672
673 // can't install broken addons
674 if (!m_database.IsAddonBroken(ID).empty())
675 return false;
676
677 return true;
678}
679
680bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon)
681{
682 if (addon == NULL)
683 return false;
684
685 CSingleLock lock(m_critSection);
686 // can't install already installed addon
687 if (IsAddonInstalled(addon->ID()))
688 return false;
689
690 // can't install broken addons
691 if (!addon->Props().broken.empty())
692 return false;
693
694 return true;
695}
696
697std::string CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag)
698{
699 if (!root)
700 return "";
701
702 const cp_cfg_element_t *eng = NULL;
703 for (unsigned int i = 0; i < root->num_children; i++)
704 {
705 const cp_cfg_element_t &child = root->children[i];
706 if (strcmp(tag, child.name) == 0)
707 { // see if we have a "lang" attribute
708 const char *lang = m_cpluff->lookup_cfg_value((cp_cfg_element_t*)&child, "@lang");
709 if (lang && 0 == strcmp(lang,g_langInfo.GetLanguageLocale().c_str()))
710 return child.value ? child.value : "";
711 if (!lang || 0 == strcmp(lang, "en"))
712 eng = &child;
713 }
714 }
715 return (eng && eng->value) ? eng->value : "";
716}
717
718AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps)
719{
720 switch (addonProps.type)
721 {
722 case ADDON_PLUGIN:
723 case ADDON_SCRIPT:
724 return AddonPtr(new CPluginSource(addonProps));
725 case ADDON_SCRIPT_LIBRARY:
726 case ADDON_SCRIPT_LYRICS:
727 case ADDON_SCRIPT_WEATHER:
728 case ADDON_SCRIPT_MODULE:
729 case ADDON_SUBTITLE_MODULE:
730 return AddonPtr(new CAddon(addonProps));
731 case ADDON_WEB_INTERFACE:
732 return AddonPtr(new CWebinterface(addonProps));
733 case ADDON_SERVICE:
734 return AddonPtr(new CService(addonProps));
735 case ADDON_SCRAPER_ALBUMS:
736 case ADDON_SCRAPER_ARTISTS:
737 case ADDON_SCRAPER_MOVIES:
738 case ADDON_SCRAPER_MUSICVIDEOS:
739 case ADDON_SCRAPER_TVSHOWS:
740 case ADDON_SCRAPER_LIBRARY:
741 return AddonPtr(new CScraper(addonProps));
742 case ADDON_SKIN:
743 return AddonPtr(new CSkinInfo(addonProps));
744#if defined(HAS_VISUALISATION)
745 case ADDON_VIZ:
746 return AddonPtr(new CVisualisation(addonProps));
747#endif
748 case ADDON_SCREENSAVER:
749 return AddonPtr(new CScreenSaver(addonProps));
750 case ADDON_VIZ_LIBRARY:
751 return AddonPtr(new CAddonLibrary(addonProps));
752 case ADDON_PVRDLL:
753 return AddonPtr(new PVR::CPVRClient(addonProps));
754 case ADDON_AUDIOENCODER:
755 return AddonPtr(new CAudioEncoder(addonProps));
756 case ADDON_REPOSITORY:
757 return AddonPtr(new CRepository(addonProps));
758 case ADDON_CONTEXT_ITEM:
759 return AddonPtr(new CContextItemAddon(addonProps));
760 default:
761 break;
762 }
763 return AddonPtr();
764}
765
766/*
767 * libcpluff interaction
768 */
769
770bool CAddonMgr::PlatformSupportsAddon(const cp_plugin_info_t *plugin) const
771{
772 if (!plugin || !plugin->num_extensions)
773 return false;
774 const cp_extension_t *metadata = GetExtension(plugin, "xbmc.addon.metadata"); //<! backword compatibilty
775 if (!metadata)
776 metadata = CAddonMgr::Get().GetExtension(plugin, "kodi.addon.metadata");
777 if (!metadata)
778 return false;
779
780 vector<std::string> platforms;
781 if (CAddonMgr::Get().GetExtList(metadata->configuration, "platform", platforms))
782 {
783 for (vector<std::string>::const_iterator platform = platforms.begin(); platform != platforms.end(); ++platform)
784 {
785 if (*platform == "all")
786 return true;
787#if defined(TARGET_ANDROID)
788 if (*platform == "android")
789#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
790 if (*platform == "linux")
791#elif defined(TARGET_WINDOWS) && defined(HAS_DX)
792 if (*platform == "windx")
793#elif defined(TARGET_DARWIN_OSX)
794// Remove this after Frodo and add an architecture filter
795// in addition to platform.
796#if defined(__x86_64__)
797 if (*platform == "osx64" || *platform == "osx")
798#else
799 if (*platform == "osx32" || *platform == "osx")
800#endif
801#elif defined(TARGET_DARWIN_IOS)
802 if (*platform == "ios")
803#endif
804 return true;
805 }
806 return false; // no <platform> works for us
807 }
808 return true; // assume no <platform> is equivalent to <platform>all</platform>
809}
810
811const cp_cfg_element_t *CAddonMgr::GetExtElement(cp_cfg_element_t *base, const char *path)
812{
813 const cp_cfg_element_t *element = NULL;
814 if (base)
815 element = m_cpluff->lookup_cfg_element(base, path);
816 return element;
817}
818
819bool CAddonMgr::GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &elements)
820{
821 if (!base || !path)
822 return false;
823
824 for (unsigned int i = 0; i < base->num_children; i++)
825 {
826 std::string temp = base->children[i].name;
827 if (!temp.compare(path))
828 elements.push_back(&base->children[i]);
829 }
830
831 return !elements.empty();
832}
833
834const cp_extension_t *CAddonMgr::GetExtension(const cp_plugin_info_t *props, const char *extension) const
835{
836 if (!props)
837 return NULL;
838 for (unsigned int i = 0; i < props->num_extensions; ++i)
839 {
840 if (0 == strcmp(props->extensions[i].ext_point_id, extension))
841 return &props->extensions[i];
842 }
843 return NULL;
844}
845
846std::string CAddonMgr::GetExtValue(cp_cfg_element_t *base, const char *path)
847{
848 const char *value = "";
849 if (base && (value = m_cpluff->lookup_cfg_value(base, path)))
850 return value;
851 else
852 return "";
853}
854
855bool CAddonMgr::GetExtList(cp_cfg_element_t *base, const char *path, vector<std::string> &result) const
856{
857 result.clear();
858 if (!base || !path)
859 return false;
860 const char *all = m_cpluff->lookup_cfg_value(base, path);
861 if (!all || *all == 0)
862 return false;
863 StringUtils::Tokenize(all, result, ' ');
864 return true;
865}
866
867AddonPtr CAddonMgr::GetAddonFromDescriptor(const cp_plugin_info_t *info,
868 const std::string& type)
869{
870 if (!info)
871 return AddonPtr();
872
873 if (!info->extensions && type.empty())
874 { // no extensions, so we need only the dep information
875 return AddonPtr(new CAddon(info));
876 }
877
878 // grab a relevant extension point, ignoring our kodi.addon.metadata extension point
879 for (unsigned int i = 0; i < info->num_extensions; ++i)
880 {
881 if (0 != strcmp("xbmc.addon.metadata" , info->extensions[i].ext_point_id) && //<! backword compatibilty
882 0 != strcmp("kodi.addon.metadata" , info->extensions[i].ext_point_id) &&
883 (type.empty() || 0 == strcmp(type.c_str(), info->extensions[i].ext_point_id)))
884 { // note that Factory takes care of whether or not we have platform support
885 return Factory(&info->extensions[i]);
886 }
887 }
888 return AddonPtr();
889}
890
891// FIXME: This function may not be required
892bool CAddonMgr::LoadAddonDescription(const std::string &path, AddonPtr &addon)
893{
894 cp_status_t status;
895 cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor(m_cp_context, CSpecialProtocol::TranslatePath(path).c_str(), &status);
896 if (info)
897 {
898 addon = GetAddonFromDescriptor(info);
899 m_cpluff->release_info(m_cp_context, info);
900 return NULL != addon.get();
901 }
902 return false;
903}
904
905bool CAddonMgr::AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons)
906{
907 // create a context for these addons
908 cp_status_t status;
909 cp_context_t *context = m_cpluff->create_context(&status);
910 if (!root || !context)
911 return false;
912
913 // each addon XML should have a UTF-8 declaration
914 TiXmlDeclaration decl("1.0", "UTF-8", "");
915 const TiXmlElement *element = root->FirstChildElement("addon");
916 while (element)
917 {
918 // dump the XML back to text
919 std::string xml;
920 xml << decl;
921 xml << *element;
922 cp_status_t status;
923 cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status);
924 if (info)
925 {
926 AddonPtr addon = GetAddonFromDescriptor(info);
927 if (addon.get())
928 addons.push_back(addon);
929 m_cpluff->release_info(context, info);
930 }
931 element = element->NextSiblingElement("addon");
932 }
933 m_cpluff->destroy_context(context);
934 return true;
935}
936
937bool CAddonMgr::LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon)
938{
939 // create a context for these addons
940 cp_status_t status;
941 cp_context_t *context = m_cpluff->create_context(&status);
942 if (!root || !context)
943 return false;
944
945 // dump the XML back to text
946 std::string xml;
947 xml << TiXmlDeclaration("1.0", "UTF-8", "");
948 xml << *root;
949 cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status);
950 if (info)
951 {
952 addon = GetAddonFromDescriptor(info);
953 m_cpluff->release_info(context, info);
954 }
955 m_cpluff->destroy_context(context);
956 return addon != NULL;
957}
958
959bool CAddonMgr::StartServices(const bool beforelogin)
960{
961 CLog::Log(LOGDEBUG, "ADDON: Starting service addons.");
962
963 VECADDONS services;
964 if (!GetAddons(ADDON_SERVICE, services))
965 return false;
966
967 bool ret = true;
968 for (IVECADDONS it = services.begin(); it != services.end(); ++it)
969 {
970 std::shared_ptr<CService> service = std::dynamic_pointer_cast<CService>(*it);
971 if (service)
972 {
973 if ( (beforelogin && service->GetStartOption() == CService::STARTUP)
974 || (!beforelogin && service->GetStartOption() == CService::LOGIN) )
975 ret &= service->Start();
976 }
977 }
978
979 return ret;
980}
981
982void CAddonMgr::StopServices(const bool onlylogin)
983{
984 CLog::Log(LOGDEBUG, "ADDON: Stopping service addons.");
985
986 VECADDONS services;
987 if (!GetAddons(ADDON_SERVICE, services))
988 return;
989
990 for (IVECADDONS it = services.begin(); it != services.end(); ++it)
991 {
992 std::shared_ptr<CService> service = std::dynamic_pointer_cast<CService>(*it);
993 if (service)
994 {
995 if ( (onlylogin && service->GetStartOption() == CService::LOGIN)
996 || (!onlylogin) )
997 service->Stop();
998 }
999 }
1000}
1001
1002int cp_to_clog(cp_log_severity_t lvl)
1003{
1004 if (lvl >= CP_LOG_ERROR)
1005 return LOGINFO;
1006 return LOGDEBUG;
1007}
1008
1009cp_log_severity_t clog_to_cp(int lvl)
1010{
1011 if (lvl >= LOG_LEVEL_DEBUG)
1012 return CP_LOG_INFO;
1013 return CP_LOG_ERROR;
1014}
1015
1016void cp_fatalErrorHandler(const char *msg)
1017{
1018 CLog::Log(LOGERROR, "ADDONS: CPluffFatalError(%s)", msg);
1019}
1020
1021void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data)
1022{
1023 if(!apid)
1024 CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s'", msg);
1025 else
1026 CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg);
1027}
1028
1029} /* namespace ADDON */
1030