/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2014-2018 Richard Hughes * Copyright (C) 2018 Matthias Klumpp * * SPDX-License-Identifier: LGPL-2.1+ */ /** * SECTION:as-release * @short_description: Object representing a single upstream release * @include: appstream-glib.h * @stability: Stable * * This object represents a single upstream release, typically a minor update. * Releases can contain a localized description of paragraph and list elements * and also have a version number and timestamp. * * Releases can be automatically generated by parsing upstream ChangeLogs or * .spec files, or can be populated using AppData files. * * See also: #AsRelease */ #include "config.h" #include #include "as-checksum-private.h" #include "as-node-private.h" #include "as-ref-string.h" #include "as-release-private.h" #include "as-tag.h" #include "as-utils-private.h" #include "as-yaml.h" typedef struct { AsUrgencyKind urgency; AsReleaseKind kind; AsReleaseState state; guint64 *sizes; AsRefString *version; GHashTable *blobs; /* of AsRefString:GBytes */ GHashTable *descriptions; GHashTable *urls; /* of AsRefString:AsRefString */ guint64 timestamp; guint64 install_duration; GPtrArray *locations; /* of AsRefString, lazy */ GPtrArray *checksums; /* of AsChecksum, lazy */ } AsReleasePrivate; G_DEFINE_TYPE_WITH_PRIVATE (AsRelease, as_release, G_TYPE_OBJECT) #define GET_PRIVATE(o) (as_release_get_instance_private (o)) static void as_release_finalize (GObject *object) { AsRelease *release = AS_RELEASE (object); AsReleasePrivate *priv = GET_PRIVATE (release); g_free (priv->sizes); g_hash_table_unref (priv->urls); if (priv->version != NULL) as_ref_string_unref (priv->version); if (priv->blobs != NULL) g_hash_table_unref (priv->blobs); if (priv->checksums != NULL) g_ptr_array_unref (priv->checksums); if (priv->locations != NULL) g_ptr_array_unref (priv->locations); if (priv->descriptions != NULL) g_hash_table_unref (priv->descriptions); G_OBJECT_CLASS (as_release_parent_class)->finalize (object); } static void as_release_init (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); priv->urgency = AS_URGENCY_KIND_UNKNOWN; priv->kind = AS_RELEASE_KIND_UNKNOWN; priv->state = AS_RELEASE_STATE_UNKNOWN; priv->urls = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) as_ref_string_unref, (GDestroyNotify) as_ref_string_unref); } static void as_release_ensure_checksums (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); if (priv->checksums != NULL) return; priv->checksums = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); } static void as_release_ensure_sizes (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); if (priv->sizes != NULL) return; priv->sizes = g_new0 (guint64, AS_SIZE_KIND_LAST); } static void as_release_ensure_locations (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); if (priv->locations != NULL) return; priv->locations = g_ptr_array_new_with_free_func ((GDestroyNotify) as_ref_string_unref); } static void as_release_ensure_blobs (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); if (priv->blobs != NULL) return; priv->blobs = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) as_ref_string_unref, (GDestroyNotify) g_bytes_unref); } static void as_release_class_init (AsReleaseClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = as_release_finalize; } /** * as_release_kind_to_string: * @kind: the #AsReleaseKind. * * Converts the enumerated value to an text representation. * * Returns: string version of @kind * * Since: 0.7.6 **/ const gchar* as_release_kind_to_string (AsReleaseKind kind) { if (kind == AS_RELEASE_KIND_STABLE) return "stable"; if (kind == AS_RELEASE_KIND_DEVELOPMENT) return "development"; return "unknown"; } /** * as_release_kind_from_string: * @kind_str: the string. * * Converts the text representation to an enumerated value. * * Returns: an #AsReleaseKind or %AS_RELEASE_KIND_UNKNOWN for unknown * * Since: 0.7.6 **/ AsReleaseKind as_release_kind_from_string (const gchar *kind_str) { if (g_strcmp0 (kind_str, "stable") == 0) return AS_RELEASE_KIND_STABLE; if (g_strcmp0 (kind_str, "development") == 0) return AS_RELEASE_KIND_DEVELOPMENT; return AS_RELEASE_KIND_UNKNOWN; } /** * as_release_state_from_string: * @state: a string * * Converts the text representation to an enumerated value. * * Return value: A #AsReleaseState, e.g. %AS_RELEASE_STATE_INSTALLED. * * Since: 0.6.6 **/ AsReleaseState as_release_state_from_string (const gchar *state) { if (g_strcmp0 (state, "installed") == 0) return AS_RELEASE_STATE_INSTALLED; if (g_strcmp0 (state, "available") == 0) return AS_RELEASE_STATE_AVAILABLE; return AS_APP_MERGE_KIND_NONE; } /** * as_release_state_to_string: * @state: the #AsReleaseState, e.g. %AS_RELEASE_STATE_INSTALLED * * Converts the enumerated value to an text representation. * * Returns: string version of @state, or %NULL for unknown * * Since: 0.6.6 **/ const gchar * as_release_state_to_string (AsReleaseState state) { if (state == AS_RELEASE_STATE_INSTALLED) return "installed"; if (state == AS_RELEASE_STATE_AVAILABLE) return "available"; return NULL; } /** * as_release_vercmp: * @rel1: a #AsRelease instance. * @rel2: a #AsRelease instance. * * Compares two release. * * Returns: -1 if rel1 > rel2, +1 if rel1 < rel2, 0 otherwise * * Since: 0.4.2 **/ gint as_release_vercmp (AsRelease *rel1, AsRelease *rel2) { AsReleasePrivate *priv1 = GET_PRIVATE (rel1); AsReleasePrivate *priv2 = GET_PRIVATE (rel2); gint val; g_return_val_if_fail (AS_IS_RELEASE (rel1), 0); g_return_val_if_fail (AS_IS_RELEASE (rel2), 0); /* prefer the timestamp */ if (priv1->timestamp > priv2->timestamp) return -1; if (priv1->timestamp < priv2->timestamp) return 1; /* fall back to the version strings */ val = as_utils_vercmp_full (priv2->version, priv1->version, AS_VERSION_COMPARE_FLAG_NONE); if (val != G_MAXINT) return val; return 0; } /** * as_release_get_size: * @release: a #AsRelease instance * @kind: a #AsSizeKind, e.g. #AS_SIZE_KIND_DOWNLOAD * * Gets the release size. * * Returns: The size in bytes, or 0 for unknown. * * Since: 0.5.2 **/ guint64 as_release_get_size (AsRelease *release, AsSizeKind kind) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), 0); if (kind >= AS_SIZE_KIND_LAST) return 0; if (priv->sizes == NULL) return 0; return priv->sizes[kind]; } /** * as_release_set_size: * @release: a #AsRelease instance * @kind: a #AsSizeKind, e.g. #AS_SIZE_KIND_DOWNLOAD * @size: a size in bytes, or 0 for unknown * * Sets the release size. * * Since: 0.5.2 **/ void as_release_set_size (AsRelease *release, AsSizeKind kind, guint64 size) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); if (kind >= AS_SIZE_KIND_LAST) return; as_release_ensure_sizes (release); priv->sizes[kind] = size; } /** * as_release_get_urgency: * @release: a #AsRelease instance. * * Gets the release urgency. * * Returns: enumberated value, or %AS_URGENCY_KIND_UNKNOWN for not set or invalid * * Since: 0.5.1 **/ AsUrgencyKind as_release_get_urgency (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), AS_URGENCY_KIND_UNKNOWN); return priv->urgency; } /** * as_release_get_state: * @release: a #AsRelease instance. * * Gets the release state. * * Returns: enumberated value, or %AS_RELEASE_STATE_UNKNOWN for not set or invalid * * Since: 0.5.8 **/ AsReleaseState as_release_get_state (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), AS_RELEASE_STATE_UNKNOWN); return priv->state; } /** * as_release_get_kind: * @release: a #AsRelease instance. * * Gets the type of the release. * * Returns: enumerated value, e.g. %AS_RELEASE_KIND_STABLE * * Since: 0.7.6 **/ AsReleaseKind as_release_get_kind (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), AS_RELEASE_KIND_UNKNOWN); return priv->kind; } /** * as_release_get_version: * @release: a #AsRelease instance. * * Gets the release version. * * Returns: string, or %NULL for not set or invalid * * Since: 0.1.0 **/ const gchar * as_release_get_version (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); return priv->version; } /** * as_release_get_blob: * @release: a #AsRelease instance. * @filename: a filename * * Gets the release blob, which is typically firmware file data. * * Returns: a #GBytes, or %NULL for not set * * Since: 0.5.2 **/ GBytes * as_release_get_blob (AsRelease *release, const gchar *filename) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); g_return_val_if_fail (filename != NULL, NULL); if (priv->blobs == NULL) return NULL; return g_hash_table_lookup (priv->blobs, filename); } /** * as_release_get_locations: * @release: a #AsRelease instance. * * Gets the release locations, typically URLs. * * Returns: (transfer none) (element-type utf8): list of locations * * Since: 0.3.5 **/ GPtrArray * as_release_get_locations (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); as_release_ensure_locations (release); return priv->locations; } /** * as_release_get_location_default: * @release: a #AsRelease instance. * * Gets the default release location, typically a URL. * * Returns: string, or %NULL for not set or invalid * * Since: 0.3.5 **/ const gchar * as_release_get_location_default (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); if (priv->locations == NULL) return NULL; if (priv->locations->len == 0) return NULL; return g_ptr_array_index (priv->locations, 0); } /** * as_release_get_checksums: * @release: a #AsRelease instance. * * Gets the release checksums. * * Returns: (transfer none) (element-type AsChecksum): list of checksums * * Since: 0.4.2 **/ GPtrArray * as_release_get_checksums (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); as_release_ensure_checksums (release); return priv->checksums; } /** * as_release_get_checksum_by_fn: * @release: a #AsRelease instance * @fn: a file basename * * Gets the checksum for a release. * * Returns: (transfer none): an #AsChecksum, or %NULL for not found * * Since: 0.4.2 **/ AsChecksum * as_release_get_checksum_by_fn (AsRelease *release, const gchar *fn) { AsChecksum *checksum; AsReleasePrivate *priv = GET_PRIVATE (release); guint i; g_return_val_if_fail (AS_IS_RELEASE (release), NULL); for (i = 0; i < priv->checksums->len; i++) { checksum = g_ptr_array_index (priv->checksums, i); if (g_strcmp0 (fn, as_checksum_get_filename (checksum)) == 0) return checksum; } return NULL; } /** * as_release_get_checksum_by_target: * @release: a #AsRelease instance * @target: a #AsChecksumTarget, e.g. %AS_CHECKSUM_TARGET_CONTAINER * * Gets the checksum for a release. * * Returns: (transfer none): an #AsChecksum, or %NULL for not found * * Since: 0.4.2 **/ AsChecksum * as_release_get_checksum_by_target (AsRelease *release, AsChecksumTarget target) { AsChecksum *checksum; AsReleasePrivate *priv = GET_PRIVATE (release); guint i; g_return_val_if_fail (AS_IS_RELEASE (release), NULL); if (priv->checksums == NULL) return NULL; for (i = 0; i < priv->checksums->len; i++) { checksum = g_ptr_array_index (priv->checksums, i); if (as_checksum_get_target (checksum) == target) return checksum; } return NULL; } /** * as_release_get_timestamp: * @release: a #AsRelease instance. * * Gets the release timestamp. * * Returns: timestamp, or 0 for unset * * Since: 0.1.0 **/ guint64 as_release_get_timestamp (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), 0); return priv->timestamp; } /** * as_release_get_install_duration: * @release: a #AsRelease instance. * * Gets the typical install duration. * * Returns: install duration in seconds, or 0 for unset * * Since: 0.7.15 **/ guint64 as_release_get_install_duration (AsRelease *release) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), 0); return priv->install_duration; } /** * as_release_get_description: * @release: a #AsRelease instance. * @locale: (nullable): the locale. e.g. "en_GB" * * Gets the release description markup for a given locale. * * Returns: markup, or %NULL for not set or invalid * * Since: 0.1.0 **/ const gchar * as_release_get_description (AsRelease *release, const gchar *locale) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (AS_IS_RELEASE (release), NULL); if (priv->descriptions == NULL) return NULL; return as_hash_lookup_by_locale (priv->descriptions, locale); } /** * as_release_set_version: * @release: a #AsRelease instance. * @version: the version string. * * Sets the release version. * * Since: 0.1.0 **/ void as_release_set_version (AsRelease *release, const gchar *version) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); as_ref_string_assign_safe (&priv->version, version); } /** * as_release_set_blob: * @release: a #AsRelease instance. * @filename: a filename * @blob: the #GBytes data blob * * Sets a release blob, which is typically firmware data or a detached signature. * * NOTE: This is not stored in the XML file, and is only available in-memory. * * Since: 0.5.2 **/ void as_release_set_blob (AsRelease *release, const gchar *filename, GBytes *blob) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); g_return_if_fail (filename != NULL); g_return_if_fail (blob != NULL); as_release_ensure_blobs (release); g_hash_table_insert (priv->blobs, as_ref_string_new (filename), g_bytes_ref (blob)); } /** * as_release_set_urgency: * @release: a #AsRelease instance. * @urgency: the release urgency, e.g. %AS_URGENCY_KIND_CRITICAL * * Sets the release urgency. * * Since: 0.5.1 **/ void as_release_set_urgency (AsRelease *release, AsUrgencyKind urgency) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); priv->urgency = urgency; } /** * as_release_set_kind: * @release: a #AsRelease instance. * @kind: the #AsReleaseKind * * Sets the release kind. * * Since: 0.7.6 **/ void as_release_set_kind (AsRelease *release, AsReleaseKind kind) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); priv->kind = kind; } /** * as_release_set_state: * @release: a #AsRelease instance. * @state: the release state, e.g. %AS_RELEASE_STATE_INSTALLED * * Sets the release state. * * Since: 0.5.8 **/ void as_release_set_state (AsRelease *release, AsReleaseState state) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); priv->state = state; } /** * as_release_add_location: * @release: a #AsRelease instance. * @location: the location string. * * Adds a release location. * * Since: 0.3.5 **/ void as_release_add_location (AsRelease *release, const gchar *location) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); /* deduplicate */ as_release_ensure_locations (release); if (as_ptr_array_find_string (priv->locations, location)) return; g_ptr_array_add (priv->locations, as_ref_string_new (location)); } /** * as_release_add_checksum: * @release: a #AsRelease instance. * @checksum: a #AsChecksum instance. * * Adds a release checksum. * * Since: 0.4.2 **/ void as_release_add_checksum (AsRelease *release, AsChecksum *checksum) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); as_release_ensure_checksums (release); g_ptr_array_add (priv->checksums, g_object_ref (checksum)); } /** * as_release_set_timestamp: * @release: a #AsRelease instance. * @timestamp: the timestamp value. * * Sets the release timestamp. * * Since: 0.1.0 **/ void as_release_set_timestamp (AsRelease *release, guint64 timestamp) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); priv->timestamp = timestamp; } /** * as_release_set_install_duration: * @release: a #AsRelease instance. * @install_duration: the install duration in seconds * * Sets the typical duration of the install. * * Since: 0.7.15 **/ void as_release_set_install_duration (AsRelease *release, guint64 install_duration) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); priv->install_duration = install_duration; } /** * as_release_set_description: * @release: a #AsRelease instance. * @locale: (nullable): the locale. e.g. "en_GB" * @description: the description markup. * * Sets the description release markup. * * Since: 0.1.0 **/ void as_release_set_description (AsRelease *release, const gchar *locale, const gchar *description) { AsReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (AS_IS_RELEASE (release)); if (locale == NULL) locale = "C"; if (priv->descriptions == NULL) { priv->descriptions = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) as_ref_string_unref, (GDestroyNotify) as_ref_string_unref); } g_hash_table_insert (priv->descriptions, as_ref_string_new (locale), as_ref_string_new (description)); } /** * as_release_get_url: * @release: a #AsRelease instance. * @url_kind: the URL kind, e.g. %AS_URL_KIND_HOMEPAGE. * * Gets a URL. * * Returns: string, or %NULL if unset * * Since: 0.7.15 **/ const gchar * as_release_get_url (AsRelease *release, AsUrlKind url_kind) { AsReleasePrivate *priv = GET_PRIVATE (release); return g_hash_table_lookup (priv->urls, as_url_kind_to_string (url_kind)); } /** * as_release_set_url: * @release: a #AsRelease instance. * @url_kind: the URL kind, e.g. %AS_URL_KIND_DETAILS * @url: the full URL. * * Adds some URL data to the application. * * Since: 0.7.15 **/ void as_release_set_url (AsRelease *release, AsUrlKind url_kind, const gchar *url) { AsReleasePrivate *priv = GET_PRIVATE (release); if (url == NULL) { g_hash_table_remove (priv->urls, as_url_kind_to_string (url_kind)); } else { g_hash_table_insert (priv->urls, (AsRefString *) as_url_kind_to_string (url_kind), as_ref_string_new (url)); } } /** * as_release_node_insert: (skip) * @release: a #AsRelease instance. * @parent: the parent #GNode to use.. * @ctx: the #AsNodeContext * * Inserts the release into the DOM tree. * * Returns: (transfer none): A populated #GNode * * Since: 0.1.1 **/ GNode * as_release_node_insert (AsRelease *release, GNode *parent, AsNodeContext *ctx) { AsReleasePrivate *priv = GET_PRIVATE (release); AsChecksum *checksum; GNode *n; g_return_val_if_fail (AS_IS_RELEASE (release), NULL); n = as_node_insert (parent, "release", NULL, AS_NODE_INSERT_FLAG_NONE, NULL); if (priv->timestamp > 0) { g_autofree gchar *timestamp_str = NULL; timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->timestamp); as_node_add_attribute (n, "timestamp", timestamp_str); } if (priv->urgency != AS_URGENCY_KIND_UNKNOWN) { as_node_add_attribute (n, "urgency", as_urgency_kind_to_string (priv->urgency)); } if (priv->kind != AS_RELEASE_KIND_UNKNOWN) { as_node_add_attribute (n, "type", as_release_kind_to_string (priv->kind)); } if (as_node_context_get_output_trusted (ctx) && priv->state != AS_RELEASE_STATE_UNKNOWN) { as_node_add_attribute (n, "state", as_release_state_to_string (priv->state)); } if (priv->version != NULL) as_node_add_attribute (n, "version", priv->version); if (priv->install_duration > 0) { g_autofree gchar *install_duration_str = NULL; install_duration_str = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->install_duration); as_node_add_attribute (n, "install_duration", install_duration_str); } for (guint i = 0; priv->locations != NULL && i < priv->locations->len; i++) { const gchar *tmp = g_ptr_array_index (priv->locations, i); as_node_insert (n, "location", tmp, AS_NODE_INSERT_FLAG_NONE, NULL); } for (guint i = 0; priv->checksums != NULL && i < priv->checksums->len; i++) { checksum = g_ptr_array_index (priv->checksums, i); as_checksum_node_insert (checksum, n, ctx); } if (priv->urls != NULL) as_node_insert_hash (n, "url", "type", priv->urls, 0); if (priv->descriptions != NULL) { as_node_insert_localized (n, "description", priv->descriptions, AS_NODE_INSERT_FLAG_PRE_ESCAPED | AS_NODE_INSERT_FLAG_DEDUPE_LANG); } /* add sizes */ if (priv->sizes != NULL) { for (guint i = 0; i < AS_SIZE_KIND_LAST; i++) { g_autofree gchar *size_str = NULL; if (priv->sizes[i] == 0) continue; size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->sizes[i]); as_node_insert (n, "size", size_str, AS_NODE_INSERT_FLAG_NONE, "type", as_size_kind_to_string (i), NULL); } } return n; } /** * as_release_node_parse: * @release: a #AsRelease instance. * @node: a #GNode. * @ctx: a #AsNodeContext. * @error: A #GError or %NULL. * * Populates the object from a DOM node. * * Returns: %TRUE for success * * Since: 0.1.0 **/ gboolean as_release_node_parse (AsRelease *release, GNode *node, AsNodeContext *ctx, GError **error) { AsReleasePrivate *priv = GET_PRIVATE (release); GNode *n; const gchar *tmp; g_return_val_if_fail (AS_IS_RELEASE (release), FALSE); tmp = as_node_get_attribute (node, "timestamp"); if (tmp != NULL) as_release_set_timestamp (release, g_ascii_strtoull (tmp, NULL, 10)); tmp = as_node_get_attribute (node, "date"); if (tmp != NULL) { g_autoptr(GDateTime) dt = NULL; dt = as_utils_iso8601_to_datetime (tmp); if (dt != NULL) as_release_set_timestamp (release, (guint64) g_date_time_to_unix (dt)); } tmp = as_node_get_attribute (node, "urgency"); if (tmp != NULL) as_release_set_urgency (release, as_urgency_kind_from_string (tmp)); tmp = as_node_get_attribute (node, "type"); if (tmp != NULL) as_release_set_kind (release, as_release_kind_from_string (tmp)); tmp = as_node_get_attribute (node, "version"); if (tmp != NULL) as_release_set_version (release, tmp); tmp = as_node_get_attribute (node, "install_duration"); if (tmp != NULL) priv->install_duration = g_ascii_strtoull (tmp, NULL, 10); /* */ for (n = node->children; n != NULL; n = n->next) { if (as_node_get_tag (n) != AS_TAG_URL) continue; tmp = as_node_get_attribute (n, "type"); as_release_set_url (release, as_url_kind_from_string (tmp), as_node_get_data (n)); } /* get optional locations */ if (priv->locations != NULL) g_ptr_array_set_size (priv->locations, 0); for (n = node->children; n != NULL; n = n->next) { AsRefString *str; if (as_node_get_tag (n) != AS_TAG_LOCATION) continue; str = as_node_get_data_as_refstr (n); if (str == NULL) continue; as_release_ensure_locations (release); g_ptr_array_add (priv->locations, as_ref_string_ref (str)); } /* get optional checksums */ for (n = node->children; n != NULL; n = n->next) { g_autoptr(AsChecksum) csum = NULL; if (as_node_get_tag (n) != AS_TAG_CHECKSUM) continue; csum = as_checksum_new (); if (!as_checksum_node_parse (csum, n, ctx, error)) return FALSE; as_release_add_checksum (release, csum); } /* get optional sizes */ for (n = node->children; n != NULL; n = n->next) { AsSizeKind kind; if (as_node_get_tag (n) != AS_TAG_SIZE) continue; tmp = as_node_get_attribute (n, "type"); if (tmp == NULL) continue; kind = as_size_kind_from_string (tmp); if (kind == AS_SIZE_KIND_UNKNOWN) continue; tmp = as_node_get_data (n); if (tmp == NULL) continue; as_release_ensure_sizes (release); priv->sizes[kind] = g_ascii_strtoull (tmp, NULL, 10); } /* AppStream: multiple tags */ if (as_node_context_get_format_kind (ctx) == AS_FORMAT_KIND_APPSTREAM) { for (n = node->children; n != NULL; n = n->next) { g_autoptr(GString) xml = NULL; if (as_node_get_tag (n) != AS_TAG_DESCRIPTION) continue; if (n->children == NULL) continue; xml = as_node_to_xml (n->children, AS_NODE_TO_XML_FLAG_INCLUDE_SIBLINGS); if (xml == NULL) continue; as_release_set_description (release, as_node_get_attribute (n, "xml:lang"), xml->str); } /* AppData: multiple languages encoded in one tag */ } else { n = as_node_find (node, "description"); if (n != NULL) { if (priv->descriptions != NULL) g_hash_table_unref (priv->descriptions); priv->descriptions = as_node_get_localized_unwrap (n, error); if (priv->descriptions == NULL) return FALSE; } } return TRUE; } /** * as_release_node_parse_dep11: * @release: a #AsRelease instance. * @node: a #GNode. * @ctx: a #AsNodeContext. * @error: A #GError or %NULL. * * Populates the object from a DEP-11 node. * * Returns: %TRUE for success * * Since: 0.5.13 **/ gboolean as_release_node_parse_dep11 (AsRelease *release, GNode *node, AsNodeContext *ctx, GError **error) { GNode *c; GNode *n; const gchar *tmp; const gchar *value; g_return_val_if_fail (AS_IS_RELEASE (release), FALSE); for (n = node->children; n != NULL; n = n->next) { tmp = as_yaml_node_get_key (n); if (g_strcmp0 (tmp, "unix-timestamp") == 0) { value = as_yaml_node_get_value (n); as_release_set_timestamp (release, g_ascii_strtoull (value, NULL, 10)); continue; } if (g_strcmp0 (tmp, "version") == 0) { as_release_set_version (release, as_yaml_node_get_value (n)); continue; } if (g_strcmp0 (tmp, "type") == 0) { as_release_set_kind (release, as_release_kind_from_string (as_yaml_node_get_value (n))); continue; } if (g_strcmp0 (tmp, "description") == 0) { for (c = n->children; c != NULL; c = c->next) { as_release_set_description (release, as_yaml_node_get_key (c), as_yaml_node_get_value (c)); } continue; } if (g_strcmp0 (tmp, "url") == 0) { for (c = n->children; c != NULL; c = c->next) { if (g_strcmp0 (as_yaml_node_get_key (c), "details") == 0) { as_release_set_url (release, AS_URL_KIND_DETAILS, as_yaml_node_get_value (c)); continue; } } continue; } } return TRUE; } /** * as_release_new: * * Creates a new #AsRelease. * * Returns: (transfer full): a #AsRelease * * Since: 0.1.0 **/ AsRelease * as_release_new (void) { AsRelease *release; release = g_object_new (AS_TYPE_RELEASE, NULL); return AS_RELEASE (release); }