1 /*
2  * GNOME Online Miners - crawls through your online content
3  * Copyright (c) 2011, 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  *
22  */
23 
24 #include <glib.h>
25 
26 #include "gom-tracker.h"
27 #include "gom-utils.h"
28 
29 static gchar *
_tracker_utils_format_into_graph(const gchar * graph)30 _tracker_utils_format_into_graph (const gchar *graph)
31 {
32   return (graph != NULL) ? g_strdup_printf ("INTO <%s> ", graph) : g_strdup ("");
33 }
34 
35 static gboolean
gom_tracker_sparql_connection_get_string_attribute(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * resource,const gchar * attribute,gchar ** value)36 gom_tracker_sparql_connection_get_string_attribute (TrackerSparqlConnection *connection,
37                                                     GCancellable *cancellable,
38                                                     GError **error,
39                                                     const gchar *resource,
40                                                     const gchar *attribute,
41                                                     gchar **value)
42 {
43   GString *select = g_string_new (NULL);
44   TrackerSparqlCursor *cursor;
45   const gchar *string_value = NULL;
46   gboolean res;
47 
48   g_string_append_printf (select, "SELECT ?val { <%s> %s ?val }",
49                           resource, attribute);
50   cursor = tracker_sparql_connection_query (connection,
51                                             select->str,
52                                             cancellable, error);
53   g_string_free (select, TRUE);
54 
55   if (*error != NULL)
56     goto out;
57 
58   res = tracker_sparql_cursor_next (cursor, cancellable, error);
59 
60   if (*error != NULL)
61     goto out;
62 
63   if (res)
64     {
65       string_value = tracker_sparql_cursor_get_string (cursor, 0, NULL);
66       goto out;
67     }
68 
69  out:
70   if (string_value != NULL && value != NULL)
71     *value = g_strdup (string_value);
72   else if (string_value == NULL)
73     res = FALSE;
74 
75   g_clear_object (&cursor);
76   return res;
77 }
78 
79 gchar *
gom_tracker_sparql_connection_ensure_resource(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,gboolean * resource_exists,const gchar * graph,const gchar * identifier,const gchar * class,...)80 gom_tracker_sparql_connection_ensure_resource (TrackerSparqlConnection *connection,
81                                                GCancellable *cancellable,
82                                                GError **error,
83                                                gboolean *resource_exists,
84                                                const gchar *graph,
85                                                const gchar *identifier,
86                                                const gchar *class,
87                                                ...)
88 {
89   GString *select, *insert, *inner;
90   va_list args;
91   const gchar *arg;
92   TrackerSparqlCursor *cursor;
93   gboolean res;
94   gchar *retval = NULL;
95   gchar *graph_str;
96   GVariant *insert_res;
97   GVariantIter *iter;
98   gchar *key = NULL, *val = NULL;
99   gboolean exists = FALSE;
100 
101   /* build the inner query with all the classes */
102   va_start (args, class);
103   inner = g_string_new (NULL);
104 
105   for (arg = class; arg != NULL; arg = va_arg (args, const gchar *))
106     g_string_append_printf (inner, " a %s; ", arg);
107 
108   g_string_append_printf (inner, "nao:identifier \"%s\"", identifier);
109 
110   va_end (args);
111 
112   /* query if such a resource is already in the DB */
113   select = g_string_new (NULL);
114   g_string_append_printf (select,
115                           "SELECT ?urn WHERE { ?urn %s }", inner->str);
116 
117   cursor = tracker_sparql_connection_query (connection,
118                                             select->str,
119                                             cancellable, error);
120 
121   g_string_free (select, TRUE);
122 
123   if (*error != NULL)
124     goto out;
125 
126   res = tracker_sparql_cursor_next (cursor, cancellable, error);
127 
128   if (*error != NULL)
129     goto out;
130 
131   if (res)
132     {
133       /* return the found resource */
134       retval = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
135       exists = TRUE;
136       g_debug ("Found resource in the store: %s", retval);
137       goto out;
138     }
139 
140   /* not found, create the resource */
141   insert = g_string_new (NULL);
142   graph_str = _tracker_utils_format_into_graph (graph);
143 
144   g_string_append_printf (insert, "INSERT %s { _:res %s }",
145                           graph_str, inner->str);
146   g_free (graph_str);
147   g_string_free (inner, TRUE);
148 
149   insert_res =
150     tracker_sparql_connection_update_blank (connection, insert->str,
151                                             G_PRIORITY_DEFAULT, NULL, error);
152 
153   g_string_free (insert, TRUE);
154 
155   if (*error != NULL)
156     goto out;
157 
158   /* the result is an "aaa{ss}" variant */
159   g_variant_get (insert_res, "aaa{ss}", &iter);
160   g_variant_iter_next (iter, "aa{ss}", &iter);
161   g_variant_iter_next (iter, "a{ss}", &iter);
162   g_variant_iter_next (iter, "{ss}", &key, &val);
163 
164   g_variant_iter_free (iter);
165   g_variant_unref (insert_res);
166 
167   if (g_strcmp0 (key, "res") == 0)
168     {
169       retval = val;
170     }
171   else
172     {
173       g_free (val);
174       goto out;
175     }
176 
177   g_debug ("Created a new resource: %s", retval);
178 
179  out:
180   if (resource_exists)
181     *resource_exists = exists;
182 
183   g_clear_object (&cursor);
184   return retval;
185 }
186 
187 gboolean
gom_tracker_sparql_connection_insert_or_replace_triple(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * graph,const gchar * resource,const gchar * property_name,const gchar * property_value)188 gom_tracker_sparql_connection_insert_or_replace_triple (TrackerSparqlConnection *connection,
189                                                         GCancellable *cancellable,
190                                                         GError **error,
191                                                         const gchar *graph,
192                                                         const gchar *resource,
193                                                         const gchar *property_name,
194                                                         const gchar *property_value)
195 {
196   GString *insert;
197   gchar *graph_str, *quoted;
198   gboolean retval = TRUE;
199 
200   graph_str = _tracker_utils_format_into_graph (graph);
201 
202   /* the "null" value must not be quoted */
203   if (property_value == NULL)
204     quoted = g_strdup ("null");
205   else
206     quoted = g_strdup_printf ("\"%s\"", property_value);
207 
208   insert = g_string_new (NULL);
209   g_string_append_printf
210     (insert,
211      "INSERT OR REPLACE %s { <%s> a nie:InformationElement ; %s %s }",
212      graph_str, resource, property_name, quoted);
213   g_free (quoted);
214 
215   g_debug ("Insert or replace triple: query %s", insert->str);
216 
217   tracker_sparql_connection_update (connection, insert->str,
218                                     G_PRIORITY_DEFAULT, cancellable,
219                                     error);
220 
221   g_string_free (insert, TRUE);
222 
223   if (*error != NULL)
224     retval = FALSE;
225 
226   g_free (graph_str);
227 
228   return retval;
229 }
230 
231 gboolean
gom_tracker_sparql_connection_set_triple(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * graph,const gchar * resource,const gchar * property_name,const gchar * property_value)232 gom_tracker_sparql_connection_set_triple (TrackerSparqlConnection *connection,
233                                           GCancellable *cancellable,
234                                           GError **error,
235                                           const gchar *graph,
236                                           const gchar *resource,
237                                           const gchar *property_name,
238                                           const gchar *property_value)
239 {
240   GString *delete;
241   gboolean retval = TRUE;
242 
243   delete = g_string_new (NULL);
244   g_string_append_printf
245     (delete,
246      "DELETE { <%s> %s ?val } WHERE { <%s> %s ?val }", resource,
247      property_name, resource, property_name);
248 
249   tracker_sparql_connection_update (connection, delete->str,
250                                     G_PRIORITY_DEFAULT, cancellable,
251                                     error);
252 
253   g_string_free (delete, TRUE);
254   if (*error != NULL)
255     {
256       retval = FALSE;
257       goto out;
258     }
259 
260   retval =
261     gom_tracker_sparql_connection_insert_or_replace_triple (connection,
262                                                             cancellable, error,
263                                                             graph, resource,
264                                                             property_name, property_value);
265 
266  out:
267   return retval;
268 }
269 
270 gboolean
gom_tracker_sparql_connection_toggle_favorite(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * resource,gboolean favorite)271 gom_tracker_sparql_connection_toggle_favorite (TrackerSparqlConnection *connection,
272                                                GCancellable *cancellable,
273                                                GError **error,
274                                                const gchar *resource,
275                                                gboolean favorite)
276 {
277   GString *update;
278   const gchar *op_str = NULL;
279   gboolean retval = TRUE;
280 
281   if (favorite)
282     op_str = "INSERT OR REPLACE";
283   else
284     op_str = "DELETE";
285 
286   update = g_string_new (NULL);
287   g_string_append_printf
288     (update,
289      "%s { <%s> nao:hasTag nao:predefined-tag-favorite }",
290      op_str, resource);
291 
292   g_debug ("Toggle favorite: query %s", update->str);
293 
294   tracker_sparql_connection_update (connection, update->str,
295                                     G_PRIORITY_DEFAULT, cancellable,
296                                     error);
297 
298   g_string_free (update, TRUE);
299 
300   if (*error != NULL)
301     retval = FALSE;
302 
303   return retval;
304 }
305 
306 gchar*
gom_tracker_utils_ensure_contact_resource(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * email,const gchar * fullname)307 gom_tracker_utils_ensure_contact_resource (TrackerSparqlConnection *connection,
308                                            GCancellable *cancellable,
309                                            GError **error,
310                                            const gchar *email,
311                                            const gchar *fullname)
312 {
313   GString *select, *insert;
314   TrackerSparqlCursor *cursor = NULL;
315   gchar *retval = NULL, *mail_uri = NULL;
316   gboolean res;
317   GVariant *insert_res;
318   GVariantIter *iter;
319   gchar *key = NULL, *val = NULL;
320 
321   mail_uri = g_strconcat ("mailto:", email, NULL);
322   select = g_string_new (NULL);
323   g_string_append_printf (select,
324                           "SELECT ?urn WHERE { ?urn a nco:Contact . "
325                           "?urn nco:hasEmailAddress ?mail . "
326                           "FILTER (fn:contains(?mail, \"%s\" )) }", mail_uri);
327 
328   cursor = tracker_sparql_connection_query (connection,
329                                             select->str,
330                                             cancellable, error);
331 
332   g_string_free (select, TRUE);
333 
334   if (*error != NULL)
335     goto out;
336 
337   res = tracker_sparql_cursor_next (cursor, cancellable, error);
338 
339   if (*error != NULL)
340     goto out;
341 
342   if (res)
343     {
344       /* return the found resource */
345       retval = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
346       g_debug ("Found resource in the store: %s", retval);
347       goto out;
348     }
349 
350   /* not found, create the resource */
351   insert = g_string_new (NULL);
352 
353   g_string_append_printf (insert,
354                           "INSERT { <%s> a nco:EmailAddress ; nco:emailAddress \"%s\" . "
355                           "_:res a nco:Contact ; nco:hasEmailAddress <%s> ; nco:fullname \"%s\" . }",
356                           mail_uri, email,
357                           mail_uri, fullname);
358 
359   insert_res =
360     tracker_sparql_connection_update_blank (connection, insert->str,
361                                             G_PRIORITY_DEFAULT, cancellable, error);
362 
363   g_string_free (insert, TRUE);
364 
365   if (*error != NULL)
366     goto out;
367 
368   /* the result is an "aaa{ss}" variant */
369   g_variant_get (insert_res, "aaa{ss}", &iter);
370   g_variant_iter_next (iter, "aa{ss}", &iter);
371   g_variant_iter_next (iter, "a{ss}", &iter);
372   g_variant_iter_next (iter, "{ss}", &key, &val);
373 
374   g_variant_iter_free (iter);
375   g_variant_unref (insert_res);
376 
377   if (g_strcmp0 (key, "res") == 0)
378     {
379       retval = val;
380     }
381   else
382     {
383       g_free (val);
384       goto out;
385     }
386 
387   g_debug ("Created a new contact resource: %s", retval);
388 
389  out:
390   g_clear_object (&cursor);
391   g_free (mail_uri);
392 
393   return retval;
394 }
395 
396 gchar *
gom_tracker_utils_ensure_equipment_resource(TrackerSparqlConnection * connection,GCancellable * cancellable,GError ** error,const gchar * make,const gchar * model)397 gom_tracker_utils_ensure_equipment_resource (TrackerSparqlConnection *connection,
398                                              GCancellable *cancellable,
399                                              GError **error,
400                                              const gchar *make,
401                                              const gchar *model)
402 {
403   GError *local_error;
404   TrackerSparqlCursor *cursor = NULL;
405   gboolean res;
406   gchar *equip_uri = NULL;
407   gchar *insert = NULL;
408   gchar *retval = NULL;
409   gchar *select = NULL;
410 
411   g_return_val_if_fail (TRACKER_SPARQL_IS_CONNECTION (connection), NULL);
412   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
413   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
414   g_return_val_if_fail (make != NULL || model != NULL, NULL);
415 
416   equip_uri = tracker_sparql_escape_uri_printf ("urn:equipment:%s:%s:",
417                                                 make != NULL ? make : "",
418                                                 model != NULL ? model : "");
419   select = g_strdup_printf ("SELECT <%s> WHERE { }", equip_uri);
420 
421   local_error = NULL;
422   cursor = tracker_sparql_connection_query (connection, select, cancellable, &local_error);
423   if (local_error != NULL)
424     {
425       g_propagate_error (error, local_error);
426       goto out;
427     }
428 
429   local_error = NULL;
430   res = tracker_sparql_cursor_next (cursor, cancellable, &local_error);
431   if (local_error != NULL)
432     {
433       g_propagate_error (error, local_error);
434       goto out;
435     }
436 
437   if (res)
438     {
439       const gchar *cursor_uri;
440 
441       cursor_uri = tracker_sparql_cursor_get_string (cursor, 0, NULL);
442       if (g_strcmp0 (cursor_uri, equip_uri) == 0)
443         {
444           /* return the found resource */
445           retval = g_strdup (cursor_uri);
446           g_debug ("Found resource in the store: %s", retval);
447           goto out;
448         }
449     }
450 
451   /* not found, create the resource */
452   insert = g_strdup_printf ("INSERT { <%s> a nfo:Equipment ; nfo:manufacturer \"%s\" ; nfo:model \"%s\" }",
453                             equip_uri,
454                             make,
455                             model);
456 
457   local_error = NULL;
458   tracker_sparql_connection_update (connection, insert, G_PRIORITY_DEFAULT, cancellable, &local_error);
459   if (local_error != NULL)
460     {
461       g_propagate_error (error, local_error);
462       goto out;
463     }
464 
465   retval = equip_uri;
466   equip_uri = NULL;
467 
468   g_debug ("Created a new equipment resource: %s", retval);
469 
470  out:
471   g_clear_object (&cursor);
472   g_free (equip_uri);
473   g_free (insert);
474   g_free (select);
475 
476   return retval;
477 }
478 
479 void
gom_tracker_update_datasource(TrackerSparqlConnection * connection,const gchar * datasource_urn,gboolean resource_exists,const gchar * identifier,const gchar * resource,GCancellable * cancellable,GError ** error)480 gom_tracker_update_datasource (TrackerSparqlConnection  *connection,
481                                const gchar              *datasource_urn,
482                                gboolean                  resource_exists,
483                                const gchar              *identifier,
484                                const gchar              *resource,
485                                GCancellable             *cancellable,
486                                GError                  **error)
487 {
488   gboolean set_datasource;
489 
490   /* only set the datasource again if it has changed; this avoids touching the
491    * DB completely if the entry didn't change at all, since we later also check
492    * the mtime. */
493   set_datasource = TRUE;
494   if (resource_exists)
495     {
496       gboolean res;
497       gchar *old_value;
498 
499       res = gom_tracker_sparql_connection_get_string_attribute
500         (connection, cancellable, error,
501          resource, "nie:dataSource", &old_value);
502       g_clear_error (error);
503 
504       if (res)
505         {
506           res = g_str_equal (old_value, datasource_urn);
507           g_free (old_value);
508         }
509 
510       if (res)
511         set_datasource = FALSE;
512     }
513 
514   if (set_datasource)
515     gom_tracker_sparql_connection_set_triple
516       (connection, cancellable, error,
517        identifier, resource,
518        "nie:dataSource", datasource_urn);
519 }
520 
521 gboolean
gom_tracker_update_mtime(TrackerSparqlConnection * connection,gint64 new_mtime,gboolean resource_exists,const gchar * identifier,const gchar * resource,GCancellable * cancellable,GError ** error)522 gom_tracker_update_mtime (TrackerSparqlConnection  *connection,
523                           gint64                    new_mtime,
524                           gboolean                  resource_exists,
525                           const gchar              *identifier,
526                           const gchar              *resource,
527                           GCancellable             *cancellable,
528                           GError                  **error)
529 {
530   GTimeVal old_mtime;
531   gboolean res;
532   gchar *old_value;
533   gchar *date;
534 
535   if (resource_exists)
536     {
537       res = gom_tracker_sparql_connection_get_string_attribute
538         (connection, cancellable, error,
539          resource, "nie:contentLastModified", &old_value);
540       g_clear_error (error);
541 
542       if (res)
543         {
544           res = g_time_val_from_iso8601 (old_value, &old_mtime);
545           g_free (old_value);
546         }
547 
548       if (res && (new_mtime == old_mtime.tv_sec))
549         return FALSE;
550     }
551 
552   date = gom_iso8601_from_timestamp (new_mtime);
553   gom_tracker_sparql_connection_insert_or_replace_triple
554     (connection, cancellable, error,
555      identifier, resource,
556      "nie:contentLastModified", date);
557   g_free (date);
558 
559   return TRUE;
560 }
561