bool CAddonInstaller::CheckDependencies(const AddonPtr &addon) { assert(addon.get()); ADDONDEPS deps = addon->GetDeps(); CAddonDatabase database; database.Open(); for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) { const CStdString &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, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str()); return false; } } // at this point we have our dep, so check that it's OK as well // TODO: should we assume that installed deps are OK? if (!CheckDependencies(dep)) return false; } return true; }
static void DeserializeMetadata(const std::string& document, CAddonBuilder& builder) { CVariant variant = CJSONVariantParser::Parse(document); builder.SetAuthor(variant["author"].asString()); builder.SetDisclaimer(variant["disclaimer"].asString()); builder.SetBroken(variant["broken"].asString()); builder.SetPackageSize(variant["size"].asUnsignedInteger()); builder.SetPath(variant["path"].asString()); builder.SetFanart(variant["fanart"].asString()); builder.SetIcon(variant["icon"].asString()); std::vector<std::string> screenshots; for (auto it = variant["screenshots"].begin_array(); it != variant["screenshots"].end_array(); ++it) screenshots.push_back(it->asString()); builder.SetScreenshots(std::move(screenshots)); builder.SetType(TranslateType(variant["extensions"][0].asString())); ADDONDEPS deps; for (auto it = variant["dependencies"].begin_array(); it != variant["dependencies"].end_array(); ++it) { AddonVersion version((*it)["version"].asString()); deps.emplace((*it)["addonId"].asString(), std::make_pair(std::move(version), (*it)["optional"].asBoolean())); } builder.SetDependencies(std::move(deps)); InfoMap extraInfo; for (auto it = variant["extrainfo"].begin_array(); it != variant["extrainfo"].end_array(); ++it) extraInfo.emplace((*it)["key"].asString(), (*it)["value"].asString()); builder.SetExtrainfo(std::move(extraInfo)); }
bool CAddonInstallJob::Install(const CStdString &installFrom) { CStdString 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); CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/"); AddonPtr addon; if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) { // failed extraction or failed to load addon description CStdString addonID = URIUtils::GetFileName(addonFolder); ReportInstallError(addonID, addonID); CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str()); DeleteAddon(addonFolder); 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; } } } return true; }
bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, std::vector<std::string>& preDeps, CAddonDatabase &database, std::pair<std::string, std::string> &failedDep) { 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::GetInstance().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(); // fill in the details of the failed dependency failedDep.first = addon->ID(); failedDep.second = version.asString(); 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, failedDep)) { database.Close(); preDeps.push_back(dep->ID()); return false; } } } database.Close(); return true; }
ADDONDEPS CAddonMgr::GetDeps(const CStdString &id) { ADDONDEPS result; cp_status_t status; cp_plugin_info_t *info = m_cpluff->get_plugin_info(m_cp_context,id.c_str(),&status); if (info) { for (unsigned int i=0;i<info->num_imports;++i) result.insert(make_pair(CStdString(info->imports[i].plugin_id), make_pair(AddonVersion(info->version), AddonVersion(info->version)))); m_cpluff->release_info(m_cp_context, info); } return result; }
bool CAddonInstallJob::Install(const CStdString &installFrom) { CStdString 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); CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/"); AddonPtr addon; if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) { // failed extraction or failed to load addon description CStdString addonID = URIUtils::GetFileName(addonFolder); ReportInstallError(addonID, addonID); CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str()); DeleteAddon(addonFolder); 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; AddonPtr dependency; if (!CAddonMgr::Get().GetAddon(it->first,dependency) || dependency->Version() < it->second.first) { // don't have the addon or the addon isn't new enough - grab it (no new job for these) if (!CAddonInstaller::Get().Install(it->first, dependency != NULL, referer, false)) { DeleteAddon(addonFolder); return false; } } } return true; }
bool CAddonInstallJob::Install(const CStdString &installFrom) { CStdString 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); CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/"); AddonPtr addon; if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) { // failed extraction or failed to load addon description CStdString addonID = URIUtils::GetFileName(addonFolder); ReportInstallError(addonID, addonID); CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str()); CFileItemList list; list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); list[0]->Select(true); CJobManager::GetInstance().AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete, list, ""), NULL); return false; } // resolve dependencies ADDONDEPS deps = addon->GetDeps(); CStdString referer; referer.Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().str.c_str()); for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it) { if (it->first.Equals("xbmc.metadata")) continue; AddonPtr dependency; if (!CAddonMgr::Get().GetAddon(it->first,dependency)) CAddonInstaller::Get().Install(it->first, false, referer, false); // no new job for these } return true; }
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<unsigned int>(deps.size()); if (ShouldCancel(0, totalSteps)) return false; // The first thing we do is install dependencies 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::GetInstance().GetAddon(addonID, dependency, ADDON_UNKNOWN, false); 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 // 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::GetInstance().HasJob(addonID)) { while (CAddonInstaller::GetInstance().HasJob(addonID)) Sleep(50); if (!CAddonMgr::GetInstance().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()) { RepositoryPtr repoForDep; AddonPtr addon; std::string hash; if (!CAddonInstaller::GetRepoForAddon(addonID, repoForDep) || !CAddonInstallJob::GetAddonWithHash(addonID, repoForDep->ID(), 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, repoForDep, hash); // 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::GetInstance().InstallOrUpdate(addonID, 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::GetInstance().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; } } SetProgress(100); return true; }
bool CAddonDatabase::GetRepositoryContent(const std::string& repoId, VECADDONS& addons) { try { if (NULL == m_pDB.get()) return false; if (NULL == m_pDS.get()) return false; auto start = XbmcThreads::SystemClockMillis(); std::string commonConstraint = PrepareSQL( "JOIN addonlinkrepo ON addon.id=addonlinkrepo.idAddon " "JOIN repo ON addonlinkrepo.idRepo=repo.id " "WHERE repo.checksum IS NOT NULL AND repo.checksum != ''"); if (!repoId.empty()) commonConstraint += PrepareSQL(" AND repo.addonId='%s'", repoId.c_str()); commonConstraint += PrepareSQL(" ORDER BY addon.addonID"); std::vector<CAddonBuilder> result; // Read basic info from the `addon` table { std::string sql = PrepareSQL("SELECT addon.*, broken.reason FROM addon " "LEFT JOIN broken ON broken.addonID=addon.addonID ") + commonConstraint; auto start = XbmcThreads::SystemClockMillis(); m_pDS->query(sql); CLog::Log(LOGDEBUG, "CAddonDatabase: query %s returned %d rows in %d ms", sql.c_str(), m_pDS->num_rows(), XbmcThreads::SystemClockMillis() - start); while (!m_pDS->eof()) { std::string addonId = m_pDS->fv(addon_addonID).get_asString(); AddonVersion version(m_pDS->fv(addon_version).get_asString()); if (!result.empty() && result.back().GetId() == addonId && result.back().GetVersion() >= version) { // We already have a version of this addon in our list which is newer. m_pDS->next(); continue; } CAddonBuilder builder; builder.SetId(addonId); builder.SetVersion(version); builder.SetType(TranslateType(m_pDS->fv(addon_type).get_asString())); builder.SetMinVersion(AddonVersion(m_pDS->fv(addon_minversion).get_asString())); builder.SetName(m_pDS->fv(addon_name).get_asString()); builder.SetSummary(m_pDS->fv(addon_summary).get_asString()); builder.SetDescription(m_pDS->fv(addon_description).get_asString()); builder.SetChangelog(m_pDS->fv(addon_changelog).get_asString()); builder.SetDisclaimer(m_pDS->fv(addon_disclaimer).get_asString()); builder.SetAuthor(m_pDS->fv(addon_author).get_asString()); builder.SetPath(m_pDS->fv(addon_path).get_asString()); builder.SetIcon(m_pDS->fv(addon_icon).get_asString()); builder.SetFanart(m_pDS->fv(addon_fanart).get_asString()); builder.SetBroken(m_pDS->fv(broken_reason).get_asString()); if (!result.empty() && result.back().GetId() == addonId) result.back() = std::move(builder); else result.push_back(std::move(builder)); m_pDS->next(); } } // Read extra info. { std::string sql = PrepareSQL( "SELECT addon.addonID as owner, addonextra.key, addonextra.value " "FROM addon JOIN addonextra ON addon.id=addonextra.id ") + commonConstraint; auto start = XbmcThreads::SystemClockMillis(); m_pDS->query(sql); CLog::Log(LOGDEBUG, "CAddonDatabase: query %s returned %d rows in %d ms", sql.c_str(), m_pDS->num_rows(), XbmcThreads::SystemClockMillis() - start); for (auto& builder : result) { //move cursor to current or next addon while (!m_pDS->eof() && m_pDS->fv(0).get_asString() < builder.GetId()) m_pDS->next(); InfoMap extraInfo; while (!m_pDS->eof() && m_pDS->fv(0).get_asString() == builder.GetId()) { extraInfo.emplace(m_pDS->fv(1).get_asString(), m_pDS->fv(2).get_asString()); m_pDS->next(); } builder.SetExtrainfo(std::move(extraInfo)); } } // Read dependency info. { std::string sql = PrepareSQL( "SELECT addon.addonID as owner, dependencies.addon, dependencies.version, dependencies.optional " "FROM addon JOIN dependencies ON addon.id=dependencies.id ") + commonConstraint; auto start = XbmcThreads::SystemClockMillis(); m_pDS->query(sql); CLog::Log(LOGDEBUG, "CAddonDatabase: query %s returned %d rows in %d ms", sql.c_str(), m_pDS->num_rows(), XbmcThreads::SystemClockMillis() - start); for (auto& builder : result) { //move cursor to the current or next addon while (!m_pDS->eof() && m_pDS->fv(0).get_asString() < builder.GetId()) m_pDS->next(); ADDONDEPS dependencies; while (!m_pDS->eof() && m_pDS->fv(0).get_asString() == builder.GetId()) { dependencies.emplace(m_pDS->fv(1).get_asString(), std::make_pair(AddonVersion(m_pDS->fv(2).get_asString()), m_pDS->fv(3).get_asBool())); m_pDS->next(); } builder.SetDependencies(std::move(dependencies)); } } m_pDS->close(); for (auto& builder : result) { auto addon = builder.Build(); if (addon) addons.push_back(std::move(addon)); } CLog::Log(LOGDEBUG, "CAddonDatabase::GetAddons took %i ms", XbmcThreads::SystemClockMillis() - start); return true; } catch (...) { CLog::Log(LOGERROR, "%s failed", __FUNCTION__); } return false; }
bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) { try { if (NULL == m_pDB.get()) return false; if (NULL == m_pDS2.get()) return false; std::string sql = "SELECT addon.*," " broken.reason," " addonextra.key, addonextra.value," " dependencies.addon, dependencies.version, dependencies.optional" " FROM addon" " LEFT JOIN broken" " ON broken.addonID = addon.addonID" " LEFT JOIN addonextra" " ON addonextra.id = addon.id" " LEFT JOIN dependencies" " ON dependencies.id = addon.id"; sql += PrepareSQL(" WHERE addon.id=%i", id); m_pDS2->query(sql); if (!m_pDS2->eof()) { const dbiplus::query_data &data = m_pDS2->get_result_set().records; const dbiplus::sql_record* const record = data[0]; CAddonBuilder builder; builder.SetId(record->at(addon_addonID).get_asString()); builder.SetType(TranslateType(record->at(addon_type).get_asString())); builder.SetVersion(AddonVersion(record->at(addon_version).get_asString())); builder.SetMinVersion(AddonVersion(record->at(addon_minversion).get_asString())); builder.SetName(record->at(addon_name).get_asString()); builder.SetSummary(record->at(addon_summary).get_asString()); builder.SetDescription(record->at(addon_description).get_asString()); builder.SetChangelog(record->at(addon_changelog).get_asString()); builder.SetDisclaimer(record->at(addon_disclaimer).get_asString()); builder.SetAuthor(record->at(addon_author).get_asString()); builder.SetBroken(record->at(broken_reason).get_asString()); builder.SetPath(record->at(addon_path).get_asString()); builder.SetIcon(record->at(addon_icon).get_asString()); builder.SetFanart(record->at(addon_fanart).get_asString()); InfoMap extrainfo; ADDONDEPS dependencies; /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that extrainfo and dependencies are maps, so insert() will insert the first instance only */ for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i) { const dbiplus::sql_record* const record = *i; if (!record->at(addonextra_key).get_asString().empty()) extrainfo.insert(std::make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString())); if (!m_pDS2->fv(dependencies_addon).get_asString().empty()) dependencies.insert(std::make_pair(record->at(dependencies_addon).get_asString(), std::make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool()))); } builder.SetExtrainfo(std::move(extrainfo)); builder.SetDependencies(std::move(dependencies)); addon = builder.Build(); return NULL != addon.get(); } } catch (...) { CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); } addon.reset(); return false; }
bool CAddonInstallJob::Install(const std::string &installFrom, const AddonPtr& repo) { // The first thing we do is install dependencies ADDONDEPS deps = m_addon->GetDeps(); 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") continue; 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; } // 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; 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)) 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); CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/"); AddonPtr addon; if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) { // failed extraction or failed to load addon description std::string addonID = URIUtils::GetFileName(addonFolder); ReportInstallError(addonID, addonID); CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str()); DeleteAddon(addonFolder); return false; } // Update the addon manager so that it has the newly installed add-on. CAddonMgr::Get().FindAddons(); } return true; }
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<unsigned int>(deps.size()); if (ShouldCancel(0, totalSteps)) return false; // The first thing we do is install dependencies 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::GetInstance().GetAddon(addonID, dependency, ADDON_UNKNOWN, false); 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 // 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::GetInstance().HasJob(addonID)) { while (CAddonInstaller::GetInstance().HasJob(addonID)) Sleep(50); if (!CAddonMgr::GetInstance().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()) { RepositoryPtr repoForDep; AddonPtr addon; std::string hash; if (!CAddonInstallJob::GetAddonWithHash(addonID, repoForDep, 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, repoForDep, hash, false); // 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::GetInstance().InstallOrUpdate(addonID, 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); CFilesystemInstaller fsInstaller; if (!fsInstaller.InstallToFilesystem(installFrom, m_addon->ID())) { ReportInstallError(m_addon->ID(), m_addon->ID()); return false; } SetProgress(100); return true; }