1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Copyright (C) 2005 Novell, Inc.
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: Anders Carlsson <andersca@imendio.com>
21 *
22 */
23
24 #include <config.h>
25 #include <gmodule.h>
26
27 #include <eel/eel-gtk-macros.h>
28
29 #include "caja-search-engine-beagle.h"
30
31 typedef struct _BeagleHit BeagleHit;
32 typedef struct _BeagleQuery BeagleQuery;
33 typedef struct _BeagleClient BeagleClient;
34 typedef struct _BeagleRequest BeagleRequest;
35 typedef struct _BeagleFinishedResponse BeagleFinishedResponse;
36 typedef struct _BeagleHitsAddedResponse BeagleHitsAddedResponse;
37 typedef struct _BeagleQueryPartProperty BeagleQueryPartProperty;
38 typedef struct _BeagleQueryPart BeagleQueryPart;
39 typedef struct _BeagleHitsSubtractedResponse BeagleHitsSubtractedResponse;
40
41 struct CajaSearchEngineBeagleDetails
42 {
43 BeagleClient *client;
44 CajaQuery *query;
45
46 BeagleQuery *current_query;
47 char *current_query_uri_prefix;
48 gboolean query_finished;
49 };
50
51 /* We dlopen() all the following from libbeagle at runtime */
52 #define BEAGLE_HIT(x) ((BeagleHit *)(x))
53 #define BEAGLE_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), beagle_request_get_type(), BeagleRequest))
54
55 typedef enum
56 {
57 BEAGLE_QUERY_PART_LOGIC_REQUIRED = 1,
58 BEAGLE_QUERY_PART_LOGIC_PROHIBITED = 2
59 } BeagleQueryPartLogic;
60
61 typedef enum
62 {
63 BEAGLE_PROPERTY_TYPE_UNKNOWN = 0,
64 BEAGLE_PROPERTY_TYPE_TEXT = 1,
65 BEAGLE_PROPERTY_TYPE_KEYWORD = 2,
66 BEAGLE_PROPERTY_TYPE_DATE = 3,
67 BEAGLE_PROPERTY_TYPE_LAST = 4
68 } BeaglePropertyType;
69
70 /* *static* wrapper function pointers */
71 static gboolean (*beagle_client_send_request_async) (BeagleClient *client,
72 BeagleRequest *request,
73 GError **err) = NULL;
74 static const char* (*beagle_hit_get_uri)(BeagleHit* hit) = NULL;
75 static GSList *(*beagle_hits_added_response_get_hits) (BeagleHitsAddedResponse *response) = NULL;
76 static BeagleQuery *(*beagle_query_new) (void) = NULL;
77 static void (*beagle_query_add_text) (BeagleQuery *query,
78 const char *str) = NULL;
79 static BeagleQueryPartProperty *(*beagle_query_part_property_new) (void) = NULL;
80 static void (*beagle_query_part_set_logic) (BeagleQueryPart *part,
81 BeagleQueryPartLogic logic) = NULL;
82 static void (*beagle_query_part_property_set_key) (BeagleQueryPartProperty *part,
83 const char *key) = NULL;
84 static void (*beagle_query_part_property_set_value) (BeagleQueryPartProperty *part,
85 const char * value) = NULL;
86 static void (*beagle_query_part_property_set_property_type) (BeagleQueryPartProperty *part,
87 BeaglePropertyType prop_type) = NULL;
88 static void (*beagle_query_add_part) (BeagleQuery *query,
89 BeagleQueryPart *part) = NULL;
90 static GType (*beagle_request_get_type) (void) = NULL;
91 static GType (*beagle_query_part_get_type) (void) = NULL;
92 static gboolean (*beagle_util_daemon_is_running) (void) = NULL;
93 static BeagleClient *(*beagle_client_new_real) (const char *client_name) = NULL;
94 static void (*beagle_query_set_max_hits) (BeagleQuery *query,
95 int max_hits) = NULL;
96 static GSList *(*beagle_hits_subtracted_response_get_uris) (BeagleHitsSubtractedResponse *response) = NULL;
97
98 static struct BeagleDlMapping
99 {
100 const char *fn_name;
101 gpointer *fn_ptr_ref;
102 } beagle_dl_mapping[] =
103 {
104 #define MAP(a) { #a, (gpointer *)&a }
105 MAP (beagle_client_send_request_async),
106 MAP (beagle_hit_get_uri),
107 MAP (beagle_hits_added_response_get_hits),
108 MAP (beagle_query_new),
109 MAP (beagle_query_add_text),
110 MAP (beagle_query_part_property_new),
111 MAP (beagle_query_part_set_logic),
112 MAP (beagle_query_part_property_set_key),
113 MAP (beagle_query_part_property_set_value),
114 MAP (beagle_query_part_property_set_property_type),
115 MAP (beagle_query_add_part),
116 MAP (beagle_request_get_type),
117 MAP (beagle_query_part_get_type),
118 MAP (beagle_util_daemon_is_running),
119 MAP (beagle_query_set_max_hits),
120 MAP (beagle_hits_subtracted_response_get_uris),
121 #undef MAP
122 { "beagle_client_new", (gpointer *)&beagle_client_new_real },
123 };
124
125 static void
open_libbeagle(void)126 open_libbeagle (void)
127 {
128 static gboolean done = FALSE;
129
130 if (!done)
131 {
132 int i;
133 GModule *beagle;
134
135 done = TRUE;
136
137 beagle = g_module_open ("libbeagle.so.1", G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
138 if (!beagle)
139 return;
140
141 for (i = 0; i < G_N_ELEMENTS (beagle_dl_mapping); i++)
142 {
143 if (!g_module_symbol (beagle, beagle_dl_mapping[i].fn_name,
144 beagle_dl_mapping[i].fn_ptr_ref))
145 {
146 g_warning ("Missing symbol '%s' in libbeagle\n",
147 beagle_dl_mapping[i].fn_name);
148 g_module_close (beagle);
149
150 for (i = 0; i < G_N_ELEMENTS (beagle_dl_mapping); i++)
151 beagle_dl_mapping[i].fn_ptr_ref = NULL;
152
153 return;
154 }
155 }
156 }
157 }
158
159 static BeagleClient *
beagle_client_new(const char * client_name)160 beagle_client_new (const char *client_name)
161 {
162 if (beagle_client_new_real)
163 return beagle_client_new_real (client_name);
164
165 return NULL;
166 }
167
168 G_DEFINE_TYPE (CajaSearchEngineBeagle,
169 caja_search_engine_beagle,
170 CAJA_TYPE_SEARCH_ENGINE);
171
172 static CajaSearchEngineClass *parent_class = NULL;
173
174 static void
finalize(GObject * object)175 finalize (GObject *object)
176 {
177 CajaSearchEngineBeagle *beagle;
178
179 beagle = CAJA_SEARCH_ENGINE_BEAGLE (object);
180
181 if (beagle->details->current_query)
182 {
183 g_object_unref (beagle->details->current_query);
184 beagle->details->current_query = NULL;
185 g_free (beagle->details->current_query_uri_prefix);
186 beagle->details->current_query_uri_prefix = NULL;
187 }
188
189 if (beagle->details->query)
190 {
191 g_object_unref (beagle->details->query);
192 beagle->details->query = NULL;
193 }
194
195 if (beagle->details->client)
196 {
197 g_object_unref (beagle->details->client);
198 beagle->details->client = NULL;
199 }
200
201 g_free (beagle->details);
202
203 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
204 }
205
206 static void
beagle_hits_added(BeagleQuery * query,BeagleHitsAddedResponse * response,CajaSearchEngineBeagle * engine)207 beagle_hits_added (BeagleQuery *query,
208 BeagleHitsAddedResponse *response,
209 CajaSearchEngineBeagle *engine)
210 {
211 GSList *hits, *list;
212 GList *hit_uris;
213
214 hit_uris = NULL;
215
216 hits = beagle_hits_added_response_get_hits (response);
217
218 for (list = hits; list != NULL; list = list->next)
219 {
220 const char *uri;
221
222 BeagleHit *hit = BEAGLE_HIT (list->data);
223
224 uri = beagle_hit_get_uri (hit);
225
226 if (engine->details->current_query_uri_prefix &&
227 !g_str_has_prefix (uri, engine->details->current_query_uri_prefix))
228 {
229 continue;
230 }
231
232 hit_uris = g_list_prepend (hit_uris, (char *)uri);
233 }
234
235 caja_search_engine_hits_added (CAJA_SEARCH_ENGINE (engine), hit_uris);
236 g_list_free (hit_uris);
237 }
238
239 static void
beagle_hits_subtracted(BeagleQuery * query,BeagleHitsSubtractedResponse * response,CajaSearchEngineBeagle * engine)240 beagle_hits_subtracted (BeagleQuery *query,
241 BeagleHitsSubtractedResponse *response,
242 CajaSearchEngineBeagle *engine)
243 {
244 GSList *uris, *list;
245 GList *hit_uris;
246
247 hit_uris = NULL;
248
249 uris = beagle_hits_subtracted_response_get_uris (response);
250
251 for (list = uris; list != NULL; list = list->next)
252 {
253 hit_uris = g_list_prepend (hit_uris, (char *)list->data);
254 }
255
256 caja_search_engine_hits_subtracted (CAJA_SEARCH_ENGINE (engine), hit_uris);
257 g_list_free (hit_uris);
258 }
259
260 static void
beagle_finished(BeagleQuery * query,BeagleFinishedResponse * response,CajaSearchEngineBeagle * engine)261 beagle_finished (BeagleQuery *query,
262 BeagleFinishedResponse *response,
263 CajaSearchEngineBeagle *engine)
264 {
265 /* For some reason we keep getting finished events,
266 * only emit finished once */
267 if (engine->details->query_finished)
268 {
269 return;
270 }
271
272 engine->details->query_finished = TRUE;
273 caja_search_engine_finished (CAJA_SEARCH_ENGINE (engine));
274 }
275
276 static void
beagle_error(BeagleQuery * query,GError * error,CajaSearchEngineBeagle * engine)277 beagle_error (BeagleQuery *query,
278 GError *error,
279 CajaSearchEngineBeagle *engine)
280 {
281 caja_search_engine_error (CAJA_SEARCH_ENGINE (engine), error->message);
282 }
283
284 static void
caja_search_engine_beagle_start(CajaSearchEngine * engine)285 caja_search_engine_beagle_start (CajaSearchEngine *engine)
286 {
287 CajaSearchEngineBeagle *beagle;
288 GError *error;
289 GList *mimetypes, *l;
290 char *text;
291
292 error = NULL;
293 beagle = CAJA_SEARCH_ENGINE_BEAGLE (engine);
294
295 if (beagle->details->current_query)
296 {
297 return;
298 }
299
300 beagle->details->query_finished = FALSE;
301 beagle->details->current_query = beagle_query_new ();
302 g_signal_connect (beagle->details->current_query,
303 "hits-added", G_CALLBACK (beagle_hits_added), engine);
304 g_signal_connect (beagle->details->current_query,
305 "hits-subtracted", G_CALLBACK (beagle_hits_subtracted), engine);
306 g_signal_connect (beagle->details->current_query,
307 "finished", G_CALLBACK (beagle_finished), engine);
308 g_signal_connect (beagle->details->current_query,
309 "error", G_CALLBACK (beagle_error), engine);
310
311 /* We only want files */
312 beagle_query_add_text (beagle->details->current_query," type:File");
313
314 beagle_query_set_max_hits (beagle->details->current_query,
315 1000);
316
317 text = caja_query_get_text (beagle->details->query);
318 beagle_query_add_text (beagle->details->current_query,
319 text);
320
321 mimetypes = caja_query_get_mime_types (beagle->details->query);
322 for (l = mimetypes; l != NULL; l = l->next)
323 {
324 char* temp;
325 char *mimetype;
326
327 mimetype = l->data;
328 temp = g_strconcat (" mimetype:", mimetype, NULL);
329 beagle_query_add_text (beagle->details->current_query,temp);
330 g_free (temp);
331 }
332
333 beagle->details->current_query_uri_prefix = caja_query_get_location (beagle->details->query);
334
335 if (!beagle_client_send_request_async (beagle->details->client,
336 BEAGLE_REQUEST (beagle->details->current_query), &error))
337 {
338 caja_search_engine_error (engine, error->message);
339 g_error_free (error);
340 }
341
342 /* These must live during the lifetime of the query */
343 g_free (text);
344 g_list_free_full (mimetypes, g_free);
345 }
346
347 static void
caja_search_engine_beagle_stop(CajaSearchEngine * engine)348 caja_search_engine_beagle_stop (CajaSearchEngine *engine)
349 {
350 CajaSearchEngineBeagle *beagle;
351
352 beagle = CAJA_SEARCH_ENGINE_BEAGLE (engine);
353
354 if (beagle->details->current_query)
355 {
356 g_object_unref (beagle->details->current_query);
357 beagle->details->current_query = NULL;
358 g_free (beagle->details->current_query_uri_prefix);
359 beagle->details->current_query_uri_prefix = NULL;
360 }
361 }
362
363 static gboolean
caja_search_engine_beagle_is_indexed(CajaSearchEngine * engine)364 caja_search_engine_beagle_is_indexed (CajaSearchEngine *engine)
365 {
366 return TRUE;
367 }
368
369 static void
caja_search_engine_beagle_set_query(CajaSearchEngine * engine,CajaQuery * query)370 caja_search_engine_beagle_set_query (CajaSearchEngine *engine, CajaQuery *query)
371 {
372 CajaSearchEngineBeagle *beagle;
373
374 beagle = CAJA_SEARCH_ENGINE_BEAGLE (engine);
375
376 if (query)
377 {
378 g_object_ref (query);
379 }
380
381 if (beagle->details->query)
382 {
383 g_object_unref (beagle->details->query);
384 }
385
386 beagle->details->query = query;
387 }
388
389 static void
caja_search_engine_beagle_class_init(CajaSearchEngineBeagleClass * class)390 caja_search_engine_beagle_class_init (CajaSearchEngineBeagleClass *class)
391 {
392 GObjectClass *gobject_class;
393 CajaSearchEngineClass *engine_class;
394
395 parent_class = g_type_class_peek_parent (class);
396
397 gobject_class = G_OBJECT_CLASS (class);
398 gobject_class->finalize = finalize;
399
400 engine_class = CAJA_SEARCH_ENGINE_CLASS (class);
401 engine_class->set_query = caja_search_engine_beagle_set_query;
402 engine_class->start = caja_search_engine_beagle_start;
403 engine_class->stop = caja_search_engine_beagle_stop;
404 engine_class->is_indexed = caja_search_engine_beagle_is_indexed;
405 }
406
407 static void
caja_search_engine_beagle_init(CajaSearchEngineBeagle * engine)408 caja_search_engine_beagle_init (CajaSearchEngineBeagle *engine)
409 {
410 engine->details = g_new0 (CajaSearchEngineBeagleDetails, 1);
411 }
412
413
414 CajaSearchEngine *
caja_search_engine_beagle_new(void)415 caja_search_engine_beagle_new (void)
416 {
417 CajaSearchEngineBeagle *engine;
418 BeagleClient *client;
419
420 open_libbeagle ();
421
422 if (beagle_util_daemon_is_running == NULL ||
423 !beagle_util_daemon_is_running ())
424 {
425 /* check whether daemon is running as beagle_client_new
426 * doesn't fail when a stale socket file exists */
427 return NULL;
428 }
429
430 client = beagle_client_new (NULL);
431
432 if (client == NULL)
433 {
434 return NULL;
435 }
436
437 engine = g_object_new (CAJA_TYPE_SEARCH_ENGINE_BEAGLE, NULL);
438
439 engine->details->client = client;
440
441 return CAJA_SEARCH_ENGINE (engine);
442 }
443