Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / addons / AddonDatabase.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
21 #include "AddonDatabase.h"
22 #include "addons/AddonManager.h"
23 #include "utils/log.h"
24 #include "utils/Variant.h"
25 #include "utils/StringUtils.h"
26 #include "XBDateTime.h"
27 #include "addons/Service.h"
28 #include "dbwrappers/dataset.h"
29 #include "pvr/PVRManager.h"
30
31 using namespace ADDON;
32 using namespace std;
33
34 CAddonDatabase::CAddonDatabase()
35 {
36 }
37
38 CAddonDatabase::~CAddonDatabase()
39 {
40 }
41
42 bool CAddonDatabase::Open()
43 {
44   return CDatabase::Open();
45 }
46
47 void CAddonDatabase::CreateTables()
48 {
49   CLog::Log(LOGINFO, "create addon table");
50   m_pDS->exec("CREATE TABLE addon (id integer primary key, type text,"
51               "name text, summary text, description text, stars integer,"
52               "path text, addonID text, icon text, version text, "
53               "changelog text, fanart text, author text, disclaimer text,"
54               "minversion text)\n");
55
56   CLog::Log(LOGINFO, "create addonextra table");
57   m_pDS->exec("CREATE TABLE addonextra (id integer, key text, value text)\n");
58
59   CLog::Log(LOGINFO, "create dependencies table");
60   m_pDS->exec("CREATE TABLE dependencies (id integer, addon text, version text, optional boolean)\n");
61
62   CLog::Log(LOGINFO, "create repo table");
63   m_pDS->exec("CREATE TABLE repo (id integer primary key, addonID text,"
64               "checksum text, lastcheck text)\n");
65
66   CLog::Log(LOGINFO, "create addonlinkrepo table");
67   m_pDS->exec("CREATE TABLE addonlinkrepo (idRepo integer, idAddon integer)\n");
68
69   CLog::Log(LOGINFO, "create disabled table");
70   m_pDS->exec("CREATE TABLE disabled (id integer primary key, addonID text)\n");
71
72   CLog::Log(LOGINFO, "create broken table");
73   m_pDS->exec("CREATE TABLE broken (id integer primary key, addonID text, reason text)\n");
74
75   CLog::Log(LOGINFO, "create blacklist table");
76   m_pDS->exec("CREATE TABLE blacklist (id integer primary key, addonID text, version text)\n");
77
78   CLog::Log(LOGINFO, "create package table");
79   m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n");
80 }
81
82 void CAddonDatabase::CreateAnalytics()
83 {
84   CLog::Log(LOGINFO, "%s creating indicies", __FUNCTION__);
85   m_pDS->exec("CREATE INDEX idxAddon ON addon(addonID)");
86   m_pDS->exec("CREATE INDEX idxAddonExtra ON addonextra(id)");
87   m_pDS->exec("CREATE INDEX idxDependencies ON dependencies(id)");
88   m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_1 ON addonlinkrepo ( idAddon, idRepo )\n");
89   m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_2 ON addonlinkrepo ( idRepo, idAddon )\n");
90   m_pDS->exec("CREATE UNIQUE INDEX idxDisabled ON disabled(addonID)");
91   m_pDS->exec("CREATE UNIQUE INDEX idxBroken ON broken(addonID)");
92   m_pDS->exec("CREATE UNIQUE INDEX idxBlack ON blacklist(addonID)");
93   m_pDS->exec("CREATE UNIQUE INDEX idxPackage ON package(filename)");
94 }
95
96 void CAddonDatabase::UpdateTables(int version)
97 {
98   if (version < 16)
99   {
100     m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n");
101   }
102 }
103
104 int CAddonDatabase::AddAddon(const AddonPtr& addon,
105                              int idRepo)
106 {
107   try
108   {
109     if (NULL == m_pDB.get()) return -1;
110     if (NULL == m_pDS.get()) return -1;
111
112     bool bDisablePVRAddon = addon->Type() == ADDON_PVRDLL && !HasAddon(addon->ID());
113
114     CStdString sql = PrepareSQL("insert into addon (id, type, name, summary,"
115                                "description, stars, path, icon, changelog, "
116                                "fanart, addonID, version, author, disclaimer, minversion)"
117                                " values(NULL, '%s', '%s', '%s', '%s', %i,"
118                                "'%s', '%s', '%s', '%s', '%s','%s','%s','%s','%s')",
119                                TranslateType(addon->Type(),false).c_str(),
120                                addon->Name().c_str(), addon->Summary().c_str(),
121                                addon->Description().c_str(),addon->Stars(),
122                                addon->Path().c_str(), addon->Props().icon.c_str(),
123                                addon->ChangeLog().c_str(),addon->FanArt().c_str(),
124                                addon->ID().c_str(), addon->Version().c_str(),
125                                addon->Author().c_str(),addon->Disclaimer().c_str(),
126                                addon->MinVersion().c_str());
127     m_pDS->exec(sql.c_str());
128     int idAddon = (int)m_pDS->lastinsertid();
129
130     sql = PrepareSQL("insert into addonlinkrepo (idRepo, idAddon) values (%i,%i)",idRepo,idAddon);
131     m_pDS->exec(sql.c_str());
132
133     const InfoMap &info = addon->ExtraInfo();
134     for (InfoMap::const_iterator i = info.begin(); i != info.end(); ++i)
135     {
136       sql = PrepareSQL("insert into addonextra(id, key, value) values (%i, '%s', '%s')", idAddon, i->first.c_str(), i->second.c_str());
137       m_pDS->exec(sql.c_str());
138     }
139     const ADDONDEPS &deps = addon->GetDeps();
140     for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
141     {
142       sql = PrepareSQL("insert into dependencies(id, addon, version, optional) values (%i, '%s', '%s', %i)", idAddon, i->first.c_str(), i->second.first.c_str(), i->second.second ? 1 : 0);
143       m_pDS->exec(sql.c_str());
144     }
145     // these need to be configured
146     if (bDisablePVRAddon)
147       DisableAddon(addon->ID(), true);
148     return idAddon;
149   }
150   catch (...)
151   {
152     CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addon->Name().c_str());
153   }
154   return -1;
155 }
156
157 AddonVersion CAddonDatabase::GetAddonVersion(const std::string &id)
158 {
159   AddonVersion maxversion("0.0.0");
160   try
161   {
162     if (NULL == m_pDB.get()) return maxversion;
163     if (NULL == m_pDS2.get()) return maxversion;
164
165     // there may be multiple addons with this id (eg from different repositories) in the database,
166     // so we want to retrieve the latest version.  Order by version won't work as the database
167     // won't know that 1.10 > 1.2, so grab them all and order outside
168     CStdString sql = PrepareSQL("select version from addon where addonID='%s'",id.c_str());
169     m_pDS2->query(sql.c_str());
170
171     if (m_pDS2->eof())
172       return maxversion;
173
174     while (!m_pDS2->eof())
175     {
176       AddonVersion version(m_pDS2->fv(0).get_asString());
177       if (version > maxversion)
178         maxversion = version;
179       m_pDS2->next();
180     }
181     return maxversion;
182   }
183   catch (...)
184   {
185     CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str());
186   }
187   return maxversion;
188 }
189
190 bool CAddonDatabase::GetAddon(const CStdString& id, AddonPtr& addon)
191 {
192   try
193   {
194     if (NULL == m_pDB.get()) return false;
195     if (NULL == m_pDS2.get()) return false;
196
197     // there may be multiple addons with this id (eg from different repositories) in the database,
198     // so we want to retrieve the latest version.  Order by version won't work as the database
199     // won't know that 1.10 > 1.2, so grab them all and order outside
200     CStdString sql = PrepareSQL("select id,version from addon where addonID='%s'",id.c_str());
201     m_pDS2->query(sql.c_str());
202
203     if (m_pDS2->eof())
204       return false;
205
206     AddonVersion maxversion("0.0.0");
207     int maxid = 0;
208     while (!m_pDS2->eof())
209     {
210       AddonVersion version(m_pDS2->fv(1).get_asString());
211       if (version > maxversion)
212       {
213         maxid = m_pDS2->fv(0).get_asInt();
214         maxversion = version;
215       }
216       m_pDS2->next();
217     }
218     return GetAddon(maxid,addon);
219   }
220   catch (...)
221   {
222     CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str());
223   }
224   addon.reset();
225   return false;
226 }
227
228 bool CAddonDatabase::GetRepoForAddon(const CStdString& addonID, CStdString& repo)
229 {
230   try
231   {
232     if (NULL == m_pDB.get()) return false;
233     if (NULL == m_pDS2.get()) return false;
234
235     CStdString sql = PrepareSQL("select repo.addonID from repo join addonlinkrepo on repo.id=addonlinkrepo.idRepo join addon on addonlinkrepo.idAddon=addon.id where addon.addonID like '%s'", addonID.c_str()); 
236     m_pDS2->query(sql.c_str());
237     if (!m_pDS2->eof())
238     {
239       repo = m_pDS2->fv(0).get_asString();
240       m_pDS2->close();
241       return true;
242     }
243   }
244   catch (...)
245   {
246     CLog::Log(LOGERROR, "%s failed for addon %s", __FUNCTION__, addonID.c_str());
247   }
248   return false;
249 }
250
251 bool CAddonDatabase::GetAddon(int id, AddonPtr &addon)
252 {
253   try
254   {
255     if (NULL == m_pDB.get()) return false;
256     if (NULL == m_pDS2.get()) return false;
257
258     std::string sql = "SELECT addon.*,"
259                       "       broken.reason,"
260                       "       addonextra.key, addonextra.value,"
261                       "       dependencies.addon, dependencies.version, dependencies.optional"
262                       "  FROM addon"
263                       "    LEFT JOIN broken"
264                       "      ON broken.addonID = addon.addonID"
265                       "    LEFT JOIN addonextra"
266                       "      ON addonextra.id = addon.id"
267                       "    LEFT JOIN dependencies"
268                       "      ON dependencies.id = addon.id";
269
270     sql += PrepareSQL(" WHERE addon.id=%i", id);
271
272     m_pDS2->query(sql.c_str());
273     if (!m_pDS2->eof())
274     {
275       const dbiplus::query_data &data = m_pDS2->get_result_set().records;
276       const dbiplus::sql_record* const record = data[0];
277       AddonProps props(record->at(addon_addonID).get_asString(),
278                        TranslateType(record->at(addon_type).get_asString()),
279                        record->at(addon_version).get_asString(),
280                        record->at(addon_minversion).get_asString());
281       props.name = record->at(addon_name).get_asString();
282       props.summary = record->at(addon_summary).get_asString();
283       props.description = record->at(addon_description).get_asString();
284       props.changelog = record->at(addon_changelog).get_asString();
285       props.path = record->at(addon_path).get_asString();
286       props.icon = record->at(addon_icon).get_asString();
287       props.fanart = record->at(addon_fanart).get_asString();
288       props.author = record->at(addon_author).get_asString();
289       props.disclaimer = record->at(addon_disclaimer).get_asString();
290       props.broken = record->at(broken_reason).get_asString();
291
292       /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that
293          extrainfo and dependencies are maps, so insert() will insert the first instance only */
294       for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i)
295       {
296         const dbiplus::sql_record* const record = *i;
297         if (!record->at(addonextra_key).get_asString().empty())
298           props.extrainfo.insert(make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString()));
299         if (!m_pDS2->fv(dependencies_addon).get_asString().empty())
300           props.dependencies.insert(make_pair(record->at(dependencies_addon).get_asString(), make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool())));
301       }
302
303       addon = CAddonMgr::AddonFromProps(props);
304       return NULL != addon.get();
305     }
306   }
307   catch (...)
308   {
309     CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id);
310   }
311   addon.reset();
312   return false;
313 }
314
315 bool CAddonDatabase::GetAddons(VECADDONS& addons)
316 {
317   try
318   {
319     if (NULL == m_pDB.get()) return false;
320     if (NULL == m_pDS2.get()) return false;
321
322     CStdString sql = PrepareSQL("select distinct addonID from addon");
323     m_pDS->query(sql.c_str());
324     while (!m_pDS->eof())
325     {
326       AddonPtr addon;
327       if (GetAddon(m_pDS->fv(0).get_asString(),addon))
328         addons.push_back(addon);
329       m_pDS->next();
330     }
331     m_pDS->close();
332     return true;
333   }
334   catch (...)
335   {
336     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
337   }
338   return false;
339 }
340
341 void CAddonDatabase::DeleteRepository(const CStdString& id)
342 {
343   try
344   {
345     if (NULL == m_pDB.get()) return;
346     if (NULL == m_pDS.get()) return;
347
348     CStdString sql = PrepareSQL("select id from repo where addonID='%s'",id.c_str());
349     m_pDS->query(sql.c_str());
350     if (!m_pDS->eof())
351       DeleteRepository(m_pDS->fv(0).get_asInt());
352   }
353   catch (...)
354   {
355     CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
356   }
357 }
358
359 void CAddonDatabase::DeleteRepository(int idRepo)
360 {
361   try
362   {
363     if (NULL == m_pDB.get()) return;
364     if (NULL == m_pDS.get()) return;
365
366     CStdString sql = PrepareSQL("delete from repo where id=%i",idRepo);
367     m_pDS->exec(sql.c_str());
368     sql = PrepareSQL("delete from addon where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo);
369     m_pDS->exec(sql.c_str());
370     sql = PrepareSQL("delete from addonextra where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo);
371     m_pDS->exec(sql.c_str());
372     sql = PrepareSQL("delete from dependencies where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo);
373     m_pDS->exec(sql.c_str());
374     sql = PrepareSQL("delete from addonlinkrepo where idRepo=%i",idRepo);
375     m_pDS->exec(sql.c_str());
376
377   }
378   catch (...)
379   {
380     CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, idRepo);
381   }
382 }
383
384 int CAddonDatabase::AddRepository(const CStdString& id, const VECADDONS& addons, const CStdString& checksum)
385 {
386   try
387   {
388     if (NULL == m_pDB.get()) return -1;
389     if (NULL == m_pDS.get()) return -1;
390
391     CStdString sql;
392     int idRepo = GetRepoChecksum(id,sql);
393     if (idRepo > -1)
394       DeleteRepository(idRepo);
395
396     BeginTransaction();
397
398     CDateTime time = CDateTime::GetCurrentDateTime();
399     sql = PrepareSQL("insert into repo (id,addonID,checksum,lastcheck) values (NULL,'%s','%s','%s')",id.c_str(),checksum.c_str(),time.GetAsDBDateTime().c_str());
400     m_pDS->exec(sql.c_str());
401     idRepo = (int)m_pDS->lastinsertid();
402     for (unsigned int i=0;i<addons.size();++i)
403       AddAddon(addons[i],idRepo);
404
405     CommitTransaction();
406     return idRepo;
407   }
408   catch (...)
409   {
410     CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
411     RollbackTransaction();
412   }
413   return -1;
414 }
415
416 int CAddonDatabase::GetRepoChecksum(const std::string& id, std::string& checksum)
417 {
418   try
419   {
420     if (NULL == m_pDB.get()) return -1;
421     if (NULL == m_pDS.get()) return -1;
422
423     std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str());
424     m_pDS->query(strSQL.c_str());
425     if (!m_pDS->eof())
426     {
427       checksum = m_pDS->fv("checksum").get_asString();
428       return m_pDS->fv("id").get_asInt();
429     }
430   }
431   catch (...)
432   {
433     CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
434   }
435   checksum.clear();
436   return -1;
437 }
438
439 CDateTime CAddonDatabase::GetRepoTimestamp(const CStdString& id)
440 {
441   CDateTime date;
442   try
443   {
444     if (NULL == m_pDB.get()) return date;
445     if (NULL == m_pDS.get()) return date;
446
447     CStdString strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str());
448     m_pDS->query(strSQL.c_str());
449     if (!m_pDS->eof())
450     {
451       date.SetFromDBDateTime(m_pDS->fv("lastcheck").get_asString());
452       return date;
453     }
454   }
455   catch (...)
456   {
457     CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
458   }
459   return date;
460 }
461
462 bool CAddonDatabase::SetRepoTimestamp(const CStdString& id, const CStdString& time)
463 {
464   try
465   {
466     if (NULL == m_pDB.get()) return false;
467     if (NULL == m_pDS.get()) return false;
468
469     CStdString sql = PrepareSQL("update repo set lastcheck='%s' where addonID='%s'",time.c_str(),id.c_str());
470     m_pDS->exec(sql.c_str());
471
472     return true;
473   }
474   catch (...)
475   {
476     CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
477   }
478   return false;
479 }
480
481 bool CAddonDatabase::GetRepository(int id, VECADDONS& addons)
482 {
483   try
484   {
485     if (NULL == m_pDB.get()) return false;
486     if (NULL == m_pDS.get()) return false;
487
488     CStdString strSQL = PrepareSQL("select * from addonlinkrepo where idRepo=%i",id);
489     m_pDS->query(strSQL.c_str());
490     while (!m_pDS->eof())
491     {
492       AddonPtr addon;
493       if (GetAddon(m_pDS->fv("idAddon").get_asInt(),addon))
494         addons.push_back(addon);
495       m_pDS->next();
496     }
497     return true;
498   }
499   catch (...)
500   {
501     CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, id);
502   }
503   return false;
504 }
505
506 bool CAddonDatabase::GetRepository(const CStdString& id, VECADDONS& addons)
507 {
508   try
509   {
510     if (NULL == m_pDB.get()) return false;
511     if (NULL == m_pDS.get()) return false;
512
513     CStdString strSQL = PrepareSQL("select id from repo where addonID='%s'",id.c_str());
514     m_pDS->query(strSQL.c_str());
515     if (!m_pDS->eof())
516       return GetRepository(m_pDS->fv(0).get_asInt(),addons);
517   }
518   catch (...)
519   {
520     CLog::Log(LOGERROR, "%s failed on repo %s", __FUNCTION__, id.c_str());
521   }
522   return false;
523 }
524
525 bool CAddonDatabase::Search(const CStdString& search, VECADDONS& addons)
526 {
527   try
528   {
529     if (NULL == m_pDB.get()) return false;
530     if (NULL == m_pDS.get()) return false;
531
532     CStdString strSQL;
533     strSQL=PrepareSQL("SELECT addonID FROM addon WHERE name LIKE '%%%s%%' OR summary LIKE '%%%s%%' OR description LIKE '%%%s%%'", search.c_str(), search.c_str(), search.c_str());
534     CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
535
536     if (!m_pDS->query(strSQL.c_str())) return false;
537     if (m_pDS->num_rows() == 0) return false;
538
539     while (!m_pDS->eof())
540     {
541       AddonPtr addon;
542       GetAddon(m_pDS->fv(0).get_asString(),addon);
543       if (addon->Type() >= ADDON_UNKNOWN+1 && addon->Type() < ADDON_SCRAPER_LIBRARY)
544         addons.push_back(addon);
545       m_pDS->next();
546     }
547     m_pDS->close();
548     return true;
549   }
550   catch (...)
551   {
552     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
553   }
554   return false;
555 }
556
557 void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon,
558                                            CFileItemPtr& pItem)
559 {
560   pItem->SetProperty("Addon.ID", addon->ID());
561   pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true));
562   pItem->SetProperty("Addon.intType", TranslateType(addon->Type()));
563   pItem->SetProperty("Addon.Name", addon->Name());
564   pItem->SetProperty("Addon.Version", addon->Version().c_str());
565   pItem->SetProperty("Addon.Summary", addon->Summary());
566   pItem->SetProperty("Addon.Description", addon->Description());
567   pItem->SetProperty("Addon.Creator", addon->Author());
568   pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer());
569   pItem->SetProperty("Addon.Rating", addon->Stars());
570   CStdString starrating = StringUtils::Format("rating%d.png", addon->Stars());
571   pItem->SetProperty("Addon.StarRating",starrating);
572   pItem->SetProperty("Addon.Path", addon->Path());
573   if (addon->Props().broken == "DEPSNOTMET")
574     pItem->SetProperty("Addon.Broken", g_localizeStrings.Get(24044));
575   else
576     pItem->SetProperty("Addon.Broken", addon->Props().broken);
577   std::map<CStdString,CStdString>::iterator it = 
578                     addon->Props().extrainfo.find("language");
579   if (it != addon->Props().extrainfo.end())
580     pItem->SetProperty("Addon.Language", it->second);
581 }
582
583 bool CAddonDatabase::DisableAddon(const CStdString &addonID, bool disable /* = true */)
584 {
585   try
586   {
587     if (NULL == m_pDB.get()) return false;
588     if (NULL == m_pDS.get()) return false;
589
590     if (disable)
591     {
592       if (!IsAddonDisabled(addonID)) // Enabled
593       {
594         CStdString sql = PrepareSQL("insert into disabled(id, addonID) values(NULL, '%s')", addonID.c_str());
595         m_pDS->exec(sql);
596
597         AddonPtr addon;
598         // If the addon is a service, stop it
599         if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) && addon)
600         {
601           boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
602           if (service)
603             service->Stop();
604         }
605         // restart the pvr manager when disabling a pvr add-on with the pvr manager enabled
606         else if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) && addon &&
607             PVR::CPVRManager::Get().IsStarted())
608           PVR::CPVRManager::Get().Start(true);
609
610         return true;
611       }
612       return false; // already disabled or failed query
613     }
614     else
615     {
616       bool disabled = IsAddonDisabled(addonID); //we need to know if service addon is running
617       CStdString sql = PrepareSQL("delete from disabled where addonID='%s'", addonID.c_str());
618       m_pDS->exec(sql);
619
620       AddonPtr addon;
621       // If the addon is a service, start it
622       if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) && addon && disabled)
623       {
624         boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
625         if (service)
626           service->Start();
627       }
628       // (re)start the pvr manager when enabling a pvr add-on
629       else if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) && addon)
630         PVR::CPVRManager::Get().Start(true);
631     }
632     return true;
633   }
634   catch (...)
635   {
636     CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addonID.c_str());
637   }
638   return false;
639 }
640
641 bool CAddonDatabase::BreakAddon(const CStdString &addonID, const CStdString& reason)
642 {
643   if (reason.empty())
644     return ExecuteQuery(PrepareSQL("DELETE FROM broken WHERE addonID='%s'", addonID.c_str()));
645   else
646     return ExecuteQuery(PrepareSQL("REPLACE INTO broken(addonID, reason) VALUES('%s', '%s')",
647                                    addonID.c_str(), reason.c_str()));
648 }
649
650 bool CAddonDatabase::HasAddon(const CStdString &addonID)
651 {
652   CStdString strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str());
653   CStdString strHasAddon = GetSingleValue("addon", "id", strWhereClause);
654   
655   return !strHasAddon.empty();
656 }
657
658 bool CAddonDatabase::IsAddonDisabled(const CStdString &addonID)
659 {
660   try
661   {
662     if (NULL == m_pDB.get()) return false;
663     if (NULL == m_pDS.get()) return false;
664
665     CStdString sql = PrepareSQL("select id from disabled where addonID='%s'", addonID.c_str());
666     m_pDS->query(sql.c_str());
667     bool ret = !m_pDS->eof(); // in the disabled table -> disabled
668     m_pDS->close();
669     return ret;
670   }
671   catch (...)
672   {
673     CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, addonID.c_str());
674   }
675   return false;
676 }
677
678 bool CAddonDatabase::IsSystemPVRAddonEnabled(const CStdString &addonID)
679 {
680   CStdString strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str());
681   CStdString strEnabled = GetSingleValue("pvrenabled", "id", strWhereClause);
682
683   return !strEnabled.empty();
684 }
685
686 CStdString CAddonDatabase::IsAddonBroken(const CStdString &addonID)
687 {
688   return GetSingleValue(PrepareSQL("SELECT reason FROM broken WHERE addonID='%s'", addonID.c_str()));
689 }
690
691 bool CAddonDatabase::HasDisabledAddons()
692 {
693   try
694   {
695     if (NULL == m_pDB.get()) return false;
696     if (NULL == m_pDS.get()) return false;
697
698     m_pDS->query("select count(id) from disabled");
699     bool ret = !m_pDS->eof() && m_pDS->fv(0).get_asInt() > 0; // have rows -> have disabled addons
700     m_pDS->close();
701     return ret;
702   }
703   catch (...)
704   {
705     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
706   }
707   return false;
708 }
709
710 bool CAddonDatabase::BlacklistAddon(const CStdString& addonID,
711                                     const CStdString& version)
712 {
713   try
714   {
715     if (NULL == m_pDB.get()) return false;
716     if (NULL == m_pDS.get()) return false;
717
718     CStdString sql = PrepareSQL("insert into blacklist(id, addonID, version) values(NULL, '%s', '%s')", addonID.c_str(),version.c_str());
719     m_pDS->exec(sql);
720
721     return true;
722   }
723   catch (...)
724   {
725     CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str());
726   }
727   return false;
728 }
729
730 bool CAddonDatabase::IsAddonBlacklisted(const CStdString& addonID,
731                                         const CStdString& version)
732 {
733   CStdString where = PrepareSQL("addonID='%s' and version='%s'",addonID.c_str(),version.c_str());
734   return !GetSingleValue("blacklist","addonID",where).empty();
735 }
736
737 bool CAddonDatabase::RemoveAddonFromBlacklist(const CStdString& addonID,
738                                               const CStdString& version)
739 {
740   try
741   {
742     if (NULL == m_pDB.get()) return false;
743     if (NULL == m_pDS.get()) return false;
744
745     CStdString sql = PrepareSQL("delete from blacklist where addonID='%s' and version='%s'",addonID.c_str(),version.c_str());
746     m_pDS->exec(sql);
747     return true;
748   }
749   catch (...)
750   {
751     CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str());
752   }
753   return false;
754 }
755
756 bool CAddonDatabase::AddPackage(const CStdString& addonID,
757                                 const CStdString& packageFileName,
758                                 const CStdString& hash)
759 {
760   CStdString sql = PrepareSQL("insert into package(id, addonID, filename, hash)"
761                               "values(NULL, '%s', '%s', '%s')",
762                               addonID.c_str(), packageFileName.c_str(), hash.c_str());
763   return ExecuteQuery(sql);
764 }
765
766 bool CAddonDatabase::GetPackageHash(const CStdString& addonID,
767                                     const CStdString& packageFileName,
768                                     CStdString&       hash)
769 {
770   CStdString where = PrepareSQL("addonID='%s' and filename='%s'",
771                                 addonID.c_str(), packageFileName.c_str());
772   hash = GetSingleValue("package", "hash", where);
773   return !hash.empty();
774 }
775
776 bool CAddonDatabase::RemovePackage(const CStdString& packageFileName)
777 {
778   CStdString sql = PrepareSQL("delete from package where filename='%s'", packageFileName.c_str());
779   return ExecuteQuery(sql);
780 }
781