Beispiel #1
0
/**
 * asb_context_detect_missing_parents:
 **/
static gboolean
asb_context_detect_missing_parents (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsApp *found;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;
	const gchar *tmp;
	g_autoptr(GHashTable) hash = NULL;

	/* add all desktop apps to the hash */
	hash = g_hash_table_new (g_str_hash, g_str_equal);
	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (!ASB_IS_APP (app))
			continue;
		if (as_app_get_pkgname_default (app) == NULL)
			continue;
		if (as_app_get_id_kind (app) != AS_ID_KIND_DESKTOP)
			continue;
		g_hash_table_insert (hash,
				     (gpointer) as_app_get_id (app),
				     app);
	}

	/* look for the thing that an addon extends */
	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (!ASB_IS_APP (app))
			continue;
		if (as_app_get_pkgname_default (app) == NULL)
			continue;
		if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON)
			continue;
		if (as_app_get_extends(app)->len == 0)
			continue;
		tmp = g_ptr_array_index (as_app_get_extends(app), 0);
		found = g_hash_table_lookup (hash, tmp);
		if (found != NULL)
			continue;
		found = as_store_get_app_by_id (priv->store_old, tmp);
		if (found != NULL)
			continue;

		/* do not add the addon */
		as_app_add_veto (app, "%s has no parent of '%s'",
				 as_app_get_id (app), tmp);
		g_print ("WARNING: %s has no parent of '%s'\n",
			 as_app_get_id (app), tmp);
	}
	return TRUE;
}
static AsApp *
get_installed_appstream_app (GsPlugin *plugin,
			     GsApp *app,
			     GCancellable *cancellable,
			     GError **error)
{
	AsApp *as_app;
	GsPluginData *priv = gs_plugin_get_data (plugin);
	const char *deploy_dir;
	g_autoptr(AsStore) store = NULL;
	g_autoptr(FlatpakInstalledRef) ref = NULL;
	g_autoptr(GFile) appstream_file = NULL;
	g_autofree char *appstream_xml = NULL;
	g_autofree char *appstream_path = NULL;

	ref = flatpak_installation_get_installed_ref (priv->installation,
						      FLATPAK_REF_KIND_APP,
						      gs_app_get_flatpak_name (app),
						      gs_app_get_flatpak_arch (app),
						      gs_app_get_flatpak_branch (app),
						      cancellable,
						      error);

	if (!ref)
		return NULL;

	deploy_dir = flatpak_installed_ref_get_deploy_dir (ref);

	appstream_xml = g_strdup_printf ("%s.appdata.xml",
					  gs_app_get_flatpak_name (app));
	appstream_path = g_build_filename (deploy_dir, "files", "share",
					   "app-info", "xmls", appstream_xml,
					   NULL);
	appstream_file = g_file_new_for_path (appstream_path);

	store = as_store_new ();
	as_store_set_add_flags (store,
				AS_STORE_ADD_FLAG_USE_UNIQUE_ID |
				AS_STORE_ADD_FLAG_USE_MERGE_HEURISTIC);
	if (!as_store_from_file (store, appstream_file, NULL, cancellable,
				 error))
		return NULL;

	as_app = as_store_get_app_by_id (store, gs_app_get_id (app));
	if (!as_app)
		g_set_error (error, AS_STORE_ERROR, AS_STORE_ERROR_FAILED,
			     "Failed to get app %s from its own installation "
			     "AppStream file.", gs_app_get_unique_id (app));

	return g_object_ref (as_app);
}
Beispiel #3
0
static void
gs_editor_app_tile_clicked_cb (GsAppTile *tile, GsEditor *self)
{
	GsApp *app = gs_app_tile_get_app (tile);
	AsApp *item = as_store_get_app_by_id (self->store, gs_app_get_id (app));
	if (item == NULL) {
		g_warning ("failed to find %s", gs_app_get_id (app));
		return;
	}
	g_set_object (&self->selected_item, item);

	gs_editor_refresh_details (self);
	gs_editor_set_page (self, "details");
}
Beispiel #4
0
/**
 * asb_context_write_xml_fail:
 **/
