1 /*
2  * Copyright (C) 2014 Carlos Garnacho  <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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 #include "config.h"
20 
21 #include <glib.h>
22 
23 #include <libtracker-common/tracker-common.h>
24 #include <libtracker-sparql/tracker-sparql.h>
25 
26 #include "tracker-decorator-private.h"
27 #include "tracker-decorator-fs.h"
28 
29 /**
30  * SECTION:tracker-decorator-fs
31  * @short_description: Filesystem implementation for TrackerDecorator
32  * @include: libtracker-miner/tracker-miner.h
33  * @title: TrackerDecoratorFS
34  * @see_also: #TrackerDecorator
35  *
36  * #TrackerDecoratorFS is used to handle extended metadata extraction
37  * for resources on file systems that are mounted or unmounted.
38  **/
39 
40 typedef struct _TrackerDecoratorFSPrivate TrackerDecoratorFSPrivate;
41 
42 struct _TrackerDecoratorFSPrivate {
43 	GVolumeMonitor *volume_monitor;
44 };
45 
46 static GInitableIface *parent_initable_iface;
47 
48 static void tracker_decorator_fs_initable_iface_init (GInitableIface *iface);
49 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(TrackerDecoratorFS,tracker_decorator_fs,TRACKER_TYPE_DECORATOR,G_ADD_PRIVATE (TrackerDecoratorFS)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,tracker_decorator_fs_initable_iface_init))50 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TrackerDecoratorFS, tracker_decorator_fs,
51                                   TRACKER_TYPE_DECORATOR,
52                                   G_ADD_PRIVATE (TrackerDecoratorFS)
53                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, tracker_decorator_fs_initable_iface_init))
54 
55 static void
56 tracker_decorator_fs_finalize (GObject *object)
57 {
58 	TrackerDecoratorFSPrivate *priv;
59 
60 	priv = TRACKER_DECORATOR_FS (object)->priv;
61 
62 	if (priv->volume_monitor)
63 		g_object_unref (priv->volume_monitor);
64 
65 	G_OBJECT_CLASS (tracker_decorator_fs_parent_class)->finalize (object);
66 }
67 
68 static void
tracker_decorator_fs_class_init(TrackerDecoratorFSClass * klass)69 tracker_decorator_fs_class_init (TrackerDecoratorFSClass *klass)
70 {
71 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
72 
73 	object_class->finalize = tracker_decorator_fs_finalize;
74 }
75 
76 static void
process_files_cb(GObject * object,GAsyncResult * result,gpointer user_data)77 process_files_cb (GObject      *object,
78                   GAsyncResult *result,
79                   gpointer      user_data)
80 {
81 	TrackerSparqlConnection *conn;
82 	TrackerSparqlCursor *cursor;
83 	GError *error = NULL;
84 
85 	conn = TRACKER_SPARQL_CONNECTION (object);
86 	cursor = tracker_sparql_connection_query_finish (conn, result, &error);
87 
88         if (error) {
89                 g_critical ("Could not check files on mount point for missing metadata: %s", error->message);
90                 g_error_free (error);
91 		return;
92 	}
93 
94 	while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
95 		gint id = tracker_sparql_cursor_get_integer (cursor, 0);
96 		gint class_name_id = tracker_sparql_cursor_get_integer (cursor, 1);
97 		tracker_decorator_prepend_id (TRACKER_DECORATOR (user_data), id, class_name_id);
98 	}
99 
100 	g_object_unref (cursor);
101 }
102 
103 static void
remove_files_cb(GObject * object,GAsyncResult * result,gpointer user_data)104 remove_files_cb (GObject *object,
105                  GAsyncResult *result,
106                  gpointer      user_data)
107 {
108 	TrackerSparqlConnection *conn;
109 	TrackerSparqlCursor *cursor;
110 	GError *error = NULL;
111 
112 	conn = TRACKER_SPARQL_CONNECTION (object);
113 	cursor = tracker_sparql_connection_query_finish (conn, result, &error);
114 
115         if (error) {
116                 g_critical ("Could not remove files on mount point with missing metadata: %s", error->message);
117                 g_error_free (error);
118 		return;
119 	}
120 
121 	while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
122 		gint id = tracker_sparql_cursor_get_integer (cursor, 0);
123 		tracker_decorator_delete_id (TRACKER_DECORATOR (user_data), id);
124 	}
125 
126 	g_object_unref (cursor);
127 }
128 
129 static void
_tracker_decorator_query_append_rdf_type_filter(TrackerDecorator * decorator,GString * query)130 _tracker_decorator_query_append_rdf_type_filter (TrackerDecorator *decorator,
131                                                  GString          *query)
132 {
133        const gchar **class_names;
134        gint i = 0;
135 
136        class_names = tracker_decorator_get_class_names (decorator);
137 
138        if (!class_names || !*class_names)
139                return;
140 
141        g_string_append (query, "&& ?type IN (");
142 
143        while (class_names[i]) {
144                if (i != 0)
145                        g_string_append (query, ",");
146 
147                g_string_append (query, class_names[i]);
148                i++;
149        }
150 
151        g_string_append (query, ") ");
152 }
153 
154 static void
check_files(TrackerDecorator * decorator,const gchar * mount_point_urn,gboolean available,GAsyncReadyCallback callback)155 check_files (TrackerDecorator    *decorator,
156              const gchar         *mount_point_urn,
157              gboolean             available,
158              GAsyncReadyCallback  callback)
159 {
160 	TrackerSparqlConnection *sparql_conn;
161 	const gchar *data_source;
162 	GString *query;
163 
164 	data_source = tracker_decorator_get_data_source (decorator);
165 	query = g_string_new ("SELECT tracker:id(?urn) tracker:id(?type) { ?urn ");
166 
167 	if (mount_point_urn) {
168 		g_string_append_printf (query,
169 		                        " nie:dataSource <%s> ;",
170 		                        mount_point_urn);
171 	}
172 
173 	g_string_append (query, " a nfo:FileDataObject ;"
174 	                        " a ?type .");
175 	g_string_append_printf (query,
176 	                        "FILTER (! EXISTS { ?urn nie:dataSource <%s> } ",
177 	                        data_source);
178 
179 	_tracker_decorator_query_append_rdf_type_filter (decorator, query);
180 
181 	if (available)
182 		g_string_append (query, "&& BOUND(tracker:available(?urn))");
183 
184 	g_string_append (query, ")}");
185 
186 	sparql_conn = tracker_miner_get_connection (TRACKER_MINER (decorator));
187 	tracker_sparql_connection_query_async (sparql_conn, query->str,
188 	                                       NULL, callback, decorator);
189 	g_string_free (query, TRUE);
190 }
191 
192 static inline gchar *
mount_point_get_uuid(GMount * mount)193 mount_point_get_uuid (GMount *mount)
194 {
195 	GVolume *volume;
196 	gchar *uuid = NULL;
197 
198 	volume = g_mount_get_volume (mount);
199 	if (volume) {
200 		uuid = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UUID);
201 		if (!uuid) {
202 			gchar *mount_name;
203 
204 			mount_name = g_mount_get_name (mount);
205 			uuid = g_compute_checksum_for_string (G_CHECKSUM_MD5, mount_name, -1);
206 			g_free (mount_name);
207 		}
208 
209 		g_object_unref (volume);
210 	}
211 
212 	return uuid;
213 }
214 
215 static void
mount_point_added_cb(GVolumeMonitor * monitor,GMount * mount,gpointer user_data)216 mount_point_added_cb (GVolumeMonitor *monitor,
217                       GMount         *mount,
218                       gpointer        user_data)
219 {
220 	gchar *uuid;
221 	gchar *urn;
222 
223 	uuid = mount_point_get_uuid (mount);
224 	urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
225 	check_files (user_data, urn, TRUE, process_files_cb);
226 	g_free (urn);
227 	g_free (uuid);
228 }
229 
230 static void
mount_point_removed_cb(GVolumeMonitor * monitor,GMount * mount,gpointer user_data)231 mount_point_removed_cb (GVolumeMonitor *monitor,
232                         GMount         *mount,
233                         gpointer        user_data)
234 {
235 	gchar *uuid;
236 	gchar *urn;
237 
238 	uuid = mount_point_get_uuid (mount);
239 	urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
240 	_tracker_decorator_invalidate_cache (user_data);
241 	check_files (user_data, urn, FALSE, remove_files_cb);
242 	g_free (urn);
243 	g_free (uuid);
244 }
245 
246 static gboolean
tracker_decorator_fs_iface_init(GInitable * initable,GCancellable * cancellable,GError ** error)247 tracker_decorator_fs_iface_init (GInitable     *initable,
248                                  GCancellable  *cancellable,
249                                  GError       **error)
250 {
251 	TrackerDecoratorFSPrivate *priv;
252 
253 	priv = TRACKER_DECORATOR_FS (initable)->priv;
254 
255 	priv->volume_monitor = g_volume_monitor_get ();
256 	g_signal_connect_object (priv->volume_monitor, "mount-added",
257 	                         G_CALLBACK (mount_point_added_cb), initable, 0);
258 	g_signal_connect_object (priv->volume_monitor, "mount-pre-unmount",
259 	                         G_CALLBACK (mount_point_removed_cb), initable, 0);
260 	g_signal_connect_object (priv->volume_monitor, "mount-removed",
261 	                         G_CALLBACK (mount_point_removed_cb), initable, 0);
262 
263 	return parent_initable_iface->init (initable, cancellable, error);
264 }
265 
266 static void
tracker_decorator_fs_initable_iface_init(GInitableIface * iface)267 tracker_decorator_fs_initable_iface_init (GInitableIface *iface)
268 {
269 	parent_initable_iface = g_type_interface_peek_parent (iface);
270 	iface->init = tracker_decorator_fs_iface_init;
271 }
272 
273 static void
tracker_decorator_fs_init(TrackerDecoratorFS * decorator)274 tracker_decorator_fs_init (TrackerDecoratorFS *decorator)
275 {
276 	decorator->priv = tracker_decorator_fs_get_instance_private (decorator);
277 }
278 
279 /**
280  * tracker_decorator_fs_prepend_file:
281  * @decorator: a #TrackerDecoratorFS
282  * @file: a #GFile to process
283  *
284  * Prepends a file for processing.
285  *
286  * Returns: the tracker:id of the element corresponding to the file
287  *
288  * Since: 1.2
289  **/
290 gint
tracker_decorator_fs_prepend_file(TrackerDecoratorFS * decorator,GFile * file)291 tracker_decorator_fs_prepend_file (TrackerDecoratorFS *decorator,
292                                    GFile              *file)
293 {
294 	TrackerSparqlConnection *sparql_conn;
295 	TrackerSparqlCursor *cursor;
296 	gchar *query, *uri;
297 	gint id, class_id;
298 
299 	g_return_val_if_fail (TRACKER_IS_DECORATOR_FS (decorator), 0);
300 	g_return_val_if_fail (G_IS_FILE (file), 0);
301 
302 	uri = g_file_get_uri (file);
303 	query = g_strdup_printf ("SELECT tracker:id(?urn) tracker:id(?type) {"
304 	                         "  ?urn a ?type; nie:url \"%s\" "
305 	                         "}", uri);
306 	g_free (uri);
307 
308 	sparql_conn = tracker_miner_get_connection (TRACKER_MINER (decorator));
309 	cursor = tracker_sparql_connection_query (sparql_conn, query,
310 	                                          NULL, NULL);
311 	g_free (query);
312 
313 	if (!cursor)
314 		return 0;
315 
316 	if (!tracker_sparql_cursor_next (cursor, NULL, NULL)) {
317 		g_object_unref (cursor);
318 		return 0;
319 	}
320 
321 	id = tracker_sparql_cursor_get_integer (cursor, 0);
322 	class_id = tracker_sparql_cursor_get_integer (cursor, 1);
323 
324 	tracker_decorator_prepend_id (TRACKER_DECORATOR (decorator),
325 	                              id, class_id);
326 	g_object_unref (cursor);
327 
328 	return id;
329 }
330