From: Michael Tremer Date: Tue, 10 May 2022 13:45:33 +0000 (+0000) Subject: archive: Read package information from JSON file X-Git-Tag: 0.9.28~799 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=20b8330243f130de99474f727594dcdbf3a323a5;p=pakfire.git archive: Read package information from JSON file Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index 65d397c2f..fe393834f 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -32,6 +32,9 @@ #include #include +// JSON-C +#include + // openssl #include #include @@ -83,6 +86,7 @@ struct pakfire_archive { // metadata unsigned int format; + struct json_object* metadata; struct pakfire_parser* parser; struct pakfire_filelist* filelist; @@ -433,6 +437,8 @@ static void pakfire_archive_free(struct pakfire_archive* archive) { pakfire_filelist_unref(archive->filelist); if (archive->package) pakfire_package_unref(archive->package); + if (archive->metadata) + json_object_put(archive->metadata); if (archive->parser) pakfire_parser_unref(archive->parser); pakfire_unref(archive->pakfire); @@ -481,7 +487,48 @@ static struct pakfire_package* pakfire_archive_get_package(struct pakfire_archiv // Metadata -static int pakfire_archive_parse_metadata(struct pakfire_archive* archive) { +static int pakfire_archive_parse_json_metadata(struct pakfire_archive* archive) { + char* data = NULL; + size_t size = 0; + + // Do nothing if metadata has already been loaded + if (archive->metadata) + return 0; + + // Read the metadata file + int r = open_archive_and_read(archive, "PKGINFO", &data, &size); + if (r) + return r; + + // Create tokener + struct json_tokener* tokener = json_tokener_new(); + if (!tokener) { + ERROR(archive->pakfire, "Could not allocate JSON tokener: %m\n"); + goto ERROR; + } + + // Parse JSON from buffer + archive->metadata = json_tokener_parse_ex(tokener, data, size); + if (!archive->metadata) { + enum json_tokener_error error = json_tokener_get_error(tokener); + + ERROR(archive->pakfire, "JSON parsing error: %s\n", + json_tokener_error_desc(error)); + goto ERROR; + } + + DEBUG(archive->pakfire, "Successfully parsed package metadata:\n%s\n", + json_object_to_json_string_ext(archive->metadata, + JSON_C_TO_STRING_PRETTY|JSON_C_TO_STRING_PRETTY_TAB)); + +ERROR: + if (tokener) + json_tokener_free(tokener); + + return r; +} + +static int pakfire_archive_parse_legacy_metadata(struct pakfire_archive* archive) { char* data = NULL; size_t size; @@ -523,6 +570,15 @@ CLEANUP: return r; } +static int pakfire_archive_parse_metadata(struct pakfire_archive* archive) { + // Parse JSON for newer formats + if (archive->format >= 6) + return pakfire_archive_parse_json_metadata(archive); + + // Use legacy parser for others + return pakfire_archive_parse_legacy_metadata(archive); +} + /* This function tries (very easily) to find out whether something is a valid archive or not. @@ -636,6 +692,54 @@ ERROR: return r; } +static struct json_object* pakfire_archive_metadata_get_object( + struct pakfire_archive* archive, const char* key1, const char* key2) { + int r = pakfire_archive_parse_metadata(archive); + if (r) + return NULL; + + struct json_object* object = archive->metadata; + + const char* keys[] = { + key1, + key2, + NULL, + }; + + // Walk through all keys + for (const char** key = keys; *key; key++) { + // Try finding a matching JSON object + r = json_object_object_get_ex(object, *key, &object); + if (!r) { + DEBUG(archive->pakfire, "Could not find JSON object at '%s': %m\n", *key); + break; + } + } + + return object; +} + +static const char* pakfire_archive_metadata_get( + struct pakfire_archive* archive, const char* key1, const char* key2) { + // Try finding an object + struct json_object* object = pakfire_archive_metadata_get_object(archive, key1, key2); + if (!object) + return NULL; + + // Return the object as string + return json_object_get_string(object); +} + +static int64_t pakfire_archive_metadata_get_int64( + struct pakfire_archive* archive, const char* key1, const char* key2) { + // Try finding an object + struct json_object* object = pakfire_archive_metadata_get_object(archive, key1, key2); + if (!object) + return 0; + + // Return the object as integer + return json_object_get_int64(object); +} PAKFIRE_EXPORT char* pakfire_archive_get(struct pakfire_archive* archive, const char* namespace, const char* key) { int r = pakfire_archive_parse_metadata(archive); @@ -1969,30 +2073,165 @@ ERROR: return r; } -/* - Copy all metadata from this archive to the package object -*/ -PAKFIRE_EXPORT int pakfire_archive_make_package(struct pakfire_archive* archive, +static int pakfire_archive_make_package_from_json(struct pakfire_archive* archive, struct pakfire_repo* repo, struct pakfire_package** package) { - struct pakfire_repo* dummy = NULL; unsigned char digest[EVP_MAX_MD_SIZE]; size_t digest_length = 0; int r; // Calculate digest + // This must be done before we create the package object r = pakfire_archive_digest(archive, PAKFIRE_DIGEST_SHA512, digest, &digest_length); if (r) { ERROR(archive->pakfire, "Could not calculate digest of %s: %m\n", archive->path); return r; } - // Use dummy repo if no repository was passed - if (!repo) { - dummy = pakfire_get_repo(archive->pakfire, PAKFIRE_REPO_DUMMY); - if (!dummy) - return 1; + // Fetch the most basic package information + const char* name = pakfire_archive_metadata_get(archive, "name", NULL); + const char* evr = pakfire_archive_metadata_get(archive, "evr", NULL); + const char* arch = pakfire_archive_metadata_get(archive, "arch", NULL); - repo = dummy; + // Create a new package object + struct pakfire_package* pkg = pakfire_package_create(archive->pakfire, + repo, name, evr, arch); + if (!pkg) + return 1; + +#ifdef ENABLE_DEBUG + const char* nevra = pakfire_package_get_nevra(pkg); + DEBUG(archive->pakfire, "Created package %s (%p) from archive %p\n", + nevra, pkg, archive); +#endif + + // Set path + pakfire_package_set_path(pkg, archive->path); + + // Set digest + pakfire_package_set_digest(pkg, PAKFIRE_DIGEST_SHA512, digest); + + // UUID + const char* uuid = pakfire_archive_metadata_get(archive, "uuid", NULL); + if (uuid) + pakfire_package_set_uuid(pkg, uuid); + + // Groups + const char* groups = pakfire_archive_metadata_get(archive, "groups", NULL); + if (groups) + pakfire_package_set_groups(pkg, groups); + + // Maintainer + const char* maintainer = pakfire_archive_metadata_get(archive, "maintainer", NULL); + if (maintainer) + pakfire_package_set_maintainer(pkg, maintainer); + + // URL + const char* url = pakfire_archive_metadata_get(archive, "url", NULL); + if (url) + pakfire_package_set_url(pkg, url); + + // License + const char* license = pakfire_archive_metadata_get(archive, "license", NULL); + if (license) + pakfire_package_set_license(pkg, license); + + // Summary + const char* summary = pakfire_archive_metadata_get(archive, "summary", NULL); + if (summary) + pakfire_package_set_summary(pkg, summary); + + // Description + const char* description = pakfire_archive_metadata_get(archive, "description", NULL); + if (description) + pakfire_package_set_description(pkg, description); + + // Installed package size + size_t installsize = pakfire_archive_metadata_get_int64(archive, "size", NULL); + if (installsize) + pakfire_package_set_installsize(pkg, installsize); + + // Build Host + const char* build_host = pakfire_archive_metadata_get(archive, "build", "host"); + if (build_host) + pakfire_package_set_build_host(pkg, build_host); + + // Build ID + const char* build_id = pakfire_archive_metadata_get(archive, "build", "id"); + if (build_id) + pakfire_package_set_build_id(pkg, build_id); + + // Build Time + time_t build_time = pakfire_archive_metadata_get_int64(archive, "build", "time"); + if (build_time) + pakfire_package_set_build_time(pkg, build_time); + + // Dependencies + const struct dependencies { + const char* type; + void (*func)(struct pakfire_package*, const char* s); + } dependencies[] = { + { "provides", pakfire_package_add_provides }, + { "prerequires", pakfire_package_add_prerequires }, + { "requires", pakfire_package_add_requires }, + { "conflicts", pakfire_package_add_conflicts }, + { "obsoletes", pakfire_package_add_obsoletes }, + { "recommends", pakfire_package_add_recommends }, + { "suggests", pakfire_package_add_suggests }, + { "supplements", pakfire_package_add_supplements }, + { "enhances", pakfire_package_add_enhances }, + { NULL, NULL }, + }; + + for (const struct dependencies* deps = dependencies; deps->type; deps++) { + struct json_object* array = pakfire_archive_metadata_get_object( + archive, "dependencies", deps->type); + if (!array) + continue; + + // Determine the length of the array + const size_t length = json_object_array_length(array); + if (!length) + continue; + + // Walk through all items in this array + for (unsigned int i = 0; i < length; i++) { + struct json_object* item = json_object_array_get_idx(array, i); + if (!item) + continue; + + // Extract the string value + const char* string = json_object_get_string(item); + if (!string) + continue; + + // Add the dependency to the package + deps->func(pkg, string); + } + } + + // Import filelist + struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive); + if (filelist) { + pakfire_package_set_filelist(pkg, filelist); + pakfire_filelist_unref(filelist); + } + + // Success! + *package = pkg; + return 0; +} + +static int pakfire_archive_make_legacy_package(struct pakfire_archive* archive, + struct pakfire_repo* repo, struct pakfire_package** package) { + unsigned char digest[EVP_MAX_MD_SIZE]; + size_t digest_length = 0; + int r; + + // Calculate digest + r = pakfire_archive_digest(archive, PAKFIRE_DIGEST_SHA512, digest, &digest_length); + if (r) { + ERROR(archive->pakfire, "Could not calculate digest of %s: %m\n", archive->path); + return r; } char* name = pakfire_archive_get(archive, "package", "name"); @@ -2179,11 +2418,39 @@ PAKFIRE_EXPORT int pakfire_archive_make_package(struct pakfire_archive* archive, *package = pkg; - // Cleanup + return 0; +} + +/* + Copy all metadata from this archive to the package object +*/ +PAKFIRE_EXPORT int pakfire_archive_make_package(struct pakfire_archive* archive, + struct pakfire_repo* repo, struct pakfire_package** package) { + struct pakfire_repo* dummy = NULL; + int r; + + // Use dummy repo if no repository was passed + if (!repo) { + dummy = pakfire_get_repo(archive->pakfire, PAKFIRE_REPO_DUMMY); + if (!dummy) + return 1; + + repo = dummy; + } + + // Make package from JSON metadata + if (archive->format >= 6) + r = pakfire_archive_make_package_from_json(archive, repo, package); + + // Use legacy stuff + else + r = pakfire_archive_make_legacy_package(archive, repo, package); + + // Free dummy repository if (dummy) pakfire_repo_unref(dummy); - return 0; + return r; } static int pakfire_archive_load_scriptlet(struct pakfire_archive* archive, diff --git a/tests/libpakfire/packager.c b/tests/libpakfire/packager.c index 0e167a8ae..f538b9db8 100644 --- a/tests/libpakfire/packager.c +++ b/tests/libpakfire/packager.c @@ -105,6 +105,13 @@ static int test_compare_metadata(const struct test* t) { // Parse package from archive ASSERT_SUCCESS(pakfire_archive_make_package(archive, repo, &pkg2)); + // Dump everything for debugging + char* dump = pakfire_package_dump(pkg2, PAKFIRE_PKG_DUMP_LONG|PAKFIRE_PKG_DUMP_FILELIST); + if (dump) { + printf("%s\n", dump); + free(dump); + } + // Compare name s = pakfire_package_get_name(pkg2); ASSERT_STRING_EQUALS(s, "test");