Example #1
0
/**
 * asb_plugin_process_app:
 */
gboolean
asb_plugin_process_app (AsbPlugin *plugin,
			AsbPackage *pkg,
			AsbApp *app,
			const gchar *tmpdir,
			GError **error)
{
	gchar **filelist;
	guint i;

	filelist = asb_package_get_filelist (pkg);
	for (i = 0; filelist[i] != NULL; i++) {
		GError *error_local = NULL;
		_cleanup_free_ gchar *filename = NULL;

		if (!g_str_has_prefix (filelist[i], "/usr/bin/"))
			continue;
		if (as_app_get_metadata_item (AS_APP (app), "X-Kudo-UsesAppMenu") != NULL)
			break;
		filename = g_build_filename (tmpdir, filelist[i], NULL);
		if (!asb_plugin_nm_app (app, filename, &error_local)) {
			asb_package_log (pkg,
					 ASB_PACKAGE_LOG_LEVEL_WARNING,
					 "Failed to run nm on %s: %s",
					 filename,
					 error_local->message);
			g_clear_error (&error_local);
		}
	}
	return TRUE;
}
/**
 * asb_plugin_process_app:
 */
gboolean
asb_plugin_process_app (AsbPlugin *plugin,
			AsbPackage *pkg,
			AsbApp *app,
			const gchar *tmpdir,
			GError **error)
{
	gchar **filelist;
	guint i;

	filelist = asb_package_get_filelist (pkg);
	for (i = 0; filelist[i] != NULL; i++) {
		GError *error_local = NULL;
		g_autofree gchar *filename = NULL;

		if (!asb_plugin_match_glob ("/usr/bin/*", filelist[i]))
			continue;
		if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_APP_MENU))
			break;
		filename = g_build_filename (tmpdir, filelist[i], NULL);
		if (!asb_plugin_gresource_app (app, filename, &error_local)) {
			asb_package_log (pkg,
					 ASB_PACKAGE_LOG_LEVEL_WARNING,
					 "Failed to get resources from %s: %s",
					 filename,
					 error_local->message);
			g_clear_error (&error_local);
		}
	}
	return TRUE;
}
/**
 * asb_plugin_merge:
 */
void
asb_plugin_merge (AsbPlugin *plugin, GList *list)
{
	AsApp *app;
	AsApp *found;
	GList *l;
	const gchar *tmp;
	_cleanup_hashtable_unref_ GHashTable *hash = NULL;

	/* add all packages to the hash */
	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
				      g_free, (GDestroyNotify) g_object_unref);
	for (l = list; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (as_app_get_vetos (app)->len > 0)
			continue;
		tmp = as_app_get_pkgname_default (app);
		if (tmp == NULL)
			continue;
		found = g_hash_table_lookup (hash, tmp);
		if (found != NULL) {
			/* ignore duplicates */
			if (g_strcmp0 (as_app_get_id (app),
				       as_app_get_id (found)) == 0) {
				continue;
			}
			asb_plugin_composite_app (app, found);
			continue;
		}
		g_hash_table_insert (hash,
				     g_strdup (as_app_get_pkgname_default (app)),
				     g_object_ref (app));
	}
}
gboolean
asb_plugin_process_app (AsbPlugin *plugin,
			AsbPackage *pkg,
			AsbApp *app,
			const gchar *tmpdir,
			GError **error)
{
	guint i;
	const gchar *app_dirs[] = {
		"/usr/share/applications",
		"/usr/share/applications/kde4",
		NULL };

	/* use the .desktop file to refine the application */
	for (i = 0; app_dirs[i] != NULL; i++) {
		g_autofree gchar *fn = NULL;
		fn = g_build_filename (tmpdir,
				       app_dirs[i],
				       as_app_get_id (AS_APP (app)),
				       NULL);
		if (g_file_test (fn, G_FILE_TEST_EXISTS)) {
			if (!asb_plugin_desktop_refine (plugin, pkg, fn,
							app, tmpdir, error))
				return FALSE;
		}
	}

	return TRUE;
}
/**
 * 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;
}
Example #6
0
/**
 * asb_context_add_app:
 * @ctx: A #AsbContext
 * @app: A #AsbApp
 *
 * Adds an application to the context.
 *
 * Since: 0.1.0
 **/
void
asb_context_add_app (AsbContext *ctx, AsbApp *app)
{
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	g_mutex_lock (&priv->apps_mutex);
	asb_plugin_add_app (&priv->apps, AS_APP (app));
	g_mutex_unlock (&priv->apps_mutex);
}
Example #7
0
/**
 * asb_plugin_merge:
 */
