Blame src/xb-builder-source.c

Packit caecb6
/*
Packit caecb6
 * Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
Packit caecb6
 *
Packit caecb6
 * SPDX-License-Identifier: LGPL-2.1+
Packit caecb6
 */
Packit caecb6
Packit caecb6
#define G_LOG_DOMAIN				"XbSilo"
Packit caecb6
Packit caecb6
#include "config.h"
Packit caecb6
Packit caecb6
#include <gio/gio.h>
Packit caecb6
#include <string.h>
Packit caecb6
Packit caecb6
#include "xb-builder-fixup-private.h"
Packit caecb6
#include "xb-builder-source-ctx-private.h"
Packit caecb6
#include "xb-builder-source-private.h"
Packit caecb6
Packit caecb6
typedef struct {
Packit caecb6
	GObject			 parent_instance;
Packit caecb6
	GInputStream		*istream;
Packit caecb6
	GFile			*file;
Packit caecb6
	GPtrArray		*fixups;	/* of XbBuilderFixup */
Packit caecb6
	GPtrArray		*adapters;	/* of XbBuilderSourceAdapter */
Packit caecb6
	XbBuilderNode		*info;
Packit caecb6
	gchar			*guid;
Packit caecb6
	gchar			*prefix;
Packit caecb6
	gchar			*content_type;
Packit caecb6
	XbBuilderSourceFlags	 flags;
Packit caecb6
} XbBuilderSourcePrivate;
Packit caecb6
Packit caecb6
G_DEFINE_TYPE_WITH_PRIVATE (XbBuilderSource, xb_builder_source, G_TYPE_OBJECT)
Packit caecb6
#define GET_PRIVATE(o) (xb_builder_source_get_instance_private (o))
Packit caecb6
Packit caecb6
typedef struct {
Packit caecb6
	gchar				*content_type;
Packit caecb6
	XbBuilderSourceAdapterFunc	 func_adapter;
Packit caecb6
	gpointer			 user_data;
Packit caecb6
	GDestroyNotify			 user_data_free;
Packit caecb6
	gboolean			 is_simple;
Packit caecb6
} XbBuilderSourceAdapter;
Packit caecb6
Packit caecb6
static XbBuilderSourceAdapter *
Packit caecb6
xb_builder_source_get_adapter_by_mime (XbBuilderSource *self,
Packit caecb6
				       const gchar *content_type)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	for (guint i = 0; i < priv->adapters->len; i++) {
Packit caecb6
		XbBuilderSourceAdapter *item = g_ptr_array_index (priv->adapters, i);
Packit caecb6
		if (item->func_adapter == NULL)
Packit caecb6
			continue;
Packit caecb6
		if (g_strcmp0 (item->content_type, content_type) == 0)
Packit caecb6
			return item;
Packit caecb6
	}
Packit caecb6
	return NULL;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_load_file:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @file: a #GFile
Packit caecb6
 * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT
Packit caecb6
 * @cancellable: a #GCancellable, or %NULL
Packit caecb6
 * @error: the #GError, or %NULL
Packit caecb6
 *
Packit caecb6
 * Loads an optionally compressed XML file to build a #XbSilo.
Packit caecb6
 *
Packit caecb6
 * Returns: %TRUE for success
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.1
Packit caecb6
 **/
Packit caecb6
gboolean
Packit caecb6
xb_builder_source_load_file (XbBuilderSource *self,
Packit caecb6
			     GFile *file,
Packit caecb6
			     XbBuilderSourceFlags flags,
Packit caecb6
			     GCancellable *cancellable,
Packit caecb6
			     GError **error)
Packit caecb6
{
Packit caecb6
	const gchar *content_type = NULL;
Packit caecb6
	guint32 ctime_usec;
Packit caecb6
	guint64 ctime;
Packit caecb6
	g_autofree gchar *fn = NULL;
Packit caecb6
	g_autoptr(GFileInfo) fileinfo = NULL;
Packit caecb6
	g_autoptr(GString) guid = NULL;
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), FALSE);
Packit caecb6
	g_return_val_if_fail (G_IS_FILE (file), FALSE);
