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 <smcv@collabora.com>
This commit is contained in:
Simon McVittie
2023-05-17 14:12:20 +01:00
parent ad0aa467d0
commit 6e81bc6f28
8 changed files with 632 additions and 580 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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,

View File

@@ -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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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 ")"

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

560
common/flatpak-xml-utils.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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;
}

View File

@@ -197,6 +197,7 @@ sources = [
'flatpak-utils-http.c',
'flatpak-utils.c',
'flatpak-uri.c',
'flatpak-xml-utils.c',
'flatpak-zstd-decompressor.c',
]