Пример #1
0
/**
 * as_provide_node_parse:
 * @provide: a #AsProvide instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.1.6
 **/
gboolean
as_provide_node_parse (AsProvide *provide, GNode *node,
		       AsNodeContext *ctx, GError **error)
{
	AsProvidePrivate *priv = GET_PRIVATE (provide);
	const gchar *tmp;

	if (g_strcmp0 (as_node_get_name (node), "dbus") == 0) {
		tmp = as_node_get_attribute (node, "type");
		if (g_strcmp0 (tmp, "system") == 0)
			priv->kind = AS_PROVIDE_KIND_DBUS_SYSTEM;
		else
			priv->kind = AS_PROVIDE_KIND_DBUS_SESSION;
	} else if (g_strcmp0 (as_node_get_name (node), "firmware") == 0) {
		tmp = as_node_get_attribute (node, "type");
		if (g_strcmp0 (tmp, "flashed") == 0)
			priv->kind = AS_PROVIDE_KIND_FIRMWARE_FLASHED;
		else
			priv->kind = AS_PROVIDE_KIND_FIRMWARE_RUNTIME;
	} else {
		priv->kind = as_provide_kind_from_string (as_node_get_name (node));
	}
	as_ref_string_assign (&priv->value, as_node_get_data_as_refstr (node));
	return TRUE;
}
Пример #2
0
/**
 * as_content_rating_node_parse:
 * @content_rating: a #AsContentRating instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.5.12
 **/
gboolean
as_content_rating_node_parse (AsContentRating *content_rating, GNode *node,
			      AsNodeContext *ctx, GError **error)
{
	AsContentRatingPrivate *priv = GET_PRIVATE (content_rating);
	GNode *c;
	const gchar *tmp;
	g_autoptr(GHashTable) captions = NULL;

	/* get ID */
	tmp = as_node_get_attribute (node, "type");
	if (tmp != NULL)
		as_content_rating_set_kind (content_rating, tmp);

	/* get keys */
	for (c = node->children; c != NULL; c = c->next) {
		AsContentRatingKey *key;
		g_autoptr(AsImage) image = NULL;
		if (as_node_get_tag (c) != AS_TAG_CONTENT_ATTRIBUTE)
			continue;
		key = g_slice_new0 (AsContentRatingKey);
		as_ref_string_assign (&key->id, as_node_get_attribute (c, "id"));
		key->value = as_content_rating_value_from_string (as_node_get_data (c));
		g_ptr_array_add (priv->keys, key);
	}
	return TRUE;
}
Пример #3
0
static AsNode *
as_node_get_child_node (const AsNode *root, const gchar *name,
			const gchar *attr_key, const gchar *attr_value)
{
	AsNodeData *data;
	AsNode *node;

	/* invalid */
	if (root == NULL)
		return NULL;
	if (name == NULL || name[0] == '\0')
		return NULL;

	/* find a node called name */
	for (node = root->children; node != NULL; node = node->next) {
		data = node->data;
		if (data == NULL)
			return NULL;
		if (g_strcmp0 (as_tag_data_get_name (data), name) == 0) {
			if (attr_key != NULL) {
				if (g_strcmp0 (as_node_get_attribute (node, attr_key),
					       attr_value) != 0) {
					continue;
				}
			}
			return node;
		}
	}
	return NULL;
}
Пример #4
0
/**
 * as_image_node_parse:
 * @image: a #AsImage instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.1.0
 **/
gboolean
as_image_node_parse (AsImage *image, GNode *node,
		     AsNodeContext *ctx, GError **error)
{
	AsImagePrivate *priv = GET_PRIVATE (image);
	const gchar *tmp;
	gchar *taken;
	guint size;

	size = as_node_get_attribute_as_int (node, "width");
	if (size != G_MAXINT)
		as_image_set_width (image, size);
	size = as_node_get_attribute_as_int (node, "height");
	if (size != G_MAXINT)
		as_image_set_height (image, size);
	tmp = as_node_get_attribute (node, "type");
	if (tmp == NULL)
		as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
	else
		as_image_set_kind (image, as_image_kind_from_string (tmp));
	taken = as_node_take_data (node);
	if (taken != NULL) {
		g_free (priv->url);
		priv->url = taken;
	}
	return TRUE;
}
Пример #5
0
/**
 * asb_plugin_process_gir:
 */