Packit caecb6
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit caecb6
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit caecb6
Packit caecb6
	/* what kind of file is this */
Packit caecb6
	fileinfo = g_file_query_info (file,
Packit caecb6
				      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
Packit caecb6
				      G_FILE_ATTRIBUTE_TIME_CHANGED ","
Packit caecb6
				      G_FILE_ATTRIBUTE_TIME_CHANGED_USEC,
Packit caecb6
				      G_FILE_QUERY_INFO_NONE,
Packit caecb6
				      cancellable,
Packit caecb6
				      error);
Packit caecb6
	if (fileinfo == NULL)
Packit caecb6
		return FALSE;
Packit caecb6
Packit caecb6
	/* add data to GUID */
Packit caecb6
	fn = g_file_get_path (file);
Packit caecb6
	guid = g_string_new (fn);
Packit caecb6
	ctime = g_file_info_get_attribute_uint64 (fileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED);
Packit caecb6
	if (ctime != 0)
Packit caecb6
		g_string_append_printf (guid, ":ctime=%" G_GUINT64_FORMAT, ctime);
Packit caecb6
	ctime_usec = g_file_info_get_attribute_uint32 (fileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC);
Packit caecb6
	if (ctime_usec != 0)
Packit caecb6
		g_string_append_printf (guid, ".%" G_GUINT32_FORMAT, ctime_usec);
Packit caecb6
	priv->guid = g_string_free (g_steal_pointer (&guid), FALSE);
Packit caecb6
Packit caecb6
	/* check content type of file */
Packit caecb6
	content_type = g_file_info_get_attribute_string (fileinfo, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
Packit caecb6
	if (content_type == NULL) {
Packit caecb6
		g_set_error_literal (error,
Packit caecb6
				     G_IO_ERROR,
Packit caecb6
				     G_IO_ERROR_NOT_SUPPORTED,
Packit caecb6
				     "cannot get content type for file");
Packit caecb6
		return FALSE;
Packit caecb6
	}
Packit caecb6
Packit caecb6
	/* success */
Packit caecb6
	priv->flags = flags;
Packit caecb6
	priv->content_type = g_strdup (content_type);
Packit caecb6
	priv->file = g_object_ref (file);
Packit caecb6
	return TRUE;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_set_info:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @info: (allow-none): a #XbBuilderNode
Packit caecb6
 *
Packit caecb6
 * Sets an optional information metadata node on the root node.
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.0
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_set_info (XbBuilderSource *self, XbBuilderNode *info)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_if_fail (XB_IS_BUILDER_SOURCE (self));
Packit caecb6
	g_set_object (&priv->info, info);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_set_prefix:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @prefix: (allow-none): an XPath prefix, e.g. `installed`
Packit caecb6
 *
Packit caecb6
 * Sets an optional prefix on the root node. This makes any nodes added
Packit caecb6
 * using this source reside under a common shared parent node.
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.0
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_set_prefix (XbBuilderSource *self, const gchar *prefix)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_if_fail (XB_IS_BUILDER_SOURCE (self));
Packit caecb6
	g_free (priv->prefix);
Packit caecb6
	priv->prefix = g_strdup (prefix);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_load_xml:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @xml: XML data
Packit caecb6
 * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT
Packit caecb6
 * @error: the #GError, or %NULL
Packit caecb6
 *
Packit caecb6
 * Loads XML data and begins to build a #XbSilo.
Packit caecb6
 *
Packit caecb6
 * Returns: %TRUE for success
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.1
Packit caecb6
 **/
Packit caecb6
gboolean
Packit caecb6
xb_builder_source_load_xml (XbBuilderSource *self,
Packit caecb6
			    const gchar *xml,
Packit caecb6
			    XbBuilderSourceFlags flags,
Packit caecb6
			    GError **error)
Packit caecb6
{
Packit caecb6
	g_autoptr(GBytes) blob = NULL;
Packit caecb6
	g_autoptr(GChecksum) csum = g_checksum_new (G_CHECKSUM_SHA1);
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), FALSE);
Packit caecb6
	g_return_val_if_fail (xml != NULL, FALSE);
