{ // repo job finished
m_repoUpdateDone.Set();
m_repoUpdateJob = 0;
+ lock.Leave();
}
else
{ // download job
JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
if (i != m_downloadJobs.end())
m_downloadJobs.erase(i);
+ lock.Leave();
PrunePackageCache();
}
- lock.Leave();
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
g_windowManager.SendThreadMessage(msg);
return false;
// check whether all the dependencies are available or not
- // TODO: we currently assume that dependencies will install correctly (and each of their dependencies and so on).
- // it may be better to install the dependencies first to minimise the chance of an addon becoming orphaned due to
- // missing deps.
if (!CheckDependencies(addon))
{
CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(24044), TOAST_DISPLAY_TIME, false);
if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon))
{
// set the correct path
- addon->Props().path = path;
+ addon->Props().path = items[0]->GetPath();
// install the addon
return DoInstall(addon);
Install(*i);
}
-bool CAddonInstaller::CheckDependencies(const AddonPtr &addon)
+bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */)
{
std::vector<std::string> preDeps;
preDeps.push_back(addon->ID());
- return CheckDependencies(addon, preDeps);
+ CAddonDatabase localDB;
+ if (!database)
+ database = &localDB;
+ return CheckDependencies(addon, preDeps, *database);
}
bool CAddonInstaller::CheckDependencies(const AddonPtr &addon,
- std::vector<std::string>& preDeps)
+ std::vector<std::string>& preDeps, CAddonDatabase &database)
{
if (!addon.get())
return true; // a NULL addon has no dependencies
ADDONDEPS deps = addon->GetDeps();
- CAddonDatabase database;
database.Open();
for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
{
if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version))
{ // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied.
CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str());
+ database.Close();
return false;
}
}
// TODO: should we assume that installed deps are OK?
if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end())
{
- if (!CheckDependencies(dep, preDeps))
+ if (!CheckDependencies(dep, preDeps, database))
+ {
+ database.Close();
return false;
+ }
preDeps.push_back(dep->ID());
}
}
+ database.Close();
return true;
}
// Prune packages
// 1. Remove the largest packages, leaving at least 2 for each add-on
- CFileItemList items;
+ CFileItemList items;
+ CAddonDatabase db;
+ db.Open();
for (std::map<CStdString,CFileItemList*>::const_iterator it = packs.begin();
it != packs.end();++it)
{
while (size > limit && i < items.Size())
{
size -= items[i]->m_dwSize;
+ db.RemovePackage(items[i]->GetPath());
CFileUtils::DeleteItem(items[i++],true);
}
while (size > limit && i < items.Size())
{
size -= items[i]->m_dwSize;
+ db.RemovePackage(items[i]->GetPath());
CFileUtils::DeleteItem(items[i++],true);
}
}
{
AddonPtr repoPtr = GetRepoForAddon(m_addon);
CStdString installFrom;
- if (!repoPtr || repoPtr->Props().libname.IsEmpty())
+ if (!repoPtr || repoPtr->Props().libname.empty())
{
// Addons are installed by downloading the .zip package on the server to the local
// packages folder, then extracting from the local .zip package into the addons folder
CStdString dest="special://home/addons/packages/";
CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/",
URIUtils::GetFileName(m_addon->Path()));
-
if (URIUtils::HasSlashAtEnd(m_addon->Path()))
{ // passed in a folder - all we need do is copy it across
installFrom = m_addon->Path();
}
else
{
- // zip passed in - download + extract
- CStdString path(m_addon->Path());
- if (!m_referer.IsEmpty() && URIUtils::IsInternetStream(path))
+ CStdString md5;
+ CAddonDatabase db;
+ db.Open();
+
+ // check that we don't already have a valid copy
+ if (!m_hash.empty() && CFile::Exists(package))
{
- CURL url(path);
- url.SetProtocolOptions(m_referer);
- path = url.Get();
+ if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5)
+ {
+ db.RemovePackage(package);
+ CFile::Delete(package);
+ }
}
- if (!CFile::Exists(package) && !DownloadPackage(path, dest))
+
+ // zip passed in - download + extract
+ if (!CFile::Exists(package))
{
- CFile::Delete(package);
- return false;
+ CStdString path(m_addon->Path());
+ if (!m_referer.empty() && URIUtils::IsInternetStream(path))
+ {
+ CURL url(path);
+ url.SetProtocolOptions(m_referer);
+ path = url.Get();
+ }
+
+ if (!DownloadPackage(path, dest))
+ {
+ CFile::Delete(package);
+ return false;
+ }
}
// at this point we have the package - check that it is valid
- if (!CFile::Exists(package) || !CheckHash(package))
+ if (!m_hash.empty())
{
- CFile::Delete(package);
- return false;
+ md5 = CUtil::GetFileMD5(package);
+ if (!md5.Equals(m_hash))
+ {
+ CFile::Delete(package);
+ ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package));
+ CLog::Log(LOGERROR, "MD5 mismatch after download %s", package.c_str());
+ return false;
+ }
+ db.AddPackage(m_addon->ID(), package, md5);
}
// check the archive as well - should have just a single folder in the root
if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder)
{ // invalid package
+ db.RemovePackage(package);
CFile::Delete(package);
return false;
}
bool CAddonInstallJob::Install(const CStdString &installFrom, const AddonPtr& repo)
{
+ // The first thing we do is install dependencies
+ ADDONDEPS deps = m_addon->GetDeps();
+ CStdString referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().c_str());
+ for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
+ {
+ if (it->first.Equals("xbmc.metadata"))
+ continue;
+
+ const CStdString &addonID = it->first;
+ const AddonVersion &version = it->second.first;
+ bool optional = it->second.second;
+ AddonPtr dependency;
+ bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
+ if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
+ { // we have it but our version isn't good enough, or we don't have it and we need it
+ bool force=(dependency != NULL);
+ // dependency is already queued up for install - ::Install will fail
+ // instead we wait until the Job has finished. note that we
+ // recall install on purpose in case prior installation failed
+ if (CAddonInstaller::Get().HasJob(addonID))
+ {
+ while (CAddonInstaller::Get().HasJob(addonID))
+ Sleep(50);
+ force = false;
+ }
+ // don't have the addon or the addon isn't new enough - grab it (no new job for these)
+ if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
+ return false;
+ }
+ }
+
+ // now that we have all our dependencies, we can install our add-on
if (repo)
{
CFileItemList dummy;
- CStdString s;
- s.Format("plugin://%s/?action=install"
- "&package=%s&version=%s", repo->ID().c_str(),
+ CStdString s = StringUtils::Format("plugin://%s/?action=install"
+ "&package=%s&version=%s", repo->ID().c_str(),
m_addon->ID().c_str(),
m_addon->Version().c_str());
if (!CDirectory::GetDirectory(s, dummy))
return false;
}
- // resolve dependencies
- CAddonMgr::Get().FindAddons(); // needed as GetDeps() grabs directly from c-pluff via the addon manager
- ADDONDEPS deps = addon->GetDeps();
- CStdString referer;
- referer.Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().c_str());
- for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
- {
- if (it->first.Equals("xbmc.metadata"))
- continue;
-
- const CStdString &addonID = it->first;
- const AddonVersion &version = it->second.first;
- bool optional = it->second.second;
- AddonPtr dependency;
- bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
- if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
- { // we have it but our version isn't good enough, or we don't have it and we need it
- bool force=(dependency != NULL);
- // dependency is already queued up for install - ::Install will fail
- // instead we wait until the Job has finished. note that we
- // recall install on purpose in case prior installation failed
- if (CAddonInstaller::Get().HasJob(addonID))
- {
- while (CAddonInstaller::Get().HasJob(addonID))
- Sleep(50);
- force = false;
- }
- // don't have the addon or the addon isn't new enough - grab it (no new job for these)
- if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
- {
- DeleteAddon(addonFolder);
- return false;
- }
- }
- }
+ // Update the addon manager so that it has the newly installed add-on.
+ CAddonMgr::Get().FindAddons();
}
return true;
}
toast->ResetTimer();
toast->Close(true);
}
- CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
+ if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
+ CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true);
+ else
+ CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
}
}
}
}
-bool CAddonInstallJob::CheckHash(const CStdString& zipFile)
-{
- if (m_hash.IsEmpty())
- return true;
- CStdString md5 = CUtil::GetFileMD5(zipFile);
- if (!md5.Equals(m_hash))
- {
- CFile::Delete(zipFile);
- ReportInstallError(m_addon->ID(), URIUtils::GetFileName(zipFile));
- CLog::Log(LOGERROR, "MD5 mismatch after download %s", zipFile.c_str());
- return false;
- }
- return true;
-}
-
CStdString CAddonInstallJob::AddonID() const
{
return (m_addon) ? m_addon->ID() : "";
AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon);
RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(repoPtr);
- if (therepo && !therepo->Props().libname.IsEmpty())
+ if (therepo && !therepo->Props().libname.empty())
{
CFileItemList dummy;
- CStdString s;
- s.Format("plugin://%s/?action=uninstall"
- "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
+ CStdString s = StringUtils::Format("plugin://%s/?action=uninstall"
+ "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
if (!CDirectory::GetDirectory(s, dummy))
return false;
}
XFILE::CFavouritesDirectory::Load(items);
for (int i=0; i < items.Size(); ++i)
{
- if (items[i]->GetPath().Find(m_addon->ID()) > -1)
+ if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos)
{
items.Remove(items[i].get());
bSave = true;