void
asb_plugin_merge (AsbPlugin *plugin, GList *list)
{
    AsApp *app;
    AsApp *found;
    GList *l;
    const gchar *tmp;
    _cleanup_hashtable_unref_ GHashTable *hash = NULL;

    /* add X-Merge-With-Parent on any metainfo files that are in a package
     * required by a desktop package */
    asb_plugin_merge_prepare_deps (list);

    /* add all packages to the hash */
    hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                  g_free, (GDestroyNotify) g_object_unref);
    for (l = list; l != NULL; l = l->next) {
        app = AS_APP (l->data);
        g_hash_table_insert (hash,
                             g_strdup (as_app_get_id (app)),
                             g_object_ref (app));
    }

    /* absorb some apps into their parent */
    for (l = list; l != NULL; l = l->next) {
        app = AS_APP (l->data);

        /* no absorb metadata */
        tmp = as_app_get_metadata_item (app, "X-Merge-With-Parent");
        if (tmp == NULL)
            continue;

        /* find the parent app */
        found = g_hash_table_lookup (hash, tmp);
        if (found == NULL) {
            as_app_add_veto (app, "No referenced '%s'", tmp);
            continue;
        }

        /* partially absorb */
        as_app_add_veto (app, "partially absorbing %s into %s",
                         as_app_get_id (app), as_app_get_id (found));
        as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL);
    }
}
static gboolean
asb_plugin_process_filename (AsbPlugin *plugin,
			     AsbPackage *pkg,
			     const gchar *filename,
			     GList **apps,
			     GError **error)
{
	gsize len;
	g_autoptr(AsbApp) app = NULL;
	g_autofree gchar *data = NULL;

	app = asb_app_new (pkg, NULL);
	if (!g_file_get_contents (filename, &data, &len, error))
		return FALSE;
	if (!as_app_parse_shell_extension_data (plugin, AS_APP (app), data, len, error))
		return FALSE;
	asb_plugin_add_app (apps, AS_APP (app));
	return TRUE;
}
/**
 * asb_plugin_process_filename:
 */
static gboolean
asb_plugin_process_filename (const gchar *filename, AsbApp *app, GError **error)
{
	g_autofree gchar *app_runtime = NULL;
	g_autofree gchar *app_sdk = NULL;
	g_autoptr(GKeyFile) kf = NULL;
	kf = g_key_file_new ();
	if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error))
		return FALSE;
	app_runtime = g_key_file_get_string (kf, "Application", "runtime", NULL);
	if (app_runtime != NULL)
		as_app_add_metadata (AS_APP (app), "BundleRuntime", app_runtime);
	app_sdk = g_key_file_get_string (kf, "Application", "sdk", NULL);
	if (app_sdk != NULL)
		as_app_add_metadata (AS_APP (app), "BundleSDK", app_sdk);
	if (g_key_file_get_string (kf, "Environment", "network", NULL))
		as_app_add_permission (AS_APP (app), "network");
	return TRUE;
}
/**
 * asb_plugin_merge:
 */
void
asb_plugin_merge (AsbPlugin *plugin, GList *list)
{
	AsApp *app;
	AsApp *found;
	GList *l;
	_cleanup_hashtable_unref_ GHashTable *hash = NULL;

	/* make a hash table of ID->AsApp */
	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
				      g_free, (GDestroyNotify) g_object_unref);
	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;
		g_hash_table_insert (hash,
				     g_strdup (as_app_get_id (app)),
				     g_object_ref (app));
	}

	/* add addons where the pkgname is different from the
	 * main package */
	for (l = list; l != NULL; l = l->next) {
		if (!ASB_IS_APP (l->data))
			continue;
		app = AS_APP (l->data);
		if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON)
			continue;
		found = g_hash_table_lookup (hash, as_app_get_id (app));
		if (found == NULL)
			continue;
		if (g_strcmp0 (as_app_get_pkgname_default (app),
			       as_app_get_pkgname_default (found)) != 0)
			continue;
		as_app_add_veto (app,
				 "absorbing addon %s shipped in "
				 "main package %s",
				 as_app_get_id (app),
				 as_app_get_pkgname_default (app));
		as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL);
	}
}
/**
 * asb_plugin_process_filename:
 */
