1 /*
2  * GNOME Online Miners - crawls through your online content
3  * Copyright (C) 2012 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  *
20  * Author: Cosimo Cecchi <cosimoc@redhat.com>
21  * Author: Jasper St. Pierre <jstpierre@mecheye.net>
22  *
23  */
24 
25 #include "config.h"
26 
27 #include <stdio.h>
28 
29 #include "gom-miner.h"
30 
31 G_DEFINE_TYPE (GomMiner, gom_miner, G_TYPE_OBJECT)
32 
33 struct _GomMinerPrivate {
34   GoaClient *client;
35   GError *client_error;
36 
37   TrackerSparqlConnection *connection;
38   GError *connection_error;
39 
40   gchar *display_name;
41   gchar **index_types;
42 };
43 
44 typedef struct {
45   GomMiner *self;
46   GList *content_objects;
47   GList *acc_objects;
48   GList *old_datasources;
49   GList *pending_jobs;
50 } CleanupJob;
51 
52 typedef struct {
53   GomMiner *self;
54   gchar *account_id;
55   gchar *shared_id;
56   gchar *shared_type;
57   gchar *source_urn;
58   gpointer service;
59 } InsertSharedContentData;
60 
61 static GThreadPool *cleanup_pool;
62 
63 static void cleanup_job (gpointer data, gpointer user_data);
64 
65 static void
gom_account_miner_job_free(GomAccountMinerJob * job)66 gom_account_miner_job_free (GomAccountMinerJob *job)
67 {
68   g_hash_table_unref (job->services);
69   g_clear_object (&job->miner);
70   g_clear_object (&job->account);
71   g_clear_object (&job->connection);
72   g_clear_object (&job->task);
73   g_clear_object (&job->parent_task);
74 
75   g_free (job->datasource_urn);
76   g_free (job->root_element_urn);
77 
78   g_hash_table_unref (job->previous_resources);
79 
80   g_slice_free (GomAccountMinerJob, job);
81 }
82 
83 static void
gom_insert_shared_content_data_free(InsertSharedContentData * data)84 gom_insert_shared_content_data_free (InsertSharedContentData *data)
85 {
86   GOM_MINER_GET_CLASS (data->self)->destroy_service (data->self, data->service);
87 
88   g_object_unref (data->self);
89   g_free (data->account_id);
90   g_free (data->shared_id);
91   g_free (data->shared_type);
92   g_free (data->source_urn);
93 
94   g_slice_free (InsertSharedContentData, data);
95 }
96 
97 static InsertSharedContentData *
gom_insert_shared_content_data_new(GomMiner * self,const gchar * account_id,const gchar * shared_id,const gchar * shared_type,const gchar * source_urn,gpointer service)98 gom_insert_shared_content_data_new (GomMiner *self,
99                                     const gchar *account_id,
100                                     const gchar *shared_id,
101                                     const gchar *shared_type,
102                                     const gchar *source_urn,
103                                     gpointer service)
104 {
105   InsertSharedContentData *retval;
106 
107   retval = g_slice_new0 (InsertSharedContentData);
108   retval->self = g_object_ref (self);
109   retval->account_id = g_strdup (account_id);
110   retval->shared_id = g_strdup (shared_id);
111   retval->shared_type = g_strdup (shared_type);
112   retval->source_urn = g_strdup (source_urn);
113   retval->service = service;
114 
115   return retval;
116 }
117 
118 static void
gom_miner_dispose(GObject * object)119 gom_miner_dispose (GObject *object)
120 {
121   GomMiner *self = GOM_MINER (object);
122 
123   g_clear_object (&self->priv->client);
124   g_clear_object (&self->priv->connection);
125 
126   g_free (self->priv->display_name);
127   g_strfreev (self->priv->index_types);
128   g_clear_error (&self->priv->client_error);
129   g_clear_error (&self->priv->connection_error);
130 
131   G_OBJECT_CLASS (gom_miner_parent_class)->dispose (object);
132 }
133 
134 static void
gom_miner_init_goa(GomMiner * self)135 gom_miner_init_goa (GomMiner *self)
136 {
137   GoaAccount *account;
138   GoaObject *object;
139   const gchar *provider_type;
140   GList *accounts, *l;
141   GomMinerClass *miner_class = GOM_MINER_GET_CLASS (self);
142 
143   self->priv->client = goa_client_new_sync (NULL, &self->priv->client_error);
144 
145   if (self->priv->client_error != NULL)
146     {
147       g_critical ("Unable to create GoaClient: %s - indexing for %s will not work",
148                   self->priv->client_error->message, miner_class->goa_provider_type);
149       return;
150     }
151 
152   accounts = goa_client_get_accounts (self->priv->client);
153   for (l = accounts; l != NULL; l = l->next)
154     {
155       object = l->data;
156 
157       account = goa_object_peek_account (object);
158       if (account == NULL)
159         continue;
160 
161       provider_type = goa_account_get_provider_type (account);
162       if (g_strcmp0 (provider_type, miner_class->goa_provider_type) == 0)
163         {
164           g_free (self->priv->display_name);
165           self->priv->display_name = goa_account_dup_provider_name (account);
166           break;
167         }
168     }
169 
170   g_list_free_full (accounts, g_object_unref);
171 }
172 
173 static void
gom_miner_constructed(GObject * obj)174 gom_miner_constructed (GObject *obj)
175 {
176   GomMiner *self = GOM_MINER (obj);
177 
178   G_OBJECT_CLASS (gom_miner_parent_class)->constructed (obj);
179 
180   gom_miner_init_goa (self);
181 }
182 
183 static void
gom_miner_init(GomMiner * self)184 gom_miner_init (GomMiner *self)
185 {
186   GomMinerClass *klass = GOM_MINER_GET_CLASS (self);
187 
188   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GOM_TYPE_MINER, GomMinerPrivate);
189   self->priv->display_name = g_strdup ("");
190 
191   self->priv->connection = tracker_sparql_connection_get (NULL, &self->priv->connection_error);
192   if (self->priv->connection_error != NULL)
193     {
194       g_critical ("Unable to create TrackerSparqlConnection: %s - indexing for %s will not work",
195                   self->priv->connection_error->message,
196                   klass->goa_provider_type);
197     }
198 }
199 
200 static void
gom_miner_class_init(GomMinerClass * klass)201 gom_miner_class_init (GomMinerClass *klass)
202 {
203   GObjectClass *oclass = G_OBJECT_CLASS (klass);
204 
205   oclass->constructed = gom_miner_constructed;
206   oclass->dispose = gom_miner_dispose;
207 
208   cleanup_pool = g_thread_pool_new (cleanup_job, NULL, 1, FALSE, NULL);
209 
210   g_type_class_add_private (klass, sizeof (GomMinerPrivate));
211 }
212 
213 static void
gom_miner_check_pending_jobs(GTask * task)214 gom_miner_check_pending_jobs (GTask *task)
215 {
216   CleanupJob *cleanup_job;
217 
218   cleanup_job = (CleanupJob *) g_task_get_task_data (task);
219 
220   if (g_list_length (cleanup_job->pending_jobs) > 0)
221     return;
222 
223   g_task_return_boolean (task, TRUE);
224   g_slice_free (CleanupJob, cleanup_job);
225 }
226 
227 static void
gom_miner_ensure_datasource(GomMiner * self,const gchar * datasource_urn,const gchar * root_element_urn,GCancellable * cancellable,GError ** error)228 gom_miner_ensure_datasource (GomMiner *self,
229                              const gchar *datasource_urn,
230                              const gchar *root_element_urn,
231                              GCancellable *cancellable,
232                              GError **error)
233 {
234   GString *datasource_insert;
235   GomMinerClass *klass = GOM_MINER_GET_CLASS (self);
236 
237   datasource_insert = g_string_new (NULL);
238   g_string_append_printf (datasource_insert,
239                           "INSERT OR REPLACE INTO <%s> {"
240                           "  <%s> a nie:DataSource ; nao:identifier \"%s\" . "
241                           "  <%s> a nie:InformationElement ; nie:rootElementOf <%s> ; nie:version \"%d\""
242                           "}",
243                           datasource_urn,
244                           datasource_urn, klass->miner_identifier,
245                           root_element_urn, datasource_urn, klass->version);
246 
247   tracker_sparql_connection_update (self->priv->connection,
248                                     datasource_insert->str,
249                                     G_PRIORITY_DEFAULT,
250                                     cancellable,
251                                     error);
252 
253   g_string_free (datasource_insert, TRUE);
254 }
255 
256 static void
gom_account_miner_job_query_existing(GomAccountMinerJob * job,GError ** error)257 gom_account_miner_job_query_existing (GomAccountMinerJob *job,
258                                       GError **error)
259 {
260   GCancellable *cancellable;
261   GString *select;
262   TrackerSparqlCursor *cursor;
263 
264   cancellable = g_task_get_cancellable (job->task);
265 
266   select = g_string_new (NULL);
267   g_string_append_printf (select,
268                           "SELECT ?urn nao:identifier(?urn) WHERE { ?urn nie:dataSource <%s> }",
269                           job->datasource_urn);
270 
271   cursor = tracker_sparql_connection_query (job->connection,
272                                             select->str,
273                                             cancellable,
274                                             error);
275   g_string_free (select, TRUE);
276 
277   if (cursor == NULL)
278     return;
279 
280   while (tracker_sparql_cursor_next (cursor, cancellable, error))
281     {
282       g_hash_table_insert (job->previous_resources,
283                            g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)),
284                            g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)));
285     }
286 
287   g_object_unref (cursor);
288 }
289 
290 static void
previous_resources_cleanup_foreach(gpointer key,gpointer value,gpointer user_data)291 previous_resources_cleanup_foreach (gpointer key,
292                                     gpointer value,
293                                     gpointer user_data)
294 {
295   const gchar *resource = value;
296   GString *delete = user_data;
297 
298   g_string_append_printf (delete, "<%s> a rdfs:Resource . ", resource);
299 }
300 
301 static void
gom_account_miner_job_cleanup_previous(GomAccountMinerJob * job,GError ** error)302 gom_account_miner_job_cleanup_previous (GomAccountMinerJob *job,
303                                         GError **error)
304 {
305   GCancellable *cancellable;
306   GString *delete;
307 
308   cancellable = g_task_get_cancellable (job->task);
309 
310   delete = g_string_new (NULL);
311   g_string_append (delete, "DELETE { ");
312 
313   /* the resources left here are those who were in the database,
314    * but were not found during the query; remove them from the database.
315    */
316   g_hash_table_foreach (job->previous_resources,
317                         previous_resources_cleanup_foreach,
318                         delete);
319 
320   g_string_append (delete, "}");
321 
322   tracker_sparql_connection_update (job->connection,
323                                     delete->str,
324                                     G_PRIORITY_DEFAULT,
325                                     cancellable,
326                                     error);
327 
328   g_string_free (delete, TRUE);
329 }
330 
331 static void
gom_account_miner_job_query(GomAccountMinerJob * job,GError ** error)332 gom_account_miner_job_query (GomAccountMinerJob *job,
333                              GError **error)
334 {
335   GomMinerClass *miner_class = GOM_MINER_GET_CLASS (job->miner);
336   GCancellable *cancellable;
337 
338   cancellable = g_task_get_cancellable (job->task);
339   miner_class->query (job, job->connection, job->previous_resources, job->datasource_urn, cancellable, error);
340 }
341 
342 static void
gom_account_miner_job(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)343 gom_account_miner_job (GTask *task,
344                        gpointer source_object,
345                        gpointer task_data,
346                        GCancellable *cancellable)
347 {
348   GomAccountMinerJob *job = task_data;
349   GError *error = NULL;
350 
351   gom_miner_ensure_datasource (job->miner, job->datasource_urn, job->root_element_urn, cancellable, &error);
352 
353   if (error != NULL)
354     goto out;
355 
356   gom_account_miner_job_query_existing (job, &error);
357 
358   if (error != NULL)
359     goto out;
360 
361   gom_account_miner_job_query (job, &error);
362 
363   if (error != NULL)
364     goto out;
365 
366   gom_account_miner_job_cleanup_previous (job, &error);
367 
368   if (error != NULL)
369     goto out;
370 
371  out:
372   if (error != NULL)
373     g_task_return_error (job->task, error);
374   else
375     g_task_return_boolean (job->task, TRUE);
376 }
377 
378 static void
gom_account_miner_job_process_async(GomAccountMinerJob * job,GAsyncReadyCallback callback,gpointer user_data)379 gom_account_miner_job_process_async (GomAccountMinerJob *job,
380                                      GAsyncReadyCallback callback,
381                                      gpointer user_data)
382 {
383   GCancellable *cancellable;
384 
385   g_assert (job->task == NULL);
386 
387   cancellable = g_task_get_cancellable (job->parent_task);
388 
389   job->task = g_task_new (NULL, cancellable, callback, user_data);
390   g_task_set_source_tag (job->task, gom_account_miner_job_process_async);
391   g_task_set_task_data (job->task, job, NULL);
392   g_task_run_in_thread (job->task, gom_account_miner_job);
393 }
394 
395 static gboolean
gom_account_miner_job_process_finish(GAsyncResult * res,GError ** error)396 gom_account_miner_job_process_finish (GAsyncResult *res,
397                                       GError **error)
398 {
399   GTask *task;
400 
401   g_assert (g_task_is_valid (res, NULL));
402   task = G_TASK (res);
403 
404   g_assert (g_task_get_source_tag (task) == gom_account_miner_job_process_async);
405 
406   return g_task_propagate_boolean (task, error);
407 }
408 
409 static GomAccountMinerJob *
gom_account_miner_job_new(GomMiner * self,GoaObject * object,GTask * parent_task)410 gom_account_miner_job_new (GomMiner *self,
411                            GoaObject *object,
412                            GTask *parent_task)
413 {
414   GomAccountMinerJob *retval;
415   GoaAccount *account;
416   GomMinerClass *miner_class = GOM_MINER_GET_CLASS (self);
417 
418   account = goa_object_get_account (object);
419   g_assert (account != NULL);
420 
421   retval = g_slice_new0 (GomAccountMinerJob);
422   retval->miner = g_object_ref (self);
423   retval->parent_task = g_object_ref (parent_task);
424   retval->account = account;
425   retval->connection = g_object_ref (self->priv->connection);
426   retval->previous_resources =
427     g_hash_table_new_full (g_str_hash, g_str_equal,
428                            (GDestroyNotify) g_free, (GDestroyNotify) g_free);
429 
430   retval->services = miner_class->create_services (self, object);
431   retval->datasource_urn = g_strdup_printf ("gd:goa-account:%s",
432                                             goa_account_get_id (retval->account));
433   retval->root_element_urn = g_strdup_printf ("gd:goa-account:%s:root-element",
434                                               goa_account_get_id (retval->account));
435 
436   return retval;
437 }
438 
439 static void
miner_job_process_ready_cb(GObject * source,GAsyncResult * res,gpointer user_data)440 miner_job_process_ready_cb (GObject *source,
441                             GAsyncResult *res,
442                             gpointer user_data)
443 {
444   CleanupJob *cleanup_job;
445   GomAccountMinerJob *account_miner_job = user_data;
446   GomMiner *self = account_miner_job->miner;
447   GError *error = NULL;
448 
449   cleanup_job = (CleanupJob *) g_task_get_task_data (account_miner_job->parent_task);
450 
451   gom_account_miner_job_process_finish (res, &error);
452 
453   if (error != NULL)
454     {
455       g_printerr ("Error while refreshing account %s: %s",
456                   goa_account_get_id (account_miner_job->account), error->message);
457 
458       g_error_free (error);
459     }
460 
461   cleanup_job->pending_jobs = g_list_remove (cleanup_job->pending_jobs,
462                                              account_miner_job);
463 
464   gom_miner_check_pending_jobs (account_miner_job->parent_task);
465   gom_account_miner_job_free (account_miner_job);
466 }
467 
468 static void
gom_miner_setup_account(GomMiner * self,GoaObject * object,GTask * task)469 gom_miner_setup_account (GomMiner *self,
470                          GoaObject *object,
471                          GTask *task)
472 {
473   CleanupJob *cleanup_job;
474   GomAccountMinerJob *account_miner_job;
475 
476   cleanup_job = (CleanupJob *) g_task_get_task_data (task);
477 
478   account_miner_job = gom_account_miner_job_new (self, object, task);
479   cleanup_job->pending_jobs = g_list_prepend (cleanup_job->pending_jobs, account_miner_job);
480 
481   gom_account_miner_job_process_async (account_miner_job, miner_job_process_ready_cb, account_miner_job);
482 }
483 
484 static gboolean
cleanup_old_accounts_done(gpointer data)485 cleanup_old_accounts_done (gpointer data)
486 {
487   GTask *task = G_TASK (data);
488   CleanupJob *job;
489   GList *l;
490   GoaObject *object;
491   GomMiner *self;
492 
493   job = (CleanupJob *) g_task_get_task_data (task);
494   self = job->self;
495 
496   /* now setup all the current accounts */
497   for (l = job->content_objects; l != NULL; l = l->next)
498     {
499       object = l->data;
500       gom_miner_setup_account (self, object, task);
501 
502       g_object_unref (object);
503     }
504 
505   if (job->content_objects != NULL)
506     {
507       g_list_free (job->content_objects);
508       job->content_objects = NULL;
509     }
510 
511   if (job->acc_objects != NULL)
512     {
513       g_list_free_full (job->acc_objects, g_object_unref);
514       job->acc_objects = NULL;
515     }
516 
517   if (job->old_datasources != NULL)
518     {
519       g_list_free_full (job->old_datasources, g_free);
520       job->old_datasources = NULL;
521     }
522 
523   gom_miner_check_pending_jobs (task);
524 
525   g_clear_object (&job->self);
526 
527   return FALSE;
528 }
529 
530 static void
cleanup_job_do_cleanup(CleanupJob * job,GCancellable * cancellable)531 cleanup_job_do_cleanup (CleanupJob *job, GCancellable *cancellable)
532 {
533   GomMiner *self = job->self;
534   GList *l;
535   GString *update;
536   GError *error = NULL;
537 
538   if (job->old_datasources == NULL)
539     return;
540 
541   update = g_string_new (NULL);
542 
543   for (l = job->old_datasources; l != NULL; l = l->next)
544     {
545       const gchar *resource;
546 
547       resource = l->data;
548       g_debug ("Cleaning up old datasource %s", resource);
549 
550       g_string_append_printf (update,
551                               "DELETE {"
552                               "  ?u a rdfs:Resource"
553                               "} WHERE {"
554                               "  ?u nie:dataSource <%s>"
555                               "}",
556                               resource);
557     }
558 
559   tracker_sparql_connection_update (self->priv->connection,
560                                     update->str,
561                                     G_PRIORITY_DEFAULT,
562                                     cancellable,
563                                     &error);
564   g_string_free (update, TRUE);
565 
566   if (error != NULL)
567     {
568       g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
569       g_error_free (error);
570     }
571 }
572 
573 static gint
cleanup_datasource_compare(gconstpointer a,gconstpointer b)574 cleanup_datasource_compare (gconstpointer a,
575                             gconstpointer b)
576 {
577   GoaObject *object = GOA_OBJECT (a);
578   const gchar *datasource = b;
579   gint res;
580 
581   GoaAccount *account;
582   gchar *object_datasource;
583 
584   account = goa_object_peek_account (object);
585   g_assert (account != NULL);
586 
587   object_datasource = g_strdup_printf ("gd:goa-account:%s", goa_account_get_id (account));
588   res = g_strcmp0 (datasource, object_datasource);
589 
590   g_free (object_datasource);
591 
592   return res;
593 }
594 
595 static void
cleanup_job(gpointer data,gpointer user_data)596 cleanup_job (gpointer data,
597              gpointer user_data)
598 {
599   GCancellable *cancellable;
600   GSource *source;
601   GString *select;
602   GTask *task = G_TASK (data);
603   GError *error = NULL;
604   TrackerSparqlCursor *cursor;
605   const gchar *datasource, *old_version_str;
606   gint old_version;
607   GList *element;
608   CleanupJob *job;
609   GomMiner *self;
610   GomMinerClass *klass;
611 
612   cancellable = g_task_get_cancellable (task);
613   job = (CleanupJob *) g_task_get_task_data (task);
614   self = job->self;
615   klass = GOM_MINER_GET_CLASS (self);
616 
617   /* find all our datasources in the tracker DB */
618   select = g_string_new (NULL);
619   g_string_append_printf (select, "SELECT ?datasource nie:version(?root) WHERE { "
620                           "?datasource a nie:DataSource . "
621                           "?datasource nao:identifier \"%s\" . "
622                           "OPTIONAL { ?root nie:rootElementOf ?datasource } }",
623                           klass->miner_identifier);
624 
625   cursor = tracker_sparql_connection_query (self->priv->connection,
626                                             select->str,
627                                             cancellable,
628                                             &error);
629   g_string_free (select, TRUE);
630 
631   if (error != NULL)
632     {
633       g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
634       goto out;
635     }
636 
637   while (tracker_sparql_cursor_next (cursor, cancellable, NULL))
638     {
639       /* If the source we found is not in the current list, add
640        * it to the cleanup list.
641        * Note that the objects here in the list might *not* support
642        * documents, in case the switch has been disabled in System Settings.
643        * In fact, we only remove all the account data in case the account
644        * is really removed from the panel.
645        *
646        * Also, cleanup sources for which the version has increased.
647        */
648       datasource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
649       element = g_list_find_custom (job->acc_objects, datasource,
650                                     cleanup_datasource_compare);
651 
652       if (element == NULL)
653         job->old_datasources = g_list_prepend (job->old_datasources,
654                                                g_strdup (datasource));
655 
656       old_version_str = tracker_sparql_cursor_get_string (cursor, 1, NULL);
657       if (old_version_str == NULL)
658         old_version = 1;
659       else
660         sscanf (old_version_str, "%d", &old_version);
661 
662       g_debug ("Stored version: %d - new version %d", old_version, klass->version);
663 
664       if ((element == NULL) || (old_version < klass->version))
665         {
666           job->old_datasources = g_list_prepend (job->old_datasources,
667                                                  g_strdup (datasource));
668         }
669     }
670 
671   g_object_unref (cursor);
672 
673   /* cleanup the DB */
674   cleanup_job_do_cleanup (job, cancellable);
675 
676  out:
677   source = g_idle_source_new ();
678   g_source_set_name (source, "[gnome-online-miners] cleanup_old_accounts_done");
679   g_task_attach_source (task, source, cleanup_old_accounts_done);
680   g_source_unref (source);
681 
682   g_object_unref (task);
683 }
684 
685 static void
gom_miner_cleanup_old_accounts(GomMiner * self,GList * content_objects,GList * acc_objects,GTask * task)686 gom_miner_cleanup_old_accounts (GomMiner *self,
687                                 GList *content_objects,
688                                 GList *acc_objects,
689                                 GTask *task)
690 {
691   CleanupJob *job = g_slice_new0 (CleanupJob);
692 
693   job->self = g_object_ref (self);
694   job->content_objects = content_objects;
695   job->acc_objects = acc_objects;
696 
697   g_task_set_task_data (task, job, NULL);
698   g_thread_pool_push (cleanup_pool, g_object_ref (task), NULL);
699 }
700 
701 static void
gom_miner_refresh_db_real(GomMiner * self,GTask * task)702 gom_miner_refresh_db_real (GomMiner *self, GTask *task)
703 {
704   GoaFiles *files;
705   GoaPhotos *photos;
706   GoaAccount *account;
707   GoaObject *object;
708   const gchar *provider_type;
709   GList *accounts, *content_objects, *acc_objects, *l;
710   GomMinerClass *miner_class = GOM_MINER_GET_CLASS (self);
711   gboolean skip_photos, skip_documents;
712 
713   content_objects = NULL;
714   acc_objects = NULL;
715 
716   accounts = goa_client_get_accounts (self->priv->client);
717   for (l = accounts; l != NULL; l = l->next)
718     {
719       object = l->data;
720 
721       account = goa_object_peek_account (object);
722       if (account == NULL)
723         continue;
724 
725       provider_type = goa_account_get_provider_type (account);
726       if (g_strcmp0 (provider_type, miner_class->goa_provider_type) != 0)
727         continue;
728 
729       acc_objects = g_list_append (acc_objects, g_object_ref (object));
730       skip_photos = skip_documents = TRUE;
731 
732       files = goa_object_peek_files (object);
733       photos = goa_object_peek_photos (object);
734 
735       if (gom_miner_supports_type (self, "photos") && photos != NULL)
736         skip_photos = FALSE;
737 
738       if (gom_miner_supports_type (self, "documents") && files != NULL)
739         skip_documents = FALSE;
740 
741       if (skip_photos && skip_documents)
742         continue;
743 
744       content_objects = g_list_append (content_objects, g_object_ref (object));
745     }
746 
747   g_list_free_full (accounts, g_object_unref);
748 
749   gom_miner_cleanup_old_accounts (self, content_objects, acc_objects, task);
750 }
751 
752 const gchar *
gom_miner_get_display_name(GomMiner * self)753 gom_miner_get_display_name (GomMiner *self)
754 {
755   return self->priv->display_name;
756 }
757 
758 static void
gom_miner_insert_shared_content_in_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)759 gom_miner_insert_shared_content_in_thread_func (GTask *task,
760                                                 gpointer source_object,
761                                                 gpointer task_data,
762                                                 GCancellable *cancellable)
763 {
764   GomMiner *self = GOM_MINER (source_object);
765   GError *error;
766   InsertSharedContentData *data = (InsertSharedContentData *) task_data;
767   gchar *datasource_urn = NULL;
768   gchar *root_element_urn = NULL;
769 
770   datasource_urn = g_strdup_printf ("gd:goa-account:%s", data->account_id);
771   root_element_urn = g_strdup_printf ("gd:goa-account:%s:root-element", data->account_id);
772 
773   error = NULL;
774   gom_miner_ensure_datasource (self, datasource_urn, root_element_urn, cancellable, &error);
775   if (error != NULL)
776     {
777       g_task_return_error (task, error);
778       goto out;
779     }
780 
781   error = NULL;
782   GOM_MINER_GET_CLASS (self)->insert_shared_content (self,
783                                                      data->service,
784                                                      self->priv->connection,
785                                                      datasource_urn,
786                                                      data->shared_id,
787                                                      data->shared_type,
788                                                      data->source_urn,
789                                                      cancellable,
790                                                      &error);
791   if (error != NULL)
792     {
793       g_task_return_error (task, error);
794       goto out;
795     }
796 
797   g_task_return_boolean (task, TRUE);
798 
799  out:
800   g_free (datasource_urn);
801   g_free (root_element_urn);
802 }
803 
804 void
gom_miner_insert_shared_content_async(GomMiner * self,const gchar * account_id,const gchar * shared_id,const gchar * shared_type,const gchar * source_urn,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)805 gom_miner_insert_shared_content_async (GomMiner *self,
806                                        const gchar *account_id,
807                                        const gchar *shared_id,
808                                        const gchar *shared_type,
809                                        const gchar *source_urn,
810                                        GCancellable *cancellable,
811                                        GAsyncReadyCallback callback,
812                                        gpointer user_data)
813 {
814   GTask *task = NULL;
815   GoaFiles *files;
816   GoaObject *object = NULL;
817   GoaPhotos *photos;
818   InsertSharedContentData *data;
819   gpointer service;
820 
821   task = g_task_new (self, cancellable, callback, user_data);
822   g_task_set_source_tag (task, gom_miner_insert_shared_content_async);
823 
824   if (self->priv->client_error != NULL)
825     {
826       g_task_return_error (task, g_error_copy (self->priv->client_error));
827       goto out;
828     }
829 
830   if (self->priv->connection_error != NULL)
831     {
832       g_task_return_error (task, g_error_copy (self->priv->connection_error));
833       goto out;
834     }
835 
836   object = goa_client_lookup_by_id (self->priv->client, account_id);
837   if (object == NULL)
838     {
839       /* throw error */
840       goto out;
841     }
842 
843   files = goa_object_peek_files (object);
844   photos = goa_object_peek_photos (object);
845 
846   if (g_strcmp0 (shared_type, "documents") == 0 && files == NULL)
847     {
848       /* throw error */
849       goto out;
850     }
851 
852   if (g_strcmp0 (shared_type, "photos") == 0 && photos == NULL)
853     {
854       /* throw error */
855       goto out;
856     }
857 
858   service = GOM_MINER_GET_CLASS (self)->create_service (self, object, shared_type);
859   if (service == NULL)
860     {
861       /* throw error */
862       goto out;
863     }
864 
865   data = gom_insert_shared_content_data_new (self, account_id, shared_id, shared_type, source_urn, service);
866   g_task_set_task_data (task, data, (GDestroyNotify) gom_insert_shared_content_data_free);
867 
868   g_task_run_in_thread (task, gom_miner_insert_shared_content_in_thread_func);
869 
870  out:
871   g_clear_object (&object);
872   g_clear_object (&task);
873 }
874 
875 gboolean
gom_miner_insert_shared_content_finish(GomMiner * self,GAsyncResult * res,GError ** error)876 gom_miner_insert_shared_content_finish (GomMiner *self, GAsyncResult *res, GError **error)
877 {
878   GTask *task;
879 
880   g_assert (g_task_is_valid (res, self));
881   task = G_TASK (res);
882 
883   g_assert (g_task_get_source_tag (task) == gom_miner_insert_shared_content_async);
884 
885   return g_task_propagate_boolean (task, error);
886 }
887 
888 void
gom_miner_refresh_db_async(GomMiner * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)889 gom_miner_refresh_db_async (GomMiner *self,
890                             GCancellable *cancellable,
891                             GAsyncReadyCallback callback,
892                             gpointer user_data)
893 {
894   GTask *task = NULL;
895 
896   task = g_task_new (self, cancellable, callback, user_data);
897   g_task_set_source_tag (task, gom_miner_refresh_db_async);
898 
899   if (self->priv->client_error != NULL)
900     {
901       g_task_return_error (task, g_error_copy (self->priv->client_error));
902       goto out;
903     }
904 
905   if (self->priv->connection_error != NULL)
906     {
907       g_task_return_error (task, g_error_copy (self->priv->connection_error));
908       goto out;
909     }
910 
911   gom_miner_refresh_db_real (self, task);
912 
913  out:
914   g_clear_object (&task);
915 }
916 
917 gboolean
gom_miner_refresh_db_finish(GomMiner * self,GAsyncResult * res,GError ** error)918 gom_miner_refresh_db_finish (GomMiner *self,
919                              GAsyncResult *res,
920                              GError **error)
921 {
922   GTask *task;
923 
924   g_assert (g_task_is_valid (res, self));
925   task = G_TASK (res);
926 
927   g_assert (g_task_get_source_tag (task) == gom_miner_refresh_db_async);
928 
929   return g_task_propagate_boolean (task, error);
930 }
931 
932 void
gom_miner_set_index_types(GomMiner * self,const char ** index_types)933 gom_miner_set_index_types (GomMiner *self, const char **index_types)
934 {
935   g_strfreev (self->priv->index_types);
936   self->priv->index_types = g_strdupv ((gchar **) index_types);
937 }
938 
939 const gchar **
gom_miner_get_index_types(GomMiner * self)940 gom_miner_get_index_types (GomMiner *self)
941 {
942   return (const gchar **) self->priv->index_types;
943 }
944 
945 gboolean
gom_miner_supports_type(GomMiner * self,gchar * type)946 gom_miner_supports_type (GomMiner *self, gchar *type)
947 {
948   gboolean retval = FALSE;
949   guint i;
950 
951   for (i = 0; self->priv->index_types[i] != NULL; i++)
952     {
953       if (g_strcmp0 (self->priv->index_types[i], type) == 0)
954         {
955           retval = TRUE;
956           break;
957         }
958     }
959 
960   return retval;
961 }
962