static gboolean
asb_context_write_xml_fail (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;
	g_autofree gchar *basename_failed = NULL;
	g_autofree gchar *filename = NULL;
	g_autoptr(GFile) file = NULL;

	/* no need to create */
	if ((priv->flags & ASB_CONTEXT_FLAG_INCLUDE_FAILED) == 0)
		return TRUE;

	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (!ASB_IS_APP (app))
			continue;
		if (as_app_get_vetos(app)->len == 0)
			continue;
		if (as_store_get_app_by_id (priv->store_failed,
					    as_app_get_id (app)) != NULL)
			continue;
		as_store_add_app (priv->store_failed, app);
	}
	filename = g_strdup_printf ("%s/%s-failed.xml.gz",
				    priv->output_dir, priv->basename);
	file = g_file_new_for_path (filename);

	g_print ("Writing %s...\n", filename);
	basename_failed = g_strdup_printf ("%s-failed", priv->origin);
	as_store_set_origin (priv->store_failed, basename_failed);
	as_store_set_api_version (priv->store_failed, priv->api_version);
	if (priv->flags & ASB_CONTEXT_FLAG_ADD_CACHE_ID) {
		g_autofree gchar *builder_id = asb_utils_get_builder_id ();
		as_store_set_builder_id (priv->store_failed, builder_id);
	}
	return as_store_to_file (priv->store_failed,
				 file,
				 AS_NODE_TO_XML_FLAG_ADD_HEADER |
				 AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
				 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
				 NULL, error);
}
Beispiel #5
0
/**
 * asb_context_add_app_ignore:
 **/
void
asb_context_add_app_ignore (AsbContext *ctx, AsbPackage *pkg)
{
	AsApp *app_tmp;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	g_autofree gchar *name_arch = NULL;
	g_autoptr(AsApp) app = NULL;
	g_autoptr(GPtrArray) apps = NULL;

	/* only do this when we are using a cache-id */
	if ((priv->flags & ASB_CONTEXT_FLAG_ADD_CACHE_ID) == 0)
		return;

	/* check not already added a dummy application for this package */
	apps = as_store_get_apps_by_metadata (priv->store_ignore,
					      "X-CacheID",
					      asb_package_get_basename (pkg));
	if (apps->len > 0) {
		g_debug ("already found CacheID of %s",
			 asb_package_get_basename (pkg));
		return;
	}

	/* package name already exists, but with a different CacheID */
	name_arch = g_strdup_printf ("%s.%s",
				     asb_package_get_name (pkg),
				     asb_package_get_arch (pkg));
	app_tmp = as_store_get_app_by_id (priv->store_ignore, name_arch);
	if (app_tmp != NULL) {
		as_app_add_metadata (AS_APP (app_tmp), "X-CacheID",
				     asb_package_get_basename (pkg));
		return;
	}

	/* never encountered before, so add */
	app = as_app_new ();
	as_app_set_id (app, name_arch);
	as_app_add_pkgname (app, asb_package_get_name (pkg));
	as_app_add_metadata (app, "X-CacheID",
			     asb_package_get_basename (pkg));
	as_store_add_app (priv->store_ignore, app);
}
Beispiel #6
0
static void
gs_editor_button_remove_clicked_cb (GtkWidget *widget, GsEditor *self)
{
	const gchar *name;
	g_autofree gchar *msg = NULL;

	if (self->selected_item == NULL)
		return;

	/* send notification */
	name = as_app_get_name (self->selected_item, NULL);
	if (name == NULL) {
		AsApp *item_global = as_store_get_app_by_id (self->store_global,
							     as_app_get_id (self->selected_item));
		if (item_global != NULL)
			name = as_app_get_name (item_global, NULL);
	}
	if (name != NULL) {
		g_autofree gchar *name_markup = NULL;
		name_markup = g_strdup_printf ("<b>%s</b>", name);
		/* TRANSLATORS, the %s is the app name, e.g. 'Inkscape' */
		msg = g_strdup_printf (_("%s banner design deleted."), name_markup);
	} else {
		/* TRANSLATORS, this is a notification */
		msg = g_strdup (_("Banner design deleted."));
	}
	gs_editor_show_notification (self, msg);

	/* save this so we can undo */
	g_set_object (&self->deleted_item, self->selected_item);

	as_store_remove_app_by_id (self->store, as_app_get_id (self->selected_item));
	self->pending_changes = TRUE;
	gs_editor_refresh_choice (self);

	/* set the appropriate page */
	gs_editor_set_page (self, as_store_get_size (self->store) == 0 ? "none" : "choice");
}
Beispiel #7
0
static GsApp *
gs_editor_convert_app (GsEditor *self, AsApp *item)
{
	AsApp *item_global;
	AsAppState item_state;
	GsApp *app;
	const gchar *keys[] = {
		"GnomeSoftware::AppTile-css",
		"GnomeSoftware::FeatureTile-css",
		"GnomeSoftware::UpgradeBanner-css",
		NULL };

	/* copy name, summary and description */
	app = gs_app_new (as_app_get_id (item));
	item_global = as_store_get_app_by_id (self->store_global, as_app_get_id (item));
	if (item_global == NULL) {
		const gchar *tmp;
		g_autoptr(AsIcon) ic = NULL;
		g_debug ("no app found for %s, using fallback", as_app_get_id (item));

		/* copy from AsApp, falling back to something sane */
		tmp = as_app_get_name (item, NULL);
		if (tmp == NULL)
			tmp = "Application";
		gs_app_set_name (app, GS_APP_QUALITY_NORMAL, tmp);
		tmp = as_app_get_comment (item, NULL);
		if (tmp == NULL)
			tmp = "Description";
		gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, tmp);
		tmp = as_app_get_description (item, NULL);
		if (tmp == NULL)
			tmp = "A multiline description";
		gs_app_set_description (app, GS_APP_QUALITY_NORMAL, tmp);
		ic = as_icon_new ();
		as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
		as_icon_set_name (ic, "application-x-executable");
		gs_app_add_icon (app, ic);
		item_state = as_app_get_state (item);
	} else {
		GPtrArray *icons;
		g_debug ("found global app for %s", as_app_get_id (item));
		gs_app_set_name (app, GS_APP_QUALITY_NORMAL,
				 as_app_get_name (item_global, NULL));
		gs_app_set_summary (app, GS_APP_QUALITY_NORMAL,
				    as_app_get_comment (item_global, NULL));
		gs_app_set_description (app, GS_APP_QUALITY_NORMAL,
					as_app_get_description (item_global, NULL));
		icons = as_app_get_icons (item_global);
		for (guint i = 0; i < icons->len; i++) {
			AsIcon *icon = g_ptr_array_index (icons, i);
			gs_app_add_icon (app, icon);
		}
		item_state = as_app_get_state (item_global);
	}

	/* copy state */
	if (item_state == AS_APP_STATE_UNKNOWN)
		item_state = AS_APP_STATE_AVAILABLE;
	gs_app_set_state (app, item_state);

	/* copy version */
	gs_app_set_version (app, "3.28");

	/* load pixbuf */
	gs_editor_refine_app_pixbuf (app);

	/* copy metadata */
	for (guint i = 0; keys[i] != NULL; i++) {
		g_autoptr(GError) error = NULL;
		const gchar *markup = as_app_get_metadata_item (item, keys[i]);
		if (markup != NULL) {
			g_autofree gchar *css_new = NULL;
			css_new = gs_editor_css_download_resources (self, markup, &error);
			if (css_new == NULL) {
				g_warning ("%s", error->message);
				gs_app_set_metadata (app, keys[i], markup);
			} else {
				gs_app_set_metadata (app, keys[i], css_new);
			}
		} else {
			gs_app_set_metadata (app, keys[i], NULL);
		}
	}
	return app;
}
Beispiel #8
0
/**
 * fu_util_verify_all:
 **/