static gboolean
asb_plugin_process_filename (const gchar *filename,
			     AsbApp *app,
			     const gchar *tmpdir,
			     GError **error)
{
	g_autofree gchar *types = NULL;
	g_autoptr(GKeyFile) kf = NULL;
	kf = g_key_file_new ();
	if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error))
		return FALSE;
	types = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP,
				       "X-KDE-ServiceTypes", NULL);
	if (types == NULL)
		return TRUE;
	if (g_strcmp0 (types, "Plasma/Runner") != 0)
		return TRUE;
	asb_package_log (asb_app_get_package (app),
			 ASB_PACKAGE_LOG_LEVEL_DEBUG,
			 "Auto-adding kudo SearchProvider for %s",
			 as_app_get_id (AS_APP (app)));
	as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_SEARCH_PROVIDER);
	return TRUE;
}
Example #12
0
/**
 * as_app_infer_file_key:
 **/
static gboolean
as_app_infer_file_key (AsApp *app,
		       GKeyFile *kf,
		       const gchar *key,
		       GError **error)
{
	g_autofree gchar *tmp = NULL;

	if (g_strcmp0 (key, "X-GNOME-UsesNotifications") == 0) {
		as_app_add_kudo_kind (AS_APP (app),
				      AS_KUDO_KIND_NOTIFICATIONS);

	} else if (g_strcmp0 (key, "X-GNOME-Bugzilla-Bugzilla") == 0) {
		tmp = g_key_file_get_string (kf,
					     G_KEY_FILE_DESKTOP_GROUP,
					     key,
					     NULL);
		if (g_strcmp0 (tmp, "GNOME") == 0)
			as_app_set_project_group (app, "GNOME");

	} else if (g_strcmp0 (key, "X-MATE-Bugzilla-Product") == 0) {
		as_app_set_project_group (app, "MATE");

	} else if (g_strcmp0 (key, "X-KDE-StartupNotify") == 0) {
		as_app_set_project_group (app, "KDE");

	} else if (g_strcmp0 (key, "X-DocPath") == 0) {
		tmp = g_key_file_get_string (kf,
					     G_KEY_FILE_DESKTOP_GROUP,
					     key,
					     NULL);
		if (g_str_has_prefix (tmp, "http://userbase.kde.org/"))
			as_app_set_project_group (app, "KDE");

	/* Exec */
	} else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_EXEC) == 0) {
		tmp = g_key_file_get_string (kf,
					     G_KEY_FILE_DESKTOP_GROUP,
					     key,
					     NULL);
		if (g_str_has_prefix (tmp, "xfce4-"))
			as_app_set_project_group (app, "XFCE");
	}

	return TRUE;
}
Example #13
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);
}
Example #14
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));
    }
}
/**
 * asb_plugin_merge_prepare_deps:
 */
static void
asb_plugin_merge_prepare_deps (GList *list)
{
	AsApp *app;
	AsbPackage *pkg;
	GList *l;
	gchar **deps;
	guint i;

	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;
		pkg = asb_app_get_package (ASB_APP (app));
		deps = asb_package_get_deps (pkg);
		for (i = 0; deps[i] != NULL; i++)
			asb_plugin_absorb_parent_for_pkgname (list, app, deps[i]);
	}
}
Example #16
0
/**
 * asb_context_detect_missing_data:
 **/
static gboolean
asb_context_detect_missing_data (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;

	/* look for the thing that an addon extends */
	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (as_app_get_name (AS_APP (app), "C") == NULL)
			as_app_add_veto (AS_APP (app), "No <name> in AppData");
		if (as_app_get_comment (AS_APP (app), "C") == NULL)
			as_app_add_veto (AS_APP (app), "No <summary> in AppData");
		if (as_app_get_id_kind (AS_APP (app)) != AS_ID_KIND_ADDON) {
			if (as_app_get_icon_default (AS_APP (app)) == NULL)
				as_app_add_veto (AS_APP (app), "Has no Icon");
		}
	}
	return TRUE;
}
Example #17
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);
}
/**
 * asb_plugin_absorb_parent_for_pkgname:
 */
static void
asb_plugin_absorb_parent_for_pkgname (GList *list, AsApp *parent, const gchar *pkgname)
{
	AsApp *app;
	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_ADDON)
			continue;
		if (g_strcmp0 (as_app_get_pkgname_default (app), pkgname) != 0)
			continue;
		g_debug ("Adding X-Merge-With-Parent on %s as %s depends on %s",
			 as_app_get_id_full (app),
			 as_app_get_pkgname_default (parent),
			 as_app_get_pkgname_default (app));
		as_app_add_metadata (app,
				     "X-Merge-With-Parent",
				     as_app_get_id_full (parent), -1);
	}
}
Example #19
0
/**
 * asb_context_convert_icons:
 **/
