1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Copyright (C) 2005 Mr Jamie McCracken
4 *
5 * Caja is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * Caja 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 GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; see the file COPYING. If not,
17 * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Jamie McCracken <jamiemcc@gnome.org>
21 *
22 */
23
24 #include <config.h>
25 #include <gmodule.h>
26 #include <string.h>
27
28 #include <eel/eel-gtk-macros.h>
29
30 #include "caja-search-engine-tracker.h"
31
32 typedef struct _TrackerClient TrackerClient;
33
34 typedef enum
35 {
36 TRACKER_0_6 = 1 << 0,
37 TRACKER_0_7 = 1 << 1,
38 TRACKER_0_8 = 1 << 2
39 } TrackerVersion;
40
41
42 /* tracker 0.6 API */
43 typedef void (*TrackerArrayReply) (char **result, GError *error, gpointer user_data);
44
45 static TrackerClient * (*tracker_connect) (gboolean enable_warnings,
46 gint timeout) = NULL;
47 static void (*tracker_disconnect) (TrackerClient *client) = NULL;
48 static void (*tracker_cancel_last_call) (TrackerClient *client) = NULL;
49 static int (*tracker_get_version) (TrackerClient *client, GError **error) = NULL;
50
51
52 static void (*tracker_search_metadata_by_text_async) (TrackerClient *client,
53 const char *query,
54 TrackerArrayReply callback,
55 gpointer user_data) = NULL;
56 static void (*tracker_search_metadata_by_text_and_mime_async) (TrackerClient *client,
57 const char *query,
58 const char **mimes,
59 TrackerArrayReply callback,
60 gpointer user_data) = NULL;
61 static void (*tracker_search_metadata_by_text_and_location_async) (TrackerClient *client,
62 const char *query,
63 const char *location,
64 TrackerArrayReply callback,
65 gpointer user_data) = NULL;
66 static void (*tracker_search_metadata_by_text_and_mime_and_location_async) (TrackerClient *client,
67 const char *query,
68 const char **mimes,
69 const char *location,
70 TrackerArrayReply callback,
71 gpointer user_data) = NULL;
72
73
74 /* tracker 0.8 API */
75 typedef enum
76 {
77 TRACKER_CLIENT_ENABLE_WARNINGS = 1 << 0
78 } TrackerClientFlags;
79
80 typedef void (*TrackerReplyGPtrArray) (GPtrArray *result,
81 GError *error,
82 gpointer user_data);
83
84 static TrackerClient * (*tracker_client_new) (TrackerClientFlags flags,
85 gint timeout) = NULL;
86 static gchar * (*tracker_sparql_escape) (const gchar *str) = NULL;
87 static guint (*tracker_resources_sparql_query_async) (TrackerClient *client,
88 const gchar *query,
89 TrackerReplyGPtrArray callback,
90 gpointer user_data) = NULL;
91
92
93 static struct TrackerDlMapping
94 {
95 const char *fn_name;
96 gpointer *fn_ptr_ref;
97 TrackerVersion versions;
98 } tracker_dl_mapping[] =
99 {
100 #define MAP(a,v) { #a, (gpointer *)&a, v }
101 MAP (tracker_connect, TRACKER_0_6 | TRACKER_0_7),
102 MAP (tracker_disconnect, TRACKER_0_6 | TRACKER_0_7),
103 MAP (tracker_get_version, TRACKER_0_6),
104 MAP (tracker_cancel_last_call, TRACKER_0_6 | TRACKER_0_7 | TRACKER_0_8),
105 MAP (tracker_search_metadata_by_text_async, TRACKER_0_6 | TRACKER_0_7),
106 MAP (tracker_search_metadata_by_text_and_location_async, TRACKER_0_6 | TRACKER_0_7),
107 MAP (tracker_client_new, TRACKER_0_8),
108 MAP (tracker_sparql_escape, TRACKER_0_8),
109 MAP (tracker_resources_sparql_query_async, TRACKER_0_8)
110 #undef MAP
111 };
112
113
114 static TrackerVersion
open_libtracker(void)115 open_libtracker (void)
116 {
117 static gboolean done = FALSE;
118 static TrackerVersion version = 0;
119 gpointer x;
120
121 if (!done)
122 {
123 int i;
124 GModule *tracker;
125 GModuleFlags flags;
126
127 done = TRUE;
128 flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL;
129
130 tracker = g_module_open ("libtracker-client-0.8.so.0", flags);
131 version = TRACKER_0_8;
132
133 if (!tracker)
134 {
135 tracker = g_module_open ("libtracker-client-0.7.so.0", flags);
136
137 if (tracker && !g_module_symbol (tracker, "tracker_resources_sparql_query_async", &x))
138 {
139 version = TRACKER_0_7;
140 }
141 }
142
143 if (!tracker)
144 {
145 tracker = g_module_open ("libtrackerclient.so.0", flags);
146 version = TRACKER_0_6;
147 }
148
149 if (!tracker)
150 {
151 tracker = g_module_open ("libtracker.so.0", flags);
152 version = TRACKER_0_6;
153 }
154
155 if (!tracker)
156 return 0;
157
158 for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
159 {
160 if ((tracker_dl_mapping[i].versions & version) == 0)
161 continue;
162
163 if (!g_module_symbol (tracker, tracker_dl_mapping[i].fn_name,
164 tracker_dl_mapping[i].fn_ptr_ref))
165 {
166 g_warning ("Missing symbol '%s' in libtracker\n",
167 tracker_dl_mapping[i].fn_name);
168 g_module_close (tracker);
169
170 for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
171 tracker_dl_mapping[i].fn_ptr_ref = NULL;
172
173 return 0;
174 }
175 }
176 }
177
178 return version;
179 }
180
181
182 struct CajaSearchEngineTrackerDetails
183 {
184 CajaQuery *query;
185 TrackerClient *client;
186 gboolean query_pending;
187 TrackerVersion version;
188 };
189
190 G_DEFINE_TYPE (CajaSearchEngineTracker,
191 caja_search_engine_tracker,
192 CAJA_TYPE_SEARCH_ENGINE);
193
194 static CajaSearchEngineClass *parent_class = NULL;
195
196 static void
finalize(GObject * object)197 finalize (GObject *object)
198 {
199 CajaSearchEngineTracker *tracker;
200
201 tracker = CAJA_SEARCH_ENGINE_TRACKER (object);
202
203 if (tracker->details->query)
204 {
205 g_object_unref (tracker->details->query);
206 tracker->details->query = NULL;
207 }
208
209 if (tracker->details->version == TRACKER_0_8)
210 {
211 g_object_unref (tracker->details->client);
212 }
213 else
214 {
215 tracker_disconnect (tracker->details->client);
216 }
217
218 g_free (tracker->details);
219
220 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
221 }
222
223
224 /* stolen from tracker sources, tracker.c */
225 static void
sparql_append_string_literal(GString * sparql,const gchar * str)226 sparql_append_string_literal (GString *sparql,
227 const gchar *str)
228 {
229 char *s;
230
231 s = tracker_sparql_escape (str);
232
233 g_string_append_c (sparql, '"');
234 g_string_append (sparql, s);
235 g_string_append_c (sparql, '"');
236
237 g_free (s);
238 }
239
240
241 static void
search_callback(gpointer results,GError * error,gpointer user_data)242 search_callback (gpointer results, GError *error, gpointer user_data)
243 {
244 CajaSearchEngineTracker *tracker;
245 GList *hit_uris;
246 gint i;
247 char *uri;
248
249 tracker = CAJA_SEARCH_ENGINE_TRACKER (user_data);
250 hit_uris = NULL;
251
252 tracker->details->query_pending = FALSE;
253
254 if (error)
255 {
256 caja_search_engine_error (CAJA_SEARCH_ENGINE (tracker), error->message);
257 g_error_free (error);
258 return;
259 }
260
261 if (! results)
262 {
263 return;
264 }
265
266 if (tracker->details->version == TRACKER_0_8)
267 {
268 GPtrArray *OUT_result;
269
270 /* new tracker 0.8 API */
271 OUT_result = (GPtrArray*) results;
272
273 for (i = 0; i < OUT_result->len; i++)
274 {
275 uri = g_strdup (((gchar **) OUT_result->pdata[i])[0]);
276 if (uri)
277 {
278 hit_uris = g_list_prepend (hit_uris, (char *)uri);
279 }
280 }
281
282 g_ptr_array_foreach (OUT_result, (GFunc) g_free, NULL);
283 g_ptr_array_free (OUT_result, TRUE);
284
285 }
286 else
287 {
288 char **results_p;
289
290 /* old tracker 0.6 API */
291 for (results_p = results; *results_p; results_p++)
292 {
293 if (tracker->details->version == TRACKER_0_6)
294 uri = g_filename_to_uri (*results_p, NULL, NULL);
295 else
296 uri = g_strdup (*results_p);
297
298 if (uri)
299 {
300 hit_uris = g_list_prepend (hit_uris, (char *)uri);
301 }
302 }
303 g_strfreev ((gchar **)results);
304 }
305
306 caja_search_engine_hits_added (CAJA_SEARCH_ENGINE (tracker), hit_uris);
307 caja_search_engine_finished (CAJA_SEARCH_ENGINE (tracker));
308 g_list_free_full (hit_uris, g_free);
309 }
310
311
312 static void
caja_search_engine_tracker_start(CajaSearchEngine * engine)313 caja_search_engine_tracker_start (CajaSearchEngine *engine)
314 {
315 CajaSearchEngineTracker *tracker;
316 GList *mimetypes, *l;
317 char *search_text, *location, *location_uri;
318 char **mimes;
319 int i, mime_count;
320 GString *sparql;
321
322 tracker = CAJA_SEARCH_ENGINE_TRACKER (engine);
323
324
325 if (tracker->details->query_pending)
326 {
327 return;
328 }
329
330 if (tracker->details->query == NULL)
331 {
332 return;
333 }
334
335 search_text = caja_query_get_text (tracker->details->query);
336
337 mimetypes = caja_query_get_mime_types (tracker->details->query);
338
339 location_uri = caja_query_get_location (tracker->details->query);
340
341 if (location_uri)
342 {
343 location = (tracker->details->version == TRACKER_0_6) ?
344 g_filename_from_uri (location_uri, NULL, NULL) :
345 g_strdup (location_uri);
346 g_free (location_uri);
347 }
348 else
349 {
350 location = NULL;
351 }
352
353 mime_count = g_list_length (mimetypes);
354
355 i = 0;
356 sparql = NULL;
357
358 if (tracker->details->version == TRACKER_0_8)
359 {
360 /* new tracker 0.8 API */
361 sparql = g_string_new ("SELECT ?url WHERE { ?file a nfo:FileDataObject ; nie:url ?url; ");
362 if (mime_count > 0)
363 g_string_append (sparql, "nie:mimeType ?mime ; ");
364 g_string_append (sparql, "fts:match ");
365 sparql_append_string_literal (sparql, search_text);
366
367 if (location || mime_count > 0)
368 {
369 g_string_append (sparql, " . FILTER (");
370
371 if (location)
372 {
373 g_string_append (sparql, "fn:starts-with(?url, ");
374 sparql_append_string_literal (sparql, location);
375 g_string_append (sparql, ")");
376 }
377
378 if (mime_count > 0)
379 {
380 if (location)
381 g_string_append (sparql, " && ");
382 g_string_append (sparql, "(");
383 for (l = mimetypes; l != NULL; l = l->next)
384 {
385 if (l != mimetypes)
386 g_string_append (sparql, " || ");
387 g_string_append (sparql, "?mime = ");
388 sparql_append_string_literal (sparql, l->data);
389 }
390 g_string_append (sparql, ")");
391 }
392
393 g_string_append (sparql, ")");
394 }
395 g_string_append (sparql, " }");
396
397 tracker_resources_sparql_query_async (tracker->details->client,
398 sparql->str,
399 (TrackerReplyGPtrArray) search_callback,
400 tracker);
401 g_string_free (sparql, TRUE);
402
403 }
404 else
405 {
406 /* old tracker 0.6 API */
407 if (mime_count > 0)
408 {
409 /* convert list into array */
410 mimes = g_new (char *, (mime_count + 1));
411
412 for (l = mimetypes; l != NULL; l = l->next)
413 {
414 mimes[i] = g_strdup (l->data);
415 i++;
416 }
417
418 mimes[mime_count] = NULL;
419
420 if (location)
421 {
422 tracker_search_metadata_by_text_and_mime_and_location_async (tracker->details->client,
423 search_text, (const char **)mimes, location,
424 (TrackerArrayReply) search_callback,
425 tracker);
426 }
427 else
428 {
429 tracker_search_metadata_by_text_and_mime_async (tracker->details->client,
430 search_text, (const char**)mimes,
431 (TrackerArrayReply) search_callback,
432 tracker);
433 }
434
435 g_strfreev (mimes);
436
437 }
438 else
439 {
440 if (location)
441 {
442 tracker_search_metadata_by_text_and_location_async (tracker->details->client,
443 search_text,
444 location,
445 (TrackerArrayReply) search_callback,
446 tracker);
447 }
448 else
449 {
450 tracker_search_metadata_by_text_async (tracker->details->client,
451 search_text,
452 (TrackerArrayReply) search_callback,
453 tracker);
454 }
455 }
456 }
457
458 g_free (location);
459
460 tracker->details->query_pending = TRUE;
461 g_free (search_text);
462 g_list_free_full (mimetypes, g_free);
463 }
464
465 static void
caja_search_engine_tracker_stop(CajaSearchEngine * engine)466 caja_search_engine_tracker_stop (CajaSearchEngine *engine)
467 {
468 CajaSearchEngineTracker *tracker;
469
470 tracker = CAJA_SEARCH_ENGINE_TRACKER (engine);
471
472 if (tracker->details->query && tracker->details->query_pending)
473 {
474 tracker_cancel_last_call (tracker->details->client);
475 tracker->details->query_pending = FALSE;
476 }
477 }
478
479 static gboolean
caja_search_engine_tracker_is_indexed(CajaSearchEngine * engine)480 caja_search_engine_tracker_is_indexed (CajaSearchEngine *engine)
481 {
482 return TRUE;
483 }
484
485 static void
caja_search_engine_tracker_set_query(CajaSearchEngine * engine,CajaQuery * query)486 caja_search_engine_tracker_set_query (CajaSearchEngine *engine, CajaQuery *query)
487 {
488 CajaSearchEngineTracker *tracker;
489
490 tracker = CAJA_SEARCH_ENGINE_TRACKER (engine);
491
492 if (query)
493 {
494 g_object_ref (query);
495 }
496
497 if (tracker->details->query)
498 {
499 g_object_unref (tracker->details->query);
500 }
501
502 tracker->details->query = query;
503 }
504
505 static void
caja_search_engine_tracker_class_init(CajaSearchEngineTrackerClass * class)506 caja_search_engine_tracker_class_init (CajaSearchEngineTrackerClass *class)
507 {
508 GObjectClass *gobject_class;
509 CajaSearchEngineClass *engine_class;
510
511 parent_class = g_type_class_peek_parent (class);
512
513 gobject_class = G_OBJECT_CLASS (class);
514 gobject_class->finalize = finalize;
515
516 engine_class = CAJA_SEARCH_ENGINE_CLASS (class);
517 engine_class->set_query = caja_search_engine_tracker_set_query;
518 engine_class->start = caja_search_engine_tracker_start;
519 engine_class->stop = caja_search_engine_tracker_stop;
520 engine_class->is_indexed = caja_search_engine_tracker_is_indexed;
521 }
522
523 static void
caja_search_engine_tracker_init(CajaSearchEngineTracker * engine)524 caja_search_engine_tracker_init (CajaSearchEngineTracker *engine)
525 {
526 engine->details = g_new0 (CajaSearchEngineTrackerDetails, 1);
527 }
528
529
530 CajaSearchEngine *
caja_search_engine_tracker_new(void)531 caja_search_engine_tracker_new (void)
532 {
533 CajaSearchEngineTracker *engine;
534 TrackerClient *tracker_client;
535 TrackerVersion version;
536
537 version = open_libtracker ();
538
539 if (version == TRACKER_0_8)
540 {
541 tracker_client = tracker_client_new (TRACKER_CLIENT_ENABLE_WARNINGS, G_MAXINT);
542 }
543 else
544 {
545 if (! tracker_connect)
546 return NULL;
547
548 tracker_client = tracker_connect (FALSE, -1);
549 }
550
551 if (!tracker_client)
552 {
553 return NULL;
554 }
555
556 if (version == TRACKER_0_6)
557 {
558 GError *err = NULL;
559
560 tracker_get_version (tracker_client, &err);
561
562 if (err != NULL)
563 {
564 g_error_free (err);
565 tracker_disconnect (tracker_client);
566 return NULL;
567 }
568 }
569
570 engine = g_object_new (CAJA_TYPE_SEARCH_ENGINE_TRACKER, NULL);
571
572 engine->details->client = tracker_client;
573 engine->details->query_pending = FALSE;
574 engine->details->version = version;
575
576 return CAJA_SEARCH_ENGINE (engine);
577 }
578