static gboolean
fu_util_verify_all (FuUtilPrivate *priv, GError **error)
{
	AsApp *app;
	FuDevice *dev;
	const gchar *tmp;
	guint i;
	_cleanup_object_unref_ AsStore *store = NULL;
	_cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
	_cleanup_ptrarray_unref_ GPtrArray *devices_tmp = NULL;

	/* get devices from daemon */
	devices_tmp = fu_util_get_devices_internal (priv, error);
	if (devices_tmp == NULL)
		return FALSE;

	/* get results */
	for (i = 0; i < devices_tmp->len; i++) {
		_cleanup_error_free_ GError *error_local = NULL;
		dev = g_ptr_array_index (devices_tmp, i);
		if (!fu_util_verify_internal (priv, fu_device_get_id (dev), &error_local)) {
			g_print ("Failed to verify %s: %s\n",
				 fu_device_get_id (dev),
				 error_local->message);
		}
	}

	/* only load firmware from the system */
	store = as_store_new ();
	as_store_add_filter (store, AS_ID_KIND_FIRMWARE);
	if (!as_store_load (store, AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM, NULL, error))
		return FALSE;

	/* print */
	devices = fu_util_get_devices_internal (priv, error);
	if (devices == NULL)
		return FALSE;
	for (i = 0; i < devices->len; i++) {
		const gchar *hash = NULL;
		const gchar *ver = NULL;
		_cleanup_free_ gchar *status = NULL;

		dev = g_ptr_array_index (devices, i);
		hash = fu_device_get_metadata (dev, FU_DEVICE_KEY_FIRMWARE_HASH);
		if (hash == NULL)
			continue;
		app = as_store_get_app_by_id (store, fu_device_get_guid (dev));
		if (app == NULL) {
			status = g_strdup ("No metadata");
		} else {
			AsRelease *rel;
			ver = fu_device_get_metadata (dev, FU_DEVICE_KEY_VERSION);
			rel = as_app_get_release (app, ver);
			if (rel == NULL) {
				status = g_strdup_printf ("No version %s", ver);
			} else {
				tmp = as_release_get_checksum (rel, G_CHECKSUM_SHA1);
				if (g_strcmp0 (tmp, hash) != 0) {
					status = g_strdup_printf ("Failed: for v%s expected %s", ver, tmp);
				} else {
					status = g_strdup ("OK");
				}
			}
		}
		g_print ("%s\t%s\t%s\n", fu_device_get_guid (dev), hash, status);
	}

	return TRUE;
}
/**
 * gs_plugin_file_to_app:
 */
