No issues found
1 /*
2 * Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21
22 #include <libtracker-common/tracker-dbus.h>
23 #include <libtracker-sparql/tracker-sparql.h>
24 #include <libtracker-miner/tracker-miner-dbus.h>
25
26 #include "tracker-writeback-listener.h"
27
28 #define TRACKER_SERVICE "org.freedesktop.Tracker1"
29 #define TRACKER_RESOURCES_OBJECT "/org/freedesktop/Tracker1/Resources"
30 #define TRACKER_INTERFACE_RESOURCES "org.freedesktop.Tracker1.Resources"
31
32 typedef struct {
33 TrackerMinerFiles *files_miner;
34 GDBusConnection *d_connection;
35 TrackerSparqlConnection *connection;
36 guint d_signal;
37 } TrackerWritebackListenerPrivate;
38
39 typedef struct {
40 TrackerWritebackListener *self;
41 GStrv rdf_types;
42 } QueryData;
43
44 typedef struct {
45 GVariantIter *iter1;
46 TrackerWritebackListener *self;
47 } DelayedLoopData;
48
49 enum {
50 PROP_0,
51 PROP_FILES_MINER
52 };
53
54 #define TRACKER_WRITEBACK_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_WRITEBACK_LISTENER, TrackerWritebackListenerPrivate))
55
56 static void writeback_listener_set_property (GObject *object,
57 guint param_id,
58 const GValue *value,
59 GParamSpec *pspec);
60 static void writeback_listener_get_property (GObject *object,
61 guint param_id,
62 GValue *value,
63 GParamSpec *pspec);
64 static void writeback_listener_finalize (GObject *object);
65 static gboolean writeback_listener_initable_init (GInitable *initable,
66 GCancellable *cancellable,
67 GError **error);
68 static void on_writeback_cb (GDBusConnection *connection,
69 const gchar *sender_name,
70 const gchar *object_path,
71 const gchar *interface_name,
72 const gchar *signal_name,
73 GVariant *parameters,
74 gpointer user_data);
75
76 static void
77 writeback_listener_initable_iface_init (GInitableIface *iface)
78 {
79 iface->init = writeback_listener_initable_init;
80 }
81
82 G_DEFINE_TYPE_WITH_CODE (TrackerWritebackListener, tracker_writeback_listener, G_TYPE_OBJECT,
83 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
84 writeback_listener_initable_iface_init));
85
86 static void
87 tracker_writeback_listener_class_init (TrackerWritebackListenerClass *klass)
88 {
89 GObjectClass *object_class;
90
91 object_class = G_OBJECT_CLASS (klass);
92
93 object_class->finalize = writeback_listener_finalize;
94 object_class->set_property = writeback_listener_set_property;
95 object_class->get_property = writeback_listener_get_property;
96
97 g_object_class_install_property (object_class,
98 PROP_FILES_MINER,
99 g_param_spec_object ("files_miner",
100 "files_miner",
101 "The FS Miner",
102 TRACKER_TYPE_MINER_FILES,
103 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
104
105 g_type_class_add_private (klass, sizeof (TrackerWritebackListenerPrivate));
106 }
107
108 static void
109 writeback_listener_set_property (GObject *object,
110 guint param_id,
111 const GValue *value,
112 GParamSpec *pspec)
113 {
114 TrackerWritebackListenerPrivate *priv;
115
116 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (object);
117
118 switch (param_id) {
119 case PROP_FILES_MINER:
120 priv->files_miner = g_value_dup_object (value);
121 break;
122 default:
123 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
124 break;
125 }
126 }
127
128 static void
129 writeback_listener_get_property (GObject *object,
130 guint param_id,
131 GValue *value,
132 GParamSpec *pspec)
133 {
134 TrackerWritebackListenerPrivate *priv;
135
136 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (object);
137
138 switch (param_id) {
139 case PROP_FILES_MINER:
140 g_value_set_object (value, priv->files_miner);
141 break;
142 default:
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
144 break;
145 }
146 }
147
148 static void
149 writeback_listener_finalize (GObject *object)
150 {
151 TrackerWritebackListenerPrivate *priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (object);
152
153 if (priv->connection && priv->d_signal) {
154 g_dbus_connection_signal_unsubscribe (priv->d_connection, priv->d_signal);
155 }
156
157 if (priv->connection) {
158 g_object_unref (priv->connection);
159 }
160
161 if (priv->d_connection) {
162 g_object_unref (priv->d_connection);
163 }
164
165 if (priv->files_miner) {
166 g_object_unref (priv->files_miner);
167 }
168 }
169
170
171 static void
172 tracker_writeback_listener_init (TrackerWritebackListener *object)
173 {
174 }
175
176 static gboolean
177 writeback_listener_initable_init (GInitable *initable,
178 GCancellable *cancellable,
179 GError **error)
180 {
181 TrackerWritebackListenerPrivate *priv;
182 GError *internal_error = NULL;
183
184 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (initable);
185
186 priv->connection = tracker_sparql_connection_get (NULL, &internal_error);
187
188 if (internal_error) {
189 g_propagate_error (error, internal_error);
190 return FALSE;
191 }
192
193 priv->d_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &internal_error);
194
195 if (internal_error) {
196 g_propagate_error (error, internal_error);
197 return FALSE;
198 }
199
200 priv->d_signal = g_dbus_connection_signal_subscribe (priv->d_connection,
201 TRACKER_SERVICE,
202 TRACKER_INTERFACE_RESOURCES,
203 "Writeback",
204 TRACKER_RESOURCES_OBJECT,
205 NULL,
206 G_DBUS_SIGNAL_FLAGS_NONE,
207 on_writeback_cb,
208 initable,
209 NULL);
210
211 return TRUE;
212 }
213
214 TrackerWritebackListener *
215 tracker_writeback_listener_new (TrackerMinerFiles *miner_files,
216 GError **error)
217 {
218 GObject *miner;
219 GError *internal_error = NULL;
220
221 miner = g_initable_new (TRACKER_TYPE_WRITEBACK_LISTENER,
222 NULL,
223 &internal_error,
224 "files-miner", miner_files,
225 NULL);
226
227 if (internal_error) {
228 g_propagate_error (error, internal_error);
229 return NULL;
230 }
231
232 return (TrackerWritebackListener *) miner;
233 }
234
235 static QueryData*
236 query_data_new (TrackerWritebackListener *self)
237 {
238 QueryData *data = g_slice_new0 (QueryData);
239
240 data->self = g_object_ref (self);
241
242 return data;
243 }
244
245 static void
246 query_data_free (QueryData *data)
247 {
248 g_object_unref (data->self);
249 if (data->rdf_types) {
250 g_strfreev (data->rdf_types);
251 }
252 g_slice_free (QueryData, data);
253 }
254
255 static void
256 sparql_query_cb (GObject *object,
257 GAsyncResult *result,
258 gpointer user_data)
259 {
260 QueryData *data = user_data;
261 TrackerWritebackListener *self = TRACKER_WRITEBACK_LISTENER (data->self);
262 TrackerWritebackListenerPrivate *priv;
263 TrackerSparqlCursor *cursor;
264 GError *error = NULL;
265
266 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (self);
267
268 cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), result, &error);
269
270 if (!error) {
271 guint cols = tracker_sparql_cursor_get_n_columns (cursor);
272 GPtrArray *results = NULL;
273 GFile *file = NULL;
274
275 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
276 GStrv row = NULL;
277 guint i;
278
279 if (file == NULL) {
280 file = g_file_new_for_uri (tracker_sparql_cursor_get_string (cursor, 0, NULL));
281 if (!g_file_query_exists (file, NULL)) {
282 g_object_unref (file);
283 g_message (" No files qualify for updates");
284 query_data_free (data);
285 g_object_unref (cursor);
286 return;
287 }
288 }
289
290 for (i = 0; i < cols; i++) {
291 if (!row) {
292 row = g_new0 (gchar*, cols);
293 }
294 row[i] = g_strdup (tracker_sparql_cursor_get_string (cursor, i, NULL));
295 }
296
297 if (!results) {
298 results = g_ptr_array_new_with_free_func ((GDestroyNotify) g_strfreev);
299 }
300
301 g_ptr_array_add (results, row);
302 }
303
304 if (results != NULL && results->len > 0) {
305 tracker_miner_fs_writeback_file (TRACKER_MINER_FS (priv->files_miner),
306 file,
307 data->rdf_types,
308 results);
309 } else {
310 g_message (" No files qualify for updates");
311 }
312
313 if (file) {
314 g_object_unref (file);
315 }
316
317 if (results) {
318 g_ptr_array_unref (results);
319 }
320
321 g_object_unref (cursor);
322 } else {
323 g_message (" No files qualify for updates (%s)", error->message);
324 g_error_free (error);
325 }
326
327 query_data_free (data);
328 }
329
330 static void
331 rdf_types_to_uris_cb (GObject *object,
332 GAsyncResult *result,
333 gpointer user_data)
334 {
335 QueryData *data = user_data;
336 TrackerWritebackListener *self = TRACKER_WRITEBACK_LISTENER (data->self);
337 TrackerWritebackListenerPrivate *priv;
338 TrackerSparqlCursor *cursor;
339 TrackerSparqlConnection *connection;
340 GError *error = NULL;
341
342 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (self);
343 connection = priv->connection;
344
345 cursor = tracker_sparql_connection_query_finish (connection, result, &error);
346
347 if (!error) {
348 gchar *query;
349 GArray *rdf_types;
350 gchar *subject = NULL;
351
352 rdf_types = g_array_new (TRUE, TRUE, sizeof (gchar *));
353
354 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
355 gchar *uri = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
356 if (subject == NULL) {
357 subject = g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL));
358 }
359 g_array_append_val (rdf_types, uri);
360 }
361
362 g_object_unref (cursor);
363
364 data->rdf_types = (GStrv) rdf_types->data;
365 g_array_free (rdf_types, FALSE);
366
367 if (subject == NULL) {
368 goto trouble;
369 }
370
371 query = g_strdup_printf ("SELECT ?url '%s' ?predicate ?object { "
372 "<%s> a nfo:FileDataObject . "
373 "<%s> ?predicate ?object ; nie:url ?url . "
374 "?predicate tracker:writeback true . "
375 "FILTER (NOT EXISTS { GRAPH <"TRACKER_MINER_FS_GRAPH_URN"> "
376 "{ <%s> ?predicate ?object } }) } ",
377 subject, subject, subject, subject);
378
379 tracker_sparql_connection_query_async (connection,
380 query,
381 NULL,
382 sparql_query_cb,
383 data);
384
385 g_free (subject);
386 g_free (query);
387
388 } else {
389 goto trouble;
390 }
391
392 return;
393
394 trouble:
395 if (error) {
396 g_message (" No files qualify for updates (%s)", error->message);
397 g_error_free (error);
398 }
399 query_data_free (data);
400 }
401
402 static gboolean
403 delayed_loop_idle (gpointer user_data)
404 {
405 DelayedLoopData *ldata = user_data;
406 GVariantIter *iter2;
407 gint subject_id = 0, rdf_type = 0;
408
409 if (g_variant_iter_next (ldata->iter1, "{iai}", &subject_id, &iter2)) {
410 TrackerWritebackListenerPrivate *priv;
411 QueryData *data = NULL;
412 GString *query;
413 gboolean comma = FALSE;
414
415 priv = TRACKER_WRITEBACK_LISTENER_GET_PRIVATE (ldata->self);
416
417 data = query_data_new (ldata->self);
418
419 /* Two queries are grouped together here to reduce the amount of
420 * queries that must be made. tracker:uri() is idd unrelated to
421 * the other part of the query (and is repeated each result, idd) */
422
423 query = g_string_new ("SELECT ");
424 g_string_append_printf (query, "?resource tracker:uri (%d) { "
425 "?resource a rdfs:Class . "
426 "FILTER (tracker:id (?resource) IN (",
427 subject_id);
428
429 while (g_variant_iter_loop (iter2, "i", &rdf_type)) {
430 if (comma) {
431 g_string_append_printf (query, ", %d", rdf_type);
432 } else {
433 g_string_append_printf (query, "%d", rdf_type);
434 comma = TRUE;
435 }
436 }
437
438 g_string_append (query, ")) }");
439
440 tracker_sparql_connection_query_async (priv->connection,
441 query->str,
442 NULL,
443 rdf_types_to_uris_cb,
444 data);
445
446 g_string_free (query, TRUE);
447
448 g_variant_iter_free (iter2);
449
450 return TRUE;
451 }
452
453 return FALSE;
454 }
455
456 static void
457 delayed_loop_finished (gpointer user_data)
458 {
459 DelayedLoopData *ldata = user_data;
460 g_variant_iter_free (ldata->iter1);
461 g_object_unref (ldata->self);
462 g_free (ldata);
463 }
464
465 static void
466 on_writeback_cb (GDBusConnection *connection,
467 const gchar *sender_name,
468 const gchar *object_path,
469 const gchar *interface_name,
470 const gchar *signal_name,
471 GVariant *parameters,
472 gpointer user_data)
473 {
474 TrackerWritebackListener *self = TRACKER_WRITEBACK_LISTENER (user_data);
475 DelayedLoopData *data = g_new (DelayedLoopData, 1);
476
477 data->self = g_object_ref (self);
478 g_variant_get (parameters, "(a{iai})", &data->iter1);
479
480 g_idle_add_full (G_PRIORITY_LOW,
481 delayed_loop_idle,
482 data,
483 delayed_loop_finished);
484 }