tracker-0.16.2/src/miners/fs/tracker-writeback-listener.c

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 }