예제 #1
0
/**
 * asb_task_explode_extra_package:
 **/
static gboolean
asb_task_explode_extra_package (AsbTask *task,
				const gchar *pkg_name,
				gboolean require_same_srpm,
				GError **error)
{
	AsbTaskPrivate *priv = GET_PRIVATE (task);
	AsbPackage *pkg_extra;
	GPtrArray *deps;
	guint i;
	const gchar *dep;

	/* if not found, that's fine */
	pkg_extra = asb_context_find_by_pkgname (priv->ctx, pkg_name);
	if (pkg_extra == NULL)
		return TRUE;

	/* check it's from the same source package */
	if (!asb_package_ensure (pkg_extra,
				 ASB_PACKAGE_ENSURE_SOURCE,
				 error))
		return FALSE;
	if (require_same_srpm &&
	    (g_strcmp0 (asb_package_get_source (pkg_extra),
		        asb_package_get_source (priv->pkg)) != 0))
		return TRUE;
	g_debug ("decompressing extra pkg %s", asb_package_get_name (pkg_extra));
	asb_package_log (priv->pkg,
			 ASB_PACKAGE_LOG_LEVEL_DEBUG,
			 "Adding extra package %s for %s",
			 asb_package_get_name (pkg_extra),
			 asb_package_get_name (priv->pkg));
	if (!asb_package_ensure (pkg_extra,
				 ASB_PACKAGE_ENSURE_FILES |
				 ASB_PACKAGE_ENSURE_DEPS,
				 error))
		return FALSE;
	if (!asb_package_explode (pkg_extra, priv->tmpdir,
				  asb_context_get_file_globs (priv->ctx),
				  error))
		return FALSE;

	/* copy all the extra package requires into the main package too */
	deps = asb_package_get_deps (pkg_extra);
	for (i = 0; i < deps->len; i++) {
		dep = g_ptr_array_index (deps, i);
		asb_package_add_dep (priv->pkg, dep);
	}

	/* free resources */
	if (!asb_package_close (pkg_extra, error))
		return FALSE;
	asb_package_clear (pkg_extra,
			   ASB_PACKAGE_ENSURE_DEPS |
			   ASB_PACKAGE_ENSURE_FILES);

	return TRUE;
}
예제 #2
0
/**
 * asb_context_write_xml:
 **/
static gboolean
asb_context_write_xml (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;
	g_autofree gchar *filename = NULL;
	g_autoptr(AsStore) store = NULL;
	g_autoptr(GFile) file = NULL;

	/* convert any vetod applications into dummy components */
	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;
		asb_context_add_app_ignore (ctx, asb_app_get_package (ASB_APP (app)));
	}

	/* add any non-vetoed applications */
	store = as_store_new ();
	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (as_app_get_vetos(app)->len > 0)
			continue;
		as_store_add_app (store, app);
		as_store_remove_app (priv->store_failed, app);

		/* remove from the ignore list if the application was useful */
		if (ASB_IS_APP (app)) {
			AsbPackage *pkg = asb_app_get_package (ASB_APP (app));
			g_autofree gchar *name_arch = NULL;
			name_arch = g_strdup_printf ("%s.%s",
						     asb_package_get_name (pkg),
						     asb_package_get_arch (pkg));
			as_store_remove_app_by_id (priv->store_ignore, name_arch);
		}

	}
	filename = g_strdup_printf ("%s/%s.xml.gz",
				    priv->output_dir,
				    priv->basename);
	file = g_file_new_for_path (filename);

	g_print ("Writing %s...\n", filename);
	as_store_set_origin (store, priv->origin);
	as_store_set_api_version (store, 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 (store, builder_id);
	}
	return as_store_to_file (store,
				 file,
				 AS_NODE_TO_XML_FLAG_ADD_HEADER |
				 AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
				 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
				 NULL, error);
}
예제 #3
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);
}
예제 #4
0
/**
 * asb_context_find_by_pkgname:
 * @ctx: A #AsbContext
 * @pkgname: a package name
 *
 * Find a package from its name.
 *
 * Returns: (transfer none): a #AsbPackage, or %NULL for not found.
 *
 * Since: 0.1.0
 **/