static gboolean
asb_context_convert_icons (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;

	/* not enabled */
	if ((priv->flags & ASB_CONTEXT_FLAG_EMBEDDED_ICONS) == 0)
		return TRUE;

	/* convert each one before saving resources */
	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);
		if (as_app_get_vetos(app)->len > 0)
			continue;
		if (!as_app_convert_icons (app, AS_ICON_KIND_EMBEDDED, error))
			return FALSE;
	}
	return TRUE;
}
Example #20
0
/**
 * as_app_builder_search_provides:
 * @app: an #AsApp
 * @prefix: a prefix to search, e.g. "/usr"
 * @flags: #AsAppBuilderFlags, e.g. %AS_APP_BUILDER_FLAG_USE_FALLBACKS
 * @error: a #GError or %NULL
 *
 * Searches a prefix for auto-detected provides.
 *
 * Returns: %TRUE for success
 *
 * Since: 0.5.8
 **/
gboolean
as_app_builder_search_provides (AsApp *app,
				const gchar *prefix,
				AsAppBuilderFlags flags,
				GError **error)
{
	/* skip for addons */
	if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_ADDON)
		return TRUE;

	if (!as_app_builder_search_dbus (app, prefix,
					 "share/dbus-1/system-services",
					 AS_PROVIDE_KIND_DBUS_SYSTEM,
					 flags, error))
		return FALSE;
	if (!as_app_builder_search_dbus (app, prefix,
					 "share/dbus-1/services",
					 AS_PROVIDE_KIND_DBUS_SESSION,
					 flags, error))
		return FALSE;
	return TRUE;
}
Example #21
0
/**
 * asb_context_save_resources:
 **/
static gboolean
asb_context_save_resources (AsbContext *ctx, GError **error)
{
	AsApp *app;
	AsbContextPrivate *priv = GET_PRIVATE (ctx);
	GList *l;

	for (l = priv->apps; l != NULL; l = l->next) {
		app = AS_APP (l->data);

		/* save icon */
		if (as_app_get_vetos(app)->len > 0)
			continue;
		if (!ASB_IS_APP (app))
			continue;
		if (!asb_app_save_resources (ASB_APP (app),
					     ASB_APP_SAVE_FLAG_ICONS,
					     error))
			return FALSE;
	}
	return TRUE;
}
/**
 * asb_plugin_process_app:
 */
gboolean
asb_plugin_process_app (AsbPlugin *plugin,
			AsbPackage *pkg,
			AsbApp *app,
			const gchar *tmpdir,
			GError **error)
{
	gchar **filelist;
	guint i;

	/* already set */
	if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_SEARCH_PROVIDER))
		return TRUE;

	/* look for a krunner provider */
	filelist = asb_package_get_filelist (pkg);
	for (i = 0; filelist[i] != NULL; i++) {
		g_autoptr(GError) error_local = NULL;
		g_autofree gchar *filename = NULL;
		if (!asb_plugin_match_glob ("/usr/share/kde4/services/*.desktop", filelist[i]))
			continue;
		filename = g_build_filename (tmpdir, filelist[i], NULL);
		if (!asb_plugin_process_filename (filename,
						  app,
						  tmpdir,
						  &error_local)) {
			asb_package_log (pkg,
					 ASB_PACKAGE_LOG_LEVEL_INFO,
					 "Failed to read KDE service file %s: %s",
					 filelist[i],
					 error_local->message);
			g_clear_error (&error_local);
		}
	}

	return TRUE;
}
/**
 * asb_plugin_hardcoded_add_screenshots:
 */
static gboolean
asb_plugin_hardcoded_add_screenshots (AsbApp *app,
				      const gchar *location,
				      GError **error)
{
	const gchar *tmp;
	gboolean ret = TRUE;
	GList *l;
	GList *list = NULL;
	_cleanup_dir_close_ GDir *dir = NULL;

	/* scan for files */
	dir = g_dir_open (location, 0, error);
	if (dir == NULL) {
		ret = FALSE;
		goto out;
	}
	while ((tmp = g_dir_read_name (dir)) != NULL) {
		if (!g_str_has_suffix (tmp, ".png"))
			continue;
		list = g_list_prepend (list, g_build_filename (location, tmp, NULL));
	}
	list = g_list_sort (list, asb_plugin_hardcoded_sort_screenshots_cb);
	for (l = list; l != NULL; l = l->next) {
		tmp = l->data;
		asb_package_log (asb_app_get_package (app),
				 ASB_PACKAGE_LOG_LEVEL_DEBUG,
				 "Adding extra screenshot: %s", tmp);
		ret = asb_app_add_screenshot_source (app, tmp, error);
		if (!ret)
			goto out;
	}
	as_app_add_metadata (AS_APP (app), "DistroScreenshots", NULL, -1);
out:
	g_list_free_full (list, g_free);
	return ret;
}
/**
 * asb_plugin_process_app:
 */
