[gnome-software] Add a plugin method to convert a local file to a GsApp



commit 3a03420b9e2538353a67f5f008427f00631780de
Author: Richard Hughes <richard hughsie com>
Date:   Wed Jan 29 08:42:25 2014 +0000

    Add a plugin method to convert a local file to a GsApp

 src/gs-cmd.c                               |   13 ++-
 src/gs-plugin-loader-sync.c                |   51 +++++++
 src/gs-plugin-loader-sync.h                |    5 +
 src/gs-plugin-loader.c                     |  195 ++++++++++++++++++++++++++++
 src/gs-plugin-loader.h                     |    9 ++
 src/gs-plugin.h                            |   10 ++
 src/plugins/gs-plugin-packagekit-refresh.c |   79 +++++++++++
 7 files changed, 361 insertions(+), 1 deletions(-)
---
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index 1133259..b02aab8 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -277,6 +277,17 @@ main (int argc, char **argv)
                        if (!ret)
                                break;
                }
+       } else if (argc == 3 && g_strcmp0 (argv[1], "filename-to-app") == 0) {
+               app = gs_plugin_loader_filename_to_app (plugin_loader,
+                                                       argv[2],
+                                                       refine_flags,
+                                                       NULL,
+                                                       &error);
+               if (app == NULL) {
+                       ret = FALSE;
+               } else {
+                       gs_plugin_add_app (&list, app);
+               }
        } else if (argc == 2 && g_strcmp0 (argv[1], "updates") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
@@ -358,7 +369,7 @@ main (int argc, char **argv)
                                     GS_PLUGIN_ERROR_FAILED,
                                     "Did not recognise option, use 'installed', "
                                     "'updates', 'popular', 'get-categories', "
-                                    "'get-category-apps', or 'search'");
+                                    "'get-category-apps', 'filename-to-app', or 'search'");
        }
        if (!ret) {
                g_print ("Failed: %s\n", error->message);
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index efd2253..144e7ae 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -52,6 +52,7 @@ typedef struct {
        GMainContext    *context;
        GMainLoop       *loop;
        gboolean         ret;
+       GsApp           *app;
 } GsPluginLoaderHelper;
 
 static void
@@ -523,4 +524,54 @@ gs_plugin_loader_refresh (GsPluginLoader *plugin_loader,
        return helper.ret;
 }
 
+static void
+gs_plugin_loader_filename_to_app_finish_sync (GObject *source_object,
+                                             GAsyncResult *res,
+                                             gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+       GsPluginLoaderHelper *helper = (GsPluginLoaderHelper *) user_data;
+       helper->app = gs_plugin_loader_filename_to_app_finish (plugin_loader,
+                                                              res,
+                                                              helper->error);
+       g_main_loop_quit (helper->loop);
+}
+
+/**
+ * gs_plugin_loader_filename_to_app:
+ **/
+GsApp *
+gs_plugin_loader_filename_to_app (GsPluginLoader *plugin_loader,
+                                 const gchar *filename,
+                                 GsPluginRefineFlags flags,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       GsPluginLoaderHelper helper;
+
+       /* create temp object */
+       helper.app = NULL;
+       helper.context = g_main_context_new ();
+       helper.loop = g_main_loop_new (helper.context, FALSE);
+       helper.error = error;
+
+       g_main_context_push_thread_default (helper.context);
+
+       /* run async method */
+       gs_plugin_loader_filename_to_app_async (plugin_loader,
+                                               filename,
+                                               flags,
+                                               cancellable,
+                                               gs_plugin_loader_filename_to_app_finish_sync,
+                                               &helper);
+       g_main_loop_run (helper.loop);
+
+       g_main_context_pop_thread_default (helper.context);
+
+       g_main_loop_unref (helper.loop);
+       g_main_context_unref (helper.context);
+
+       return helper.app;
+}
+
 /* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index 64f940c..ec509bf 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -78,6 +78,11 @@ GsApp                *gs_plugin_loader_get_app_by_id         (GsPluginLoader 
*plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+GsApp          *gs_plugin_loader_filename_to_app       (GsPluginLoader *plugin_loader,
+                                                        const gchar    *filename,
+                                                        GsPluginRefineFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 G_END_DECLS
 
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 9334630..796db23 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -644,6 +644,7 @@ typedef struct {
        GsPluginLoader                  *plugin_loader;
        GsPluginRefineFlags              flags;
        gchar                           *value;
+       gchar                           *filename;
        guint                            cache_age;
        GsCategory                      *category;
        GsApp                           *app;
@@ -2996,4 +2997,198 @@ gs_plugin_loader_refresh_finish (GsPluginLoader *plugin_loader,
 
 /******************************************************************************/
 
+/**
+ * gs_plugin_loader_filename_to_app_state_finish:
+ **/
+static void
+gs_plugin_loader_filename_to_app_state_finish (GsPluginLoaderAsyncState *state,
+                                              const GError *error)
+{
+       if (state->app != NULL) {
+               g_simple_async_result_set_op_res_gpointer (state->res,
+                                                          g_object_ref (state->app),
+                                                          (GDestroyNotify) g_object_unref);
+       } else {
+               g_simple_async_result_set_from_error (state->res, error);
+       }
+
+       if (state->cancellable != NULL)
+               g_object_unref (state->cancellable);
+       if (state->app != NULL)
+               g_object_unref (state->app);
+       g_free (state->filename);
+       gs_plugin_list_free (state->list);
+       g_object_unref (state->res);
+       g_object_unref (state->plugin_loader);
+       g_slice_free (GsPluginLoaderAsyncState, state);
+}
+
+/**
+ * gs_plugin_loader_filename_to_app_thread_cb:
+ **/
+static void
+gs_plugin_loader_filename_to_app_thread_cb (GSimpleAsyncResult *res,
+                                           GObject *object,
+                                           GCancellable *cancellable)
+{
+       const gchar *function_name = "gs_plugin_filename_to_app";
+       gboolean ret = TRUE;
+       gchar *profile_id;
+       GError *error = NULL;
+       GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+       GsPlugin *plugin;
+       GsPluginFilenameToAppFunc plugin_func = NULL;
+       guint i;
+
+       /* run each plugin */
+       for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
+               plugin = g_ptr_array_index (plugin_loader->priv->plugins, i);
+               if (!plugin->enabled)
+                       continue;
+               ret = g_cancellable_set_error_if_cancelled (cancellable, &error);
+               if (ret) {
+                       gs_plugin_loader_filename_to_app_state_finish (state, error);
+                       g_error_free (error);
+                       goto out;
+               }
+               ret = g_module_symbol (plugin->module,
+                                      function_name,
+                                      (gpointer *) &plugin_func);
+               if (!ret)
+                       continue;
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
+               ret = plugin_func (plugin, &state->list, state->filename, cancellable, &error);
+               if (!ret) {
+                       gs_plugin_loader_filename_to_app_state_finish (state, error);
+                       g_error_free (error);
+                       goto out;
+               }
+               gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
+       }
+
+       /* dedupe applications we already know about */
+       gs_plugin_loader_list_dedupe (plugin_loader, state->list);
+
+       /* run refine() on each one */
+       ret = gs_plugin_loader_run_refine (plugin_loader,
+                                          function_name,
+                                          state->list,
+                                          state->flags,
+                                          cancellable,
+                                          &error);
+       if (!ret) {
+               gs_plugin_loader_filename_to_app_state_finish (state, error);
+               g_error_free (error);
+               goto out;
+       }
+
+       /* filter package list */
+       gs_plugin_list_filter_duplicates (&state->list);
+       if (state->list == NULL) {
+               g_set_error (&error,
+                            GS_PLUGIN_LOADER_ERROR,
+                            GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+                            "no filename_to_app results to show");
+               gs_plugin_loader_filename_to_app_state_finish (state, error);
+               g_error_free (error);
+               goto out;
+       }
+
+       /* success */
+       if (g_list_length (state->list) != 1) {
+               g_set_error (&error,
+                            GS_PLUGIN_LOADER_ERROR,
+                            GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+                            "no application was created for %s",
+                            state->filename);
+               gs_plugin_loader_filename_to_app_state_finish (state, error);
+               g_error_free (error);
+               goto out;
+       }
+       state->app = g_object_ref (state->list->data);
+       gs_plugin_loader_filename_to_app_state_finish (state, NULL);
+out:
+       return;
+}
+
+/**
+ * gs_plugin_loader_filename_to_app_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_add_filename_to_app()
+ * function. The plugins can either return #GsApp objects of kind
+ * %GS_APP_KIND_NORMAL for bonafide applications, or #GsApp's of kind
+ * %GS_APP_KIND_PACKAGE for packages that may or may not be applications.
+ *
+ * Once the list of updates is refined, some of the #GsApp's of kind
+ * %GS_APP_KIND_PACKAGE will have been promoted to a kind of %GS_APP_KIND_NORMAL,
+ * or if they are core applications.
+ **/
+void
+gs_plugin_loader_filename_to_app_async (GsPluginLoader *plugin_loader,
+                                       const gchar *filename,
+                                       GsPluginRefineFlags flags,
+                                       GCancellable *cancellable,
+                                       GAsyncReadyCallback callback,
+                                       gpointer user_data)
+{
+       GCancellable *tmp;
+       GsPluginLoaderAsyncState *state;
+
+       g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+       /* save state */
+       state = g_slice_new0 (GsPluginLoaderAsyncState);
+       state->res = g_simple_async_result_new (G_OBJECT (plugin_loader),
+                                               callback,
+                                               user_data,
+                                               gs_plugin_loader_filename_to_app_async);
+       state->plugin_loader = g_object_ref (plugin_loader);
+       state->flags = flags;
+       state->filename = g_strdup (filename);
+       if (cancellable != NULL)
+               state->cancellable = g_object_ref (cancellable);
+
+       /* run in a thread */
+       tmp = g_cancellable_new ();
+       g_object_set_data (G_OBJECT (tmp), "state", state);
+       g_simple_async_result_run_in_thread (G_SIMPLE_ASYNC_RESULT (state->res),
+                                            gs_plugin_loader_filename_to_app_thread_cb,
+                                            0,
+                                            (GCancellable *) tmp);
+       g_object_unref (tmp);
+}
+
+/**
+ * gs_plugin_loader_filename_to_app_finish:
+ *
+ * Return value: (element-type GsApp) (transfer full): An application, or %NULL
+ **/
+GsApp *
+gs_plugin_loader_filename_to_app_finish (GsPluginLoader *plugin_loader,
+                                        GAsyncResult *res,
+                                        GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+       g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       /* failed */
+       simple = G_SIMPLE_ASYNC_RESULT (res);
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+
+       /* grab application */
+       return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+/******************************************************************************/
+
 /* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 73d7af9..302043e 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -136,6 +136,15 @@ void                gs_plugin_loader_search_async          (GsPluginLoader 
*plugin_loader,
 GList          *gs_plugin_loader_search_finish         (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
+void            gs_plugin_loader_filename_to_app_async (GsPluginLoader *plugin_loader,
+                                                        const gchar    *filename,
+                                                        GsPluginRefineFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
+GsApp          *gs_plugin_loader_filename_to_app_finish(GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
 gboolean        gs_plugin_loader_setup                 (GsPluginLoader *plugin_loader,
                                                         GError         **error);
 void            gs_plugin_loader_dump_state            (GsPluginLoader *plugin_loader);
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 3f4240f..1350cc7 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -135,6 +135,11 @@ typedef gboolean    (*GsPluginRefreshFunc  )       (GsPlugin       *plugin,
                                                         GsPluginRefreshFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+typedef gboolean        (*GsPluginFilenameToAppFunc)   (GsPlugin       *plugin,
+                                                        GList          **list,
+                                                        const gchar    *filename,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 const gchar    *gs_plugin_get_name                     (void);
 void            gs_plugin_initialize                   (GsPlugin       *plugin);
@@ -216,6 +221,11 @@ gboolean    gs_plugin_refresh                      (GsPlugin       *plugin,
                                                         GsPluginRefreshFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_filename_to_app              (GsPlugin       *plugin,
+                                                        GList          **list,
+                                                        const gchar    *filename,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 G_END_DECLS
 
diff --git a/src/plugins/gs-plugin-packagekit-refresh.c b/src/plugins/gs-plugin-packagekit-refresh.c
index 866387f..73f62db 100644
--- a/src/plugins/gs-plugin-packagekit-refresh.c
+++ b/src/plugins/gs-plugin-packagekit-refresh.c
@@ -169,3 +169,82 @@ out:
                g_object_unref (results);
        return ret;
 }
+
+/**
+ * gs_plugin_refresh:
+ */
+gboolean
+gs_plugin_filename_to_app (GsPlugin *plugin,
+                          GList **list,
+                          const gchar *filename,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       const gchar *package_id;
+       gboolean ret = TRUE;
+       gchar **files;
+       gchar **split = NULL;
+       GPtrArray *array = NULL;
+       GsApp *app = NULL;
+       PkDetails *item;
+       PkResults *results;
+
+       /* get details */
+       files = g_strsplit (filename, "\t", -1);
+       pk_client_set_cache_age (PK_CLIENT (plugin->priv->task), G_MAXUINT);
+       results = pk_client_get_details_local (PK_CLIENT (plugin->priv->task),
+                                              files,
+                                              cancellable,
+                                              gs_plugin_packagekit_progress_cb, plugin,
+                                              error);
+       if (results == NULL) {
+               ret = FALSE;
+               goto out;
+       }
+
+       /* get results */
+       array = pk_results_get_details_array (results);
+       if (array->len == 0) {
+               ret = FALSE;
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "no details for %s", filename);
+               goto out;
+       }
+       if (array->len > 1) {
+               ret = FALSE;
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "too many details [%i] for %s",
+                            array->len, filename);
+               goto out;
+       }
+
+       /* create application */
+       item = g_ptr_array_index (array, 0);
+       app = gs_app_new (NULL);
+       package_id = pk_details_get_package_id (item);
+       split = pk_package_id_split (package_id);
+       gs_app_set_kind (app, GS_APP_KIND_PACKAGE);
+       gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+       gs_app_set_name (app, split[PK_PACKAGE_ID_NAME]);
+       gs_app_set_version (app, split[PK_PACKAGE_ID_VERSION]);
+       gs_app_set_metadata (app, "PackageKit::local-filename", filename);
+       gs_app_add_source (app, split[PK_PACKAGE_ID_NAME]);
+       gs_app_add_source_id (app, package_id);
+       gs_app_set_description (app, pk_details_get_description (item));
+       gs_app_set_url (app, GS_APP_URL_KIND_HOMEPAGE, pk_details_get_url (item));
+       gs_app_set_size (app, pk_details_get_size (item));
+       gs_app_set_licence (app, pk_details_get_license (item));
+       gs_plugin_add_app (list, app);
+out:
+       if (app != NULL)
+               g_object_unref (app);
+       if (array != NULL)
+               g_ptr_array_unref (array);
+       g_strfreev (split);
+       g_strfreev (files);
+       return ret;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]