AsbPackage *
asb_context_find_by_pkgname (AsbContext *ctx, const gchar *pkgname)
{
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	AsbPackage *pkg;
	guint i;

	for (i = 0; i < priv->packages->len; i++) {
		pkg = g_ptr_array_index (priv->packages, i);
		if (g_strcmp0 (asb_package_get_name (pkg), pkgname) == 0)
			return pkg;
	}
	return NULL;
}
예제 #5
0
/**
 * asb_context_add_filename:
 * @ctx: A #AsbContext
 * @filename: package filename
 * @error: A #GError or %NULL
 *
 * Adds a filename to the list of packages to be processed
 *
 * Returns: %TRUE for success, %FALSE otherwise
 *
 * Since: 0.1.0
 **/
gboolean
asb_context_add_filename (AsbContext *ctx, const gchar *filename, GError **error)
{
	g_autoptr(AsbPackage) pkg = NULL;

	/* can find in existing metadata */
	if (asb_context_find_in_cache (ctx, filename)) {
		g_debug ("Found %s in old metadata", filename);
		return TRUE;
	}

	/* open */
#if HAVE_RPM
	if (g_str_has_suffix (filename, ".rpm"))
		pkg = asb_package_rpm_new ();
#endif
#if HAVE_ALPM
	if (g_str_has_suffix (filename, ".pkg.tar.xz"))
		pkg = asb_package_alpm_new ();
#endif
	if (g_str_has_suffix (filename, ".cab"))
		pkg = asb_package_cab_new ();
	if (g_str_has_suffix (filename, ".deb"))
		pkg = asb_package_deb_new ();
	if (pkg == NULL) {
		g_set_error (error,
			     ASB_PLUGIN_ERROR,
			     ASB_PLUGIN_ERROR_FAILED,
			     "No idea how to handle %s",
			     filename);
		return FALSE;
	}

	/* add to array */
	asb_package_set_filename (pkg, filename);

	/* failed to guess the nevra */
	if (asb_package_get_name (pkg) == NULL) {
		if (!asb_package_open (pkg, filename, error))
			return FALSE;
	}

	asb_context_add_package (ctx, pkg);
	return TRUE;
}
예제 #6
0
/**
 * asb_plugin_merge_prepare_deps:
 */
static void
asb_plugin_merge_prepare_deps (GList *list)
{
    AsApp *app;
    AsbPackage *pkg;
    GList *l;

    for (l = list; l != NULL; l = l->next) {
        app = AS_APP (l->data);
        if (as_app_get_id_kind (app) != AS_ID_KIND_DESKTOP)
            continue;
        if (!ASB_IS_APP (app))
            continue;
        if (as_app_get_vetos(app)->len > 0)
            continue;
        pkg = asb_app_get_package (ASB_APP (app));
        asb_plugin_absorb_parent_for_pkgname (list, app, asb_package_get_name (pkg));
    }
}
예제 #7
0
/**
 * asb_context_disable_older_pkgs:
 **/
static void
asb_context_disable_older_pkgs (AsbContext *ctx)
{
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	AsbPackage *found;
	AsbPackage *pkg;
	const gchar *key;
	guint i;
	g_autoptr(GHashTable) newest = NULL;

	newest = g_hash_table_new_full (g_str_hash, g_str_equal,
					g_free, (GDestroyNotify) g_object_unref);
	for (i = 0; i < priv->packages->len; i++) {
		pkg = ASB_PACKAGE (g_ptr_array_index (priv->packages, i));
		if (!asb_package_get_enabled (pkg))
			continue;
		key = asb_package_get_name (pkg);
		if (key == NULL)
			continue;
		switch (asb_package_get_kind (pkg)) {
		case ASB_PACKAGE_KIND_DEFAULT:
		case ASB_PACKAGE_KIND_BUNDLE:
			found = g_hash_table_lookup (newest, key);
			if (found != NULL) {
				if (asb_package_compare (pkg, found) <= 0) {
					asb_package_set_enabled (pkg, FALSE);
					continue;
				}
				asb_package_set_enabled (found, FALSE);
			}
			break;
		default:
			/* allow multiple versions */
			break;
		}
		g_hash_table_insert (newest, g_strdup (key), g_object_ref (pkg));
	}
}
예제 #8
0
/**
 * asb_task_process:
 * @task: A #AsbTask
 * @error_not_used: A #GError or %NULL
 *
 * Processes the task.
 *
 * Returns: %TRUE for success, %FALSE otherwise
 *
 * Since: 0.1.0
 **/