gboolean
asb_plugin_process_app (AsbPlugin *plugin,
			AsbPackage *pkg,
			AsbApp *app,
			const gchar *tmpdir,
			GError **error)
{
	gchar **filelist;
	guint i;

	/* already set */
	if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_MODERN_TOOLKIT))
		return TRUE;

	/* look for any GIR files */
	filelist = asb_package_get_filelist (pkg);
	for (i = 0; filelist[i] != NULL; i++) {
		if (!_asb_plugin_check_filename (filelist[i]))
			continue;
		if (!asb_plugin_process_gir (app, tmpdir, filelist[i], error))
			return FALSE;
	}
	return TRUE;
}
/**
 * cra_plugin_process_filename:
 */
static gboolean
cra_plugin_process_filename (CraPlugin *plugin,
			     CraPackage *pkg,
			     const gchar *filename,
			     GList **apps,
			     const gchar *tmpdir,
			     GError **error)
{
	const gchar *key;
	gboolean ret;
	_cleanup_free_ gchar *app_id = NULL;
	_cleanup_free_ gchar *full_filename = NULL;
	_cleanup_free_ gchar *icon_filename = NULL;
	_cleanup_object_unref_ CraApp *app = NULL;
	_cleanup_object_unref_ GdkPixbuf *pixbuf = NULL;

	/* create app */
	app_id = g_path_get_basename (filename);
	app = cra_app_new (pkg, app_id);
	full_filename = g_build_filename (tmpdir, filename, NULL);
	ret = as_app_parse_file (AS_APP (app),
				 full_filename,
				 AS_APP_PARSE_FLAG_USE_HEURISTICS,
				 error);
	if (!ret)
		return FALSE;

	/* NoDisplay requires AppData */
	if (as_app_get_metadata_item (AS_APP (app), "NoDisplay") != NULL)
		cra_app_add_requires_appdata (app, "NoDisplay=true");

	/* Settings or DesktopSettings requires AppData */
	if (as_app_has_category (AS_APP (app), "Settings"))
		cra_app_add_requires_appdata (app, "Category=Settings");
	if (as_app_has_category (AS_APP (app), "DesktopSettings"))
		cra_app_add_requires_appdata (app, "Category=DesktopSettings");

	/* is the icon a stock-icon-name? */
	key = as_app_get_icon (AS_APP (app));
	if (key != NULL) {
		if (as_app_get_icon_kind (AS_APP (app)) == AS_ICON_KIND_STOCK) {
			cra_package_log (pkg,
					 CRA_PACKAGE_LOG_LEVEL_DEBUG,
					 "using stock icon %s", key);
		} else {

			/* is icon XPM or GIF */
			if (g_str_has_suffix (key, ".xpm"))
				cra_app_add_veto (app, "Uses XPM icon: %s", key);
			else if (g_str_has_suffix (key, ".gif"))
				cra_app_add_veto (app, "Uses GIF icon: %s", key);
			else if (g_str_has_suffix (key, ".ico"))
				cra_app_add_veto (app, "Uses ICO icon: %s", key);

			/* find icon */
			pixbuf = cra_app_find_icon (tmpdir, key, error);
			if (pixbuf == NULL)
				return FALSE;

			/* save in target directory */
			icon_filename = g_strdup_printf ("%s.png",
							 as_app_get_id (AS_APP (app)));
			as_app_set_icon (AS_APP (app), icon_filename, -1);
			as_app_set_icon_kind (AS_APP (app), AS_ICON_KIND_CACHED);
			cra_app_set_pixbuf (app, pixbuf);
		}
	}

	/* add */
	cra_plugin_add_app (apps, app);
	return TRUE;
}
/**
 * cra_plugin_process_app:
 */