Packit caecb6
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit caecb6
Packit caecb6
	/* add a GUID of the SHA1 hash of the entire string */
Packit caecb6
	g_checksum_update (csum, (const guchar *) xml, -1);
Packit caecb6
	priv->guid = g_strdup (g_checksum_get_string (csum));
Packit caecb6
Packit caecb6
	/* create input stream */
Packit caecb6
	blob = g_bytes_new (xml, strlen (xml));
Packit caecb6
	priv->istream = g_memory_input_stream_new_from_bytes (blob);
Packit caecb6
	if (priv->istream == NULL)
Packit caecb6
		return FALSE;
Packit caecb6
Packit caecb6
	/* success */
Packit caecb6
	priv->flags = flags;
Packit caecb6
	return TRUE;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_load_bytes:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @bytes: a #GBytes
Packit caecb6
 * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT
Packit caecb6
 * @error: the #GError, or %NULL
Packit caecb6
 *
Packit caecb6
 * Loads XML data and begins to build a #XbSilo.
Packit caecb6
 *
Packit caecb6
 * Returns: %TRUE for success
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.2
Packit caecb6
 **/
Packit caecb6
gboolean
Packit caecb6
xb_builder_source_load_bytes (XbBuilderSource *self,
Packit caecb6
			      GBytes *bytes,
Packit caecb6
			      XbBuilderSourceFlags flags,
Packit caecb6
			      GError **error)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_autoptr(GChecksum) csum = g_checksum_new (G_CHECKSUM_SHA1);
Packit caecb6
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), FALSE);
Packit caecb6
	g_return_val_if_fail (bytes != NULL, FALSE);
Packit caecb6
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit caecb6
Packit caecb6
	/* add a GUID of the SHA1 hash of the entire blob */
Packit caecb6
	g_checksum_update (csum,
Packit caecb6
			   (const guchar *) g_bytes_get_data (bytes, NULL),
Packit caecb6
			   (gssize) g_bytes_get_size (bytes));
Packit caecb6
	priv->guid = g_strdup (g_checksum_get_string (csum));
Packit caecb6
Packit caecb6
	/* create input stream */
Packit caecb6
	priv->istream = g_memory_input_stream_new_from_bytes (bytes);
Packit caecb6
	if (priv->istream == NULL)
Packit caecb6
		return FALSE;
Packit caecb6
Packit caecb6
	/* success */
Packit caecb6
	priv->flags = flags;
Packit caecb6
	return TRUE;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_add_fixup:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @fixup: a #XbBuilderFixup
Packit caecb6
 *
Packit caecb6
 * Adds a function that will get run on every #XbBuilderNode compile creates
Packit caecb6
 * with this source.
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.3
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_add_fixup (XbBuilderSource *self, XbBuilderFixup *fixup)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_if_fail (XB_IS_BUILDER_SOURCE (self));
Packit caecb6
	g_return_if_fail (XB_IS_BUILDER_FIXUP (fixup));
Packit caecb6
	g_ptr_array_add (priv->fixups, g_object_ref (fixup));
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_add_node_func:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @id: a text ID value, e.g. `AppStreamUpgrade`
Packit caecb6
 * @func: a callback
Packit caecb6
 * @user_data: user pointer to pass to @func, or %NULL
Packit caecb6
 * @user_data_free: a function which gets called to free @user_data, or %NULL
Packit caecb6
 *