gboolean
asb_task_process (AsbTask *task, GError **error_not_used)
{
	AsRelease *release;
	AsbApp *app;
	AsbPlugin *plugin = NULL;
	AsbTaskPrivate *priv = GET_PRIVATE (task);
	GList *apps = NULL;
	GList *l;
	GPtrArray *array;
	gboolean ret;
	gchar *cache_id;
	guint i;
	guint nr_added = 0;
	g_autoptr(GError) error = NULL;
	g_autofree gchar *basename = NULL;

	/* reset the profile timer */
	asb_package_log_start (priv->pkg);

	/* ensure nevra read */
	if (!asb_package_ensure (priv->pkg,
				 ASB_PACKAGE_ENSURE_NEVRA,
				 error_not_used))
		return FALSE;

	g_debug ("starting: %s", asb_package_get_name (priv->pkg));

	/* treat archive as a special case */
	if (g_str_has_suffix (priv->filename, ".cab")) {
		AsApp *app_tmp;
		GPtrArray *apps_tmp;
		g_autoptr(AsStore) store = as_store_new ();
		g_autoptr(GFile) file = g_file_new_for_path (priv->filename);
		if (!as_store_from_file (store, file, NULL, NULL, &error)) {
			asb_package_log (priv->pkg,
					 ASB_PACKAGE_LOG_LEVEL_WARNING,
					 "Failed to parse %s: %s",
					 asb_package_get_filename (priv->pkg),
					 error->message);
			return TRUE;
		}
		apps_tmp = as_store_get_apps (store);
		for (i = 0; i < apps_tmp->len; i++) {
			g_autoptr(AsbApp) app2 = NULL;
			app_tmp = AS_APP (g_ptr_array_index (apps_tmp, i));
			app2 = asb_app_new (priv->pkg, as_app_get_id (app_tmp));
			as_app_subsume (AS_APP (app2), app_tmp);
			asb_context_add_app (priv->ctx, app2);

			/* set cache-id in case we want to use the metadata directly */
			if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID)) {
				cache_id = asb_utils_get_cache_id_for_filename (priv->filename);
				as_app_add_metadata (AS_APP (app2),
						     "X-CacheID",
						     cache_id);
				g_free (cache_id);
			}
			nr_added++;
		}
		g_debug ("added %i apps from archive", apps_tmp->len);
		goto skip;
	}

	/* ensure file list read */
	if (!asb_package_ensure (priv->pkg,
				 ASB_PACKAGE_ENSURE_FILES,
				 error_not_used))
		return FALSE;

	/* did we get a file match on any plugin */
	basename = g_path_get_basename (priv->filename);
	asb_package_log (priv->pkg,
			 ASB_PACKAGE_LOG_LEVEL_DEBUG,
			 "Getting filename match for %s",
			 basename);
	asb_task_add_suitable_plugins (task);
	if (priv->plugins_to_run->len == 0) {
		asb_context_add_app_ignore (priv->ctx, priv->pkg);
		goto out;
	}

	/* delete old tree if it exists */
	ret = asb_utils_ensure_exists_and_empty (priv->tmpdir, &error);
	if (!ret) {
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "Failed to clear: %s", error->message);
		goto out;
	}

	/* explode tree */
	g_debug ("decompressing files: %s", asb_package_get_name (priv->pkg));
	asb_package_log (priv->pkg,
			 ASB_PACKAGE_LOG_LEVEL_DEBUG,
			 "Exploding tree for %s",
			 asb_package_get_name (priv->pkg));
	ret = asb_package_explode (priv->pkg,
				   priv->tmpdir,
				   asb_context_get_file_globs (priv->ctx),
				   &error);
	if (!ret) {
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "Failed to explode: %s", error->message);
		g_clear_error (&error);
		goto skip;
	}

	/* add extra packages */
	if (!asb_package_ensure (priv->pkg,
				 ASB_PACKAGE_ENSURE_DEPS |
				 ASB_PACKAGE_ENSURE_SOURCE,
				 error_not_used))
		return FALSE;
	ret = asb_task_explode_extra_packages (task, &error);
	if (!ret) {
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "Failed to explode extra file: %s",
				 error->message);
		goto skip;
	}

	/* run plugins */
	g_debug ("examining: %s", asb_package_get_name (priv->pkg));
	for (i = 0; i < priv->plugins_to_run->len; i++) {
		GList *apps_tmp = NULL;
		plugin = g_ptr_array_index (priv->plugins_to_run, i);
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_DEBUG,
				 "Processing %s with %s",
				 basename,
				 plugin->name);
		apps_tmp = asb_plugin_process (plugin, priv->pkg, priv->tmpdir, &error);
		if (apps_tmp == NULL) {
			asb_package_log (priv->pkg,
					 ASB_PACKAGE_LOG_LEVEL_WARNING,
					 "Failed to run process '%s': %s",
					 plugin->name, error->message);
			g_clear_error (&error);
		}
		for (l = apps_tmp; l != NULL; l = l->next) {
			app = ASB_APP (l->data);
			asb_plugin_add_app (&apps, AS_APP (app));
		}
		g_list_free_full (apps_tmp, g_object_unref);
	}
	if (apps == NULL)
		goto skip;

	/* print */
	g_debug ("processing: %s", asb_package_get_name (priv->pkg));
	for (l = apps; l != NULL; l = l->next) {
		app = l->data;

		/* never set */
		if (as_app_get_id (AS_APP (app)) == NULL) {
			asb_package_log (priv->pkg,
					 ASB_PACKAGE_LOG_LEVEL_INFO,
					 "app id not set for %s",
					 asb_package_get_name (priv->pkg));
			continue;
		}

		/* copy data from pkg into app */
		if (!asb_package_ensure (priv->pkg,
					 ASB_PACKAGE_ENSURE_LICENSE |
					 ASB_PACKAGE_ENSURE_RELEASES |
					 ASB_PACKAGE_ENSURE_VCS |
					 ASB_PACKAGE_ENSURE_URL,
					 error_not_used))
			return FALSE;
		if (asb_package_get_url (priv->pkg) != NULL &&
		    as_app_get_url_item (AS_APP (app), AS_URL_KIND_HOMEPAGE) == NULL) {
			as_app_add_url (AS_APP (app),
					AS_URL_KIND_HOMEPAGE,
					asb_package_get_url (priv->pkg));
		}
		if (asb_package_get_license (priv->pkg) != NULL &&
		    as_app_get_project_license (AS_APP (app)) == NULL) {
			as_app_set_project_license (AS_APP (app),
						    asb_package_get_license (priv->pkg));
		}

		/* add the source name so we can suggest these together */
		if (g_strcmp0 (asb_package_get_source_pkgname (priv->pkg),
			       asb_package_get_name (priv->pkg)) != 0) {
			as_app_set_source_pkgname (AS_APP (app),
						   asb_package_get_source_pkgname (priv->pkg));
		}

		/* set all the releases on the app */
		array = asb_package_get_releases (priv->pkg);
		for (i = 0; i < array->len; i++) {
			release = g_ptr_array_index (array, i);
			as_app_add_release (AS_APP (app), release);
		}

		/* run each refine plugin on each app */
		ret = asb_plugin_loader_process_app (asb_context_get_plugin_loader (priv->ctx),
						     priv->pkg,
						     app,
						     priv->tmpdir,
						     &error);
		if (!ret) {
			asb_package_log (priv->pkg,
					 ASB_PACKAGE_LOG_LEVEL_WARNING,
					 "Failed to run process on %s: %s",
					 as_app_get_id (AS_APP (app)),
					 error->message);
			g_clear_error (&error);
			goto skip;
		}

		/* set cache-id in case we want to use the metadata directly */
		if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID)) {
			cache_id = asb_utils_get_cache_id_for_filename (priv->filename);
			as_app_add_metadata (AS_APP (app),
					     "X-CacheID",
					     cache_id);
			g_free (cache_id);
		}

		/* set the VCS information into the metadata */
		if (asb_package_get_vcs (priv->pkg) != NULL) {
			as_app_add_metadata (AS_APP (app),
					     "VersionControlSystem",
					     asb_package_get_vcs (priv->pkg));
		}

		/* save any screenshots early */
		if (array->len == 0) {
			if (!asb_app_save_resources (ASB_APP (app),
						     ASB_APP_SAVE_FLAG_SCREENSHOTS,
						     error_not_used))
				return FALSE;
		}

		/* all okay */
		asb_context_add_app (priv->ctx, app);
		nr_added++;
	}
