From 9d11b08ad61b1f0d6d7023ce403285d8662efaed Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 4 Mar 2015 00:23:39 +0100 Subject: sync with upstream --- xbmc/addons/AddonInstaller.cpp | 974 ----------------------------------------- 1 file changed, 974 deletions(-) delete mode 100644 xbmc/addons/AddonInstaller.cpp (limited to 'xbmc/addons/AddonInstaller.cpp') diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp deleted file mode 100644 index f4a241c..0000000 --- a/xbmc/addons/AddonInstaller.cpp +++ /dev/null @@ -1,974 +0,0 @@ -/* - * Copyright (C) 2011-2013 Team XBMC - * http://xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#include "AddonInstaller.h" -#include "utils/log.h" -#include "utils/FileUtils.h" -#include "utils/URIUtils.h" -#include "Util.h" -#include "guilib/LocalizeStrings.h" -#include "filesystem/Directory.h" -#include "settings/AdvancedSettings.h" -#include "settings/Settings.h" -#include "ApplicationMessenger.h" -#include "filesystem/FavouritesDirectory.h" -#include "utils/JobManager.h" -#include "dialogs/GUIDialogYesNo.h" -#include "addons/AddonManager.h" -#include "addons/Repository.h" -#include "guilib/GUIWindowManager.h" // for callback -#include "GUIUserMessages.h" // for callback -#include "utils/StringUtils.h" -#include "dialogs/GUIDialogKaiToast.h" -#include "dialogs/GUIDialogOK.h" -#include "dialogs/GUIDialogProgress.h" -#include "URL.h" - -#include - -using namespace std; -using namespace XFILE; -using namespace ADDON; - - -struct find_map : public binary_function -{ - bool operator() (CAddonInstaller::JobMap::value_type t, unsigned int id) const - { - return (t.second.jobID == id); - } -}; - -CAddonInstaller::CAddonInstaller() - : m_repoUpdateJob(0) -{ } - -CAddonInstaller::~CAddonInstaller() -{ } - -CAddonInstaller &CAddonInstaller::Get() -{ - static CAddonInstaller addonInstaller; - return addonInstaller; -} - -void CAddonInstaller::OnJobComplete(unsigned int jobID, bool success, CJob* job) -{ - if (success) - CAddonMgr::Get().FindAddons(); - - CSingleLock lock(m_critSection); - if (strncmp(job->GetType(), "repoupdate", 10) == 0) - { - // 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(); - } - - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); - g_windowManager.SendThreadMessage(msg); -} - -void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job) -{ - CSingleLock lock(m_critSection); - JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID)); - if (i != m_downloadJobs.end()) - { - // update job progress - i->second.progress = progress; - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM); - msg.SetStringParam(i->first); - lock.Leave(); - g_windowManager.SendThreadMessage(msg); - } -} - -bool CAddonInstaller::IsDownloading() const -{ - CSingleLock lock(m_critSection); - return !m_downloadJobs.empty(); -} - -void CAddonInstaller::GetInstallList(VECADDONS &addons) const -{ - CSingleLock lock(m_critSection); - vector addonIDs; - for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i) - { - if (i->second.jobID) - addonIDs.push_back(i->first); - } - lock.Leave(); - - CAddonDatabase database; - database.Open(); - for (vector::iterator it = addonIDs.begin(); it != addonIDs.end(); ++it) - { - AddonPtr addon; - if (database.GetAddon(*it, addon)) - addons.push_back(addon); - } -} - -bool CAddonInstaller::GetProgress(const std::string &addonID, unsigned int &percent) const -{ - CSingleLock lock(m_critSection); - JobMap::const_iterator i = m_downloadJobs.find(addonID); - if (i != m_downloadJobs.end()) - { - percent = i->second.progress; - return true; - } - return false; -} - -bool CAddonInstaller::Cancel(const std::string &addonID) -{ - CSingleLock lock(m_critSection); - JobMap::iterator i = m_downloadJobs.find(addonID); - if (i != m_downloadJobs.end()) - { - CJobManager::GetInstance().CancelJob(i->second.jobID); - m_downloadJobs.erase(i); - return true; - } - - return false; -} - -bool CAddonInstaller::InstallModal(const std::string &addonID, ADDON::AddonPtr &addon, bool promptForInstall /* = true */) -{ - if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) - return false; - - // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called) - if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false)) - return false; // addon is installed but disabled, and the user has specifically activated something that needs - // the addon - should we enable it? - - // check we have it available - CAddonDatabase database; - database.Open(); - if (!database.GetAddon(addonID, addon)) - return false; - - // if specified ask the user if he wants it installed - if (promptForInstall && - !CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100), - addon->Name().c_str(), g_localizeStrings.Get(24101))) - return false; - - if (!Install(addonID, true, "", false, true)) - return false; - - return CAddonMgr::Get().GetAddon(addonID, addon); -} - -bool CAddonInstaller::Install(const std::string &addonID, bool force /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) -{ - AddonPtr addon; - bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false); - if (addonInstalled && !force) - return true; - - if (referer.empty()) - { - if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) - return false; - } - - // check whether we have it available in a repository - std::string hash; - if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) - return false; - - return DoInstall(addon, hash, addonInstalled, referer, background, modal); -} - -bool CAddonInstaller::DoInstall(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) -{ - // check whether we already have the addon installing - CSingleLock lock(m_critSection); - if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end()) - return false; - - CAddonInstallJob* installJob = new CAddonInstallJob(addon, hash, update, referer); - if (background) - { - unsigned int jobID = CJobManager::GetInstance().AddJob(installJob, this); - m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID))); - return true; - } - - m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0))); - lock.Leave(); - - bool result = false; - if (modal) - result = installJob->DoModal(); - else - result = installJob->DoWork(); - delete installJob; - - lock.Enter(); - JobMap::iterator i = m_downloadJobs.find(addon->ID()); - m_downloadJobs.erase(i); - - return result; -} - -bool CAddonInstaller::InstallFromZip(const std::string &path) -{ - if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) - return false; - - // grab the descriptive XML document from the zip, and read it in - CFileItemList items; - // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol - CURL pathToUrl(path); - CURL zipDir = URIUtils::CreateArchivePath("zip", pathToUrl, ""); - if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder) - { - CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); - return false; - } - - // TODO: possibly add support for github generated zips here? - std::string archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml"); - - CXBMCTinyXML xml; - AddonPtr addon; - if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon)) - { - // set the correct path - addon->Props().path = items[0]->GetPath(); - addon->Props().icon = URIUtils::AddFileToFolder(items[0]->GetPath(), "icon.png"); - - // install the addon - return DoInstall(addon); - } - - CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); - return false; -} - -void CAddonInstaller::InstallFromXBMCRepo(const set &addonIDs) -{ - // first check we have the our repositories up to date (and wait until we do) - UpdateRepos(false, true); - - // now install the addons - for (set::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i) - Install(*i); -} - -bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */) -{ - std::vector preDeps; - preDeps.push_back(addon->ID()); - CAddonDatabase localDB; - if (!database) - database = &localDB; - - return CheckDependencies(addon, preDeps, *database); -} - -bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, - std::vector& preDeps, CAddonDatabase &database) -{ - if (addon == NULL) - return true; // a NULL addon has no dependencies - - if (!database.Open()) - return false; - - ADDONDEPS deps = addon->GetDeps(); - for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) - { - const std::string &addonID = i->first; - const AddonVersion &version = i->second.first; - bool optional = i->second.second; - AddonPtr dep; - bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep); - if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional)) - { - // we have it but our version isn't good enough, or we don't have it and we need it - 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, "CAddonInstallJob[%s]: requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.asString().c_str()); - database.Close(); - return false; - } - } - - // at this point we have our dep, or the dep is optional (and we don't have it) so check that it's OK as well - // 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, database)) - { - database.Close(); - preDeps.push_back(dep->ID()); - return false; - } - } - } - database.Close(); - - return true; -} - -CDateTime CAddonInstaller::LastRepoUpdate() const -{ - CDateTime update; - CAddonDatabase database; - if (!database.Open()) - return update; - - VECADDONS addons; - CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons); - for (unsigned int i = 0; i < addons.size(); i++) - { - CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID()); - if (lastUpdate.IsValid() && lastUpdate > update) - update = lastUpdate; - } - - return update; -} - -void CAddonInstaller::UpdateRepos(bool force, bool wait) -{ - CSingleLock lock(m_critSection); - if (m_repoUpdateJob) - { - if (wait) - { - // wait for our job to complete - lock.Leave(); - CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__); - m_repoUpdateDone.Wait(); - } - return; - } - - // don't run repo update jobs while on the login screen which runs under the master profile - if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN) - return; - - if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600) - return; - - CAddonDatabase database; - if (!database.Open()) - return; - - m_repoUpdateWatch.StartZero(); - - VECADDONS addons; - if (CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons)) - { - for (const auto& repo : addons) - { - CDateTime lastUpdate = database.GetRepoTimestamp(repo->ID()); - if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0, 24, 0, 0) < CDateTime::GetCurrentDateTime() - || repo->Version() != database.GetRepoVersion(repo->ID())) - { - CLog::Log(LOGDEBUG, "Checking repositories for updates (triggered by %s)", repo->Name().c_str()); - m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this); - if (wait) - { - // wait for our job to complete - lock.Leave(); - CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__); - m_repoUpdateDone.Wait(); - } - - return; - } - } - } -} - -bool CAddonInstaller::HasJob(const std::string& ID) const -{ - CSingleLock lock(m_critSection); - return m_downloadJobs.find(ID) != m_downloadJobs.end(); -} - -void CAddonInstaller::PrunePackageCache() -{ - std::map packs; - int64_t size = EnumeratePackageFolder(packs); - int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize * 1024 * 1024; - if (size < limit) - return; - - // Prune packages - // 1. Remove the largest packages, leaving at least 2 for each add-on - CFileItemList items; - CAddonDatabase db; - db.Open(); - for (std::map::const_iterator it = packs.begin(); it != packs.end(); ++it) - { - it->second->Sort(SortByLabel, SortOrderDescending); - for (int j = 2; j < it->second->Size(); j++) - items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j)))); - } - - items.Sort(SortBySize, SortOrderDescending); - int i = 0; - while (size > limit && i < items.Size()) - { - size -= items[i]->m_dwSize; - db.RemovePackage(items[i]->GetPath()); - CFileUtils::DeleteItem(items[i++], true); - } - - if (size > limit) - { - // 2. Remove the oldest packages (leaving least 1 for each add-on) - items.Clear(); - for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) - { - if (it->second->Size() > 1) - items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1)))); - } - - items.Sort(SortByDate, SortOrderAscending); - i = 0; - while (size > limit && i < items.Size()) - { - size -= items[i]->m_dwSize; - db.RemovePackage(items[i]->GetPath()); - CFileUtils::DeleteItem(items[i++],true); - } - } - - // clean up our mess - for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) - delete it->second; -} - -int64_t CAddonInstaller::EnumeratePackageFolder(std::map& result) -{ - CFileItemList items; - CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); - int64_t size = 0; - for (int i = 0; i < items.Size(); i++) - { - if (items[i]->m_bIsFolder) - continue; - - size += items[i]->m_dwSize; - std::string pack,dummy; - AddonVersion::SplitFileName(pack, dummy, items[i]->GetLabel()); - if (result.find(pack) == result.end()) - result[pack] = new CFileItemList; - result[pack]->Add(CFileItemPtr(new CFileItem(*items[i]))); - } - - return size; -} - -CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */) - : m_addon(addon), - m_hash(hash), - m_update(update), - m_referer(referer) -{ } - -AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon) -{ - AddonPtr repoPtr; - - CAddonDatabase database; - if (!database.Open()) - return repoPtr; - - std::string repo; - if (!database.GetRepoForAddon(addon->ID(), repo)) - return repoPtr; - - if (!CAddonMgr::Get().GetAddon(repo, repoPtr)) - return repoPtr; - - if (std::dynamic_pointer_cast(repoPtr) == NULL) - { - repoPtr.reset(); - return repoPtr; - } - - return repoPtr; -} - -bool CAddonInstallJob::GetAddonWithHash(const std::string& addonID, ADDON::AddonPtr& addon, std::string& hash) -{ - CAddonDatabase database; - if (!database.Open()) - return false; - - if (!database.GetAddon(addonID, addon)) - return false; - - AddonPtr ptr = GetRepoForAddon(addon); - if (ptr == NULL) - return false; - - RepositoryPtr repo = std::dynamic_pointer_cast(ptr); - if (repo == NULL) - return false; - - hash = repo->GetAddonHash(addon); - return true; -} - -bool CAddonInstallJob::DoWork() -{ - SetTitle(StringUtils::Format(g_localizeStrings.Get(24057).c_str(), m_addon->Name().c_str())); - SetProgress(0); - - // check whether all the dependencies are available or not - SetText(g_localizeStrings.Get(24058)); - if (!CAddonInstaller::Get().CheckDependencies(m_addon)) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: dependency check failed", m_addon->ID().c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24044)); - return false; - } - - AddonPtr repoPtr = GetRepoForAddon(m_addon); - std::string installFrom; - 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 - // Both these functions are achieved by "copying" using the vfs. - - std::string dest = "special://home/addons/packages/"; - std::string 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 - { - CAddonDatabase db; - if (!db.Open()) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to open database", m_addon->ID().c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID()); - return false; - } - - std::string md5; - // check that we don't already have a valid copy - if (!m_hash.empty() && CFile::Exists(package)) - { - if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) - { - db.RemovePackage(package); - CFile::Delete(package); - } - } - - // zip passed in - download + extract - if (!CFile::Exists(package)) - { - std::string 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); - - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to download %s", m_addon->ID().c_str(), package.c_str()); - ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); - return false; - } - } - - // at this point we have the package - check that it is valid - SetText(g_localizeStrings.Get(24077)); - if (!m_hash.empty()) - { - md5 = CUtil::GetFileMD5(package); - if (!StringUtils::EqualsNoCase(md5, m_hash)) - { - CFile::Delete(package); - - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: MD5 mismatch after download %s", m_addon->ID().c_str(), package.c_str()); - ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); - return false; - } - - db.AddPackage(m_addon->ID(), package, md5); - } - - // check the archive as well - should have just a single folder in the root - CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); - - CFileItemList archivedFiles; - CDirectory::GetDirectory(archive, archivedFiles); - - if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) - { - // invalid package - db.RemovePackage(package); - CFile::Delete(package); - - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); - ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); - return false; - } - - installFrom = archivedFiles[0]->GetPath(); - } - repoPtr.reset(); - } - - // run any pre-install functions - bool reloadAddon = OnPreInstall(); - - // perform install - if (!Install(installFrom, repoPtr)) - return false; - - // run any post-install guff - OnPostInstall(reloadAddon); - - // and we're done! - MarkFinished(); - return true; -} - -bool CAddonInstallJob::DownloadPackage(const std::string &path, const std::string &dest) -{ - if (ShouldCancel(0, 1)) - return false; - - SetText(g_localizeStrings.Get(24078)); - - // need to download/copy the package first - CFileItemList list; - list.Add(CFileItemPtr(new CFileItem(path, false))); - list[0]->Select(true); - - return DoFileOperation(CFileOperationJob::ActionReplace, list, dest, true); -} - -bool CAddonInstallJob::DoFileOperation(FileAction action, CFileItemList &items, const std::string &file, bool useSameJob /* = true */) -{ - bool result = false; - if (useSameJob) - { - SetFileOperation(action, items, file); - - // temporarily disable auto-closing so not to close the current progress indicator - bool autoClose = GetAutoClose(); - if (autoClose) - SetAutoClose(false); - // temporarily disable updating title or text - bool updateInformation = GetUpdateInformation(); - if (updateInformation) - SetUpdateInformation(false); - - result = CFileOperationJob::DoWork(); - - SetUpdateInformation(updateInformation); - SetAutoClose(autoClose); - } - else - { - CFileOperationJob job(action, items, file); - - // pass our progress indicators to the temporary job and only allow it to - // show progress updates (no title or text changes) - job.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), GetUpdateProgress(), false); - - result = job.DoWork(); - } - - return result; -} - -bool CAddonInstallJob::OnPreInstall() -{ - return m_addon->OnPreInstall(); -} - -bool CAddonInstallJob::DeleteAddon(const std::string &addonFolder) -{ - CFileItemList list; - list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); - list[0]->Select(true); - - return DoFileOperation(CFileOperationJob::ActionDelete, list, "", false); -} - -bool CAddonInstallJob::Install(const std::string &installFrom, const AddonPtr& repo) -{ - SetText(g_localizeStrings.Get(24079)); - ADDONDEPS deps = m_addon->GetDeps(); - - unsigned int totalSteps = static_cast(deps.size()); - if (ShouldCancel(0, totalSteps)) - return false; - - // The first thing we do is install dependencies - std::string referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().asString().c_str()); - for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it) - { - if (it->first != "xbmc.metadata") - { - const std::string &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; - - if (!CAddonMgr::Get().IsAddonInstalled(addonID)) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); - return false; - } - } - // don't have the addon or the addon isn't new enough - grab it (no new job for these) - else if (IsModal()) - { - AddonPtr addon; - std::string hash; - if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to find dependency %s", m_addon->ID().c_str(), addonID.c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); - return false; - } - - CAddonInstallJob dependencyJob(addon, hash, force, referer); - - // pass our progress indicators to the temporary job and don't allow it to - // show progress or information updates (no progress, title or text changes) - dependencyJob.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), false, false); - - if (!dependencyJob.DoModal()) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); - return false; - } - } - else if (!CAddonInstaller::Get().Install(addonID, force, referer, false)) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); - return false; - } - } - } - - if (ShouldCancel(std::distance(deps.begin(), it), totalSteps)) - return false; - } - - SetText(g_localizeStrings.Get(24086)); - SetProgress(0); - - // now that we have all our dependencies, we can install our add-on - if (repo != NULL) - { - CFileItemList dummy; - std::string s = StringUtils::Format("plugin://%s/?action=install&package=%s&version=%s", repo->ID().c_str(), - m_addon->ID().c_str(), m_addon->Version().asString().c_str()); - if (!CDirectory::GetDirectory(s, dummy)) - { - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: installation of repository failed", m_addon->ID().c_str()); - ReportInstallError(m_addon->ID(), m_addon->ID()); - return false; - } - } - else - { - std::string addonFolder = installFrom; - URIUtils::RemoveSlashAtEnd(addonFolder); - addonFolder = URIUtils::AddFileToFolder("special://home/addons/", URIUtils::GetFileName(addonFolder)); - - CFileItemList install; - install.Add(CFileItemPtr(new CFileItem(installFrom, true))); - install[0]->Select(true); - - AddonPtr addon; - if (!DoFileOperation(CFileOperationJob::ActionReplace, install, "special://home/addons/", false) || - !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) - { - // failed extraction or failed to load addon description - DeleteAddon(addonFolder); - - std::string addonID = URIUtils::GetFileName(addonFolder); - CLog::Log(LOGERROR, "CAddonInstallJob[%s]: could not read addon description of %s", addonID.c_str(), addonFolder.c_str()); - ReportInstallError(addonID, addonID); - return false; - } - - // Update the addon manager so that it has the newly installed add-on. - CAddonMgr::Get().FindAddons(); - } - SetProgress(100); - - return true; -} - -void CAddonInstallJob::OnPostInstall(bool reloadAddon) -{ - if (!IsModal() && CSettings::Get().GetBool("general.addonnotifications")) - CGUIDialogKaiToast::QueueNotification(m_addon->Icon(), m_addon->Name(), - g_localizeStrings.Get(m_update ? 24065 : 24064), - TOAST_DISPLAY_TIME, false, TOAST_DISPLAY_TIME); - - m_addon->OnPostInstall(reloadAddon, m_update, IsModal()); -} - -void CAddonInstallJob::ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message /* = "" */) -{ - AddonPtr addon; - CAddonDatabase database; - if (database.Open()) - { - database.GetAddon(addonID, addon); - database.Close(); - } - - MarkFinished(); - - std::string msg = message; - if (addon != NULL) - { - AddonPtr addon2; - CAddonMgr::Get().GetAddon(addonID, addon2); - if (msg.empty()) - msg = g_localizeStrings.Get(addon2 != NULL ? 113 : 114); - - if (IsModal()) - CGUIDialogOK::ShowAndGetInput(m_addon->Name(), msg); - else - CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), msg, TOAST_DISPLAY_TIME, false); - } - else - { - if (msg.empty()) - msg = g_localizeStrings.Get(114); - if (IsModal()) - CGUIDialogOK::ShowAndGetInput(fileName, msg); - else - CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, fileName, msg, TOAST_DISPLAY_TIME, false); - } -} - -std::string CAddonInstallJob::AddonID() const -{ - return m_addon ? m_addon->ID() : ""; -} - -CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon) - : m_addon(addon) -{ } - -bool CAddonUnInstallJob::DoWork() -{ - m_addon->OnPreUnInstall(); - - AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon); - RepositoryPtr therepo = std::dynamic_pointer_cast(repoPtr); - if (therepo != NULL && !therepo->Props().libname.empty()) - { - CFileItemList dummy; - std::string 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; - } - else if (!DeleteAddon(m_addon->Path())) - return false; - - OnPostUnInstall(); - - return true; -} - -bool CAddonUnInstallJob::DeleteAddon(const std::string &addonFolder) -{ - CFileItemList list; - list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); - list[0]->Select(true); - - SetFileOperation(CFileOperationJob::ActionDelete, list, ""); - return CFileOperationJob::DoWork(); -} - -void CAddonUnInstallJob::OnPostUnInstall() -{ - bool bSave = false; - CFileItemList items; - XFILE::CFavouritesDirectory::Load(items); - for (int i = 0; i < items.Size(); i++) - { - if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos) - { - items.Remove(items[i].get()); - bSave = true; - } - } - - if (bSave) - CFavouritesDirectory::Save(items); - - m_addon->OnPostUnInstall(); -} -- cgit v1.2.3