only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / addons / Repository.cpp
1 /*
2  *      Copyright (C) 2005-2010 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "Repository.h"
23 #include "tinyXML/tinyxml.h"
24 #include "filesystem/File.h"
25 #include "AddonDatabase.h"
26 #include "settings/Settings.h"
27 #include "FileItem.h"
28 #include "utils/JobManager.h"
29 #include "addons/AddonInstaller.h"
30 #include "utils/log.h"
31 #include "utils/URIUtils.h"
32 #include "dialogs/GUIDialogYesNo.h"
33 #include "dialogs/GUIDialogKaiToast.h"
34
35 using namespace XFILE;
36 using namespace ADDON;
37
38 AddonPtr CRepository::Clone(const AddonPtr &self) const
39 {
40   CRepository* result = new CRepository(*this, self);
41   result->m_info = m_info;
42   result->m_checksum = m_checksum;
43   result->m_datadir = m_datadir;
44   result->m_compressed = m_compressed;
45   result->m_zipped = m_zipped;
46   return AddonPtr(result);
47 }
48
49 CRepository::CRepository(const AddonProps& props) :
50   CAddon(props)
51 {
52   m_compressed = false;
53   m_zipped = false;
54 }
55
56 CRepository::CRepository(const cp_extension_t *ext)
57   : CAddon(ext)
58 {
59   m_compressed = false;
60   m_zipped = false;
61   // read in the other props that we need
62   if (ext)
63   {
64     m_checksum = CAddonMgr::Get().GetExtValue(ext->configuration, "checksum");
65     m_compressed = CAddonMgr::Get().GetExtValue(ext->configuration, "info@compressed").Equals("true");
66     m_info = CAddonMgr::Get().GetExtValue(ext->configuration, "info");
67     m_datadir = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir");
68     m_zipped = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir@zip").Equals("true");
69     m_hashes = CAddonMgr::Get().GetExtValue(ext->configuration, "hashes").Equals("true");
70   }
71 }
72
73 CRepository::CRepository(const CRepository &rhs, const AddonPtr &self)
74   : CAddon(rhs, self)
75 {
76 }
77
78 CRepository::~CRepository()
79 {
80 }
81
82 CStdString CRepository::Checksum()
83 {
84   if (!m_checksum.IsEmpty())
85     return FetchChecksum(m_checksum);
86   return "";
87 }
88
89 CStdString CRepository::FetchChecksum(const CStdString& url)
90 {
91   CSingleLock lock(m_critSection);
92   CFile file;
93   file.Open(url);
94   CStdString checksum;
95   try
96   {
97     char* temp = new char[(size_t)file.GetLength()+1];
98     file.Read(temp,file.GetLength());
99     temp[file.GetLength()] = 0;
100     checksum = temp;
101     delete[] temp;
102   }
103   catch (...)
104   {
105   }
106   return checksum;
107 }
108
109 CStdString CRepository::GetAddonHash(const AddonPtr& addon)
110 {
111   CStdString checksum;
112   if (m_hashes)
113   {
114     checksum = FetchChecksum(addon->Path()+".md5");
115     size_t pos = checksum.find_first_of(" \n");
116     if (pos != CStdString::npos)
117       return checksum.Left(pos);
118   }
119   return checksum;
120 }
121
122 #define SET_IF_NOT_EMPTY(x,y) \
123   { \
124     if (!x.IsEmpty()) \
125        x = y; \
126   }
127
128 VECADDONS CRepository::Parse()
129 {
130   CSingleLock lock(m_critSection);
131
132   VECADDONS result;
133   TiXmlDocument doc;
134
135   CStdString file = m_info;
136   if (m_compressed)
137   {
138     CURL url(m_info);
139     CStdString opts = url.GetProtocolOptions();
140     if (!opts.IsEmpty())
141       opts += "&";
142     url.SetProtocolOptions(opts+"Encoding=gzip");
143     file = url.Get();
144   }
145
146   doc.LoadFile(file);
147   if (doc.RootElement())
148   {
149     CAddonMgr::Get().AddonsFromRepoXML(doc.RootElement(), result);
150     for (IVECADDONS i = result.begin(); i != result.end(); ++i)
151     {
152       AddonPtr addon = *i;
153       if (m_zipped)
154       {
155         CStdString file;
156         file.Format("%s/%s-%s.zip", addon->ID().c_str(), addon->ID().c_str(), addon->Version().c_str());
157         addon->Props().path = URIUtils::AddFileToFolder(m_datadir,file);
158         SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/icon.png"))
159         file.Format("%s/changelog-%s.txt", addon->ID().c_str(), addon->Version().c_str());
160         SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(m_datadir,file))
161         SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/fanart.jpg"))
162       }
163       else
164       {
165         addon->Props().path = URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/");
166         SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/icon.png"))
167         SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/changelog.txt"))
168         SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/fanart.jpg"))
169       }
170     }
171   }
172
173   return result;
174 }
175
176 CRepositoryUpdateJob::CRepositoryUpdateJob(const VECADDONS &repos)
177   : m_repos(repos)
178 {
179 }
180
181 bool CRepositoryUpdateJob::DoWork()
182 {
183   VECADDONS addons;
184   for (VECADDONS::const_iterator i = m_repos.begin(); i != m_repos.end(); ++i)
185   {
186     RepositoryPtr repo = boost::dynamic_pointer_cast<CRepository>(*i);
187     VECADDONS newAddons = GrabAddons(repo);
188     addons.insert(addons.end(), newAddons.begin(), newAddons.end());
189   }
190   if (addons.empty())
191     return false;
192
193   // check for updates
194   CAddonDatabase database;
195   database.Open();
196   for (unsigned int i=0;i<addons.size();++i)
197   {
198     if (!CAddonInstaller::Get().CheckDependencies(addons[i]))
199       addons[i]->Props().broken = g_localizeStrings.Get(24044);
200
201     AddonPtr addon;
202     CAddonMgr::Get().GetAddon(addons[i]->ID(),addon);
203     if (addon && addons[i]->Version() > addon->Version() &&
204         !database.IsAddonBlacklisted(addons[i]->ID(),addons[i]->Version().c_str()))
205     {
206       if (g_settings.m_bAddonAutoUpdate || addon->Type() >= ADDON_VIZ_LIBRARY)
207       {
208         CStdString referer;
209         if (URIUtils::IsInternetStream(addons[i]->Path()))
210           referer.Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().c_str());
211
212         CAddonInstaller::Get().Install(addon->ID(), true, referer);
213       }
214       else if (g_settings.m_bAddonNotifications)
215       {
216         CGUIDialogKaiToast::QueueNotification(addon->Icon(),
217                                               g_localizeStrings.Get(24061),
218                                               addon->Name(),TOAST_DISPLAY_TIME,false,TOAST_DISPLAY_TIME);
219       }
220     }
221     if (!addons[i]->Props().broken.IsEmpty())
222     {
223       if (database.IsAddonBroken(addons[i]->ID()).IsEmpty())
224       {
225         if (addon && CGUIDialogYesNo::ShowAndGetInput(addons[i]->Name(),
226                                              g_localizeStrings.Get(24096),
227                                              g_localizeStrings.Get(24097),
228                                              ""))
229           database.DisableAddon(addons[i]->ID());
230       }
231     }
232     database.BreakAddon(addons[i]->ID(), addons[i]->Props().broken);
233   }
234
235   return true;
236 }
237
238 VECADDONS CRepositoryUpdateJob::GrabAddons(RepositoryPtr& repo)
239 {
240   CAddonDatabase database;
241   database.Open();
242   CStdString checksum;
243   int idRepo = database.GetRepoChecksum(repo->ID(),checksum);
244   CStdString reposum = repo->Checksum();
245   VECADDONS addons;
246   if (idRepo == -1 || !checksum.Equals(reposum))
247   {
248     addons = repo->Parse();
249     if (!addons.empty())
250       database.AddRepository(repo->ID(),addons,reposum);
251     else
252       CLog::Log(LOGERROR,"Repository %s returned no add-ons, listing may have failed",repo->Name().c_str());
253   }
254   else
255   {
256     database.GetRepository(repo->ID(),addons);
257     database.SetRepoTimestamp(repo->ID(),CDateTime::GetCurrentDateTime().GetAsDBDateTime());
258   }
259
260   return addons;
261 }
262