[gnome-documents] miner: Reduce copy/pasted code with inheritance



commit 32686b5c896a8fd9343345542e3ba19341302f1f
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Wed Aug 1 15:16:55 2012 -0300

    miner: Reduce copy/pasted code with inheritance
    
    A large amount of the code in each miner is copy/pasted. We can do
    better than this. Define a base subclass that does most of the work,
    and override the parts that are different in each subclass.
    
    While we're at it, deduplicate the miner main start, as well.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=681028

 src/Makefile-miner.am        |    4 +
 src/miner/gd-gdata-miner.c   |  717 ++---------------------------------------
 src/miner/gd-gdata-miner.h   |   16 +-
 src/miner/gd-miner.c         |  656 ++++++++++++++++++++++++++++++++++++++
 src/miner/gd-miner.h         |   97 ++++++
 src/miner/gd-zpj-miner.c     |  658 ++-------------------------------------
 src/miner/gd-zpj-miner.h     |   16 +-
 src/miner/gdata-miner-main.c |  230 +-------------
 src/miner/miner-main.c       |  248 +++++++++++++++
 src/miner/zpj-miner-main.c   |  230 +-------------
 10 files changed, 1080 insertions(+), 1792 deletions(-)
---
diff --git a/src/Makefile-miner.am b/src/Makefile-miner.am
index e590a59..e282697 100644
--- a/src/Makefile-miner.am
+++ b/src/Makefile-miner.am
@@ -31,6 +31,8 @@ libexec_PROGRAMS += gd-tracker-gdata-miner
 
 gd_tracker_gdata_miner_SOURCES = \
     miner/gdata-miner-main.c \
+    miner/gd-miner.c \
+    miner/gd-miner.h \
     miner/gd-gdata-miner.c \
     miner/gd-gdata-miner.h \
     $(NULL)
@@ -53,6 +55,8 @@ libexec_PROGRAMS += gd-tracker-zpj-miner
 
 gd_tracker_zpj_miner_SOURCES = \
     miner/zpj-miner-main.c \
+    miner/gd-miner.c \
+    miner/gd-miner.h \
     miner/gd-zpj-miner.c \
     miner/gd-zpj-miner.h \
     $(NULL)
diff --git a/src/miner/gd-gdata-miner.c b/src/miner/gd-gdata-miner.c
index 4fd258d..d747ecb 100644
--- a/src/miner/gd-gdata-miner.c
+++ b/src/miner/gd-gdata-miner.c
@@ -21,150 +21,23 @@
 
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <gdata/gdata.h>
-#include <goa/goa.h>
-#include <unistd.h>
 
 #include "gd-gdata-miner.h"
-#include "gd-miner-tracker.h"
-#include "gd-utils.h"
 
 #define MINER_IDENTIFIER "gd:gdata:miner:86ec9bc9-c242-427f-aa19-77b5a2c9b6f0"
 #define STARRED_CATEGORY_TERM "http://schemas.google.com/g/2005/labels#starred";
 #define PARENT_LINK_REL "http://schemas.google.com/docs/2007#parent";
 
-G_DEFINE_TYPE (GdGDataMiner, gd_gdata_miner, G_TYPE_OBJECT)
-
-struct _GdGDataMinerPrivate {
-  GoaClient *client;
-  TrackerSparqlConnection *connection;
-
-  GCancellable *cancellable;
-  GSimpleAsyncResult *result;
-
-  GList *pending_jobs;
-};
-
-typedef struct {
-  GdGDataMiner *self;
-  TrackerSparqlConnection *connection; /* borrowed from GdGDataMiner */
-  gulong miner_cancellable_id;
-
-  GoaAccount *account;
-  GDataDocumentsService *service;
-  GSimpleAsyncResult *async_result;
-  GCancellable *cancellable;
-
-  GHashTable *previous_resources;
-} AccountMinerJob;
-
-static void
-miner_cancellable_cancelled_cb (GCancellable *cancellable,
-                                gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-
-  /* forward the cancel signal to the ongoing job */
-  g_cancellable_cancel (job->cancellable);
-}
-
-static void
-account_miner_job_free (AccountMinerJob *job)
-{
-  if (job->miner_cancellable_id != 0)
-    g_cancellable_disconnect (job->self->priv->cancellable,
-                              job->miner_cancellable_id);
-
-  g_clear_object (&job->service);
-  g_clear_object (&job->self);
-  g_clear_object (&job->account);
-  g_clear_object (&job->async_result);
-
-  g_hash_table_unref (job->previous_resources);
-
-  g_slice_free (AccountMinerJob, job);
-}
-
-static AccountMinerJob *
-account_miner_job_new (GdGDataMiner *self,
-                       GoaObject *object)
-{
-  AccountMinerJob *retval;
-  GDataGoaAuthorizer *authorizer;
-  GoaAccount *account;
-
-  account = goa_object_get_account (object);
-  g_assert (account != NULL);
-
-  retval = g_slice_new0 (AccountMinerJob);
-  retval->self = g_object_ref (self);
-  retval->cancellable = g_cancellable_new ();
-  retval->account = account;
-  retval->connection = self->priv->connection;
-  retval->previous_resources = 
-    g_hash_table_new_full (g_str_hash, g_str_equal,
-                           (GDestroyNotify) g_free, (GDestroyNotify) g_free);
-
-  if (self->priv->cancellable != NULL)
-      retval->miner_cancellable_id = 
-        g_cancellable_connect (self->priv->cancellable,
-                               G_CALLBACK (miner_cancellable_cancelled_cb),
-                               retval, NULL);
-
-  authorizer = gdata_goa_authorizer_new (object);
-  retval->service = gdata_documents_service_new (GDATA_AUTHORIZER (authorizer));
-
-  /* the service takes ownership of the authorizer */
-  g_object_unref (authorizer);
-
-  return retval;
-}
-
-static void
-previous_resources_cleanup_foreach (gpointer key,
-                                    gpointer value,
-                                    gpointer user_data)
-{
-  const gchar *resource = value;
-  GString *delete = user_data;
-
-  g_string_append_printf (delete, "<%s> a rdfs:Resource . ", resource);
-}
-
-static void
-account_miner_job_cleanup_previous (AccountMinerJob *job,
-                                    GError **error)
-{
-  GString *delete;
-
-  delete = g_string_new (NULL);
-  g_string_append (delete, "DELETE { ");
-
-  /* the resources left here are those who were in the database,
-   * but were not found during the query; remove them from the database.
-   */
-  g_hash_table_foreach (job->previous_resources,
-                        previous_resources_cleanup_foreach,
-                        delete);
-
-  g_string_append (delete, "}");
-
-  tracker_sparql_connection_update (job->connection,
-                                    delete->str,
-                                    G_PRIORITY_DEFAULT,
-                                    job->cancellable,
-                                    error);
-
-  g_string_free (delete, TRUE);
-}
+G_DEFINE_TYPE (GdGDataMiner, gd_gdata_miner, GD_TYPE_MINER)
 
 static gboolean
-account_miner_job_process_entry (AccountMinerJob *job,
+account_miner_job_process_entry (GdAccountMinerJob *job,
                                  GDataDocumentsEntry *doc_entry,
                                  GError **error)
 {
   GDataEntry *entry = GDATA_ENTRY (doc_entry);
   gchar *resource = NULL;
-  gchar *date, *resource_url, *datasource_urn, *identifier;
+  gchar *date, *resource_url, *identifier;
   const gchar *class = NULL;
 
   GList *authors, *l, *parents = NULL;
@@ -209,24 +82,20 @@ account_miner_job_process_entry (AccountMinerJob *job,
     class = "nfo:PaginatedTextDocument";
   else if (GDATA_IS_DOCUMENTS_FOLDER (doc_entry))
     class = "nfo:DataContainer";
- 
+
   resource = gd_miner_tracker_sparql_connection_ensure_resource
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      resource_url, identifier,
      "nfo:RemoteDataObject", class, NULL);
-  
+
   if (*error != NULL)
     goto out;
 
-  datasource_urn = g_strdup_printf ("gd:goa-account:%s", 
-                                    goa_account_get_id (job->account));
   gd_miner_tracker_sparql_connection_set_triple
     (job->connection, job->cancellable, error,
      identifier, resource,
-     "nie:dataSource", datasource_urn);
-
-  g_free (datasource_urn);
+     "nie:dataSource", job->datasource_urn);
 
   if (*error != NULL)
     goto out;
@@ -235,7 +104,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
   alternate_uri = gdata_link_get_uri (alternate);
 
   gd_miner_tracker_sparql_connection_insert_or_replace_triple
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      identifier, resource,
      "nie:url", alternate_uri);
@@ -249,7 +118,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
       gchar *parent_resource_urn, *parent_resource_id;
 
       parent = l->data;
-      parent_resource_id = 
+      parent_resource_id =
         g_strdup_printf ("gd:collection:%s", gdata_link_get_uri (parent));
 
       parent_resource_urn = gd_miner_tracker_sparql_connection_ensure_resource
@@ -284,7 +153,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
     }
 
   gd_miner_tracker_sparql_connection_toggle_favorite
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      resource, starred);
 
@@ -292,7 +161,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
     goto out;
 
   gd_miner_tracker_sparql_connection_insert_or_replace_triple
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      identifier, resource,
      "nie:description", gdata_entry_get_summary (entry));
@@ -301,7 +170,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
     goto out;
 
   gd_miner_tracker_sparql_connection_insert_or_replace_triple
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      identifier, resource,
      "nie:title", gdata_entry_get_title (entry));
@@ -325,7 +194,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
         goto out;
 
       gd_miner_tracker_sparql_connection_insert_or_replace_triple
-        (job->connection, 
+        (job->connection,
          job->cancellable, error,
          identifier, resource,
          "nco:creator", contact_resource);
@@ -380,7 +249,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
 
   date = gd_iso8601_from_timestamp (gdata_entry_get_published (entry));
   gd_miner_tracker_sparql_connection_insert_or_replace_triple
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      identifier, resource,
      "nie:contentCreated", date);
