From 6e81bc6f28efa987fab4bf527c5fbd055bc3dc2b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 17 May 2023 14:12:20 +0100 Subject: [PATCH] common: Move functions for dealing with AppStream XML to their own file This seems like an obvious set of functionality to be grouped together in service of the wider goal of making flatpak-utils smaller and more manageable. Signed-off-by: Simon McVittie --- app/flatpak-builtins.h | 1 + common/flatpak-dir.c | 1 + common/flatpak-oci-registry.c | 1 + common/flatpak-utils-private.h | 46 --- common/flatpak-utils.c | 535 +-------------------------- common/flatpak-xml-utils-private.h | 67 ++++ common/flatpak-xml-utils.c | 560 +++++++++++++++++++++++++++++ common/meson.build | 1 + 8 files changed, 632 insertions(+), 580 deletions(-) create mode 100644 common/flatpak-xml-utils-private.h create mode 100644 common/flatpak-xml-utils.c diff --git a/app/flatpak-builtins.h b/app/flatpak-builtins.h index 7ea0b4f1..e4136ed9 100644 --- a/app/flatpak-builtins.h +++ b/app/flatpak-builtins.h @@ -28,6 +28,7 @@ #include "flatpak-tty-utils-private.h" #include "flatpak-utils-private.h" #include "flatpak-dir-private.h" +#include "flatpak-xml-utils-private.h" G_BEGIN_DECLS diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index af68db67..bb7cfe22 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -57,6 +57,7 @@ #include "flatpak-utils-base-private.h" #include "flatpak-variant-private.h" #include "flatpak-variant-impl-private.h" +#include "flatpak-xml-utils-private.h" #include "libglnx.h" #include "system-helper/flatpak-system-helper.h" diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c index dddfe664..8b63de99 100644 --- a/common/flatpak-oci-registry.c +++ b/common/flatpak-oci-registry.c @@ -32,6 +32,7 @@ #include "flatpak-utils-private.h" #include "flatpak-uri-private.h" #include "flatpak-dir-private.h" +#include "flatpak-xml-utils-private.h" #include "flatpak-zstd-decompressor-private.h" #define MAX_JSON_SIZE (1024 * 1024) diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h index 5d32b650..53eb198b 100644 --- a/common/flatpak-utils-private.h +++ b/common/flatpak-utils-private.h @@ -554,58 +554,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakRepoTransaction, flatpak_repo_transaction_ #define AUTOLOCK(name) G_GNUC_UNUSED __attribute__((cleanup (flatpak_auto_unlock_helper))) GMutex * G_PASTE (auto_unlock, __LINE__) = flatpak_auto_lock_helper (&G_LOCK_NAME (name)) -typedef struct FlatpakXml FlatpakXml; - -struct FlatpakXml -{ - gchar *element_name; /* NULL == text */ - char **attribute_names; - char **attribute_values; - char *text; - FlatpakXml *parent; - FlatpakXml *first_child; - FlatpakXml *last_child; - FlatpakXml *next_sibling; -}; - -FlatpakXml *flatpak_xml_new (const gchar *element_name); -FlatpakXml *flatpak_xml_new_text (const gchar *text); -void flatpak_xml_add (FlatpakXml *parent, - FlatpakXml *node); -void flatpak_xml_free (FlatpakXml *node); -FlatpakXml *flatpak_xml_parse (GInputStream * in, - gboolean compressed, - GCancellable *cancellable, - GError **error); -void flatpak_xml_to_string (FlatpakXml *node, - GString *res); -FlatpakXml *flatpak_xml_unlink (FlatpakXml *node, - FlatpakXml *prev_sibling); -FlatpakXml *flatpak_xml_find (FlatpakXml *node, - const char *type, - FlatpakXml **prev_child_out); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakXml, flatpak_xml_free); - -FlatpakXml *flatpak_appstream_xml_new (void); -gboolean flatpak_appstream_xml_migrate (FlatpakXml *source, - FlatpakXml *dest, - const char *ref, - const char *id, - GKeyFile *metadata); -gboolean flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, - GBytes **uncompressed, - GBytes **compressed, - GError **error); gboolean flatpak_repo_generate_appstream (OstreeRepo *repo, const char **gpg_key_ids, const char *gpg_homedir, guint64 timestamp, GCancellable *cancellable, GError **error); -void flatpak_appstream_xml_filter (FlatpakXml *appstream, - GRegex *allow_refs, - GRegex *deny_refs); char * flatpak_filter_glob_to_regexp (const char *glob, gboolean runtime_only, GError **error); gboolean flatpak_parse_filters (const char *data, diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index 86061914..c6d98490 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -50,6 +50,7 @@ #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" #include "flatpak-variant-impl-private.h" +#include "flatpak-xml-utils-private.h" #include "libglnx.h" #include "valgrind-private.h" @@ -4963,164 +4964,6 @@ flatpak_mtree_ensure_dir_metadata (OstreeRepo *repo, return TRUE; } -static gboolean -validate_component (FlatpakXml *component, - const char *ref, - const char *id, - char **tags, - const char *runtime, - const char *sdk) -{ - FlatpakXml *bundle, *text, *prev, *id_node, *id_text_node, *metadata, *value; - g_autofree char *id_text = NULL; - int i; - - if (g_strcmp0 (component->element_name, "component") != 0) - return FALSE; - - id_node = flatpak_xml_find (component, "id", NULL); - if (id_node == NULL) - return FALSE; - - id_text_node = flatpak_xml_find (id_node, NULL, NULL); - if (id_text_node == NULL || id_text_node->text == NULL) - return FALSE; - - id_text = g_strstrip (g_strdup (id_text_node->text)); - - /* Drop .desktop file suffix (unless the actual app id ends with .desktop) */ - if (g_str_has_suffix (id_text, ".desktop") && - !g_str_has_suffix (id, ".desktop")) - id_text[strlen (id_text) - strlen (".desktop")] = 0; - - if (!g_str_has_prefix (id_text, id)) - { - g_warning ("Invalid id %s", id_text); - return FALSE; - } - - while ((bundle = flatpak_xml_find (component, "bundle", &prev)) != NULL) - flatpak_xml_free (flatpak_xml_unlink (bundle, prev)); - - bundle = flatpak_xml_new ("bundle"); - bundle->attribute_names = g_new0 (char *, 2 * 4); - bundle->attribute_values = g_new0 (char *, 2 * 4); - bundle->attribute_names[0] = g_strdup ("type"); - bundle->attribute_values[0] = g_strdup ("flatpak"); - - i = 1; - if (runtime && !g_str_has_prefix (runtime, "runtime/")) - { - bundle->attribute_names[i] = g_strdup ("runtime"); - bundle->attribute_values[i] = g_strdup (runtime); - i++; - } - - if (sdk) - { - bundle->attribute_names[i] = g_strdup ("sdk"); - bundle->attribute_values[i] = g_strdup (sdk); - i++; - } - - text = flatpak_xml_new (NULL); - text->text = g_strdup (ref); - flatpak_xml_add (bundle, text); - - flatpak_xml_add (component, flatpak_xml_new_text (" ")); - flatpak_xml_add (component, bundle); - flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); - - if (tags != NULL && tags[0] != NULL) - { - metadata = flatpak_xml_find (component, "metadata", NULL); - if (metadata == NULL) - { - metadata = flatpak_xml_new ("metadata"); - metadata->attribute_names = g_new0 (char *, 1); - metadata->attribute_values = g_new0 (char *, 1); - - flatpak_xml_add (component, flatpak_xml_new_text (" ")); - flatpak_xml_add (component, metadata); - flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); - } - - value = flatpak_xml_new ("value"); - value->attribute_names = g_new0 (char *, 2); - value->attribute_values = g_new0 (char *, 2); - value->attribute_names[0] = g_strdup ("key"); - value->attribute_values[0] = g_strdup ("X-Flatpak-Tags"); - flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); - flatpak_xml_add (metadata, value); - flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); - - text = flatpak_xml_new (NULL); - text->text = g_strjoinv (",", tags); - flatpak_xml_add (value, text); - } - - return TRUE; -} - -gboolean -flatpak_appstream_xml_migrate (FlatpakXml *source, - FlatpakXml *dest, - const char *ref, - const char *id, - GKeyFile *metadata) -{ - FlatpakXml *source_components; - FlatpakXml *dest_components; - FlatpakXml *component; - FlatpakXml *prev_component; - gboolean migrated = FALSE; - g_auto(GStrv) tags = NULL; - g_autofree const char *runtime = NULL; - g_autofree const char *sdk = NULL; - const char *group; - - if (source->first_child == NULL || - source->first_child->next_sibling != NULL || - g_strcmp0 (source->first_child->element_name, "components") != 0) - return FALSE; - - if (g_str_has_prefix (ref, "app/")) - group = FLATPAK_METADATA_GROUP_APPLICATION; - else - group = FLATPAK_METADATA_GROUP_RUNTIME; - - tags = g_key_file_get_string_list (metadata, group, FLATPAK_METADATA_KEY_TAGS, - NULL, NULL); - runtime = g_key_file_get_string (metadata, group, - FLATPAK_METADATA_KEY_RUNTIME, NULL); - sdk = g_key_file_get_string (metadata, group, FLATPAK_METADATA_KEY_SDK, NULL); - - source_components = source->first_child; - dest_components = dest->first_child; - - component = source_components->first_child; - prev_component = NULL; - while (component != NULL) - { - FlatpakXml *next = component->next_sibling; - - if (validate_component (component, ref, id, tags, runtime, sdk)) - { - flatpak_xml_add (dest_components, - flatpak_xml_unlink (component, prev_component)); - migrated = TRUE; - } - else - { - prev_component = component; - } - - component = next; - } - - return migrated; -} - static gboolean copy_icon (const char *id, GFile *icons_dir, @@ -5262,114 +5105,6 @@ extract_appstream (OstreeRepo *repo, return TRUE; } -FlatpakXml * -flatpak_appstream_xml_new (void) -{ - FlatpakXml *appstream_root = NULL; - FlatpakXml *appstream_components; - - appstream_root = flatpak_xml_new ("root"); - appstream_components = flatpak_xml_new ("components"); - flatpak_xml_add (appstream_root, appstream_components); - flatpak_xml_add (appstream_components, flatpak_xml_new_text ("\n ")); - - appstream_components->attribute_names = g_new0 (char *, 3); - appstream_components->attribute_values = g_new0 (char *, 3); - appstream_components->attribute_names[0] = g_strdup ("version"); - appstream_components->attribute_values[0] = g_strdup ("0.8"); - appstream_components->attribute_names[1] = g_strdup ("origin"); - appstream_components->attribute_values[1] = g_strdup ("flatpak"); - - return appstream_root; -} - -gboolean -flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, - GBytes **uncompressed, - GBytes **compressed, - GError **error) -{ - g_autoptr(GString) xml = NULL; - g_autoptr(GZlibCompressor) compressor = NULL; - g_autoptr(GOutputStream) out2 = NULL; - g_autoptr(GOutputStream) out = NULL; - - flatpak_xml_add (appstream_root->first_child, flatpak_xml_new_text ("\n")); - - xml = g_string_new (""); - flatpak_xml_to_string (appstream_root, xml); - - if (compressed) - { - compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1); - out = g_memory_output_stream_new_resizable (); - out2 = g_converter_output_stream_new (out, G_CONVERTER (compressor)); - if (!g_output_stream_write_all (out2, xml->str, xml->len, - NULL, NULL, error)) - return FALSE; - if (!g_output_stream_close (out2, NULL, error)) - return FALSE; - } - - if (uncompressed) - *uncompressed = g_string_free_to_bytes (g_steal_pointer (&xml)); - - if (compressed) - *compressed = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out)); - - return TRUE; -} - -void -flatpak_appstream_xml_filter (FlatpakXml *appstream, - GRegex *allow_refs, - GRegex *deny_refs) -{ - FlatpakXml *components; - FlatpakXml *component; - FlatpakXml *prev_component, *old; - - for (components = appstream->first_child; - components != NULL; - components = components->next_sibling) - { - if (g_strcmp0 (components->element_name, "components") != 0) - continue; - - - prev_component = NULL; - component = components->first_child; - while (component != NULL) - { - FlatpakXml *bundle; - gboolean allow = FALSE; - - if (g_strcmp0 (component->element_name, "component") == 0) - { - bundle = flatpak_xml_find (component, "bundle", NULL); - if (bundle && bundle->first_child && bundle->first_child->text) - allow = flatpak_filters_allow_ref (allow_refs, deny_refs, bundle->first_child->text); - } - - if (allow) - { - prev_component = component; - component = component->next_sibling; - } - else - { - old = component; - - /* prev_component is same as before */ - component = component->next_sibling; - - flatpak_xml_unlink (old, prev_component); - flatpak_xml_free (old); - } - } - } -} - /* This is similar to ostree_repo_list_refs(), but returns only valid flatpak * refs, as FlatpakDecomposed. */ static GHashTable * @@ -6125,274 +5860,6 @@ flatpak_list_extensions (GKeyFile *metakey, return g_list_sort (g_list_reverse (res), flatpak_extension_compare); } -typedef struct -{ - FlatpakXml *current; -} XmlData; - -FlatpakXml * -flatpak_xml_new (const gchar *element_name) -{ - FlatpakXml *node = g_new0 (FlatpakXml, 1); - - node->element_name = g_strdup (element_name); - return node; -} - -FlatpakXml * -flatpak_xml_new_text (const gchar *text) -{ - FlatpakXml *node = g_new0 (FlatpakXml, 1); - - node->text = g_strdup (text); - return node; -} - -void -flatpak_xml_add (FlatpakXml *parent, FlatpakXml *node) -{ - node->parent = parent; - - if (parent->first_child == NULL) - parent->first_child = node; - else - parent->last_child->next_sibling = node; - parent->last_child = node; -} - -static void -xml_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - FlatpakXml *node; - - node = flatpak_xml_new (element_name); - node->attribute_names = g_strdupv ((char **) attribute_names); - node->attribute_values = g_strdupv ((char **) attribute_values); - - flatpak_xml_add (data->current, node); - data->current = node; -} - -static void -xml_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - - data->current = data->current->parent; -} - -static void -xml_text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - FlatpakXml *node; - - node = flatpak_xml_new (NULL); - node->text = g_strndup (text, text_len); - flatpak_xml_add (data->current, node); -} - -static void -xml_passthrough (GMarkupParseContext *context, - const gchar *passthrough_text, - gsize text_len, - gpointer user_data, - GError **error) -{ -} - -static GMarkupParser xml_parser = { - xml_start_element, - xml_end_element, - xml_text, - xml_passthrough, - NULL -}; - -void -flatpak_xml_free (FlatpakXml *node) -{ - FlatpakXml *child; - - if (node == NULL) - return; - - child = node->first_child; - while (child != NULL) - { - FlatpakXml *next = child->next_sibling; - flatpak_xml_free (child); - child = next; - } - - g_free (node->element_name); - g_free (node->text); - g_strfreev (node->attribute_names); - g_strfreev (node->attribute_values); - g_free (node); -} - - -void -flatpak_xml_to_string (FlatpakXml *node, GString *res) -{ - int i; - FlatpakXml *child; - - if (node->parent == NULL) - g_string_append (res, "\n"); - - if (node->element_name) - { - if (node->parent != NULL) - { - g_string_append (res, "<"); - g_string_append (res, node->element_name); - if (node->attribute_names) - { - for (i = 0; node->attribute_names[i] != NULL; i++) - { - g_string_append_printf (res, " %s=\"%s\"", - node->attribute_names[i], - node->attribute_values[i]); - } - } - if (node->first_child == NULL) - g_string_append (res, "/>"); - else - g_string_append (res, ">"); - } - - child = node->first_child; - while (child != NULL) - { - flatpak_xml_to_string (child, res); - child = child->next_sibling; - } - if (node->parent != NULL) - { - if (node->first_child != NULL) - g_string_append_printf (res, "", node->element_name); - } - } - else if (node->text) - { - g_autofree char *escaped = g_markup_escape_text (node->text, -1); - g_string_append (res, escaped); - } -} - -FlatpakXml * -flatpak_xml_unlink (FlatpakXml *node, - FlatpakXml *prev_sibling) -{ - FlatpakXml *parent = node->parent; - - if (parent == NULL) - return node; - - if (parent->first_child == node) - parent->first_child = node->next_sibling; - - if (parent->last_child == node) - parent->last_child = prev_sibling; - - if (prev_sibling) - prev_sibling->next_sibling = node->next_sibling; - - node->parent = NULL; - node->next_sibling = NULL; - - return node; -} - -FlatpakXml * -flatpak_xml_find (FlatpakXml *node, - const char *type, - FlatpakXml **prev_child_out) -{ - FlatpakXml *child = NULL; - FlatpakXml *prev_child = NULL; - - child = node->first_child; - prev_child = NULL; - while (child != NULL) - { - FlatpakXml *next = child->next_sibling; - - if (g_strcmp0 (child->element_name, type) == 0) - { - if (prev_child_out) - *prev_child_out = prev_child; - return child; - } - - prev_child = child; - child = next; - } - - return NULL; -} - - -FlatpakXml * -flatpak_xml_parse (GInputStream *in, - gboolean compressed, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GInputStream) real_in = NULL; - g_autoptr(FlatpakXml) xml_root = NULL; - XmlData data = { 0 }; - char buffer[32 * 1024]; - gssize len; - g_autoptr(GMarkupParseContext) ctx = NULL; - - if (compressed) - { - g_autoptr(GZlibDecompressor) decompressor = NULL; - decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); - real_in = g_converter_input_stream_new (in, G_CONVERTER (decompressor)); - } - else - { - real_in = g_object_ref (in); - } - - xml_root = flatpak_xml_new ("root"); - data.current = xml_root; - - ctx = g_markup_parse_context_new (&xml_parser, - G_MARKUP_PREFIX_ERROR_POSITION, - &data, - NULL); - - while ((len = g_input_stream_read (real_in, buffer, sizeof (buffer), - cancellable, error)) > 0) - { - if (!g_markup_parse_context_parse (ctx, buffer, len, error)) - return NULL; - } - - if (len < 0) - return NULL; - - return g_steal_pointer (&xml_root); -} - #define OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "(uayttay)" #define OSTREE_STATIC_DELTA_FALLBACK_FORMAT "(yaytt)" #define OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT "(a{sv}tayay" OSTREE_COMMIT_GVARIANT_STRING "aya" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT ")" diff --git a/common/flatpak-xml-utils-private.h b/common/flatpak-xml-utils-private.h new file mode 100644 index 00000000..970c72f9 --- /dev/null +++ b/common/flatpak-xml-utils-private.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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.1 of the License, or (at your option) any later version. + * + * This library 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 library. If not, see . + */ + +#pragma once + +#include "libglnx.h" + +typedef struct FlatpakXml FlatpakXml; + +struct FlatpakXml +{ + gchar *element_name; /* NULL == text */ + char **attribute_names; + char **attribute_values; + char *text; + FlatpakXml *parent; + FlatpakXml *first_child; + FlatpakXml *last_child; + FlatpakXml *next_sibling; +}; + +FlatpakXml *flatpak_xml_new (const gchar *element_name); +FlatpakXml *flatpak_xml_new_text (const gchar *text); +void flatpak_xml_add (FlatpakXml *parent, + FlatpakXml *node); +void flatpak_xml_free (FlatpakXml *node); +FlatpakXml *flatpak_xml_parse (GInputStream * in, + gboolean compressed, + GCancellable *cancellable, + GError **error); +void flatpak_xml_to_string (FlatpakXml *node, + GString *res); +FlatpakXml *flatpak_xml_unlink (FlatpakXml *node, + FlatpakXml *prev_sibling); +FlatpakXml *flatpak_xml_find (FlatpakXml *node, + const char *type, + FlatpakXml **prev_child_out); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakXml, flatpak_xml_free); + +FlatpakXml *flatpak_appstream_xml_new (void); +gboolean flatpak_appstream_xml_migrate (FlatpakXml *source, + FlatpakXml *dest, + const char *ref, + const char *id, + GKeyFile *metadata); +gboolean flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, + GBytes **uncompressed, + GBytes **compressed, + GError **error); +void flatpak_appstream_xml_filter (FlatpakXml *appstream, + GRegex *allow_refs, + GRegex *deny_refs); diff --git a/common/flatpak-xml-utils.c b/common/flatpak-xml-utils.c new file mode 100644 index 00000000..3e553058 --- /dev/null +++ b/common/flatpak-xml-utils.c @@ -0,0 +1,560 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 1995-1998 Free Software Foundation, Inc. + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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.1 of the License, or (at your option) any later version. + * + * This library 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 library. If not, see . + * + * Authors: + * Alexander Larsson + */ + +#include "config.h" +#include "flatpak-xml-utils-private.h" + +#include "flatpak-run-private.h" +#include "flatpak-utils-private.h" + +typedef struct +{ + FlatpakXml *current; +} XmlData; + +FlatpakXml * +flatpak_xml_new (const gchar *element_name) +{ + FlatpakXml *node = g_new0 (FlatpakXml, 1); + + node->element_name = g_strdup (element_name); + return node; +} + +FlatpakXml * +flatpak_xml_new_text (const gchar *text) +{ + FlatpakXml *node = g_new0 (FlatpakXml, 1); + + node->text = g_strdup (text); + return node; +} + +void +flatpak_xml_add (FlatpakXml *parent, FlatpakXml *node) +{ + node->parent = parent; + + if (parent->first_child == NULL) + parent->first_child = node; + else + parent->last_child->next_sibling = node; + parent->last_child = node; +} + +static void +xml_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + XmlData *data = user_data; + FlatpakXml *node; + + node = flatpak_xml_new (element_name); + node->attribute_names = g_strdupv ((char **) attribute_names); + node->attribute_values = g_strdupv ((char **) attribute_values); + + flatpak_xml_add (data->current, node); + data->current = node; +} + +static void +xml_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + XmlData *data = user_data; + + data->current = data->current->parent; +} + +static void +xml_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + XmlData *data = user_data; + FlatpakXml *node; + + node = flatpak_xml_new (NULL); + node->text = g_strndup (text, text_len); + flatpak_xml_add (data->current, node); +} + +static void +xml_passthrough (GMarkupParseContext *context, + const gchar *passthrough_text, + gsize text_len, + gpointer user_data, + GError **error) +{ +} + +static GMarkupParser xml_parser = { + xml_start_element, + xml_end_element, + xml_text, + xml_passthrough, + NULL +}; + +void +flatpak_xml_free (FlatpakXml *node) +{ + FlatpakXml *child; + + if (node == NULL) + return; + + child = node->first_child; + while (child != NULL) + { + FlatpakXml *next = child->next_sibling; + flatpak_xml_free (child); + child = next; + } + + g_free (node->element_name); + g_free (node->text); + g_strfreev (node->attribute_names); + g_strfreev (node->attribute_values); + g_free (node); +} + + +void +flatpak_xml_to_string (FlatpakXml *node, GString *res) +{ + int i; + FlatpakXml *child; + + if (node->parent == NULL) + g_string_append (res, "\n"); + + if (node->element_name) + { + if (node->parent != NULL) + { + g_string_append (res, "<"); + g_string_append (res, node->element_name); + if (node->attribute_names) + { + for (i = 0; node->attribute_names[i] != NULL; i++) + { + g_string_append_printf (res, " %s=\"%s\"", + node->attribute_names[i], + node->attribute_values[i]); + } + } + if (node->first_child == NULL) + g_string_append (res, "/>"); + else + g_string_append (res, ">"); + } + + child = node->first_child; + while (child != NULL) + { + flatpak_xml_to_string (child, res); + child = child->next_sibling; + } + if (node->parent != NULL) + { + if (node->first_child != NULL) + g_string_append_printf (res, "", node->element_name); + } + } + else if (node->text) + { + g_autofree char *escaped = g_markup_escape_text (node->text, -1); + g_string_append (res, escaped); + } +} + +FlatpakXml * +flatpak_xml_unlink (FlatpakXml *node, + FlatpakXml *prev_sibling) +{ + FlatpakXml *parent = node->parent; + + if (parent == NULL) + return node; + + if (parent->first_child == node) + parent->first_child = node->next_sibling; + + if (parent->last_child == node) + parent->last_child = prev_sibling; + + if (prev_sibling) + prev_sibling->next_sibling = node->next_sibling; + + node->parent = NULL; + node->next_sibling = NULL; + + return node; +} + +FlatpakXml * +flatpak_xml_find (FlatpakXml *node, + const char *type, + FlatpakXml **prev_child_out) +{ + FlatpakXml *child = NULL; + FlatpakXml *prev_child = NULL; + + child = node->first_child; + prev_child = NULL; + while (child != NULL) + { + FlatpakXml *next = child->next_sibling; + + if (g_strcmp0 (child->element_name, type) == 0) + { + if (prev_child_out) + *prev_child_out = prev_child; + return child; + } + + prev_child = child; + child = next; + } + + return NULL; +} + + +FlatpakXml * +flatpak_xml_parse (GInputStream *in, + gboolean compressed, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GInputStream) real_in = NULL; + g_autoptr(FlatpakXml) xml_root = NULL; + XmlData data = { 0 }; + char buffer[32 * 1024]; + gssize len; + g_autoptr(GMarkupParseContext) ctx = NULL; + + if (compressed) + { + g_autoptr(GZlibDecompressor) decompressor = NULL; + decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + real_in = g_converter_input_stream_new (in, G_CONVERTER (decompressor)); + } + else + { + real_in = g_object_ref (in); + } + + xml_root = flatpak_xml_new ("root"); + data.current = xml_root; + + ctx = g_markup_parse_context_new (&xml_parser, + G_MARKUP_PREFIX_ERROR_POSITION, + &data, + NULL); + + while ((len = g_input_stream_read (real_in, buffer, sizeof (buffer), + cancellable, error)) > 0) + { + if (!g_markup_parse_context_parse (ctx, buffer, len, error)) + return NULL; + } + + if (len < 0) + return NULL; + + return g_steal_pointer (&xml_root); +} + +FlatpakXml * +flatpak_appstream_xml_new (void) +{ + FlatpakXml *appstream_root = NULL; + FlatpakXml *appstream_components; + + appstream_root = flatpak_xml_new ("root"); + appstream_components = flatpak_xml_new ("components"); + flatpak_xml_add (appstream_root, appstream_components); + flatpak_xml_add (appstream_components, flatpak_xml_new_text ("\n ")); + + appstream_components->attribute_names = g_new0 (char *, 3); + appstream_components->attribute_values = g_new0 (char *, 3); + appstream_components->attribute_names[0] = g_strdup ("version"); + appstream_components->attribute_values[0] = g_strdup ("0.8"); + appstream_components->attribute_names[1] = g_strdup ("origin"); + appstream_components->attribute_values[1] = g_strdup ("flatpak"); + + return appstream_root; +} + +gboolean +flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, + GBytes **uncompressed, + GBytes **compressed, + GError **error) +{ + g_autoptr(GString) xml = NULL; + g_autoptr(GZlibCompressor) compressor = NULL; + g_autoptr(GOutputStream) out2 = NULL; + g_autoptr(GOutputStream) out = NULL; + + flatpak_xml_add (appstream_root->first_child, flatpak_xml_new_text ("\n")); + + xml = g_string_new (""); + flatpak_xml_to_string (appstream_root, xml); + + if (compressed) + { + compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1); + out = g_memory_output_stream_new_resizable (); + out2 = g_converter_output_stream_new (out, G_CONVERTER (compressor)); + if (!g_output_stream_write_all (out2, xml->str, xml->len, + NULL, NULL, error)) + return FALSE; + if (!g_output_stream_close (out2, NULL, error)) + return FALSE; + } + + if (uncompressed) + *uncompressed = g_string_free_to_bytes (g_steal_pointer (&xml)); + + if (compressed) + *compressed = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out)); + + return TRUE; +} + +void +flatpak_appstream_xml_filter (FlatpakXml *appstream, + GRegex *allow_refs, + GRegex *deny_refs) +{ + FlatpakXml *components; + FlatpakXml *component; + FlatpakXml *prev_component, *old; + + for (components = appstream->first_child; + components != NULL; + components = components->next_sibling) + { + if (g_strcmp0 (components->element_name, "components") != 0) + continue; + + + prev_component = NULL; + component = components->first_child; + while (component != NULL) + { + FlatpakXml *bundle; + gboolean allow = FALSE; + + if (g_strcmp0 (component->element_name, "component") == 0) + { + bundle = flatpak_xml_find (component, "bundle", NULL); + if (bundle && bundle->first_child && bundle->first_child->text) + allow = flatpak_filters_allow_ref (allow_refs, deny_refs, bundle->first_child->text); + } + + if (allow) + { + prev_component = component; + component = component->next_sibling; + } + else + { + old = component; + + /* prev_component is same as before */ + component = component->next_sibling; + + flatpak_xml_unlink (old, prev_component); + flatpak_xml_free (old); + } + } + } +} + +static gboolean +validate_component (FlatpakXml *component, + const char *ref, + const char *id, + char **tags, + const char *runtime, + const char *sdk) +{ + FlatpakXml *bundle, *text, *prev, *id_node, *id_text_node, *metadata, *value; + g_autofree char *id_text = NULL; + int i; + + if (g_strcmp0 (component->element_name, "component") != 0) + return FALSE; + + id_node = flatpak_xml_find (component, "id", NULL); + if (id_node == NULL) + return FALSE; + + id_text_node = flatpak_xml_find (id_node, NULL, NULL); + if (id_text_node == NULL || id_text_node->text == NULL) + return FALSE; + + id_text = g_strstrip (g_strdup (id_text_node->text)); + + /* Drop .desktop file suffix (unless the actual app id ends with .desktop) */ + if (g_str_has_suffix (id_text, ".desktop") && + !g_str_has_suffix (id, ".desktop")) + id_text[strlen (id_text) - strlen (".desktop")] = 0; + + if (!g_str_has_prefix (id_text, id)) + { + g_warning ("Invalid id %s", id_text); + return FALSE; + } + + while ((bundle = flatpak_xml_find (component, "bundle", &prev)) != NULL) + flatpak_xml_free (flatpak_xml_unlink (bundle, prev)); + + bundle = flatpak_xml_new ("bundle"); + bundle->attribute_names = g_new0 (char *, 2 * 4); + bundle->attribute_values = g_new0 (char *, 2 * 4); + bundle->attribute_names[0] = g_strdup ("type"); + bundle->attribute_values[0] = g_strdup ("flatpak"); + + i = 1; + if (runtime && !g_str_has_prefix (runtime, "runtime/")) + { + bundle->attribute_names[i] = g_strdup ("runtime"); + bundle->attribute_values[i] = g_strdup (runtime); + i++; + } + + if (sdk) + { + bundle->attribute_names[i] = g_strdup ("sdk"); + bundle->attribute_values[i] = g_strdup (sdk); + i++; + } + + text = flatpak_xml_new (NULL); + text->text = g_strdup (ref); + flatpak_xml_add (bundle, text); + + flatpak_xml_add (component, flatpak_xml_new_text (" ")); + flatpak_xml_add (component, bundle); + flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); + + if (tags != NULL && tags[0] != NULL) + { + metadata = flatpak_xml_find (component, "metadata", NULL); + if (metadata == NULL) + { + metadata = flatpak_xml_new ("metadata"); + metadata->attribute_names = g_new0 (char *, 1); + metadata->attribute_values = g_new0 (char *, 1); + + flatpak_xml_add (component, flatpak_xml_new_text (" ")); + flatpak_xml_add (component, metadata); + flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); + } + + value = flatpak_xml_new ("value"); + value->attribute_names = g_new0 (char *, 2); + value->attribute_values = g_new0 (char *, 2); + value->attribute_names[0] = g_strdup ("key"); + value->attribute_values[0] = g_strdup ("X-Flatpak-Tags"); + flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); + flatpak_xml_add (metadata, value); + flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); + + text = flatpak_xml_new (NULL); + text->text = g_strjoinv (",", tags); + flatpak_xml_add (value, text); + } + + return TRUE; +} + +gboolean +flatpak_appstream_xml_migrate (FlatpakXml *source, + FlatpakXml *dest, + const char *ref, + const char *id, + GKeyFile *metadata) +{ + FlatpakXml *source_components; + FlatpakXml *dest_components; + FlatpakXml *component; + FlatpakXml *prev_component; + gboolean migrated = FALSE; + g_auto(GStrv) tags = NULL; + g_autofree const char *runtime = NULL; + g_autofree const char *sdk = NULL; + const char *group; + + if (source->first_child == NULL || + source->first_child->next_sibling != NULL || + g_strcmp0 (source->first_child->element_name, "components") != 0) + return FALSE; + + if (g_str_has_prefix (ref, "app/")) + group = FLATPAK_METADATA_GROUP_APPLICATION; + else + group = FLATPAK_METADATA_GROUP_RUNTIME; + + tags = g_key_file_get_string_list (metadata, group, FLATPAK_METADATA_KEY_TAGS, + NULL, NULL); + runtime = g_key_file_get_string (metadata, group, + FLATPAK_METADATA_KEY_RUNTIME, NULL); + sdk = g_key_file_get_string (metadata, group, FLATPAK_METADATA_KEY_SDK, NULL); + + source_components = source->first_child; + dest_components = dest->first_child; + + component = source_components->first_child; + prev_component = NULL; + while (component != NULL) + { + FlatpakXml *next = component->next_sibling; + + if (validate_component (component, ref, id, tags, runtime, sdk)) + { + flatpak_xml_add (dest_components, + flatpak_xml_unlink (component, prev_component)); + migrated = TRUE; + } + else + { + prev_component = component; + } + + component = next; + } + + return migrated; +} diff --git a/common/meson.build b/common/meson.build index 6f75101c..439887e5 100644 --- a/common/meson.build +++ b/common/meson.build @@ -197,6 +197,7 @@ sources = [ 'flatpak-utils-http.c', 'flatpak-utils.c', 'flatpak-uri.c', + 'flatpak-xml-utils.c', 'flatpak-zstd-decompressor.c', ]