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