]> git.ipfire.org Git - pakfire.git/commitdiff
archive: Read package information from JSON file
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 10 May 2022 13:45:33 +0000 (13:45 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 10 May 2022 13:47:08 +0000 (13:47 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c
tests/libpakfire/packager.c

index 65d397c2f3e7aa067d7ab76c745f644929652024..fe393834f19caef83075e77aade6dbe00b0d361a 100644 (file)
@@ -32,6 +32,9 @@
 #include <archive.h>
 #include <archive_entry.h>
 
+// JSON-C
+#include <json.h>
+
 // openssl
 #include <openssl/crypto.h>
 #include <openssl/err.h>
@@ -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,
index 0e167a8ae1b293d067820f45eacc04a7e52cd1e1..f538b9db8f66fef6ae44ba648ac24713af7e0917 100644 (file)
@@ -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");