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