summaryrefslogtreecommitdiffstats
path: root/libcli/wsp
diff options
context:
space:
mode:
Diffstat (limited to 'libcli/wsp')
-rw-r--r--libcli/wsp/test_wsp_parser.c402
-rw-r--r--libcli/wsp/wscript_build41
-rw-r--r--libcli/wsp/wsp_aqs.c877
-rw-r--r--libcli/wsp/wsp_aqs.h166
-rw-r--r--libcli/wsp/wsp_aqs_lexer.l152
-rw-r--r--libcli/wsp/wsp_aqs_parser.y422
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;
+ }
+ }
+ ;
+%%