#include <archive.h>
#include <archive_entry.h>
+// JSON-C
+#include <json.h>
+
// openssl
#include <openssl/crypto.h>
#include <openssl/err.h>
// metadata
unsigned int format;
+ struct json_object* metadata;
struct pakfire_parser* parser;
struct pakfire_filelist* filelist;
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);
// 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;
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.
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);
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");
*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,