static gboolean
asb_plugin_process_gir (AsbApp *app,
			const gchar *tmpdir,
			const gchar *filename,
			GError **error)
{
	GNode *l;
	GNode *node = NULL;
	const gchar *name;
	const gchar *version;
	gboolean ret = TRUE;
	g_autofree gchar *filename_full = NULL;
	g_autoptr(GFile) file = NULL;

	/* load file */
	filename_full = g_build_filename (tmpdir, filename, NULL);
	file = g_file_new_for_path (filename_full);
	node = as_node_from_file (file, AS_NODE_FROM_XML_FLAG_NONE, NULL, error);
	if (node == NULL) {
		ret = FALSE;
		goto out;
	}

	/* look for includes */
	l = as_node_find (node, "repository");
	if (l == NULL)
		goto out;
	for (l = l->children; l != NULL; l = l->next) {
		if (g_strcmp0 (as_node_get_name (l), "include") != 0)
			continue;
		name = as_node_get_attribute (l, "name");
		version = as_node_get_attribute (l, "version");
		if (g_strcmp0 (name, "Gtk") == 0 &&
		    g_strcmp0 (version, "3.0") == 0) {
			asb_package_log (asb_app_get_package (app),
					 ASB_PACKAGE_LOG_LEVEL_DEBUG,
					 "Auto-adding kudo ModernToolkit for %s",
					 as_app_get_id (AS_APP (app)));
			as_app_add_kudo_kind (AS_APP (app),
					      AS_KUDO_KIND_MODERN_TOOLKIT);
		}
	}
out:
	if (node != NULL)
		as_node_unref (node);
	return ret;
}
Пример #6
0
/**
 * as_icon_node_parse:
 * @icon: a #AsIcon instance.
 * @node: a #GNode.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.3.1
 **/
gboolean
as_icon_node_parse (AsIcon *icon, GNode *node, GError **error)
{
	AsIconPrivate *priv = GET_PRIVATE (icon);
	const gchar *tmp;
	gint size;
	gboolean prepend_size = TRUE;

	tmp = as_node_get_attribute (node, "type");
	as_icon_set_kind (icon, as_icon_kind_from_string (tmp));
	switch (priv->kind) {
	case AS_ICON_KIND_EMBEDDED:
		if (!as_icon_node_parse_embedded (icon, node, error))
			return FALSE;
		break;
	default:

		/* store the name without any prefix */
		tmp = as_node_get_data (node);
		if (g_strstr_len (tmp, -1, "/") == NULL) {
			as_icon_set_name (icon, tmp, -1);
		} else {
			_cleanup_free_ gchar *basename = NULL;
			basename = g_path_get_basename (tmp);
			as_icon_set_name (icon, basename, -1);
		}

		/* width is optional, assume 64px if missing */
		size = as_node_get_attribute_as_int (node, "width");
		if (size == G_MAXINT) {
			size = 64;
			prepend_size = FALSE;
		}
		priv->width = size;

		/* height is optional, assume 64px if missing */
		size = as_node_get_attribute_as_int (node, "height");
		if (size == G_MAXINT) {
			size = 64;
			prepend_size = FALSE;
		}
		priv->height = size;

		/* only use the size if the metadata has width and height */
		if (prepend_size) {
			g_free (priv->prefix_private);
			priv->prefix_private = g_strdup_printf ("%s/%ix%i",
								priv->prefix,
								priv->width,
								priv->height);
		}
		break;
	}

	return TRUE;
}
Пример #7
0
/**
 * as_image_node_parse:
 * @image: a #AsImage instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.1.0
 **/
gboolean
as_image_node_parse (AsImage *image, GNode *node,
		     AsNodeContext *ctx, GError **error)
{
	AsImagePrivate *priv = GET_PRIVATE (image);
	const gchar *tmp;
	guint size;

	size = as_node_get_attribute_as_uint (node, "width");
	if (size != G_MAXUINT)
		as_image_set_width (image, size);
	size = as_node_get_attribute_as_uint (node, "height");
	if (size != G_MAXUINT)
		as_image_set_height (image, size);
	tmp = as_node_get_attribute (node, "type");
	if (tmp == NULL)
		as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
	else
		as_image_set_kind (image, as_image_kind_from_string (tmp));
	as_ref_string_assign (&priv->url, as_node_get_data (node));
	as_ref_string_assign (&priv->locale, as_node_get_attribute (node, "xml:lang"));
	return TRUE;
}
Пример #8
0
/**
 * as_require_node_parse:
 * @require: a #AsRequire instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.6.7
 **/