Packit caecb6
 * Adds a function that will get run on every #XbBuilderNode compile creates.
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.0
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_add_node_func (XbBuilderSource *self,
Packit caecb6
				 const gchar *id,
Packit caecb6
				 XbBuilderSourceNodeFunc func,
Packit caecb6
				 gpointer user_data,
Packit caecb6
				 GDestroyNotify user_data_free)
Packit caecb6
{
Packit caecb6
	g_autoptr(XbBuilderFixup) fixup = NULL;
Packit caecb6
	/* close enough... */
Packit caecb6
	fixup = xb_builder_fixup_new (id, (XbBuilderFixupFunc) func,
Packit caecb6
				      user_data, user_data_free);
Packit caecb6
	xb_builder_source_add_fixup (self, fixup);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_add_converter:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @content_types: mimetypes, e.g. `application/x-desktop,application/gzip`
Packit caecb6
 * @func: a callback
Packit caecb6
 * @user_data: user pointer to pass to @func, or %NULL
Packit caecb6
 * @user_data_free: a function which gets called to free @user_data, or %NULL
Packit caecb6
 *
Packit caecb6
 * This function is now deprecated, and does nothing.
Packit caecb6
 *
Packit caecb6
 * See also: xb_builder_source_add_adapter()
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.1
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_add_converter (XbBuilderSource *self,
Packit caecb6
				 const gchar *content_types,
Packit caecb6
				 XbBuilderSourceConverterFunc func,
Packit caecb6
				 gpointer user_data,
Packit caecb6
				 GDestroyNotify user_data_free)
Packit caecb6
{
Packit caecb6
	g_warning ("%s() does nothing", G_STRFUNC);
Packit caecb6
}
Packit caecb6
Packit caecb6
static void
Packit caecb6
xb_builder_source_init_adapter (XbBuilderSource *self,
Packit caecb6
			        const gchar *content_types,
Packit caecb6
			        XbBuilderSourceAdapterFunc func,
Packit caecb6
			        gpointer user_data,
Packit caecb6
			        GDestroyNotify user_data_free,
Packit caecb6
				gboolean is_simple)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_auto(GStrv) split = NULL;
Packit caecb6
Packit caecb6
	g_return_if_fail (XB_IS_BUILDER_SOURCE (self));
Packit caecb6
	g_return_if_fail (content_types != NULL);
Packit caecb6
	g_return_if_fail (func != NULL);
Packit caecb6
Packit caecb6
	/* add each */
Packit caecb6
	split = g_strsplit (content_types, ",", -1);
Packit caecb6
	for (guint i = 0; split[i] != NULL; i++) {
Packit caecb6
		XbBuilderSourceAdapter *item;
Packit caecb6
		item = g_slice_new0 (XbBuilderSourceAdapter);
Packit caecb6
		item->content_type = g_strdup (split[i]);
Packit caecb6
		item->func_adapter = func;
Packit caecb6
		item->user_data = user_data;
Packit caecb6
		item->user_data_free = user_data_free;
Packit caecb6
		item->is_simple = is_simple;
Packit caecb6
		g_ptr_array_add (priv->adapters, item);
Packit caecb6
	}
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_add_adapter:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @content_types: mimetypes, e.g. `application/x-desktop,application/gzip`
Packit caecb6
 * @func: a callback, or %NULL
Packit caecb6
 * @user_data: user pointer to pass to @func, or %NULL
Packit caecb6
 * @user_data_free: a function which gets called to free @user_data, or %NULL
Packit caecb6
 *
Packit caecb6
 * Adds a function that can be used to convert streams loaded with
Packit caecb6
 * xb_builder_source_load_xml().
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.7
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_add_adapter (XbBuilderSource *self,
Packit caecb6
			       const gchar *content_types,
Packit caecb6
			       XbBuilderSourceAdapterFunc func,
Packit caecb6
			       gpointer user_data,
Packit caecb6
			       GDestroyNotify user_data_free)
Packit caecb6
{
Packit caecb6
	xb_builder_source_init_adapter (self, content_types, func,
Packit caecb6
					user_data, user_data_free, FALSE);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_add_simple_adapter:
Packit caecb6
 * @self: a #XbBuilderSource
Packit caecb6
 * @content_types: mimetypes, e.g. `application/x-desktop,application/gzip`
Packit caecb6
 * @func: a callback, or %NULL
Packit caecb6
 * @user_data: user pointer to pass to @func, or %NULL
Packit caecb6
 * @user_data_free: a function which gets called to free @user_data, or %NULL
Packit caecb6
 *
Packit caecb6
 * Adds a function that can be used to convert streams loaded with
Packit caecb6
 * xb_builder_source_load_xml().
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.15
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_builder_source_add_simple_adapter (XbBuilderSource *self,
Packit caecb6
				      const gchar *content_types,
Packit caecb6
				      XbBuilderSourceAdapterFunc func,
Packit caecb6
				      gpointer user_data,
Packit caecb6
				      GDestroyNotify user_data_free)
Packit caecb6
{
Packit caecb6
	xb_builder_source_init_adapter (self, content_types, func,
Packit caecb6
					user_data, user_data_free, TRUE);
Packit caecb6
}
Packit caecb6
Packit caecb6
gboolean
Packit caecb6
xb_builder_source_fixup (XbBuilderSource *self, XbBuilderNode *bn, GError **error)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	for (guint i = 0; i < priv->fixups->len; i++) {
Packit caecb6
		XbBuilderFixup *fixup = g_ptr_array_index (priv->fixups, i);
Packit caecb6
		if (!xb_builder_fixup_node (fixup, bn, error))
Packit caecb6
			return FALSE;
Packit caecb6
	}
Packit caecb6
	return TRUE;
Packit caecb6
}
Packit caecb6
Packit caecb6
static gboolean
Packit caecb6
xb_builder_source_info_guid_cb (XbBuilderNode *bn, gpointer data)
Packit caecb6
{
Packit caecb6
	GString *str = (GString *) data;
Packit caecb6
	if (xb_builder_node_get_text (bn) != NULL) {
Packit caecb6
		g_string_append_printf (str, ":%s=%s",
Packit caecb6
					xb_builder_node_get_element (bn),
Packit caecb6
					xb_builder_node_get_text (bn));
Packit caecb6
	}
Packit caecb6
	return FALSE;
Packit caecb6
}
Packit caecb6
Packit caecb6
gchar *
Packit caecb6
xb_builder_source_get_guid (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	GString *str = g_string_new (priv->guid);
Packit caecb6
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), NULL);
Packit caecb6
Packit caecb6
	/* append function IDs */