gboolean
cra_plugin_process_app (CraPlugin *plugin,
			CraPackage *pkg,
			CraApp *app,
			const gchar *tmpdir,
			GError **error)
{
	const gchar *tmp;
	AsRelease *release;
	gchar **deps;
	gchar **filelist;
	GPtrArray *releases;
	guint i;
	guint secs;
	guint days;

	/* add extra categories */
	tmp = as_app_get_id (AS_APP (app));
	if (g_strcmp0 (tmp, "0install") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "alacarte") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "deja-dup") == 0)
		as_app_add_category (AS_APP (app), "Utility", -1);
	if (g_strcmp0 (tmp, "gddccontrol") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "nautilus") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "pessulus") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "pmdefaults") == 0)
		as_app_add_category (AS_APP (app), "System", -1);
	if (g_strcmp0 (tmp, "fwfstab") == 0)
		as_app_add_category (AS_APP (app), "System", -1);

	/* add extra project groups */
	if (g_strcmp0 (tmp, "nemo") == 0)
		as_app_set_project_group (AS_APP (app), "Cinnamon", -1);
	if (g_strcmp0 (tmp, "xfdashboard") == 0)
		as_app_set_project_group (AS_APP (app), "XFCE", -1);

	/* use the URL to guess the project group */
	tmp = cra_package_get_url (pkg);
	if (as_app_get_project_group (AS_APP (app)) == NULL && tmp != NULL) {
		tmp = cra_glob_value_search (plugin->priv->project_groups, tmp);
		if (tmp != NULL)
			as_app_set_project_group (AS_APP (app), tmp, -1);
	}

	/* use summary to guess the project group */
	tmp = as_app_get_comment (AS_APP (app), NULL);
	if (tmp != NULL && g_strstr_len (tmp, -1, "for KDE") != NULL)
		as_app_set_project_group (AS_APP (app), "KDE", -1);

	/* look for any installed docs */
	filelist = cra_package_get_filelist (pkg);
	for (i = 0; filelist[i] != NULL; i++) {
		if (g_str_has_prefix (filelist[i],
				      "/usr/share/help/")) {
			as_app_add_metadata (AS_APP (app),
					     "X-Kudo-InstallsUserDocs", "", -1);
			break;
		}
	}

	/* look for a shell search provider */
	for (i = 0; filelist[i] != NULL; i++) {
		if (g_str_has_prefix (filelist[i],
				      "/usr/share/gnome-shell/search-providers/")) {
			as_app_add_metadata (AS_APP (app),
					     "X-Kudo-SearchProvider", "", -1);
			break;
		}
	}

	/* look for a modern toolkit */
	deps = cra_package_get_deps (pkg);
	for (i = 0; deps != NULL && deps[i] != NULL; i++) {
		if (g_strcmp0 (deps[i], "libgtk-3.so.0") == 0) {
			as_app_add_metadata (AS_APP (app),
					     "X-Kudo-GTK3", "", -1);
			break;
		}
		if (g_strcmp0 (deps[i], "libQt5Core.so.5") == 0) {
			as_app_add_metadata (AS_APP (app),
					     "X-Kudo-QT5", "", -1);
			break;
		}
	}

	/* look for ancient toolkits */
	for (i = 0; deps != NULL && deps[i] != NULL; i++) {
		if (g_strcmp0 (deps[i], "libgtk-1.2.so.0") == 0) {
			cra_app_add_veto (app, "Uses obsolete GTK1 toolkit");
			break;
		}
		if (g_strcmp0 (deps[i], "libqt-mt.so.3") == 0) {
			cra_app_add_veto (app, "Uses obsolete QT3 toolkit");
			break;
		}
		if (g_strcmp0 (deps[i], "liblcms.so.1") == 0) {
			cra_app_add_veto (app, "Uses obsolete LCMS library");
			break;
		}
		if (g_strcmp0 (deps[i], "libelektra.so.4") == 0) {
			cra_app_add_veto (app, "Uses obsolete Elektra library");
			break;
		}
		if (g_strcmp0 (deps[i], "libXt.so.6") == 0) {
			cra_app_add_requires_appdata (app, "Uses obsolete X11 toolkit");
			break;
		}
		if (g_strcmp0 (deps[i], "wine-core") == 0) {
			cra_app_add_requires_appdata (app, "Uses wine");
			break;
		}
	}

	/* has the application been updated in the last year */
	releases = as_app_get_releases (AS_APP (app));
	for (i = 0; i < releases->len; i++) {
		release = g_ptr_array_index (releases, i);
		secs = (g_get_real_time () / G_USEC_PER_SEC) -
			as_release_get_timestamp (release);
		days = secs / (60 * 60 * 24);
		if (days < 365) {
			as_app_add_metadata (AS_APP (app),
					     "X-Kudo-RecentRelease", "", -1);
			break;
		}
	}

	/* has there been no upstream version recently */
	if (releases->len > 0 &&
	    as_app_get_id_kind (AS_APP (app)) == AS_ID_KIND_DESKTOP) {
		release = g_ptr_array_index (releases, 0);
		secs = (g_get_real_time () / G_USEC_PER_SEC) -
			as_release_get_timestamp (release);
		days = secs / (60 * 60 * 24);

		/* this is just too old for us to care about */
		if (days > 365 * 10) {
			cra_app_add_veto (app, "Dead upstream for %i years",
					  secs / (60 * 60 * 24 * 365));
		}

		/* we need AppData if the app needs saving */
		else if (days > 365 * 5) {
			cra_app_add_requires_appdata (app,
				"Dead upstream for > %i years", 5);
		}
	}

	/* do any extra screenshots exist */
	tmp = cra_package_get_config (pkg, "ScreenshotsExtra");
	if (tmp != NULL) {
		_cleanup_free_ gchar *dirname = NULL;
		dirname = g_build_filename (tmp, as_app_get_id (AS_APP (app)), NULL);
		if (g_file_test (dirname, G_FILE_TEST_EXISTS)) {
			if (!cra_plugin_hardcoded_add_screenshots (app, dirname, error))
				return FALSE;
		}
	}

	/* a ConsoleOnly category means we require AppData */
	if (as_app_has_category (AS_APP(app), "ConsoleOnly"))
		cra_app_add_requires_appdata (app, "ConsoleOnly");

	/* no categories means we require AppData */
	if (as_app_get_categories(AS_APP(app))->len == 0)
		cra_app_add_requires_appdata (app, "no Categories");

	return TRUE;
}
Example #27
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;
}
/**
 * asb_plugin_loader_merge:
 * @plugin_loader: A #AsbPluginLoader
 * @apps: (element-type AsbApp): a list of applications that need merging
 *
 * Merge the list of applications using the plugins.
 *
 * Since: 0.2.5
 **/