gboolean
as_require_node_parse (AsRequire *require, GNode *node,
		       AsNodeContext *ctx, GError **error)
{
	AsRequirePrivate *priv = GET_PRIVATE (require);
	const gchar *tmp;
	tmp = as_node_get_name (node);
	if (tmp != NULL)
		as_require_set_kind (require, as_require_kind_from_string (tmp));
	tmp = as_node_get_attribute (node, "compare");
	if (tmp != NULL)
		as_require_set_compare (require, as_require_compare_from_string (tmp));
	as_ref_string_assign (&priv->version, as_node_get_attribute_as_refstr (node, "version"));
	as_ref_string_assign (&priv->value, as_node_get_data_as_refstr (node));
	return TRUE;
}
Пример #9
0
static void
as_node_passthrough_cb (GMarkupParseContext *context,
			const gchar         *passthrough_text,
			gsize                passthrough_len,
			gpointer             user_data,
			GError             **error)
{
	AsNodeToXmlHelper *helper = (AsNodeToXmlHelper *) user_data;
	const gchar *existing;
	const gchar *tmp;
	gchar *found;
	g_autofree gchar *text = NULL;

	/* only keep comments when told to */
	if ((helper->flags & AS_NODE_FROM_XML_FLAG_KEEP_COMMENTS) == 0)
		return;

	/* xml header */
	if (g_strstr_len (passthrough_text, passthrough_len, "<?xml") != NULL)
		return;

	/* get stripped comment without '<!--' and '-->' */
	text = g_strndup (passthrough_text, passthrough_len);
	if (!g_str_has_prefix (text, "<!--")) {
		g_warning ("Unexpected input: %s", text);
		return;
	}
	found = g_strrstr (text, "-->");
	if (found != NULL)
		*found = '\0';
	tmp = text + 4;
	if ((helper->flags & AS_NODE_FROM_XML_FLAG_LITERAL_TEXT) == 0)
		tmp = g_strstrip ((gchar *) tmp);
	if (tmp == NULL || tmp[0] == '\0')
		return;

	/* append together comments */
	existing = as_node_get_attribute (helper->current, "@comment-tmp");
	if (existing == NULL) {
		as_node_add_attribute (helper->current, "@comment-tmp", tmp);
	} else {
		g_autofree gchar *join = NULL;
		join = g_strdup_printf ("%s<&>%s", existing, tmp);
		as_node_add_attribute (helper->current, "@comment-tmp", join);
	}
}
Пример #10
0
/**
 * as_node_get_attribute_as_int:
 * @node: a #AsNode
 * @key: the attribute key
 *
 * Gets a node attribute, e.g. 34
 *
 * Return value: integer value, or %G_MAXINT for error
 *
 * Since: 0.1.0
 **/