skip:
	/* add a dummy element to the AppStream metadata so that we don't keep
	 * parsing this every time */
	if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID) && nr_added == 0)
		asb_context_add_app_ignore (priv->ctx, priv->pkg);

	/* delete tree */
	g_debug ("deleting temp files: %s", asb_package_get_name (priv->pkg));
	if (!asb_utils_rmtree (priv->tmpdir, &error)) {
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "Failed to delete tree: %s",
				 error->message);
		goto out;
	}

	/* write log */
	g_debug ("writing log: %s", asb_package_get_name (priv->pkg));
	if (!asb_package_log_flush (priv->pkg, &error)) {
		asb_package_log (priv->pkg,
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "Failed to write package log: %s",
				 error->message);
		goto out;
	}
out:
	/* clear loaded resources */
	asb_package_close (priv->pkg, NULL);
	asb_package_clear (priv->pkg,
			   ASB_PACKAGE_ENSURE_DEPS |
			   ASB_PACKAGE_ENSURE_FILES);
	g_list_free_full (apps, (GDestroyNotify) g_object_unref);
	return TRUE;
}
예제 #9
0
/**
 * asb_task_explode_extra_packages:
 **/
static gboolean
asb_task_explode_extra_packages (AsbTask *task, GError **error)
{
	AsbTaskPrivate *priv = GET_PRIVATE (task);
	GPtrArray *deps;
	const gchar *ignore[] = { "rtld", NULL };
	const gchar *tmp;
	guint i;
	g_autoptr(GHashTable) hash = NULL;
	g_autoptr(GPtrArray) array = NULL;
	g_autoptr(GPtrArray) icon_themes = NULL;

	/* anything the package requires */
	hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	for (i = 0; ignore[i] != NULL; i++) {
		g_hash_table_insert (hash,
				     g_strdup (ignore[i]),
				     GINT_TO_POINTER (1));
	}
	array = g_ptr_array_new_with_free_func (g_free);
	icon_themes = g_ptr_array_new_with_free_func (g_free);
	deps = asb_package_get_deps (priv->pkg);
	for (i = 0; i < deps->len; i++) {
		tmp = g_ptr_array_index (deps, i);
		if (g_strstr_len (tmp, -1, " ") != NULL)
			continue;
		if (g_strstr_len (tmp, -1, ".so") != NULL)
			continue;
		if (g_str_has_prefix (tmp, "/"))
			continue;
		if (g_hash_table_lookup (hash, tmp) != NULL)
			continue;
		if (g_strcmp0 (tmp, asb_package_get_name (priv->pkg)) == 0)
			continue;

		/* libreoffice making things complicated */
		if (g_strcmp0 (tmp, "libreoffice-core") == 0)
			g_ptr_array_add (array, g_strdup ("libreoffice-data"));

		/* if an app depends on kde-runtime, that means the
		 * oxygen icon set is available to them */
		if (g_strcmp0 (tmp, "oxygen-icon-theme") == 0 ||
		    g_strcmp0 (tmp, "kde-runtime") == 0) {
			g_hash_table_insert (hash, g_strdup ("oxygen-icon-theme"),
					     GINT_TO_POINTER (1));
			g_ptr_array_add (icon_themes,
					 g_strdup ("oxygen-icon-theme"));
		} else {
			g_ptr_array_add (array, g_strdup (tmp));
		}
		g_hash_table_insert (hash, g_strdup (tmp), GINT_TO_POINTER (1));
	}

	/* explode any potential packages */
	for (i = 0; i < array->len; i++) {
		tmp = g_ptr_array_index (array, i);
		if (!asb_task_explode_extra_package (task, tmp, TRUE, error))
			return FALSE;
	}

	/* explode any icon themes */
	for (i = 0; i < icon_themes->len; i++) {
		tmp = g_ptr_array_index (icon_themes, i);
		if (!asb_task_explode_extra_package (task, tmp, FALSE, error))
			return FALSE;
	}
	return TRUE;
}
/**
 * asb_plugin_process:
 */