Packit caecb6
	for (guint i = 0; i < priv->fixups->len; i++) {
Packit caecb6
		XbBuilderFixup *fixup = g_ptr_array_index (priv->fixups, i);
Packit caecb6
		g_autofree gchar *tmp = xb_builder_fixup_get_guid (fixup);
Packit caecb6
		g_string_append_printf (str, ":%s", tmp);
Packit caecb6
	}
Packit caecb6
Packit caecb6
	/* append any info */
Packit caecb6
	if (priv->info != NULL) {
Packit caecb6
		xb_builder_node_traverse (priv->info, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
Packit caecb6
					  xb_builder_source_info_guid_cb, str);
Packit caecb6
	}
Packit caecb6
Packit caecb6
	/* append prefix */
Packit caecb6
	if (priv->prefix != NULL)
Packit caecb6
		g_string_append_printf (str, ":prefix=%s", priv->prefix);
Packit caecb6
	return g_string_free (str, FALSE);
Packit caecb6
}
Packit caecb6
Packit caecb6
const gchar *
Packit caecb6
xb_builder_source_get_prefix (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), NULL);
Packit caecb6
	return priv->prefix;
Packit caecb6
}
Packit caecb6
Packit caecb6
XbBuilderNode *
Packit caecb6
xb_builder_source_get_info (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), NULL);
Packit caecb6
	return priv->info;
