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