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