void CGUIDialogAddonInfo::GrabRollbackVersions() { CFileItemList items; XFILE::CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); items.Sort(SortByLabel, SortOrderAscending); CAddonDatabase db; db.Open(); for (int i=0;i<items.Size();++i) { if (items[i]->m_bIsFolder) continue; std::string ID, version; AddonVersion::SplitFileName(ID,version,items[i]->GetLabel()); if (ID == m_localAddon->ID()) { std::string hash, path(items[i]->GetPath()); if (db.GetPackageHash(m_localAddon->ID(), path, hash)) { std::string md5 = CUtil::GetFileMD5(path); if (md5 == hash) m_rollbackVersions.push_back(version); else /* The package has been corrupted */ { CLog::Log(LOGWARNING, "%s: Removing corrupt addon package %s.", __FUNCTION__, path.c_str()); CFile::Delete(path); db.RemovePackage(path); } } } } }
void CGUIDialogAddonInfo::OnUpdate() { if (!m_localAddon) return; std::vector<std::pair<AddonVersion, std::string>> versions; CAddonDatabase database; database.Open(); database.GetAvailableVersions(m_localAddon->ID(), versions); CFileItemList items; if (XFILE::CDirectory::GetDirectory("special://home/addons/packages/", items, ".zip", DIR_FLAG_NO_FILE_DIRS)) { for (int i = 0; i < items.Size(); ++i) { std::string packageId; std::string versionString; if (AddonVersion::SplitFileName(packageId, versionString, items[i]->GetLabel())) { if (packageId == m_localAddon->ID()) { std::string hash; std::string path(items[i]->GetPath()); if (database.GetPackageHash(m_localAddon->ID(), items[i]->GetPath(), hash)) { std::string md5 = CUtil::GetFileMD5(path); if (md5 == hash) versions.push_back(std::make_pair(AddonVersion(versionString), LOCAL_CACHE)); } } } } } if (versions.empty()) CGUIDialogOK::ShowAndGetInput(CVariant{21341}, CVariant{21342}); else { int i = AskForVersion(versions); if (i != -1) { Close(); //turn auto updating off if downgrading if (m_localAddon->Version() > versions[i].first) CAddonMgr::GetInstance().AddToUpdateBlacklist(m_localAddon->ID()); if (versions[i].second == LOCAL_CACHE) CAddonInstaller::GetInstance().InstallFromZip(StringUtils::Format( "special://home/addons/packages/%s-%s.zip", m_localAddon->ID().c_str(), versions[i].first.asString().c_str())); else CAddonInstaller::GetInstance().Install(m_localAddon->ID(), versions[i].first, versions[i].second); } } }
void CGUIDialogAddonInfo::OnUpdate() { if (!m_localAddon) return; CAddonDatabase database; if (!database.Open()) return; std::vector<std::pair<AddonVersion, std::string>> versions; if (!database.GetAvailableVersions(m_localAddon->ID(), versions)) return; CFileItemList items; if (XFILE::CDirectory::GetDirectory("special://home/addons/packages/", items, ".zip", DIR_FLAG_NO_FILE_DIRS)) { for (int i = 0; i < items.Size(); ++i) { std::string packageId; std::string versionString; if (AddonVersion::SplitFileName(packageId, versionString, items[i]->GetLabel())) { if (packageId == m_localAddon->ID()) { std::string hash; std::string path(items[i]->GetPath()); if (database.GetPackageHash(m_localAddon->ID(), items[i]->GetPath(), hash)) { std::string md5 = CUtil::GetFileMD5(path); if (md5 == hash) versions.push_back(std::make_pair(AddonVersion(versionString), LOCAL_CACHE)); } } } } } if (versions.empty()) { CGUIDialogOK::ShowAndGetInput(CVariant{21341}, CVariant{21342}); return; } auto* dialog = static_cast<CGUIDialogSelect*>(g_windowManager.GetWindow(WINDOW_DIALOG_SELECT)); dialog->Reset(); dialog->SetHeading(CVariant{21338}); dialog->SetUseDetails(true); std::stable_sort(versions.begin(), versions.end(), CompareVersion); for (const auto& versionInfo : versions) { CFileItem item(StringUtils::Format(g_localizeStrings.Get(21339).c_str(), versionInfo.first.asString().c_str())); if (versionInfo.first == m_localAddon->Version()) item.Select(true); AddonPtr repo; if (versionInfo.second == LOCAL_CACHE) { item.SetProperty("Addon.Summary", g_localizeStrings.Get(24095)); item.SetIconImage("DefaultAddonRepository.png"); dialog->Add(item); } else if (CAddonMgr::GetInstance().GetAddon(versionInfo.second, repo, ADDON_REPOSITORY)) { item.SetProperty("Addon.Summary", repo->Name()); item.SetIconImage(repo->Icon()); dialog->Add(item); } } dialog->Open(); if (dialog->IsConfirmed()) { Close(); auto selected = versions.at(dialog->GetSelectedLabel()); //turn auto updating off if downgrading if (selected.first < m_localAddon->Version()) CAddonMgr::GetInstance().AddToUpdateBlacklist(m_localAddon->ID()); if (selected.second == LOCAL_CACHE) CAddonInstaller::GetInstance().InstallFromZip(StringUtils::Format("special://home/addons/packages/%s-%s.zip", m_localAddon->ID().c_str(), selected.first.asString().c_str())); else CAddonInstaller::GetInstance().Install(m_addon->ID(), selected.first, selected.second); } }
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)); std::pair<std::string, std::string> failedDep; if (!CAddonInstaller::GetInstance().CheckDependencies(m_addon, failedDep)) { std::string details = StringUtils::Format(g_localizeStrings.Get(24142).c_str(), failedDep.first.c_str(), failedDep.second.c_str()); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: %s", m_addon->ID().c_str(), details.c_str()); ReportInstallError(m_addon->ID(), m_addon->ID(), details); return false; } std::string installFrom; if (!m_repo || m_repo->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 (!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(); } m_repo.reset(); } // run any pre-install functions ADDON::OnPreInstall(m_addon); // perform install if (!Install(installFrom, m_repo)) return false; CAddonMgr::GetInstance().UnregisterAddon(m_addon->ID()); CAddonMgr::GetInstance().FindAddons(); // run any post-install guff CEventLog::GetInstance().Add( EventPtr(new CAddonManagementEvent(m_addon, m_update ? 24065 : 24064)), !IsModal() && CSettings::GetInstance().GetBool(CSettings::SETTING_GENERAL_ADDONNOTIFICATIONS), false); ADDON::OnPostInstall(m_addon, m_update, IsModal()); //Clear addon from the disabled table CAddonDatabase database; database.Open(); database.DisableAddon(m_addon->ID(), false); // and we're done! MarkFinished(); return true; }
bool CAddonInstallJob::DoWork() { AddonPtr repoPtr = GetRepoForAddon(m_addon); CStdString 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. 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 { CStdString md5; CAddonDatabase db; db.Open(); // 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)) { 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 (!m_hash.empty()) { 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 CStdString archive; URIUtils::CreateArchivePath(archive,"zip",package,""); CFileItemList archivedFiles; CDirectory::GetDirectory(archive, archivedFiles); if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) { // invalid package db.RemovePackage(package); CFile::Delete(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; // something went wrong // run any post-install guff OnPostInstall(reloadAddon); // and we're done! 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)); std::pair<std::string, std::string> failedDep; if (!CAddonInstaller::GetInstance().CheckDependencies(m_addon, failedDep)) { std::string details = StringUtils::Format(g_localizeStrings.Get(24142).c_str(), failedDep.first.c_str(), failedDep.second.c_str()); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: %s", m_addon->ID().c_str(), details.c_str()); ReportInstallError(m_addon->ID(), m_addon->ID(), details); return false; } std::string installFrom; { // 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 (!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. Expected %s, was %s", m_addon->ID().c_str(), m_hash.c_str(), md5.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } db.AddPackage(m_addon->ID(), package, md5); } // check if the archive is valid CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); CFileItemList archivedFiles; AddonPtr temp; if (!CDirectory::GetDirectory(archive, archivedFiles) || archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder || !CAddonMgr::GetInstance().LoadAddonDescription(archivedFiles[0]->GetPath(), temp)) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); db.RemovePackage(package); CFile::Delete(package); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } installFrom = package; } } // run any pre-install functions ADDON::OnPreInstall(m_addon); // perform install if (!Install(installFrom, m_repo)) return false; if (!CAddonMgr::GetInstance().ReloadAddon(m_addon)) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to reload addon", m_addon->ID().c_str()); return false; } g_localizeStrings.LoadAddonStrings(URIUtils::AddFileToFolder(m_addon->Path(), "resources/language/"), CServiceBroker::GetSettings().GetString(CSettings::SETTING_LOCALE_LANGUAGE), m_addon->ID()); ADDON::OnPostInstall(m_addon, m_isUpdate, IsModal()); { CAddonDatabase database; database.Open(); database.SetOrigin(m_addon->ID(), m_repo ? m_repo->ID() : ""); if (m_isUpdate) database.SetLastUpdated(m_addon->ID(), CDateTime::GetCurrentDateTime()); } bool notify = (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_ADDONS_NOTIFICATIONS) || !m_isAutoUpdate) && !IsModal(); CEventLog::GetInstance().Add( EventPtr(new CAddonManagementEvent(m_addon, m_isUpdate ? 24065 : 24084)), notify, false); if (m_isAutoUpdate && !m_addon->Broken().empty()) { CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: auto-disabling due to being marked as broken", m_addon->ID().c_str()); CAddonMgr::GetInstance().DisableAddon(m_addon->ID()); CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(m_addon, 24094)), true, false); } // and we're done! MarkFinished(); return true; }