gboolean
gs_plugin_file_to_app (GsPlugin *plugin,
		       GList **list,
		       GFile *file,
		       GCancellable *cancellable,
		       GError **error)
{
	g_autofree gchar *content_type = NULL;
	g_autofree gchar *id_prefixed = NULL;
	g_autoptr(GBytes) appstream_gz = NULL;
	g_autoptr(GBytes) icon_data = NULL;
	g_autoptr(GBytes) metadata = NULL;
	g_autoptr(GsApp) app = NULL;
	g_autoptr(XdgAppBundleRef) xref_bundle = NULL;
	const gchar *mimetypes[] = {
		"application/vnd.xdgapp",
		NULL };

	/* does this match any of the mimetypes we support */
	content_type = gs_utils_get_content_type (file, cancellable, error);
	if (content_type == NULL)
		return FALSE;
	if (!g_strv_contains (mimetypes, content_type))
		return TRUE;

	/* load bundle */
	xref_bundle = xdg_app_bundle_ref_new (file, error);
	if (xref_bundle == NULL) {
		g_prefix_error (error, "error loading bundle: ");
		return FALSE;
	}

	/* create a virtual ID */
	id_prefixed = gs_plugin_xdg_app_build_id (XDG_APP_REF (xref_bundle));

	/* load metadata */
	app = gs_app_new (id_prefixed);
	gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
	gs_app_set_state (app, AS_APP_STATE_AVAILABLE_LOCAL);
	gs_app_set_size_installed (app, xdg_app_bundle_ref_get_installed_size (xref_bundle));
	gs_plugin_xdg_app_set_metadata (app, XDG_APP_REF (xref_bundle));
	metadata = xdg_app_bundle_ref_get_metadata (xref_bundle);
	if (!gs_plugin_xdg_app_set_app_metadata (app,
						 g_bytes_get_data (metadata, NULL),
						 g_bytes_get_size (metadata),
						 error))
		return FALSE;

	/* load AppStream */
	appstream_gz = xdg_app_bundle_ref_get_appstream (xref_bundle);
	if (appstream_gz != NULL) {
		g_autoptr(GZlibDecompressor) decompressor = NULL;
		g_autoptr(GInputStream) stream_gz = NULL;
		g_autoptr(GInputStream) stream_data = NULL;
		g_autoptr(GBytes) appstream = NULL;
		g_autoptr(AsStore) store = NULL;
		g_autofree gchar *id = NULL;
		AsApp *item;

		/* decompress data */
		decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
		stream_gz = g_memory_input_stream_new_from_bytes (appstream_gz);
		if (stream_gz == NULL)
			return FALSE;
		stream_data = g_converter_input_stream_new (stream_gz,
							    G_CONVERTER (decompressor));

		appstream = g_input_stream_read_bytes (stream_data,
						       0x100000, /* 1Mb */
						       cancellable,
						       error);
		if (appstream == NULL)
			return FALSE;
		store = as_store_new ();
		if (!as_store_from_bytes (store, appstream, cancellable, error))
			return FALSE;

		/* find app */
		id = g_strdup_printf ("%s.desktop", gs_app_get_xdgapp_name (app));
		item = as_store_get_app_by_id (store, id);
		if (item == NULL) {
			g_set_error (error,
				     GS_PLUGIN_ERROR,
				     GS_PLUGIN_ERROR_FAILED,
				     "application %s not found",
				     id);
			return FALSE;
		}

		/* copy details from AppStream to app */
		if (!gs_appstream_refine_app (plugin, app, item, error))
			return FALSE;
	}

	/* load icon */
	icon_data = xdg_app_bundle_ref_get_icon (xref_bundle,
						 64 * gs_plugin_get_scale (plugin));
	if (icon_data == NULL)
		icon_data = xdg_app_bundle_ref_get_icon (xref_bundle, 64);
	if (icon_data != NULL) {
		g_autoptr(GInputStream) stream_icon = NULL;
		g_autoptr(GdkPixbuf) pixbuf = NULL;
		stream_icon = g_memory_input_stream_new_from_bytes (icon_data);
		pixbuf = gdk_pixbuf_new_from_stream (stream_icon, cancellable, error);
		if (pixbuf == NULL)
			return FALSE;
		gs_app_set_pixbuf (app, pixbuf);
	} else {
		g_autoptr(AsIcon) icon = NULL;
		icon = as_icon_new ();
		as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
		as_icon_set_name (icon, "application-x-executable");
		gs_app_set_icon (app, icon);
	}

	/* not quite true: this just means we can update this specific app */
	if (xdg_app_bundle_ref_get_origin (xref_bundle))
		gs_app_add_quirk (app, AS_APP_QUIRK_HAS_SOURCE);

	g_debug ("created local app: %s", gs_app_to_string (app));
	gs_app_list_add (list, app);
	return TRUE;
}
Beispiel #10
0
static gboolean
gs_plugin_steam_update_store_app (GsPlugin *plugin,
				  AsStore *store,
				  GHashTable *app,
				  GError **error)
{
	const gchar *name;
	GVariant *tmp;
	guint32 gameid;
	gchar *app_id;
	g_autofree gchar *cache_basename = NULL;
	g_autofree gchar *cache_fn = NULL;
	g_autofree gchar *gameid_str = NULL;
	g_autofree gchar *html = NULL;
	g_autofree gchar *uri = NULL;
	g_autoptr(AsApp) item = NULL;

	/* this is the key */
	tmp = g_hash_table_lookup (app, "gameid");
	if (tmp == NULL)
		return TRUE;
	gameid = g_variant_get_uint32 (tmp);

	/* valve use the name as the application ID, not the gameid */
	tmp = g_hash_table_lookup (app, "name");
	if (tmp == NULL)
		return TRUE;
	name = g_variant_get_string (tmp, NULL);
	app_id = g_strdup_printf ("%s.desktop", name);

	/* already exists */
	if (as_store_get_app_by_id (store, app_id) != NULL) {
		g_debug ("already exists %" G_GUINT32_FORMAT ", skipping", gameid);
		return TRUE;
	}

	/* create application with the gameid as the key */
	g_debug ("parsing steam %" G_GUINT32_FORMAT, gameid);
	item = as_app_new ();
	as_app_set_kind (item, AS_APP_KIND_DESKTOP);
	as_app_set_project_license (item, "Steam");
	as_app_set_id (item, app_id);
	as_app_set_name (item, NULL, name);
	as_app_add_category (item, "Game");
	as_app_add_kudo_kind (item, AS_KUDO_KIND_MODERN_TOOLKIT);
	as_app_set_comment (item, NULL, "Available on Steam");

	/* this is for the GNOME Software plugin */
	gameid_str = g_strdup_printf ("%" G_GUINT32_FORMAT, gameid);
	as_app_add_metadata (item, "X-Steam-GameID", gameid_str);
	as_app_add_metadata (item, "GnomeSoftware::Plugin", "steam");

	/* ban certains apps based on the name */
	if (g_strstr_len (name, -1, "Dedicated Server") != NULL)
		as_app_add_veto (item, "Dedicated Server");

	/* oslist */
	tmp = g_hash_table_lookup (app, "oslist");
	if (tmp == NULL) {
		as_app_add_veto (item, "No operating systems listed");
	} else if (g_strstr_len (g_variant_get_string (tmp, NULL), -1, "linux") == NULL) {
		as_app_add_veto (item, "No Linux support");
	}

	/* url: homepage */
	tmp = g_hash_table_lookup (app, "homepage");
	if (tmp != NULL)
		as_app_add_url (item, AS_URL_KIND_HOMEPAGE, g_variant_get_string (tmp, NULL));

	/* developer name */
	tmp = g_hash_table_lookup (app, "developer");
	if (tmp != NULL)
		as_app_set_developer_name (item, NULL, g_variant_get_string (tmp, NULL));

	/* type */
	tmp = g_hash_table_lookup (app, "type");
	if (tmp != NULL) {
		const gchar *kind = g_variant_get_string (tmp, NULL);
		if (g_strcmp0 (kind, "DLC") == 0 ||
		    g_strcmp0 (kind, "Config") == 0 ||
		    g_strcmp0 (kind, "Tool") == 0)
			as_app_add_veto (item, "type is %s", kind);
	}

	/* don't bother saving apps with failures */
	if (as_app_get_vetos(item)->len > 0)
		return TRUE;

	/* icons */
	tmp = g_hash_table_lookup (app, "clienticns");
	if (tmp != NULL) {
		g_autoptr(GError) error_local = NULL;
		g_autofree gchar *ic_uri = NULL;
		ic_uri = g_strdup_printf ("https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.icns",
					  gameid, g_variant_get_string (tmp, NULL));
		if (!gs_plugin_steam_download_icon (plugin, item, ic_uri, &error_local)) {
			g_warning ("Failed to parse clienticns: %s",
				   error_local->message);
		}
	}

	/* try clienticon */
	if (as_app_get_icons(item)->len == 0) {
		tmp = g_hash_table_lookup (app, "clienticon");
		if (tmp != NULL) {
			g_autoptr(GError) error_local = NULL;
			g_autofree gchar *ic_uri = NULL;
			ic_uri = g_strdup_printf ("http://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.ico",
						  gameid, g_variant_get_string (tmp, NULL));
			if (!gs_plugin_steam_download_icon (plugin, item, ic_uri, &error_local)) {
				g_warning ("Failed to parse clienticon: %s",
					   error_local->message);
			}
		}
	}

	/* fall back to a resized logo */
	if (as_app_get_icons(item)->len == 0) {
		tmp = g_hash_table_lookup (app, "logo");
		if (tmp != NULL) {
			AsIcon *icon = NULL;
			g_autofree gchar *ic_uri = NULL;
			ic_uri = g_strdup_printf ("http://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.jpg",
						  gameid, g_variant_get_string (tmp, NULL));
			icon = as_icon_new ();
			as_icon_set_kind (icon, AS_ICON_KIND_REMOTE);
			as_icon_set_url (icon, ic_uri);
			as_app_add_icon (item, icon);
		}
	}

	/* size */
	tmp = g_hash_table_lookup (app, "maxsize");
	if (tmp != NULL) {
		/* string when over 16Gb... :/ */
		if (g_strcmp0 (g_variant_get_type_string (tmp), "u") == 0) {
			g_autofree gchar *val = NULL;
			val = g_strdup_printf ("%" G_GUINT32_FORMAT,
					       g_variant_get_uint32 (tmp));
			as_app_add_metadata (item, "X-Steam-Size", val);
		} else {
			as_app_add_metadata (item, "X-Steam-Size",
					     g_variant_get_string (tmp, NULL));
		}
	}

	/* download page from the store */
	cache_basename = g_strdup_printf ("%s.html", gameid_str);
	cache_fn = gs_utils_get_cache_filename ("steam",
						cache_basename,
						GS_UTILS_CACHE_FLAG_WRITEABLE,
						error);
	if (cache_fn == NULL)
		return FALSE;
	if (!g_file_test (cache_fn, G_FILE_TEST_EXISTS)) {
		g_autoptr(GsApp) app_dl = gs_app_new (gs_plugin_get_name (plugin));
		uri = g_strdup_printf ("http://store.steampowered.com/app/%s/", gameid_str);
		if (!gs_plugin_download_file (plugin,
					      app_dl,
					      uri,
					      cache_fn,
					      NULL, /* GCancellable */
					      error))
			return FALSE;
	}

	/* get screenshots and descriptions */
	if (!g_file_get_contents (cache_fn, &html, NULL, error)) {
		gs_utils_error_convert_gio (error);
		return FALSE;
	}
	if (!gs_plugin_steam_update_screenshots (item, html, error))
		return FALSE;
	if (!gs_plugin_steam_update_description (item, html, error))
		return FALSE;

	/* add */
	as_store_add_app (store, item);
	return TRUE;
}