]> arthur.barton.de Git - netatalk.git/blob - etc/spotlight/slmod_rdf.c
Basic Tracker RDF queries working
[netatalk.git] / etc / spotlight / slmod_rdf.c
1 /*
2   Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program 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
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <string.h>
20 #include <locale.h>
21
22 #include <gio/gio.h>
23 #include <tracker.h>
24
25 #include <atalk/util.h>
26 #include <atalk/errchk.h>
27 #include <atalk/logger.h>
28 #include <atalk/unix.h>
29 #include <atalk/spotlight.h>
30
31 #include "slmod_rdf_parser.h"
32
33 #define MAX_SL_RESULTS 20
34
35 TrackerClient *client;
36
37 static int sl_mod_init(void *p)
38 {
39     EC_INIT;
40     GError *error = NULL;
41     const char *msg = p;
42
43     LOG(log_info, logtype_sl, "Initializing Tracker 0.6 RDF Spotlight module");
44
45     g_type_init();
46     setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/spotlight.ipc", 1);
47
48     become_root();
49     client = tracker_connect(FALSE);
50     unbecome_root();
51
52     if (!client) {
53         LOG(log_error, logtype_sl, "Failed connecting to Tracker");
54         EC_FAIL;
55     }
56
57 EC_CLEANUP:
58     EC_EXIT;
59 }
60
61
62 static int sl_mod_start_search(void *p)
63 {
64     EC_INIT;
65     slq_t *slq = p; 
66     GError *error = NULL;
67
68     LOG(log_debug, logtype_sl, "sl_mod_start_search: Spotlight query: \"%s\"", slq->slq_qstring);
69
70     EC_ZERO_LOG( map_spotlight_to_rdf_query(slq,
71                                             &slq->slq_trackerquery,
72                                             &slq->slq_fts) );
73
74     LOG(log_debug, logtype_sl, "sl_mod_start_search: Tracker service: %s, FTS: %s, RDF query:\n%s",
75         tracker_type_to_service_name(slq->slq_service),
76         slq->slq_fts,
77         slq->slq_trackerquery ? slq->slq_trackerquery : "false");
78
79     if (slq->slq_trackerquery)
80         slq->slq_state = SLQ_STATE_RUNNING;
81     else
82         slq->slq_state = SLQ_STATE_DONE;
83
84 EC_CLEANUP:
85     EC_EXIT;
86 }
87
88 static int add_filemeta(sl_array_t *reqinfo, sl_array_t *fm_array, cnid_t id, const char *path)
89 {
90     EC_INIT;
91     sl_array_t *meta;
92     sl_nil_t nil = 0;
93     int i, metacount;
94
95     if ((metacount = talloc_array_length(reqinfo->dd_talloc_array)) == 0) {
96         dalloc_add_copy(fm_array, &nil, sl_nil_t);
97         goto EC_CLEANUP;
98     }
99
100     LOG(log_debug, logtype_sl, "add_filemeta: metadata count: %d", metacount);
101
102     meta = talloc_zero(fm_array, sl_array_t);
103
104     for (i = 0; i < metacount; i++) {
105         if (STRCMP(reqinfo->dd_talloc_array[i], ==, "kMDItemDisplayName")) {
106             char *p, *name;
107             if ((p = strrchr(path, '/'))) {
108                 name = dalloc_strdup(meta, p + 1);
109                 dalloc_add(meta, name, "char *");
110             }
111         } else {
112             dalloc_add_copy(meta, &nil, sl_nil_t);
113         }
114     }
115
116     dalloc_add(fm_array, meta, sl_array_t);
117
118 EC_CLEANUP:
119     EC_EXIT;
120 }
121
122 static int cnid_cmp_fn(const void *p1, const void *p2)
123 {
124     const uint64_t *cnid1 = p1, *cnid2 = p2;
125     if (*cnid1 == *cnid2)
126         return 0;
127     if (*cnid1 < *cnid2)
128         return -1;
129     else
130         return 1;            
131 }
132
133 #if 0
134 static void get_meta_table_data (gpointer value)
135 {
136     gchar **meta;
137
138     meta = value;
139 }
140 #endif
141
142 static int sl_mod_fetch_result(void *p)
143 {
144     EC_INIT;
145     slq_t *slq = p;
146     GError *error = NULL;
147     int i = 0;
148     cnid_t did, id;
149     sl_cnids_t *cnids;
150     sl_filemeta_t *fm;
151     sl_array_t *fm_array;
152     sl_nil_t nil;
153     uint64_t uint64;
154     gboolean qres, firstmatch = true;
155     GPtrArray *array = NULL;
156
157     /* Prepare CNIDs */
158     cnids = talloc_zero(slq->slq_reply, sl_cnids_t);
159     cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
160     cnids->ca_unkn1 = 0xadd;
161     cnids->ca_context = slq->slq_ctx2;
162
163     /* Prepare FileMeta */
164     fm = talloc_zero(slq->slq_reply, sl_filemeta_t);
165     fm_array = talloc_zero(fm, sl_array_t);
166     dalloc_add(fm, fm_array, sl_array_t);
167
168     LOG(log_debug, logtype_sl, "sl_mod_fetch_result");
169
170     if (slq->slq_state == SLQ_STATE_RUNNING) {
171         /* Run the query */
172         LOG(log_debug, logtype_sl, "sl_mod_fetch_result: calling tracker");
173         become_root();
174         array = tracker_search_query(client,
175                                      time(NULL),
176                                      slq->slq_service,
177                                      NULL, /* Fields */
178                                      slq->slq_fts, /* FTS search test */
179                                      NULL,         /* Keywords */
180                                      slq->slq_trackerquery,
181                                      slq->slq_offset,
182                                      MAX_SL_RESULTS,
183                                      FALSE, /* Sort by service */
184                                      NULL,
185                                      FALSE,
186                                      &error);
187         unbecome_root();
188
189         if (error) {
190             slq->slq_state = SLQ_STATE_DONE;
191             LOG(log_error, logtype_sl, "Couldn't query Tracker: '%s'", error->message);
192             g_clear_error(&error);
193             EC_FAIL;
194         }
195
196         if (!array) {
197             slq->slq_state = SLQ_STATE_DONE;
198             LOG(log_debug, logtype_sl, "sl_mod_fetch_result: no results found");
199             EC_EXIT_STATUS(0);
200         }
201
202         while (i < array->len) {
203             char **resmeta = g_ptr_array_index(array, i);
204             char *respath = resmeta[0];
205             LOG(log_debug, logtype_sl, "sl_mod_fetch_result: result %d: %s", slq->slq_offset, respath);
206
207             if (firstmatch) {
208                 /* For some reason the list of results always starts with a nil entry */
209                 dalloc_add_copy(fm_array, &nil, sl_nil_t);
210                 firstmatch = false;
211             }
212
213             if ((id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, respath, &did)) == CNID_INVALID) {
214                 LOG(log_error, logtype_sl, "sl_mod_fetch_result: cnid_for_path error: %s", respath);
215                 goto loop_continue;
216             }
217             LOG(log_debug, logtype_sl, "Result %d: CNID: %" PRIu32 ", path: \"%s\"", i, ntohl(id), respath);
218
219             uint64 = ntohl(id);
220             if (slq->slq_cnids) {
221                 if (!bsearch(&uint64, slq->slq_cnids, slq->slq_cnids_num, sizeof(uint64_t), cnid_cmp_fn))
222                     goto loop_continue;
223             }
224
225             dalloc_add_copy(cnids->ca_cnids, &uint64, uint64_t);
226             add_filemeta(slq->slq_reqinfo, fm_array, id, respath);
227
228         loop_continue:
229             i++;
230             slq->slq_offset++;
231         }
232
233         g_ptr_array_free(array, TRUE);
234         array = NULL;
235
236         if (i < MAX_SL_RESULTS)
237             slq->slq_state = SLQ_STATE_DONE;
238     }
239
240     uint64 = (i > 0) ? 35 : 0; /* OS X AFP server returns 35 here if results are found */
241     dalloc_add_copy(slq->slq_reply, &uint64, uint64_t);
242     dalloc_add(slq->slq_reply, cnids, sl_cnids_t);
243     dalloc_add(slq->slq_reply, fm, sl_filemeta_t);
244
245 EC_CLEANUP:
246     if (array)
247         g_ptr_array_free(array, TRUE);
248     EC_EXIT;
249 }
250
251 /* Free ressources allocated in this module */
252 static int sl_mod_close_query(void *p)
253 {
254     EC_INIT;
255     slq_t *slq = p;
256
257 EC_CLEANUP:
258     EC_EXIT;
259 }
260
261 static int sl_mod_error(void *p)
262 {
263     EC_INIT;
264     slq_t *slq = p;
265
266     if (!slq)
267         goto EC_CLEANUP;
268
269 EC_CLEANUP:
270     EC_EXIT;
271 }
272
273 static int sl_mod_index_file(const void *p)
274 {
275     return 0;
276 }
277
278 struct sl_module_export sl_mod = {
279     SL_MODULE_VERSION,
280     sl_mod_init,
281     sl_mod_start_search,
282     sl_mod_fetch_result,
283     sl_mod_close_query,
284     sl_mod_error,
285     sl_mod_index_file
286 };