Packit caecb6
}
Packit caecb6
Packit caecb6
/* converts foo.xml.gz to foo.xml */
Packit caecb6
static void
Packit caecb6
xb_builder_source_remove_last_extension (gchar *basename)
Packit caecb6
{
Packit caecb6
	gchar *tmp = g_strrstr (basename, ".");
Packit caecb6
	if (tmp != NULL)
Packit caecb6
		*tmp = '\0';
Packit caecb6
}
Packit caecb6
Packit caecb6
GInputStream *
Packit caecb6
xb_builder_source_get_istream (XbBuilderSource *self,
Packit caecb6
			       GCancellable *cancellable,
Packit caecb6
			       GError **error)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_autofree gchar *basename = NULL;
Packit caecb6
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), NULL);
Packit caecb6
Packit caecb6
	/* nothing required */
Packit caecb6
	if (priv->istream != NULL)
Packit caecb6
		return g_object_ref (priv->istream);
Packit caecb6
Packit caecb6
	/* convert the file to a GFileInputStream */
Packit caecb6
	priv->istream = G_INPUT_STREAM (g_file_read (priv->file, cancellable, error));
Packit caecb6
	if (priv->istream == NULL)
Packit caecb6
		return NULL;
Packit caecb6
Packit caecb6
	/* run the content type handlers until we get application/xml */
Packit caecb6
	basename = g_file_get_basename (priv->file);
Packit caecb6
	do {
Packit caecb6
		XbBuilderSourceAdapter *item;
Packit caecb6
		g_autofree gchar *content_type = NULL;
Packit caecb6
		g_autoptr(GInputStream) istream_tmp = NULL;
Packit caecb6
		g_autoptr(XbBuilderSourceCtx) ctx = xb_builder_source_ctx_new (priv->istream);
Packit caecb6
Packit caecb6
		/* get the content type of the stream */
Packit caecb6
		xb_builder_source_ctx_set_filename (ctx, basename);
Packit caecb6
		content_type = xb_builder_source_ctx_get_content_type (ctx,
Packit caecb6
								       cancellable,
Packit caecb6
								       error);
Packit caecb6
		if (content_type == NULL)
Packit caecb6
			return NULL;
Packit caecb6
		if (g_strcmp0 (content_type, "application/xml") == 0)
Packit caecb6
			break;
Packit caecb6
Packit caecb6
		/* convert the stream */
Packit caecb6
		item = xb_builder_source_get_adapter_by_mime (self, content_type);
Packit caecb6
		if (item == NULL || item->func_adapter == NULL) {
Packit caecb6
			g_set_error (error,
Packit caecb6
				     G_IO_ERROR,
Packit caecb6
				     G_IO_ERROR_NOT_SUPPORTED,
Packit caecb6
				     "cannot process content type %s",
Packit caecb6
				     content_type);
Packit caecb6
			return NULL;
Packit caecb6
		}
Packit caecb6
		istream_tmp = item->func_adapter (self, ctx, item->user_data,
Packit caecb6
						  cancellable, error);
Packit caecb6
		if (istream_tmp == NULL)
Packit caecb6
			return NULL;
Packit caecb6
		xb_builder_source_remove_last_extension (basename);
Packit caecb6
		g_set_object (&priv->istream, istream_tmp);
Packit caecb6
Packit caecb6
		if (item->is_simple)
Packit caecb6
			break;
Packit caecb6
	} while (TRUE);
Packit caecb6
	return g_object_ref (priv->istream);
Packit caecb6
}
Packit caecb6
Packit caecb6
GFile *
Packit caecb6
xb_builder_source_get_file (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), NULL);
Packit caecb6
	return priv->file;