void
asb_plugin_loader_merge (AsbPluginLoader *plugin_loader, GList *apps)
{
	AsbApp *app;
	AsbApp *found;
	AsbPluginLoaderPrivate *priv = GET_PRIVATE (plugin_loader);
	AsbPluginMergeFunc plugin_func = NULL;
	AsbPlugin *plugin;
	GList *l;
	const gchar *key;
	const gchar *tmp;
	gboolean ret;
	guint i;
	g_autoptr(GHashTable) hash = NULL;

	/* run each plugin */
	for (i = 0; i < priv->plugins->len; i++) {
		plugin = g_ptr_array_index (priv->plugins, i);
		ret = g_module_symbol (plugin->module,
				       "asb_plugin_merge",
				       (gpointer *) &plugin_func);
		if (!ret)
			continue;
		plugin_func (plugin, apps);
	}

	/* FIXME: move to font plugin */
	for (l = apps; l != NULL; l = l->next) {
		if (!ASB_IS_APP (l->data))
			continue;
		app = ASB_APP (l->data);
		as_app_remove_metadata (AS_APP (app), "FontFamily");
		as_app_remove_metadata (AS_APP (app), "FontFullName");
		as_app_remove_metadata (AS_APP (app), "FontIconText");
		as_app_remove_metadata (AS_APP (app), "FontParent");
		as_app_remove_metadata (AS_APP (app), "FontSampleText");
		as_app_remove_metadata (AS_APP (app), "FontSubFamily");
		as_app_remove_metadata (AS_APP (app), "FontClassifier");
	}

	/* deduplicate */
	hash = g_hash_table_new (g_str_hash, g_str_equal);
	for (l = apps; l != NULL; l = l->next) {
		if (!ASB_IS_APP (l->data))
			continue;
		app = ASB_APP (l->data);
		if (as_app_get_vetos(AS_APP(app))->len > 0)
			continue;
		key = as_app_get_id (AS_APP (app));
		found = g_hash_table_lookup (hash, key);
		if (found == NULL) {
			g_hash_table_insert (hash,
					     (gpointer) key,
					     (gpointer) app);
			continue;
		}
		if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_FIRMWARE) {
			as_app_subsume_full (AS_APP (found), AS_APP (app),
					     AS_APP_SUBSUME_FLAG_MERGE);
		}
		tmp = asb_package_get_nevr (asb_app_get_package (found));
		as_app_add_veto (AS_APP (app), "duplicate of %s", tmp);
		asb_package_log (asb_app_get_package (app),
				 ASB_PACKAGE_LOG_LEVEL_WARNING,
				 "duplicate %s not included as added from %s",
				 key, tmp);
	}
}
/**
 * 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;
}
/**
 * asb_plugin_process_filename:
 */