@@ -391,7 +260,7 @@ account_miner_job_process_entry (AccountMinerJob *job,
 
   date = gd_iso8601_from_timestamp (gdata_entry_get_updated (entry));
   gd_miner_tracker_sparql_connection_insert_or_replace_triple
-    (job->connection, 
+    (job->connection,
      job->cancellable, error,
      identifier, resource,
      "nie:contentLastModified", date);
@@ -415,8 +284,8 @@ account_miner_job_process_entry (AccountMinerJob *job,
 }
 
 static void
-account_miner_job_query_gdata (AccountMinerJob *job,
-                               GError **error)
+query_gdata (GdAccountMinerJob *job,
+             GError **error)
 {
   GDataDocumentsQuery *query;
   GDataDocumentsFeed *feed;
@@ -424,8 +293,8 @@ account_miner_job_query_gdata (AccountMinerJob *job,
 
   query = gdata_documents_query_new (NULL);
   gdata_documents_query_set_show_folders (query, TRUE);
-  feed = gdata_documents_service_query_documents 
-    (job->service, query, 
+  feed = gdata_documents_service_query_documents
+    (GDATA_DOCUMENTS_SERVICE (job->service), query,
      job->cancellable, NULL, NULL, error);
 
   g_object_unref (query);
@@ -448,551 +317,35 @@ account_miner_job_query_gdata (AccountMinerJob *job,
   g_object_unref (feed);
 }
 
-
-static void
-account_miner_job_query_existing (AccountMinerJob *job,
-                                  GError **error)
-{
-  GString *select;
-  TrackerSparqlCursor *cursor;
-
-  select = g_string_new (NULL);
-  g_string_append_printf (select,
-                          "SELECT ?urn nao:identifier(?urn) WHERE { ?urn nie:dataSource <gd:goa-account:%s> }",
-                          goa_account_get_id (job->account));
-
-  cursor = tracker_sparql_connection_query (job->connection,
-                                            select->str,
-                                            job->cancellable,
-                                            error);
-  g_string_free (select, TRUE);
-
-  if (cursor == NULL)
-    return;
-
-  while (tracker_sparql_cursor_next (cursor, job->cancellable, error))
-    {
-      g_hash_table_insert (job->previous_resources, 
-                           g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)),
-                           g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)));
-    }
-
-  g_object_unref (cursor);
-}
-
-static void
-account_miner_job_ensure_datasource (AccountMinerJob *job,
-                                     GError **error)
-{
-  GString *datasource_insert;
-
-  datasource_insert = g_string_new (NULL);
-  g_string_append_printf (datasource_insert,
-                          "INSERT OR REPLACE { <gd:goa-account:%s> a nie:DataSource ; nao:identifier \"%s\" }",
-                          goa_account_get_id (job->account), MINER_IDENTIFIER);
-
-  tracker_sparql_connection_update (job->connection, 
-                                    datasource_insert->str,
-                                    G_PRIORITY_DEFAULT,
-                                    job->cancellable,
-                                    error);
-
-  g_string_free (datasource_insert, TRUE);
-}
-
-static gboolean
-account_miner_job (GIOSchedulerJob *sched_job,
-                   GCancellable *cancellable,
-                   gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-  GError *error = NULL;
-
-  account_miner_job_ensure_datasource (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_query_existing (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_query_gdata (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_cleanup_previous (job, &error);
-
-  if (error != NULL)
-    goto out;
-
- out:
-  if (error != NULL)
-    g_simple_async_result_take_error (job->async_result, error);
-
-  g_simple_async_result_complete_in_idle (job->async_result);
-
-  return FALSE;
-}
-
-static void
-account_miner_job_process_async (AccountMinerJob *job,
-                                 GAsyncReadyCallback callback,
-                                 gpointer user_data)
-{
-  g_assert (job->async_result == NULL);
-
-  job->async_result = g_simple_async_result_new (NULL, callback, user_data,
-                                                 account_miner_job_process_async);
-  g_simple_async_result_set_op_res_gpointer (job->async_result, job, NULL);
-
-  g_io_scheduler_push_job (account_miner_job, job, NULL,
-                           G_PRIORITY_DEFAULT,
-                           job->cancellable);
-}
-
-static gboolean
-account_miner_job_process_finish (GAsyncResult *res,
-                                  GError **error)
-{
-  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
-
-  g_assert (g_simple_async_result_is_valid (res, NULL,
-                                            account_miner_job_process_async));
-
-  if (g_simple_async_result_propagate_error (simple_res, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-gd_gdata_miner_complete_error (GdGDataMiner *self,
-                               GError *error)
-{
-  g_assert (self->priv->result != NULL);
-
-  g_simple_async_result_take_error (self->priv->result, error);
-  g_simple_async_result_complete_in_idle (self->priv->result);  
-}
-
-static void
-gd_gdata_miner_check_pending_jobs (GdGDataMiner *self)
-{
-  if (g_list_length (self->priv->pending_jobs) == 0)
-    g_simple_async_result_complete_in_idle (self->priv->result);    
-}
-
-static void
-miner_job_process_ready_cb (GObject *source,
-                            GAsyncResult *res,
-                            gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-  GdGDataMiner *self = job->self;
-  GError *error = NULL;
-
-  account_miner_job_process_finish (res, &error);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while refreshing account %s: %s", 
-                  goa_account_get_id (job->account), error->message);
-
-      g_error_free (error);
-    }
-
-  self->priv->pending_jobs = g_list_remove (self->priv->pending_jobs,
-                                            job);
-  account_miner_job_free (job);
-
-  gd_gdata_miner_check_pending_jobs (self);
-}
-
-static void
-gd_gdata_miner_setup_account (GdGDataMiner *self,
-                              GoaObject *object)
-{
-  AccountMinerJob *job;
-
-  job = account_miner_job_new (self, object);
-  self->priv->pending_jobs = g_list_prepend (self->priv->pending_jobs, job);
-
-  account_miner_job_process_async (job, miner_job_process_ready_cb, job);
-}
-
-typedef struct {
-  GdGDataMiner *self;
-  GList *doc_objects;
-  GList *acc_objects;
-  GList *old_datasources;
-} CleanupJob;
-
-static gboolean
-cleanup_old_accounts_done (gpointer data)
+static GObject *
+create_service (GdMiner *self,
+                GoaObject *object)
 {
-  CleanupJob *job = data;
-  GList *l;
-  GoaObject *object;
-  GdGDataMiner *self = job->self;
-
-  /* now setup all the current accounts */
-  for (l = job->doc_objects; l != NULL; l = l->next)
-    {
-      object = l->data;
-      gd_gdata_miner_setup_account (self, object);
-
-      g_object_unref (object);
-    }
-
-  if (job->doc_objects != NULL)
-    {
-      g_list_free (job->doc_objects);
-      job->doc_objects = NULL;
-    }
-
-  if (job->acc_objects != NULL)
-    {
-      g_list_free_full (job->acc_objects, g_object_unref);
-      job->acc_objects = NULL;
-    }
-
-  if (job->old_datasources != NULL)
-    {
-      g_list_free_full (job->old_datasources, g_free);
-      job->old_datasources = NULL;
-    }
-
-  gd_gdata_miner_check_pending_jobs (self);
-
-  g_clear_object (&job->self);
-  g_slice_free (CleanupJob, job);
-
-  return FALSE;
-}
-
-static void
-cleanup_job_do_cleanup (CleanupJob *job)
-{
-  GdGDataMiner *self = job->self;
-  GString *select, *update;
-  gboolean append_union = FALSE;
-  GList *l;
-  TrackerSparqlCursor *cursor;
-  GError *error = NULL;
-  const gchar *resource;
-
-  if (job->old_datasources == NULL)
-    return;
-
-  update = g_string_new (NULL);
-  g_string_append (update, "DELETE { ");
-
-  /* select all documents from the datasources we want to remove */
-  select = g_string_new (NULL);
-  g_string_append (select, "SELECT ?urn WHERE { ");
-
-  for (l = job->old_datasources; l != NULL; l = l->next)
-    {
-      resource = l->data;
-      g_debug ("Cleaning up old datasource %s", resource);
-
-      if (append_union)
-        g_string_append (select, " UNION ");
-      else
-        append_union = TRUE;
-
-      g_string_append_printf (select, "{ ?urn nie:dataSource \"%s\" }", resource);
-
-      /* also append the datasource itself to the list of resources to delete */
-      g_string_append_printf (update, "<%s> a rdfs:Resource . ", resource);
-    }
-
-  g_string_append (select, " }");
-
-  cursor = tracker_sparql_connection_query (self->priv->connection,
-                                            select->str,
-                                            self->priv->cancellable,
-                                            &error);
-
-  g_string_free (select, TRUE);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
-      return;
-    }
-
-  /* gather all the documents we want to remove */
-  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
-    {
-      resource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
-      g_debug ("Cleaning up resource %s belonging to an old datasource", resource);
-
-      if (resource != NULL)
-        g_string_append_printf (update, "<%s> a rdfs:Resource . ", resource);
-    }
-
-  g_string_append (update, " }");
-  g_object_unref (cursor);
-
-  /* actually remove everything we have to remove */
-  tracker_sparql_connection_update (self->priv->connection,
-                                    update->str,
-                                    G_PRIORITY_DEFAULT,
-                                    self->priv->cancellable,
-                                    &error);
-
-  g_string_free (update, TRUE);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
-      return;
-    }
-}
-
-static gint
-cleanup_datasource_compare (gconstpointer a,
-                            gconstpointer b)
-{
-  GoaObject *object = GOA_OBJECT (a);
-  const gchar *datasource = b;
-  gint res;
-
-  GoaAccount *account;
-  gchar *object_datasource;
-
-  account = goa_object_peek_account (object);
-  g_assert (account != NULL);
-
-  object_datasource = g_strdup_printf ("gd:goa-account:%s", goa_account_get_id (account));
-  res = g_strcmp0 (datasource, object_datasource);
-
-  g_free (object_datasource);
-
-  return res;
-}
-
-static gboolean
-cleanup_job (GIOSchedulerJob *sched_job,
-             GCancellable *cancellable,
-             gpointer user_data)
-{
-  GString *select;
-  GError *error = NULL;
-  TrackerSparqlCursor *cursor;
-  const gchar *datasource;
-  GList *element;
-  CleanupJob *job = user_data;
-  GdGDataMiner *self = job->self;
-
-  /* find all our datasources in the tracker DB */
-  select = g_string_new (NULL);
-  g_string_append_printf (select, "SELECT ?datasource WHERE { ?datasource a nie:DataSource . "
-                          "?datasource nao:identifier \"%s\" }", MINER_IDENTIFIER);
-
-  cursor = tracker_sparql_connection_query (self->priv->connection,
-                                            select->str,
-                                            self->priv->cancellable,
-                                            &error);
-  g_string_free (select, TRUE);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
-      goto out;
-    }
-
-  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
-    {
-      /* If the source we found is not in the current list, add
-       * it to the cleanup list.
-       * Note that the objects here in the list might *not* support
-       * documents, in case the switch has been disabled in System Settings.
-       * In fact, we only remove all the account data in case the account
-       * is really removed from the panel.
-       */
-      datasource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
-      element = g_list_find_custom (job->acc_objects, datasource,
-                                    cleanup_datasource_compare);
-
-      if (element == NULL)
-        job->old_datasources = g_list_prepend (job->old_datasources,
-                                               g_strdup (datasource));
-    }
-
-  g_object_unref (cursor);
-
-  /* cleanup the DB */
-  cleanup_job_do_cleanup (job);
-
- out:
-  g_io_scheduler_job_send_to_mainloop_async (sched_job,
-                                             cleanup_old_accounts_done, job, NULL);
-  return FALSE;
-}
-
-static void
-gd_gdata_miner_cleanup_old_accounts (GdGDataMiner *self,
-                                     GList *doc_objects,
-                                     GList *acc_objects)
-{
-  CleanupJob *job = g_slice_new0 (CleanupJob);
-
-  job->self = g_object_ref (self);
-  job->doc_objects = doc_objects;
-  job->acc_objects = acc_objects;
-
-  g_io_scheduler_push_job (cleanup_job, job, NULL,
-                           G_PRIORITY_DEFAULT,
-                           self->priv->cancellable);
-}
-
-static void
-client_ready_cb (GObject *source,
-                 GAsyncResult *res,
-                 gpointer user_data)
-{
-  GdGDataMiner *self = user_data;
-  GoaDocuments *documents;
-  GoaAccount *account;
-  GoaObject *object;
-  const gchar *provider_type;
-  GError *error = NULL;
-  GList *accounts, *doc_objects, *acc_objects, *l;
-
-  self->priv->client = goa_client_new_finish (res, &error);
-
-  if (error != NULL)
-    {
-      gd_gdata_miner_complete_error (self, error);
-      return;
-    }
-
-  doc_objects = NULL;
-  acc_objects = NULL;
-
-  accounts = goa_client_get_accounts (self->priv->client);
-  for (l = accounts; l != NULL; l = l->next)
-    {
-      object = l->data;
-
-      account = goa_object_peek_account (object);
-      if (account == NULL)
-        continue;
-
-      provider_type = goa_account_get_provider_type (account);
-      if (g_strcmp0 (provider_type, "google") != 0)
-        continue;
-
-      acc_objects = g_list_append (acc_objects, g_object_ref (object));
-
-      documents = goa_object_peek_documents (object);
-      if (documents == NULL)
-        continue;
-
-      doc_objects = g_list_append (doc_objects, g_object_ref (object));
-    }
-
-  g_list_free_full (accounts, g_object_unref);
-
-  gd_gdata_miner_cleanup_old_accounts (self, doc_objects, acc_objects);
-}
-
-static void
-sparql_connection_ready_cb (GObject *object,
-                            GAsyncResult *res,
-                            gpointer user_data)
-{
-  GError *error = NULL;
-  GdGDataMiner *self = user_data;
-
-  self->priv->connection = tracker_sparql_connection_get_finish (res, &error);
-
-  if (error != NULL)
-    {
-      gd_gdata_miner_complete_error (self, error);
-      return;
-    }
-
-  goa_client_new (self->priv->cancellable, client_ready_cb, self);
-}
-
-static void
-gd_gdata_miner_dispose (GObject *object)
-{
-  GdGDataMiner *self = GD_GDATA_MINER (object);
+  GDataGoaAuthorizer *authorizer;
+  GDataDocumentsService *service;
 
-  if (self->priv->pending_jobs != NULL)
-    {
-      g_list_free_full (self->priv->pending_jobs,
-                        (GDestroyNotify) account_miner_job_free);
-      self->priv->pending_jobs = NULL;
-    }
+  authorizer = gdata_goa_authorizer_new (object);
+  service = gdata_documents_service_new (GDATA_AUTHORIZER (authorizer));
 
-  g_clear_object (&self->priv->client);
-  g_clear_object (&self->priv->connection);
-  g_clear_object (&self->priv->cancellable);
-  g_clear_object (&self->priv->result);
+  /* the service takes ownership of the authorizer */
+  g_object_unref (authorizer);
 
-  G_OBJECT_CLASS (gd_gdata_miner_parent_class)->dispose (object);
+  return G_OBJECT (service);
 }
 
 static void
-gd_gdata_miner_init (GdGDataMiner *self)
+gd_gdata_miner_init (GdGDataMiner *miner)
 {
-  self->priv =
-    G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_GDATA_MINER, GdGDataMinerPrivate);
 }
 
 static void
 gd_gdata_miner_class_init (GdGDataMinerClass *klass)
 {
-  GObjectClass *oclass = G_OBJECT_CLASS (klass);
-
-  oclass->dispose = gd_gdata_miner_dispose;
-
-  g_type_class_add_private (klass, sizeof (GdGDataMinerPrivate));
-}
-
-GdGDataMiner *
-gd_gdata_miner_new (void)
-{
-  return g_object_new (GD_TYPE_GDATA_MINER, NULL);
-}
-
-void
-gd_gdata_miner_refresh_db_async (GdGDataMiner *self,
-                                 GCancellable *cancellable,
-                                 GAsyncReadyCallback callback,
-                                 gpointer user_data)
-{
-  self->priv->result = 
-    g_simple_async_result_new (G_OBJECT (self),
-                               callback, user_data,
-                               gd_gdata_miner_refresh_db_async);
-  self->priv->cancellable = 
-    (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
-
-  tracker_sparql_connection_get_async (self->priv->cancellable,
-                                       sparql_connection_ready_cb, self);
-}
-
-gboolean
-gd_gdata_miner_refresh_db_finish (GdGDataMiner *self,
-                                  GAsyncResult *res,
-                                  GError **error)
-{
-  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
-
-  g_assert (g_simple_async_result_is_valid (res, G_OBJECT (self),
-                                            gd_gdata_miner_refresh_db_async));
+  GdMinerClass *miner_class = GD_MINER_CLASS (klass);
 
-  if (g_simple_async_result_propagate_error (simple_res, error))
-    return FALSE;
+  miner_class->goa_provider_type = "google";
+  miner_class->miner_identifier = MINER_IDENTIFIER;
 
-  return TRUE;
+  miner_class->create_service = create_service;
+  miner_class->query = query_gdata;
 }
diff --git a/src/miner/gd-gdata-miner.h b/src/miner/gd-gdata-miner.h
index 3e5b6b9..e42c774 100644
--- a/src/miner/gd-gdata-miner.h
+++ b/src/miner/gd-gdata-miner.h
@@ -22,8 +22,8 @@
 #ifndef __GD_GDATA_MINER_H__
 #define __GD_GDATA_MINER_H__
 
-#include <libtracker-miner/tracker-miner.h>
 #include <gio/gio.h>
+#include "gd-miner.h"
 
 G_BEGIN_DECLS
 
@@ -54,27 +54,17 @@ typedef struct _GdGDataMinerClass GdGDataMinerClass;
 typedef struct _GdGDataMinerPrivate GdGDataMinerPrivate;
 
 struct _GdGDataMiner {
-  GObject parent;
+  GdMiner parent;
 
   GdGDataMinerPrivate *priv;
 };
 
 struct _GdGDataMinerClass {
-  GObjectClass parent_class;
+  GdMinerClass parent_class;
 };
 
 GType gd_gdata_miner_get_type(void);
 
-GdGDataMiner * gd_gdata_miner_new (void);
-
-void           gd_gdata_miner_refresh_db_async (GdGDataMiner *self,
-                                                GCancellable *cancellable,
-                                                GAsyncReadyCallback callback,
-                                                gpointer user_data);
-gboolean       gd_gdata_miner_refresh_db_finish (GdGDataMiner *self,
-                                                 GAsyncResult *res,
-                                                 GError **error);
-
 G_END_DECLS
 
 #endif /* __GD_GDATA_MINER_H__ */
diff --git a/src/miner/gd-miner.c b/src/miner/gd-miner.c
new file mode 100644
index 0000000..8c17ea9
--- /dev/null
+++ b/src/miner/gd-miner.c
@@ -0,0 +1,656 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ * Author: Jasper St. Pierre <jstpierre mecheye net>
+ *
+ */
+
+#include "config.h"
+
+#include "gd-miner.h"
+
+G_DEFINE_TYPE (GdMiner, gd_miner, G_TYPE_OBJECT)
+
+struct _GdMinerPrivate {
+  GoaClient *client;
+  TrackerSparqlConnection *connection;
+
+  GCancellable *cancellable;
+  GSimpleAsyncResult *result;
+
+  GList *pending_jobs;
+};
+
+static void
+gd_account_miner_job_free (GdAccountMinerJob *job)
+{
+  if (job->miner_cancellable_id != 0)
+    g_cancellable_disconnect (job->miner->priv->cancellable,
+                              job->miner_cancellable_id);
+
+  g_clear_object (&job->service);
+  g_clear_object (&job->miner);
+  g_clear_object (&job->account);
+  g_clear_object (&job->async_result);
+
+  g_hash_table_unref (job->previous_resources);
+
+  g_slice_free (GdAccountMinerJob, job);
+}
+
+static void
+gd_miner_dispose (GObject *object)
+{
+  GdMiner *self = GD_MINER (object);
+
+  if (self->priv->pending_jobs != NULL)
+    {
+      g_list_free_full (self->priv->pending_jobs,
+                        (GDestroyNotify) gd_account_miner_job_free);
+      self->priv->pending_jobs = NULL;
+    }
+
+  g_clear_object (&self->priv->client);
+  g_clear_object (&self->priv->connection);
+  g_clear_object (&self->priv->cancellable);
+  g_clear_object (&self->priv->result);
+
+  G_OBJECT_CLASS (gd_miner_parent_class)->dispose (object);
+}
+
+static void
+gd_miner_init (GdMiner *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MINER, GdMinerPrivate);
+}
+
+static void
+gd_miner_class_init (GdMinerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->dispose = gd_miner_dispose;
+
+  g_type_class_add_private (klass, sizeof (GdMinerPrivate));
+}
+
+static void
+gd_miner_complete_error (GdMiner *self,
+                         GError *error)
+{
+  g_assert (self->priv->result != NULL);
+
+  g_simple_async_result_take_error (self->priv->result, error);
+  g_simple_async_result_complete_in_idle (self->priv->result);
+}
+
+static void
+gd_miner_check_pending_jobs (GdMiner *self)
+{
+  if (g_list_length (self->priv->pending_jobs) == 0)
+    g_simple_async_result_complete_in_idle (self->priv->result);
+}
+
+static void
+gd_account_miner_job_ensure_datasource (GdAccountMinerJob *job,
+                                        GError **error)
+{
+  GString *datasource_insert;
+
+  datasource_insert = g_string_new (NULL);
+  g_string_append_printf (datasource_insert,
+                          "INSERT OR REPLACE INTO <%s> {"
+                          "  <%s> a nie:DataSource ; nao:identifier \"%s\""
+                          "}",
+                          job->datasource_urn,
+                          job->datasource_urn,
+                          GD_MINER_GET_CLASS (job->miner)->miner_identifier);
+
+  tracker_sparql_connection_update (job->connection,
+                                    datasource_insert->str,
+                                    G_PRIORITY_DEFAULT,
+                                    job->cancellable,
+                                    error);
+
+  g_string_free (datasource_insert, TRUE);
+}
+
+static void
+gd_account_miner_job_query_existing (GdAccountMinerJob *job,
+                                     GError **error)
+{
+  GString *select;
+  TrackerSparqlCursor *cursor;
+
+  select = g_string_new (NULL);
+  g_string_append_printf (select,
+                          "SELECT ?urn nao:identifier(?urn) WHERE { ?urn nie:dataSource <gd:goa-account:%s> }",
+                          goa_account_get_id (job->account));
+
+  cursor = tracker_sparql_connection_query (job->connection,
+                                            select->str,
+                                            job->cancellable,
+                                            error);
+  g_string_free (select, TRUE);
+
+  if (cursor == NULL)
+    return;
+
+  while (tracker_sparql_cursor_next (cursor, job->cancellable, error))
+    {
+      g_hash_table_insert (job->previous_resources,
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)),
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)));
+    }
+
+  g_object_unref (cursor);
+}
+
+static void
+previous_resources_cleanup_foreach (gpointer key,
+                                    gpointer value,
+                                    gpointer user_data)
+{
+  const gchar *resource = value;
+  GString *delete = user_data;
+
+  g_string_append_printf (delete, "<%s> a rdfs:Resource . ", resource);
+}
+
+static void
+gd_account_miner_job_cleanup_previous (GdAccountMinerJob *job,
+                                       GError **error)
+{
+  GString *delete;
+
+  delete = g_string_new (NULL);
+  g_string_append (delete, "DELETE { ");
+
+  /* the resources left here are those who were in the database,
+   * but were not found during the query; remove them from the database.
+   */
+  g_hash_table_foreach (job->previous_resources,
+                        previous_resources_cleanup_foreach,
+                        delete);
+
+  g_string_append (delete, "}");
+
+  tracker_sparql_connection_update (job->connection,
+                                    delete->str,
+                                    G_PRIORITY_DEFAULT,
+                                    job->cancellable,
+                                    error);
+
+  g_string_free (delete, TRUE);
+}
+
+static void
+gd_account_miner_job_query (GdAccountMinerJob *job,
+                            GError **error)
+{
+  GdMinerClass *miner_class = GD_MINER_GET_CLASS (job->miner);
+
+  miner_class->query (job, error);
+}
+
+static gboolean
+gd_account_miner_job (GIOSchedulerJob *sched_job,
+                      GCancellable *cancellable,
+                      gpointer user_data)
+{
+  GdAccountMinerJob *job = user_data;
+  GError *error = NULL;
+
+  gd_account_miner_job_ensure_datasource (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  gd_account_miner_job_query_existing (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  gd_account_miner_job_query (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  gd_account_miner_job_cleanup_previous (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+ out:
+  if (error != NULL)
+    g_simple_async_result_take_error (job->async_result, error);
+
+  g_simple_async_result_complete_in_idle (job->async_result);
+
+  return FALSE;
+}
+
+static void
+gd_account_miner_job_process_async (GdAccountMinerJob *job,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+  g_assert (job->async_result == NULL);
+
+  job->async_result = g_simple_async_result_new (NULL, callback, user_data,
+                                                 gd_account_miner_job_process_async);
+  g_simple_async_result_set_op_res_gpointer (job->async_result, job, NULL);
+
+  g_io_scheduler_push_job (gd_account_miner_job, job, NULL,
+                           G_PRIORITY_DEFAULT,
+                           job->cancellable);
+}
+
+static gboolean
+gd_account_miner_job_process_finish (GAsyncResult *res,
+                                     GError **error)
+{
+  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_assert (g_simple_async_result_is_valid (res, NULL,
+                                            gd_account_miner_job_process_async));
+
+  if (g_simple_async_result_propagate_error (simple_res, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+miner_cancellable_cancelled_cb (GCancellable *cancellable,
+                                gpointer user_data)
+{
+  GdAccountMinerJob *job = user_data;
+
+  /* forward the cancel signal to the ongoing job */
+  g_cancellable_cancel (job->cancellable);
+}
+
+static GdAccountMinerJob *
+gd_account_miner_job_new (GdMiner *self,
+                          GoaObject *object)
+{
+  GdAccountMinerJob *retval;
+  GoaAccount *account;
+  GdMinerClass *miner_class = GD_MINER_GET_CLASS (self);
+
+  account = goa_object_get_account (object);
+  g_assert (account != NULL);
+
+  retval = g_slice_new0 (GdAccountMinerJob);
+  retval->miner = g_object_ref (self);
+  retval->cancellable = g_cancellable_new ();
+  retval->account = account;
+  retval->connection = self->priv->connection;
+  retval->previous_resources =
+    g_hash_table_new_full (g_str_hash, g_str_equal,
+                           (GDestroyNotify) g_free, (GDestroyNotify) g_free);
+
+  if (self->priv->cancellable != NULL)
+      retval->miner_cancellable_id =
+        g_cancellable_connect (self->priv->cancellable,
+                               G_CALLBACK (miner_cancellable_cancelled_cb),
+                               retval, NULL);
+
+  retval->service = miner_class->create_service (self, object);
+  retval->datasource_urn = g_strdup_printf ("gd:goa-account:%s",
+                                            goa_account_get_id (retval->account));
+
+  return retval;
+}
+
+static void
+miner_job_process_ready_cb (GObject *source,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+  GdAccountMinerJob *job = user_data;
+  GdMiner *self = job->miner;
+  GError *error = NULL;
+
+  gd_account_miner_job_process_finish (res, &error);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while refreshing account %s: %s",
+                  goa_account_get_id (job->account), error->message);
+
+      g_error_free (error);
+    }
+
+  self->priv->pending_jobs = g_list_remove (self->priv->pending_jobs,
+                                            job);
+  gd_account_miner_job_free (job);
+
+  gd_miner_check_pending_jobs (self);
+}
+
+static void
+gd_miner_setup_account (GdMiner *self,
+                        GoaObject *object)
+{
+  GdAccountMinerJob *job;
+
+  job = gd_account_miner_job_new (self, object);
+  self->priv->pending_jobs = g_list_prepend (self->priv->pending_jobs, job);
+
+  gd_account_miner_job_process_async (job, miner_job_process_ready_cb, job);
+}
+
+typedef struct {
+  GdMiner *self;
+  GList *doc_objects;
+  GList *acc_objects;
+  GList *old_datasources;
+} CleanupJob;
+
+static gboolean
+cleanup_old_accounts_done (gpointer data)
+{
+  CleanupJob *job = data;
+  GList *l;
+  GoaObject *object;
+  GdMiner *self = job->self;
+
+  /* now setup all the current accounts */
+  for (l = job->doc_objects; l != NULL; l = l->next)
+    {
+      object = l->data;
+      gd_miner_setup_account (self, object);
+
+      g_object_unref (object);
+    }
+
+  if (job->doc_objects != NULL)
+    {
+      g_list_free (job->doc_objects);
+      job->doc_objects = NULL;
+    }
+
+  if (job->acc_objects != NULL)
+    {
+      g_list_free_full (job->acc_objects, g_object_unref);
+      job->acc_objects = NULL;
+    }
+
+  if (job->old_datasources != NULL)
+    {
+      g_list_free_full (job->old_datasources, g_free);
+      job->old_datasources = NULL;
+    }
+
+  gd_miner_check_pending_jobs (self);
+
+  g_clear_object (&job->self);
+  g_slice_free (CleanupJob, job);
+
+  return FALSE;
+}
+
+static void
+cleanup_job_do_cleanup (CleanupJob *job)
+{
+  GdMiner *self = job->self;
+  GList *l;
+  GString *update;
+  GError *error = NULL;
+
+  if (job->old_datasources == NULL)
+    return;
+
+  update = g_string_new (NULL);
+
+  for (l = job->old_datasources; l != NULL; l = l->next)
+    {
+      const gchar *resource;
+
+      resource = l->data;
+      g_debug ("Cleaning up old datasource %s", resource);
+
+      g_string_append_printf (update,
+                              "DELETE {"
+                              "  ?u a rdfs:Resource"
+                              "} WHERE {"
+                              "  GRAPH <%s> {"
+                              "    ?u a rdfs:Resource"
+                              "  }"
+                              "}",
+                              resource);
+    }
+
+  tracker_sparql_connection_update (self->priv->connection,
+                                    update->str,
+                                    G_PRIORITY_DEFAULT,
+                                    self->priv->cancellable,
+                                    &error);
+  g_string_free (update, TRUE);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
+      g_error_free (error);
+    }
+}
+
+static gint
+cleanup_datasource_compare (gconstpointer a,
+                            gconstpointer b)
+{
+  GoaObject *object = GOA_OBJECT (a);
+  const gchar *datasource = b;
+  gint res;
+
+  GoaAccount *account;
+  gchar *object_datasource;
+
+  account = goa_object_peek_account (object);
+  g_assert (account != NULL);
+
+  object_datasource = g_strdup_printf ("gd:goa-account:%s", goa_account_get_id (account));
+  res = g_strcmp0 (datasource, object_datasource);
+
+  g_free (object_datasource);
+
+  return res;
+}
+
+static gboolean
+cleanup_job (GIOSchedulerJob *sched_job,
+             GCancellable *cancellable,
+             gpointer user_data)
+{
+  GString *select;
+  GError *error = NULL;
+  TrackerSparqlCursor *cursor;
+  const gchar *datasource;
+  GList *element;
+  CleanupJob *job = user_data;
+  GdMiner *self = job->self;
+
+  /* find all our datasources in the tracker DB */
+  select = g_string_new (NULL);
+  g_string_append_printf (select, "SELECT ?datasource WHERE { ?datasource a nie:DataSource . "
+                          "?datasource nao:identifier \"%s\" }",
+                          GD_MINER_GET_CLASS (self)->miner_identifier);
+
+  cursor = tracker_sparql_connection_query (self->priv->connection,
+                                            select->str,
+                                            self->priv->cancellable,
+                                            &error);
+  g_string_free (select, TRUE);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
+      goto out;
+    }
+
+  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
+    {
+      /* If the source we found is not in the current list, add
+       * it to the cleanup list.
+       * Note that the objects here in the list might *not* support
+       * documents, in case the switch has been disabled in System Settings.
+       * In fact, we only remove all the account data in case the account
+       * is really removed from the panel.
+       */
+      datasource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+      element = g_list_find_custom (job->acc_objects, datasource,
+                                    cleanup_datasource_compare);
+
+      if (element == NULL)
+        job->old_datasources = g_list_prepend (job->old_datasources,
+                                               g_strdup (datasource));
+    }
+
+  g_object_unref (cursor);
+
+  /* cleanup the DB */
+  cleanup_job_do_cleanup (job);
+
+ out:
+  g_io_scheduler_job_send_to_mainloop_async (sched_job,
+                                             cleanup_old_accounts_done, job, NULL);
+  return FALSE;
+}
+
+static void
+gd_miner_cleanup_old_accounts (GdMiner *self,
+                               GList *doc_objects,
+                               GList *acc_objects)
+{
+  CleanupJob *job = g_slice_new0 (CleanupJob);
+
+  job->self = g_object_ref (self);
+  job->doc_objects = doc_objects;
+  job->acc_objects = acc_objects;
+
+  g_io_scheduler_push_job (cleanup_job, job, NULL,
+                           G_PRIORITY_DEFAULT,
+                           self->priv->cancellable);
+}
+
+static void
+client_ready_cb (GObject *source,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+  GdMiner *self = user_data;
+  GoaDocuments *documents;
+  GoaAccount *account;
+  GoaObject *object;
+  const gchar *provider_type;
+  GError *error = NULL;
+  GList *accounts, *doc_objects, *acc_objects, *l;
+  GdMinerClass *miner_class = GD_MINER_GET_CLASS (self);
+
+  self->priv->client = goa_client_new_finish (res, &error);
+
+  if (error != NULL)
+    {
+      gd_miner_complete_error (self, error);
+      return;
+    }
+
+  doc_objects = NULL;
+  acc_objects = NULL;
+
+  accounts = goa_client_get_accounts (self->priv->client);
+  for (l = accounts; l != NULL; l = l->next)
+    {
+      object = l->data;
+
+      account = goa_object_peek_account (object);
+      if (account == NULL)
+        continue;
+
+      provider_type = goa_account_get_provider_type (account);
+      if (g_strcmp0 (provider_type, miner_class->goa_provider_type) != 0)
+        continue;
+
+      acc_objects = g_list_append (acc_objects, g_object_ref (object));
+
+      documents = goa_object_peek_documents (object);
+      if (documents == NULL)
+        continue;
+
+      doc_objects = g_list_append (doc_objects, g_object_ref (object));
+    }
+
+  g_list_free_full (accounts, g_object_unref);
+
+  gd_miner_cleanup_old_accounts (self, doc_objects, acc_objects);
+}
+
+static void
+sparql_connection_ready_cb (GObject *object,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+  GError *error = NULL;
+  GdMiner *self = user_data;
+
+  self->priv->connection = tracker_sparql_connection_get_finish (res, &error);
+
+  if (error != NULL)
+    {
+      gd_miner_complete_error (self, error);
+      return;
+    }
+
+  goa_client_new (self->priv->cancellable, client_ready_cb, self);
+}
+
+void
+gd_miner_refresh_db_async (GdMiner *self,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+  self->priv->result =
+    g_simple_async_result_new (G_OBJECT (self),
+                               callback, user_data,
+                               gd_miner_refresh_db_async);
+  self->priv->cancellable =
+    (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
+
+  tracker_sparql_connection_get_async (self->priv->cancellable,
+                                       sparql_connection_ready_cb, self);
+}
+
+gboolean
+gd_miner_refresh_db_finish (GdMiner *self,
+                            GAsyncResult *res,
+                            GError **error)
+{
+  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_assert (g_simple_async_result_is_valid (res, G_OBJECT (self),
+                                            gd_miner_refresh_db_async));
+
+  if (g_simple_async_result_propagate_error (simple_res, error))
+    return FALSE;
+
+  return TRUE;
+}
diff --git a/src/miner/gd-miner.h b/src/miner/gd-miner.h
new file mode 100644
index 0000000..c61135a
--- /dev/null
+++ b/src/miner/gd-miner.h
@@ -0,0 +1,97 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Jasper St. Pierre <jstpierre mecheye net>
+ *
+ */
+
+
+#ifndef __GD_MINER_H__
+#define __GD_MINER_H__
+
+#include <libtracker-miner/tracker-miner.h>
+#include <glib-object.h>
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+#include "gd-miner-tracker.h"
+#include "gd-utils.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MINER               (gd_miner_get_type ())
+#define GD_MINER(obj)                           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_MINER, GdMiner))
+#define GD_MINER_CLASS(klass)                   (G_TYPE_CHECK_CLASS_CAST ((klass),  GD_TYPE_MINER, GdMinerClass))
+#define GD_IS_MINER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_MINER))
+#define GD_IS_MINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GD_TYPE_MINER))
+#define GD_MINER_GET_CLASS(obj)                 (G_TYPE_INSTANCE_GET_CLASS ((obj),  GD_TYPE_MINER, GdMinerClass))
+
+typedef struct _GdMiner        GdMiner;
+typedef struct _GdMinerClass   GdMinerClass;
+typedef struct _GdMinerPrivate GdMinerPrivate;
+
+typedef struct {
+  GdMiner *miner;
+  TrackerSparqlConnection *connection; /* borrowed from GdMiner */
+  gulong miner_cancellable_id;
+
+  GoaAccount *account;
+  GObject *service;
+  GSimpleAsyncResult *async_result;
+  GCancellable *cancellable;
+
+  GHashTable *previous_resources;
+  gchar *datasource_urn;
+} GdAccountMinerJob;
+
+struct _GdMiner
+{
+  GObject parent;
+
+  GdMinerPrivate *priv;
+};
+
+struct _GdMinerClass
+{
+  GObjectClass parent_class;
+
+  char *goa_provider_type;
+  char *miner_identifier;
+
+  GObject * (*create_service) (GdMiner *self,
+                               GoaObject *object);
+
+  void (*query) (GdAccountMinerJob *job,
+                 GError **error);
+};
+
+GType gd_miner_get_type (void);
+
+void gd_miner_refresh_db_async (GdMiner *self,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data);
+
+gboolean gd_miner_refresh_db_finish (GdMiner *self,
+                                     GAsyncResult *res,
+                                     GError **error);
+
+G_END_DECLS
+
+#endif /* __GD_MINER_H__ */
diff --git a/src/miner/gd-zpj-miner.c b/src/miner/gd-zpj-miner.c
index 280da22..976e2c3 100644
--- a/src/miner/gd-zpj-miner.c
+++ b/src/miner/gd-zpj-miner.c
@@ -22,146 +22,15 @@
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <goa/goa.h>
 #include <zpj/zpj.h>
-#include <unistd.h>
 
 #include "gd-zpj-miner.h"
-#include "gd-miner-tracker.h"
-#include "gd-utils.h"
 
 #define MINER_IDENTIFIER "gd:zpj:miner:30058620-777c-47a3-a19c-a6cdf4a315c4"
 
-G_DEFINE_TYPE (GdZpjMiner, gd_zpj_miner, G_TYPE_OBJECT)
-
-struct _GdZpjMinerPrivate {
-  GoaClient *client;
-  TrackerSparqlConnection *connection;
-
-  GCancellable *cancellable;
-  GSimpleAsyncResult *result;
-
-  GList *pending_jobs;
-};
-
-typedef struct {
-  GdZpjMiner *self;
-  TrackerSparqlConnection *connection; /* borrowed from GdZpjMiner */
-  gulong miner_cancellable_id;
-
-  GoaAccount *account;
-  ZpjSkydrive *service;
-  GSimpleAsyncResult *async_result;
-  GCancellable *cancellable;
-
-  GHashTable *previous_resources;
-  gchar *datasource_urn;
-} AccountMinerJob;
-
-static void
-miner_cancellable_cancelled_cb (GCancellable *cancellable,
-                                gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-
-  /* forward the cancel signal to the ongoing job */
-  g_cancellable_cancel (job->cancellable);
-}
-
-static void
-account_miner_job_free (AccountMinerJob *job)
-{
-  if (job->miner_cancellable_id != 0)
-    g_cancellable_disconnect (job->self->priv->cancellable,
-                              job->miner_cancellable_id);
-
-  g_clear_object (&job->service);
-  g_clear_object (&job->self);
-  g_clear_object (&job->account);
-  g_clear_object (&job->async_result);
-
-  g_hash_table_unref (job->previous_resources);
-  g_free (job->datasource_urn);
-
-  g_slice_free (AccountMinerJob, job);
-}
-
-static AccountMinerJob *
-account_miner_job_new (GdZpjMiner *self,
-                       GoaObject *object)
-{
-  AccountMinerJob *retval;
-  ZpjGoaAuthorizer *authorizer;
-  GoaAccount *account;
-
-  account = goa_object_get_account (object);
-  g_assert (account != NULL);
-
-  retval = g_slice_new0 (AccountMinerJob);
-  retval->self = g_object_ref (self);
-  retval->cancellable = g_cancellable_new ();
-  retval->account = account;
-  retval->connection = self->priv->connection;
-  retval->previous_resources =
-    g_hash_table_new_full (g_str_hash, g_str_equal,
-                           (GDestroyNotify) g_free, (GDestroyNotify) g_free);
-
-  if (self->priv->cancellable != NULL)
-      retval->miner_cancellable_id =
-        g_cancellable_connect (self->priv->cancellable,
-                               G_CALLBACK (miner_cancellable_cancelled_cb),
-                               retval, NULL);
-
-  authorizer = zpj_goa_authorizer_new (object);
-  retval->service = zpj_skydrive_new (ZPJ_AUTHORIZER (authorizer));
-
-  /* the service takes ownership of the authorizer */
-  g_object_unref (authorizer);
-
-  retval->datasource_urn = g_strconcat ("gd:goa-account:",
-                                        goa_account_get_id (retval->account),
-                                        NULL);
-  return retval;
-}
-
-static void
-previous_resources_cleanup_foreach (gpointer key,
-                                    gpointer value,
-                                    gpointer user_data)
-{
-  const gchar *resource = value;
-  GString *delete = user_data;
-
-  g_string_append_printf (delete, "<%s> a rdfs:Resource . ", resource);
-}
-
-static void
-account_miner_job_cleanup_previous (AccountMinerJob *job,
-                                    GError **error)
-{
-  GString *delete;
-
-  delete = g_string_new (NULL);
-  g_string_append (delete, "DELETE { ");
-
-  /* the resources left here are those who were in the database,
-   * but were not found during the query; remove them from the database.
-   */
-  g_hash_table_foreach (job->previous_resources,
-                        previous_resources_cleanup_foreach,
-                        delete);
-
-  g_string_append (delete, "}");
-
-  tracker_sparql_connection_update (job->connection,
-                                    delete->str,
-                                    G_PRIORITY_DEFAULT,
-                                    job->cancellable,
-                                    error);
-
-  g_string_free (delete, TRUE);
-}
+G_DEFINE_TYPE (GdZpjMiner, gd_zpj_miner, GD_TYPE_MINER)
 
 static gboolean
-account_miner_job_process_entry (AccountMinerJob *job,
+account_miner_job_process_entry (GdAccountMinerJob *job,
                                  ZpjSkydriveEntry *entry,
                                  GError **error)
 {
@@ -324,13 +193,13 @@ account_miner_job_process_entry (AccountMinerJob *job,
 }
 
 static void
-account_miner_job_traverse_folder (AccountMinerJob *job,
+account_miner_job_traverse_folder (GdAccountMinerJob *job,
                                    const gchar *folder_id,
                                    GError **error)
 {
   GList *entries, *l;
 
-  entries = zpj_skydrive_list_folder_id (job->service,
+  entries = zpj_skydrive_list_folder_id (ZPJ_SKYDRIVE (job->service),
                                          folder_id,
                                          job->cancellable,
                                          error);
@@ -368,527 +237,44 @@ account_miner_job_traverse_folder (AccountMinerJob *job,
 }
 
 static void
-account_miner_job_query_zpj (AccountMinerJob *job,
-                             GError **error)
+query_zpj (GdAccountMinerJob *job,
+           GError **error)
 {
   account_miner_job_traverse_folder (job,
                                      ZPJ_SKYDRIVE_FOLDER_SKYDRIVE,
                                      error);
 }
 
-
-static void
-account_miner_job_query_existing (AccountMinerJob *job,
-                                  GError **error)
-{
-  GString *select;
-  TrackerSparqlCursor *cursor;
-
-  select = g_string_new (NULL);
-  g_string_append_printf (select,
-                          "SELECT ?urn nao:identifier(?urn) WHERE { ?urn nie:dataSource <gd:goa-account:%s> }",
-                          goa_account_get_id (job->account));
-
-  cursor = tracker_sparql_connection_query (job->connection,
-                                            select->str,
-                                            job->cancellable,
-                                            error);
-  g_string_free (select, TRUE);
-
-  if (cursor == NULL)
-    return;
-
-  while (tracker_sparql_cursor_next (cursor, job->cancellable, error))
-    {
-      g_hash_table_insert (job->previous_resources,
-                           g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)),
-                           g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)));
-    }
-
-  g_object_unref (cursor);
-}
-
-static void
-account_miner_job_ensure_datasource (AccountMinerJob *job,
-                                     GError **error)
-{
-  GString *datasource_insert;
-
-  datasource_insert = g_string_new (NULL);
-  g_string_append_printf (datasource_insert,
-                          "INSERT OR REPLACE INTO <%s> {"
-                          "  <%s> a nie:DataSource ; nao:identifier \"%s\""
-                          "}",
-                          job->datasource_urn,
-                          job->datasource_urn,
-                          MINER_IDENTIFIER);
-
-  tracker_sparql_connection_update (job->connection,
-                                    datasource_insert->str,
-                                    G_PRIORITY_DEFAULT,
-                                    job->cancellable,
-                                    error);
-
-  g_string_free (datasource_insert, TRUE);
-}
-
-static gboolean
-account_miner_job (GIOSchedulerJob *sched_job,
-                   GCancellable *cancellable,
-                   gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-  GError *error = NULL;
-
-  account_miner_job_ensure_datasource (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_query_existing (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_query_zpj (job, &error);
-
-  if (error != NULL)
-    goto out;
-
-  account_miner_job_cleanup_previous (job, &error);
-
-  if (error != NULL)
-    goto out;
-
- out:
-  if (error != NULL)
-    g_simple_async_result_take_error (job->async_result, error);
-
-  g_simple_async_result_complete_in_idle (job->async_result);
-
-  return FALSE;
-}
-
-static void
-account_miner_job_process_async (AccountMinerJob *job,
-                                 GAsyncReadyCallback callback,
-                                 gpointer user_data)
+static GObject *
+create_service (GdMiner *self,
+                GoaObject *object)
 {
-  g_assert (job->async_result == NULL);
-
-  job->async_result = g_simple_async_result_new (NULL, callback, user_data,
-                                                 account_miner_job_process_async);
-  g_simple_async_result_set_op_res_gpointer (job->async_result, job, NULL);
-
-  g_io_scheduler_push_job (account_miner_job, job, NULL,
-                           G_PRIORITY_DEFAULT,
-                           job->cancellable);
-}
-
-static gboolean
-account_miner_job_process_finish (GAsyncResult *res,
-                                  GError **error)
-{
-  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
-
-  g_assert (g_simple_async_result_is_valid (res, NULL,
-                                            account_miner_job_process_async));
-
-  if (g_simple_async_result_propagate_error (simple_res, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-gd_zpj_miner_complete_error (GdZpjMiner *self,
-                               GError *error)
-{
-  g_assert (self->priv->result != NULL);
-
-  g_simple_async_result_take_error (self->priv->result, error);
-  g_simple_async_result_complete_in_idle (self->priv->result);
-}
-
-static void
-gd_zpj_miner_check_pending_jobs (GdZpjMiner *self)
-{
-  if (g_list_length (self->priv->pending_jobs) == 0)
-    g_simple_async_result_complete_in_idle (self->priv->result);
-}
-
-static void
-miner_job_process_ready_cb (GObject *source,
-                            GAsyncResult *res,
-                            gpointer user_data)
-{
-  AccountMinerJob *job = user_data;
-  GdZpjMiner *self = job->self;
-  GError *error = NULL;
-
-  account_miner_job_process_finish (res, &error);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while refreshing account %s: %s",
-                  goa_account_get_id (job->account), error->message);
-
-      g_error_free (error);
-    }
-
-  self->priv->pending_jobs = g_list_remove (self->priv->pending_jobs,
-                                            job);
-  account_miner_job_free (job);
-
-  gd_zpj_miner_check_pending_jobs (self);
-}
-
-static void
-gd_zpj_miner_setup_account (GdZpjMiner *self,
-                              GoaObject *object)
-{
-  AccountMinerJob *job;
-
-  job = account_miner_job_new (self, object);
-  self->priv->pending_jobs = g_list_prepend (self->priv->pending_jobs, job);
-
-  account_miner_job_process_async (job, miner_job_process_ready_cb, job);
-}
-
-typedef struct {
-  GdZpjMiner *self;
-  GList *doc_objects;
-  GList *acc_objects;
-  GList *old_datasources;
-} CleanupJob;
-
-static gboolean
-cleanup_old_accounts_done (gpointer data)
-{
-  CleanupJob *job = data;
-  GList *l;
-  GoaObject *object;
-  GdZpjMiner *self = job->self;
-
-  /* now setup all the current accounts */
-  for (l = job->doc_objects; l != NULL; l = l->next)
-    {
-      object = l->data;
-      gd_zpj_miner_setup_account (self, object);
-
-      g_object_unref (object);
-    }
-
-  if (job->doc_objects != NULL)
-    {
-      g_list_free (job->doc_objects);
-      job->doc_objects = NULL;
-    }
-
-  if (job->acc_objects != NULL)
-    {
-      g_list_free_full (job->acc_objects, g_object_unref);
-      job->acc_objects = NULL;
-    }
-
-  if (job->old_datasources != NULL)
-    {
-      g_list_free_full (job->old_datasources, g_free);
-      job->old_datasources = NULL;
-    }
-
-  gd_zpj_miner_check_pending_jobs (self);
-
-  g_clear_object (&job->self);
-  g_slice_free (CleanupJob, job);
-
-  return FALSE;
-}
-
-static void
-cleanup_job_do_cleanup (CleanupJob *job)
-{
-  GdZpjMiner *self = job->self;
-  GList *l;
-  GString *update;
-  GError *error = NULL;
-
-  if (job->old_datasources == NULL)
-    return;
-
-  update = g_string_new (NULL);
-
-  for (l = job->old_datasources; l != NULL; l = l->next)
-    {
-      const gchar *resource;
-
-      resource = l->data;
-      g_debug ("Cleaning up old datasource %s", resource);
-
-      g_string_append_printf (update,
-                              "DELETE {"
-                              "  ?u a rdfs:Resource"
-                              "} WHERE {"
-                              "  GRAPH <%s> {"
-                              "    ?u a rdfs:Resource"
-                              "  }"
-                              "}",
-                              resource);
-    }
-
-  tracker_sparql_connection_update (self->priv->connection,
-                                    update->str,
-                                    G_PRIORITY_DEFAULT,
-                                    self->priv->cancellable,
-                                    &error);
-  g_string_free (update, TRUE);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
-      g_error_free (error);
-    }
-}
-
-static gint
-cleanup_datasource_compare (gconstpointer a,
-                            gconstpointer b)
-{
-  GoaObject *object = GOA_OBJECT (a);
-  const gchar *datasource = b;
-  gint res;
-
-  GoaAccount *account;
-  gchar *object_datasource;
-
-  account = goa_object_peek_account (object);
-  g_assert (account != NULL);
-
-  object_datasource = g_strdup_printf ("gd:goa-account:%s", goa_account_get_id (account));
-  res = g_strcmp0 (datasource, object_datasource);
-
-  g_free (object_datasource);
-
-  return res;
-}
-
-static gboolean
-cleanup_job (GIOSchedulerJob *sched_job,
-             GCancellable *cancellable,
-             gpointer user_data)
-{
-  GString *select;
-  GError *error = NULL;
-  TrackerSparqlCursor *cursor;
-  const gchar *datasource;
-  GList *element;
-  CleanupJob *job = user_data;
-  GdZpjMiner *self = job->self;
-
-  /* find all our datasources in the tracker DB */
-  select = g_string_new (NULL);
-  g_string_append_printf (select, "SELECT ?datasource WHERE { ?datasource a nie:DataSource . "
-                          "?datasource nao:identifier \"%s\" }", MINER_IDENTIFIER);
-
-  cursor = tracker_sparql_connection_query (self->priv->connection,
-                                            select->str,
-                                            self->priv->cancellable,
-                                            &error);
-  g_string_free (select, TRUE);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
-      goto out;
-    }
-
-  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
-    {
-      /* If the source we found is not in the current list, add
-       * it to the cleanup list.
-       * Note that the objects here in the list might *not* support
-       * documents, in case the switch has been disabled in System Settings.
-       * In fact, we only remove all the account data in case the account
-       * is really removed from the panel.
-       */
-      datasource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
-      element = g_list_find_custom (job->acc_objects, datasource,
-                                    cleanup_datasource_compare);
-
-      if (element == NULL)
-        job->old_datasources = g_list_prepend (job->old_datasources,
-                                               g_strdup (datasource));
-    }
-
-  g_object_unref (cursor);
-
-  /* cleanup the DB */
-  cleanup_job_do_cleanup (job);
-
- out:
-  g_io_scheduler_job_send_to_mainloop_async (sched_job,
-                                             cleanup_old_accounts_done, job, NULL);
-  return FALSE;
-}
-
-static void
-gd_zpj_miner_cleanup_old_accounts (GdZpjMiner *self,
-                                     GList *doc_objects,
-                                     GList *acc_objects)
-{
-  CleanupJob *job = g_slice_new0 (CleanupJob);
-
-  job->self = g_object_ref (self);
-  job->doc_objects = doc_objects;
-  job->acc_objects = acc_objects;
-
-  g_io_scheduler_push_job (cleanup_job, job, NULL,
-                           G_PRIORITY_DEFAULT,
-                           self->priv->cancellable);
-}
-
-static void
-client_ready_cb (GObject *source,
-                 GAsyncResult *res,
-                 gpointer user_data)
-{
-  GdZpjMiner *self = user_data;
-  GoaDocuments *documents;
-  GoaAccount *account;
-  GoaObject *object;
-  const gchar *provider_type;
-  GError *error = NULL;
-  GList *accounts, *doc_objects, *acc_objects, *l;
-
-  self->priv->client = goa_client_new_finish (res, &error);
-
-  if (error != NULL)
-    {
-      gd_zpj_miner_complete_error (self, error);
-      return;
-    }
-
-  doc_objects = NULL;
-  acc_objects = NULL;
-
-  accounts = goa_client_get_accounts (self->priv->client);
-  for (l = accounts; l != NULL; l = l->next)
-    {
-      object = l->data;
-
-      account = goa_object_peek_account (object);
-      if (account == NULL)
-        continue;
-
-      provider_type = goa_account_get_provider_type (account);
-      if (g_strcmp0 (provider_type, "windows_live") != 0)
-        continue;
-
-      acc_objects = g_list_append (acc_objects, g_object_ref (object));
-
-      documents = goa_object_peek_documents (object);
-      if (documents == NULL)
-        continue;
-
-      doc_objects = g_list_append (doc_objects, g_object_ref (object));
-    }
-
-  g_list_free_full (accounts, g_object_unref);
-
-  gd_zpj_miner_cleanup_old_accounts (self, doc_objects, acc_objects);
-}
-
-static void
-sparql_connection_ready_cb (GObject *object,
-                            GAsyncResult *res,
-                            gpointer user_data)
-{
-  GError *error = NULL;
-  GdZpjMiner *self = user_data;
-
-  self->priv->connection = tracker_sparql_connection_get_finish (res, &error);
-
-  if (error != NULL)
-    {
-      gd_zpj_miner_complete_error (self, error);
-      return;
-    }
-
-  goa_client_new (self->priv->cancellable, client_ready_cb, self);
-}
-
-static void
-gd_zpj_miner_dispose (GObject *object)
-{
-  GdZpjMiner *self = GD_ZPJ_MINER (object);
+  ZpjGoaAuthorizer *authorizer;
+  ZpjSkydrive *service;
 
-  if (self->priv->pending_jobs != NULL)
-    {
-      g_list_free_full (self->priv->pending_jobs,
-                        (GDestroyNotify) account_miner_job_free);
-      self->priv->pending_jobs = NULL;
-    }
+  authorizer = zpj_goa_authorizer_new (object);
+  service = zpj_skydrive_new (ZPJ_AUTHORIZER (authorizer));
 
-  g_clear_object (&self->priv->client);
-  g_clear_object (&self->priv->connection);
-  g_clear_object (&self->priv->cancellable);
-  g_clear_object (&self->priv->result);
+  /* the service takes ownership of the authorizer */
+  g_object_unref (authorizer);
 
-  G_OBJECT_CLASS (gd_zpj_miner_parent_class)->dispose (object);
+  return G_OBJECT (service);
 }
 
 static void
 gd_zpj_miner_init (GdZpjMiner *self)
 {
-  self->priv =
-    G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_ZPJ_MINER, GdZpjMinerPrivate);
+
 }
 
 static void
 gd_zpj_miner_class_init (GdZpjMinerClass *klass)
 {
-  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GdMinerClass *miner_class = GD_MINER_CLASS (klass);
 
-  oclass->dispose = gd_zpj_miner_dispose;
+  miner_class->goa_provider_type = "windows_live";
+  miner_class->miner_identifier = MINER_IDENTIFIER;
 
-  g_type_class_add_private (klass, sizeof (GdZpjMinerPrivate));
-}
-
-GdZpjMiner *
-gd_zpj_miner_new (void)
-{
-  return g_object_new (GD_TYPE_ZPJ_MINER, NULL);
-}
-
-void
-gd_zpj_miner_refresh_db_async (GdZpjMiner *self,
-                               GCancellable *cancellable,
-                               GAsyncReadyCallback callback,
-                               gpointer user_data)
-{
-  self->priv->result =
-    g_simple_async_result_new (G_OBJECT (self),
-                               callback, user_data,
-                               gd_zpj_miner_refresh_db_async);
-  self->priv->cancellable =
-    (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
-
-  tracker_sparql_connection_get_async (self->priv->cancellable,
-                                       sparql_connection_ready_cb, self);
-}
-
-gboolean
-gd_zpj_miner_refresh_db_finish (GdZpjMiner *self,
-                                GAsyncResult *res,
-                                GError **error)
-{
-  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
-
-  g_assert (g_simple_async_result_is_valid (res, G_OBJECT (self),
-                                            gd_zpj_miner_refresh_db_async));
-
-  if (g_simple_async_result_propagate_error (simple_res, error))
-    return FALSE;
-
-  return TRUE;
+  miner_class->create_service = create_service;
+  miner_class->query = query_zpj;
 }
diff --git a/src/miner/gd-zpj-miner.h b/src/miner/gd-zpj-miner.h
index 20b0b2d..2ece166 100644
--- a/src/miner/gd-zpj-miner.h
+++ b/src/miner/gd-zpj-miner.h
@@ -22,8 +22,8 @@
 #ifndef __GD_ZPJ_MINER_H__
 #define __GD_ZPJ_MINER_H__
 
-#include <libtracker-miner/tracker-miner.h>
 #include <gio/gio.h>
+#include "gd-miner.h"
 
 G_BEGIN_DECLS
 
@@ -54,27 +54,17 @@ typedef struct _GdZpjMinerClass GdZpjMinerClass;
 typedef struct _GdZpjMinerPrivate GdZpjMinerPrivate;
 
 struct _GdZpjMiner {
-  GObject parent;
+  GdMiner parent;
 
   GdZpjMinerPrivate *priv;
 };
 
 struct _GdZpjMinerClass {
-  GObjectClass parent_class;
+  GdMinerClass parent_class;
 };
 
 GType gd_zpj_miner_get_type(void);
 
-GdZpjMiner * gd_zpj_miner_new (void);
-
-void           gd_zpj_miner_refresh_db_async (GdZpjMiner *self,
-                                              GCancellable *cancellable,
-                                              GAsyncReadyCallback callback,
-                                              gpointer user_data);
-gboolean       gd_zpj_miner_refresh_db_finish (GdZpjMiner *self,
-                                               GAsyncResult *res,
-                                               GError **error);
-
 G_END_DECLS
 
 #endif /* __GD_ZPJ_MINER_H__ */
diff --git a/src/miner/gdata-miner-main.c b/src/miner/gdata-miner-main.c
index 0a96cc9..8ecde9b 100644
--- a/src/miner/gdata-miner-main.c
+++ b/src/miner/gdata-miner-main.c
@@ -19,229 +19,11 @@
  *
  */
 
-#include <glib-unix.h>
-#include <glib.h>
+#define INSIDE_MINER
+#define MINER_NAME "GDATA"
+#define MINER_TYPE GD_TYPE_GDATA_MINER
+#define MINER_BUS_NAME "org.gnome.Documents.GDataMiner"
+#define MINER_OBJECT_PATH "/org/gnome/Documents/GDataMiner"
 
 #include "gd-gdata-miner.h"
-
-#define BUS_NAME "org.gnome.Documents.GDataMiner"
-#define AUTOQUIT_TIMEOUT 5 /* seconds */
-
-static const gchar introspection_xml[] =
-  "<node>"
-  "  <interface name='org.gnome.Documents.Miner'>"
-  "    <method name='RefreshDB'>"
-  "    </method>"
-  "  </interface>"
-  "</node>";
-
-static GDBusNodeInfo *introspection_data = NULL;
-static GCancellable *cancellable = NULL;
-static GMainLoop *loop = NULL;
-static guint name_owner_id = 0;
-static guint autoquit_id = 0;
-static gboolean refreshing = FALSE;
-
-static gboolean
-autoquit_timeout_cb (gpointer _unused)
-{
-  g_debug ("Timeout reached, quitting...");
-
-  autoquit_id = 0;
-  g_main_loop_quit (loop);
-
-  return FALSE;
-}
-
-static void
-ensure_autoquit_off (void)
-{
-  if (g_getenv ("GDATA_MINER_PERSIST") != NULL)
-    return;
-
-  if (autoquit_id != 0)
-    {
-      g_source_remove (autoquit_id);
-      autoquit_id = 0;
-    }
-}
-
-static void
-ensure_autoquit_on (void)
-{
-  if (g_getenv ("GDATA_MINER_PERSIST") != NULL)
-    return;
-
-  autoquit_id = 
-    g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
-                           autoquit_timeout_cb, NULL);
-}
-
-static gboolean
-signal_handler_cb (gpointer user_data)
-{
-  GMainLoop *loop = user_data;
-
-  if (cancellable != NULL)
-    g_cancellable_cancel (cancellable);
-
-  g_main_loop_quit (loop);
-
-  return FALSE;
-}
-
-static void
-miner_refresh_db_ready_cb (GObject *source,
-                           GAsyncResult *res,
-                           gpointer user_data)
-{
-  GDBusMethodInvocation *invocation = user_data;
-  GError *error = NULL;
-
-  gd_gdata_miner_refresh_db_finish (GD_GDATA_MINER (source), res, &error);
-
-  refreshing = FALSE;
-  ensure_autoquit_on ();
-
-  if (error != NULL)
-    {
-      g_printerr ("Failed to refresh the DB cache: %s\n", error->message);
-      g_dbus_method_invocation_return_gerror (invocation, error);
-    }
-  else
-    {
-      g_dbus_method_invocation_return_value (invocation, NULL);
-    }
-}
-
-static void
-handle_refresh_db (GDBusMethodInvocation *invocation)
-{
-  GdGDataMiner *miner;
-
-  ensure_autoquit_off ();
-
-  /* if we're refreshing already, compress with the current request */
-  if (refreshing)
-    return;
-
-  refreshing = TRUE;
-  cancellable = g_cancellable_new ();
-  miner = gd_gdata_miner_new ();
-
-  gd_gdata_miner_refresh_db_async (miner, cancellable,
-                                   miner_refresh_db_ready_cb, invocation);
-
-  g_object_unref (miner);
-}
-
-static void
-handle_method_call (GDBusConnection       *connection,
-                    const gchar           *sender,
-                    const gchar           *object_path,
-                    const gchar           *interface_name,
-                    const gchar           *method_name,
-                    GVariant              *parameters,
-                    GDBusMethodInvocation *invocation,
-                    gpointer               user_data)
-{
-  if (g_strcmp0 (method_name, "RefreshDB") == 0)
-    handle_refresh_db (g_object_ref (invocation));
-  else
-    g_assert_not_reached ();
-}
-
-static const GDBusInterfaceVTable interface_vtable =
-{
-  handle_method_call,
-  NULL, /* get_property */
-  NULL, /* set_property */
-};
-
-static void
-on_bus_acquired (GDBusConnection *connection,
-                 const gchar *name,
-                 gpointer user_data)
-{
-  GError *error = NULL;
-
-  g_debug ("Connected to the session bus: %s", name);
-
-  g_dbus_connection_register_object (connection,
-                                     "/org/gnome/Documents/GDataMiner",
-                                     introspection_data->interfaces[0],
-                                     &interface_vtable,
-                                     NULL,
-                                     NULL,
-                                     &error);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error exporting object on the session bus: %s",
-                  error->message);
-      g_error_free (error);
-
-      _exit (1);
-    }
-
-  g_debug ("Object exported on the session bus");
-}
-
-static void
-on_name_lost (GDBusConnection *connection,
-              const gchar *name,
-              gpointer user_data)
-{
-  g_debug ("Lost bus name: %s, exiting", name);
-
-  if (cancellable != NULL)
-    g_cancellable_cancel (cancellable);
-
-  name_owner_id = 0;
-}
-
-static void
-on_name_acquired (GDBusConnection *connection,
-                  const gchar *name,
-                  gpointer user_data)
-{
-  g_debug ("Acquired bus name: %s", name);
-}
-
-int
-main (int argc,
-      char **argv)
-{
-  g_type_init ();
-
-  ensure_autoquit_on ();
-  loop = g_main_loop_new (NULL, FALSE);
-
-  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
-			  SIGTERM,
-			  signal_handler_cb,
-			  loop, NULL);
-  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
-			  SIGINT,
-			  signal_handler_cb,
-			  loop, NULL);
-
-  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
-  g_assert (introspection_data != NULL);
-
-  name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
-                                  BUS_NAME,
-                                  G_BUS_NAME_OWNER_FLAGS_NONE,
-                                  on_bus_acquired,
-                                  on_name_acquired,
-                                  on_name_lost,
-                                  NULL, NULL);
-
-  g_main_loop_run (loop);
-  g_main_loop_unref (loop);
-
-  if (name_owner_id != 0)
-    g_bus_unown_name (name_owner_id);
-
-  return 0;
-}
+#include "miner-main.c"
diff --git a/src/miner/miner-main.c b/src/miner/miner-main.c
new file mode 100644
index 0000000..0e2b9cd
--- /dev/null
+++ b/src/miner/miner-main.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef INSIDE_MINER
+#error "miner-main.c is meant to be included, not compiled standalone"
+#endif
+
+#include <glib-unix.h>
+#include <glib.h>
+
+#define AUTOQUIT_TIMEOUT 5 /* seconds */
+
+static const gchar introspection_xml[] =
+  "<node>"
+  "  <interface name='org.gnome.Documents.Miner'>"
+  "    <method name='RefreshDB'>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static GDBusNodeInfo *introspection_data = NULL;
+static GCancellable *cancellable = NULL;
+static GMainLoop *loop = NULL;
+static guint name_owner_id = 0;
+static guint autoquit_id = 0;
+static gboolean refreshing = FALSE;
+
+static gboolean
+autoquit_timeout_cb (gpointer _unused)
+{
+  g_debug ("Timeout reached, quitting...");
+
+  autoquit_id = 0;
+  g_main_loop_quit (loop);
+
+  return FALSE;
+}
+
+static void
+ensure_autoquit_off (void)
+{
+  if (g_getenv (MINER_NAME "MINER_PERSIST") != NULL)
+    return;
+
+  if (autoquit_id != 0)
+    {
+      g_source_remove (autoquit_id);
+      autoquit_id = 0;
+    }
+}
+
+static void
+ensure_autoquit_on (void)
+{
+  if (g_getenv (MINER_NAME "_MINER_PERSIST") != NULL)
+    return;
+
+  autoquit_id =
+    g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
+                           autoquit_timeout_cb, NULL);
+}
+
+static gboolean
+signal_handler_cb (gpointer user_data)
+{
+  GMainLoop *loop = user_data;
+
+  if (cancellable != NULL)
+    g_cancellable_cancel (cancellable);
+
+  g_main_loop_quit (loop);
+
+  return FALSE;
+}
+
+static void
+miner_refresh_db_ready_cb (GObject *source,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+  GDBusMethodInvocation *invocation = user_data;
+  GError *error = NULL;
+
+  gd_miner_refresh_db_finish (GD_MINER (source), res, &error);
+
+  refreshing = FALSE;
+  ensure_autoquit_on ();
+
+  if (error != NULL)
+    {
+      g_printerr ("Failed to refresh the DB cache: %s\n", error->message);
+      g_dbus_method_invocation_return_gerror (invocation, error);
+    }
+  else
+    {
+      g_dbus_method_invocation_return_value (invocation, NULL);
+    }
+}
+
+static void
+handle_refresh_db (GDBusMethodInvocation *invocation)
+{
+  GdMiner *miner;
+
+  ensure_autoquit_off ();
+
+  /* if we're refreshing already, compress with the current request */
+  if (refreshing)
+    return;
+
+  refreshing = TRUE;
+  cancellable = g_cancellable_new ();
+  miner = g_object_new (MINER_TYPE, NULL);
+
+  gd_miner_refresh_db_async (miner, cancellable,
+                             miner_refresh_db_ready_cb, invocation);
+
+  g_object_unref (miner);
+}
+
+static void
+handle_method_call (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+  if (g_strcmp0 (method_name, "RefreshDB") == 0)
+    handle_refresh_db (g_object_ref (invocation));
+  else
+    g_assert_not_reached ();
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+  handle_method_call,
+  NULL, /* get_property */
+  NULL, /* set_property */
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar *name,
+                 gpointer user_data)
+{
+  GError *error = NULL;
+
+  g_debug ("Connected to the session bus: %s", name);
+
+  g_dbus_connection_register_object (connection,
+                                     MINER_OBJECT_PATH,
+                                     introspection_data->interfaces[0],
+                                     &interface_vtable,
+                                     NULL,
+                                     NULL,
+                                     &error);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error exporting object on the session bus: %s",
+                  error->message);
+      g_error_free (error);
+
+      _exit (1);
+    }
+
+  g_debug ("Object exported on the session bus");
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar *name,
+              gpointer user_data)
+{
+  g_debug ("Lost bus name: %s, exiting", name);
+
+  if (cancellable != NULL)
+    g_cancellable_cancel (cancellable);
+
+  name_owner_id = 0;
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+  g_debug ("Acquired bus name: %s", name);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+  g_type_init ();
+
+  ensure_autoquit_on ();
+  loop = g_main_loop_new (NULL, FALSE);
+
+  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
+			  SIGTERM,
+			  signal_handler_cb,
+			  loop, NULL);
+  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
+			  SIGINT,
+			  signal_handler_cb,
+			  loop, NULL);
+
+  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+  g_assert (introspection_data != NULL);
+
+  name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                  MINER_BUS_NAME,
+                                  G_BUS_NAME_OWNER_FLAGS_NONE,
+                                  on_bus_acquired,
+                                  on_name_acquired,
+                                  on_name_lost,
+                                  NULL, NULL);
+
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+
+  if (name_owner_id != 0)
+    g_bus_unown_name (name_owner_id);
+
+  return 0;
+}
diff --git a/src/miner/zpj-miner-main.c b/src/miner/zpj-miner-main.c
index d07e529..8433eef 100644
--- a/src/miner/zpj-miner-main.c
+++ b/src/miner/zpj-miner-main.c
@@ -19,229 +19,11 @@
  *
  */
 
