diff --git a/.gitignore b/.gitignore index 5aef53c..ce72cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/libdnf-0.48.0.tar.gz +SOURCES/libdnf-0.55.0.tar.gz diff --git a/.libdnf.metadata b/.libdnf.metadata index b596b13..85f3501 100644 --- a/.libdnf.metadata +++ b/.libdnf.metadata @@ -1 +1 @@ -452c2195741b627bd97b0be11cd4a4dc4118e330 SOURCES/libdnf-0.48.0.tar.gz +5d997c2c7113cd50af986ada5ff10a62853f1943 SOURCES/libdnf-0.55.0.tar.gz diff --git a/SOURCES/0001-Better-msgs-if-basecachedir-or-proxy-password-isn-t-set-RhBug-1888946.patch b/SOURCES/0001-Better-msgs-if-basecachedir-or-proxy-password-isn-t-set-RhBug-1888946.patch new file mode 100644 index 0000000..7666421 --- /dev/null +++ b/SOURCES/0001-Better-msgs-if-basecachedir-or-proxy-password-isn-t-set-RhBug-1888946.patch @@ -0,0 +1,87 @@ +From 2353dfbcb49a16bd37115915517417678fe49b19 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Fri, 13 Nov 2020 09:55:23 +0100 +Subject: [PATCH] Better msgs if "basecachedir" or "proxy_password" isn't set + (RhBug:1888946) + +Generates more specific error messages: +- repo '%s': 'basecachedir' is not set +- repo '%s': 'proxy_username' is set but not 'proxy_password' +- 'proxy_username' is set but not 'proxy_password' +instead of generic "GetValue(): Value not set" +--- + libdnf/repo/Repo.cpp | 24 ++++++++++++++++++++++-- + 1 file changed, 22 insertions(+), 2 deletions(-) + +diff --git a/libdnf/repo/Repo.cpp b/libdnf/repo/Repo.cpp +index d7c137d75..34539e1ee 100644 +--- a/libdnf/repo/Repo.cpp ++++ b/libdnf/repo/Repo.cpp +@@ -484,8 +484,12 @@ std::unique_ptr Repo::Impl::lrHandleInitLocal() + handleSetOpt(h.get(), LRO_LOCAL, 1L); + #ifdef LRO_SUPPORTS_CACHEDIR + /* If zchunk is enabled, set librepo cache dir */ +- if (conf->getMasterConfig().zchunk().getValue()) ++ if (conf->getMasterConfig().zchunk().getValue()) { ++ if (conf->basecachedir().empty()) { ++ throw Exception(tfm::format(_("repo '%s': 'basecachedir' is not set"), id)); ++ } + handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().getValue().c_str()); ++ } + #endif + return h; + } +@@ -526,6 +530,9 @@ std::unique_ptr Repo::Impl::lrHandleInitRemote(const char *destdir) + handleSetOpt(h.get(), LRO_METALINKURL, tmp.c_str()); + } + handleSetOpt(h.get(), LRO_FASTESTMIRROR, conf->fastestmirror().getValue() ? 1L : 0L); ++ if (conf->basecachedir().empty()) { ++ throw Exception(tfm::format(_("repo '%s': 'basecachedir' is not set"), id)); ++ } + auto fastestMirrorCacheDir = conf->basecachedir().getValue(); + if (fastestMirrorCacheDir.back() != '/') + fastestMirrorCacheDir.push_back('/'); +@@ -569,8 +576,12 @@ std::unique_ptr Repo::Impl::lrHandleInitRemote(const char *destdir) + + #ifdef LRO_SUPPORTS_CACHEDIR + /* If zchunk is enabled, set librepo cache dir */ +- if (conf->getMasterConfig().zchunk().getValue()) ++ if (conf->getMasterConfig().zchunk().getValue()) { ++ if (conf->basecachedir().empty()) { ++ throw Exception(tfm::format(_("repo '%s': 'basecachedir' is not set"), id)); ++ } + handleSetOpt(h.get(), LRO_CACHEDIR, conf->basecachedir().getValue().c_str()); ++ } + #endif + + auto minrate = conf->minrate().getValue(); +@@ -610,6 +621,9 @@ std::unique_ptr Repo::Impl::lrHandleInitRemote(const char *destdir) + if (!conf->proxy_username().empty()) { + userpwd = conf->proxy_username().getValue(); + if (!userpwd.empty()) { ++ if (conf->proxy_password().empty()) { ++ throw RepoError(tfm::format(_("repo '%s': 'proxy_username' is set but not 'proxy_password'"), id)); ++ } + userpwd = formatUserPassString(userpwd, conf->proxy_password().getValue(), true); + handleSetOpt(h.get(), LRO_PROXYUSERPWD, userpwd.c_str()); + } +@@ -1346,6 +1360,9 @@ std::string Repo::Impl::getHash() const + + std::string Repo::Impl::getCachedir() const + { ++ if (conf->basecachedir().empty()) { ++ throw Exception(tfm::format(_("repo '%s': 'basecachedir' is not set"), id)); ++ } + auto repodir(conf->basecachedir().getValue()); + if (repodir.back() != '/') + repodir.push_back('/'); +@@ -1690,6 +1707,9 @@ static LrHandle * newHandle(ConfigMain * conf) + if (!conf->proxy_username().empty()) { + auto userpwd = conf->proxy_username().getValue(); + if (!userpwd.empty()) { ++ if (conf->proxy_password().empty()) { ++ throw RepoError(_("'proxy_username' is set but not 'proxy_password'")); ++ } + userpwd = formatUserPassString(userpwd, conf->proxy_password().getValue(), true); + handleSetOpt(h, LRO_PROXYUSERPWD, userpwd.c_str()); + } diff --git a/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch b/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch deleted file mode 100644 index 079524d..0000000 --- a/SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch +++ /dev/null @@ -1,244 +0,0 @@ -From a96d701f7f55ff475e11ac9cda63b81c31c54e7a Mon Sep 17 00:00:00 2001 -From: Daniel Mach -Date: Wed, 6 May 2020 08:34:46 +0200 -Subject: [PATCH] history: Fix dnf history rollback when a package was removed - (RhBug:1683134) - ---- - libdnf/transaction/MergedTransaction.cpp | 25 +++- - .../transaction/MergedTransactionTest.cpp | 120 ++++++++++++++++-- - .../transaction/MergedTransactionTest.hpp | 6 +- - 3 files changed, 137 insertions(+), 14 deletions(-) - -diff --git a/libdnf/transaction/MergedTransaction.cpp b/libdnf/transaction/MergedTransaction.cpp -index a7c06ffa9..a8d878cb5 100644 ---- a/libdnf/transaction/MergedTransaction.cpp -+++ b/libdnf/transaction/MergedTransaction.cpp -@@ -19,6 +19,7 @@ - */ - - #include "MergedTransaction.hpp" -+#include - #include - - namespace libdnf { -@@ -171,6 +172,21 @@ MergedTransaction::getConsoleOutput() - return output; - } - -+ -+static bool transaction_item_sort_function(const std::shared_ptr lhs, const std::shared_ptr rhs) { -+ if (lhs->isForwardAction() && rhs->isForwardAction()) { -+ return false; -+ } -+ if (lhs->isBackwardAction() && rhs->isBackwardAction()) { -+ return false; -+ } -+ if (lhs->isBackwardAction()) { -+ return true; -+ } -+ return false; -+} -+ -+ - /** - * Get list of transaction items involved in the merged transaction - * Actions are merged using following rules: -@@ -203,6 +219,9 @@ MergedTransaction::getItems() - // iterate over transaction - for (auto t : transactions) { - auto transItems = t->getItems(); -+ // sort transaction items by their action type - forward/backward -+ // this fixes behavior of the merging algorithm in several edge cases -+ std::sort(transItems.begin(), transItems.end(), transaction_item_sort_function); - // iterate over transaction items - for (auto transItem : transItems) { - // get item and its type -@@ -383,10 +402,6 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT - auto firstState = previousItemPair.first->getAction(); - auto newState = mTransItem->getAction(); - -- if (firstState == TransactionItemAction::INSTALL && mTransItem->isBackwardAction()) { -- return; -- } -- - switch (firstState) { - case TransactionItemAction::REMOVE: - case TransactionItemAction::OBSOLETED: -@@ -399,6 +414,8 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT - // Install -> Remove = (nothing) - itemPairMap.erase(name); - break; -+ } else if (mTransItem->isBackwardAction()) { -+ break; - } - // altered -> transfer install to the altered package - mTransItem->setAction(TransactionItemAction::INSTALL); -diff --git a/tests/libdnf/transaction/MergedTransactionTest.cpp b/tests/libdnf/transaction/MergedTransactionTest.cpp -index 90ad182cf..52507700b 100644 ---- a/tests/libdnf/transaction/MergedTransactionTest.cpp -+++ b/tests/libdnf/transaction/MergedTransactionTest.cpp -@@ -700,7 +700,7 @@ MergedTransactionTest::test_downgrade() - } - - void --MergedTransactionTest::test_install_downgrade() -+MergedTransactionTest::test_install_downgrade_upgrade_remove() - { - auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn); - trans1->addItem( -@@ -724,19 +724,123 @@ MergedTransactionTest::test_install_downgrade() - TransactionItemReason::USER - ); - -+ auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn); -+ trans3->addItem( -+ nevraToRPMItem(conn, "tour-0:4.6-1.noarch"), -+ "repo2", -+ TransactionItemAction::UPGRADED, -+ TransactionItemReason::USER -+ ); -+ trans3->addItem( -+ nevraToRPMItem(conn, "tour-0:4.8-1.noarch"), -+ "repo1", -+ TransactionItemAction::UPGRADE, -+ TransactionItemReason::USER -+ ); -+ -+ auto trans4 = std::make_shared< libdnf::swdb_private::Transaction >(conn); -+ trans4->addItem( -+ nevraToRPMItem(conn, "tour-0:4.8-1.noarch"), -+ "repo1", -+ TransactionItemAction::REMOVE, -+ TransactionItemReason::USER -+ ); -+ - MergedTransaction merged(trans1); -+ -+ // test merging trans1, trans2 - merged.merge(trans2); -+ auto items2 = merged.getItems(); -+ CPPUNIT_ASSERT_EQUAL(1, (int)items2.size()); -+ auto item2 = items2.at(0); -+ CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item2->getItem()->toStr()); -+ CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item2->getRepoid()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item2->getAction()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason()); - -- auto items = merged.getItems(); -- CPPUNIT_ASSERT_EQUAL(1, (int)items.size()); -+ // test merging trans1, trans2, trans3 -+ merged.merge(trans3); -+ auto items3 = merged.getItems(); -+ CPPUNIT_ASSERT_EQUAL(1, (int)items3.size()); -+ auto item3 = items3.at(0); -+ CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr()); -+ CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item3->getAction()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason()); - -- auto item = items.at(0); -- CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item->getItem()->toStr()); -- CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item->getRepoid()); -- CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item->getAction()); -- CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item->getReason()); -+ // test merging trans1, trans2, trans3, trans4 -+ merged.merge(trans4); -+ auto items4 = merged.getItems(); -+ CPPUNIT_ASSERT_EQUAL(0, (int)items4.size()); -+ // trans4 removes the package, empty output is expected - } - -+ -+void -+MergedTransactionTest::test_downgrade_upgrade_remove() -+{ -+ auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn); -+ trans1->addItem( -+ nevraToRPMItem(conn, "tour-0:4.6-1.noarch"), -+ "repo2", -+ TransactionItemAction::DOWNGRADE, -+ TransactionItemReason::USER -+ ); -+ trans1->addItem( -+ nevraToRPMItem(conn, "tour-0:4.8-1.noarch"), -+ "repo1", -+ TransactionItemAction::DOWNGRADED, -+ TransactionItemReason::USER -+ ); -+ -+ // items are in reversed order than in test_install_downgrade_upgrade_remove() -+ // fixing this required ordering transaction items by forward/backward action -+ auto trans2 = std::make_shared< libdnf::swdb_private::Transaction >(conn); -+ trans2->addItem( -+ nevraToRPMItem(conn, "tour-0:4.8-1.noarch"), -+ "repo1", -+ TransactionItemAction::UPGRADE, -+ TransactionItemReason::USER -+ ); -+ trans2->addItem( -+ nevraToRPMItem(conn, "tour-0:4.6-1.noarch"), -+ "repo2", -+ TransactionItemAction::UPGRADED, -+ TransactionItemReason::USER -+ ); -+ -+ auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn); -+ trans3->addItem( -+ nevraToRPMItem(conn, "tour-0:4.8-1.noarch"), -+ "repo1", -+ TransactionItemAction::REMOVE, -+ TransactionItemReason::USER -+ ); -+ -+ MergedTransaction merged(trans1); -+ -+ // test merging trans1, trans2 -+ merged.merge(trans2); -+ auto items2 = merged.getItems(); -+ CPPUNIT_ASSERT_EQUAL(1, (int)items2.size()); -+ auto item2 = items2.at(0); -+ CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item2->getItem()->toStr()); -+ CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item2->getRepoid()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REINSTALL, item2->getAction()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason()); -+ -+ // test merging trans1, trans2, trans3 -+ merged.merge(trans3); -+ auto items3 = merged.getItems(); -+ CPPUNIT_ASSERT_EQUAL(1, (int)items3.size()); -+ auto item3 = items3.at(0); -+ CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr()); -+ CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REMOVE, item3->getAction()); -+ CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason()); -+} -+ -+ - void - MergedTransactionTest::test_multilib_identity() - { -diff --git a/tests/libdnf/transaction/MergedTransactionTest.hpp b/tests/libdnf/transaction/MergedTransactionTest.hpp -index 9f1ed660a..77585e865 100644 ---- a/tests/libdnf/transaction/MergedTransactionTest.hpp -+++ b/tests/libdnf/transaction/MergedTransactionTest.hpp -@@ -26,7 +26,8 @@ class MergedTransactionTest : public CppUnit::TestCase { - CPPUNIT_TEST(test_add_obsoleted_obsoleted); - - CPPUNIT_TEST(test_downgrade); -- CPPUNIT_TEST(test_install_downgrade); -+ CPPUNIT_TEST(test_install_downgrade_upgrade_remove); -+ CPPUNIT_TEST(test_downgrade_upgrade_remove); - - CPPUNIT_TEST(test_multilib_identity); - -@@ -56,7 +57,8 @@ class MergedTransactionTest : public CppUnit::TestCase { - // END: tests ported from DNF unit tests - - void test_downgrade(); -- void test_install_downgrade(); -+ void test_install_downgrade_upgrade_remove(); -+ void test_downgrade_upgrade_remove(); - - void test_multilib_identity(); - private: diff --git a/SOURCES/0002-modules-Add-special-handling-for-src-artifacts-RhBug-1809314.patch b/SOURCES/0002-modules-Add-special-handling-for-src-artifacts-RhBug-1809314.patch new file mode 100644 index 0000000..a16d544 --- /dev/null +++ b/SOURCES/0002-modules-Add-special-handling-for-src-artifacts-RhBug-1809314.patch @@ -0,0 +1,73 @@ +From 71db968178e5d8cd4c01ed36fa940c2a95f3e494 Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Mon, 9 Nov 2020 18:10:37 +0100 +Subject: [PATCH] [modules] Add special handling for src artifacts + (RhBug:1809314) + +Source packages are special because they cannot be installed and provide +nothing, therefore they should be handled by a different way than binary +packages. Source rpm should not trigger a removal of binary packages. +--- + libdnf/dnf-sack.cpp | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/libdnf/dnf-sack.cpp b/libdnf/dnf-sack.cpp +index 9fd2c72d1..d44e1d86d 100644 +--- a/libdnf/dnf-sack.cpp ++++ b/libdnf/dnf-sack.cpp +@@ -2335,20 +2335,29 @@ setModuleExcludes(DnfSack *sack, const char ** hotfixRepos, + { + dnf_sack_set_module_excludes(sack, nullptr); + std::vector names; ++ std::vector srcNames; + libdnf::DependencyContainer nameDependencies{sack}; + libdnf::Nevra nevra; + for (const auto &rpm : includeNEVRAs) { + if (nevra.parse(rpm.c_str(), HY_FORM_NEVRA)) { +- names.push_back(nevra.getName()); +- nameDependencies.addReldep(nevra.getName().c_str()); ++ auto arch = nevra.getArch(); ++ // source packages do not provide anything and must not cause excluding binary packages ++ if (arch == "src" || arch == "nosrc") { ++ srcNames.push_back(nevra.getName()); ++ } else { ++ names.push_back(nevra.getName()); ++ nameDependencies.addReldep(nevra.getName().c_str()); ++ } + } + } + + std::vector namesCString(names.size() + 1); ++ std::vector srcNamesCString(srcNames.size() + 1); + std::vector excludeNEVRAsCString(excludeNEVRAs.size() + 1); + std::vector includeNEVRAsCString(includeNEVRAs.size() + 1); + + transform(names.begin(), names.end(), namesCString.begin(), std::mem_fn(&std::string::c_str)); ++ transform(srcNames.begin(), srcNames.end(), srcNamesCString.begin(), std::mem_fn(&std::string::c_str)); + transform(excludeNEVRAs.begin(), excludeNEVRAs.end(), excludeNEVRAsCString.begin(), + std::mem_fn(&std::string::c_str)); + transform(includeNEVRAs.begin(), includeNEVRAs.end(), includeNEVRAsCString.begin(), +@@ -2363,6 +2372,7 @@ setModuleExcludes(DnfSack *sack, const char ** hotfixRepos, + libdnf::Query excludeQuery{keepPackages}; + libdnf::Query excludeProvidesQuery{keepPackages}; + libdnf::Query excludeNamesQuery(keepPackages); ++ libdnf::Query excludeSrcNamesQuery(keepPackages); + includeQuery.addFilter(HY_PKG_NEVRA_STRICT, HY_EQ, includeNEVRAsCString.data()); + + excludeQuery.addFilter(HY_PKG_NEVRA_STRICT, HY_EQ, excludeNEVRAsCString.data()); +@@ -2372,8 +2382,14 @@ setModuleExcludes(DnfSack *sack, const char ** hotfixRepos, + excludeProvidesQuery.addFilter(HY_PKG_PROVIDES, &nameDependencies); + excludeProvidesQuery.queryDifference(includeQuery); + +- // Requred to filtrate out source packages and packages with incompatible architectures ++ // Search for source packages with same names as included source artifacts ++ excludeSrcNamesQuery.addFilter(HY_PKG_NAME, HY_EQ, srcNamesCString.data()); ++ const char * srcArchs[] = {"src", "nosrc", nullptr}; ++ excludeSrcNamesQuery.addFilter(HY_PKG_ARCH, HY_EQ, srcArchs); ++ ++ // Required to filtrate out source packages and packages with incompatible architectures + excludeNamesQuery.addFilter(HY_PKG_NAME, HY_EQ, namesCString.data()); ++ excludeNamesQuery.queryUnion(excludeSrcNamesQuery); + excludeNamesQuery.queryDifference(includeQuery); + + dnf_sack_set_module_excludes(sack, excludeQuery.getResultPset()); diff --git a/SOURCES/0003-Avoid-multilib-file-conflict-in-config.h-RhBug-1918818.patch b/SOURCES/0003-Avoid-multilib-file-conflict-in-config.h-RhBug-1918818.patch new file mode 100644 index 0000000..ae93fb0 --- /dev/null +++ b/SOURCES/0003-Avoid-multilib-file-conflict-in-config.h-RhBug-1918818.patch @@ -0,0 +1,101 @@ +From 3f6adc99506f065d0858e4d9d46055be9d070634 Mon Sep 17 00:00:00 2001 +From: Nicola Sella +Date: Fri, 22 Jan 2021 16:07:37 +0100 +Subject: [PATCH] Avoid multilib file conflict in config.h (RhBug:1918818) + +=changelog= +msg: Avoid multilib file conflicts in config.h +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1918818 +--- + .gitignore | 2 +- + libdnf/CMakeLists.txt | 8 ++++++- + libdnf/{config.h.in => config-64.h.in} | 6 +++--- + libdnf/config.h | 29 ++++++++++++++++++++++++++ + 4 files changed, 40 insertions(+), 5 deletions(-) + rename libdnf/{config.h.in => config-64.h.in} (87%) + create mode 100644 libdnf/config.h + +diff --git a/.gitignore b/.gitignore +index e17a9b9bb..0a63bdae7 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -5,4 +5,4 @@ + build + *.pyc + data/tests/modules/yum.repos.d/test.repo +-libdnf/config.h ++libdnf/config-64.h +diff --git a/libdnf/CMakeLists.txt b/libdnf/CMakeLists.txt +index e82aac11e..25f33d7b0 100644 +--- a/libdnf/CMakeLists.txt ++++ b/libdnf/CMakeLists.txt +@@ -35,7 +35,13 @@ set(LIBDNF_SRCS + include_directories(transaction) + add_subdirectory("transaction") + +-configure_file("config.h.in" ${CMAKE_CURRENT_SOURCE_DIR}/config.h) ++if(CMAKE_SIZEOF_VOID_P EQUAL 8) ++ set(MULTILIB_ARCH "64") ++ configure_file("config-64.h.in" ${CMAKE_CURRENT_SOURCE_DIR}/config-64.h) ++elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) ++ set(MULTILIB_ARCH "32") ++ configure_file("config-64.h.in" ${CMAKE_CURRENT_SOURCE_DIR}/config-32.h) ++endif() + configure_file("dnf-version.h.in" ${CMAKE_CURRENT_SOURCE_DIR}/dnf-version.h) + configure_file("libdnf.pc.in" ${CMAKE_CURRENT_BINARY_DIR}/libdnf.pc @ONLY) + +diff --git a/libdnf/config.h.in b/libdnf/config-64.h.in +similarity index 87% +rename from libdnf/config.h.in +rename to libdnf/config-64.h.in +index 77974f757..e2329fe71 100644 +--- a/libdnf/config.h.in ++++ b/libdnf/config-64.h.in +@@ -18,9 +18,9 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +-#ifndef _LIBDNF_CONFIG_H_ +-#define _LIBDNF_CONFIG_H_ ++#ifndef _LIBDNF_CONFIG_@MULTILIB_ARCH@_H_ ++#define _LIBDNF_CONFIG_@MULTILIB_ARCH@_H_ + + #define DEFAULT_PLUGINS_DIRECTORY "@CMAKE_INSTALL_FULL_LIBDIR@/libdnf/plugins/" + +-#endif // _LIBDNF_CONFIG_H_ ++#endif // _LIBDNF_CONFIG_@MULTILIB_ARCH@_H_ +diff --git a/libdnf/config.h b/libdnf/config.h +new file mode 100644 +index 000000000..16121f6f5 +--- /dev/null ++++ b/libdnf/config.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 ++ ++#if __WORDSIZE == 32 ++#include "config-32.h" ++#elif __WORDSIZE == 64 ++#include "config-64.h" ++#else ++#error "Unknown word size" ++#endif diff --git a/SOURCES/0004-context-improve-retrieving-repository-configuration.patch b/SOURCES/0004-context-improve-retrieving-repository-configuration.patch new file mode 100644 index 0000000..79bdeff --- /dev/null +++ b/SOURCES/0004-context-improve-retrieving-repository-configuration.patch @@ -0,0 +1,1136 @@ +From be8449aa177473a834a5b2c401a8a3fcc61522b4 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Wed, 2 Dec 2020 08:00:07 +0100 +Subject: [PATCH 1/9] Option: Add reset() method + +The method resets the option to its initial state. +Can be used, for example, before reloading the configuration in daemon +mode (PackageKit). +--- + libdnf/conf/Option.hpp | 2 ++ + libdnf/conf/OptionBool.hpp | 9 ++++++++- + libdnf/conf/OptionChild.hpp | 14 ++++++++++++++ + libdnf/conf/OptionEnum.hpp | 15 +++++++++++++++ + libdnf/conf/OptionNumber.hpp | 10 +++++++++- + libdnf/conf/OptionString.cpp | 11 ++++++++--- + libdnf/conf/OptionString.hpp | 8 ++++++++ + libdnf/conf/OptionStringList.hpp | 9 ++++++++- + 8 files changed, 72 insertions(+), 6 deletions(-) + +diff --git a/libdnf/conf/Option.hpp b/libdnf/conf/Option.hpp +index e9a9dfc84..849871fe7 100644 +--- a/libdnf/conf/Option.hpp ++++ b/libdnf/conf/Option.hpp +@@ -62,6 +62,8 @@ class Option { + virtual void set(Priority priority, const std::string & value) = 0; + virtual std::string getValueString() const = 0; + virtual bool empty() const noexcept; ++ /// Resets the option to its initial state. ++ virtual void reset() = 0; + virtual ~Option() = default; + + protected: +diff --git a/libdnf/conf/OptionBool.hpp b/libdnf/conf/OptionBool.hpp +index c27ab0b79..a5e647807 100644 +--- a/libdnf/conf/OptionBool.hpp ++++ b/libdnf/conf/OptionBool.hpp +@@ -47,6 +47,7 @@ class OptionBool : public Option { + std::string getValueString() const override; + const char * const * getTrueValues() const noexcept; + const char * const * getFalseValues() const noexcept; ++ void reset() override; + + protected: + const char * const * const trueValues; +@@ -84,7 +85,13 @@ inline const char * const * OptionBool::getTrueValues() const noexcept + + inline const char * const * OptionBool::getFalseValues() const noexcept + { +- return falseValues ? falseValues : defFalseValues; ++ return falseValues ? falseValues : defFalseValues; ++} ++ ++inline void OptionBool::reset() ++{ ++ value = defaultValue; ++ priority = Priority::DEFAULT; + } + + } +diff --git a/libdnf/conf/OptionChild.hpp b/libdnf/conf/OptionChild.hpp +index 5d1503cb6..3056345f9 100644 +--- a/libdnf/conf/OptionChild.hpp ++++ b/libdnf/conf/OptionChild.hpp +@@ -39,6 +39,7 @@ class OptionChild : public Option { + const typename ParentOptionType::ValueType getDefaultValue() const; + std::string getValueString() const override; + bool empty() const noexcept override; ++ void reset() override; + + private: + const ParentOptionType * parent; +@@ -56,6 +57,7 @@ class OptionChild::empty() const noexcept + return priority == Priority::EMPTY && parent->empty(); + } + ++template ++inline void OptionChild::reset() ++{ ++ priority = Priority::EMPTY; ++} ++ + template + inline OptionChild::value>::type>::OptionChild(const ParentOptionType & parent) + : parent(&parent) {} +@@ -171,6 +179,12 @@ inline bool OptionChildempty(); + } + ++template ++inline void OptionChild::value>::type>::reset() ++{ ++ priority = Priority::EMPTY; ++} ++ + } + + #endif +diff --git a/libdnf/conf/OptionEnum.hpp b/libdnf/conf/OptionEnum.hpp +index c63156cb3..d2f710f20 100644 +--- a/libdnf/conf/OptionEnum.hpp ++++ b/libdnf/conf/OptionEnum.hpp +@@ -49,6 +49,7 @@ class OptionEnum : public Option { + T getDefaultValue() const; + std::string toString(ValueType value) const; + std::string getValueString() const override; ++ void reset() override; + + protected: + FromStringFunc fromStringUser; +@@ -74,6 +75,7 @@ class OptionEnum : public Option { + const std::string & getValue() const; + const std::string & getDefaultValue() const; + std::string getValueString() const override; ++ void reset() override; + + protected: + FromStringFunc fromStringUser; +@@ -88,6 +90,13 @@ inline OptionEnum * OptionEnum::clone() const + return new OptionEnum(*this); + } + ++template ++inline void OptionEnum::reset() ++{ ++ value = defaultValue; ++ priority = Priority::DEFAULT; ++} ++ + inline OptionEnum * OptionEnum::clone() const + { + return new OptionEnum(*this); +@@ -108,6 +117,12 @@ inline std::string OptionEnum::getValueString() const + return value; + } + ++inline void OptionEnum::reset() ++{ ++ value = defaultValue; ++ priority = Priority::DEFAULT; ++} ++ + } + + #endif +diff --git a/libdnf/conf/OptionNumber.hpp b/libdnf/conf/OptionNumber.hpp +index 98988fd50..f7a7b3d6e 100644 +--- a/libdnf/conf/OptionNumber.hpp ++++ b/libdnf/conf/OptionNumber.hpp +@@ -50,6 +50,7 @@ class OptionNumber : public Option { + T getDefaultValue() const; + std::string toString(ValueType value) const; + std::string getValueString() const override; ++ void reset() override; + + protected: + FromStringFunc fromStringUser; +@@ -80,7 +81,14 @@ inline T OptionNumber::getDefaultValue() const + template + inline std::string OptionNumber::getValueString() const + { +- return toString(value); ++ return toString(value); ++} ++ ++template ++inline void OptionNumber::reset() ++{ ++ value = defaultValue; ++ priority = Priority::DEFAULT; + } + + extern template class OptionNumber; +diff --git a/libdnf/conf/OptionString.cpp b/libdnf/conf/OptionString.cpp +index d27194f7a..b42e6c633 100644 +--- a/libdnf/conf/OptionString.cpp ++++ b/libdnf/conf/OptionString.cpp +@@ -27,18 +27,21 @@ + namespace libdnf { + + OptionString::OptionString(const std::string & defaultValue) +-: Option(Priority::DEFAULT), defaultValue(defaultValue), value(defaultValue) {} ++: Option(Priority::DEFAULT), initPriority(Priority::DEFAULT), defaultValue(defaultValue), value(defaultValue) {} + + OptionString::OptionString(const char * defaultValue) + { + if (defaultValue) { + this->value = this->defaultValue = defaultValue; +- this->priority = Priority::DEFAULT; ++ this->initPriority = this->priority = Priority::DEFAULT; ++ } else { ++ this->initPriority = Priority::EMPTY; + } + } + + 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); } ++: Option(Priority::DEFAULT), initPriority(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) +@@ -48,6 +51,8 @@ OptionString::OptionString(const char * defaultValue, const std::string & regex, + test(this->defaultValue); + this->value = this->defaultValue; + this->priority = Priority::DEFAULT; ++ } else { ++ this->initPriority = Priority::EMPTY; + } + } + +diff --git a/libdnf/conf/OptionString.hpp b/libdnf/conf/OptionString.hpp +index 2e26305c4..03fef8bcf 100644 +--- a/libdnf/conf/OptionString.hpp ++++ b/libdnf/conf/OptionString.hpp +@@ -42,8 +42,10 @@ class OptionString : public Option { + const std::string & getValue() const; + const std::string & getDefaultValue() const noexcept; + std::string getValueString() const override; ++ void reset() override; + + protected: ++ Priority initPriority; + std::string regex; + bool icase; + std::string defaultValue; +@@ -70,6 +72,12 @@ inline std::string OptionString::fromString(const std::string & value) const + return value; + } + ++inline void OptionString::reset() ++{ ++ value = defaultValue; ++ priority = initPriority; ++} ++ + } + + #endif +diff --git a/libdnf/conf/OptionStringList.hpp b/libdnf/conf/OptionStringList.hpp +index 942e56b16..20debaa8c 100644 +--- a/libdnf/conf/OptionStringList.hpp ++++ b/libdnf/conf/OptionStringList.hpp +@@ -45,6 +45,7 @@ class OptionStringList : public Option { + const ValueType & getDefaultValue() const; + std::string toString(const ValueType & value) const; + std::string getValueString() const override; ++ void reset() override; + + protected: + std::string regex; +@@ -70,7 +71,13 @@ inline const OptionStringList::ValueType & OptionStringList::getDefaultValue() c + + inline std::string OptionStringList::getValueString() const + { +- return toString(value); ++ return toString(value); ++} ++ ++inline void OptionStringList::reset() ++{ ++ value = defaultValue; ++ priority = Priority::DEFAULT; + } + + } + +From 372a000414875f323147cd342dd8b4c8c7ebe260 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Tue, 1 Dec 2020 08:29:53 +0100 +Subject: [PATCH 2/9] Add OptionBinds::getOption() method + +Sometime we want direct access to the underlying Option. +E.g. we want to get its original value (not just a string representation) +or find out the Option type. +--- + libdnf/conf/OptionBinds.cpp | 9 +++++++++ + libdnf/conf/OptionBinds.hpp | 2 ++ + 2 files changed, 11 insertions(+) + +diff --git a/libdnf/conf/OptionBinds.cpp b/libdnf/conf/OptionBinds.cpp +index f7c67540b..ab53518a3 100644 +--- a/libdnf/conf/OptionBinds.cpp ++++ b/libdnf/conf/OptionBinds.cpp +@@ -66,6 +66,15 @@ bool OptionBinds::Item::getAddValue() const + return addValue; + } + ++const Option & OptionBinds::Item::getOption() const ++{ ++ return *option; ++} ++ ++Option & OptionBinds::Item::getOption() ++{ ++ return *option; ++} + + // =========== OptionBinds class =============== + +diff --git a/libdnf/conf/OptionBinds.hpp b/libdnf/conf/OptionBinds.hpp +index 715c37e26..515120b93 100644 +--- a/libdnf/conf/OptionBinds.hpp ++++ b/libdnf/conf/OptionBinds.hpp +@@ -55,6 +55,8 @@ class OptionBinds { + void newString(Option::Priority priority, const std::string & value); + std::string getValueString() const; + bool getAddValue() const; ++ const Option & getOption() const; ++ Option & getOption(); + + private: + friend class OptionBinds; + +From 3a686c378978c90538a6ac5d9826d52ce7c8daf6 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Tue, 1 Dec 2020 08:37:14 +0100 +Subject: [PATCH 3/9] [context] Add dnf_repo_conf_from_gkeyfile() and + dnf_repo_conf_reset() + +dnf_repo_conf_from_gkeyfile(): +The function reloads repository configuration from GKeyFile. + +dnf_repo_conf_reset(): +Resets repository configuration options previously readed from repository +configuration file to initial state. +--- + libdnf/dnf-repo.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 64 insertions(+) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index 00f4bbf7b..9f283df55 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -936,6 +936,70 @@ dnf_repo_get_boolean(GKeyFile *keyfile, + return false; + } + ++/* Resets repository configuration options previously readed from repository ++ * configuration file to initial state. */ ++static void ++dnf_repo_conf_reset(libdnf::ConfigRepo &config) ++{ ++ for (auto & item : config.optBinds()) { ++ auto & itemOption = item.second; ++ if (itemOption.getPriority() == libdnf::Option::Priority::REPOCONFIG) { ++ itemOption.getOption().reset(); ++ } ++ } ++} ++ ++/* Loads repository configuration from GKeyFile */ ++static void ++dnf_repo_conf_from_gkeyfile(libdnf::ConfigRepo &config, const char *repoId, GKeyFile *gkeyFile) ++{ ++ // Reset to the initial state before reloading the configuration. ++ dnf_repo_conf_reset(config); ++ ++ g_auto(GStrv) keys = g_key_file_get_keys(gkeyFile, repoId, NULL, NULL); ++ for (auto it = keys; *it != NULL; ++it) { ++ auto key = *it; ++ g_autofree gchar *str = g_key_file_get_value(gkeyFile, repoId, key, NULL); ++ if (str) { ++ try { ++ auto & optionItem = config.optBinds().at(key); ++ ++ if (dynamic_cast(&optionItem.getOption()) || ++ dynamic_cast*>(&optionItem.getOption()) ++ ) { ++ ++ // reload list option from gKeyFile using g_key_file_get_string_list() ++ // g_key_file_get_value () is problematic for multiline lists ++ g_auto(GStrv) list = g_key_file_get_string_list(gkeyFile, repoId, key, NULL, NULL); ++ if (list) { ++ // list can be ['value1', 'value2, value3'] therefore we first join ++ // to have 'value1, value2, value3' ++ g_autofree gchar * tmp_strval = g_strjoinv(",", list); ++ try { ++ optionItem.newString(libdnf::Option::Priority::REPOCONFIG, tmp_strval); ++ } catch (const std::exception & ex) { ++ g_debug("Invalid configuration value: %s = %s in %s; %s", key, str, repoId, ex.what()); ++ } ++ } ++ ++ } else { ++ ++ // process other (non list) options ++ try { ++ optionItem.newString(libdnf::Option::Priority::REPOCONFIG, str); ++ } catch (const std::exception & ex) { ++ g_debug("Invalid configuration value: %s = %s in %s; %s", key, str, repoId, ex.what()); ++ } ++ ++ } ++ ++ } catch (const std::exception &) { ++ g_debug("Unknown configuration option: %s = %s in %s", key, str, repoId); ++ } ++ } ++ } ++} ++ + /* Initialize (or potentially reset) repo & LrHandle from keyfile values. */ + static gboolean + dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + +From 5f1c06a66fcdb2c2340c11c07c5ba0ea3abf4b77 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Wed, 2 Dec 2020 11:37:26 +0100 +Subject: [PATCH 4/9] [context] Use dnf_repo_conf_from_gkeyfile() for repo + configuration reload + +The dnf_repo_set_key_file_data() uses dnf_repo_conf_from_gkeyfile() now. +All occurrences of the direct use 'repo->getConfig()->.*set' and +newString() were removed. +--- + libdnf/dnf-repo.cpp | 121 +++++++++----------------------------------- + 1 file changed, 25 insertions(+), 96 deletions(-) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index 9f283df55..2837580f7 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -1006,7 +1006,6 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + { + DnfRepoPrivate *priv = GET_PRIVATE(repo); + guint cost; +- gboolean module_hotfixes = false; + g_autofree gchar *metadata_expire_str = NULL; + g_autofree gchar *mirrorlist = NULL; + g_autofree gchar *mirrorlisturl = NULL; +@@ -1016,48 +1015,28 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + g_autofree gchar *usr = NULL; + g_autofree gchar *usr_pwd = NULL; + g_autofree gchar *usr_pwd_proxy = NULL; +- g_auto(GStrv) baseurls; + + auto repoId = priv->repo->getId().c_str(); + g_debug("setting keyfile data for %s", repoId); + +- /* skip_if_unavailable is optional */ +- if (g_key_file_has_key(priv->keyfile, repoId, "skip_if_unavailable", NULL)) { +- bool skip = dnf_repo_get_boolean(priv->keyfile, repoId, "skip_if_unavailable"); +- priv->repo->getConfig()->skip_if_unavailable().set(libdnf::Option::Priority::REPOCONFIG, skip); +- } ++ auto conf = priv->repo->getConfig(); + +- /* priority is optional */ +- g_autofree gchar * priority_str = g_key_file_get_string(priv->keyfile, repoId, "priority", NULL); +- if (priority_str) { +- priv->repo->getConfig()->priority().set(libdnf::Option::Priority::REPOCONFIG, priority_str); +- } ++ // Reload repository configuration from keyfile. ++ dnf_repo_conf_from_gkeyfile(*conf, repoId, priv->keyfile); + + /* cost is optional */ + cost = g_key_file_get_integer(priv->keyfile, repoId, "cost", NULL); + if (cost != 0) + dnf_repo_set_cost(repo, cost); + +- module_hotfixes = g_key_file_get_boolean(priv->keyfile, repoId, "module_hotfixes", NULL); +- priv->repo->getConfig()->module_hotfixes().set(libdnf::Option::Priority::REPOCONFIG, module_hotfixes); +- + /* baseurl is optional; if missing, unset it */ +- baseurls = g_key_file_get_string_list(priv->keyfile, repoId, "baseurl", NULL, NULL); +- if (baseurls) { +- // baseruls can be ['value1', 'value2, value3'] therefore we first join to have 'value1, value2, value3' +- g_autofree gchar * tmp_strval = g_strjoinv(",", baseurls); +- +- auto & bindBaseurls = priv->repo->getConfig()->optBinds().at("baseurl"); +- bindBaseurls.newString(libdnf::Option::Priority::REPOCONFIG, tmp_strval); +- +- auto & repoBaseurls = priv->repo->getConfig()->baseurl(); +- if (!repoBaseurls.getValue().empty()){ +- auto len = repoBaseurls.getValue().size(); +- g_strfreev(baseurls); +- baseurls = g_new0(char *, len + 1); +- for (size_t i = 0; i < len; ++i) { +- baseurls[i] = g_strdup(repoBaseurls.getValue()[i].c_str()); +- } ++ g_auto(GStrv) baseurls = NULL; ++ auto & repoBaseurls = conf->baseurl().getValue(); ++ if (!repoBaseurls.empty()){ ++ auto len = repoBaseurls.size(); ++ baseurls = g_new0(char *, len + 1); ++ for (size_t i = 0; i < len; ++i) { ++ baseurls[i] = g_strdup(repoBaseurls[i].c_str()); + } + } + if (!lr_handle_setopt(priv->repo_handle, error, LRO_URLS, baseurls)) +@@ -1093,18 +1072,6 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + if (!lr_handle_setopt(priv->repo_handle, error, LRO_METALINKURL, metalinkurl)) + return FALSE; + +- /* needed in order for addCountmeFlag() to use the same persistdir as DNF +- * would */ +- if (metalinkurl) +- priv->repo->getConfig()->metalink().set(libdnf::Option::Priority::REPOCONFIG, metalinkurl); +- if (mirrorlisturl) +- priv->repo->getConfig()->mirrorlist().set(libdnf::Option::Priority::REPOCONFIG, mirrorlisturl); +- +- if (g_key_file_has_key(priv->keyfile, repoId, "countme", NULL)) { +- bool countme = dnf_repo_get_boolean(priv->keyfile, repoId, "countme"); +- priv->repo->getConfig()->countme().set(libdnf::Option::Priority::REPOCONFIG, countme); +- } +- + /* file:// */ + if (baseurls != NULL && baseurls[0] != NULL && + mirrorlisturl == NULL && metalinkurl == NULL) { +@@ -1150,42 +1117,20 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + dnf_repo_set_location_tmp(repo, tmp->str); + } + +- /* gpgkey is optional for gpgcheck=1, but required for repo_gpgcheck=1 */ ++ // Sync priv->gpgkeys + g_strfreev(priv->gpgkeys); +- priv->gpgkeys = NULL; +- +- g_auto(GStrv) gpgkeys; +- gpgkeys = g_key_file_get_string_list(priv->keyfile, repoId, "gpgkey", NULL, NULL); +- +- if (gpgkeys) { +- // gpgkeys can be ['value1', 'value2, value3'] therefore we first join to have 'value1, value2, value3' +- g_autofree gchar * tmp_strval = g_strjoinv(",", gpgkeys); +- +- auto & bindGpgkeys = priv->repo->getConfig()->optBinds().at("gpgkey"); +- bindGpgkeys.newString(libdnf::Option::Priority::REPOCONFIG, tmp_strval); +- +- auto & repoGpgkeys = priv->repo->getConfig()->gpgkey(); +- if (!repoGpgkeys.getValue().empty()){ +- auto len = repoGpgkeys.getValue().size(); +- priv->gpgkeys = g_new0(char *, len + 1); +- for (size_t i = 0; i < len; ++i) { +- priv->gpgkeys[i] = g_strdup(repoGpgkeys.getValue()[i].c_str()); +- } +- } else { +- /* Canonicalize the empty list to NULL for ease of checking elsewhere */ +- g_strfreev(static_cast(g_steal_pointer(&priv->gpgkeys))); ++ auto & repoGpgkeys = conf->gpgkey().getValue(); ++ if (!repoGpgkeys.empty()){ ++ auto len = repoGpgkeys.size(); ++ priv->gpgkeys = g_new0(char *, len + 1); ++ for (size_t i = 0; i < len; ++i) { ++ priv->gpgkeys[i] = g_strdup(repoGpgkeys[i].c_str()); + } ++ } else { ++ priv->gpgkeys = NULL; + } + +- if (g_key_file_has_key(priv->keyfile, repoId, "gpgcheck", NULL)) { +- auto gpgcheck_pkgs = dnf_repo_get_boolean(priv->keyfile, repoId, "gpgcheck"); +- priv->repo->getConfig()->gpgcheck().set(libdnf::Option::Priority::REPOCONFIG, gpgcheck_pkgs); +- } +- +- if (g_key_file_has_key(priv->keyfile, repoId, "repo_gpgcheck", NULL)) { +- auto gpgcheck_md = dnf_repo_get_boolean(priv->keyfile, repoId, "repo_gpgcheck"); +- priv->repo->getConfig()->repo_gpgcheck().set(libdnf::Option::Priority::REPOCONFIG, gpgcheck_md); +- } ++ /* gpgkey is optional for gpgcheck=1, but required for repo_gpgcheck=1 */ + auto gpgcheck_md = priv->repo->getConfig()->repo_gpgcheck().getValue(); + if (gpgcheck_md && priv->gpgkeys == NULL) { + g_set_error_literal(error, +@@ -1199,35 +1144,19 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + if (!lr_handle_setopt(priv->repo_handle, error, LRO_GPGCHECK, (long)gpgcheck_md)) + return FALSE; + +- auto & repoExcludepkgs = priv->repo->getConfig()->excludepkgs(); +- repoExcludepkgs.set(libdnf::Option::Priority::REPOCONFIG, ""); +- +- auto & bindExcludepkgs = priv->repo->getConfig()->optBinds().at("excludepkgs"); +- if (auto excludepkgs = g_key_file_get_string(priv->keyfile, repoId, "exclude", NULL)) { +- bindExcludepkgs.newString(libdnf::Option::Priority::REPOCONFIG, excludepkgs); +- g_free(excludepkgs); +- } +- if (auto excludepkgs = g_key_file_get_string(priv->keyfile, repoId, "excludepkgs", NULL)) { +- bindExcludepkgs.newString(libdnf::Option::Priority::REPOCONFIG, excludepkgs); +- g_free(excludepkgs); +- } +- ++ // Sync priv->exclude_packages + g_strfreev(priv->exclude_packages); +- if (!repoExcludepkgs.getValue().empty()) { +- auto len = repoExcludepkgs.getValue().size(); ++ auto & repoExcludepkgs = conf->excludepkgs().getValue(); ++ if (!repoExcludepkgs.empty()) { ++ auto len = repoExcludepkgs.size(); + priv->exclude_packages = g_new0(char *, len + 1); + for (size_t i = 0; i < len; ++i) { +- priv->exclude_packages[i] = g_strdup(repoExcludepkgs.getValue()[i].c_str()); ++ priv->exclude_packages[i] = g_strdup(repoExcludepkgs[i].c_str()); + } + } else { + priv->exclude_packages = NULL; + } + +- if (auto includepkgs = g_key_file_get_string(priv->keyfile, repoId, "includepkgs", NULL)) { +- priv->repo->getConfig()->includepkgs().set(libdnf::Option::Priority::REPOCONFIG, includepkgs); +- g_free(includepkgs); +- } +- + /* proxy is optional */ + proxy = g_key_file_get_string(priv->keyfile, repoId, "proxy", NULL); + auto repoProxy = proxy ? (strcasecmp(proxy, "_none_") == 0 ? NULL : proxy) + +From c6afbb4f93eee480c68201297e9c5c7afdf05dd3 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Wed, 2 Dec 2020 13:26:51 +0100 +Subject: [PATCH 5/9] [context] Fix: "cost" and "metadata_expire" repository + options + +Changes in dnf_repo_set_keyfile_data(): +Removed the dnf_repo_set_cost() call. +Removed the "metadata_expire" parsing and dnf_repo_set_metadata_expire() call. + +The options were set earlier. The function calls were redundant and +set the priority to the wrong RUNTIME value. +--- + libdnf/dnf-repo.cpp | 103 -------------------------------------------- + 1 file changed, 103 deletions(-) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index 2837580f7..61d496750 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -816,93 +816,6 @@ dnf_repo_set_metadata_expire(DnfRepo *repo, guint metadata_expire) + priv->repo->getConfig()->metadata_expire().set(libdnf::Option::Priority::RUNTIME, metadata_expire); + } + +-/** +- * dnf_repo_parse_time_from_str +- * @expression: a expression to be parsed +- * @out_parsed_time: (out): return location for parsed time +- * @error: error item +- * +- * Parse String into an integer value of seconds, or a human +- * readable variation specifying days, hours, minutes or seconds +- * until something happens. Note that due to historical president +- * -1 means "never", so this accepts that and allows +- * the word never, too. +- * +- * Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never. +- * Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y. +- +- * Returns: integer value in seconds +- **/ +- +-static gboolean +-dnf_repo_parse_time_from_str(const gchar *expression, guint *out_parsed_time, GError **error) +-{ +- gint multiplier; +- gdouble parsed_time; +- gchar *endptr = NULL; +- +- if (!g_strcmp0(expression, "")) { +- g_set_error_literal(error, +- DNF_ERROR, +- DNF_ERROR_FILE_INVALID, +- "no metadata value specified"); +- return FALSE; +- } +- +- if (g_strcmp0(expression, "-1") == 0 || +- g_strcmp0(expression,"never") == 0) { +- *out_parsed_time = G_MAXUINT; +- return TRUE; /* Note early return */ +- } +- +- gchar last_char = expression[ strlen(expression) - 1 ]; +- +- /* check if the input ends with h, m ,d ,s as units */ +- if (g_ascii_isalpha(last_char)) { +- if (last_char == 'h') +- multiplier = 60 * 60; +- else if (last_char == 's') +- multiplier = 1; +- else if (last_char == 'm') +- multiplier = 60; +- else if (last_char == 'd') +- multiplier = 60 * 60 * 24; +- else { +- g_set_error(error, DNF_ERROR, DNF_ERROR_FILE_INVALID, +- "unknown unit %c", last_char); +- return FALSE; +- } +- } +- else +- multiplier = 1; +- +- /* convert expression into a double*/ +- parsed_time = g_ascii_strtod(expression, &endptr); +- +- /* failed to parse */ +- if (expression == endptr) { +- g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, +- "failed to parse time: %s", expression); +- return FALSE; +- } +- +- /* time can not be below zero */ +- if (parsed_time < 0) { +- g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, +- "seconds value must not be negative %s",expression ); +- return FALSE; +- } +- +- /* time too large */ +- if (parsed_time > G_MAXDOUBLE || (parsed_time * multiplier) > G_MAXUINT){ +- g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, +- "time too large"); +- return FALSE; +- } +- +- *out_parsed_time = (guint) (parsed_time * multiplier); +- return TRUE; +-} + /** + * dnf_repo_get_username_password_string: + */ +@@ -1005,8 +918,6 @@ static gboolean + dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + { + DnfRepoPrivate *priv = GET_PRIVATE(repo); +- guint cost; +- g_autofree gchar *metadata_expire_str = NULL; + g_autofree gchar *mirrorlist = NULL; + g_autofree gchar *mirrorlisturl = NULL; + g_autofree gchar *metalinkurl = NULL; +@@ -1024,11 +935,6 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + // Reload repository configuration from keyfile. + dnf_repo_conf_from_gkeyfile(*conf, repoId, priv->keyfile); + +- /* cost is optional */ +- cost = g_key_file_get_integer(priv->keyfile, repoId, "cost", NULL); +- if (cost != 0) +- dnf_repo_set_cost(repo, cost); +- + /* baseurl is optional; if missing, unset it */ + g_auto(GStrv) baseurls = NULL; + auto & repoBaseurls = conf->baseurl().getValue(); +@@ -1042,15 +948,6 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + if (!lr_handle_setopt(priv->repo_handle, error, LRO_URLS, baseurls)) + return FALSE; + +- /* metadata_expire is optional, if shown, we parse the string to add the time */ +- metadata_expire_str = g_key_file_get_string(priv->keyfile, repoId, "metadata_expire", NULL); +- if (metadata_expire_str) { +- guint metadata_expire; +- if (!dnf_repo_parse_time_from_str(metadata_expire_str, &metadata_expire, error)) +- return FALSE; +- dnf_repo_set_metadata_expire(repo, metadata_expire); +- } +- + /* the "mirrorlist" entry could be either a real mirrorlist, or a metalink entry */ + mirrorlist = g_key_file_get_string(priv->keyfile, repoId, "mirrorlist", NULL); + if (mirrorlist) { + +From b11ac5204dc4c7048a7b6880813f2f9b1d8eb242 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Wed, 2 Dec 2020 13:21:35 +0100 +Subject: [PATCH 6/9] [context] Fix: username, password, proxy, proxy_username, + proxy_password + +- Uses global configuration options when they are not defined + in the repository configuration. +- proxy_username and proxy_password is urlEncoded before passing to librepo. +--- + libdnf/dnf-repo.cpp | 78 ++++++++++++++++++++++++++------------------- + 1 file changed, 46 insertions(+), 32 deletions(-) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index 61d496750..005721ef6 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -817,18 +817,22 @@ dnf_repo_set_metadata_expire(DnfRepo *repo, guint metadata_expire) + } + + /** +- * dnf_repo_get_username_password_string: +- */ +-static gchar * +-dnf_repo_get_username_password_string(const gchar *user, const gchar *pass) +-{ +- if (user == NULL && pass == NULL) +- return NULL; +- if (user != NULL && pass == NULL) +- return g_strdup(user); +- if (user == NULL && pass != NULL) +- return g_strdup_printf(":%s", pass); +- return g_strdup_printf("%s:%s", user, pass); ++* @brief Format user password string ++* ++* Returns user and password in user:password form. If encode 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 libdnf::urlEncode(user) + ":" + libdnf::urlEncode(passwd); ++ else ++ return user + ":" + passwd; + } + + static gboolean +@@ -921,11 +925,8 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + g_autofree gchar *mirrorlist = NULL; + g_autofree gchar *mirrorlisturl = NULL; + g_autofree gchar *metalinkurl = NULL; +- g_autofree gchar *proxy = NULL; +- g_autofree gchar *pwd = NULL; +- g_autofree gchar *usr = NULL; +- g_autofree gchar *usr_pwd = NULL; +- g_autofree gchar *usr_pwd_proxy = NULL; ++ std::string tmp_str; ++ const char *tmp_cstr; + + auto repoId = priv->repo->getId().c_str(); + g_debug("setting keyfile data for %s", repoId); +@@ -1054,26 +1055,39 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + priv->exclude_packages = NULL; + } + +- /* proxy is optional */ +- proxy = g_key_file_get_string(priv->keyfile, repoId, "proxy", NULL); +- auto repoProxy = proxy ? (strcasecmp(proxy, "_none_") == 0 ? NULL : proxy) +- : dnf_context_get_http_proxy(priv->context); +- if (!lr_handle_setopt(priv->repo_handle, error, LRO_PROXY, repoProxy)) ++ tmp_str = conf->proxy().getValue(); ++ tmp_cstr = tmp_str.empty() ? dnf_context_get_http_proxy(priv->context) : tmp_str.c_str(); ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_PROXY, tmp_cstr)) + return FALSE; + +- /* both parts of the proxy auth are optional */ +- usr = g_key_file_get_string(priv->keyfile, repoId, "proxy_username", NULL); +- pwd = g_key_file_get_string(priv->keyfile, repoId, "proxy_password", NULL); +- usr_pwd_proxy = dnf_repo_get_username_password_string(usr, pwd); +- if (!lr_handle_setopt(priv->repo_handle, error, LRO_PROXYUSERPWD, usr_pwd_proxy)) ++ // setup proxy username and password ++ tmp_cstr = NULL; ++ if (!conf->proxy_username().empty()) { ++ tmp_str = conf->proxy_username().getValue(); ++ if (!tmp_str.empty()) { ++ if (conf->proxy_password().empty()) { ++ g_set_error(error, DNF_ERROR, DNF_ERROR_FILE_INVALID, ++ "repo '%s': 'proxy_username' is set but not 'proxy_password'", repoId); ++ return FALSE; ++ } ++ tmp_str = formatUserPassString(tmp_str, conf->proxy_password().getValue(), true); ++ tmp_cstr = tmp_str.c_str(); ++ } ++ } ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_PROXYUSERPWD, tmp_cstr)) + return FALSE; + +- /* both parts of the HTTP auth are optional */ +- usr = g_key_file_get_string(priv->keyfile, repoId, "username", NULL); +- pwd = g_key_file_get_string(priv->keyfile, repoId, "password", NULL); +- usr_pwd = dnf_repo_get_username_password_string(usr, pwd); +- if (!lr_handle_setopt(priv->repo_handle, error, LRO_USERPWD, usr_pwd)) ++ // setup username and password ++ tmp_cstr = NULL; ++ tmp_str = conf->username().getValue(); ++ if (!tmp_str.empty()) { ++ // TODO Use URL encoded form, needs support in librepo ++ tmp_str = formatUserPassString(tmp_str, conf->password().getValue(), false); ++ tmp_cstr = tmp_str.c_str(); ++ } ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_USERPWD, tmp_cstr)) + return FALSE; ++ + return TRUE; + } + + +From fd07b29ccaec2648cfc050122e16e4846d7ac4be Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Wed, 2 Dec 2020 13:53:45 +0100 +Subject: [PATCH 7/9] [context] Add support for options: minrate, throttle, + bandwidth, timeout + +--- + libdnf/dnf-repo.cpp | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index 005721ef6..c6dd027be 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -1055,6 +1055,35 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + priv->exclude_packages = NULL; + } + ++ auto minrate = conf->minrate().getValue(); ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_LOWSPEEDLIMIT, static_cast(minrate))) ++ return FALSE; ++ ++ auto maxspeed = conf->throttle().getValue(); ++ if (maxspeed > 0 && maxspeed <= 1) ++ maxspeed *= conf->bandwidth().getValue(); ++ if (maxspeed != 0 && maxspeed < minrate) { ++ g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FILE_INVALID, ++ "Maximum download speed is lower than minimum. " ++ "Please change configuration of minrate or throttle"); ++ return FALSE; ++ } ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_MAXSPEED, static_cast(maxspeed))) ++ return FALSE; ++ ++ long timeout = conf->timeout().getValue(); ++ if (timeout > 0) { ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_CONNECTTIMEOUT, timeout)) ++ return FALSE; ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_LOWSPEEDTIME, timeout)) ++ return FALSE; ++ } else { ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT)) ++ return FALSE; ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_LOWSPEEDTIME, LRO_LOWSPEEDTIME_DEFAULT)) ++ return FALSE; ++ } ++ + tmp_str = conf->proxy().getValue(); + tmp_cstr = tmp_str.empty() ? dnf_context_get_http_proxy(priv->context) : tmp_str.c_str(); + if (!lr_handle_setopt(priv->repo_handle, error, LRO_PROXY, tmp_cstr)) + +From c484de218699dff834fb32133cf502b2d0c64162 Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Thu, 3 Dec 2020 10:55:02 +0100 +Subject: [PATCH 8/9] [context] Remove g_key_file_get_string() from + dnf_repo_set_keyfile_data() + +Removes the remaining usage of g_key_file_get_string() from +dnf_repo_set_keyfile_data(). Use the values from ConfigRepo instead. +--- + libdnf/dnf-repo.cpp | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index c6dd027be..c5c50d55c 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -922,9 +922,6 @@ static gboolean + dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + { + DnfRepoPrivate *priv = GET_PRIVATE(repo); +- g_autofree gchar *mirrorlist = NULL; +- g_autofree gchar *mirrorlisturl = NULL; +- g_autofree gchar *metalinkurl = NULL; + std::string tmp_str; + const char *tmp_cstr; + +@@ -949,20 +946,22 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + if (!lr_handle_setopt(priv->repo_handle, error, LRO_URLS, baseurls)) + return FALSE; + ++ const char *mirrorlisturl = NULL; ++ const char *metalinkurl = NULL; ++ + /* the "mirrorlist" entry could be either a real mirrorlist, or a metalink entry */ +- mirrorlist = g_key_file_get_string(priv->keyfile, repoId, "mirrorlist", NULL); +- if (mirrorlist) { +- if (strstr(mirrorlist, "metalink")) +- metalinkurl = static_cast(g_steal_pointer(&mirrorlist)); ++ tmp_cstr = conf->mirrorlist().empty() ? NULL : conf->mirrorlist().getValue().c_str(); ++ if (tmp_cstr) { ++ if (strstr(tmp_cstr, "metalink")) ++ metalinkurl = tmp_cstr; + else /* it really is a mirrorlist */ +- mirrorlisturl = static_cast(g_steal_pointer(&mirrorlist)); ++ mirrorlisturl = tmp_cstr; + } + + /* let "metalink" entry override metalink-as-mirrorlist entry */ +- if (g_key_file_has_key(priv->keyfile, repoId, "metalink", NULL)) { +- g_free(metalinkurl); +- metalinkurl = g_key_file_get_string(priv->keyfile, repoId, "metalink", NULL); +- } ++ tmp_cstr = conf->metalink().empty() ? NULL : conf->metalink().getValue().c_str(); ++ if (tmp_cstr) ++ metalinkurl = tmp_cstr; + + /* now set the final values (or unset them) */ + if (!lr_handle_setopt(priv->repo_handle, error, LRO_MIRRORLISTURL, mirrorlisturl)) + +From ce44d3dced4b800e3b7f80556fac1daf7e7fa49d Mon Sep 17 00:00:00 2001 +From: Jaroslav Rohel +Date: Thu, 3 Dec 2020 11:43:18 +0100 +Subject: [PATCH 9/9] [context] Remove the extra gpgkey member from + DnfRepoPrivate + +The value stored in ConfigRepo can be used directly. +--- + libdnf/dnf-repo.cpp | 81 ++++++++++++++++++--------------------------- + 1 file changed, 33 insertions(+), 48 deletions(-) + +diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp +index c5c50d55c..193999902 100644 +--- a/libdnf/dnf-repo.cpp ++++ b/libdnf/dnf-repo.cpp +@@ -63,7 +63,6 @@ + typedef struct + { + DnfRepoEnabled enabled; +- gchar **gpgkeys; + gchar **exclude_packages; + gchar *filename; /* /etc/yum.repos.d/updates.repo */ + gchar *location; /* /var/cache/PackageKit/metadata/fedora */ +@@ -97,7 +96,6 @@ dnf_repo_finalize(GObject *object) + DnfRepoPrivate *priv = GET_PRIVATE(repo); + + g_free(priv->filename); +- g_strfreev(priv->gpgkeys); + g_strfreev(priv->exclude_packages); + g_free(priv->location_tmp); + g_free(priv->location); +@@ -225,16 +223,13 @@ gchar ** + dnf_repo_get_public_keys(DnfRepo *repo) + { + DnfRepoPrivate *priv = GET_PRIVATE(repo); +- g_autoptr(GPtrArray) ret = g_ptr_array_new(); +- for (char **iter = priv->gpgkeys; iter && *iter; iter++) { +- const char *key = *iter; +- g_autofree gchar *key_bn = g_path_get_basename(key); +- /* transfer ownership to ptrarray */ +- g_ptr_array_add(ret, g_build_filename(priv->location, key_bn, NULL)); ++ const auto & keys = priv->repo->getConfig()->gpgkey().getValue(); ++ gchar **ret = g_new0(gchar *, keys.size() + 1); ++ for (size_t i = 0; i < keys.size(); ++i) { ++ g_autofree gchar *key_bn = g_path_get_basename(keys[i].c_str()); ++ ret[i] = g_build_filename(priv->location, key_bn, NULL); + } +- g_ptr_array_add(ret, NULL); +- /* transfer ownership of container and elements to caller */ +- return (gchar**)g_ptr_array_free(static_cast(g_steal_pointer(&ret)), FALSE); ++ return ret; + } + + /** +@@ -1014,22 +1009,9 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + dnf_repo_set_location_tmp(repo, tmp->str); + } + +- // Sync priv->gpgkeys +- g_strfreev(priv->gpgkeys); +- auto & repoGpgkeys = conf->gpgkey().getValue(); +- if (!repoGpgkeys.empty()){ +- auto len = repoGpgkeys.size(); +- priv->gpgkeys = g_new0(char *, len + 1); +- for (size_t i = 0; i < len; ++i) { +- priv->gpgkeys[i] = g_strdup(repoGpgkeys[i].c_str()); +- } +- } else { +- priv->gpgkeys = NULL; +- } +- + /* gpgkey is optional for gpgcheck=1, but required for repo_gpgcheck=1 */ +- auto gpgcheck_md = priv->repo->getConfig()->repo_gpgcheck().getValue(); +- if (gpgcheck_md && priv->gpgkeys == NULL) { ++ auto repo_gpgcheck = conf->repo_gpgcheck().getValue(); ++ if (repo_gpgcheck && conf->gpgkey().getValue().empty()) { + g_set_error_literal(error, + DNF_ERROR, + DNF_ERROR_FILE_INVALID, +@@ -1038,7 +1020,7 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, GError **error) + } + + /* XXX: setopt() expects a long, so we need a long on the stack */ +- if (!lr_handle_setopt(priv->repo_handle, error, LRO_GPGCHECK, (long)gpgcheck_md)) ++ if (!lr_handle_setopt(priv->repo_handle, error, LRO_GPGCHECK, (long)repo_gpgcheck)) + return FALSE; + + // Sync priv->exclude_packages +@@ -1750,28 +1732,31 @@ dnf_repo_update(DnfRepo *repo, + goto out; + } + +- if (priv->gpgkeys && +- (priv->repo->getConfig()->repo_gpgcheck().getValue() || priv->repo->getConfig()->gpgcheck().getValue())) { +- for (char **iter = priv->gpgkeys; iter && *iter; iter++) { +- const char *gpgkey = *iter; +- g_autofree char *gpgkey_name = g_path_get_basename(gpgkey); +- g_autofree char *key_tmp = g_build_filename(priv->location_tmp, gpgkey_name, NULL); +- +- /* download and import public key */ +- if ((g_str_has_prefix(gpgkey, "https://") || +- g_str_has_prefix(gpgkey, "file://"))) { +- g_debug("importing public key %s", gpgkey); +- +- ret = dnf_repo_download_import_public_key(repo, gpgkey, key_tmp, error); +- if (!ret) +- goto out; +- } ++ { ++ const auto & gpgkeys = priv->repo->getConfig()->gpgkey().getValue(); ++ if (!gpgkeys.empty() && ++ (priv->repo->getConfig()->repo_gpgcheck().getValue() || priv->repo->getConfig()->gpgcheck().getValue())) { ++ for (const auto & key : gpgkeys) { ++ const char *gpgkey = key.c_str(); ++ g_autofree char *gpgkey_name = g_path_get_basename(gpgkey); ++ g_autofree char *key_tmp = g_build_filename(priv->location_tmp, gpgkey_name, NULL); ++ ++ /* download and import public key */ ++ if ((g_str_has_prefix(gpgkey, "https://") || ++ g_str_has_prefix(gpgkey, "file://"))) { ++ g_debug("importing public key %s", gpgkey); ++ ++ ret = dnf_repo_download_import_public_key(repo, gpgkey, key_tmp, error); ++ if (!ret) ++ goto out; ++ } + +- /* do we autoimport this into librpm? */ +- if ((flags & DNF_REPO_UPDATE_FLAG_IMPORT_PUBKEY) > 0) { +- ret = dnf_repo_add_public_key(repo, key_tmp, error); +- if (!ret) +- goto out; ++ /* do we autoimport this into librpm? */ ++ if ((flags & DNF_REPO_UPDATE_FLAG_IMPORT_PUBKEY) > 0) { ++ ret = dnf_repo_add_public_key(repo, key_tmp, error); ++ if (!ret) ++ goto out; ++ } + } + } + } diff --git a/SOURCES/0005-Allow-loading-incomplete-cache-and-loading-ext-solv-files-without-its-repodata.patch b/SOURCES/0005-Allow-loading-incomplete-cache-and-loading-ext-solv-files-without-its-repodata.patch new file mode 100644 index 0000000..30421d4 --- /dev/null +++ b/SOURCES/0005-Allow-loading-incomplete-cache-and-loading-ext-solv-files-without-its-repodata.patch @@ -0,0 +1,134 @@ +From 816d18d826dc7134e553eae28f4aaca9a27e2307 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Mon, 2 Nov 2020 11:43:19 +0100 +Subject: [PATCH 1/2] Allow loading ext metadata even if only cache (solv) is + present + +If we have a valid (checksum matches with repomd) solv file for +requested type of metadata allow using it even if we no longer have the +original xml metadata. +--- + libdnf/dnf-sack.cpp | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/libdnf/dnf-sack.cpp b/libdnf/dnf-sack.cpp +index 6a43f01e3..608103d18 100644 +--- a/libdnf/dnf-sack.cpp ++++ b/libdnf/dnf-sack.cpp +@@ -371,20 +371,9 @@ load_ext(DnfSack *sack, HyRepo hrepo, _hy_repo_repodata which_repodata, + auto repoImpl = libdnf::repoGetImpl(hrepo); + Repo *repo = repoImpl->libsolvRepo; + const char *name = repo->name; +- auto fn = hrepo->getMetadataPath(which_filename); + FILE *fp; + gboolean done = FALSE; + +- /* nothing set */ +- if (fn.empty()) { +- g_set_error (error, +- DNF_ERROR, +- DNF_ERROR_NO_CAPABILITY, +- _("no %1$s string for %2$s"), +- which_filename, name); +- return FALSE; +- } +- + char *fn_cache = dnf_sack_give_cache_fn(sack, name, suffix); + fp = fopen(fn_cache, "r"); + assert(libdnf::repoGetImpl(hrepo)->checksum); +@@ -416,6 +405,17 @@ load_ext(DnfSack *sack, HyRepo hrepo, _hy_repo_repodata which_repodata, + if (done) + return TRUE; + ++ auto fn = hrepo->getMetadataPath(which_filename); ++ /* nothing set */ ++ if (fn.empty()) { ++ g_set_error (error, ++ DNF_ERROR, ++ DNF_ERROR_NO_CAPABILITY, ++ _("no %1$s string for %2$s"), ++ which_filename, name); ++ return FALSE; ++ } ++ + fp = solv_xfopen(fn.c_str(), "r"); + if (fp == NULL) { + g_set_error (error, + +From aa2a372158f1b264708f960f387218deea17ef2a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Thu, 10 Dec 2020 14:21:03 +0100 +Subject: [PATCH 2/2] Extend repo loadCache method with ignoreMissing parameter + +This allows loading even incomplete cache of xml files, only repomd.xml +is requried. + += changelog = +msg: Extend repo loadCache method with ignoreMissing parameter to allow +loading incomplete xml cache (repomd.xml is required). +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + VERSION.cmake | 2 +- + libdnf.spec | 2 +- + libdnf/repo/Repo-private.hpp | 2 +- + libdnf/repo/Repo.cpp | 8 ++++++-- + libdnf/repo/Repo.hpp | 2 +- + 5 files changed, 10 insertions(+), 6 deletions(-) + +diff --git a/libdnf/repo/Repo-private.hpp b/libdnf/repo/Repo-private.hpp +index 1e4ea4d20..c2ce369dc 100644 +--- a/libdnf/repo/Repo-private.hpp ++++ b/libdnf/repo/Repo-private.hpp +@@ -111,7 +111,7 @@ class Repo::Impl { + ~Impl(); + + bool load(); +- bool loadCache(bool throwExcept); ++ bool loadCache(bool throwExcept, bool ignoreMissing=false); + void downloadMetadata(const std::string & destdir); + bool isInSync(); + void fetch(const std::string & destdir, std::unique_ptr && h); +diff --git a/libdnf/repo/Repo.cpp b/libdnf/repo/Repo.cpp +index 34539e1ee..84702c294 100644 +--- a/libdnf/repo/Repo.cpp ++++ b/libdnf/repo/Repo.cpp +@@ -379,7 +379,7 @@ std::string Repo::getLocalBaseurl() const + } + + bool Repo::load() { return pImpl->load(); } +-bool Repo::loadCache(bool throwExcept) { return pImpl->loadCache(throwExcept); } ++bool Repo::loadCache(bool throwExcept, bool ignoreMissing) { return pImpl->loadCache(throwExcept, ignoreMissing); } + 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; } +@@ -963,11 +963,15 @@ std::unique_ptr Repo::Impl::lrHandlePerform(LrHandle * handle, const s + return result; + } + +-bool Repo::Impl::loadCache(bool throwExcept) ++bool Repo::Impl::loadCache(bool throwExcept, bool ignoreMissing) + { + std::unique_ptr h(lrHandleInitLocal()); + std::unique_ptr r; + ++ if (ignoreMissing) { ++ handleSetOpt(h.get(), LRO_IGNOREMISSING, 1L); ++ } ++ + // Fetch data + try { + r = lrHandlePerform(h.get(), getCachedir(), conf->repo_gpgcheck().getValue()); +diff --git a/libdnf/repo/Repo.hpp b/libdnf/repo/Repo.hpp +index eeec651c3..be376f60c 100644 +--- a/libdnf/repo/Repo.hpp ++++ b/libdnf/repo/Repo.hpp +@@ -167,7 +167,7 @@ struct Repo { + * @return true if fresh metadata were downloaded, false otherwise. + */ + bool load(); +- bool loadCache(bool throwExcept); ++ bool loadCache(bool throwExcept, bool ignoreMissing=false); + void downloadMetadata(const std::string & destdir); + bool getUseIncludes() const; + void setUseIncludes(bool enabled); diff --git a/SOURCES/0006-Add-new-option-module-stream-switch.patch b/SOURCES/0006-Add-new-option-module-stream-switch.patch new file mode 100644 index 0000000..1a9cfd9 --- /dev/null +++ b/SOURCES/0006-Add-new-option-module-stream-switch.patch @@ -0,0 +1,53 @@ +From a1c96ecae6f2052407345a66293710109323de3a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Tue, 21 Jul 2020 15:37:05 +0200 +Subject: [PATCH] Add new option module_stream_switch + += changelog = +msg: Add new options module_stream_switch +type: enhancement +--- + libdnf/conf/ConfigMain.cpp | 3 +++ + libdnf/conf/ConfigMain.hpp | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/libdnf/conf/ConfigMain.cpp b/libdnf/conf/ConfigMain.cpp +index 1ffd3b336..abfc2082b 100644 +--- a/libdnf/conf/ConfigMain.cpp ++++ b/libdnf/conf/ConfigMain.cpp +@@ -278,6 +278,7 @@ class ConfigMain::Impl { + OptionBool downloadonly{false}; // runtime only option + OptionBool ignorearch{false}; + OptionString module_platform_id{nullptr}; ++ OptionBool module_stream_switch{false}; + + OptionString user_agent{getUserAgent()}; + OptionBool countme{false}; +@@ -434,6 +435,7 @@ ConfigMain::Impl::Impl(Config & owner) + owner.optBinds().add("comment", comment); + owner.optBinds().add("ignorearch", ignorearch); + owner.optBinds().add("module_platform_id", module_platform_id); ++ owner.optBinds().add("module_stream_switch", module_stream_switch); + owner.optBinds().add("user_agent", user_agent); + owner.optBinds().add("countme", countme); + owner.optBinds().add("protect_running_kernel", protect_running_kernel); +@@ -569,6 +571,7 @@ OptionBool & ConfigMain::downloadonly() { return pImpl->downloadonly; } + OptionBool & ConfigMain::ignorearch() { return pImpl->ignorearch; } + + OptionString & ConfigMain::module_platform_id() { return pImpl->module_platform_id; } ++OptionBool & ConfigMain::module_stream_switch() { return pImpl->module_stream_switch; } + OptionString & ConfigMain::user_agent() { return pImpl->user_agent; } + OptionBool & ConfigMain::countme() { return pImpl->countme; } + OptionBool & ConfigMain::protect_running_kernel() {return pImpl->protect_running_kernel; } +diff --git a/libdnf/conf/ConfigMain.hpp b/libdnf/conf/ConfigMain.hpp +index 226c74d50..835e1fc65 100644 +--- a/libdnf/conf/ConfigMain.hpp ++++ b/libdnf/conf/ConfigMain.hpp +@@ -125,6 +125,7 @@ class ConfigMain : public Config { + OptionBool & ignorearch(); + + OptionString & module_platform_id(); ++ OptionBool & module_stream_switch(); + OptionString & user_agent(); + OptionBool & countme(); + OptionBool & protect_running_kernel(); diff --git a/SOURCES/0007-Fix-removal-step-during-modular-enable-in-context-part.patch b/SOURCES/0007-Fix-removal-step-during-modular-enable-in-context-part.patch new file mode 100644 index 0000000..d43bfcc --- /dev/null +++ b/SOURCES/0007-Fix-removal-step-during-modular-enable-in-context-part.patch @@ -0,0 +1,23 @@ +From 831d023c3c6fb4a28903cb3170efdd9f85645e1a Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Fri, 20 Nov 2020 16:30:17 +0100 +Subject: [PATCH] Fix removal step during modular enable in context part + +It resolves `free(): double free detected in tcache 2` +--- + libdnf/dnf-context.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libdnf/dnf-context.cpp b/libdnf/dnf-context.cpp +index 069267119..bc4a15b68 100644 +--- a/libdnf/dnf-context.cpp ++++ b/libdnf/dnf-context.cpp +@@ -3087,7 +3087,7 @@ static std::vectorfirst != enabledOrDefaultStream) { +- stream_dict.erase(iter); ++ stream_dict.erase(iter++); + } else { + ++iter; + } diff --git a/SPECS/libdnf.spec b/SPECS/libdnf.spec index 65f8aac..0fd799b 100644 --- a/SPECS/libdnf.spec +++ b/SPECS/libdnf.spec @@ -1,12 +1,14 @@ %global libsolv_version 0.7.7 %global libmodulemd_version 2.5.0 %global librepo_version 1.12.0 -%global dnf_conflict 4.2.23 +%global dnf_conflict 4.3.0 %global swig_version 3.0.12 %global libdnf_major_version 0 -%global libdnf_minor_version 48 +%global libdnf_minor_version 55 %global libdnf_micro_version 0 +%define __cmake_in_source_build 1 + # set sphinx package name according to distro %global requires_python2_sphinx python2-sphinx %global requires_python3_sphinx python3-sphinx @@ -54,12 +56,19 @@ Name: libdnf Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version} -Release: 2%{?dist} +Release: 6%{?dist} Summary: Library providing simplified C and Python API to libsolv License: LGPLv2+ URL: https://github.com/rpm-software-management/libdnf Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz -Patch1: 0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch +Patch0: 0001-Better-msgs-if-basecachedir-or-proxy-password-isn-t-set-RhBug-1888946.patch +Patch1: 0002-modules-Add-special-handling-for-src-artifacts-RhBug-1809314.patch +Patch2: 0003-Avoid-multilib-file-conflict-in-config.h-RhBug-1918818.patch +Patch3: 0004-context-improve-retrieving-repository-configuration.patch +Patch4: 0005-Allow-loading-incomplete-cache-and-loading-ext-solv-files-without-its-repodata.patch +Patch5: 0006-Add-new-option-module-stream-switch.patch +Patch6: 0007-Fix-removal-step-during-modular-enable-in-context-part.patch + BuildRequires: cmake BuildRequires: gcc @@ -307,6 +316,42 @@ popd %endif %changelog +* Fri Feb 12 2021 Nicola Sella - 0.55.0-6 +- Fix removal step during modular enable in context part + +* Thu Feb 11 2021 Nicola Sella - 0.55.0-5 +- Add new option module_stream_switch + +* Mon Feb 08 2021 Nicola Sella - 0.55.0-4 +- [context] improve retrieving repository configuration +- Allow loading incomplete cache and loading ext solv files without its repodata + +* Fri Jan 29 2021 Nicola Sella - 0.55.0-3 +- Avoid multilib file conflict in config.h (RhBug:1918818) +- [modules] Add special handling for src artifacts (RhBug:1809314) + +* Fri Jan 15 2021 Nicola Sella - 0.55.0-2 +- Better msgs if "basecachedir" or "proxy_password" isn't set (RhBug:1888946) + +* Mon Nov 09 2020 Nicola Sella - 0.55.0-1 +- Add vendor to dnf API (RhBug:1876561) +- Add formatting function for solver error +- Add error types in ModulePackageContainer +- Implement module enable for context part +- Improve string formatting for translation +- Remove redundant printf and change logging info to notice (RhBug:1827424) +- Add allow_vendor_change option (RhBug:1788371) (RhBug:1788371) + +* Thu Aug 20 2020 Nicola Sella - 0.48.0-5 +- [covscan] Handle exception in a python binding by the proper function (RhBug:1870492) + +* Tue Jul 28 2020 Marek Blaha - 0.48.0-4 +- Update translations (RhBug:1820548) + +* Fri Jul 17 2020 Nicola Sella - 0.48.0-3 +- Add log file level main config option (RhBug:1802074) +- Accept '==' as an operator in reldeps (RhBug:1847946) + * Wed Jun 10 2020 Ales Matej - 0.48.0-2 - [history] Fix dnf history rollback when a package was removed (RhBug:1683134)