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