2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines / Elasticsearch backend
5 Copyright (C) Ralph Boehme 2019
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_es.h"
25 #include "rpc_server/mdssvc/es_parser.tab.h"
26 #include "rpc_server/mdssvc/es_mapping.h"
27 #include "lib/util/smb_strtox.h"
31 * allow building with -O3 -Wp,-D_FORTIFY_SOURCE=2
33 * /tmp/samba-testbase/.../mdssvc/es_parser.y: In function
35 * es_parser.tab.c:1124:6: error: assuming pointer wraparound
36 * does not occur when comparing P +- C1 with P +- C2
37 * [-Werror=strict-overflow]
39 * The generated code in es_parser.tab.c looks like this:
41 * if (yyss + yystacksize - 1 <= yyssp)
43 #pragma GCC diagnostic ignored "-Wstrict-overflow"
45 #define YYMALLOC SMB_MALLOC
46 #define YYREALLOC SMB_REALLOC
48 struct yy_buffer_state
;
49 typedef
struct yy_buffer_state
*YY_BUFFER_STATE
;
51 void mdsyylerror
(char const *);
52 void *mdsyylterminate
(void);
53 YY_BUFFER_STATE mdsyyl_scan_string
(const char *str
);
54 void mdsyyl_delete_buffer
(YY_BUFFER_STATE buffer
);
56 /* forward declarations */
57 static char *isodate_to_sldate
(const char *s
);
58 static char *map_expr
(const struct es_attr_map
*attr
,
63 /* global vars, eg needed by the lexer */
64 struct es_parser_state
{
68 bool ignore_unknown_attribute
;
69 bool ignore_unknown_type
;
73 } *global_es_parser_state
;
79 #include "rpc_server/mdssvc/mdssvc.h"
81 /* 2001-01-01T00:00:00Z - Unix Epoch = SP_RAW_TIME_OFFSET */
82 #define SP_RAW_TIME_OFFSET 978307200
85 bool map_spotlight_to_es_query
(TALLOC_CTX
*mem_ctx
,
87 const char *path_scope
,
88 const char *query_string
,
95 struct es_attr_map
*attr_map
;
98 %define api.prefix
{mdsyyl
}
100 %define parse.
error verbose
102 %type
<sval
> match expr line function value isodate
103 %type
<attr_map
> attribute
105 %token
<sval
> WORD PHRASE
106 %token
<bval
> BOOLEAN
109 %token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
124 if
(global_es_parser_state
->type_error
) {
127 global_es_parser_state
->result
= $1;
136 $$
= talloc_asprintf
(talloc_tos
(), "(%s)", $2);
137 if
($$
== NULL
) YYABORT;
141 if
($1 == NULL
&& $3 == NULL
) {
143 } else if
($1 == NULL
) {
145 } else if
($3 == NULL
) {
148 $$
= talloc_asprintf
(talloc_tos
(), "(%s) AND (%s)", $1, $3);
149 if
($$
== NULL
) YYABORT;
153 if
($1 == NULL
&& $3 == NULL
) {
155 } else if
($1 == NULL
) {
157 } else if
($3 == NULL
) {
160 $$
= talloc_asprintf
(talloc_tos
(), "%s OR %s", $1, $3);
161 if
($$
== NULL
) YYABORT;
169 * We can't properly handle these in expressions, fortunately this
170 * is probably only ever used by OS X as sole element in an
171 * expression ie "False" (when Finder window selected our share
172 * but no search string entered yet). Packet traces showed that OS
173 * X Spotlight server then returns a failure (ie -1) which is what
174 * we do here too by calling YYABORT.
180 attribute EQUAL value
{
184 $$
= map_expr
($1, '=', $3, NULL
);
187 | attribute UNEQUAL value
{
191 $$
= map_expr
($1, '!', $3, NULL
);
194 | attribute LT value
{
198 $$
= map_expr
($1, '<', $3, NULL
);
201 | attribute GT value
{
205 $$
= map_expr
($1, '>', $3, NULL
);
216 FUNC_INRANGE OBRACE attribute COMMA WORD COMMA WORD CBRACE
{
220 $$
= map_expr
($3, '~', $5, $7);
226 $$
= es_map_sl_attr
(global_es_parser_state
->frame
,
227 global_es_parser_state
->kmd_map
,
230 !global_es_parser_state
->ignore_unknown_attribute
)
245 DATE_ISO OBRACE WORD CBRACE
{
246 $$
= isodate_to_sldate
($3);
247 if
($$
== NULL
) YYABORT;
253 * Spotlight has two date formats:
254 * - seconds since 2001-01-01 00:00:00Z
255 * - as string "$time.iso(%Y-%m-%dT%H:%M:%SZ)"
256 * This function converts the latter to the former as string, so the parser
257 * can work on a uniform format.
259 static char *isodate_to_sldate
(const char *isodate
)
261 struct es_parser_state
*s
= global_es_parser_state
;
263 const char *p
= NULL
;
267 p
= strptime
(isodate
, "%Y-%m-%dT%H:%M:%SZ", &tm
);
269 DBG_ERR
("strptime [%s] failed\n", isodate
);
274 t
-= SP_RAW_TIME_OFFSET
;
276 tstr
= talloc_asprintf
(s
->frame
, "%jd", (intmax_t)t
);
284 static char *map_type
(const struct es_attr_map
*attr
,
288 struct es_parser_state
*s
= global_es_parser_state
;
289 const char *mime_type_list
= NULL
;
290 char *esc_mime_type_list
= NULL
;
291 const char *not
= NULL
;
292 const char *end
= NULL
;
295 mime_type_list
= es_map_sl_type
(s
->mime_map
, val
);
296 if
(mime_type_list
== NULL
) {
297 DBG_DEBUG
("Mapping type [%s] failed\n", val
);
298 if
(!s
->ignore_unknown_type
) {
299 s
->type_error
= true
;
304 esc_mime_type_list
= es_escape_str
(s
->frame
,
307 if
(esc_mime_type_list
== NULL
) {
321 DBG_ERR
("Mapping type [%s] unexpected op [%c]\n", val
, op
);
324 es
= talloc_asprintf
(s
->frame
,
337 static char *map_num
(const struct es_attr_map
*attr
,
342 struct es_parser_state
*s
= global_es_parser_state
;
347 es
= talloc_asprintf
(s
->frame
,
353 es
= talloc_asprintf
(s
->frame
,
359 es
= talloc_asprintf
(s
->frame
,
366 es
= talloc_asprintf
(s
->frame
,
372 es
= talloc_asprintf
(s
->frame
,
378 DBG_ERR
("Mapping num unexpected op [%c]\n", op
);
388 static char *map_fts
(const struct es_attr_map
*attr
,
392 struct es_parser_state
*s
= global_es_parser_state
;
393 const char *not
= NULL
;
394 const char *end
= NULL
;
398 esval
= es_escape_str
(s
->frame
, val
, "*\\\"");
400 yyerror("es_escape_str failed");
414 DBG_ERR
("Mapping fts [%s] unexpected op [%c]\n", val
, op
);
417 es
= talloc_asprintf
(s
->frame
,
428 static char *map_str
(const struct es_attr_map
*attr
,
432 struct es_parser_state
*s
= global_es_parser_state
;
435 const char *not
= NULL
;
436 const char *end
= NULL
;
438 esval
= es_escape_str
(s
->frame
, val
, "*\\\"");
440 yyerror("es_escape_str failed");
454 DBG_ERR
("Mapping string [%s] unexpected op [%c]\n", val
, op
);
458 es
= talloc_asprintf
(s
->frame
,
471 * Convert Spotlight date seconds since 2001-01-01 00:00:00Z
472 * to a date string in the format %Y-%m-%dT%H:%M:%SZ.
474 static char *map_sldate_to_esdate
(TALLOC_CTX
*mem_ctx
,
477 struct tm
*tm
= NULL
;
484 t
= (time_t)smb_strtoull
(sldate
, NULL
, 10, &error, SMB_STR_STANDARD
);
486 DBG_ERR
("smb_strtoull [%s] failed\n", sldate
);
489 t
+= SP_RAW_TIME_OFFSET
;
493 DBG_ERR
("localtime [%s] failed\n", sldate
);
497 len
= strftime
(buf
, sizeof
(buf
),
498 "%Y-%m-%dT%H:%M:%SZ", tm
);
500 DBG_ERR
("strftime [%s] failed\n", sldate
);
504 esdate
= es_escape_str
(mem_ctx
, buf
, NULL
);
505 if
(esdate
== NULL
) {
506 yyerror("es_escape_str failed");
512 static char *map_date
(const struct es_attr_map
*attr
,
517 struct es_parser_state
*s
= global_es_parser_state
;
518 char *esdate1
= NULL
;
519 char *esdate2
= NULL
;
522 if
(op
== '~' && sldate2
== NULL
) {
523 DBG_ERR
("Date range query, but second date is NULL\n");
527 esdate1
= map_sldate_to_esdate
(s
->frame
, sldate1
);
528 if
(esdate1
== NULL
) {
529 DBG_ERR
("map_sldate_to_esdate [%s] failed\n", sldate1
);
532 if
(sldate2
!= NULL
) {
533 esdate2
= map_sldate_to_esdate
(s
->frame
, sldate2
);
534 if
(esdate2
== NULL
) {
535 DBG_ERR
("map_sldate_to_esdate [%s] failed\n", sldate2
);
542 es
= talloc_asprintf
(s
->frame
,
548 es
= talloc_asprintf
(s
->frame
,
554 es
= talloc_asprintf
(s
->frame
,
561 es
= talloc_asprintf
(s
->frame
,
567 es
= talloc_asprintf
(s
->frame
,
579 static char *map_expr
(const struct es_attr_map
*attr
,
586 switch
(attr
->type
) {
588 es
= map_type
(attr
, op
, val1
);
591 es
= map_num
(attr
, op
, val1
, val2
);
594 es
= map_fts
(attr
, op
, val1
);
597 es
= map_str
(attr
, op
, val1
);
600 es
= map_date
(attr
, op
, val1
, val2
);
606 DBG_DEBUG
("Mapping [%s %c %s (%s)] failed\n",
607 attr
->name
, op
, val1
, val2 ? val2
: "");
614 void mdsyylerror
(const char *str
)
616 DBG_ERR
("Parser failed: %s\n", str
);
625 * Map a Spotlight RAW query string to a ES query string
627 bool map_spotlight_to_es_query
(TALLOC_CTX
*mem_ctx
,
629 const char *path_scope
,
630 const char *query_string
,
633 struct es_parser_state s
= {
634 .frame
= talloc_stackframe
(),
637 char *es_query
= NULL
;
639 s.kmd_map
= json_object_get
(mappings
, "attribute_mappings");
640 if
(s.kmd_map
== NULL
) {
641 DBG_ERR
("Failed to load attribute_mappings from JSON\n");
644 s.mime_map
= json_object_get
(mappings
, "mime_mappings");
645 if
(s.mime_map
== NULL
) {
646 DBG_ERR
("Failed to load mime_mappings from JSON\n");
650 s.s
= mdsyyl_scan_string
(query_string
);
652 DBG_WARNING
("Failed to parse [%s]\n", query_string
);
653 TALLOC_FREE
(s.frame
);
657 s.ignore_unknown_attribute
= lp_parm_bool
(GLOBAL_SECTION_SNUM
,
659 "ignore unknown attribute",
661 s.ignore_unknown_type
= lp_parm_bool
(GLOBAL_SECTION_SNUM
,
663 "ignore unknown type",
666 global_es_parser_state
= &s
;
667 result
= mdsyylparse
();
668 global_es_parser_state
= NULL
;
669 mdsyyl_delete_buffer
(s.s
);
672 TALLOC_FREE
(s.frame
);
676 es_query
= talloc_asprintf
(mem_ctx
,
677 "(%s) AND path.real.fulltext:\\\"%s\\\"",
678 s.result
, path_scope
);
679 TALLOC_FREE
(s.frame
);
680 if
(es_query
== NULL
) {
684 *_es_query
= es_query
;