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
);
69 %define api.prefix
{mdsyy
}
71 %define parse.
error verbose
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 attribute,
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
) {