diff --git a/dnf-4/libdnf/conf/Config-private.hpp b/dnf-4/libdnf/conf/Config-private.hpp deleted file mode 100644 index 6411f54381..0000000000 --- a/dnf-4/libdnf/conf/Config-private.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_CONFIG_PRIVATE_HPP -#define _LIBDNF_CONFIG_PRIVATE_HPP - -#include "Option.hpp" - -namespace libdnf { - -template -static void optionTListAppend(T & option, Option::Priority priority, const std::string & value) -{ - if (value.empty()) { - option.set(priority, value); - return; - } - auto addPriority = priority < option.getPriority() ? option.getPriority() : priority; - auto val = option.fromString(value); - bool first = true; - for (auto & item : val) { - if (item.empty()) { - if (first) { - option.set(priority, item); - } - } else { - auto origValue = option.getValue(); - origValue.push_back(item); - option.set(addPriority, origValue); - } - first = false; - } -} - -} - -#endif diff --git a/dnf-4/libdnf/conf/Config.hpp b/dnf-4/libdnf/conf/Config.hpp deleted file mode 100644 index 8bb07c8894..0000000000 --- a/dnf-4/libdnf/conf/Config.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_CONFIG_HPP -#define _LIBDNF_CONFIG_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "OptionBinds.hpp" - -namespace libdnf { - -class Config { -public: - OptionBinds & optBinds() { return binds; } - -private: - OptionBinds binds; -}; - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/ConfigMain.cpp b/dnf-4/libdnf/conf/ConfigMain.cpp deleted file mode 100644 index 6c735dbd2b..0000000000 --- a/dnf-4/libdnf/conf/ConfigMain.cpp +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "ConfigMain.hpp" -#include "Const.hpp" -#include "Config-private.hpp" -#include "libdnf/utils/os-release.hpp" -#include "utils.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -extern char **environ; - -namespace libdnf { - -/** -* @brief Converts a friendly bandwidth option to bytes -* -* Function converts a friendly bandwidth option to bytes. The input -* should be a string containing a (possibly floating point) -* number followed by an optional single character unit. Valid -* units are 'k', 'M', 'G'. Case is ignored. The convention that -* 1k = 1024 bytes is used. -* -* @param str Bandwidth as user friendly string -* @return int Number of bytes -*/ -static int strToBytes(const std::string & str) -{ - if (str.empty()) - throw Option::InvalidValue(_("no value specified")); - - std::size_t idx; - auto res = std::stod(str, &idx); - if (res < 0) - throw Option::InvalidValue(tfm::format(_("seconds value '%s' must not be negative"), str)); - - if (idx < str.length()) { - if (idx < str.length() - 1) - throw Option::InvalidValue(tfm::format(_("could not convert '%s' to bytes"), str)); - switch (str.back()) { - case 'k': case 'K': - res *= 1024; - break; - case 'm': case 'M': - res *= 1024 * 1024; - break; - case 'g': case 'G': - res *= 1024 * 1024 * 1024; - break; - default: - throw Option::InvalidValue(tfm::format(_("unknown unit '%s'"), str.back())); - } - } - - return res; -} - -static void addFromFile(std::ostream & out, const std::string & filePath) -{ - std::ifstream ifs(filePath); - if (!ifs) - throw std::runtime_error("addFromFile(): Can't open file"); - ifs.exceptions(std::ifstream::badbit); - - std::string line; - while (!ifs.eof()) { - std::getline(ifs, line); - auto start = line.find_first_not_of(" \t\r"); - if (start == std::string::npos) - continue; - if (line[start] == '#') - continue; - auto end = line.find_last_not_of(" \t\r"); - - out.write(line.c_str()+start, end - start + 1); - out.put(' '); - } -} - -static void addFromFiles(std::ostream & out, const std::string & globPath) -{ - glob_t globBuf; - glob(globPath.c_str(), GLOB_MARK | GLOB_NOSORT, NULL, &globBuf); - for (size_t i = 0; i < globBuf.gl_pathc; ++i) { - auto path = globBuf.gl_pathv[i]; - if (path[strlen(path)-1] != '/') - addFromFile(out, path); - } - globfree(&globBuf); -} - -/** -* @brief Replaces globs (like /etc/foo.d/\\*.foo) by content of matching files. -* -* Ignores comment lines (start with '#') and blank lines in files. -* Result: -* Words delimited by spaces. Characters ',' and '\n' are replaced by spaces. -* Extra spaces are removed. -* @param strWithGlobs Input string with globs -* @return Words delimited by space -*/ -static std::string resolveGlobs(const std::string & strWithGlobs) -{ - std::ostringstream res; - std::string::size_type start{0}; - while (start < strWithGlobs.length()) { - auto end = strWithGlobs.find_first_of(" ,\n", start); - if (strWithGlobs.compare(start, 5, "glob:") == 0) { - start += 5; - if (start >= strWithGlobs.length()) - break; - if (end == std::string::npos) { - addFromFiles(res, strWithGlobs.substr(start)); - break; - } - if (end - start != 0) - addFromFiles(res, strWithGlobs.substr(start, end - start)); - } else { - if (end == std::string::npos) { - res << strWithGlobs.substr(start); - break; - } - if (end - start != 0) - res << strWithGlobs.substr(start, end - start) << " "; - } - start = end + 1; - } - return res.str(); -} - -class ConfigMain::Impl { - friend class ConfigMain; - - Impl(Config & owner); - - Config & owner; - - OptionNumber debuglevel{2, 0, 10}; - OptionNumber errorlevel{3, 0, 10}; - OptionPath installroot{"/", false, true}; - OptionPath config_file_path{CONF_FILENAME}; - OptionBool plugins{true}; - OptionStringList pluginpath{std::vector{}}; - OptionStringList pluginconfpath{std::vector{}}; - OptionPath persistdir{PERSISTDIR}; - OptionBool transformdb{true}; - OptionNumber recent{7, 0}; - OptionBool reset_nice{true}; - OptionPath system_cachedir{SYSTEM_CACHEDIR}; - OptionBool cacheonly{false}; - OptionBool keepcache{false}; - OptionString logdir{"/var/log"}; - OptionNumber log_size{1024 * 1024, strToBytes}; - OptionNumber log_rotate{4, 0}; - OptionStringList varsdir{VARS_DIRS}; - OptionStringList reposdir{{"/etc/yum.repos.d", "/etc/yum/repos.d", "/etc/distro.repos.d"}}; - OptionBool debug_solver{false}; - OptionStringList installonlypkgs{INSTALLONLYPKGS}; - OptionStringList group_package_types{GROUP_PACKAGE_TYPES}; - - OptionNumber installonly_limit{3, 0, - [](const std::string & value)->std::uint32_t{ - if (value == "") - return 0; - try { - return std::stoul(value); - } - catch (...) { - return 0; - } - } - }; - - OptionStringList tsflags{std::vector{}}; - OptionBool assumeyes{false}; - OptionBool assumeno{false}; - OptionBool check_config_file_age{true}; - OptionBool defaultyes{false}; - OptionBool diskspacecheck{true}; - OptionBool localpkg_gpgcheck{false}; - OptionBool gpgkey_dns_verification{false}; - OptionBool obsoletes{true}; - OptionBool showdupesfromrepos{false}; - OptionBool exit_on_lock{false}; - OptionSeconds metadata_timer_sync{60 * 60 * 3}; // 3 hours - OptionStringList disable_excludes{std::vector{}}; - OptionEnum multilib_policy{"best", {"best", "all"}}; // :api - OptionBool best{false}; // :api - OptionBool install_weak_deps{true}; - OptionString bugtracker_url{BUGTRACKER}; - OptionBool zchunk{true}; - - OptionEnum color{"auto", {"auto", "never", "always"}, - [](const std::string & value){ - const std::array always{{"on", "yes", "1", "true"}}; - const std::array never{{"off", "no", "0", "false"}}; - const std::array aut{{"tty", "if-tty"}}; - std::string tmp; - if (std::find(always.begin(), always.end(), value) != always.end()) - tmp = "always"; - else if (std::find(never.begin(), never.end(), value) != never.end()) - tmp = "never"; - else if (std::find(aut.begin(), aut.end(), value) != aut.end()) - tmp = "auto"; - else - tmp = value; - return tmp; - } - }; - - OptionString color_list_installed_older{"yellow"}; - OptionString color_list_installed_newer{"bold,yellow"}; - OptionString color_list_installed_reinstall{"dim,cyan"}; - OptionString color_list_installed_extra{"bold,red"}; - OptionString color_list_available_upgrade{"bold,blue"}; - OptionString color_list_available_downgrade{"dim,magenta"}; - OptionString color_list_available_reinstall{"bold,underline,green"}; - OptionString color_list_available_install{"bold,cyan"}; - OptionString color_update_installed{"dim,red"}; - OptionString color_update_local{"dim,green"}; - OptionString color_update_remote{"bold,green"}; - OptionString color_search_match{"bold,magenta"}; - OptionBool history_record{true}; - OptionStringList history_record_packages{std::vector{"dnf", "rpm"}}; - OptionString rpmverbosity{"info"}; - OptionBool strict{true}; // :api - OptionBool skip_broken{false}; // :yum-compatibility - OptionBool autocheck_running_kernel{true}; // :yum-compatibility - OptionBool clean_requirements_on_remove{true}; - - OptionEnum history_list_view{"commands", {"single-user-commands", "users", "commands"}, - [](const std::string & value){ - if (value == "cmds" || value == "default") - return std::string("commands"); - else - return value; - } - }; - - OptionBool upgrade_group_objects_upgrade{true}; // :api - OptionPath destdir{nullptr}; - OptionString comment{nullptr}; - OptionBool downloadonly{false}; // runtime only option - OptionBool ignorearch{false}; - OptionString module_platform_id{nullptr}; - - OptionString user_agent{getUserAgent()}; - OptionBool countme{false}; - - // Repo main config - - OptionNumber retries{10}; - OptionString cachedir{nullptr}; - OptionBool fastestmirror{false}; - OptionStringList excludepkgs{std::vector{}}; - OptionStringList includepkgs{std::vector{}}; - OptionString proxy{""}; - OptionString proxy_username{nullptr}; - OptionString proxy_password{nullptr}; - - OptionEnum proxy_auth_method{"any", {"any", "none", "basic", "digest", - "negotiate", "ntlm", "digest_ie", "ntlm_wb"}, - [](const std::string & value){ - auto tmp = value; - std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); - return tmp; - } - }; - - OptionStringList protected_packages{resolveGlobs("dnf glob:/etc/yum/protected.d/*.conf " \ - "glob:/etc/dnf/protected.d/*.conf")}; - OptionString username{""}; - OptionString password{""}; - OptionBool gpgcheck{false}; - OptionBool repo_gpgcheck{false}; - OptionBool enabled{true}; - OptionBool enablegroups{true}; - OptionNumber bandwidth{0, strToBytes}; - OptionNumber minrate{1000, strToBytes}; - - OptionEnum ip_resolve{"whatever", {"ipv4", "ipv6", "whatever"}, - [](const std::string & value){ - auto tmp = value; - if (value == "4") tmp = "ipv4"; - else if (value == "6") tmp = "ipv6"; - else std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); - return tmp; - } - }; - - OptionNumber throttle{0, 0, - [](const std::string & value)->float{ - if (!value.empty() && value.back()=='%') { - std::size_t idx; - auto res = std::stod(value, &idx); - if (res < 0 || res > 100) - throw Option::InvalidValue(tfm::format(_("percentage '%s' is out of range"), value)); - return res/100; - } - return strToBytes(value); - } - }; - - OptionSeconds timeout{30}; - OptionNumber max_parallel_downloads{3, 1}; - OptionSeconds metadata_expire{60 * 60 * 48}; - OptionString sslcacert{""}; - OptionBool sslverify{true}; - OptionString sslclientcert{""}; - OptionString sslclientkey{""}; - OptionBool deltarpm{true}; - OptionNumber deltarpm_percentage{75}; - OptionBool skip_if_unavailable{false}; -}; - -ConfigMain::Impl::Impl(Config & owner) -: owner(owner) -{ - owner.optBinds().add("debuglevel", debuglevel); - owner.optBinds().add("errorlevel", errorlevel); - owner.optBinds().add("installroot", installroot); - owner.optBinds().add("config_file_path", config_file_path); - owner.optBinds().add("plugins", plugins); - owner.optBinds().add("pluginpath", pluginpath); - owner.optBinds().add("pluginconfpath", pluginconfpath); - owner.optBinds().add("persistdir", persistdir); - owner.optBinds().add("transformdb", transformdb); - owner.optBinds().add("recent", recent); - owner.optBinds().add("reset_nice", reset_nice); - owner.optBinds().add("system_cachedir", system_cachedir); - owner.optBinds().add("cacheonly", cacheonly); - owner.optBinds().add("keepcache", keepcache); - owner.optBinds().add("logdir", logdir); - owner.optBinds().add("log_size", log_size); - owner.optBinds().add("log_rotate", log_rotate); - owner.optBinds().add("varsdir", varsdir); - owner.optBinds().add("reposdir", reposdir); - owner.optBinds().add("debug_solver", debug_solver); - - owner.optBinds().add("installonlypkgs", installonlypkgs, - [&](Option::Priority priority, const std::string & value){ - optionTListAppend(installonlypkgs, priority, value); - }, nullptr, true - ); - - owner.optBinds().add("group_package_types", group_package_types); - owner.optBinds().add("installonly_limit", installonly_limit); - - owner.optBinds().add("tsflags", tsflags, - [&](Option::Priority priority, const std::string & value){ - optionTListAppend(tsflags, priority, value); - }, nullptr, true - ); - - owner.optBinds().add("assumeyes", assumeyes); - owner.optBinds().add("assumeno", assumeno); - owner.optBinds().add("check_config_file_age", check_config_file_age); - owner.optBinds().add("defaultyes", defaultyes); - owner.optBinds().add("diskspacecheck", diskspacecheck); - owner.optBinds().add("localpkg_gpgcheck", localpkg_gpgcheck); - owner.optBinds().add("gpgkey_dns_verification", gpgkey_dns_verification); - owner.optBinds().add("obsoletes", obsoletes); - owner.optBinds().add("showdupesfromrepos", showdupesfromrepos); - owner.optBinds().add("exit_on_lock", exit_on_lock); - owner.optBinds().add("metadata_timer_sync", metadata_timer_sync); - owner.optBinds().add("disable_excludes", disable_excludes); - owner.optBinds().add("multilib_policy", multilib_policy); - owner.optBinds().add("best", best); - owner.optBinds().add("install_weak_deps", install_weak_deps); - owner.optBinds().add("bugtracker_url", bugtracker_url); - owner.optBinds().add("zchunk", zchunk); - owner.optBinds().add("color", color); - owner.optBinds().add("color_list_installed_older", color_list_installed_older); - owner.optBinds().add("color_list_installed_newer", color_list_installed_newer); - owner.optBinds().add("color_list_installed_reinstall", color_list_installed_reinstall); - owner.optBinds().add("color_list_installed_extra", color_list_installed_extra); - owner.optBinds().add("color_list_available_upgrade", color_list_available_upgrade); - owner.optBinds().add("color_list_available_downgrade", color_list_available_downgrade); - owner.optBinds().add("color_list_available_reinstall", color_list_available_reinstall); - owner.optBinds().add("color_list_available_install", color_list_available_install); - owner.optBinds().add("color_update_installed", color_update_installed); - owner.optBinds().add("color_update_local", color_update_local); - owner.optBinds().add("color_update_remote", color_update_remote); - owner.optBinds().add("color_search_match", color_search_match); - owner.optBinds().add("history_record", history_record); - owner.optBinds().add("history_record_packages", history_record_packages); - owner.optBinds().add("rpmverbosity", rpmverbosity); - owner.optBinds().add("strict", strict); - owner.optBinds().add("skip_broken", skip_broken); - owner.optBinds().add("autocheck_running_kernel", autocheck_running_kernel); - owner.optBinds().add("clean_requirements_on_remove", clean_requirements_on_remove); - owner.optBinds().add("history_list_view", history_list_view); - owner.optBinds().add("upgrade_group_objects_upgrade", upgrade_group_objects_upgrade); - owner.optBinds().add("destdir", destdir); - owner.optBinds().add("comment", comment); - owner.optBinds().add("ignorearch", ignorearch); - owner.optBinds().add("module_platform_id", module_platform_id); - owner.optBinds().add("user_agent", user_agent); - owner.optBinds().add("countme", countme); - - // Repo main config - - owner.optBinds().add("retries", retries); - owner.optBinds().add("cachedir", cachedir); - owner.optBinds().add("fastestmirror", fastestmirror); - - owner.optBinds().add("excludepkgs", excludepkgs, - [&](Option::Priority priority, const std::string & value){ - optionTListAppend(excludepkgs, priority, value); - }, nullptr, true - ); - owner.optBinds().add("exclude", excludepkgs, //compatibility with yum - [&](Option::Priority priority, const std::string & value){ - optionTListAppend(excludepkgs, priority, value); - }, nullptr, true - ); - - owner.optBinds().add("includepkgs", includepkgs, - [&](Option::Priority priority, const std::string & value){ - optionTListAppend(includepkgs, priority, value); - }, nullptr, true - ); - - owner.optBinds().add("proxy", proxy); - owner.optBinds().add("proxy_username", proxy_username); - owner.optBinds().add("proxy_password", proxy_password); - owner.optBinds().add("proxy_auth_method", proxy_auth_method); - owner.optBinds().add("protected_packages", protected_packages, - [&](Option::Priority priority, const std::string & value){ - if (priority >= protected_packages.getPriority()) - protected_packages.set(priority, resolveGlobs(value)); - }, nullptr, false - ); - - owner.optBinds().add("username", username); - owner.optBinds().add("password", password); - owner.optBinds().add("gpgcheck", gpgcheck); - owner.optBinds().add("repo_gpgcheck", repo_gpgcheck); - owner.optBinds().add("enabled", enabled); - owner.optBinds().add("enablegroups", enablegroups); - owner.optBinds().add("bandwidth", bandwidth); - owner.optBinds().add("minrate", minrate); - owner.optBinds().add("ip_resolve", ip_resolve); - owner.optBinds().add("throttle", throttle); - owner.optBinds().add("timeout", timeout); - owner.optBinds().add("max_parallel_downloads", max_parallel_downloads); - owner.optBinds().add("metadata_expire", metadata_expire); - owner.optBinds().add("sslcacert", sslcacert); - owner.optBinds().add("sslverify", sslverify); - owner.optBinds().add("sslclientcert", sslclientcert); - owner.optBinds().add("sslclientkey", sslclientkey); - owner.optBinds().add("deltarpm", deltarpm); - owner.optBinds().add("deltarpm_percentage", deltarpm_percentage); - owner.optBinds().add("skip_if_unavailable", skip_if_unavailable); -} - -ConfigMain::ConfigMain() { pImpl = std::unique_ptr(new Impl(*this)); } -ConfigMain::~ConfigMain() = default; - -OptionNumber & ConfigMain::debuglevel() { return pImpl->debuglevel; } -OptionNumber & ConfigMain::errorlevel() { return pImpl->errorlevel; } -OptionString & ConfigMain::installroot() { return pImpl->installroot; } -OptionString & ConfigMain::config_file_path() { return pImpl->config_file_path; } -OptionBool & ConfigMain::plugins() { return pImpl->plugins; } -OptionStringList & ConfigMain::pluginpath() { return pImpl->pluginpath; } -OptionStringList & ConfigMain::pluginconfpath() { return pImpl->pluginconfpath; } -OptionString & ConfigMain::persistdir() { return pImpl->persistdir; } -OptionBool & ConfigMain::transformdb() { return pImpl->transformdb; } -OptionNumber & ConfigMain::recent() { return pImpl->recent; } -OptionBool & ConfigMain::reset_nice() { return pImpl->reset_nice; } -OptionString & ConfigMain::system_cachedir() { return pImpl->system_cachedir; } -OptionBool & ConfigMain::cacheonly() { return pImpl->cacheonly; } -OptionBool & ConfigMain::keepcache() { return pImpl->keepcache; } -OptionString & ConfigMain::logdir() { return pImpl->logdir; } -OptionNumber & ConfigMain::log_size() { return pImpl->log_size; } -OptionNumber & ConfigMain::log_rotate() { return pImpl->log_rotate; } -OptionStringList & ConfigMain::varsdir() { return pImpl->varsdir; } -OptionStringList & ConfigMain::reposdir() { return pImpl->reposdir; } -OptionBool & ConfigMain::debug_solver() { return pImpl->debug_solver; } -OptionStringList & ConfigMain::installonlypkgs() { return pImpl->installonlypkgs; } -OptionStringList & ConfigMain::group_package_types() { return pImpl->group_package_types; } -OptionNumber & ConfigMain::installonly_limit() { return pImpl->installonly_limit; } -OptionStringList & ConfigMain::tsflags() { return pImpl->tsflags; } -OptionBool & ConfigMain::assumeyes() { return pImpl->assumeyes; } -OptionBool & ConfigMain::assumeno() { return pImpl->assumeno; } -OptionBool & ConfigMain::check_config_file_age() { return pImpl->check_config_file_age; } -OptionBool & ConfigMain::defaultyes() { return pImpl->defaultyes; } -OptionBool & ConfigMain::diskspacecheck() { return pImpl->diskspacecheck; } -OptionBool & ConfigMain::localpkg_gpgcheck() { return pImpl->localpkg_gpgcheck; } -OptionBool & ConfigMain::gpgkey_dns_verification() { return pImpl->gpgkey_dns_verification; } -OptionBool & ConfigMain::obsoletes() { return pImpl->obsoletes; } -OptionBool & ConfigMain::showdupesfromrepos() { return pImpl->showdupesfromrepos; } -OptionBool & ConfigMain::exit_on_lock() { return pImpl->exit_on_lock; } -OptionSeconds & ConfigMain::metadata_timer_sync() { return pImpl->metadata_timer_sync; } -OptionStringList & ConfigMain::disable_excludes() { return pImpl->disable_excludes; } -OptionEnum & ConfigMain::multilib_policy() { return pImpl->multilib_policy; } -OptionBool & ConfigMain::best() { return pImpl->best; } -OptionBool & ConfigMain::install_weak_deps() { return pImpl->install_weak_deps; } -OptionString & ConfigMain::bugtracker_url() { return pImpl->bugtracker_url; } -OptionBool & ConfigMain::zchunk() { return pImpl->zchunk; } -OptionEnum & ConfigMain::color() { return pImpl->color; } -OptionString & ConfigMain::color_list_installed_older() { return pImpl->color_list_installed_older; } -OptionString & ConfigMain::color_list_installed_newer() { return pImpl->color_list_installed_newer; } -OptionString & ConfigMain::color_list_installed_reinstall() { return pImpl->color_list_installed_reinstall; } -OptionString & ConfigMain::color_list_installed_extra() { return pImpl->color_list_installed_extra; } -OptionString & ConfigMain::color_list_available_upgrade() { return pImpl->color_list_available_upgrade; } -OptionString & ConfigMain::color_list_available_downgrade() { return pImpl->color_list_available_downgrade; } -OptionString & ConfigMain::color_list_available_reinstall() { return pImpl->color_list_available_reinstall; } -OptionString & ConfigMain::color_list_available_install() { return pImpl->color_list_available_install; } -OptionString & ConfigMain::color_update_installed() { return pImpl->color_update_installed; } -OptionString & ConfigMain::color_update_local() { return pImpl->color_update_local; } -OptionString & ConfigMain::color_update_remote() { return pImpl->color_update_remote; } -OptionString & ConfigMain::color_search_match() { return pImpl->color_search_match; } -OptionBool & ConfigMain::history_record() { return pImpl->history_record; } -OptionStringList & ConfigMain::history_record_packages() { return pImpl->history_record_packages; } -OptionString & ConfigMain::rpmverbosity() { return pImpl->rpmverbosity; } -OptionBool & ConfigMain::strict() { return pImpl->strict; } -OptionBool & ConfigMain::skip_broken() { return pImpl->skip_broken; } -OptionBool & ConfigMain::autocheck_running_kernel() { return pImpl->autocheck_running_kernel; } -OptionBool & ConfigMain::clean_requirements_on_remove() { return pImpl->clean_requirements_on_remove; } -OptionEnum & ConfigMain::history_list_view() { return pImpl->history_list_view; } -OptionBool & ConfigMain::upgrade_group_objects_upgrade() { return pImpl->upgrade_group_objects_upgrade; } -OptionPath & ConfigMain::destdir() { return pImpl->destdir; } -OptionString & ConfigMain::comment() { return pImpl->comment; } -OptionBool & ConfigMain::downloadonly() { return pImpl->downloadonly; } -OptionBool & ConfigMain::ignorearch() { return pImpl->ignorearch; } - -OptionString & ConfigMain::module_platform_id() { return pImpl->module_platform_id; } -OptionString & ConfigMain::user_agent() { return pImpl->user_agent; } -OptionBool & ConfigMain::countme() { return pImpl->countme; } - -// Repo main config -OptionNumber & ConfigMain::retries() { return pImpl->retries; } -OptionString & ConfigMain::cachedir() { return pImpl->cachedir; } -OptionBool & ConfigMain::fastestmirror() { return pImpl->fastestmirror; } -OptionStringList & ConfigMain::excludepkgs() { return pImpl->excludepkgs; } -OptionStringList & ConfigMain::includepkgs() { return pImpl->includepkgs; } -OptionString & ConfigMain::proxy() { return pImpl->proxy; } -OptionString & ConfigMain::proxy_username() { return pImpl->proxy_username; } -OptionString & ConfigMain::proxy_password() { return pImpl->proxy_password; } -OptionEnum & ConfigMain::proxy_auth_method() { return pImpl->proxy_auth_method; } -OptionStringList & ConfigMain::protected_packages() { return pImpl->protected_packages; } -OptionString & ConfigMain::username() { return pImpl->username; } -OptionString & ConfigMain::password() { return pImpl->password; } -OptionBool & ConfigMain::gpgcheck() { return pImpl->gpgcheck; } -OptionBool & ConfigMain::repo_gpgcheck() { return pImpl->repo_gpgcheck; } -OptionBool & ConfigMain::enabled() { return pImpl->enabled; } -OptionBool & ConfigMain::enablegroups() { return pImpl->enablegroups; } -OptionNumber & ConfigMain::bandwidth() { return pImpl->bandwidth; } -OptionNumber & ConfigMain::minrate() { return pImpl->minrate; } -OptionEnum & ConfigMain::ip_resolve() { return pImpl->ip_resolve; } -OptionNumber & ConfigMain::throttle() { return pImpl->throttle; } -OptionSeconds & ConfigMain::timeout() { return pImpl->timeout; } -OptionNumber & ConfigMain::max_parallel_downloads() { return pImpl->max_parallel_downloads; } -OptionSeconds & ConfigMain::metadata_expire() { return pImpl->metadata_expire; } -OptionString & ConfigMain::sslcacert() { return pImpl->sslcacert; } -OptionBool & ConfigMain::sslverify() { return pImpl->sslverify; } -OptionString & ConfigMain::sslclientcert() { return pImpl->sslclientcert; } -OptionString & ConfigMain::sslclientkey() { return pImpl->sslclientkey; } -OptionBool & ConfigMain::deltarpm() { return pImpl->deltarpm; } -OptionNumber & ConfigMain::deltarpm_percentage() { return pImpl->deltarpm_percentage; } -OptionBool & ConfigMain::skip_if_unavailable() { return pImpl->skip_if_unavailable; } - -static void DIRClose(DIR *d) { closedir(d); } - -void ConfigMain::addVarsFromDir(std::map & varsMap, const std::string & dirPath) -{ - if (DIR * dir = opendir(dirPath.c_str())) { - std::unique_ptr dirGuard(dir, &DIRClose); - while (auto ent = readdir(dir)) { - auto dname = ent->d_name; - if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) - continue; - - auto fullPath = dirPath; - if (fullPath.back() != '/') - fullPath += "/"; - fullPath += dname; - std::ifstream inStream(fullPath); - if (inStream.fail()) { - // log.warning() - continue; - } - std::string line; - std::getline(inStream, line); - if (inStream.fail()) { - // log.warning() - continue; - } - varsMap[dname] = std::move(line); - } - } -} - -void ConfigMain::addVarsFromEnv(std::map & varsMap) -{ - if (!environ) - return; - - for (const char * const * varPtr = environ; *varPtr; ++varPtr) { - auto var = *varPtr; - if (auto eqlPtr = strchr(var, '=')) { - auto eqlIdx = eqlPtr - var; - // DNF[0-9] - if (eqlIdx == 4 && strncmp("DNF", var, 3) == 0 && isdigit(var[3])) - varsMap[std::string(var, eqlIdx)] = eqlPtr + 1; - // DNF_VAR_[A-Za-z0-9_]+ , DNF_VAR_ prefix is cut off - else if (eqlIdx > 8 && strncmp("DNF_VAR_", var, 8) == 0 && - static_cast(strspn(var + 8, ASCII_LETTERS DIGITS "_")) == eqlIdx - 8) - varsMap[std::string(var + 8, eqlIdx - 8)] = eqlPtr + 1; - } - } -} - -} diff --git a/dnf-4/libdnf/conf/ConfigParser.cpp b/dnf-4/libdnf/conf/ConfigParser.cpp deleted file mode 100644 index 4a461f02ab..0000000000 --- a/dnf-4/libdnf/conf/ConfigParser.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "ConfigParser.hpp" -#include "../utils/iniparser/iniparser.hpp" - -#include -#include - -namespace libdnf { - -void ConfigParser::substitute(std::string & text, - const std::map & substitutions) -{ - auto start = text.find_first_of("$"); - while (start != text.npos) - { - auto variable = start + 1; - if (variable >= text.length()) - break; - bool bracket; - if (text[variable] == '{') { - bracket = true; - if (++variable >= text.length()) - break; - } else - bracket = false; - auto it = std::find_if_not(text.begin()+variable, text.end(), - [](char c){return std::isalnum(c) || c=='_';}); - if (bracket && it == text.end()) - break; - auto pastVariable = std::distance(text.begin(), it); - if (bracket && *it != '}') { - start = text.find_first_of("$", pastVariable); - continue; - } - auto subst = substitutions.find(text.substr(variable, pastVariable - variable)); - if (subst != substitutions.end()) { - if (bracket) - ++pastVariable; - text.replace(start, pastVariable - start, subst->second); - start = text.find_first_of("$", start + subst->second.length()); - } else { - start = text.find_first_of("$", pastVariable); - } - } -} - -static void read(ConfigParser & cfgParser, IniParser & parser) -{ - IniParser::ItemType readedType; - while ((readedType = parser.next()) != IniParser::ItemType::END_OF_INPUT) { - auto section = parser.getSection(); - if (readedType == IniParser::ItemType::SECTION) { - cfgParser.addSection(std::move(section), std::move(parser.getRawItem())); - } - else if (readedType == IniParser::ItemType::KEY_VAL) { - cfgParser.setValue(section, std::move(parser.getKey()), std::move(parser.getValue()), std::move(parser.getRawItem())); - } - else if (readedType == IniParser::ItemType::COMMENT_LINE || readedType == IniParser::ItemType::EMPTY_LINE) { - if (section.empty()) - cfgParser.getHeader() += parser.getRawItem(); - else - cfgParser.addCommentLine(section, std::move(parser.getRawItem())); - } - } -} - -void ConfigParser::read(const std::string & filePath) -{ - try { - IniParser parser(filePath); - ::libdnf::read(*this, parser); - } catch (const IniParser::CantOpenFile & e) { - throw CantOpenFile(e.what()); - } catch (const IniParser::Exception & e) { - throw ParsingError(e.what() + std::string(" at line ") + std::to_string(e.getLineNumber())); - } -} - -void ConfigParser::read(std::unique_ptr && inputStream) -{ - try { - IniParser parser(std::move(inputStream)); - ::libdnf::read(*this, parser); - } catch (const IniParser::CantOpenFile & e) { - throw CantOpenFile(e.what()); - } catch (const IniParser::Exception & e) { - throw ParsingError(e.what() + std::string(" at line ") + std::to_string(e.getLineNumber())); - } -} - -static std::string createRawItem(const std::string & value, const std::string & oldRawItem) -{ - auto eqlPos = oldRawItem.find('='); - if (eqlPos == oldRawItem.npos) - return ""; - auto valuepos = oldRawItem.find_first_not_of(" \t", eqlPos + 1); - auto keyAndDelimLength = valuepos != oldRawItem.npos ? valuepos : oldRawItem.length(); - return oldRawItem.substr(0, keyAndDelimLength) + value + '\n'; -} - -void ConfigParser::setValue(const std::string & section, const std::string & key, const std::string & value) -{ - auto rawIter = rawItems.find(section + ']' + key); - auto raw = createRawItem(value, rawIter != rawItems.end() ? rawIter->second : ""); - setValue(section, key, value, raw); -} - -void ConfigParser::setValue(const std::string & section, std::string && key, std::string && value) -{ - auto rawIter = rawItems.find(section + ']' + key); - auto raw = createRawItem(value, rawIter != rawItems.end() ? rawIter->second : ""); - setValue(section, std::move(key), std::move(value), std::move(raw)); -} - -const std::string & -ConfigParser::getValue(const std::string & section, const std::string & key) const -{ - auto sect = data.find(section); - if (sect == data.end()) - throw MissingSection("OptionReader::getValue(): Missing section " + section); - auto keyVal = sect->second.find(key); - if (keyVal == sect->second.end()) - throw MissingOption("OptionReader::getValue(): Missing option " + key + - " in section " + section); - return keyVal->second; -} - -std::string -ConfigParser::getSubstitutedValue(const std::string & section, const std::string & key) const -{ - auto ret = getValue(section, key); - substitute(ret, substitutions); - return ret; -} - -static void writeKeyVals(std::ostream & out, const std::string & section, const ConfigParser::Container::mapped_type & keyValMap, const std::map & rawItems) -{ - for (const auto & keyVal : keyValMap) { - auto first = keyVal.first[0]; - if (first == '#' || first == ';') - out << keyVal.second; - else { - auto rawItem = rawItems.find(section + ']' + keyVal.first); - if (rawItem != rawItems.end()) - out << rawItem->second; - else { - out << keyVal.first << "="; - for (const auto chr : keyVal.second) { - out << chr; - if (chr == '\n') - out << " "; - } - out << "\n"; - } - } - } -} - -static void writeSection(std::ostream & out, const std::string & section, const ConfigParser::Container::mapped_type & keyValMap, const std::map & rawItems) -{ - auto rawItem = rawItems.find(section); - if (rawItem != rawItems.end()) - out << rawItem->second; - else - out << "[" << section << "]" << "\n"; - writeKeyVals(out, section, keyValMap, rawItems); -} - -void ConfigParser::write(const std::string & filePath, bool append) const -{ - std::ofstream ofs; - ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); - ofs.open(filePath, append ? std::ofstream::app : std::ofstream::trunc); - write(ofs); -} - -void ConfigParser::write(const std::string & filePath, bool append, const std::string & section) const -{ - auto sit = data.find(section); - if (sit == data.end()) - throw MissingSection("ConfigParser::write(): Missing section " + section); - std::ofstream ofs; - ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); - ofs.open(filePath, append ? std::ofstream::app : std::ofstream::trunc); - writeSection(ofs, sit->first, sit->second, rawItems); -} - -void ConfigParser::write(std::ostream & outputStream) const -{ - outputStream << header; - for (const auto & section : data) { - writeSection(outputStream, section.first, section.second, rawItems); - } -} - -void ConfigParser::write(std::ostream & outputStream, const std::string & section) const -{ - auto sit = data.find(section); - if (sit == data.end()) - throw MissingSection("ConfigParser::write(): Missing section " + section); - writeSection(outputStream, sit->first, sit->second, rawItems); -} - -} diff --git a/dnf-4/libdnf/conf/ConfigParser.hpp b/dnf-4/libdnf/conf/ConfigParser.hpp deleted file mode 100644 index f43ea3fac0..0000000000 --- a/dnf-4/libdnf/conf/ConfigParser.hpp +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBDNF_CONFIG_PARSER_HPP -#define LIBDNF_CONFIG_PARSER_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "../utils/PreserveOrderMap.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace libdnf { - -/** -* @class ConfigParser -* -* @brief Class for parsing dnf/yum .ini configuration files. -* -* ConfigParser is used for parsing files. The class adds support for substitutions. -* User can get both substituded and original parsed values. -* The parsed items are stored into the PreserveOrderMap. -* IniParser preserve order of items. Comments and empty lines are kept. -*/ -struct ConfigParser { -public: - typedef PreserveOrderMap> Container; - - struct Exception : public std::runtime_error { - Exception(const std::string & what) : runtime_error(what) {} - }; - struct CantOpenFile : public Exception { - CantOpenFile(const std::string & what) : Exception(what) {} - }; - struct ParsingError : public Exception { - ParsingError(const std::string & what) : Exception(what) {} - }; - struct MissingSection : public Exception { - MissingSection(const std::string & what) : Exception(what) {} - }; - struct MissingOption : public Exception { - MissingOption(const std::string & what) : Exception(what) {} - }; - - /** - * @brief Substitute values in text according to the substitutions map - * - * @param text The text for substitution - * @param substitutions Substitution map - */ - static void substitute(std::string & text, - const std::map & substitutions); - void setSubstitutions(const std::map & substitutions); - void setSubstitutions(std::map && substitutions); - const std::map & getSubstitutions() const; - /** - * @brief Reads/parse one INI file - * - * Can be called repeately for reading/merge more INI files. - * - * @param filePath Name (with path) of file to read - */ - void read(const std::string & filePath); - /** - * @brief Reads/parse from istream - * - * Can be called repeately for reading/merge more istreams. - * - * @param inputStream Stream to read - */ - void read(std::unique_ptr && inputStream); - /** - * @brief Writes all data (all sections) to INI file - * - * @param filePath Name (with path) of file to write - * @param append If true, existent file will be appended, otherwise overwritten - */ - void write(const std::string & filePath, bool append) const; - /** - * @brief Writes one section data to INI file - * - * @param filePath Name (with path) of file to write - * @param append If true, existent file will be appended, otherwise overwritten - * @param section Section to write - */ - void write(const std::string & filePath, bool append, const std::string & section) const; - /** - * @brief Writes one section data to stream - * - * @param outputStream Stream to write - * @param section Section to write - */ - void write(std::ostream & outputStream, const std::string & section) const; - /** - * @brief Writes all data (all sections) to stream - * - * @param outputStream Stream to write - */ - void write(std::ostream & outputStream) const; - bool addSection(const std::string & section, const std::string & rawLine); - bool addSection(const std::string & section); - bool addSection(std::string && section, std::string && rawLine); - bool addSection(std::string && section); - bool hasSection(const std::string & section) const noexcept; - bool hasOption(const std::string & section, const std::string & key) const noexcept; - void setValue(const std::string & section, const std::string & key, const std::string & value, const std::string & rawItem); - void setValue(const std::string & section, const std::string & key, const std::string & value); - void setValue(const std::string & section, std::string && key, std::string && value, std::string && rawItem); - void setValue(const std::string & section, std::string && key, std::string && value); - bool removeSection(const std::string & section); - bool removeOption(const std::string & section, const std::string & key); - void addCommentLine(const std::string & section, const std::string & comment); - void addCommentLine(const std::string & section, std::string && comment); - const std::string & getValue(const std::string & section, const std::string & key) const; - std::string getSubstitutedValue(const std::string & section, const std::string & key) const; - const std::string & getHeader() const noexcept; - std::string & getHeader() noexcept; - const Container & getData() const noexcept; - Container & getData() noexcept; - -private: - std::map substitutions; - Container data; - int itemNumber{0}; - std::string header; - std::map rawItems; -}; - -inline void ConfigParser::setSubstitutions(const std::map & substitutions) -{ - this->substitutions = substitutions; -} - -inline void ConfigParser::setSubstitutions(std::map && substitutions) -{ - this->substitutions = std::move(substitutions); -} - -inline const std::map & ConfigParser::getSubstitutions() const -{ - return substitutions; -} - -inline bool ConfigParser::addSection(const std::string & section, const std::string & rawLine) -{ - if (data.find(section) != data.end()) - return false; - if (!rawLine.empty()) - rawItems[section] = rawLine; - data[section] = {}; - return true; -} - -inline bool ConfigParser::addSection(const std::string & section) -{ - return addSection(section, ""); -} - -inline bool ConfigParser::addSection(std::string && section, std::string && rawLine) -{ - if (data.find(section) != data.end()) - return false; - if (!rawLine.empty()) - rawItems[section] = std::move(rawLine); - data[std::move(section)] = {}; - return true; -} - -inline bool ConfigParser::addSection(std::string && section) -{ - return addSection(std::move(section), ""); -} - -inline bool ConfigParser::hasSection(const std::string & section) const noexcept -{ - return data.find(section) != data.end(); -} - -inline bool ConfigParser::hasOption(const std::string & section, const std::string & key) const noexcept -{ - auto sectionIter = data.find(section); - return sectionIter != data.end() && sectionIter->second.find(key) != sectionIter->second.end(); -} - -inline void ConfigParser::setValue(const std::string & section, const std::string & key, const std::string & value, const std::string & rawItem) -{ - auto sectionIter = data.find(section); - if (sectionIter == data.end()) - throw MissingSection(section); - if (rawItem.empty()) - rawItems.erase(section + ']' + key); - else - rawItems[section + ']' + key] = rawItem; - sectionIter->second[key] = value; -} - -inline void ConfigParser::setValue(const std::string & section, std::string && key, std::string && value, std::string && rawItem) -{ - auto sectionIter = data.find(section); - if (sectionIter == data.end()) - throw MissingSection(section); - if (rawItem.empty()) - rawItems.erase(section + ']' + key); - else - rawItems[section + ']' + key] = std::move(rawItem); - sectionIter->second[std::move(key)] = std::move(value); -} - -inline bool ConfigParser::removeSection(const std::string & section) -{ - auto removed = data.erase(section) > 0; - if (removed) - rawItems.erase(section); - return removed; -} - -inline bool ConfigParser::removeOption(const std::string & section, const std::string & key) -{ - auto sectionIter = data.find(section); - if (sectionIter == data.end()) - return false; - auto removed = sectionIter->second.erase(key) > 0; - if (removed) - rawItems.erase(section + ']' + key); - return removed; -} - -inline void ConfigParser::addCommentLine(const std::string & section, const std::string & comment) -{ - auto sectionIter = data.find(section); - if (sectionIter == data.end()) - throw MissingSection(section); - sectionIter->second["#"+std::to_string(++itemNumber)] = comment; -} - -inline void ConfigParser::addCommentLine(const std::string & section, std::string && comment) -{ - auto sectionIter = data.find(section); - if (sectionIter == data.end()) - throw MissingSection(section); - sectionIter->second["#"+std::to_string(++itemNumber)] = std::move(comment); -} - -inline const std::string & ConfigParser::getHeader() const noexcept -{ - return header; -} - -inline std::string & ConfigParser::getHeader() noexcept -{ - return header; -} - -inline const ConfigParser::Container & ConfigParser::getData() const noexcept -{ - return data; -} - -inline ConfigParser::Container & ConfigParser::getData() noexcept -{ - return data; -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/Const.hpp b/dnf-4/libdnf/conf/Const.hpp deleted file mode 100644 index ba21bbe661..0000000000 --- a/dnf-4/libdnf/conf/Const.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_CONFIG_CONST_HPP -#define _LIBDNF_CONFIG_CONST_HPP - -#include -#include - -namespace libdnf { - -constexpr const char * PERSISTDIR = "/var/lib/dnf"; -constexpr const char * SYSTEM_CACHEDIR = "/var/cache/dnf"; - -constexpr const char * CONF_FILENAME = "/etc/dnf/dnf.conf"; - -// More important varsdirs must be on the end of vector -const std::vector VARS_DIRS{"/etc/yum/vars", "/etc/dnf/vars"}; - -const std::vector GROUP_PACKAGE_TYPES{"mandatory", "default", "conditional"}; -const std::vector INSTALLONLYPKGS{"kernel", "kernel-PAE", - "installonlypkg(kernel)", - "installonlypkg(kernel-module)", - "installonlypkg(vm)", - "multiversion(kernel)"}; - -constexpr const char * BUGTRACKER="https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=dnf"; - -} - -#endif diff --git a/dnf-4/libdnf/conf/Option.hpp b/dnf-4/libdnf/conf/Option.hpp deleted file mode 100644 index e9a9dfc842..0000000000 --- a/dnf-4/libdnf/conf/Option.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_HPP -#define _LIBDNF_OPTION_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include -#include - -namespace libdnf { - -class Option { -public: - enum class Priority { - EMPTY = 0, - DEFAULT = 10, - MAINCONFIG = 20, - AUTOMATICCONFIG = 30, - REPOCONFIG = 40, - PLUGINDEFAULT = 50, - PLUGINCONFIG = 60, - DROPINCONFIG = 65, - COMMANDLINE = 70, - RUNTIME = 80 - }; - - struct Exception : public std::runtime_error { - Exception(const std::string & msg) : runtime_error(msg) {} - Exception(const char * msg) : runtime_error(msg) {} - }; - struct InvalidValue : Exception { - InvalidValue(const std::string & msg) : Exception(msg) {} - InvalidValue(const char * msg) : Exception(msg) {} - }; - struct ValueNotSet : Exception { - ValueNotSet(const std::string & msg) : Exception(msg) {} - ValueNotSet(const char * msg) : Exception(msg) {} - }; - - Option(Priority priority = Priority::EMPTY); - virtual Option * clone() const = 0; - virtual Priority getPriority() const; - virtual void set(Priority priority, const std::string & value) = 0; - virtual std::string getValueString() const = 0; - virtual bool empty() const noexcept; - virtual ~Option() = default; - -protected: - Priority priority; -}; - -inline Option::Option(Priority priority) -: priority(priority) {} - -inline Option::Priority Option::getPriority() const -{ - return priority; -} - -inline bool Option::empty() const noexcept -{ - return priority == Priority::EMPTY; -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionBinds.cpp b/dnf-4/libdnf/conf/OptionBinds.cpp deleted file mode 100644 index f7c67540b9..0000000000 --- a/dnf-4/libdnf/conf/OptionBinds.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionBinds.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -#include - -namespace libdnf { - -// ========== OptionBinds::Item class =============== - -OptionBinds::Item::Item(Option & option, const NewStringFunc & newString, - const GetValueStringFunc & getValueString, bool addValue) -: option(&option), newStr(newString), getValueStr(getValueString), addValue(addValue) {} - -OptionBinds::Item::Item(Option & option, NewStringFunc && newString, - GetValueStringFunc && getValueString, bool addValue) -: option(&option), newStr(std::move(newString)), getValueStr(std::move(getValueString)), addValue(addValue) {} - -OptionBinds::Item::Item(Option & option) -: option(&option) {} - -Option::Priority OptionBinds::Item::getPriority() const -{ - return option->getPriority(); -} - -void OptionBinds::Item::newString(Option::Priority priority, const std::string & value) -{ - if (newStr) - newStr(priority, value); - else - option->set(priority, value); -} - -std::string OptionBinds::Item::getValueString() const -{ - if (getValueStr) - return getValueStr(); - else - return option->getValueString(); -} - -bool OptionBinds::Item::getAddValue() const -{ - return addValue; -} - - -// =========== OptionBinds class =============== - -const char * OptionBinds::OutOfRange::what() const noexcept -{ - try { - if (tmpMsg.empty()) - tmpMsg = tfm::format(_("Configuration: OptionBinding with id \"%s\" does not exist"), - Exception::what()); - return tmpMsg.c_str(); - } catch (...) { - return Exception::what(); - } -} - -const char * OptionBinds::AlreadyExists::what() const noexcept -{ - try { - if (tmpMsg.empty()) - tmpMsg = tfm::format(_("Configuration: OptionBinding with id \"%s\" already exists"), - Exception::what()); - return tmpMsg.c_str(); - } catch (...) { - return Exception::what(); - } -} - -OptionBinds::Item & OptionBinds::at(const std::string & id) -{ - auto item = items.find(id); - if (item == items.end()) - throw OutOfRange(id); - return item->second; -} - -const OptionBinds::Item & OptionBinds::at(const std::string & id) const -{ - auto item = items.find(id); - if (item == items.end()) - throw OutOfRange(id); - return item->second; -} - -OptionBinds::Item & OptionBinds::add(const std::string & id, Option & option, - const Item::NewStringFunc & newString, const Item::GetValueStringFunc & getValueString, bool addValue) -{ - auto item = items.find(id); - if (item != items.end()) - throw AlreadyExists(id); - auto res = items.emplace(id, Item(option, newString, getValueString, addValue)); - return res.first->second; -} - -OptionBinds::Item & OptionBinds::add(const std::string & id, Option & option, - Item::NewStringFunc && newString, Item::GetValueStringFunc && getValueString, bool addValue) -{ - auto item = items.find(id); - if (item != items.end()) - throw AlreadyExists(id); - auto res = items.emplace(id, Item(option, std::move(newString), std::move(getValueString), addValue)); - return res.first->second; -} - -OptionBinds::Item & OptionBinds::add(const std::string & id, Option & option) -{ - auto item = items.find(id); - if (item != items.end()) - throw AlreadyExists(id); - auto res = items.emplace(id, Item(option)); - return res.first->second; -} - -} diff --git a/dnf-4/libdnf/conf/OptionBinds.hpp b/dnf-4/libdnf/conf/OptionBinds.hpp deleted file mode 100644 index 715c37e263..0000000000 --- a/dnf-4/libdnf/conf/OptionBinds.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_BINDS_HPP -#define _LIBDNF_OPTION_BINDS_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -#include -#include - -namespace libdnf { - -class OptionBinds { -public: - struct Exception : public std::runtime_error { - Exception(const std::string & what) : runtime_error(what) {} - protected: - mutable std::string tmpMsg; - }; - struct OutOfRange : public Exception { - OutOfRange(const std::string & id) : Exception(id) {} - const char * what() const noexcept override; - }; - struct AlreadyExists : public Exception { - AlreadyExists(const std::string & id) : Exception(id) {} - const char * what() const noexcept override; - }; - - class Item final { - public: - typedef std::function NewStringFunc; - typedef std::function GetValueStringFunc; - - Option::Priority getPriority() const; - void newString(Option::Priority priority, const std::string & value); - std::string getValueString() const; - bool getAddValue() const; - - private: - friend class OptionBinds; - - Item(Option & option, const NewStringFunc & newString, - const GetValueStringFunc & getValueString, bool addValue); - Item(Option & option, NewStringFunc && newString, - GetValueStringFunc && getValueString, bool addValue); - Item(Option & option); - Option * option; - NewStringFunc newStr; - GetValueStringFunc getValueStr; - bool addValue{false}; // hint that new value be added - }; - - typedef std::map Container; - typedef Container::iterator iterator; - typedef Container::const_iterator const_iterator; - - Item & add(const std::string & id, Option & option, const Item::NewStringFunc & newString, - const Item::GetValueStringFunc & getValueString, bool addValue); - Item & add(const std::string & id, Option & option, Item::NewStringFunc && newString, - Item::GetValueStringFunc && getValueString, bool addValue); - Item & add(const std::string & id, Option & option); - Item & at(const std::string & id); - const Item & at(const std::string & id) const; - bool empty() const noexcept { return items.empty(); } - std::size_t size() const noexcept { return items.size(); } - iterator begin() noexcept { return items.begin(); } - const_iterator begin() const noexcept { return items.begin(); } - const_iterator cbegin() const noexcept { return items.cbegin(); } - iterator end() noexcept { return items.end(); } - const_iterator end() const noexcept { return items.end(); } - const_iterator cend() const noexcept { return items.cend(); } - iterator find(const std::string & id) { return items.find(id); } - const_iterator find(const std::string & id) const { return items.find(id); } - -private: - Container items; -}; - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionBool.cpp b/dnf-4/libdnf/conf/OptionBool.cpp deleted file mode 100644 index 5516e6a2fd..0000000000 --- a/dnf-4/libdnf/conf/OptionBool.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionBool.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -namespace libdnf { - -OptionBool::OptionBool(bool defaultValue, const char * const trueVals[], const char * const falseVals[]) -: Option(Priority::DEFAULT), trueValues(trueVals), falseValues(falseVals) -, defaultValue(defaultValue), value(defaultValue) {} - -OptionBool::OptionBool(bool defaultValue) -: OptionBool(defaultValue, nullptr, nullptr) {} - -bool OptionBool::fromString(std::string value) const -{ - for (auto & ch : value) - ch = std::tolower(ch); - for (auto it = getFalseValues(); *it; ++it) { - if (value == *it) - return false; - } - for (auto it = getTrueValues(); *it; ++it) { - if (value == *it) - return true; - } - throw InvalidValue(tfm::format(_("invalid boolean value '%s'"), value)); -} - -void OptionBool::set(Priority priority, bool value) -{ - if (priority >= this->priority) { - this->value = value; - this->priority = priority; - } -} - -void OptionBool::set(Priority priority, const std::string & value) -{ - set(priority, fromString(value)); -} - -std::string OptionBool::toString(bool value) const -{ - std::ostringstream oss; - oss << value; - return oss.str(); -} - -} diff --git a/dnf-4/libdnf/conf/OptionBool.hpp b/dnf-4/libdnf/conf/OptionBool.hpp deleted file mode 100644 index c27ab0b792..0000000000 --- a/dnf-4/libdnf/conf/OptionBool.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_BOOL_HPP -#define _LIBDNF_OPTION_BOOL_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -namespace libdnf { - -constexpr const char * defTrueValues[]{"1", "yes", "true", "on", nullptr}; -constexpr const char * defFalseValues[]{"0", "no", "false", "off", nullptr}; - -class OptionBool : public Option { -public: - typedef bool ValueType; - - OptionBool(bool defaultValue, const char * const trueVals[], const char * const falseVals[]); - OptionBool(bool defaultValue); - OptionBool * clone() const override; - void test(bool) const; - bool fromString(std::string value) const; - void set(Priority priority, bool value); - void set(Priority priority, const std::string & value) override; - bool getValue() const noexcept; - bool getDefaultValue() const noexcept; - std::string toString(bool value) const; - std::string getValueString() const override; - const char * const * getTrueValues() const noexcept; - const char * const * getFalseValues() const noexcept; - -protected: - const char * const * const trueValues; - const char * const * const falseValues; - bool defaultValue; - bool value; -}; - -inline OptionBool * OptionBool::clone() const -{ - return new OptionBool(*this); -} - -inline void OptionBool::test(bool) const {} - -inline bool OptionBool::getValue() const noexcept -{ - return value; -} - -inline bool OptionBool::getDefaultValue() const noexcept -{ - return defaultValue; -} - -inline std::string OptionBool::getValueString() const -{ - return toString(value); -} - -inline const char * const * OptionBool::getTrueValues() const noexcept -{ - return trueValues ? trueValues : defTrueValues; -} - -inline const char * const * OptionBool::getFalseValues() const noexcept -{ - return falseValues ? falseValues : defFalseValues; -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionChild.hpp b/dnf-4/libdnf/conf/OptionChild.hpp deleted file mode 100644 index 5d1503cb66..0000000000 --- a/dnf-4/libdnf/conf/OptionChild.hpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_CHILD_HPP -#define _LIBDNF_OPTION_CHILD_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -namespace libdnf { - -template -class OptionChild : public Option { -public: - OptionChild(const ParentOptionType & parent); - OptionChild * clone() const override; - Priority getPriority() const override; - void set(Priority priority, const typename ParentOptionType::ValueType & value); - void set(Priority priority, const std::string & value) override; - const typename ParentOptionType::ValueType getValue() const; - const typename ParentOptionType::ValueType getDefaultValue() const; - std::string getValueString() const override; - bool empty() const noexcept override; - -private: - const ParentOptionType * parent; - typename ParentOptionType::ValueType value; -}; - -template -class OptionChild::value>::type> : public Option { -public: - OptionChild(const ParentOptionType & parent); - OptionChild * clone() const override; - Priority getPriority() const override; - void set(Priority priority, const std::string & value) override; - const std::string & getValue() const; - const std::string & getDefaultValue() const; - std::string getValueString() const override; - bool empty() const noexcept override; - -private: - const ParentOptionType * parent; - std::string value; -}; - -template -inline OptionChild::OptionChild(const ParentOptionType & parent) -: parent(&parent) {} - -template -inline OptionChild * OptionChild::clone() const -{ - return new OptionChild(*this); -} - -template -inline Option::Priority OptionChild::getPriority() const -{ - return priority != Priority::EMPTY ? priority : parent->getPriority(); -} - -template -inline void OptionChild::set(Priority priority, const typename ParentOptionType::ValueType & value) -{ - if (priority >= this->priority) { - parent->test(value); - this->priority = priority; - this->value = value; - } -} - -template -inline void OptionChild::set(Priority priority, const std::string & value) -{ - if (priority >= this->priority) - set(priority, parent->fromString(value)); -} - -template -inline const typename ParentOptionType::ValueType OptionChild::getValue() const -{ - return priority != Priority::EMPTY ? value : parent->getValue(); -} - -template -inline const typename ParentOptionType::ValueType OptionChild::getDefaultValue() const -{ - return parent->getDefaultValue(); -} - -template -inline std::string OptionChild::getValueString() const -{ - return priority != Priority::EMPTY ? parent->toString(value) : parent->getValueString(); -} - -template -inline bool OptionChild::empty() const noexcept -{ - return priority == Priority::EMPTY && parent->empty(); -} - -template -inline OptionChild::value>::type>::OptionChild(const ParentOptionType & parent) -: parent(&parent) {} - -template -inline OptionChild::value>::type> * -OptionChild::value>::type>::clone() const -{ - return new OptionChild(*this); -} - -template -inline Option::Priority OptionChild::value>::type>::getPriority() const -{ - return priority != Priority::EMPTY ? priority : parent->getPriority(); -} - -template -inline void OptionChild::value>::type>::set(Priority priority, const std::string & value) -{ - auto val = parent->fromString(value); - if (priority >= this->priority) { - parent->test(val); - this->priority = priority; - this->value = val; - } -} - -template -inline const std::string & OptionChild::value>::type>::getValue() const -{ - return priority != Priority::EMPTY ? value : parent->getValue(); -} - -template -inline const std::string & OptionChild::value>::type>::getDefaultValue() const -{ - return parent->getDefaultValue(); -} - -template -inline std::string OptionChild::value>::type>::getValueString() const -{ - return priority != Priority::EMPTY ? value : parent->getValue(); -} - -template -inline bool OptionChild::value>::type>::empty() const noexcept -{ - return priority == Priority::EMPTY && parent->empty(); -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionEnum.cpp b/dnf-4/libdnf/conf/OptionEnum.cpp deleted file mode 100644 index 8d644c4398..0000000000 --- a/dnf-4/libdnf/conf/OptionEnum.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionEnum.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -#include - -namespace libdnf { - -template -bool fromString(T & out, const std::string & in, std::ios_base & (*manipulator)(std::ios_base &)) -{ - std::istringstream iss(in); - return !(iss >> manipulator >> out).fail(); -} - -template -OptionEnum::OptionEnum(ValueType defaultValue, const std::vector & enumVals) -: Option(Priority::DEFAULT), enumVals(enumVals), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -template -OptionEnum::OptionEnum(ValueType defaultValue, std::vector && enumVals) -: Option(Priority::DEFAULT), enumVals(std::move(enumVals)), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -template -OptionEnum::OptionEnum(ValueType defaultValue, const std::vector & enumVals, FromStringFunc && fromStringFunc) -: Option(Priority::DEFAULT), fromStringUser(std::move(fromStringFunc)) -, enumVals(enumVals), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -template -OptionEnum::OptionEnum(ValueType defaultValue, std::vector && enumVals, FromStringFunc && fromStringFunc) -: Option(Priority::DEFAULT), fromStringUser(std::move(fromStringFunc)) -, enumVals(std::move(enumVals)), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -template -void OptionEnum::test(ValueType value) const -{ - auto it = std::find(enumVals.begin(), enumVals.end(), value); - if (it == enumVals.end()) - throw InvalidValue(tfm::format(_("'%s' is not an allowed value"), value)); -} - -template -T OptionEnum::fromString(const std::string & value) const -{ - if (fromStringUser) - return fromStringUser(value); - T val; - if (libdnf::fromString(val, value, std::dec)) - return val; - throw InvalidValue(_("invalid value")); -} - -template -void OptionEnum::set(Priority priority, ValueType value) -{ - if (priority >= this->priority) { - test(value); - this->value = value; - this->priority = priority; - } -} - -template -void OptionEnum::set(Priority priority, const std::string & value) -{ - set(priority, fromString(value)); -} - -template -T OptionEnum::getValue() const -{ - return value; -} - -template -T OptionEnum::getDefaultValue() const -{ - return defaultValue; -} - -template -std::string OptionEnum::toString(ValueType value) const -{ - std::ostringstream oss; - oss << value; - return oss.str(); -} - -template -std::string OptionEnum::getValueString() const -{ - return toString(value); -} - -OptionEnum::OptionEnum(const std::string & defaultValue, const std::vector & enumVals) -: Option(Priority::DEFAULT), enumVals(enumVals), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -OptionEnum::OptionEnum(const std::string & defaultValue, std::vector && enumVals) -: Option(Priority::DEFAULT), enumVals(std::move(enumVals)), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -OptionEnum::OptionEnum(const std::string & defaultValue, const std::vector & enumVals, FromStringFunc && fromStringFunc) -: Option(Priority::DEFAULT), fromStringUser(std::move(fromStringFunc)) -, enumVals(enumVals), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -OptionEnum::OptionEnum(const std::string & defaultValue, std::vector && enumVals, FromStringFunc && fromStringFunc) -: Option(Priority::DEFAULT), fromStringUser(std::move(fromStringFunc)) -, enumVals(std::move(enumVals)), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -void OptionEnum::test(const std::string & value) const -{ - auto it = std::find(enumVals.begin(), enumVals.end(), value); - if (it == enumVals.end()) - throw InvalidValue(tfm::format(_("'%s' is not an allowed value"), value)); -} - -std::string OptionEnum::fromString(const std::string & value) const -{ - if (fromStringUser) - return fromStringUser(value); - return value; -} - -void OptionEnum::set(Priority priority, const std::string & value) -{ - auto val = fromString(value); - if (priority >= this->priority) { - test(val); - this->value = val; - this->priority = priority; - } -} - -} diff --git a/dnf-4/libdnf/conf/OptionEnum.hpp b/dnf-4/libdnf/conf/OptionEnum.hpp deleted file mode 100644 index c63156cb34..0000000000 --- a/dnf-4/libdnf/conf/OptionEnum.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_ENUM_HPP -#define _LIBDNF_OPTION_ENUM_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -#include -#include - -namespace libdnf { - -template -class OptionEnum : public Option { -public: - typedef T ValueType; - typedef std::function FromStringFunc; - - OptionEnum(ValueType defaultValue, const std::vector & enumVals); - OptionEnum(ValueType defaultValue, std::vector && enumVals); - OptionEnum(ValueType defaultValue, const std::vector & enumVals, FromStringFunc && fromStringFunc); - OptionEnum(ValueType defaultValue, std::vector && enumVals, FromStringFunc && fromStringFunc); - OptionEnum * clone() const override; - void test(ValueType value) const; - ValueType fromString(const std::string & value) const; - void set(Priority priority, ValueType value); - void set(Priority priority, const std::string & value) override; - T getValue() const; - T getDefaultValue() const; - std::string toString(ValueType value) const; - std::string getValueString() const override; - -protected: - FromStringFunc fromStringUser; - std::vector enumVals; - ValueType defaultValue; - ValueType value; -}; - -template <> -class OptionEnum : public Option { -public: - typedef std::string ValueType; - typedef std::function FromStringFunc; - - OptionEnum(const std::string & defaultValue, const std::vector & enumVals); - OptionEnum(const std::string & defaultValue, std::vector && enumVals); - OptionEnum(const std::string & defaultValue, const std::vector & enumVals, FromStringFunc && fromStringFunc); - OptionEnum(const std::string & defaultValue, std::vector && enumVals, FromStringFunc && fromStringFunc); - OptionEnum * clone() const override; - void test(const std::string & value) const; - std::string fromString(const std::string & value) const; - void set(Priority priority, const std::string & value) override; - const std::string & getValue() const; - const std::string & getDefaultValue() const; - std::string getValueString() const override; - -protected: - FromStringFunc fromStringUser; - std::vector enumVals; - ValueType defaultValue; - ValueType value; -}; - -template -inline OptionEnum * OptionEnum::clone() const -{ - return new OptionEnum(*this); -} - -inline OptionEnum * OptionEnum::clone() const -{ - return new OptionEnum(*this); -} - -inline const std::string & OptionEnum::getValue() const -{ - return value; -} - -inline const std::string & OptionEnum::getDefaultValue() const -{ - return defaultValue; -} - -inline std::string OptionEnum::getValueString() const -{ - return value; -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionNumber.cpp b/dnf-4/libdnf/conf/OptionNumber.cpp deleted file mode 100644 index 21dd939fe4..0000000000 --- a/dnf-4/libdnf/conf/OptionNumber.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionNumber.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -#include - -namespace libdnf { - -template -bool fromString(T & out, const std::string & in, std::ios_base & (*manipulator)(std::ios_base &)) -{ - std::istringstream iss(in); - return !(iss >> manipulator >> out).fail(); -} - -template -OptionNumber::OptionNumber(T defaultValue, T min, T max) -: Option(Priority::DEFAULT), defaultValue(defaultValue), min(min), max(max), value(defaultValue) -{ - test(defaultValue); -} - -template -OptionNumber::OptionNumber(T defaultValue, T min) -: OptionNumber(defaultValue, min, std::numeric_limits::max()) {} - -template -OptionNumber::OptionNumber(T defaultValue) -: OptionNumber(defaultValue, std::numeric_limits::min(), std::numeric_limits::max()) {} - -template -OptionNumber::OptionNumber(T defaultValue, T min, T max, FromStringFunc && fromStringFunc) -: Option(Priority::DEFAULT) -, fromStringUser(std::move(fromStringFunc)) -, defaultValue(defaultValue), min(min), max(max), value(defaultValue) -{ - test(defaultValue); -} - -template -OptionNumber::OptionNumber(T defaultValue, T min, FromStringFunc && fromStringFunc) -: OptionNumber(defaultValue, min, std::numeric_limits::max(), std::move(fromStringFunc)) {} - -template -OptionNumber::OptionNumber(T defaultValue, FromStringFunc && fromStringFunc) -: OptionNumber(defaultValue, std::numeric_limits::min(), std::numeric_limits::max(), std::move(fromStringFunc)) {} - -template -void OptionNumber::test(ValueType value) const -{ - if (value > max) - throw InvalidValue(tfm::format(_("given value [%d] should be less than " - "allowed value [%d]."), value, max)); - else if (value < min) - throw InvalidValue(tfm::format(_("given value [%d] should be greater than " - "allowed value [%d]."), value, min)); -} - -template -T OptionNumber::fromString(const std::string & value) const -{ - if (fromStringUser) - return fromStringUser(value); - ValueType val; - if (libdnf::fromString(val, value, std::dec)) - return val; - throw InvalidValue(_("invalid value")); -} - -template -void OptionNumber::set(Priority priority, ValueType value) -{ - if (priority >= this->priority) { - test(value); - this->value = value; - this->priority = priority; - } -} - -template -void OptionNumber::set(Option::Priority priority, const std::string & value) -{ - set(priority, fromString(value)); -} - -template -std::string OptionNumber::toString(ValueType value) const -{ - std::ostringstream oss; - oss << value; - return oss.str(); -} - -template class OptionNumber; -template class OptionNumber; -template class OptionNumber; -template class OptionNumber; -template class OptionNumber; - -} diff --git a/dnf-4/libdnf/conf/OptionNumber.hpp b/dnf-4/libdnf/conf/OptionNumber.hpp deleted file mode 100644 index 98988fd50e..0000000000 --- a/dnf-4/libdnf/conf/OptionNumber.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_NUMBER_HPP -#define _LIBDNF_OPTION_NUMBER_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -#include - -namespace libdnf { - -template -class OptionNumber : public Option { -public: - typedef T ValueType; - typedef std::function FromStringFunc; - - OptionNumber(T defaultValue, T min, T max); - OptionNumber(T defaultValue, T min); - OptionNumber(T defaultValue); - OptionNumber(T defaultValue, T min, T max, FromStringFunc && fromStringFunc); - OptionNumber(T defaultValue, T min, FromStringFunc && fromStringFunc); - OptionNumber(T defaultValue, FromStringFunc && fromStringFunc); - OptionNumber * clone() const override; - void test(ValueType value) const; - T fromString(const std::string & value) const; - void set(Priority priority, ValueType value); - void set(Priority priority, const std::string & value) override; - T getValue() const; - T getDefaultValue() const; - std::string toString(ValueType value) const; - std::string getValueString() const override; - -protected: - FromStringFunc fromStringUser; - ValueType defaultValue; - ValueType min; - ValueType max; - ValueType value; -}; - -template -inline OptionNumber * OptionNumber::clone() const -{ - return new OptionNumber(*this); -} - -template -inline T OptionNumber::getValue() const -{ - return value; -} - -template -inline T OptionNumber::getDefaultValue() const -{ - return defaultValue; -} - -template -inline std::string OptionNumber::getValueString() const -{ - return toString(value); -} - -extern template class OptionNumber; -extern template class OptionNumber; -extern template class OptionNumber; -extern template class OptionNumber; -extern template class OptionNumber; - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionPath.cpp b/dnf-4/libdnf/conf/OptionPath.cpp deleted file mode 100644 index eff6d49202..0000000000 --- a/dnf-4/libdnf/conf/OptionPath.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionPath.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -#include -#include -#include - -namespace libdnf { - -static std::string removeFileProt(const std::string & value) -{ - if (value.compare(0, 7, "file://") == 0) - return value.substr(7); - return value; -} - -OptionPath::OptionPath(const std::string & defaultValue, bool exists, bool absPath) -: OptionString(defaultValue), exists(exists), absPath(absPath) -{ - this->defaultValue = removeFileProt(this->defaultValue); - test(this->defaultValue); - this->value = this->defaultValue; -} - -OptionPath::OptionPath(const char * defaultValue, bool exists, bool absPath) -: OptionString(defaultValue), exists(exists), absPath(absPath) -{ - if (defaultValue) { - this->defaultValue = removeFileProt(this->defaultValue); - test(this->defaultValue); - this->value = this->defaultValue; - } -} - -OptionPath::OptionPath(const std::string & defaultValue, const std::string & regex, bool icase, bool exists, bool absPath) -: OptionString(removeFileProt(defaultValue), regex, icase), exists(exists), absPath(absPath) -{ - this->defaultValue = removeFileProt(this->defaultValue); - test(this->defaultValue); - this->value = this->defaultValue; -} - -OptionPath::OptionPath(const char * defaultValue, const std::string & regex, bool icase, bool exists, bool absPath) -: OptionString(defaultValue, regex, icase), exists(exists), absPath(absPath) -{ - if (defaultValue) { - this->defaultValue = removeFileProt(this->defaultValue); - test(this->defaultValue); - this->value = this->defaultValue; - } -} - -void OptionPath::test(const std::string & value) const -{ - if (absPath && value[0] != '/') - throw InvalidValue(tfm::format(_("given path '%s' is not absolute."), value)); - - struct stat buffer; - if (exists && stat(value.c_str(), &buffer)) - throw InvalidValue(tfm::format(_("given path '%s' does not exist."), value)); -} - -void OptionPath::set(Priority priority, const std::string & value) -{ - if (priority >= this->priority) { - OptionString::test(value); - auto val = removeFileProt(value); - test(val); - this->value = val; - this->priority = priority; - } -} - -} diff --git a/dnf-4/libdnf/conf/OptionPath.hpp b/dnf-4/libdnf/conf/OptionPath.hpp deleted file mode 100644 index b8a86a484d..0000000000 --- a/dnf-4/libdnf/conf/OptionPath.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_PATH_HPP -#define _LIBDNF_OPTION_PATH_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "OptionString.hpp" - -namespace libdnf { - -/** -* @class OptionPath -* -* @brief Option for file path which can validate path existence. -* -*/ -class OptionPath : public OptionString { -public: - OptionPath(const std::string & defaultValue, bool exists = false, bool absPath = false); - OptionPath(const char * defaultValue, bool exists = false, bool absPath = false); - OptionPath(const std::string & defaultValue, const std::string & regex, bool icase, bool exists = false, bool absPath = false); - OptionPath(const char * defaultValue, const std::string & regex, bool icase, bool exists = false, bool absPath = false); - OptionPath * clone() const override; - void test(const std::string & value) const; - void set(Priority priority, const std::string & value) override; - -protected: - bool exists; - bool absPath; -}; - -inline OptionPath * OptionPath::clone() const -{ - return new OptionPath(*this); -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionSeconds.cpp b/dnf-4/libdnf/conf/OptionSeconds.cpp deleted file mode 100644 index 989032a073..0000000000 --- a/dnf-4/libdnf/conf/OptionSeconds.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionSeconds.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" - -namespace libdnf { - -OptionSeconds::OptionSeconds(ValueType defaultValue, ValueType min, ValueType max) -: OptionNumber(defaultValue, min, max) {} - -OptionSeconds::OptionSeconds(ValueType defaultValue, ValueType min) -: OptionNumber(defaultValue, min) {} - -OptionSeconds::OptionSeconds(ValueType defaultValue) -: OptionNumber(defaultValue) {} - -OptionSeconds::ValueType OptionSeconds::fromString(const std::string & value) const -{ - if (value.empty()) - throw InvalidValue(_("no value specified")); - - if (value == "-1" || value == "never") // Special cache timeout, meaning never - return -1; - - std::size_t idx; - auto res = std::stod(value, &idx); - if (res < 0) - throw InvalidValue(tfm::format(_("seconds value '%s' must not be negative"), value)); - - if (idx < value.length()) { - if (idx < value.length() - 1) - throw InvalidValue(tfm::format(_("could not convert '%s' to seconds"), value)); - switch (value.back()) { - case 's': case 'S': - break; - case 'm': case 'M': - res *= 60; - break; - case 'h': case 'H': - res *= 60 * 60; - break; - case 'd': case 'D': - res *= 60 * 60 * 24; - break; - default: - throw InvalidValue(tfm::format(_("unknown unit '%s'"), value.back())); - } - } - - return res; -} - -void OptionSeconds::set(Priority priority, const std::string & value) -{ - set(priority, fromString(value)); -} - -} diff --git a/dnf-4/libdnf/conf/OptionSeconds.hpp b/dnf-4/libdnf/conf/OptionSeconds.hpp deleted file mode 100644 index dc714b23af..0000000000 --- a/dnf-4/libdnf/conf/OptionSeconds.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_SECONDS_HPP -#define _LIBDNF_OPTION_SECONDS_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "OptionNumber.hpp" - -namespace libdnf { - -/** -* @class OptionSeconds -* -* @brief An option representing an integer value of seconds. -* -* Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never. -* Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y. -*/ -class OptionSeconds : public OptionNumber { -public: - OptionSeconds(ValueType defaultValue, ValueType min, ValueType max); - OptionSeconds(ValueType defaultValue, ValueType min); - OptionSeconds(ValueType defaultValue); - OptionSeconds * clone() const override; - ValueType fromString(const std::string & value) const; - using OptionNumber::set; - void set(Priority priority, const std::string & value) override; -}; - -inline OptionSeconds * OptionSeconds::clone() const -{ - return new OptionSeconds(*this); -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionString.cpp b/dnf-4/libdnf/conf/OptionString.cpp deleted file mode 100644 index d27194f7a3..0000000000 --- a/dnf-4/libdnf/conf/OptionString.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionString.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" -#include "regex/regex.hpp" - -namespace libdnf { - -OptionString::OptionString(const std::string & defaultValue) -: Option(Priority::DEFAULT), defaultValue(defaultValue), value(defaultValue) {} - -OptionString::OptionString(const char * defaultValue) -{ - if (defaultValue) { - this->value = this->defaultValue = defaultValue; - this->priority = Priority::DEFAULT; - } -} - -OptionString::OptionString(const std::string & defaultValue, const std::string & regex, bool icase) -: Option(Priority::DEFAULT), regex(regex), icase(icase), defaultValue(defaultValue), value(defaultValue) { test(defaultValue); } - -OptionString::OptionString(const char * defaultValue, const std::string & regex, bool icase) -: regex(regex), icase(icase) -{ - if (defaultValue) { - this->defaultValue = defaultValue; - test(this->defaultValue); - this->value = this->defaultValue; - this->priority = Priority::DEFAULT; - } -} - -void OptionString::test(const std::string & value) const -{ - if (regex.empty()) - return; - if (!Regex(regex.c_str(), (icase ? REG_ICASE : 0) | REG_EXTENDED | REG_NOSUB).match(value.c_str())) - throw InvalidValue(tfm::format(_("'%s' is not an allowed value"), value)); -} - -void OptionString::set(Priority priority, const std::string & value) -{ - if (priority >= this->priority) { - test(value); - this->value = value; - this->priority = priority; - } -} - -const std::string & OptionString::getValue() const -{ - if (priority == Priority::EMPTY) - throw ValueNotSet(_("GetValue(): Value not set")); - return value; -} - -} diff --git a/dnf-4/libdnf/conf/OptionString.hpp b/dnf-4/libdnf/conf/OptionString.hpp deleted file mode 100644 index 2e26305c42..0000000000 --- a/dnf-4/libdnf/conf/OptionString.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_STRING_HPP -#define _LIBDNF_OPTION_STRING_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" - -namespace libdnf { - -class OptionString : public Option { -public: - typedef std::string ValueType; - - OptionString(const std::string & defaultValue); - OptionString(const char * defaultValue); - OptionString(const std::string & defaultValue, const std::string & regex, bool icase); - OptionString(const char * defaultValue, const std::string & regex, bool icase); - OptionString * clone() const override; - void test(const std::string & value) const; - void set(Priority priority, const std::string & value) override; - std::string fromString(const std::string & value) const; - const std::string & getValue() const; - const std::string & getDefaultValue() const noexcept; - std::string getValueString() const override; - -protected: - std::string regex; - bool icase; - std::string defaultValue; - std::string value; -}; - -inline OptionString * OptionString::clone() const -{ - return new OptionString(*this); -} - -inline const std::string & OptionString::getDefaultValue() const noexcept -{ - return defaultValue; -} - -inline std::string OptionString::getValueString() const -{ - return getValue(); -} - -inline std::string OptionString::fromString(const std::string & value) const -{ - return value; -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/conf/OptionStringList.cpp b/dnf-4/libdnf/conf/OptionStringList.cpp deleted file mode 100644 index 55904716a6..0000000000 --- a/dnf-4/libdnf/conf/OptionStringList.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OptionStringList.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" -#include "regex/regex.hpp" - -namespace libdnf { - -OptionStringList::OptionStringList(const ValueType & defaultValue) -: Option(Priority::DEFAULT), defaultValue(defaultValue), value(defaultValue) {} - -OptionStringList::OptionStringList(const ValueType & defaultValue, const std::string & regex, bool icase) -: Option(Priority::DEFAULT), regex(regex), icase(icase), defaultValue(defaultValue), value(defaultValue) -{ - test(defaultValue); -} - -OptionStringList::OptionStringList(const std::string & defaultValue) -: Option(Priority::DEFAULT) -{ - this->value = this->defaultValue = fromString(defaultValue); -} - -OptionStringList::OptionStringList(const std::string & defaultValue, const std::string & regex, bool icase) -: Option(Priority::DEFAULT), regex(regex), icase(icase) -{ - this->defaultValue = fromString(defaultValue); - test(this->defaultValue); - value = this->defaultValue; -} - -void OptionStringList::test(const std::vector & value) const -{ - if (regex.empty()) - return; - Regex regexObj(regex.c_str(), (icase ? REG_ICASE : 0) | REG_EXTENDED | REG_NOSUB); - for (const auto & val : value) { - if (!regexObj.match(val.c_str())) - throw InvalidValue(tfm::format(_("'%s' is not an allowed value"), val)); - } -} - -OptionStringList::ValueType OptionStringList::fromString(const std::string & value) const -{ - std::vector tmp; - auto start = value.find_first_not_of(" "); - while (start != value.npos && start < value.length()) { - auto end = value.find_first_of(" ,\n", start); - if (end == value.npos) { - tmp.push_back(value.substr(start)); - break; - } - tmp.push_back(value.substr(start, end - start)); - start = value.find_first_not_of(" ", end + 1); - if (start != value.npos && value[start] == ',' && value[end] == ' ') { - end = start; - start = value.find_first_not_of(" ", start + 1); - } - if (start != value.npos && value[start] == '\n' && (value[end] == ' ' || value[end] == ',')) - start = value.find_first_not_of(" ", start + 1); - } - return tmp; -} - -void OptionStringList::set(Priority priority, const ValueType & value) -{ - if (priority >= this->priority) { - test(value); - this->value = value; - this->priority = priority; - } -} - -void OptionStringList::set(Priority priority, const std::string & value) -{ - set(priority, fromString(value)); -} - -std::string OptionStringList::toString(const ValueType & value) const -{ - std::ostringstream oss; - bool next{false}; - for (auto & val : value) { - if (next) - oss << ", "; - else - next = true; - oss << val; - } - return oss.str(); -} - -} diff --git a/dnf-4/libdnf/conf/OptionStringList.hpp b/dnf-4/libdnf/conf/OptionStringList.hpp deleted file mode 100644 index 942e56b165..0000000000 --- a/dnf-4/libdnf/conf/OptionStringList.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_OPTION_STRING_LIST_HPP -#define _LIBDNF_OPTION_STRING_LIST_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Option.hpp" -#include - -namespace libdnf { - -class OptionStringList : public Option { -public: - typedef std::vector ValueType; - - OptionStringList(const ValueType & defaultValue); - OptionStringList(const ValueType & defaultValue, const std::string & regex, bool icase); - OptionStringList(const std::string & defaultValue); - OptionStringList(const std::string & defaultValue, const std::string & regex, bool icase); - OptionStringList * clone() const override; - void test(const std::vector & value) const; - ValueType fromString(const std::string & value) const; - virtual void set(Priority priority, const ValueType & value); - void set(Priority priority, const std::string & value) override; - const ValueType & getValue() const; - const ValueType & getDefaultValue() const; - std::string toString(const ValueType & value) const; - std::string getValueString() const override; - -protected: - std::string regex; - bool icase; - ValueType defaultValue; - ValueType value; -}; - -inline OptionStringList * OptionStringList::clone() const -{ - return new OptionStringList(*this); -} - -inline const OptionStringList::ValueType & OptionStringList::getValue() const -{ - return value; -} - -inline const OptionStringList::ValueType & OptionStringList::getDefaultValue() const -{ - return defaultValue; -} - -inline std::string OptionStringList::getValueString() const -{ - return toString(value); -} - -} - -#endif - -#endif diff --git a/dnf-4/libdnf/repo/Repo-private.hpp b/dnf-4/libdnf/repo/Repo-private.hpp deleted file mode 100644 index 890a371dbf..0000000000 --- a/dnf-4/libdnf/repo/Repo-private.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2019 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_REPO_PRIVATE_HPP -#define _LIBDNF_REPO_PRIVATE_HPP - -#include "Repo.hpp" -#include "../dnf-utils.h" -#include "../hy-iutil.h" -#include "../hy-util-private.hpp" -#include "../hy-types.h" - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#define MD_TYPE_PRIMARY "primary" -#define MD_TYPE_FILELISTS "filelists" -#define MD_TYPE_PRESTODELTA "prestodelta" -#define MD_TYPE_GROUP_GZ "group_gz" -#define MD_TYPE_GROUP "group" -#define MD_TYPE_UPDATEINFO "updateinfo" -#define MD_TYPE_MODULES "modules" -/* "other" in this context is not a generic "any other metadata", but real metadata type named "other" - * containing changelogs for packages */ -#define MD_TYPE_OTHER "other" - -enum _hy_repo_state { - _HY_NEW, - _HY_LOADED_FETCH, - _HY_LOADED_CACHE, - _HY_WRITTEN -}; - -namespace std { - -template<> -struct default_delete { - void operator()(LrHandle * ptr) noexcept { lr_handle_free(ptr); } -}; - -} // namespace std - -namespace libdnf { - -typedef ::Repo LibsolvRepo; - -class Key { -public: - Key(gpgme_key_t key, gpgme_subkey_t subkey) - { - id = subkey->keyid; - fingerprint = subkey->fpr; - timestamp = subkey->timestamp; - userid = key->uids->uid; - } - - std::string getId() const { return id; } - std::string getUserId() const { return userid; } - std::string getFingerprint() const { return fingerprint; } - long int getTimestamp() const { return timestamp; } - - std::vector rawKey; - std::string url; - -private: - std::string id; - std::string fingerprint; - std::string userid; - long int timestamp; -}; - -class Repo::Impl { -public: - Impl(Repo & owner, const std::string & id, Type type, std::unique_ptr && conf); - ~Impl(); - - bool load(); - bool loadCache(bool throwExcept); - void downloadMetadata(const std::string & destdir); - bool isInSync(); - void fetch(const std::string & destdir, std::unique_ptr && h); - std::string getCachedir() const; - std::string getPersistdir() const; - int getAge() const; - void expire(); - bool isExpired() const; - int getExpiresIn() const; - void downloadUrl(const char * url, int fd); - void addCountmeFlag(LrHandle *handle); - void setHttpHeaders(const char * headers[]); - const char * const * getHttpHeaders() const; - const std::string & getMetadataPath(const std::string &metadataType) const; - - std::unique_ptr lrHandleInitBase(); - std::unique_ptr lrHandleInitLocal(); - std::unique_ptr lrHandleInitRemote(const char *destdir, bool mirrorSetup = true); - - void attachLibsolvRepo(LibsolvRepo * libsolvRepo); - void detachLibsolvRepo(); - - std::string id; - Type type; - std::unique_ptr conf; - - char ** mirrors{nullptr}; - int maxMirrorTries{0}; // try them all - // 0 forces expiration on the next call to load(), -1 means undefined value - int timestamp; - int maxTimestamp{0}; - bool preserveRemoteTime{false}; - std::string repomdFn; - std::set additionalMetadata; - std::string revision; - std::vector content_tags; - std::vector> distro_tags; - std::vector> metadata_locations; - unsigned char checksum[CHKSUM_BYTES]; - bool useIncludes{false}; - bool loadMetadataOther; - std::map substitutions; - - std::unique_ptr callbacks; - std::string repoFilePath; - LrHandle * getCachedHandle(); - - SyncStrategy syncStrategy; - std::map metadataPaths; - - LibsolvRepo * libsolvRepo{nullptr}; - bool needs_internalizing{false}; - int nrefs{1}; - - enum _hy_repo_state state_main{_HY_NEW}; - enum _hy_repo_state state_filelists{_HY_NEW}; - enum _hy_repo_state state_presto{_HY_NEW}; - enum _hy_repo_state state_updateinfo{_HY_NEW}; - enum _hy_repo_state state_other{_HY_NEW}; - Id filenames_repodata{0}; - Id presto_repodata{0}; - Id updateinfo_repodata{0}; - Id other_repodata{0}; - int load_flags{0}; - /* the following three elements are needed for repo rewriting */ - int main_nsolvables{0}; - int main_nrepodata{0}; - int main_end{0}; - - // Lock attachLibsolvRepo(), detachLibsolvRepo() and hy_repo_free() to ensure atomic behavior - // in threaded environment such as PackageKit. - std::mutex attachLibsolvMutex; - -private: - Repo * owner; - std::unique_ptr lrHandlePerform(LrHandle * handle, const std::string & destDirectory, - bool setGPGHomeDir); - bool isMetalinkInSync(); - bool isRepomdInSync(); - void resetMetadataExpired(); - std::vector retrieve(const std::string & url); - void importRepoKeys(); - - static int progressCB(void * data, double totalToDownload, double downloaded); - static void fastestMirrorCB(void * data, LrFastestMirrorStages stage, void *ptr); - static int mirrorFailureCB(void * data, const char * msg, const char * url, const char * metadata); - - bool expired; - std::unique_ptr handle; - std::unique_ptr> httpHeaders{nullptr, [](char ** ptr) - { - for (auto item = ptr; *item; ++item) - delete[] *item; - delete[] ptr; - }}; - bool endsWith(std::string const &str, std::string const &ending) const; - std::string getHash() const; -}; - -} - -#endif diff --git a/dnf-4/libdnf/repo/Repo.cpp b/dnf-4/libdnf/repo/Repo.cpp deleted file mode 100644 index 9d117ab7fc..0000000000 --- a/dnf-4/libdnf/repo/Repo.cpp +++ /dev/null @@ -1,2228 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define METADATA_RELATIVE_DIR "repodata" -#define PACKAGES_RELATIVE_DIR "packages" -#define METALINK_FILENAME "metalink.xml" -#define MIRRORLIST_FILENAME "mirrorlist" -#define RECOGNIZED_CHKSUMS {"sha512", "sha256"} - -#include "../log.hpp" -#include "Repo-private.hpp" -#include "../dnf-utils.h" -#include "../dnf-context.hpp" -#include "../hy-iutil.h" -#include "../hy-repo-private.hpp" -#include "../hy-util-private.hpp" -#include "../hy-iutil-private.hpp" -#include "../hy-types.h" -#include "libdnf/utils/File.hpp" -#include "libdnf/utils/utils.hpp" -#include "libdnf/utils/os-release.hpp" - -#include "bgettext/bgettext-lib.h" -#include "tinyformat/tinyformat.hpp" -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -// -// COUNTME CONSTANTS -// -// width of the sliding time window (in seconds) -const int COUNTME_WINDOW = 7*24*60*60; // 1 week -// starting point of the sliding time window relative to the UNIX epoch -// allows for aligning the window with a specific weekday -const int COUNTME_OFFSET = 345600; // Monday (1970-01-05 00:00:00 UTC) -// estimated number of metalink requests sent over the window -// used to generate the probability distribution of counting events -const int COUNTME_BUDGET = 4; // metadata_expire defaults to 2 days -// cookie file name -const std::string COUNTME_COOKIE = "countme"; -// cookie file format version -const int COUNTME_VERSION = 0; -// longevity buckets that we report in the flag -// example: {A, B, C} defines 4 buckets [0, A), [A, B), [B, C), [C, infinity) -// where each letter represents a window step (starting from 0) -const std::array COUNTME_BUCKETS = { {2, 5, 25} }; - -namespace std { - -template<> -struct default_delete { - void operator()(GError * ptr) noexcept { g_error_free(ptr); } -}; - -template<> -struct default_delete { - void operator()(LrResult * ptr) noexcept { lr_result_free(ptr); } -}; - -template<> -struct default_delete { - void operator()(LrPackageTarget * ptr) noexcept { lr_packagetarget_free(ptr); } -}; - -template<> -struct default_delete::type> { - void operator()(gpgme_ctx_t ptr) noexcept { gpgme_release(ptr); } -}; - -} // namespace std - -namespace libdnf { - -class LrExceptionWithSourceUrl : public LrException { -public: - LrExceptionWithSourceUrl(int code, const std::string & msg, const std::string & sourceUrl) - : LrException(code, msg), sourceUrl(sourceUrl) {} - const std::string & getSourceUrl() const { return sourceUrl; } -private: - std::string sourceUrl; -}; - -static void throwException(std::unique_ptr && err) -{ - throw LrException(err->code, err->message); -} - -template -inline static void handleSetOpt(LrHandle * handle, LrHandleOption option, T value) -{ - GError * errP{nullptr}; - if (!lr_handle_setopt(handle, &errP, option, value)) { - throwException(std::unique_ptr(errP)); - } -} - -inline static void handleGetInfo(LrHandle * handle, LrHandleInfoOption option, void * value) -{ - GError * errP{nullptr}; - if (!lr_handle_getinfo(handle, &errP, option, value)) { - throwException(std::unique_ptr(errP)); - } -} - -template -inline static void resultGetInfo(LrResult * result, LrResultInfoOption option, T value) -{ - GError * errP{nullptr}; - if (!lr_result_getinfo(result, &errP, option, value)) { - throwException(std::unique_ptr(errP)); - } -} - -/* Callback stuff */ - -int RepoCB::progress(double totalToDownload, double downloaded) { return 0; } -void RepoCB::fastestMirror(FastestMirrorStage stage, const char * ptr) {} -int RepoCB::handleMirrorFailure(const char * msg, const char * url, const char * metadata) { return 0; } - -bool RepoCB::repokeyImport(const std::string & id, const std::string & userId, - const std::string & fingerprint, const std::string & url, long int timestamp) -{ - return true; -} - - -// Map string from config option proxy_auth_method to librepo LrAuth value -static constexpr struct { - const char * name; - LrAuth code; -} PROXYAUTHMETHODS[] = { - {"none", LR_AUTH_NONE}, - {"basic", LR_AUTH_BASIC}, - {"digest", LR_AUTH_DIGEST}, - {"negotiate", LR_AUTH_NEGOTIATE}, - {"ntlm", LR_AUTH_NTLM}, - {"digest_ie", LR_AUTH_DIGEST_IE}, - {"ntlm_wb", LR_AUTH_NTLM_WB}, - {"any", LR_AUTH_ANY} -}; - -bool Repo::Impl::endsWith(const std::string &str, const std::string &ending) const { - if (str.length() >= ending.length()) - return (str.compare(str.length() - ending.length(), ending.length(), ending) == 0); - else - return false; -} - -const std::string & Repo::Impl::getMetadataPath(const std::string &metadataType) const { -// auto logger(Log::getLogger()); - static const std::string empty; - std::string lookupMetadataType = metadataType; - if (conf->getMasterConfig().zchunk().getValue()) { - if(!endsWith(metadataType, "_zck")) - lookupMetadataType = metadataType + "_zck"; - } - auto it = metadataPaths.find(lookupMetadataType); - if(it == metadataPaths.end() && lookupMetadataType != metadataType) - it = metadataPaths.find(metadataType); - auto & ret = (it != metadataPaths.end()) ? it->second : empty; -// if (ret.empty()) -// logger->debug(tfm::format("not found \"%s\" for: %s", metadataType, conf->name().getValue())); - return ret; -} - -int Repo::Impl::progressCB(void * data, double totalToDownload, double downloaded) -{ - if (!data) - return 0; - auto cbObject = static_cast(data); - return cbObject->progress(totalToDownload, downloaded); -} - -void Repo::Impl::fastestMirrorCB(void * data, LrFastestMirrorStages stage, void *ptr) -{ - if (!data) - return; - auto cbObject = static_cast(data); - const char * msg; - std::string msgString; - if (ptr) { - switch (stage) { - case LR_FMSTAGE_CACHELOADING: - case LR_FMSTAGE_CACHELOADINGSTATUS: - case LR_FMSTAGE_STATUS: - msg = static_cast(ptr); - break; - case LR_FMSTAGE_DETECTION: - msgString = std::to_string(*((long *)ptr)); - msg = msgString.c_str(); - break; - default: - msg = nullptr; - } - } else - msg = nullptr; - cbObject->fastestMirror(static_cast(stage), msg); -} - -int Repo::Impl::mirrorFailureCB(void * data, const char * msg, const char * url, const char * metadata) -{ - if (!data) - return 0; - auto cbObject = static_cast(data); - return cbObject->handleMirrorFailure(msg, url, metadata); -}; - - -/** -* @brief Converts the given input string to a URL encoded string -* -* All input characters that are not a-z, A-Z, 0-9, '-', '.', '_' or '~' are converted -* to their "URL escaped" version (%NN where NN is a two-digit hexadecimal number). -* -* @param src String to encode -* @return URL encoded string -*/ -static std::string urlEncode(const std::string & src) -{ - auto noEncode = [](char ch) - { - return isalnum(ch) || ch=='-' || ch == '.' || ch == '_' || ch == '~'; - }; - - // compute length of encoded string - auto len = src.length(); - for (auto ch : src) { - if (!noEncode(ch)) - len += 2; - } - - // encode the input string - std::string encoded; - encoded.reserve(len); - for (auto ch : src) { - if (noEncode(ch)) - encoded.push_back(ch); - else { - encoded.push_back('%'); - unsigned char hex; - hex = static_cast(ch) >> 4; - hex += hex <= 9 ? '0' : 'a' - 10; - encoded.push_back(hex); - hex = static_cast(ch) & 0x0F; - hex += hex <= 9 ? '0' : 'a' - 10; - encoded.push_back(hex); - } - } - - return encoded; -} - -/** -* @brief Format user password string -* -* Returns user and password in user:password form. If quote is True, -* special characters in user and password are URL encoded. -* -* @param user Username -* @param passwd Password -* @param encode If quote is True, special characters in user and password are URL encoded. -* @return User and password in user:password form -*/ -static std::string formatUserPassString(const std::string & user, const std::string & passwd, bool encode) -{ - if (encode) - return urlEncode(user) + ":" + urlEncode(passwd); - else - return user + ":" + passwd; -} - -Repo::Impl::Impl(Repo & owner, const std::string & id, Type type, std::unique_ptr && conf) -: id(id), type(type), conf(std::move(conf)), timestamp(-1), loadMetadataOther(false) -, syncStrategy(SyncStrategy::TRY_CACHE), owner(&owner), expired(false) {} - -Repo::Impl::~Impl() -{ - g_strfreev(mirrors); - if (libsolvRepo) - libsolvRepo->appdata = nullptr; -} - -Repo::Repo(const std::string & id, std::unique_ptr && conf, Repo::Type type) -{ - if (type == Type::AVAILABLE) { - auto idx = verifyId(id); - if (idx >= 0) { - std::string msg = tfm::format(_("Bad id for repo: %s, byte = %s %d"), id, id[idx], idx); - throw std::runtime_error(msg); - } - } - pImpl.reset(new Impl(*this, id, type, std::move(conf))); -} - -Repo::~Repo() = default; - -void Repo::setCallbacks(std::unique_ptr && callbacks) -{ - pImpl->callbacks = std::move(callbacks); -} - -int Repo::verifyId(const std::string & id) -{ - auto idx = id.find_first_not_of(REPOID_CHARS); - return idx == id.npos ? -1 : idx; -} - -void Repo::verify() const -{ - if (pImpl->conf->baseurl().empty() && - (pImpl->conf->metalink().empty() || pImpl->conf->metalink().getValue().empty()) && - (pImpl->conf->mirrorlist().empty() || pImpl->conf->mirrorlist().getValue().empty())) - throw std::runtime_error(tfm::format(_("Repository %s has no mirror or baseurl set."), pImpl->id)); - - const auto & type = pImpl->conf->type().getValue(); - const char * supportedRepoTypes[]{"rpm-md", "rpm", "repomd", "rpmmd", "yum", "YUM"}; - if (!type.empty()) { - for (auto supported : supportedRepoTypes) { - if (type == supported) - return; - } - throw std::runtime_error(tfm::format(_("Repository '%s' has unsupported type: 'type=%s', skipping."), - pImpl->id, type)); - } -} - -ConfigRepo * Repo::getConfig() noexcept -{ - return pImpl->conf.get(); -} - -const std::string & Repo::getId() const noexcept -{ - return pImpl->id; -} - -void Repo::enable() -{ - pImpl->conf->enabled().set(Option::Priority::RUNTIME, true); -} - -void Repo::disable() -{ - pImpl->conf->enabled().set(Option::Priority::RUNTIME, false); -} - -bool Repo::isEnabled() const -{ - return pImpl->conf->enabled().getValue(); -} - -bool Repo::isLocal() const -{ - auto & conf = pImpl->conf; - if ((!conf->metalink().empty() && !conf->metalink().getValue().empty()) || - (!conf->mirrorlist().empty() && !conf->mirrorlist().getValue().empty())) - return false; - if (!conf->baseurl().getValue().empty() && conf->baseurl().getValue()[0].compare(0, 7, "file://") == 0) - return true; - return false; -} - -bool Repo::load() { return pImpl->load(); } -bool Repo::loadCache(bool throwExcept) { return pImpl->loadCache(throwExcept); } -void Repo::downloadMetadata(const std::string & destdir) { pImpl->downloadMetadata(destdir); } -bool Repo::getUseIncludes() const { return pImpl->useIncludes; } -void Repo::setUseIncludes(bool enabled) { pImpl->useIncludes = enabled; } -bool Repo::getLoadMetadataOther() const { return pImpl->loadMetadataOther; } -void Repo::setLoadMetadataOther(bool value) { pImpl->loadMetadataOther = value; } -int Repo::getCost() const { return pImpl->conf->cost().getValue(); } -int Repo::getPriority() const { return pImpl->conf->priority().getValue(); } -std::string Repo::getCompsFn() { - auto tmp = pImpl->getMetadataPath(MD_TYPE_GROUP_GZ); - if (tmp.empty()) - tmp = pImpl->getMetadataPath(MD_TYPE_GROUP); - return tmp; -} - - -#ifdef MODULEMD -std::string Repo::getModulesFn() { return pImpl->getMetadataPath(MD_TYPE_MODULES); } -#endif - -int Repo::getAge() const { return pImpl->getAge(); } -void Repo::expire() { pImpl->expire(); } -bool Repo::isExpired() const { return pImpl->isExpired(); } -int Repo::getExpiresIn() const { return pImpl->getExpiresIn(); } - -void Repo::setSubstitutions(const std::map & substitutions) -{ - pImpl->substitutions = substitutions; -} - -void Repo::addMetadataTypeToDownload(const std::string &metadataType) -{ - pImpl->additionalMetadata.insert(metadataType); -} - -void Repo::removeMetadataTypeFromDownload(const std::string &metadataType) -{ - pImpl->additionalMetadata.erase(metadataType); -} - -std::string Repo::getMetadataPath(const std::string &metadataType) -{ - return pImpl->getMetadataPath(metadataType); -} - -std::string Repo::getMetadataContent(const std::string &metadataType) -{ - auto path = getMetadataPath(metadataType); - if (path.empty()) return ""; - - auto mdfile = File::newFile(path); - mdfile->open("r"); - const auto &content = mdfile->getContent(); - mdfile->close(); - return content; -} - -std::unique_ptr Repo::Impl::lrHandleInitBase() -{ - std::unique_ptr h(lr_handle_init()); - std::vector dlist = {MD_TYPE_PRIMARY, MD_TYPE_FILELISTS, MD_TYPE_PRESTODELTA, - MD_TYPE_GROUP_GZ, MD_TYPE_UPDATEINFO}; - -#ifdef MODULEMD - dlist.push_back(MD_TYPE_MODULES); -#endif - if (loadMetadataOther) { - dlist.push_back(MD_TYPE_OTHER); - } - for (auto &item : additionalMetadata) { - dlist.push_back(item.c_str()); - } - dlist.push_back(NULL); - handleSetOpt(h.get(), LRO_PRESERVETIME, static_cast(preserveRemoteTime)); - handleSetOpt(h.get(), LRO_REPOTYPE, LR_YUMREPO); - handleSetOpt(h.get(), LRO_USERAGENT, conf->user_agent().getValue().c_str()); - handleSetOpt(h.get(), LRO_YUMDLIST, dlist.data()); - handleSetOpt(h.get(), LRO_INTERRUPTIBLE, 1L); - handleSetOpt(h.get(), LRO_GPGCHECK, conf->repo_gpgcheck().getValue()); - handleSetOpt(h.get(), LRO_MAXMIRRORTRIES, static_cast(maxMirrorTries)); - handleSetOpt(h.get(), LRO_MAXPARALLELDOWNLOADS, - conf->max_parallel_downloads().getValue()); - - LrUrlVars * vars = NULL; - vars = lr_urlvars_set(vars, MD_TYPE_GROUP_GZ, MD_TYPE_GROUP); - handleSetOpt(h.get(), LRO_YUMSLIST, vars); - - return h; -} - -std::unique_ptr Repo::Impl::lrHandleInitLocal() -{ - std::unique_ptr h(lrHandleInitBase()); - - LrUrlVars * vars = NULL; - for (const auto & item : substitutions) - vars = lr_urlvars_set(vars, item.first.c_str(), item.second.c_str()); - handleSetOpt(h.get(), LRO_VARSUB, vars); - auto cachedir = getCachedir(); - handleSetOpt(h.get(), LRO_DESTDIR, cachedir.c_str()); - const char *urls[] = {cachedir.c_str(), NULL}; - handleSetOpt(h.get(), LRO_URLS, urls); - handleSetOpt(h.get(), LRO_LOCAL, 1L); -#ifdef LRO_SUPPORTS_CACHEDIR - /* If zchunk is enabled, set librepo cache dir */ - if (conf->getMasterConfig().zchunk().getValue()) - handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().getValue().c_str()); -#endif - return h; -} - -std::unique_ptr Repo::Impl::lrHandleInitRemote(const char *destdir, bool mirrorSetup) -{ - std::unique_ptr h(lrHandleInitBase()); - handleSetOpt(h.get(), LRO_HTTPHEADER, httpHeaders.get()); - - LrUrlVars * vars = NULL; - for (const auto & item : substitutions) - vars = lr_urlvars_set(vars, item.first.c_str(), item.second.c_str()); - handleSetOpt(h.get(), LRO_VARSUB, vars); - - handleSetOpt(h.get(), LRO_DESTDIR, destdir); - - auto & ipResolve = conf->ip_resolve().getValue(); - if (ipResolve == "ipv4") - handleSetOpt(h.get(), LRO_IPRESOLVE, LR_IPRESOLVE_V4); - else if (ipResolve == "ipv6") - handleSetOpt(h.get(), LRO_IPRESOLVE, LR_IPRESOLVE_V6); - - enum class Source {NONE, METALINK, MIRRORLIST} source{Source::NONE}; - std::string tmp; - if (!conf->metalink().empty() && !(tmp=conf->metalink().getValue()).empty()) - source = Source::METALINK; - else if (!conf->mirrorlist().empty() && !(tmp=conf->mirrorlist().getValue()).empty()) - source = Source::MIRRORLIST; - if (source != Source::NONE) { - handleSetOpt(h.get(), LRO_HMFCB, static_cast(mirrorFailureCB)); - handleSetOpt(h.get(), LRO_PROGRESSDATA, callbacks.get()); - if (mirrorSetup) { - if (source == Source::METALINK) - handleSetOpt(h.get(), LRO_METALINKURL, tmp.c_str()); - else { - handleSetOpt(h.get(), LRO_MIRRORLISTURL, tmp.c_str()); - // YUM-DNF compatibility hack. YUM guessed by content of keyword "metalink" if - // mirrorlist is really mirrorlist or metalink) - if (tmp.find("metalink") != tmp.npos) - handleSetOpt(h.get(), LRO_METALINKURL, tmp.c_str()); - } - handleSetOpt(h.get(), LRO_FASTESTMIRROR, conf->fastestmirror().getValue() ? 1L : 0L); - auto fastestMirrorCacheDir = conf->basecachedir().getValue(); - if (fastestMirrorCacheDir.back() != '/') - fastestMirrorCacheDir.push_back('/'); - fastestMirrorCacheDir += "fastestmirror.cache"; - handleSetOpt(h.get(), LRO_FASTESTMIRRORCACHE, fastestMirrorCacheDir.c_str()); - } else { - // use already resolved mirror list - handleSetOpt(h.get(), LRO_URLS, mirrors); - } - } else if (!conf->baseurl().getValue().empty()) { - handleSetOpt(h.get(), LRO_HMFCB, static_cast(mirrorFailureCB)); - size_t len = conf->baseurl().getValue().size(); - const char * urls[len + 1]; - for (size_t idx = 0; idx < len; ++idx) - urls[idx] = conf->baseurl().getValue()[idx].c_str(); - urls[len] = nullptr; - handleSetOpt(h.get(), LRO_URLS, urls); - } else - throw std::runtime_error(tfm::format(_("Cannot find a valid baseurl for repo: %s"), id)); - - // setup username/password if needed - auto userpwd = conf->username().getValue(); - if (!userpwd.empty()) { - // TODO Use URL encoded form, needs support in librepo - userpwd = formatUserPassString(userpwd, conf->password().getValue(), false); - handleSetOpt(h.get(), LRO_USERPWD, userpwd.c_str()); - } - - // setup ssl stuff - if (!conf->sslcacert().getValue().empty()) - handleSetOpt(h.get(), LRO_SSLCACERT, conf->sslcacert().getValue().c_str()); - if (!conf->sslclientcert().getValue().empty()) - handleSetOpt(h.get(), LRO_SSLCLIENTCERT, conf->sslclientcert().getValue().c_str()); - if (!conf->sslclientkey().getValue().empty()) - handleSetOpt(h.get(), LRO_SSLCLIENTKEY, conf->sslclientkey().getValue().c_str()); - - handleSetOpt(h.get(), LRO_PROGRESSCB, static_cast(progressCB)); - handleSetOpt(h.get(), LRO_PROGRESSDATA, callbacks.get()); - handleSetOpt(h.get(), LRO_FASTESTMIRRORCB, static_cast(fastestMirrorCB)); - handleSetOpt(h.get(), LRO_FASTESTMIRRORDATA, callbacks.get()); - -#ifdef LRO_SUPPORTS_CACHEDIR - /* If zchunk is enabled, set librepo cache dir */ - if (conf->getMasterConfig().zchunk().getValue()) - handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().getValue().c_str()); -#endif - - auto minrate = conf->minrate().getValue(); - handleSetOpt(h.get(), LRO_LOWSPEEDLIMIT, static_cast(minrate)); - - auto maxspeed = conf->throttle().getValue(); - if (maxspeed > 0 && maxspeed <= 1) - maxspeed *= conf->bandwidth().getValue(); - if (maxspeed != 0 && maxspeed < minrate) - throw std::runtime_error(_("Maximum download speed is lower than minimum. " - "Please change configuration of minrate or throttle")); - handleSetOpt(h.get(), LRO_MAXSPEED, static_cast(maxspeed)); - - long timeout = conf->timeout().getValue(); - if (timeout > 0) { - handleSetOpt(h.get(), LRO_CONNECTTIMEOUT, timeout); - handleSetOpt(h.get(), LRO_LOWSPEEDTIME, timeout); - } else { - handleSetOpt(h.get(), LRO_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT); - handleSetOpt(h.get(), LRO_LOWSPEEDTIME, LRO_LOWSPEEDTIME_DEFAULT); - } - - if (!conf->proxy().empty() && !conf->proxy().getValue().empty()) - handleSetOpt(h.get(), LRO_PROXY, conf->proxy().getValue().c_str()); - - //set proxy authorization method - auto proxyAuthMethodStr = conf->proxy_auth_method().getValue(); - auto proxyAuthMethod = LR_AUTH_ANY; - for (auto & auth : PROXYAUTHMETHODS) { - if (proxyAuthMethodStr == auth.name) { - proxyAuthMethod = auth.code; - break; - } - } - handleSetOpt(h.get(), LRO_PROXYAUTHMETHODS, static_cast(proxyAuthMethod)); - - if (!conf->proxy_username().empty()) { - userpwd = conf->proxy_username().getValue(); - if (!userpwd.empty()) { - userpwd = formatUserPassString(userpwd, conf->proxy_password().getValue(), true); - handleSetOpt(h.get(), LRO_PROXYUSERPWD, userpwd.c_str()); - } - } - - auto sslverify = conf->sslverify().getValue() ? 1L : 0L; - handleSetOpt(h.get(), LRO_SSLVERIFYHOST, sslverify); - handleSetOpt(h.get(), LRO_SSLVERIFYPEER, sslverify); - - return h; -} - -static void gpgImportKey(gpgme_ctx_t context, int keyFd) -{ - auto logger(Log::getLogger()); - gpg_error_t gpgErr; - gpgme_data_t keyData; - - gpgErr = gpgme_data_new_from_fd(&keyData, keyFd); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_data_new_from_fd(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - - gpgErr = gpgme_op_import(context, keyData); - gpgme_data_release(keyData); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_op_import(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } -} - -static void gpgImportKey(gpgme_ctx_t context, std::vector key) -{ - auto logger(Log::getLogger()); - gpg_error_t gpgErr; - gpgme_data_t keyData; - - gpgErr = gpgme_data_new_from_mem(&keyData, key.data(), key.size(), 0); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_data_new_from_fd(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - - gpgErr = gpgme_op_import(context, keyData); - gpgme_data_release(keyData); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_op_import(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } -} - -static std::vector rawkey2infos(int fd) { - auto logger(Log::getLogger()); - gpg_error_t gpgErr; - - std::vector keyInfos; - gpgme_ctx_t ctx; - gpgme_new(&ctx); - std::unique_ptr::type> context(ctx); - - // set GPG home dir - char tmpdir[] = "/tmp/tmpdir.XXXXXX"; - mkdtemp(tmpdir); - Finalizer tmpDirRemover([&tmpdir](){ - dnf_remove_recursive(tmpdir, NULL); - }); - gpgErr = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, NULL, tmpdir); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - - gpgImportKey(ctx, fd); - - gpgme_key_t key; - gpgErr = gpgme_op_keylist_start(ctx, NULL, 0); - while (!gpgErr) - { - gpgErr = gpgme_op_keylist_next(ctx, &key); - if (gpgErr) - break; - - // _extract_signing_subkey - auto subkey = key->subkeys; - while (subkey && !key->subkeys->can_sign) { - subkey = subkey->next; - } - if (subkey) - keyInfos.emplace_back(key, subkey); - gpgme_key_release(key); - } - if (gpg_err_code(gpgErr) != GPG_ERR_EOF) - { - gpgme_op_keylist_end(ctx); - auto msg = tfm::format(_("can not list keys: %s"), gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - gpgme_set_armor(ctx, 1); - for (auto & keyInfo : keyInfos) { - gpgme_data_t sink; - gpgme_data_new(&sink); - gpgme_op_export(ctx, keyInfo.getId().c_str(), 0, sink); - gpgme_data_rewind(sink); - - char buf[4096]; - ssize_t readed; - do { - readed = gpgme_data_read(sink, buf, sizeof(buf)); - if (readed > 0) - keyInfo.rawKey.insert(keyInfo.rawKey.end(), buf, buf + sizeof(buf)); - } while (readed == sizeof(buf)); - } - return keyInfos; -} - -static std::vector keyidsFromPubring(const std::string & gpgDir) -{ - auto logger(Log::getLogger()); - gpg_error_t gpgErr; - - std::vector keyids; - - struct stat sb; - if (stat(gpgDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { - - gpgme_ctx_t ctx; - gpgme_new(&ctx); - std::unique_ptr::type> context(ctx); - - // set GPG home dir - gpgErr = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, NULL, gpgDir.c_str()); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - - gpgme_key_t key; - gpgErr = gpgme_op_keylist_start(ctx, NULL, 0); - while (!gpgErr) - { - gpgErr = gpgme_op_keylist_next(ctx, &key); - if (gpgErr) - break; - - // _extract_signing_subkey - auto subkey = key->subkeys; - while (subkey && !key->subkeys->can_sign) { - subkey = subkey->next; - } - if (subkey) - keyids.push_back(subkey->keyid); - gpgme_key_release(key); - } - if (gpg_err_code(gpgErr) != GPG_ERR_EOF) - { - gpgme_op_keylist_end(ctx); - auto msg = tfm::format(_("can not list keys: %s"), gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - } - return keyids; -} - -// download key from URL -std::vector Repo::Impl::retrieve(const std::string & url) -{ - auto logger(Log::getLogger()); - char tmpKeyFile[] = "/tmp/repokey.XXXXXX"; - auto fd = mkstemp(tmpKeyFile); - if (fd == -1) { - auto msg = tfm::format("Error creating temporary file \"%s\": %s", - tmpKeyFile, std::system_category().message(errno)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - unlink(tmpKeyFile); - Finalizer tmpFileCloser([fd](){ - close(fd); - }); - - try { - downloadUrl(url.c_str(), fd); - } - catch (const LrExceptionWithSourceUrl & e) { - auto msg = tfm::format(_("Failed to retrieve GPG key for repo '%s': %s"), id, e.what()); - throw std::runtime_error(msg); - } - lseek(fd, SEEK_SET, 0); - auto keyInfos = rawkey2infos(fd); - for (auto & key : keyInfos) - key.url = url; - return keyInfos; -} - -/* - * Creates the '/run/user/$UID' directory if it doesn't exist. If this - * directory exists, gpgagent will create its sockets under - * '/run/user/$UID/gnupg'. - * - * If this directory doesn't exist, gpgagent will create its sockets in gpg - * home directory, which is under '/var/cache/yum/metadata/' and this was - * causing trouble with container images, see [1]. - * - * Previous solution was to send the agent a "KILLAGENT" message, but that - * would cause a race condition with calling gpgme_release(), see [2], [3], - * [4]. - * - * Since the agent doesn't clean up its sockets properly, by creating this - * directory we make sure they are in a place that is not causing trouble with - * container images. - * - * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1650266 - * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1769831 - * [3] https://github.com/rpm-software-management/microdnf/issues/50 - * [4] https://bugzilla.redhat.com/show_bug.cgi?id=1781601 - */ -static void ensure_socket_dir_exists() { - auto logger(Log::getLogger()); - std::string dirname = "/run/user/" + std::to_string(getuid()); - int res = mkdir(dirname.c_str(), 0700); - if (res != 0 && errno != EEXIST) { - logger->debug(tfm::format("Failed to create directory \"%s\": %d - %s", - dirname, errno, strerror(errno))); - } -} - -void Repo::Impl::importRepoKeys() -{ - auto logger(Log::getLogger()); - - auto gpgDir = getCachedir() + "/pubring"; - auto knownKeys = keyidsFromPubring(gpgDir); - ensure_socket_dir_exists(); - for (const auto & gpgkeyUrl : conf->gpgkey().getValue()) { - auto keyInfos = retrieve(gpgkeyUrl); - for (auto & keyInfo : keyInfos) { - if (std::find(knownKeys.begin(), knownKeys.end(), keyInfo.getId()) != knownKeys.end()) { - logger->debug(tfm::format(_("repo %s: 0x%s already imported"), id, keyInfo.getId())); - continue; - } - - if (callbacks) { - if (!callbacks->repokeyImport(keyInfo.getId(), keyInfo.getUserId(), keyInfo.getFingerprint(), - keyInfo.url, keyInfo.getTimestamp())) - continue; - } - - struct stat sb; - if (stat(gpgDir.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) - mkdir(gpgDir.c_str(), 0777); - - gpgme_ctx_t ctx; - gpgme_new(&ctx); - std::unique_ptr::type> context(ctx); - - // set GPG home dir - auto gpgErr = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, NULL, gpgDir.c_str()); - if (gpgErr != GPG_ERR_NO_ERROR) { - auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpgErr)); - logger->debug(msg); - throw LrException(LRE_GPGERROR, msg); - } - - gpgImportKey(ctx, keyInfo.rawKey); - - logger->debug(tfm::format(_("repo %s: imported key 0x%s."), id, keyInfo.getId())); - } - - } -} - -std::unique_ptr Repo::Impl::lrHandlePerform(LrHandle * handle, const std::string & destDirectory, - bool setGPGHomeDir) -{ - if (setGPGHomeDir) { - auto pubringdir = getCachedir() + "/pubring"; - handleSetOpt(handle, LRO_GNUPGHOMEDIR, pubringdir.c_str()); - } - - // Start and end is called only if progress callback is set in handle. - LrProgressCb progressFunc; - handleGetInfo(handle, LRI_PROGRESSCB, &progressFunc); - - addCountmeFlag(handle); - - std::unique_ptr result; - bool ret; - bool badGPG = false; - do { - if (callbacks && progressFunc) - callbacks->start( - !conf->name().getValue().empty() ? conf->name().getValue().c_str() : - (!id.empty() ? id.c_str() : "unknown") - ); - - GError * errP{nullptr}; - result.reset(lr_result_init()); - ret = lr_handle_perform(handle, result.get(), &errP); - std::unique_ptr err(errP); - - if (callbacks && progressFunc) - callbacks->end(); - - if (ret || badGPG || errP->code != LRE_BADGPG) { - if (!ret) { - std::string source; - if (conf->metalink().empty() || (source=conf->metalink().getValue()).empty()) { - if (conf->mirrorlist().empty() || (source=conf->mirrorlist().getValue()).empty()) { - bool first = true; - for (const auto & url : conf->baseurl().getValue()) { - if (first) - first = false; - else - source += ", "; - source += url; - } - } - } - throw LrExceptionWithSourceUrl(err->code, err->message, source); - } - break; - } - badGPG = true; - importRepoKeys(); - dnf_remove_recursive((destDirectory + "/" + METADATA_RELATIVE_DIR).c_str(), NULL); - } while (true); - - return result; -} - -bool Repo::Impl::loadCache(bool throwExcept) -{ - std::unique_ptr h(lrHandleInitLocal()); - std::unique_ptr r; - - // Fetch data - try { - r = lrHandlePerform(h.get(), getCachedir(), conf->repo_gpgcheck().getValue()); - } catch (std::exception & ex) { - if (throwExcept) - throw; - return false; - } - - char **mirrors; - LrYumRepo *yum_repo; - LrYumRepoMd *yum_repomd; - handleGetInfo(h.get(), LRI_MIRRORS, &mirrors); - resultGetInfo(r.get(), LRR_YUM_REPO, &yum_repo); - resultGetInfo(r.get(), LRR_YUM_REPOMD, &yum_repomd); - - // Populate repo - repomdFn = yum_repo->repomd; - metadataPaths.clear(); - for (auto *elem = yum_repo->paths; elem; elem = g_slist_next(elem)) { - if (elem->data) { - auto yumrepopath = static_cast(elem->data); - metadataPaths.emplace(yumrepopath->type, yumrepopath->path); - } - } - - content_tags.clear(); - for (auto elem = yum_repomd->content_tags; elem; elem = g_slist_next(elem)) { - if (elem->data) - content_tags.emplace_back(static_cast(elem->data)); - } - - distro_tags.clear(); - for (auto elem = yum_repomd->distro_tags; elem; elem = g_slist_next(elem)) { - if (elem->data) { - auto distroTag = static_cast(elem->data); - if (distroTag->tag) - distro_tags.emplace_back(distroTag->cpeid, distroTag->tag); - } - } - - metadata_locations.clear(); - for (auto elem = yum_repomd->records; elem; elem = g_slist_next(elem)) { - if (elem->data) { - auto rec = static_cast(elem->data); - metadata_locations.emplace_back(rec->type, rec->location_href); - } - } - - if (auto cRevision = yum_repomd->revision) { - revision = cRevision; - } - maxTimestamp = lr_yum_repomd_get_highest_timestamp(yum_repomd, NULL); - - // Load timestamp unless explicitly expired - if (timestamp != 0) { - timestamp = mtime(getMetadataPath(MD_TYPE_PRIMARY).c_str()); - } - g_strfreev(this->mirrors); - this->mirrors = mirrors; - return true; -} - -void Repo::Impl::addCountmeFlag(LrHandle *handle) { - /* - * The countme flag will be added once (and only once) in every position of - * a sliding time window (COUNTME_WINDOW) that starts at COUNTME_OFFSET and - * moves along the time axis, by one length at a time, in such a way that - * the current point in time always stays within: - * - * UNIX epoch now - * | | - * |---*-----|-----|-----|-----[-*---]---> time - * | ~~~~~~~ - * COUNTME_OFFSET COUNTME_WINDOW - * - * This is to align the time window with an absolute point in time rather - * than the last counting event (which could facilitate tracking across - * multiple such events). - */ - auto logger(Log::getLogger()); - - // Bail out if not counting or not running as root (since the persistdir is - // only root-writable) - if (!conf->countme().getValue() || getuid() != 0) - return; - - // Bail out if not a remote handle - long local; - handleGetInfo(handle, LRI_LOCAL, &local); - if (local) - return; - - // Bail out if no metalink or mirrorlist is defined - auto & metalink = conf->metalink(); - auto & mirrorlist = conf->mirrorlist(); - if ((metalink.empty() || metalink.getValue().empty()) && - (mirrorlist.empty() || mirrorlist.getValue().empty())) - return; - - // Load the cookie - std::string fname = getPersistdir() + "/" + COUNTME_COOKIE; - int ver = COUNTME_VERSION; // file format version (for future use) - time_t epoch = 0; // position of first-ever counted window - time_t win = COUNTME_OFFSET; // position of last counted window - int budget = -1; // budget for this window (-1 = generate) - std::ifstream(fname) >> ver >> epoch >> win >> budget; - - // Bail out if the window has not advanced since - time_t now = time(NULL); - time_t delta = now - win; - if (delta < COUNTME_WINDOW) { - logger->debug(tfm::format("countme: no event for %s: window already counted", id)); - return; - } - - // Evenly distribute the probability of the counting event over the first N - // requests in this window (where N = COUNTME_BUDGET), by defining a random - // "budget" of ordinary requests that we first have to spend. This ensures - // that no particular request is special and thus no privacy loss is - // incurred by adding the flag within N requests. - if (budget < 0) - budget = numeric::random(1, COUNTME_BUDGET); - budget--; - if (!budget) { - // Budget exhausted, counting! - - // Compute the position of this window - win = now - (delta % COUNTME_WINDOW); - if (!epoch) - epoch = win; - // Window step (0 at epoch) - int step = (win - epoch) / COUNTME_WINDOW; - - // Compute the bucket we are in - unsigned int i; - for (i = 0; i < COUNTME_BUCKETS.size(); ++i) - if (step < COUNTME_BUCKETS[i]) - break; - int bucket = i + 1; // Buckets are indexed from 1 - - // Set the flag - std::string flag = "countme=" + std::to_string(bucket); - handleSetOpt(handle, LRO_ONETIMEFLAG, flag.c_str()); - logger->debug(tfm::format("countme: event triggered for %s: bucket %i", id, bucket)); - - // Request a new budget - budget = -1; - } else { - logger->debug(tfm::format("countme: no event for %s: budget to spend: %i", id, budget)); - } - - // Save the cookie - std::ofstream(fname) << COUNTME_VERSION << " " << epoch << " " << win - << " " << budget; -} - -// Use metalink to check whether our metadata are still current. -bool Repo::Impl::isMetalinkInSync() -{ - auto logger(Log::getLogger()); - char tmpdir[] = "/tmp/tmpdir.XXXXXX"; - mkdtemp(tmpdir); - Finalizer tmpDirRemover([&tmpdir](){ - dnf_remove_recursive(tmpdir, NULL); - }); - - std::unique_ptr h(lrHandleInitRemote(tmpdir)); - - handleSetOpt(h.get(), LRO_FETCHMIRRORS, 1L); - auto r = lrHandlePerform(h.get(), tmpdir, false); - LrMetalink * metalink; - handleGetInfo(h.get(), LRI_METALINK, &metalink); - if (!metalink) { - logger->debug(tfm::format(_("reviving: repo '%s' skipped, no metalink."), id)); - return false; - } - - // check all recognized hashes - auto chksumFree = [](Chksum * ptr){solv_chksum_free(ptr, nullptr);}; - struct hashInfo { - const LrMetalinkHash * lrMetalinkHash; - std::unique_ptr chksum; - }; - std::vector hashes; - for (auto hash = metalink->hashes; hash; hash = hash->next) { - auto lrMetalinkHash = static_cast(hash->data); - for (auto algorithm : RECOGNIZED_CHKSUMS) { - if (strcmp(lrMetalinkHash->type, algorithm) == 0) - hashes.push_back({lrMetalinkHash, {nullptr, chksumFree}}); - } - } - if (hashes.empty()) { - logger->debug(tfm::format(_("reviving: repo '%s' skipped, no usable hash."), id)); - return false; - } - - for (auto & hash : hashes) { - auto chkType = solv_chksum_str2type(hash.lrMetalinkHash->type); - hash.chksum.reset(solv_chksum_create(chkType)); - } - - std::ifstream repomd(repomdFn, std::ifstream::binary); - char buf[4096]; - int readed; - while ((readed = repomd.readsome(buf, sizeof(buf))) > 0) { - for (auto & hash : hashes) - solv_chksum_add(hash.chksum.get(), buf, readed); - } - - for (auto & hash : hashes) { - int chksumLen; - auto chksum = solv_chksum_get(hash.chksum.get(), &chksumLen); - char chksumHex[chksumLen * 2 + 1]; - solv_bin2hex(chksum, chksumLen, chksumHex); - if (strcmp(chksumHex, hash.lrMetalinkHash->value) != 0) { - logger->debug(tfm::format(_("reviving: failed for '%s', mismatched %s sum."), - id, hash.lrMetalinkHash->type)); - return false; - } - } - - logger->debug(tfm::format(_("reviving: '%s' can be revived - metalink checksums match."), id)); - return true; -} - -// Use repomd to check whether our metadata are still current. -bool Repo::Impl::isRepomdInSync() -{ - auto logger(Log::getLogger()); - LrYumRepo *yum_repo; - char tmpdir[] = "/tmp/tmpdir.XXXXXX"; - mkdtemp(tmpdir); - Finalizer tmpDirRemover([&tmpdir](){ - dnf_remove_recursive(tmpdir, NULL); - }); - - const char *dlist[] = LR_YUM_REPOMDONLY; - - std::unique_ptr h(lrHandleInitRemote(tmpdir)); - - handleSetOpt(h.get(), LRO_YUMDLIST, dlist); - auto r = lrHandlePerform(h.get(), tmpdir, conf->repo_gpgcheck().getValue()); - resultGetInfo(r.get(), LRR_YUM_REPO, &yum_repo); - - auto same = haveFilesSameContent(repomdFn.c_str(), yum_repo->repomd); - if (same) - logger->debug(tfm::format(_("reviving: '%s' can be revived - repomd matches."), id)); - else - logger->debug(tfm::format(_("reviving: failed for '%s', mismatched repomd."), id)); - return same; -} - -bool Repo::Impl::isInSync() -{ - if (!conf->metalink().empty() && !conf->metalink().getValue().empty()) - return isMetalinkInSync(); - return isRepomdInSync(); -} - - - -void Repo::Impl::fetch(const std::string & destdir, std::unique_ptr && h) -{ - auto repodir = destdir + "/" + METADATA_RELATIVE_DIR; - if (g_mkdir_with_parents(destdir.c_str(), 0755) == -1) { - const char * errTxt = strerror(errno); - throw std::runtime_error(tfm::format(_("Cannot create repo destination directory \"%s\": %s"), - destdir, errTxt)); - } - auto tmpdir = destdir + "/tmpdir.XXXXXX"; - if (!mkdtemp(&tmpdir.front())) { - const char * errTxt = strerror(errno); - throw std::runtime_error(tfm::format(_("Cannot create repo temporary directory \"%s\": %s"), - tmpdir.c_str(), errTxt)); - } - Finalizer tmpDirRemover([&tmpdir](){ - dnf_remove_recursive(tmpdir.c_str(), NULL); - }); - auto tmprepodir = tmpdir + "/" + METADATA_RELATIVE_DIR; - - handleSetOpt(h.get(), LRO_DESTDIR, tmpdir.c_str()); - auto r = lrHandlePerform(h.get(), tmpdir, conf->repo_gpgcheck().getValue()); - - dnf_remove_recursive(repodir.c_str(), NULL); - if (g_mkdir_with_parents(repodir.c_str(), 0755) == -1) { - const char * errTxt = strerror(errno); - throw std::runtime_error(tfm::format(_("Cannot create directory \"%s\": %s"), - repodir, errTxt)); - } - // move all downloaded object from tmpdir to destdir - if (auto * dir = opendir(tmpdir.c_str())) { - Finalizer tmpDirRemover([dir](){ closedir(dir); }); - while (auto ent = readdir(dir)) { - auto elName = ent->d_name; - if (elName[0] == '.' && (elName[1] == '\0' || (elName[1] == '.' && elName[2] == '\0'))) { - continue; - } - auto targetElement = destdir + "/" + elName; - if (filesystem::exists(targetElement)) { - if (filesystem::isDIR(targetElement.c_str())) { - dnf_remove_recursive(targetElement.c_str(), NULL); - } else { - dnf_ensure_file_unlinked(targetElement.c_str(), NULL); - } - } - auto tempElement = tmpdir + "/" + elName; - GError * error = NULL; - if (!dnf_move_recursive(tempElement.c_str(), targetElement.c_str(), &error)) { - std::string errTxt = tfm::format( - _("Cannot rename directory \"%s\" to \"%s\": %s"), - tempElement, targetElement, error->message); - g_error_free(error); - throw std::runtime_error(errTxt); - } - } - } -} - -void Repo::Impl::downloadMetadata(const std::string & destdir) -{ - std::unique_ptr h(lrHandleInitRemote(nullptr)); - handleSetOpt(h.get(), LRO_YUMDLIST, LR_RPMMD_FULL); - fetch(destdir, std::move(h)); -} - -bool Repo::Impl::load() -{ - auto logger(Log::getLogger()); - try { - if (!getMetadataPath(MD_TYPE_PRIMARY).empty() || loadCache(false)) { - resetMetadataExpired(); - if (!expired || syncStrategy == SyncStrategy::ONLY_CACHE || syncStrategy == SyncStrategy::LAZY) { - logger->debug(tfm::format(_("repo: using cache for: %s"), id)); - return false; - } - - if (isInSync()) { - // the expired metadata still reflect the origin: - utimes(getMetadataPath(MD_TYPE_PRIMARY).c_str(), NULL); - expired = false; - return true; - } - } - if (syncStrategy == SyncStrategy::ONLY_CACHE) { - auto msg = tfm::format(_("Cache-only enabled but no cache for '%s'"), id); - throw std::runtime_error(msg); - } - - logger->debug(tfm::format(_("repo: downloading from remote: %s"), id)); - const auto cacheDir = getCachedir(); - fetch(cacheDir, lrHandleInitRemote(nullptr)); - timestamp = -1; - loadCache(true); - } catch (const LrExceptionWithSourceUrl & e) { - auto msg = tfm::format(_("Failed to download metadata for repo '%s': %s"), id, e.what()); - throw std::runtime_error(msg); - } - expired = false; - return true; -} - -std::string Repo::Impl::getHash() const -{ - std::string tmp; - if (conf->metalink().empty() || (tmp=conf->metalink().getValue()).empty()) { - if (conf->mirrorlist().empty() || (tmp=conf->mirrorlist().getValue()).empty()) { - if (!conf->baseurl().getValue().empty()) - tmp = conf->baseurl().getValue()[0]; - if (tmp.empty()) - tmp = id; - } - } - - auto chksumObj = solv_chksum_create(REPOKEY_TYPE_SHA256); - solv_chksum_add(chksumObj, tmp.c_str(), tmp.length()); - int chksumLen; - auto chksum = solv_chksum_get(chksumObj, &chksumLen); - static constexpr int USE_CHECKSUM_BYTES = 8; - if (chksumLen < USE_CHECKSUM_BYTES) { - solv_chksum_free(chksumObj, nullptr); - throw std::runtime_error(_("getCachedir(): Computation of SHA256 failed")); - } - char chksumCStr[USE_CHECKSUM_BYTES * 2 + 1]; - solv_bin2hex(chksum, USE_CHECKSUM_BYTES, chksumCStr); - solv_chksum_free(chksumObj, nullptr); - - return id + "-" + chksumCStr; -} - -std::string Repo::Impl::getCachedir() const -{ - auto repodir(conf->basecachedir().getValue()); - if (repodir.back() != '/') - repodir.push_back('/'); - return repodir + getHash(); -} - -std::string Repo::Impl::getPersistdir() const -{ - auto persdir(conf->getMasterConfig().persistdir().getValue()); - if (persdir.back() != '/') - persdir.push_back('/'); - std::string result = persdir + "repos/" + getHash(); - if (g_mkdir_with_parents(result.c_str(), 0755) == -1) { - const char * errTxt = strerror(errno); - throw std::runtime_error(tfm::format(_("Cannot create persistdir \"%s\": %s"), - result, errTxt)); - } - return result; -} - -int Repo::Impl::getAge() const -{ - return time(NULL) - mtime(getMetadataPath(MD_TYPE_PRIMARY).c_str()); -} - -void Repo::Impl::expire() -{ - expired = true; - timestamp = 0; -} - -bool Repo::Impl::isExpired() const -{ - if (expired) - // explicitly requested expired state - return true; - if (conf->metadata_expire().getValue() == -1) - return false; - return getAge() > conf->metadata_expire().getValue(); -} - -int Repo::Impl::getExpiresIn() const -{ - return conf->metadata_expire().getValue() - getAge(); -} - -void Repo::Impl::downloadUrl(const char * url, int fd) -{ - if (callbacks) - callbacks->start( - !conf->name().getValue().empty() ? conf->name().getValue().c_str() : - (!id.empty() ? id.c_str() : "unknown") - ); - - GError * errP{nullptr}; - lr_download_url(getCachedHandle(), url, fd, &errP); - std::unique_ptr err(errP); - - if (callbacks) - callbacks->end(); - - if (err) - throw LrExceptionWithSourceUrl(err->code, err->message, url); -} - -void Repo::Impl::setHttpHeaders(const char * headers[]) -{ - if (!headers) { - httpHeaders.reset(); - return; - } - size_t headersCount = 0; - while (headers[headersCount]) - ++headersCount; - httpHeaders.reset(new char*[headersCount + 1]{}); - for (size_t i = 0; i < headersCount; ++i) { - httpHeaders[i] = new char[strlen(headers[i]) + 1]; - strcpy(httpHeaders[i], headers[i]); - } -} - -const char * const * Repo::Impl::getHttpHeaders() const -{ - return httpHeaders.get(); -} - -bool Repo::fresh() -{ - return pImpl->timestamp >= 0; -} - -void Repo::Impl::resetMetadataExpired() -{ - if (expired || conf->metadata_expire().getValue() == -1) - return; - if (conf->getMasterConfig().check_config_file_age().getValue() && - !repoFilePath.empty() && - mtime(repoFilePath.c_str()) > mtime(getMetadataPath(MD_TYPE_PRIMARY).c_str())) - expired = true; - else - expired = getAge() > conf->metadata_expire().getValue(); -} - - -/* Returns a librepo handle, set as per the repo options - Note that destdir is None, and the handle is cached.*/ -LrHandle * Repo::Impl::getCachedHandle() -{ - if (!handle) - handle = lrHandleInitRemote(nullptr); - handleSetOpt(handle.get(), LRO_HTTPHEADER, httpHeaders.get()); - return handle.get(); -} - -void Repo::Impl::attachLibsolvRepo(LibsolvRepo * libsolvRepo) -{ - std::lock_guard guard(attachLibsolvMutex); - - if (this->libsolvRepo) - // A libsolvRepo was attached to this object before. Remove it's reference to this object. - this->libsolvRepo->appdata = nullptr; - else - // The libsolvRepo will reference this object. Increase reference counter. - ++nrefs; - - libsolvRepo->appdata = owner; // The libsolvRepo references back to us. - libsolvRepo->subpriority = -owner->getCost(); - libsolvRepo->priority = -owner->getPriority(); - this->libsolvRepo = libsolvRepo; -} - -void Repo::Impl::detachLibsolvRepo() -{ - attachLibsolvMutex.lock(); - if (!libsolvRepo) { - // Nothing to do, libsolvRepo is not attached. - attachLibsolvMutex.unlock(); - return; - } - - libsolvRepo->appdata = nullptr; // Removes reference to this object from libsolvRepo. - this->libsolvRepo = nullptr; - - if (--nrefs <= 0) { - // There is no reference to this object, we are going to destroy it. - // Mutex is part of this object, we must unlock it before destroying. - attachLibsolvMutex.unlock(); - delete owner; - } else - attachLibsolvMutex.unlock(); -} - -void Repo::setMaxMirrorTries(int maxMirrorTries) -{ - pImpl->maxMirrorTries = maxMirrorTries; -} - -int Repo::getTimestamp() const -{ - return pImpl->timestamp; -} - -int Repo::getMaxTimestamp() -{ - return pImpl->maxTimestamp; -} - -void Repo::setPreserveRemoteTime(bool preserveRemoteTime) -{ - pImpl->preserveRemoteTime = preserveRemoteTime; -} - -bool Repo::getPreserveRemoteTime() const -{ - return pImpl->preserveRemoteTime; -} - -const std::vector & Repo::getContentTags() -{ - return pImpl->content_tags; -} - -const std::vector> & Repo::getDistroTags() -{ - return pImpl->distro_tags; -} - -const std::vector> Repo::getMetadataLocations() const -{ - return pImpl->metadata_locations; -} - -const std::string & Repo::getRevision() const -{ - return pImpl->revision; -} - -std::string Repo::getCachedir() const -{ - return pImpl->getCachedir(); -} - -void Repo::setRepoFilePath(const std::string & path) -{ - pImpl->repoFilePath = path; -} - -const std::string & Repo::getRepoFilePath() const noexcept -{ - return pImpl->repoFilePath; -} - -void Repo::setSyncStrategy(SyncStrategy strategy) -{ - pImpl->syncStrategy = strategy; -} - -Repo::SyncStrategy Repo::getSyncStrategy() const noexcept -{ - return pImpl->syncStrategy; -} - -void Repo::downloadUrl(const char * url, int fd) -{ - pImpl->downloadUrl(url, fd); -} - -void Repo::setHttpHeaders(const char * headers[]) -{ - pImpl->setHttpHeaders(headers); -} - -const char * const * Repo::getHttpHeaders() const -{ - return pImpl->getHttpHeaders(); -} - -std::vector Repo::getMirrors() const -{ - std::vector mirrors; - if (pImpl->mirrors) { - for (auto mirror = pImpl->mirrors; *mirror; ++mirror) - mirrors.emplace_back(*mirror); - } - return mirrors; -} - -int PackageTargetCB::end(TransferStatus status, const char * msg) { return 0; } -int PackageTargetCB::progress(double totalToDownload, double downloaded) { return 0; } -int PackageTargetCB::mirrorFailure(const char *msg, const char *url) { return 0; } - -class PackageTarget::Impl { -public: - Impl(Repo * repo, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks); - - Impl(ConfigMain * cfg, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks, - const char * httpHeaders[]); - - void download(); - - ~Impl(); - - PackageTargetCB * callbacks; - - std::unique_ptr lrPkgTarget; - -private: - void init(LrHandle * handle, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd); - - static int endCB(void * data, LrTransferStatus status, const char * msg); - static int progressCB(void * data, double totalToDownload, double downloaded); - static int mirrorFailureCB(void * data, const char * msg, const char * url); - - std::unique_ptr lrHandle; - -}; - - -int PackageTarget::Impl::endCB(void * data, LrTransferStatus status, const char * msg) -{ - if (!data) - return 0; - auto cbObject = static_cast(data); - return cbObject->end(static_cast(status), msg); -} - -int PackageTarget::Impl::progressCB(void * data, double totalToDownload, double downloaded) -{ - if (!data) - return 0; - auto cbObject = static_cast(data); - return cbObject->progress(totalToDownload, downloaded); -} - -int PackageTarget::Impl::mirrorFailureCB(void * data, const char * msg, const char * url) -{ - if (!data) - return 0; - auto cbObject = static_cast(data); - return cbObject->mirrorFailure(msg, url); -} - - -static LrHandle * newHandle(ConfigMain * conf) -{ - LrHandle *h = lr_handle_init(); - const char * user_agent = USER_AGENT; - // see dnf.repo.Repo._handle_new_remote() how to pass - if (conf) { - user_agent = conf->user_agent().getValue().c_str(); - auto minrate = conf->minrate().getValue(); - handleSetOpt(h, LRO_LOWSPEEDLIMIT, static_cast(minrate)); - - auto maxspeed = conf->throttle().getValue(); - if (maxspeed > 0 && maxspeed <= 1) - maxspeed *= conf->bandwidth().getValue(); - if (maxspeed != 0 && maxspeed < minrate) - throw std::runtime_error(_("Maximum download speed is lower than minimum. " - "Please change configuration of minrate or throttle")); - handleSetOpt(h, LRO_MAXSPEED, static_cast(maxspeed)); - - if (!conf->proxy().empty() && !conf->proxy().getValue().empty()) - handleSetOpt(h, LRO_PROXY, conf->proxy().getValue().c_str()); - - //set proxy authorization method - auto proxyAuthMethodStr = conf->proxy_auth_method().getValue(); - auto proxyAuthMethod = LR_AUTH_ANY; - for (auto & auth : PROXYAUTHMETHODS) { - if (proxyAuthMethodStr == auth.name) { - proxyAuthMethod = auth.code; - break; - } - } - handleSetOpt(h, LRO_PROXYAUTHMETHODS, static_cast(proxyAuthMethod)); - - if (!conf->proxy_username().empty()) { - auto userpwd = conf->proxy_username().getValue(); - if (!userpwd.empty()) { - userpwd = formatUserPassString(userpwd, conf->proxy_password().getValue(), true); - handleSetOpt(h, LRO_PROXYUSERPWD, userpwd.c_str()); - } - } - - auto sslverify = conf->sslverify().getValue() ? 1L : 0L; - handleSetOpt(h, LRO_SSLVERIFYHOST, sslverify); - handleSetOpt(h, LRO_SSLVERIFYPEER, sslverify); - } - handleSetOpt(h, LRO_USERAGENT, user_agent); - return h; -} - -PackageTarget::ChecksumType PackageTarget::checksumType(const std::string & name) -{ - return static_cast(lr_checksum_type(name.c_str())); -} - -void PackageTarget::downloadPackages(std::vector & targets, bool failFast) -{ - // Convert vector to GSList - GSList * list{nullptr}; - for (auto it = targets.rbegin(); it != targets.rend(); ++it) - list = g_slist_prepend(list, (*it)->pImpl->lrPkgTarget.get()); - std::unique_ptr listGuard(list, &g_slist_free); - - LrPackageDownloadFlag flags = static_cast(0); - if (failFast) - flags = static_cast(flags | LR_PACKAGEDOWNLOAD_FAILFAST); - - GError * errP{nullptr}; - lr_download_packages(list, flags, &errP); - std::unique_ptr err(errP); - - if (err) - throwException(std::move(err)); -} - - -PackageTarget::Impl::~Impl() {} - -PackageTarget::Impl::Impl(Repo * repo, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks) -: callbacks(callbacks) -{ - init(repo->pImpl->getCachedHandle(), relativeUrl, dest, chksType, chksum, expectedSize, - baseUrl, resume, byteRangeStart, byteRangeEnd); -} - -PackageTarget::Impl::Impl(ConfigMain * cfg, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks, - const char * httpHeaders[]) -: callbacks(callbacks) -{ - lrHandle.reset(newHandle(cfg)); - handleSetOpt(lrHandle.get(), LRO_HTTPHEADER, httpHeaders); - handleSetOpt(lrHandle.get(), LRO_REPOTYPE, LR_YUMREPO); - init(lrHandle.get(), relativeUrl, dest, chksType, chksum, expectedSize, baseUrl, resume, - byteRangeStart, byteRangeEnd); -} - -void PackageTarget::Impl::init(LrHandle * handle, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd) -{ - LrChecksumType lrChksType = static_cast(chksType); - - if (resume && byteRangeStart) { - auto msg = _("resume cannot be used simultaneously with the byterangestart param"); - throw std::runtime_error(msg); - } - - GError * errP{nullptr}; - lrPkgTarget.reset(lr_packagetarget_new_v3(handle, relativeUrl, dest, lrChksType, chksum, expectedSize, - baseUrl, resume, progressCB, callbacks, endCB, mirrorFailureCB, - byteRangeStart, byteRangeEnd, &errP)); - std::unique_ptr err(errP); - - if (!lrPkgTarget) { - auto msg = tfm::format(_("PackageTarget initialization failed: %s"), err->message); - throw std::runtime_error(msg); - } -} - -PackageTarget::PackageTarget(Repo * repo, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks) -: pImpl(new Impl(repo, relativeUrl, dest, chksType, chksum, expectedSize, baseUrl, resume, - byteRangeStart, byteRangeEnd, callbacks)) -{} - -PackageTarget::PackageTarget(ConfigMain * cfg, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks, - const char * httpHeaders[]) -: pImpl(new Impl(cfg, relativeUrl, dest, chksType, chksum, expectedSize, baseUrl, resume, - byteRangeStart, byteRangeEnd, callbacks, httpHeaders)) -{} - - -PackageTarget::~PackageTarget() {} - -PackageTargetCB * PackageTarget::getCallbacks() -{ - return pImpl->callbacks; -} - -const char * PackageTarget::getErr() -{ - return pImpl->lrPkgTarget->err; -} - -void Downloader::downloadURL(ConfigMain * cfg, const char * url, int fd) -{ - std::unique_ptr lrHandle(newHandle(cfg)); - GError * errP{nullptr}; - lr_download_url(lrHandle.get(), url, fd, &errP); - std::unique_ptr err(errP); - - if (err) - throwException(std::move(err)); -} - -// ============ librepo logging =========== - -#define LR_LOGDOMAIN "librepo" - -class LrHandleLogData { -public: - std::string filePath; - long uid; - FILE *fd; - bool used{false}; - guint handlerId; - - ~LrHandleLogData(); -}; - -LrHandleLogData::~LrHandleLogData() -{ - if (used) - g_log_remove_handler(LR_LOGDOMAIN, handlerId); - fclose(fd); -} - -static std::list> lrLogDatas; -static std::mutex lrLogDatasMutex; - -static const char * lrLogLevelFlagToCStr(GLogLevelFlags logLevelFlag) -{ - if (logLevelFlag & G_LOG_LEVEL_ERROR) - return "ERROR"; - if (logLevelFlag & G_LOG_LEVEL_CRITICAL) - return "CRITICAL"; - if (logLevelFlag & G_LOG_LEVEL_WARNING) - return "WARNING"; - if (logLevelFlag & G_LOG_LEVEL_MESSAGE) - return "MESSAGE"; - if (logLevelFlag & G_LOG_LEVEL_INFO) - return "INFO"; - if (logLevelFlag & G_LOG_LEVEL_DEBUG) - return "DEBUG"; - return "USER"; -} - -static void librepoLogCB(G_GNUC_UNUSED const gchar *log_domain, GLogLevelFlags log_level, - const char *msg, gpointer user_data) noexcept -{ - // Ignore exception during logging. Eg. exception generated during logging of exception is not good. - try { - auto data = static_cast(user_data); - auto now = time(NULL); - struct tm nowTm; - gmtime_r(&now, &nowTm); - - std::ostringstream ss; - ss << std::setfill('0'); - ss << std::setw(4) << nowTm.tm_year+1900 << "-" << std::setw(2) << nowTm.tm_mon+1; - ss << "-" << std::setw(2) << nowTm.tm_mday; - ss << "T" << std::setw(2) << nowTm.tm_hour << ":" << std::setw(2) << nowTm.tm_min; - ss << ":" << std::setw(2) << nowTm.tm_sec << "Z "; - ss << lrLogLevelFlagToCStr(log_level) << " " << msg << std::endl; - auto str = ss.str(); - fwrite(str.c_str(), sizeof(char), str.length(), data->fd); - fflush(data->fd); - } catch (const std::exception &) { - } -} - -long LibrepoLog::addHandler(const std::string & filePath, bool debug) -{ - static long uid = 0; - - // Open the file - FILE *fd = fopen(filePath.c_str(), "a"); - if (!fd) - throw std::runtime_error(tfm::format(_("Cannot open %s: %s"), filePath, g_strerror(errno))); - - // Setup user data - std::unique_ptr data(new LrHandleLogData); - data->filePath = filePath; - data->fd = fd; - - // Set handler - GLogLevelFlags log_mask = debug ? G_LOG_LEVEL_MASK : static_cast( - G_LOG_LEVEL_INFO | - G_LOG_LEVEL_MESSAGE | - G_LOG_LEVEL_WARNING | - G_LOG_LEVEL_CRITICAL | - G_LOG_LEVEL_ERROR); - - data->handlerId = g_log_set_handler(LR_LOGDOMAIN, log_mask, librepoLogCB, data.get()); - data->used = true; - - // Save user data (in a thread safe way) - { - std::lock_guard guard(lrLogDatasMutex); - - // Get unique ID of the handler - data->uid = ++uid; - - // Append the data to the global list - lrLogDatas.push_front(std::move(data)); - } - - // Log librepo version and current time (including timezone) - lr_log_librepo_summary(); - - // Return unique id of the handler data - return uid; -} - -void LibrepoLog::removeHandler(long uid) -{ - std::lock_guard guard(lrLogDatasMutex); - - // Search for the corresponding LogFileData - auto it = lrLogDatas.begin(); - for (; it != lrLogDatas.end() && (*it)->uid != uid; ++it); - if (it == lrLogDatas.end()) - throw std::runtime_error(tfm::format(_("Log handler with id %ld doesn't exist"), uid)); - - // Remove the handler and free the data - lrLogDatas.erase(it); -} - -void LibrepoLog::removeAllHandlers() -{ - std::lock_guard guard(lrLogDatasMutex); - lrLogDatas.clear(); -} - -Repo::Impl * repoGetImpl(Repo * repo) -{ - return repo->pImpl.get(); -} - -} - -// hawkey -#include "../hy-repo-private.hpp" - -void -repo_internalize_all_trigger(Pool *pool) -{ - int i; - Repo *repo; - - FOR_REPOS(i, repo) - repo_internalize_trigger(repo); -} - -void -repo_internalize_trigger(Repo * repo) -{ - if (!repo) - return; - - if (auto hrepo = static_cast(repo->appdata)) { - // HyRepo is attached. The hint needs_internalizing will be used. - auto repoImpl = libdnf::repoGetImpl(hrepo); - assert(repoImpl->libsolvRepo == repo); - if (!repoImpl->needs_internalizing) - return; - repoImpl->needs_internalizing = false; - } - - repo_internalize(repo); -} - -void -repo_update_state(HyRepo repo, enum _hy_repo_repodata which, - enum _hy_repo_state state) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - assert(state <= _HY_WRITTEN); - switch (which) { - case _HY_REPODATA_FILENAMES: - repoImpl->state_filelists = state; - return; - case _HY_REPODATA_PRESTO: - repoImpl->state_presto = state; - return; - case _HY_REPODATA_UPDATEINFO: - repoImpl->state_updateinfo = state; - return; - case _HY_REPODATA_OTHER: - repoImpl->state_other = state; - return; - default: - assert(0); - } - return; -} - -Id -repo_get_repodata(HyRepo repo, enum _hy_repo_repodata which) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - switch (which) { - case _HY_REPODATA_FILENAMES: - return repoImpl->filenames_repodata; - case _HY_REPODATA_PRESTO: - return repoImpl->presto_repodata; - case _HY_REPODATA_UPDATEINFO: - return repoImpl->updateinfo_repodata; - case _HY_REPODATA_OTHER: - return repoImpl->other_repodata; - default: - assert(0); - return 0; - } -} - -void -repo_set_repodata(HyRepo repo, enum _hy_repo_repodata which, Id repodata) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - switch (which) { - case _HY_REPODATA_FILENAMES: - repoImpl->filenames_repodata = repodata; - return; - case _HY_REPODATA_PRESTO: - repoImpl->presto_repodata = repodata; - return; - case _HY_REPODATA_UPDATEINFO: - repoImpl->updateinfo_repodata = repodata; - return; - case _HY_REPODATA_OTHER: - repoImpl->other_repodata = repodata; - return; - default: - assert(0); - return; - } -} - -// public functions - -HyRepo -hy_repo_create(const char *name) -{ - assert(name); - auto & cfgMain = libdnf::getGlobalMainConfig(); - std::unique_ptr cfgRepo(new libdnf::ConfigRepo(cfgMain)); - auto repo = new libdnf::Repo(name, std::move(cfgRepo), libdnf::Repo::Type::COMMANDLINE); - auto repoImpl = libdnf::repoGetImpl(repo); - repoImpl->conf->name().set(libdnf::Option::Priority::RUNTIME, name); - return repo; -} - -int -hy_repo_get_cost(HyRepo repo) -{ - return repo->getCost(); -} - -int -hy_repo_get_priority(HyRepo repo) -{ - return repo->getPriority(); -} - -gboolean -hy_repo_get_use_includes(HyRepo repo) -{ - return repo->getUseIncludes(); -} - -guint -hy_repo_get_n_solvables(HyRepo repo) -{ - return (guint)libdnf::repoGetImpl(repo)->libsolvRepo->nsolvables; -} - -void -hy_repo_set_cost(HyRepo repo, int value) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - repoImpl->conf->cost().set(libdnf::Option::Priority::RUNTIME, value); - if (repoImpl->libsolvRepo) - repoImpl->libsolvRepo->subpriority = -value; -} - -void -hy_repo_set_priority(HyRepo repo, int value) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - repoImpl->conf->priority().set(libdnf::Option::Priority::RUNTIME, value); - if (repoImpl->libsolvRepo) - repoImpl->libsolvRepo->priority = -value; -} - -void -hy_repo_set_use_includes(HyRepo repo, gboolean enabled) -{ - repo->setUseIncludes(enabled); -} - -void -hy_repo_set_string(HyRepo repo, int which, const char *str_val) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - switch (which) { - case HY_REPO_NAME: - repoImpl->id = str_val; - repoImpl->conf->name().set(libdnf::Option::Priority::RUNTIME, str_val); - break; - case HY_REPO_MD_FN: - repoImpl->repomdFn = str_val ? str_val : ""; - break; - case HY_REPO_PRIMARY_FN: - repoImpl->metadataPaths[MD_TYPE_PRIMARY] = str_val ? str_val : ""; - break; - case HY_REPO_FILELISTS_FN: - repoImpl->metadataPaths[MD_TYPE_FILELISTS] = str_val ? str_val : ""; - break; - case HY_REPO_PRESTO_FN: - repoImpl->metadataPaths[MD_TYPE_PRESTODELTA] = str_val ? str_val : ""; - break; - case HY_REPO_UPDATEINFO_FN: - repoImpl->metadataPaths[MD_TYPE_UPDATEINFO] = str_val ? str_val : ""; - break; - case HY_REPO_OTHER_FN: - repoImpl->metadataPaths[MD_TYPE_OTHER] = str_val ? str_val : ""; - break; - case MODULES_FN: - repoImpl->metadataPaths[MD_TYPE_MODULES] = str_val ? str_val : ""; - break; - default: - assert(0); - } -} - -const char * -hy_repo_get_string(HyRepo repo, int which) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - const char * ret; - switch(which) { - case HY_REPO_NAME: - return repoImpl->id.c_str(); - case HY_REPO_MD_FN: - ret = repoImpl->repomdFn.c_str(); - break; - case HY_REPO_PRIMARY_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_PRIMARY).c_str(); - break; - case HY_REPO_FILELISTS_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_FILELISTS).c_str(); - break; - case HY_REPO_PRESTO_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_PRESTODELTA).c_str(); - break; - case HY_REPO_UPDATEINFO_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_UPDATEINFO).c_str(); - break; - case HY_REPO_OTHER_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_OTHER).c_str(); - break; - case MODULES_FN: - ret = repoImpl->getMetadataPath(MD_TYPE_MODULES).c_str(); - break; - default: - return nullptr; - } - return ret[0] == '\0' ? nullptr : ret; -} - -void -hy_repo_free(HyRepo repo) -{ - auto repoImpl = libdnf::repoGetImpl(repo); - { - std::lock_guard guard(repoImpl->attachLibsolvMutex); - if (--repoImpl->nrefs > 0) - return; // There is still a reference to this object. Don't destroy it. - } - assert(!repoImpl->libsolvRepo); - delete repo; -} diff --git a/dnf-4/libdnf/utils/PreserveOrderMap.hpp b/dnf-4/libdnf/utils/PreserveOrderMap.hpp deleted file mode 100644 index 73ffdc5a5a..0000000000 --- a/dnf-4/libdnf/utils/PreserveOrderMap.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2019 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_PRESERVE_ORDER_MAP_HPP -#define _LIBDNF_PRESERVE_ORDER_MAP_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include -#include -#include -#include - -namespace libdnf { - -template> -class PreserveOrderMap { -public: - typedef Key key_type; - typedef T mapped_type; - typedef KeyEqual key_equal; - typedef std::pair value_type; - typedef std::vector> container_type; - typedef typename container_type::size_type size_type; - - template - struct MyBidirIterator { - typedef std::bidirectional_iterator_tag iterator_category; - typedef typename PreserveOrderMap::value_type value_type; - typedef ptrdiff_t difference_type; - typedef valueType * pointer; - typedef valueType & reference; - - explicit MyBidirIterator() = default; - explicit MyBidirIterator(ContainerTypeIterator ci) : ci(ci) {} - - reference operator *() { return reinterpret_cast(*ci); } - pointer operator ->() const { return reinterpret_cast(ci.operator->()); } - - MyBidirIterator& operator ++() { ++ci; return *this; } - MyBidirIterator operator ++(int) { auto tmp = *this; ++*this; return tmp; } - MyBidirIterator& operator --() { --ci; return *this; } - MyBidirIterator operator --(int) { auto tmp = *this; --*this; return tmp; } - - bool operator ==(const MyBidirIterator& other) const { return ci == other.ci; } - bool operator !=(const MyBidirIterator& other) const { return ci != other.ci; } - - private: - friend class PreserveOrderMap; - ContainerTypeIterator ci; - }; - typedef MyBidirIterator iterator; - typedef MyBidirIterator const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - bool empty() const noexcept { return items.empty(); } - size_type size() const noexcept { return items.size(); } - size_type max_size() const noexcept { return items.max_size(); } - void reserve(size_type newCapacity) { items.reserve(newCapacity); } - size_type capacity() const noexcept { return items.capacity(); } - void shrink_to_fit() { items.shrink_to_fit(); } - - iterator begin() noexcept { return iterator(items.begin()); } - const_iterator begin() const noexcept { return const_iterator(items.begin()); } - const_iterator cbegin() const noexcept { return const_iterator(items.cbegin()); } - iterator end() noexcept { return iterator(items.end()); } - const_iterator end() const noexcept { return const_iterator(items.end()); } - const_iterator cend() const noexcept { return const_iterator(items.cend()); } - reverse_iterator rbegin() noexcept { return reverse_iterator(items.rbegin()); } - const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(items.rbegin()); } - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(items.crbegin()); } - reverse_iterator rend() noexcept { return reverse_iterator(items.rend()); } - const_reverse_iterator rend() const noexcept { return const_reverse_iterator(items.rend()); } - const_reverse_iterator crend() const noexcept { return const_reverse_iterator(items.crend()); } - - void clear() noexcept { items.clear(); } - - std::pair insert(const value_type & value) - { - auto it = find(value.first); - if (it == end()) { - it = iterator(items.insert(it.ci, value)); - return {it, true}; - } else { - return {it, false}; - } - } - - iterator erase(const_iterator pos) { return iterator(items.erase(pos.ci)); } - iterator erase(const_iterator first, const_iterator last) { return iterator(items.erase(first.ci, last.ci)); } - - size_type erase(const Key & key) - { - auto it = find(key); - if (it == end()) - return 0; - items.erase(it.ci); - return 1; - } - - size_type count(const Key & key) const - { - return find(key) != end() ? 1 : 0; - } - - iterator find(const Key & key) - { - auto it = begin(); - while (it != end() && !KeyEqual()(it->first, key)) - ++it; - return it; - } - - const_iterator find(const Key & key) const - { - auto it = cbegin(); - while (it != cend() && !KeyEqual()(it->first, key)) - ++it; - return it; - } - - T & operator[](const Key & key) { - for (auto & item : items) { - if (KeyEqual()(item.first, key)) - return item.second; - } - items.push_back({key, {}}); - return items.back().second; - } - - T & operator[](Key && key) - { - for (auto & item : items) { - if (KeyEqual()(item.first, key)) - return item.second; - } - items.push_back({std::move(key), {}}); - return items.back().second; - } - - T & at(const Key & key) - { - for (auto & item : items) { - if (KeyEqual()(item.first, key)) - return item.second; - } - throw std::out_of_range("PreserveOrderMap::at"); - } - - const T & at(const Key & key) const - { - for (auto & item : items) { - if (KeyEqual()(item.first, key)) - return item.second; - } - throw std::out_of_range("PreserveOrderMap::at"); - } - -private: - container_type items; -}; - -} - -#endif - -#endif //_LIBDNF_PRESERVE_ORDER_MAP_HPP diff --git a/dnf-4/libdnf/utils/iniparser/iniparser.cpp b/dnf-4/libdnf/utils/iniparser/iniparser.cpp deleted file mode 100644 index 14fef743c6..0000000000 --- a/dnf-4/libdnf/utils/iniparser/iniparser.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "iniparser.hpp" - -constexpr char DELIMITER = '\n'; - -const char * IniParser::CantOpenFile::what() const noexcept -{ - return "IniParser: Can't open file"; -} - -const char * IniParser::MissingSectionHeader::what() const noexcept -{ - return "IniParser: Missing section header"; -} - -const char * IniParser::MissingBracket::what() const noexcept -{ - return "IniParser: Missing ']'"; -} - -const char * IniParser::EmptySectionName::what() const noexcept -{ - return "IniParser: Empty section name"; -} - -const char * IniParser::TextAfterSection::what() const noexcept -{ - return "IniParser: Text after section"; -} - -const char * IniParser::IllegalContinuationLine::what() const noexcept -{ - return "IniParser: Illegal continuation line"; -} - -const char * IniParser::MissingKey::what() const noexcept -{ - return "IniParser: Missing key"; -} - -const char * IniParser::MissingEqual::what() const noexcept -{ - return "IniParser: Missing '='"; -} - -IniParser::IniParser(const std::string & filePath) -: is(new std::ifstream(filePath)) -{ - if (!(*is)) - throw CantOpenFile(); - is->exceptions(std::ifstream::badbit); - lineNumber = 0; -} - -IniParser::IniParser(std::unique_ptr && inputStream) -: is(std::move(inputStream)) -{ - if (!(*is)) - throw CantOpenFile(); - is->exceptions(std::ifstream::badbit); - lineNumber = 0; -} - -void IniParser::trimValue() noexcept { - auto end = value.find_last_not_of(DELIMITER); - if (end != value.npos) - value.resize(end + 1); - if (value.length() > 1 && - value.front() == value.back() && - (value.front() == '\"' || value.front() == '\'')) { - value.erase(--value.end()); - value.erase(value.begin()); - } -} - -IniParser::ItemType IniParser::next() -{ - bool previousLineWithKeyVal = false; - rawItem.clear(); - while (!line.empty() || !is->eof()) { - if (line.empty()) { - std::getline(*is, line, DELIMITER); - ++lineNumber; - } - - // remove UTF-8 BOM - if (lineNumber == 1 && line.length() >= 3 && - static_cast(line[0]) == 0xEF && - static_cast(line[1]) == 0xBB && - static_cast(line[2]) == 0xBF) - line.erase(0, 3); - - if (line.length() == 0 || line[0] == '#' || line[0] == ';') {// do not support [rR][eE][mM] comment - if (previousLineWithKeyVal) { - trimValue(); - return ItemType::KEY_VAL; - } - if (line.length() == 0) - return ItemType::EMPTY_LINE; - rawItem = line + DELIMITER; - line.clear(); - return ItemType::COMMENT_LINE; - } - auto start = line.find_first_not_of(" \t\r"); - if (start == std::string::npos) { - if (previousLineWithKeyVal) { - value += DELIMITER; - rawItem += line + DELIMITER; - line.clear(); - continue; - } - rawItem = line + DELIMITER; - line.clear(); - return ItemType::EMPTY_LINE; - } - auto end = line.find_last_not_of(" \t\r"); - - if (previousLineWithKeyVal && (start == 0 || line[start] == '[')) { - trimValue(); - return ItemType::KEY_VAL; - } - - if (line[start] == '[') { - auto endSectPos = line.find("]", ++start); - if (endSectPos == line.npos) - throw MissingBracket(lineNumber); - else if (endSectPos == start) - throw EmptySectionName(lineNumber); - for (auto idx = endSectPos + 1; idx < end; ++idx) - { - auto ch = line[idx]; - if (ch == '#' || ch == ';') - break; - if (ch != ' ' && ch != '\t' && ch != '\r') - throw TextAfterSection(lineNumber); - } - this->section = line.substr(start, endSectPos - start); - rawItem = line + DELIMITER; - line.clear(); - return ItemType::SECTION; - } - - if (section.empty()) - throw MissingSectionHeader(lineNumber); - - if (start > 0) { - if (!previousLineWithKeyVal) - throw IllegalContinuationLine(lineNumber); - value += DELIMITER + line.substr(start, end - start + 1); - rawItem += line + DELIMITER; - line.clear(); - } else { - if (line[start] == '=') - throw MissingKey(lineNumber); - auto eqlpos = line.find_first_of("="); - if (eqlpos == std::string::npos) - throw MissingEqual(lineNumber); - auto endkeypos = line.find_last_not_of(" \t", eqlpos - 1); - auto valuepos = line.find_first_not_of(" \t", eqlpos + 1); - key = line.substr(start, endkeypos - start + 1); - if (valuepos != line.npos) - value = line.substr(valuepos, end - valuepos + 1); - else - value.clear(); - previousLineWithKeyVal = true; - rawItem = line + DELIMITER; - line.clear(); - } - } - trimValue(); - return previousLineWithKeyVal ? ItemType::KEY_VAL : ItemType::END_OF_INPUT; -} diff --git a/dnf-4/libdnf/utils/iniparser/iniparser.hpp b/dnf-4/libdnf/utils/iniparser/iniparser.hpp deleted file mode 100644 index a7c75fcb73..0000000000 --- a/dnf-4/libdnf/utils/iniparser/iniparser.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _INIPARSER_HPP -#define _INIPARSER_HPP - -#include -#include -#include -#include - -/** -* @class IniParser -* -* @brief Simple .INI file parser -* -* The goal is to be compatible with dnf / yum .ini configuration files. -*/ -class IniParser { -public: - struct Exception : public std::exception { - Exception(int lineNumber) : lineNumber(lineNumber) {} - Exception() : lineNumber(0) {} - int getLineNumber() const noexcept { return lineNumber; } - protected: - int lineNumber; - }; - struct CantOpenFile : public Exception { - CantOpenFile() {} - const char * what() const noexcept override; - }; - struct MissingSectionHeader : public Exception { - MissingSectionHeader(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct MissingBracket : public Exception { - MissingBracket(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct EmptySectionName : public Exception { - EmptySectionName(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct TextAfterSection : public Exception { - TextAfterSection(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct IllegalContinuationLine : public Exception { - IllegalContinuationLine(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct MissingKey : public Exception { - MissingKey(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - struct MissingEqual : public Exception { - MissingEqual(int lineNumber) : Exception(lineNumber) {} - const char * what() const noexcept override; - }; - - enum class ItemType { - SECTION, // [section_name] - KEY_VAL, // key = value, (multiline value supported) - COMMENT_LINE, // line starting with '#' or ';' character - EMPTY_LINE, // zero length or only contains whitespace characters - END_OF_INPUT - }; - - IniParser(const std::string & filePath); - IniParser(std::unique_ptr && inputStream); - /** - * @brief Parse one item from input file - * - * Returns type of parsed item. Parsed values can be obtained by methods - * getSection(), getKey(), getValue(). - * - * @return IniParser::ItemType Type of parsed value - */ - ItemType next(); - const std::string & getSection() const noexcept; - const std::string & getKey() const noexcept; - std::string & getKey() noexcept; - const std::string & getValue() const noexcept; - std::string & getValue() noexcept; - const std::string & getRawItem() const noexcept; - std::string & getRawItem() noexcept; - const std::string & getLine() const noexcept; - void clearLine() noexcept; - void trimValue() noexcept; - -private: - std::unique_ptr is; - int lineNumber; - std::string section; - std::string key; - std::string value; - std::string rawItem; - std::string line; -}; - -inline const std::string & IniParser::getSection() const noexcept { return section; } -inline const std::string & IniParser::getKey() const noexcept { return key; } -inline std::string & IniParser::getKey() noexcept { return key; } -inline const std::string & IniParser::getValue() const noexcept { return value; } -inline std::string & IniParser::getValue() noexcept { return value; } -inline const std::string & IniParser::getRawItem() const noexcept { return rawItem; } -inline std::string & IniParser::getRawItem() noexcept { return rawItem; } -inline const std::string & IniParser::getLine() const noexcept { return line; } -inline void IniParser::clearLine() noexcept { line.clear(); } - -#endif diff --git a/include/libdnf/base/base.hpp b/include/libdnf/base/base.hpp index ff13e0b16f..e1af717a19 100644 --- a/include/libdnf/base/base.hpp +++ b/include/libdnf/base/base.hpp @@ -20,6 +20,12 @@ along with libdnf. If not, see . #ifndef LIBDNF_BASE_BASE_HPP #define LIBDNF_BASE_BASE_HPP +#include "libdnf/conf/config_main.hpp" +#include "libdnf/logger/log_router.hpp" +#include "libdnf/rpm/repo/repo_sack.hpp" + +#include +#include namespace libdnf { @@ -29,8 +35,21 @@ namespace libdnf { /// :class:`.Base` instances are stateful objects owning various data. class Base { public: + void load_config_from_file(const std::filesystem::path & path); + void load_config_from_file(); + void load_config_from_dir(const std::filesystem::path & path); + void load_config_from_dir(); + + ConfigMain * get_config() { return &config_main; } + LogRouter * get_logger() { return &log_router; } + rpm::RepoSack * get_rpm_repo_sack() { return &rpm_repo_sack; } + std::map * get_substitutions() { return &substitutions; } private: + LogRouter log_router; + ConfigMain config_main; + std::map substitutions; + rpm::RepoSack rpm_repo_sack{*this}; }; diff --git a/include/libdnf/common/sack/match_int64.hpp b/include/libdnf/common/sack/match_int64.hpp new file mode 100644 index 0000000000..da4028704e --- /dev/null +++ b/include/libdnf/common/sack/match_int64.hpp @@ -0,0 +1,41 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_COMMON_SACK_MATCH_INT64_HPP +#define LIBDNF_COMMON_SACK_MATCH_INT64_HPP + + +#include "query_cmp.hpp" + +#include +#include + + +namespace libdnf::sack { + + +bool match_int64(int64_t value, QueryCmp cmp, int64_t pattern); +bool match_int64(int64_t value, QueryCmp cmp, const std::vector & patterns); +bool match_int64(const std::vector & values, QueryCmp cmp, int64_t pattern); +bool match_int64(const std::vector & values, QueryCmp cmp, const std::vector & patterns); + + +} // namespace libdnf::sack + +#endif diff --git a/include/libdnf/common/sack/match_string.hpp b/include/libdnf/common/sack/match_string.hpp new file mode 100644 index 0000000000..6eca537db0 --- /dev/null +++ b/include/libdnf/common/sack/match_string.hpp @@ -0,0 +1,42 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_COMMON_SACK_MATCH_STRING_HPP +#define LIBDNF_COMMON_SACK_MATCH_STRING_HPP + + +#include "query_cmp.hpp" + +#include +#include + + +namespace libdnf::sack { + + +bool match_string(const std::string & value, QueryCmp cmp, const std::string & pattern); +bool match_string(const std::string & value, QueryCmp cmp, const std::vector & patterns); +bool match_string(const std::vector & values, QueryCmp cmp, const std::string & pattern); +bool match_string(const std::vector & values, QueryCmp cmp, const std::vector & patterns); + + +} // namespace libdnf::sack + + +#endif diff --git a/include/libdnf/common/sack/query.hpp b/include/libdnf/common/sack/query.hpp new file mode 100644 index 0000000000..2f98f2ab1f --- /dev/null +++ b/include/libdnf/common/sack/query.hpp @@ -0,0 +1,243 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_COMMON_SACK_QUERY_HPP +#define LIBDNF_COMMON_SACK_QUERY_HPP + + +#include "match_int64.hpp" +#include "match_string.hpp" +#include "query_cmp.hpp" + +#include "libdnf/utils/exception.hpp" +#include "libdnf/utils/set.hpp" + +#include +#include +#include +#include +#include + + +namespace libdnf::sack { + + +/// Query is a Set with filtering capabilities. +template +class Query : public Set { +public: + class UnsupportedOperation : public LogicError { + public: + using LogicError::LogicError; + const char * get_domain_name() const noexcept override { return "libdnf::Query"; } + const char * get_name() const noexcept override { return "UnsupportedOperation"; } + const char * get_description() const noexcept override { return "Unsupported operator"; } + }; + + using FilterFunctionBool = bool(const T & obj); + using FilterFunctionCString = char *(const T & obj); + using FilterFunctionInt64 = int64_t(const T & obj); + using FilterFunctionString = std::string(const T & obj); + using FilterFunctionVectorInt64 = std::vector(const T & obj); + using FilterFunctionVectorString = std::vector(const T & obj); + + Query() = default; + explicit Query(const Set & src_set) : Set::Set(src_set) {} + explicit Query(Set && src_set) : Set::Set(std::move(src_set)) {} + + // std::size_t ifilter(FilterFunctionString * getter, QueryCmp cmp, const std::string & pattern); + Query & ifilter(std::string (*getter)(const T &), QueryCmp cmp, const std::string & pattern); + Query & ifilter(std::vector (*getter)(const T &), QueryCmp cmp, const std::string & pattern); + Query & ifilter(std::string (*getter)(const T &), QueryCmp cmp, const std::vector & patterns); + Query & ifilter( + std::vector (*getter)(const T &), QueryCmp cmp, const std::vector & patterns); + + Query & ifilter(int64_t (*getter)(const T &), QueryCmp cmp, int64_t pattern); + Query & ifilter(std::vector (*getter)(const T &), QueryCmp cmp, int64_t pattern); + Query & ifilter(int64_t (*getter)(const T &), QueryCmp cmp, const std::vector & patterns); + Query & ifilter(std::vector (*getter)(const T &), QueryCmp cmp, const std::vector & patterns); + + Query & ifilter(bool (*getter)(const T &), QueryCmp cmp, bool pattern); + + Query & ifilter(char * (*getter)(const T &), QueryCmp cmp, const std::string & pattern); + + /// Get a single object. Raise an exception if none or multiple objects match the query. + const T & get() const { + if (get_data().size() == 1) { + return *get_data().begin(); + } + throw std::runtime_error("Query must contain exactly one object."); + } + + /// List all objects matching the query. + const std::set & list() const noexcept { return get_data(); } + + // operators; OR at least + // copy() + using Set::get_data; +}; + + +template +inline Query & Query::ifilter( + Query::FilterFunctionString * getter, QueryCmp cmp, const std::string & pattern) { + + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (match_string(value, cmp, pattern)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + + +template +inline Query & Query::ifilter( + Query::FilterFunctionVectorString * getter, QueryCmp cmp, const std::string & pattern) { + std::size_t filtered = 0; + + for (auto it = get_data().begin(); it != get_data().end();) { + auto values = getter(*it); + if (match_string(values, cmp, pattern)) { + ++it; + } else { + it = get_data().erase(it); + ++filtered; + } + } + return filtered; +} + +template +inline Query & Query::ifilter( + Query::FilterFunctionString * getter, QueryCmp cmp, const std::vector & patterns) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (match_string(value, cmp, patterns)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter( + Query::FilterFunctionVectorString * getter, QueryCmp cmp, const std::vector & patterns) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto values = getter(*it); + if (match_string(values, cmp, patterns)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter(FilterFunctionInt64 * getter, QueryCmp cmp, int64_t pattern) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (match_int64(value, cmp, pattern)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter(FilterFunctionVectorInt64 * getter, QueryCmp cmp, int64_t pattern) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto values = getter(*it); + if (match_int64(values, cmp, pattern)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter(FilterFunctionInt64 * getter, QueryCmp cmp, const std::vector & patterns) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (match_int64(value, cmp, patterns)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter( + FilterFunctionVectorInt64 * getter, QueryCmp cmp, const std::vector & patterns) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto values = getter(*it); + if (match_int64(values, cmp, patterns)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +// TODO: other cmp +template +inline Query & Query::ifilter(Query::FilterFunctionBool * getter, QueryCmp cmp, bool pattern) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (cmp == QueryCmp::EQ && value == pattern) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + +template +inline Query & Query::ifilter( + Query::FilterFunctionCString * getter, QueryCmp cmp, const std::string & pattern) { + for (auto it = get_data().begin(); it != get_data().end();) { + auto value = getter(*it); + if (match_string(value, cmp, pattern)) { + ++it; + } else { + it = get_data().erase(it); + } + } + return *this; +} + + +} // namespace libdnf::sack + + +#endif diff --git a/include/libdnf/common/sack/query_cmp.hpp b/include/libdnf/common/sack/query_cmp.hpp new file mode 100644 index 0000000000..6c9121b8dc --- /dev/null +++ b/include/libdnf/common/sack/query_cmp.hpp @@ -0,0 +1,81 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_COMMON_SACK_QUERY_CMP_HPP +#define LIBDNF_COMMON_SACK_QUERY_CMP_HPP + + +#include +#include + + +namespace libdnf::sack { + + +// https://docs.djangoproject.com/en/3.0/ref/models/querysets/#field-lookups +enum class QueryCmp : uint32_t { + // MODIFIERS + NOT = (1 << 0), // negation + ICASE = (1 << 1), // case-insensitive match + + // NULL + ISNULL = (1 << 7), // value is NULL/None + + // NUMBERS + EQ = (1 << 8), // equal; identical meaning to EXACT + NEQ = (NOT | EQ), // not equal + GT = (1 << 9), // greater than + GTE = (GT | EQ), // greater than or equal to + LT = (1 << 10), // less than + LTE = (LT | EQ), // less than or equal to + + // STRINGS + EXACT = (EQ), // case-sensitive exact match + IEXACT = (ICASE | EXACT), // case-insensitive exact match + CONTAINS = (1 << 16), // case-sensitive containment test + ICONTAINS = (ICASE | CONTAINS), // case-insensitive containment test + STARTSWITH = (1 << 17), // case-sensitive starts-with + ISTARTSWITH = (ICASE | STARTSWITH), // case-insensitive starts-with + ENDSWITH = (1 << 18), // case-sensitive ends-with + IENDSWITH = (ICASE | ENDSWITH), // case-insensitive ends-with + REGEX = (1 << 19), // case-sensitive regular expression match. + IREGEX = (ICASE | REGEX), // case-insensitive regular expression match. + GLOB = (1 << 20), // case-sensitive glob match + IGLOB = (ICASE | GLOB), // case-insensitive glob match +}; + + +inline QueryCmp operator|(QueryCmp lhs, QueryCmp rhs) { + return static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs)); +} + + +inline QueryCmp operator&(QueryCmp lhs, QueryCmp rhs) { + return static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs)); +} + + +} // namespace libdnf::sack + + +#endif diff --git a/include/libdnf/common/sack/sack.hpp b/include/libdnf/common/sack/sack.hpp new file mode 100644 index 0000000000..63a0398c50 --- /dev/null +++ b/include/libdnf/common/sack/sack.hpp @@ -0,0 +1,103 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_COMMON_SACK_SACK_HPP +#define LIBDNF_COMMON_SACK_SACK_HPP + +#include "libdnf/utils/set.hpp" +#include "libdnf/utils/weak_ptr.hpp" + +#include +#include + +namespace libdnf::sack { + + +template +class Sack { +public: + using DataItemWeakPtr = WeakPtr; + + // EXCLUDES + + const Set & get_excludes() const noexcept { return excludes; } + void add_excludes(const Set & value) { excludes.update(value); } + void remove_excludes(const Set & value) { excludes.difference(value); } + void set_excludes(const Set & value) { excludes = value; } + + // INCLUDES + + const Set & get_includes() const noexcept { return includes; } + void add_includes(const Set & value) { includes.update(value); } + void remove_includes(const Set & value) { includes.difference(value); } + void set_includes(const Set & value) { includes = value; } + bool get_use_includes() const noexcept { return use_includes; } + void set_use_includes(bool value) { use_includes = value; } + + QueryT new_query(); + +protected: + Sack() = default; + DataItemWeakPtr add_item_with_return(std::unique_ptr && item); + void add_item(std::unique_ptr && item); + std::vector> & get_data() { return data; } + +private: + WeakPtrGuard data_guard; + Set excludes; + Set includes; + bool use_includes = false; + std::vector> data; // Owns the data set. Objects get deleted when the Sack is deleted. +}; + +template +QueryT Sack::new_query() { + QueryT result; + + if (this->get_use_includes()) { + // if includes are used, add only includes to the query + result = QueryT(includes); + } else { + // else add all items + for (auto & it : this->get_data()) { + result.add(DataItemWeakPtr(it.get(), &data_guard)); + } + } + + // apply excludes + result -= excludes; + + return result; +} + +template +typename Sack::DataItemWeakPtr Sack::add_item_with_return(std::unique_ptr && item) { + auto ret = DataItemWeakPtr(item.get(), &data_guard); + data.push_back(std::move(item)); + return ret; +} + +template +void Sack::add_item(std::unique_ptr && item) { + data.push_back(std::move(item)); +} + +} // namespace libdnf::sack + +#endif diff --git a/include/libdnf/conf/config.hpp b/include/libdnf/conf/config.hpp new file mode 100644 index 0000000000..412bfa2d3a --- /dev/null +++ b/include/libdnf/conf/config.hpp @@ -0,0 +1,37 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_CONFIG_HPP +#define LIBDNF_CONF_CONFIG_HPP + +#include "option_binds.hpp" + +namespace libdnf { + +class Config { +public: + OptionBinds & opt_binds() { return binds; } + +private: + OptionBinds binds; +}; + +} // namespace libdnf + +#endif diff --git a/dnf-4/libdnf/conf/ConfigMain.hpp b/include/libdnf/conf/config_main.hpp similarity index 74% rename from dnf-4/libdnf/conf/ConfigMain.hpp rename to include/libdnf/conf/config_main.hpp index 118ecbf1c5..a98c9653ed 100644 --- a/dnf-4/libdnf/conf/ConfigMain.hpp +++ b/include/libdnf/conf/config_main.hpp @@ -1,36 +1,35 @@ /* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_CONFIG_MAIN_HPP -#define _LIBDNF_CONFIG_MAIN_HPP - -#ifdef LIBDNF_UNSTABLE_API - -#include "Config.hpp" -#include "OptionBool.hpp" -#include "OptionEnum.hpp" -#include "OptionNumber.hpp" -#include "OptionPath.hpp" -#include "OptionSeconds.hpp" -#include "OptionString.hpp" -#include "OptionStringList.hpp" +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_CONFIG_MAIN_HPP +#define LIBDNF_CONF_CONFIG_MAIN_HPP + +#include "config.hpp" +#include "option_bool.hpp" +#include "option_enum.hpp" +#include "option_number.hpp" +#include "option_path.hpp" +#include "option_seconds.hpp" +#include "option_string.hpp" +#include "option_string_list.hpp" + +#include "libdnf/logger/logger.hpp" #include @@ -87,10 +86,11 @@ class ConfigMain : public Config { OptionBool & obsoletes(); OptionBool & showdupesfromrepos(); OptionBool & exit_on_lock(); + OptionBool & allow_vendor_change(); OptionSeconds & metadata_timer_sync(); OptionStringList & disable_excludes(); - OptionEnum & multilib_policy(); // :api - OptionBool & best(); // :api + OptionEnum & multilib_policy(); // :api + OptionBool & best(); // :api OptionBool & install_weak_deps(); OptionString & bugtracker_url(); OptionBool & zchunk(); @@ -110,9 +110,9 @@ class ConfigMain : public Config { OptionBool & history_record(); OptionStringList & history_record_packages(); OptionString & rpmverbosity(); - OptionBool & strict(); // :api - OptionBool & skip_broken(); // :yum-compatibility - OptionBool & autocheck_running_kernel(); // :yum-compatibility + OptionBool & strict(); // :api + OptionBool & skip_broken(); // :yum-compatibility + OptionBool & autocheck_running_kernel(); // :yum-compatibility OptionBool & clean_requirements_on_remove(); OptionEnum & history_list_view(); OptionBool & upgrade_group_objects_upgrade(); @@ -166,7 +166,7 @@ class ConfigMain : public Config { * @param varsMap Storage where the variables are added. * @param dirPath Path to directory. */ - static void addVarsFromDir(std::map & varsMap, const std::string & dirPath); + static void add_vars_from_dir(std::map & vars_map, const std::string & dir_path); /** * @brief Adds variables from environment @@ -176,15 +176,13 @@ class ConfigMain : public Config { * * @param varsMap Storage where the variables are added. */ - static void addVarsFromEnv(std::map & varsMap); + static void add_vars_from_env(std::map & vars_map); private: class Impl; - std::unique_ptr pImpl; + std::unique_ptr p_impl; }; -} - -#endif +} // namespace libdnf #endif diff --git a/include/libdnf/conf/config_parser.hpp b/include/libdnf/conf/config_parser.hpp new file mode 100644 index 0000000000..b0d89b3224 --- /dev/null +++ b/include/libdnf/conf/config_parser.hpp @@ -0,0 +1,287 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_CONFIG_PARSER_HPP +#define LIBDNF_CONF_CONFIG_PARSER_HPP + +#include "libdnf/utils/exception.hpp" +#include "libdnf/utils/preserve_order_map.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace libdnf { + +/** +* @class ConfigParser +* +* @brief Class for parsing dnf/yum .ini configuration files. +* +* ConfigParser is used for parsing files. The class adds support for substitutions. +* User can get both substituded and original parsed values. +* The parsed items are stored into the PreserveOrderMap. +* IniParser preserve order of items. Comments and empty lines are kept. +*/ +struct ConfigParser { +public: + using Container = PreserveOrderMap>; + + class Exception : public RuntimeError { + public: + using RuntimeError::RuntimeError; + const char * get_domain_name() const noexcept override { return "libdnf::ConfigParser"; } + const char * get_name() const noexcept override { return "Exception"; } + const char * get_description() const noexcept override { return "ConfigParser exception"; } + }; + class SectionNotFound : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "MissingSection"; } + const char * get_description() const noexcept override { return "Section not found"; } + }; + class OptionNotFound : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "OptionNotFound"; } + const char * get_description() const noexcept override { return "Option not found"; } + }; + + /** + * @brief Substitute values in text according to the substitutions map + * + * @param text The text for substitution + * @param substitutions Substitution map + */ + static void substitute(std::string & text, const std::map & substitutions); + void set_substitutions(const std::map & substitutions); + void set_substitutions(std::map && substitutions); + const std::map & get_substitutions() const; + /** + * @brief Reads/parse one INI file + * + * Can be called repeately for reading/merge more INI files. + * + * @param file_path Name (with path) of file to read + */ + void read(const std::string & file_path); + /** + * @brief Reads/parse from istream + * + * Can be called repeately for reading/merge more istreams. + * + * @param inputStream Stream to read + */ + void read(std::unique_ptr && input_stream); + /** + * @brief Writes all data (all sections) to INI file + * + * @param file_path Name (with path) of file to write + * @param append If true, existent file will be appended, otherwise overwritten + */ + void write(const std::string & file_path, bool append) const; + /** + * @brief Writes one section data to INI file + * + * @param file_path Name (with path) of file to write + * @param append If true, existent file will be appended, otherwise overwritten + * @param section Section to write + */ + void write(const std::string & file_path, bool append, const std::string & section) const; + /** + * @brief Writes one section data to stream + * + * @param output_stream Stream to write + * @param section Section to write + */ + void write(std::ostream & output_stream, const std::string & section) const; + /** + * @brief Writes all data (all sections) to stream + * + * @param output_stream Stream to write + */ + void write(std::ostream & output_stream) const; + bool add_section(const std::string & section, const std::string & raw_line); + bool add_section(const std::string & section); + bool add_section(std::string && section, std::string && raw_line); + bool add_section(std::string && section); + bool has_section(const std::string & section) const noexcept; + bool has_option(const std::string & section, const std::string & key) const noexcept; + void set_value( + const std::string & section, const std::string & key, const std::string & value, const std::string & raw_item); + void set_value(const std::string & section, const std::string & key, const std::string & value); + void set_value(const std::string & section, std::string && key, std::string && value, std::string && raw_item); + void set_value(const std::string & section, std::string && key, std::string && value); + bool remove_section(const std::string & section); + bool remove_option(const std::string & section, const std::string & key); + void add_comment_line(const std::string & section, const std::string & comment); + void add_comment_line(const std::string & section, std::string && comment); + const std::string & get_value(const std::string & section, const std::string & key) const; + std::string get_substituted_value(const std::string & section, const std::string & key) const; + const std::string & get_header() const noexcept; + std::string & get_header() noexcept; + const Container & get_data() const noexcept; + Container & get_data() noexcept; + +private: + std::map substitutions; + Container data; + int item_number{0}; + std::string header; + std::map raw_items; +}; + +inline void ConfigParser::set_substitutions(const std::map & substitutions) { + this->substitutions = substitutions; +} + +inline void ConfigParser::set_substitutions(std::map && substitutions) { + this->substitutions = std::move(substitutions); +} + +inline const std::map & ConfigParser::get_substitutions() const { + return substitutions; +} + +inline bool ConfigParser::add_section(const std::string & section, const std::string & raw_line) { + if (data.find(section) != data.end()) { + return false; + } + if (!raw_line.empty()) { + raw_items[section] = raw_line; + } + data[section] = {}; + return true; +} + +inline bool ConfigParser::add_section(const std::string & section) { + return add_section(section, ""); +} + +inline bool ConfigParser::add_section(std::string && section, std::string && raw_line) { + if (data.find(section) != data.end()) { + return false; + } + if (!raw_line.empty()) { + raw_items[section] = std::move(raw_line); + } + data[std::move(section)] = {}; + return true; +} + +inline bool ConfigParser::add_section(std::string && section) { + return add_section(std::move(section), ""); +} + +inline bool ConfigParser::has_section(const std::string & section) const noexcept { + return data.find(section) != data.end(); +} + +inline bool ConfigParser::has_option(const std::string & section, const std::string & key) const noexcept { + auto section_iter = data.find(section); + return section_iter != data.end() && section_iter->second.find(key) != section_iter->second.end(); +} + +inline void ConfigParser::set_value( + const std::string & section, const std::string & key, const std::string & value, const std::string & raw_item) { + auto section_iter = data.find(section); + if (section_iter == data.end()) { + throw SectionNotFound(section); + } + if (raw_items.empty()) { + raw_items.erase(section + ']' + key); + } else { + raw_items[section + ']' + key] = raw_item; + } + section_iter->second[key] = value; +} + +inline void ConfigParser::set_value( + const std::string & section, std::string && key, std::string && value, std::string && raw_item) { + auto section_iter = data.find(section); + if (section_iter == data.end()) { + throw SectionNotFound(section); + } + if (raw_items.empty()) { + raw_items.erase(section + ']' + key); + } else { + raw_items[section + ']' + key] = std::move(raw_item); + } + section_iter->second[std::move(key)] = std::move(value); +} + +inline bool ConfigParser::remove_section(const std::string & section) { + auto removed = data.erase(section) > 0; + if (removed) { + raw_items.erase(section); + } + return removed; +} + +inline bool ConfigParser::remove_option(const std::string & section, const std::string & key) { + auto section_iter = data.find(section); + if (section_iter == data.end()) { + return false; + } + auto removed = section_iter->second.erase(key) > 0; + if (removed) { + raw_items.erase(section + ']' + key); + } + return removed; +} + +inline void ConfigParser::add_comment_line(const std::string & section, const std::string & comment) { + auto section_iter = data.find(section); + if (section_iter == data.end()) { + throw SectionNotFound(section); + } + section_iter->second["#" + std::to_string(++item_number)] = comment; +} + +inline void ConfigParser::add_comment_line(const std::string & section, std::string && comment) { + auto section_iter = data.find(section); + if (section_iter == data.end()) { + throw SectionNotFound(section); + } + section_iter->second["#" + std::to_string(++item_number)] = std::move(comment); +} + +inline const std::string & ConfigParser::get_header() const noexcept { + return header; +} + +inline std::string & ConfigParser::get_header() noexcept { + return header; +} + +inline const ConfigParser::Container & ConfigParser::get_data() const noexcept { + return data; +} + +inline ConfigParser::Container & ConfigParser::get_data() noexcept { + return data; +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/const.hpp b/include/libdnf/conf/const.hpp new file mode 100644 index 0000000000..01f4f0962c --- /dev/null +++ b/include/libdnf/conf/const.hpp @@ -0,0 +1,49 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONST_CONFIG_CONST_HPP +#define LIBDNF_CONST_CONFIG_CONST_HPP + +#include +#include + +namespace libdnf { + +constexpr const char * PERSISTDIR = "/var/lib/dnf"; +constexpr const char * SYSTEM_CACHEDIR = "/var/cache/dnf"; + +constexpr const char * CONF_FILENAME = "/etc/dnf/dnf.conf"; +constexpr const char * CONF_DIRECTORY = "/etc/dnf/conf.d"; + +// More important varsdirs must be on the end of vector +const std::vector VARS_DIRS{"/etc/yum/vars", "/etc/dnf/vars"}; + +const std::vector GROUP_PACKAGE_TYPES{"mandatory", "default", "conditional"}; +const std::vector INSTALLONLYPKGS{"kernel", + "kernel-PAE", + "installonlypkg(kernel)", + "installonlypkg(kernel-module)", + "installonlypkg(vm)", + "multiversion(kernel)"}; + +constexpr const char * BUGTRACKER = "https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=dnf"; + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option.hpp b/include/libdnf/conf/option.hpp new file mode 100644 index 0000000000..0961af6285 --- /dev/null +++ b/include/libdnf/conf/option.hpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_HPP +#define LIBDNF_CONF_OPTION_HPP + +#include "libdnf/utils/exception.hpp" + +#include + +namespace libdnf { + +class Option { +public: + enum class Priority { + EMPTY = 0, + DEFAULT = 10, + MAINCONFIG = 20, + AUTOMATICCONFIG = 30, + REPOCONFIG = 40, + PLUGINDEFAULT = 50, + PLUGINCONFIG = 60, + DROPINCONFIG = 65, + COMMANDLINE = 70, + RUNTIME = 80 + }; + + class Exception : public RuntimeError { + public: + using RuntimeError::RuntimeError; + const char * get_domain_name() const noexcept override { return "libdnf::Option"; } + const char * get_name() const noexcept override { return "Exception"; } + const char * get_description() const noexcept override { return "Option exception"; } + }; + + class InvalidValue : public Exception { + public: + explicit InvalidValue(const std::string & msg) : Exception(msg) {} + explicit InvalidValue(const char * msg) : Exception(msg) {} + const char * get_name() const noexcept override { return "InvalidValue"; } + const char * get_description() const noexcept override { return "Invalid value"; } + }; + + explicit Option(Priority priority = Priority::EMPTY); + Option(const Option & src) = default; + virtual ~Option() = default; + + virtual Option * clone() const = 0; + virtual Priority get_priority() const; + virtual void set(Priority priority, const std::string & value) = 0; + virtual std::string get_value_string() const = 0; + virtual bool empty() const noexcept; + +protected: + void set_priority(Priority priority); + +private: + Priority priority; +}; + +inline Option::Option(Priority priority) : priority(priority) {} + +inline Option::Priority Option::get_priority() const { + return priority; +} + +inline bool Option::empty() const noexcept { + return priority == Priority::EMPTY; +} + +inline void Option::set_priority(Priority priority) { + this->priority = priority; +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_binds.hpp b/include/libdnf/conf/option_binds.hpp new file mode 100644 index 0000000000..a3c30ccb5f --- /dev/null +++ b/include/libdnf/conf/option_binds.hpp @@ -0,0 +1,105 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_BINDS_HPP +#define LIBDNF_CONF_OPTION_BINDS_HPP + + +#include "option.hpp" + +#include +#include + +namespace libdnf { + +class OptionBinds { +public: + struct Exception : public RuntimeError { + using RuntimeError::RuntimeError; + const char * get_domain_name() const noexcept override { return "libdnf::OptionBinds"; } + const char * get_name() const noexcept override { return "Exception"; } + const char * get_description() const noexcept override { return "OptionBinds exception"; } + }; + + class OptionNotFound : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "OptionNotFound"; } + const char * get_description() const noexcept override { return "Option not found"; } + }; + + class OptionAlreadyExists : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "OptionAlreadyExists"; } + const char * get_description() const noexcept override { return "Option already exists"; } + }; + + class Item final { + public: + using NewStringFunc = std::function; + using GetValueStringFunc = std::function; + + Option::Priority get_priority() const; + void new_string(Option::Priority priority, const std::string & value); + std::string get_value_string() const; + bool get_add_value() const; + + private: + friend class OptionBinds; + + Item(Option & option, NewStringFunc new_string, GetValueStringFunc get_value_string, bool add_value); + explicit Item(Option & option); + Option * option; + NewStringFunc new_str; + GetValueStringFunc get_value_str; + bool add_value{false}; // hint that new value be added + }; + + using Container = std::map; + using iterator = Container::iterator; + using const_iterator = Container::const_iterator; + + Item & add( + const std::string & id, + Option & option, + Item::NewStringFunc new_string, + Item::GetValueStringFunc get_value_string, + bool add_value); + Item & add(const std::string & id, Option & option); + Item & at(const std::string & id); + const Item & at(const std::string & id) const; + bool empty() const noexcept { return items.empty(); } + std::size_t size() const noexcept { return items.size(); } + iterator begin() noexcept { return items.begin(); } + const_iterator begin() const noexcept { return items.begin(); } + const_iterator cbegin() const noexcept { return items.cbegin(); } + iterator end() noexcept { return items.end(); } + const_iterator end() const noexcept { return items.end(); } + const_iterator cend() const noexcept { return items.cend(); } + iterator find(const std::string & id) { return items.find(id); } + const_iterator find(const std::string & id) const { return items.find(id); } + +private: + Container items; +}; + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_bool.hpp b/include/libdnf/conf/option_bool.hpp new file mode 100644 index 0000000000..0be0e8f075 --- /dev/null +++ b/include/libdnf/conf/option_bool.hpp @@ -0,0 +1,113 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_BOOL_HPP +#define LIBDNF_CONF_OPTION_BOOL_HPP + + +#include "option.hpp" + +#include +#include +#include + +namespace libdnf { + + +class OptionBool : public Option { +public: + using ValueType = bool; + + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionBool"; } + }; + + explicit OptionBool(bool default_value); + OptionBool( + bool default_value, const std::vector & true_vals, const std::vector & false_vals); + OptionBool(const OptionBool & src); + OptionBool(OptionBool && src) noexcept = default; + ~OptionBool() override = default; + + OptionBool & operator=(const OptionBool & src); + OptionBool & operator=(OptionBool && src) noexcept = default; + + OptionBool * clone() const override; + void test(bool /*unused*/) const; + bool from_string(const std::string & value) const; + void set(Priority priority, bool value); + void set(Priority priority, const std::string & value) override; + bool get_value() const noexcept; + bool get_default_value() const noexcept; + std::string to_string(bool value) const; + std::string get_value_string() const override; + static const std::vector & get_default_true_values() noexcept; + static const std::vector & get_default_false_values() noexcept; + const std::vector & get_true_values() const noexcept; + const std::vector & get_false_values() const noexcept; + +private: + std::unique_ptr> true_values; + std::unique_ptr> false_values; + bool default_value; + bool value; +}; + + +inline OptionBool * OptionBool::clone() const { + return new OptionBool(*this); +} + +inline void OptionBool::test(bool /*unused*/) const {} + +inline bool OptionBool::get_value() const noexcept { + return value; +} + +inline bool OptionBool::get_default_value() const noexcept { + return default_value; +} + +inline std::string OptionBool::get_value_string() const { + return to_string(value); +} + +inline const std::vector & OptionBool::get_default_true_values() noexcept { + static std::vector true_values = {"1", "yes", "true", "on"}; + return true_values; +} + +inline const std::vector & OptionBool::get_default_false_values() noexcept { + static std::vector false_values = {"0", "no", "false", "off"}; + return false_values; +} + +inline const std::vector & OptionBool::get_true_values() const noexcept { + return true_values ? *true_values : get_default_true_values(); +} + +inline const std::vector & OptionBool::get_false_values() const noexcept { + return false_values ? *false_values : get_default_false_values(); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_child.hpp b/include/libdnf/conf/option_child.hpp new file mode 100644 index 0000000000..a87a6fe521 --- /dev/null +++ b/include/libdnf/conf/option_child.hpp @@ -0,0 +1,188 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_CHILD_HPP +#define LIBDNF_CONF_OPTION_CHILD_HPP + +#include "option.hpp" + +namespace libdnf { + +template +class OptionChild : public Option { +public: + explicit OptionChild(const ParentOptionType & parent); + OptionChild * clone() const override; + Priority get_priority() const override; + void set(Priority priority, const typename ParentOptionType::ValueType & value); + void set(Priority priority, const std::string & value) override; + typename ParentOptionType::ValueType get_value() const; + typename ParentOptionType::ValueType get_default_value() const; + std::string get_value_string() const override; + bool empty() const noexcept override; + +private: + const ParentOptionType * parent; + typename ParentOptionType::ValueType value; +}; + +template +class OptionChild< + ParentOptionType, + typename std::enable_if::value>::type> + : public Option { +public: + explicit OptionChild(const ParentOptionType & parent); + OptionChild * clone() const override; + Priority get_priority() const override; + void set(Priority priority, const std::string & value) override; + const std::string & get_value() const; + const std::string & get_default_value() const; + std::string get_value_string() const override; + bool empty() const noexcept override; + +private: + const ParentOptionType * parent; + std::string value; +}; + +template +inline OptionChild::OptionChild(const ParentOptionType & parent) : parent(&parent) {} + +template +inline OptionChild * OptionChild::clone() const { + return new OptionChild(*this); +} + +template +inline Option::Priority OptionChild::get_priority() const { + return Option::get_priority() != Priority::EMPTY ? Option::get_priority() : parent->get_priority(); +} + +template +inline void OptionChild::set( + Priority priority, const typename ParentOptionType::ValueType & value) { + if (priority >= Option::get_priority()) { + parent->test(value); + set_priority(priority); + this->value = value; + } +} + +template +inline void OptionChild::set(Priority priority, const std::string & value) { + if (priority >= Option::get_priority()) { + set(priority, parent->from_string(value)); + } +} + +template +inline typename ParentOptionType::ValueType OptionChild::get_value() const { + return Option::get_priority() != Priority::EMPTY ? value : parent->get_value(); +} + +template +inline typename ParentOptionType::ValueType OptionChild::get_default_value() const { + return parent->getDefaultValue(); +} + +template +inline std::string OptionChild::get_value_string() const { + return Option::get_priority() != Priority::EMPTY ? parent->to_string(value) : parent->get_value_string(); +} + +template +inline bool OptionChild::empty() const noexcept { + return Option::get_priority() == Priority::EMPTY && parent->empty(); +} + +template +inline OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>:: + OptionChild(const ParentOptionType & parent) + : parent(&parent) {} + +template +inline OptionChild< + ParentOptionType, + typename std::enable_if::value>::type> * +OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>::clone() + const { + return new OptionChild(*this); +} + +template +inline Option::Priority OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>:: + get_priority() const { + return Option::get_priority() != Priority::EMPTY ? Option::get_priority() : parent->get_priority(); +} + +template +inline void OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>:: + set(Priority priority, const std::string & value) { + auto val = parent->from_string(value); + if (priority >= Option::get_priority()) { + parent->test(val); + set_priority(priority); + this->value = val; + } +} + +template +inline const std::string & OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>::get_value() + const { + return Option::get_priority() != Priority::EMPTY ? value : parent->get_value(); +} + +template +inline const std::string & OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>:: + get_default_value() const { + return parent->getDefaultValue(); +} + +template +inline std::string OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>:: + get_value_string() const { + return Option::get_priority() != Priority::EMPTY ? value : parent->get_value(); +} + +template +inline bool OptionChild< + ParentOptionType, + typename std::enable_if::value>::type>::empty() + const noexcept { + return Option::get_priority() == Priority::EMPTY && parent->empty(); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_enum.hpp b/include/libdnf/conf/option_enum.hpp new file mode 100644 index 0000000000..2ea1ba0e3e --- /dev/null +++ b/include/libdnf/conf/option_enum.hpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_ENUM_HPP +#define LIBDNF_CONF_OPTION_ENUM_HPP + +#include "option.hpp" + +#include +#include + +namespace libdnf { + +template +class OptionEnum : public Option { +public: + using ValueType = T; + using FromStringFunc = std::function; + + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionEnum"; } + }; + + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + OptionEnum(ValueType default_value, const std::vector & enum_vals); + OptionEnum(ValueType default_value, std::vector && enum_vals); + OptionEnum(ValueType default_value, const std::vector & enum_vals, FromStringFunc && from_string_func); + OptionEnum(ValueType default_value, std::vector && enum_vals, FromStringFunc && from_string_func); + OptionEnum * clone() const override; + void test(ValueType value) const; + ValueType from_string(const std::string & value) const; + void set(Priority priority, ValueType value); + void set(Priority priority, const std::string & value) override; + T get_value() const; + T get_default_value() const; + std::string to_string(ValueType value) const; + std::string get_value_string() const override; + +private: + FromStringFunc from_string_user; + std::vector enum_vals; + ValueType default_value; + ValueType value; +}; + +template <> +class OptionEnum : public Option { +public: + using ValueType = std::string; + using FromStringFunc = std::function; + + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionEnum"; } + }; + + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + OptionEnum(const std::string & default_value, std::vector enum_vals); + OptionEnum(const std::string & default_value, std::vector enum_vals, FromStringFunc && from_string_func); + OptionEnum * clone() const override; + void test(const std::string & value) const; + std::string from_string(const std::string & value) const; + void set(Priority priority, const std::string & value) override; + const std::string & get_value() const; + const std::string & get_default_value() const; + std::string get_value_string() const override; + +private: + FromStringFunc from_string_user; + std::vector enum_vals; + ValueType default_value; + ValueType value; +}; + +template +inline OptionEnum * OptionEnum::clone() const { + return new OptionEnum(*this); +} + +inline OptionEnum * OptionEnum::clone() const { + return new OptionEnum(*this); +} + +inline const std::string & OptionEnum::get_value() const { + return value; +} + +inline const std::string & OptionEnum::get_default_value() const { + return default_value; +} + +inline std::string OptionEnum::get_value_string() const { + return value; +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_number.hpp b/include/libdnf/conf/option_number.hpp new file mode 100644 index 0000000000..bf35876eaa --- /dev/null +++ b/include/libdnf/conf/option_number.hpp @@ -0,0 +1,99 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_NUMBER_HPP +#define LIBDNF_CONF_OPTION_NUMBER_HPP + +#include "option.hpp" + +#include + +namespace libdnf { + +template +class OptionNumber : public Option { +public: + using ValueType = T; + using FromStringFunc = std::function; + + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionNumber"; } + }; + + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + OptionNumber(T default_value, T min, T max); + OptionNumber(T default_value, T min); + explicit OptionNumber(T default_value); + OptionNumber(T default_value, T min, T max, FromStringFunc && from_string_func); + OptionNumber(T default_value, T min, FromStringFunc && from_string_func); + OptionNumber(T default_value, FromStringFunc && from_string_func); + OptionNumber * clone() const override; + void test(ValueType value) const; + T from_string(const std::string & value) const; + void set(Priority priority, ValueType value); + void set(Priority priority, const std::string & value) override; + T get_value() const; + T get_default_value() const; + std::string to_string(ValueType value) const; + std::string get_value_string() const override; + +private: + FromStringFunc from_string_user; + ValueType default_value; + ValueType min; + ValueType max; + ValueType value; +}; + +template +inline OptionNumber * OptionNumber::clone() const { + return new OptionNumber(*this); +} + +template +inline T OptionNumber::get_value() const { + return value; +} + +template +inline T OptionNumber::get_default_value() const { + return default_value; +} + +template +inline std::string OptionNumber::get_value_string() const { + return to_string(value); +} + +extern template class OptionNumber; +extern template class OptionNumber; +extern template class OptionNumber; +extern template class OptionNumber; +extern template class OptionNumber; + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_path.hpp b/include/libdnf/conf/option_path.hpp new file mode 100644 index 0000000000..2d9d120796 --- /dev/null +++ b/include/libdnf/conf/option_path.hpp @@ -0,0 +1,74 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_PATH_HPP +#define LIBDNF_CONF_OPTION_PATH_HPP + +#include "option_string.hpp" + +namespace libdnf { + +/** +* @class OptionPath +* +* @brief Option for file path which can validate path existence. +* +*/ +class OptionPath : public OptionString { +public: + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionPath"; } + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + class PathNotExists : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionPath"; } + const char * get_name() const noexcept override { return "PathNotExists"; } + }; + + explicit OptionPath(const std::string & default_value, bool exists = false, bool abs_path = false); + explicit OptionPath(const char * default_value, bool exists = false, bool abs_path = false); + OptionPath( + const std::string & default_value, + const std::string & regex, + bool icase, + bool exists = false, + bool abs_path = false); + OptionPath( + const char * default_value, const std::string & regex, bool icase, bool exists = false, bool abs_path = false); + OptionPath * clone() const override; + void test(const std::string & value) const; + void set(Priority priority, const std::string & value) override; + +private: + bool exists; + bool abs_path; +}; + +inline OptionPath * OptionPath::clone() const { + return new OptionPath(*this); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_seconds.hpp b/include/libdnf/conf/option_seconds.hpp new file mode 100644 index 0000000000..37744a0654 --- /dev/null +++ b/include/libdnf/conf/option_seconds.hpp @@ -0,0 +1,71 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_SECONDS_HPP +#define LIBDNF_CONF_OPTION_SECONDS_HPP + +#include "option_number.hpp" + +namespace libdnf { + +/** +* @class OptionSeconds +* +* @brief An option representing an integer value of seconds. +* +* Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never. +* Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y. +*/ +class OptionSeconds : public OptionNumber { +public: + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionSeconds"; } + }; + + class NegativeValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "Negative value"; } + const char * get_description() const noexcept override { return "Seconds value must not be negative"; }; + }; + + class UnknownUnit : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "UnknownUnit"; } + }; + + OptionSeconds(ValueType default_value, ValueType min, ValueType max); + OptionSeconds(ValueType default_value, ValueType min); + explicit OptionSeconds(ValueType default_value); + OptionSeconds * clone() const override; + ValueType from_string(const std::string & value) const; + using OptionNumber::set; + void set(Priority priority, const std::string & value) override; +}; + +inline OptionSeconds * OptionSeconds::clone() const { + return new OptionSeconds(*this); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_string.hpp b/include/libdnf/conf/option_string.hpp new file mode 100644 index 0000000000..ab4dcc16a3 --- /dev/null +++ b/include/libdnf/conf/option_string.hpp @@ -0,0 +1,87 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_STRING_HPP +#define LIBDNF_CONF_OPTION_STRING_HPP + +#include "option.hpp" + +namespace libdnf { + +class OptionString : public Option { +public: + using ValueType = std::string; + + class InvalidValue : public Option::InvalidValue { + public: + using Option::InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionString"; } + }; + + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + class ValueNotSet : public Exception { + public: + ValueNotSet() : Exception("") {} + const char * get_domain_name() const noexcept override { return "libdnf::OptionString"; } + const char * get_name() const noexcept override { return "ValueNotSet"; } + }; + + explicit OptionString(const std::string & default_value); + explicit OptionString(const char * default_value); + OptionString(const std::string & default_value, std::string regex, bool icase); + OptionString(const char * default_value, std::string regex, bool icase); + OptionString * clone() const override; + void test(const std::string & value) const; + void set(Priority priority, const std::string & value) override; + std::string from_string(const std::string & value) const; + const std::string & get_value() const; + const std::string & get_default_value() const noexcept; + std::string get_value_string() const override; + +protected: + std::string regex; + bool icase; + std::string default_value; + std::string value; +}; + +inline OptionString * OptionString::clone() const { + return new OptionString(*this); +} + +inline const std::string & OptionString::get_default_value() const noexcept { + return default_value; +} + +inline std::string OptionString::get_value_string() const { + return get_value(); +} + +inline std::string OptionString::from_string(const std::string & value) const { + return value; +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/conf/option_string_list.hpp b/include/libdnf/conf/option_string_list.hpp new file mode 100644 index 0000000000..fe76f42b37 --- /dev/null +++ b/include/libdnf/conf/option_string_list.hpp @@ -0,0 +1,79 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_OPTION_STRING_LIST_HPP +#define LIBDNF_CONF_OPTION_STRING_LIST_HPP + +#include "option.hpp" + +#include + +namespace libdnf { + +class OptionStringList : public Option { +public: + using ValueType = std::vector; + + class NotAllowedValue : public InvalidValue { + public: + using InvalidValue::InvalidValue; + const char * get_domain_name() const noexcept override { return "libdnf::OptionStringList"; } + const char * get_name() const noexcept override { return "NotAllowedValue"; } + }; + + OptionStringList(const ValueType & default_value); + OptionStringList(const ValueType & default_value, std::string regex, bool icase); + OptionStringList(const std::string & default_value); + OptionStringList(const std::string & default_value, std::string regex, bool icase); + OptionStringList * clone() const override; + void test(const std::vector & value) const; + ValueType from_string(const std::string & value) const; + virtual void set(Priority priority, const ValueType & value); + void set(Priority priority, const std::string & value) override; + const ValueType & get_value() const; + const ValueType & get_default_value() const; + std::string to_string(const ValueType & value) const; + std::string get_value_string() const override; + +protected: + std::string regex; + bool icase; + ValueType default_value; + ValueType value; +}; + +inline OptionStringList * OptionStringList::clone() const { + return new OptionStringList(*this); +} + +inline const OptionStringList::ValueType & OptionStringList::get_value() const { + return value; +} + +inline const OptionStringList::ValueType & OptionStringList::get_default_value() const { + return default_value; +} + +inline std::string OptionStringList::get_value_string() const { + return to_string(value); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/logger/log_router.hpp b/include/libdnf/logger/log_router.hpp index ca9a1f6d4b..119a07d737 100644 --- a/include/libdnf/logger/log_router.hpp +++ b/include/libdnf/logger/log_router.hpp @@ -35,6 +35,7 @@ class LogRouter : public Logger { void add_logger(std::unique_ptr && logger) { loggers.push_back(std::move(logger)); } Logger * get_logger(size_t index) { return loggers.at(index).get(); } std::unique_ptr release_logger(size_t index); + void swap_logger(std::unique_ptr & logger, size_t index) { loggers.at(index).swap(logger); } void write(Level level, const std::string & message) noexcept override; void write(time_t time, pid_t pid, Level level, const std::string & message) noexcept override; diff --git a/include/libdnf/rpm/repo/config_repo.hpp b/include/libdnf/rpm/repo/config_repo.hpp new file mode 100644 index 0000000000..a5cc2e41e8 --- /dev/null +++ b/include/libdnf/rpm/repo/config_repo.hpp @@ -0,0 +1,103 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_RPM_REPO_CONFIG_REPO_HPP +#define LIBDNF_RPM_REPO_CONFIG_REPO_HPP + +#include "libdnf/conf/config_main.hpp" +#include "libdnf/conf/option_child.hpp" + +#include + +namespace libdnf::rpm { + +constexpr int32_t DEFAULT_REPO_PRIORITY = 99; +constexpr int32_t DEFAULT_REPO_COST = 1000; + +/** +* @class ConfigRepo +* +* @brief Holds repo configuration options +* +* Default values of some options are inherited from ConfigMain. +* +*/ +class ConfigRepo : public Config { +public: + explicit ConfigRepo(ConfigMain & master_config); + ~ConfigRepo(); + ConfigRepo(ConfigRepo && src) noexcept; + + ConfigMain & get_master_config(); + + OptionString & name(); + OptionChild & enabled(); + OptionChild & basecachedir(); + OptionStringList & baseurl(); + OptionString & mirrorlist(); + OptionString & metalink(); + OptionString & type(); + OptionString & mediaid(); + OptionStringList & gpgkey(); + OptionStringList & excludepkgs(); + OptionStringList & includepkgs(); + OptionChild & fastestmirror(); + OptionChild & proxy(); + OptionChild & proxy_username(); + OptionChild & proxy_password(); + OptionChild> & proxy_auth_method(); + OptionChild & username(); + OptionChild & password(); + OptionChild & protected_packages(); + OptionChild & gpgcheck(); + OptionChild & repo_gpgcheck(); + OptionChild & enablegroups(); + OptionChild> & retries(); + OptionChild> & bandwidth(); + OptionChild> & minrate(); + OptionChild> & ip_resolve(); + OptionChild> & throttle(); + OptionChild & timeout(); + OptionChild> & max_parallel_downloads(); + OptionChild & metadata_expire(); + OptionNumber & cost(); + OptionNumber & priority(); + OptionBool & module_hotfixes(); + OptionChild & sslcacert(); + OptionChild & sslverify(); + OptionChild & sslclientcert(); + OptionChild & sslclientkey(); + OptionChild & deltarpm(); + OptionChild> & deltarpm_percentage(); + OptionChild & skip_if_unavailable(); + // option recognized by other tools, e.g. gnome-software, but unused in dnf + OptionString & enabled_metadata(); + OptionChild & user_agent(); + OptionChild & countme(); + // yum compatibility options + OptionEnum & failovermethod(); + +private: + class Impl; + std::unique_ptr p_impl; +}; + +} // namespace libdnf::rpm + +#endif diff --git a/dnf-4/libdnf/repo/Repo.hpp b/include/libdnf/rpm/repo/repo.hpp similarity index 51% rename from dnf-4/libdnf/repo/Repo.hpp rename to include/libdnf/rpm/repo/repo.hpp index 00c2556c83..4ae44a11a8 100644 --- a/dnf-4/libdnf/repo/Repo.hpp +++ b/include/libdnf/rpm/repo/repo.hpp @@ -1,41 +1,49 @@ /* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBDNF_REPO_HPP -#define _LIBDNF_REPO_HPP +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_RPM_REPO_REPO_HPP +#define LIBDNF_RPM_REPO_REPO_HPP #define MODULEMD -#include "../conf/ConfigRepo.hpp" -#include "../hy-types.h" +#include "config_repo.hpp" #include #include namespace libdnf { -class LrException : public std::runtime_error { +class Base; + +} // namespace libdnf + +namespace libdnf::rpm { + +class LrException : public RuntimeError { public: - LrException(int code, const char * msg) : runtime_error(msg), code(code) {} - LrException(int code, const std::string & msg) : runtime_error(msg), code(code) {} - int getCode() const noexcept { return code; } + LrException(int code, const char * msg) : RuntimeError(msg), code(code) {} + LrException(int code, const std::string & msg) : RuntimeError(msg), code(code) {} + const char * get_domain_name() const noexcept override { return "librepo"; } + const char * get_name() const noexcept override { return "LrException"; } + const char * get_description() const noexcept override { return "Librepo exception"; } + int get_code() const noexcept { return code; } + private: int code; }; @@ -80,13 +88,17 @@ class RepoCB { (Do not modify or free the string) */ }; - virtual void start(const char *what) {} + virtual void start(const char * /*what*/) {} virtual void end() {} - virtual int progress(double totalToDownload, double downloaded); - virtual void fastestMirror(FastestMirrorStage stage, const char *msg); - virtual int handleMirrorFailure(const char *msg, const char *url, const char *metadata); - virtual bool repokeyImport(const std::string & id, const std::string & userId, - const std::string & fingerprint, const std::string & url, long int timestamp); + virtual int progress(double total_to_download, double downloaded); + virtual void fastest_mirror(FastestMirrorStage stage, const char * msg); + virtual int handle_mirror_failure(const char * msg, const char * url, const char * metadata); + virtual bool repokey_import( + const std::string & id, + const std::string & user_id, + const std::string & fingerprint, + const std::string & url, + long int timestamp); virtual ~RepoCB() = default; }; @@ -99,14 +111,9 @@ class RepoCB { * Remote metadata is cached locally. * */ -struct Repo { +class Repo { public: - - enum class Type { - AVAILABLE, - SYSTEM, - COMMANDLINE - }; + enum class Type { AVAILABLE, SYSTEM, COMMANDLINE }; enum class SyncStrategy { // use the local cache even if it's expired. download if there's no cache. @@ -124,7 +131,7 @@ struct Repo { * @param id repo ID to verify * @return index of the first invalid character in the repo ID (if present) or -1 */ - static int verifyId(const std::string & id); + static int verify_id(const std::string & id); /** * @brief Construct the Repo object @@ -132,11 +139,15 @@ struct Repo { * @param id repo ID to use * @param conf configuration to use */ - Repo(const std::string & id, std::unique_ptr && conf, Repo::Type type = Repo::Type::AVAILABLE); + Repo( + const std::string & id, + std::unique_ptr && conf, + Base & base, + Repo::Type type = Repo::Type::AVAILABLE); - Repo & operator =(Repo && repo) = delete; + Repo & operator=(Repo && repo) = delete; - void setCallbacks(std::unique_ptr && callbacks); + void set_callbacks(std::unique_ptr && callbacks); /** * @brief Verify repo object configuration @@ -144,12 +155,12 @@ struct Repo { * Will throw exception if Repo has no mirror or baseurl set or if Repo type is unsupported. */ void verify() const; - ConfigRepo * getConfig() noexcept; - const std::string & getId() const noexcept; + ConfigRepo * get_config() noexcept; + const std::string & get_id() const noexcept; void enable(); void disable(); - bool isEnabled() const; - bool isLocal() const; + bool is_enabled() const; + bool is_local() const; /** * @brief Initialize the repo with metadata * @@ -158,20 +169,20 @@ struct Repo { * @return true if fresh metadata were downloaded, false otherwise. */ bool load(); - bool loadCache(bool throwExcept); - void downloadMetadata(const std::string & destdir); - bool getUseIncludes() const; - void setUseIncludes(bool enabled); - bool getLoadMetadataOther() const; - void setLoadMetadataOther(bool value); - int getCost() const; - int getPriority() const; - std::string getCompsFn(); // this is temporarily made public for DNF compatibility + bool load_cache(bool throw_except); + void download_metadata(const std::string & destdir); + bool get_use_includes() const; + void set_use_includes(bool enabled); + bool get_load_metadata_other() const; + void set_load_metadata_other(bool value); + int get_cost() const; + int get_priority() const; + std::string get_comps_fn(); // this is temporarily made public for DNF compatibility #ifdef MODULEMD - std::string getModulesFn(); // temporary made public + std::string get_modules_fn(); // temporary made public #endif - const std::string & getRevision() const; - int getAge() const; + const std::string & get_revision() const; + int64_t get_age() const; /** * @brief Ask for additional repository metadata type to download @@ -180,7 +191,7 @@ struct Repo { * * @param metadataType metadata type (filelists, other, productid...) */ - void addMetadataTypeToDownload(const std::string &metadataType); + void add_metadata_type_to_download(const std::string & metadata_type); /** * @brief Stop asking for this additional repository metadata type @@ -190,7 +201,7 @@ struct Repo { * * @param metadataType metadata type (filelists, other, productid...) */ - void removeMetadataTypeFromDownload(const std::string &metadataType); + void remove_metadata_type_from_download(const std::string & metadata_type); /** * @brief Return path to the particular downloaded repository metadata in cache @@ -199,7 +210,7 @@ struct Repo { * * @return file path or empty string in case the requested metadata does not exist */ - std::string getMetadataPath(const std::string &metadataType); + std::string get_metadata_path(const std::string & metadata_type); /** * @brief Return content of the particular downloaded repository metadata @@ -210,7 +221,7 @@ struct Repo { * * @return content of metadata file or empty string in case the requested metadata does not exist */ - std::string getMetadataContent(const std::string &metadataType); + //std::string get_metadata_content(const std::string & metadataType); /** * @brief Mark whatever is in the current cache expired. @@ -225,7 +236,7 @@ struct Repo { * * @return bool */ - bool isExpired() const; + bool is_expired() const; /** * @brief Get the number of seconds after which the cached metadata will expire. @@ -234,11 +245,11 @@ struct Repo { * * @return Seconds to expiration */ - int getExpiresIn() const; + int get_expires_in() const; bool fresh(); - void setMaxMirrorTries(int maxMirrorTries); - int getTimestamp() const; - int getMaxTimestamp(); + void set_max_mirror_tries(int max_mirror_tries); + int64_t get_timestamp() const; + int get_max_timestamp(); /** * @brief Try to preserve remote side timestamps @@ -250,11 +261,11 @@ struct Repo { * * @param preserveRemoteTime true - use remote file timestamp, false - use the current time */ - void setPreserveRemoteTime(bool preserveRemoteTime); - bool getPreserveRemoteTime() const; + void set_preserve_remote_time(bool preserve_remote_time); + bool get_preserve_remote_time() const; - const std::vector & getContentTags(); - const std::vector> & getDistroTags(); + const std::vector & get_content_tags(); + const std::vector> & get_distro_tags(); /** * @brief Get list of relative locations of metadata files inside the repo @@ -263,14 +274,14 @@ struct Repo { * * @return vector of (metadata_type, location) string pairs */ - const std::vector> getMetadataLocations() const; + const std::vector> get_metadata_locations() const; - std::string getCachedir() const; - void setRepoFilePath(const std::string & path); - const std::string & getRepoFilePath() const noexcept; - void setSyncStrategy(SyncStrategy strategy); - SyncStrategy getSyncStrategy() const noexcept; - void downloadUrl(const char * url, int fd); + std::string get_cachedir() const; + void set_repo_file_path(const std::string & path); + const std::string & get_repo_file_path() const noexcept; + void set_sync_strategy(SyncStrategy strategy); + SyncStrategy get_sync_strategy() const noexcept; + void download_url(const char * url, int fd); /** * @brief Set http headers. @@ -280,30 +291,30 @@ struct Repo { * * @param headers nullptr terminated array of C strings */ - void setHttpHeaders(const char * headers[]); + void set_http_headers(const char * headers[]); /** * @brief Get array of added/changed/removed http headers. * * @return nullptr terminated array of C strings */ - const char * const * getHttpHeaders() const; - std::vector getMirrors() const; + const char * const * get_http_headers() const; + std::vector get_mirrors() const; - void setSubstitutions(const std::map & substitutions); + void set_substitutions(const std::map & substitutions); ~Repo(); class Impl; + private: friend struct PackageTarget; - friend Impl * repoGetImpl(Repo * repo); - std::unique_ptr pImpl; + std::unique_ptr p_impl; }; struct Downloader { public: - static void downloadURL(ConfigMain * cfg, const char * url, int fd); + static void download_url(ConfigMain * cfg, const char * url, int fd); }; /** @@ -316,15 +327,11 @@ struct Downloader { class PackageTargetCB { public: /** Transfer status codes */ - enum class TransferStatus { - SUCCESSFUL, - ALREADYEXISTS, - ERROR - }; + enum class TransferStatus { SUCCESSFUL, ALREADYEXISTS, ERROR }; virtual int end(TransferStatus status, const char * msg); - virtual int progress(double totalToDownload, double downloaded); - virtual int mirrorFailure(const char *msg, const char *url); + virtual int progress(double total_to_download, double downloaded); + virtual int mirror_failure(const char * msg, const char * url); virtual ~PackageTargetCB() = default; }; @@ -340,40 +347,59 @@ struct PackageTarget { */ enum class ChecksumType { UNKNOWN, - MD5, /* The most weakest hash */ - SHA1, /* | */ - SHA224, /* | */ - SHA256, /* | */ - SHA384, /* \|/ */ - SHA512, /* The most secure hash */ + MD5, /* The most weakest hash */ + SHA1, /* | */ + SHA224, /* | */ + SHA256, /* | */ + SHA384, /* \|/ */ + SHA512, /* The most secure hash */ }; - static ChecksumType checksumType(const std::string & name); - static void downloadPackages(std::vector & targets, bool failFast); - - PackageTarget(Repo * repo, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks); - PackageTarget(ConfigMain * cfg, const char * relativeUrl, const char * dest, int chksType, - const char * chksum, int64_t expectedSize, const char * baseUrl, bool resume, - int64_t byteRangeStart, int64_t byteRangeEnd, PackageTargetCB * callbacks, - const char * httpHeaders[] = nullptr); + static ChecksumType checksum_type(const std::string & name); + static void download_packages(std::vector & targets, bool fail_fast); + + PackageTarget( + Repo * repo, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks); + PackageTarget( + ConfigMain * cfg, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks, + const char * http_headers[] = nullptr); ~PackageTarget(); - PackageTargetCB * getCallbacks(); - const char * getErr(); + PackageTargetCB * get_callbacks(); + const char * get_err(); + private: class Impl; - std::unique_ptr pImpl; + std::unique_ptr p_impl; }; struct LibrepoLog { public: - static long addHandler(const std::string & filePath, bool debug = false); - static void removeHandler(long uid); - static void removeAllHandlers(); + static long add_handler(const std::string & file_path, bool debug = false); + static void remove_handler(long uid); + static void remove_all_handlers(); }; -} +} // namespace libdnf::rpm #endif diff --git a/include/libdnf/rpm/repo/repo_query.hpp b/include/libdnf/rpm/repo/repo_query.hpp new file mode 100644 index 0000000000..1afeb1b647 --- /dev/null +++ b/include/libdnf/rpm/repo/repo_query.hpp @@ -0,0 +1,70 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_RPM_REPO_REPO_QUERY_HPP +#define LIBDNF_RPM_REPO_REPO_QUERY_HPP + +#include "libdnf/common/sack/query.hpp" +#include "libdnf/rpm/repo/repo.hpp" +#include "libdnf/utils/weak_ptr.hpp" + +namespace libdnf::rpm { + +using RepoWeakPtr = libdnf::WeakPtr; + +class RepoQuery : public libdnf::sack::Query { +public: + using Query::Query; + + struct F { + static bool enabled(const RepoWeakPtr & obj) { return obj->is_enabled(); } + static bool expired(const RepoWeakPtr & obj) { return obj->is_expired(); } + static bool local(const RepoWeakPtr & obj) { return obj->is_local(); } + static std::string id(const RepoWeakPtr & obj) { return obj->get_id(); } + }; + + RepoQuery & ifilter_enabled(bool enabled); + RepoQuery & ifilter_expired(bool expired); + RepoQuery & ifilter_local(bool local); + RepoQuery & ifilter_id(sack::QueryCmp cmp, const std::string & pattern); +}; + +inline RepoQuery & RepoQuery::ifilter_enabled(bool enabled) { + ifilter(F::enabled, sack::QueryCmp::EQ, enabled); + return *this; +} + +inline RepoQuery & RepoQuery::ifilter_expired(bool expired) { + ifilter(F::expired, sack::QueryCmp::EQ, expired); + return *this; +} + +inline RepoQuery & RepoQuery::ifilter_local(bool local) { + ifilter(F::local, sack::QueryCmp::EQ, local); + return *this; +} + +inline RepoQuery & RepoQuery::ifilter_id(sack::QueryCmp cmp, const std::string & pattern) { + ifilter(F::id, cmp, pattern); + return *this; +} + +} // namespace libdnf::rpm + +#endif diff --git a/include/libdnf/rpm/repo/repo_sack.hpp b/include/libdnf/rpm/repo/repo_sack.hpp new file mode 100644 index 0000000000..41572ab8b0 --- /dev/null +++ b/include/libdnf/rpm/repo/repo_sack.hpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_RPM_REPO_REPO_SACK_HPP +#define LIBDNF_RPM_REPO_REPO_SACK_HPP + +#include "repo.hpp" +#include "repo_query.hpp" + +#include +#include "libdnf/common/sack/sack.hpp" +#include "libdnf/logger/logger.hpp" + +namespace libdnf { + +class Base; + +} // namespace libdnf + +namespace libdnf::rpm { + +class RepoSack : public libdnf::sack::Sack { +public: + explicit RepoSack(Base & base) : base(&base) {} + + RepoWeakPtr new_repo(const std::string & id); + + void new_repos_from_file(const std::filesystem::path & path); + void new_repos_from_file(); + void new_repos_from_dir(const std::filesystem::path & path); + void new_repos_from_dirs(); + +private: + Base * base; +}; + +} // namespace libdnf::rpm + +#endif diff --git a/include/libdnf/utils/exception.hpp b/include/libdnf/utils/exception.hpp new file mode 100644 index 0000000000..acbd3de972 --- /dev/null +++ b/include/libdnf/utils/exception.hpp @@ -0,0 +1,75 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_EXCEPTION_HPP +#define LIBDNF_UTILS_EXCEPTION_HPP + + +#include +#include + +namespace libdnf { + +class Exception : public std::runtime_error { +public: + using runtime_error::runtime_error; + virtual const char * get_domain_name() const noexcept { return "libdnf"; } + virtual const char * get_name() const noexcept { return "Exception"; } + virtual const char * get_description() const noexcept { return ""; } +}; + +class LogicError : public Exception { +public: + using Exception::Exception; + const char * get_name() const noexcept override { return "LogicError"; } + const char * get_description() const noexcept override { return "General LogicError exception"; } +}; + +class RuntimeError : public Exception { +public: + using Exception::Exception; + const char * get_name() const noexcept override { return "RuntimeError"; } + const char * get_description() const noexcept override { return "General RuntimeError exception"; } +}; + +class SystemError : public RuntimeError { +public: + SystemError(int code, const std::string & what) : RuntimeError(what), error_code(code) {} + SystemError(int code, const char * what) : RuntimeError(what), error_code(code) {} + const char * get_domain_name() const noexcept override { return "system"; } + const char * get_name() const noexcept override { return "SystemError"; } + const char * get_description() const noexcept override { return "System error"; } + const char * what() const noexcept override { + try { + str_what = std::system_category().default_error_condition(error_code).message(); + return str_what.c_str(); + } catch (...) { + return ""; + } + } + int get_code() const noexcept { return error_code; } + +private: + int error_code; + mutable std::string str_what; +}; + +} // namespace libdnf + +#endif diff --git a/include/libdnf/utils/iniparser.hpp b/include/libdnf/utils/iniparser.hpp new file mode 100644 index 0000000000..861109cd83 --- /dev/null +++ b/include/libdnf/utils/iniparser.hpp @@ -0,0 +1,174 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_INIPARSER_HPP +#define LIBDNF_UTILS_INIPARSER_HPP + +#include "exception.hpp" + +#include +#include +#include + +namespace libdnf { + +/** +* @class IniParser +* +* @brief Simple .INI file parser +* +* The goal is to be compatible with dnf / yum .ini configuration files. +*/ +class IniParser { +public: + class Exception : public RuntimeError { + public: + using RuntimeError::RuntimeError; + const char * get_domain_name() const noexcept override { return "libdnf::IniParser"; } + const char * get_name() const noexcept override { return "Exception"; } + const char * get_description() const noexcept override { return "IniParser exception"; } + }; + + class CantOpenFile : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "CantOpenFile"; } + const char * get_description() const noexcept override { return "Can't open file"; } + }; + + class MissingSectionHeader : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "MissingSectionHeader"; } + const char * get_description() const noexcept override { return "Missing section header"; } + }; + + class MissingBracket : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "MissingBracket"; } + const char * get_description() const noexcept override { return "Missing ']'"; } + }; + + class EmptySectionName : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "EmptySectionName"; } + const char * get_description() const noexcept override { return "Empty section name"; } + }; + + class TextAfterSection : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "TextAfterSection"; } + const char * get_description() const noexcept override { return "Text after section"; } + }; + + class IllegalContinuationLine : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "IllegalContinuationLine"; } + const char * get_description() const noexcept override { return "Illegal continuation line"; } + }; + + class MissingKey : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "MissingKey"; } + const char * get_description() const noexcept override { return "Missing key"; } + }; + + class MissingEqual : public Exception { + public: + using Exception::Exception; + const char * get_name() const noexcept override { return "MissingEqual"; } + const char * get_description() const noexcept override { return "Missing '='"; } + }; + + enum class ItemType { + SECTION, // [section_name] + KEY_VAL, // key = value, (multiline value supported) + COMMENT_LINE, // line starting with '#' or ';' character + EMPTY_LINE, // zero length or only contains whitespace characters + END_OF_INPUT + }; + + explicit IniParser(const std::string & file_path); + explicit IniParser(std::unique_ptr && input_stream); + /** + * @brief Parse one item from input file + * + * Returns type of parsed item. Parsed values can be obtained by methods + * getSection(), getKey(), getValue(). + * + * @return IniParser::ItemType Type of parsed value + */ + ItemType next(); + const std::string & get_section() const noexcept; + const std::string & get_key() const noexcept; + std::string & get_key() noexcept; + const std::string & get_value() const noexcept; + std::string & get_value() noexcept; + const std::string & get_raw_item() const noexcept; + std::string & get_raw_item() noexcept; + const std::string & get_line() const noexcept; + void clear_line() noexcept; + void trim_value() noexcept; + +private: + std::unique_ptr is; + int line_number; + std::string section; + std::string key; + std::string value; + std::string raw_item; + std::string line; +}; + +inline const std::string & IniParser::get_section() const noexcept { + return section; +} +inline const std::string & IniParser::get_key() const noexcept { + return key; +} +inline std::string & IniParser::get_key() noexcept { + return key; +} +inline const std::string & IniParser::get_value() const noexcept { + return value; +} +inline std::string & IniParser::get_value() noexcept { + return value; +} +inline const std::string & IniParser::get_raw_item() const noexcept { + return raw_item; +} +inline std::string & IniParser::get_raw_item() noexcept { + return raw_item; +} +inline const std::string & IniParser::get_line() const noexcept { + return line; +} +inline void IniParser::clear_line() noexcept { + line.clear(); +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/utils/preserve_order_map.hpp b/include/libdnf/utils/preserve_order_map.hpp new file mode 100644 index 0000000000..4d010c8b9d --- /dev/null +++ b/include/libdnf/utils/preserve_order_map.hpp @@ -0,0 +1,190 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_PRESERVE_ORDER_MAP_HPP +#define LIBDNF_UTILS_PRESERVE_ORDER_MAP_HPP + +#include +#include +#include +#include + +namespace libdnf { + +template > +class PreserveOrderMap { +public: + using key_type = Key; + using mapped_type = T; + using key_equal = KeyEqual; + using value_type = std::pair; + using container_type = std::vector>; + using size_type = typename container_type::size_type; + + template + struct MyBidirIterator { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename PreserveOrderMap::value_type; + using difference_type = ptrdiff_t; + using pointer = valueType *; + using reference = valueType &; + + explicit MyBidirIterator() = default; + explicit MyBidirIterator(ContainerTypeIterator ci) : ci(ci) {} + + reference operator*() { return reinterpret_cast(*ci); } + pointer operator->() const { return reinterpret_cast(ci.operator->()); } + + MyBidirIterator & operator++() { + ++ci; + return *this; + } + MyBidirIterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + MyBidirIterator & operator--() { + --ci; + return *this; + } + MyBidirIterator operator--(int) { + auto tmp = *this; + --*this; + return tmp; + } + + bool operator==(const MyBidirIterator & other) const { return ci == other.ci; } + bool operator!=(const MyBidirIterator & other) const { return ci != other.ci; } + + private: + friend class PreserveOrderMap; + ContainerTypeIterator ci; + }; + using iterator = MyBidirIterator; + using const_iterator = MyBidirIterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + bool empty() const noexcept { return items.empty(); } + size_type size() const noexcept { return items.size(); } + size_type max_size() const noexcept { return items.max_size(); } + void reserve(size_type new_capacity) { items.reserve(new_capacity); } + size_type capacity() const noexcept { return items.capacity(); } + void shrink_to_fit() { items.shrink_to_fit(); } + + iterator begin() noexcept { return iterator(items.begin()); } + const_iterator begin() const noexcept { return const_iterator(items.begin()); } + const_iterator cbegin() const noexcept { return const_iterator(items.cbegin()); } + iterator end() noexcept { return iterator(items.end()); } + const_iterator end() const noexcept { return const_iterator(items.end()); } + const_iterator cend() const noexcept { return const_iterator(items.cend()); } + reverse_iterator rbegin() noexcept { return reverse_iterator(items.rbegin()); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(items.rbegin()); } + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(items.crbegin()); } + reverse_iterator rend() noexcept { return reverse_iterator(items.rend()); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(items.rend()); } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(items.crend()); } + + void clear() noexcept { items.clear(); } + + std::pair insert(const value_type & value) { + auto it = find(value.first); + if (it == end()) { + it = iterator(items.insert(it.ci, value)); + return {it, true}; + } + return {it, false}; + } + + iterator erase(const_iterator pos) { return iterator(items.erase(pos.ci)); } + iterator erase(const_iterator first, const_iterator last) { return iterator(items.erase(first.ci, last.ci)); } + + size_type erase(const Key & key) { + auto it = find(key); + if (it == end()) { + return 0; + } + items.erase(it.ci); + return 1; + } + + size_type count(const Key & key) const { return find(key) != end() ? 1 : 0; } + + iterator find(const Key & key) { + auto it = begin(); + while (it != end() && !KeyEqual()(it->first, key)) { + ++it; + } + return it; + } + + const_iterator find(const Key & key) const { + auto it = cbegin(); + while (it != cend() && !KeyEqual()(it->first, key)) { + ++it; + } + return it; + } + + T & operator[](const Key & key) { + for (auto & item : items) { + if (KeyEqual()(item.first, key)) { + return item.second; + } + } + items.push_back({key, {}}); + return items.back().second; + } + + T & operator[](Key && key) { + for (auto & item : items) { + if (KeyEqual()(item.first, key)) { + return item.second; + } + } + items.push_back({std::move(key), {}}); + return items.back().second; + } + + T & at(const Key & key) { + for (auto & item : items) { + if (KeyEqual()(item.first, key)) { + return item.second; + } + } + throw std::out_of_range("PreserveOrderMap::at"); + } + + const T & at(const Key & key) const { + for (auto & item : items) { + if (KeyEqual()(item.first, key)) { + return item.second; + } + } + throw std::out_of_range("PreserveOrderMap::at"); + } + +private: + container_type items; +}; + +} // namespace libdnf + +#endif diff --git a/include/libdnf/utils/set.hpp b/include/libdnf/utils/set.hpp new file mode 100644 index 0000000000..f67897a03c --- /dev/null +++ b/include/libdnf/utils/set.hpp @@ -0,0 +1,165 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_SET_HPP +#define LIBDNF_UTILS_SET_HPP + +#include +#include + +namespace libdnf { + +/// Set represents set of objects (e.g. packages, or groups) +/// and implements set operations such as unions or differences. +template +class Set { +public: + Set() = default; + Set(const Set & other) : data(other.data) {} + Set(Set && other) : data(std::move(other.data)) {} + Set(std::initializer_list ilist) : data(ilist) {} + ~Set() = default; + + // GENERIC OPERATIONS + bool empty() const noexcept { return data.empty(); }; + std::size_t size() const noexcept { return data.size(); } + void clear() noexcept { data.clear(); } + + // ITEM OPERATIONS + + void add(const T & obj) { data.insert(obj); } + void add(T && obj) { data.insert(std::move(obj)); } + void remove(const T & obj) { data.erase(obj); } + void swap(Set & other) noexcept { data.swap(other); } + + bool contains(const T & obj) const { return data.find(obj) != data.end(); } + bool is_subset(const Set & other) const; + bool is_superset(const Set & other) const; + + // SET OPERATIONS + Set & operator =(const Set & other); + Set & operator =(Set && other) noexcept; + Set & operator =(std::initializer_list ilist); + Set & operator |=(const Set & other); + Set & operator &=(const Set & other); + Set & operator -=(const Set & other); + Set & operator ^=(const Set & other); + + // update == union + void update(const Set & other) { *this |= other; } + void difference(const Set & other) { *this -= other; } + void intersection(const Set & other) { *this &= other; } + void symmetric_difference(const Set & other) { *this ^= other; } + + /// Return reference to underlying std::set + const std::set & get_data() const noexcept { return data; } + std::set & get_data() noexcept { return data; } + +private: + std::set data; +}; + +template +inline Set & Set::operator =(const Set & other) { + data = other.data; + return *this; +} + +template +inline Set & Set::operator =(Set && other) noexcept { + data = std::move(other.data); + return *this; +} + +template +inline Set & Set::operator =(std::initializer_list ilist) { + data = ilist; + return *this; +} + +template +inline bool Set::is_subset(const Set & other) const { + return std::includes(other.data.begin(), other.data.end(), data.begin(), data.end()); +} + +template +inline bool Set::is_superset(const Set & other) const { + return std::includes(data.begin(), data.end(), other.data.begin(), other.data.end()); +} + +template +inline Set & Set::operator |=(const Set & other) { + data.merge(other); + return *this; +} + +template +inline Set & Set::operator &=(const Set & other) { + std::set result; + std::set_intersection( + data.begin(), data.end(), other.data.begin(), other.data.end(), std::inserter(result, result.begin())); + data = std::move(result); + return *this; +} + +template +inline Set & Set::operator -=(const Set & other) { + std::set result; + std::set_difference( + data.begin(), data.end(), other.data.begin(), other.data.end(), std::inserter(result, result.begin())); + data = result; + return *this; +} + +template +inline Set & Set::operator ^=(const Set & other) { + std::set result; + std::set_symmetric_difference( + data.begin(), data.end(), other.data.begin(), other.data.end(), std::inserter(result, result.begin())); + data = result; + return *this; +} + +template +inline Set operator |(const Set & lhs, const Set & rhs) { + Set ret(lhs); + return ret |= rhs; +} + +template +inline Set operator &(const Set & lhs, const Set & rhs) { + Set ret(lhs); + return ret &= rhs; +} + +template +inline Set operator -(const Set & lhs, const Set & rhs) { + Set ret(lhs); + return ret -= rhs; +} + +template +inline Set operator ^(const Set & lhs, const Set & rhs) { + Set ret(lhs); + return ret ^= rhs; +} + +} // namespace libdnf + +#endif diff --git a/include/libdnf/utils/utils.hpp b/include/libdnf/utils/utils.hpp new file mode 100644 index 0000000000..ce5415f8a5 --- /dev/null +++ b/include/libdnf/utils/utils.hpp @@ -0,0 +1,29 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_UTILS_HPP +#define LIBDNF_UTILS_UTILS_HPP + +namespace libdnf { + +bool have_files_same_content(const char * file_path1, const char * file_path2); + +} // namespace libdnf + +#endif diff --git a/include/libdnf/utils/weak_ptr.hpp b/include/libdnf/utils/weak_ptr.hpp new file mode 100644 index 0000000000..1d2547519f --- /dev/null +++ b/include/libdnf/utils/weak_ptr.hpp @@ -0,0 +1,237 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_UTILS_WEAK_PTR_HPP +#define LIBDNF_UTILS_WEAK_PTR_HPP + +#include "exception.hpp" + +#include + +namespace libdnf { + +template +struct WeakPtr; + + +template +struct WeakPtrGuard { +public: + using TWeakPtr = WeakPtr; + + WeakPtrGuard() = default; + WeakPtrGuard(const WeakPtrGuard &) = delete; + WeakPtrGuard(WeakPtrGuard && src) noexcept : registered_weak_ptrs(std::move(src.registered_weak_ptrs)) { + for (auto it : registered_weak_ptrs) { + it->guard = this; + } + } + ~WeakPtrGuard() { clear(); } + + WeakPtrGuard & operator=(const WeakPtrGuard & src) = delete; + WeakPtrGuard & operator=(WeakPtrGuard && src) noexcept { + registered_weak_ptrs = std::move(src.registered_weak_ptrs); + for (auto it : registered_weak_ptrs) { + it->guard = this; + } + } + + /// Returns true if the guard is empty, false otherwise. + bool empty() const noexcept { return registered_weak_ptrs.empty(); } + + /// Returns the number of registered weak pointers. + size_t size() const noexcept { return registered_weak_ptrs.size(); } + + /// Deregisters and invalidates all registered weak pointers. After this call, size() returns zero. + void clear() noexcept { + for (auto it : registered_weak_ptrs) { + it->invalidate_guard(); + } + } + +private: + friend TWeakPtr; + void register_ptr(TWeakPtr * weak_ptr) { registered_weak_ptrs.insert(weak_ptr); } + void unregister_ptr(TWeakPtr * weak_ptr) noexcept { registered_weak_ptrs.erase(weak_ptr); } + std::unordered_set registered_weak_ptrs; +}; + + +template +struct WeakPtr { +public: + using TWeakPtrGuard = WeakPtrGuard; + + class InvalidPtr : public RuntimeError { + public: + using RuntimeError::RuntimeError; + const char * get_domain_name() const noexcept override { return "libdnf::WeakPtr"; } + const char * get_name() const noexcept override { return "InvalidPtr"; } + const char * get_description() const noexcept override { return "Invalid pointer"; } + }; + + WeakPtr() = delete; + + WeakPtr(TPtr * ptr, TWeakPtrGuard * guard) : ptr(ptr), guard(guard) { + if (!ptr) { + throw InvalidPtr("Creating null WeakPtr is not allowed"); + } + if (!guard) { + throw InvalidPtr("Creating WeakPtr without guard is not allowed"); + } + guard->register_ptr(this); + } + + // TODO(jrohel): Want we to allow copying invalid WeakPtr? + WeakPtr(const WeakPtr & src) : guard(src.guard) { + if constexpr (ptr_owner) { + ptr = src.ptr ? new TPtr(*src.ptr) : nullptr; + } else { + ptr = src.ptr; + } + if (is_valid()) { + guard->register_ptr(this); + } + } + + // TODO(jrohel): Want we to allow moving invalid WeakPtr? + template ::type = 0> + WeakPtr(WeakPtr && src) : guard(src.guard) { + if (src.is_valid()) { + src.guard->unregister_ptr(src); + } + src.guard = nullptr; + ptr = src.ptr; + src.ptr = nullptr; + if (is_valid()) { + guard->register_ptr(this); + } + } + + ~WeakPtr() { + if (is_valid()) { + guard->unregister_ptr(this); + } + if constexpr (ptr_owner) { + delete ptr; + } + } + + // TODO(jrohel): Want we to allow copying invalid WeakPtr? + template ::type = 0> + WeakPtr & operator=(const WeakPtr & src) { + if (guard == src.guard) { + delete ptr; + ptr = src.ptr ? new TPtr(*src.ptr) : nullptr; + } else { + if (is_valid()) { + guard->unregister_ptr(this); + } + guard = src.guard; + delete ptr; + ptr = src.ptr ? new TPtr(*src.ptr) : nullptr; + if (is_valid()) { + guard->register_ptr(this); + } + } + return *this; + } + + // TODO(jrohel): Want we to allow copying invalid WeakPtr? + template ::type = 0> + WeakPtr & operator=(const WeakPtr & src) { + if (guard == src.guard) { + ptr = src.ptr; + } else { + if (is_valid()) { + guard->unregister_ptr(this); + } + guard = src.guard; + ptr = src.ptr; + if (is_valid()) { + guard->register_ptr(this); + } + } + return *this; + } + + // TODO(jrohel): Want we to allow moving invalid WeakPtr? + template ::type = 0> + WeakPtr & operator=(WeakPtr && src) { + if (guard == src.guard) { + if (src.is_valid()) { + src.guard->unregister_ptr(src); + } + src.guard = nullptr; + ptr = src.ptr; + src.ptr = nullptr; + } else { + if (is_valid()) { + guard->unregister_ptr(this); + } + if (src.is_valid()) { + src.guard->unregister_ptr(src); + } + guard = src.guard; + src.guard = nullptr; + ptr = src.ptr; + src.ptr = nullptr; + if (is_valid()) { + guard->register_ptr(this); + } + } + return *this; + } + + TPtr * operator->() const { + check(); + return ptr; + } + + TPtr * get() const { + check(); + return ptr; + } + + bool is_valid() const noexcept { return guard; } + bool has_same_guard(const WeakPtr & other) const noexcept { return guard == other.guard; } + + bool operator==(const WeakPtr & other) const { return ptr == other.ptr; } + bool operator!=(const WeakPtr & other) const { return ptr != other.ptr; } + bool operator<(const WeakPtr & other) const { return ptr < other.ptr; } + bool operator>(const WeakPtr & other) const { return ptr > other.ptr; } + bool operator<=(const WeakPtr & other) const { return ptr <= other.ptr; } + bool operator>=(const WeakPtr & other) const { return ptr >= other.ptr; } + +private: + friend TWeakPtrGuard; + void invalidate_guard() noexcept { guard = nullptr; } + void check() const { + if (!is_valid()) { + throw InvalidPtr("Data guard is invalid"); + } + } + + TPtr * ptr; + TWeakPtrGuard * guard; +}; + +} // namespace libdnf + +#endif diff --git a/libdnf/CMakeLists.txt b/libdnf/CMakeLists.txt index cd52084ffe..0103745a1a 100644 --- a/libdnf/CMakeLists.txt +++ b/libdnf/CMakeLists.txt @@ -6,6 +6,8 @@ file(GLOB_RECURSE LIBDNF_SOURCES *.cpp) list(APPEND LIBDNF_PC_REQUIRES) list(APPEND LIBDNF_PC_REQUIRES_PRIVATE) +# set gettext domain for translations +add_definitions(-DGETTEXT_DOMAIN=\"libdnf\") # build libdnf.so add_library(libdnf SHARED ${LIBDNF_SOURCES}) @@ -21,6 +23,10 @@ install(TARGETS libdnf LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) # link libraries and set pkg-config requires +pkg_check_modules(LIBFMT REQUIRED fmt) +list(APPEND LIBDNF_PC_REQUIRES "${LIBFMT_MODULE_NAME}") +target_link_libraries(libdnf ${LIBFMT_LIBRARIES}) + pkg_check_modules(LIBSOLV REQUIRED libsolv>=0.7.7) list(APPEND LIBDNF_PC_REQUIRES "${LIBSOLV_MODULE_NAME}") target_link_libraries(libdnf ${LIBSOLV_LIBRARIES}) @@ -39,6 +45,20 @@ if(WITH_COMPS) target_link_libraries(libdnf ${LIBCOMPS_LIBRARIES}) endif() +# GLIB librepo and libmodulemd uses glib2 in API :( +pkg_check_modules (GLIB2 glib-2.0>=2.46.0) +include_directories(${GLIB2_INCLUDE_DIRS}) +target_link_libraries(libdnf ${GLIB2_LIBRARIES}) + +# GPGME +PKG_CHECK_MODULES(GPGME gpgme REQUIRED) +list(APPEND LIBDNF_PC_REQUIRES "${LIBREPO_MODULE_NAME}") +target_link_libraries(libdnf ${GPGME_LIBRARIES}) + +pkg_check_modules(LIBREPO REQUIRED librepo) +list(APPEND LIBDNF_PC_REQUIRES "${LIBREPO_MODULE_NAME}") +target_link_libraries(libdnf ${LIBREPO_LIBRARIES}) + # sort the pkg-config requires and concatenate them into a string list(SORT LIBDNF_PC_REQUIRES) diff --git a/libdnf/base/base.cpp b/libdnf/base/base.cpp new file mode 100644 index 0000000000..2e6faeba3c --- /dev/null +++ b/libdnf/base/base.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/base/base.hpp" + +#include "libdnf/conf/config_parser.hpp" +#include "libdnf/conf/const.hpp" + +#include + +#include +#include +#include + +namespace libdnf { + +static void load_config_from_parser( + Config & conf, const ConfigParser & parser, const std::string & section, Logger & log) { + const auto & cfg_parser_data = parser.get_data(); + auto cfg_parser_data_iter = cfg_parser_data.find(section); + if (cfg_parser_data_iter != cfg_parser_data.end()) { + auto opt_binds = conf.opt_binds(); + const auto & cfg_parser_main_sect = cfg_parser_data_iter->second; + for (const auto & opt : cfg_parser_main_sect) { + auto opt_binds_iter = opt_binds.find(opt.first); + if (opt_binds_iter != opt_binds.end()) { + try { + opt_binds_iter->second.new_string(libdnf::Option::Priority::MAINCONFIG, opt.second); + } catch (const Option::Exception & ex) { + auto msg = fmt::format( + R"**(Config error in section "{}" key "{}": {}: {})**", + section, + opt.first, + ex.get_description(), + ex.what()); + log.warning(msg); + } + } + } + } +} + +static void load_config_from_file_path( + Config & conf, const std::filesystem::path & file, const std::string & section, Logger & log) { + log.debug(fmt::format(R"**(Start of loading section "{}" from file "{}")**", section, file.string())); + ConfigParser parser; + parser.read(file.string()); + load_config_from_parser(conf, parser, section, log); + log.debug(fmt::format(R"**(Loading of section "{}" from file "{}" done)**", section, file.string())); +} + +void Base::load_config_from_file(const std::filesystem::path & path) { + load_config_from_file_path(config_main, path, "main", log_router); +} + +void Base::load_config_from_file() { + load_config_from_file(config_main.config_file_path().get_value()); +} + +void Base::load_config_from_dir(const std::filesystem::path & dir_path) { + std::vector paths; + for (auto & dentry : std::filesystem::directory_iterator(dir_path)) { + auto & path = dentry.path(); + if (path.extension() == ".conf") { + paths.push_back(path); + } + } + std::sort(paths.begin(), paths.end()); + for (auto & path : paths) { + load_config_from_file_path(*get_config(), path, "main", *get_logger()); + } +} + +void Base::load_config_from_dir() { + load_config_from_dir(libdnf::CONF_DIRECTORY); +} + +} // namespace libdnf diff --git a/libdnf/common/sack/match_int64.cpp b/libdnf/common/sack/match_int64.cpp new file mode 100644 index 0000000000..ee19c28592 --- /dev/null +++ b/libdnf/common/sack/match_int64.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/common/sack/match_int64.hpp" + +#include "libdnf/utils/exception.hpp" + +#include + + +namespace libdnf::sack { + + +bool match_int64(int64_t value, QueryCmp cmp, int64_t pattern) { + bool result = false; + switch (cmp) { + case QueryCmp::EQ: + result = value == pattern; + break; + case QueryCmp::NEQ: + result = value != pattern; + break; + case QueryCmp::LT: + result = value < pattern; + break; + case QueryCmp::LTE: + result = value <= pattern; + break; + case QueryCmp::GT: + result = value > pattern; + break; + case QueryCmp::GTE: + result = value >= pattern; + break; + case QueryCmp::IEXACT: + case QueryCmp::GLOB: + case QueryCmp::IGLOB: + case QueryCmp::REGEX: + case QueryCmp::IREGEX: + case QueryCmp::CONTAINS: + case QueryCmp::ICONTAINS: + case QueryCmp::STARTSWITH: + case QueryCmp::ISTARTSWITH: + case QueryCmp::ENDSWITH: + case QueryCmp::IENDSWITH: + case QueryCmp::ISNULL: + throw std::runtime_error("Unsupported operator"); + break; + case QueryCmp::NOT: + case QueryCmp::ICASE: + throw std::runtime_error("Operator flag cannot be used standalone"); + break; + } + return result; +} + + +bool match_int64(int64_t value, QueryCmp cmp, const std::vector & patterns) { + bool result = false; + for (auto & pattern : patterns) { + if (match_int64(value, cmp, pattern)) { + result = true; + break; + } + } + return result; +} + + +bool match_int64(const std::vector & values, QueryCmp cmp, int64_t pattern) { + bool result = false; + for (auto & value : values) { + if (match_int64(value, cmp, pattern)) { + result = true; + break; + } + } + return result; +} + + +bool match_int64(const std::vector & values, QueryCmp cmp, const std::vector & patterns) { + bool result = false; + bool found = false; + for (auto & value : values) { + for (auto & pattern : patterns) { + if (match_int64(value, cmp, pattern)) { + result = true; + found = true; + break; + } + } + if (found) { + break; + } + } + return result; +} + + +} // namespace libdnf::sack diff --git a/libdnf/common/sack/match_string.cpp b/libdnf/common/sack/match_string.cpp new file mode 100644 index 0000000000..55383650e7 --- /dev/null +++ b/libdnf/common/sack/match_string.cpp @@ -0,0 +1,131 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/common/sack/match_string.hpp" + +#include + +#include +#include + + +namespace libdnf::sack { + + +inline std::string tolower(const std::string & s) { + std::string result = s; + std::for_each(result.begin(), result.end(), [](char & c) { c = static_cast(::tolower(c)); }); + return result; +} + + +bool match_string(const std::string & value, QueryCmp cmp, const std::string & pattern) { + bool result = false; + switch (cmp) { + case QueryCmp::EXACT: + result = value == pattern; + break; + case QueryCmp::NEQ: + result = value != pattern; + break; + case QueryCmp::IEXACT: + result = tolower(value) == tolower(pattern); + break; + case QueryCmp::GLOB: + result = fnmatch(pattern.c_str(), value.c_str(), FNM_EXTMATCH) == 0; + break; + case QueryCmp::IGLOB: + result = fnmatch(pattern.c_str(), value.c_str(), FNM_CASEFOLD | FNM_EXTMATCH) == 0; + break; + case QueryCmp::REGEX: { + std::regex re(pattern); + result = std::regex_match(value, re); + } break; + case QueryCmp::IREGEX: { + std::regex re(pattern, std::regex::icase); + result = std::regex_match(value, re); + } break; + case QueryCmp::CONTAINS: + case QueryCmp::ICONTAINS: + case QueryCmp::STARTSWITH: + case QueryCmp::ISTARTSWITH: + case QueryCmp::ENDSWITH: + case QueryCmp::IENDSWITH: + throw std::runtime_error("Not implemented yet"); + break; + case QueryCmp::ISNULL: + case QueryCmp::LT: + case QueryCmp::LTE: + case QueryCmp::GT: + case QueryCmp::GTE: + throw std::runtime_error("Unsupported operator"); + break; + case QueryCmp::NOT: + case QueryCmp::ICASE: + throw std::runtime_error("Operator flag cannot be used standalone"); + break; + } + return result; +} + + +bool match_string(const std::string & value, QueryCmp cmp, const std::vector & patterns) { + bool result = false; + for (auto & pattern : patterns) { + if (match_string(value, cmp, pattern)) { + result = true; + break; + } + } + return result; +} + + +bool match_string(const std::vector & values, QueryCmp cmp, const std::string & pattern) { + bool result = false; + for (auto & value : values) { + if (match_string(value, cmp, pattern)) { + result = true; + break; + } + } + return result; +} + + +bool match_string(const std::vector & values, QueryCmp cmp, const std::vector & patterns) { + bool result = false; + bool found = false; + for (auto & value : values) { + for (auto & pattern : patterns) { + if (match_string(value, cmp, pattern)) { + result = true; + found = true; + break; + } + } + if (found) { + break; + } + } + return result; +} + + +} // namespace libdnf::sack diff --git a/libdnf/conf/config-private.hpp b/libdnf/conf/config-private.hpp new file mode 100644 index 0000000000..e278825b0a --- /dev/null +++ b/libdnf/conf/config-private.hpp @@ -0,0 +1,52 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_CONF_CONFIG_PRIVATE_HPP +#define LIBDNF_CONF_CONFIG_PRIVATE_HPP + +#include "libdnf/conf/option.hpp" + +namespace libdnf { + +template +static void optionTListAppend(T & option, Option::Priority priority, const std::string & value) { + if (value.empty()) { + option.set(priority, value); + return; + } + auto add_priority = priority < option.get_priority() ? option.get_priority() : priority; + auto val = option.from_string(value); + bool first = true; + for (auto & item : val) { + if (item.empty()) { + if (first) { + option.set(priority, item); + } + } else { + auto orig_value = option.get_value(); + orig_value.push_back(item); + option.set(add_priority, orig_value); + } + first = false; + } +} + +} // namespace libdnf + +#endif diff --git a/libdnf/conf/config_main.cpp b/libdnf/conf/config_main.cpp new file mode 100644 index 0000000000..4d08fdb10e --- /dev/null +++ b/libdnf/conf/config_main.cpp @@ -0,0 +1,877 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/config_main.hpp" +#include "config-private.hpp" + +#include "libdnf/conf/const.hpp" +#include "libdnf/conf/config_parser.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASCII_LOWERCASE "abcdefghijklmnopqrstuvwxyz" +#define ASCII_UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define ASCII_LETTERS ASCII_LOWERCASE ASCII_UPPERCASE +#define DIGITS "0123456789" +#define REPOID_CHARS ASCII_LETTERS DIGITS "-_.:" + +extern char ** environ; + +namespace libdnf { + +/** +* @brief Converts a friendly bandwidth option to bytes +* +* Function converts a friendly bandwidth option to bytes. The input +* should be a string containing a (possibly floating point) +* number followed by an optional single character unit. Valid +* units are 'k', 'M', 'G'. Case is ignored. The convention that +* 1k = 1024 bytes is used. +* +* @param str Bandwidth as user friendly string +* @return int Number of bytes +*/ +static int str_to_bytes(const std::string & str) { + if (str.empty()) { + throw Option::InvalidValue("no value specified"); + } + + std::size_t idx; + auto res = std::stod(str, &idx); + if (res < 0) { + throw Option::InvalidValue(fmt::format("seconds value '{}' must not be negative", str)); + } + + if (idx < str.length()) { + if (idx < str.length() - 1) { + throw Option::InvalidValue(fmt::format("could not convert '%s' to bytes", str)); + } + switch (str.back()) { + case 'k': + case 'K': + res *= 1024; + break; + case 'm': + case 'M': + res *= 1024 * 1024; + break; + case 'g': + case 'G': + res *= 1024 * 1024 * 1024; + break; + default: + throw Option::InvalidValue(fmt::format("unknown unit '{}'", str.back())); + } + } + + return static_cast(res); +} + +static void add_from_file(std::ostream & out, const std::string & file_path) { + std::ifstream ifs(file_path); + if (!ifs) { + throw std::runtime_error("add_from_file(): Can't open file"); + } + ifs.exceptions(std::ifstream::badbit); + + std::string line; + while (!ifs.eof()) { + std::getline(ifs, line); + auto start = line.find_first_not_of(" \t\r"); + if (start == std::string::npos) { + continue; + } + if (line[start] == '#') { + continue; + } + auto end = line.find_last_not_of(" \t\r"); + + out.write(line.c_str() + start, static_cast(end - start + 1)); + out.put(' '); + } +} + +static void add_from_files(std::ostream & out, const std::string & glob_path) { + glob_t glob_buf; + glob(glob_path.c_str(), GLOB_MARK | GLOB_NOSORT, nullptr, &glob_buf); + for (size_t i = 0; i < glob_buf.gl_pathc; ++i) { + auto path = glob_buf.gl_pathv[i]; + if (path[strlen(path) - 1] != '/') { + add_from_file(out, path); + } + } + globfree(&glob_buf); +} + +/** +* @brief Replaces globs (like /etc/foo.d/\\*.foo) by content of matching files. +* +* Ignores comment lines (start with '#') and blank lines in files. +* Result: +* Words delimited by spaces. Characters ',' and '\n' are replaced by spaces. +* Extra spaces are removed. +* @param strWithGlobs Input string with globs +* @return Words delimited by space +*/ +static std::string resolve_globs(const std::string & str_with_globs) { + std::ostringstream res; + std::string::size_type start{0}; + while (start < str_with_globs.length()) { + auto end = str_with_globs.find_first_of(" ,\n", start); + if (str_with_globs.compare(start, 5, "glob:") == 0) { + start += 5; + if (start >= str_with_globs.length()) { + break; + } + if (end == std::string::npos) { + add_from_files(res, str_with_globs.substr(start)); + break; + } + if ((end - start) != 0) { + add_from_files(res, str_with_globs.substr(start, end - start)); + } + } else { + if (end == std::string::npos) { + res << str_with_globs.substr(start); + break; + } + if ((end - start) != 0) { + res << str_with_globs.substr(start, end - start) << " "; + } + } + start = end + 1; + } + return res.str(); +} + +class ConfigMain::Impl { + friend class ConfigMain; + + explicit Impl(Config & owner); + + Config & owner; + + OptionNumber debuglevel{2, 0, 10}; + OptionNumber errorlevel{3, 0, 10}; + OptionPath installroot{"/", false, true}; + OptionPath config_file_path{CONF_FILENAME}; + OptionBool plugins{true}; + OptionStringList pluginpath{std::vector{}}; + OptionStringList pluginconfpath{std::vector{}}; + OptionPath persistdir{PERSISTDIR}; + OptionBool transformdb{true}; + OptionNumber recent{7, 0}; + OptionBool reset_nice{true}; + OptionPath system_cachedir{SYSTEM_CACHEDIR}; + OptionBool cacheonly{false}; + OptionBool keepcache{false}; + OptionString logdir{"/var/log"}; + OptionNumber log_size{1024 * 1024, str_to_bytes}; + OptionNumber log_rotate{4, 0}; + OptionStringList varsdir{VARS_DIRS}; + OptionStringList reposdir{{"/etc/yum.repos.d", "/etc/yum/repos.d", "/etc/distro.repos.d"}}; + OptionBool debug_solver{false}; + OptionStringList installonlypkgs{INSTALLONLYPKGS}; + OptionStringList group_package_types{GROUP_PACKAGE_TYPES}; + + OptionNumber installonly_limit{3, 0, [](const std::string & value) -> std::uint32_t { + if (value == "") { + return 0; + } + try { + return static_cast(std::stoul(value)); + } catch (...) { + return 0; + } + }}; + + OptionStringList tsflags{std::vector{}}; + OptionBool assumeyes{false}; + OptionBool assumeno{false}; + OptionBool check_config_file_age{true}; + OptionBool defaultyes{false}; + OptionBool diskspacecheck{true}; + OptionBool localpkg_gpgcheck{false}; + OptionBool gpgkey_dns_verification{false}; + OptionBool obsoletes{true}; + OptionBool showdupesfromrepos{false}; + OptionBool exit_on_lock{false}; + OptionBool allow_vendor_change{true}; + OptionSeconds metadata_timer_sync{60 * 60 * 3}; // 3 hours + OptionStringList disable_excludes{std::vector{}}; + OptionEnum multilib_policy{"best", {"best", "all"}}; // :api + OptionBool best{false}; // :api + OptionBool install_weak_deps{true}; + OptionString bugtracker_url{BUGTRACKER}; + OptionBool zchunk{true}; + + OptionEnum color{"auto", {"auto", "never", "always"}, [](const std::string & value) { + const std::array always{{"on", "yes", "1", "true"}}; + const std::array never{{"off", "no", "0", "false"}}; + const std::array aut{{"tty", "if-tty"}}; + std::string tmp; + if (std::find(always.begin(), always.end(), value) != always.end()) { + tmp = "always"; + } else if (std::find(never.begin(), never.end(), value) != never.end()) { + tmp = "never"; + } else if (std::find(aut.begin(), aut.end(), value) != aut.end()) { + tmp = "auto"; + } else { + tmp = value; + } + return tmp; + }}; + + OptionString color_list_installed_older{"yellow"}; + OptionString color_list_installed_newer{"bold,yellow"}; + OptionString color_list_installed_reinstall{"dim,cyan"}; + OptionString color_list_installed_extra{"bold,red"}; + OptionString color_list_available_upgrade{"bold,blue"}; + OptionString color_list_available_downgrade{"dim,magenta"}; + OptionString color_list_available_reinstall{"bold,underline,green"}; + OptionString color_list_available_install{"bold,cyan"}; + OptionString color_update_installed{"dim,red"}; + OptionString color_update_local{"dim,green"}; + OptionString color_update_remote{"bold,green"}; + OptionString color_search_match{"bold,magenta"}; + OptionBool history_record{true}; + OptionStringList history_record_packages{std::vector{"dnf", "rpm"}}; + OptionString rpmverbosity{"info"}; + OptionBool strict{true}; // :api + OptionBool skip_broken{false}; // :yum-compatibility + OptionBool autocheck_running_kernel{true}; // :yum-compatibility + OptionBool clean_requirements_on_remove{true}; + + OptionEnum history_list_view{ + "commands", {"single-user-commands", "users", "commands"}, [](const std::string & value) { + if (value == "cmds" || value == "default") { + return std::string("commands"); + } + return value; + }}; + + OptionBool upgrade_group_objects_upgrade{true}; // :api + OptionPath destdir{nullptr}; + OptionString comment{nullptr}; + OptionBool downloadonly{false}; // runtime only option + OptionBool ignorearch{false}; + OptionString module_platform_id{nullptr}; + + OptionString user_agent{"libdnf"}; // TODO: getUserAgent() + OptionBool countme{false}; + + // Repo main config + + OptionNumber retries{10}; + OptionString cachedir{nullptr}; + OptionBool fastestmirror{false}; + OptionStringList excludepkgs{std::vector{}}; + OptionStringList includepkgs{std::vector{}}; + OptionString proxy{""}; + OptionString proxy_username{nullptr}; + OptionString proxy_password{nullptr}; + + OptionEnum proxy_auth_method{ + "any", + {"any", "none", "basic", "digest", "negotiate", "ntlm", "digest_ie", "ntlm_wb"}, + [](const std::string & value) { + auto tmp = value; + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + return tmp; + }}; + + OptionStringList protected_packages{ + resolve_globs("dnf glob:/etc/yum/protected.d/*.conf " + "glob:/etc/dnf/protected.d/*.conf")}; + OptionString username{""}; + OptionString password{""}; + OptionBool gpgcheck{false}; + OptionBool repo_gpgcheck{false}; + OptionBool enabled{true}; + OptionBool enablegroups{true}; + OptionNumber bandwidth{0, str_to_bytes}; + OptionNumber minrate{1000, str_to_bytes}; + + OptionEnum ip_resolve{"whatever", {"ipv4", "ipv6", "whatever"}, [](const std::string & value) { + auto tmp = value; + if (value == "4") { + tmp = "ipv4"; + } else if (value == "6") { + tmp = "ipv6"; + } else { + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + } + return tmp; + }}; + + OptionNumber throttle{0, 0, [](const std::string & value) { + if (!value.empty() && value.back() == '%') { + std::size_t idx; + auto res = std::stof(value, &idx); + if (res < 0 || res > 100) { + // throw Option::InvalidValue(tfm::format(_("percentage '%s' is out of range"), value)); + throw Option::InvalidValue(value); + } + return res / 100; + } + return static_cast(str_to_bytes(value)); + }}; + + OptionSeconds timeout{30}; + OptionNumber max_parallel_downloads{3, 1}; + OptionSeconds metadata_expire{60 * 60 * 48}; + OptionString sslcacert{""}; + OptionBool sslverify{true}; + OptionString sslclientcert{""}; + OptionString sslclientkey{""}; + OptionBool deltarpm{true}; + OptionNumber deltarpm_percentage{75}; + OptionBool skip_if_unavailable{false}; +}; + +ConfigMain::Impl::Impl(Config & owner) : owner(owner) { + owner.opt_binds().add("debuglevel", debuglevel); + owner.opt_binds().add("errorlevel", errorlevel); + owner.opt_binds().add("installroot", installroot); + owner.opt_binds().add("config_file_path", config_file_path); + owner.opt_binds().add("plugins", plugins); + owner.opt_binds().add("pluginpath", pluginpath); + owner.opt_binds().add("pluginconfpath", pluginconfpath); + owner.opt_binds().add("persistdir", persistdir); + owner.opt_binds().add("transformdb", transformdb); + owner.opt_binds().add("recent", recent); + owner.opt_binds().add("reset_nice", reset_nice); + owner.opt_binds().add("system_cachedir", system_cachedir); + owner.opt_binds().add("cacheonly", cacheonly); + owner.opt_binds().add("keepcache", keepcache); + owner.opt_binds().add("logdir", logdir); + owner.opt_binds().add("log_size", log_size); + owner.opt_binds().add("log_rotate", log_rotate); + owner.opt_binds().add("varsdir", varsdir); + owner.opt_binds().add("reposdir", reposdir); + owner.opt_binds().add("debug_solver", debug_solver); + + owner.opt_binds().add( + "installonlypkgs", + installonlypkgs, + [&](Option::Priority priority, const std::string & value) { + optionTListAppend(installonlypkgs, priority, value); + }, + nullptr, + true); + + owner.opt_binds().add("group_package_types", group_package_types); + owner.opt_binds().add("installonly_limit", installonly_limit); + + owner.opt_binds().add( + "tsflags", + tsflags, + [&](Option::Priority priority, const std::string & value) { optionTListAppend(tsflags, priority, value); }, + nullptr, + true); + + owner.opt_binds().add("assumeyes", assumeyes); + owner.opt_binds().add("assumeno", assumeno); + owner.opt_binds().add("check_config_file_age", check_config_file_age); + owner.opt_binds().add("defaultyes", defaultyes); + owner.opt_binds().add("diskspacecheck", diskspacecheck); + owner.opt_binds().add("localpkg_gpgcheck", localpkg_gpgcheck); + owner.opt_binds().add("gpgkey_dns_verification", gpgkey_dns_verification); + owner.opt_binds().add("obsoletes", obsoletes); + owner.opt_binds().add("showdupesfromrepos", showdupesfromrepos); + owner.opt_binds().add("exit_on_lock", exit_on_lock); + owner.opt_binds().add("allow_vendor_change", allow_vendor_change); + owner.opt_binds().add("metadata_timer_sync", metadata_timer_sync); + owner.opt_binds().add("disable_excludes", disable_excludes); + owner.opt_binds().add("multilib_policy", multilib_policy); + owner.opt_binds().add("best", best); + owner.opt_binds().add("install_weak_deps", install_weak_deps); + owner.opt_binds().add("bugtracker_url", bugtracker_url); + owner.opt_binds().add("zchunk", zchunk); + owner.opt_binds().add("color", color); + owner.opt_binds().add("color_list_installed_older", color_list_installed_older); + owner.opt_binds().add("color_list_installed_newer", color_list_installed_newer); + owner.opt_binds().add("color_list_installed_reinstall", color_list_installed_reinstall); + owner.opt_binds().add("color_list_installed_extra", color_list_installed_extra); + owner.opt_binds().add("color_list_available_upgrade", color_list_available_upgrade); + owner.opt_binds().add("color_list_available_downgrade", color_list_available_downgrade); + owner.opt_binds().add("color_list_available_reinstall", color_list_available_reinstall); + owner.opt_binds().add("color_list_available_install", color_list_available_install); + owner.opt_binds().add("color_update_installed", color_update_installed); + owner.opt_binds().add("color_update_local", color_update_local); + owner.opt_binds().add("color_update_remote", color_update_remote); + owner.opt_binds().add("color_search_match", color_search_match); + owner.opt_binds().add("history_record", history_record); + owner.opt_binds().add("history_record_packages", history_record_packages); + owner.opt_binds().add("rpmverbosity", rpmverbosity); + owner.opt_binds().add("strict", strict); + owner.opt_binds().add("skip_broken", skip_broken); + owner.opt_binds().add("autocheck_running_kernel", autocheck_running_kernel); + owner.opt_binds().add("clean_requirements_on_remove", clean_requirements_on_remove); + owner.opt_binds().add("history_list_view", history_list_view); + owner.opt_binds().add("upgrade_group_objects_upgrade", upgrade_group_objects_upgrade); + owner.opt_binds().add("destdir", destdir); + owner.opt_binds().add("comment", comment); + owner.opt_binds().add("ignorearch", ignorearch); + owner.opt_binds().add("module_platform_id", module_platform_id); + owner.opt_binds().add("user_agent", user_agent); + owner.opt_binds().add("countme", countme); + + // Repo main config + + owner.opt_binds().add("retries", retries); + owner.opt_binds().add("cachedir", cachedir); + owner.opt_binds().add("fastestmirror", fastestmirror); + + owner.opt_binds().add( + "excludepkgs", + excludepkgs, + [&](Option::Priority priority, const std::string & value) { optionTListAppend(excludepkgs, priority, value); }, + nullptr, + true); + owner.opt_binds().add( + "exclude", + excludepkgs, //compatibility with yum + [&](Option::Priority priority, const std::string & value) { optionTListAppend(excludepkgs, priority, value); }, + nullptr, + true); + + owner.opt_binds().add( + "includepkgs", + includepkgs, + [&](Option::Priority priority, const std::string & value) { optionTListAppend(includepkgs, priority, value); }, + nullptr, + true); + + owner.opt_binds().add("proxy", proxy); + owner.opt_binds().add("proxy_username", proxy_username); + owner.opt_binds().add("proxy_password", proxy_password); + owner.opt_binds().add("proxy_auth_method", proxy_auth_method); + owner.opt_binds().add( + "protected_packages", + protected_packages, + [&](Option::Priority priority, const std::string & value) { + if (priority >= protected_packages.get_priority()) { + protected_packages.set(priority, resolve_globs(value)); + } + }, + nullptr, + false); + + owner.opt_binds().add("username", username); + owner.opt_binds().add("password", password); + owner.opt_binds().add("gpgcheck", gpgcheck); + owner.opt_binds().add("repo_gpgcheck", repo_gpgcheck); + owner.opt_binds().add("enabled", enabled); + owner.opt_binds().add("enablegroups", enablegroups); + owner.opt_binds().add("bandwidth", bandwidth); + owner.opt_binds().add("minrate", minrate); + owner.opt_binds().add("ip_resolve", ip_resolve); + owner.opt_binds().add("throttle", throttle); + owner.opt_binds().add("timeout", timeout); + owner.opt_binds().add("max_parallel_downloads", max_parallel_downloads); + owner.opt_binds().add("metadata_expire", metadata_expire); + owner.opt_binds().add("sslcacert", sslcacert); + owner.opt_binds().add("sslverify", sslverify); + owner.opt_binds().add("sslclientcert", sslclientcert); + owner.opt_binds().add("sslclientkey", sslclientkey); + owner.opt_binds().add("deltarpm", deltarpm); + owner.opt_binds().add("deltarpm_percentage", deltarpm_percentage); + owner.opt_binds().add("skip_if_unavailable", skip_if_unavailable); +} + +ConfigMain::ConfigMain() { + p_impl = std::unique_ptr(new Impl(*this)); +} +ConfigMain::~ConfigMain() = default; + +OptionNumber & ConfigMain::debuglevel() { + return p_impl->debuglevel; +} +OptionNumber & ConfigMain::errorlevel() { + return p_impl->errorlevel; +} +OptionString & ConfigMain::installroot() { + return p_impl->installroot; +} +OptionString & ConfigMain::config_file_path() { + return p_impl->config_file_path; +} +OptionBool & ConfigMain::plugins() { + return p_impl->plugins; +} +OptionStringList & ConfigMain::pluginpath() { + return p_impl->pluginpath; +} +OptionStringList & ConfigMain::pluginconfpath() { + return p_impl->pluginconfpath; +} +OptionString & ConfigMain::persistdir() { + return p_impl->persistdir; +} +OptionBool & ConfigMain::transformdb() { + return p_impl->transformdb; +} +OptionNumber & ConfigMain::recent() { + return p_impl->recent; +} +OptionBool & ConfigMain::reset_nice() { + return p_impl->reset_nice; +} +OptionString & ConfigMain::system_cachedir() { + return p_impl->system_cachedir; +} +OptionBool & ConfigMain::cacheonly() { + return p_impl->cacheonly; +} +OptionBool & ConfigMain::keepcache() { + return p_impl->keepcache; +} +OptionString & ConfigMain::logdir() { + return p_impl->logdir; +} +OptionNumber & ConfigMain::log_size() { + return p_impl->log_size; +} +OptionNumber & ConfigMain::log_rotate() { + return p_impl->log_rotate; +} +OptionStringList & ConfigMain::varsdir() { + return p_impl->varsdir; +} +OptionStringList & ConfigMain::reposdir() { + return p_impl->reposdir; +} +OptionBool & ConfigMain::debug_solver() { + return p_impl->debug_solver; +} +OptionStringList & ConfigMain::installonlypkgs() { + return p_impl->installonlypkgs; +} +OptionStringList & ConfigMain::group_package_types() { + return p_impl->group_package_types; +} +OptionNumber & ConfigMain::installonly_limit() { + return p_impl->installonly_limit; +} +OptionStringList & ConfigMain::tsflags() { + return p_impl->tsflags; +} +OptionBool & ConfigMain::assumeyes() { + return p_impl->assumeyes; +} +OptionBool & ConfigMain::assumeno() { + return p_impl->assumeno; +} +OptionBool & ConfigMain::check_config_file_age() { + return p_impl->check_config_file_age; +} +OptionBool & ConfigMain::defaultyes() { + return p_impl->defaultyes; +} +OptionBool & ConfigMain::diskspacecheck() { + return p_impl->diskspacecheck; +} +OptionBool & ConfigMain::localpkg_gpgcheck() { + return p_impl->localpkg_gpgcheck; +} +OptionBool & ConfigMain::gpgkey_dns_verification() { + return p_impl->gpgkey_dns_verification; +} +OptionBool & ConfigMain::obsoletes() { + return p_impl->obsoletes; +} +OptionBool & ConfigMain::showdupesfromrepos() { + return p_impl->showdupesfromrepos; +} +OptionBool & ConfigMain::exit_on_lock() { + return p_impl->exit_on_lock; +} +OptionBool & ConfigMain::allow_vendor_change() { return p_impl->allow_vendor_change; } +OptionSeconds & ConfigMain::metadata_timer_sync() { + return p_impl->metadata_timer_sync; +} +OptionStringList & ConfigMain::disable_excludes() { + return p_impl->disable_excludes; +} +OptionEnum & ConfigMain::multilib_policy() { + return p_impl->multilib_policy; +} +OptionBool & ConfigMain::best() { + return p_impl->best; +} +OptionBool & ConfigMain::install_weak_deps() { + return p_impl->install_weak_deps; +} +OptionString & ConfigMain::bugtracker_url() { + return p_impl->bugtracker_url; +} +OptionBool & ConfigMain::zchunk() { + return p_impl->zchunk; +} +OptionEnum & ConfigMain::color() { + return p_impl->color; +} +OptionString & ConfigMain::color_list_installed_older() { + return p_impl->color_list_installed_older; +} +OptionString & ConfigMain::color_list_installed_newer() { + return p_impl->color_list_installed_newer; +} +OptionString & ConfigMain::color_list_installed_reinstall() { + return p_impl->color_list_installed_reinstall; +} +OptionString & ConfigMain::color_list_installed_extra() { + return p_impl->color_list_installed_extra; +} +OptionString & ConfigMain::color_list_available_upgrade() { + return p_impl->color_list_available_upgrade; +} +OptionString & ConfigMain::color_list_available_downgrade() { + return p_impl->color_list_available_downgrade; +} +OptionString & ConfigMain::color_list_available_reinstall() { + return p_impl->color_list_available_reinstall; +} +OptionString & ConfigMain::color_list_available_install() { + return p_impl->color_list_available_install; +} +OptionString & ConfigMain::color_update_installed() { + return p_impl->color_update_installed; +} +OptionString & ConfigMain::color_update_local() { + return p_impl->color_update_local; +} +OptionString & ConfigMain::color_update_remote() { + return p_impl->color_update_remote; +} +OptionString & ConfigMain::color_search_match() { + return p_impl->color_search_match; +} +OptionBool & ConfigMain::history_record() { + return p_impl->history_record; +} +OptionStringList & ConfigMain::history_record_packages() { + return p_impl->history_record_packages; +} +OptionString & ConfigMain::rpmverbosity() { + return p_impl->rpmverbosity; +} +OptionBool & ConfigMain::strict() { + return p_impl->strict; +} +OptionBool & ConfigMain::skip_broken() { + return p_impl->skip_broken; +} +OptionBool & ConfigMain::autocheck_running_kernel() { + return p_impl->autocheck_running_kernel; +} +OptionBool & ConfigMain::clean_requirements_on_remove() { + return p_impl->clean_requirements_on_remove; +} +OptionEnum & ConfigMain::history_list_view() { + return p_impl->history_list_view; +} +OptionBool & ConfigMain::upgrade_group_objects_upgrade() { + return p_impl->upgrade_group_objects_upgrade; +} +OptionPath & ConfigMain::destdir() { + return p_impl->destdir; +} +OptionString & ConfigMain::comment() { + return p_impl->comment; +} +OptionBool & ConfigMain::downloadonly() { + return p_impl->downloadonly; +} +OptionBool & ConfigMain::ignorearch() { + return p_impl->ignorearch; +} + +OptionString & ConfigMain::module_platform_id() { + return p_impl->module_platform_id; +} +OptionString & ConfigMain::user_agent() { + return p_impl->user_agent; +} +OptionBool & ConfigMain::countme() { + return p_impl->countme; +} + +// Repo main config +OptionNumber & ConfigMain::retries() { + return p_impl->retries; +} +OptionString & ConfigMain::cachedir() { + return p_impl->cachedir; +} +OptionBool & ConfigMain::fastestmirror() { + return p_impl->fastestmirror; +} +OptionStringList & ConfigMain::excludepkgs() { + return p_impl->excludepkgs; +} +OptionStringList & ConfigMain::includepkgs() { + return p_impl->includepkgs; +} +OptionString & ConfigMain::proxy() { + return p_impl->proxy; +} +OptionString & ConfigMain::proxy_username() { + return p_impl->proxy_username; +} +OptionString & ConfigMain::proxy_password() { + return p_impl->proxy_password; +} +OptionEnum & ConfigMain::proxy_auth_method() { + return p_impl->proxy_auth_method; +} +OptionStringList & ConfigMain::protected_packages() { + return p_impl->protected_packages; +} +OptionString & ConfigMain::username() { + return p_impl->username; +} +OptionString & ConfigMain::password() { + return p_impl->password; +} +OptionBool & ConfigMain::gpgcheck() { + return p_impl->gpgcheck; +} +OptionBool & ConfigMain::repo_gpgcheck() { + return p_impl->repo_gpgcheck; +} +OptionBool & ConfigMain::enabled() { + return p_impl->enabled; +} +OptionBool & ConfigMain::enablegroups() { + return p_impl->enablegroups; +} +OptionNumber & ConfigMain::bandwidth() { + return p_impl->bandwidth; +} +OptionNumber & ConfigMain::minrate() { + return p_impl->minrate; +} +OptionEnum & ConfigMain::ip_resolve() { + return p_impl->ip_resolve; +} +OptionNumber & ConfigMain::throttle() { + return p_impl->throttle; +} +OptionSeconds & ConfigMain::timeout() { + return p_impl->timeout; +} +OptionNumber & ConfigMain::max_parallel_downloads() { + return p_impl->max_parallel_downloads; +} +OptionSeconds & ConfigMain::metadata_expire() { + return p_impl->metadata_expire; +} +OptionString & ConfigMain::sslcacert() { + return p_impl->sslcacert; +} +OptionBool & ConfigMain::sslverify() { + return p_impl->sslverify; +} +OptionString & ConfigMain::sslclientcert() { + return p_impl->sslclientcert; +} +OptionString & ConfigMain::sslclientkey() { + return p_impl->sslclientkey; +} +OptionBool & ConfigMain::deltarpm() { + return p_impl->deltarpm; +} +OptionNumber & ConfigMain::deltarpm_percentage() { + return p_impl->deltarpm_percentage; +} +OptionBool & ConfigMain::skip_if_unavailable() { + return p_impl->skip_if_unavailable; +} + +static void dir_close(DIR * d) { + closedir(d); +} + +void ConfigMain::add_vars_from_dir(std::map & vars_map, const std::string & dir_path) { + if (DIR * dir = opendir(dir_path.c_str())) { + std::unique_ptr dir_guard(dir, &dir_close); + while (auto ent = readdir(dir)) { + auto dname = ent->d_name; + if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) + continue; + + auto full_path = dir_path; + if (full_path.back() != '/') { + full_path += "/"; + } + full_path += dname; + std::ifstream in_stream(full_path); + if (in_stream.fail()) { + // log.warning() + continue; + } + std::string line; + std::getline(in_stream, line); + if (in_stream.fail()) { + // log.warning() + continue; + } + vars_map[dname] = std::move(line); + } + } +} + +void ConfigMain::add_vars_from_env(std::map & vars_map) { + if (!environ) { + return; + } + + for (const char * const * var_ptr = environ; *var_ptr; ++var_ptr) { + auto var = *var_ptr; + if (auto eql_ptr = strchr(var, '=')) { + auto eql_idx = eql_ptr - var; + // DNF[0-9] + if (eql_idx == 4 && strncmp("DNF", var, 3) == 0 && isdigit(var[3]) != 0) { + vars_map[std::string(var, static_cast(eql_idx))] = eql_ptr + 1; + // DNF_VAR_[A-Za-z0-9_]+ , DNF_VAR_ prefix is cut off + } else if ( + eql_idx > 8 && strncmp("DNF_VAR_", var, 8) == 0 && + static_cast(strspn(var + 8, ASCII_LETTERS DIGITS "_")) == eql_idx - 8) { + vars_map[std::string(var + 8, static_cast(eql_idx - 8))] = eql_ptr + 1; + } + } + } +} + +} // namespace libdnf diff --git a/libdnf/conf/config_parser.cpp b/libdnf/conf/config_parser.cpp new file mode 100644 index 0000000000..2bdc32c38c --- /dev/null +++ b/libdnf/conf/config_parser.cpp @@ -0,0 +1,212 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/config_parser.hpp" + +#include "libdnf/utils/iniparser.hpp" + +#include +#include + +namespace libdnf { + +void ConfigParser::substitute(std::string & text, const std::map & substitutions) { + auto start = text.find_first_of('$'); + while (start != std::string::npos) { + auto variable = start + 1; + if (variable >= text.length()) { + break; + } + bool bracket; + if (text[variable] == '{') { + bracket = true; + if (++variable >= text.length()) { + break; + } + } else { + bracket = false; + } + auto it = std::find_if_not( + text.begin() + variable, text.end(), [](char c) { return std::isalnum(c) != 0 || c == '_'; }); + if (bracket && it == text.end()) { + break; + } + auto past_variable = std::distance(text.begin(), it); + if (bracket && *it != '}') { + start = text.find_first_of('$', past_variable); + continue; + } + auto subst = substitutions.find(text.substr(variable, past_variable - variable)); + if (subst != substitutions.end()) { + if (bracket) { + ++past_variable; + } + text.replace(start, past_variable - start, subst->second); + start = text.find_first_of('$', start + subst->second.length()); + } else { + start = text.find_first_of('$', past_variable); + } + } +} + +static void read(ConfigParser & cfg_parser, IniParser & parser) { + IniParser::ItemType readed_type; + while ((readed_type = parser.next()) != IniParser::ItemType::END_OF_INPUT) { + auto section = parser.get_section(); + if (readed_type == IniParser::ItemType::SECTION) { + cfg_parser.add_section(std::move(section), std::move(parser.get_raw_item())); + } else if (readed_type == IniParser::ItemType::KEY_VAL) { + cfg_parser.set_value( + section, std::move(parser.get_key()), std::move(parser.get_value()), std::move(parser.get_raw_item())); + } else if (readed_type == IniParser::ItemType::COMMENT_LINE || readed_type == IniParser::ItemType::EMPTY_LINE) { + if (section.empty()) { + cfg_parser.get_header() += parser.get_raw_item(); + } else { + cfg_parser.add_comment_line(section, std::move(parser.get_raw_item())); + } + } + } +} + +void ConfigParser::read(const std::string & file_path) { + IniParser parser(file_path); + ::libdnf::read(*this, parser); +} + +void ConfigParser::read(std::unique_ptr && input_stream) { + IniParser parser(std::move(input_stream)); + ::libdnf::read(*this, parser); +} + +static std::string create_raw_item(const std::string & value, const std::string & old_raw_item) { + auto eql_pos = old_raw_item.find('='); + if (eql_pos == std::string::npos) { + return ""; + } + auto value_pos = old_raw_item.find_first_not_of(" \t", eql_pos + 1); + auto key_and_delim_length = value_pos != std::string::npos ? value_pos : old_raw_item.length(); + return old_raw_item.substr(0, key_and_delim_length) + value + '\n'; +} + +void ConfigParser::set_value(const std::string & section, const std::string & key, const std::string & value) { + auto raw_iter = raw_items.find(section + ']' + key); + auto raw = create_raw_item(value, raw_iter != raw_items.end() ? raw_iter->second : ""); + set_value(section, key, value, raw); +} + +void ConfigParser::set_value(const std::string & section, std::string && key, std::string && value) { + auto raw_iter = raw_items.find(section + ']' + key); + auto raw = create_raw_item(value, raw_iter != raw_items.end() ? raw_iter->second : ""); + set_value(section, std::move(key), std::move(value), std::move(raw)); +} + +const std::string & ConfigParser::get_value(const std::string & section, const std::string & key) const { + auto sect = data.find(section); + if (sect == data.end()) { + throw SectionNotFound(section); + } + auto key_val = sect->second.find(key); + if (key_val == sect->second.end()) { + throw OptionNotFound(key + " in section " + section); + } + return key_val->second; +} + +std::string ConfigParser::get_substituted_value(const std::string & section, const std::string & key) const { + auto ret = get_value(section, key); + substitute(ret, substitutions); + return ret; +} + +static void write_key_vals( + std::ostream & out, + const std::string & section, + const ConfigParser::Container::mapped_type & key_val_map, + const std::map & raw_items) { + for (const auto & key_val : key_val_map) { + auto first = key_val.first[0]; + if (first == '#' || first == ';') { + out << key_val.second; + } else { + auto raw_item = raw_items.find(section + ']' + key_val.first); + if (raw_item != raw_items.end()) { + out << raw_item->second; + } else { + out << key_val.first << "="; + for (const auto chr : key_val.second) { + out << chr; + if (chr == '\n') { + out << " "; + } + } + out << "\n"; + } + } + } +} + +static void write_section( + std::ostream & out, + const std::string & section, + const ConfigParser::Container::mapped_type & key_val_map, + const std::map & raw_items) { + auto raw_item = raw_items.find(section); + if (raw_item != raw_items.end()) { + out << raw_item->second; + } else { + out << "[" << section << "]" + << "\n"; + } + write_key_vals(out, section, key_val_map, raw_items); +} + +void ConfigParser::write(const std::string & file_path, bool append) const { + std::ofstream ofs; + ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); + ofs.open(file_path, append ? std::ofstream::app : std::ofstream::trunc); + write(ofs); +} + +void ConfigParser::write(const std::string & file_path, bool append, const std::string & section) const { + auto sit = data.find(section); + if (sit == data.end()) { + throw SectionNotFound(section); + } + std::ofstream ofs; + ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit); + ofs.open(file_path, append ? std::ofstream::app : std::ofstream::trunc); + write_section(ofs, sit->first, sit->second, raw_items); +} + +void ConfigParser::write(std::ostream & output_stream) const { + output_stream << header; + for (const auto & section : data) { + write_section(output_stream, section.first, section.second, raw_items); + } +} + +void ConfigParser::write(std::ostream & output_stream, const std::string & section) const { + auto sit = data.find(section); + if (sit == data.end()) { + throw SectionNotFound(section); + } + write_section(output_stream, sit->first, sit->second, raw_items); +} + +} // namespace libdnf diff --git a/libdnf/conf/option_binds.cpp b/libdnf/conf/option_binds.cpp new file mode 100644 index 0000000000..d01dd85056 --- /dev/null +++ b/libdnf/conf/option_binds.cpp @@ -0,0 +1,101 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_binds.hpp" + +#include + +namespace libdnf { + +// ========== OptionBinds::Item class =============== + +OptionBinds::Item::Item(Option & option, NewStringFunc new_string, GetValueStringFunc get_value_string, bool add_value) + : option(&option) + , new_str(std::move(new_string)) + , get_value_str(std::move(get_value_string)) + , add_value(add_value) {} + +OptionBinds::Item::Item(Option & option) : option(&option) {} + +Option::Priority OptionBinds::Item::get_priority() const { + return option->get_priority(); +} + +void OptionBinds::Item::new_string(Option::Priority priority, const std::string & value) { + if (new_str) { + new_str(priority, value); + } else { + option->set(priority, value); + } +} + +std::string OptionBinds::Item::get_value_string() const { + if (get_value_str) { + return get_value_str(); + } + return option->get_value_string(); +} + +bool OptionBinds::Item::get_add_value() const { + return add_value; +} + + +// =========== OptionBinds class =============== + +OptionBinds::Item & OptionBinds::at(const std::string & id) { + auto item = items.find(id); + if (item == items.end()) { + throw OptionNotFound(id); + } + return item->second; +} + +const OptionBinds::Item & OptionBinds::at(const std::string & id) const { + auto item = items.find(id); + if (item == items.end()) { + throw OptionNotFound(id); + } + return item->second; +} + +OptionBinds::Item & OptionBinds::add( + const std::string & id, + Option & option, + Item::NewStringFunc new_string, + Item::GetValueStringFunc get_value_string, + bool add_value) { + auto item = items.find(id); + if (item != items.end()) { + throw OptionAlreadyExists(id); + } + auto res = items.emplace(id, Item(option, std::move(new_string), std::move(get_value_string), add_value)); + return res.first->second; +} + +OptionBinds::Item & OptionBinds::add(const std::string & id, Option & option) { + auto item = items.find(id); + if (item != items.end()) { + throw OptionAlreadyExists(id); + } + auto res = items.emplace(id, Item(option)); + return res.first->second; +} + +} // namespace libdnf diff --git a/libdnf/conf/option_bool.cpp b/libdnf/conf/option_bool.cpp new file mode 100644 index 0000000000..02d5b79dda --- /dev/null +++ b/libdnf/conf/option_bool.cpp @@ -0,0 +1,99 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_bool.hpp" + +#include + +namespace libdnf { + +OptionBool::OptionBool(const OptionBool & src) : Option(src), default_value(src.default_value), value(src.value) { + if (src.true_values) { + true_values = std::make_unique>(*src.true_values); + } + if (src.false_values) { + false_values = std::make_unique>(*src.false_values); + } +} + +OptionBool & OptionBool::operator=(const OptionBool & src) { + if (this == &src) { + return *this; + } + if (src.true_values) { + true_values = std::make_unique>(*src.true_values); + } + if (src.false_values) { + false_values = std::make_unique>(*src.false_values); + } + default_value = src.default_value; + value = src.value; + return *this; +} + + +OptionBool::OptionBool( + bool default_value, const std::vector & true_vals, const std::vector & false_vals) + : Option(Priority::DEFAULT) + , true_values(std::make_unique>(true_vals)) + , false_values(std::make_unique>(false_vals)) + , default_value(default_value) + , value(default_value) {} + +OptionBool::OptionBool(bool default_value) + : Option(Priority::DEFAULT) + , default_value(default_value) + , value(default_value) {} + +bool OptionBool::from_string(const std::string & value) const { + auto tmp_value = value; + for (auto & ch : tmp_value) { + ch = static_cast(std::tolower(static_cast(ch))); + } + for (auto & false_value : get_false_values()) { + if (tmp_value == false_value) { + return false; + } + } + for (auto & true_value : get_true_values()) { + if (tmp_value == true_value) { + return true; + } + } + throw InvalidValue(value); +} + +void OptionBool::set(Priority priority, bool value) { + if (priority >= get_priority()) { + this->value = value; + set_priority(priority); + } +} + +void OptionBool::set(Priority priority, const std::string & value) { + set(priority, from_string(value)); +} + +std::string OptionBool::to_string(bool value) const { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +} // namespace libdnf diff --git a/libdnf/conf/option_enum.cpp b/libdnf/conf/option_enum.cpp new file mode 100644 index 0000000000..8482499350 --- /dev/null +++ b/libdnf/conf/option_enum.cpp @@ -0,0 +1,170 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_enum.hpp" + +#include +#include + +namespace libdnf { + +template +bool from_string(T & out, const std::string & in, std::ios_base & (*manipulator)(std::ios_base &)) { + std::istringstream iss(in); + return !(iss >> manipulator >> out).fail(); +} + +template +OptionEnum::OptionEnum(ValueType default_value, const std::vector & enum_vals) + : Option(Priority::DEFAULT) + , enum_vals(enum_vals) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +template +OptionEnum::OptionEnum(ValueType default_value, std::vector && enum_vals) + : Option(Priority::DEFAULT) + , enum_vals(std::move(enum_vals)) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +template +OptionEnum::OptionEnum( + ValueType default_value, const std::vector & enum_vals, FromStringFunc && from_string_func) + : Option(Priority::DEFAULT) + , from_string_user(std::move(from_string_func)) + , enum_vals(enum_vals) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +template +OptionEnum::OptionEnum( + ValueType default_value, std::vector && enum_vals, FromStringFunc && from_string_func) + : Option(Priority::DEFAULT) + , from_string_user(std::move(from_string_func)) + , enum_vals(std::move(enum_vals)) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +template +void OptionEnum::test(ValueType value) const { + auto it = std::find(enum_vals.begin(), enum_vals.end(), value); + if (it == enum_vals.end()) { + throw NotAllowedValue(to_string(value)); + } +} + +template +T OptionEnum::from_string(const std::string & value) const { + if (from_string_user) { + return from_string_user(value); + } + T val; + if (libdnf::from_string(val, value, std::dec)) { + return val; + } + throw InvalidValue(value); +} + +template +void OptionEnum::set(Priority priority, ValueType value) { + if (priority >= this->priority) { + test(value); + this->value = value; + this->priority = priority; + } +} + +template +void OptionEnum::set(Priority priority, const std::string & value) { + set(priority, from_string(value)); +} + +template +T OptionEnum::get_value() const { + return value; +} + +template +T OptionEnum::get_default_value() const { + return default_value; +} + +template +std::string OptionEnum::to_string(ValueType value) const { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +template +std::string OptionEnum::get_value_string() const { + return to_string(value); +} + +OptionEnum::OptionEnum(const std::string & default_value, std::vector enum_vals) + : Option(Priority::DEFAULT) + , enum_vals(std::move(enum_vals)) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +OptionEnum::OptionEnum( + const std::string & default_value, std::vector enum_vals, FromStringFunc && from_string_func) + : Option(Priority::DEFAULT) + , from_string_user(std::move(from_string_func)) + , enum_vals(std::move(enum_vals)) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +void OptionEnum::test(const std::string & value) const { + auto it = std::find(enum_vals.begin(), enum_vals.end(), value); + if (it == enum_vals.end()) { + throw NotAllowedValue(value); + } +} + +std::string OptionEnum::from_string(const std::string & value) const { + if (from_string_user) { + return from_string_user(value); + } + return value; +} + +void OptionEnum::set(Priority priority, const std::string & value) { + auto val = from_string(value); + if (priority >= get_priority()) { + test(val); + this->value = val; + set_priority(priority); + } +} + +} // namespace libdnf diff --git a/libdnf/conf/option_number.cpp b/libdnf/conf/option_number.cpp new file mode 100644 index 0000000000..415a2eb097 --- /dev/null +++ b/libdnf/conf/option_number.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_number.hpp" + +#include +#include + +namespace libdnf { + +template +bool from_string(T & out, const std::string & in, std::ios_base & (*manipulator)(std::ios_base &)) { + std::istringstream iss(in); + return !(iss >> manipulator >> out).fail(); +} + +template +OptionNumber::OptionNumber(T default_value, T min, T max) + : Option(Priority::DEFAULT) + , default_value(default_value) + , min(min) + , max(max) + , value(default_value) { + test(default_value); +} + +template +OptionNumber::OptionNumber(T default_value, T min) + : OptionNumber(default_value, min, std::numeric_limits::max()) {} + +template +OptionNumber::OptionNumber(T default_value) + : OptionNumber(default_value, std::numeric_limits::min(), std::numeric_limits::max()) {} + +template +OptionNumber::OptionNumber(T default_value, T min, T max, FromStringFunc && from_string_func) + : Option(Priority::DEFAULT) + , from_string_user(std::move(from_string_func)) + , default_value(default_value) + , min(min) + , max(max) + , value(default_value) { + test(default_value); +} + +template +OptionNumber::OptionNumber(T default_value, T min, FromStringFunc && from_string_func) + : OptionNumber(default_value, min, std::numeric_limits::max(), std::move(from_string_func)) {} + +template +OptionNumber::OptionNumber(T default_value, FromStringFunc && from_string_func) + : OptionNumber( + default_value, std::numeric_limits::min(), std::numeric_limits::max(), std::move(from_string_func)) {} + +template +void OptionNumber::test(ValueType value) const { + if (value < min || value > max) { + throw NotAllowedValue(to_string(value)); + } +} + +template +T OptionNumber::from_string(const std::string & value) const { + if (from_string_user) { + return from_string_user(value); + } + ValueType val; + if (libdnf::from_string(val, value, std::dec)) { + return val; + } + throw InvalidValue(value); +} + +template +void OptionNumber::set(Priority priority, ValueType value) { + if (priority >= get_priority()) { + test(value); + this->value = value; + set_priority(priority); + } +} + +template +void OptionNumber::set(Option::Priority priority, const std::string & value) { + set(priority, from_string(value)); +} + +template +std::string OptionNumber::to_string(ValueType value) const { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +template class OptionNumber; +template class OptionNumber; +template class OptionNumber; +template class OptionNumber; +template class OptionNumber; + +} // namespace libdnf diff --git a/libdnf/conf/option_path.cpp b/libdnf/conf/option_path.cpp new file mode 100644 index 0000000000..623aeb716b --- /dev/null +++ b/libdnf/conf/option_path.cpp @@ -0,0 +1,98 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_path.hpp" + +#include +#include +#include + +namespace libdnf { + +static std::string remove_file_prot(const std::string & value) { + const int prefix_len = 7; + if (value.compare(0, prefix_len, "file://") == 0) { + return value.substr(prefix_len); + } + return value; +} + +OptionPath::OptionPath(const std::string & default_value, bool exists, bool abs_path) + : OptionString(default_value) + , exists(exists) + , abs_path(abs_path) { + this->default_value = remove_file_prot(this->default_value); + test(this->default_value); + this->value = this->default_value; +} + +OptionPath::OptionPath(const char * default_value, bool exists, bool abs_path) + : OptionString(default_value) + , exists(exists) + , abs_path(abs_path) { + if (default_value) { + this->default_value = remove_file_prot(this->default_value); + test(this->default_value); + this->value = this->default_value; + } +} + +OptionPath::OptionPath( + const std::string & default_value, const std::string & regex, bool icase, bool exists, bool abs_path) + : OptionString(remove_file_prot(default_value), regex, icase) + , exists(exists) + , abs_path(abs_path) { + this->default_value = remove_file_prot(this->default_value); + test(this->default_value); + this->value = this->default_value; +} + +OptionPath::OptionPath(const char * default_value, const std::string & regex, bool icase, bool exists, bool abs_path) + : OptionString(default_value, regex, icase) + , exists(exists) + , abs_path(abs_path) { + if (default_value) { + this->default_value = remove_file_prot(this->default_value); + test(this->default_value); + this->value = this->default_value; + } +} + +void OptionPath::test(const std::string & value) const { + if (abs_path && value[0] != '/') { + throw NotAllowedValue(value); + } + + struct stat buffer; + if (exists && stat(value.c_str(), &buffer) != 0) { + throw PathNotExists(value); + } +} + +void OptionPath::set(Priority priority, const std::string & value) { + if (priority >= get_priority()) { + OptionString::test(value); + auto val = remove_file_prot(value); + test(val); + this->value = val; + set_priority(priority); + } +} + +} // namespace libdnf diff --git a/libdnf/conf/option_seconds.cpp b/libdnf/conf/option_seconds.cpp new file mode 100644 index 0000000000..4cf9f9c78e --- /dev/null +++ b/libdnf/conf/option_seconds.cpp @@ -0,0 +1,81 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_seconds.hpp" + +namespace libdnf { + +OptionSeconds::OptionSeconds(ValueType default_value, ValueType min, ValueType max) + : OptionNumber(default_value, min, max) {} + +OptionSeconds::OptionSeconds(ValueType default_value, ValueType min) : OptionNumber(default_value, min) {} + +OptionSeconds::OptionSeconds(ValueType default_value) : OptionNumber(default_value) {} + +OptionSeconds::ValueType OptionSeconds::from_string(const std::string & value) const { + constexpr int seconds_in_minute = 60; + constexpr int minutes_in_hour = 60; + constexpr int hours_in_day = 24; + if (value.empty()) { + throw InvalidValue(value); + } + + if (value == "-1" || value == "never") { // Special cache timeout, meaning never + return -1; + } + + std::size_t idx; + auto res = std::stod(value, &idx); + if (res < 0) { + throw NegativeValue(value); + } + + if (idx < value.length()) { + if (idx < value.length() - 1) { + throw InvalidValue(value); + } + switch (value.back()) { + case 's': + case 'S': + break; + case 'm': + case 'M': + res *= seconds_in_minute; + break; + case 'h': + case 'H': + res *= seconds_in_minute * minutes_in_hour; + break; + case 'd': + case 'D': + res *= seconds_in_minute * minutes_in_hour * hours_in_day; + break; + default: + throw UnknownUnit(std::string(&value.back(), 1)); + } + } + + return static_cast(res); +} + +void OptionSeconds::set(Priority priority, const std::string & value) { + set(priority, from_string(value)); +} + +} // namespace libdnf diff --git a/libdnf/conf/option_string.cpp b/libdnf/conf/option_string.cpp new file mode 100644 index 0000000000..b912571760 --- /dev/null +++ b/libdnf/conf/option_string.cpp @@ -0,0 +1,86 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_string.hpp" + +#include + +namespace libdnf { + +OptionString::OptionString(const std::string & default_value) + : Option(Priority::DEFAULT) + , icase(false) + , default_value(default_value) + , value(default_value) {} + +OptionString::OptionString(const char * default_value) : icase(false) { + if (default_value) { + this->value = this->default_value = default_value; + set_priority(Priority::DEFAULT); + } +} + +OptionString::OptionString(const std::string & default_value, std::string regex, bool icase) + : Option(Priority::DEFAULT) + , regex(std::move(regex)) + , icase(icase) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +OptionString::OptionString(const char * default_value, std::string regex, bool icase) + : regex(std::move(regex)) + , icase(icase) { + if (default_value) { + this->default_value = default_value; + test(this->default_value); + this->value = this->default_value; + set_priority(Priority::DEFAULT); + } +} + +void OptionString::test(const std::string & value) const { + if (regex.empty()) { + return; + } + std::regex re( + regex, + std::regex::nosubs | std::regex::extended | (icase ? std::regex::icase : std::regex_constants::ECMAScript)); + if (!std::regex_match(value, re)) { + throw NotAllowedValue(value); + } +} + +void OptionString::set(Priority priority, const std::string & value) { + if (priority >= get_priority()) { + test(value); + this->value = value; + set_priority(priority); + } +} + +const std::string & OptionString::get_value() const { + if (get_priority() == Priority::EMPTY) { + throw ValueNotSet(); + } + return value; +} + +} // namespace libdnf diff --git a/libdnf/conf/option_string_list.cpp b/libdnf/conf/option_string_list.cpp new file mode 100644 index 0000000000..d4949b9e72 --- /dev/null +++ b/libdnf/conf/option_string_list.cpp @@ -0,0 +1,116 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/conf/option_string_list.hpp" + +#include + +namespace libdnf { + +OptionStringList::OptionStringList(const ValueType & default_value) + : Option(Priority::DEFAULT) + , icase(false) + , default_value(default_value) + , value(default_value) {} + +OptionStringList::OptionStringList(const ValueType & default_value, std::string regex, bool icase) + : Option(Priority::DEFAULT) + , regex(std::move(regex)) + , icase(icase) + , default_value(default_value) + , value(default_value) { + test(default_value); +} + +OptionStringList::OptionStringList(const std::string & default_value) : Option(Priority::DEFAULT), icase(false) { + this->value = this->default_value = from_string(default_value); +} + +OptionStringList::OptionStringList(const std::string & default_value, std::string regex, bool icase) + : Option(Priority::DEFAULT) + , regex(std::move(regex)) + , icase(icase) { + this->default_value = from_string(default_value); + test(this->default_value); + value = this->default_value; +} + +void OptionStringList::test(const std::vector & value) const { + if (regex.empty()) { + return; + } + std::regex re( + regex, + std::regex::nosubs | std::regex::extended | (icase ? std::regex::icase : std::regex_constants::ECMAScript)); + for (const auto & val : value) { + if (!std::regex_match(val, re)) { + throw NotAllowedValue(val); + } + } +} + +OptionStringList::ValueType OptionStringList::from_string(const std::string & value) const { + std::vector tmp; + auto start = value.find_first_not_of(" "); + while (start != std::string::npos && start < value.length()) { + auto end = value.find_first_of(" ,\n", start); + if (end == std::string::npos) { + tmp.push_back(value.substr(start)); + break; + } + tmp.push_back(value.substr(start, end - start)); + start = value.find_first_not_of(" ", end + 1); + if (start != std::string::npos && value[start] == ',' && value[end] == ' ') { + end = start; + start = value.find_first_not_of(" ", start + 1); + } + if (start != std::string::npos && value[start] == '\n' && (value[end] == ' ' || value[end] == ',')) { + start = value.find_first_not_of(" ", start + 1); + } + } + return tmp; +} + +void OptionStringList::set(Priority priority, const ValueType & value) { + if (priority >= get_priority()) { + test(value); + this->value = value; + set_priority(priority); + } +} + +void OptionStringList::set(Priority priority, const std::string & value) { + set(priority, from_string(value)); +} + +std::string OptionStringList::to_string(const ValueType & value) const { + std::ostringstream oss; + bool next{false}; + for (auto & val : value) { + if (next) { + oss << ", "; + } else { + next = true; + } + oss << val; + } + return oss.str(); +} + +} // namespace libdnf diff --git a/libdnf/rpm/repo/config_repo.cpp b/libdnf/rpm/repo/config_repo.cpp new file mode 100644 index 0000000000..465c35065e --- /dev/null +++ b/libdnf/rpm/repo/config_repo.cpp @@ -0,0 +1,305 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/rpm/repo/config_repo.hpp" + +#include "../../conf/config-private.hpp" + +#include "libdnf/conf/const.hpp" + +namespace libdnf::rpm { + +class ConfigRepo::Impl { + friend class ConfigRepo; + + Impl(Config & owner, ConfigMain & master_config); + + Config & owner; + ConfigMain & master_config; + + OptionString name{""}; + OptionChild enabled{master_config.enabled()}; + OptionChild basecachedir{master_config.cachedir()}; + OptionStringList baseurl{std::vector{}}; + OptionString mirrorlist{nullptr}; + OptionString metalink{nullptr}; + OptionString type{""}; + OptionString mediaid{""}; + OptionStringList gpgkey{std::vector{}}; + OptionStringList excludepkgs{std::vector{}}; + OptionStringList includepkgs{std::vector{}}; + OptionChild fastestmirror{master_config.fastestmirror()}; + OptionChild proxy{master_config.proxy()}; + OptionChild proxy_username{master_config.proxy_username()}; + OptionChild proxy_password{master_config.proxy_password()}; + OptionChild> proxy_auth_method{master_config.proxy_auth_method()}; + OptionChild username{master_config.username()}; + OptionChild password{master_config.password()}; + OptionChild protected_packages{master_config.protected_packages()}; + OptionChild gpgcheck{master_config.gpgcheck()}; + OptionChild repo_gpgcheck{master_config.repo_gpgcheck()}; + OptionChild enablegroups{master_config.enablegroups()}; + OptionChild> retries{master_config.retries()}; + OptionChild> bandwidth{master_config.bandwidth()}; + OptionChild> minrate{master_config.minrate()}; + OptionChild> ip_resolve{master_config.ip_resolve()}; + OptionChild> throttle{master_config.throttle()}; + OptionChild timeout{master_config.timeout()}; + OptionChild> max_parallel_downloads{master_config.max_parallel_downloads()}; + OptionChild metadata_expire{master_config.metadata_expire()}; + OptionNumber cost{DEFAULT_REPO_COST}; + OptionNumber priority{DEFAULT_REPO_PRIORITY}; + OptionBool module_hotfixes{false}; + OptionChild sslcacert{master_config.sslcacert()}; + OptionChild sslverify{master_config.sslverify()}; + OptionChild sslclientcert{master_config.sslclientcert()}; + OptionChild sslclientkey{master_config.sslclientkey()}; + OptionChild deltarpm{master_config.deltarpm()}; + OptionChild> deltarpm_percentage{master_config.deltarpm_percentage()}; + OptionChild skip_if_unavailable{master_config.skip_if_unavailable()}; + OptionString enabled_metadata{""}; + OptionChild user_agent{master_config.user_agent()}; + OptionChild countme{master_config.countme()}; + OptionEnum failovermethod{"priority", {"priority", "roundrobin"}}; +}; + +ConfigRepo::Impl::Impl(Config & owner, ConfigMain & master_config) : owner(owner), master_config(master_config) { + owner.opt_binds().add("name", name); + owner.opt_binds().add("enabled", enabled); + owner.opt_binds().add("cachedir", basecachedir); + owner.opt_binds().add("baseurl", baseurl); + owner.opt_binds().add("mirrorlist", mirrorlist); + owner.opt_binds().add("metalink", metalink); + owner.opt_binds().add("type", type); + owner.opt_binds().add("mediaid", mediaid); + owner.opt_binds().add("gpgkey", gpgkey); + + owner.opt_binds().add( + "excludepkgs", + excludepkgs, + [&](Option::Priority priority, const std::string & value) { optionTListAppend(excludepkgs, priority, value); }, + nullptr, + true); + owner.opt_binds().add( + "exclude", + excludepkgs, //compatibility with yum + [&](Option::Priority priority, const std::string & value) { optionTListAppend(excludepkgs, priority, value); }, + nullptr, + true); + + owner.opt_binds().add( + "includepkgs", + includepkgs, + [&](Option::Priority priority, const std::string & value) { optionTListAppend(includepkgs, priority, value); }, + nullptr, + true); + + owner.opt_binds().add("fastestmirror", fastestmirror); + + owner.opt_binds().add( + "proxy", + proxy, + [&](Option::Priority priority, const std::string & value) { + auto tmp_value(value); + for (auto & ch : tmp_value) { + ch = static_cast(std::tolower(ch)); + } + if (tmp_value == "_none_") { + proxy.set(priority, ""); + } else { + proxy.set(priority, value); + } + }, + nullptr, + false); + + owner.opt_binds().add("proxy_username", proxy_username); + owner.opt_binds().add("proxy_password", proxy_password); + owner.opt_binds().add("proxy_auth_method", proxy_auth_method); + owner.opt_binds().add("username", username); + owner.opt_binds().add("password", password); + owner.opt_binds().add("protected_packages", protected_packages); + owner.opt_binds().add("gpgcheck", gpgcheck); + owner.opt_binds().add("repo_gpgcheck", repo_gpgcheck); + owner.opt_binds().add("enablegroups", enablegroups); + owner.opt_binds().add("retries", retries); + owner.opt_binds().add("bandwidth", bandwidth); + owner.opt_binds().add("minrate", minrate); + owner.opt_binds().add("ip_resolve", ip_resolve); + owner.opt_binds().add("throttle", throttle); + owner.opt_binds().add("timeout", timeout); + owner.opt_binds().add("max_parallel_downloads", max_parallel_downloads); + owner.opt_binds().add("metadata_expire", metadata_expire); + owner.opt_binds().add("cost", cost); + owner.opt_binds().add("priority", priority); + owner.opt_binds().add("module_hotfixes", module_hotfixes); + owner.opt_binds().add("sslcacert", sslcacert); + owner.opt_binds().add("sslverify", sslverify); + owner.opt_binds().add("sslclientcert", sslclientcert); + owner.opt_binds().add("sslclientkey", sslclientkey); + owner.opt_binds().add("deltarpm", deltarpm); + owner.opt_binds().add("deltarpm_percentage", deltarpm_percentage); + owner.opt_binds().add("skip_if_unavailable", skip_if_unavailable); + owner.opt_binds().add("enabled_metadata", enabled_metadata); + owner.opt_binds().add("user_agent", user_agent); + owner.opt_binds().add("countme", countme); +} + +ConfigRepo::ConfigRepo(ConfigMain & master_config) : p_impl(new Impl(*this, master_config)) {} +ConfigRepo::~ConfigRepo() = default; +ConfigRepo::ConfigRepo(ConfigRepo && src) noexcept : p_impl(std::move(src.p_impl)) {} + +ConfigMain & ConfigRepo::get_master_config() { + return p_impl->master_config; +} + +OptionString & ConfigRepo::name() { + return p_impl->name; +} +OptionChild & ConfigRepo::enabled() { + return p_impl->enabled; +} +OptionChild & ConfigRepo::basecachedir() { + return p_impl->basecachedir; +} +OptionStringList & ConfigRepo::baseurl() { + return p_impl->baseurl; +} +OptionString & ConfigRepo::mirrorlist() { + return p_impl->mirrorlist; +} +OptionString & ConfigRepo::metalink() { + return p_impl->metalink; +} +OptionString & ConfigRepo::type() { + return p_impl->type; +} +OptionString & ConfigRepo::mediaid() { + return p_impl->mediaid; +} +OptionStringList & ConfigRepo::gpgkey() { + return p_impl->gpgkey; +} +OptionStringList & ConfigRepo::excludepkgs() { + return p_impl->excludepkgs; +} +OptionStringList & ConfigRepo::includepkgs() { + return p_impl->includepkgs; +} +OptionChild & ConfigRepo::fastestmirror() { + return p_impl->fastestmirror; +} +OptionChild & ConfigRepo::proxy() { + return p_impl->proxy; +} +OptionChild & ConfigRepo::proxy_username() { + return p_impl->proxy_username; +} +OptionChild & ConfigRepo::proxy_password() { + return p_impl->proxy_password; +} +OptionChild> & ConfigRepo::proxy_auth_method() { + return p_impl->proxy_auth_method; +} +OptionChild & ConfigRepo::username() { + return p_impl->username; +} +OptionChild & ConfigRepo::password() { + return p_impl->password; +} +OptionChild & ConfigRepo::protected_packages() { + return p_impl->protected_packages; +} +OptionChild & ConfigRepo::gpgcheck() { + return p_impl->gpgcheck; +} +OptionChild & ConfigRepo::repo_gpgcheck() { + return p_impl->repo_gpgcheck; +} +OptionChild & ConfigRepo::enablegroups() { + return p_impl->enablegroups; +} +OptionChild> & ConfigRepo::retries() { + return p_impl->retries; +} +OptionChild> & ConfigRepo::bandwidth() { + return p_impl->bandwidth; +} +OptionChild> & ConfigRepo::minrate() { + return p_impl->minrate; +} +OptionChild> & ConfigRepo::ip_resolve() { + return p_impl->ip_resolve; +} +OptionChild> & ConfigRepo::throttle() { + return p_impl->throttle; +} +OptionChild & ConfigRepo::timeout() { + return p_impl->timeout; +} +OptionChild> & ConfigRepo::max_parallel_downloads() { + return p_impl->max_parallel_downloads; +} +OptionChild & ConfigRepo::metadata_expire() { + return p_impl->metadata_expire; +} +OptionNumber & ConfigRepo::cost() { + return p_impl->cost; +} +OptionNumber & ConfigRepo::priority() { + return p_impl->priority; +} +OptionBool & ConfigRepo::module_hotfixes() { + return p_impl->module_hotfixes; +} +OptionChild & ConfigRepo::sslcacert() { + return p_impl->sslcacert; +} +OptionChild & ConfigRepo::sslverify() { + return p_impl->sslverify; +} +OptionChild & ConfigRepo::sslclientcert() { + return p_impl->sslclientcert; +} +OptionChild & ConfigRepo::sslclientkey() { + return p_impl->sslclientkey; +} +OptionChild & ConfigRepo::deltarpm() { + return p_impl->deltarpm; +} +OptionChild> & ConfigRepo::deltarpm_percentage() { + return p_impl->deltarpm_percentage; +} +OptionChild & ConfigRepo::skip_if_unavailable() { + return p_impl->skip_if_unavailable; +} +OptionString & ConfigRepo::enabled_metadata() { + return p_impl->enabled_metadata; +} +OptionChild & ConfigRepo::user_agent() { + return p_impl->user_agent; +} +OptionChild & ConfigRepo::countme() { + return p_impl->countme; +} +OptionEnum & ConfigRepo::failovermethod() { + return p_impl->failovermethod; +} + +} // namespace libdnf::rpm diff --git a/libdnf/rpm/repo/repo-private.hpp b/libdnf/rpm/repo/repo-private.hpp new file mode 100644 index 0000000000..e7fa84abae --- /dev/null +++ b/libdnf/rpm/repo/repo-private.hpp @@ -0,0 +1,207 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_RPM_REPO_REPO_PRIVATE_HPP +#define LIBDNF_RPM_REPO_REPO_PRIVATE_HPP + +#include "libdnf/base/base.hpp" +#include "libdnf/rpm/repo/repo.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MD_TYPE_PRIMARY "primary" +#define MD_TYPE_FILELISTS "filelists" +#define MD_TYPE_PRESTODELTA "prestodelta" +#define MD_TYPE_GROUP_GZ "group_gz" +#define MD_TYPE_GROUP "group" +#define MD_TYPE_UPDATEINFO "updateinfo" +#define MD_TYPE_MODULES "modules" +/* "other" in this context is not a generic "any other metadata", but real metadata type named "other" + * containing changelogs for packages */ +#define MD_TYPE_OTHER "other" + +#define CHKSUM_BYTES 32 + +enum _hy_repo_state { _HY_NEW, _HY_LOADED_FETCH, _HY_LOADED_CACHE, _HY_WRITTEN }; + +namespace std { + +template <> +struct default_delete { + void operator()(LrHandle * ptr) noexcept { lr_handle_free(ptr); } +}; + +} // namespace std + +namespace libdnf::rpm { + +using LibsolvRepo = ::Repo; + +class Key { +public: + Key(gpgme_key_t key, gpgme_subkey_t subkey) { + id = subkey->keyid; + fingerprint = subkey->fpr; + timestamp = subkey->timestamp; + userid = key->uids->uid; + } + + std::string get_id() const { return id; } + std::string get_user_id() const { return userid; } + std::string get_fingerprint() const { return fingerprint; } + long int get_timestamp() const { return timestamp; } + + std::vector raw_key; + std::string url; + +private: + std::string id; + std::string fingerprint; + std::string userid; + long int timestamp; +}; + +class Repo::Impl { +public: + Impl(Repo & owner, std::string id, Type type, std::unique_ptr && conf, Base & base); + ~Impl(); + + bool load(); + bool load_cache(bool throw_except); + void download_metadata(const std::string & destdir); + bool is_in_sync(); + void fetch(const std::string & destdir, std::unique_ptr && h); + std::string get_cachedir() const; + std::string get_persistdir() const; + int64_t get_age() const; + void expire(); + bool is_expired() const; + int get_expires_in() const; + void download_url(const char * url, int fd); + void add_countme_flag(LrHandle * handle); + void set_http_headers(const char * headers[]); + const char * const * get_http_headers() const; + const std::string & get_metadata_path(const std::string & metadata_type) const; + + std::unique_ptr lr_handle_init_base(); + std::unique_ptr lr_handle_init_local(); + std::unique_ptr lr_handle_init_remote(const char * destdir, bool mirror_setup = true); + + void attach_libsolv_repo(LibsolvRepo * libsolv_repo); + void detach_libsolv_repo(); + + LrHandle * get_cached_handle(); + +private: + friend class Repo; + std::string id; + Type type; + std::unique_ptr conf; + + char ** mirrors{nullptr}; + int max_mirror_tries{0}; // try them all + // 0 forces expiration on the next call to load(), -1 means undefined value + int64_t timestamp; + int max_timestamp{0}; + bool preserve_remote_time{false}; + std::string repomd_fn; + std::set additional_metadata; + std::string revision; + std::vector content_tags; + std::vector> distro_tags; + std::vector> metadata_locations; + unsigned char checksum[CHKSUM_BYTES]; + bool use_includes{false}; + bool load_metadata_other; + std::map substitutions; + + std::unique_ptr callbacks; + std::string repo_file_path; + + SyncStrategy sync_strategy; + std::map metadata_paths; + + LibsolvRepo * libsolv_repo{nullptr}; + bool needs_internalizing{false}; + int nrefs{1}; + + enum _hy_repo_state state_main { _HY_NEW }; + enum _hy_repo_state state_filelists { _HY_NEW }; + enum _hy_repo_state state_presto { _HY_NEW }; + enum _hy_repo_state state_updateinfo { _HY_NEW }; + enum _hy_repo_state state_other { _HY_NEW }; + Id filenames_repodata{0}; + Id presto_repodata{0}; + Id updateinfo_repodata{0}; + Id other_repodata{0}; + int load_flags{0}; + /* the following three elements are needed for repo rewriting */ + int main_nsolvables{0}; + int main_nrepodata{0}; + int main_end{0}; + + // Lock attachLibsolvRepo(), detachLibsolvRepo() and hy_repo_free() to ensure atomic behavior + // in threaded environment such as PackageKit. + std::mutex attach_libsolv_mutex; + + Repo * owner; + Base * base; + std::unique_ptr lr_handle_perform( + LrHandle * handle, const std::string & dest_directory, bool set_gpg__home_dir); + bool is_metalink_in_sync(); + bool is_repomd_in_sync(); + void reset_metadata_expired(); + std::vector retrieve(const std::string & url); + void import_repo_keys(); + + static int progress_cb(void * data, double total_to_download, double downloaded); + static void fastest_mirror_cb(void * data, LrFastestMirrorStages stage, void * ptr); + static int mirror_failure_cb(void * data, const char * msg, const char * url, const char * metadata); + + bool expired; + std::unique_ptr handle; + std::unique_ptr> http_headers { + nullptr, [](char ** ptr) { + for (auto item = ptr; *item; ++item) + delete[] * item; + delete[] ptr; + } + }; + static bool ends_with(std::string const & str, std::string const & ending); + std::string get_hash() const; +}; + +} // namespace libdnf::rpm + +#endif diff --git a/libdnf/rpm/repo/repo.cpp b/libdnf/rpm/repo/repo.cpp new file mode 100644 index 0000000000..5f12b0f516 --- /dev/null +++ b/libdnf/rpm/repo/repo.cpp @@ -0,0 +1,2094 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#define METADATA_RELATIVE_DIR "repodata" +#define PACKAGES_RELATIVE_DIR "packages" +#define METALINK_FILENAME "metalink.xml" +#define MIRRORLIST_FILENAME "mirrorlist" +#define RECOGNIZED_CHKSUMS \ + { "sha512", "sha256" } + +#define USER_AGENT "libdnf" + +constexpr const char * REPOID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:"; + +#include "../libdnf/utils/bgettext/bgettext-lib.h" +#include "../libdnf/utils/tinyformat/tinyformat.hpp" +#include "repo-private.hpp" + +#include "libdnf/logger/logger.hpp" +#include "libdnf/utils/utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// COUNTME CONSTANTS +// +// width of the sliding time window (in seconds) +const int COUNTME_WINDOW = 7 * 24 * 60 * 60; // 1 week +// starting point of the sliding time window relative to the UNIX epoch +// allows for aligning the window with a specific weekday +const int COUNTME_OFFSET = 345600; // Monday (1970-01-05 00:00:00 UTC) +// estimated number of metalink requests sent over the window +// used to generate the probability distribution of counting events +const int COUNTME_BUDGET = 4; // metadata_expire defaults to 2 days +// cookie file name +const std::string COUNTME_COOKIE = "countme"; +// cookie file format version +const int COUNTME_VERSION = 0; +// longevity buckets that we report in the flag +// example: {A, B, C} defines 4 buckets [0, A), [A, B), [B, C), [C, infinity) +// where each letter represents a window step (starting from 0) +const std::array COUNTME_BUCKETS = {{2, 5, 25}}; + +namespace std { + +template <> +struct default_delete { + void operator()(GError * ptr) noexcept { g_error_free(ptr); } +}; + +template <> +struct default_delete { + void operator()(LrResult * ptr) noexcept { lr_result_free(ptr); } +}; + +template <> +struct default_delete { + void operator()(LrPackageTarget * ptr) noexcept { lr_packagetarget_free(ptr); } +}; + +template <> +struct default_delete::type> { + void operator()(gpgme_ctx_t ptr) noexcept { gpgme_release(ptr); } +}; + +} // namespace std + +namespace libdnf::rpm { + +class LrExceptionWithSourceUrl : public LrException { +public: + LrExceptionWithSourceUrl(int code, const std::string & msg, std::string source_url) + : LrException(code, msg) + , source_url(std::move(source_url)) {} + const std::string & get_source_url() const { return source_url; } + +private: + std::string source_url; +}; + +static void move_recursive(const std::string & src, const std::string & dest) { + try { + std::filesystem::rename(src, dest); + } catch (const std::filesystem::filesystem_error & ex) { + std::filesystem::copy(src, dest, std::filesystem::copy_options::recursive); + std::filesystem::remove_all(src); + } +} + +static int64_t mtime(const char * filename) { + struct stat st; + stat(filename, &st); + return st.st_mtime; +} + +static void throw_exception(std::unique_ptr && err) { + throw LrException(err->code, err->message); +} + +template +inline static void handle_set_opt(LrHandle * handle, LrHandleOption option, T value) { + GError * err_p{nullptr}; + if (!lr_handle_setopt(handle, &err_p, option, value)) { + throw_exception(std::unique_ptr(err_p)); + } +} + +inline static void handle_get_info(LrHandle * handle, LrHandleInfoOption option, void * value) { + GError * err_p{nullptr}; + if (!lr_handle_getinfo(handle, &err_p, option, value)) { + throw_exception(std::unique_ptr(err_p)); + } +} + +template +inline static void result_get_info(LrResult * result, LrResultInfoOption option, T value) { + GError * err_p{nullptr}; + if (!lr_result_getinfo(result, &err_p, option, value)) { + throw_exception(std::unique_ptr(err_p)); + } +} + +/* Callback stuff */ + +int RepoCB::progress(double /*totalToDownload*/, double /*downloaded*/) { + return 0; +} +void RepoCB::fastest_mirror(FastestMirrorStage /*stage*/, const char * /*ptr*/) {} +int RepoCB::handle_mirror_failure(const char * /*msg*/, const char * /*url*/, const char * /*metadata*/) { + return 0; +} + +bool RepoCB::repokey_import( + const std::string & /*id*/, + const std::string & /*userId*/, + const std::string & /*fingerprint*/, + const std::string & /*url*/, + long int /*timestamp*/) { + return true; +} + + +// Map string from config option proxy_auth_method to librepo LrAuth value +static constexpr struct { + const char * name; + LrAuth code; +} PROXYAUTHMETHODS[] = {{"none", LR_AUTH_NONE}, + {"basic", LR_AUTH_BASIC}, + {"digest", LR_AUTH_DIGEST}, + {"negotiate", LR_AUTH_NEGOTIATE}, + {"ntlm", LR_AUTH_NTLM}, + {"digest_ie", LR_AUTH_DIGEST_IE}, + {"ntlm_wb", LR_AUTH_NTLM_WB}, + {"any", LR_AUTH_ANY}}; + +bool Repo::Impl::ends_with(const std::string & str, const std::string & ending) { + if (str.length() >= ending.length()) { + return (str.compare(str.length() - ending.length(), ending.length(), ending) == 0); + } + return false; +} + +const std::string & Repo::Impl::get_metadata_path(const std::string & metadata_type) const { + auto logger = base->get_logger(); + static const std::string empty; + std::string lookup_metadata_type = metadata_type; + if (conf->get_master_config().zchunk().get_value()) { + if (!ends_with(metadata_type, "_zck")) { + lookup_metadata_type = metadata_type + "_zck"; + } + } + auto it = metadata_paths.find(lookup_metadata_type); + if (it == metadata_paths.end() && lookup_metadata_type != metadata_type) { + it = metadata_paths.find(metadata_type); + } + auto & ret = (it != metadata_paths.end()) ? it->second : empty; + if (ret.empty()) { + logger->debug(tfm::format("not found \"%s\" for: %s", metadata_type, conf->name().get_value())); + } + return ret; +} + +int Repo::Impl::progress_cb(void * data, double total_to_download, double downloaded) { + if (!data) { + return 0; + } + auto cb_object = static_cast(data); + return cb_object->progress(total_to_download, downloaded); +} + +void Repo::Impl::fastest_mirror_cb(void * data, LrFastestMirrorStages stage, void * ptr) { + if (!data) { + return; + } + auto cb_object = static_cast(data); + const char * msg; + std::string msg_string; + if (ptr) { + switch (stage) { + case LR_FMSTAGE_CACHELOADING: + case LR_FMSTAGE_CACHELOADINGSTATUS: + case LR_FMSTAGE_STATUS: + msg = static_cast(ptr); + break; + case LR_FMSTAGE_DETECTION: + msg_string = std::to_string(*(static_cast(ptr))); + msg = msg_string.c_str(); + break; + default: + msg = nullptr; + } + } else { + msg = nullptr; + } + cb_object->fastest_mirror(static_cast(stage), msg); +} + +int Repo::Impl::mirror_failure_cb(void * data, const char * msg, const char * url, const char * metadata) { + if (!data) { + return 0; + } + auto cb_object = static_cast(data); + return cb_object->handle_mirror_failure(msg, url, metadata); +}; + + +/** +* @brief Converts the given input string to a URL encoded string +* +* All input characters that are not a-z, A-Z, 0-9, '-', '.', '_' or '~' are converted +* to their "URL escaped" version (%NN where NN is a two-digit hexadecimal number). +* +* @param src String to encode +* @return URL encoded string +*/ +static std::string url_encode(const std::string & src) { + auto no_encode = [](char ch) { return isalnum(ch) != 0 || ch == '-' || ch == '.' || ch == '_' || ch == '~'; }; + + // compute length of encoded string + auto len = src.length(); + for (auto ch : src) { + if (!no_encode(ch)) { + len += 2; + } + } + + // encode the input string + std::string encoded; + encoded.reserve(len); + for (auto ch : src) { + if (no_encode(ch)) { + encoded.push_back(ch); + } else { + encoded.push_back('%'); + int hex; + hex = static_cast(ch) >> 4; + hex += hex <= 9 ? '0' : 'a' - 10; + encoded.push_back(static_cast(hex)); + hex = static_cast(ch) & 0x0F; + hex += hex <= 9 ? '0' : 'a' - 10; + encoded.push_back(static_cast(hex)); + } + } + + return encoded; +} + +/** +* @brief Format user password string +* +* Returns user and password in user:password form. If quote is True, +* special characters in user and password are URL encoded. +* +* @param user Username +* @param passwd Password +* @param encode If quote is True, special characters in user and password are URL encoded. +* @return User and password in user:password form +*/ +static std::string format_user_pass_string(const std::string & user, const std::string & passwd, bool encode) { + if (encode) { + return url_encode(user) + ":" + url_encode(passwd); + } else { + return user + ":" + passwd; + } +} + +Repo::Impl::Impl(Repo & owner, std::string id, Type type, std::unique_ptr && conf, Base & base) + : id(std::move(id)) + , type(type) + , conf(std::move(conf)) + , timestamp(-1) + , load_metadata_other(false) + , sync_strategy(SyncStrategy::TRY_CACHE) + , owner(&owner) + , base(&base) + , expired(false) {} + +Repo::Impl::~Impl() { + g_strfreev(mirrors); + if (libsolv_repo) { + libsolv_repo->appdata = nullptr; + } +} + +Repo::Repo(const std::string & id, std::unique_ptr && conf, Base & base, Repo::Type type) { + if (type == Type::AVAILABLE) { + auto idx = verify_id(id); + if (idx >= 0) { + std::string msg = tfm::format(_("Bad id for repo: %s, byte = %s %d"), id, id[idx], idx); + throw std::runtime_error(msg); + } + } + p_impl.reset(new Impl(*this, id, type, std::move(conf), base)); +} + +Repo::~Repo() = default; + +void Repo::set_callbacks(std::unique_ptr && callbacks) { + p_impl->callbacks = std::move(callbacks); +} + +int Repo::verify_id(const std::string & id) { + auto idx = id.find_first_not_of(REPOID_CHARS); + return idx == id.npos ? -1 : static_cast(idx); +} + +void Repo::verify() const { + if (p_impl->conf->baseurl().empty() && + (p_impl->conf->metalink().empty() || p_impl->conf->metalink().get_value().empty()) && + (p_impl->conf->mirrorlist().empty() || p_impl->conf->mirrorlist().get_value().empty())) + throw std::runtime_error(tfm::format(_("Repository %s has no mirror or baseurl set."), p_impl->id)); + + const auto & type = p_impl->conf->type().get_value(); + const char * supportedRepoTypes[]{"rpm-md", "rpm", "repomd", "rpmmd", "yum", "YUM"}; + if (!type.empty()) { + for (auto supported : supportedRepoTypes) { + if (type == supported) { + return; + } + } + throw std::runtime_error( + tfm::format(_("Repository '%s' has unsupported type: 'type=%s', skipping."), p_impl->id, type)); + } +} + +ConfigRepo * Repo::get_config() noexcept { + return p_impl->conf.get(); +} + +const std::string & Repo::get_id() const noexcept { + return p_impl->id; +} + +void Repo::enable() { + p_impl->conf->enabled().set(Option::Priority::RUNTIME, true); +} + +void Repo::disable() { + p_impl->conf->enabled().set(Option::Priority::RUNTIME, false); +} + +bool Repo::is_enabled() const { + return p_impl->conf->enabled().get_value(); +} + +bool Repo::is_local() const { + auto & conf = p_impl->conf; + if ((!conf->metalink().empty() && !conf->metalink().get_value().empty()) || + (!conf->mirrorlist().empty() && !conf->mirrorlist().get_value().empty())) { + return false; + } + if (!conf->baseurl().get_value().empty() && conf->baseurl().get_value()[0].compare(0, 7, "file://") == 0) { + return true; + } + return false; +} + +bool Repo::load() { + return p_impl->load(); +} +bool Repo::load_cache(bool throw_except) { + return p_impl->load_cache(throw_except); +} +void Repo::download_metadata(const std::string & destdir) { + p_impl->download_metadata(destdir); +} +bool Repo::get_use_includes() const { + return p_impl->use_includes; +} +void Repo::set_use_includes(bool enabled) { + p_impl->use_includes = enabled; +} +bool Repo::get_load_metadata_other() const { + return p_impl->load_metadata_other; +} +void Repo::set_load_metadata_other(bool value) { + p_impl->load_metadata_other = value; +} +int Repo::get_cost() const { + return p_impl->conf->cost().get_value(); +} +int Repo::get_priority() const { + return p_impl->conf->priority().get_value(); +} +std::string Repo::get_comps_fn() { + auto tmp = p_impl->get_metadata_path(MD_TYPE_GROUP_GZ); + if (tmp.empty()) + tmp = p_impl->get_metadata_path(MD_TYPE_GROUP); + return tmp; +} + + +#ifdef MODULEMD +std::string Repo::get_modules_fn() { + return p_impl->get_metadata_path(MD_TYPE_MODULES); +} +#endif + +int64_t Repo::get_age() const { + return p_impl->get_age(); +} +void Repo::expire() { + p_impl->expire(); +} +bool Repo::is_expired() const { + return p_impl->is_expired(); +} +int Repo::get_expires_in() const { + return p_impl->get_expires_in(); +} + +void Repo::set_substitutions(const std::map & substitutions) { + p_impl->substitutions = substitutions; +} + +void Repo::add_metadata_type_to_download(const std::string & metadata_type) { + p_impl->additional_metadata.insert(metadata_type); +} + +void Repo::remove_metadata_type_from_download(const std::string & metadata_type) { + p_impl->additional_metadata.erase(metadata_type); +} + +std::string Repo::get_metadata_path(const std::string & metadata_type) { + return p_impl->get_metadata_path(metadata_type); +} + +/*std::string Repo::getMetadataContent(const std::string &metadataType) +{ + auto path = getMetadataPath(metadataType); + if (path.empty()) return ""; + + auto mdfile = File::newFile(path); + mdfile->open("r"); + const auto &content = mdfile->getContent(); + mdfile->close(); + return content; +}*/ + +std::unique_ptr Repo::Impl::lr_handle_init_base() { + std::unique_ptr h(lr_handle_init()); + std::vector dlist = { + MD_TYPE_PRIMARY, MD_TYPE_FILELISTS, MD_TYPE_PRESTODELTA, MD_TYPE_GROUP_GZ, MD_TYPE_UPDATEINFO}; + +#ifdef MODULEMD + dlist.push_back(MD_TYPE_MODULES); +#endif + if (load_metadata_other) { + dlist.push_back(MD_TYPE_OTHER); + } + for (auto & item : additional_metadata) { + dlist.push_back(item.c_str()); + } + dlist.push_back(nullptr); + handle_set_opt(h.get(), LRO_PRESERVETIME, static_cast(preserve_remote_time)); + handle_set_opt(h.get(), LRO_REPOTYPE, LR_YUMREPO); + handle_set_opt(h.get(), LRO_USERAGENT, conf->user_agent().get_value().c_str()); + handle_set_opt(h.get(), LRO_YUMDLIST, dlist.data()); + handle_set_opt(h.get(), LRO_INTERRUPTIBLE, 1L); + handle_set_opt(h.get(), LRO_GPGCHECK, conf->repo_gpgcheck().get_value()); + handle_set_opt(h.get(), LRO_MAXMIRRORTRIES, static_cast(max_mirror_tries)); + handle_set_opt(h.get(), LRO_MAXPARALLELDOWNLOADS, conf->max_parallel_downloads().get_value()); + + LrUrlVars * vars = nullptr; + vars = lr_urlvars_set(vars, MD_TYPE_GROUP_GZ, MD_TYPE_GROUP); + handle_set_opt(h.get(), LRO_YUMSLIST, vars); + + return h; +} + +std::unique_ptr Repo::Impl::lr_handle_init_local() { + std::unique_ptr h(lr_handle_init_base()); + + LrUrlVars * vars = nullptr; + for (const auto & item : substitutions) { + vars = lr_urlvars_set(vars, item.first.c_str(), item.second.c_str()); + } + handle_set_opt(h.get(), LRO_VARSUB, vars); + auto cachedir = get_cachedir(); + handle_set_opt(h.get(), LRO_DESTDIR, cachedir.c_str()); + const char * urls[] = {cachedir.c_str(), nullptr}; + handle_set_opt(h.get(), LRO_URLS, urls); + handle_set_opt(h.get(), LRO_LOCAL, 1L); +#ifdef LRO_SUPPORTS_CACHEDIR + /* If zchunk is enabled, set librepo cache dir */ + if (conf->getMasterConfig().zchunk().get_value()) { + handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().get_value().c_str()); + } +#endif + return h; +} + +std::unique_ptr Repo::Impl::lr_handle_init_remote(const char * destdir, bool mirror_setup) { + std::unique_ptr h(lr_handle_init_base()); + handle_set_opt(h.get(), LRO_HTTPHEADER, http_headers.get()); + + LrUrlVars * vars = nullptr; + for (const auto & item : substitutions) { + vars = lr_urlvars_set(vars, item.first.c_str(), item.second.c_str()); + } + handle_set_opt(h.get(), LRO_VARSUB, vars); + + handle_set_opt(h.get(), LRO_DESTDIR, destdir); + + auto & ip_resolve = conf->ip_resolve().get_value(); + if (ip_resolve == "ipv4") { + handle_set_opt(h.get(), LRO_IPRESOLVE, LR_IPRESOLVE_V4); + } else if (ip_resolve == "ipv6") { + handle_set_opt(h.get(), LRO_IPRESOLVE, LR_IPRESOLVE_V6); + } + + enum class Source { NONE, METALINK, MIRRORLIST } source{Source::NONE}; + std::string tmp; + if (!conf->metalink().empty() && !(tmp = conf->metalink().get_value()).empty()) { + source = Source::METALINK; + } else if (!conf->mirrorlist().empty() && !(tmp = conf->mirrorlist().get_value()).empty()) { + source = Source::MIRRORLIST; + } + if (source != Source::NONE) { + handle_set_opt(h.get(), LRO_HMFCB, static_cast(mirror_failure_cb)); + handle_set_opt(h.get(), LRO_PROGRESSDATA, callbacks.get()); + if (mirror_setup) { + if (source == Source::METALINK) { + handle_set_opt(h.get(), LRO_METALINKURL, tmp.c_str()); + } else { + handle_set_opt(h.get(), LRO_MIRRORLISTURL, tmp.c_str()); + // YUM-DNF compatibility hack. YUM guessed by content of keyword "metalink" if + // mirrorlist is really mirrorlist or metalink) + if (tmp.find("metalink") != tmp.npos) + handle_set_opt(h.get(), LRO_METALINKURL, tmp.c_str()); + } + handle_set_opt(h.get(), LRO_FASTESTMIRROR, conf->fastestmirror().get_value() ? 1L : 0L); + auto fastest_mirror_cache_dir = conf->basecachedir().get_value(); + if (fastest_mirror_cache_dir.back() != '/') { + fastest_mirror_cache_dir.push_back('/'); + } + fastest_mirror_cache_dir += "fastestmirror.cache"; + handle_set_opt(h.get(), LRO_FASTESTMIRRORCACHE, fastest_mirror_cache_dir.c_str()); + } else { + // use already resolved mirror list + handle_set_opt(h.get(), LRO_URLS, mirrors); + } + } else if (!conf->baseurl().get_value().empty()) { + handle_set_opt(h.get(), LRO_HMFCB, static_cast(mirror_failure_cb)); + size_t len = conf->baseurl().get_value().size(); + const char * urls[len + 1]; + for (size_t idx = 0; idx < len; ++idx) { + urls[idx] = conf->baseurl().get_value()[idx].c_str(); + } + urls[len] = nullptr; + handle_set_opt(h.get(), LRO_URLS, urls); + } else { + throw std::runtime_error(tfm::format(_("Cannot find a valid baseurl for repo: %s"), id)); + } + + // setup username/password if needed + auto userpwd = conf->username().get_value(); + if (!userpwd.empty()) { + // TODO Use URL encoded form, needs support in librepo + userpwd = format_user_pass_string(userpwd, conf->password().get_value(), false); + handle_set_opt(h.get(), LRO_USERPWD, userpwd.c_str()); + } + + // setup ssl stuff + if (!conf->sslcacert().get_value().empty()) { + handle_set_opt(h.get(), LRO_SSLCACERT, conf->sslcacert().get_value().c_str()); + } + if (!conf->sslclientcert().get_value().empty()) { + handle_set_opt(h.get(), LRO_SSLCLIENTCERT, conf->sslclientcert().get_value().c_str()); + } + if (!conf->sslclientkey().get_value().empty()) { + handle_set_opt(h.get(), LRO_SSLCLIENTKEY, conf->sslclientkey().get_value().c_str()); + } + + handle_set_opt(h.get(), LRO_PROGRESSCB, static_cast(progress_cb)); + handle_set_opt(h.get(), LRO_PROGRESSDATA, callbacks.get()); + handle_set_opt(h.get(), LRO_FASTESTMIRRORCB, static_cast(fastest_mirror_cb)); + handle_set_opt(h.get(), LRO_FASTESTMIRRORDATA, callbacks.get()); + +#ifdef LRO_SUPPORTS_CACHEDIR + /* If zchunk is enabled, set librepo cache dir */ + if (conf->getMasterConfig().zchunk().get_value()) { + handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().get_value().c_str()); + } +#endif + + auto minrate = conf->minrate().get_value(); + handle_set_opt(h.get(), LRO_LOWSPEEDLIMIT, static_cast(minrate)); + + auto maxspeed = conf->throttle().get_value(); + if (maxspeed > 0 && maxspeed <= 1) { + maxspeed *= static_cast(conf->bandwidth().get_value()); + } + if (maxspeed != 0 && maxspeed < static_cast(minrate)) { + throw std::runtime_error( + _("Maximum download speed is lower than minimum. " + "Please change configuration of minrate or throttle")); + } + handle_set_opt(h.get(), LRO_MAXSPEED, static_cast(maxspeed)); + + long timeout = conf->timeout().get_value(); + if (timeout > 0) { + handle_set_opt(h.get(), LRO_CONNECTTIMEOUT, timeout); + handle_set_opt(h.get(), LRO_LOWSPEEDTIME, timeout); + } else { + handle_set_opt(h.get(), LRO_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT); + handle_set_opt(h.get(), LRO_LOWSPEEDTIME, LRO_LOWSPEEDTIME_DEFAULT); + } + + if (!conf->proxy().empty() && !conf->proxy().get_value().empty()) { + handle_set_opt(h.get(), LRO_PROXY, conf->proxy().get_value().c_str()); + } + + //set proxy authorization method + auto proxy_auth_method_str = conf->proxy_auth_method().get_value(); + auto proxy_auth_method = LR_AUTH_ANY; + for (auto & auth : PROXYAUTHMETHODS) { + if (proxy_auth_method_str == auth.name) { + proxy_auth_method = auth.code; + break; + } + } + handle_set_opt(h.get(), LRO_PROXYAUTHMETHODS, static_cast(proxy_auth_method)); + + if (!conf->proxy_username().empty()) { + userpwd = conf->proxy_username().get_value(); + if (!userpwd.empty()) { + userpwd = format_user_pass_string(userpwd, conf->proxy_password().get_value(), true); + handle_set_opt(h.get(), LRO_PROXYUSERPWD, userpwd.c_str()); + } + } + + auto sslverify = conf->sslverify().get_value() ? 1L : 0L; + handle_set_opt(h.get(), LRO_SSLVERIFYHOST, sslverify); + handle_set_opt(h.get(), LRO_SSLVERIFYPEER, sslverify); + + return h; +} + +static void gpg_import_key(gpgme_ctx_t context, int key_fd, Logger * logger) { + gpg_error_t gpg_err; + gpgme_data_t key_data; + + gpg_err = gpgme_data_new_from_fd(&key_data, key_fd); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_data_new_from_fd(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + + gpg_err = gpgme_op_import(context, key_data); + gpgme_data_release(key_data); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_op_import(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } +} + +static void gpg_import_key(gpgme_ctx_t context, std::vector key, Logger * logger) { + gpg_error_t gpg_err; + gpgme_data_t key_data; + + gpg_err = gpgme_data_new_from_mem(&key_data, key.data(), key.size(), 0); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_data_new_from_fd(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + + gpg_err = gpgme_op_import(context, key_data); + gpgme_data_release(key_data); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_op_import(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } +} + +static std::vector rawkey2infos(int fd, Logger * logger) { + gpg_error_t gpg_err; + + std::vector key_infos; + gpgme_ctx_t ctx; + gpgme_new(&ctx); + std::unique_ptr::type> context(ctx); + + // set GPG home dir + char tmpdir[] = "/tmp/tmpdir.XXXXXX"; + mkdtemp(tmpdir); + std::unique_ptr> tmp_dir_remover{ + tmpdir, [](char * tmpdir) { std::filesystem::remove_all(tmpdir); }}; + gpg_err = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, nullptr, tmpdir); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + + gpg_import_key(ctx, fd, logger); + + gpgme_key_t key; + gpg_err = gpgme_op_keylist_start(ctx, nullptr, 0); + while (gpg_err == GPG_ERR_NO_ERROR) { + gpg_err = gpgme_op_keylist_next(ctx, &key); + if (gpg_err) { + break; + } + + // _extract_signing_subkey + auto subkey = key->subkeys; + while (subkey && !key->subkeys->can_sign) { + subkey = subkey->next; + } + if (subkey) + key_infos.emplace_back(key, subkey); + gpgme_key_release(key); + } + if (gpg_err_code(gpg_err) != GPG_ERR_EOF) { + gpgme_op_keylist_end(ctx); + auto msg = tfm::format(_("can not list keys: %s"), gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + gpgme_set_armor(ctx, 1); + for (auto & key_info : key_infos) { + gpgme_data_t sink; + gpgme_data_new(&sink); + gpgme_op_export(ctx, key_info.get_id().c_str(), 0, sink); + gpgme_data_rewind(sink); + + char buf[4096]; + ssize_t readed; + do { + readed = gpgme_data_read(sink, buf, sizeof(buf)); + if (readed > 0) + key_info.raw_key.insert(key_info.raw_key.end(), buf, buf + sizeof(buf)); + } while (readed == sizeof(buf)); + } + return key_infos; +} + +static std::vector keyids_from_pubring(const std::string & gpg_dir, Logger * logger) { + gpg_error_t gpg_err; + + std::vector keyids; + + struct stat sb; + if (stat(gpg_dir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { + gpgme_ctx_t ctx; + gpgme_new(&ctx); + std::unique_ptr::type> context(ctx); + + // set GPG home dir + gpg_err = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, nullptr, gpg_dir.c_str()); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + + gpgme_key_t key; + gpg_err = gpgme_op_keylist_start(ctx, nullptr, 0); + while (gpg_err == GPG_ERR_NO_ERROR) { + gpg_err = gpgme_op_keylist_next(ctx, &key); + if (gpg_err) { + break; + } + + // _extract_signing_subkey + auto subkey = key->subkeys; + while (subkey && !key->subkeys->can_sign) { + subkey = subkey->next; + } + if (subkey) + keyids.push_back(subkey->keyid); + gpgme_key_release(key); + } + if (gpg_err_code(gpg_err) != GPG_ERR_EOF) { + gpgme_op_keylist_end(ctx); + auto msg = tfm::format(_("can not list keys: %s"), gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + } + return keyids; +} + +// download key from URL +std::vector Repo::Impl::retrieve(const std::string & url) { + auto logger = base->get_logger(); + char tmp_key_file[] = "/tmp/repokey.XXXXXX"; + auto fd = mkstemp(tmp_key_file); + if (fd == -1) { + auto msg = tfm::format( + "Error creating temporary file \"%s\": %s", tmp_key_file, std::system_category().message(errno)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + unlink(tmp_key_file); + std::unique_ptr> tmp_file_closer{&fd, [](int * fd) { close(*fd); }}; + + try { + download_url(url.c_str(), fd); + } catch (const LrExceptionWithSourceUrl & e) { + auto msg = tfm::format(_("Failed to retrieve GPG key for repo '%s': %s"), id, e.what()); + throw std::runtime_error(msg); + } + lseek(fd, SEEK_SET, 0); + auto key_infos = rawkey2infos(fd, logger); + for (auto & key : key_infos) + key.url = url; + return key_infos; +} + +/* + * Creates the '/run/user/$UID' directory if it doesn't exist. If this + * directory exists, gpgagent will create its sockets under + * '/run/user/$UID/gnupg'. + * + * If this directory doesn't exist, gpgagent will create its sockets in gpg + * home directory, which is under '/var/cache/yum/metadata/' and this was + * causing trouble with container images, see [1]. + * + * Previous solution was to send the agent a "KILLAGENT" message, but that + * would cause a race condition with calling gpgme_release(), see [2], [3], + * [4]. + * + * Since the agent doesn't clean up its sockets properly, by creating this + * directory we make sure they are in a place that is not causing trouble with + * container images. + * + * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1650266 + * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1769831 + * [3] https://github.com/rpm-software-management/microdnf/issues/50 + * [4] https://bugzilla.redhat.com/show_bug.cgi?id=1781601 + */ +static void ensure_socket_dir_exists(Logger * logger) { + std::string dirname = "/run/user/" + std::to_string(getuid()); + int res = mkdir(dirname.c_str(), 0700); + if (res != 0 && errno != EEXIST) { + logger->debug(tfm::format("Failed to create directory \"%s\": %d - %s", dirname, errno, strerror(errno))); + } +} + +void Repo::Impl::import_repo_keys() { + auto logger = base->get_logger(); + + auto gpg_dir = get_cachedir() + "/pubring"; + auto known_keys = keyids_from_pubring(gpg_dir, logger); + ensure_socket_dir_exists(logger); + for (const auto & gpgkeyUrl : conf->gpgkey().get_value()) { + auto key_infos = retrieve(gpgkeyUrl); + for (auto & key_info : key_infos) { + if (std::find(known_keys.begin(), known_keys.end(), key_info.get_id()) != known_keys.end()) { + logger->debug(tfm::format(_("repo %s: 0x%s already imported"), id, key_info.get_id())); + continue; + } + + if (callbacks) { + if (!callbacks->repokey_import( + key_info.get_id(), + key_info.get_user_id(), + key_info.get_fingerprint(), + key_info.url, + key_info.get_timestamp())) + continue; + } + + struct stat sb; + if (stat(gpg_dir.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) + mkdir(gpg_dir.c_str(), 0777); + + gpgme_ctx_t ctx; + gpgme_new(&ctx); + std::unique_ptr::type> context(ctx); + + // set GPG home dir + auto gpg_err = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, nullptr, gpg_dir.c_str()); + if (gpg_err != GPG_ERR_NO_ERROR) { + auto msg = tfm::format(_("%s: gpgme_ctx_set_engine_info(): %s"), __func__, gpgme_strerror(gpg_err)); + logger->debug(msg); + throw LrException(LRE_GPGERROR, msg); + } + + gpg_import_key(ctx, key_info.raw_key, logger); + + logger->debug(tfm::format(_("repo %s: imported key 0x%s."), id, key_info.get_id())); + } + } +} + +std::unique_ptr Repo::Impl::lr_handle_perform( + LrHandle * handle, const std::string & dest_directory, bool set_gpg_home_dir) { + if (set_gpg_home_dir) { + auto pubringdir = get_cachedir() + "/pubring"; + handle_set_opt(handle, LRO_GNUPGHOMEDIR, pubringdir.c_str()); + } + + // Start and end is called only if progress callback is set in handle. + LrProgressCb progressFunc; + handle_get_info(handle, LRI_PROGRESSCB, &progressFunc); + + add_countme_flag(handle); + + std::unique_ptr result; + bool ret; + bool bad_gpg = false; + do { + if (callbacks && progressFunc) + callbacks->start( + !conf->name().get_value().empty() ? conf->name().get_value().c_str() + : (!id.empty() ? id.c_str() : "unknown")); + + GError * err_p{nullptr}; + result.reset(lr_result_init()); + ret = ::lr_handle_perform(handle, result.get(), &err_p); + std::unique_ptr err(err_p); + + if (callbacks && progressFunc) + callbacks->end(); + + if (ret || bad_gpg || err_p->code != LRE_BADGPG) { + if (!ret) { + std::string source; + if (conf->metalink().empty() || (source = conf->metalink().get_value()).empty()) { + if (conf->mirrorlist().empty() || (source = conf->mirrorlist().get_value()).empty()) { + bool first = true; + for (const auto & url : conf->baseurl().get_value()) { + if (first) + first = false; + else + source += ", "; + source += url; + } + } + } + throw LrExceptionWithSourceUrl(err->code, err->message, source); + } + break; + } + bad_gpg = true; + import_repo_keys(); + std::filesystem::remove_all(dest_directory + "/" + METADATA_RELATIVE_DIR); + } while (true); + + return result; +} + +bool Repo::Impl::load_cache(bool throw_except) { + std::unique_ptr h(lr_handle_init_local()); + std::unique_ptr r; + + // Fetch data + try { + r = lr_handle_perform(h.get(), get_cachedir(), conf->repo_gpgcheck().get_value()); + } catch (std::exception & ex) { + if (throw_except) + throw; + return false; + } + + char ** mirrors; + LrYumRepo * yum_repo; + LrYumRepoMd * yum_repomd; + handle_get_info(h.get(), LRI_MIRRORS, &mirrors); + result_get_info(r.get(), LRR_YUM_REPO, &yum_repo); + result_get_info(r.get(), LRR_YUM_REPOMD, &yum_repomd); + + // Populate repo + repomd_fn = yum_repo->repomd; + metadata_paths.clear(); + for (auto * elem = yum_repo->paths; elem; elem = g_slist_next(elem)) { + if (elem->data) { + auto yumrepopath = static_cast(elem->data); + metadata_paths.emplace(yumrepopath->type, yumrepopath->path); + } + } + + content_tags.clear(); + for (auto elem = yum_repomd->content_tags; elem; elem = g_slist_next(elem)) { + if (elem->data) + content_tags.emplace_back(static_cast(elem->data)); + } + + distro_tags.clear(); + for (auto elem = yum_repomd->distro_tags; elem; elem = g_slist_next(elem)) { + if (elem->data) { + auto distro_tag = static_cast(elem->data); + if (distro_tag->tag) + distro_tags.emplace_back(distro_tag->cpeid, distro_tag->tag); + } + } + + metadata_locations.clear(); + for (auto elem = yum_repomd->records; elem; elem = g_slist_next(elem)) { + if (elem->data) { + auto rec = static_cast(elem->data); + metadata_locations.emplace_back(rec->type, rec->location_href); + } + } + + if (auto c_revision = yum_repomd->revision) { + revision = c_revision; + } + max_timestamp = static_cast(lr_yum_repomd_get_highest_timestamp(yum_repomd, nullptr)); + + // Load timestamp unless explicitly expired + if (timestamp != 0) { + timestamp = mtime(get_metadata_path(MD_TYPE_PRIMARY).c_str()); + } + g_strfreev(this->mirrors); + this->mirrors = mirrors; + return true; +} + +void Repo::Impl::add_countme_flag(LrHandle * handle) { + /* + * The countme flag will be added once (and only once) in every position of + * a sliding time window (COUNTME_WINDOW) that starts at COUNTME_OFFSET and + * moves along the time axis, by one length at a time, in such a way that + * the current point in time always stays within: + * + * UNIX epoch now + * | | + * |---*-----|-----|-----|-----[-*---]---> time + * | ~~~~~~~ + * COUNTME_OFFSET COUNTME_WINDOW + * + * This is to align the time window with an absolute point in time rather + * than the last counting event (which could facilitate tracking across + * multiple such events). + */ + auto logger = base->get_logger(); + + // Bail out if not counting or not running as root (since the persistdir is + // only root-writable) + if (!conf->countme().get_value() || getuid() != 0) + return; + + // Bail out if not a remote handle + long local; + handle_get_info(handle, LRI_LOCAL, &local); + if (local) + return; + + // Bail out if no metalink or mirrorlist is defined + auto & metalink = conf->metalink(); + auto & mirrorlist = conf->mirrorlist(); + if ((metalink.empty() || metalink.get_value().empty()) && (mirrorlist.empty() || mirrorlist.get_value().empty())) + return; + + // Load the cookie + std::string fname = get_persistdir() + "/" + COUNTME_COOKIE; + int ver = COUNTME_VERSION; // file format version (for future use) + time_t epoch = 0; // position of first-ever counted window + time_t win = COUNTME_OFFSET; // position of last counted window + int budget = -1; // budget for this window (-1 = generate) + std::ifstream(fname) >> ver >> epoch >> win >> budget; + + // Bail out if the window has not advanced since + time_t now = time(nullptr); + time_t delta = now - win; + if (delta < COUNTME_WINDOW) { + logger->debug(tfm::format("countme: no event for %s: window already counted", id)); + return; + } + + // Evenly distribute the probability of the counting event over the first N + // requests in this window (where N = COUNTME_BUDGET), by defining a random + // "budget" of ordinary requests that we first have to spend. This ensures + // that no particular request is special and thus no privacy loss is + // incurred by adding the flag within N requests. + if (budget < 0) { + std::random_device rd; + std::default_random_engine gen(rd()); + std::uniform_int_distribution dist(1, COUNTME_BUDGET); + budget = dist(gen); + } + budget--; + if (!budget) { + // Budget exhausted, counting! + + // Compute the position of this window + win = now - (delta % COUNTME_WINDOW); + if (!epoch) + epoch = win; + // Window step (0 at epoch) + int64_t step = (win - epoch) / COUNTME_WINDOW; + + // Compute the bucket we are in + unsigned int i; + for (i = 0; i < COUNTME_BUCKETS.size(); ++i) + if (step < COUNTME_BUCKETS[i]) + break; + int bucket = i + 1; // Buckets are indexed from 1 + + // Set the flag + std::string flag = "countme=" + std::to_string(bucket); + handle_set_opt(handle, LRO_ONETIMEFLAG, flag.c_str()); + logger->debug(tfm::format("countme: event triggered for %s: bucket %i", id, bucket)); + + // Request a new budget + budget = -1; + } else { + logger->debug(tfm::format("countme: no event for %s: budget to spend: %i", id, budget)); + } + + // Save the cookie + std::ofstream(fname) << COUNTME_VERSION << " " << epoch << " " << win << " " << budget; +} + +// Use metalink to check whether our metadata are still current. +bool Repo::Impl::is_metalink_in_sync() { + auto logger = base->get_logger(); + char tmpdir[] = "/tmp/tmpdir.XXXXXX"; + mkdtemp(tmpdir); + + std::unique_ptr> tmp_dir_remover{ + tmpdir, [](char * tmpdir) { std::filesystem::remove_all(tmpdir); }}; + + std::unique_ptr h(lr_handle_init_remote(tmpdir)); + + handle_set_opt(h.get(), LRO_FETCHMIRRORS, 1L); + auto r = lr_handle_perform(h.get(), tmpdir, false); + LrMetalink * metalink; + handle_get_info(h.get(), LRI_METALINK, &metalink); + if (!metalink) { + logger->debug(tfm::format(_("reviving: repo '%s' skipped, no metalink."), id)); + return false; + } + + // check all recognized hashes + auto chksum_free = [](Chksum * ptr) { solv_chksum_free(ptr, nullptr); }; + struct hashInfo { + const LrMetalinkHash * lr_metalink_hash; + std::unique_ptr chksum; + }; + std::vector hashes; + for (auto hash = metalink->hashes; hash; hash = hash->next) { + auto lr_metalink_hash = static_cast(hash->data); + for (auto algorithm : RECOGNIZED_CHKSUMS) { + if (strcmp(lr_metalink_hash->type, algorithm) == 0) + hashes.push_back({lr_metalink_hash, {nullptr, chksum_free}}); + } + } + if (hashes.empty()) { + logger->debug(tfm::format(_("reviving: repo '%s' skipped, no usable hash."), id)); + return false; + } + + for (auto & hash : hashes) { + auto chk_type = solv_chksum_str2type(hash.lr_metalink_hash->type); + hash.chksum.reset(solv_chksum_create(chk_type)); + } + + std::ifstream repomd(repomd_fn, std::ifstream::binary); + char buf[4096]; + int readed; + while ((readed = static_cast(repomd.readsome(buf, sizeof(buf)))) > 0) { + for (auto & hash : hashes) + solv_chksum_add(hash.chksum.get(), buf, readed); + } + + for (auto & hash : hashes) { + int chksumLen; + auto chksum = solv_chksum_get(hash.chksum.get(), &chksumLen); + char chksumHex[chksumLen * 2 + 1]; + solv_bin2hex(chksum, chksumLen, chksumHex); + if (strcmp(chksumHex, hash.lr_metalink_hash->value) != 0) { + logger->debug( + tfm::format(_("reviving: failed for '%s', mismatched %s sum."), id, hash.lr_metalink_hash->type)); + return false; + } + } + + logger->debug(tfm::format(_("reviving: '%s' can be revived - metalink checksums match."), id)); + return true; +} + +// Use repomd to check whether our metadata are still current. +bool Repo::Impl::is_repomd_in_sync() { + auto logger = base->get_logger(); + LrYumRepo * yum_repo; + char tmpdir[] = "/tmp/tmpdir.XXXXXX"; + mkdtemp(tmpdir); + + std::unique_ptr> tmp_dir_remover{ + tmpdir, [](char * tmpdir) { std::filesystem::remove_all(tmpdir); }}; + + const char * dlist[] = LR_YUM_REPOMDONLY; + + std::unique_ptr h(lr_handle_init_remote(tmpdir)); + + handle_set_opt(h.get(), LRO_YUMDLIST, dlist); + auto r = lr_handle_perform(h.get(), tmpdir, conf->repo_gpgcheck().get_value()); + result_get_info(r.get(), LRR_YUM_REPO, &yum_repo); + + auto same = have_files_same_content(repomd_fn.c_str(), yum_repo->repomd); + if (same) + logger->debug(tfm::format(_("reviving: '%s' can be revived - repomd matches."), id)); + else + logger->debug(tfm::format(_("reviving: failed for '%s', mismatched repomd."), id)); + return same; +} + +bool Repo::Impl::is_in_sync() { + if (!conf->metalink().empty() && !conf->metalink().get_value().empty()) + return is_metalink_in_sync(); + return is_repomd_in_sync(); +} + + +void Repo::Impl::fetch(const std::string & destdir, std::unique_ptr && h) { + auto repodir = destdir + "/" + METADATA_RELATIVE_DIR; + if (g_mkdir_with_parents(destdir.c_str(), 0755) == -1) { + const char * err_txt = strerror(errno); + throw std::runtime_error( + tfm::format(_("Cannot create repo destination directory \"%s\": %s"), destdir, err_txt)); + } + auto tmpdir = destdir + "/tmpdir.XXXXXX"; + if (!mkdtemp(&tmpdir.front())) { + const char * err_txt = strerror(errno); + throw std::runtime_error( + tfm::format(_("Cannot create repo temporary directory \"%s\": %s"), tmpdir.c_str(), err_txt)); + } + std::unique_ptr> tmp_dir_remover{ + &tmpdir.front(), [](char * tmpdir) { std::filesystem::remove_all(tmpdir); }}; + auto tmprepodir = tmpdir + "/" + METADATA_RELATIVE_DIR; + + handle_set_opt(h.get(), LRO_DESTDIR, tmpdir.c_str()); + auto r = lr_handle_perform(h.get(), tmpdir, conf->repo_gpgcheck().get_value()); + + std::filesystem::remove_all(repodir); + if (g_mkdir_with_parents(repodir.c_str(), 0755) == -1) { + const char * err_txt = strerror(errno); + throw std::runtime_error(tfm::format(_("Cannot create directory \"%s\": %s"), repodir, err_txt)); + } + // move all downloaded object from tmpdir to destdir + if (auto * dir = opendir(tmpdir.c_str())) { + std::unique_ptr> tmp_dir_remover{dir, [](DIR * dir) { closedir(dir); }}; + + while (auto ent = readdir(dir)) { + auto el_name = ent->d_name; + if (el_name[0] == '.' && (el_name[1] == '\0' || (el_name[1] == '.' && el_name[2] == '\0'))) { + continue; + } + auto target_element = destdir + "/" + el_name; + std::filesystem::remove_all(target_element); + auto tempElement = tmpdir + "/" + el_name; + try { + move_recursive(tempElement.c_str(), target_element.c_str()); + } catch (const std::filesystem::filesystem_error & ex) { + std::string err_txt = tfm::format( + _("Cannot rename directory \"%s\" to \"%s\": %s"), tempElement, target_element, ex.what()); + throw std::runtime_error(err_txt); + } + } + } +} + +void Repo::Impl::download_metadata(const std::string & destdir) { + std::unique_ptr h(lr_handle_init_remote(nullptr)); + handle_set_opt(h.get(), LRO_YUMDLIST, LR_RPMMD_FULL); + fetch(destdir, std::move(h)); +} + +bool Repo::Impl::load() { + auto logger = base->get_logger(); + try { + if (!get_metadata_path(MD_TYPE_PRIMARY).empty() || load_cache(false)) { + reset_metadata_expired(); + if (!expired || sync_strategy == SyncStrategy::ONLY_CACHE || sync_strategy == SyncStrategy::LAZY) { + logger->debug(tfm::format(_("repo: using cache for: %s"), id)); + return false; + } + + if (is_in_sync()) { + // the expired metadata still reflect the origin: + utimes(get_metadata_path(MD_TYPE_PRIMARY).c_str(), nullptr); + expired = false; + return true; + } + } + if (sync_strategy == SyncStrategy::ONLY_CACHE) { + auto msg = tfm::format(_("Cache-only enabled but no cache for '%s'"), id); + throw std::runtime_error(msg); + } + + logger->debug(tfm::format(_("repo: downloading from remote: %s"), id)); + const auto cache_dir = get_cachedir(); + fetch(cache_dir, lr_handle_init_remote(nullptr)); + timestamp = -1; + load_cache(true); + } catch (const LrExceptionWithSourceUrl & e) { + auto msg = tfm::format(_("Failed to download metadata for repo '%s': %s"), id, e.what()); + throw std::runtime_error(msg); + } + expired = false; + return true; +} + +std::string Repo::Impl::get_hash() const { + std::string tmp; + if (conf->metalink().empty() || (tmp = conf->metalink().get_value()).empty()) { + if (conf->mirrorlist().empty() || (tmp = conf->mirrorlist().get_value()).empty()) { + if (!conf->baseurl().get_value().empty()) + tmp = conf->baseurl().get_value()[0]; + if (tmp.empty()) + tmp = id; + } + } + + auto chksum_obj = solv_chksum_create(REPOKEY_TYPE_SHA256); + solv_chksum_add(chksum_obj, tmp.c_str(), static_cast(tmp.length())); + int chksum_len; + auto chksum = solv_chksum_get(chksum_obj, &chksum_len); + static constexpr int USE_CHECKSUM_BYTES = 8; + if (chksum_len < USE_CHECKSUM_BYTES) { + solv_chksum_free(chksum_obj, nullptr); + throw std::runtime_error(_("getCachedir(): Computation of SHA256 failed")); + } + char chksum_cstr[USE_CHECKSUM_BYTES * 2 + 1]; + solv_bin2hex(chksum, USE_CHECKSUM_BYTES, chksum_cstr); + solv_chksum_free(chksum_obj, nullptr); + + return id + "-" + chksum_cstr; +} + +std::string Repo::Impl::get_cachedir() const { + auto repodir(conf->basecachedir().get_value()); + if (repodir.back() != '/') + repodir.push_back('/'); + return repodir + get_hash(); +} + +std::string Repo::Impl::get_persistdir() const { + auto persdir(conf->get_master_config().persistdir().get_value()); + if (persdir.back() != '/') + persdir.push_back('/'); + std::string result = persdir + "repos/" + get_hash(); + if (g_mkdir_with_parents(result.c_str(), 0755) == -1) { + const char * err_txt = strerror(errno); + throw std::runtime_error(tfm::format(_("Cannot create persistdir \"%s\": %s"), result, err_txt)); + } + return result; +} + +int64_t Repo::Impl::get_age() const { + return time(nullptr) - mtime(get_metadata_path(MD_TYPE_PRIMARY).c_str()); +} + +void Repo::Impl::expire() { + expired = true; + timestamp = 0; +} + +bool Repo::Impl::is_expired() const { + if (expired) + // explicitly requested expired state + return true; + if (conf->metadata_expire().get_value() == -1) + return false; + return get_age() > conf->metadata_expire().get_value(); +} + +int Repo::Impl::get_expires_in() const { + return conf->metadata_expire().get_value() - static_cast(get_age()); +} + +void Repo::Impl::download_url(const char * url, int fd) { + if (callbacks) + callbacks->start( + !conf->name().get_value().empty() ? conf->name().get_value().c_str() + : (!id.empty() ? id.c_str() : "unknown")); + + GError * err_p{nullptr}; + lr_download_url(get_cached_handle(), url, fd, &err_p); + std::unique_ptr err(err_p); + + if (callbacks) + callbacks->end(); + + if (err) + throw LrExceptionWithSourceUrl(err->code, err->message, url); +} + +void Repo::Impl::set_http_headers(const char * headers[]) { + if (!headers) { + http_headers.reset(); + return; + } + size_t headers_count = 0; + while (headers[headers_count]) + ++headers_count; + http_headers.reset(new char * [headers_count + 1] {}); + for (size_t i = 0; i < headers_count; ++i) { + http_headers[i] = new char[strlen(headers[i]) + 1]; + strcpy(http_headers[i], headers[i]); + } +} + +const char * const * Repo::Impl::get_http_headers() const { + return http_headers.get(); +} + +bool Repo::fresh() { + return p_impl->timestamp >= 0; +} + +void Repo::Impl::reset_metadata_expired() { + if (expired || conf->metadata_expire().get_value() == -1) + return; + if (conf->get_master_config().check_config_file_age().get_value() && !repo_file_path.empty() && + mtime(repo_file_path.c_str()) > mtime(get_metadata_path(MD_TYPE_PRIMARY).c_str())) + expired = true; + else + expired = get_age() > conf->metadata_expire().get_value(); +} + + +/* Returns a librepo handle, set as per the repo options + Note that destdir is None, and the handle is cached.*/ +LrHandle * Repo::Impl::get_cached_handle() { + if (!handle) + handle = lr_handle_init_remote(nullptr); + handle_set_opt(handle.get(), LRO_HTTPHEADER, http_headers.get()); + return handle.get(); +} + +void Repo::Impl::attach_libsolv_repo(LibsolvRepo * libsolv_repo) { + std::lock_guard guard(attach_libsolv_mutex); + + if (this->libsolv_repo) + // A libsolvRepo was attached to this object before. Remove it's reference to this object. + this->libsolv_repo->appdata = nullptr; + else + // The libsolvRepo will reference this object. Increase reference counter. + ++nrefs; + + libsolv_repo->appdata = owner; // The libsolvRepo references back to us. + libsolv_repo->subpriority = -owner->get_cost(); + libsolv_repo->priority = -owner->get_priority(); + this->libsolv_repo = libsolv_repo; +} + +void Repo::Impl::detach_libsolv_repo() { + attach_libsolv_mutex.lock(); + if (!libsolv_repo) { + // Nothing to do, libsolvRepo is not attached. + attach_libsolv_mutex.unlock(); + return; + } + + libsolv_repo->appdata = nullptr; // Removes reference to this object from libsolvRepo. + this->libsolv_repo = nullptr; + + if (--nrefs <= 0) { + // There is no reference to this object, we are going to destroy it. + // Mutex is part of this object, we must unlock it before destroying. + attach_libsolv_mutex.unlock(); + delete owner; + } else + attach_libsolv_mutex.unlock(); +} + +void Repo::set_max_mirror_tries(int max_mirror_tries) { + p_impl->max_mirror_tries = max_mirror_tries; +} + +int64_t Repo::get_timestamp() const { + return p_impl->timestamp; +} + +int Repo::get_max_timestamp() { + return p_impl->max_timestamp; +} + +void Repo::set_preserve_remote_time(bool preserve_remote_time) { + p_impl->preserve_remote_time = preserve_remote_time; +} + +bool Repo::get_preserve_remote_time() const { + return p_impl->preserve_remote_time; +} + +const std::vector & Repo::get_content_tags() { + return p_impl->content_tags; +} + +const std::vector> & Repo::get_distro_tags() { + return p_impl->distro_tags; +} + +const std::vector> Repo::get_metadata_locations() const { + return p_impl->metadata_locations; +} + +const std::string & Repo::get_revision() const { + return p_impl->revision; +} + +std::string Repo::get_cachedir() const { + return p_impl->get_cachedir(); +} + +void Repo::set_repo_file_path(const std::string & path) { + p_impl->repo_file_path = path; +} + +const std::string & Repo::get_repo_file_path() const noexcept { + return p_impl->repo_file_path; +} + +void Repo::set_sync_strategy(SyncStrategy strategy) { + p_impl->sync_strategy = strategy; +} + +Repo::SyncStrategy Repo::get_sync_strategy() const noexcept { + return p_impl->sync_strategy; +} + +void Repo::download_url(const char * url, int fd) { + p_impl->download_url(url, fd); +} + +void Repo::set_http_headers(const char * headers[]) { + p_impl->set_http_headers(headers); +} + +const char * const * Repo::get_http_headers() const { + return p_impl->get_http_headers(); +} + +std::vector Repo::get_mirrors() const { + std::vector mirrors; + if (p_impl->mirrors) { + for (auto mirror = p_impl->mirrors; *mirror; ++mirror) + mirrors.emplace_back(*mirror); + } + return mirrors; +} + +int PackageTargetCB::end(TransferStatus /*status*/, const char * /*msg*/) { + return 0; +} +int PackageTargetCB::progress(double /*totalToDownload*/, double /*downloaded*/) { + return 0; +} +int PackageTargetCB::mirror_failure(const char * /*msg*/, const char * /*url*/) { + return 0; +} + +class PackageTarget::Impl { +public: + Impl( + Repo * repo, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks); + + Impl( + ConfigMain * cfg, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks, + const char * http_headers[]); + + void download(); + + ~Impl(); + + PackageTargetCB * callbacks; + + std::unique_ptr lr_pkg_target; + +private: + void init( + LrHandle * handle, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end); + + static int end_cb(void * data, LrTransferStatus status, const char * msg); + static int progress_cb(void * data, double total_to_download, double downloaded); + static int mirror_failure_cb(void * data, const char * msg, const char * url); + + std::unique_ptr lrHandle; +}; + + +int PackageTarget::Impl::end_cb(void * data, LrTransferStatus status, const char * msg) { + if (!data) + return 0; + auto cb_object = static_cast(data); + return cb_object->end(static_cast(status), msg); +} + +int PackageTarget::Impl::progress_cb(void * data, double total_to_download, double downloaded) { + if (!data) + return 0; + auto cb_object = static_cast(data); + return cb_object->progress(total_to_download, downloaded); +} + +int PackageTarget::Impl::mirror_failure_cb(void * data, const char * msg, const char * url) { + if (!data) + return 0; + auto cb_object = static_cast(data); + return cb_object->mirror_failure(msg, url); +} + + +static LrHandle * new_handle(ConfigMain * conf) { + LrHandle * h = ::lr_handle_init(); + const char * user_agent = USER_AGENT; + // see dnf.repo.Repo._handle_new_remote() how to pass + if (conf) { + user_agent = conf->user_agent().get_value().c_str(); + auto minrate = conf->minrate().get_value(); + handle_set_opt(h, LRO_LOWSPEEDLIMIT, static_cast(minrate)); + + auto maxspeed = conf->throttle().get_value(); + if (maxspeed > 0 && maxspeed <= 1) { + maxspeed *= static_cast(conf->bandwidth().get_value()); + } + if (maxspeed != 0 && maxspeed < static_cast(minrate)) + throw std::runtime_error( + _("Maximum download speed is lower than minimum. " + "Please change configuration of minrate or throttle")); + handle_set_opt(h, LRO_MAXSPEED, static_cast(maxspeed)); + + if (!conf->proxy().empty() && !conf->proxy().get_value().empty()) + handle_set_opt(h, LRO_PROXY, conf->proxy().get_value().c_str()); + + //set proxy authorization method + auto proxy_auth_method_str = conf->proxy_auth_method().get_value(); + auto proxy_auth_method = LR_AUTH_ANY; + for (auto & auth : PROXYAUTHMETHODS) { + if (proxy_auth_method_str == auth.name) { + proxy_auth_method = auth.code; + break; + } + } + handle_set_opt(h, LRO_PROXYAUTHMETHODS, static_cast(proxy_auth_method)); + + if (!conf->proxy_username().empty()) { + auto userpwd = conf->proxy_username().get_value(); + if (!userpwd.empty()) { + userpwd = format_user_pass_string(userpwd, conf->proxy_password().get_value(), true); + handle_set_opt(h, LRO_PROXYUSERPWD, userpwd.c_str()); + } + } + + auto sslverify = conf->sslverify().get_value() ? 1L : 0L; + handle_set_opt(h, LRO_SSLVERIFYHOST, sslverify); + handle_set_opt(h, LRO_SSLVERIFYPEER, sslverify); + } + handle_set_opt(h, LRO_USERAGENT, user_agent); + return h; +} + +PackageTarget::ChecksumType PackageTarget::checksum_type(const std::string & name) { + return static_cast(lr_checksum_type(name.c_str())); +} + +void PackageTarget::download_packages(std::vector & targets, bool fail_fast) { + // Convert vector to GSList + GSList * list{nullptr}; + for (auto it = targets.rbegin(); it != targets.rend(); ++it) + list = g_slist_prepend(list, (*it)->p_impl->lr_pkg_target.get()); + std::unique_ptr list_guard(list, &g_slist_free); + + LrPackageDownloadFlag flags = static_cast(0); + if (fail_fast) + flags = static_cast(flags | LR_PACKAGEDOWNLOAD_FAILFAST); + + GError * err_p{nullptr}; + lr_download_packages(list, flags, &err_p); + std::unique_ptr err(err_p); + + if (err) + throw_exception(std::move(err)); +} + + +PackageTarget::Impl::~Impl() = default; + +PackageTarget::Impl::Impl( + Repo * repo, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks) + : callbacks(callbacks) { + init( + repo->p_impl->get_cached_handle(), + relative_url, + dest, + chks_type, + chksum, + expected_size, + base_url, + resume, + byte_range_start, + byte_range_end); +} + +PackageTarget::Impl::Impl( + ConfigMain * cfg, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks, + const char * http_headers[]) + : callbacks(callbacks) { + lrHandle.reset(new_handle(cfg)); + handle_set_opt(lrHandle.get(), LRO_HTTPHEADER, http_headers); + handle_set_opt(lrHandle.get(), LRO_REPOTYPE, LR_YUMREPO); + init( + lrHandle.get(), + relative_url, + dest, + chks_type, + chksum, + expected_size, + base_url, + resume, + byte_range_start, + byte_range_end); +} + +void PackageTarget::Impl::init( + LrHandle * handle, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end) { + auto lr_chks_type = static_cast(chks_type); + + if (resume && byte_range_start) { + auto msg = _("resume cannot be used simultaneously with the byterangestart param"); + throw std::runtime_error(msg); + } + + GError * err_p{nullptr}; + lr_pkg_target.reset(lr_packagetarget_new_v3( + handle, + relative_url, + dest, + lr_chks_type, + chksum, + expected_size, + base_url, + resume, + progress_cb, + callbacks, + end_cb, + mirror_failure_cb, + byte_range_start, + byte_range_end, + &err_p)); + std::unique_ptr err(err_p); + + if (!lr_pkg_target) { + auto msg = tfm::format(_("PackageTarget initialization failed: %s"), err->message); + throw std::runtime_error(msg); + } +} + +PackageTarget::PackageTarget( + Repo * repo, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks) + : p_impl(new Impl( + repo, + relative_url, + dest, + chks_type, + chksum, + expected_size, + base_url, + resume, + byte_range_start, + byte_range_end, + callbacks)) {} + +PackageTarget::PackageTarget( + ConfigMain * cfg, + const char * relative_url, + const char * dest, + int chks_type, + const char * chksum, + int64_t expected_size, + const char * base_url, + bool resume, + int64_t byte_range_start, + int64_t byte_range_end, + PackageTargetCB * callbacks, + const char * http_headers[]) + : p_impl(new Impl( + cfg, + relative_url, + dest, + chks_type, + chksum, + expected_size, + base_url, + resume, + byte_range_start, + byte_range_end, + callbacks, + http_headers)) {} + + +PackageTarget::~PackageTarget() {} + +PackageTargetCB * PackageTarget::get_callbacks() { + return p_impl->callbacks; +} + +const char * PackageTarget::get_err() { + return p_impl->lr_pkg_target->err; +} + +void Downloader::download_url(ConfigMain * cfg, const char * url, int fd) { + std::unique_ptr lr_handle(new_handle(cfg)); + GError * err_p{nullptr}; + lr_download_url(lr_handle.get(), url, fd, &err_p); + std::unique_ptr err(err_p); + + if (err) + throw_exception(std::move(err)); +} + +/* +void repo_internalize_trigger(LibsolvRepo * repo) { + if (!repo) + return; + + if (auto hrepo = static_cast(repo->appdata)) { + // HyRepo is attached. The hint needs_internalizing will be used. + auto repoImpl = libdnf::repo_get_impl(hrepo); + assert(repoImpl->libsolv_repo == repo); + if (!repoImpl->needs_internalizing) + return; + repoImpl->needs_internalizing = false; + } + + repo_internalize(repo); +} + +void repo_internalize_all_trigger(Pool * pool) { + int i; + LibsolvRepo * repo; + + FOR_REPOS(i, repo) + repo_internalize_trigger(repo); +} +*/ + +// ============ librepo logging =========== + +#define LR_LOGDOMAIN "librepo" + +class LrHandleLogData { +public: + std::string file_path; + long uid; + FILE * fd; + bool used{false}; + guint handler_id; + + ~LrHandleLogData(); +}; + +LrHandleLogData::~LrHandleLogData() { + if (used) + g_log_remove_handler(LR_LOGDOMAIN, handler_id); + fclose(fd); +} + +static std::list> lr_log_datas; +static std::mutex lr_log_datas_mutex; + +static const char * lr_log_level_flag_to_cstr(GLogLevelFlags log_level_flag) { + if (log_level_flag & G_LOG_LEVEL_ERROR) + return "ERROR"; + if (log_level_flag & G_LOG_LEVEL_CRITICAL) + return "CRITICAL"; + if (log_level_flag & G_LOG_LEVEL_WARNING) + return "WARNING"; + if (log_level_flag & G_LOG_LEVEL_MESSAGE) + return "MESSAGE"; + if (log_level_flag & G_LOG_LEVEL_INFO) + return "INFO"; + if (log_level_flag & G_LOG_LEVEL_DEBUG) + return "DEBUG"; + return "USER"; +} + +static void librepo_log_cb( + G_GNUC_UNUSED const gchar * log_domain, GLogLevelFlags log_level, const char * msg, gpointer user_data) noexcept { + // Ignore exception during logging. Eg. exception generated during logging of exception is not good. + try { + auto data = static_cast(user_data); + auto now = time(nullptr); + struct tm now_tm; + gmtime_r(&now, &now_tm); + + std::ostringstream ss; + ss << std::put_time(&now_tm, "%FT%TZ "); // "YYYY-MM-DDTHH:MM:SSZ " + ss << lr_log_level_flag_to_cstr(log_level) << " " << msg << std::endl; + auto str = ss.str(); + fwrite(str.c_str(), sizeof(char), str.length(), data->fd); + fflush(data->fd); + } catch (const std::exception &) { + } +} + +long LibrepoLog::add_handler(const std::string & file_path, bool debug) { + static long uid = 0; + + // Open the file + FILE * fd = fopen(file_path.c_str(), "ae"); + if (!fd) { + throw std::runtime_error(tfm::format(_("Cannot open %s: %s"), file_path, g_strerror(errno))); + } + + // Setup user data + std::unique_ptr data(new LrHandleLogData); + data->file_path = file_path; + data->fd = fd; + + // Set handler + GLogLevelFlags log_mask = debug ? G_LOG_LEVEL_MASK + : static_cast( + G_LOG_LEVEL_INFO | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING | + G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR); + + data->handler_id = g_log_set_handler(LR_LOGDOMAIN, log_mask, librepo_log_cb, data.get()); + data->used = true; + + // Save user data (in a thread safe way) + { + std::lock_guard guard(lr_log_datas_mutex); + + // Get unique ID of the handler + data->uid = ++uid; + + // Append the data to the global list + lr_log_datas.push_front(std::move(data)); + } + + // Log librepo version and current time (including timezone) + lr_log_librepo_summary(); + + // Return unique id of the handler data + return uid; +} + +void LibrepoLog::remove_handler(long uid) { + std::lock_guard guard(lr_log_datas_mutex); + + // Search for the corresponding LogFileData + auto it = lr_log_datas.begin(); + while (it != lr_log_datas.end() && (*it)->uid != uid) { + ++it; + } + if (it == lr_log_datas.end()) { + throw std::runtime_error(tfm::format(_("Log handler with id %ld doesn't exist"), uid)); + } + + // Remove the handler and free the data + lr_log_datas.erase(it); +} + +void LibrepoLog::remove_all_handlers() { + std::lock_guard guard(lr_log_datas_mutex); + lr_log_datas.clear(); +} + +} // namespace libdnf::rpm + diff --git a/libdnf/rpm/repo/repo_sack.cpp b/libdnf/rpm/repo/repo_sack.cpp new file mode 100644 index 0000000000..da74cb85d0 --- /dev/null +++ b/libdnf/rpm/repo/repo_sack.cpp @@ -0,0 +1,124 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/rpm/repo/repo_sack.hpp" + +#include "libdnf/base/base.hpp" +#include "libdnf/conf/config_parser.hpp" + +#include + +namespace libdnf::rpm { + +RepoWeakPtr RepoSack::new_repo(const std::string & id) { + // TODO(jrohel): Test repo exists + auto repo_config = std::make_unique(*base->get_config()); + auto repo = std::make_unique(id, std::move(repo_config), *base); + return add_item_with_return(std::move(repo)); +} + +static void load_config_from_parser( + Config & conf, const ConfigParser & parser, const std::string & section, Base & base) { + auto logger = base.get_logger(); + const auto & cfg_parser_data = parser.get_data(); + auto cfg_parser_data_iter = cfg_parser_data.find(section); + if (cfg_parser_data_iter != cfg_parser_data.end()) { + auto opt_binds = conf.opt_binds(); + const auto & cfg_parser_main_sect = cfg_parser_data_iter->second; + for (const auto & opt : cfg_parser_main_sect) { + auto opt_binds_iter = opt_binds.find(opt.first); + if (opt_binds_iter != opt_binds.end()) { + try { + auto value = opt.second; + parser.substitute(value, *base.get_substitutions()); + opt_binds_iter->second.new_string(libdnf::Option::Priority::MAINCONFIG, value); + } catch (const Option::Exception & ex) { + auto msg = fmt::format( + R"**(Config error in section "{}" key "{}": {}: {})**", + section, + opt.first, + ex.get_description(), + ex.what()); + logger->warning(msg); + } + } + } + } +} + +void RepoSack::new_repos_from_file(const std::filesystem::path & path) { + auto logger = base->get_logger(); + ConfigParser parser; + parser.read(path.string()); + const auto & cfg_parser_data = parser.get_data(); + for (const auto & cfg_parser_data_iter : cfg_parser_data) { + const auto repo_id = cfg_parser_data_iter.first; + if (repo_id != "main") { + + auto bad_char_idx = libdnf::rpm::Repo::verify_id(repo_id); + if (bad_char_idx >= 0) { + logger->warning(fmt::format("Bad id for repo: {}, byte = {} {}", repo_id, repo_id[bad_char_idx], bad_char_idx)); + continue; + } + + auto repo = new_repo(repo_id); + auto repo_cfg = repo->get_config(); + logger->debug(fmt::format(R"**(Start of loading section "{}" from file "{}")**", repo_id, path.string())); + load_config_from_parser(*repo_cfg, parser, repo_id, *base); + logger->debug(fmt::format(R"**(Loading of section "{}" from file "{}" done)**", repo_id, path.string())); + + if (repo_cfg->name().get_priority() == libdnf::Option::Priority::DEFAULT) { + logger->warning(fmt::format("Repository \"{}\" is missing name in configuration file \"{}\", using id.", repo_id, path.string())); + repo_cfg->name().set(libdnf::Option::Priority::REPOCONFIG, repo_id); + } + } + } +} + +void RepoSack::new_repos_from_file() { + new_repos_from_file(std::filesystem::path(base->get_config()->config_file_path().get_value())); +} + +void RepoSack::new_repos_from_dir(const std::filesystem::path & path) { + std::filesystem::directory_iterator di(path); + std::vector paths; + for (auto & dentry : di) { + auto & path = dentry.path(); + if (dentry.is_regular_file() && path.extension() == ".repo") { + paths.push_back(path); + } + } + std::sort(paths.begin(), paths.end()); + for (auto & path : paths) { + new_repos_from_file(path); + } +} + +void RepoSack::new_repos_from_dirs() { + auto logger = base->get_logger(); + for (auto & dir : base->get_config()->reposdir().get_value()) { + try { + new_repos_from_dir(std::filesystem::path(dir)); + } catch (const std::filesystem::filesystem_error & ex) { + logger->info(ex.what()); + } + } +} + +} // namespace libdnf::rpm diff --git a/dnf-4/libdnf/utils/bgettext/CMakeLists.txt b/libdnf/utils/bgettext/CMakeLists.txt similarity index 100% rename from dnf-4/libdnf/utils/bgettext/CMakeLists.txt rename to libdnf/utils/bgettext/CMakeLists.txt diff --git a/dnf-4/libdnf/utils/bgettext/README.md b/libdnf/utils/bgettext/README.md similarity index 100% rename from dnf-4/libdnf/utils/bgettext/README.md rename to libdnf/utils/bgettext/README.md diff --git a/dnf-4/libdnf/utils/bgettext/bgettext-common.h b/libdnf/utils/bgettext/bgettext-common.h similarity index 100% rename from dnf-4/libdnf/utils/bgettext/bgettext-common.h rename to libdnf/utils/bgettext/bgettext-common.h diff --git a/dnf-4/libdnf/utils/bgettext/bgettext-lib.h b/libdnf/utils/bgettext/bgettext-lib.h similarity index 100% rename from dnf-4/libdnf/utils/bgettext/bgettext-lib.h rename to libdnf/utils/bgettext/bgettext-lib.h diff --git a/dnf-4/libdnf/utils/bgettext/bgettext.c b/libdnf/utils/bgettext/bgettext.c similarity index 100% rename from dnf-4/libdnf/utils/bgettext/bgettext.c rename to libdnf/utils/bgettext/bgettext.c diff --git a/dnf-4/libdnf/utils/bgettext/bgettext.h b/libdnf/utils/bgettext/bgettext.h similarity index 100% rename from dnf-4/libdnf/utils/bgettext/bgettext.h rename to libdnf/utils/bgettext/bgettext.h diff --git a/libdnf/utils/iniparser.cpp b/libdnf/utils/iniparser.cpp new file mode 100644 index 0000000000..52ae756eba --- /dev/null +++ b/libdnf/utils/iniparser.cpp @@ -0,0 +1,158 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/utils/iniparser.hpp" + +namespace libdnf { + +constexpr char DELIMITER = '\n'; + +IniParser::IniParser(const std::string & file_path) : is(new std::ifstream(file_path)) { + if (!(*is)) { + throw CantOpenFile(file_path); + } + is->exceptions(std::ifstream::badbit); + line_number = 0; +} + +IniParser::IniParser(std::unique_ptr && input_stream) : is(std::move(input_stream)) { + if (!(*is)) { + throw CantOpenFile("bad input stream"); + } + is->exceptions(std::ifstream::badbit); + line_number = 0; +} + +void IniParser::trim_value() noexcept { + auto end = value.find_last_not_of(DELIMITER); + if (end != std::string::npos) { + value.resize(end + 1); + } + if (value.length() > 1 && value.front() == value.back() && (value.front() == '\"' || value.front() == '\'')) { + value.erase(--value.end()); + value.erase(value.begin()); + } +} + +IniParser::ItemType IniParser::next() { + bool previous_line_with_key_val = false; + raw_item.clear(); + while (!line.empty() || !is->eof()) { + if (line.empty()) { + std::getline(*is, line, DELIMITER); + ++line_number; + } + + // remove UTF-8 BOM (Byte order mark) + constexpr const char * utf8_bom = "\xEF\xBB\xBF"; + if (line_number == 1 && line.compare(0, 3, utf8_bom) == 0) { + line.erase(0, 3); + } + + if (line.length() == 0 || line[0] == '#' || line[0] == ';') { // do not support [rR][eE][mM] comment + if (previous_line_with_key_val) { + trim_value(); + return ItemType::KEY_VAL; + } + if (line.length() == 0) { + return ItemType::EMPTY_LINE; + } + raw_item = line + DELIMITER; + line.clear(); + return ItemType::COMMENT_LINE; + } + auto start = line.find_first_not_of(" \t\r"); + if (start == std::string::npos) { + if (previous_line_with_key_val) { + value += DELIMITER; + raw_item += line + DELIMITER; + line.clear(); + continue; + } + raw_item = line + DELIMITER; + line.clear(); + return ItemType::EMPTY_LINE; + } + auto end = line.find_last_not_of(" \t\r"); + + if (previous_line_with_key_val && (start == 0 || line[start] == '[')) { + trim_value(); + return ItemType::KEY_VAL; + } + + if (line[start] == '[') { + auto end_sect_pos = line.find(']', ++start); + if (end_sect_pos == std::string::npos) { + throw MissingBracket(std::to_string(line_number)); + } + if (end_sect_pos == start) { + throw EmptySectionName(std::to_string(line_number)); + } + for (auto idx = end_sect_pos + 1; idx < end; ++idx) { + auto ch = line[idx]; + if (ch == '#' || ch == ';') { + break; + } + if (ch != ' ' && ch != '\t' && ch != '\r') { + throw TextAfterSection(std::to_string(line_number)); + } + } + this->section = line.substr(start, end_sect_pos - start); + raw_item = line + DELIMITER; + line.clear(); + return ItemType::SECTION; + } + + if (section.empty()) { + throw MissingSectionHeader(std::to_string(line_number)); + } + + if (start > 0) { + if (!previous_line_with_key_val) { + throw IllegalContinuationLine(std::to_string(line_number)); + } + value += DELIMITER + line.substr(start, end - start + 1); + raw_item += line + DELIMITER; + line.clear(); + } else { + if (line[start] == '=') { + throw MissingKey(std::to_string(line_number)); + } + auto eql_pos = line.find_first_of('='); + if (eql_pos == std::string::npos) { + throw MissingEqual(std::to_string(line_number)); + } + auto endkeypos = line.find_last_not_of(" \t", eql_pos - 1); + auto valuepos = line.find_first_not_of(" \t", eql_pos + 1); + key = line.substr(start, endkeypos - start + 1); + if (valuepos != std::string::npos) { + value = line.substr(valuepos, end - valuepos + 1); + } else { + value.clear(); + } + previous_line_with_key_val = true; + raw_item = line + DELIMITER; + line.clear(); + } + } + trim_value(); + return previous_line_with_key_val ? ItemType::KEY_VAL : ItemType::END_OF_INPUT; +} + +} // namespace libdnf diff --git a/dnf-4/libdnf/utils/tinyformat/README.md b/libdnf/utils/tinyformat/README.md similarity index 100% rename from dnf-4/libdnf/utils/tinyformat/README.md rename to libdnf/utils/tinyformat/README.md diff --git a/dnf-4/libdnf/utils/tinyformat/tinyformat.hpp b/libdnf/utils/tinyformat/tinyformat.hpp similarity index 99% rename from dnf-4/libdnf/utils/tinyformat/tinyformat.hpp rename to libdnf/utils/tinyformat/tinyformat.hpp index af14329e0d..6efe2299c5 100644 --- a/dnf-4/libdnf/utils/tinyformat/tinyformat.hpp +++ b/libdnf/utils/tinyformat/tinyformat.hpp @@ -955,7 +955,7 @@ typedef const FormatList& FormatListRef; namespace detail { // Format list subclass with fixed storage to avoid dynamic allocation -template +template class FormatListN : public FormatList { public: diff --git a/libdnf/utils/utils.cpp b/libdnf/utils/utils.cpp new file mode 100644 index 0000000000..8a5db42969 --- /dev/null +++ b/libdnf/utils/utils.cpp @@ -0,0 +1,74 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/utils/utils.hpp" + +#include +#include + +#include + +namespace libdnf { + +bool have_files_same_content(const char * file_path1, const char * file_path2) { + static constexpr int block_size = 4096; + bool ret = false; + int fd1 = -1; + int fd2 = -1; + do { + if ((fd1 = open(file_path1, O_CLOEXEC)) == -1) { + break; + } + if ((fd2 = open(file_path2, O_CLOEXEC)) == -1) { + break; + } + auto len1 = lseek(fd1, 0, SEEK_END); + auto len2 = lseek(fd2, 0, SEEK_END); + if (len1 != len2) { + break; + } + ret = true; + if (len1 == 0) { + break; + } + lseek(fd1, 0, SEEK_SET); + lseek(fd2, 0, SEEK_SET); + char buf1[block_size]; + char buf2[block_size]; + ssize_t readed; + do { + readed = read(fd1, &buf1, block_size); + auto readed2 = read(fd2, &buf2, block_size); + if (readed2 != readed || std::memcmp(&buf1, &buf2, static_cast(readed)) != 0) { + ret = false; + break; + } + } while (readed == block_size); + } while (false); + + if (fd1 != -1) { + close(fd1); + } + if (fd2 != -1) { + close(fd2); + } + return ret; +} + +} // namespace libdnf diff --git a/microdnf/CMakeLists.txt b/microdnf/CMakeLists.txt index 132dfb53bf..0c80c5c997 100644 --- a/microdnf/CMakeLists.txt +++ b/microdnf/CMakeLists.txt @@ -4,6 +4,6 @@ endif() file(GLOB_RECURSE MICRODNF_SOURCES *.cpp) -add_executable(microdnf main.cpp ${DNFDAEMON_CLIENT_SOURCES}) +add_executable(microdnf main.cpp utils.cpp ${DNFDAEMON_CLIENT_SOURCES}) target_link_libraries(microdnf PUBLIC libdnf libdnf-cli) install(TARGETS microdnf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/microdnf/main.cpp b/microdnf/main.cpp index 16baf9e469..6041729907 100644 --- a/microdnf/main.cpp +++ b/microdnf/main.cpp @@ -17,7 +17,177 @@ You should have received a copy of the GNU General Public License along with microdnf. If not, see . */ +#include "utils.hpp" + +#include +#include +#include + +#include +#include +#include + +static bool userconfirm(libdnf::ConfigMain & config) { + std::string msg; + if (config.defaultyes().get_value()) { + msg = "Is this ok [Y/n]: "; + } else { + msg = "Is this ok [y/N]: "; + } + while (true) { + std::cout << msg; + + std::string choice; + std::getline(std::cin, choice); + + if (choice.empty()) { + return config.defaultyes().get_value(); + } + if (choice == "y" || choice == "Y") { + return true; + } + if (choice == "n" || choice == "N") { + return false; + } + } +} + +class DNFPRepoCB : public libdnf::rpm::RepoCB { +public: + explicit DNFPRepoCB(libdnf::ConfigMain & config) + : config(&config) {} + + void start(const char *what) override + { + std::cout << "Start downloading: \"" << what << "\"" << std::endl; + } + + void end() override + { + std::cout << "End downloading" << std::endl; + } + + int progress(double /*totalToDownload*/, double /*downloaded*/) override + { + //std::cout << "Downloaded " << downloaded << "/" << totalToDownload << std::endl; + return 0; + } + + bool repokey_import( + const std::string & id, + const std::string & user_id, + const std::string & fingerprint, + const std::string & url, + long int /*timestamp*/) override { + + auto tmp_id = id.size() > 8 ? id.substr(id.size() - 8) : id; + std::cout << "Importing GPG key 0x" << id << ":\n"; + std::cout << " Userid : \"" << user_id << "\"\n"; + std::cout << " Fingerprint: " << fingerprint << "\n"; + std::cout << " From : " << url << std::endl; + + if (config->assumeyes().get_value()) { + return true; + } + if (config->assumeno().get_value()) { + return false; + } + return userconfirm(*config); + } + +private: + libdnf::ConfigMain * config; +}; int main() { + libdnf::Base base; + + auto logger = base.get_logger(); + + // Add circular memory buffer logger + const std::size_t max_log_items_to_keep = 10000; + const std::size_t prealloc_log_items = 256; + logger->add_logger(std::make_unique(max_log_items_to_keep, prealloc_log_items)); + + logger->info("Microdnf start"); + + // load configuration from file and from directory with drop-in files + base.load_config_from_file(); + base.load_config_from_dir(); + + // determine cache and log paths + auto config = base.get_config(); + if (microdnf::am_i_root()) { + config->cachedir().set(libdnf::Option::Priority::DEFAULT, "/var/cache/dnf"); + config->logdir().set(libdnf::Option::Priority::DEFAULT, "/var/log"); + } else { + auto cachedir = microdnf::get_cache_dir(); + config->cachedir().set(libdnf::Option::Priority::DEFAULT, cachedir); + config->logdir().set(libdnf::Option::Priority::DEFAULT, cachedir); + } + + // swap to destination logger and write messages from memory buffer logger to it + auto log_stream = std::make_unique(config->logdir().get_value() + "/microdnf5.log", std::ios::app); + std::unique_ptr stream_logger = std::make_unique(std::move(log_stream)); + logger->swap_logger(stream_logger, 0); + dynamic_cast(*stream_logger).write_to_logger(logger); + + // detect values of basic variables (arch, basearch, and releasever) for substitutions + auto arch = microdnf::detect_arch(); + auto & substitutions = *base.get_substitutions(); + substitutions["arch"] = arch; + substitutions["basearch"] = microdnf::get_base_arch(arch.c_str()); + substitutions["releasever"] = microdnf::detect_release(config->installroot().get_value()); + + // load additional variables from environment and directories + libdnf::ConfigMain::add_vars_from_env(substitutions); + for (auto & dir : config->varsdir().get_value()) { + libdnf::ConfigMain::add_vars_from_dir(substitutions, dir); + } + + // create rpm repositories according configuration files + auto rpm_repo_sack = base.get_rpm_repo_sack(); + rpm_repo_sack->new_repos_from_file(); + rpm_repo_sack->new_repos_from_dirs(); + + auto query = rpm_repo_sack->new_query(); + for (auto & repo : query.ifilter_enabled(true).get_data()) { + repo->set_substitutions(substitutions); + repo->set_callbacks(std::make_unique(*config)); + try { + repo->load(); + } catch (const std::runtime_error & ex) { + logger->warning(ex.what()); + std::cout << ex.what() << std::endl; + } + } + + //TODO(jrohel): only test/demo code will gone out + using SackQueryCmp = libdnf::sack::QueryCmp; + + // version 1 + { + using RpmRepoFilter = libdnf::rpm::RepoQuery::F; + query.ifilter(RpmRepoFilter::enabled, SackQueryCmp::EQ, true); + query.ifilter(RpmRepoFilter::id, SackQueryCmp::REGEX, "fedora.*"); + std::cout << "Found repos:" << std::endl; + auto repos = query.get_data(); + for (auto & repo : repos) { + std::cout << repo->get_id() << std::endl; + } + } + + //version 2 + { + query.ifilter_enabled(true).ifilter_id(SackQueryCmp::REGEX, "fedora.*"); + std::cout << "\nFound repos:" << std::endl; + auto repos = query.get_data(); + for (auto & repo : repos) { + std::cout << repo->get_id() << std::endl; + } + } + + logger->info("Microdnf end"); + return 0; } diff --git a/microdnf/utils.cpp b/microdnf/utils.cpp new file mode 100644 index 0000000000..31fafcf59d --- /dev/null +++ b/microdnf/utils.cpp @@ -0,0 +1,247 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of microdnf: https://github.com/rpm-software-management/libdnf/ + +Microdnf 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 of the License, or +(at your option) any later version. + +Microdnf 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 microdnf. If not, see . +*/ + +// Jaroslav Rohel +// It is code from my C++ dnf development version. It will be replaced. + +#include "utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace microdnf { + +bool am_i_root() { + return geteuid() == 0; +} + +bool is_directory(const char * path) { + struct stat statbuf; + if (stat(path, &statbuf) != 0) { + return false; + } + return S_ISDIR(statbuf.st_mode); +} + +void ensure_dir(const std::string & dname) { + auto ret = mkdir(dname.c_str(), 0755); + if (ret == -1) { + auto err_code = errno; + if (err_code != EEXIST || !is_directory(dname.c_str())) { + char err_buf[1024]; + strerror_r(err_code, err_buf, sizeof(err_buf)); + throw std::runtime_error(err_buf); + } + } +} + +std::string normalize_time(time_t timestamp) { + char buf[128]; + if (strftime(buf, sizeof(buf), "%c", localtime(×tamp)) == 0) + return ""; + return buf; +} + +// ================================================ +// Next utils probably will be move to libdnf + +/* return a path to a valid and safe cachedir - only used when not running + as root or when --tempcache is set */ +std::string get_cache_dir() { + constexpr const char * base_prefix = "dnf"; + constexpr const char * tmpdir = "/var/tmp/"; + + uid_t uid = geteuid(); + auto pw = getpwuid(uid); + std::string prefix = std::string(base_prefix) + "-"; + if (pw) { + prefix += pw->pw_name; + } else { + prefix += std::to_string(uid); + } + prefix += "-"; + + // check for /var/tmp/prefix-* - + std::string dirpath = tmpdir + prefix + "*"; + std::string cachedir; + + /*for (auto & dentry : std::filesystem::directory_iterator(tmpdir)) { + auto & path = dentry.path().string(); + if (path.compare(0, dirpath.size(), dirpath) == 0 && dentry.is_directory() && dentry.status().permissions() == std::perm::owner_all && + + ) { + cachedir = path; + break; + } + }*/ + + glob_t glob_buf; + glob(dirpath.c_str(), GLOB_MARK, nullptr, &glob_buf); + for (size_t i = 0; i < glob_buf.gl_pathc; ++i) { + auto path = glob_buf.gl_pathv[i]; + struct stat stat_buf; + if (lstat(path, &stat_buf) == 0) { + if (S_ISDIR(stat_buf.st_mode) && ((stat_buf.st_mode & 0x1FF) == 448) && stat_buf.st_uid == uid) + cachedir = path; + } + } + globfree(&glob_buf); + + // make the dir (tempfile.mkdtemp()) + if (cachedir.empty()) { + cachedir = tmpdir + prefix + "XXXXXX"; + mkdtemp(&cachedir.front()); + // std::cout << "Generate new: " << cachedir << std::endl; + } + + return cachedir; +} + +#define MAX_NATIVE_ARCHES 12 + +/* data taken from DNF */ +static const struct { + const char * base; + const char * native[MAX_NATIVE_ARCHES]; +} ARCH_MAP[] = {{"aarch64", {"aarch64", nullptr}}, + {"alpha", + {"alpha", + "alphaev4", + "alphaev45", + "alphaev5", + "alphaev56", + "alphaev6", + "alphaev67", + "alphaev68", + "alphaev7", + "alphapca56", + nullptr}}, + {"arm", {"armv5tejl", "armv5tel", "armv5tl", "armv6l", "armv7l", "armv8l", nullptr}}, + {"armhfp", {"armv7hl", "armv7hnl", "armv8hl", "armv8hnl", "armv8hcnl", nullptr}}, + {"i386", {"i386", "athlon", "geode", "i386", "i486", "i586", "i686", nullptr}}, + {"ia64", {"ia64", nullptr}}, + {"mips", {"mips", nullptr}}, + {"mipsel", {"mipsel", nullptr}}, + {"mips64", {"mips64", nullptr}}, + {"mips64el", {"mips64el", nullptr}}, + {"noarch", {"noarch", nullptr}}, + {"ppc", {"ppc", nullptr}}, + {"ppc64", {"ppc64", "ppc64iseries", "ppc64p7", "ppc64pseries", nullptr}}, + {"ppc64le", {"ppc64le", nullptr}}, + {"riscv32", {"riscv32", nullptr}}, + {"riscv64", {"riscv64", nullptr}}, + {"riscv128", {"riscv128", nullptr}}, + {"s390", {"s390", nullptr}}, + {"s390x", {"s390x", nullptr}}, + {"sh3", {"sh3", nullptr}}, + {"sh4", {"sh4", "sh4a", nullptr}}, + {"sparc", {"sparc", "sparc64", "sparc64v", "sparcv8", "sparcv9", "sparcv9v", nullptr}}, + {"x86_64", {"x86_64", "amd64", "ia32e", nullptr}}, + {nullptr, {nullptr}}}; + +/* find the base architecture */ +const char * get_base_arch(const char * arch) { + for (int i = 0; ARCH_MAP[i].base; ++i) { + for (int j = 0; ARCH_MAP[i].native[j]; ++j) { + if (strcmp(ARCH_MAP[i].native[j], arch) == 0) { + return ARCH_MAP[i].base; + } + } + } + return nullptr; +} + +static void init_lib_rpm() { + static bool lib_rpm_initiated{false}; + if (!lib_rpm_initiated) { + if (rpmReadConfigFiles(nullptr, nullptr) != 0) { + throw std::runtime_error("failed to read rpm config files\n"); + } + lib_rpm_initiated = true; + } +} + +//#define RELEASEVER_PROV "system-release(releasever)" + +static constexpr const char * DISTROVERPKGS[] = {"system-release(releasever)", + "system-release", + "distribution-release(releasever)", + "distribution-release", + "redhat-release", + "suse-release"}; + +std::string detect_arch() { + init_lib_rpm(); + const char * value; + rpmGetArchInfo(&value, nullptr); + return value; +} + +/** + * dnf_context_set_os_release: + **/ +std::string detect_release(const std::string & install_root_path) { + init_lib_rpm(); + std::string release_ver; + + bool found_in_rpmdb{false}; + auto ts = rpmtsCreate(); + rpmtsSetRootDir(ts, install_root_path.c_str()); + for (auto distroverpkg : DISTROVERPKGS) { + auto mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, distroverpkg, 0); + while (auto hdr = rpmdbNextIterator(mi)) { + auto version = headerGetString(hdr, RPMTAG_VERSION); + rpmds ds = rpmdsNew(hdr, RPMTAG_PROVIDENAME, 0); + while (rpmdsNext(ds) >= 0) { + if (strcmp(rpmdsN(ds), distroverpkg) == 0 && rpmdsFlags(ds) == RPMSENSE_EQUAL) { + version = rpmdsEVR(ds); + } + } + release_ver = version; + found_in_rpmdb = true; + rpmdsFree(ds); + break; + } + rpmdbFreeIterator(mi); + if (found_in_rpmdb) { + break; + } + } + rpmtsFree(ts); + if (found_in_rpmdb) { + return release_ver; + } + return ""; +} + +} // namespace microdnf diff --git a/microdnf/utils.hpp b/microdnf/utils.hpp new file mode 100644 index 0000000000..a8c1585685 --- /dev/null +++ b/microdnf/utils.hpp @@ -0,0 +1,48 @@ +/* +Copyright (C) 2020 Red Hat, Inc. + +This file is part of microdnf: https://github.com/rpm-software-management/libdnf/ + +Microdnf 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 of the License, or +(at your option) any later version. + +Microdnf 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 microdnf. If not, see . +*/ + +#ifndef MICRODNF_UTILS_HPP +#define MICRODNF_UTILS_HPP + +#include + +namespace microdnf { + +bool am_i_root(); +bool is_directory(const char * path); +void ensure_dir(const std::string & dname); +std::string normalize_time(time_t timestamp); + + +// ================================================ +// Next utils probably will be movde to libdnf + +/* return a path to a valid and safe cachedir - only used when not running + as root or when --tempcache is set */ +std::string get_cache_dir(); + +/* find the base architecture */ +const char * get_base_arch(const char * arch); + +std::string detect_arch(); +std::string detect_release(const std::string & install_root_path); + +} // namespace microdnf + +#endif