-#include <glib-unix.h>
-#include <glib.h>
+#define INSIDE_MINER
+#define MINER_NAME "ZPJ"
+#define MINER_TYPE GD_TYPE_ZPJ_MINER
+#define MINER_BUS_NAME "org.gnome.Documents.ZpjMiner"
+#define MINER_OBJECT_PATH "/org/gnome/Documents/ZpjMiner"
 
 #include "gd-zpj-miner.h"
-
-#define BUS_NAME "org.gnome.Documents.ZpjMiner"
-#define AUTOQUIT_TIMEOUT 5 /* seconds */
-
-static const gchar introspection_xml[] =
-  "<node>"
-  "  <interface name='org.gnome.Documents.Miner'>"
-  "    <method name='RefreshDB'>"
-  "    </method>"
-  "  </interface>"
-  "</node>";
-
-static GDBusNodeInfo *introspection_data = NULL;
-static GCancellable *cancellable = NULL;
-static GMainLoop *loop = NULL;
-static guint name_owner_id = 0;
-static guint autoquit_id = 0;
-static gboolean refreshing = FALSE;
-
-static gboolean
-autoquit_timeout_cb (gpointer _unused)
-{
-  g_debug ("Timeout reached, quitting...");
-
-  autoquit_id = 0;
-  g_main_loop_quit (loop);
-
-  return FALSE;
-}
-
-static void
-ensure_autoquit_off (void)
-{
-  if (g_getenv ("ZPJ_MINER_PERSIST") != NULL)
-    return;
-
-  if (autoquit_id != 0)
-    {
-      g_source_remove (autoquit_id);
-      autoquit_id = 0;
-    }
-}
-
-static void
-ensure_autoquit_on (void)
-{
-  if (g_getenv ("ZPJ_MINER_PERSIST") != NULL)
-    return;
-
-  autoquit_id =
-    g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
-                           autoquit_timeout_cb, NULL);
-}
-
-static gboolean
-signal_handler_cb (gpointer user_data)
-{
-  GMainLoop *loop = user_data;
-
-  if (cancellable != NULL)
-    g_cancellable_cancel (cancellable);
-
-  g_main_loop_quit (loop);
-
-  return FALSE;
-}
-
-static void
-miner_refresh_db_ready_cb (GObject *source,
-                           GAsyncResult *res,
-                           gpointer user_data)
-{
-  GDBusMethodInvocation *invocation = user_data;
-  GError *error = NULL;
-
-  gd_zpj_miner_refresh_db_finish (GD_ZPJ_MINER (source), res, &error);
-
-  refreshing = FALSE;
-  ensure_autoquit_on ();
-
-  if (error != NULL)
-    {
-      g_printerr ("Failed to refresh the DB cache: %s\n", error->message);
-      g_dbus_method_invocation_return_gerror (invocation, error);
-    }
-  else
-    {
-      g_dbus_method_invocation_return_value (invocation, NULL);
-    }
-}
-
-static void
-handle_refresh_db (GDBusMethodInvocation *invocation)
-{
-  GdZpjMiner *miner;
-
-  ensure_autoquit_off ();
-
-  /* if we're refreshing already, compress with the current request */
-  if (refreshing)
-    return;
-
-  refreshing = TRUE;
-  cancellable = g_cancellable_new ();
-  miner = gd_zpj_miner_new ();
-
-  gd_zpj_miner_refresh_db_async (miner, cancellable,
-                                 miner_refresh_db_ready_cb, invocation);
-
-  g_object_unref (miner);
-}
-
-static void
-handle_method_call (GDBusConnection       *connection,
-                    const gchar           *sender,
-                    const gchar           *object_path,
-                    const gchar           *interface_name,
-                    const gchar           *method_name,
-                    GVariant              *parameters,
-                    GDBusMethodInvocation *invocation,
-                    gpointer               user_data)
-{
-  if (g_strcmp0 (method_name, "RefreshDB") == 0)
-    handle_refresh_db (g_object_ref (invocation));
-  else
-    g_assert_not_reached ();
-}
-
-static const GDBusInterfaceVTable interface_vtable =
-{
-  handle_method_call,
-  NULL, /* get_property */
-  NULL, /* set_property */
-};
-
-static void
-on_bus_acquired (GDBusConnection *connection,
-                 const gchar *name,
-                 gpointer user_data)
-{
-  GError *error = NULL;
-
-  g_debug ("Connected to the session bus: %s", name);
-
-  g_dbus_connection_register_object (connection,
-                                     "/org/gnome/Documents/ZpjMiner",
-                                     introspection_data->interfaces[0],
-                                     &interface_vtable,
-                                     NULL,
-                                     NULL,
-                                     &error);
-
-  if (error != NULL)
-    {
-      g_printerr ("Error exporting object on the session bus: %s",
-                  error->message);
-      g_error_free (error);
-
-      _exit (1);
-    }
-
-  g_debug ("Object exported on the session bus");
-}
-
-static void
-on_name_lost (GDBusConnection *connection,
-              const gchar *name,
-              gpointer user_data)
-{
-  g_debug ("Lost bus name: %s, exiting", name);
-
-  if (cancellable != NULL)
-    g_cancellable_cancel (cancellable);
-
-  name_owner_id = 0;
-}
-
-static void
-on_name_acquired (GDBusConnection *connection,
-                  const gchar *name,
-                  gpointer user_data)
-{
-  g_debug ("Acquired bus name: %s", name);
-}
-
-int
-main (int argc,
-      char **argv)
-{
-  g_type_init ();
-
-  ensure_autoquit_on ();
-  loop = g_main_loop_new (NULL, FALSE);
-
-  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
-			  SIGTERM,
-			  signal_handler_cb,
-			  loop, NULL);
-  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
-			  SIGINT,
-			  signal_handler_cb,
-			  loop, NULL);
-
-  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
-  g_assert (introspection_data != NULL);
-
-  name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
-                                  BUS_NAME,
-                                  G_BUS_NAME_OWNER_FLAGS_NONE,
-                                  on_bus_acquired,
-                                  on_name_acquired,
-                                  on_name_lost,
-                                  NULL, NULL);
-
-  g_main_loop_run (loop);
-  g_main_loop_unref (loop);
-
-  if (name_owner_id != 0)
-    g_bus_unown_name (name_owner_id);
-
-  return 0;
-}
+#include "miner-main.c"



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