/* * Copyright (C) 2015 Red Hat, Inc * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Alexander Larsson */ #include "config.h" #include "string.h" #include "flatpak-json-oci.h" #include "flatpak-utils.h" #include "libglnx.h" const char * flatpak_arch_to_oci_arch (const char *flatpak_arch) { if (strcmp (flatpak_arch, "x86_64") == 0) return "amd64"; if (strcmp (flatpak_arch, "aarch64") == 0) return "arm64"; if (strcmp (flatpak_arch, "i386") == 0) return "386"; return flatpak_arch; } FlatpakOciDescriptor * flatpak_oci_descriptor_new (const char *mediatype, const char *digest, gint64 size) { FlatpakOciDescriptor *desc = g_new0 (FlatpakOciDescriptor, 1); desc->mediatype = g_strdup (mediatype); desc->digest = g_strdup (digest); desc->size = size; desc->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); return desc; } void flatpak_oci_descriptor_copy (FlatpakOciDescriptor *source, FlatpakOciDescriptor *dest) { flatpak_oci_descriptor_destroy (dest); dest->mediatype = g_strdup (source->mediatype); dest->digest = g_strdup (source->digest); dest->size = source->size; dest->urls = g_strdupv ((char **)source->urls); dest->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); if (source->annotations) flatpak_oci_copy_annotations (source->annotations, dest->annotations); } void flatpak_oci_descriptor_destroy (FlatpakOciDescriptor *self) { g_free (self->mediatype); g_free (self->digest); g_strfreev (self->urls); if (self->annotations) g_hash_table_destroy (self->annotations); } void flatpak_oci_descriptor_free (FlatpakOciDescriptor *self) { flatpak_oci_descriptor_destroy (self); g_free (self); } static FlatpakJsonProp flatpak_oci_descriptor_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciDescriptor, mediatype, "mediaType"), FLATPAK_JSON_STRING_PROP (FlatpakOciDescriptor, digest, "digest"), FLATPAK_JSON_INT64_PROP (FlatpakOciDescriptor, size, "size"), FLATPAK_JSON_STRV_PROP (FlatpakOciDescriptor, urls, "urls"), FLATPAK_JSON_STRMAP_PROP(FlatpakOciDescriptor, annotations, "annotations"), FLATPAK_JSON_LAST_PROP }; static void flatpak_oci_manifest_platform_destroy (FlatpakOciManifestPlatform *self) { g_free (self->architecture); g_free (self->os); g_free (self->os_version); g_strfreev (self->os_features); g_free (self->variant); g_strfreev (self->features); } FlatpakOciManifestDescriptor * flatpak_oci_manifest_descriptor_new (void) { return g_new0 (FlatpakOciManifestDescriptor, 1); } void flatpak_oci_manifest_descriptor_destroy (FlatpakOciManifestDescriptor *self) { flatpak_oci_manifest_platform_destroy (&self->platform); flatpak_oci_descriptor_destroy (&self->parent); } void flatpak_oci_manifest_descriptor_free (FlatpakOciManifestDescriptor *self) { flatpak_oci_manifest_descriptor_destroy (self); g_free (self); } static FlatpakJsonProp flatpak_oci_manifest_platform_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, architecture, "architecture"), FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, os, "os"), FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, os_version, "os.version"), FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, variant, "variant"), FLATPAK_JSON_STRV_PROP (FlatpakOciManifestPlatform, os_features, "os.features"), FLATPAK_JSON_STRV_PROP (FlatpakOciManifestPlatform, features, "features"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp flatpak_oci_manifest_descriptor_props[] = { FLATPAK_JSON_PARENT_PROP (FlatpakOciManifestDescriptor, parent, flatpak_oci_descriptor_props), FLATPAK_JSON_OPT_STRUCT_PROP (FlatpakOciManifestDescriptor, platform, "platform", flatpak_oci_manifest_platform_props), FLATPAK_JSON_LAST_PROP }; G_DEFINE_TYPE (FlatpakOciVersioned, flatpak_oci_versioned, FLATPAK_TYPE_JSON); static void flatpak_oci_versioned_finalize (GObject *object) { FlatpakOciVersioned *self = FLATPAK_OCI_VERSIONED (object); g_free (self->mediatype); G_OBJECT_CLASS (flatpak_oci_versioned_parent_class)->finalize (object); } static void flatpak_oci_versioned_class_init (FlatpakOciVersionedClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass); static FlatpakJsonProp props[] = { FLATPAK_JSON_INT64_PROP (FlatpakOciVersioned, version, "schemaVersion"), FLATPAK_JSON_STRING_PROP (FlatpakOciVersioned, mediatype, "mediaType"), FLATPAK_JSON_LAST_PROP }; object_class->finalize = flatpak_oci_versioned_finalize; json_class->props = props; } static void flatpak_oci_versioned_init (FlatpakOciVersioned *self) { } FlatpakOciVersioned * flatpak_oci_versioned_from_json (GBytes *bytes, GError **error) { g_autoptr(JsonParser) parser = NULL; JsonNode *root = NULL; const gchar *mediatype; JsonObject *object; parser = json_parser_new (); if (!json_parser_load_from_data (parser, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), error)) return NULL; root = json_parser_get_root (parser); object = json_node_get_object (root); mediatype = json_object_get_string_member (object, "mediaType"); if (mediatype == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Versioned object lacks mediatype"); return NULL; } if (strcmp (mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST) == 0) return (FlatpakOciVersioned *) flatpak_json_from_node (root, FLATPAK_TYPE_OCI_MANIFEST, error); if (strcmp (mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX) == 0) return (FlatpakOciVersioned *) flatpak_json_from_node (root, FLATPAK_TYPE_OCI_INDEX, error); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Unsupported media type %s", mediatype); return NULL; } const char * flatpak_oci_versioned_get_mediatype (FlatpakOciVersioned *self) { return self->mediatype; } gint64 flatpak_oci_versioned_get_version (FlatpakOciVersioned *self) { return self->version; } G_DEFINE_TYPE (FlatpakOciManifest, flatpak_oci_manifest, FLATPAK_TYPE_OCI_VERSIONED); static void flatpak_oci_manifest_finalize (GObject *object) { FlatpakOciManifest *self = (FlatpakOciManifest *) object; int i; for (i = 0; self->layers != NULL && self->layers[i] != NULL; i++) flatpak_oci_descriptor_free (self->layers[i]); g_free (self->layers); flatpak_oci_descriptor_destroy (&self->config); if (self->annotations) g_hash_table_destroy (self->annotations); G_OBJECT_CLASS (flatpak_oci_manifest_parent_class)->finalize (object); } static void flatpak_oci_manifest_class_init (FlatpakOciManifestClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass); static FlatpakJsonProp props[] = { FLATPAK_JSON_STRUCT_PROP(FlatpakOciManifest, config, "config", flatpak_oci_descriptor_props), FLATPAK_JSON_STRUCTV_PROP(FlatpakOciManifest, layers, "layers", flatpak_oci_descriptor_props), FLATPAK_JSON_STRMAP_PROP(FlatpakOciManifest, annotations, "annotations"), FLATPAK_JSON_LAST_PROP }; object_class->finalize = flatpak_oci_manifest_finalize; json_class->props = props; json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST; } static void flatpak_oci_manifest_init (FlatpakOciManifest *self) { } FlatpakOciManifest * flatpak_oci_manifest_new (void) { FlatpakOciManifest *manifest; manifest = g_object_new (FLATPAK_TYPE_OCI_MANIFEST, NULL); manifest->parent.version = 2; manifest->parent.mediatype = g_strdup (FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST); manifest->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); return manifest; } void flatpak_oci_manifest_set_config (FlatpakOciManifest *self, FlatpakOciDescriptor *desc) { g_free (self->config.mediatype); self->config.mediatype = g_strdup (desc->mediatype); g_free (self->config.digest); self->config.digest = g_strdup (desc->digest); self->config.size = desc->size; } static int ptrv_count (gpointer *ptrs) { int count; for (count = 0; ptrs != NULL && ptrs[count] != NULL; count++) ; return count; } void flatpak_oci_manifest_set_layer (FlatpakOciManifest *self, FlatpakOciDescriptor *desc) { FlatpakOciDescriptor *descs[2] = { desc, NULL }; flatpak_oci_manifest_set_layers (self, descs); } void flatpak_oci_manifest_set_layers (FlatpakOciManifest *self, FlatpakOciDescriptor **descs) { int i, count; for (i = 0; self->layers != NULL && self->layers[i] != NULL; i++) flatpak_oci_descriptor_free (self->layers[i]); g_free (self->layers); count = ptrv_count ((gpointer *)descs); self->layers = g_new0 (FlatpakOciDescriptor *, count + 1); for (i = 0; i < count; i++) { self->layers[i] = g_new0 (FlatpakOciDescriptor, 1); self->layers[i]->mediatype = g_strdup (descs[i]->mediatype); self->layers[i]->digest = g_strdup (descs[i]->digest); self->layers[i]->size = descs[i]->size; } } int flatpak_oci_manifest_get_n_layers (FlatpakOciManifest *self) { return ptrv_count ((gpointer *)self->layers); } const char * flatpak_oci_manifest_get_layer_digest (FlatpakOciManifest *self, int i) { return self->layers[i]->digest; } GHashTable * flatpak_oci_manifest_get_annotations (FlatpakOciManifest *self) { return self->annotations; } G_DEFINE_TYPE (FlatpakOciIndex, flatpak_oci_index, FLATPAK_TYPE_OCI_VERSIONED); static void flatpak_oci_index_finalize (GObject *object) { FlatpakOciIndex *self = (FlatpakOciIndex *) object; int i; for (i = 0; self->manifests != NULL && self->manifests[i] != NULL; i++) flatpak_oci_manifest_descriptor_free (self->manifests[i]); g_free (self->manifests); if (self->annotations) g_hash_table_destroy (self->annotations); G_OBJECT_CLASS (flatpak_oci_index_parent_class)->finalize (object); } static void flatpak_oci_index_class_init (FlatpakOciIndexClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass); static FlatpakJsonProp props[] = { FLATPAK_JSON_STRUCTV_PROP(FlatpakOciIndex, manifests, "manifests", flatpak_oci_manifest_descriptor_props), FLATPAK_JSON_STRMAP_PROP(FlatpakOciIndex, annotations, "annotations"), FLATPAK_JSON_LAST_PROP }; object_class->finalize = flatpak_oci_index_finalize; json_class->props = props; json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX; } static void flatpak_oci_index_init (FlatpakOciIndex *self) { } FlatpakOciIndex * flatpak_oci_index_new (void) { FlatpakOciIndex *index; index = g_object_new (FLATPAK_TYPE_OCI_INDEX, NULL); index->parent.version = 2; index->parent.mediatype = g_strdup (FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX); index->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); return index; } static FlatpakOciManifestDescriptor * manifest_desc_for_desc (FlatpakOciDescriptor *src_descriptor) { FlatpakOciManifestDescriptor *desc; desc = flatpak_oci_manifest_descriptor_new (); flatpak_oci_descriptor_copy (src_descriptor, &desc->parent); return desc; } int flatpak_oci_index_get_n_manifests (FlatpakOciIndex *self) { return ptrv_count ((gpointer *)self->manifests); } void flatpak_oci_index_add_manifest (FlatpakOciIndex *self, FlatpakOciDescriptor *desc) { FlatpakOciManifestDescriptor *m; const char *m_ref = NULL; int count; if (desc->annotations != NULL) m_ref = g_hash_table_lookup (desc->annotations, "org.opencontainers.ref.name"); if (m_ref != NULL) flatpak_oci_index_remove_manifest (self, m_ref); count = flatpak_oci_index_get_n_manifests (self); m = manifest_desc_for_desc (desc); self->manifests = g_renew (FlatpakOciManifestDescriptor *, self->manifests, count + 2); self->manifests[count] = m; self->manifests[count+1] = NULL; } const char * flatpak_oci_manifest_descriptor_get_ref (FlatpakOciManifestDescriptor *m) { if (m->parent.mediatype == NULL || strcmp (m->parent.mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST) != 0) return NULL; if (m->parent.annotations == NULL) return NULL; return g_hash_table_lookup (m->parent.annotations, "org.opencontainers.ref.name"); } static int index_find_ref (FlatpakOciIndex *self, const char *ref) { int i; if (self->manifests == NULL) return -1; for (i = 0; self->manifests[i] != NULL; i++) { const char *m_ref = flatpak_oci_manifest_descriptor_get_ref (self->manifests[i]); if (m_ref == NULL) continue; if (strcmp (ref, m_ref) == 0) return i; } return -1; } FlatpakOciManifestDescriptor * flatpak_oci_index_get_manifest (FlatpakOciIndex *self, const char *ref) { int i = index_find_ref (self, ref); if (i >= 0) return self->manifests[i]; return NULL; } FlatpakOciManifestDescriptor * flatpak_oci_index_get_only_manifest (FlatpakOciIndex *self) { int i, found = -1; if (self->manifests == NULL) return NULL; for (i = 0; self->manifests[i] != NULL; i++) { const char *m_ref = flatpak_oci_manifest_descriptor_get_ref (self->manifests[i]); if (m_ref == NULL) continue; if (found == -1) found = i; else return NULL; } if (found >= 0) return self->manifests[found]; return NULL; } gboolean flatpak_oci_index_remove_manifest (FlatpakOciIndex *self, const char *ref) { int i = index_find_ref (self, ref); if (i < 0) return FALSE; for (; self->manifests[i] != NULL; i++) self->manifests[i] = self->manifests[i+1]; return TRUE; } G_DEFINE_TYPE (FlatpakOciImage, flatpak_oci_image, FLATPAK_TYPE_JSON); static void flatpak_oci_image_rootfs_destroy (FlatpakOciImageRootfs *self) { g_free (self->type); g_strfreev (self->diff_ids); } static void flatpak_oci_image_config_destroy (FlatpakOciImageConfig *self) { g_free (self->user); g_free (self->working_dir); g_strfreev (self->env); g_strfreev (self->cmd); g_strfreev (self->entrypoint); g_strfreev (self->exposed_ports); g_strfreev (self->volumes); if (self->labels) g_hash_table_destroy (self->labels); } static void flatpak_oci_image_history_free (FlatpakOciImageHistory *self) { g_free (self->created); g_free (self->created_by); g_free (self->author); g_free (self->comment); g_free (self); } static void flatpak_oci_image_finalize (GObject *object) { FlatpakOciImage *self = (FlatpakOciImage *) object; int i; g_free (self->created); g_free (self->author); g_free (self->architecture); g_free (self->os); flatpak_oci_image_rootfs_destroy (&self->rootfs); flatpak_oci_image_config_destroy (&self->config); for (i = 0; self->history != NULL && self->history[i] != NULL; i++) flatpak_oci_image_history_free (self->history[i]); g_free (self->history); G_OBJECT_CLASS (flatpak_oci_image_parent_class)->finalize (object); } static void flatpak_oci_image_class_init (FlatpakOciImageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass); static FlatpakJsonProp config_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciImageConfig, user, "User"), FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, memory, "Memory"), FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, memory_swap, "MemorySwap"), FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, cpu_shares, "CpuShares"), FLATPAK_JSON_BOOLMAP_PROP (FlatpakOciImageConfig, exposed_ports, "ExposedPorts"), FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, env, "Env"), FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, entrypoint, "Entrypoint"), FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, cmd, "Cmd"), FLATPAK_JSON_BOOLMAP_PROP (FlatpakOciImageConfig, volumes, "Volumes"), FLATPAK_JSON_STRING_PROP (FlatpakOciImageConfig, working_dir, "WorkingDir"), FLATPAK_JSON_STRMAP_PROP(FlatpakOciImageConfig, labels, "Labels"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp rootfs_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciImageRootfs, type, "type"), FLATPAK_JSON_STRV_PROP (FlatpakOciImageRootfs, diff_ids, "diff_ids"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp history_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, created, "created"), FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, created_by, "created_by"), FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, author, "author"), FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, comment, "comment"), FLATPAK_JSON_BOOL_PROP (FlatpakOciImageHistory, empty_layer, "empty_layer"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciImage, created, "created"), FLATPAK_JSON_STRING_PROP (FlatpakOciImage, author, "author"), FLATPAK_JSON_STRING_PROP (FlatpakOciImage, architecture, "architecture"), FLATPAK_JSON_STRING_PROP (FlatpakOciImage, os, "os"), FLATPAK_JSON_STRUCT_PROP (FlatpakOciImage, config, "config", config_props), FLATPAK_JSON_STRUCT_PROP (FlatpakOciImage, rootfs, "rootfs", rootfs_props), FLATPAK_JSON_STRUCTV_PROP (FlatpakOciImage, history, "history", history_props), FLATPAK_JSON_LAST_PROP }; object_class->finalize = flatpak_oci_image_finalize; json_class->props = props; json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_CONFIG; } static void flatpak_oci_image_init (FlatpakOciImage *self) { } FlatpakOciImage * flatpak_oci_image_new (void) { FlatpakOciImage *image; GTimeVal stamp; stamp.tv_sec = time (NULL); stamp.tv_usec = 0; image = g_object_new (FLATPAK_TYPE_OCI_IMAGE, NULL); /* Some default values */ image->created = g_time_val_to_iso8601 (&stamp); image->architecture = g_strdup ("arm64"); image->os = g_strdup ("linux"); image->rootfs.type = g_strdup ("layers"); image->rootfs.diff_ids = g_new0 (char *, 1); return image; } void flatpak_oci_image_set_created (FlatpakOciImage *image, const char *created) { g_free (image->created); image->created = g_strdup (created); } void flatpak_oci_image_set_architecture (FlatpakOciImage *image, const char *arch) { g_free (image->architecture); image->architecture = g_strdup (arch); } void flatpak_oci_image_set_os (FlatpakOciImage *image, const char *os) { g_free (image->os); image->os = g_strdup (os); } void flatpak_oci_image_set_layers (FlatpakOciImage *image, const char **layers) { g_strfreev (image->rootfs.diff_ids); image->rootfs.diff_ids = g_strdupv ((char **)layers); } void flatpak_oci_image_set_layer (FlatpakOciImage *image, const char *layer) { const char *layers[] = {layer, NULL}; flatpak_oci_image_set_layers (image, layers); } void flatpak_oci_export_annotations (GHashTable *source, GHashTable *dest) { const char *keys[] = { "org.opencontainers.ref.name", "org.flatpak.installed-size", "org.flatpak.download-size", "org.flatpak.metadata", }; int i; for (i = 0; i < G_N_ELEMENTS (keys); i++) { const char *key = keys[i]; const char *value = g_hash_table_lookup (source, key); if (value) g_hash_table_replace (dest, g_strdup (key), g_strdup (value)); } } void flatpak_oci_copy_annotations (GHashTable *source, GHashTable *dest) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, source); while (g_hash_table_iter_next (&iter, &key, &value)) g_hash_table_replace (dest, g_strdup ((char *)key), g_strdup ((char *)value)); } static void add_annotation (GHashTable *annotations, const char *key, const char *value) { g_hash_table_replace (annotations, g_strdup (key), g_strdup (value)); } void flatpak_oci_add_annotations_for_commit (GHashTable *annotations, const char *ref, const char *commit, GVariant *commit_data) { if (ref) add_annotation (annotations,"org.opencontainers.ref.name", ref); if (commit) add_annotation (annotations,"org.flatpak.commit", commit); if (commit_data) { g_autofree char *parent = NULL; g_autofree char *subject = NULL; g_autofree char *body = NULL; g_autofree char *timestamp = NULL; g_autoptr(GVariant) metadata = NULL; int i; parent = ostree_commit_get_parent (commit_data); if (parent) add_annotation (annotations, "org.flatpak.parent-commit", parent); metadata = g_variant_get_child_value (commit_data, 0); for (i = 0; i < g_variant_n_children (metadata); i++) { g_autoptr(GVariant) elm = g_variant_get_child_value (metadata, i); g_autoptr(GVariant) value = g_variant_get_child_value (elm, 1); g_autofree char *key = NULL; g_autofree char *full_key = NULL; g_autofree char *value_base64 = NULL; g_variant_get_child (elm, 0, "s", &key); full_key = g_strdup_printf ("org.flatpak.commit-metadata.%s", key); value_base64 = g_base64_encode (g_variant_get_data (value), g_variant_get_size (value)); add_annotation (annotations, full_key, value_base64); } timestamp = g_strdup_printf ("%"G_GUINT64_FORMAT, ostree_commit_get_timestamp (commit_data)); add_annotation (annotations, "org.flatpak.timestamp", timestamp); g_variant_get_child (commit_data, 3, "s", &subject); add_annotation (annotations, "org.flatpak.subject", subject); g_variant_get_child (commit_data, 4, "s", &body); add_annotation (annotations, "org.flatpak.body", body); } } void flatpak_oci_parse_commit_annotations (GHashTable *annotations, guint64 *out_timestamp, char **out_subject, char **out_body, char **out_ref, char **out_commit, char **out_parent_commit, GVariantBuilder *metadata_builder) { const char *oci_timestamp, *oci_subject, *oci_body, *oci_parent_commit, *oci_commit, *oci_ref; GHashTableIter iter; gpointer _key, _value; oci_ref = g_hash_table_lookup (annotations, "org.opencontainers.ref.name"); if (oci_ref != NULL && out_ref != NULL && *out_ref == NULL) *out_ref = g_strdup (oci_ref); oci_commit = g_hash_table_lookup (annotations, "org.flatpak.commit"); if (oci_commit != NULL && out_commit != NULL && *out_commit == NULL) *out_commit = g_strdup (oci_commit); oci_parent_commit = g_hash_table_lookup (annotations, "org.flatpak.parent-commit"); if (oci_parent_commit != NULL && out_parent_commit != NULL && *out_parent_commit == NULL) *out_parent_commit = g_strdup (oci_parent_commit); oci_timestamp = g_hash_table_lookup (annotations, "org.flatpak.timestamp"); if (oci_timestamp != NULL && out_timestamp != NULL && *out_timestamp == 0) *out_timestamp = g_ascii_strtoull (oci_timestamp, NULL, 10); oci_subject = g_hash_table_lookup (annotations, "org.flatpak.subject"); if (oci_subject != NULL && out_subject != NULL && *out_subject == NULL) *out_subject = g_strdup (oci_subject); oci_body = g_hash_table_lookup (annotations, "org.flatpak.body"); if (oci_body != NULL && out_body != NULL && *out_body == NULL) *out_body = g_strdup (oci_body); if (metadata_builder) { g_hash_table_iter_init (&iter, annotations); while (g_hash_table_iter_next (&iter, &_key, &_value)) { const char *key = _key; const char *value = _value; guchar *bin; gsize bin_len; g_autoptr(GVariant) data = NULL; if (!g_str_has_prefix (key, "org.flatpak.commit-metadata.")) continue; key += strlen ("org.flatpak.commit-metadata."); bin = g_base64_decode (value, &bin_len); data = g_variant_ref_sink (g_variant_new_from_data (G_VARIANT_TYPE("v"), bin, bin_len, FALSE, g_free, bin)); g_variant_builder_add (metadata_builder, "{s@v}", key, data); } } } G_DEFINE_TYPE (FlatpakOciSignature, flatpak_oci_signature, FLATPAK_TYPE_JSON); static void flatpak_oci_signature_critical_destroy (FlatpakOciSignatureCritical *self) { g_free (self->type); g_free (self->image.digest); g_free (self->identity.ref); } static void flatpak_oci_signature_optional_destroy (FlatpakOciSignatureOptional *self) { g_free (self->creator); } static void flatpak_oci_signature_finalize (GObject *object) { FlatpakOciSignature *self = (FlatpakOciSignature *) object; flatpak_oci_signature_critical_destroy (&self->critical); flatpak_oci_signature_optional_destroy (&self->optional); G_OBJECT_CLASS (flatpak_oci_signature_parent_class)->finalize (object); } static void flatpak_oci_signature_class_init (FlatpakOciSignatureClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass); static FlatpakJsonProp image_props[] = { FLATPAK_JSON_MANDATORY_STRING_PROP (FlatpakOciSignatureCriticalImage, digest, "oci-image-manifest-digest"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp identity_props[] = { FLATPAK_JSON_MANDATORY_STRING_PROP (FlatpakOciSignatureCriticalIdentity, ref, "oci-image-ref"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp critical_props[] = { FLATPAK_JSON_MANDATORY_STRING_PROP (FlatpakOciSignatureCritical, type, "type"), FLATPAK_JSON_MANDATORY_STRICT_STRUCT_PROP(FlatpakOciSignatureCritical, image, "image", image_props), FLATPAK_JSON_MANDATORY_STRICT_STRUCT_PROP(FlatpakOciSignatureCritical, identity, "identity", identity_props), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp optional_props[] = { FLATPAK_JSON_STRING_PROP (FlatpakOciSignatureOptional, creator, "creator"), FLATPAK_JSON_INT64_PROP (FlatpakOciSignatureOptional, timestamp, "timestamp"), FLATPAK_JSON_LAST_PROP }; static FlatpakJsonProp props[] = { FLATPAK_JSON_MANDATORY_STRICT_STRUCT_PROP (FlatpakOciSignature, critical, "critical", critical_props), FLATPAK_JSON_STRUCT_PROP (FlatpakOciSignature, optional, "optional", optional_props), FLATPAK_JSON_LAST_PROP }; object_class->finalize = flatpak_oci_signature_finalize; json_class->props = props; } static void flatpak_oci_signature_init (FlatpakOciSignature *self) { } FlatpakOciSignature * flatpak_oci_signature_new (const char *digest, const char *ref) { FlatpakOciSignature *signature; signature = g_object_new (FLATPAK_TYPE_OCI_SIGNATURE, NULL); /* Some default values */ signature->critical.type = g_strdup (FLATPAK_OCI_SIGNATURE_TYPE_FLATPAK); signature->critical.image.digest = g_strdup (digest); signature->critical.identity.ref = g_strdup (ref); signature->optional.creator = g_strdup ("flatpak " PACKAGE_VERSION); signature->optional.timestamp = time (NULL); return signature; }