s3-mdssvc: factor out Tracker backend logic
[garming/samba-autobuild/.git] / source3 / rpc_server / mdssvc / sparql_parser.y
1 /*
2    Unix SMB/CIFS implementation.
3    Main metadata server / Spotlight routines
4
5    Copyright (C) Ralph Boehme 2012-2014
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 %{
22         #include "includes.h"
23         #include "rpc_server/mdssvc/mdssvc.h"
24         #include "rpc_server/mdssvc/mdssvc_tracker.h"
25         #include "rpc_server/mdssvc/sparql_parser.tab.h"
26         #include "rpc_server/mdssvc/sparql_mapping.h"
27
28         #define YYMALLOC SMB_MALLOC
29         #define YYREALLOC SMB_REALLOC
30
31         struct yy_buffer_state;
32         typedef struct yy_buffer_state *YY_BUFFER_STATE;
33         extern int mdsyylex (void);
34         extern void mdsyyerror (char const *);
35         extern void *mdsyyterminate(void);
36         extern YY_BUFFER_STATE mdsyy_scan_string( const char *str);
37         extern void mdsyy_delete_buffer ( YY_BUFFER_STATE buffer );
38
39         /* forward declarations */
40         static const char *map_expr(const char *attr, char op, const char *val);
41         static const char *map_daterange(const char *dateattr,
42                                          time_t date1, time_t date2);
43         static time_t isodate2unix(const char *s);
44
45         /* global vars, eg needed by the lexer */
46         struct sparql_parser_state {
47                 TALLOC_CTX *frame;
48                 YY_BUFFER_STATE s;
49                 char var;
50                 const char *result;
51         } *global_sparql_parser_state;
52 %}
53
54 %code provides {
55         #include <stdbool.h>
56         #include "rpc_server/mdssvc/mdssvc.h"
57         #define SPRAW_TIME_OFFSET 978307200
58         extern int mdsyywrap(void);
59         extern bool map_spotlight_to_sparql_query(struct sl_query *slq);
60 }
61
62 %union {
63         int ival;
64         const char *sval;
65         bool bval;
66         time_t tval;
67 }
68
69 %name-prefix "mdsyy"
70 %expect 5
71 %error-verbose
72
73 %type <sval> match expr line function
74 %type <tval> date
75
76 %token <sval> WORD
77 %token <bval> BOOL
78 %token FUNC_INRANGE
79 %token DATE_ISO
80 %token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
81 %left AND
82 %left OR
83 %%
84
85 input:
86 /* empty */
87 | input line
88 ;
89
90 line:
91 expr {
92         global_sparql_parser_state->result = $1;
93 }
94 ;
95
96 expr:
97 BOOL {
98         /*
99          * We can't properly handle these in expressions, fortunately this
100          * is probably only ever used by OS X as sole element in an
101          * expression ie "False" (when Finder window selected our share
102          * but no search string entered yet). Packet traces showed that OS
103          * X Spotlight server then returns a failure (ie -1) which is what
104          * we do here too by calling YYABORT.
105          */
106         YYABORT;
107 }
108 /*
109  * We have "match OR match" and "expr OR expr", because the former is
110  * supposed to catch and coalesque expressions of the form
111  *
112  *   MDSattribute1="hello"||MDSattribute2="hello"
113  *
114  * into a single SPARQL expression for the case where both
115  * MDSattribute1 and MDSattribute2 map to the same SPARQL attibute,
116  * which is eg the case for "*" and "kMDItemTextContent" which both
117  * map to SPARQL "fts:match".
118  */
119
120 | match OR match {
121         if (strcmp($1, $3) != 0) {
122                 $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
123         } else {
124                 $$ = talloc_asprintf(talloc_tos(), "%s", $1);
125         }
126 }
127 | match {
128         $$ = $1;
129 }
130 | function {
131         $$ = $1;
132 }
133 | OBRACE expr CBRACE {
134         $$ = talloc_asprintf(talloc_tos(), "%s", $2);
135 }
136 | expr AND expr {
137         $$ = talloc_asprintf(talloc_tos(), "%s . %s", $1, $3);
138 }
139 | expr OR expr {
140         if (strcmp($1, $3) != 0) {
141                 $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
142         } else {
143                 $$ = talloc_asprintf(talloc_tos(), "%s", $1);
144         }
145 }
146 ;
147
148 match:
149 WORD EQUAL QUOTE WORD QUOTE {
150         $$ = map_expr($1, '=', $4);
151         if ($$ == NULL) YYABORT;
152 }
153 | WORD UNEQUAL QUOTE WORD QUOTE {
154         $$ = map_expr($1, '!', $4);
155         if ($$ == NULL) YYABORT;
156 }
157 | WORD LT QUOTE WORD QUOTE {
158         $$ = map_expr($1, '<', $4);
159         if ($$ == NULL) YYABORT;
160 }
161 | WORD GT QUOTE WORD QUOTE {
162         $$ = map_expr($1, '>', $4);
163         if ($$ == NULL) YYABORT;
164 }
165 | WORD EQUAL QUOTE WORD QUOTE WORD {
166         $$ = map_expr($1, '=', $4);
167         if ($$ == NULL) YYABORT;
168 }
169 | WORD UNEQUAL QUOTE WORD QUOTE WORD {
170         $$ = map_expr($1, '!', $4);
171         if ($$ == NULL) YYABORT;
172 }
173 | WORD LT QUOTE WORD QUOTE WORD {
174         $$ = map_expr($1, '<', $4);
175         if ($$ == NULL) YYABORT;
176 }
177 | WORD GT QUOTE WORD QUOTE WORD {
178         $$ = map_expr($1, '>', $4);
179         if ($$ == NULL) YYABORT;
180 }
181 ;
182
183 function:
184 FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {
185         $$ = map_daterange($3, $5, $7);
186         if ($$ == NULL) YYABORT;
187 }
188 ;
189
190 date:
191 DATE_ISO OBRACE WORD CBRACE    {$$ = isodate2unix($3);}
192 | WORD                         {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
193 ;
194
195 %%
196
197 static time_t isodate2unix(const char *s)
198 {
199         struct tm tm;
200         const char *p;
201
202         p = strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm);
203         if (p == NULL) {
204                 return (time_t)-1;
205         }
206         return mktime(&tm);
207 }
208
209 static const char *map_daterange(const char *dateattr,
210                                  time_t date1, time_t date2)
211 {
212         struct sparql_parser_state *s = global_sparql_parser_state;
213         int result = 0;
214         char *sparql = NULL;
215         const struct sl_attr_map *p;
216         struct tm *tmp;
217         char buf1[64], buf2[64];
218
219         if (s->var == 'z') {
220                 return NULL;
221         }
222
223         tmp = localtime(&date1);
224         if (tmp == NULL) {
225                 return NULL;
226         }
227         result = strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
228         if (result == 0) {
229                 return NULL;
230         }
231
232         tmp = localtime(&date2);
233         if (tmp == NULL) {
234                 return NULL;
235         }
236         result = strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
237         if (result == 0) {
238                 return NULL;
239         }
240
241         p = sl_attr_map_by_spotlight(dateattr);
242         if (p == NULL) {
243                 return NULL;
244         }
245
246         sparql = talloc_asprintf(talloc_tos(),
247                                  "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
248                                  p->sparql_attr,
249                                  s->var,
250                                  s->var,
251                                  buf1,
252                                  s->var,
253                                  buf2);
254         if (sparql == NULL) {
255                 return NULL;
256         }
257
258         s->var++;
259         return sparql;
260 }
261
262 static char *map_type_search(const char *attr, char op, const char *val)
263 {
264         char *result = NULL;
265         const char *sparqlAttr;
266         const struct sl_type_map *p;
267
268         p = sl_type_map_by_spotlight(val);
269         if (p == NULL) {
270                 return NULL;
271         }
272
273         switch (p->type) {
274         case kMDTypeMapRDF:
275                 sparqlAttr = "rdf:type";
276                 break;
277         case kMDTypeMapMime:
278                 sparqlAttr = "nie:mimeType";
279                 break;
280         default:
281                 return NULL;
282         }
283
284         result = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
285                                  sparqlAttr,
286                                  p->sparql_type);
287         if (result == NULL) {
288                 return NULL;
289         }
290
291         return result;
292 }
293
294 static const char *map_expr(const char *attr, char op, const char *val)
295 {
296         struct sparql_parser_state *s = global_sparql_parser_state;
297         int result = 0;
298         char *sparql = NULL;
299         const struct sl_attr_map *p;
300         time_t t;
301         struct tm *tmp;
302         char buf1[64];
303         char *q;
304         const char *start;
305
306         if (s->var == 'z') {
307                 return NULL;
308         }
309
310         p = sl_attr_map_by_spotlight(attr);
311         if (p == NULL) {
312                 return NULL;
313         }
314
315         if ((p->type != ssmt_type) && (p->sparql_attr == NULL)) {
316                 yyerror("unsupported Spotlight attribute");
317                 return NULL;
318         }
319
320         switch (p->type) {
321         case ssmt_bool:
322                 sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
323                                          p->sparql_attr, val);
324                 if (sparql == NULL) {
325                         return NULL;
326                 }
327                 break;
328
329         case ssmt_num:
330                 sparql = talloc_asprintf(talloc_tos(),
331                                          "?obj %s ?%c FILTER(?%c %c%c '%s')",
332                                          p->sparql_attr,
333                                          s->var,
334                                          s->var,
335                                          op,
336                                          /* append '=' to '!' */
337                                          op == '!' ? '=' : ' ',
338                                          val);
339                 if (sparql == NULL) {
340                         return NULL;
341                 }
342                 s->var++;
343                 break;
344
345         case ssmt_str:
346                 q = talloc_strdup(talloc_tos(), "");
347                 if (q == NULL) {
348                         return NULL;
349                 }
350                 start = val;
351                 while (*val) {
352                         if (*val != '*') {
353                                 val++;
354                                 continue;
355                         }
356                         if (val > start) {
357                                 q = talloc_strndup_append(q, start, val - start);
358                                 if (q == NULL) {
359                                         return NULL;
360                                 }
361                         }
362                         q = talloc_strdup_append(q, ".*");
363                         if (q == NULL) {
364                                 return NULL;
365                         }
366                         val++;
367                         start = val;
368                 }
369                 if (val > start) {
370                         q = talloc_strndup_append(q, start, val - start);
371                         if (q == NULL) {
372                                 return NULL;
373                         }
374                 }
375                 sparql = talloc_asprintf(talloc_tos(),
376                                          "?obj %s ?%c "
377                                          "FILTER(regex(?%c, '^%s$', 'i'))",
378                                          p->sparql_attr,
379                                          s->var,
380                                          s->var,
381                                          q);
382                 TALLOC_FREE(q);
383                 if (sparql == NULL) {
384                         return NULL;
385                 }
386                 s->var++;
387                 break;
388
389         case ssmt_fts:
390                 sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
391                                          p->sparql_attr, val);
392                 if (sparql == NULL) {
393                         return NULL;
394                 }
395                 break;
396
397         case ssmt_date:
398                 t = atoi(val) + SPRAW_TIME_OFFSET;
399                 tmp = localtime(&t);
400                 if (tmp == NULL) {
401                         return NULL;
402                 }
403                 result = strftime(buf1, sizeof(buf1),
404                                   "%Y-%m-%dT%H:%M:%SZ", tmp);
405                 if (result == 0) {
406                         return NULL;
407                 }
408                 sparql = talloc_asprintf(talloc_tos(),
409                                          "?obj %s ?%c FILTER(?%c %c '%s')",
410                                          p->sparql_attr,
411                                          s->var,
412                                          s->var,
413                                          op,
414                                          buf1);
415                 if (sparql == NULL) {
416                         return NULL;
417                 }
418                 s->var++;
419                 break;
420
421         case ssmt_type:
422                 sparql = map_type_search(attr, op, val);
423                 if (sparql == NULL) {
424                         return NULL;
425                 }
426                 break;
427
428         default:
429                 return NULL;
430         }
431
432         return sparql;
433 }
434
435 void mdsyyerror(const char *str)
436 {
437         DEBUG(1, ("mdsyyerror: %s\n", str));
438 }
439
440 int mdsyywrap(void)
441 {
442         return 1;
443 }
444
445 /**
446  * Map a Spotlight RAW query string to a SPARQL query string
447  **/
448 bool map_spotlight_to_sparql_query(struct sl_query *slq)
449 {
450         struct sl_tracker_query *tq = talloc_get_type_abort(
451                 slq->backend_private, struct sl_tracker_query);
452         struct sparql_parser_state s = {
453                 .frame = talloc_stackframe(),
454                 .var = 'a',
455         };
456         int result;
457
458         s.s = mdsyy_scan_string(slq->query_string);
459         if (s.s == NULL) {
460                 TALLOC_FREE(s.frame);
461                 return false;
462         }
463         global_sparql_parser_state = &s;
464         result = mdsyyparse();
465         global_sparql_parser_state = NULL;
466         mdsyy_delete_buffer(s.s);
467
468         if (result != 0) {
469                 TALLOC_FREE(s.frame);
470                 return false;
471         }
472
473         tq->sparql_query = talloc_asprintf(slq,
474                 "SELECT ?url WHERE { %s . ?obj nie:url ?url . "
475                 "FILTER(tracker:uri-is-descendant('file://%s/', ?url)) }",
476                 s.result, tq->path_scope);
477         TALLOC_FREE(s.frame);
478         if (tq->sparql_query == NULL) {
479                 return false;
480         }
481
482         return true;
483 }