static gboolean
asb_plugin_process_filename (AsbPlugin *plugin,
                             AsbPackage *pkg,
                             const gchar *filename,
                             GList **apps,
                             const gchar *tmpdir,
                             GError **error)
{
    gboolean ret = TRUE;
    gchar *error_msg = 0;
    gchar *filename_tmp;
    gint rc;
    guint i;
    sqlite3 *db = NULL;
    _cleanup_free_ gchar *basename = NULL;
    _cleanup_free_ gchar *description = NULL;
    _cleanup_free_ gchar *language_string = NULL;
    _cleanup_free_ gchar *name = NULL;
    _cleanup_free_ gchar *symbol = NULL;
    _cleanup_object_unref_ AsbApp *app = NULL;
    _cleanup_object_unref_ AsIcon *icon = NULL;
    _cleanup_strv_free_ gchar **languages = NULL;

    /* open IME database */
    filename_tmp = g_build_filename (tmpdir, filename, NULL);
    rc = sqlite3_open (filename_tmp, &db);
    if (rc) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "Can't open database: %s",
                     sqlite3_errmsg (db));
        goto out;
    }

    /* get name */
    rc = sqlite3_exec(db, "SELECT * FROM ime WHERE attr = 'name' LIMIT 1;",
                      asb_plugin_sqlite_callback_cb,
                      &name, &error_msg);
    if (rc != SQLITE_OK) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "Can't get IME name from %s: %s",
                     filename, error_msg);
        sqlite3_free(error_msg);
        goto out;
    }

    /* get symbol */
    rc = sqlite3_exec(db, "SELECT * FROM ime WHERE attr = 'symbol' LIMIT 1;",
                      asb_plugin_sqlite_callback_cb,
                      &symbol, &error_msg);
    if (rc != SQLITE_OK) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "Can't get IME symbol from %s: %s",
                     filename, error_msg);
        sqlite3_free(error_msg);
        goto out;
    }

    /* get languages */
    rc = sqlite3_exec(db, "SELECT * FROM ime WHERE attr = 'languages' LIMIT 1;",
                      asb_plugin_sqlite_callback_cb,
                      &language_string, &error_msg);
    if (rc != SQLITE_OK) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "Can't get IME languages from %s: %s",
                     filename, error_msg);
        sqlite3_free(error_msg);
        goto out;
    }

    /* get description */
    rc = sqlite3_exec(db, "SELECT * FROM ime WHERE attr = 'description' LIMIT 1;",
                      asb_plugin_sqlite_callback_cb,
                      &description, &error_msg);
    if (rc != SQLITE_OK) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "Can't get IME name from %s: %s",
                     filename, error_msg);
        sqlite3_free(error_msg);
        goto out;
    }

    /* this is _required_ */
    if (name == NULL || description == NULL) {
        ret = FALSE;
        g_set_error (error,
                     ASB_PLUGIN_ERROR,
                     ASB_PLUGIN_ERROR_FAILED,
                     "No 'name' and 'description' in %s",
                     filename);
        goto out;
    }

    /* create new app */
    basename = g_path_get_basename (filename);
    app = asb_app_new (pkg, basename);
    as_app_set_id_kind (AS_APP (app), AS_ID_KIND_INPUT_METHOD);
    as_app_add_category (AS_APP (app), "Addons");
    as_app_add_category (AS_APP (app), "InputSources");
    as_app_set_name (AS_APP (app), "C", name);
    as_app_set_comment (AS_APP (app), "C", description);
    if (symbol != NULL && symbol[0] != '\0')
        as_app_add_metadata (AS_APP (app), "X-IBus-Symbol", symbol);
    if (language_string != NULL) {
        languages = g_strsplit (language_string, ",", -1);
        for (i = 0; languages[i] != NULL; i++) {
            if (g_strcmp0 (languages[i], "other") == 0)
                continue;
            as_app_add_language (AS_APP (app),
                                 100, languages[i]);
        }
    }
    asb_app_set_requires_appdata (app, TRUE);
    asb_app_set_hidpi_enabled (app, asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS));

    /* add icon */
    icon = as_icon_new ();
    as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
    as_icon_set_name (icon, "system-run-symbolic");
    as_app_add_icon (AS_APP (app), icon);

    asb_plugin_add_app (apps, AS_APP (app));
out:
    if (db != NULL)
        sqlite3_close (db);
    return ret;
}