diff options
Diffstat (limited to 'libcli/wsp')
-rw-r--r-- | libcli/wsp/test_wsp_parser.c | 402 | ||||
-rw-r--r-- | libcli/wsp/wscript_build | 41 | ||||
-rw-r--r-- | libcli/wsp/wsp_aqs.c | 877 | ||||
-rw-r--r-- | libcli/wsp/wsp_aqs.h | 166 | ||||
-rw-r--r-- | libcli/wsp/wsp_aqs_lexer.l | 152 | ||||
-rw-r--r-- | libcli/wsp/wsp_aqs_parser.y | 422 |
6 files changed, 2060 insertions, 0 deletions
diff --git a/libcli/wsp/test_wsp_parser.c b/libcli/wsp/test_wsp_parser.c new file mode 100644 index 0000000..9edebda --- /dev/null +++ b/libcli/wsp/test_wsp_parser.c @@ -0,0 +1,402 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include <setjmp.h> +#include <cmocka.h> +#include <talloc.h> +#include "lib/cmdline/cmdline.h" +#include "libcli/util/ntstatus.h" +#include "lib/util/samba_util.h" +#include "lib/torture/torture.h" +#include "lib/param/param.h" +#include "libcli/wsp/wsp_aqs.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "librpc/wsp/wsp_util.h" + +/* + * some routines to help stringify the parsed AQS + * query so we can test parsing + */ + +static bool is_operator_node(t_query *node) +{ + if (node->type == eVALUE) { + return false; + } + return true; +} + +static const char *nodetype_as_string(t_nodetype node) +{ + const char *result = NULL; + switch (node) { + case eNOT: + result = "NOT"; + break; + case eAND: + result = "AND"; + break; + case eOR: + result = "OR"; + break; + case eVALUE: + default: + break; + } + return result; +} + +static const char *restriction_as_string(TALLOC_CTX *ctx, + struct wsp_crestriction *crestriction ) +{ + const char *result = NULL; + if (crestriction->ultype == RTPROPERTY) { + struct wsp_cpropertyrestriction *prop_restr = + &crestriction->restriction.cpropertyrestriction; + struct wsp_cbasestoragevariant *value = &prop_restr->prval; + result = variant_as_string(ctx, value, true); + } else { + struct wsp_ccontentrestriction *cont_restr = NULL; + cont_restr = &crestriction->restriction.ccontentrestriction; + result = talloc_strdup(ctx, cont_restr->pwcsphrase); + } + return result; +} + +static const char *prop_name_from_restriction( + TALLOC_CTX *ctx, + struct wsp_crestriction *restriction) +{ + const char *result = NULL; + struct wsp_cfullpropspec *prop; + if (restriction->ultype == RTCONTENT) { + prop = &restriction->restriction.ccontentrestriction.property; + } else { + prop = &restriction->restriction.cpropertyrestriction.property; + } + result = prop_from_fullprop(ctx, prop); + return result; +} + +static char *print_basic_query(TALLOC_CTX *ctx, + struct wsp_crestriction *restriction) +{ + const char *op_str = op_as_string(restriction); + const char *val_str = restriction_as_string(ctx, restriction); + const char *prop_name = prop_name_from_restriction(ctx, restriction); + char *res = talloc_asprintf(ctx, + "%s %s %s", prop_name, op_str ? op_str : "", val_str); + return res; +} + +static char *print_node(TALLOC_CTX *ctx, t_query *node, bool is_rpn) +{ + switch(node->type) { + case eAND: + case eOR: + case eNOT: + return talloc_asprintf(ctx, + " %s ", nodetype_as_string(node->type)); + break; + case eVALUE: + default: + return print_basic_query(ctx, node->restriction); + break; + } +} + +/* + * Algorithm infix (tree) + * Print the infix expression for an expression tree. + * Pre : tree is a pointer to an expression tree + * Post: the infix expression has been printed + * start infix + * if (tree not empty) + * if (tree token is operator) + * print (open parenthesis) + * end if + * infix (tree left subtree) + * print (tree token) + * infix (tree right subtree) + * if (tree token is operator) + * print (close parenthesis) + * end if + * end if + * end infix + */ + +static char *infix(TALLOC_CTX *ctx, t_query *tree) +{ + char *sresult = NULL; + char *stree = NULL; + char *sleft = NULL; + char *sright = NULL; + if (tree == NULL) { + return NULL; + } + + if (is_operator_node(tree)) { + sresult = talloc_strdup(ctx, "("); + SMB_ASSERT(sresult != NULL); + } + sleft = infix(ctx, tree->left); + stree = print_node(ctx, tree, false); + sright = infix(ctx, tree->right); + sresult = talloc_asprintf(ctx, "%s%s%s%s", + sresult ? sresult : "", + sleft ? sleft : "", + stree? stree : "", + sright ? sright : ""); + + if (is_operator_node(tree)) { + sresult = talloc_asprintf(ctx, "%s)", sresult); + SMB_ASSERT(sresult != NULL); + } + return sresult; +} + +static struct { + const char *aqs; + const char *stringified; +} no_col_map_queries [] = { + + /* equals (numeric) */ + { + "System.Size:10241", + "System.Size = 10241" + }, + { + "System.Size := 10241", + "System.Size = 10241" + }, + /* not equals */ + { + "System.Size:!=10241", + "System.Size != 10241" + }, + /* equals (string property) */ + { + "ALL:(somestring)", + "All = 'somestring'" + }, + { + "ALL:=somestring", + "All = 'somestring'" + }, + { + "ALL:somestring", + "All = 'somestring'" + }, + /* not equals (string) */ + { + "ALL:!=somestring", + "All != 'somestring'" + }, + /* Greater than */ + { + "System.Size:(>10241)", + "System.Size > 10241" + }, + { + "System.Size:>10241", + "System.Size > 10241" + }, + /* Less than */ + { + "System.Size:(<10241)", + "System.Size < 10241" + }, + /* Greater than or equals */ + { + "System.Size:(>=10241)", + "System.Size >= 10241" + }, + { + "System.Size:>=10241", + "System.Size >= 10241" + }, + /* Less than or equals */ + { + "System.Size:(<=10241)", + "System.Size <= 10241" + }, + { + "System.Size:<=10241", + "System.Size <= 10241" + }, + /* equals (in the sense of matches) */ + { + "ALL:($=somestring)", + "All equals somestring" + }, + /* starts with */ + { + "ALL:($<somestring)", + "All starts with somestring" + }, + /* range */ + { + "System.Size:10241-102401", + "(System.Size >= 10241 AND System.Size < 102401)" + }, + { + "System.Size:small", + "(System.Size >= 10241 AND System.Size < 102401)" + }, + /* NOT */ + { + "NOT System.Size:10241", + "( NOT System.Size = 10241)" + }, + /* AND */ + { + "System.Size:(>=10241) AND System.Size:(<102401)", + "(System.Size >= 10241 AND System.Size < 102401)" + }, + /* OR */ + { + "System.Kind:picture OR System.Kind:video", + "(System.Kind = 'picture' OR System.Kind = 'video')" + }, + /* MULTIPLE LOGICAL */ + { + "System.Kind:picture AND NOT System.Kind:video OR " + "System.Kind:movie", + "(System.Kind = 'picture' AND (( NOT System.Kind = 'video') OR " + "System.Kind = 'movie'))" + }, + /* parenthesized MULTIPLE LOGICAL */ + { + "(System.Kind:picture AND NOT System.Kind:video) OR " + "System.Kind:movie", + "((System.Kind = 'picture' AND ( NOT System.Kind = 'video')) " + "OR System.Kind = 'movie')" + }, +}; + +static char *dump_cols(TALLOC_CTX *ctx, t_select_stmt *select) +{ + t_col_list *cols = select->cols; + char *res = NULL; + if (cols) { + int i; + for (i = 0; i < cols->num_cols; i++) { + if (i == 0) { + res = talloc_strdup(ctx, + cols->cols[i]); + } else { + res = talloc_asprintf(ctx, + "%s, %s", + res, cols->cols[i]); + } + } + } + return res; +} + +static void test_wsp_parser(void **state) +{ + int i; + t_select_stmt *select_stmt = NULL; + const char *col_query = + "SELECT System.ItemName, System.ItemURL, System.Size WHERE " + "System.Kind:picture"; + char *res = NULL; + + TALLOC_CTX *frame = talloc_stackframe(); + for (i = 0; i < ARRAY_SIZE(no_col_map_queries); i++) { + select_stmt = get_wsp_sql_tree(no_col_map_queries[i].aqs); + assert_non_null(select_stmt); + assert_null(select_stmt->cols); + res = infix(frame, select_stmt->where); + DBG_DEBUG("reading query => %s parsed => %s\n", + no_col_map_queries[i].aqs, + res); + assert_string_equal(res, no_col_map_queries[i].stringified); + } + select_stmt = get_wsp_sql_tree(col_query); + res = infix(frame, select_stmt->where); + assert_string_equal(res, "System.Kind = 'picture'"); + assert_non_null(select_stmt->cols); + res = dump_cols(frame, select_stmt); + assert_string_equal(res, + "System.ItemName, System.ItemURL, System.Size"); + TALLOC_FREE(frame); +} + +int main(int argc, const char *argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_wsp_parser), + }; + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + poptContext pc; + int opt; + bool ok; + struct loadparm_context *lp_ctx = NULL; + + TALLOC_CTX *frame = talloc_stackframe(); + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + lp_ctx = samba_cmdline_get_lp_ctx(); + if (!lp_ctx) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "Unknown Option: %c\n", opt); + exit(1); + } + } + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/libcli/wsp/wscript_build b/libcli/wsp/wscript_build new file mode 100644 index 0000000..2d34879 --- /dev/null +++ b/libcli/wsp/wscript_build @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +#default flex recepie doesn't create a header file +bld.SAMBA_GENERATOR('wsp_flex', + source='wsp_aqs_lexer.l', + target='wsp_aqs_lexer.h wsp_aqs_lexer.c', + group='build_source', + rule='${FLEX} --header-file=${TGT[0].abspath(env)} --outfile=${TGT[1].abspath(env)} ${SRC[0].abspath(env)}', + enabled=bld.env.with_wsp + ) + +# With centos7-o3 CI job (and gcc 4.8.5) we get +# an error with -Wstrict-overflow. +# Same code is good with gcc version +# gcc 8.5.0 (centos8) and whatever versions of +# gcc we have in the other XXXX-o3 images. +# We turn off strict-overflow just for this generated +# file +parser_cflags='' +if bld.CONFIG_SET('HAVE_WNO_STRICT_OVERFLOW'): + parser_cflags += ' -Wno-strict-overflow' + +bld.SAMBA_SUBSYSTEM('LIBSAMBA_WSP_PARSER', + source='wsp_aqs_parser.y', + deps='talloc wsp_flex', + cflags_end=parser_cflags, + enabled=bld.env.with_wsp + ) +bld.SAMBA_SUBSYSTEM('LIBSAMBA_WSP', + source='wsp_aqs.c wsp_aqs_lexer.c', + public_deps='LIBSAMBA_WSP_PARSER', + enabled=bld.env.with_wsp + ) + +bld.SAMBA_BINARY('test_wsp_parser', + source='test_wsp_parser.c', + deps= 'dcerpc CMDLINE_S3 LIBSAMBA_WSP NDR_WSP NDR_WSP_DATA WSP_UTIL cmocka', + enabled=bld.env.with_wsp, + install=False + ) + diff --git a/libcli/wsp/wsp_aqs.c b/libcli/wsp/wsp_aqs.c new file mode 100644 index 0000000..acf1229 --- /dev/null +++ b/libcli/wsp/wsp_aqs.c @@ -0,0 +1,877 @@ +/* + * Window Search Service + * + * Copyright (c) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.tab.h" +#include "libcli/wsp/wsp_aqs_lexer.h" +#include "librpc/wsp/wsp_util.h" +#include "librpc/gen_ndr/ndr_wsp.h" +#include <stdio.h> +#include <stdbool.h> + +int yyparse(t_select_stmt **select, yyscan_t scanner); + +static void reverse_cols(t_select_stmt *select) +{ + int num_elems, fwd, rev; + char **cols; + + + if (!select->cols) { + return; + } + num_elems = select->cols->num_cols; + cols = select->cols->cols; + + for(fwd = 0, rev = num_elems - 1; fwd <= rev; fwd++, rev--) { + char * tmp = cols[rev]; + cols[rev] = cols[fwd]; + cols[fwd] = tmp; + } + +} + +t_select_stmt *get_wsp_sql_tree(const char *expr) +{ + t_select_stmt *select = NULL; + yyscan_t scanner; + YY_BUFFER_STATE state; + + if (yylex_init(&scanner)) { + DBG_ERR("couldn't initialize\n"); + return NULL; + } + + state = yy_scan_string(expr, scanner); + + if (yyparse(&select, scanner)) { + DBG_ERR("some parse error\n"); + return NULL; + } + /* + * parsed columns are in reverse order to how they are specified + * in the AQS like statement, reverse them again to correct this. + */ + reverse_cols(select); + + yy_delete_buffer(state, scanner); + + yylex_destroy(scanner); + + return select; +} + + +t_col_list *create_cols(TALLOC_CTX *ctx, const char *col, t_col_list *append_list) +{ + t_col_list *cols = append_list; + if (!get_prop_info(col)) { + DBG_ERR("Unknown property %s\n", col); + return NULL; + } + if (cols == NULL) { + cols = talloc_zero(ctx, t_col_list); + if (cols == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + cols->num_cols = 0; + cols->cols = NULL; + DBG_INFO("returning new cols %p with item %s\n", cols, col); + } + if (col) { + int old_index = cols->num_cols; + if (old_index == 0) { + cols->cols = talloc_array(cols, char*, 1); + } else { + cols->cols = (char **)talloc_realloc(cols, cols->cols, char*, old_index + 1); + } + if (!cols->cols) { + return NULL; /* can we create a parser error here */ + } + cols->num_cols++; + cols->cols[old_index] = talloc_strdup(cols, col); + if (cols->cols[old_index] == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + + } + return cols; +} + +t_select_stmt *create_select(TALLOC_CTX *ctx, t_col_list *cols, t_query *where) +{ + t_select_stmt *result = talloc_zero(ctx, t_select_stmt); + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + result->cols = cols; + result->where = where; + return result; +} + +t_basic_restr *create_basic_restr(TALLOC_CTX *ctx, + uint32_t prop_type, + t_optype op, + t_value_holder *values) +{ + t_basic_restr *result = talloc_zero(ctx, t_basic_restr); + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + result->prop_type = prop_type; + result->op = op; + if (values->type == VALUE_RANGE) { + t_restr *left_node; + t_restr *right_node; + t_basic_restr *left_val; + t_basic_restr *right_val; + if (op != eEQ) { + DBG_ERR("Unsupported operation %d\n", op); + TALLOC_FREE(result); + goto out; + } + + if (values->value.value_range->lower == NULL) { + DBG_ERR("range lower limit doesn't exist\n"); + TALLOC_FREE(result); + goto out; + } + /* + * detect special case where upper range doesn't exist + * and convert to a property value. (this won't happen from + * the cmdline directly but only as a result of a range + * created 'specially' in code, e.g. special gigantic size + * range. + */ + if (values->value.value_range->upper == NULL) { + result->op = eGE; + result->values = values->value.value_range->lower; + goto out; + } + result->values = talloc_zero(result, t_value_holder); + if (result->values == NULL) { + DBG_ERR("out of memory\n"); + TALLOC_FREE(result); + goto out; + } + /* + * try create a restriction tree (>=lower AND <upper) to + * represent the range + */ + left_val = create_basic_restr(result->values, + prop_type, + eGE, + values->value.value_range->lower); + + right_val = create_basic_restr(result->values, + prop_type, + eLT, + values->value.value_range->upper); + + if (!left_val || !right_val) { + DBG_ERR("Failed creating basic_restriction values " + "for range\n"); + TALLOC_FREE(result); + goto out; + } + + left_node = create_restr(result->values, eVALUE, NULL, NULL, left_val); + right_node = create_restr(result->values, eVALUE, NULL, NULL, right_val); + + + if (!left_node || !right_node) { + DBG_ERR("Failed creating restr nodes for range\n"); + TALLOC_FREE(result); + goto out; + } + result->values->type = RESTR; + result->values->value.restr_tree = create_restr(result->values, + eAND, + left_node, + right_node, + NULL); + if (!result->values->value.restr_tree) { + DBG_ERR("Failed creating restr tree for range\n"); + TALLOC_FREE(result); + goto out; + } + } else { + result->values = values; + } +out: + return result; +} + +/* + * The parser reads numbers as VT_UI8, booleans as VT_BOOL and strings as + * VT_LPWSTR + */ +typedef bool (*conv_func) (TALLOC_CTX *ctx, t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type); + +/* + * default converter #TODO probably should cater for detecting over/underrun + * depending on the dest_type we are narrowing to + */ +static bool default_convertor(TALLOC_CTX *ctx, + t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type) +{ + if (src->type != NUMBER) { + return false; + } + dest->vvalue.vt_ui8 = src->value.number; + dest->vtype = dest_type; + return true; +} + +static bool convert_string_to_lpwstr_v(TALLOC_CTX *ctx, + t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type) +{ + const char *str = src->value.string; + set_variant_lpwstr_vector(ctx, dest, &str, 1); + return true; +} + +static bool convert_string_to_lpwstr(TALLOC_CTX *ctx, + t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type) +{ + const char *str = src->value.string; + set_variant_lpwstr(ctx, dest, str); + return true; +} + +static bool convert_bool_to_lpwstr(TALLOC_CTX *ctx, + t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type) +{ + set_variant_lpwstr( + ctx, + dest, + src->value.boolean ? "true": "false"); + return true; +} + +static bool convert_string_to_filetime(TALLOC_CTX *ctx, + t_value_holder *src, + struct wsp_cbasestoragevariant *dest, + uint16_t dest_type) +{ + + static const char *fmts[] = { + "%FT%TZ", + "%FT%T", + "%F %T", + "%F %R", + "%F", + }; + struct tm tm; + time_t timeval = 0; + int i; + ZERO_STRUCT(tm); + + for (i = 0; i < ARRAY_SIZE(fmts); i++) { + if (strptime(src->value.string, fmts[i], &tm)) { + timeval = timegm(&tm); + break; + } + } + + if (timeval) { + NTTIME nt; + unix_to_nt_time(&nt, timeval); + dest->vtype = VT_FILETIME; + dest->vvalue.vt_filetime = nt; + return true; + } + return false; +} + +const struct { + uint16_t src_vtype; + uint16_t dest_vtype; + conv_func convert_type; +} type_conv_map[] = { + {NUMBER, VT_I8, default_convertor}, + {NUMBER, VT_UI8, default_convertor}, + {NUMBER, VT_INT, default_convertor}, + {NUMBER, VT_UINT, default_convertor}, + {NUMBER, VT_I4, default_convertor}, + {NUMBER, VT_UI4, default_convertor}, + {NUMBER, VT_I2, default_convertor}, + {NUMBER, VT_UI2, default_convertor}, + {NUMBER, VT_BOOL, default_convertor}, + {NUMBER, VT_FILETIME, default_convertor}, + {NUMBER, VT_BOOL, default_convertor}, + {BOOL, VT_LPWSTR, convert_bool_to_lpwstr}, + {STRING, VT_LPWSTR, convert_string_to_lpwstr}, + {STRING, VT_LPWSTR | VT_VECTOR, convert_string_to_lpwstr_v}, + {STRING, VT_FILETIME, convert_string_to_filetime}, +}; + +static bool process_prop_value(TALLOC_CTX *ctx, + const struct full_propset_info *prop_info, + t_value_holder *node_value, + struct wsp_cbasestoragevariant *prop_value) +{ + int i; + + /* coerce type as required */ + for (i = 0; i < ARRAY_SIZE(type_conv_map); i++ ) { + if (type_conv_map[i].src_vtype == node_value->type && + type_conv_map[i].dest_vtype == prop_info->vtype) { + type_conv_map[i].convert_type(ctx, + node_value, + prop_value, + prop_info->vtype); + return true; + } + } + return false; +} + +t_basic_query *create_basic_query(TALLOC_CTX *ctx, const char *propname, t_basic_restr *restr) +{ + t_basic_query *result = talloc_zero(ctx, t_basic_query); + if (result == NULL) { + DBG_ERR("out of memory\n"); + goto out; + } + result->prop = talloc_strdup(result, propname); + result->prop_info = get_propset_info_with_guid(propname, &result->guid); + + if (!result->prop_info) { + DBG_ERR("Unknown property %s\n",propname); + TALLOC_FREE(result); + goto out; + } + result->basic_restriction = restr; +out: + return result; +} + +static struct wsp_crestriction *create_restriction(TALLOC_CTX *ctx, + t_basic_query *query) +{ + struct wsp_crestriction *crestriction = NULL; + struct wsp_cfullpropspec *prop = NULL; + t_basic_restr *restr = NULL; + t_value_holder *src = NULL; + crestriction = talloc_zero(ctx, struct wsp_crestriction); + if (crestriction == NULL) { + DBG_ERR("out of memory\n"); + goto done; + } + + restr = query->basic_restriction; + src = restr->values; + + if (restr->prop_type == RTNONE) { + /* shouldn't end up here */ + DBG_ERR("Unexpected t_basic_restr type\n"); + TALLOC_FREE(crestriction); + goto done; + } + + crestriction->weight = 1000; + + if (restr->prop_type == RTCONTENT) { + struct wsp_ccontentrestriction *content = NULL; + crestriction->ultype = RTCONTENT; + if (src->type != STRING) { + DBG_ERR("expected string value for %s\n", + query->prop); + TALLOC_FREE(crestriction); + goto done; + } + content = &crestriction->restriction.ccontentrestriction; + content->pwcsphrase = src->value.string; + content->cc = strlen(src->value.string); + /* + * In the future we might generate the lcid from + * environ (or config) + */ + content->lcid = WSP_DEFAULT_LCID; + if (restr->op == eEQUALS) { + content->ulgeneratemethod = 0; + } else { + content->ulgeneratemethod = 1; + } + + prop = &content->property; + } else if (restr->prop_type == RTPROPERTY) { + struct wsp_cbasestoragevariant *dest = + &crestriction->restriction.cpropertyrestriction.prval; + crestriction->ultype = RTPROPERTY; + if (!process_prop_value(ctx, query->prop_info, src, dest)) { + DBG_ERR("Failed to process value for property %s\n", + query->prop); + TALLOC_FREE(crestriction); + goto done; + } + crestriction->restriction.cpropertyrestriction.relop = + restr->op; + prop = &crestriction->restriction.cpropertyrestriction.property; + } else { + TALLOC_FREE(crestriction); + goto done; + } + prop->guidpropset = query->guid; + prop->ulkind = PRSPEC_PROPID; + prop->name_or_id.prspec = query->prop_info->id; +done: + return crestriction; +} + +/* expands restr_node into a tree of t_query nodes */ +static void build_query(TALLOC_CTX *ctx, t_query *node, t_restr *restr_node, + const char* prop) +{ + if (!node) { + return; + } + if (!restr_node) { + return; + } + + node->type = restr_node->type; + + if (restr_node->left) { + node->left = talloc_zero(ctx, t_query); + SMB_ASSERT(node->left != NULL); + build_query(ctx, node->left, restr_node->left, prop); + } + + if (restr_node->right) { + node->right = talloc_zero(ctx, t_query); + SMB_ASSERT(node->right != NULL); + build_query(ctx, node->right, restr_node->right, prop); + } + + if (restr_node->type == eVALUE) { + node->restriction = + create_restriction(ctx, + create_basic_query(ctx, + prop, + restr_node->basic_restr)); + } +} + +t_query *create_query_node(TALLOC_CTX *ctx, t_nodetype op, t_query *left, t_query *right, t_basic_query *value) +{ + t_query *result = talloc_zero(ctx, t_query); + if (result == NULL) { + return result; + } + result->type = op; + result->left = left; + result->right = right; + if (op == eVALUE) { + t_basic_restr *restr = value->basic_restriction; + /* expand restr node */ + if (restr->values->type == RESTR) { + build_query(ctx, + result, + restr->values->value.restr_tree, + value->prop); + } else { + result->restriction = + create_restriction(ctx, value); + if (!result->restriction) { + TALLOC_FREE(result); + } + } + } + return result; +} + +t_restr *create_restr(TALLOC_CTX *ctx, t_nodetype op, t_restr *left, t_restr *right, t_basic_restr *value) +{ + t_restr *result = talloc_zero(ctx, t_restr); + if (result == NULL) { + return result; + } + result->type = op; + result->right = right; + result->left = left; + result->basic_restr = value; + return result; +} + +t_value_holder *create_string_val(TALLOC_CTX* ctx, const char *text) +{ + t_value_holder *result = + talloc_zero(ctx, t_value_holder); + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + result->value.string = text; + result->type = STRING; + return result; +} + +t_value_holder *create_num_val(TALLOC_CTX* ctx, int64_t val) +{ + t_value_holder *result = + talloc_zero(ctx, t_value_holder); + + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + + result->type = NUMBER; + result->value.number = val; + return result; +} + +t_value_holder *create_bool_val(TALLOC_CTX* ctx, bool val) +{ + t_value_holder *result = + talloc_zero(ctx, t_value_holder); + + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + + result->type = BOOL; + result->value.boolean = val; + return result; +} + +t_value_holder *create_value_range(TALLOC_CTX* ctx, + t_value_holder *left, + t_value_holder *right) +{ + t_value_holder *result = + talloc_zero(ctx, t_value_holder); + + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + + result->type = VALUE_RANGE; + result->value.value_range = talloc_zero(result, struct value_range); + if (!result->value.value_range) { + TALLOC_FREE(result); + goto out; + } + result->value.value_range->lower = left; + result->value.value_range->upper = right; +out: + return result; +} + +static void zero_time(struct tm *tm) +{ + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; +} + +typedef bool (*daterange_func) (TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2); + + +static bool create_date_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2, + int32_t lower_mday_adj, + int32_t lower_mon_adj, + int32_t upper_mday_adj, + int32_t upper_mon_adj) +{ + struct tm tm_now; + time_t now; + + struct tm tm_tmp; + time_t lower; + time_t upper; + + time(&now); + gmtime_r(&now, &tm_now); + + tm_tmp = tm_now; + zero_time(&tm_tmp); + tm_tmp.tm_mday += lower_mday_adj; + tm_tmp.tm_mon += lower_mon_adj; + lower = mktime(&tm_tmp); + tm_tmp = tm_now; + zero_time(&tm_tmp); + tm_tmp.tm_mday += upper_mday_adj; + tm_tmp.tm_mon += upper_mon_adj; + upper = mktime(&tm_tmp); + unix_to_nt_time(date1, lower); + unix_to_nt_time(date2, upper); + return true; +} + +static void get_now_tm(struct tm *tm_now) +{ + time_t now; + time(&now); + gmtime_r(&now, tm_now); +} + +static bool create_thismonth_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + int32_t firstofmonth_adj; + + get_now_tm(&tm_now); + firstofmonth_adj = 1 - tm_now.tm_mday; + return create_date_range(ctx, date1, + date2, firstofmonth_adj, + 0, firstofmonth_adj, 1); +} + +static bool create_lastyear_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + int32_t firstofmonth_adj; + int32_t january_adj; + get_now_tm(&tm_now); + + firstofmonth_adj = 1 - tm_now.tm_mday; + january_adj = -tm_now.tm_mon; + return create_date_range(ctx, date1, + date2, firstofmonth_adj, + january_adj - 12, firstofmonth_adj, january_adj); +} + +static bool create_thisyear_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + int32_t firstofmonth_adj; + int32_t january_adj; + + get_now_tm(&tm_now); + + firstofmonth_adj = 1 - tm_now.tm_mday; + january_adj = -tm_now.tm_mon; + return create_date_range(ctx, date1, + date2, firstofmonth_adj, + january_adj, firstofmonth_adj, january_adj + 12); +} + +static bool create_lastmonth_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + int32_t firstofmonth_adj; + get_now_tm(&tm_now); + + firstofmonth_adj = 1 - tm_now.tm_mday; + return create_date_range(ctx, date1, + date2, firstofmonth_adj, + -1, firstofmonth_adj, 0); +} + +static bool create_today_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + return create_date_range(ctx, date1, + date2, 0, 0, 1, 0); +} + +static bool create_yesterday_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + return create_date_range(ctx, date1, + date2, -1, 0, 0, 0); +} + +static bool create_thisweek_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + time_t now; + int32_t startofweek_adj; + time(&now); + gmtime_r(&now, &tm_now); + if (tm_now.tm_wday) { + startofweek_adj = 1 - tm_now.tm_wday; + } else { + startofweek_adj = -6; + } + /* lower will be the start of this week */ + return create_date_range(ctx, date1, + date2, startofweek_adj, + 0, startofweek_adj + 7, 0); +} + +static bool create_lastweek_range(TALLOC_CTX *ctx, uint64_t *date1, + uint64_t *date2) +{ + struct tm tm_now; + time_t now; + int32_t startofweek_adj; + time(&now); + gmtime_r(&now, &tm_now); + if (tm_now.tm_wday) { + startofweek_adj = 1 - tm_now.tm_wday; + } else { + startofweek_adj = -6; + } + /* upper will be the start of this week */ + return create_date_range(ctx, date1, + date2, startofweek_adj - 7, + 0,startofweek_adj, 0); +} + +t_value_holder *create_date_range_shortcut(TALLOC_CTX *ctx, + daterange_type daterange) +{ + int i; + static const struct { + daterange_type range; + daterange_func create_fn; + } date_conv_map[] = { + {eYESTERDAY, create_yesterday_range}, + {eTODAY, create_today_range}, + {eTHISMONTH, create_thismonth_range}, + {eLASTMONTH, create_lastmonth_range}, + {eTHISWEEK, create_thisweek_range}, + {eLASTWEEK, create_lastweek_range}, + {eTHISYEAR, create_thisyear_range}, + {eLASTYEAR, create_lastyear_range}, + }; + t_value_holder *result = NULL; + t_value_holder *lower = NULL; + t_value_holder *upper = NULL; + + lower = talloc_zero(ctx, t_value_holder); + if (lower == NULL) { + DBG_ERR("out of memory\n"); + goto out; + } + + upper = talloc_zero(ctx, t_value_holder); + if (upper == NULL) { + DBG_ERR("out of memory\n"); + goto out; + } + + result = create_value_range(result, lower, upper); + + if (result == NULL) { + TALLOC_FREE(result); + goto out; + } + + lower->type = NUMBER; + upper->type = NUMBER; + + result->value.value_range->lower = lower; + result->value.value_range->upper = upper; + + for (i = 0; i < ARRAY_SIZE(date_conv_map); i++) { + if (date_conv_map[i].range == daterange) { + if (!date_conv_map[i].create_fn(result, + &lower->value.number, + &upper->value.number)) { + TALLOC_FREE(result); + break; + } + break; + } + } +out: + return result; +} + +t_value_holder *create_size_range_shortcut(TALLOC_CTX *ctx, + sizerange_type sizerange) +{ + static const struct { + sizerange_type range; + uint32_t lower; + uint32_t upper; + } sizes[] = { + {eEMPTY, 0x0, 0x1}, + {eTINY, 0x1, 0x2801}, + {eSMALL, 0x2801, 0x19001}, + {eMEDIUM, 0x19001, 0x100001}, + {eLARGE, 0x100001, 0x10000001}, + {eHUGE, 0x10000001, 0x80000001}, + {eGIGANTIC, 0x80000001, 0} /* special case not a range */ + }; + int i; + t_value_holder *result = NULL; + uint32_t lower_size; + uint32_t upper_size; + bool rangefound = false; + t_value_holder *left = NULL; + t_value_holder *right = NULL; + for (i = 0; i < ARRAY_SIZE(sizes); i++) { + if (sizes[i].range == sizerange) { + result = talloc_zero(ctx, t_value_holder); + if (result == NULL) { + DBG_ERR("out of memory\n"); + return NULL; + } + lower_size = sizes[i].lower; + upper_size = sizes[i].upper; + rangefound = true; + break; + } + } + + if (!rangefound) { + return NULL; + } + + left = talloc_zero(ctx, t_value_holder); + + if (left == NULL) { + return NULL; + } + + left->type = NUMBER; + left->value.number = lower_size; + + if (upper_size) { + right = talloc_zero(ctx, t_value_holder); + if (right == NULL) { + return NULL; + } + right->type = NUMBER; + right->value.number = upper_size; + } + + result = create_value_range(ctx, left, right); + return result; +} diff --git a/libcli/wsp/wsp_aqs.h b/libcli/wsp/wsp_aqs.h new file mode 100644 index 0000000..b34dd52 --- /dev/null +++ b/libcli/wsp/wsp_aqs.h @@ -0,0 +1,166 @@ +/* + * Window Search Service + * + * Copyright (c) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WSP_AQS_H__ +#define __WSP_AQS_H__ +#include "librpc/gen_ndr/wsp.h" + +typedef enum nodetype +{ + eAND, + eOR, + eNOT, + eVALUE, +} t_nodetype; + +typedef enum op +{ + eLT = PRLT, + eLE = PRLE, + eGT = PRGT, + eGE = PRGE, + eEQ = PREQ, + eNE = PRNE, + eSTARTSWITH, + eEQUALS, + /* + * eMATCHES, + * + * not sure we can express the above in the grammar without + * some custom operator :/ + */ +} t_optype; + +struct restr; + +typedef enum { + NUMBER, + STRING, + BOOL, + RESTR, + VALUE_RANGE, +} value_type; + +typedef enum { + eTODAY, + eYESTERDAY, + eLASTWEEK, + eTHISWEEK, + eTHISMONTH, + eLASTMONTH, + eTHISYEAR, + eLASTYEAR, +} daterange_type; + +typedef enum { + eEMPTY, + eTINY, + eSMALL, + eMEDIUM, + eLARGE, + eHUGE, + eGIGANTIC, +} sizerange_type; + +struct value_range; + +typedef struct { + value_type type; + union { + bool boolean; + const char *string; + uint64_t number; + struct restr *restr_tree; + struct value_range *value_range; + } value; +} t_value_holder; + +struct value_range +{ + t_value_holder *lower; + t_value_holder *upper; +}; +typedef struct basic_restr +{ + uint32_t prop_type; + t_optype op; + t_value_holder *values; +} t_basic_restr; + +typedef struct basic_query +{ + struct GUID guid; + const struct full_propset_info *prop_info; + char *prop; + t_basic_restr *basic_restriction; +} t_basic_query; + +t_basic_query *create_basic_query(TALLOC_CTX *ctx, const char *prop, t_basic_restr *restr); + +typedef struct restr +{ + t_nodetype type; + struct restr *left; + struct restr *right; + t_basic_restr *basic_restr; +} t_restr; + +t_restr *create_restr(TALLOC_CTX *ctx, t_nodetype op, t_restr *left, t_restr *right, t_basic_restr *value); + +t_basic_restr *create_basic_restr(TALLOC_CTX *ctx, + uint32_t prop_type, + t_optype op, + t_value_holder *values); + +typedef struct query +{ + t_nodetype type; + struct query *left; + struct query *right; + struct wsp_crestriction *restriction; +} t_query; + +t_query *create_query_node(TALLOC_CTX *ctx, t_nodetype op, t_query *left, t_query *right, t_basic_query *value); + + +typedef struct col_list { + int num_cols; + char **cols; +} t_col_list; + +typedef struct select_stmt { + t_col_list *cols; + t_query *where; +} t_select_stmt; + +t_col_list *create_cols(TALLOC_CTX *ctx, const char *col, t_col_list *append_list); +t_select_stmt *create_select(TALLOC_CTX *ctx, t_col_list *cols, t_query *where); + +t_select_stmt *get_wsp_sql_tree(const char *expr); +t_value_holder *create_string_val(TALLOC_CTX*, const char *text); +t_value_holder *create_num_val(TALLOC_CTX*, int64_t val); +t_value_holder *create_bool_val(TALLOC_CTX*, bool val); +t_value_holder *create_value_range(TALLOC_CTX*, + t_value_holder *left, + t_value_holder *right); +t_value_holder *create_date_range_shortcut(TALLOC_CTX *ctx, + daterange_type daterange); +t_value_holder *create_size_range_shortcut(TALLOC_CTX *ctx, + sizerange_type size); +#endif /* __WSP_AQS_H__ */ diff --git a/libcli/wsp/wsp_aqs_lexer.l b/libcli/wsp/wsp_aqs_lexer.l new file mode 100644 index 0000000..dff4c10 --- /dev/null +++ b/libcli/wsp/wsp_aqs_lexer.l @@ -0,0 +1,152 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.tab.h" + + +#include <stdio.h> + +#define YY_NO_INPUT + +%} + +%option warn nodefault nounput + +%option reentrant noyywrap never-interactive nounistd +%option bison-bridge + +LPAREN "(" +RPAREN ")" +AND "AND" +OR "OR" +NOT "NOT" +EQ "==" +NE "!=" +GE ">=" +LE "<=" +LESS "<" +GREATER ">" +COMMA "," +WHERE "WHERE" +SELECT "SELECT" +PROP_EQUALS ":" +TRUE "true" +FALSE "false" + +TODAY "today" +YESTERDAY "yesterday" +THISWEEK "thisweek" +LASTWEEK "lastweek" +THISMONTH "thismonth" +LASTMONTH "lastmonth" +THISYEAR "thisyear" +LASTYEAR "lastyear" + +EMPTY "empty" +TINY "tiny" +SMALL "small" +MEDIUM "medium" +LARGE "large" +HUGE "huge" +GIGANTIC "gigantic" + +STARTS_WITH "$<" +EQUALS "$=" +K "K" +M "M" +G "G" +T "T" +KB "KB" +MB "MB" +GB "GB" +TB "TB" +RANGE "-" + + +NUMBER [0-9]+ +WS [ \r\n\t]* +IDENTIFIER [a-z\."A-Z_][a-z\."A-Z_0-9]* +STRING_LITERAL L?\"(\\.|[^\\"])*\" + +%% + +{WS} { /* Skip blanks. */ } + +{NUMBER} { sscanf(yytext, "%"PRId64, &yylval->num); return TOKEN_NUMBER; } + +{AND} { return TOKEN_AND; } +{OR} { return TOKEN_OR; } +{NOT} { return TOKEN_NOT; } +{EQ} { return TOKEN_EQ; } +{NE} { return TOKEN_NE; } +{GE} { return TOKEN_GE; } +{LE} { return TOKEN_LE; } +{LESS} { return TOKEN_LT; } +{GREATER} { return TOKEN_GT; } +{LPAREN} { return TOKEN_LPAREN; } +{RPAREN} { return TOKEN_RPAREN; } +{COMMA} { return TOKEN_COMMA; } +{WHERE} { return TOKEN_WHERE; } +{SELECT} { return TOKEN_SELECT; } +{TRUE} { return TOKEN_TRUE; } +{FALSE} { return TOKEN_FALSE; } +{PROP_EQUALS} { return TOKEN_PROP_EQUALS; } + +{STARTS_WITH} { return TOKEN_STARTS_WITH;} +{EQUALS} { return TOKEN_EQUALS;} + +{K} { return TOKEN_K; } +{M} { return TOKEN_M; } +{G} { return TOKEN_G; } +{T} { return TOKEN_T; } +{KB} { return TOKEN_KB; } +{MB} { return TOKEN_MB; } +{GB} { return TOKEN_GB; } +{TB} { return TOKEN_TB; } +{RANGE} { return TOKEN_RANGE; } +{TODAY} { return TOKEN_TODAY; } +{YESTERDAY} { return TOKEN_YESTERDAY;} +{THISWEEK} { return TOKEN_THISWEEK;} +{LASTWEEK} { return TOKEN_LASTWEEK;} +{THISMONTH} { return TOKEN_THISMONTH; } +{LASTMONTH} { return TOKEN_LASTMONTH; } +{THISYEAR} { return TOKEN_THISYEAR; } +{LASTYEAR} { return TOKEN_LASTYEAR; } +{EMPTY} { return TOKEN_EMPTY; } +{TINY} { return TOKEN_TINY; } +{SMALL} { return TOKEN_SMALL; } +{MEDIUM} { return TOKEN_MEDIUM; } +{LARGE} { return TOKEN_LARGE; } +{HUGE} { return TOKEN_HUGE; } +{GIGANTIC} { return TOKEN_GIGANTIC; } + + +{STRING_LITERAL} { yylval->strval = talloc_asprintf(talloc_tos(), "%s", yytext); return TOKEN_STRING_LITERAL; } + +{IDENTIFIER} { yylval->strval = talloc_asprintf(talloc_tos(), "%s", yytext); return TOKEN_IDENTIFIER; } +. { } + +%% + diff --git a/libcli/wsp/wsp_aqs_parser.y b/libcli/wsp/wsp_aqs_parser.y new file mode 100644 index 0000000..2b0d7bd --- /dev/null +++ b/libcli/wsp/wsp_aqs_parser.y @@ -0,0 +1,422 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.tab.h" +#include "libcli/wsp/wsp_aqs_lexer.h" + +static int yyerror(t_select_stmt **stmt, yyscan_t scanner, const char *msg) +{ + fprintf(stderr,"Error :%s\n",msg); return 0; +} +%} +%code requires { + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +} + +%define api.pure +%lex-param { yyscan_t scanner } +%parse-param { t_select_stmt **select } +%parse-param { yyscan_t scanner } + +%union { + char *strval; + int64_t num; + t_value_holder *value; + t_select_stmt *select_stmt; + t_select_stmt *query_stmt; + t_basic_restr *bas_rest; + t_basic_query *bas_query; + t_restr *restr; + t_query *query; + t_col_list *columns; + daterange_type daterange; + sizerange_type sizerange; + t_optype prop_op; +} + +%left "AND" TOKEN_AND +%left "OR" TOKEN_OR +%left "!=" TOKEN_NE +%left ">=" TOKEN_GE +%left "<=" TOKEN_LE +%left "<" TOKEN_LT +%left ">" TOKEN_GT +%right "NOT" TOKEN_NOT +%right "==" TOKEN_EQ +%right ":" TOKEN_PROP_EQUALS + +%right "$<" TOKEN_STARTS_WITH +%right "$=" TOKEN_EQUALS + +%token TOKEN_LPAREN +%token TOKEN_RPAREN +%token TOKEN_AND +%token TOKEN_OR +%token TOKEN_WHERE +%token TOKEN_SELECT +%token TOKEN_TRUE +%token TOKEN_FALSE +%token TOKEN_COMMA +%token TOKEN_STARTS_WITH +%token TOKEN_EQUALS +%token TOKEN_MATCHES +%token TOKEN_K +%token TOKEN_M +%token TOKEN_G +%token TOKEN_T +%token TOKEN_KB +%token TOKEN_MB +%token TOKEN_GB +%token TOKEN_TB +%token TOKEN_RANGE +%token TOKEN_TODAY +%token TOKEN_YESTERDAY +%token TOKEN_THISWEEK +%token TOKEN_LASTWEEK +%token TOKEN_THISMONTH +%token TOKEN_LASTMONTH +%token TOKEN_THISYEAR +%token TOKEN_LASTYEAR +%token TOKEN_EMPTY +%token TOKEN_TINY +%token TOKEN_SMALL +%token TOKEN_MEDIUM +%token TOKEN_LARGE +%token TOKEN_HUGE +%token TOKEN_GIGANTIC + +%token <num> TOKEN_NUMBER +%token <strval> TOKEN_IDENTIFIER +%token <strval> TOKEN_STRING_LITERAL + +%type <strval> prop +%type <bas_rest> basic_restr +%type <restr> restr +%type <bas_query> basic_query +%type <query> query +%type <columns> cols +%type <strval> col +%type <select_stmt> select_stmt +%type <value> simple_value +%type <value> value +%type <daterange> date_shortcut +%type <prop_op> property_op +%type <prop_op> content_op +%type <sizerange> size_shortcut + +%% + +input: + select_stmt { + *select = $1; + } +; + +select_stmt: + TOKEN_SELECT cols[C] TOKEN_WHERE query[Q] { + $$ = create_select(talloc_tos(), $C, $Q ); + if (!$$) { + YYERROR; + } + } + | query[Q] { + $$ = create_select(talloc_tos(), NULL, $Q ); + if (!$$) { + YYERROR; + } + } + ; + +cols : + col[C] { + $$ = create_cols(talloc_tos(), $1, NULL); + if (!$$) { + YYERROR; + } + } + | col[C] TOKEN_COMMA cols[CS] { + $$ = create_cols(talloc_tos(), $C, $CS); + if (!$$) { + YYERROR; + } + } + ; + +col: + TOKEN_IDENTIFIER[I] { + $$ = $I; + if (!$$) { + YYERROR; + } + } + ; + +query: + basic_query { + $$ = create_query_node(talloc_tos(), eVALUE, NULL, NULL, $1); + if (!$$) { + YYERROR; + } + } + | TOKEN_LPAREN query[Q] TOKEN_RPAREN { + $$ = $Q; + if (!$$) { + YYERROR; + } + } + | query[L] TOKEN_AND query[R] { + $$ = create_query_node(talloc_tos(), eAND, $L, $R, NULL); + if (!$$) { + YYERROR; + } + } + | query[L] TOKEN_OR query[R] { + $$ = create_query_node(talloc_tos(), eOR, $L, $R, NULL); + if (!$$) { + YYERROR; + } + } + | TOKEN_NOT query[R] { + $$ = create_query_node(talloc_tos(), eNOT, NULL, $R, NULL); + if (!$$) { + YYERROR; + } + } + ; + +basic_query: + prop[P] TOKEN_PROP_EQUALS basic_restr[V] { + $$ = create_basic_query(talloc_tos(), $P, $V); + if (!$$) { + YYERROR; + } + } + ; + +prop: TOKEN_IDENTIFIER[I] { + $$ = $I; + if (!$$) { + YYERROR; + } + } + ; + +basic_restr: + value[V] { + $$ = create_basic_restr(talloc_tos(), RTPROPERTY, eEQ, $V); + if (!$$) { + YYERROR; + } + } + | property_op[P] value[T] { + $$ = create_basic_restr(talloc_tos(), RTPROPERTY, $P, $T); + if (!$$) { + YYERROR; + } + } + | content_op[P] value[T] { + $$ = create_basic_restr(talloc_tos(), RTCONTENT, $P, $T); + if (!$$) { + YYERROR; + } + } + | TOKEN_LPAREN restr[R] TOKEN_RPAREN { + t_value_holder *holder = talloc_zero(talloc_tos(), t_value_holder); + holder->type = RESTR; + holder->value.restr_tree = $R; + $$ = create_basic_restr(talloc_tos(), RTNONE, eEQ, holder); + if (!$$) { + YYERROR; + } + } + ; + +property_op: + TOKEN_EQ { $$ = eEQ; } + | TOKEN_NE { $$ = eNE; } + | TOKEN_GE { $$ = eGE; } + | TOKEN_LE { $$ = eLE; } + | TOKEN_LT { $$ = eLT; } + | TOKEN_GT { $$ = eGT; } + ; + +content_op: + TOKEN_STARTS_WITH { $$ = eSTARTSWITH; } + | TOKEN_EQUALS { $$ = eEQUALS; } + ; + +value: + simple_value[V] { $$ = $V;} + | simple_value[L] TOKEN_RANGE simple_value[R] { + $$ = create_value_range(talloc_tos(), $L, $R); + if (!$$) { + YYERROR; + } + } + | date_shortcut[D] { + $$ = create_date_range_shortcut(talloc_tos(), $D); + if (!$$) { + YYERROR; + } + } + | size_shortcut[S] { + $$ = create_size_range_shortcut(talloc_tos(), $S); + if (!$$) { + YYERROR; + } + } + ; + +date_shortcut: + TOKEN_TODAY { $$ = eTODAY; } + | TOKEN_YESTERDAY { $$ = eYESTERDAY; } + | TOKEN_THISWEEK { $$ = eTHISWEEK; } + | TOKEN_LASTWEEK { $$ = eLASTWEEK; } + | TOKEN_THISMONTH { $$ = eTHISMONTH; } + | TOKEN_LASTMONTH { $$ = eTHISMONTH; } + | TOKEN_THISYEAR { $$ = eTHISYEAR; } + | TOKEN_LASTYEAR { $$ = eLASTYEAR; } + ; + +size_shortcut: + TOKEN_EMPTY { $$ = eEMPTY; } + | TOKEN_TINY { $$ = eTINY; } + | TOKEN_SMALL { $$ = eSMALL; } + | TOKEN_MEDIUM { $$ = eMEDIUM; } + | TOKEN_LARGE { $$ = eLARGE; } + | TOKEN_HUGE { $$ = eHUGE; } + | TOKEN_GIGANTIC { $$ = eGIGANTIC; } + ; + +simple_value: + TOKEN_NUMBER[N] { + $$ = create_num_val(talloc_tos(), $N); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_K { + $$ = create_num_val(talloc_tos(), $N * 1024); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_M { + $$ = create_num_val( talloc_tos(), $N * 1024 * 1024); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_G { + $$ = create_num_val(talloc_tos(), $N * 1024 * 1024 * 1024); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_T { + $$ = create_num_val(talloc_tos(), + $N * 1024 * 1024 * 1024 * 1024); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_KB { + $$ = create_num_val(talloc_tos(), $N * 1000); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_MB { + $$ = create_num_val( talloc_tos(), $N * 1000 * 1000); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_GB { + $$ = create_num_val(talloc_tos(), $N * 1000 * 1000 * 1000); + if (!$$) { + YYERROR; + } + } + | TOKEN_NUMBER[N] TOKEN_TB { + $$ = create_num_val(talloc_tos(), + $N * 1000 * 1000 * 1000 * 1000); + if (!$$) { + YYERROR; + } + } + | TOKEN_TRUE { + $$ = create_bool_val(talloc_tos(), true); + if (!$$) { + YYERROR; + } + } + | TOKEN_FALSE { + $$ = create_num_val(talloc_tos(), false); + if (!$$) { + YYERROR; + } + } + | TOKEN_STRING_LITERAL[S] { + char *tmp_str = talloc_strdup(talloc_tos(), $S+1); + tmp_str[strlen(tmp_str)-1] = '\0'; + $$ = create_string_val(talloc_tos(), tmp_str); + if (!$$) { + YYERROR; + } + } + | TOKEN_IDENTIFIER[I] { + $$ = create_string_val(talloc_tos(), $I); + if (!$$) { + YYERROR; + } + } + ; + +restr: basic_restr[V] { + $$ = create_restr(talloc_tos(), eVALUE, NULL, NULL, $V); + if (!$$) { + YYERROR; + } + } + | restr[L] TOKEN_AND restr[R] { + $$ = create_restr(talloc_tos(), eAND, $L, $R, NULL); + if (!$$) { + YYERROR; + } + } + | restr[L] TOKEN_OR restr[R] { + $$ = create_restr(talloc_tos(), eOR, $L, $R, NULL); + if (!$$) { + YYERROR; + } + } + ; +%% |