gint
as_node_get_attribute_as_int (const AsNode *node, const gchar *key)
{
	const gchar *tmp;
	gchar *endptr = NULL;
	gint64 value_tmp;

	tmp = as_node_get_attribute (node, key);
	if (tmp == NULL)
		return G_MAXINT;
	value_tmp = g_ascii_strtoll (tmp, &endptr, 10);
	if (value_tmp == 0 && tmp == endptr)
		return G_MAXINT;
	if (value_tmp > G_MAXINT || value_tmp < G_MININT)
		return G_MAXINT;
	return value_tmp;
}
Пример #11
0
static gboolean
as_app_validate_description (const gchar *xml,
			     AsAppValidateHelper *helper,
			     guint number_para_min,
			     guint number_para_max,
			     gboolean allow_short_para,
			     GError **error)
{
	GNode *l;
	GNode *l2;
	g_autoptr(AsNode) node = NULL;

	/* parse xml */
	node = as_node_from_xml (xml, AS_NODE_FROM_XML_FLAG_NONE, error);
	if (node == NULL)
		return FALSE;
	helper->number_paragraphs = 0;
	helper->previous_para_was_short = FALSE;
	for (l = node->children; l != NULL; l = l->next) {
		if (g_strcmp0 (as_node_get_name (l), "p") == 0) {
			if (as_node_get_attribute (l, "xml:lang") != NULL)
				continue;
			as_app_validate_description_para (as_node_get_data (l),
							  helper);
		} else if (g_strcmp0 (as_node_get_name (l), "ul") == 0 ||
			   g_strcmp0 (as_node_get_name (l), "ol") == 0) {
			as_app_validate_description_list (as_node_get_data (l),
							  allow_short_para,
							  helper);
			for (l2 = l->children; l2 != NULL; l2 = l2->next) {
				if (g_strcmp0 (as_node_get_name (l2), "li") == 0) {
					if (as_node_get_attribute (l2, "xml:lang") != NULL)
						continue;
					as_app_validate_description_li (as_node_get_data (l2),
									helper);
				} else {
					/* only <li> supported */
					g_set_error (error,
						     AS_APP_ERROR,
						     AS_APP_ERROR_FAILED,
						     "invalid markup: <%s> follows <%s>",
						     as_node_get_name (l2),
						     as_node_get_name (l));
					return FALSE;
				}
			}
		} else {
			/* only <p>, <ol> and <ul> supported */
			g_set_error (error,
				     AS_APP_ERROR,
				     AS_APP_ERROR_FAILED,
				     "invalid markup: tag <%s> invalid here",
				     as_node_get_name (l));
			return FALSE;
		}
	}

	/* previous paragraph wasn't long enough */
	if (helper->previous_para_was_short) {
		ai_app_validate_add (helper,
				     AS_PROBLEM_KIND_STYLE_INCORRECT,
				     "<p> is too short [%s]",
				     helper->previous_para_was_short_str);
	}
	if (helper->number_paragraphs < number_para_min) {
		ai_app_validate_add (helper,
				     AS_PROBLEM_KIND_STYLE_INCORRECT,
				     "Not enough <p> tags for a good description [%u/%u]",
				     helper->number_paragraphs,
				     number_para_min);
	}
	if (helper->number_paragraphs > number_para_max) {
		ai_app_validate_add (helper,
				     AS_PROBLEM_KIND_STYLE_INCORRECT,
				     "Too many <p> tags for a good description [%u/%u]",
				     helper->number_paragraphs,
				     number_para_max);
	}
	return TRUE;
}
Пример #12
0
/**
 * as_screenshot_node_parse:
 * @screenshot: a #AsScreenshot instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.1.0
 **/
gboolean
as_screenshot_node_parse (AsScreenshot *screenshot, GNode *node,
			  AsNodeContext *ctx, GError **error)
{
	AsScreenshotPrivate *priv = GET_PRIVATE (screenshot);
	GList *l;
	GNode *c;
	const gchar *tmp;
	guint size;
	gint priority;
	g_autoptr(GHashTable) captions = NULL;

	tmp = as_node_get_attribute (node, "type");
	if (tmp != NULL) {
		as_screenshot_set_kind (screenshot,
					as_screenshot_kind_from_string (tmp));
	}
	priority = as_node_get_attribute_as_int (node, "priority");
	if (priority != G_MAXINT)
		as_screenshot_set_priority (screenshot, priority);

	/* add captions */
	captions = as_node_get_localized (node, "caption");
	if (captions != NULL) {
		g_autoptr(GList) keys = NULL;
		keys = g_hash_table_get_keys (captions);
		for (l = keys; l != NULL; l = l->next) {
			tmp = l->data;
			as_screenshot_set_caption (screenshot,
						   tmp,
						   g_hash_table_lookup (captions, tmp));
		}
	}

	/* AppData files does not have <image> tags */
	tmp = as_node_get_data (node);
	if (tmp != NULL) {
		AsImage *image;
		image = as_image_new ();
		as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
		size = as_node_get_attribute_as_uint (node, "width");
		if (size != G_MAXINT)
			as_image_set_width (image, size);
		size = as_node_get_attribute_as_uint (node, "height");
		if (size != G_MAXINT)
			as_image_set_height (image, size);
		as_image_set_url (image, tmp);
		g_ptr_array_add (priv->images, image);
	}

	/* add images */
	for (c = node->children; c != NULL; c = c->next) {
		g_autoptr(AsImage) image = NULL;
		if (as_node_get_tag (c) != AS_TAG_IMAGE)
			continue;
		image = as_image_new ();
		if (!as_image_node_parse (image, c, ctx, error))
			return FALSE;
		g_ptr_array_add (priv->images, g_object_ref (image));
	}
	return TRUE;
}
Пример #13
0
/**
 * as_icon_node_parse:
 * @icon: a #AsIcon instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.3.1
 **/