Packit caecb6
}
Packit caecb6
Packit caecb6
XbBuilderSourceFlags
Packit caecb6
xb_builder_source_get_flags (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	g_return_val_if_fail (XB_IS_BUILDER_SOURCE (self), 0);
Packit caecb6
	return priv->flags;
Packit caecb6
}
Packit caecb6
Packit caecb6
static GInputStream *
Packit caecb6
xb_builder_source_load_gzip_cb (XbBuilderSource *self,
Packit caecb6
				XbBuilderSourceCtx *ctx,
Packit caecb6
				gpointer user_data,
Packit caecb6
				GCancellable *cancellable,
Packit caecb6
				GError **error)
Packit caecb6
{
Packit caecb6
	GInputStream *istream = xb_builder_source_ctx_get_stream (ctx);
Packit caecb6
	g_autoptr(GConverter) conv = NULL;
Packit caecb6
	conv = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
Packit caecb6
	return g_converter_input_stream_new (istream, conv);
Packit caecb6
}
Packit caecb6
Packit caecb6
static void
Packit caecb6
xb_builder_source_adapter_free (XbBuilderSourceAdapter *item)
Packit caecb6
{
Packit caecb6
	if (item->user_data_free != NULL)
Packit caecb6
		item->user_data_free (item->user_data);
Packit caecb6
	g_free (item->content_type);
Packit caecb6
	g_slice_free (XbBuilderSourceAdapter, item);
Packit caecb6
}
Packit caecb6
Packit caecb6
static void
Packit caecb6
xb_builder_source_finalize (GObject *obj)
Packit caecb6
{
Packit caecb6
	XbBuilderSource *self = XB_BUILDER_SOURCE (obj);
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
Packit caecb6
	if (priv->istream != NULL)
Packit caecb6
		g_object_unref (priv->istream);
Packit caecb6
	if (priv->info != NULL)
Packit caecb6
		g_object_unref (priv->info);
Packit caecb6
	if (priv->file != NULL)
Packit caecb6
		g_object_unref (priv->file);
Packit caecb6
	g_ptr_array_unref (priv->fixups);
Packit caecb6
	g_ptr_array_unref (priv->adapters);
Packit caecb6
	g_free (priv->guid);
Packit caecb6
	g_free (priv->prefix);
Packit caecb6
	g_free (priv->content_type);
Packit caecb6
Packit caecb6
	G_OBJECT_CLASS (xb_builder_source_parent_class)->finalize (obj);
Packit caecb6
}
Packit caecb6
Packit caecb6
static void
Packit caecb6
xb_builder_source_class_init (XbBuilderSourceClass *klass)
Packit caecb6
{
Packit caecb6
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit caecb6
	object_class->finalize = xb_builder_source_finalize;
Packit caecb6
}
Packit caecb6
Packit caecb6
static void
Packit caecb6
xb_builder_source_init (XbBuilderSource *self)
Packit caecb6
{
Packit caecb6
	XbBuilderSourcePrivate *priv = GET_PRIVATE (self);
Packit caecb6
	priv->fixups = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Packit caecb6
	priv->adapters = g_ptr_array_new_with_free_func ((GDestroyNotify) xb_builder_source_adapter_free);
Packit caecb6
	xb_builder_source_add_adapter (self, "application/gzip,application/x-gzip",
Packit caecb6
				       xb_builder_source_load_gzip_cb, NULL, NULL);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_builder_source_new:
Packit caecb6
 *
Packit caecb6
 * Creates a new builder source.
Packit caecb6
 *
Packit caecb6
 * Returns: a new #XbBuilderSource
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.1
Packit caecb6
 **/
Packit caecb6
XbBuilderSource *
Packit caecb6
xb_builder_source_new (void)
Packit caecb6
{
Packit caecb6
	return g_object_new (XB_TYPE_BUILDER_SOURCE, NULL);
Packit caecb6
}