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/>.
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 {
51 } *global_sparql_parser_state;
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);
73 %type <sval> match expr line function
80 %token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
92 global_sparql_parser_state->result = $1;
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.
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 attibute,
116 * which is eg the case for "*" and "kMDItemTextContent" which both
117 * map to SPARQL "fts:match".
121 if (strcmp($1, $3) != 0) {
122 $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
124 $$ = talloc_asprintf(talloc_tos(), "%s", $1);
133 | OBRACE expr CBRACE {
134 $$ = talloc_asprintf(talloc_tos(), "%s", $2);
137 $$ = talloc_asprintf(talloc_tos(), "%s . %s", $1, $3);
140 if (strcmp($1, $3) != 0) {
141 $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
143 $$ = talloc_asprintf(talloc_tos(), "%s", $1);
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;
184 FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {
185 $$ = map_daterange($3, $5, $7);
186 if ($$ == NULL) YYABORT;
191 DATE_ISO OBRACE WORD CBRACE {$$ = isodate2unix($3);}
192 | WORD {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
197 static time_t isodate2unix(const char *s)
202 p = strptime(s, "%Y-%m-%dT%H:%M:%SZ", &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;
215 const struct sl_attr_map *p;
217 char buf1[64], buf2[64];
223 tmp = localtime(&date1);
227 result = strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
232 tmp = localtime(&date2);
236 result = strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
241 p = sl_attr_map_by_spotlight(dateattr);
246 sparql = talloc_asprintf(talloc_tos(),
247 "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
254 if (sparql == NULL) {
262 static char *map_type_search(const char *attr, char op, const char *val)
265 const char *sparqlAttr;
266 const struct sl_type_map *p;
268 p = sl_type_map_by_spotlight(val);
275 sparqlAttr = "rdf:type";
278 sparqlAttr = "nie:mimeType";
284 result = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
287 if (result == NULL) {
294 static const char *map_expr(const char *attr, char op, const char *val)
296 struct sparql_parser_state *s = global_sparql_parser_state;
299 const struct sl_attr_map *p;
310 p = sl_attr_map_by_spotlight(attr);
315 if ((p->type != ssmt_type) && (p->sparql_attr == NULL)) {
316 yyerror("unsupported Spotlight attribute");
322 sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
323 p->sparql_attr, val);
324 if (sparql == NULL) {
330 sparql = talloc_asprintf(talloc_tos(),
331 "?obj %s ?%c FILTER(?%c %c%c '%s')",
336 /* append '=' to '!' */
337 op == '!' ? '=' : ' ',
339 if (sparql == NULL) {
346 q = talloc_strdup(talloc_tos(), "");
357 q = talloc_strndup_append(q, start, val - start);
362 q = talloc_strdup_append(q, ".*");
370 q = talloc_strndup_append(q, start, val - start);
375 sparql = talloc_asprintf(talloc_tos(),
377 "FILTER(regex(?%c, '^%s$', 'i'))",
383 if (sparql == NULL) {
390 sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
391 p->sparql_attr, val);
392 if (sparql == NULL) {
398 t = atoi(val) + SPRAW_TIME_OFFSET;
403 result = strftime(buf1, sizeof(buf1),
404 "%Y-%m-%dT%H:%M:%SZ", tmp);
408 sparql = talloc_asprintf(talloc_tos(),
409 "?obj %s ?%c FILTER(?%c %c '%s')",
415 if (sparql == NULL) {
422 sparql = map_type_search(attr, op, val);
423 if (sparql == NULL) {
435 void mdsyyerror(const char *str)
437 DEBUG(1, ("mdsyyerror: %s\n", str));
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(),
458 s.s = mdsyy_scan_string(slq->query_string);
460 TALLOC_FREE(s.frame);
463 global_sparql_parser_state = &s;
464 result = mdsyyparse();
465 global_sparql_parser_state = NULL;
466 mdsyy_delete_buffer(s.s);
469 TALLOC_FREE(s.frame);
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) {