gboolean
as_icon_node_parse (AsIcon *icon, GNode *node,
		    AsNodeContext *ctx, GError **error)
{
	AsIconPrivate *priv = GET_PRIVATE (icon);
	const gchar *tmp;
	guint size;
	gboolean prepend_size = TRUE;

	tmp = as_node_get_attribute (node, "type");
	as_icon_set_kind (icon, as_icon_kind_from_string (tmp));
	switch (priv->kind) {
	case AS_ICON_KIND_EMBEDDED:
		if (!as_icon_node_parse_embedded (icon, node, error))
			return FALSE;
		break;
	default:

		/* preserve the URL for remote icons */
		tmp = as_node_get_data (node);
		if (tmp == NULL) {
			g_set_error (error,
				     AS_ICON_ERROR,
				     AS_ICON_ERROR_FAILED,
				     "no data for icon of type %s",
				     as_icon_kind_to_string (priv->kind));
			return FALSE;
		}
		if (priv->kind == AS_ICON_KIND_REMOTE)
			as_icon_set_url (icon, tmp);
		else if (priv->kind == AS_ICON_KIND_LOCAL)
			as_icon_set_filename (icon, tmp);

		/* store the name without any prefix */
		if (g_strstr_len (tmp, -1, "/") == NULL) {
			as_icon_set_name (icon, tmp);
		} else {
			g_autofree gchar *basename = NULL;
			basename = g_path_get_basename (tmp);
			as_icon_set_name (icon, basename);
		}

		/* width is optional, assume 64px if missing */
		size = as_node_get_attribute_as_uint (node, "width");
		if (size == G_MAXUINT) {
			size = 64;
			prepend_size = FALSE;
		}
		priv->width = size;

		/* height is optional, assume 64px if missing */
		size = as_node_get_attribute_as_uint (node, "height");
		if (size == G_MAXUINT) {
			size = 64;
			prepend_size = FALSE;
		}
		priv->height = size;

		/* only use the size if the metadata has width and height */
		if (prepend_size) {
			g_free (priv->prefix_private);
			priv->prefix_private = g_strdup_printf ("%s/%ux%u",
								priv->prefix,
								priv->width,
								priv->height);
		}
		break;
	}

	return TRUE;
}
Пример #14
0
/**
 * as_node_get_comment:
 * @node: a #AsNode
 *
 * Gets the node data, e.g. "Copyright 2014 Richard Hughes"
 *
 * Return value: string value, or %NULL
 *
 * Since: 0.1.6
 **/
const gchar *
as_node_get_comment (const AsNode *node)
{
	return as_node_get_attribute (node, "@comment");
}
Пример #15
0
/**
 * as_node_get_localized_unwrap:
 * @node: a #AsNode.
 * @error: A #GError or %NULL.
 *
 * Denormalize AppData data like this:
 *
 * |[
 * <description>
 *  <p>Hi</p>
 *  <p xml:lang="pl">Czesc</p>
 *  <ul>
 *   <li>First</li>
 *   <li xml:lang="pl">Pierwszy</li>
 *  </ul>
 * </description>
 * ]|
 *
 * into a hash that contains:
 *
 * |[
 * "C"  ->  "<p>Hi</p><ul><li>First</li></ul>"
 * "pl" ->  "<p>Czesc</p><ul><li>Pierwszy</li></ul>"
 * ]|
 *
 * Returns: (transfer full): a hash table of data
 *
 * Since: 0.1.0
 **/
