From 12dc26051c82074867bf782de650c912db23d60e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 28 Apr 2026 12:40:36 +0200 Subject: [PATCH] Add a header for the backend interface functions --- backend/dl.cc | 12 +- backend/dl.h | 4 +- backend/job.cc | 864 ++++++++++++++++++++++++++++++++- backend/job.h | 26 +- backend/pk-backend-slack.cc | 931 ------------------------------------ backend/pkgtools.cc | 16 +- backend/pkgtools.h | 4 +- backend/slackpkg.cc | 14 +- backend/slackpkg.h | 4 +- backend/utils.cc | 12 +- backend/utils.h | 8 +- 11 files changed, 894 insertions(+), 1001 deletions(-) delete mode 100644 backend/pk-backend-slack.cc diff --git a/backend/dl.cc b/backend/dl.cc index 06698c9..305b4e0 100644 --- a/backend/dl.cc +++ b/backend/dl.cc @@ -8,10 +8,10 @@ #include "dl.h" #include "utils.h" -namespace slack { - +namespace katja +{ /** - * slack::Dl::collect_cache_info: + * katja::Dl::collect_cache_info: * @tmpl: temporary directory for downloading the files. * * Download files needed to get the information like the list of packages @@ -59,7 +59,7 @@ Dl::collect_cache_info (const char *tmpl) noexcept } /** - * slack::Dl::generate_cache: + * katja::Dl::generate_cache: * @job_data: A #JobData. * @tmpl: temporary directory for downloading the files. * @@ -253,7 +253,7 @@ Dl::~Dl () noexcept } /** - * slack::Dl::Dl: + * katja::Dl::Dl: * @name: Repository name. * @mirror: Repository mirror. * @order: Repository order. @@ -262,7 +262,7 @@ Dl::~Dl () noexcept * * Constructor. * - * Return value: New #slack::Dl. + * Return value: New #katja::Dl. **/ Dl::Dl (const char *name, const char *mirror, std::uint8_t order, const char *blacklist, char *index_file) noexcept diff --git a/backend/dl.h b/backend/dl.h index 47b4d2a..260acc3 100644 --- a/backend/dl.h +++ b/backend/dl.h @@ -9,8 +9,8 @@ #include "pkgtools.h" #include "utils.h" -namespace slack { - +namespace katja +{ class Dl final : public Pkgtools { public: diff --git a/backend/job.cc b/backend/job.cc index d1c6c81..b9e1656 100644 --- a/backend/job.cc +++ b/backend/job.cc @@ -3,36 +3,184 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#include "job.h" - -#include -#include "utils.h" +#include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include "job.h" +#include "dl.h" +#include "pkgtools.h" +#include "slackpkg.h" +#include "utils.h" -namespace slack { +static GSList *repos = nullptr; -/** - * Returns true if the package isn't filtered out by the filters, false - * otherwise. - */ -bool -filter_package (PkBitfield filters, bool is_installed) +void pk_backend_initialize(GKeyFile *conf) { - if ((is_installed && !pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED)) - || (!is_installed && !pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED))) + char *path, **groups; + int ret; + gushort i; + gsize groups_len; + GFile *conf_file; + GFileInfo *file_info; + GKeyFile *key_conf; + GError *err = nullptr; + void *repo = nullptr; + sqlite3 *db; + sqlite3_stmt *stmt; + + g_debug("backend: initialize"); + curl_global_init(CURL_GLOBAL_DEFAULT); + + /* Open the database. We will need it to save the time the configuration file was last modified. */ + path = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); + if (sqlite3_open(path, &db) != SQLITE_OK) { - return true; + g_error("%s: %s", path, sqlite3_errmsg(db)); } - return false; + g_free(path); + + /* Read the configuration file */ + key_conf = g_key_file_new(); + path = g_build_filename(SYSCONFDIR, "PackageKit", "Slackware.conf", nullptr); + g_key_file_load_from_file(key_conf, path, G_KEY_FILE_NONE, &err); + if (err) + { + g_error("%s: %s", path, err->message); + g_error_free(err); + } + + conf_file = g_file_new_for_path(path); + if (!(file_info = g_file_query_info(conf_file, + "time::modified-usec", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + nullptr, + &err))) + { + g_error("%s", err->message); + g_error_free(err); + } + + if ((ret = sqlite3_prepare_v2(db, + "UPDATE cache_info SET value = ? WHERE key LIKE 'last_modification'", + -1, + &stmt, + nullptr)) == SQLITE_OK) { + ret = sqlite3_bind_int(stmt, 1, g_file_info_get_attribute_uint32(file_info, "time::modified-usec")); + if (ret == SQLITE_OK) + { + ret = sqlite3_step(stmt); + } + sqlite3_finalize(stmt); + } + if ((ret != SQLITE_OK) && (ret != SQLITE_DONE)) + { + g_error("%s: %s", path, sqlite3_errstr(ret)); + } + else if (!sqlite3_changes(db)) + { + g_error("Failed to update database: %s", path); + } + + g_object_unref(file_info); + g_object_unref(conf_file); + sqlite3_close_v2(db); + g_free(path); + + /* Initialize an object for each well-formed repository */ + groups = g_key_file_get_groups(key_conf, &groups_len); + for (i = 0; i < groups_len; i++) + { + char *blacklist = g_key_file_get_string(key_conf, groups[i], "Blacklist", nullptr); + char *mirror = g_key_file_get_string(key_conf, groups[i], "Mirror", nullptr); + + if (g_key_file_has_key(key_conf, groups[i], "Priority", nullptr)) + { + repo = new Slackpkg (groups[i], mirror, i + 1, blacklist, + g_key_file_get_string_list(key_conf, groups[i], "Priority", nullptr, nullptr)); + } + else if (g_key_file_has_key(key_conf, groups[i], "IndexFile", nullptr)) + { + repo = new Dl (groups[i], mirror, i + 1, blacklist, + g_key_file_get_string(key_conf, groups[i], "IndexFile", nullptr)); + } + + if (repo) + { + repos = g_slist_append(repos, repo); + } + else + { + g_free(groups[i]); + } + g_free(mirror); + g_free(blacklist); + } + g_free(groups); + + g_key_file_free(key_conf); } +void pk_backend_destroy() +{ + g_debug("backend: destroy"); + + for (GSList *l = repos; l; l = g_slist_next (l)) + { + delete static_cast (l->data); + } + + g_slist_free (repos); + curl_global_cleanup (); } -void +void pk_backend_start_job(PkBackendJob *job) +{ + char *db_filename = nullptr; + JobData *job_data = g_new0(JobData, 1); + + db_filename = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); + if (sqlite3_open(db_filename, &job_data->db) == SQLITE_OK) { /* Some SQLite settings */ + sqlite3_exec(job_data->db, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr); + } + else + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, + "%s: %s", + db_filename, + sqlite3_errmsg(job_data->db)); + goto out; + } + + pk_backend_job_set_user_data(job, job_data); + +out: + g_free(db_filename); +} + +void pk_backend_stop_job(PkBackendJob *job) +{ + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + if (job_data->curl) + { + curl_easy_cleanup(job_data->curl); + } + + sqlite3_close(job_data->db); + g_free(job_data); + pk_backend_job_set_user_data(job, nullptr); +} + +static void pk_backend_search_thread (PkBackendJob *job, GVariant *params, void *user_data) { - auto job_data = reinterpret_cast (pk_backend_job_get_user_data (job)); + auto job_data = reinterpret_cast (pk_backend_job_get_user_data (job)); char **vals; PkBitfield filters; @@ -51,17 +199,17 @@ pk_backend_search_thread (PkBackendJob *job, GVariant *params, void *user_data) /* Now we're ready to output all packages */ while (sqlite3_step (stmt) == SQLITE_ROW) { - slack::Info info = slack::is_installed ( + katja::Info info = katja::is_installed ( reinterpret_cast (sqlite3_column_text (stmt, 2))); - if ((info == slack::Info::installed || info == slack::Info::updating) - && slack::filter_package (filters, true)) + if ((info == katja::Info::installed || info == katja::Info::updating) + && !pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED)) { - pk_backend_job_package (job, slack::Info::installed, + pk_backend_job_package (job, katja::Info::installed, reinterpret_cast (sqlite3_column_text (stmt, 0)), reinterpret_cast (sqlite3_column_text (stmt, 1))); } - else if (info == slack::Info::installing && slack::filter_package (filters, false)) + else if (info == katja::Info::installing && !pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED)) { pk_backend_job_package(job, PK_INFO_ENUM_AVAILABLE, reinterpret_cast (sqlite3_column_text (stmt, 0)), @@ -79,3 +227,673 @@ pk_backend_search_thread (PkBackendJob *job, GVariant *params, void *user_data) sqlite3_free (query); g_free (search); } + +void pk_backend_search_names(PkBackendJob *job, char **values) +{ + pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "name", nullptr); +} + +void pk_backend_search_details(PkBackendJob *job, char **values) +{ + pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "desc", nullptr); +} + +void pk_backend_search_groups(PkBackendJob *job, char **values) +{ + pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "cat", nullptr); +} + +void pk_backend_search_files(PkBackendJob *job, char **values) +{ + char *search; + char *query; + sqlite3_stmt *stmt; + Info ret; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + pk_backend_job_set_percentage(job, 0); + + search = g_strjoinv("%", values); + + query = sqlite3_mprintf("SELECT (p.name || ';' || p.ver || ';' || p.arch || ';' || r.repo), p.summary, " + "p.full_name FROM filelist AS f NATURAL JOIN pkglist AS p NATURAL JOIN repos AS r " + "WHERE f.filename LIKE '%%%q%%' GROUP BY f.full_name", search); + + if ((sqlite3_prepare_v2(job_data->db, query, -1, &stmt, nullptr) == SQLITE_OK)) + { + /* Now we're ready to output all packages */ + while (sqlite3_step(stmt) == SQLITE_ROW) + { + ret = is_installed((char*) sqlite3_column_text(stmt, 2)); + if ((ret == Info::installed) || (ret == Info::updating)) + { + pk_backend_job_package(job, PK_INFO_ENUM_INSTALLED, + (char*) sqlite3_column_text(stmt, 0), + (char*) sqlite3_column_text(stmt, 1)); + } + else if (ret == PK_INFO_ENUM_INSTALLING) + { + pk_backend_job_package(job, PK_INFO_ENUM_AVAILABLE, + (char*) sqlite3_column_text(stmt, 0), + (char*) sqlite3_column_text(stmt, 1)); + } + } + sqlite3_finalize(stmt); + } + else + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + } + sqlite3_free(query); + g_free(search); + + pk_backend_job_set_percentage(job, 100); +} + +void pk_backend_get_details(PkBackendJob *job, char **package_ids) +{ + char *homepage = nullptr; + char** tokens; + gsize i; + GString *desc; + GRegex *expr; + GMatchInfo *match_info; + GError *err = nullptr; + sqlite3_stmt *stmt; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT p.desc, p.cat, p.uncompressed FROM pkglist AS p NATURAL JOIN repos AS r " + "WHERE name LIKE @name AND r.repo LIKE @repo AND ext NOT LIKE 'obsolete'", + -1, + &stmt, + nullptr) != SQLITE_OK)) { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + goto out; + } + + tokens = pk_package_id_split(package_ids[0]); + sqlite3_bind_text(stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); + g_strfreev(tokens); + + if (sqlite3_step(stmt) != SQLITE_ROW) + goto out; + + desc = g_string_new((char *) sqlite3_column_text(stmt, 0)); + + /* Regular expression for searching a homepage */ + expr = g_regex_new("(?:http|ftp):\\/\\/[[:word:]\\/\\-\\.]+[[:word:]\\/](?=\\.?$)", + (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), + (GRegexMatchFlags)(0), + &err); + if (err) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_UNKNOWN, "%s", err->message); + g_error_free(err); + goto out; + } + if (g_regex_match(expr, desc->str, (GRegexMatchFlags)0, &match_info)) + { + homepage = g_match_info_fetch(match_info, 0); /* URL */ + /* Remove the last sentence with the copied URL */ + for (i = desc->len - 1; i > 0; i--) + { + if ((desc->str[i - 1] == '.') && (desc->str[i] == ' ')) + { + g_string_truncate(desc, i); + break; + } + } + g_match_info_free(match_info); + } + g_regex_unref(expr); + + /* Ready */ + pk_backend_job_details(job, + package_ids[0], + nullptr, + nullptr, + pk_group_enum_from_string((char *) sqlite3_column_text(stmt, 1)), + desc->str, + homepage, + sqlite3_column_int(stmt, 2), + G_MAXUINT64); + + g_free(homepage); + if (desc) + { + g_string_free(desc, true); + } + +out: + sqlite3_finalize(stmt); +} + +void pk_backend_resolve(PkBackendJob *job, char **packages) +{ + char **val; + sqlite3_stmt *stmt; + PkInfoEnum ret; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + pk_backend_job_set_percentage(job, 0); + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT (p1.name || ';' || p1.ver || ';' || p1.arch || ';' || r.repo), p1.summary, " + "p1.full_name FROM pkglist AS p1 NATURAL JOIN repos AS r " + "WHERE p1.name LIKE @search AND p1.repo_order = " + "(SELECT MIN(p2.repo_order) FROM pkglist AS p2 WHERE p2.name = p1.name GROUP BY p2.name)", + -1, + &stmt, + nullptr) == SQLITE_OK)) { + /* Output packages matching each pattern */ + for (val = packages; *val; val++) + { + sqlite3_bind_text(stmt, 1, *val, -1, SQLITE_TRANSIENT); + + while (sqlite3_step(stmt) == SQLITE_ROW) + { + if ((ret == Info::installed) || (ret == Info::updating)) + { + pk_backend_job_package(job, PK_INFO_ENUM_INSTALLED, + (char*) sqlite3_column_text(stmt, 0), + (char*) sqlite3_column_text(stmt, 1)); + } + else if (ret == PK_INFO_ENUM_INSTALLING) + { + pk_backend_job_package(job, PK_INFO_ENUM_AVAILABLE, + (char*) sqlite3_column_text(stmt, 0), + (char*) sqlite3_column_text(stmt, 1)); + } + } + + sqlite3_clear_bindings(stmt); + sqlite3_reset(stmt); + } + sqlite3_finalize(stmt); + } else { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + } + + pk_backend_job_set_percentage(job, 100); +} + +void pk_backend_download_packages(PkBackendJob *job, char **package_ids, const char *directory) +{ + char *path, *to_strv[] = {nullptr, nullptr}; + unsigned i; + sqlite3_stmt *stmt; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT summary, (full_name || '.' || ext) FROM pkglist NATURAL JOIN repos " + "WHERE name LIKE @name AND ver LIKE @ver AND arch LIKE @arch AND repo LIKE @repo", + -1, + &stmt, + nullptr) != SQLITE_OK)) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + goto out; + } + + for (i = 0; package_ids[i]; ++i) + { + char **tokens = pk_package_id_split(package_ids[i]); + + sqlite3_bind_text(stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, tokens[PK_PACKAGE_ID_VERSION], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 3, tokens[PK_PACKAGE_ID_ARCH], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 4, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); + if (sqlite3_step(stmt) == SQLITE_ROW) + { + GSList *repo; + if ((repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo))) + { + pk_backend_job_package(job, PK_INFO_ENUM_DOWNLOADING, + package_ids[i], + (char *) sqlite3_column_text(stmt, 0)); + static_cast (repo->data)->download (job, + directory, tokens[PK_PACKAGE_ID_NAME]); + path = g_build_filename(directory, (char *) sqlite3_column_text(stmt, 1), nullptr); + to_strv[0] = path; + pk_backend_job_files(job, nullptr, to_strv); + g_free(path); + } + } + sqlite3_clear_bindings(stmt); + sqlite3_reset(stmt); + g_strfreev(tokens); + } + +out: + sqlite3_finalize(stmt); +} + +void pk_backend_install_packages(PkBackendJob *job, char **package_ids) +{ + char *dest_dir_name; + unsigned i; + gdouble percent_step; + GSList *install_list = nullptr, *l; + sqlite3_stmt *pkglist_stmt = nullptr, *collection_stmt = nullptr; + PkInfoEnum ret; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT summary, cat FROM pkglist NATURAL JOIN repos " + "WHERE name LIKE @name AND ver LIKE @ver AND arch LIKE @arch AND repo LIKE @repo", + -1, + &pkglist_stmt, + nullptr) != SQLITE_OK) || + (sqlite3_prepare_v2(job_data->db, + "SELECT (c.collection_pkg || ';' || p.ver || ';' || p.arch || ';' || r.repo), p.summary, " + "p.full_name, p.ext FROM collections AS c " + "JOIN pkglist AS p ON c.collection_pkg = p.name " + "JOIN repos AS r ON p.repo_order = r.repo_order " + "WHERE c.name LIKE @name AND r.repo LIKE @repo", + -1, + &collection_stmt, + nullptr) != SQLITE_OK)) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + goto out; + } + + for (i = 0; package_ids[i]; i++) + { + char **tokens = pk_package_id_split(package_ids[i]); + sqlite3_bind_text(pkglist_stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(pkglist_stmt, 2, tokens[PK_PACKAGE_ID_VERSION], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(pkglist_stmt, 3, tokens[PK_PACKAGE_ID_ARCH], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(pkglist_stmt, 4, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); + + if (sqlite3_step(pkglist_stmt) == SQLITE_ROW) + { + /* If it isn't a collection */ + if (g_strcmp0((char *) sqlite3_column_text(pkglist_stmt, 1), "collections")) + { + install_list = g_slist_append(install_list, g_strdup(package_ids[i])); + } + else + { + sqlite3_bind_text(collection_stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(collection_stmt, 2, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); + + while (sqlite3_step(collection_stmt) == SQLITE_ROW) + { + ret = is_installed((char*) sqlite3_column_text(collection_stmt, 2)); + if ((ret == Info::installing) || (ret == Info::updating)) + { + install_list = g_slist_append(install_list, + g_strdup((char *) sqlite3_column_text(collection_stmt, 0))); + } + } + sqlite3_clear_bindings(collection_stmt); + sqlite3_reset(collection_stmt); + } + } + + sqlite3_clear_bindings(pkglist_stmt); + sqlite3_reset(pkglist_stmt); + g_strfreev(tokens); + } + + if (install_list) + { + /* / 2 means total percentage for installing and for downloading */ + percent_step = 100.0 / g_slist_length(install_list) / 2; + + /* Download the packages */ + dest_dir_name = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "downloads", nullptr); + for (l = install_list, i = 0; l; l = g_slist_next(l), i++) + { + char **tokens; + GSList *repo; + + pk_backend_job_set_percentage(job, percent_step * i); + tokens = pk_package_id_split((char *)(l->data)); + repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); + + if (repo) + { + static_cast (repo->data)->download (job, + dest_dir_name, tokens[PK_PACKAGE_ID_NAME]); + } + g_strfreev(tokens); + } + g_free(dest_dir_name); + + /* Install the packages */ + for (l = install_list; l; l = g_slist_next(l), i++) + { + char **tokens; + GSList *repo; + + pk_backend_job_set_percentage(job, percent_step * i); + tokens = pk_package_id_split((char *)(l->data)); + repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); + + if (repo) + { + static_cast (repo->data)->install (job, tokens[PK_PACKAGE_ID_NAME]); + } + g_strfreev(tokens); + } + } + g_slist_free_full(install_list, g_free); + +out: + sqlite3_finalize(pkglist_stmt); + sqlite3_finalize(collection_stmt); +} + +void pk_backend_remove_packages(PkBackendJob *job, char **package_ids) +{ + char *cmd_line; + unsigned i; + gdouble percent_step; + GError *err = nullptr; + + /* Add percent_step percents per removed package */ + percent_step = 100.0 / g_strv_length(package_ids); + for (i = 0; package_ids[i]; i++) + { + char **tokens; + + pk_backend_job_set_percentage(job, percent_step * i); + tokens = pk_package_id_split(package_ids[i]); + cmd_line = g_strconcat("/sbin/removepkg ", tokens[PK_PACKAGE_ID_NAME], nullptr); + + /* Pkgtools return always 0 */ + g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, &err); + + g_free(cmd_line); + g_strfreev(tokens); + + if (err) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_PACKAGE_FAILED_TO_REMOVE, "%s", err->message); + g_error_free(err); + + return; + } + + pk_backend_job_set_percentage(job, 100); + } +} + +void pk_backend_get_updates(PkBackendJob *job) +{ + char *pkg_id, *full_name, *desc; + const char *pkg_metadata_filename; + GFile *pkg_metadata_dir; + GFileEnumerator *pkg_metadata_enumerator; + GFileInfo *pkg_metadata_file_info; + GError *err = nullptr; + sqlite3_stmt *stmt; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT p1.full_name, p1.name, p1.ver, p1.arch, r.repo, p1.summary, p1.ext " + "FROM pkglist AS p1 NATURAL JOIN repos AS r " + "WHERE p1.name LIKE @name AND p1.repo_order = " + "(SELECT MIN(p2.repo_order) FROM pkglist AS p2 WHERE p2.name = p1.name GROUP BY p2.name)", + -1, + &stmt, + nullptr) != SQLITE_OK)) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); + goto out; + } + + /* Read the package metadata directory and comprare all installed packages with ones in the cache */ + pkg_metadata_dir = g_file_new_for_path("/var/log/packages"); + pkg_metadata_enumerator = g_file_enumerate_children(pkg_metadata_dir, "standard::name", + G_FILE_QUERY_INFO_NONE, + nullptr, + &err); + g_object_unref(pkg_metadata_dir); + if (err) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, "/var/log/packages: %s", err->message); + g_error_free(err); + goto out; + } + + while ((pkg_metadata_file_info = g_file_enumerator_next_file(pkg_metadata_enumerator, nullptr, nullptr))) + { + char **tokens; + + pkg_metadata_filename = g_file_info_get_name(pkg_metadata_file_info); + tokens = split_package_name(pkg_metadata_filename); + + /* Select the package from the database */ + sqlite3_bind_text(stmt, 1, tokens[0], -1, SQLITE_TRANSIENT); + + /* If there are more packages with the same name, remember the one from the + * repository with the lowest order. */ + if ((sqlite3_step(stmt) == SQLITE_ROW) + || g_slist_find_custom(repos, ((char *) sqlite3_column_text(stmt, 4)), cmp_repo)) + { + + full_name = g_strdup((char *) sqlite3_column_text(stmt, 0)); + + if (!g_strcmp0((char *) sqlite3_column_text(stmt, 6), "obsolete")) + { /* Remove if obsolete */ + pkg_id = pk_package_id_build(tokens[PK_PACKAGE_ID_NAME], + tokens[PK_PACKAGE_ID_VERSION], + tokens[PK_PACKAGE_ID_ARCH], + "obsolete"); + /* TODO: + * 1: Use the repository name instead of "obsolete" above and check in pk_backend_update_packages() + if the package is obsolete or not + * 2: Get description from /var/log/packages, not from the database */ + desc = g_strdup((char *) sqlite3_column_text(stmt, 5)); + + pk_backend_job_package(job, PK_INFO_ENUM_REMOVING, pkg_id, desc); + + g_free(desc); + g_free(pkg_id); + } + else if (g_strcmp0(pkg_metadata_filename, full_name)) + { /* Update available */ + pkg_id = pk_package_id_build((char *) sqlite3_column_text(stmt, 1), + (char *) sqlite3_column_text(stmt, 2), + (char *) sqlite3_column_text(stmt, 3), + (char *) sqlite3_column_text(stmt, 4)); + desc = g_strdup((char *) sqlite3_column_text(stmt, 5)); + + pk_backend_job_package(job, PK_INFO_ENUM_NORMAL, pkg_id, desc); + + g_free(desc); + g_free(pkg_id); + } + g_free(full_name); + } + + sqlite3_clear_bindings(stmt); + sqlite3_reset(stmt); + + g_strfreev(tokens); + g_object_unref(pkg_metadata_file_info); + } + g_object_unref(pkg_metadata_enumerator); + +out: + sqlite3_finalize(stmt); +} + +void pk_backend_update_packages(PkBackendJob *job, char **package_ids) +{ + char *dest_dir_name, *cmd_line; + unsigned i; + + /* Download the packages */ + dest_dir_name = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "downloads", nullptr); + for (i = 0; package_ids[i]; i++) + { + char **tokens = pk_package_id_split(package_ids[i]); + + if (g_strcmp0(tokens[PK_PACKAGE_ID_DATA], "obsolete")) + { + GSList *repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); + + if (repo) + { + static_cast (repo->data)->download (job, + dest_dir_name, tokens[PK_PACKAGE_ID_NAME]); + } + } + + g_strfreev(tokens); + } + g_free(dest_dir_name); + + /* Install the packages */ + for (i = 0; package_ids[i]; i++) + { + char **tokens = pk_package_id_split(package_ids[i]); + + if (g_strcmp0(tokens[PK_PACKAGE_ID_DATA], "obsolete")) + { + GSList *repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); + + if (repo) + { + static_cast (repo->data)->install (job, + tokens[PK_PACKAGE_ID_NAME]); + } + } + else + { + /* Remove obsolete package + * TODO: Removing should be an independent operation (not during installing updates) */ + cmd_line = g_strconcat("/sbin/removepkg ", tokens[PK_PACKAGE_ID_NAME], nullptr); + g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, nullptr); + g_free(cmd_line); + } + g_strfreev(tokens); + } +} + +void pk_backend_refresh_cache(PkBackendJob *job, bool force) +{ + char *tmp_dir_name, *db_err, *path = nullptr; + int ret; + GSList *file_list = nullptr; + GFile *db_file = nullptr; + GFileInfo *file_info = nullptr; + GError *err = nullptr; + sqlite3_stmt *stmt = nullptr; + auto job_data = static_cast (pk_backend_job_get_user_data(job)); + + /* Create temporary directory */ + tmp_dir_name = g_dir_make_tmp("PackageKit.XXXXXX", &err); + if (!tmp_dir_name) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_INTERNAL_ERROR, "%s", err->message); + g_error_free(err); + return; + } + + /* Force the complete cache refresh if the read configuration file is newer than the metadata cache */ + if (!force) + { + path = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); + db_file = g_file_new_for_path(path); + file_info = g_file_query_info(db_file, "time::modified-usec", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &err); + if (err) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, "%s: %s", path, err->message); + g_error_free(err); + goto out; + } + ret = sqlite3_prepare_v2(job_data->db, + "SELECT value FROM cache_info WHERE key LIKE 'last_modification'", + -1, + &stmt, + nullptr); + if ((ret != SQLITE_OK) || ((ret = sqlite3_step(stmt)) != SQLITE_ROW)) + { + pk_backend_job_error_code(job, + PK_ERROR_ENUM_NO_CACHE, + "%s: %s", + path, + sqlite3_errstr(ret)); + goto out; + } + if ((std::uint32_t) sqlite3_column_int(stmt, 0) > g_file_info_get_attribute_uint32(file_info, "time::modified-usec")) + { + force = true; + } + } + if (force) /* It should empty all tables */ + { + if (sqlite3_exec(job_data->db, "DELETE FROM repos", nullptr, 0, &db_err) != SQLITE_OK) + { + pk_backend_job_error_code(job, PK_ERROR_ENUM_INTERNAL_ERROR, "%s", db_err); + sqlite3_free(db_err); + goto out; + } + } + + // Get list of files that should be downloaded. + for (GSList *l = repos; l; l = g_slist_next(l)) + { + file_list = g_slist_concat(file_list, + static_cast (l->data)->collect_cache_info (tmp_dir_name)); + } + + /* Download repository */ + for (GSList *l = file_list; l; l = g_slist_next(l)) + { + get_file(&job_data->curl, static_cast (l->data)[0], + static_cast (l->data)[1]); + } + g_slist_free_full(file_list, (GDestroyNotify)g_strfreev); + + /* Refresh cache */ + for (GSList *l = repos; l; l = g_slist_next(l)) + { + static_cast (l->data)->generate_cache (job, tmp_dir_name); + } + +out: + sqlite3_finalize(stmt); + if (file_info) + { + g_object_unref(file_info); + } + if (db_file) + { + g_object_unref(db_file); + } + g_free(path); + + pk_directory_remove_contents(tmp_dir_name); + g_rmdir(tmp_dir_name); + g_free(tmp_dir_name); +} + +void pk_backend_get_update_detail(PkBackendJob *job, char **package_ids) +{ + unsigned i; + + for (i = 0; package_ids[i] != nullptr; i++) + { + pk_backend_job_update_detail (job, + package_ids[i], + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + PK_RESTART_ENUM_NONE, + nullptr, + nullptr, + PK_UPDATE_STATE_ENUM_STABLE, + nullptr, + nullptr); + } +} diff --git a/backend/job.h b/backend/job.h index f99baee..15ef341 100644 --- a/backend/job.h +++ b/backend/job.h @@ -5,17 +5,27 @@ */ #pragma once -#include -#include +using namespace katja; -namespace slack { +void pk_backend_initialize(GKeyFile *conf); +void pk_backend_destroy(); -bool filter_package (PkBitfield filters, bool is_installed); +void pk_backend_start_job(PkBackendJob *job); +void pk_backend_stop_job(PkBackendJob *job); -} +void pk_backend_search_names(PkBackendJob *job, char **values); +void pk_backend_search_details(PkBackendJob *job, char **values); +void pk_backend_search_groups(PkBackendJob *job, char **values); +void pk_backend_search_files(PkBackendJob *job, char **values); -extern "C" { +void pk_backend_get_details(PkBackendJob *job, char **package_ids); +void pk_backend_resolve(PkBackendJob *job, char **packages); -void pk_backend_search_thread (PkBackendJob *job, GVariant *params, void *user_data); +void pk_backend_download_packages(PkBackendJob *job, char **package_ids, const char *directory); +void pk_backend_install_packages(PkBackendJob *job, char **package_ids); +void pk_backend_remove_packages(PkBackendJob *job, char **package_ids); -} +void pk_backend_get_updates(PkBackendJob *job); +void pk_backend_update_packages(PkBackendJob *job, char **package_ids); +void pk_backend_refresh_cache(PkBackendJob *job, bool force); +void pk_backend_get_update_detail(PkBackendJob *job, char **package_ids); diff --git a/backend/pk-backend-slack.cc b/backend/pk-backend-slack.cc deleted file mode 100644 index 7f618a4..0000000 --- a/backend/pk-backend-slack.cc +++ /dev/null @@ -1,931 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "job.h" -#include "dl.h" -#include "pkgtools.h" -#include "slackpkg.h" -#include "utils.h" - -using namespace slack; - -static GSList *repos = nullptr; - -void pk_backend_initialize(GKeyFile *conf) -{ - char *path, **groups; - int ret; - gushort i; - gsize groups_len; - GFile *conf_file; - GFileInfo *file_info; - GKeyFile *key_conf; - GError *err = nullptr; - void *repo = nullptr; - sqlite3 *db; - sqlite3_stmt *stmt; - - g_debug("backend: initialize"); - curl_global_init(CURL_GLOBAL_DEFAULT); - - /* Open the database. We will need it to save the time the configuration file was last modified. */ - path = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); - if (sqlite3_open(path, &db) != SQLITE_OK) - { - g_error("%s: %s", path, sqlite3_errmsg(db)); - } - g_free(path); - - /* Read the configuration file */ - key_conf = g_key_file_new(); - path = g_build_filename(SYSCONFDIR, "PackageKit", "Slackware.conf", nullptr); - g_key_file_load_from_file(key_conf, path, G_KEY_FILE_NONE, &err); - if (err) - { - g_error("%s: %s", path, err->message); - g_error_free(err); - } - - conf_file = g_file_new_for_path(path); - if (!(file_info = g_file_query_info(conf_file, - "time::modified-usec", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - nullptr, - &err))) - { - g_error("%s", err->message); - g_error_free(err); - } - - if ((ret = sqlite3_prepare_v2(db, - "UPDATE cache_info SET value = ? WHERE key LIKE 'last_modification'", - -1, - &stmt, - nullptr)) == SQLITE_OK) { - ret = sqlite3_bind_int(stmt, 1, g_file_info_get_attribute_uint32(file_info, "time::modified-usec")); - if (ret == SQLITE_OK) - { - ret = sqlite3_step(stmt); - } - sqlite3_finalize(stmt); - } - if ((ret != SQLITE_OK) && (ret != SQLITE_DONE)) - { - g_error("%s: %s", path, sqlite3_errstr(ret)); - } - else if (!sqlite3_changes(db)) - { - g_error("Failed to update database: %s", path); - } - - g_object_unref(file_info); - g_object_unref(conf_file); - sqlite3_close_v2(db); - g_free(path); - - /* Initialize an object for each well-formed repository */ - groups = g_key_file_get_groups(key_conf, &groups_len); - for (i = 0; i < groups_len; i++) - { - char *blacklist = g_key_file_get_string(key_conf, groups[i], "Blacklist", nullptr); - char *mirror = g_key_file_get_string(key_conf, groups[i], "Mirror", nullptr); - - if (g_key_file_has_key(key_conf, groups[i], "Priority", nullptr)) - { - repo = new Slackpkg (groups[i], mirror, i + 1, blacklist, - g_key_file_get_string_list(key_conf, groups[i], "Priority", nullptr, nullptr)); - } - else if (g_key_file_has_key(key_conf, groups[i], "IndexFile", nullptr)) - { - repo = new Dl (groups[i], mirror, i + 1, blacklist, - g_key_file_get_string(key_conf, groups[i], "IndexFile", nullptr)); - } - - if (repo) - { - repos = g_slist_append(repos, repo); - } - else - { - g_free(groups[i]); - } - g_free(mirror); - g_free(blacklist); - } - g_free(groups); - - g_key_file_free(key_conf); -} - -void -pk_backend_destroy() -{ - g_debug("backend: destroy"); - - for (GSList *l = repos; l; l = g_slist_next (l)) - { - delete static_cast (l->data); - } - - g_slist_free (repos); - curl_global_cleanup (); -} - -void -pk_backend_start_job(PkBackendJob *job) -{ - char *db_filename = nullptr; - JobData *job_data = g_new0(JobData, 1); - - db_filename = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); - if (sqlite3_open(db_filename, &job_data->db) == SQLITE_OK) { /* Some SQLite settings */ - sqlite3_exec(job_data->db, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr); - } - else - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, - "%s: %s", - db_filename, - sqlite3_errmsg(job_data->db)); - goto out; - } - - pk_backend_job_set_user_data(job, job_data); - -out: - g_free(db_filename); -} - -void -pk_backend_stop_job(PkBackendJob *job) -{ - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - if (job_data->curl) - { - curl_easy_cleanup(job_data->curl); - } - - sqlite3_close(job_data->db); - g_free(job_data); - pk_backend_job_set_user_data(job, nullptr); -} - -void -pk_backend_search_names(PkBackendJob *job, char **values) -{ - pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "name", nullptr); -} - -void -pk_backend_search_details(PkBackendJob *job, char **values) -{ - pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "desc", nullptr); -} - -void -pk_backend_search_groups(PkBackendJob *job, char **values) -{ - pk_backend_job_thread_create(job, pk_backend_search_thread, (void *) "cat", nullptr); -} - -static void -pk_backend_search_files_thread(PkBackendJob *job, GVariant *params, void *user_data) -{ - char **vals, *search; - char *query; - sqlite3_stmt *stmt; - Info ret; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - pk_backend_job_set_percentage(job, 0); - - g_variant_get(params, "(t^a&s)", nullptr, &vals); - search = g_strjoinv("%", vals); - - query = sqlite3_mprintf("SELECT (p.name || ';' || p.ver || ';' || p.arch || ';' || r.repo), p.summary, " - "p.full_name FROM filelist AS f NATURAL JOIN pkglist AS p NATURAL JOIN repos AS r " - "WHERE f.filename LIKE '%%%q%%' GROUP BY f.full_name", search); - - if ((sqlite3_prepare_v2(job_data->db, query, -1, &stmt, nullptr) == SQLITE_OK)) - { - /* Now we're ready to output all packages */ - while (sqlite3_step(stmt) == SQLITE_ROW) - { - ret = is_installed((char*) sqlite3_column_text(stmt, 2)); - if ((ret == Info::installed) || (ret == Info::updating)) - { - pk_backend_job_package(job, PK_INFO_ENUM_INSTALLED, - (char*) sqlite3_column_text(stmt, 0), - (char*) sqlite3_column_text(stmt, 1)); - } - else if (ret == PK_INFO_ENUM_INSTALLING) - { - pk_backend_job_package(job, PK_INFO_ENUM_AVAILABLE, - (char*) sqlite3_column_text(stmt, 0), - (char*) sqlite3_column_text(stmt, 1)); - } - } - sqlite3_finalize(stmt); - } - else - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - } - sqlite3_free(query); - g_free(search); - - pk_backend_job_set_percentage(job, 100); -} - -void -pk_backend_search_files(PkBackendJob *job, char **values) -{ - pk_backend_job_thread_create(job, pk_backend_search_files_thread, nullptr, nullptr); -} - -static void -pk_backend_get_details_thread(PkBackendJob *job, char **pkg_ids, void *user_data) -{ - char *homepage = nullptr; - char** tokens; - gsize i; - GString *desc; - GRegex *expr; - GMatchInfo *match_info; - GError *err = nullptr; - sqlite3_stmt *stmt; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT p.desc, p.cat, p.uncompressed FROM pkglist AS p NATURAL JOIN repos AS r " - "WHERE name LIKE @name AND r.repo LIKE @repo AND ext NOT LIKE 'obsolete'", - -1, - &stmt, - nullptr) != SQLITE_OK)) { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - goto out; - } - - tokens = pk_package_id_split(pkg_ids[0]); - sqlite3_bind_text(stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); - g_strfreev(tokens); - - if (sqlite3_step(stmt) != SQLITE_ROW) - goto out; - - desc = g_string_new((char *) sqlite3_column_text(stmt, 0)); - - /* Regular expression for searching a homepage */ - expr = g_regex_new("(?:http|ftp):\\/\\/[[:word:]\\/\\-\\.]+[[:word:]\\/](?=\\.?$)", - (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), - (GRegexMatchFlags)(0), - &err); - if (err) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_UNKNOWN, "%s", err->message); - g_error_free(err); - goto out; - } - if (g_regex_match(expr, desc->str, (GRegexMatchFlags)0, &match_info)) - { - homepage = g_match_info_fetch(match_info, 0); /* URL */ - /* Remove the last sentence with the copied URL */ - for (i = desc->len - 1; i > 0; i--) - { - if ((desc->str[i - 1] == '.') && (desc->str[i] == ' ')) - { - g_string_truncate(desc, i); - break; - } - } - g_match_info_free(match_info); - } - g_regex_unref(expr); - - /* Ready */ - pk_backend_job_details(job, - pkg_ids[0], - nullptr, - nullptr, - pk_group_enum_from_string((char *) sqlite3_column_text(stmt, 1)), - desc->str, - homepage, - sqlite3_column_int(stmt, 2), - G_MAXUINT64); - - g_free(homepage); - if (desc) - { - g_string_free(desc, true); - } - -out: - sqlite3_finalize(stmt); -} - -void -pk_backend_get_details(PkBackendJob *job, char **package_ids) -{ - pk_backend_job_thread_create(job, pk_backend_get_details_thread, nullptr, nullptr); -} - -static void -pk_backend_resolve_thread(PkBackendJob *job, GVariant *params, void *user_data) -{ - char **vals, **val; - sqlite3_stmt *stmt; - PkInfoEnum ret; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - pk_backend_job_set_percentage(job, 0); - - g_variant_get(params, "(t^a&s)", nullptr, &vals); - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT (p1.name || ';' || p1.ver || ';' || p1.arch || ';' || r.repo), p1.summary, " - "p1.full_name FROM pkglist AS p1 NATURAL JOIN repos AS r " - "WHERE p1.name LIKE @search AND p1.repo_order = " - "(SELECT MIN(p2.repo_order) FROM pkglist AS p2 WHERE p2.name = p1.name GROUP BY p2.name)", - -1, - &stmt, - nullptr) == SQLITE_OK)) { - /* Output packages matching each pattern */ - for (val = vals; *val; val++) - { - sqlite3_bind_text(stmt, 1, *val, -1, SQLITE_TRANSIENT); - - while (sqlite3_step(stmt) == SQLITE_ROW) - { - if ((ret == Info::installed) || (ret == Info::updating)) - { - pk_backend_job_package(job, PK_INFO_ENUM_INSTALLED, - (char*) sqlite3_column_text(stmt, 0), - (char*) sqlite3_column_text(stmt, 1)); - } - else if (ret == PK_INFO_ENUM_INSTALLING) - { - pk_backend_job_package(job, PK_INFO_ENUM_AVAILABLE, - (char*) sqlite3_column_text(stmt, 0), - (char*) sqlite3_column_text(stmt, 1)); - } - } - - sqlite3_clear_bindings(stmt); - sqlite3_reset(stmt); - } - sqlite3_finalize(stmt); - } else { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - } - - pk_backend_job_set_percentage(job, 100); -} - -void -pk_backend_resolve(PkBackendJob *job, char **packages) -{ - pk_backend_job_thread_create(job, pk_backend_resolve_thread, nullptr, nullptr); -} - -static void -pk_backend_download_packages_thread(PkBackendJob *job, GVariant *params, void *user_data) -{ - char *dir_path, *path, **pkg_ids, *to_strv[] = {nullptr, nullptr}; - unsigned i; - sqlite3_stmt *stmt; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - g_variant_get(params, "(^a&ss)", &pkg_ids, &dir_path); - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT summary, (full_name || '.' || ext) FROM pkglist NATURAL JOIN repos " - "WHERE name LIKE @name AND ver LIKE @ver AND arch LIKE @arch AND repo LIKE @repo", - -1, - &stmt, - nullptr) != SQLITE_OK)) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - goto out; - } - - for (i = 0; pkg_ids[i]; ++i) - { - char **tokens = pk_package_id_split(pkg_ids[i]); - - sqlite3_bind_text(stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, tokens[PK_PACKAGE_ID_VERSION], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 3, tokens[PK_PACKAGE_ID_ARCH], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 4, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); - if (sqlite3_step(stmt) == SQLITE_ROW) - { - GSList *repo; - if ((repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo))) - { - pk_backend_job_package(job, PK_INFO_ENUM_DOWNLOADING, - pkg_ids[i], - (char *) sqlite3_column_text(stmt, 0)); - static_cast (repo->data)->download (job, - dir_path, tokens[PK_PACKAGE_ID_NAME]); - path = g_build_filename(dir_path, (char *) sqlite3_column_text(stmt, 1), nullptr); - to_strv[0] = path; - pk_backend_job_files(job, nullptr, to_strv); - g_free(path); - } - } - sqlite3_clear_bindings(stmt); - sqlite3_reset(stmt); - g_strfreev(tokens); - } - -out: - sqlite3_finalize(stmt); -} - -void -pk_backend_download_packages(PkBackendJob *job, char **package_ids, const char *directory) -{ - pk_backend_job_thread_create(job, pk_backend_download_packages_thread, nullptr, nullptr); -} - -static void -pk_backend_install_packages_thread(PkBackendJob *job, char **pkg_ids, void *user_data) -{ - char *dest_dir_name; - unsigned i; - gdouble percent_step; - GSList *install_list = nullptr, *l; - sqlite3_stmt *pkglist_stmt = nullptr, *collection_stmt = nullptr; - PkInfoEnum ret; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT summary, cat FROM pkglist NATURAL JOIN repos " - "WHERE name LIKE @name AND ver LIKE @ver AND arch LIKE @arch AND repo LIKE @repo", - -1, - &pkglist_stmt, - nullptr) != SQLITE_OK) || - (sqlite3_prepare_v2(job_data->db, - "SELECT (c.collection_pkg || ';' || p.ver || ';' || p.arch || ';' || r.repo), p.summary, " - "p.full_name, p.ext FROM collections AS c " - "JOIN pkglist AS p ON c.collection_pkg = p.name " - "JOIN repos AS r ON p.repo_order = r.repo_order " - "WHERE c.name LIKE @name AND r.repo LIKE @repo", - -1, - &collection_stmt, - nullptr) != SQLITE_OK)) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - goto out; - } - - for (i = 0; pkg_ids[i]; i++) - { - char **tokens = pk_package_id_split(pkg_ids[i]); - sqlite3_bind_text(pkglist_stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(pkglist_stmt, 2, tokens[PK_PACKAGE_ID_VERSION], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(pkglist_stmt, 3, tokens[PK_PACKAGE_ID_ARCH], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(pkglist_stmt, 4, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); - - if (sqlite3_step(pkglist_stmt) == SQLITE_ROW) - { - /* If it isn't a collection */ - if (g_strcmp0((char *) sqlite3_column_text(pkglist_stmt, 1), "collections")) - { - install_list = g_slist_append(install_list, g_strdup(pkg_ids[i])); - } - else - { - sqlite3_bind_text(collection_stmt, 1, tokens[PK_PACKAGE_ID_NAME], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(collection_stmt, 2, tokens[PK_PACKAGE_ID_DATA], -1, SQLITE_TRANSIENT); - - while (sqlite3_step(collection_stmt) == SQLITE_ROW) - { - ret = is_installed((char*) sqlite3_column_text(collection_stmt, 2)); - if ((ret == Info::installing) || (ret == Info::updating)) - { - install_list = g_slist_append(install_list, - g_strdup((char *) sqlite3_column_text(collection_stmt, 0))); - } - } - sqlite3_clear_bindings(collection_stmt); - sqlite3_reset(collection_stmt); - } - } - - sqlite3_clear_bindings(pkglist_stmt); - sqlite3_reset(pkglist_stmt); - g_strfreev(tokens); - } - - if (install_list) - { - /* / 2 means total percentage for installing and for downloading */ - percent_step = 100.0 / g_slist_length(install_list) / 2; - - /* Download the packages */ - dest_dir_name = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "downloads", nullptr); - for (l = install_list, i = 0; l; l = g_slist_next(l), i++) - { - char **tokens; - GSList *repo; - - pk_backend_job_set_percentage(job, percent_step * i); - tokens = pk_package_id_split((char *)(l->data)); - repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); - - if (repo) - { - static_cast (repo->data)->download (job, - dest_dir_name, tokens[PK_PACKAGE_ID_NAME]); - } - g_strfreev(tokens); - } - g_free(dest_dir_name); - - /* Install the packages */ - for (l = install_list; l; l = g_slist_next(l), i++) - { - char **tokens; - GSList *repo; - - pk_backend_job_set_percentage(job, percent_step * i); - tokens = pk_package_id_split((char *)(l->data)); - repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); - - if (repo) - { - static_cast (repo->data)->install (job, tokens[PK_PACKAGE_ID_NAME]); - } - g_strfreev(tokens); - } - } - g_slist_free_full(install_list, g_free); - -out: - sqlite3_finalize(pkglist_stmt); - sqlite3_finalize(collection_stmt); -} - -void -pk_backend_install_packages(PkBackendJob *job, char **package_ids) -{ - pk_backend_job_thread_create(job, pk_backend_install_packages_thread, nullptr, nullptr); -} - -static void -pk_backend_remove_packages_thread(PkBackendJob* job, char **pkg_ids) -{ - char *cmd_line; - unsigned i; - gdouble percent_step; - GError *err = nullptr; - - /* Add percent_step percents per removed package */ - percent_step = 100.0 / g_strv_length(pkg_ids); - for (i = 0; pkg_ids[i]; i++) - { - char **tokens; - - pk_backend_job_set_percentage(job, percent_step * i); - tokens = pk_package_id_split(pkg_ids[i]); - cmd_line = g_strconcat("/sbin/removepkg ", tokens[PK_PACKAGE_ID_NAME], nullptr); - - /* Pkgtools return always 0 */ - g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, &err); - - g_free(cmd_line); - g_strfreev(tokens); - - if (err) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_PACKAGE_FAILED_TO_REMOVE, "%s", err->message); - g_error_free(err); - - return; - } - - pk_backend_job_set_percentage(job, 100); - } -} - -void -pk_backend_remove_packages(PkBackendJob *job, char **package_ids) -{ - pk_backend_job_thread_create(job, pk_backend_remove_packages_thread, nullptr, nullptr); -} - -static void -pk_backend_get_updates_thread(PkBackendJob *job, void *user_data) -{ - char *pkg_id, *full_name, *desc; - const char *pkg_metadata_filename; - GFile *pkg_metadata_dir; - GFileEnumerator *pkg_metadata_enumerator; - GFileInfo *pkg_metadata_file_info; - GError *err = nullptr; - sqlite3_stmt *stmt; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT p1.full_name, p1.name, p1.ver, p1.arch, r.repo, p1.summary, p1.ext " - "FROM pkglist AS p1 NATURAL JOIN repos AS r " - "WHERE p1.name LIKE @name AND p1.repo_order = " - "(SELECT MIN(p2.repo_order) FROM pkglist AS p2 WHERE p2.name = p1.name GROUP BY p2.name)", - -1, - &stmt, - nullptr) != SQLITE_OK)) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_CANNOT_GET_FILELIST, "%s", sqlite3_errmsg(job_data->db)); - goto out; - } - - /* Read the package metadata directory and comprare all installed packages with ones in the cache */ - pkg_metadata_dir = g_file_new_for_path("/var/log/packages"); - pkg_metadata_enumerator = g_file_enumerate_children(pkg_metadata_dir, "standard::name", - G_FILE_QUERY_INFO_NONE, - nullptr, - &err); - g_object_unref(pkg_metadata_dir); - if (err) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, "/var/log/packages: %s", err->message); - g_error_free(err); - goto out; - } - - while ((pkg_metadata_file_info = g_file_enumerator_next_file(pkg_metadata_enumerator, nullptr, nullptr))) - { - char **tokens; - - pkg_metadata_filename = g_file_info_get_name(pkg_metadata_file_info); - tokens = split_package_name(pkg_metadata_filename); - - /* Select the package from the database */ - sqlite3_bind_text(stmt, 1, tokens[0], -1, SQLITE_TRANSIENT); - - /* If there are more packages with the same name, remember the one from the - * repository with the lowest order. */ - if ((sqlite3_step(stmt) == SQLITE_ROW) - || g_slist_find_custom(repos, ((char *) sqlite3_column_text(stmt, 4)), cmp_repo)) - { - - full_name = g_strdup((char *) sqlite3_column_text(stmt, 0)); - - if (!g_strcmp0((char *) sqlite3_column_text(stmt, 6), "obsolete")) - { /* Remove if obsolete */ - pkg_id = pk_package_id_build(tokens[PK_PACKAGE_ID_NAME], - tokens[PK_PACKAGE_ID_VERSION], - tokens[PK_PACKAGE_ID_ARCH], - "obsolete"); - /* TODO: - * 1: Use the repository name instead of "obsolete" above and check in pk_backend_update_packages() - if the package is obsolete or not - * 2: Get description from /var/log/packages, not from the database */ - desc = g_strdup((char *) sqlite3_column_text(stmt, 5)); - - pk_backend_job_package(job, PK_INFO_ENUM_REMOVING, pkg_id, desc); - - g_free(desc); - g_free(pkg_id); - } - else if (g_strcmp0(pkg_metadata_filename, full_name)) - { /* Update available */ - pkg_id = pk_package_id_build((char *) sqlite3_column_text(stmt, 1), - (char *) sqlite3_column_text(stmt, 2), - (char *) sqlite3_column_text(stmt, 3), - (char *) sqlite3_column_text(stmt, 4)); - desc = g_strdup((char *) sqlite3_column_text(stmt, 5)); - - pk_backend_job_package(job, PK_INFO_ENUM_NORMAL, pkg_id, desc); - - g_free(desc); - g_free(pkg_id); - } - g_free(full_name); - } - - sqlite3_clear_bindings(stmt); - sqlite3_reset(stmt); - - g_strfreev(tokens); - g_object_unref(pkg_metadata_file_info); - } - g_object_unref(pkg_metadata_enumerator); - -out: - sqlite3_finalize(stmt); -} - -void -pk_backend_get_updates(PkBackendJob *job) -{ - pk_backend_job_thread_create(job, pk_backend_get_updates_thread, nullptr, nullptr); -} - -static void -pk_backend_update_packages_thread(PkBackendJob *job, char **pkg_ids, void *user_data) -{ - char *dest_dir_name, *cmd_line; - unsigned i; - - /* Download the packages */ - dest_dir_name = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "downloads", nullptr); - for (i = 0; pkg_ids[i]; i++) - { - char **tokens = pk_package_id_split(pkg_ids[i]); - - if (g_strcmp0(tokens[PK_PACKAGE_ID_DATA], "obsolete")) - { - GSList *repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); - - if (repo) - { - static_cast (repo->data)->download (job, - dest_dir_name, tokens[PK_PACKAGE_ID_NAME]); - } - } - - g_strfreev(tokens); - } - g_free(dest_dir_name); - - /* Install the packages */ - for (i = 0; pkg_ids[i]; i++) - { - char **tokens = pk_package_id_split(pkg_ids[i]); - - if (g_strcmp0(tokens[PK_PACKAGE_ID_DATA], "obsolete")) - { - GSList *repo = g_slist_find_custom(repos, tokens[PK_PACKAGE_ID_DATA], cmp_repo); - - if (repo) - { - static_cast (repo->data)->install (job, - tokens[PK_PACKAGE_ID_NAME]); - } - } - else - { - /* Remove obsolete package - * TODO: Removing should be an independent operation (not during installing updates) */ - cmd_line = g_strconcat("/sbin/removepkg ", tokens[PK_PACKAGE_ID_NAME], nullptr); - g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, nullptr); - g_free(cmd_line); - } - g_strfreev(tokens); - } -} - -void -pk_backend_update_packages(PkBackendJob *job, char **package_ids) -{ - pk_backend_job_thread_create(job, pk_backend_update_packages_thread, nullptr, nullptr); -} - -static void -pk_backend_refresh_cache_thread(PkBackendJob *job, bool force, void *user_data) -{ - char *tmp_dir_name, *db_err, *path = nullptr; - int ret; - GSList *file_list = nullptr; - GFile *db_file = nullptr; - GFileInfo *file_info = nullptr; - GError *err = nullptr; - sqlite3_stmt *stmt = nullptr; - auto job_data = static_cast (pk_backend_job_get_user_data(job)); - - /* Create temporary directory */ - tmp_dir_name = g_dir_make_tmp("PackageKit.XXXXXX", &err); - if (!tmp_dir_name) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_INTERNAL_ERROR, "%s", err->message); - g_error_free(err); - return; - } - - /* Force the complete cache refresh if the read configuration file is newer than the metadata cache */ - if (!force) - { - path = g_build_filename(LOCALSTATEDIR, "cache", "PackageKit", "metadata", "metadata.db", nullptr); - db_file = g_file_new_for_path(path); - file_info = g_file_query_info(db_file, "time::modified-usec", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &err); - if (err) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_NO_CACHE, "%s: %s", path, err->message); - g_error_free(err); - goto out; - } - ret = sqlite3_prepare_v2(job_data->db, - "SELECT value FROM cache_info WHERE key LIKE 'last_modification'", - -1, - &stmt, - nullptr); - if ((ret != SQLITE_OK) || ((ret = sqlite3_step(stmt)) != SQLITE_ROW)) - { - pk_backend_job_error_code(job, - PK_ERROR_ENUM_NO_CACHE, - "%s: %s", - path, - sqlite3_errstr(ret)); - goto out; - } - if ((std::uint32_t) sqlite3_column_int(stmt, 0) > g_file_info_get_attribute_uint32(file_info, "time::modified-usec")) - { - force = true; - } - } - if (force) /* It should empty all tables */ - { - if (sqlite3_exec(job_data->db, "DELETE FROM repos", nullptr, 0, &db_err) != SQLITE_OK) - { - pk_backend_job_error_code(job, PK_ERROR_ENUM_INTERNAL_ERROR, "%s", db_err); - sqlite3_free(db_err); - goto out; - } - } - - // Get list of files that should be downloaded. - for (GSList *l = repos; l; l = g_slist_next(l)) - { - file_list = g_slist_concat(file_list, - static_cast (l->data)->collect_cache_info (tmp_dir_name)); - } - - /* Download repository */ - for (GSList *l = file_list; l; l = g_slist_next(l)) - { - get_file(&job_data->curl, static_cast (l->data)[0], - static_cast (l->data)[1]); - } - g_slist_free_full(file_list, (GDestroyNotify)g_strfreev); - - /* Refresh cache */ - for (GSList *l = repos; l; l = g_slist_next(l)) - { - static_cast (l->data)->generate_cache (job, tmp_dir_name); - } - -out: - sqlite3_finalize(stmt); - if (file_info) - { - g_object_unref(file_info); - } - if (db_file) - { - g_object_unref(db_file); - } - g_free(path); - - pk_directory_remove_contents(tmp_dir_name); - g_rmdir(tmp_dir_name); - g_free(tmp_dir_name); -} - -void -pk_backend_refresh_cache(PkBackendJob *job, bool force) -{ - pk_backend_job_thread_create(job, pk_backend_refresh_cache_thread, nullptr, nullptr); -} - -static void -pk_backend_get_update_detail_thread(PkBackendJob *job, char **pkg_ids, void *user_data) -{ - unsigned i; - - for (i = 0; pkg_ids[i] != nullptr; i++) - { - pk_backend_job_update_detail (job, - pkg_ids[i], - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - PK_RESTART_ENUM_NONE, - nullptr, - nullptr, - PK_UPDATE_STATE_ENUM_STABLE, - nullptr, - nullptr); - } -} - -void -pk_backend_get_update_detail(PkBackendJob *job, char **package_ids) -{ - pk_backend_job_thread_create(job, pk_backend_get_update_detail_thread, nullptr, nullptr); -} diff --git a/backend/pkgtools.cc b/backend/pkgtools.cc index 6ffbaff..2f32b77 100644 --- a/backend/pkgtools.cc +++ b/backend/pkgtools.cc @@ -7,10 +7,10 @@ #include #include "pkgtools.h" -namespace slack { - +namespace katja +{ /** - * slack::Pkgtools::download: + * katja::Pkgtools::download: * @job: A #PkBackendJob. * @dest_dir_name: Destination directory. * @pkg_name: Package name. @@ -73,7 +73,7 @@ Pkgtools::download (JobData *job_data, } /** - * slack::Pkgtools::install: + * katja::Pkgtools::install: * @job_data: A #JobData. * @pkg_name: Package name. * @@ -120,7 +120,7 @@ Pkgtools::~Pkgtools () noexcept } /** - * slack::Pkgtools::get_name: + * katja::Pkgtools::get_name: * * Retrieves the repository name. * @@ -133,7 +133,7 @@ Pkgtools::get_name () const noexcept } /** - * slack::Pkgtools::get_mirror: + * katja::Pkgtools::get_mirror: * * Retrieves the repository mirror. * @@ -146,7 +146,7 @@ Pkgtools::get_mirror () const noexcept } /** - * slack::Pkgtools::get_order: + * katja::Pkgtools::get_order: * * Retrieves the repository order. * @@ -159,7 +159,7 @@ Pkgtools::get_order () const noexcept } /** - * slack::Pkgtools:is_blacklisted: + * katja::Pkgtools:is_blacklisted: * @pkg: Package name to check for. * * Checks whether a package is blacklisted. diff --git a/backend/pkgtools.h b/backend/pkgtools.h index e32e2c1..e51b4b9 100644 --- a/backend/pkgtools.h +++ b/backend/pkgtools.h @@ -9,8 +9,8 @@ #include #include "utils.h" -namespace slack { - +namespace katja +{ class Pkgtools { public: diff --git a/backend/slackpkg.cc b/backend/slackpkg.cc index 9e99535..33fc4d7 100644 --- a/backend/slackpkg.cc +++ b/backend/slackpkg.cc @@ -5,12 +5,12 @@ #include "slackpkg.h" #include "utils.h" -namespace slack { - +namespace katja +{ GHashTable *Slackpkg::cat_map = nullptr; /* - * slack::Slackpkg::manifest: + * katja::Slackpkg::manifest: * @job: a #PkBackendJob. * @tmpl: temporary directory. * @filename: manifest filename @@ -150,7 +150,7 @@ out: } /** - * slack::Slackpkg::collect_cache_info: + * katja::Slackpkg::collect_cache_info: * @tmpl: temporary directory for downloading the files. * * Download files needed to get the information like the list of packages @@ -228,7 +228,7 @@ out: } /** - * slack::Slackpkg::generate_cache: + * katja::Slackpkg::generate_cache: * @job_data: A #JobData. * @tmpl: temporary directory for downloading the files. * @@ -463,7 +463,7 @@ Slackpkg::~Slackpkg () noexcept } /** - * slack::Slackpkg::Slackpkg: + * katja::Slackpkg::Slackpkg: * @name: Repository name. * @mirror: Repository mirror. * @order: Repository order. @@ -472,7 +472,7 @@ Slackpkg::~Slackpkg () noexcept * * Constructor. * - * Returns: New #slack::Slackpkg. + * Returns: New #katja::Slackpkg. **/ Slackpkg::Slackpkg (const char *name, const char *mirror, std::uint8_t order, const char *blacklist, char **priority) noexcept diff --git a/backend/slackpkg.h b/backend/slackpkg.h index bb45a46..c358dd9 100644 --- a/backend/slackpkg.h +++ b/backend/slackpkg.h @@ -10,8 +10,8 @@ #include "pkgtools.h" #include "utils.h" -namespace slack { - +namespace katja +{ class Slackpkg final : public Pkgtools { public: diff --git a/backend/utils.cc b/backend/utils.cc index ccecef0..d5c8e84 100644 --- a/backend/utils.cc +++ b/backend/utils.cc @@ -9,10 +9,10 @@ #include "utils.h" #include "pkgtools.h" -namespace slack { - +namespace katja +{ /** - * slack::get_file: + * katja::get_file: * @curl: curl easy handle. * @source_url: source url. * @dest: destination. @@ -73,7 +73,7 @@ get_file (CURL **curl, char *source_url, char *dest) } /** - * slack::split_package_name: + * katja::split_package_name: * Got the name of a package, without version-arch-release data. **/ char ** @@ -125,7 +125,7 @@ split_package_name (const char *pkg_filename) } /** - * slack::is_installed: + * katja::is_installed: * Checks if a package is already installed in the system. * * Params: @@ -222,7 +222,7 @@ is_installed (const char *pkg_fullname) } /** - * slack::cmp_repo: + * katja::cmp_repo: **/ int cmp_repo (const void *a, const void *b) diff --git a/backend/utils.h b/backend/utils.h index ea3635b..61d8e81 100644 --- a/backend/utils.h +++ b/backend/utils.h @@ -7,8 +7,8 @@ #include -namespace slack { - +namespace katja +{ struct JobData { sqlite3 *db; @@ -33,10 +33,6 @@ char **split_package_name (const char *pkg_filename); Info is_installed (const char *pkg_fullname); -extern "C" { - int cmp_repo (const void *a, const void *b); } - -}