GList *
asb_plugin_process (AsbPlugin *plugin,
		    AsbPackage *pkg,
		    const gchar *tmpdir,
		    GError **error)
{
	const gchar *tmp;
	gchar **split;
	GList *apps = NULL;
	GPtrArray *keywords;
	guint i;
	guint j;
	_cleanup_free_ gchar *app_id = NULL;
	_cleanup_object_unref_ AsbApp *app = NULL;
	_cleanup_object_unref_ AsIcon *icon = NULL;
	_cleanup_string_free_ GString *str = NULL;

	/* use the pkgname suffix as the app-id */
	tmp = asb_package_get_name (pkg);
	if (g_str_has_prefix (tmp, "gstreamer1-"))
		tmp += 11;
	if (g_str_has_prefix (tmp, "gstreamer-"))
		tmp += 10;
	if (g_str_has_prefix (tmp, "plugins-"))
		tmp += 8;
	app_id = g_strdup_printf ("gstreamer-%s", tmp);

	/* create app */
	app = asb_app_new (pkg, app_id);
	as_app_set_id_kind (AS_APP (app), AS_ID_KIND_CODEC);
	as_app_set_name (AS_APP (app), "C", "GStreamer Multimedia Codecs", -1);
	asb_app_set_requires_appdata (app, TRUE);
	asb_app_set_hidpi_enabled (app, asb_context_get_hidpi_enabled (plugin->ctx));
	as_app_add_category (AS_APP (app), "Addons", -1);
	as_app_add_category (AS_APP (app), "Codecs", -1);

	/* add icon */
	icon = as_icon_new ();
	as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
	as_icon_set_name (icon, "application-x-executable", -1);
	as_app_add_icon (AS_APP (app), icon);

	for (i = 0; data[i].path != NULL; i++) {
		if (!asb_utils_is_file_in_tmpdir (tmpdir, data[i].path))
			continue;
		split = g_strsplit (data[i].text, "|", -1);
		for (j = 0; split[j] != NULL; j++)
			as_app_add_keyword (AS_APP (app), NULL, split[j], -1);
		g_strfreev (split);
	}

	/* no codecs we care about */
	keywords = as_app_get_keywords (AS_APP (app), NULL);
	if (keywords == NULL) {
		g_set_error (error,
			     ASB_PLUGIN_ERROR,
			     ASB_PLUGIN_ERROR_FAILED,
			     "nothing interesting in %s",
			     asb_package_get_basename (pkg));
		return NULL;
	}

	/* sort categories by name */
	g_ptr_array_sort (keywords, asb_utils_string_sort_cb);

	/* create a description */
	str = g_string_new ("Multimedia playback for ");
	if (keywords->len > 1) {
		for (i = 0; i < keywords->len - 1; i++) {
			tmp = g_ptr_array_index (keywords, i);
			g_string_append_printf (str, "%s, ", tmp);
		}
		g_string_truncate (str, str->len - 2);
		tmp = g_ptr_array_index (keywords, keywords->len - 1);
		g_string_append_printf (str, " and %s", tmp);
	} else {
		g_string_append (str, g_ptr_array_index (keywords, 0));
	}
	as_app_set_comment (AS_APP (app), "C", str->str, -1);

	/* add */
	asb_plugin_add_app (&apps, AS_APP (app));
	return apps;
}