GHashTable *
as_node_get_localized_unwrap (const AsNode *node, GError **error)
{
	AsNodeData *data;
	GHashTable *results;
	GList *l;
	AsNode *tmp;
	GString *str;
	const gchar *xml_lang;
	gboolean is_li_translated = TRUE;
	g_autoptr(GHashTable) hash = NULL;
	g_autoptr(GList) keys = NULL;

	g_return_val_if_fail (node != NULL, NULL);

	/* work out what kind of normalization this is */
	xml_lang = as_node_get_attribute (node, "xml:lang");
	if (xml_lang != NULL && node->children != NULL) {
		str = as_node_to_xml (node->children, AS_NODE_TO_XML_FLAG_NONE);
		results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
		g_hash_table_insert (results,
				     g_strdup (xml_lang),
				     g_strdup (str->str));
		g_string_free (str, TRUE);
		return results;
	}
	for (tmp = node->children; tmp != NULL; tmp = tmp->next) {
		data = tmp->data;
		if (g_strcmp0 (data->name, "ul") == 0 ||
		    g_strcmp0 (data->name, "ol") == 0) {
			if (as_node_attr_lookup (data, "xml:lang") != NULL) {
				is_li_translated = FALSE;
				break;
			}
		}
	}

	/* unwrap it to a hash */
	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
				      g_free, (GDestroyNotify) as_node_string_free);
	if (is_li_translated) {
		if (!as_node_get_localized_unwrap_type_li (node, hash, error))
			return NULL;
	} else {
		if (!as_node_get_localized_unwrap_type_ul (node, hash, error))
			return NULL;
	}

	/* copy into a hash table of the correct size */
	results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
	keys = g_hash_table_get_keys (hash);
	for (l = keys; l != NULL; l = l->next) {
		gchar *locale_fixed;
		xml_lang = l->data;
		locale_fixed = as_node_fix_locale (xml_lang);
		if (locale_fixed == NULL)
			continue;
		str = g_hash_table_lookup (hash, xml_lang);
		g_hash_table_insert (results,
				     locale_fixed,
				     g_strdup (str->str));
	}
	return results;
}
Пример #16
0
/**
 * as_review_node_parse:
 * @review: a #AsReview instance.
 * @node: a #GNode.
 * @ctx: a #AsNodeContext.
 * @error: A #GError or %NULL.
 *
 * Populates the object from a DOM node.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.6.1
 **/
gboolean
as_review_node_parse (AsReview *review, GNode *node,
		     AsNodeContext *ctx, GError **error)
{
	AsReviewPrivate *priv = GET_PRIVATE (review);
	AsNode *c;
	const gchar *tmp;
	gint itmp;

	itmp = as_node_get_attribute_as_int (node, "rating");
	if (itmp != G_MAXINT)
		as_review_set_rating (review, itmp);
	tmp = as_node_get_attribute (node, "date");
	if (tmp != NULL) {
		g_autoptr(GDateTime) dt = as_utils_iso8601_to_datetime (tmp);
		if (dt != NULL)
			as_review_set_date (review, dt);
	}
	tmp = as_node_get_attribute (node, "id");
	if (tmp != NULL)
		as_review_set_id (review, tmp);
	for (c = node->children; c != NULL; c = c->next) {
		if (as_node_get_tag (c) == AS_TAG_SUMMARY) {
			as_review_set_summary (review, as_node_get_data (c));
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_PRIORITY) {
			gint64 prio = g_ascii_strtoll (as_node_get_data (c),
						       NULL, 10);
			as_review_set_priority (review, (gint) prio);
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_DESCRIPTION) {
			g_autoptr(GString) xml = NULL;
			xml = as_node_to_xml (c->children, AS_NODE_TO_XML_FLAG_INCLUDE_SIBLINGS);
			as_review_set_description (review, xml->str);
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_VERSION) {
			as_review_set_version (review, as_node_get_data (c));
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_REVIEWER_ID) {
			as_review_set_reviewer_id (review, as_node_get_data (c));
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_REVIEWER_NAME) {
			as_review_set_reviewer_name (review, as_node_get_data (c));
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_LANG) {
			as_review_set_locale (review, as_node_get_data (c));
			continue;
		}
		if (as_node_get_tag (c) == AS_TAG_METADATA) {
			AsNode *c2;
			for (c2 = c->children; c2 != NULL; c2 = c2->next) {
				AsRefString *key;
				AsRefString *value;
				if (as_node_get_tag (c2) != AS_TAG_VALUE)
					continue;
				key = as_node_get_attribute (c2, "key");
				value = as_node_get_data (c2);
				if (value == NULL) {
					g_hash_table_insert (priv->metadata,
							     as_ref_string_ref (key),
							     as_ref_string_new_static (""));
				} else {
					g_hash_table_insert (priv->metadata,
							     as_ref_string_ref (key),
							     as_ref_string_ref (value));
				}
			}
			continue;
		}
	}
	return TRUE;
}