ctdb-server: Remove duplicate logic
[samba4-gss.git] / source3 / rpc_server / mdssvc / sparql_parser.y
blob19d42d4e54da6c9bfc801cc79d557219faab0397
1 /*
2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines
5 Copyright (C) Ralph Boehme 2012-2014
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.
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.
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/>.
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"
28 #define YYMALLOC SMB_MALLOC
29 #define YYREALLOC SMB_REALLOC
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 );
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);
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;
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);
62 %union {
63 int ival;
64 const char *sval;
65 bool bval;
66 time_t tval;
69 %define api.prefix {mdsyy}
70 %expect 5
71 %define parse.error verbose
73 %type <sval> match expr line function
74 %type <tval> date
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
85 input:
86 /* empty */
87 | input line
90 line:
91 expr {
92 global_sparql_parser_state->result = $1;
96 expr:
97 BOOL {
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.
106 YYABORT;
109 * We have "match OR match" and "expr OR expr", because the former is
110 * supposed to catch and coalesque expressions of the form
112 * MDSattribute1="hello"||MDSattribute2="hello"
114 * into a single SPARQL expression for the case where both
115 * MDSattribute1 and MDSattribute2 map to the same SPARQL attribute,
116 * which is eg the case for "*" and "kMDItemTextContent" which both
117 * map to SPARQL "fts:match".
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);
127 | match {
128 $$ = $1;
130 | function {
131 $$ = $1;
133 | OBRACE expr CBRACE {
134 $$ = talloc_asprintf(talloc_tos(), "%s", $2);
136 | expr AND expr {
137 $$ = talloc_asprintf(talloc_tos(), "%s . %s", $1, $3);
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);
148 match:
149 WORD EQUAL QUOTE WORD QUOTE {
150 $$ = map_expr($1, '=', $4);
151 if ($$ == NULL) YYABORT;
153 | WORD UNEQUAL QUOTE WORD QUOTE {
154 $$ = map_expr($1, '!', $4);
155 if ($$ == NULL) YYABORT;
157 | WORD LT QUOTE WORD QUOTE {
158 $$ = map_expr($1, '<', $4);
159 if ($$ == NULL) YYABORT;
161 | WORD GT QUOTE WORD QUOTE {
162 $$ = map_expr($1, '>', $4);
163 if ($$ == NULL) YYABORT;
165 | WORD EQUAL QUOTE WORD QUOTE WORD {
166 $$ = map_expr($1, '=', $4);
167 if ($$ == NULL) YYABORT;
169 | WORD UNEQUAL QUOTE WORD QUOTE WORD {
170 $$ = map_expr($1, '!', $4);
171 if ($$ == NULL) YYABORT;
173 | WORD LT QUOTE WORD QUOTE WORD {
174 $$ = map_expr($1, '<', $4);
175 if ($$ == NULL) YYABORT;
177 | WORD GT QUOTE WORD QUOTE WORD {
178 $$ = map_expr($1, '>', $4);
179 if ($$ == NULL) YYABORT;
183 function:
184 FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {
185 $$ = map_daterange($3, $5, $7);
186 if ($$ == NULL) YYABORT;
190 date:
191 DATE_ISO OBRACE WORD CBRACE {$$ = isodate2unix($3);}
192 | WORD {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
197 static time_t isodate2unix(const char *s)
199 struct tm tm = {};
200 const char *p;
202 p = strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm);
203 if (p == NULL) {
204 return (time_t)-1;
206 return mktime(&tm);
209 static const char *map_daterange(const char *dateattr,
210 time_t date1, time_t date2)
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];
219 if (s->var == 'z') {
220 return NULL;
223 tmp = localtime(&date1);
224 if (tmp == NULL) {
225 return NULL;
227 result = strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
228 if (result == 0) {
229 return NULL;
232 tmp = localtime(&date2);
233 if (tmp == NULL) {
234 return NULL;
236 result = strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
237 if (result == 0) {
238 return NULL;
241 p = sl_attr_map_by_spotlight(dateattr);
242 if (p == NULL) {
243 return NULL;
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;
258 s->var++;
259 return sparql;
262 static char *map_type_search(const char *attr, char op, const char *val)
264 char *result = NULL;
265 const char *sparqlAttr;
266 const struct sl_type_map *p;
268 p = sl_type_map_by_spotlight(val);
269 if (p == NULL) {
270 return NULL;
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;
284 result = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
285 sparqlAttr,
286 p->sparql_type);
287 if (result == NULL) {
288 return NULL;
291 return result;
294 static const char *map_expr(const char *attr, char op, const char *val)
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;
306 if (s->var == 'z') {
307 return NULL;
310 p = sl_attr_map_by_spotlight(attr);
311 if (p == NULL) {
312 return NULL;
315 if ((p->type != ssmt_type) && (p->sparql_attr == NULL)) {
316 yyerror("unsupported Spotlight attribute");
317 return NULL;
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;
327 break;
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,
336 /* append '=' to '!' */
337 op == '!' ? '=' : ' ',
338 val);
339 if (sparql == NULL) {
340 return NULL;
342 s->var++;
343 break;
345 case ssmt_str:
346 q = talloc_strdup(talloc_tos(), "");
347 if (q == NULL) {
348 return NULL;
350 start = val;
351 while (*val) {
352 if (*val != '*') {
353 val++;
354 continue;
356 if (val > start) {
357 q = talloc_strndup_append(q, start, val - start);
358 if (q == NULL) {
359 return NULL;
362 q = talloc_strdup_append(q, ".*");
363 if (q == NULL) {
364 return NULL;
366 val++;
367 start = val;
369 if (val > start) {
370 q = talloc_strndup_append(q, start, val - start);
371 if (q == NULL) {
372 return NULL;
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,
382 TALLOC_FREE(q);
383 if (sparql == NULL) {
384 return NULL;
386 s->var++;
387 break;
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;
395 break;
397 case ssmt_date:
398 t = atoi(val) + SPRAW_TIME_OFFSET;
399 tmp = localtime(&t);
400 if (tmp == NULL) {
401 return NULL;
403 result = strftime(buf1, sizeof(buf1),
404 "%Y-%m-%dT%H:%M:%SZ", tmp);
405 if (result == 0) {
406 return NULL;
408 sparql = talloc_asprintf(talloc_tos(),
409 "?obj %s ?%c FILTER(?%c %c '%s')",
410 p->sparql_attr,
411 s->var,
412 s->var,
414 buf1);
415 if (sparql == NULL) {
416 return NULL;
418 s->var++;
419 break;
421 case ssmt_type:
422 sparql = map_type_search(attr, op, val);
423 if (sparql == NULL) {
424 return NULL;
426 break;
428 default:
429 return NULL;
432 return sparql;
435 void mdsyyerror(const char *str)
437 DEBUG(1, ("mdsyyerror: %s\n", str));
440 int mdsyywrap(void)
442 return 1;
446 * Map a Spotlight RAW query string to a SPARQL query string
448 bool map_spotlight_to_sparql_query(struct sl_query *slq)
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',
456 int result;
458 s.s = mdsyy_scan_string(slq->query_string);
459 if (s.s == NULL) {
460 TALLOC_FREE(s.frame);
461 return false;
463 global_sparql_parser_state = &s;
464 result = mdsyyparse();
465 global_sparql_parser_state = NULL;
466 mdsyy_delete_buffer(s.s);
468 if (result != 0) {
469 TALLOC_FREE(s.frame);
470 return false;
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;
482 return true;