diff --git a/app/flatpak-builtins.h b/app/flatpak-builtins.h
index 7ea0b4f14a..e4136ed931 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 af68db671e..bb7cfe22b7 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 dddfe6644c..8b63de99c5 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 5d32b6501f..53eb198b6d 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 8606191467..c6d984907d 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, "%s>", 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 0000000000..970c72f9aa
--- /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 0000000000..3e55305889
--- /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, "%s>", 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 6f75101c85..439887e5a3 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',
]