>From 510d18e2d2644d39f753f30f71c064d56ed3fe9a Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 29 Jun 2016 12:47:18 +0100 Subject: [PATCH 01/21] s4/librpc: Implement a custom dcerpc_binding_handle for rawpipe Intended to be used by clients of raw pipe services. Signed-off-by: Noel Power --- source4/librpc/rpc/dcerpc_raw.c | 382 ++++++++++++++++++++++++++++++++++++++++ source4/librpc/rpc/dcerpc_raw.h | 6 + source4/librpc/wscript_build | 2 +- 3 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 source4/librpc/rpc/dcerpc_raw.c create mode 100644 source4/librpc/rpc/dcerpc_raw.h diff --git a/source4/librpc/rpc/dcerpc_raw.c b/source4/librpc/rpc/dcerpc_raw.c new file mode 100644 index 0000000..ac46be0 --- /dev/null +++ b/source4/librpc/rpc/dcerpc_raw.c @@ -0,0 +1,382 @@ +#include "includes.h" +#include "system/filesys.h" +#include "librpc/rpc/dcerpc_raw.h" +#include "lib/events/events.h" +#include "librpc/rpc/dcerpc.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "param/param.h" +#include "lib/util/tevent_ntstatus.h" +#include "librpc/rpc/rpc_common.h" +#include "lib/tsocket/tsocket.h" + +struct rawpipe_bh_state { + struct dcerpc_pipe *p; +}; + +static bool rawpipe_bh_ref_alloc(struct dcerpc_binding_handle *h) +{ + return true; +} + +static bool rawpipe_bh_is_connected(struct dcerpc_binding_handle *h) +{ + struct rawpipe_bh_state *hs = dcerpc_binding_handle_data(h, + struct rawpipe_bh_state); + + if (!hs->p) { + return false; + } + + if (!hs->p->conn) { + return false; + } + + if (hs->p->conn->dead) { + return false; + } + + return true; +} + +static uint32_t rawpipe_bh_set_timeout(struct dcerpc_binding_handle *h, + uint32_t timeout) +{ + struct rawpipe_bh_state *hs = dcerpc_binding_handle_data(h, + struct rawpipe_bh_state); + uint32_t old; + + if (!hs->p) { + return DCERPC_REQUEST_TIMEOUT; + } + + old = hs->p->request_timeout; + hs->p->request_timeout = timeout; + + return old; +} + +static void rawpipe_bh_auth_info(struct dcerpc_binding_handle *h, + enum dcerpc_AuthType *auth_type, + enum dcerpc_AuthLevel *auth_level) +{ + struct rawpipe_bh_state *hs = dcerpc_binding_handle_data(h, + struct rawpipe_bh_state); + if (hs->p == NULL) { + return; + } + + if (hs->p->conn == NULL) { + return; + } + + *auth_type = hs->p->conn->security_state.auth_type; + *auth_level = hs->p->conn->security_state.auth_level; +} + +struct rawpipe_bh_disconnect_state { + uint8_t _dummy; +}; + +static struct tevent_req *rawpipe_bh_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct dcerpc_binding_handle *h) +{ + struct rawpipe_bh_state *hs = dcerpc_binding_handle_data(h, + struct rawpipe_bh_state); + struct tevent_req *req; + struct dcerpc_bh_disconnect_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct rawpipe_bh_disconnect_state); + if (req == NULL) { + return NULL; + } + + ok = rawpipe_bh_is_connected(h); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED); + return tevent_req_post(req, ev); + } + + /* TODO: do a real disconnect ... */ + hs->p = NULL; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static NTSTATUS rawpipe_bh_disconnect_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +struct rawpipe_bh_raw_call_state { + DATA_BLOB out_data; + struct iovec req; + struct iovec resp; + struct tevent_context *ev; + struct dcecli_connection *conn; +}; + +struct rpc_write_state { + struct tevent_context *ev; + DATA_BLOB buffer; + struct dcecli_connection *conn; + struct iovec in; + struct iovec out; +}; + +static void raw_tstream_trans_writev(struct tevent_req *subreq); +static void raw_tstream_trans_readv_done(struct tevent_req *subreq); + +static struct tevent_req *raw_pipe_req_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct dcerpc_pipe *p, + struct iovec *req_data) +{ + struct tevent_req *req, *subreq; + struct rpc_write_state *state; + struct timeval endtime; + + req = tevent_req_create(mem_ctx, &state, struct rpc_write_state); + if (req == NULL) { + return NULL; + } + + /* + * #TODO check if stream is connected + */ + + state->ev = ev; + state->conn = p->conn; + state->in = *req_data; + + endtime = timeval_current_ofs_msec(p->request_timeout); + + subreq = tstream_writev_queue_send(state, + ev, + state->conn->transport.stream, + state->conn->transport.write_queue, + &state->in, + 1); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + if (!tevent_req_set_endtime(subreq, ev, endtime)) { + return tevent_req_post(req, ev); + } + + tevent_req_set_callback(subreq, raw_tstream_trans_writev, req); + state->buffer.data = talloc_array(state, uint8_t, 1); + state->buffer.length = talloc_array_length(state->buffer.data); + state->out.iov_base = state->buffer.data; + state->out.iov_len = state->buffer.length; + subreq = tstream_readv_send(state, ev, + p->conn->transport.stream, + &state->out, + 1); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + if (!tevent_req_set_endtime(subreq, ev, endtime)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, raw_tstream_trans_readv_done, req); + + return req; +} + +static void raw_tstream_trans_readv_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + + struct rpc_write_state *state = + tevent_req_data(req, + struct rpc_write_state); + + int ret; + int err = 0; + int to_read; + ssize_t ofs = 0; + + ret = tstream_readv_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix_common(err)); + return; + } + + to_read = tstream_pending_bytes(state->conn->transport.stream); + if (!to_read) { + /* we're done */ + tevent_req_done(req); + return; + } + + ofs = state->buffer.length; + state->buffer.data = talloc_realloc(state, + state->buffer.data, + uint8_t, + to_read + ofs); + state->buffer.length = to_read + state->buffer.length; + state->out.iov_base = (void *) (state->buffer.data + ofs); + state->out.iov_len = state->buffer.length - ofs; + subreq = tstream_readv_send(state, + state->ev, + state->conn->transport.stream, + &state->out, + 1); + if (tevent_req_nomem(subreq, req)) { + tevent_req_post(req, state->ev); + return; + } + + tevent_req_set_callback(subreq, raw_tstream_trans_readv_done, req); +} + +static void raw_tstream_trans_writev(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + int ret; + int err; + ret = tstream_writev_queue_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix_common(err)); + return; + } +} + +static void rawpipe_bh_call_send_done(struct tevent_req *subreq); +static struct tevent_req *rawpipe_bh_call_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct dcerpc_binding_handle *h, + const struct GUID *object, + uint32_t opnum, + uint32_t in_flags, + const uint8_t *in_data, + size_t in_length) +{ + struct rawpipe_bh_state *hs = dcerpc_binding_handle_data(h, + struct rawpipe_bh_state); + struct tevent_req *req; + bool ok; + struct tevent_req *subreq; + struct rawpipe_bh_raw_call_state* state; + + req = tevent_req_create(mem_ctx, &state, + struct rawpipe_bh_raw_call_state); + if (req == NULL) { + return NULL; + } + state->req.iov_len = in_length; + state->req.iov_base = discard_const_p(uint8_t, in_data); + + state->out_data = data_blob_null; + state->conn = hs->p->conn; + state->ev = ev; + + ok = rawpipe_bh_is_connected(h); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED); + return tevent_req_post(req, ev); + } + subreq = raw_pipe_req_send(state, ev, hs->p, + &state->req); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, rawpipe_bh_call_send_done, req); + return req; +} + +static void rawpipe_bh_call_send_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct rawpipe_bh_raw_call_state *state = + tevent_req_data(req, + struct rawpipe_bh_raw_call_state); + struct rpc_write_state *write_state = + tevent_req_data(subreq, + struct rpc_write_state); + NTSTATUS status; + if (tevent_req_is_nterror(subreq, &status)) { + tevent_req_nterror(req, status); + return; + } + state->out_data.data = talloc_move(state, &write_state->buffer.data); + state->out_data.length = write_state->buffer.length; + TALLOC_FREE(subreq); + tevent_req_done(req); +} + +static NTSTATUS rawpipe_bh_call_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **out_data, + size_t *out_length, + uint32_t *out_flags) +{ + NTSTATUS status; + struct rawpipe_bh_raw_call_state *state = + tevent_req_data(req, + struct rawpipe_bh_raw_call_state); + + *out_data = talloc_move(mem_ctx, &state->out_data.data); + *out_length = state->out_data.length; + status = NT_STATUS_OK; + if (tevent_req_is_nterror(req, &status)) { + } + tevent_req_received(req); + + return status; +} + +static const struct dcerpc_binding_handle_ops raw_pipe_ccli_bh_ops = { + .name = "raw_pipe_ccli", + .is_connected = rawpipe_bh_is_connected, + .set_timeout = rawpipe_bh_set_timeout, + .auth_info = rawpipe_bh_auth_info, + .raw_call_send = rawpipe_bh_call_send, + .raw_call_recv = rawpipe_bh_call_recv, + .disconnect_send = rawpipe_bh_disconnect_send, + .disconnect_recv = rawpipe_bh_disconnect_recv, + + .ref_alloc = rawpipe_bh_ref_alloc, +}; + +struct dcerpc_binding_handle *create_rawpipe_handle(struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *h = NULL; + + struct rawpipe_bh_state *hs; + h = dcerpc_binding_handle_create(p, + &raw_pipe_ccli_bh_ops, + NULL, + NULL, + &hs, + struct rawpipe_bh_state, + __location__); + if (h == NULL) { + return NULL; + } + hs->p = p; + return h; +} diff --git a/source4/librpc/rpc/dcerpc_raw.h b/source4/librpc/rpc/dcerpc_raw.h new file mode 100644 index 0000000..2c53b0d --- /dev/null +++ b/source4/librpc/rpc/dcerpc_raw.h @@ -0,0 +1,6 @@ +#ifndef __DCERPC_RAW_H__ +#define __DCERPC_RAW_H__ +struct dcerpc_pipe; +struct dcerpc_binding_handle; +struct dcerpc_binding_handle *create_rawpipe_handle(struct dcerpc_pipe *p); +#endif /* __DCERPC_RAW_H__ */ diff --git a/source4/librpc/wscript_build b/source4/librpc/wscript_build index 77d1777..0f97cc5 100755 --- a/source4/librpc/wscript_build +++ b/source4/librpc/wscript_build @@ -109,7 +109,7 @@ bld.SAMBA_LIBRARY('dcerpc', source='''rpc/dcerpc.c rpc/dcerpc_auth.c rpc/dcerpc_schannel.c rpc/dcerpc_util.c rpc/dcerpc_smb.c rpc/dcerpc_sock.c rpc/dcerpc_roh_channel_in.c rpc/dcerpc_roh_channel_out.c rpc/dcerpc_roh.c - rpc/dcerpc_connect.c rpc/dcerpc_secondary.c''', + rpc/dcerpc_connect.c rpc/dcerpc_secondary.c rpc/dcerpc_raw.c''', pc_files='dcerpc.pc', deps='samba_socket LIBCLI_RESOLVE LIBCLI_SMB LIBCLI_SMB2 ndr NDR_DCERPC RPC_NDR_EPMAPPER NDR_SCHANNEL RPC_NDR_NETLOGON RPC_NDR_MGMT gensec LIBCLI_AUTH smbclient-raw LP_RESOLVE tevent-util dcerpc-binding param_options http', autoproto='rpc/dcerpc_proto.h', -- 2.10.2 >From 9124d4e401faee54d9560d96113cc5b09b81b43d Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 5 Jun 2014 10:52:54 +0100 Subject: [PATCH 02/21] libcli/smb: Allow dynamic setting of the max_data in SMB Pipe transaction. Some services like WSP can send larger messages than the current 'Max Ioctl' limit, this results in the server producing a BUFFER_OVERFLOW status (and additionally clipping the message sent). Add support to allow a client to modify the hardcoded 'Max Ioctl' default value to allow the server to successfully send larger responses. Signed-off-by: Noel Power --- libcli/smb/tstream_smbXcli_np.c | 33 +++++++++++++++++++++++++-------- libcli/smb/tstream_smbXcli_np.h | 3 +++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/libcli/smb/tstream_smbXcli_np.c b/libcli/smb/tstream_smbXcli_np.c index a59db13..1ffde42 100644 --- a/libcli/smb/tstream_smbXcli_np.c +++ b/libcli/smb/tstream_smbXcli_np.c @@ -57,6 +57,7 @@ struct tstream_smbXcli_np { uint16_t fnum; uint64_t fid_persistent; uint64_t fid_volatile; + uint32_t max_data; struct { bool active; @@ -358,7 +359,7 @@ NTSTATUS _tstream_smbXcli_np_open_recv(struct tevent_req *req, cli_nps->fnum = state->fnum; cli_nps->fid_persistent = state->fid_persistent; cli_nps->fid_volatile = state->fid_volatile; - + cli_nps->max_data = TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE; talloc_set_destructor(cli_nps, tstream_smbXcli_np_destructor); talloc_set_destructor(cli_nps->conn_ref, tstream_smbXcli_np_ref_destructor); @@ -426,6 +427,14 @@ NTSTATUS tstream_smbXcli_np_use_trans(struct tstream_context *stream) return NT_STATUS_OK; } +void tstream_smbXcli_np_set_max_data(struct tstream_context *stream, + uint32_t max_data) +{ + struct tstream_smbXcli_np *cli_nps = + tstream_context_data(stream, struct tstream_smbXcli_np); + cli_nps->max_data = max_data; +} + unsigned int tstream_smbXcli_np_set_timeout(struct tstream_context *stream, unsigned int timeout) { @@ -524,6 +533,7 @@ static void tstream_smbXcli_np_writev_write_next(struct tevent_req *req) struct tevent_req *subreq; size_t i; size_t left = 0; + uint32_t max_data = cli_nps->max_data; for (i=0; i < state->count; i++) { left += state->vector[i].iov_len; @@ -536,7 +546,7 @@ static void tstream_smbXcli_np_writev_write_next(struct tevent_req *req) } cli_nps->write.ofs = 0; - cli_nps->write.left = MIN(left, TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); + cli_nps->write.left = MIN(left, max_data); cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf, uint8_t, cli_nps->write.left); if (tevent_req_nomem(cli_nps->write.buf, req)) { @@ -803,6 +813,7 @@ static void tstream_smbXcli_np_readv_read_next(struct tevent_req *req) tstream_context_data(state->stream, struct tstream_smbXcli_np); struct tevent_req *subreq; + uint32_t max_data = cli_nps->max_data; /* * copy the pending buffer first @@ -858,14 +869,14 @@ static void tstream_smbXcli_np_readv_read_next(struct tevent_req *req) cli_nps->session, cli_nps->fnum, 0, /* offset */ - TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); + max_data); } else { subreq = smb2cli_read_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, - TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, /* length */ + max_data, /* length */ 0, /* offset */ cli_nps->fid_persistent, cli_nps->fid_volatile, @@ -891,6 +902,7 @@ static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req) tstream_context_data(state->stream, struct tstream_smbXcli_np); struct tevent_req *subreq; + uint32_t max_data = cli_nps->max_data; state->trans.im = tevent_create_immediate(state); if (tevent_req_nomem(state->trans.im, req)) { @@ -913,7 +925,7 @@ static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req) NULL, 0, 0, cli_nps->write.buf, cli_nps->write.ofs, - TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); + max_data); } else { DATA_BLOB in_input_buffer = data_blob_null; DATA_BLOB in_output_buffer = data_blob_null; @@ -932,7 +944,7 @@ static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req) 0, /* in_max_input_length */ &in_input_buffer, /* in_max_output_length */ - TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, + max_data, &in_output_buffer, SMB2_IOCTL_FLAG_IS_FSCTL); } @@ -959,10 +971,14 @@ static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq) tevent_req_data(req, struct tstream_smbXcli_np_readv_state); struct tstream_smbXcli_np *cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np); + uint32_t max_data = cli_nps->max_data; uint8_t *rcvbuf; uint32_t received; NTSTATUS status; + /* reset max_data, should be set different every time (if required) */ + cli_nps->max_data = TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE; + if (cli_nps->is_smb1) { status = smb1cli_trans_recv(subreq, state, NULL, NULL, 0, NULL, NULL, 0, NULL, @@ -995,7 +1011,7 @@ static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq) return; } - if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) { + if (received > max_data) { tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__); return; } @@ -1045,6 +1061,7 @@ static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq) tevent_req_data(req, struct tstream_smbXcli_np_readv_state); struct tstream_smbXcli_np *cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np); + uint32_t max_data = cli_nps->max_data; uint8_t *rcvbuf; uint32_t received; NTSTATUS status; @@ -1079,7 +1096,7 @@ static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq) return; } - if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) { + if (received > max_data) { TALLOC_FREE(subreq); tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__); return; diff --git a/libcli/smb/tstream_smbXcli_np.h b/libcli/smb/tstream_smbXcli_np.h index e8c5c39..d7a4e3c 100644 --- a/libcli/smb/tstream_smbXcli_np.h +++ b/libcli/smb/tstream_smbXcli_np.h @@ -69,4 +69,7 @@ unsigned int tstream_smbXcli_np_set_timeout(struct tstream_context *stream, */ #define TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE 4280 +void tstream_smbXcli_np_set_max_data(struct tstream_context *stream, + uint32_t max_data); + #endif /* _CLI_NP_TSTREAM_H_ */ -- 2.10.2 >From d626da872a3ae990a471772b141b18af7c57ed10 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 24 Nov 2016 17:02:32 +0000 Subject: [PATCH 03/21] libcli/smb: Implement SMB2 FSCTL_PIPE_WAIT & SMB1 WaitNamedPipe functions Signed-off-by: Noel Power --- libcli/smb/smbXcli_base.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++ libcli/smb/smbXcli_base.h | 19 +++++ 2 files changed, 202 insertions(+) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index e24090d..5a37b8b 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -6251,3 +6251,186 @@ bool smb2cli_tcon_is_encryption_on(struct smbXcli_tcon *tcon) { return tcon->smb2.should_encrypt; } + + +struct fsctl_pipe_wait_req +{ + int64_t timeout; + uint32_t namelength; + uint8_t timeout_specified; + const char* name; +}; + +static bool write_pipe_wait_request_data( + struct fsctl_pipe_wait_req *wait_request, + DATA_BLOB *output) +{ + uint8_t *buf = output->data; + uint32_t pos = 0; + /* lenght of data up to pipe name */ + uint32_t min_len = 14; + bool result = false; + + if (output->length < min_len) { + result = false; + goto out; + } + SBVALS(buf, pos, wait_request->timeout); + pos += 8; + + SIVAL(buf, pos, wait_request->namelength); + pos +=4; + + if (min_len + wait_request->namelength > output->length) { + DEBUG(0,("incorrect buffer len, buffer size is %zu but we require %d\n", output->length, min_len + wait_request->namelength)); + result =false; + goto out; + } + + *(buf + pos) = wait_request->timeout_specified; + pos++; + + pos++; /* skip padding */ + + push_string((buf + pos), wait_request->name, + wait_request->namelength, + STR_UNICODE | STR_NOALIGN); + result = true; +out: + return result; +} + +struct smb2cli_wait_pipe_state { + struct smbXcli_conn *conn; + DATA_BLOB in_input_buffer; + DATA_BLOB in_output_buffer; +}; + +static void smb2cli_wait_pipe_done(struct tevent_req *subreq); + +struct tevent_req *smb2cli_wait_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + struct smbXcli_session *session, + struct smbXcli_tcon *tcon, + int64_t pipe_wait_timeout, + const char *pipe_name) +{ + struct tevent_req *req, *subreq; + struct smb2cli_wait_pipe_state *state; + struct fsctl_pipe_wait_req wait_req; + ZERO_STRUCT(wait_req); + + req = tevent_req_create(mem_ctx, &state, + struct smb2cli_wait_pipe_state); + if (req == NULL) { + return NULL; + } + state->conn = conn; + state->in_input_buffer = data_blob_talloc_zero(state, 14 + strlen(pipe_name) * 2); + wait_req.timeout = pipe_wait_timeout; + wait_req.namelength = strlen(pipe_name) * 2; + + wait_req.timeout_specified = false; + wait_req.name = pipe_name; + + if (!write_pipe_wait_request_data(&wait_req, &state->in_input_buffer)) { + return tevent_req_post(req, ev); + } + subreq = smb2cli_ioctl_send(state, ev, conn, + timeout_msec, session, tcon, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_PIPE_WAIT, + 0, /* in_max_input_length */ + &state->in_input_buffer, + 0, /* in_max_output_length */ + &state->in_output_buffer, + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + smb2cli_wait_pipe_done, + req); + return req; +} + +static void smb2cli_wait_pipe_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct smb2cli_wait_pipe_state *state = + tevent_req_data(req, struct smb2cli_wait_pipe_state); + NTSTATUS status; + + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + + status = smb2cli_ioctl_recv(subreq, + state, + &out_input_buffer, + &out_output_buffer); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + + +NTSTATUS smb2cli_wait_pipe_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct tevent_req *smb1cli_wait_named_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + uint32_t pid, + struct smbXcli_session *session, + struct smbXcli_tcon *tcon, + const char *pipe_name) +{ + struct tevent_req *req; + uint16_t function[] = {TRANSACT_WAITNAMEDPIPEHANDLESTATE, 0}; + const char *tmp_pipe_name = pipe_name; + + if (strstr(pipe_name, "\\PIPE\\") != pipe_name) { + tmp_pipe_name = talloc_asprintf(mem_ctx, + "\\PIPE\\%s", + pipe_name); + } + + if (tmp_pipe_name == NULL) { + return NULL; + } + + req = smb1cli_trans_send(mem_ctx, ev, + conn, + SMBtrans, + 0, 0, /* *_flags */ + 0, 0, /* *_flags2 */ + timeout_msec, + pid, + tcon, + session, + tmp_pipe_name, + 0, 0, 0, + function, 2, + 0, + NULL, 0, 0, + NULL, + 0, + 0); + + return req; +} + +NTSTATUS smb1cli_wait_named_pipe_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 702eedf..f6abd02 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -840,4 +840,23 @@ NTSTATUS smb2cli_echo_recv(struct tevent_req *req); NTSTATUS smb2cli_echo(struct smbXcli_conn *conn, uint32_t timeout_msec); +struct tevent_req *smb2cli_wait_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + struct smbXcli_session *session, + struct smbXcli_tcon *tcon, + int64_t pipe_wait_timeout, + const char *pipe_name); +NTSTATUS smb2cli_wait_pipe_recv(struct tevent_req *req); +struct tevent_req *smb1cli_wait_named_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXcli_conn *conn, + uint32_t timeout_msec, + uint32_t pid, + struct smbXcli_session *session, + struct smbXcli_tcon *tcon, + const char *pipe_name); +NTSTATUS smb1cli_wait_named_pipe_recv(struct tevent_req *req); + #endif /* _SMBXCLI_BASE_H_ */ -- 2.10.2 >From 0abf512bb715cd91345b365575264a828bb881e7 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 2 Dec 2014 17:26:41 +0000 Subject: [PATCH 04/21] pidl/lib: Add recursion detection logic to prevent looping. Under some circumstances 'can_contain_deferred' & 'align_type functions' can loop. This prevents a hang when processing sample idl like interface hang { typedef [public] struct { wsp_cbasestoragevariant variant[NUM_ENTRIES]; } vt_variant_wrap; typedef [public,nodiscriminant,switch_type(uint16)] union { [case(VT_I1)] int8 vt_i1; [case(VT_VARIANT)] vt_variant_wrap vt_variant_wrap; } variant_types; typedef [public] struct { [switch_is(vtype)] variant_types vvalue; } wsp_cbasestoragevariant; }; which will hang with the following command pidl --header --ndr-parser -- foo.idl Signed-off-by: Noel Power --- pidl/lib/Parse/Pidl/NDR.pm | 73 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/pidl/lib/Parse/Pidl/NDR.pm b/pidl/lib/Parse/Pidl/NDR.pm index 4659e31..c3be61f 100644 --- a/pidl/lib/Parse/Pidl/NDR.pm +++ b/pidl/lib/Parse/Pidl/NDR.pm @@ -402,16 +402,38 @@ sub can_contain_deferred($) return 0 if (Parse::Pidl::Typelist::is_scalar($type)); - return can_contain_deferred($type->{DATA}) if ($type->{TYPE} eq "TYPEDEF"); + if ( not defined($type->{INCANCONTAINDERRED})) { + $type->{INCANCONTAINDERRED} = 0; + } + + my $res; + + if ($type->{INCANCONTAINDERRED} == 1 ){ + $res = 0; + goto out_recurse; + } - return 0 unless defined($type->{ELEMENTS}); + $type->{INCANCONTAINDERRED} = 1; + if ($type->{TYPE} eq "TYPEDEF") { + $res = can_contain_deferred($type->{DATA}); + goto out; + } + + if (!defined($type->{ELEMENTS})) { + $res = 0; + goto out; + } foreach (@{$type->{ELEMENTS}}) { - return 1 if ($_->{POINTERS}); - return 1 if (can_contain_deferred ($_->{TYPE})); + if (($_->{POINTERS}) || can_contain_deferred ($_->{TYPE})) { + $res = 1; + goto out; + } } - - return 0; +out: + $type->{INCANCONTAINDERRED} = 0; +out_recurse: + return $res; } sub pointer_type($) @@ -481,23 +503,48 @@ sub align_type($) my $dt = getType($e); + if ( not defined($dt->{INGETALIGNTYPE})) { + $dt->{INGETALIGNTYPE} = 0; + } + + my $res; + if ($dt->{INGETALIGNTYPE} == 1 ){ + # reset it + $res = 0; + goto out_recurse; + } + + $dt->{INGETALIGNTYPE} = 1; + if ($dt->{TYPE} eq "TYPEDEF") { - return align_type($dt->{DATA}); + $res = align_type($dt->{DATA}); + goto out; } elsif ($dt->{TYPE} eq "CONFORMANCE") { - return $dt->{DATA}->{ALIGN}; + $res = $dt->{DATA}->{ALIGN}; + goto out; } elsif ($dt->{TYPE} eq "ENUM") { - return align_type(Parse::Pidl::Typelist::enum_type_fn($dt)); + $res = align_type(Parse::Pidl::Typelist::enum_type_fn($dt)); + goto out; } elsif ($dt->{TYPE} eq "BITMAP") { - return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt)); + $res = align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt)); + goto out; } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) { # Struct/union without body: assume 4 - return 4 unless (defined($dt->{ELEMENTS})); - return find_largest_alignment($dt); + $res = 4; + if (defined($dt->{ELEMENTS})) { + $res = find_largest_alignment($dt); + } + goto out; } elsif (($dt->{TYPE} eq "PIPE")) { - return 5; + $res = 5; + goto out; } die("Unknown data type type $dt->{TYPE}"); +out: + $dt->{INGETALIGNTYPE} = 0; +out_recurse: + return $res; } sub ParseElement($$$) -- 2.10.2 >From a28615b61146f6d94f55054b25d49a8ba8def349 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Mon, 25 Aug 2014 11:53:30 +0100 Subject: [PATCH 05/21] pidl/tests: Add tests for hang with nested struct. make sure hang test calls Parse::Pidl::Typelist::LoadIdl which triggers part of the hang Signed-off-by: Noel Power --- pidl/tests/header.pl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pidl/tests/header.pl b/pidl/tests/header.pl index db59484..bbd0b58 100755 --- a/pidl/tests/header.pl +++ b/pidl/tests/header.pl @@ -4,7 +4,7 @@ use strict; use warnings; -use Test::More tests => 27; +use Test::More tests => 30; use FindBin qw($RealBin); use lib "$RealBin"; use Util; @@ -23,6 +23,16 @@ sub parse_idl($) return Parse::Pidl::Samba4::Header::Parse($ndr); } +sub load_and_parse_idl($) +{ + my $text = shift; + my $ndr; + my $idl = Parse::Pidl::IDL::parse_string($text, "nofile"); + Parse::Pidl::Typelist::LoadIdl($idl, "noname"); + $ndr = Parse::Pidl::NDR::Parse($idl); + return Parse::Pidl::Samba4::Header::Parse($ndr); +} + like(parse_idl(""), qr/\/\* header auto-generated by pidl \*\/\n/sm, "includes work"); like(parse_idl("interface x {}"), qr/\/\* header auto-generated by pidl \*\/\n/sm, "simple empty interface doesn't cause overhead"); like(parse_idl("interface p { typedef struct { int y; } x; };"), @@ -59,6 +69,15 @@ like(parse_idl("interface p { typedef struct x { int p; } x; };"), like(parse_idl("cpp_quote(\"some-foo\")"), qr/some-foo/sm, "cpp quote"); +like(load_and_parse_idl("interface hang {typedef [public] struct { wsp_cbasestoragevariant a[SINGLE]; } foo; typedef [public,nodiscriminant,switch_type(uint16)] union { [case(VT_I1)] int8 vt_i1; [case(VT_VARIANT)] foo b; } variant_types; typedef [public] struct { [switch_is(vtype)] variant_types vvalue; } bar;};"), + qr/struct foo.*{.*struct wsp_cbasestoragevariant \*a.*struct bar {.*union variant_types vvalue.*;/sm,"test for hang with nested struct with union"); + +like(load_and_parse_idl("interface hang { typedef struct { uint32 count; bar a[count];} foo ; typedef struct { foo b; } bar; };"), + qr/struct foo.*{.*struct bar \*a;/sm,"test for hang with nested struct"); + +like(load_and_parse_idl("interface hang { typedef struct { bar a; } foo ; typedef struct { foo b; } bar; };"), + qr/struct foo.*{.*struct bar a;/sm,"test for hang with uncompilable nested struct"); + # Make sure GenerateFunctionInEnv and GenerateFunctionOutEnv work my $fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] }; is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionInEnv($fn)); -- 2.10.2 >From f91a4f24079b4c8aa5464c89d681dfda49e49ddc Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 3 Dec 2014 10:56:18 +0000 Subject: [PATCH 06/21] librpc/idl: Add idl for WSP and also some required helper functions. Represent the message data, structures and constants to do with the WSP (Windows Search Protocol) as idl. (see: https://msdn.microsoft.com/en-us/library/cc251767.aspx) Signed-off-by: Noel Power --- librpc/idl/wscript_build | 2 +- librpc/idl/wsp.idl | 1213 ++++++++++++++++++++++++++++++++++++++++++++++ librpc/idl/wsp_data.idl | 301 ++++++++++++ librpc/rpc/wsp_helper.c | 52 ++ librpc/rpc/wsp_helper.h | 31 ++ librpc/wscript_build | 12 +- 6 files changed, 1609 insertions(+), 2 deletions(-) create mode 100644 librpc/idl/wsp.idl create mode 100644 librpc/idl/wsp_data.idl create mode 100644 librpc/rpc/wsp_helper.c create mode 100644 librpc/rpc/wsp_helper.h diff --git a/librpc/idl/wscript_build b/librpc/idl/wscript_build index 13b29b2..172929a 100644 --- a/librpc/idl/wscript_build +++ b/librpc/idl/wscript_build @@ -35,7 +35,7 @@ bld.SAMBA_PIDL_LIST('PIDL', bld.SAMBA_PIDL_LIST('PIDL', '''rap.idl ntprinting.idl preg.idl ioctl.idl printcap.idl - fsrvp_state.idl''', + fsrvp_state.idl wsp_data.idl wsp.idl''', options='--header --ndr-parser', output_dir='../gen_ndr') diff --git a/librpc/idl/wsp.idl b/librpc/idl/wsp.idl new file mode 100644 index 0000000..2da9699 --- /dev/null +++ b/librpc/idl/wsp.idl @@ -0,0 +1,1213 @@ +#include "idl_types.h" +import "wsp_data.idl"; +import "misc.idl"; + +[ + version(1.0), + helpstring("Windows Search WSP Protocol"), + helper("../librpc/rpc/wsp_helper.h"), + pointer_default(unique) +] + +interface msftewds +{ + /* structs that contain hyper end up having + * an alignment of 8, alignment of 8 e.g. when + * handling wsp_cbstoragevariant doesn't work + * we need to represent the hyper as 2 4 byte + * numbers to ensure the alignment is still 4 + */ + + typedef [public] struct { + uint32 hi; + uint32 lo; + } wsp_hyper; + + typedef [public] struct { + uint32 hi32; + uint32 mid32; + uint32 lo32; + } vt_decimal; + + typedef [public] struct { + uint32 vvector_elements; + vt_decimal vvector_data[vvector_elements]; + } vt_decimal_vec; + /* + * variant elements in a vector (and presumably safearray also) + * must be aligned to 4-byte boundry, think this is automatic for + * elements which are structures + */ + typedef [public] struct { + [value(strlen_m_term(value)*2)] uint32 nbytes; + [flag(STR_NULLTERM)] string value; + } vt_bstr; + + typedef [public] struct { + uint32 vvector_elements; + vt_bstr vvector_data[vvector_elements]; + } vt_bstr_vec; + + typedef [public] struct { + [value(strlen_m_term(value))] uint32 nbytes; + [flag(STR_NULLTERM)] string value; + } vt_lpwstr; + + typedef [public] struct { + uint32 vvector_elements; + vt_lpwstr vvector_data[vvector_elements]; + } vt_lpwstr_vec; + + typedef [public] struct { + uint32 cclen; + uint8 bytes[cclen]; + } vt_compressed_lpwstr; + + typedef [public] struct { + uint32 vvector_elements; + vt_compressed_lpwstr vvector_data[vvector_elements]; + } vt_compressed_lpwstr_vec; + + typedef [public] struct { + uint32 vvector_elements; + int32 vvector_data[vvector_elements]; + } vt_i4_vec; + + typedef [public] struct { + uint32 vvector_elements; + uint32 vvector_data[vvector_elements]; + } vt_ui4_vec; +/* is there some way to specify the above like below instead of having a vector + for each element type e.g. see vt_lpwstr_vec, vt_bstr_vec & vt_i4_vec? + typedef [public] struct { + uint32 num; + variant_types vec[num]; + } vt_vector; +*/ + + typedef [public] struct { + uint32 celements; + uint32 ilbound; + } safearraybound; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + int32 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i4_safe_array; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + uint32 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui4_safe_array; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + vt_bstr vdata[calc_array_size(rgsabound, cdims)]; + } vt_bstr_safe_array; + + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + int8 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i1_safe_array; + + typedef [public] struct { + uint32 vvector_elements; + int8 vvector_data[vvector_elements]; + } vt_i1_vec; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + uint8 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui1_safe_array; + + typedef [public] struct { + uint32 vvector_elements; + uint8 vvector_data[vvector_elements]; + } vt_ui1_vec; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + int16 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i2_safe_array; + + typedef [public] struct { + uint32 vvector_elements; + int16 vvector_data[vvector_elements]; + } vt_i2_vec; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + uint16 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui2_safe_array; + + typedef [public] struct { + uint32 vvector_elements; + uint16 vvector_data[vvector_elements]; + } vt_ui2_vec; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + wsp_hyper vdata[calc_array_size(rgsabound, cdims)]; + } vt_wsp_hyper_safe_array; + + typedef [public] struct { + uint32 vvector_elements; + wsp_hyper vvector_data[vvector_elements]; + } vt_wsp_hyper_vec; + + typedef [public] struct { + uint32 vvector_elements; + GUID vvector_data[vvector_elements]; + } vt_clsid_vec; + + typedef [public] struct { + /* + * hack to allow wsp_cbasestoragevariant to be used before + * it is defined + */ + wsp_cbasestoragevariant variant[SINGLE_ITEM]; + } vt_variant_wrap; + + typedef [public] struct { + uint16 cdims; + uint16 ffeatures; + uint32 cbelements; + safearraybound rgsabound[cdims]; + vt_variant_wrap vdata[calc_array_size(rgsabound, cdims)]; + } vt_variant_wrap_safearray; + + typedef [public] struct { + uint32 vvector_elements; + vt_variant_wrap vvector_data[vvector_elements]; + } vt_variant_wrap_vec; + + typedef [public,nodiscriminant,switch_type(uint16)] union { + [case(VT_I1)] int8 vt_i1; + [case(VT_I1 | VT_ARRAY)] vt_i1_safe_array vt_i1_array; + [case(VT_I1 | VT_VECTOR)] vt_i1_vec vt_i1_vec; + + [case(VT_UI1)] uint8 vt_ui1; + [case(VT_UI1 | VT_ARRAY)] vt_ui1_safe_array vt_ui1_array; + [case(VT_UI1 | VT_VECTOR)] vt_ui1_vec vt_ui1_vec; + + [case(VT_I2)] int16 vt_i2; + [case(VT_I2 | VT_ARRAY)] vt_i2_safe_array vt_i2_array; + [case(VT_I2 | VT_VECTOR)] vt_i2_vec vt_i2_vec; + + [case(VT_UI2)] uint16 vt_ui2; + [case(VT_UI2 | VT_ARRAY)] vt_ui2_safe_array vt_ui2_array; + [case(VT_UI2 | VT_VECTOR)] vt_ui2_vec vt_ui2_vec; + + [case(VT_BOOL)] uint16 vt_bool; + [case(VT_BOOL | VT_ARRAY)] vt_ui2_safe_array vt_bool_array; + [case(VT_BOOL | VT_VECTOR)] vt_ui2_vec vt_bool_vec; + + [case(VT_I4)] int32 vt_i4; + [case(VT_I4 | VT_VECTOR)] vt_i4_vec vt_i4_vec; + [case(VT_I4 | VT_ARRAY)] vt_i4_safe_array vt_i4_array; + + [case(VT_UI4)] uint32 vt_ui4; + [case(VT_UI4 | VT_VECTOR)] vt_ui4_vec vt_ui4_vec; + [case(VT_UI4 | VT_ARRAY)] vt_ui4_safe_array vt_ui4_array; + + [case(VT_R4)] uint32 vt_r4; + [case(VT_R4 | VT_VECTOR)] vt_i4_vec vt_r4_vec; + [case(VT_R4 | VT_ARRAY)] vt_i4_safe_array vt_r4_array; + + [case(VT_INT)] int32 vt_int; + [case(VT_INT | VT_ARRAY)] vt_i4_safe_array vt_int_array; + + [case(VT_UINT)] uint32 vt_uint; + [case(VT_UINT | VT_ARRAY)] vt_ui4_safe_array vt_uint_array; + + [case(VT_ERROR)] uint32 vt_error; + [case(VT_ERROR | VT_VECTOR)] vt_ui4_vec vt_error_vec; + [case(VT_ERROR | VT_ARRAY)] vt_ui4_safe_array vt_error_array; + + [case(VT_I8)] wsp_hyper vt_i8; + [case(VT_I8 | VT_VECTOR)] vt_wsp_hyper_vec vt_i8_vec; + + [case(VT_UI8)] wsp_hyper vt_ui8; + [case(VT_UI8 | VT_VECTOR)] vt_wsp_hyper_vec vt_ui8_vec; + + [case(VT_R8)] wsp_hyper vt_r8; + [case(VT_R8 | VT_VECTOR)] vt_wsp_hyper_vec vt_r8_vec; + [case(VT_R8 | VT_ARRAY)] vt_wsp_hyper_safe_array vt_r8_array; + + [case(VT_CY)] wsp_hyper vt_cy; + [case(VT_CY | VT_VECTOR)] vt_wsp_hyper_vec vt_cy_vec; + [case(VT_CY | VT_ARRAY)] vt_wsp_hyper_safe_array vt_cy_array; + + [case(VT_DATE)] wsp_hyper vt_date; + [case(VT_DATE | VT_VECTOR)] vt_wsp_hyper_vec vt_date_vec; + [case(VT_DATE| VT_ARRAY)] vt_wsp_hyper_safe_array vt_date_array; + + [case(VT_FILETIME)] wsp_hyper vt_filetime; + [case(VT_FILETIME | VT_VECTOR)] vt_wsp_hyper_vec vt_filetime_vec; + + [case(VT_BSTR)] vt_bstr vt_bstr; + [case(VT_BSTR | VT_VECTOR)] vt_bstr_vec vt_bstr_v; + [case(VT_BSTR | VT_ARRAY)] vt_bstr_safe_array vt_bstr_array; + + [case(VT_LPWSTR)] vt_lpwstr vt_lpwstr; + [case(VT_LPWSTR | VT_VECTOR)] vt_lpwstr_vec vt_lpwstr_v; + + [case(VT_COMPRESSED_LPWSTR)] vt_compressed_lpwstr vt_compressed_lpwstr; + [case(VT_COMPRESSED_LPWSTR | VT_VECTOR)] vt_compressed_lpwstr_vec vt_compresseed_lpwstr_v; + + [case(VT_DECIMAL)] vt_decimal vt_decimal; + [case(VT_DECIMAL | VT_VECTOR)] vt_decimal_vec vt_decimal_v; + + [case(VT_CLSID)] GUID vt_clid; + [case(VT_CLSID | VT_VECTOR)] vt_clsid_vec vt_clsid_v; + + [case(VT_BLOB)] DATA_BLOB vt_blob; + [case(VT_BLOB_OBJECT)] DATA_BLOB vt_blob_object; + + [case(VT_NULL)]; + [case(VT_EMPTY)]; + + [case(VT_VARIANT)] vt_variant_wrap vt_variant_wrap; + [case(VT_VARIANT | VT_VECTOR)] vt_variant_wrap_vec vt_variant_wrap_vec; + [case(VT_VARIANT | VT_ARRAY)] vt_variant_wrap_safearray vt_variant_wrap_array; + } variant_types; + + typedef [public] struct { + uint16 vtype; + uint8 vdata1; + uint8 vdata2; + [switch_is(vtype)] variant_types vvalue; + } wsp_cbasestoragevariant; + + typedef [public, nodiscriminant, switch_type(uint32)] union { + [case(DBKIND_GUID_NAME)] string vstring; + [default]; + } wsp_cdbcolid_opt_name; + + typedef [public] struct { + uint32 ekind; + [flag(NDR_ALIGN8)] DATA_BLOB _pad1; + GUID guid; + uint32 uiid; + [switch_is(ekind)] wsp_cdbcolid_opt_name vstring; + } wsp_cdbcolid; + + + typedef [public] struct { + uint32 msg; + uint32 status; + uint32 checksum; + uint32 ulreserved2; + } wsp_header; + + typedef [public,flag(NDR_ALIGN4)] struct { + uint32 dbpropid; + uint32 dbpropoptions; + uint32 dbpropstatus; + wsp_cdbcolid colid; + wsp_cbasestoragevariant vvalue; + } wsp_cdbprop; + + typedef [flag(NDR_NOALIGN),public] struct { + GUID guidpropertyset; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + uint32 cproperties; + wsp_cdbprop aprops[cproperties]; + } wsp_cdbpropset; + + typedef [public] struct { + uint32 pidcolimn; + uint32 dworder; + uint32 dwindividual; + uint32 locale; + } wsp_csort; + + typedef [public] struct { + /* !! weirdly 8 bytes seems to be junk or unknown here. */ + uint32 unknown1; + uint32 unknown2; + uint32 count; + wsp_csort sortarray[count]; + } wsp_csortset; + + typedef [public] struct { + uint32 cpropsets; + wsp_cdbpropset propertyset1; + wsp_cdbpropset propertyset2; + } connectin_propsets; + + typedef [public] struct { + uint32 cextpropset; + wsp_cdbpropset apropertysets[cextpropset]; + } connectin_extpropsets; + + typedef [public] struct { + uint32 iclientversion; + uint32 fclientisremote; + uint32 cbblob1; + uint32 paddingcbblob2; + uint32 cbblob2; + uint8 padding[12]; + [flag(STR_NULLTERM)] string machinename; + [flag(STR_NULLTERM)] string username; + [flag(NDR_ALIGN8)] DATA_BLOB _pad1; + uint8 propsets[cbblob1]; + [flag(NDR_ALIGN8)] DATA_BLOB _pad2; + uint8 extpropsets[cbblob2]; + } wsp_cpmconnectin; + + typedef [public] struct { + uint32 reserved; + uint32 dwwinvermajor; + uint32 dwwinverminor; + uint32 dwnlsvermajor; + uint32 dwnlsverminor; + } version_info; + + typedef [public, nodiscriminant, switch_type(uint32)] union { + [case(WINDOWS_7)] version_info version_info; + [case(WINDOWS_2008)] version_info version_info; + [default] uint32 reserved[4]; + } version_dependant; + + typedef [public] struct { + uint32 server_version; + [switch_is(server_version)] version_dependant version_dependant; + } wsp_cpmconnectout; + + typedef [public] struct { + uint32 count; + uint32 indexes[count]; + } wsp_ccolumnset; + + + typedef [public] struct { + uint32 cnode; + wsp_crestriction panode[cnode]; + } wsp_cnoderestriction; + + typedef [public] struct { + uint32 len; + [charset(UTF16)] uint8 vstring[len*2]; + } wsp_len_string_pair; + + typedef [public, nodiscriminant, switch_type(uint32)] union { + [case(PRSPEC_LPWSTR)] wsp_len_string_pair propname; + [case(PRSPEC_PROPID)] uint32 prspec; + } wsp_propname_or_propid; + + typedef [public] struct { + uint32 cclabel; + [charset(UTF16)] uint8 vstring[cclabel*2]; + } wsp_labeldata; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(0)]; + [case(1)] wsp_labeldata data; + } opt_labeldata; + + typedef [public] struct { + uint32 ultype; + wsp_cbasestoragevariant prval; + uint8 labelpresent; + [switch_is(labelpresent)] opt_labeldata opt_data; + } wsp_rangeboundary; + + typedef [public] struct { + uint32 lcid; + uint32 crange; + wsp_rangeboundary arangebegin[crange + 1]; + } wsp_crangecategspec; + + typedef [public] struct { + uint32 ulcategtype; + wsp_csort sortkey; + wsp_crangecategspec crangecategspec; + } wsp_ccategspec; + + typedef [public] struct { + uint32 ulmaxnumtoret; + uint32 idrepresentitive; + } wsp_repofdata; + + typedef [public,nodiscriminant,switch_type(uint8)] union { + [case(DBAGGTTYPE_FIRST)] uint32 firstmaxnumret; + [case(DBAGGTTYPE_BYFREQ)] uint32 firstbyfreq; + [case(DBAGGTTYPE_REPRESENTATIVEOF)] wsp_repofdata repofdata; + [default]; + } opt_type_data; + + typedef [public] struct { + uint8 type; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + uint32 ccalias; + [charset(UTF16)] uint8 alias[ccalias*2]; + uint32 idcolumn; + [switch_is(type)] opt_type_data opt_data; + } wsp_caggregspec; + + typedef [public] struct { + uint32 ccount; + wsp_caggregspec aggregspecs[ccount]; + } wsp_caggregset; + + typedef [public] struct { + uint32 order; + wsp_caggregspec columnspec; + } wsp_caggregsortkey; + + typedef [public] struct { + uint32 ccount; + wsp_caggregsortkey sortkeys[ccount]; + } wsp_csortaggregset; + + typedef [public] struct { + uint8 type; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + wsp_cbasestoragevariant ingroupid; + wsp_csortaggregset sortaggregset; + } wsp_cingroupsortaggregset; + + typedef [public] struct { + uint32 ccount; + wsp_cingroupsortaggregset sortsets[ccount]; + } wsp_cingroupsortaggregsets; + + typedef [public] struct { + wsp_ccolumnset cscolumns; + wsp_ccategspec spec; + wsp_caggregset aggregset; + wsp_csortaggregset sortaggregset; + wsp_cingroupsortaggregsets ingroupsortaggset; + uint32 cmaxresults; + } wsp_ccategorizationspec; + + typedef [public] struct { + uint32 size; + wsp_ccategorizationspec categories[size]; + } wsp_ccategorizationset; + + typedef [flag(NDR_NOALIGN),public] struct { + [flag(NDR_ALIGN8)] DATA_BLOB _pad1; + GUID guidpropset; + uint32 ulkind; + [switch_is(ulkind)] wsp_propname_or_propid name_or_id; + } wsp_cfullpropspec; + + typedef [public] struct { + wsp_cfullpropspec property; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + uint32 cc; + [charset(UTF16)] uint8 pwcsphrase[cc*2]; + [flag(NDR_ALIGN4)] DATA_BLOB _pad2; + uint32 lcid; + uint32 ulgeneratemethod; + } wsp_ccontentrestriction; + + typedef [public] struct { + uint32 relop; + wsp_cfullpropspec property; + wsp_cbasestoragevariant prval; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + uint32 lcid; + } wsp_cpropertyrestriction; + + typedef [public] struct { + wsp_cfullpropspec property; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + uint32 cc; + [charset(UTF16)] uint8 pwcsphrase[cc*2]; + [flag(NDR_ALIGN4)] DATA_BLOB _pad2; + uint32 lcid; + } wsp_cnatlanguagerestriction; + + /* #FIXME circumvent 'incomplete type' error in gen code */ + typedef [public] struct { + wsp_crestriction restriction[SINGLE_ITEM]; + } wsp_wrap_crestriction; + + typedef [public] struct { + /* no IEEE 754 implementation for float ?? */ + /* float ffvalue; */ + uint32 ffvalue; + wsp_crestriction childres[SINGLE_ITEM]; + } wsp_ccoercionrestriction; + + typedef [public] struct { + uint32 pres; + [flag(NDR_ALIGN4)] DATA_BLOB padding; + uint32 uirankmethod; + } wsp_cvectorrestriction; + + typedef [public] struct { + uint32 cclowerpath; + [charset(UTF16)] uint8 lowerpath[cclowerpath*2]; + [flag(NDR_ALIGN4)] DATA_BLOB padding; + uint32 length; + uint32 frecursive; + uint32 fvirtual; + } wsp_cscoperestriction; + + typedef [public] struct { + uint32 whereid; + } wsp_creusewhere; + + typedef [public] struct { + uint32 relop; + uint32 pid; + wsp_cbasestoragevariant prval; + uint32 lcid; + uint8 restrictionpresent; + wsp_crestriction nextrestriction[SINGLE_ITEM]; + } wsp_cinternalpropertyrestriction; + + typedef [public] struct { + wsp_cfullpropspec property; + uint32 fik1; + uint32 fik2; + uint32 fik3; + uint32 flb; + uint32 cfeedbackdoc; + uint32 probquerypid; + } wsp_cprobrestriction; + + typedef [public] struct { + uint32 feedbackdoc; + wsp_cfullpropspec property; + } wsp_cfeedbackrestriction; + + typedef [public] struct { + wsp_cbasestoragevariant vdocument; + } wsp_creldocrestriction; + + typedef [public,nodiscriminant,switch_type(uint32)] union { + [case(RTNONE)]; + [case(RTNOT)] wsp_wrap_crestriction restriction; + // without pidl patches uncomment line below to hang pidl + [case(RTAND)] wsp_cnoderestriction cnoderestriction; + [case(RTOR)] wsp_cnoderestriction orcnoderestriction; + [case(RTCONTENT)] wsp_ccontentrestriction ccontentrestriction; + [case(RTPROPERTY)] wsp_cpropertyrestriction cpropertyrestriction; + [case(RTPROXIMITY)] wsp_cnoderestriction proximityrestriction; + [case(RTVECTOR)] wsp_cvectorrestriction vectorrestriction; + [case(RTNATLANGUAGE)] wsp_cnatlanguagerestriction cnatlanguagerestriction; + [case(RTSCOPE)] wsp_cscoperestriction scoperestriction; + [case(RTREUSEWHERE)] wsp_creusewhere reusewhere; + [case(RTINTERNALPROP)] wsp_cinternalpropertyrestriction internalpropertyrestriction; + [case(RTPHRASE)] wsp_cnoderestriction phraserestriction; + [case(RTCOERCE_ABSOLUTE)] wsp_ccoercionrestriction ccoercionrestriction_abs; + [case(RTCOERCE_ADD)] wsp_ccoercionrestriction ccoercionrestriction_add; + [case(RTCOERCE_MULTIPLY)] wsp_ccoercionrestriction ccoercionrestriction_mul; + [case(RTPROB)] wsp_cprobrestriction probrestriction; + [case(RTFEEDBACK)] wsp_cfeedbackrestriction feedbackrestriction; + [case(RTRELDOC)] wsp_creldocrestriction reldocrestriction; + + } wsp_crestrictions; + + typedef [public] struct { + uint32 ultype; + uint32 weight; + [switch_is(ultype)] wsp_crestrictions restriction; + } wsp_crestriction; + + typedef [flag(NDR_NOALIGN),public] struct { + uint8 count; + uint8 ispresent; + [flag(NDR_ALIGN4)] DATA_BLOB _pad1; + wsp_crestriction restrictions[count]; + } wsp_crestrictionarray; + + + typedef [public] struct { + uint32 ubooleanoptions; + uint32 ulmaxopenrows; + uint32 ulmemoryusage; + uint32 cmaxresults; + uint32 ccmdtimeout; + } wsp_crowsetproperties; + + typedef [public] struct { + uint32 count; + [flag(NDR_ALIGN4)] DATA_BLOB _pad2; + wsp_cfullpropspec apropspec[count]; + } wsp_cpidmapper; + + typedef [public] struct { + uint32 pid; + uint32 weight; + } wsp_sproperty; + + typedef [public] struct { + uint32 count; + uint32 grouppid; + wsp_sproperty props[count]; + } wsp_ccolumngroup; + + typedef [public] struct { + uint32 count; + wsp_ccolumngroup agrouparray[count]; + } wsp_ccolumngrouparray; + + typedef [public,nodiscriminant,switch_type(uint8)] union { + [case(0)]; + [default] wsp_csortset sortset; + }opt_wsp_csortset; + + typedef [public,nodiscriminant,switch_type(uint8)] union { + [case(0)]; + [default] wsp_crestrictionarray restrictionarray; + }opt_wsp_crestrictionarray; + + typedef [public,nodiscriminant,switch_type(uint8)] union { + [case(0)]; + [default] wsp_ccolumnset columnset; + }opt_wsp_ccolumnset; + + typedef [public,nodiscriminant,switch_type(uint8)] union { + [case(0)]; + [default] wsp_ccategorizationset ccategorizationset; + }opt_wsp_ccategorizationset; + + typedef [public] struct { + uint32 size; + uint8 ccolumnsetpresent; + /* at the moment it seems padding is not needed here (and below) + * as structures seems to be default aligned to 4 byte + * boundries, perhaps we need to define all structures as + * non-aligned and do all aligning manually? however for + * the moment lets see how it works out leaving defaults in + * place + */ + /*[flag(NDR_ALIGN4)] DATA_BLOB _pad1;*/ + [switch_is(ccolumnsetpresent)] opt_wsp_ccolumnset columnset; + uint8 crestrictionpresent; + [switch_is(crestrictionpresent)] opt_wsp_crestrictionarray restrictionarray; + uint8 csortsetpresent; + /*[flag(NDR_ALIGN4)] DATA_BLOB _pad2;*/ + [switch_is(csortsetpresent)] opt_wsp_csortset sortset; + uint8 ccategorizationsetpresent; + /*[flag(NDR_ALIGN4)] DATA_BLOB _pad3;*/ + [switch_is(ccategorizationsetpresent)] opt_wsp_ccategorizationset ccategorizationset; + wsp_crowsetproperties rowsetproperties; + wsp_cpidmapper pidmapper; + wsp_ccolumngrouparray grouparray; + uint32 lcid; + } wsp_cpmcreatequeryin; + + typedef [public] struct { + uint32 ftruesequential; + uint32 fWorkIdUnique; + /* + * uint32 acursors[SIZE]; + * + * after fWorkIdUnique is an array of uint32 cursors, + * actually there is always at least 1 item in the array, + * SIZE is determined by the optional ccategorizationset field in + * the request + */ + } wsp_cpmcreatequeryout; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(1)] uint8 value; + [case(0)]; + } aggregatetype; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(1)] uint16 value; + [case(0)]; + } valueoffset; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(1)] uint16 value; + [case(0)]; + } valuesize; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(1)] uint16 value; + [case(0)]; + } lengthoffset; + + typedef [public, nodiscriminant, switch_type(uint8)] union { + [case(1)] uint16 value; + [case(0)]; + } statusoffset; + + typedef [public] struct { + wsp_cfullpropspec propspec; + uint32 vtype; + uint8 aggregateused; + [switch_is(aggregateused)] aggregatetype aggregatetype; + uint8 valueused; + [switch_is(valueused)] valueoffset valueoffset; /* auto aligned to 2 byte boundry */ + [switch_is(valueused)] valuesize valuesize; /* auto aligned to 2 byte boundry */ + uint8 statusused; + [switch_is(statusused)] statusoffset statusoffset; /* auto aligned to 2 byte boundry */ + uint8 lengthused; + [switch_is(lengthused)] lengthoffset lengthoffset; /* auto aligned to 2 byte boundry */ + } wsp_ctablecolumn; + + /* can't see where the struct below is referenced */ + typedef [public] struct { + uint32 type; + uint32 lcid; + uint32 ccomplstrings; + wsp_serializedpropertyvalue apszcomplstrings[ccomplstrings]; + uint32 ccomplpids; + uint32 acomplpids[ccomplpids]; + } wsp_ccompletioncategspec; + + typedef [public] struct { + uint32 hcursor; + uint32 brow; + uint32 bbindingdesc; + uint32 dummy; + uint32 ccolumns; + wsp_ctablecolumn acolumns[ccolumns]; + } wsp_cpmsetbindingsin; + + typedef [public] struct { + uint32 cskip; + } wsp_crowseeknext; + + typedef [public] struct { + uint32 bmkoffset; + uint32 cskip; + uint32 hregion; + } wsp_crowseekat; + + typedef [public] struct { + uint32 ulnumerator; + uint32 uldenominator; + uint32 hregion; + } wsp_crowseekatratio; + + typedef [public] struct { + uint32 cbookmarks; + uint32 abookmarks[cbookmarks]; + uint32 maxret; + uint32 ascret[maxret]; + } wsp_crowseekbybookmark; + + typedef [public,nodiscriminant,switch_type(uint32)] union { + [case(EROWSEEKNONE)]; + [case(EROWSEEKNEXT)] wsp_crowseeknext crowseeknext; + [case(EROWSEEKAT)] wsp_crowseekat crowseekat; + [case(EROWSEEKATRATIO)] wsp_crowseekatratio crowseekatratio; + [case(EROWSEEKBYBOOKMARK)] wsp_crowseekbybookmark crowseekbybookmark; + } wsp_seekdescription; + + typedef [public] struct { + uint32 hcursor; + uint32 crowstotransfer; + uint32 cbrowWidth; + uint32 cbseek; + uint32 cbreserved; + uint32 cbreadbuffer; + uint32 ulclientbase; + uint32 fbwdfetch; + uint32 etype; + uint32 chapt; + [switch_is(etype)] wsp_seekdescription seekdescription; + } wsp_cpmgetrowsin; + + typedef [public] struct { + uint16 vtype; + uint16 reserved1; + uint32 reserved2; + uint32 offset; + } wsp_crowvariant32; + + typedef [public] struct { + uint32 rowsreturned; + uint32 etype; + uint32 chapt; + [switch_is(etype)] wsp_seekdescription seekdescription; + /* + * following rows data is not defined here, size is unknown + * in the context of this structure but is the size of cbreadbuffer + * defined in cpmgetrowsin. + */ + } wsp_cpmgetrowsout; + + typedef [public] struct { + uint32 hcursor; + } wsp_cpmfreecursorin; + + typedef [public] struct { + uint32 ccursorsremaining; + } wsp_cpmfreecursorout; + + typedef [public] struct { + uint32 hcursor; + } wsp_cpmgetquerystatusin; + + typedef [public] struct { + uint32 qstatus; + } wsp_cpmgetquerystatusout; + + typedef [public] struct { + uint32 hcursor; + uint32 bmk; + } wsp_cpmgetquerystatusexin; + + typedef [public] struct { + uint32 qstatus; + uint32 cfiltereddocuments; + uint32 cdocumentstofilter; + uint32 dwratiofinisheddenominator; + uint32 dwratiofinishednumerator; + uint32 irowbmk; + uint32 crowstotal; + uint32 maxrank; + uint32 resultsfound; + uint32 whereid; + } wsp_cpmgetquerystatusexout; + + typedef [public] struct { + uint32 hcursor; + uint32 chapter; + } wsp_cpmrestartpositionin; + + typedef [public] struct { + uint32 hcursor; + uint32 fquick; + } wsp_cpmratiofinishedin; + + typedef [public] struct { + uint32 ulnumerator; + uint32 uldenominator; + uint32 crows; + uint32 fnewrows; + } wsp_cpmratiofinishedout; + + typedef [public] struct { + uint32 wid; + uint32 cbsofar; + uint32 cbpropspec; + uint32 cbchunk; + wsp_cfullpropspec propspec; + } wsp_cpmfetchvaluein; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + int8 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i1_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + uint8 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui1_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + int16 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i2_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + uint16 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui2_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + int32 vdata[calc_array_size(rgsabound, cdims)]; + } vt_i4_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + uint32 vdata[calc_array_size(rgsabound, cdims)]; + } vt_ui4_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + wsp_hyper vdata[calc_array_size(rgsabound, cdims)]; + } vt_wsp_hyper_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + vt_bstr vdata[calc_array_size(rgsabound, cdims)]; + } vt_bstr_safe2_array; + + typedef [public] struct { + uint16 cdims; + safearraybound rgsabound[cdims]; + vt_variant_wrap vdata[calc_array_size(rgsabound, cdims)]; + } vt_variant_wrap_safearray2; + + typedef [public,nodiscriminant,switch_type(uint32)] union { + [case(VT_I1)] int8 vt_i1; + [case(VT_I1 | VT_ARRAY)] vt_i1_safe2_array vt_i1_array; + [case(VT_I1 | VT_VECTOR)] vt_i1_vec vt_i1_vec; + + [case(VT_UI1)] uint8 vt_ui1; + [case(VT_UI1 | VT_ARRAY)] vt_ui1_safe2_array vt_ui1_array; + [case(VT_UI1 | VT_VECTOR)] vt_ui1_vec vt_ui1_vec; + + [case(VT_I2)] int16 vt_i2; + [case(VT_I2 | VT_ARRAY)] vt_i2_safe2_array vt_i2_array; + [case(VT_I2 | VT_VECTOR)] vt_i2_vec vt_i2_vec; + + [case(VT_UI2)] uint16 vt_ui2; + [case(VT_UI2 | VT_ARRAY)] vt_ui2_safe2_array vt_ui2_array; + [case(VT_UI2 | VT_VECTOR)] vt_ui2_vec vt_ui2_vec; + + [case(VT_BOOL)] uint16 vt_bool; + [case(VT_BOOL | VT_ARRAY)] vt_ui2_safe2_array vt_bool_array; + [case(VT_BOOL | VT_VECTOR)] vt_ui2_vec vt_bool_vec; + + [case(VT_I4)] int32 vt_i4; + [case(VT_I4 | VT_VECTOR)] vt_i4_vec vt_i4_vec; + [case(VT_I4 | VT_ARRAY)] vt_i4_safe2_array vt_i4_array; + + [case(VT_UI4)] uint32 vt_ui4; + [case(VT_UI4 | VT_VECTOR)] vt_ui4_vec vt_ui4_vec; + [case(VT_UI4 | VT_ARRAY)] vt_ui4_safe2_array vt_ui4_array; + + [case(VT_R4)] uint32 vt_r4; + [case(VT_R4 | VT_VECTOR)] vt_i4_vec vt_r4_vec; + [case(VT_R4 | VT_ARRAY)] vt_i4_safe2_array vt_r4_array; + + [case(VT_INT)] int32 vt_int; + [case(VT_INT | VT_ARRAY)] vt_i4_safe2_array vt_int_array; + + [case(VT_UINT)] uint32 vt_uint; + [case(VT_UINT | VT_ARRAY)] vt_ui4_safe2_array vt_uint_array; + + [case(VT_ERROR)] uint32 vt_error; + [case(VT_ERROR | VT_VECTOR)] vt_ui4_vec vt_error_vec; + [case(VT_ERROR | VT_ARRAY)] vt_ui4_safe2_array vt_error_array; + + [case(VT_I8)] wsp_hyper vt_i8; + [case(VT_I8 | VT_VECTOR)] vt_wsp_hyper_vec vt_i8_vec; + + [case(VT_UI8)] wsp_hyper vt_ui8; + [case(VT_UI8 | VT_VECTOR)] vt_wsp_hyper_vec vt_ui8_vec; + + [case(VT_R8)] wsp_hyper vt_r8; + [case(VT_R8 | VT_VECTOR)] vt_wsp_hyper_vec vt_r8_vec; + [case(VT_R8 | VT_ARRAY)] vt_wsp_hyper_safe2_array vt_r8_array; + + [case(VT_CY)] wsp_hyper vt_cy; + [case(VT_CY | VT_VECTOR)] vt_wsp_hyper_vec vt_cy_vec; + [case(VT_CY | VT_ARRAY)] vt_wsp_hyper_safe2_array vt_cy_array; + + [case(VT_DATE)] wsp_hyper vt_date; + [case(VT_DATE | VT_VECTOR)] vt_wsp_hyper_vec vt_date_vec; + [case(VT_DATE| VT_ARRAY)] vt_wsp_hyper_safe2_array vt_date_array; + + [case(VT_FILETIME)] wsp_hyper vt_filetime; + [case(VT_FILETIME | VT_VECTOR)] vt_wsp_hyper_vec vt_filetime_vec; + + [case(VT_BSTR)] vt_bstr vt_bstr; + [case(VT_BSTR | VT_VECTOR)] vt_bstr_vec vt_bstr_v; + [case(VT_BSTR | VT_ARRAY)] vt_bstr_safe2_array vt_bstr_array; + + [case(VT_LPWSTR)] vt_lpwstr vt_lpwstr; + [case(VT_LPWSTR | VT_VECTOR)] vt_lpwstr_vec vt_lpwstr_v; + + [case(VT_COMPRESSED_LPWSTR)] vt_compressed_lpwstr vt_compressed_lpwstr; + [case(VT_COMPRESSED_LPWSTR | VT_VECTOR)] vt_compressed_lpwstr_vec vt_compresseed_lpwstr_v; + + [case(VT_DECIMAL)] vt_decimal vt_decimal; + [case(VT_DECIMAL | VT_VECTOR)] vt_decimal_vec vt_decimal_v; + + [case(VT_CLSID)] GUID vt_clid; + [case(VT_CLSID | VT_VECTOR)] vt_clsid_vec vt_clsid_v; + + [case(VT_BLOB)] DATA_BLOB vt_blob; + [case(VT_BLOB_OBJECT)] DATA_BLOB vt_blob_object; + + [case(VT_NULL)]; + [case(VT_EMPTY)]; + + [case(VT_VARIANT)] vt_variant_wrap vt_variant_wrap; + [case(VT_VARIANT | VT_VECTOR)] vt_variant_wrap_vec vt_variant_wrap_vec; + [case(VT_VARIANT | VT_ARRAY)] vt_variant_wrap_safearray2 vt_variant_wrap_array; + } serialised_types; + + typedef [public] struct { + uint32 dwtype; + [switch_is(dwtype)] serialised_types rgb; + } wsp_serializedpropertyvalue; + + typedef [public] struct { + uint32 cbvalue; + uint32 fmoreexists; + uint32 fvalueexists; + /* + * very nearly the same as wsp_cbasestoragevariant, only + * different in how array types are represented, also only + * a portion of the value (serializedpropertyvalue) is here + */ + uint8 value[cbvalue]; + } wsp_cpmfetchvalueout; + + typedef [public] struct { + uint32 priority; + uint32 eventfrequency; + } wsp_cpmsetscopeprioritizationin; + + typedef [public] struct { + uint32 watchnotify; + } wsp_cpmsendnotifyout; + + typedef [public] struct { + uint32 wid; + uint8 eventinfo; + uint8 rowitemstate; + uint8 changeditemstate; + uint8 rowsetevent; + wsp_hyper rowseteventdata1; + wsp_hyper rowseteventdata2; + } wsp_cpmgetrowsetnotifyout; + + typedef [public] struct { + uint32 dwindexeditems; + uint32 dwoutstandingadds; + uint32 dwoustandingmodifies; + } wsp_cpmgetscopestatisticsout; + + typedef [public] struct { + uint32 hcursor; + uint32 chapt; + uint32 bmk; + } wsp_cpmgetapproximatepositionin; + + typedef [public] struct { + uint32 numerator; + uint32 denominator; + } wsp_cpmgetapproximatepositionout; + + typedef [public] struct { + uint32 hcursor; + uint32 chapt; + uint32 bmkfirst; + uint32 bmksecond; + } wsp_cpmcomparebmkin; + + typedef [public] struct { + uint32 dwcomparison; + } wsp_cpmcomparebmkout; + + + typedef [public] struct { + uint32 cbstruct; + uint32 cwordlist; + uint32 cpersistentindex; + uint32 cqueries; + uint32 cdocuments; + uint32 cfreshtest; + uint32 dwmergeprogress; + uint32 estate; + uint32 cfiltereddocuments; + uint32 ctotaldocuments; + uint32 cpendingscans; + uint32 dwindexsize; + uint32 cuniquekeys; + uint32 csecqdocuments; + uint32 dwpropcachesize; + } wsp_cpmcistateinout; + + typedef [public] struct { + uint32 cwids; + uint32 cdepthprev; + uint32 pwids[cwids]; + uint32 prgirowprev[cdepthprev]; + } wsp_findindicesin; + + typedef [public] struct { + uint32 cdepthnext; + uint32 prgirownext[cdepthnext]; + } wsp_findindicesout; + + typedef [public] struct { + uint32 hcursor; + uint32 chapt; + } wsp_cpmsresetstartpos; + + typedef [public, nodiscriminant, switch_type(uint32)] union { + [case(CPMCONNECT)] wsp_cpmconnectin cpmconnect; + [case(CPMCREATEQUERY)] wsp_cpmcreatequeryin cpmcreatequery; + [case(CPMFREECURSOR)] wsp_cpmfreecursorin cpmfreecursor; + [case(CPMGETROWS)] wsp_cpmgetrowsin cpmgetrows; + [case(CPMSETBINDINGSIN)] wsp_cpmsetbindingsin cpmsetbindings; + [case(CPMRESTARTPOSITIONIN)] wsp_cpmsresetstartpos cpmresetstartpos; + [case(CPMGETQUERYSTATUS)] wsp_cpmgetquerystatusin cpmgetquerystatus; + [case(CPMGETQUERYSTATUSEX)] wsp_cpmgetquerystatusexin cpmgetquerystatusex; + [case(CPMSETSCOPEPRIORITIZATION)] wsp_cpmsetscopeprioritizationin cpmsetscopeprioritizationin; + [case(CPMGETNOTIFY)]; /*header only*/ + [case(CPMGETROWSETNOTIFY)]; /*header only*/ + [case(CPMDISCONNECT)]; /*header only*/ + [case(CPMGETSCOPESTATISTICS)]; /*header only*/ + [case(CPMGETAPPROXIMATEPOSITION)] wsp_cpmgetapproximatepositionin getapproximateposition; + [case(CPMCOMPAREBMK)] wsp_cpmcomparebmkin cpmcomparebmk; + [case(CPMCISTATEOUT)] wsp_cpmcistateinout wsp_cpmcistate; + [case(CPMFINDINDICES)] wsp_findindicesin wsp_findindices; + [case(CPMRATIOFINISHED)] wsp_cpmratiofinishedin wsp_cpmratiofinished; + } req_message; + + typedef [public, nodiscriminant, switch_type(uint32)] union { + [case(CPMCONNECT)] wsp_cpmconnectout cpmconnect; + [case(CPMCREATEQUERY)] wsp_cpmcreatequeryout cpmcreatequery; + [case(CPMFREECURSOR)] wsp_cpmfreecursorout cpmfreecursor; + [case(CPMGETROWS)] wsp_cpmgetrowsout cpmgetrows; + [case(CPMSETBINDINGSIN)]; /* just has header */ + [case(CPMRESTARTPOSITIONIN)]; /* just has header */ + [case(CPMGETQUERYSTATUS)] wsp_cpmgetquerystatusout cpmgetquerystatus; + [case(CPMSENDNOTIFYOUT)] wsp_cpmsendnotifyout cpmsendnotifyoutcpmgetquerystatus; + [case(CPMGETQUERYSTATUSEX)] wsp_cpmgetquerystatusexout cpmgetquerystatusex; + [case(CPMSETSCOPEPRIORITIZATION)]; /* just had header */ + [case(CPMGETROWSETNOTIFY)] wsp_cpmgetrowsetnotifyout cpmgetrowsetnotifyout; + [case(CPMGETAPPROXIMATEPOSITION)] wsp_cpmgetapproximatepositionout getapproximateposition; + [case(CPMCOMPAREBMK)] wsp_cpmcomparebmkout cpmcomparebmk; + [case(CPMCISTATEOUT)] wsp_cpmcistateinout wsp_cpmcistate; + [case(CPMFINDINDICES)] wsp_findindicesout wsp_findindices; + [case(CPMGETSCOPESTATISTICS)] wsp_cpmgetscopestatisticsout cpmgetscopestatistics; + [case(CPMRATIOFINISHED)] wsp_cpmratiofinishedout wsp_cpmratiofinished; + } resp_message; + + typedef [public] struct { + wsp_header header; + [switch_is(header.msg)] req_message message; + } wsp_request; + + typedef [public] struct { + wsp_header header; + [switch_is(header.msg)] resp_message message; + } wsp_response; + +}; + diff --git a/librpc/idl/wsp_data.idl b/librpc/idl/wsp_data.idl new file mode 100644 index 0000000..4fa175f --- /dev/null +++ b/librpc/idl/wsp_data.idl @@ -0,0 +1,301 @@ +#include "idl_types.h" +[ + pointer_default(unique) +] + +interface constants +{ + /* values for guidPropertySet */ + const char* DBPROPSET_FSCIFRMWRK_EXT = "A9BD1526-6A80-11D0-8C9D-0020AF1D740E"; + const char* DBPROPSET_QUERYEXT = "A7AC77ED-F8D7-11CE-A798-0020F8008025"; + const char* DBPROPSET_CIFRMWRKCORE_EXT = "AFAFACA5-B5D1-11D0-8C62-00C04FC2DB8D"; + const char* DBPROPSET_MSIDXS_ROWSETEXT = "AA6EE6B0-E828-11D0-B23E-00AA0047FC01"; + + /* Chapter and bookmark handle well known values */ + const uint32_t DB_NULL_HCHAPTER = 0x00000000; + const uint32_t DBBMK_FIRST = 0xFFFFFFFC; + const uint32_t DBBMK_LAST = 0xFFFFFFFD; + /* properties of DBPROPSET_FSCIFRMWRK_EXT propertyset */ + const uint32_t DBPROP_CI_CATALOG_NAME = 0x00000002; + const uint32_t DBPROP_CI_INCLUDE_SCOPES = 0x00000003; + const uint32_t DBPROP_CI_SCOPE_FLAGS = 0x00000004; + const uint32_t DBPROP_CI_QUERY_TYPE = 0x00000007; + const uint32_t DBPROP_GENERICOPTIONS_STRING = 0x00000006; + const uint32_t DBPROP_USECONTENTINDEX = 0x00000002; + const uint32_t DBPROP_IGNORENOISEONLYCLAUSES = 0x00000005; + const uint32_t DBPROP_DEFERCATALOGVERIFICATION = 0x00000008; + const uint32_t DBPROP_IGNORESBRI = 0x0000000E; + const uint32_t DBPROP_GENERATEPARSETREE = 0x0000000A; + const uint32_t DBPROP_FREETEXTANYTERM = 0x0000000C; + const uint32_t DBPROP_FREETEXTUSESTEMMING = 0x0000000D; + + /* properties of DBPROPSET_QUERYEXT propertyset */ + const uint32_t DBPROP_DEFERNONINDEXEDTRIMMING = 0x00000003; + const uint32_t DBPROP_USEEXTENDEDDBTYPES = 0x00000004; + const uint32_t DBPROP_FIRSTROWS = 0x00000007; + const uint32_t DBPROP_ENABLEROWSETEVENTS = 0x00000010; + + /* properties of DBPROPSET_MSIDXS_ROWSETEXT */ + + const uint32_t MSIDXSPROP_ROWSETQUERYSTATUS = 0x02; + const uint32_t MSIDXSPROP_COMMAND_LOCALE_STRING = 0x03; + const uint32_t MSIDXSPROP_QUERY_RESTRICTION = 0x04; + const uint32_t MSIDXSPROP_PARSE_TREE = 0x05; + const uint32_t MSIDXSPROP_MAX_RANK = 0x06; + const uint32_t MSIDXSPROP_RESULTS_FOUND = 0x07; + + /* flags of DBPROP_CI_SCOPE_FLAGS property */ + const uint32_t QUERY_DEEP = 0x01; + const uint32_t QUERY_VIRTUAL_PATH = 0x02; + + /* query type for BPROP_CI_QUERY_TYPE property */ + const uint32_t CINORMAL = 0x00000000; + + /* properties of DBPROPSET_CIFRMWRKCORE_EXT propertyset */ + + const uint32_t DBPROP_MACHINE = 0x00000002; + const uint32_t DBPROP_CLIENT_CLSID = 0x00000003; + + /* + * STAT bit constants + */ + + /* The asynchronous query is still running. */ + const uint32_t STAT_BUSY = 0x00000000; + /* The query is in an error state. */ + const uint32_t STAT_ERROR = 0x00000001; + /* The query is complete and rows can be requested. */ + const uint32_t STAT_DONE = 0x00000002; + /* The query is comp*/ + const uint32_t STAT_REFRESH = 0x00000003; + /* + * Noise words were replaced by wildcard characters in the + * content query. + */ + const uint32_t STAT_NOISE_WORDS = 0x00000010; + /* + * The results of the query might be incorrect because the + * query involved modified but unindexed files. + */ + const uint32_t STAT_CONTENT_OUT_OF_DATE = 0x00000020; + /* + * The content query was too complex to complete or + * required enumeration instead of use of the content index. + */ + const uint32_t STAT_CONTENT_QUERY_INCOMPLETE = 0x00000080; + /* + * The results of the query might be incorrect because the + * query execution reached the maximum allowable time. + */ + const uint32_t STAT_TIME_LIMIT_EXCEEDED = 0x00000100; + + /* + * a const to force an inline array to be evaluated at runtime to + * to get around an incomplete type error + */ + const uint32 SINGLE_ITEM = 1; + + /* WSP message types */ + + /* CPMConnectIn or CPMConnectOut */ + const uint32 CPMCONNECT = 0x000000C8; + /* CPMDisconnect */ + const uint32 CPMDISCONNECT = 0x000000C9; + /* CPMCreateQueryIn or CPMCreateQueryOut */ + const uint32 CPMCREATEQUERY = 0x000000CA; + /* CPMFreeCursorIn or CPMFreeCursorOut */ + const uint32 CPMFREECURSOR = 0x000000CB; + /* CPMGetRowsIn or CPMGetRowsOut */ + const uint32 CPMGETROWS = 0x000000CC; + /* CPMRatioFinishedIn or CPMRatioFinishedOut */ + const uint32 CPMRATIOFINISHED = 0x000000CD; + /* CPMCompareBmkIn or CPMCompareBmkOut */ + const uint32 CPMCOMPAREBMK = 0x000000CE; + /* CPMGetApproximatePositionIn or CPMGetApproximatePositionOut */ + const uint32 CPMGETAPPROXIMATEPOSITION = 0x000000CF; + /* CPMSetBindingsIn */ + const uint32 CPMSETBINDINGSIN = 0x000000D0; + /* CPMGetNotify */ + const uint32 CPMGETNOTIFY = 0x000000D1; + /* CPMSendNotifyOut */ + const uint32 CPMSENDNOTIFYOUT = 0x000000D2; + /* CPMGetQueryStatusIn or CPMGetQueryStatusOut */ + const uint32 CPMGETQUERYSTATUS = 0x000000D7; + /* CPMCiStateInOut */ + const uint32 CPMCISTATEOUT = 0x000000D9; + /* CPMFetchValueIn or CPMFetchValueOut */ + const uint32 CPMFETCHVALUE = 0x000000E4; + /* CPMGetQueryStatusExIn or CPMGetQueryStatusExOut */ + const uint32 CPMGETQUERYSTATUSEX = 0x000000E7; + /* CPMRestartPositionIn */ + const uint32 CPMRESTARTPOSITIONIN = 0x000000E8; + /* CPMSetCatStateIn (not supported) */ + const uint32 CPMSETCATSTATEIN = 0x000000EC; + /* CPMGetRowsetNotifyIn or CPMGetRowsetNotifyOut */ + const uint32 CPMGETROWSETNOTIFY = 0x000000F1; + /* CPMFindIndicesIn, or CPMFindIndicesOut */ + const uint32 CPMFINDINDICES = 0x000000F2; + /* CPMSetScopePrioritizationIn or CPMSetScopePrioritizationOut */ + const uint32 CPMSETSCOPEPRIORITIZATION = 0x000000F3; + /* CPMGetScopeStatisticsIn or CPMGetScopeStatisticsOut */ + const uint32 CPMGETSCOPESTATISTICS = 0x000000F4; + + const uint32 DBKIND_GUID_NAME = 0x00000000; + const uint32 DBKIND_GUID_PROPID = 0x00000001; + const uint32 PRSPEC_LPWSTR = 0x00000000; + const uint32 PRSPEC_PROPID = 0x00000001; + /* type constants for variant types */ + + const uint32 VT_EMPTY = 0x0000; + const uint32 VT_NULL = 0x0001; + const uint32 VT_I2 = 0x0002; + const uint32 VT_I4 = 0x0003; + const uint32 VT_R4 = 0x0004; + const uint32 VT_R8 = 0x0005; + const uint32 VT_CY = 0x0006; + const uint32 VT_DATE = 0x0007; + const uint32 VT_BSTR = 0x0008; + const uint32 VT_I1 = 0x0010; + const uint32 VT_UI1 = 0x0011; + const uint32 VT_UI2 = 0x0012; + const uint32 VT_UI4 = 0x0013; + const uint32 VT_I8 = 0x0014; + const uint32 VT_UI8 = 0x0015; + const uint32 VT_INT = 0x0016; + const uint32 VT_UINT = 0x0017; + const uint32 VT_ERROR = 0x000A; + const uint32 VT_BOOL = 0x000B; + const uint32 VT_VARIANT = 0x000C; + const uint32 VT_DECIMAL = 0x000E; + const uint32 VT_FILETIME = 0x0040; + const uint32 VT_BLOB = 0x0041; + const uint32 VT_BLOB_OBJECT = 0x0046; + const uint32 VT_CLSID = 0x0048; + const uint32 VT_LPSTR = 0x001E; + const uint32 VT_LPWSTR = 0x001F; + const uint32 VT_COMPRESSED_LPWSTR = 0x0023; + const uint32 VT_VECTOR = 0x1000; + const uint32 VT_ARRAY = 0x2000; + + /* restriction types */ + const uint32 RTNONE = 0x00000000; + const uint32 RTAND = 0x00000001; + const uint32 RTOR = 0x00000002; + const uint32 RTNOT = 0x00000003; + const uint32 RTCONTENT = 0x00000004; + const uint32 RTPROPERTY = 0x00000005; + const uint32 RTPROXIMITY = 0x00000006; + const uint32 RTVECTOR = 0x00000007; + const uint32 RTNATLANGUAGE = 0x00000008; + const uint32 RTSCOPE = 0x00000009; + const uint32 RTREUSEWHERE = 0x00000011; + const uint32 RTINTERNALPROP = 0x00FFFFFA; + const uint32 RTPHRASE = 0x00FFFFFD; + const uint32 RTCOERCE_ADD = 0x0000000A; + const uint32 RTCOERCE_MULTIPLY = 0x0000000B; + const uint32 RTCOERCE_ABSOLUTE = 0x0000000C; + const uint32 RTPROB = 0x0000000D; + const uint32 RTFEEDBACK = 0x0000000E; + const uint32 RTRELDOC = 0x0000000F; + + + /* Row seek types */ + const uint32 EROWSEEKNONE = 0x00000000; + const uint32 EROWSEEKNEXT = 0x00000001; + const uint32 EROWSEEKAT = 0x00000002; + const uint32 EROWSEEKATRATIO = 0x00000003; + const uint32 EROWSEEKBYBOOKMARK = 0x00000004; + + const uint32 WINDOWS_7 = 0x00000700; + const uint32 WINDOWS_2008 = 0x00010700; + + /* Relops */ + const uint32 PRLT = 0x00000000; + const uint32 PRLE = 0x00000001; + const uint32 PRGT = 0x00000002; + const uint32 PRGE = 0x00000003; + const uint32 PREQ = 0x00000004; + const uint32 PRNE = 0x00000005; + const uint32 PRRE = 0x00000006; + const uint32 PRALLBITS = 0x00000007; + const uint32 PRSOMEBITS = 0x00000008; + const uint32 PRALL = 0x00000100; + const uint32 PRANY = 0x00000200; + + const uint32 PROPAGATE_NONE = 0; + const uint32 PROPAGATE_ADD = 1; + const uint32 PROPAGATE_DELETE = 2; + const uint32 PROPAGATE_MODIFY = 3; + const uint32 PROPAGATE_ROWSET = 4; + + const uint32 ROWSETEVENT_ITEMSTATE_NOTINROWSET = 0; + const uint32 ROWSETEVENT_ITEMSTATE_INROWSET = 1; + const uint32 ROWSETEVENT_ITEMSTATE_UNKNOWN = 2; + + const uint32 ROWSETEVENT_TYPE_DATAEXPIRED = 0; + const uint32 ROWSETEVENT_TYPE_FOREGROUNDLOST = 1; + const uint32 ROWSETEVENT_TYPE_SCOPESTATISTICS = 2; + + const uint32 DBCOMPARE_LT = 0x00000000; + const uint32 DBCOMPARE_EQ = 0x00000001; + const uint32 DBCOMPARE_GT = 0x00000002; + const uint32 DBCOMPARE_NE = 0x00000003; + const uint32 DBCOMPARE_NOTCOMPARABLE = 0x00000004; + + const uint32 VECTOR_RANK_MIN = 0x00000000; + const uint32 VECTOR_RANK_MAX = 0x00000001; + const uint32 VECTOR_RANK_INNER = 0x00000002; + const uint32 VECTOR_RANK_DICE = 0x00000003; + const uint32 VECTOR_RANK_JACCARD = 0x00000004; + + const uint32 DBAGGTTYPE_BYNONE = 0x00000000; + const uint32 DBAGGTTYPE_SUM = 0x00000001; + const uint32 DBAGGTTYPE_MAX = 0x00000002; + const uint32 DBAGGTTYPE_MIN = 0x00000003; + const uint32 DBAGGTTYPE_AVG = 0x00000004; + const uint32 DBAGGTTYPE_COUNT = 0x00000005; + const uint32 DBAGGTTYPE_CHILDCOUNT = 0x00000006; + const uint32 DBAGGTTYPE_BYFREQ = 0x00000007; + const uint32 DBAGGTTYPE_FIRST = 0x00000008; + const uint32 DBAGGTTYPE_DATERANGE = 0x00000009; + const uint32 DBAGGTTYPE_REPRESENTATIVEOF= 0x0000000a; + const uint32 DBAGGTTYPE_EDITDISTANCE = 0x0000000b; + + const uint32 ESEQUENTIAL = 0x00000001; + const uint32 ELOCATEABLE = 0x00000003; + const uint32 ESCROLLABLE = 0x00000007; + const uint32 EASYNCHRONOUS = 0x00000008; + const uint32 EFIRSTROWS = 0x00000080; + const uint32 EHOLDROWS = 0x00000200; + const uint32 ECHAPTERED = 0x00000800; + const uint32 EUSECI = 0x00001000; + const uint32 EDEFERTRIMMING = 0x00002000; + const uint32 ENABLEROWSETEVENTS = 0x00800000; + const uint32 EDONOTCOMPUTEEXPENSIVEPROPS = 0x00400000; + + const uint32 CI_STATE_SHADOW_MERGE = 0x00000001; + const uint32 CI_STATE_MASTER_MERGE = 0x00000002; + const uint32 CI_STATE_ANNEALING_MERGE = 0x00000008; + const uint32 CI_STATE_SCANNING = 0x00000010; + const uint32 CI_STATE_LOW_MEMORY = 0x00000080; + const uint32 CI_STATE_HIGH_IO = 0x00000100; + const uint32 CI_STATE_MASTER_MERGE_PAUSED = 0x00000200; + const uint32 CI_STATE_READ_ONLY = 0x00000400; + const uint32 CI_STATE_BATTERY_POWER = 0x00000800; + const uint32 CI_STATE_USER_ACTIVE = 0x00001000; + const uint32 CI_STATE_LOW_DISK = 0x00010000; + const uint32 CI_STATE_HIGH_CPU = 0x00020000; + + const uint32 STORESTATUSOK = 0x00000000; + const uint32 STORESTATUSDEFERRED = 0x00000001; + const uint32 STORESTATUSNULL = 0x00000002; + + const uint32 DB_S_ENDOFROWSET = 0x00040EC6; + + const uint32 XOR_CONST = 0x59533959; + const uint32 E_UNEXPECTED = 0x8000FFFF; + const uint32 WIN_UPDATE_ERR = 0x80070003; + + const uint32 QUERY_SORTASCEND = 0x00000000; + const uint32 QUERY_DESCEND = 0x00000001; +} diff --git a/librpc/rpc/wsp_helper.c b/librpc/rpc/wsp_helper.c new file mode 100644 index 0000000..3d88a88 --- /dev/null +++ b/librpc/rpc/wsp_helper.c @@ -0,0 +1,52 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ +#include "includes.h" +#include "librpc/rpc/wsp_helper.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" + +void uint64_to_wsp_hyper(uint64_t src, struct wsp_hyper *dest) +{ + dest->hi = (uint32_t)src; + dest->lo = (uint32_t)(src>>32); +} + +void wsp_hyper_to_uint64(struct wsp_hyper *src, uint64_t *dest) +{ + *dest = src->lo; + *dest <<= 32; + *dest |= src->hi; +} + +uint32_t calc_array_size(struct safearraybound *bounds, uint32_t ndims) +{ + int i; + int result = 0; + + for(i = 0; i < ndims; i++) { + uint32_t celements = bounds[i].celements; + if (i) { + result = result * celements; + } else { + result = celements; + } + } + return result; +} diff --git a/librpc/rpc/wsp_helper.h b/librpc/rpc/wsp_helper.h new file mode 100644 index 0000000..76b5156 --- /dev/null +++ b/librpc/rpc/wsp_helper.h @@ -0,0 +1,31 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ +#ifndef __LIBRPC_WSP_HELPER_H__ +#define __LIBRPC_WSP_HELPER_H__ + +struct safearraybound; +struct wsp_hyper; + +uint32_t calc_array_size(struct safearraybound *bounds, uint32_t ndims); +void uint64_to_wsp_hyper(uint64_t src, struct wsp_hyper *dest); +void wsp_hyper_to_uint64(struct wsp_hyper *src, uint64_t *dest); + +#endif // __LIBRPC_WSP_HELPER_H__ diff --git a/librpc/wscript_build b/librpc/wscript_build index 1c7cc0a..1d07b42 100644 --- a/librpc/wscript_build +++ b/librpc/wscript_build @@ -330,6 +330,16 @@ bld.SAMBA_SUBSYSTEM('NDR_FSRVP', public_deps='ndr' ) +bld.SAMBA_SUBSYSTEM('NDR_WSP', + source='gen_ndr/ndr_wsp.c rpc/wsp_helper.c', + public_deps='ndr' + ) + +bld.SAMBA_SUBSYSTEM('NDR_WSP_DATA', + source='gen_ndr/ndr_wsp_data.c', + public_deps='ndr' + ) + bld.SAMBA_SUBSYSTEM('NDR_WITNESS', source='gen_ndr/ndr_witness.c ndr/ndr_witness.c', public_deps='ndr' @@ -685,7 +695,7 @@ bld.SAMBA_LIBRARY('ndr-samba', source=[], deps='''NDR_DRSBLOBS NDR_DRSUAPI NDR_IDMAP NDR_NTLMSSP NDR_NEGOEX NDR_SCHANNEL NDR_MGMT NDR_DNSSERVER NDR_EPMAPPER NDR_XATTR NDR_UNIXINFO NDR_NAMED_PIPE_AUTH NDR_DCOM - NDR_NTPRINTING NDR_FSRVP NDR_WITNESS NDR_MDSSVC NDR_OPEN_FILES NDR_SMBXSRV''', + NDR_NTPRINTING NDR_FSRVP NDR_WITNESS NDR_MDSSVC NDR_OPEN_FILES NDR_SMBXSRV NDR_WSP''', private_library=True, grouping_library=True ) -- 2.10.2 >From 6237134f97d20ee859220156a750beaa40efe541 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 26 Jul 2016 11:47:43 +0100 Subject: [PATCH 07/21] s3/build: Add support for WSP in configure script. Building the wsp service daemon and cli client can be controlled by specifying '--enable-wsp' Note: By default this option is not enabled. --- source3/wscript | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/wscript b/source3/wscript index c6b2421..ca8102f 100644 --- a/source3/wscript +++ b/source3/wscript @@ -76,6 +76,7 @@ def set_options(opt): action="store_true", dest='enable_vxfs', default=False) opt.SAMBA3_ADD_OPTION('spotlight', with_name="enable", without_name="disable", default=False) + opt.SAMBA3_ADD_OPTION('wsp', with_name="enable", without_name="disable", default=False) def configure(conf): from samba_utils import TO_LIST @@ -1643,6 +1644,11 @@ main() { conf.env['libtracker']='' conf.env.with_spotlight = False + conf.env.with_wsp = False + if Options.options.with_wsp: + Logs.info("building with WSP support") + conf.DEFINE('WITH_WSP', '1') + conf.env.with_wsp = True if Options.options.with_spotlight: versions = ['1.0', '0.16', '0.14'] for version in versions: -- 2.10.2 >From cbf481c3a4b9a5d9cd7c4a59b24868ac25e1f564 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 26 Jul 2016 12:23:38 +0100 Subject: [PATCH 08/21] librpc/rpc: Add windows propertyset info and associated accessor and helper api. Signed-off-by: Noel Power --- librpc/rpc/wsp_helper.c | 2074 +++++++++++++++++++++++++++++++++++++++++++++++ librpc/rpc/wsp_helper.h | 61 +- 2 files changed, 2134 insertions(+), 1 deletion(-) diff --git a/librpc/rpc/wsp_helper.c b/librpc/rpc/wsp_helper.c index 3d88a88..debf31d 100644 --- a/librpc/rpc/wsp_helper.c +++ b/librpc/rpc/wsp_helper.c @@ -50,3 +50,2077 @@ uint32_t calc_array_size(struct safearraybound *bounds, uint32_t ndims) } return result; } + +struct full_guid_propset { + struct GUID guid; + const struct full_propset_info *prop_info; +}; + +static const struct full_propset_info guid_properties_0[] = { + {0x64,"System.Calendar.IsOnline",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_1[] = { + {0x64,"System.Contact.OtherAddressStreet",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_2[] = { + {0x64,"System.ThumbnailCacheId",VT_UI8, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_3[] = { + {0x2,"System.DRM.IsProtected",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_4[] = { + {0x64,"System.Calendar.OptionalAttendeeNames",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_5[] = { + {0x64,"System.Calendar.ShowTimeAs",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_6[] = { + {0x3,"System.Kind",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_7[] = { + {0x8,"System.Message.HasAttachments",VT_BOOL, true, false, true, false, 2}, + {0x5,"System.Priority",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_8[] = { + {0x64,"System.ParentalRatingReason",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_9[] = { + {0x64,"System.Contact.OtherAddressCountry",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_10[] = { + {0x9,"System.Status",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_11[] = { + {0x64,"System.DateArchived",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_12[] = { + {0x64,"System.Contact.CarTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_13[] = { + {0x5,"System.ComputerName",VT_LPWSTR, true, false, true, false, 512}, + {0x8,"System.ItemPathDisplayNarrow",VT_LPWSTR, true, false, true, false, 520}, + {0xb,"System.ItemType",VT_LPWSTR, true, true, true, false, 512}, + {0x18,"System.ParsingName",VT_LPWSTR, true, true, true, false, 520}, + {0x19,"System.SFGAOFlags",VT_UI4, true, false, true, false, 4}, + {0x9,"PercivedType",VT_I4, false, false, false, true, 0}, + {0xc,"FileCount",VT_UI8, false, false, false, true, 0}, + {0xe,"TotalFileSize",VT_UI8, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_14[] = { + {0x64,"System.Calendar.ResponseStatus",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_15[] = { + {0x64,"System.Task.BillingInformation",VT_LPWSTR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_16[] = { + {0x64,"System.Calendar.Duration",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_17[] = { + {0x64,"System.Message.SenderName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_18[] = { + {0x64,"System.Document.DocumentID",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_19[] = { + {0x64,"System.RecordedTV.NetworkAffiliation",VT_LPWSTR, true, false, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_20[] = { + {0x64,"System.PriorityText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_21[] = { + {0x64,"System.Contact.Children",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_22[] = { + {0x64,"System.RecordedTV.RecordingTime",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_23[] = { + {0x64,"System.FlagColorText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_24[] = { + {0x64,"System.Contact.OtherAddressPostalCode",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_25[] = { + {0x64,"System.Photo.SharpnessText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_26[] = { + {0x64,"System.Contact.OtherAddress",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_27[] = { + {0x2,"System.Search.AutoSummary",VT_LPWSTR, true, false, true, false, 2048}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_28[] = { + {0x64,"System.Contact.BusinessAddress",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_29[] = { + {0x64,"System.IsIncomplete",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_30[] = { + {0x64,"System.Contact.EmailAddress2",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_31[] = { + {0x64,"System.Contact.BusinessTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_32[] = { + {0x64,"System.RecordedTV.StationName",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_33[] = { + {0xa,"System.ContentUrl",VT_LPWSTR, true, true, true, false, 4168}, + {0x9,"System.ItemUrl",VT_LPWSTR, true, true, true, false, 4168}, + {0x5,"System.Search.EntryID",VT_I4, true, false, true, false, 4}, + {0x4,"System.Search.HitCount",VT_I4, true, false, true, false, 4}, + {0x3,"System.Search.Rank",VT_I4, true, false, true, false, 4}, + {0x8,"System.Search.ReverseFileName",VT_LPWSTR, true, true, true, false, 520}, + {0x2,"RankVector",VT_UI4 | VT_VECTOR, false, false, false, true, 0}, + {0x6,"All",VT_LPWSTR, false, false, false, true, 0}, + {0xf,"System.Important.Unknown",VT_I4, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_34[] = { + {0x64,"System.Image.CompressionText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_35[] = { + {0x64,"System.Contact.HomeAddressState",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_36[] = { + {0x64,"System.Contact.EmailAddress3",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_37[] = { + {0x64,"System.Music.IsCompilation",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_38[] = { + {0x64,"System.Communication.FollowupIconIndex",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_39[] = { + {0x64,"System.Photo.TagViewAggregate",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_40[] = { + {0x64,"System.Message.ToDoTitle",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_41[] = { + {0x64,"System.Search.Store",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_42[] = { + {0x64,"System.FileName",VT_LPWSTR, true, true, true, false, 520}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_43[] = { + {0x64,"System.Contact.HomeAddressStreet",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_44[] = { + {0x64,"System.Contact.HomeAddressPostalCode",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_45[] = { + {0x64,"System.Contact.BusinessHomePage",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_46[] = { + {0x64,"System.Message.ConversationID",VT_LPWSTR, true, true, true, false, 512}, + {0x65,"System.Message.ConversationIndex",VT_BLOB_OBJECT, true, false, true, false, 1024}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_47[] = { + {0x64,"System.Calendar.RequiredAttendeeNames",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_48[] = { + {0xb,"System.Copyright",VT_LPWSTR, true, true, true, false, 512}, + {0xd,"System.Media.ClassPrimaryID",VT_LPWSTR, true, false, true, false, 512}, + {0xe,"System.Media.ClassSecondaryID",VT_LPWSTR, true, false, true, false, 512}, + {0x18,"System.Media.CollectionGroupID",VT_LPWSTR, true, false, true, false, 512}, + {0x19,"System.Media.CollectionID",VT_LPWSTR, true, false, true, false, 512}, + {0x12,"System.Media.ContentDistributor",VT_LPWSTR, true, false, true, false, 512}, + {0x1a,"System.Media.ContentID",VT_LPWSTR, true, false, true, false, 512}, + {0x1b,"System.Media.CreatorApplication",VT_LPWSTR, true, true, true, false, 512}, + {0x1c,"System.Media.CreatorApplicationVersion",VT_LPWSTR, true, true, true, false, 512}, + {0xf,"System.Media.DVDID",VT_LPWSTR, true, false, true, false, 512}, + {0x24,"System.Media.EncodedBy",VT_LPWSTR, true, true, true, false, 512}, + {0x10,"System.Media.MCDI",VT_LPWSTR, true, false, true, false, 512}, + {0x11,"System.Media.MetadataContentProvider",VT_LPWSTR, true, false, true, false, 512}, + {0x16,"System.Media.Producer",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x26,"System.Media.ProtectionType",VT_LPWSTR, true, false, true, false, 512}, + {0x27,"System.Media.ProviderRating",VT_LPWSTR, true, false, true, false, 512}, + {0x28,"System.Media.ProviderStyle",VT_LPWSTR, true, false, true, false, 512}, + {0x1e,"System.Media.Publisher",VT_LPWSTR, true, true, true, false, 512}, + {0x23,"System.Media.UniqueFileIdentifier",VT_LPWSTR, true, false, true, false, 512}, + {0x29,"System.Media.UserNoAutoInfo",VT_LPWSTR, true, false, true, false, 512}, + {0x22,"System.Media.UserWebUrl",VT_LPWSTR, true, false, true, false, 4168}, + {0x17,"System.Media.Writer",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x13,"System.Music.Composer",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x1f,"System.Music.Period",VT_LPWSTR, true, true, true, false, 512}, + {0x15,"System.ParentalRating",VT_LPWSTR, true, true, true, false, 512}, + {0x9,"System.Rating",VT_UI4, true, false, true, false, 4}, + {0x14,"System.Video.Director",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_49[] = { + {0x64,"System.Message.ProofInProgress",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_50[] = { + {0x64,"System.Contact.PrimaryAddressPostOfficeBox",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_51[] = { + {0x64,"System.Calendar.IsRecurring",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_52[] = { + {0x64,"System.Contact.HomeAddress",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_53[] = { + {0x64,"System.ItemParticipants",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_54[] = { + {0x64,"System.Media.DateReleased",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_55[] = { + {0x64,"System.Journal.Contacts",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_56[] = { + {0x64,"System.Contact.Gender",VT_LPWSTR, true, true, true, false, 512}, + {0x65,"System.Contact.GenderValue",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_57[] = { + {0x67,"System.Message.MessageClass",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_58[] = { + {0x64,"System.FlagColor",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_59[] = { + {0x64,"System.Calendar.OrganizerName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_60[] = { + {0x3,"System.Link.TargetSFGAOFlagsStrings",VT_LPWSTR | VT_VECTOR, true, true, false, false, 512}, + {0x2,"System.Shell.SFGAOFlagsStrings",VT_LPWSTR | VT_VECTOR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_61[] = { + {0x64,"System.Photo.PeopleNames",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_62[] = { + {0x7,"System.Audio.ChannelCount",VT_UI4, true, false, true, false, 4}, + {0x4,"System.Audio.EncodingBitrate",VT_UI4, true, false, true, false, 4}, + {0x5,"System.Audio.SampleRate",VT_UI4, true, false, true, false, 4}, + {0x6,"System.Audio.SampleSize",VT_UI4, true, false, true, false, 4}, + {0x3,"System.Media.Duration",VT_UI8, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_63[] = { + {0x2,"DBPROP_CI_CATALOG_NAME",VT_LPWSTR, false, false, false, true, 0}, + {0x3,"DBPROP_CI_INCLUDE_SCOPES",VT_LPWSTR | VT_VECTOR, false, false, false, true, 0}, + {0x4,"DBPROP_CI_SCOPE_FLAGS",VT_I4 | VT_VECTOR, false, false, false, true, 0}, + {0x7,"DBPROP_CI_QUERY_TYPE",VT_I4, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_64[] = { + {0x2,"DBPROP_MACHINE",VT_BSTR, false, false, false, true, 0}, + {0x3,"DBPROP_CLIENT_CLSID",VT_CLSID, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_65[] = { + {0x4752,"System.DateImported",VT_FILETIME, true, false, true, false, 8}, + {0x103,"System.Image.Compression",VT_UI2, true, false, true, false, 2}, + {0x9202,"System.Photo.Aperture",VT_R8, true, false, true, false, 8}, + {0x10f,"System.Photo.CameraManufacturer",VT_LPWSTR, true, true, true, false, 512}, + {0x110,"System.Photo.CameraModel",VT_LPWSTR, true, true, true, false, 512}, + {0x9003,"System.Photo.DateTaken",VT_FILETIME, true, false, true, false, 8}, + {0x4748,"System.Photo.Event",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0x9204,"System.Photo.ExposureBias",VT_R8, true, false, true, false, 8}, + {0x8822,"System.Photo.ExposureProgram",VT_UI4, true, false, true, false, 4}, + {0x829a,"System.Photo.ExposureTime",VT_R8, true, false, true, false, 8}, + {0x9209,"System.Photo.Flash",VT_UI1, true, false, true, false, 1}, + {0x829d,"System.Photo.FNumber",VT_R8, true, false, true, false, 8}, + {0x920a,"System.Photo.FocalLength",VT_R8, true, false, true, false, 8}, + {0x8827,"System.Photo.ISOSpeed",VT_UI2, true, false, true, false, 2}, + {0x9208,"System.Photo.LightSource",VT_UI4, true, false, true, false, 4}, + {0x9207,"System.Photo.MeteringMode",VT_UI2, true, false, true, false, 2}, + {0x112,"System.Photo.Orientation",VT_UI2, true, false, true, false, 2}, + {0x9201,"System.Photo.ShutterSpeed",VT_R8, true, false, true, false, 8}, + {0x9206,"System.Photo.SubjectDistance",VT_R8, true, false, true, false, 8}, + {0x131,"System.SoftwareUsed",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_66[] = { + {0x64,"System.Contact.TTYTDDTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_67[] = { + {0x2f,"System.Contact.Birthday",VT_FILETIME, true, false, true, false, 8}, + {0x41,"System.Contact.HomeAddressCity",VT_LPWSTR, true, true, true, false, 512}, + {0x14,"System.Contact.HomeTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0x6,"System.Contact.JobTitle",VT_LPWSTR, true, true, true, false, 512}, + {0x47,"System.Contact.MiddleName",VT_LPWSTR, true, true, true, false, 256}, + {0x23,"System.Contact.MobileTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0x4a,"System.Contact.NickName",VT_LPWSTR, true, true, true, false, 256}, + {0x7,"System.Contact.OfficeLocation",VT_LPWSTR, true, true, true, false, 512}, + {0x45,"System.Contact.PersonalTitle",VT_LPWSTR, true, true, true, false, 512}, + {0x30,"System.Contact.PrimaryEmailAddress",VT_LPWSTR, true, true, true, false, 256}, + {0x19,"System.Contact.PrimaryTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0x49,"System.Contact.Suffix",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_68[] = { + {0x64,"System.Photo.PhotometricInterpretationText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_69[] = { + {0x64,"System.Contact.OtherAddressPostOfficeBox",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_70[] = { + {0x64,"System.Calendar.ReminderTime",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_71[] = { + {0x64,"System.Calendar.RequiredAttendeeAddresses",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_72[] = { + {0x64,"System.Calendar.OrganizerAddress",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_73[] = { + {0x64,"System.Photo.WhiteBalanceText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_74[] = { + {0x2,"System.Link.TargetParsingPath",VT_LPWSTR, true, false, true, false, 520}, + {0x8,"System.Link.TargetSFGAOFlags",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_75[] = { + {0x64,"System.IsAttachment",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_76[] = { + {0x64,"System.Photo.GainControlText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_77[] = { + {0x64,"System.Contact.Hobbies",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_78[] = { + {0x64,"System.Contact.HomeAddressPostOfficeBox",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_79[] = { + {0x64,"System.Contact.CompanyMainTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_80[] = { + {0x64,"System.IsFlagged",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_81[] = { + {0x64,"System.Contact.FirstName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_82[] = { + {0xa,"System.IsEncrypted",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_83[] = { + {0x64,"System.Media.AverageLevel",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_84[] = { + {0x5,"System.MIMEType",VT_LPWSTR, true, true, true, false, 512}, + {0x9,"System.Search.AccessCount",VT_UI4, true, false, true, false, 4}, + {0x8,"System.Search.GatherTime",VT_FILETIME, true, false, true, false, 8}, + {0xb,"System.Search.LastIndexedTotalTime",VT_R8, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_85[] = { + {0x64,"System.Calendar.OptionalAttendeeAddresses",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_86[] = { + {0x64,"System.ProviderItemID",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_87[] = { + {0x64,"System.Contact.BusinessAddressCountry",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_88[] = { + {0x64,"System.Contact.EmailName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_89[] = { + {0x64,"System.Photo.FocalLengthInFilm",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_90[] = { + {0x64,"System.IsDeleted",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_91[] = { + {0x64,"System.Contact.IMAddress",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_92[] = { + {0x64,"System.DateAcquired",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_93[] = { + {0x64,"System.DateCompleted",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_94[] = { + {0x64,"System.ItemName",VT_LPWSTR, true, false, true, false, 520}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_95[] = { + {0x64,"System.Contact.PrimaryAddressPostalCode",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_96[] = { + {0x26,"System.Media.SubTitle",VT_LPWSTR, true, true, true, false, 512}, + {0x5,"System.Media.Year",VT_UI4, true, false, true, false, 4}, + {0xd,"System.Music.AlbumArtist",VT_LPWSTR, true, true, true, false, 256}, + {0x64,"System.Music.AlbumID",VT_LPWSTR, true, true, true, false, 2048}, + {0x4,"System.Music.AlbumTitle",VT_LPWSTR, true, true, true, false, 512}, + {0x2,"System.Music.Artist",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x23,"System.Music.BeatsPerMinute",VT_LPWSTR, true, true, true, false, 512}, + {0x24,"System.Music.Conductor",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x21,"System.Music.ContentGroupDescription",VT_LPWSTR, true, false, true, false, 512}, + {0xb,"System.Music.Genre",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0x22,"System.Music.InitialKey",VT_LPWSTR, true, true, true, false, 512}, + {0xc,"System.Music.Lyrics",VT_LPWSTR, true, true, false, false, 512}, + {0x27,"System.Music.Mood",VT_LPWSTR, true, true, true, false, 512}, + {0x25,"System.Music.PartOfSet",VT_LPWSTR, true, false, true, false, 512}, + {0x7,"System.Music.TrackNumber",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_97[] = { + {0x64,"System.Document.ClientID",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_98[] = { + {0x64,"System.Photo.ExposureProgramText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_99[] = { + {0x12,"System.ApplicationName",VT_LPWSTR, true, true, true, false, 512}, + {0x4,"System.Author",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x6,"System.Comment",VT_LPWSTR, true, true, true, false, 2048}, + {0x10,"System.Document.CharacterCount",VT_I4, true, false, true, false, 4}, + {0xc,"System.Document.DateCreated",VT_FILETIME, true, false, true, false, 8}, + {0xb,"System.Document.DatePrinted",VT_FILETIME, true, false, true, false, 8}, + {0xd,"System.Document.DateSaved",VT_FILETIME, true, false, true, false, 8}, + {0x8,"System.Document.LastAuthor",VT_LPWSTR, true, true, true, false, 256}, + {0xe,"System.Document.PageCount",VT_I4, true, false, true, false, 4}, + {0x9,"System.Document.RevisionNumber",VT_LPWSTR, true, true, true, false, 512}, + {0xa,"System.Document.TotalEditingTime",VT_UI8, true, false, true, false, 8}, + {0xf,"System.Document.WordCount",VT_I4, true, false, true, false, 4}, + {0x5,"System.Keywords",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0x3,"System.Subject",VT_LPWSTR, true, true, true, false, 520}, + {0x2,"System.Title",VT_LPWSTR, true, true, true, false, 520}, + {0x7,"DocTemplate",VT_LPWSTR, false, false, false, true, 0}, + {0x11,"DocThumbnail",VT_BLOB_OBJECT, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_100[] = { + {0x64,"System.Note.ColorText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_101[] = { + {0x64,"System.Photo.MeteringModeText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_102[] = { + {0x2,"System.Link.TargetExtension",VT_LPWSTR | VT_VECTOR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_103[] = { + {0x64,"System.Contact.BusinessAddressState",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_104[] = { + {0x64,"System.Photo.OrientationText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_105[] = { + {0x64,"System.Contact.Label",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_106[] = { + {0x64,"System.Calendar.Location",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_107[] = { + {0x64,"System.Photo.SaturationText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_108[] = { + {0x64,"System.Contact.PrimaryAddressCity",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_109[] = { + {0x64,"System.Contact.Anniversary",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_110[] = { + {0x64,"System.Contact.FileAsName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_111[] = { + {0x64,"System.GPS.Date",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_112[] = { + {0x2,"System.Contact.JA.CompanyNamePhonetic",VT_LPWSTR, true, true, true, false, 256}, + {0x3,"System.Contact.JA.FirstNamePhonetic",VT_LPWSTR, true, true, true, false, 512}, + {0x4,"System.Contact.JA.LastNamePhonetic",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_113[] = { + {0x64,"System.Communication.SecurityFlags",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_114[] = { + {0x64,"System.Identity",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_115[] = { + {0x64,"System.Contact.BusinessAddressPostOfficeBox",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_116[] = { + {0x64,"System.FileExtension",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_117[] = { + {0x64,"System.AcquisitionID",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_118[] = { + {0x64,"System.Contact.EmailAddresses",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_119[] = { + {0x2,"System.Category",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0xf,"System.Company",VT_LPWSTR, true, true, true, false, 512}, + {0x1b,"System.ContentStatus",VT_LPWSTR, true, true, true, false, 512}, + {0x1a,"System.ContentType",VT_LPWSTR, true, true, true, false, 512}, + {0x4,"System.Document.ByteCount",VT_I4, true, false, true, false, 4}, + {0x9,"System.Document.HiddenSlideCount",VT_I4, true, false, true, false, 4}, + {0x5,"System.Document.LineCount",VT_I4, true, false, true, false, 4}, + {0xe,"System.Document.Manager",VT_LPWSTR, true, true, true, false, 512}, + {0x6,"System.Document.ParagraphCount",VT_I4, true, false, true, false, 4}, + {0x3,"System.Document.PresentationFormat",VT_LPWSTR, true, true, true, false, 512}, + {0x7,"System.Document.SlideCount",VT_I4, true, false, true, false, 4}, + {0x1d,"System.Document.Version",VT_LPWSTR, true, false, true, false, 512}, + {0x1c,"System.Language",VT_LPWSTR, true, true, true, false, 512}, + {0x8,"DocNoteCount",VT_I4, false, false, false, true, 0}, + {0xd,"DocPartTitles",VT_LPWSTR | VT_VECTOR, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_120[] = { + {0x64,"System.Communication.TaskStatus",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_121[] = { + {0x64,"System.Contact.LastName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_122[] = { + {0x64,"System.Communication.DateItemExpires",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_123[] = { + {0x64,"System.ImportanceText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_124[] = { + {0x64,"System.Search.ContainerHash",VT_LPWSTR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_125[] = { + {0x64,"System.Contact.BusinessFaxNumber",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_126[] = { + {0xa,"System.Video.Compression",VT_LPWSTR, true, true, true, false, 512}, + {0x8,"System.Video.EncodingBitrate",VT_UI4, true, false, true, false, 4}, + {0x2c,"System.Video.FourCC",VT_UI4, true, false, true, false, 4}, + {0x4,"System.Video.FrameHeight",VT_UI4, true, false, true, false, 4}, + {0x6,"System.Video.FrameRate",VT_UI4, true, false, true, false, 4}, + {0x3,"System.Video.FrameWidth",VT_UI4, true, false, true, false, 4}, + {0x2a,"System.Video.HorizontalAspectRatio",VT_UI4, true, false, true, false, 4}, + {0x9,"System.Video.SampleSize",VT_UI4, true, false, true, false, 4}, + {0x2,"System.Video.StreamName",VT_LPWSTR, true, true, true, false, 512}, + {0x2b,"System.Video.TotalBitrate",VT_UI4, true, false, true, false, 4}, + {0x2d,"System.Video.VerticalAspectRatio",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_127[] = { + {0x1a,"System.IconIndex",VT_I4, true, false, true, false, 4}, + {0x2,"System.Link.TargetUrl",VT_LPWSTR, true, true, true, false, 4168}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_128[] = { + {0x64,"System.IsFlaggedComplete",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_129[] = { + {0x64,"System.Task.Owner",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_130[] = { + {0x64,"System.ItemFolderPathDisplayNarrow",VT_LPWSTR, true, true, true, false, 520}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_131[] = { + {0x64,"System.Photo.ProgramModeText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_132[] = { + {0x64,"System.Contact.PrimaryAddressCountry",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_133[] = { + {0x2,"System.Shell.OmitFromView",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_134[] = { + {0x64,"System.Contact.OtherAddressState",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_135[] = { + {0x64,"System.Message.AttachmentContents",VT_LPWSTR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_136[] = { + {0x64,"System.Communication.TaskStatusText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_137[] = { + {0x64,"System.Communication.HeaderItem",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_138[] = { + {0x64,"System.Contact.EmailAddress",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_139[] = { + {0x64,"System.Contact.FullName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_140[] = { + {0x64,"System.Document.Division",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_141[] = { + {0x64,"System.Contact.BusinessAddressPostalCode",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_142[] = { + {0x64,"System.ItemNamePrefix",VT_LPWSTR, true, false, true, false, 520}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_143[] = { + {0x64,"System.Photo.DigitalZoom",VT_R8, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_144[] = { + {0x64,"System.SourceItem",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_145[] = { + {0x64,"System.Photo.WhiteBalance",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_146[] = { + {0x64,"System.SensitivityText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_147[] = { + {0x2,"MSIDXSPROP_ROWSETQUERYSTATUS",VT_I4, false, false, false, true, 0}, + {0x3,"MSIDXSPROP_COMMAND_LOCALE_STRING",VT_BSTR, false, false, false, true, 0}, + {0x4,"MSIDXSPROP_QUERY_RESTRICTION",VT_BSTR, false, false, false, true, 0}, + {0x5,"MSIDXSPROP_PARSE_TREE",VT_BSTR, false, false, false, true, 0}, + {0x6,"MSIDXSPROP_MAX_RANK",VT_I4, false, false, false, true, 0}, + {0x7,"MSIDXSPROP_RESULTS_FOUND",VT_I4, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_148[] = { + {0x64,"System.Photo.MaxAperture",VT_R8, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_149[] = { + {0x64,"System.Calendar.Resources",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_150[] = { + {0x64,"System.Contact.OtherAddressCity",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_151[] = { + {0x64,"System.Music.DisplayArtist",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_152[] = { + {0x64,"System.Message.SenderAddress",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_153[] = { + {0x64,"System.Contact.PrimaryAddressState",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_154[] = { + {0x64,"System.StartDate",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_155[] = { + {0x10,"System.DateAccessed",VT_FILETIME, true, false, true, false, 8}, + {0xf,"System.DateCreated",VT_FILETIME, true, false, true, false, 8}, + {0xe,"System.DateModified",VT_FILETIME, true, false, true, false, 8}, + {0xd,"System.FileAttributes",VT_UI4, true, false, true, false, 4}, + {0x15,"System.FileFRN",VT_UI8, true, false, true, false, 8}, + {0x2,"System.ItemFolderNameDisplay",VT_LPWSTR, true, true, true, false, 512}, + {0xa,"System.ItemNameDisplay",VT_LPWSTR, true, true, true, false, 520}, + {0x4,"System.ItemTypeText",VT_LPWSTR, true, true, true, false, 512}, + {0x13,"System.Search.Contents",VT_LPWSTR, true, true, false, false, 512}, + {0xc,"System.Size",VT_UI8, true, false, true, false, 8}, + {0x3,"ClassId",VT_CLSID, false, false, false, true, 0}, + {0x8,"FileIndex",VT_UI8, false, false, false, true, 0}, + {0x9,"USN",VT_I8, false, false, false, true, 0}, + {0xb,"Path",VT_LPWSTR, false, false, false, true, 0}, + {0x12,"AllocSize",VT_I8, false, false, false, true, 0}, + {0x14,"ShortFilename",VT_LPWSTR, false, false, false, true, 0}, + {0x16,"Scope",VT_LPWSTR, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_156[] = { + {0x64,"System.Contact.BusinessAddressStreet",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_157[] = { + {0x64,"System.Sensitivity",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_158[] = { + {0x64,"System.Contact.HomeAddressCountry",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_159[] = { + {0x64,"System.Task.CompletionStatus",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_160[] = { + {0x10,"System.Software.DateLastUsed",VT_FILETIME, true, true, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_161[] = { + {0x64,"System.Contact.Department",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_162[] = { + {0x64,"System.Calendar.ShowTimeAsText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_163[] = { + {0x4,"System.FileOwner",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_164[] = { + {0x64,"System.RecordedTV.OriginalBroadcastDate",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_165[] = { + {0x64,"System.IsFolder",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_166[] = { + {0x64,"System.DueDate",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_167[] = { + {0x3,"System.FileDescription",VT_LPWSTR, true, true, false, false, 512}, + {0x6,"System.OriginalFileName",VT_LPWSTR, true, true, true, false, 520}, + {0x7,"System.Software.ProductName",VT_LPWSTR, true, true, false, false, 512}, + {0x8,"System.Software.ProductVersion",VT_LPWSTR, true, true, false, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_168[] = { + {0x64,"System.MileageInformation",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_169[] = { + {0x7,"System.RecordedTV.ChannelNumber",VT_UI4, true, false, true, false, 4}, + {0xf,"System.RecordedTV.DateContentExpires",VT_FILETIME, true, false, true, false, 8}, + {0x2,"System.RecordedTV.EpisodeName",VT_LPWSTR, true, true, true, false, 512}, + {0x10,"System.RecordedTV.IsATSCContent",VT_BOOL, true, false, true, false, 2}, + {0xc,"System.RecordedTV.IsClosedCaptioningAvailable",VT_BOOL, true, false, true, false, 2}, + {0x11,"System.RecordedTV.IsDTVContent",VT_BOOL, true, false, true, false, 2}, + {0x12,"System.RecordedTV.IsHDContent",VT_BOOL, true, false, true, false, 2}, + {0xd,"System.RecordedTV.IsRepeatBroadcast",VT_BOOL, true, false, true, false, 2}, + {0xe,"System.RecordedTV.IsSAP",VT_BOOL, true, false, true, false, 2}, + {0x3,"System.RecordedTV.ProgramDescription",VT_LPWSTR, true, true, true, false, 2048}, + {0x5,"System.RecordedTV.StationCallSign",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_170[] = { + {0x64,"System.Audio.PeakValue",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_171[] = { + {0x64,"System.ItemDate",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_172[] = { + {0x64,"System.Contact.SpouseName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_173[] = { + {0x64,"System.Message.Flags",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_174[] = { + {0x64,"System.Contact.AssistantTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_175[] = { + {0x64,"System.KindText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_176[] = { + {0x64,"System.Photo.ContrastText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_177[] = { + {0x7,"System.Image.BitDepth",VT_UI4, true, false, true, false, 4}, + {0xd,"System.Image.Dimensions",VT_LPWSTR, true, true, true, false, 512}, + {0x5,"System.Image.HorizontalResolution",VT_R8, true, false, true, false, 8}, + {0x3,"System.Image.HorizontalSize",VT_UI4, true, false, true, false, 4}, + {0x6,"System.Image.VerticalResolution",VT_R8, true, false, true, false, 8}, + {0x4,"System.Image.VerticalSize",VT_UI4, true, false, true, false, 4}, + {0xc,"System.Media.FrameCount",VT_UI4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_178[] = { + {0x64,"System.Message.IsFwdOrReply",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_179[] = { + {0x64,"System.ItemAuthors",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_180[] = { + {0x64,"System.Contact.TelexNumber",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_181[] = { + {0x64,"System.Communication.PolicyTag",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_182[] = { + {0x64,"System.Contact.HomeFaxNumber",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_183[] = { + {0x64,"System.FlagStatusText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_184[] = { + {0x64,"System.Contact.AssistantName",VT_LPWSTR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_185[] = { + {0x64,"System.Message.ToDoFlags",VT_I4, true, false, true, false, 4}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_186[] = { + {0x64,"System.RatingText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_187[] = { + {0x64,"System.Document.Contributor",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_188[] = { + {0x64,"System.Contact.CallbackTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_189[] = { + {0x64,"System.EndDate",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_190[] = { + {0x64,"System.Media.DateEncoded",VT_FILETIME, true, false, true, false, 8}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_191[] = { + {0x64,"System.Photo.FlashText",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_192[] = { + {0x64,"System.Photo.FlashFired",VT_BOOL, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_193[] = { + {0x64,"System.Contact.Profession",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_194[] = { + {0x64,"System.Contact.PagerTelephone",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_195[] = { + {0x2,"DBPROP_USECONTENTINDEX",VT_BOOL, false, false, false, true, 0}, + {0x3,"DBPROP_DEFERNONINDEXEDTRIMMING",VT_BOOL, false, false, false, true, 0}, + {0x4,"DBPROP_USEEXTENDEDDBTYPES",VT_BOOL, false, false, false, true, 0}, + {0x5,"DBPROP_IGNORENOISEONLYCLAUSES",VT_BOOL, false, false, false, true, 0}, + {0x6,"DBPROP_GENERICOPTIONS_STRING",VT_BSTR, false, false, false, true, 0}, + {0x7,"DBPROP_FIRSTROWS",VT_BOOL, false, false, false, true, 0}, + {0x8,"DBPROP_DEFERCATALOGVERIFICATION",VT_BOOL, false, false, false, true, 0}, + {0xa,"DBPROP_GENERATEPARSETREE",VT_BOOL, false, false, false, true, 0}, + {0xc,"DBPROP_FREETEXTANYTERM",VT_BOOL, false, false, false, true, 0}, + {0xd,"DBPROP_FREETEXTUSESTEMMING",VT_BOOL, false, false, false, true, 0}, + {0xe,"DBPROP_IGNORESBRI",VT_BOOL, false, false, false, true, 0}, + {0x10,"DBPROP_ENABLEROWSETEVENTS",VT_BOOL, false, false, false, true, 0}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_196[] = { + {0x64,"System.Contact.BusinessAddressCity",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_197[] = { + {0x64,"System.Media.SubscriptionContentId",VT_LPWSTR, true, false, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_198[] = { + {0x64,"System.Contact.PrimaryAddressStreet",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_199[] = { + {0x64,"System.Project",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_200[] = { + {0x64,"System.Note.Color",VT_UI2, true, false, true, false, 2}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_201[] = { + {0x9,"System.Communication.AccountName",VT_LPWSTR, true, true, true, false, 512}, + {0x12,"System.Contact.WebPage",VT_LPWSTR, true, true, true, false, 4168}, + {0xc,"System.FlagStatus",VT_I4, true, false, true, false, 4}, + {0xb,"System.Importance",VT_I4, true, false, true, false, 4}, + {0xa,"System.IsRead",VT_BOOL, true, false, true, false, 2}, + {0x6,"System.ItemFolderPathDisplay",VT_LPWSTR, true, true, true, false, 520}, + {0x7,"System.ItemPathDisplay",VT_LPWSTR, true, true, true, false, 520}, + {0x15,"System.Message.AttachmentNames",VT_LPWSTR | VT_VECTOR, true, true, true, false, 512}, + {0x2,"System.Message.BccAddress",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x3,"System.Message.BccName",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x4,"System.Message.CcAddress",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x5,"System.Message.CcName",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x14,"System.Message.DateReceived",VT_FILETIME, true, false, true, false, 8}, + {0x13,"System.Message.DateSent",VT_FILETIME, true, false, true, false, 8}, + {0xd,"System.Message.FromAddress",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0xe,"System.Message.FromName",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0xf,"System.Message.Store",VT_LPWSTR, true, false, true, false, 512}, + {0x10,"System.Message.ToAddress",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0x11,"System.Message.ToName",VT_LPWSTR | VT_VECTOR, true, true, true, false, 256}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_202[] = { + {0x64,"System.Journal.EntryType",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + +static const struct full_propset_info guid_properties_203[] = { + {0x64,"System.Contact.MailingAddress",VT_LPWSTR, true, true, true, false, 512}, + {0,NULL} +}; + + +static struct full_guid_propset full_propertyset[] = { + {{0xBFEE9149, 0xE3E2, 0x49A7, {0xA8, 0x62}, {0xC0, 0x59, 0x88, 0x14, 0x5C, 0xEC}},guid_properties_0}, + {{0xFF962609, 0xB7D6, 0x4999, {0x86, 0x2D}, {0x95, 0x18, 0x0D, 0x52, 0x9A, 0xEA}},guid_properties_1}, + {{0x446D16B1, 0x8DAD, 0x4870, {0xA7, 0x48}, {0x40, 0x2E, 0xA4, 0x3D, 0x78, 0x8C}},guid_properties_2}, + {{0xAEAC19E4, 0x89AE, 0x4508, {0xB9, 0xB7}, {0xBB, 0x86, 0x7A, 0xBE, 0xE2, 0xED}},guid_properties_3}, + {{0x09429607, 0x582D, 0x437F, {0x84, 0xC3}, {0xDE, 0x93, 0xA2, 0xB2, 0x4C, 0x3C}},guid_properties_4}, + {{0x5BF396D4, 0x5EB2, 0x466F, {0xBD, 0xE9}, {0x2F, 0xB3, 0xF2, 0x36, 0x1D, 0x6E}},guid_properties_5}, + {{0x1E3EE840, 0xBC2B, 0x476C, {0x82, 0x37}, {0x2A, 0xCD, 0x1A, 0x83, 0x9B, 0x22}},guid_properties_6}, + {{0x9C1FCF74, 0x2D97, 0x41BA, {0xB4, 0xAE}, {0xCB, 0x2E, 0x36, 0x61, 0xA6, 0xE4}},guid_properties_7}, + {{0x10984E0A, 0xF9F2, 0x4321, {0xB7, 0xEF}, {0xBA, 0xF1, 0x95, 0xAF, 0x43, 0x19}},guid_properties_8}, + {{0x8F167568, 0x0AAE, 0x4322, {0x8E, 0xD9}, {0x60, 0x55, 0xB7, 0xB0, 0xE3, 0x98}},guid_properties_9}, + {{0x000214A1, 0x0000, 0x0000, {0xC0, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x46}},guid_properties_10}, + {{0x43F8D7B7, 0xA444, 0x4F87, {0x93, 0x83}, {0x52, 0x27, 0x1C, 0x9B, 0x91, 0x5C}},guid_properties_11}, + {{0x8FDC6DEA, 0xB929, 0x412B, {0xBA, 0x90}, {0x39, 0x7A, 0x25, 0x74, 0x65, 0xFE}},guid_properties_12}, + {{0x28636AA6, 0x953D, 0x11D2, {0xB5, 0xD6}, {0x00, 0xC0, 0x4F, 0xD9, 0x18, 0xD0}},guid_properties_13}, + {{0x188C1F91, 0x3C40, 0x4132, {0x9E, 0xC5}, {0xD8, 0xB0, 0x3B, 0x72, 0xA8, 0xA2}},guid_properties_14}, + {{0xD37D52C6, 0x261C, 0x4303, {0x82, 0xB3}, {0x08, 0xB9, 0x26, 0xAC, 0x6F, 0x12}},guid_properties_15}, + {{0x293CA35A, 0x09AA, 0x4DD2, {0xB1, 0x80}, {0x1F, 0xE2, 0x45, 0x72, 0x8A, 0x52}},guid_properties_16}, + {{0x0DA41CFA, 0xD224, 0x4A18, {0xAE, 0x2F}, {0x59, 0x61, 0x58, 0xDB, 0x4B, 0x3A}},guid_properties_17}, + {{0xE08805C8, 0xE395, 0x40DF, {0x80, 0xD2}, {0x54, 0xF0, 0xD6, 0xC4, 0x31, 0x54}},guid_properties_18}, + {{0x2C53C813, 0xFB63, 0x4E22, {0xA1, 0xAB}, {0x0B, 0x33, 0x1C, 0xA1, 0xE2, 0x73}},guid_properties_19}, + {{0xD98BE98B, 0xB86B, 0x4095, {0xBF, 0x52}, {0x9D, 0x23, 0xB2, 0xE0, 0xA7, 0x52}},guid_properties_20}, + {{0xD4729704, 0x8EF1, 0x43EF, {0x90, 0x24}, {0x2B, 0xD3, 0x81, 0x18, 0x7F, 0xD5}},guid_properties_21}, + {{0xA5477F61, 0x7A82, 0x4ECA, {0x9D, 0xDE}, {0x98, 0xB6, 0x9B, 0x24, 0x79, 0xB3}},guid_properties_22}, + {{0x45EAE747, 0x8E2A, 0x40AE, {0x8C, 0xBF}, {0xCA, 0x52, 0xAB, 0xA6, 0x15, 0x2A}},guid_properties_23}, + {{0x95C656C1, 0x2ABF, 0x4148, {0x9E, 0xD3}, {0x9E, 0xC6, 0x02, 0xE3, 0xB7, 0xCD}},guid_properties_24}, + {{0x51EC3F47, 0xDD50, 0x421D, {0x87, 0x69}, {0x33, 0x4F, 0x50, 0x42, 0x4B, 0x1E}},guid_properties_25}, + {{0x508161FA, 0x313B, 0x43D5, {0x83, 0xA1}, {0xC1, 0xAC, 0xCF, 0x68, 0x62, 0x2C}},guid_properties_26}, + {{0x560C36C0, 0x503A, 0x11CF, {0xBA, 0xA1}, {0x00, 0x00, 0x4C, 0x75, 0x2A, 0x9A}},guid_properties_27}, + {{0x730FB6DD, 0xCF7C, 0x426B, {0xA0, 0x3F}, {0xBD, 0x16, 0x6C, 0xC9, 0xEE, 0x24}},guid_properties_28}, + {{0x346C8BD1, 0x2E6A, 0x4C45, {0x89, 0xA4}, {0x61, 0xB7, 0x8E, 0x8E, 0x70, 0x0F}},guid_properties_29}, + {{0x38965063, 0xEDC8, 0x4268, {0x84, 0x91}, {0xB7, 0x72, 0x31, 0x72, 0xCF, 0x29}},guid_properties_30}, + {{0x6A15E5A0, 0x0A1E, 0x4CD7, {0xBB, 0x8C}, {0xD2, 0xF1, 0xB0, 0xC9, 0x29, 0xBC}},guid_properties_31}, + {{0x1B5439E7, 0xEBA1, 0x4AF8, {0xBD, 0xD7}, {0x7A, 0xF1, 0xD4, 0x54, 0x94, 0x93}},guid_properties_32}, + {{0x49691C90, 0x7E17, 0x101A, {0xA9, 0x1C}, {0x08, 0x00, 0x2B, 0x2E, 0xCD, 0xA9}},guid_properties_33}, + {{0x3F08E66F, 0x2F44, 0x4BB9, {0xA6, 0x82}, {0xAC, 0x35, 0xD2, 0x56, 0x23, 0x22}},guid_properties_34}, + {{0xC89A23D0, 0x7D6D, 0x4EB8, {0x87, 0xD4}, {0x77, 0x6A, 0x82, 0xD4, 0x93, 0xE5}},guid_properties_35}, + {{0x644D37B4, 0xE1B3, 0x4BAD, {0xB0, 0x99}, {0x7E, 0x7C, 0x04, 0x96, 0x6A, 0xCA}},guid_properties_36}, + {{0xC449D5CB, 0x9EA4, 0x4809, {0x82, 0xE8}, {0xAF, 0x9D, 0x59, 0xDE, 0xD6, 0xD1}},guid_properties_37}, + {{0x83A6347E, 0x6FE4, 0x4F40, {0xBA, 0x9C}, {0xC4, 0x86, 0x52, 0x40, 0xD1, 0xF4}},guid_properties_38}, + {{0xB812F15D, 0xC2D8, 0x4BBF, {0xBA, 0xCD}, {0x79, 0x74, 0x43, 0x46, 0x11, 0x3F}},guid_properties_39}, + {{0xBCCC8A3C, 0x8CEF, 0x42E5, {0x9B, 0x1C}, {0xC6, 0x90, 0x79, 0x39, 0x8B, 0xC7}},guid_properties_40}, + {{0xA06992B3, 0x8CAF, 0x4ED7, {0xA5, 0x47}, {0xB2, 0x59, 0xE3, 0x2A, 0xC9, 0xFC}},guid_properties_41}, + {{0x41CF5AE0, 0xF75A, 0x4806, {0xBD, 0x87}, {0x59, 0xC7, 0xD9, 0x24, 0x8E, 0xB9}},guid_properties_42}, + {{0x0ADEF160, 0xDB3F, 0x4308, {0x9A, 0x21}, {0x06, 0x23, 0x7B, 0x16, 0xFA, 0x2A}},guid_properties_43}, + {{0x8AFCC170, 0x8A46, 0x4B53, {0x9E, 0xEE}, {0x90, 0xBA, 0xE7, 0x15, 0x1E, 0x62}},guid_properties_44}, + {{0x56310920, 0x2491, 0x4919, {0x99, 0xCE}, {0xEA, 0xDB, 0x06, 0xFA, 0xFD, 0xB2}},guid_properties_45}, + {{0xDC8F80BD, 0xAF1E, 0x4289, {0x85, 0xB6}, {0x3D, 0xFC, 0x1B, 0x49, 0x39, 0x92}},guid_properties_46}, + {{0xB33AF30B, 0xF552, 0x4584, {0x93, 0x6C}, {0xCB, 0x93, 0xE5, 0xCD, 0xA2, 0x9F}},guid_properties_47}, + {{0x64440492, 0x4C8B, 0x11D1, {0x8B, 0x70}, {0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03}},guid_properties_48}, + {{0x9098F33C, 0x9A7D, 0x48A8, {0x8D, 0xE5}, {0x2E, 0x12, 0x27, 0xA6, 0x4E, 0x91}},guid_properties_49}, + {{0xDE5EF3C7, 0x46E1, 0x484E, {0x99, 0x99}, {0x62, 0xC5, 0x30, 0x83, 0x94, 0xC1}},guid_properties_50}, + {{0x315B9C8D, 0x80A9, 0x4EF9, {0xAE, 0x16}, {0x8E, 0x74, 0x6D, 0xA5, 0x1D, 0x70}},guid_properties_51}, + {{0x98F98354, 0x617A, 0x46B8, {0x85, 0x60}, {0x5B, 0x1B, 0x64, 0xBF, 0x1F, 0x89}},guid_properties_52}, + {{0xD4D0AA16, 0x9948, 0x41A4, {0xAA, 0x85}, {0xD9, 0x7F, 0xF9, 0x64, 0x69, 0x93}},guid_properties_53}, + {{0xDE41CC29, 0x6971, 0x4290, {0xB4, 0x72}, {0xF5, 0x9F, 0x2E, 0x2F, 0x31, 0xE2}},guid_properties_54}, + {{0xDEA7C82C, 0x1D89, 0x4A66, {0x94, 0x27}, {0xA4, 0xE3, 0xDE, 0xBA, 0xBC, 0xB1}},guid_properties_55}, + {{0x3C8CEE58, 0xD4F0, 0x4CF9, {0xB7, 0x56}, {0x4E, 0x5D, 0x24, 0x44, 0x7B, 0xCD}},guid_properties_56}, + {{0xCD9ED458, 0x08CE, 0x418F, {0xA7, 0x0E}, {0xF9, 0x12, 0xC7, 0xBB, 0x9C, 0x5C}},guid_properties_57}, + {{0x67DF94DE, 0x0CA7, 0x4D6F, {0xB7, 0x92}, {0x05, 0x3A, 0x3E, 0x4F, 0x03, 0xCF}},guid_properties_58}, + {{0xAAA660F9, 0x9865, 0x458E, {0xB4, 0x84}, {0x01, 0xBC, 0x7F, 0xE3, 0x97, 0x3E}},guid_properties_59}, + {{0xD6942081, 0xD53B, 0x443D, {0xAD, 0x47}, {0x5E, 0x05, 0x9D, 0x9C, 0xD2, 0x7A}},guid_properties_60}, + {{0xE8309B6E, 0x084C, 0x49B4, {0xB1, 0xFC}, {0x90, 0xA8, 0x03, 0x31, 0xB6, 0x38}},guid_properties_61}, + {{0x64440490, 0x4C8B, 0x11D1, {0x8B, 0x70}, {0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03}},guid_properties_62}, + {{0xA9BD1526, 0x6A80, 0x11D0, {0x8C, 0x9D}, {0x00, 0x20, 0xAF, 0x1D, 0x74, 0x0E}},guid_properties_63}, + {{0xAFAFACA5, 0xB5D1, 0x11D0, {0x8C, 0x62}, {0x00, 0xC0, 0x4F, 0xC2, 0xDB, 0x8D}},guid_properties_64}, + {{0x14B81DA1, 0x0135, 0x4D31, {0x96, 0xD9}, {0x6C, 0xBF, 0xC9, 0x67, 0x1A, 0x99}},guid_properties_65}, + {{0xAAF16BAC, 0x2B55, 0x45E6, {0x9F, 0x6D}, {0x41, 0x5E, 0xB9, 0x49, 0x10, 0xDF}},guid_properties_66}, + {{0x176DC63C, 0x2688, 0x4E89, {0x81, 0x43}, {0xA3, 0x47, 0x80, 0x0F, 0x25, 0xE9}},guid_properties_67}, + {{0x821437D6, 0x9EAB, 0x4765, {0xA5, 0x89}, {0x3B, 0x1C, 0xBB, 0xD2, 0x2A, 0x61}},guid_properties_68}, + {{0x8B26EA41, 0x058F, 0x43F6, {0xAE, 0xCC}, {0x40, 0x35, 0x68, 0x1C, 0xE9, 0x77}},guid_properties_69}, + {{0x72FC5BA4, 0x24F9, 0x4011, {0x9F, 0x3F}, {0xAD, 0xD2, 0x7A, 0xFA, 0xD8, 0x18}},guid_properties_70}, + {{0x0BA7D6C3, 0x568D, 0x4159, {0xAB, 0x91}, {0x78, 0x1A, 0x91, 0xFB, 0x71, 0xE5}},guid_properties_71}, + {{0x744C8242, 0x4DF5, 0x456C, {0xAB, 0x9E}, {0x01, 0x4E, 0xFB, 0x90, 0x21, 0xE3}},guid_properties_72}, + {{0x6336B95E, 0xC7A7, 0x426D, {0x86, 0xFD}, {0x7A, 0xE3, 0xD3, 0x9C, 0x84, 0xB4}},guid_properties_73}, + {{0xB9B4B3FC, 0x2B51, 0x4A42, {0xB5, 0xD8}, {0x32, 0x41, 0x46, 0xAF, 0xCF, 0x25}},guid_properties_74}, + {{0xF23F425C, 0x71A1, 0x4FA8, {0x92, 0x2F}, {0x67, 0x8E, 0xA4, 0xA6, 0x04, 0x08}},guid_properties_75}, + {{0xC06238B2, 0x0BF9, 0x4279, {0xA7, 0x23}, {0x25, 0x85, 0x67, 0x15, 0xCB, 0x9D}},guid_properties_76}, + {{0x5DC2253F, 0x5E11, 0x4ADF, {0x9C, 0xFE}, {0x91, 0x0D, 0xD0, 0x1E, 0x3E, 0x70}},guid_properties_77}, + {{0x7B9F6399, 0x0A3F, 0x4B12, {0x89, 0xBD}, {0x4A, 0xDC, 0x51, 0xC9, 0x18, 0xAF}},guid_properties_78}, + {{0x8589E481, 0x6040, 0x473D, {0xB1, 0x71}, {0x7F, 0xA8, 0x9C, 0x27, 0x08, 0xED}},guid_properties_79}, + {{0x5DA84765, 0xE3FF, 0x4278, {0x86, 0xB0}, {0xA2, 0x79, 0x67, 0xFB, 0xDD, 0x03}},guid_properties_80}, + {{0x14977844, 0x6B49, 0x4AAD, {0xA7, 0x14}, {0xA4, 0x51, 0x3B, 0xF6, 0x04, 0x60}},guid_properties_81}, + {{0x90E5E14E, 0x648B, 0x4826, {0xB2, 0xAA}, {0xAC, 0xAF, 0x79, 0x0E, 0x35, 0x13}},guid_properties_82}, + {{0x09EDD5B6, 0xB301, 0x43C5, {0x99, 0x90}, {0xD0, 0x03, 0x02, 0xEF, 0xFD, 0x46}},guid_properties_83}, + {{0x0B63E350, 0x9CCC, 0x11D0, {0xBC, 0xDB}, {0x00, 0x80, 0x5F, 0xCC, 0xCE, 0x04}},guid_properties_84}, + {{0xD55BAE5A, 0x3892, 0x417A, {0xA6, 0x49}, {0xC6, 0xAC, 0x5A, 0xAA, 0xEA, 0xB3}},guid_properties_85}, + {{0xF21D9941, 0x81F0, 0x471A, {0xAD, 0xEE}, {0x4E, 0x74, 0xB4, 0x92, 0x17, 0xED}},guid_properties_86}, + {{0xB0B87314, 0xFCF6, 0x4FEB, {0x8D, 0xFF}, {0xA5, 0x0D, 0xA6, 0xAF, 0x56, 0x1C}},guid_properties_87}, + {{0xCC6F4F24, 0x6083, 0x4BD4, {0x87, 0x54}, {0x67, 0x4D, 0x0D, 0xE8, 0x7A, 0xB8}},guid_properties_88}, + {{0xA0E74609, 0xB84D, 0x4F49, {0xB8, 0x60}, {0x46, 0x2B, 0xD9, 0x97, 0x1F, 0x98}},guid_properties_89}, + {{0x5CDA5FC8, 0x33EE, 0x4FF3, {0x90, 0x94}, {0xAE, 0x7B, 0xD8, 0x86, 0x8C, 0x4D}},guid_properties_90}, + {{0xD68DBD8A, 0x3374, 0x4B81, {0x99, 0x72}, {0x3E, 0xC3, 0x06, 0x82, 0xDB, 0x3D}},guid_properties_91}, + {{0x2CBAA8F5, 0xD81F, 0x47CA, {0xB1, 0x7A}, {0xF8, 0xD8, 0x22, 0x30, 0x01, 0x31}},guid_properties_92}, + {{0x72FAB781, 0xACDA, 0x43E5, {0xB1, 0x55}, {0xB2, 0x43, 0x4F, 0x85, 0xE6, 0x78}},guid_properties_93}, + {{0x6B8DA074, 0x3B5C, 0x43BC, {0x88, 0x6F}, {0x0A, 0x2C, 0xDC, 0xE0, 0x0B, 0x6F}},guid_properties_94}, + {{0x18BBD425, 0xECFD, 0x46EF, {0xB6, 0x12}, {0x7B, 0x4A, 0x60, 0x34, 0xED, 0xA0}},guid_properties_95}, + {{0x56A3372E, 0xCE9C, 0x11D2, {0x9F, 0x0E}, {0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6}},guid_properties_96}, + {{0x276D7BB0, 0x5B34, 0x4FB0, {0xAA, 0x4B}, {0x15, 0x8E, 0xD1, 0x2A, 0x18, 0x09}},guid_properties_97}, + {{0xFEC690B7, 0x5F30, 0x4646, {0xAE, 0x47}, {0x4C, 0xAA, 0xFB, 0xA8, 0x84, 0xA3}},guid_properties_98}, + {{0xF29F85E0, 0x4FF9, 0x1068, {0xAB, 0x91}, {0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9}},guid_properties_99}, + {{0x46B4E8DE, 0xCDB2, 0x440D, {0x88, 0x5C}, {0x16, 0x58, 0xEB, 0x65, 0xB9, 0x14}},guid_properties_100}, + {{0xF628FD8C, 0x7BA8, 0x465A, {0xA6, 0x5B}, {0xC5, 0xAA, 0x79, 0x26, 0x3A, 0x9E}},guid_properties_101}, + {{0x7A7D76F4, 0xB630, 0x4BD7, {0x95, 0xFF}, {0x37, 0xCC, 0x51, 0xA9, 0x75, 0xC9}},guid_properties_102}, + {{0x446F787F, 0x10C4, 0x41CB, {0xA6, 0xC4}, {0x4D, 0x03, 0x43, 0x55, 0x15, 0x97}},guid_properties_103}, + {{0xA9EA193C, 0xC511, 0x498A, {0xA0, 0x6B}, {0x58, 0xE2, 0x77, 0x6D, 0xCC, 0x28}},guid_properties_104}, + {{0x97B0AD89, 0xDF49, 0x49CC, {0x83, 0x4E}, {0x66, 0x09, 0x74, 0xFD, 0x75, 0x5B}},guid_properties_105}, + {{0xF6272D18, 0xCECC, 0x40B1, {0xB2, 0x6A}, {0x39, 0x11, 0x71, 0x7A, 0xA7, 0xBD}},guid_properties_106}, + {{0x61478C08, 0xB600, 0x4A84, {0xBB, 0xE4}, {0xE9, 0x9C, 0x45, 0xF0, 0xA0, 0x72}},guid_properties_107}, + {{0xC8EA94F0, 0xA9E3, 0x4969, {0xA9, 0x4B}, {0x9C, 0x62, 0xA9, 0x53, 0x24, 0xE0}},guid_properties_108}, + {{0x9AD5BADB, 0xCEA7, 0x4470, {0xA0, 0x3D}, {0xB8, 0x4E, 0x51, 0xB9, 0x94, 0x9E}},guid_properties_109}, + {{0xF1A24AA7, 0x9CA7, 0x40F6, {0x89, 0xEC}, {0x97, 0xDE, 0xF9, 0xFF, 0xE8, 0xDB}},guid_properties_110}, + {{0x3602C812, 0x0F3B, 0x45F0, {0x85, 0xAD}, {0x60, 0x34, 0x68, 0xD6, 0x94, 0x23}},guid_properties_111}, + {{0x897B3694, 0xFE9E, 0x43E6, {0x80, 0x66}, {0x26, 0x0F, 0x59, 0x0C, 0x01, 0x00}},guid_properties_112}, + {{0x8619A4B6, 0x9F4D, 0x4429, {0x8C, 0x0F}, {0xB9, 0x96, 0xCA, 0x59, 0xE3, 0x35}},guid_properties_113}, + {{0xA26F4AFC, 0x7346, 0x4299, {0xBE, 0x47}, {0xEB, 0x1A, 0xE6, 0x13, 0x13, 0x9F}},guid_properties_114}, + {{0xBC4E71CE, 0x17F9, 0x48D5, {0xBE, 0xE9}, {0x02, 0x1D, 0xF0, 0xEA, 0x54, 0x09}},guid_properties_115}, + {{0xE4F10A3C, 0x49E6, 0x405D, {0x82, 0x88}, {0xA2, 0x3B, 0xD4, 0xEE, 0xAA, 0x6C}},guid_properties_116}, + {{0x65A98875, 0x3C80, 0x40AB, {0xAB, 0xBC}, {0xEF, 0xDA, 0xF7, 0x7D, 0xBE, 0xE2}},guid_properties_117}, + {{0x84D8F337, 0x981D, 0x44B3, {0x96, 0x15}, {0xC7, 0x59, 0x6D, 0xBA, 0x17, 0xE3}},guid_properties_118}, + {{0xD5CDD502, 0x2E9C, 0x101B, {0x93, 0x97}, {0x08, 0x00, 0x2B, 0x2C, 0xF9, 0xAE}},guid_properties_119}, + {{0xBE1A72C6, 0x9A1D, 0x46B7, {0xAF, 0xE7}, {0xAF, 0xAF, 0x8C, 0xEF, 0x49, 0x99}},guid_properties_120}, + {{0x8F367200, 0xC270, 0x457C, {0xB1, 0xD4}, {0xE0, 0x7C, 0x5B, 0xCD, 0x90, 0xC7}},guid_properties_121}, + {{0x428040AC, 0xA177, 0x4C8A, {0x97, 0x60}, {0xF6, 0xF7, 0x61, 0x22, 0x7F, 0x9A}},guid_properties_122}, + {{0xA3B29791, 0x7713, 0x4E1D, {0xBB, 0x40}, {0x17, 0xDB, 0x85, 0xF0, 0x18, 0x31}},guid_properties_123}, + {{0xBCEEE283, 0x35DF, 0x4D53, {0x82, 0x6A}, {0xF3, 0x6A, 0x3E, 0xEF, 0xC6, 0xBE}},guid_properties_124}, + {{0x91EFF6F3, 0x2E27, 0x42CA, {0x93, 0x3E}, {0x7C, 0x99, 0x9F, 0xBE, 0x31, 0x0B}},guid_properties_125}, + {{0x64440491, 0x4C8B, 0x11D1, {0x8B, 0x70}, {0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03}},guid_properties_126}, + {{0x5CBF2787, 0x48CF, 0x4208, {0xB9, 0x0E}, {0xEE, 0x5E, 0x5D, 0x42, 0x02, 0x94}},guid_properties_127}, + {{0xA6F360D2, 0x55F9, 0x48DE, {0xB9, 0x09}, {0x62, 0x0E, 0x09, 0x0A, 0x64, 0x7C}},guid_properties_128}, + {{0x08C7CC5F, 0x60F2, 0x4494, {0xAD, 0x75}, {0x55, 0xE3, 0xE0, 0xB5, 0xAD, 0xD0}},guid_properties_129}, + {{0xDABD30ED, 0x0043, 0x4789, {0xA7, 0xF8}, {0xD0, 0x13, 0xA4, 0x73, 0x66, 0x22}},guid_properties_130}, + {{0x7FE3AA27, 0x2648, 0x42F3, {0x89, 0xB0}, {0x45, 0x4E, 0x5C, 0xB1, 0x50, 0xC3}},guid_properties_131}, + {{0xE53D799D, 0x0F3F, 0x466E, {0xB2, 0xFF}, {0x74, 0x63, 0x4A, 0x3C, 0xB7, 0xA4}},guid_properties_132}, + {{0xDE35258C, 0xC695, 0x4CBC, {0xB9, 0x82}, {0x38, 0xB0, 0xAD, 0x24, 0xCE, 0xD0}},guid_properties_133}, + {{0x71B377D6, 0xE570, 0x425F, {0xA1, 0x70}, {0x80, 0x9F, 0xAE, 0x73, 0xE5, 0x4E}},guid_properties_134}, + {{0x3143BF7C, 0x80A8, 0x4854, {0x88, 0x80}, {0xE2, 0xE4, 0x01, 0x89, 0xBD, 0xD0}},guid_properties_135}, + {{0xA6744477, 0xC237, 0x475B, {0xA0, 0x75}, {0x54, 0xF3, 0x44, 0x98, 0x29, 0x2A}},guid_properties_136}, + {{0xC9C34F84, 0x2241, 0x4401, {0xB6, 0x07}, {0xBD, 0x20, 0xED, 0x75, 0xAE, 0x7F}},guid_properties_137}, + {{0xF8FA7FA3, 0xD12B, 0x4785, {0x8A, 0x4E}, {0x69, 0x1A, 0x94, 0xF7, 0xA3, 0xE7}},guid_properties_138}, + {{0x635E9051, 0x50A5, 0x4BA2, {0xB9, 0xDB}, {0x4E, 0xD0, 0x56, 0xC7, 0x72, 0x96}},guid_properties_139}, + {{0x1E005EE6, 0xBF27, 0x428B, {0xB0, 0x1C}, {0x79, 0x67, 0x6A, 0xCD, 0x28, 0x70}},guid_properties_140}, + {{0xE1D4A09E, 0xD758, 0x4CD1, {0xB6, 0xEC}, {0x34, 0xA8, 0xB5, 0xA7, 0x3F, 0x80}},guid_properties_141}, + {{0xD7313FF1, 0xA77A, 0x401C, {0x8C, 0x99}, {0x3D, 0xBD, 0xD6, 0x8A, 0xDD, 0x36}},guid_properties_142}, + {{0xF85BF840, 0xA925, 0x4BC2, {0xB0, 0xC4}, {0x8E, 0x36, 0xB5, 0x98, 0x67, 0x9E}},guid_properties_143}, + {{0x668CDFA5, 0x7A1B, 0x4323, {0xAE, 0x4B}, {0xE5, 0x27, 0x39, 0x3A, 0x1D, 0x81}},guid_properties_144}, + {{0xEE3D3D8A, 0x5381, 0x4CFA, {0xB1, 0x3B}, {0xAA, 0xF6, 0x6B, 0x5F, 0x4E, 0xC9}},guid_properties_145}, + {{0xD0C7F054, 0x3F72, 0x4725, {0x85, 0x27}, {0x12, 0x9A, 0x57, 0x7C, 0xB2, 0x69}},guid_properties_146}, + {{0xAA6EE6B0, 0xE828, 0x11D0, {0xB2, 0x3E}, {0x00, 0xAA, 0x00, 0x47, 0xFC, 0x01}},guid_properties_147}, + {{0x08F6D7C2, 0xE3F2, 0x44FC, {0xAF, 0x1E}, {0x5A, 0xA5, 0xC8, 0x1A, 0x2D, 0x3E}},guid_properties_148}, + {{0x00F58A38, 0xC54B, 0x4C40, {0x86, 0x96}, {0x97, 0x23, 0x59, 0x80, 0xEA, 0xE1}},guid_properties_149}, + {{0x6E682923, 0x7F7B, 0x4F0C, {0xA3, 0x37}, {0xCF, 0xCA, 0x29, 0x66, 0x87, 0xBF}},guid_properties_150}, + {{0xFD122953, 0xFA93, 0x4EF7, {0x92, 0xC3}, {0x04, 0xC9, 0x46, 0xB2, 0xF7, 0xC8}},guid_properties_151}, + {{0x0BE1C8E7, 0x1981, 0x4676, {0xAE, 0x14}, {0xFD, 0xD7, 0x8F, 0x05, 0xA6, 0xE7}},guid_properties_152}, + {{0xF1176DFE, 0x7138, 0x4640, {0x8B, 0x4C}, {0xAE, 0x37, 0x5D, 0xC7, 0x0A, 0x6D}},guid_properties_153}, + {{0x48FD6EC8, 0x8A12, 0x4CDF, {0xA0, 0x3E}, {0x4E, 0xC5, 0xA5, 0x11, 0xED, 0xDE}},guid_properties_154}, + {{0xB725F130, 0x47EF, 0x101A, {0xA5, 0xF1}, {0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC}},guid_properties_155}, + {{0xDDD1460F, 0xC0BF, 0x4553, {0x8C, 0xE4}, {0x10, 0x43, 0x3C, 0x90, 0x8F, 0xB0}},guid_properties_156}, + {{0xF8D3F6AC, 0x4874, 0x42CB, {0xBE, 0x59}, {0xAB, 0x45, 0x4B, 0x30, 0x71, 0x6A}},guid_properties_157}, + {{0x08A65AA1, 0xF4C9, 0x43DD, {0x9D, 0xDF}, {0xA3, 0x3D, 0x8E, 0x7E, 0xAD, 0x85}},guid_properties_158}, + {{0x084D8A0A, 0xE6D5, 0x40DE, {0xBF, 0x1F}, {0xC8, 0x82, 0x0E, 0x7C, 0x87, 0x7C}},guid_properties_159}, + {{0x841E4F90, 0xFF59, 0x4D16, {0x89, 0x47}, {0xE8, 0x1B, 0xBF, 0xFA, 0xB3, 0x6D}},guid_properties_160}, + {{0xFC9F7306, 0xFF8F, 0x4D49, {0x9F, 0xB6}, {0x3F, 0xFE, 0x5C, 0x09, 0x51, 0xEC}},guid_properties_161}, + {{0x53DA57CF, 0x62C0, 0x45C4, {0x81, 0xDE}, {0x76, 0x10, 0xBC, 0xEF, 0xD7, 0xF5}},guid_properties_162}, + {{0x9B174B34, 0x40FF, 0x11D2, {0xA2, 0x7E}, {0x00, 0xC0, 0x4F, 0xC3, 0x08, 0x71}},guid_properties_163}, + {{0x4684FE97, 0x8765, 0x4842, {0x9C, 0x13}, {0xF0, 0x06, 0x44, 0x7B, 0x17, 0x8C}},guid_properties_164}, + {{0x09329B74, 0x40A3, 0x4C68, {0xBF, 0x07}, {0xAF, 0x9A, 0x57, 0x2F, 0x60, 0x7C}},guid_properties_165}, + {{0x3F8472B5, 0xE0AF, 0x4DB2, {0x80, 0x71}, {0xC5, 0x3F, 0xE7, 0x6A, 0xE7, 0xCE}},guid_properties_166}, + {{0x0CEF7D53, 0xFA64, 0x11D1, {0xA2, 0x03}, {0x00, 0x00, 0xF8, 0x1F, 0xED, 0xEE}},guid_properties_167}, + {{0xFDF84370, 0x031A, 0x4ADD, {0x9E, 0x91}, {0x0D, 0x77, 0x5F, 0x1C, 0x66, 0x05}},guid_properties_168}, + {{0x6D748DE2, 0x8D38, 0x4CC3, {0xAC, 0x60}, {0xF0, 0x09, 0xB0, 0x57, 0xC5, 0x57}},guid_properties_169}, + {{0x2579E5D0, 0x1116, 0x4084, {0xBD, 0x9A}, {0x9B, 0x4F, 0x7C, 0xB4, 0xDF, 0x5E}},guid_properties_170}, + {{0xF7DB74B4, 0x4287, 0x4103, {0xAF, 0xBA}, {0xF1, 0xB1, 0x3D, 0xCD, 0x75, 0xCF}},guid_properties_171}, + {{0x9D2408B6, 0x3167, 0x422B, {0x82, 0xB0}, {0xF5, 0x83, 0xB7, 0xA7, 0xCF, 0xE3}},guid_properties_172}, + {{0xA82D9EE7, 0xCA67, 0x4312, {0x96, 0x5E}, {0x22, 0x6B, 0xCE, 0xA8, 0x50, 0x23}},guid_properties_173}, + {{0x9A93244D, 0xA7AD, 0x4FF8, {0x9B, 0x99}, {0x45, 0xEE, 0x4C, 0xC0, 0x9A, 0xF6}},guid_properties_174}, + {{0xF04BEF95, 0xC585, 0x4197, {0xA2, 0xB7}, {0xDF, 0x46, 0xFD, 0xC9, 0xEE, 0x6D}},guid_properties_175}, + {{0x59DDE9F2, 0x5253, 0x40EA, {0x9A, 0x8B}, {0x47, 0x9E, 0x96, 0xC6, 0x24, 0x9A}},guid_properties_176}, + {{0x6444048F, 0x4C8B, 0x11D1, {0x8B, 0x70}, {0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03}},guid_properties_177}, + {{0x9A9BC088, 0x4F6D, 0x469E, {0x99, 0x19}, {0xE7, 0x05, 0x41, 0x20, 0x40, 0xF9}},guid_properties_178}, + {{0xD0A04F0A, 0x462A, 0x48A4, {0xBB, 0x2F}, {0x37, 0x06, 0xE8, 0x8D, 0xBD, 0x7D}},guid_properties_179}, + {{0xC554493C, 0xC1F7, 0x40C1, {0xA7, 0x6C}, {0xEF, 0x8C, 0x06, 0x14, 0x00, 0x3E}},guid_properties_180}, + {{0xEC0B4191, 0xAB0B, 0x4C66, {0x90, 0xB6}, {0xC6, 0x63, 0x7C, 0xDE, 0xBB, 0xAB}},guid_properties_181}, + {{0x660E04D6, 0x81AB, 0x4977, {0xA0, 0x9F}, {0x82, 0x31, 0x31, 0x13, 0xAB, 0x26}},guid_properties_182}, + {{0xDC54FD2E, 0x189D, 0x4871, {0xAA, 0x01}, {0x08, 0xC2, 0xF5, 0x7A, 0x4A, 0xBC}},guid_properties_183}, + {{0xCD102C9C, 0x5540, 0x4A88, {0xA6, 0xF6}, {0x64, 0xE4, 0x98, 0x1C, 0x8C, 0xD1}},guid_properties_184}, + {{0x1F856A9F, 0x6900, 0x4ABA, {0x95, 0x05}, {0x2D, 0x5F, 0x1B, 0x4D, 0x66, 0xCB}},guid_properties_185}, + {{0x90197CA7, 0xFD8F, 0x4E8C, {0x9D, 0xA3}, {0xB5, 0x7E, 0x1E, 0x60, 0x92, 0x95}},guid_properties_186}, + {{0xF334115E, 0xDA1B, 0x4509, {0x9B, 0x3D}, {0x11, 0x95, 0x04, 0xDC, 0x7A, 0xBB}},guid_properties_187}, + {{0xBF53D1C3, 0x49E0, 0x4F7F, {0x85, 0x67}, {0x5A, 0x82, 0x1D, 0x8A, 0xC5, 0x42}},guid_properties_188}, + {{0xC75FAA05, 0x96FD, 0x49E7, {0x9C, 0xB4}, {0x9F, 0x60, 0x10, 0x82, 0xD5, 0x53}},guid_properties_189}, + {{0x2E4B640D, 0x5019, 0x46D8, {0x88, 0x81}, {0x55, 0x41, 0x4C, 0xC5, 0xCA, 0xA0}},guid_properties_190}, + {{0x6B8B68F6, 0x200B, 0x47EA, {0x8D, 0x25}, {0xD8, 0x05, 0x0F, 0x57, 0x33, 0x9F}},guid_properties_191}, + {{0x2D152B40, 0xCA39, 0x40DB, {0xB2, 0xCC}, {0x57, 0x37, 0x25, 0xB2, 0xFE, 0xC5}},guid_properties_192}, + {{0x7268AF55, 0x1CE4, 0x4F6E, {0xA4, 0x1F}, {0xB6, 0xE4, 0xEF, 0x10, 0xE4, 0xA9}},guid_properties_193}, + {{0xD6304E01, 0xF8F5, 0x4F45, {0x8B, 0x15}, {0xD0, 0x24, 0xA6, 0x29, 0x67, 0x89}},guid_properties_194}, + {{0xA7AC77ED, 0xF8D7, 0x11CE, {0xA7, 0x98}, {0x00, 0x20, 0xF8, 0x00, 0x80, 0x25}},guid_properties_195}, + {{0x402B5934, 0xEC5A, 0x48C3, {0x93, 0xE6}, {0x85, 0xE8, 0x6A, 0x2D, 0x93, 0x4E}},guid_properties_196}, + {{0x9AEBAE7A, 0x9644, 0x487D, {0xA9, 0x2C}, {0x65, 0x75, 0x85, 0xED, 0x75, 0x1A}},guid_properties_197}, + {{0x63C25B20, 0x96BE, 0x488F, {0x87, 0x88}, {0xC0, 0x9C, 0x40, 0x7A, 0xD8, 0x12}},guid_properties_198}, + {{0x39A7F922, 0x477C, 0x48DE, {0x8B, 0xC8}, {0xB2, 0x84, 0x41, 0xE3, 0x42, 0xE3}},guid_properties_199}, + {{0x4776CAFA, 0xBCE4, 0x4CB1, {0xA2, 0x3E}, {0x26, 0x5E, 0x76, 0xD8, 0xEB, 0x11}},guid_properties_200}, + {{0xE3E0584C, 0xB788, 0x4A5A, {0xBB, 0x20}, {0x7F, 0x5A, 0x44, 0xC9, 0xAC, 0xDD}},guid_properties_201}, + {{0x95BEB1FC, 0x326D, 0x4644, {0xB3, 0x96}, {0xCD, 0x3E, 0xD9, 0x0E, 0x6D, 0xDF}},guid_properties_202}, + {{0xC0AC206A, 0x827E, 0x4650, {0x95, 0xAE}, {0x77, 0xE2, 0xBB, 0x74, 0xFC, 0xC9}},guid_properties_203}, +}; + +const struct full_propset_info *get_propset_info_with_guid( + const char *prop_name, + struct GUID *propset_guid) +{ + int i; + struct full_guid_propset *guid_propset = NULL; + const struct full_propset_info *result = NULL; + for (i = 0; i < ARRAY_SIZE(full_propertyset); i++) { + const struct full_propset_info *item = NULL; + guid_propset = &full_propertyset[i]; + item = guid_propset->prop_info; + while (item->id) { + if (strequal(prop_name, item->name)) { + *propset_guid = guid_propset->guid; + result = item; + break; + } + item++; + } + if (result) { + break; + } + } + return result; +} + +const struct full_propset_info *get_prop_info(const char *prop_name) +{ + const struct full_propset_info *result = NULL; + struct GUID guid; + result = get_propset_info_with_guid(prop_name, &guid); + return result; +} + +char *prop_from_fullprop(TALLOC_CTX *ctx, struct wsp_cfullpropspec *fullprop) +{ + int i; + char *result = NULL; + const struct full_propset_info *item = NULL; + bool search_by_id = (fullprop->ulkind == PRSPEC_PROPID); + + for (i = 0; i < ARRAY_SIZE(full_propertyset); i++) { + /* find propset */ + if (GUID_equal(&fullprop->guidpropset, + &full_propertyset[i].guid)) { + item = full_propertyset[i].prop_info; + break; + } + } + if (item) { + while (item->id) { + if (search_by_id) { + if( fullprop->name_or_id.prspec == item->id) { + result = talloc_strdup(ctx, item->name); + break; + } + } else if (strcmp(item->name, + fullprop->name_or_id.propname.vstring) + == 0) { + result = talloc_strdup(ctx, item->name); + break; + } + item++; + } + } + + if (!result) { + result = GUID_string(ctx, &fullprop->guidpropset); + + if (search_by_id) { + result = talloc_asprintf(result, "%s/%d", result, + fullprop->name_or_id.prspec); + } else { + result = talloc_asprintf(result, "%s/%s", result, + fullprop->name_or_id.propname.vstring); + } + } + return result; +} + +static const struct { + uint32_t id; + const char *name; +} typename_map[] = { + {VT_EMPTY, "Empty"}, + {VT_NULL, "Null"}, + {VT_I2, "VT_I2"}, + {VT_I4, "VT_I4"}, + {VT_I4, "VT_I4"}, + {VT_R4, "VT_R4"}, + {VT_R8, "VT_R8"}, + {VT_CY, "VT_CY"}, + {VT_DATE, "VT_DATE"}, + {VT_BSTR, "VT_BSTR"}, + {VT_I1, "VT_I1"}, + {VT_UI1, "VT_UI1"}, + {VT_UI2, "VT_UI2"}, + {VT_UI4, "VT_UI4"}, + {VT_I8, "VT_I8"}, + {VT_UI8, "VT_UI8"}, + {VT_INT, "VT_INT"}, + {VT_UINT, "VT_UINT"}, + {VT_ERROR, "VT_ERROR"}, + {VT_BOOL, "VT_BOOL"}, + {VT_VARIANT, "VT_VARIANT"}, + {VT_DECIMAL, "VT_DECIMAL"}, + {VT_FILETIME, "VT_FILETIME"}, + {VT_BLOB, "VT_BLOB"}, + {VT_BLOB_OBJECT, "VT_BLOB_OBJECT"}, + {VT_CLSID, "VT_CLSID"}, + {VT_LPSTR, "VT_LPSTR"}, + {VT_LPWSTR, "VT_LPWSTR"}, + {VT_COMPRESSED_LPWSTR, "VT_COMPRESSED_LPWSTR"}, +}; + +const char * get_vtype_name(uint32_t type) +{ + const char *type_name = NULL; + static char result_buf[255]; + int i; + uint32_t temp = type & ~(VT_VECTOR | VT_ARRAY); + for (i = 0; i < ARRAY_SIZE(typename_map); i++) { + if (temp == typename_map[i].id) { + type_name = typename_map[i].name; + break; + } + } + if (type & VT_VECTOR) { + snprintf(result_buf, sizeof(result_buf), "Vector | %s", type_name); + } else if (type & VT_ARRAY) { + snprintf(result_buf, sizeof(result_buf), "Array | %s", type_name); + } else { + snprintf(result_buf, sizeof(result_buf), "%s", type_name); + } + return result_buf; +} + +bool is_variable_size(uint16_t vtype) +{ + bool result; + switch(vtype) { + case VT_LPWSTR: + case VT_BSTR: + case VT_BLOB: + case VT_BLOB_OBJECT: + case VT_VARIANT: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +const char *get_store_status(uint8_t status_byte) +{ + const char *result; + switch(status_byte) { + case 0: + result = "StoreStatusOk"; + break; + case 1: + result = "StoreStatusDeferred"; + break; + case 2: + result = "StoreStatusNull"; + break; + default: + result = "Unknown Status"; + break; + } + return result; +} + +void set_variant_lpwstr(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *vvalue, + const char *string_val) +{ + vvalue->vtype = VT_LPWSTR; + vvalue->vvalue.vt_lpwstr.value = talloc_strdup(ctx, string_val); +} + +void set_variant_i4(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *vvalue, + uint32_t val) +{ + vvalue->vtype = VT_I4; + vvalue->vvalue.vt_i4 = val; +} + +void set_variant_vt_bool(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + bool bval) +{ + variant->vtype = VT_BOOL; + variant->vvalue.vt_bool = bval; +} + +static void fill_int32_vec(TALLOC_CTX* ctx, + int32_t **pdest, + int32_t* ivector, uint32_t elems) +{ + int i; + int32_t *dest = talloc_zero_array(ctx, int32_t, elems); + for ( i = 0; i < elems; i++ ) { + dest[ i ] = ivector[ i ]; + } + *pdest = dest; +} + +void set_variant_i4_vector(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + int32_t* ivector, uint32_t elems) +{ + variant->vtype = VT_VECTOR | VT_I4; + variant->vvalue.vt_i4_vec.vvector_elements = elems; + fill_int32_vec(ctx, &variant->vvalue.vt_i4_vec.vvector_data, ivector, elems); +} + +static void fill_string_vec(TALLOC_CTX* ctx, + struct wsp_cbasestoragevariant *variant, + const char **strings, uint16_t elems) +{ + int i; + variant->vvalue.vt_lpwstr_v.vvector_elements = elems; + variant->vvalue.vt_lpwstr_v.vvector_data = talloc_zero_array(ctx, + struct vt_lpwstr, + elems); + + for( i = 0; i < elems; i++ ) { + variant->vvalue.vt_lpwstr_v.vvector_data[ i ].value = talloc_strdup(ctx, strings[ i ]); + } +} + +static void fill_bstr_vec(TALLOC_CTX *ctx, + struct vt_bstr **pvector, + const char **strings, uint16_t elems) +{ + int i; + struct vt_bstr *vdata = talloc_zero_array(ctx, struct vt_bstr, elems); + + for( i = 0; i < elems; i++ ) { + vdata [ i ].value = talloc_strdup(ctx, strings[ i ]); + } + *pvector = vdata; +} + +void set_variant_bstr(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, + const char *string_val) +{ + variant->vtype = VT_BSTR; + variant->vvalue.vt_bstr.value = talloc_strdup(ctx, string_val); +} + +void set_variant_lpwstr_vector(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + const char **string_vals, uint32_t elems) +{ + variant->vtype = VT_LPWSTR | VT_VECTOR; + fill_string_vec(ctx, variant, string_vals, elems); +} + +void set_variant_array_bstr(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + const char **string_vals, uint16_t elems) +{ + variant->vtype = VT_BSTR | VT_ARRAY; + variant->vvalue.vt_bstr_array.cdims = 1; + variant->vvalue.vt_bstr_array.ffeatures = 0; + + variant->vvalue.vt_bstr_array.rgsabound = + talloc_zero_array(ctx, struct safearraybound, 1); + + variant->vvalue.vt_bstr_array.rgsabound[0].celements = elems; + variant->vvalue.vt_bstr_array.rgsabound[0].ilbound = 0; + variant->vvalue.vt_bstr_array.cbelements = 0; + fill_bstr_vec(ctx, &variant->vvalue.vt_bstr_array.vdata, + string_vals, elems); + /* + * if cbelements is the num bytes per elem it kindof means each + * string in the array must be the same size ? + */ + + if (elems >0) { + variant->vvalue.vt_bstr_array.cbelements = + strlen_m_term(variant->vvalue.vt_bstr_array.vdata[0].value)*2; + } +} + +/* create single dim array of vt_i4 */ +void set_variant_array_i4(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + int32_t *vals, uint16_t elems) +{ + /* #TODO see if we can combine with other set_variant_array methods */ + variant->vtype = VT_I4 | VT_ARRAY; + variant->vvalue.vt_i4_array.cdims = 1; + variant->vvalue.vt_i4_array.ffeatures = 0; + + variant->vvalue.vt_i4_array.rgsabound = + talloc_zero_array(ctx, struct safearraybound, 1); + + variant->vvalue.vt_i4_array.rgsabound[0].celements = elems; + variant->vvalue.vt_i4_array.rgsabound[0].ilbound = 0; + variant->vvalue.vt_i4_array.cbelements = sizeof(uint32_t); + fill_int32_vec(ctx, &variant->vvalue.vt_i4_array.vdata, vals, elems); +} + +const char *genmeth_to_string(uint32_t genmethod) +{ + const char *result; + switch (genmethod) { + case 0: + result = "equals"; + break; + case 1: + result = "starts with"; + break; + case 2: + result = "matches inflection"; + break; + default: + result = "ERROR, unknown generate method"; + break; + } + return result; +} + +bool is_operator(struct wsp_crestriction *restriction) { + bool result; + switch(restriction->ultype) { + case RTAND: + case RTOR: + case RTNOT: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +const char *op_as_string(struct wsp_crestriction *restriction) +{ + const char *op = NULL; + if (is_operator(restriction)) { + switch(restriction->ultype) { + case RTAND: + op = " && "; + break; + case RTOR: + op = " || "; + break; + case RTNOT: + op = "!"; + break; + } + } else if (restriction->ultype == RTPROPERTY) { + struct wsp_cpropertyrestriction *prop_restr = + &restriction->restriction.cpropertyrestriction; + switch (prop_restr->relop & 0XF) { + case PREQ: + op = "="; + break; + case PRNE: + op = "!="; + break; + case PRGE: + op = ">="; + break; + case PRLE: + op = "<="; + break; + case PRLT: + op = "<"; + break; + case PRGT: + op = ">"; + break; + default: + break; + } + } else if (restriction->ultype == RTCONTENT) { + struct wsp_ccontentrestriction *content = NULL; + content = &restriction->restriction.ccontentrestriction; + op = genmeth_to_string(content->ulgeneratemethod); + } else if (restriction->ultype == RTNATLANGUAGE) { + op = "="; + } + return op; +} + +struct wsp_cfullpropspec *get_full_prop(struct wsp_crestriction *restriction) +{ + struct wsp_cfullpropspec *result; + switch (restriction->ultype) { + case RTPROPERTY: + result = &restriction->restriction.cpropertyrestriction.property; + break; + case RTCONTENT: + result = &restriction->restriction.ccontentrestriction.property; + break; + case RTNATLANGUAGE: + result = &restriction->restriction.cnatlanguagerestriction.property; + break; + default: + result = NULL; + break; + } + return result; +} + +const char *variant_as_string(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *value, bool quote) +{ + const char* result = NULL; + switch(value->vtype) { + case VT_I4: + case VT_UI4: + case VT_INT: + case VT_UINT: + case VT_I2: + case VT_UI2: + result = talloc_asprintf(ctx, "%d", + value->vvalue.vt_i4); + break; + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: { + uint64_t val; + wsp_hyper_to_uint64(&value->vvalue.vt_ui8, &val); + if (value->vtype == VT_I8) { + result = talloc_asprintf(ctx, "%" PRId64, + val); + } else { + result = talloc_asprintf(ctx, "%" PRIu64, + val); + } + break; + } + case VT_LPWSTR: + result = talloc_asprintf(ctx, "%s%s%s", + quote ? "\'" : "", + value->vvalue.vt_lpwstr.value, + quote ? "\'" : ""); + break; + case VT_LPWSTR | VT_VECTOR: { + int num_elems = + value->vvalue.vt_lpwstr_v.vvector_elements; + int i; + for(i = 0; i < num_elems; i++) { + struct vt_lpwstr_vec *vec; + const char *val; + vec = &value->vvalue.vt_lpwstr_v; + val = vec->vvector_data[i].value; + result = + talloc_asprintf(ctx, + "%s%s%s%s%s", + result ? result : "", + i ? "," : "", + quote ? "\'" : "", + val, + quote ? "\'" : ""); + } + break; + } + default: + DBG_INFO("#FIXME unsupported type 0x%x\n", + value->vtype); + break; + } + return result; +} + +static NTSTATUS restriction_node_to_string(TALLOC_CTX *ctx, + struct wsp_crestriction *restriction, + const char **str_result) +{ + const char *result = NULL; + struct wsp_cfullpropspec *full_prop = get_full_prop(restriction); + const char *op_str = op_as_string(restriction); + const char *propname = NULL; + const char *value = NULL; + NTSTATUS status = NT_STATUS_OK; + if (is_operator(restriction)) { + result = talloc_strdup(ctx, op_str); + goto out; + } + + if (restriction->ultype == RTPROPERTY + || restriction->ultype == RTCONTENT + || restriction->ultype == RTNATLANGUAGE) { + if (full_prop) { + propname = prop_from_fullprop(ctx, full_prop); + } + if (propname == NULL) { + DBG_ERR("Unknown propname\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + } + if (op_str == NULL) { + DBG_WARNING("Unknow operation for prop %s\n", propname); + } + switch(restriction->ultype) { + case RTCONTENT: { + struct wsp_ccontentrestriction *content = NULL; + content = + &restriction->restriction.ccontentrestriction; + value = talloc_strdup(ctx, content->pwcsphrase); + result = talloc_asprintf(ctx, "RTCONTENT %s %s %s", propname, op_str, value); + break; + } + case RTPROPERTY: { + struct wsp_cpropertyrestriction *prop = + &restriction->restriction.cpropertyrestriction; + struct wsp_cbasestoragevariant *variant = &prop->prval; + value = variant_as_string(ctx, variant, true); + result = talloc_asprintf(ctx, "RTPROPERTY %s %s %s", propname, op_str, value); + break; + } + case RTNATLANGUAGE: { + struct wsp_cnatlanguagerestriction *cnat = + &restriction->restriction.cnatlanguagerestriction; + result = talloc_asprintf(ctx, + "RTNATLANGUAGE %s %s %s", + propname, + op_str, + cnat->pwcsphrase); + + break; + } + case RTCOERCE_ABSOLUTE: { + struct wsp_crestriction *child_restrict = + restriction->restriction.ccoercionrestriction_abs.childres; + result = raw_restriction_to_string(ctx, child_restrict); + if (!result) { + status = NT_STATUS_UNSUCCESSFUL; + } + break; + } + case RTREUSEWHERE: { + uint32_t id = + restriction->restriction.reusewhere.whereid; + result = talloc_asprintf(ctx, + "insert expression for WHEREID = %d", + id); + + break; + } + default: + DBG_ERR("## unknown type 0x%x\n", restriction->ultype); + status = NT_STATUS_INVALID_PARAMETER; + break; + } +out: + *str_result = result; + return status; +} + +static NTSTATUS infix_restriction(TALLOC_CTX *ctx, + struct wsp_crestriction *restriction, + const char **str_result) +{ + const char *tmp = *str_result; + const char *token = NULL; + struct wsp_crestriction *left = NULL; + struct wsp_crestriction *right = NULL; + NTSTATUS status; + if (!restriction) { + status = NT_STATUS_OK; + goto out; + } + if (is_operator(restriction)) { + if (restriction->ultype == RTAND + || restriction->ultype == RTOR) { + struct wsp_cnoderestriction *cnodes = + &restriction->restriction.cnoderestriction; + if (cnodes->cnode) { + left = &cnodes->panode[0]; + if (cnodes->cnode > 1) { + right = &cnodes->panode[1]; + } + } + } else { + right = restriction->restriction.restriction.restriction; + } + tmp = talloc_asprintf(ctx, "%s(", tmp ? tmp : ""); + } + status = infix_restriction(ctx, left, &tmp); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = restriction_node_to_string(ctx, restriction, &token); + + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + tmp = talloc_asprintf(ctx, "%s%s", tmp ? tmp : "", token); + + status = infix_restriction(ctx, right, &tmp); + + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + if (is_operator(restriction)) { + tmp = talloc_asprintf(ctx, "%s)",tmp); + } + *str_result = tmp; +out: + return status; +} + +const char *raw_restriction_to_string(TALLOC_CTX *ctx, + struct wsp_crestriction *restriction) +{ + const char *result = NULL; + infix_restriction(ctx, restriction, &result); + return result; +} diff --git a/librpc/rpc/wsp_helper.h b/librpc/rpc/wsp_helper.h index 76b5156..01efb62 100644 --- a/librpc/rpc/wsp_helper.h +++ b/librpc/rpc/wsp_helper.h @@ -22,10 +22,69 @@ #define __LIBRPC_WSP_HELPER_H__ struct safearraybound; +struct wsp_cfullpropspec; +struct wsp_cpmgetrowsin; +struct wsp_cpmgetrowsout; +struct wsp_cpmsetbindingsin; +struct wsp_cbasestoragevariant; +struct wsp_crestriction; struct wsp_hyper; - +struct GUID; uint32_t calc_array_size(struct safearraybound *bounds, uint32_t ndims); + +struct full_propset_info { + uint32_t id; + const char *name; + uint16_t vtype; + bool extra_info; + bool in_inverted_index; + bool is_column; + bool can_col_be_indexed; + uint16_t max_size; +}; + +char *prop_from_fullprop(TALLOC_CTX *ctx, struct wsp_cfullpropspec *fullprop); +const struct full_propset_info *get_prop_info(const char *prop_name); +const struct full_propset_info *get_propset_info_with_guid( + const char *prop_name, + struct GUID *guid); +const char * get_vtype_name(uint32_t type); +bool is_variable_size(uint16_t vtype); void uint64_to_wsp_hyper(uint64_t src, struct wsp_hyper *dest); void wsp_hyper_to_uint64(struct wsp_hyper *src, uint64_t *dest); +const char *get_store_status(uint8_t status_byte); + +bool is_operator(struct wsp_crestriction *restriction); +const char *op_as_string(struct wsp_crestriction *restriction); +const char *raw_restriction_to_string(TALLOC_CTX *ctx, + struct wsp_crestriction *restriction); +struct wsp_cfullpropspec *get_full_prop(struct wsp_crestriction *restriction); +const char *genmeth_to_string(uint32_t genmethod); +const char *variant_as_string(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *value, + bool quote); +void set_variant_lpwstr(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *vvalue, + const char *string_val); +void set_variant_i4(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *vvalue, + uint32_t val); +void set_variant_vt_bool(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + bool bval); +void set_variant_bstr(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, + const char *string_val); +void set_variant_lpwstr_vector(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + const char **string_vals, uint32_t elems); +void set_variant_array_bstr(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + const char **string_vals, uint16_t elems); +void set_variant_i4_vector(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + int32_t* ivector, uint32_t elems); +void set_variant_array_i4(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *variant, + int32_t *vals, uint16_t elems); #endif // __LIBRPC_WSP_HELPER_H__ -- 2.10.2 >From 180146b0b4188a652fc2d17889c6f215696ea7bc Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 29 Jun 2016 11:29:54 +0100 Subject: [PATCH 09/21] s4/libcli: Add simple client api for wsp client code. Signed-off-by: Noel Power --- source4/libcli/wscript_build | 5 + source4/libcli/wsp/wsp_aqs.c | 800 ++++++++++++ source4/libcli/wsp/wsp_aqs.h | 166 +++ source4/libcli/wsp/wsp_aqs_lexer.c | 2305 ++++++++++++++++++++++++++++++++++ source4/libcli/wsp/wsp_aqs_lexer.h | 346 ++++++ source4/libcli/wsp/wsp_aqs_lexer.l | 150 +++ source4/libcli/wsp/wsp_aqs_parser.c | 2319 +++++++++++++++++++++++++++++++++++ source4/libcli/wsp/wsp_aqs_parser.h | 158 +++ source4/libcli/wsp/wsp_aqs_parser.y | 425 +++++++ source4/libcli/wsp/wsp_cli.c | 1794 +++++++++++++++++++++++++++ source4/libcli/wsp/wsp_cli.h | 110 ++ 11 files changed, 8578 insertions(+) create mode 100644 source4/libcli/wsp/wsp_aqs.c create mode 100644 source4/libcli/wsp/wsp_aqs.h create mode 100644 source4/libcli/wsp/wsp_aqs_lexer.c create mode 100644 source4/libcli/wsp/wsp_aqs_lexer.h create mode 100644 source4/libcli/wsp/wsp_aqs_lexer.l create mode 100644 source4/libcli/wsp/wsp_aqs_parser.c create mode 100644 source4/libcli/wsp/wsp_aqs_parser.h create mode 100644 source4/libcli/wsp/wsp_aqs_parser.y create mode 100644 source4/libcli/wsp/wsp_cli.c create mode 100644 source4/libcli/wsp/wsp_cli.h diff --git a/source4/libcli/wscript_build b/source4/libcli/wscript_build index 38a8f4e..05056db 100755 --- a/source4/libcli/wscript_build +++ b/source4/libcli/wscript_build @@ -8,6 +8,11 @@ bld.SAMBA_SUBSYSTEM('LIBSAMBA_TSOCKET', public_deps='LIBTSOCKET tevent-util' ) +bld.SAMBA_SUBSYSTEM('LIBSAMBA_WSP', + source='wsp/wsp_cli.c wsp/wsp_cli.c wsp/wsp_aqs.c wsp/wsp_aqs_parser.c wsp/wsp_aqs_lexer.c', + public_deps='tevent-util', + enabled=bld.env.with_wsp + ) bld.SAMBA_SUBSYSTEM('LIBCLI_LSA', source='util/clilsa.c', diff --git a/source4/libcli/wsp/wsp_aqs.c b/source4/libcli/wsp/wsp_aqs.c new file mode 100644 index 0000000..ef6cc7c --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs.c @@ -0,0 +1,800 @@ +/* + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.h" +#include "libcli/wsp/wsp_aqs_lexer.h" +#include "librpc/rpc/wsp_helper.h" +#include +#include + +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)) { + printf("couldn't initialize\n"); + return NULL; + } + + state = yy_scan_string(expr, scanner); + + if (yyparse(&select, scanner)) { + printf("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); + cols->num_cols = 0; + cols->cols = NULL; + printf("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); + } + 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); + 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); + 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 propery 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); + /* + * try create a restriction tree (>=lower AND 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 convertor #TODO probabaly 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; + } + uint64_to_wsp_hyper(src->value.number, &dest->vvalue.vt_ui8); + 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; + 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) { + uint64_t filetime; + struct wsp_hyper *p_hyper = &dest->vvalue.vt_filetime; + filetime = ((timeval * 10000000) + 116444736000000000); + dest->vtype = VT_FILETIME; + uint64_to_wsp_hyper(filetime, p_hyper); + 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); + 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 = + talloc_zero(ctx, struct wsp_crestriction); + struct wsp_cfullpropspec *prop; + t_basic_restr *restr = query->basic_restriction; + t_value_holder *src = restr->values; + if (restr->prop_type == RTNONE) { + /* shouldn't end up here */ + DBG_ERR("Unexpected t_basic_restr type\n"); + 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); + /* #TODO we might be able to generate the lcid from environ */ + content->lcid = 0x00000409; + 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); + build_query(ctx, node->left, restr_node->left, prop); + } + + if (restr_node->right) { + node->right = talloc_zero(ctx, t_query); + 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); + 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); + + 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); + + 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); + 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); + *date1 = ((lower * 10000000) + 116444736000000000); + *date2 = ((upper * 10000000) + 116444736000000000); + 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 = talloc_zero(ctx, t_value_holder); + t_value_holder *upper = talloc_zero(ctx, t_value_holder); + result = create_value_range(result, lower, upper); + + if (result == NULL || lower == NULL || upper == 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); + 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/source4/libcli/wsp/wsp_aqs.h b/source4/libcli/wsp/wsp_aqs.h new file mode 100644 index 0000000..f8ffc63 --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs.h @@ -0,0 +1,166 @@ +/* + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#ifndef __WSP_SQL_H__ +#define __WSP_SQL_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 grammer 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 // __EXPRESSION_H__ diff --git a/source4/libcli/wsp/wsp_aqs_lexer.c b/source4/libcli/wsp/wsp_aqs_lexer.c new file mode 100644 index 0000000..79b5f93 --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_lexer.c @@ -0,0 +1,2305 @@ +#line 2 "wsp_aqs_lexer.c" + +#line 4 "wsp_aqs_lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +static void yyensure_buffer_stack (yyscan_t yyscanner ); +static void yy_load_buffer_state (yyscan_t yyscanner ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 49 +#define YY_END_OF_BUFFER 50 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[153] = + { 0, + 1, 1, 50, 48, 1, 1, 48, 47, 48, 12, + 13, 14, 30, 47, 2, 19, 10, 48, 11, 47, + 24, 22, 47, 23, 47, 47, 47, 25, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 1, 7, + 0, 46, 47, 0, 20, 21, 47, 2, 9, 6, + 8, 47, 28, 26, 47, 27, 47, 4, 47, 29, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 46, 3, 5, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 44, 47, 47, 47, + + 47, 47, 40, 47, 17, 47, 47, 15, 39, 18, + 47, 43, 47, 47, 47, 47, 41, 47, 47, 47, + 31, 47, 16, 47, 47, 47, 47, 42, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 45, 47, 34, 38, 47, 33, 37, 47, 36, 35, + 32, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 6, 1, 1, 1, 7, + 8, 1, 1, 9, 10, 11, 1, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 13, 1, 14, + 15, 16, 1, 1, 17, 18, 19, 20, 21, 11, + 22, 23, 11, 11, 24, 25, 26, 27, 28, 11, + 11, 29, 30, 31, 11, 11, 32, 11, 11, 11, + 1, 33, 1, 1, 11, 1, 34, 11, 35, 36, + + 37, 38, 39, 40, 41, 11, 42, 43, 44, 45, + 46, 47, 11, 48, 49, 50, 51, 11, 52, 11, + 53, 11, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[54] = + { 0, + 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, + 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2 + } ; + +static yyconst flex_int16_t yy_base[156] = + { 0, + 0, 0, 205, 206, 52, 54, 189, 57, 54, 206, + 206, 206, 206, 0, 191, 206, 187, 186, 185, 172, + 180, 179, 191, 177, 166, 164, 171, 173, 167, 145, + 154, 146, 135, 151, 147, 139, 34, 145, 74, 206, + 73, 0, 106, 178, 206, 206, 0, 168, 206, 206, + 206, 159, 0, 0, 0, 0, 147, 0, 152, 0, + 155, 128, 131, 134, 133, 35, 135, 136, 128, 123, + 131, 115, 116, 206, 0, 0, 143, 134, 112, 112, + 126, 122, 119, 107, 115, 112, 105, 100, 118, 114, + 100, 130, 127, 94, 109, 100, 0, 107, 35, 87, + + 94, 41, 0, 83, 0, 98, 103, 0, 0, 0, + 83, 0, 86, 94, 93, 85, 0, 82, 90, 89, + 0, 77, 0, 83, 78, 81, 83, 0, 60, 67, + 69, 66, 66, 50, 57, 50, 47, 54, 47, 58, + 0, 51, 0, 0, 49, 0, 0, 33, 0, 0, + 0, 206, 139, 79, 141 + } ; + +static yyconst flex_int16_t yy_def[156] = + { 0, + 152, 1, 152, 152, 152, 152, 152, 153, 152, 152, + 152, 152, 152, 154, 152, 152, 152, 152, 152, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 152, 152, + 155, 154, 153, 155, 152, 152, 154, 152, 152, 152, + 152, 154, 154, 154, 43, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 152, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 0, 152, 152, 152 + } ; + +static yyconst flex_int16_t yy_nxt[260] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 14, 14, 14, + 14, 21, 14, 22, 23, 24, 25, 26, 14, 27, + 28, 29, 4, 14, 14, 14, 30, 31, 32, 33, + 14, 14, 34, 35, 14, 14, 14, 14, 36, 37, + 14, 14, 38, 39, 39, 39, 39, 41, 41, 41, + 41, 42, 41, 41, 41, 41, 41, 45, 46, 41, + 41, 41, 41, 69, 70, 39, 39, 74, 113, 71, + 47, 72, 83, 84, 118, 151, 114, 115, 150, 44, + 149, 148, 119, 120, 147, 146, 145, 144, 143, 142, + + 141, 140, 139, 138, 137, 44, 41, 41, 41, 41, + 42, 41, 41, 41, 41, 41, 136, 135, 41, 41, + 41, 41, 134, 133, 132, 131, 130, 129, 128, 127, + 126, 125, 124, 123, 122, 121, 117, 116, 44, 43, + 43, 41, 41, 112, 111, 110, 109, 108, 107, 106, + 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, + 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, + 85, 82, 81, 80, 79, 78, 77, 76, 75, 48, + 152, 73, 68, 67, 66, 65, 64, 63, 62, 61, + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, + + 50, 49, 48, 40, 152, 3, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152 + } ; + +static yyconst flex_int16_t yy_chk[260] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 5, 5, 6, 6, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 9, 8, + 8, 8, 8, 37, 37, 39, 39, 41, 99, 37, + 154, 37, 66, 66, 102, 148, 99, 99, 145, 8, + 142, 140, 102, 102, 139, 138, 137, 136, 135, 134, + + 133, 132, 131, 130, 129, 41, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 127, 126, 43, 43, + 43, 43, 125, 124, 122, 120, 119, 118, 116, 115, + 114, 113, 111, 107, 106, 104, 101, 100, 43, 153, + 153, 155, 155, 98, 96, 95, 94, 93, 92, 91, + 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, + 80, 79, 78, 77, 73, 72, 71, 70, 69, 68, + 67, 65, 64, 63, 62, 61, 59, 57, 52, 48, + 44, 38, 36, 35, 34, 33, 32, 31, 30, 29, + 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, + + 18, 17, 15, 7, 3, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "wsp_aqs_lexer.l" +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ +#line 23 "wsp_aqs_lexer.l" + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.h" + +#include + +#define YY_NO_UNISTD_H 1 +#line 569 "wsp_aqs_lexer.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 91 "wsp_aqs_lexer.l" + + +#line 809 "wsp_aqs_lexer.c" + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 153 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 152 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 93 "wsp_aqs_lexer.l" +{ /* Skip blanks. */ } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 95 "wsp_aqs_lexer.l" +{ sscanf(yytext, "%"PRId64, &yylval->num); return TOKEN_NUMBER; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 97 "wsp_aqs_lexer.l" +{ return TOKEN_AND; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 98 "wsp_aqs_lexer.l" +{ return TOKEN_OR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 99 "wsp_aqs_lexer.l" +{ return TOKEN_NOT; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 100 "wsp_aqs_lexer.l" +{ return TOKEN_EQ; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 101 "wsp_aqs_lexer.l" +{ return TOKEN_NE; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 102 "wsp_aqs_lexer.l" +{ return TOKEN_GE; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 103 "wsp_aqs_lexer.l" +{ return TOKEN_LE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 104 "wsp_aqs_lexer.l" +{ return TOKEN_LT; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 105 "wsp_aqs_lexer.l" +{ return TOKEN_GT; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 106 "wsp_aqs_lexer.l" +{ return TOKEN_LPAREN; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 107 "wsp_aqs_lexer.l" +{ return TOKEN_RPAREN; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 108 "wsp_aqs_lexer.l" +{ return TOKEN_COMMA; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 109 "wsp_aqs_lexer.l" +{ return TOKEN_WHERE; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 110 "wsp_aqs_lexer.l" +{ return TOKEN_SELECT; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 111 "wsp_aqs_lexer.l" +{ return TOKEN_TRUE; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 112 "wsp_aqs_lexer.l" +{ return TOKEN_FALSE; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 113 "wsp_aqs_lexer.l" +{ return TOKEN_PROP_EQUALS; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 115 "wsp_aqs_lexer.l" +{ return TOKEN_STARTS_WITH;} + YY_BREAK +case 21: +YY_RULE_SETUP +#line 116 "wsp_aqs_lexer.l" +{ return TOKEN_EQUALS;} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 118 "wsp_aqs_lexer.l" +{ return TOKEN_K; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 119 "wsp_aqs_lexer.l" +{ return TOKEN_M; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 120 "wsp_aqs_lexer.l" +{ return TOKEN_G; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 121 "wsp_aqs_lexer.l" +{ return TOKEN_T; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 122 "wsp_aqs_lexer.l" +{ return TOKEN_KB; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 123 "wsp_aqs_lexer.l" +{ return TOKEN_MB; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 124 "wsp_aqs_lexer.l" +{ return TOKEN_GB; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 125 "wsp_aqs_lexer.l" +{ return TOKEN_TB; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 126 "wsp_aqs_lexer.l" +{ return TOKEN_RANGE; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 127 "wsp_aqs_lexer.l" +{ return TOKEN_TODAY; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 128 "wsp_aqs_lexer.l" +{ return TOKEN_YESTERDAY;} + YY_BREAK +case 33: +YY_RULE_SETUP +#line 129 "wsp_aqs_lexer.l" +{ return TOKEN_THISWEEK;} + YY_BREAK +case 34: +YY_RULE_SETUP +#line 130 "wsp_aqs_lexer.l" +{ return TOKEN_LASTWEEK;} + YY_BREAK +case 35: +YY_RULE_SETUP +#line 131 "wsp_aqs_lexer.l" +{ return TOKEN_THISMONTH; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 132 "wsp_aqs_lexer.l" +{ return TOKEN_LASTMONTH; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 133 "wsp_aqs_lexer.l" +{ return TOKEN_THISYEAR; } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 134 "wsp_aqs_lexer.l" +{ return TOKEN_LASTYEAR; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 135 "wsp_aqs_lexer.l" +{ return TOKEN_EMPTY; } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 136 "wsp_aqs_lexer.l" +{ return TOKEN_TINY; } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 137 "wsp_aqs_lexer.l" +{ return TOKEN_SMALL; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 138 "wsp_aqs_lexer.l" +{ return TOKEN_MEDIUM; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 139 "wsp_aqs_lexer.l" +{ return TOKEN_LARGE; } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 140 "wsp_aqs_lexer.l" +{ return TOKEN_HUGE; } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 141 "wsp_aqs_lexer.l" +{ return TOKEN_GIGANTIC; } + YY_BREAK +case 46: +/* rule 46 can match eol */ +YY_RULE_SETUP +#line 144 "wsp_aqs_lexer.l" +{ yylval->strval = talloc_asprintf(talloc_tos(), "%s", yytext); return TOKEN_STRING_LITERAL; } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 146 "wsp_aqs_lexer.l" +{ yylval->strval = talloc_asprintf(talloc_tos(), "%s", yytext); return TOKEN_IDENTIFIER; } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 147 "wsp_aqs_lexer.l" +{ } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 149 "wsp_aqs_lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1137 "wsp_aqs_lexer.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 153 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 153 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 152); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register yy_size_t number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + yy_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ,yyscanner ); + + yyfree((void *) b ,yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void yyset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int yylex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ + +int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 149 "wsp_aqs_lexer.l" + + + + diff --git a/source4/libcli/wsp/wsp_aqs_lexer.h b/source4/libcli/wsp/wsp_aqs_lexer.h new file mode 100644 index 0000000..b55f088 --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_lexer.h @@ -0,0 +1,346 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "wsp_aqs_lexer.h" + +#line 8 "wsp_aqs_lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 149 "wsp_aqs_lexer.l" + + +#line 345 "wsp_aqs_lexer.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/source4/libcli/wsp/wsp_aqs_lexer.l b/source4/libcli/wsp/wsp_aqs_lexer.l new file mode 100644 index 0000000..14fda37 --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_lexer.l @@ -0,0 +1,150 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +%{ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.h" + +#include + +%} + +%option outfile="wsp_aqs_lexer.c" header-file="wsp_aqs_lexer.h" +%option warn nodefault + +%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/source4/libcli/wsp/wsp_aqs_parser.c b/source4/libcli/wsp/wsp_aqs_parser.c new file mode 100644 index 0000000..c7ceecd --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_parser.c @@ -0,0 +1,2319 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.7" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +/* Line 371 of yacc.c */ +#line 22 "wsp_aqs_parser.y" + + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.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; +} + +/* Line 371 of yacc.c */ +#line 81 "wsp_aqs_parser.c" + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "wsp_aqs_parser.h". */ +#ifndef YY_YY_WSP_AQS_PARSER_H_INCLUDED +# define YY_YY_WSP_AQS_PARSER_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +/* Line 387 of yacc.c */ +#line 34 "wsp_aqs_parser.y" + + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + + + +/* Line 387 of yacc.c */ +#line 123 "wsp_aqs_parser.c" + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TOKEN_AND = 259, + TOKEN_OR = 261, + TOKEN_NE = 263, + TOKEN_GE = 265, + TOKEN_LE = 267, + TOKEN_LT = 269, + TOKEN_GT = 271, + TOKEN_NOT = 273, + TOKEN_EQ = 275, + TOKEN_PROP_EQUALS = 277, + TOKEN_STARTS_WITH = 279, + TOKEN_EQUALS = 281, + TOKEN_LPAREN = 282, + TOKEN_RPAREN = 283, + TOKEN_WHERE = 284, + TOKEN_SELECT = 285, + TOKEN_TRUE = 286, + TOKEN_FALSE = 287, + TOKEN_COMMA = 288, + TOKEN_MATCHES = 289, + TOKEN_K = 290, + TOKEN_M = 291, + TOKEN_G = 292, + TOKEN_T = 293, + TOKEN_KB = 294, + TOKEN_MB = 295, + TOKEN_GB = 296, + TOKEN_TB = 297, + TOKEN_RANGE = 298, + TOKEN_TODAY = 299, + TOKEN_YESTERDAY = 300, + TOKEN_THISWEEK = 301, + TOKEN_LASTWEEK = 302, + TOKEN_THISMONTH = 303, + TOKEN_LASTMONTH = 304, + TOKEN_THISYEAR = 305, + TOKEN_LASTYEAR = 306, + TOKEN_EMPTY = 307, + TOKEN_TINY = 308, + TOKEN_SMALL = 309, + TOKEN_MEDIUM = 310, + TOKEN_LARGE = 311, + TOKEN_HUGE = 312, + TOKEN_GIGANTIC = 313, + TOKEN_NUMBER = 314, + TOKEN_IDENTIFIER = 315, + TOKEN_STRING_LITERAL = 316 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 387 of yacc.c */ +#line 51 "wsp_aqs_parser.y" + + 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; + + +/* Line 387 of yacc.c */ +#line 204 "wsp_aqs_parser.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (t_select_stmt **select, yyscan_t scanner); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_WSP_AQS_PARSER_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +/* Line 390 of yacc.c */ +#line 231 "wsp_aqs_parser.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(N) (N) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 15 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 107 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 62 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 16 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 61 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 81 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 316 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 10, 12, 14, 18, 20, 22, + 26, 30, 34, 37, 41, 43, 45, 48, 51, 55, + 57, 59, 61, 63, 65, 67, 69, 71, 73, 77, + 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, + 99, 101, 103, 105, 107, 109, 111, 113, 116, 119, + 122, 125, 128, 131, 134, 137, 139, 141, 143, 145, + 147, 151 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 63, 0, -1, 64, -1, 30, 65, 29, 67, -1, + 67, -1, 66, -1, 66, 33, 65, -1, 60, -1, + 68, -1, 27, 67, 28, -1, 67, 4, 67, -1, + 67, 6, 67, -1, 18, 67, -1, 69, 22, 70, + -1, 60, -1, 73, -1, 71, 73, -1, 72, 73, + -1, 27, 77, 28, -1, 20, -1, 8, -1, 10, + -1, 12, -1, 14, -1, 16, -1, 24, -1, 26, + -1, 76, -1, 76, 43, 76, -1, 74, -1, 75, + -1, 44, -1, 45, -1, 46, -1, 47, -1, 48, + -1, 49, -1, 50, -1, 51, -1, 52, -1, 53, + -1, 54, -1, 55, -1, 56, -1, 57, -1, 58, + -1, 59, -1, 59, 35, -1, 59, 36, -1, 59, + 37, -1, 59, 38, -1, 59, 39, -1, 59, 40, + -1, 59, 41, -1, 59, 42, -1, 31, -1, 32, + -1, 61, -1, 60, -1, 70, -1, 77, 4, 77, + -1, 77, 6, 77, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 140, 140, 146, 152, 161, 167, 176, 185, 191, + 197, 203, 209, 218, 226, 235, 241, 247, 253, 265, + 266, 267, 268, 269, 270, 274, 275, 279, 280, 286, + 292, 301, 302, 303, 304, 305, 306, 307, 308, 312, + 313, 314, 315, 316, 317, 318, 322, 328, 334, 340, + 346, 353, 359, 365, 371, 378, 384, 390, 398, 406, + 412, 418 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "\"AND\"", "TOKEN_AND", "\"OR\"", + "TOKEN_OR", "\"!=\"", "TOKEN_NE", "\">=\"", "TOKEN_GE", "\"<=\"", + "TOKEN_LE", "\"<\"", "TOKEN_LT", "\">\"", "TOKEN_GT", "\"NOT\"", + "TOKEN_NOT", "\"==\"", "TOKEN_EQ", "\":\"", "TOKEN_PROP_EQUALS", + "\"$<\"", "TOKEN_STARTS_WITH", "\"$=\"", "TOKEN_EQUALS", "TOKEN_LPAREN", + "TOKEN_RPAREN", "TOKEN_WHERE", "TOKEN_SELECT", "TOKEN_TRUE", + "TOKEN_FALSE", "TOKEN_COMMA", "TOKEN_MATCHES", "TOKEN_K", "TOKEN_M", + "TOKEN_G", "TOKEN_T", "TOKEN_KB", "TOKEN_MB", "TOKEN_GB", "TOKEN_TB", + "TOKEN_RANGE", "TOKEN_TODAY", "TOKEN_YESTERDAY", "TOKEN_THISWEEK", + "TOKEN_LASTWEEK", "TOKEN_THISMONTH", "TOKEN_LASTMONTH", "TOKEN_THISYEAR", + "TOKEN_LASTYEAR", "TOKEN_EMPTY", "TOKEN_TINY", "TOKEN_SMALL", + "TOKEN_MEDIUM", "TOKEN_LARGE", "TOKEN_HUGE", "TOKEN_GIGANTIC", + "TOKEN_NUMBER", "TOKEN_IDENTIFIER", "TOKEN_STRING_LITERAL", "$accept", + "input", "select_stmt", "cols", "col", "query", "basic_query", "prop", + "basic_restr", "property_op", "content_op", "value", "date_shortcut", + "size_shortcut", "simple_value", "restr", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 62, 63, 64, 64, 65, 65, 66, 67, 67, + 67, 67, 67, 68, 69, 70, 70, 70, 70, 71, + 71, 71, 71, 71, 71, 72, 72, 73, 73, 73, + 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, + 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 77, + 77, 77 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 4, 1, 1, 3, 1, 1, 3, + 3, 3, 2, 3, 1, 1, 2, 2, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 3, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 14, 0, 2, 4, 8, 0, + 12, 0, 7, 0, 5, 1, 0, 0, 0, 9, + 0, 0, 10, 11, 20, 21, 22, 23, 24, 19, + 25, 26, 0, 55, 56, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 58, 57, 13, 0, 0, 15, 29, 30, 27, + 3, 6, 59, 0, 47, 48, 49, 50, 51, 52, + 53, 54, 16, 17, 0, 0, 0, 18, 28, 60, + 61 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 5, 6, 13, 14, 7, 8, 9, 62, 54, + 55, 56, 57, 58, 59, 63 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -62 +static const yytype_int8 yypact[] = +{ + -5, -1, -1, -55, -62, 20, -62, -3, -62, 5, + -62, 3, -62, 0, -12, -62, -1, -1, -8, -62, + -1, -55, 22, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -8, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, + 55, -62, -62, -62, 25, 25, -62, -62, -62, -13, + -3, -62, -62, 59, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, 1, -8, -8, -62, -62, 28, + -62 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -62, -62, -62, 14, -62, 87, -62, -62, 36, -62, + -62, -44, -62, -62, -16, -61 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 24, 16, 25, 17, 26, 12, 27, 16, 28, 17, + 72, 73, 29, 1, 79, 80, 30, 1, 31, 32, + 15, 21, 2, 33, 34, 3, 2, 18, 17, 20, + 74, 19, 33, 34, 76, 61, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 4, 33, 34, 78, 4, + 50, 51, 52, 75, 0, 76, 0, 0, 0, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 77, 10, 11, + 64, 65, 66, 67, 68, 69, 70, 71, 0, 0, + 0, 0, 0, 22, 23, 0, 0, 60 +}; + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-62))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 8, 4, 10, 6, 12, 60, 14, 4, 16, 6, + 54, 55, 20, 18, 75, 76, 24, 18, 26, 27, + 0, 33, 27, 31, 32, 30, 27, 22, 6, 29, + 43, 28, 31, 32, 6, 21, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 18, 60, 31, 32, 74, 60, + 59, 60, 61, 4, -1, 6, -1, -1, -1, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 28, 1, 2, + 35, 36, 37, 38, 39, 40, 41, 42, -1, -1, + -1, -1, -1, 16, 17, -1, -1, 20 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 18, 27, 30, 60, 63, 64, 67, 68, 69, + 67, 67, 60, 65, 66, 0, 4, 6, 22, 28, + 29, 33, 67, 67, 8, 10, 12, 14, 16, 20, + 24, 26, 27, 31, 32, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 70, 71, 72, 73, 74, 75, 76, + 67, 65, 70, 77, 35, 36, 37, 38, 39, 40, + 41, 42, 73, 73, 43, 4, 6, 28, 76, 77, + 77 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (select, scanner, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, select, scanner); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, t_select_stmt **select, yyscan_t scanner) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, select, scanner) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + t_select_stmt **select; + yyscan_t scanner; +#endif +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; + YYUSE (select); + YYUSE (scanner); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, t_select_stmt **select, yyscan_t scanner) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, select, scanner) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + t_select_stmt **select; + yyscan_t scanner; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, select, scanner); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule, t_select_stmt **select, yyscan_t scanner) +#else +static void +yy_reduce_print (yyvsp, yyrule, select, scanner) + YYSTYPE *yyvsp; + int yyrule; + t_select_stmt **select; + yyscan_t scanner; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , select, scanner); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule, select, scanner); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, t_select_stmt **select, yyscan_t scanner) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, select, scanner) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + t_select_stmt **select; + yyscan_t scanner; +#endif +{ + YYUSE (yyvaluep); + YYUSE (select); + YYUSE (scanner); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (t_select_stmt **select, yyscan_t scanner) +#else +int +yyparse (select, scanner) + t_select_stmt **select; + yyscan_t scanner; +#endif +#endif +{ +/* The lookahead symbol. */ +int yychar; + + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +static YYSTYPE yyval_default; +# define YY_INITIAL_VALUE(Value) = Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +/* Line 1792 of yacc.c */ +#line 140 "wsp_aqs_parser.y" + { + *select = (yyvsp[(1) - (1)].select_stmt); + } + break; + + case 3: +/* Line 1792 of yacc.c */ +#line 146 "wsp_aqs_parser.y" + { + (yyval.select_stmt) = create_select(talloc_tos(), (yyvsp[(2) - (4)].columns), (yyvsp[(4) - (4)].query) ); + if (!(yyval.select_stmt)) { + YYERROR; + } + } + break; + + case 4: +/* Line 1792 of yacc.c */ +#line 152 "wsp_aqs_parser.y" + { + (yyval.select_stmt) = create_select(talloc_tos(), NULL, (yyvsp[(1) - (1)].query) ); + if (!(yyval.select_stmt)) { + YYERROR; + } + } + break; + + case 5: +/* Line 1792 of yacc.c */ +#line 161 "wsp_aqs_parser.y" + { + (yyval.columns) = create_cols(talloc_tos(), (yyvsp[(1) - (1)].strval), NULL); + if (!(yyval.columns)) { + YYERROR; + } + } + break; + + case 6: +/* Line 1792 of yacc.c */ +#line 167 "wsp_aqs_parser.y" + { + (yyval.columns) = create_cols(talloc_tos(), (yyvsp[(1) - (3)].strval), (yyvsp[(3) - (3)].columns)); + if (!(yyval.columns)) { + YYERROR; + } + } + break; + + case 7: +/* Line 1792 of yacc.c */ +#line 176 "wsp_aqs_parser.y" + { + (yyval.strval) = (yyvsp[(1) - (1)].strval); + if (!(yyval.strval)) { + YYERROR; + } + } + break; + + case 8: +/* Line 1792 of yacc.c */ +#line 185 "wsp_aqs_parser.y" + { + (yyval.query) = create_query_node(talloc_tos(), eVALUE, NULL, NULL, (yyvsp[(1) - (1)].bas_query)); + if (!(yyval.query)) { + YYERROR; + } + } + break; + + case 9: +/* Line 1792 of yacc.c */ +#line 191 "wsp_aqs_parser.y" + { + (yyval.query) = (yyvsp[(2) - (3)].query); + if (!(yyval.query)) { + YYERROR; + } + } + break; + + case 10: +/* Line 1792 of yacc.c */ +#line 197 "wsp_aqs_parser.y" + { + (yyval.query) = create_query_node(talloc_tos(), eAND, (yyvsp[(1) - (3)].query), (yyvsp[(3) - (3)].query), NULL); + if (!(yyval.query)) { + YYERROR; + } + } + break; + + case 11: +/* Line 1792 of yacc.c */ +#line 203 "wsp_aqs_parser.y" + { + (yyval.query) = create_query_node(talloc_tos(), eOR, (yyvsp[(1) - (3)].query), (yyvsp[(3) - (3)].query), NULL); + if (!(yyval.query)) { + YYERROR; + } + } + break; + + case 12: +/* Line 1792 of yacc.c */ +#line 209 "wsp_aqs_parser.y" + { + (yyval.query) = create_query_node(talloc_tos(), eNOT, NULL, (yyvsp[(2) - (2)].query), NULL); + if (!(yyval.query)) { + YYERROR; + } + } + break; + + case 13: +/* Line 1792 of yacc.c */ +#line 218 "wsp_aqs_parser.y" + { + (yyval.bas_query) = create_basic_query(talloc_tos(), (yyvsp[(1) - (3)].strval), (yyvsp[(3) - (3)].bas_rest)); + if (!(yyval.bas_query)) { + YYERROR; + } + } + break; + + case 14: +/* Line 1792 of yacc.c */ +#line 226 "wsp_aqs_parser.y" + { + (yyval.strval) = (yyvsp[(1) - (1)].strval); + if (!(yyval.strval)) { + YYERROR; + } + } + break; + + case 15: +/* Line 1792 of yacc.c */ +#line 235 "wsp_aqs_parser.y" + { + (yyval.bas_rest) = create_basic_restr(talloc_tos(), RTPROPERTY, eEQ, (yyvsp[(1) - (1)].value)); + if (!(yyval.bas_rest)) { + YYERROR; + } + } + break; + + case 16: +/* Line 1792 of yacc.c */ +#line 241 "wsp_aqs_parser.y" + { + (yyval.bas_rest) = create_basic_restr(talloc_tos(), RTPROPERTY, (yyvsp[(1) - (2)].prop_op), (yyvsp[(2) - (2)].value)); + if (!(yyval.bas_rest)) { + YYERROR; + } + } + break; + + case 17: +/* Line 1792 of yacc.c */ +#line 247 "wsp_aqs_parser.y" + { + (yyval.bas_rest) = create_basic_restr(talloc_tos(), RTCONTENT, (yyvsp[(1) - (2)].prop_op), (yyvsp[(2) - (2)].value)); + if (!(yyval.bas_rest)) { + YYERROR; + } + } + break; + + case 18: +/* Line 1792 of yacc.c */ +#line 253 "wsp_aqs_parser.y" + { + t_value_holder *holder = talloc_zero(talloc_tos(), t_value_holder); + holder->type = RESTR; + holder->value.restr_tree = (yyvsp[(2) - (3)].restr); + (yyval.bas_rest) = create_basic_restr(talloc_tos(), RTNONE, eEQ, holder); + if (!(yyval.bas_rest)) { + YYERROR; + } + } + break; + + case 19: +/* Line 1792 of yacc.c */ +#line 265 "wsp_aqs_parser.y" + { (yyval.prop_op) = eEQ; } + break; + + case 20: +/* Line 1792 of yacc.c */ +#line 266 "wsp_aqs_parser.y" + { (yyval.prop_op) = eNE; } + break; + + case 21: +/* Line 1792 of yacc.c */ +#line 267 "wsp_aqs_parser.y" + { (yyval.prop_op) = eGE; } + break; + + case 22: +/* Line 1792 of yacc.c */ +#line 268 "wsp_aqs_parser.y" + { (yyval.prop_op) = eLE; } + break; + + case 23: +/* Line 1792 of yacc.c */ +#line 269 "wsp_aqs_parser.y" + { (yyval.prop_op) = eLT; } + break; + + case 24: +/* Line 1792 of yacc.c */ +#line 270 "wsp_aqs_parser.y" + { (yyval.prop_op) = eGT; } + break; + + case 25: +/* Line 1792 of yacc.c */ +#line 274 "wsp_aqs_parser.y" + { (yyval.prop_op) = eSTARTSWITH; } + break; + + case 26: +/* Line 1792 of yacc.c */ +#line 275 "wsp_aqs_parser.y" + { (yyval.prop_op) = eEQUALS; } + break; + + case 27: +/* Line 1792 of yacc.c */ +#line 279 "wsp_aqs_parser.y" + { (yyval.value) = (yyvsp[(1) - (1)].value);} + break; + + case 28: +/* Line 1792 of yacc.c */ +#line 280 "wsp_aqs_parser.y" + { + (yyval.value) = create_value_range(talloc_tos(), (yyvsp[(1) - (3)].value), (yyvsp[(3) - (3)].value)); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 29: +/* Line 1792 of yacc.c */ +#line 286 "wsp_aqs_parser.y" + { + (yyval.value) = create_date_range_shortcut(talloc_tos(), (yyvsp[(1) - (1)].daterange)); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 30: +/* Line 1792 of yacc.c */ +#line 292 "wsp_aqs_parser.y" + { + (yyval.value) = create_size_range_shortcut(talloc_tos(), (yyvsp[(1) - (1)].sizerange)); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 31: +/* Line 1792 of yacc.c */ +#line 301 "wsp_aqs_parser.y" + { (yyval.daterange) = eTODAY; } + break; + + case 32: +/* Line 1792 of yacc.c */ +#line 302 "wsp_aqs_parser.y" + { (yyval.daterange) = eYESTERDAY; } + break; + + case 33: +/* Line 1792 of yacc.c */ +#line 303 "wsp_aqs_parser.y" + { (yyval.daterange) = eTHISWEEK; } + break; + + case 34: +/* Line 1792 of yacc.c */ +#line 304 "wsp_aqs_parser.y" + { (yyval.daterange) = eLASTWEEK; } + break; + + case 35: +/* Line 1792 of yacc.c */ +#line 305 "wsp_aqs_parser.y" + { (yyval.daterange) = eTHISMONTH; } + break; + + case 36: +/* Line 1792 of yacc.c */ +#line 306 "wsp_aqs_parser.y" + { (yyval.daterange) = eTHISMONTH; } + break; + + case 37: +/* Line 1792 of yacc.c */ +#line 307 "wsp_aqs_parser.y" + { (yyval.daterange) = eTHISYEAR; } + break; + + case 38: +/* Line 1792 of yacc.c */ +#line 308 "wsp_aqs_parser.y" + { (yyval.daterange) = eLASTYEAR; } + break; + + case 39: +/* Line 1792 of yacc.c */ +#line 312 "wsp_aqs_parser.y" + { (yyval.sizerange) = eEMPTY; } + break; + + case 40: +/* Line 1792 of yacc.c */ +#line 313 "wsp_aqs_parser.y" + { (yyval.sizerange) = eTINY; } + break; + + case 41: +/* Line 1792 of yacc.c */ +#line 314 "wsp_aqs_parser.y" + { (yyval.sizerange) = eSMALL; } + break; + + case 42: +/* Line 1792 of yacc.c */ +#line 315 "wsp_aqs_parser.y" + { (yyval.sizerange) = eMEDIUM; } + break; + + case 43: +/* Line 1792 of yacc.c */ +#line 316 "wsp_aqs_parser.y" + { (yyval.sizerange) = eLARGE; } + break; + + case 44: +/* Line 1792 of yacc.c */ +#line 317 "wsp_aqs_parser.y" + { (yyval.sizerange) = eHUGE; } + break; + + case 45: +/* Line 1792 of yacc.c */ +#line 318 "wsp_aqs_parser.y" + { (yyval.sizerange) = eGIGANTIC; } + break; + + case 46: +/* Line 1792 of yacc.c */ +#line 322 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), (yyvsp[(1) - (1)].num)); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 47: +/* Line 1792 of yacc.c */ +#line 328 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), (yyvsp[(1) - (2)].num) * 1024); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 48: +/* Line 1792 of yacc.c */ +#line 334 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val( talloc_tos(), (yyvsp[(1) - (2)].num) * 1024 * 1024); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 49: +/* Line 1792 of yacc.c */ +#line 340 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), (yyvsp[(1) - (2)].num) * 1024 * 1024 * 1024); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 50: +/* Line 1792 of yacc.c */ +#line 346 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), + (yyvsp[(1) - (2)].num) * 1024 * 1024 * 1024 * 1024); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 51: +/* Line 1792 of yacc.c */ +#line 353 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), (yyvsp[(1) - (2)].num) * 1000); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 52: +/* Line 1792 of yacc.c */ +#line 359 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val( talloc_tos(), (yyvsp[(1) - (2)].num) * 1000 * 1000); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 53: +/* Line 1792 of yacc.c */ +#line 365 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), (yyvsp[(1) - (2)].num) * 1000 * 1000 * 1000); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 54: +/* Line 1792 of yacc.c */ +#line 371 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), + (yyvsp[(1) - (2)].num) * 1000 * 1000 * 1000 * 1000); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 55: +/* Line 1792 of yacc.c */ +#line 378 "wsp_aqs_parser.y" + { + (yyval.value) = create_bool_val(talloc_tos(), true); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 56: +/* Line 1792 of yacc.c */ +#line 384 "wsp_aqs_parser.y" + { + (yyval.value) = create_num_val(talloc_tos(), false); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 57: +/* Line 1792 of yacc.c */ +#line 390 "wsp_aqs_parser.y" + { + char *tmp_str = talloc_strdup(talloc_tos(), (yyvsp[(1) - (1)].strval)+1); + tmp_str[strlen(tmp_str)-1] = '\0'; + (yyval.value) = create_string_val(talloc_tos(), tmp_str); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 58: +/* Line 1792 of yacc.c */ +#line 398 "wsp_aqs_parser.y" + { + (yyval.value) = create_string_val(talloc_tos(), (yyvsp[(1) - (1)].strval)); + if (!(yyval.value)) { + YYERROR; + } + } + break; + + case 59: +/* Line 1792 of yacc.c */ +#line 406 "wsp_aqs_parser.y" + { + (yyval.restr) = create_restr(talloc_tos(), eVALUE, NULL, NULL, (yyvsp[(1) - (1)].bas_rest)); + if (!(yyval.restr)) { + YYERROR; + } + } + break; + + case 60: +/* Line 1792 of yacc.c */ +#line 412 "wsp_aqs_parser.y" + { + (yyval.restr) = create_restr(talloc_tos(), eAND, (yyvsp[(1) - (3)].restr), (yyvsp[(3) - (3)].restr), NULL); + if (!(yyval.restr)) { + YYERROR; + } + } + break; + + case 61: +/* Line 1792 of yacc.c */ +#line 418 "wsp_aqs_parser.y" + { + (yyval.restr) = create_restr(talloc_tos(), eOR, (yyvsp[(1) - (3)].restr), (yyvsp[(3) - (3)].restr), NULL); + if (!(yyval.restr)) { + YYERROR; + } + } + break; + + +/* Line 1792 of yacc.c */ +#line 2087 "wsp_aqs_parser.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (select, scanner, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (select, scanner, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, select, scanner); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, select, scanner); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (select, scanner, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, select, scanner); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, select, scanner); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +/* Line 2055 of yacc.c */ +#line 425 "wsp_aqs_parser.y" + diff --git a/source4/libcli/wsp/wsp_aqs_parser.h b/source4/libcli/wsp/wsp_aqs_parser.h new file mode 100644 index 0000000..01c4f6f --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_parser.h @@ -0,0 +1,158 @@ +/* A Bison parser, made by GNU Bison 2.7. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_WSP_AQS_PARSER_H_INCLUDED +# define YY_YY_WSP_AQS_PARSER_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +/* Line 2058 of yacc.c */ +#line 34 "wsp_aqs_parser.y" + + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + + + +/* Line 2058 of yacc.c */ +#line 56 "wsp_aqs_parser.h" + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TOKEN_AND = 259, + TOKEN_OR = 261, + TOKEN_NE = 263, + TOKEN_GE = 265, + TOKEN_LE = 267, + TOKEN_LT = 269, + TOKEN_GT = 271, + TOKEN_NOT = 273, + TOKEN_EQ = 275, + TOKEN_PROP_EQUALS = 277, + TOKEN_STARTS_WITH = 279, + TOKEN_EQUALS = 281, + TOKEN_LPAREN = 282, + TOKEN_RPAREN = 283, + TOKEN_WHERE = 284, + TOKEN_SELECT = 285, + TOKEN_TRUE = 286, + TOKEN_FALSE = 287, + TOKEN_COMMA = 288, + TOKEN_MATCHES = 289, + TOKEN_K = 290, + TOKEN_M = 291, + TOKEN_G = 292, + TOKEN_T = 293, + TOKEN_KB = 294, + TOKEN_MB = 295, + TOKEN_GB = 296, + TOKEN_TB = 297, + TOKEN_RANGE = 298, + TOKEN_TODAY = 299, + TOKEN_YESTERDAY = 300, + TOKEN_THISWEEK = 301, + TOKEN_LASTWEEK = 302, + TOKEN_THISMONTH = 303, + TOKEN_LASTMONTH = 304, + TOKEN_THISYEAR = 305, + TOKEN_LASTYEAR = 306, + TOKEN_EMPTY = 307, + TOKEN_TINY = 308, + TOKEN_SMALL = 309, + TOKEN_MEDIUM = 310, + TOKEN_LARGE = 311, + TOKEN_HUGE = 312, + TOKEN_GIGANTIC = 313, + TOKEN_NUMBER = 314, + TOKEN_IDENTIFIER = 315, + TOKEN_STRING_LITERAL = 316 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 2058 of yacc.c */ +#line 51 "wsp_aqs_parser.y" + + 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; + + +/* Line 2058 of yacc.c */ +#line 137 "wsp_aqs_parser.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (t_select_stmt **select, yyscan_t scanner); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_WSP_AQS_PARSER_H_INCLUDED */ diff --git a/source4/libcli/wsp/wsp_aqs_parser.y b/source4/libcli/wsp/wsp_aqs_parser.y new file mode 100644 index 0000000..726d4d3 --- /dev/null +++ b/source4/libcli/wsp/wsp_aqs_parser.y @@ -0,0 +1,425 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +%{ + +#include "includes.h" +#include "libcli/wsp/wsp_aqs.h" +#include "libcli/wsp/wsp_aqs_parser.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 + +} + +%output "wsp_aqs_parser.c" +%defines "wsp_aqs_parser.h" + +%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 TOKEN_NUMBER +%token TOKEN_IDENTIFIER +%token TOKEN_STRING_LITERAL + +%type prop +%type basic_restr +%type restr +%type basic_query +%type query +%type cols +%type col +%type select_stmt +%type simple_value +%type value +%type date_shortcut +%type property_op +%type content_op +%type 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; + } + } + ; +%% diff --git a/source4/libcli/wsp/wsp_cli.c b/source4/libcli/wsp/wsp_cli.c new file mode 100644 index 0000000..f313d89 --- /dev/null +++ b/source4/libcli/wsp/wsp_cli.c @@ -0,0 +1,1794 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include "includes.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "libcli/wsp/wsp_cli.h" +#include "param/param.h" +#include "dcerpc.h" +#include "libcli/raw/interfaces.h" +#include "auth/credentials/credentials.h" +#include "libcli/libcli.h" +#include "libcli/smb/tstream_smbXcli_np.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "smb_composite/smb_composite.h" +#include "lib/cmdline/popt_common.h" +#include "libcli/resolve/resolve.h" +#include "librpc/rpc/dcerpc_raw.h" +#include +#include + +#define MSG_HDR_SIZE 16 +#if __WORDSIZE == 64 +static const uint32_t CLIENTVERSION = 0x00010700; +#else +static const uint32_t CLIENTVERSION = 0x00000700; +#endif + +static int32_t scope_flags_vector[] = {0x00000001}; +static const char * root_scope_string_vector[] = {"\\"}; + +/* sets sensible defaults */ +static void init_wsp_prop(struct wsp_cdbprop *prop) +{ + prop->dbpropoptions = 0x0000000; + prop->dbpropstatus = 0x0000000; + ZERO_STRUCT(prop->colid.guid); /* just in case */ + ZERO_STRUCT(prop->vvalue); /* just in case */ + prop->colid.ekind = DBKIND_GUID_PROPID; + prop->colid.uiid = 0x00000000; +} + + +static void create_restriction_array(TALLOC_CTX *ctx, + struct wsp_crestriction **pelements, + uint32_t nnodes) +{ + struct wsp_crestriction *elements = talloc_zero_array(ctx, + struct wsp_crestriction, + nnodes); + *pelements = elements; +} + + +static void create_noderestriction(TALLOC_CTX *ctx, + struct wsp_cnoderestriction *pnode, + uint32_t nnodes) +{ + pnode->cnode = nnodes; + create_restriction_array(ctx, &pnode->panode, nnodes); +} + +static void fill_sortarray(TALLOC_CTX *ctx, struct wsp_csort **dest, + struct wsp_csort *src, uint32_t num) +{ + int i; + struct wsp_csort *psort = talloc_zero_array(ctx, struct wsp_csort, + num); + for (i = 0; i < num; i++) { + psort[i] = src[i]; + } + *dest = psort; +} + + + +static bool set_fullpropspec(TALLOC_CTX *ctx, struct wsp_cfullpropspec *prop, + const char* propname, uint32_t kind) +{ + struct GUID guid; + const struct full_propset_info *prop_info; + ZERO_STRUCT(guid); + prop_info = get_propset_info_with_guid(propname, &guid); + if (!prop_info) { + /* #FIXME error handling */ + DBG_ERR("Failed to handle property named %s\n", + propname); + return false; + } + prop->guidpropset = guid; + prop->ulkind = kind; + if (kind == PRSPEC_LPWSTR) { + prop->name_or_id.propname.vstring = talloc_strdup(ctx, + propname); + prop->name_or_id.propname.len = strlen(propname); + } else { + prop->name_or_id.prspec = prop_info->id; + } + return true; +} + +static int32_t get_value_size(bool is_64bit) { + int32_t value_size = 0x10; + if (is_64bit) { + value_size = 0x18; + } + return value_size; +} + +struct binding +{ + uint32_t status_off; + uint32_t value_off; + uint32_t len_off; +}; + +static bool set_ctablecolumn(TALLOC_CTX *ctx, struct wsp_ctablecolumn *tablecol, + const char* propname, struct binding *offsets, + bool is_64bit) +{ + struct wsp_cfullpropspec *prop = &tablecol->propspec; + + if (!set_fullpropspec(ctx, prop, propname, PRSPEC_PROPID)) { + return false; + } + tablecol->vtype =VT_VARIANT ; + tablecol->aggregateused = 1; + tablecol->valueused = 1; + tablecol->valueoffset.value = offsets->value_off; + tablecol->valuesize.value = get_value_size(is_64bit); + tablecol->statusused = 1; + tablecol->statusoffset.value = offsets->status_off; + tablecol->lengthused = 1; + tablecol->lengthoffset.value = offsets->len_off; + return true; +} + + +static void fill_uint32_vec(TALLOC_CTX* ctx, + uint32_t **pdest, + uint32_t* ivector, uint32_t elems) +{ + int i; + uint32_t *dest = talloc_zero_array(ctx, uint32_t, elems); + for ( i = 0; i < elems; i++ ) { + dest[ i ] = ivector[ i ]; + } + *pdest = dest; +} + +static void init_propset1(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset) +{ + int i; + + GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 4; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* initialise first 4 props */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] */ + propertyset->aprops[0].dbpropid = DBPROP_CI_CATALOG_NAME; + set_variant_lpwstr(tmp_ctx, &propertyset->aprops[0].vvalue, + "Windows\\SYSTEMINDEX"); + /* set value prop[1] */ + propertyset->aprops[1].dbpropid = DBPROP_CI_QUERY_TYPE; + set_variant_i4(tmp_ctx, &propertyset->aprops[1].vvalue, + CINORMAL); + /* set value prop[2] */ + propertyset->aprops[2].dbpropid = DBPROP_CI_SCOPE_FLAGS; + set_variant_i4_vector(tmp_ctx, &propertyset->aprops[2].vvalue, + scope_flags_vector, ARRAY_SIZE(scope_flags_vector)); + /* set value prop[3] */ + propertyset->aprops[3].dbpropid = DBPROP_CI_INCLUDE_SCOPES; + set_variant_lpwstr_vector(tmp_ctx, + &propertyset->aprops[3].vvalue, + root_scope_string_vector, + ARRAY_SIZE(root_scope_string_vector)); +} + +static void init_propset2(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset, + const char* server) +{ + int i; + + GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 1; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* initialise first 1 props */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] */ + propertyset->aprops[0].dbpropid = DBPROP_MACHINE; + set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, + server); +} + +static void init_apropset0(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset) +{ + int i; + + GUID_from_string(DBPROPSET_MSIDXS_ROWSETEXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 7; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* initialise props */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] MSIDXSPROP_ROWSETQUERYSTATUS where is this specified ? */ + propertyset->aprops[0].dbpropid = 0x00000002; + set_variant_i4(tmp_ctx, &propertyset->aprops[0].vvalue, 0x00000000); + + /* set value prop[1] MSIDXSPROP_COMMAND_LOCALE_STRING */ + propertyset->aprops[1].dbpropid = 0x00000003; + set_variant_bstr(tmp_ctx, &propertyset->aprops[1].vvalue, + "en-ie"); + /* set value prop[2] MSIDXSPROP_QUERY_RESTRICTION */ + propertyset->aprops[2].dbpropid = 0x00000004; + set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue, + ""); + /* set value prop[3] MSIDXSPROP_PARSE_TREE */ + propertyset->aprops[3].dbpropid = 0x00000005; + set_variant_bstr(tmp_ctx, &propertyset->aprops[3].vvalue, + ""); + /* set value prop[4] MSIDXSPROP_MAX_RANK */ + propertyset->aprops[4].dbpropid = 0x00000006; + set_variant_i4(tmp_ctx, &propertyset->aprops[4].vvalue, 0x00000000); + /* set value prop[5] MSIDXSPROP_RESULTS_FOUND */ + propertyset->aprops[5].dbpropid = 0x00000007; + set_variant_i4(tmp_ctx, &propertyset->aprops[5].vvalue, 0x00000000); + /* set value prop[6] ?? */ + propertyset->aprops[6].dbpropid = 0x00000008; + set_variant_i4(tmp_ctx, &propertyset->aprops[6].vvalue, 0x00000000); +} + +static void init_apropset1(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset) +{ + int i; + GUID_from_string(DBPROPSET_QUERYEXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 0x0000000B; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* init properties */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] */ + propertyset->aprops[0].dbpropid = DBPROP_USECONTENTINDEX; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[0].vvalue, false); + /* set value prop[1] */ + propertyset->aprops[1].dbpropid = DBPROP_DEFERNONINDEXEDTRIMMING; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[1].vvalue, false); + /* set value prop[2] */ + propertyset->aprops[2].dbpropid = DBPROP_USEEXTENDEDDBTYPES; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[2].vvalue, false); + /* set value prop[3] */ + propertyset->aprops[3].dbpropid = DBPROP_IGNORENOISEONLYCLAUSES; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[3].vvalue, false); + /* set value prop[4] */ + propertyset->aprops[4].dbpropid = DBPROP_GENERICOPTIONS_STRING; + set_variant_bstr(tmp_ctx, &propertyset->aprops[4].vvalue, ""); + /* set value prop[5] */ + propertyset->aprops[5].dbpropid = DBPROP_DEFERCATALOGVERIFICATION; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[5].vvalue, false); + /* set value prop[6] */ + propertyset->aprops[6].dbpropid = DBPROP_IGNORESBRI; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[6].vvalue, false); + /* set value prop[7] */ + propertyset->aprops[7].dbpropid = DBPROP_GENERATEPARSETREE; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[7].vvalue, false); + /* set value prop[8] */ + propertyset->aprops[8].dbpropid = DBPROP_FREETEXTANYTERM; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[8].vvalue, false); + /* set value prop[9] */ + propertyset->aprops[9].dbpropid = DBPROP_FREETEXTUSESTEMMING; + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[9].vvalue, false); + /* set value prop[10] */ + propertyset->aprops[10].dbpropid = 0x0000000f; /* ??? */ + set_variant_vt_bool(tmp_ctx, &propertyset->aprops[10].vvalue, false); +} + +static void init_apropset2(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset, + const char* server) +{ + int i; + GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 0x00000001; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* init properties */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] */ + propertyset->aprops[0].dbpropid = DBPROP_MACHINE; + set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, server); + +} + + +static void init_apropset3(TALLOC_CTX* tmp_ctx, + struct wsp_cdbpropset *propertyset) +{ + int i; + + GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT, + &propertyset->guidpropertyset); + + propertyset->cproperties = 0x00000003; + propertyset->aprops = + talloc_zero_array(tmp_ctx, struct wsp_cdbprop, + propertyset->cproperties); + /* init properties */ + for( i = 0; i < propertyset->cproperties; i++) { + init_wsp_prop(&propertyset->aprops[i]); + } + + /* set value prop[0] */ + propertyset->aprops[0].dbpropid = DBPROP_CI_INCLUDE_SCOPES; + set_variant_array_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, + root_scope_string_vector, + ARRAY_SIZE(root_scope_string_vector)); + /* set value prop[1] */ + propertyset->aprops[1].dbpropid = DBPROP_CI_SCOPE_FLAGS; + set_variant_array_i4(tmp_ctx, &propertyset->aprops[1].vvalue, + scope_flags_vector, + ARRAY_SIZE(scope_flags_vector)); + /* set value prop[2] */ + propertyset->aprops[2].dbpropid = DBPROP_CI_CATALOG_NAME; + set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue, + "Windows\\SYSTEMINDEX"); +} + +void init_connectin_request(TALLOC_CTX *ctx, + struct wsp_request* request, + const char* clientmachine, + const char* clientuser, + const char* server) +{ + enum ndr_err_code err; + struct connectin_propsets *props = + talloc_zero(ctx, struct connectin_propsets); + struct connectin_extpropsets *ext_props = + talloc_zero(ctx, struct connectin_extpropsets) ; + DATA_BLOB props_blob; + struct ndr_push *ndr_props; + struct wsp_cpmconnectin *connectin = &request->message.cpmconnect; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + + request->header.msg = CPMCONNECT; + connectin->iclientversion = CLIENTVERSION; + connectin->fclientisremote = 0x00000001; + connectin->machinename = clientmachine; + connectin->username = clientuser; + props->cpropsets = 2; + + /* =================== */ + /* set up PropertySet1 */ + /* =================== */ + init_propset1(ctx, &props->propertyset1); + + /* =================== */ + /* set up PropertySet2 */ + /* =================== */ + init_propset2(ctx, &props->propertyset2, server); + /* 4 ExtPropSets */ + ext_props->cextpropset = 4; + ext_props->apropertysets = talloc_zero_array(ctx, struct wsp_cdbpropset, + ext_props->cextpropset); + + /* ======================= */ + /* set up aPropertySets[0] */ + /* ======================= */ + init_apropset0(ctx, &ext_props->apropertysets[0]); + + /* ======================= */ + /* set up aPropertySets[1] */ + /* ======================= */ + init_apropset1(ctx, &ext_props->apropertysets[1]); + + /* ======================= */ + /* set up aPropertySets[2] */ + /* ======================= */ + init_apropset2(ctx, &ext_props->apropertysets[2], server); + + /* ======================= */ + /* set up aPropertySets[3] */ + /* ======================= */ + init_apropset3(ctx, &ext_props->apropertysets[3]); + + /* we also have to fill the opaque blobs that contain the propsets */ + ndr_props = ndr_push_init_ctx(ctx); + + /* first connectin_propsets */ + err = ndr_push_connectin_propsets(ndr_props, ndr_flags, props); + if (err) { + DBG_ERR("Failed to push propset, error %d\n", err); + goto out; + } + props_blob = ndr_push_blob(ndr_props); + connectin->cbblob1 = props_blob.length; + connectin->propsets = talloc_zero_array(ctx, uint8_t, + connectin->cbblob1); + memcpy(connectin->propsets, props_blob.data, props_blob.length); + + /* then connectin_extpropsets */ + TALLOC_FREE(ndr_props); + ndr_props = ndr_push_init_ctx(ctx); + err = ndr_push_connectin_extpropsets(ndr_props, ndr_flags, ext_props); + + if (err) { + DBG_ERR("Failed to push extpropset, error %d\n", err); + goto out; + } + + props_blob = ndr_push_blob(ndr_props); + connectin->cbblob2 = props_blob.length; + connectin->extpropsets = talloc_zero_array(ctx, uint8_t, + connectin->cbblob2); + memcpy(connectin->extpropsets, props_blob.data, props_blob.length); + TALLOC_FREE(ndr_props); +out: + return; +} + +void create_seekat_getrows_request(TALLOC_CTX * ctx, + struct wsp_request* request, + bool is_64bit, + uint32_t cursor, + uint32_t bookmark, + uint32_t skip, + uint32_t rows, + uint32_t cbreserved, + uint64_t client_buf_addr, + uint32_t cbrowwidth, + uint32_t fbwdfetch) +{ + struct wsp_cpmgetrowsin *getrows = &request->message.cpmgetrows; + request->header.msg = CPMGETROWS; + getrows->hcursor = cursor; + getrows->crowstotransfer = rows; + getrows->cbrowWidth = cbrowwidth; + getrows->cbreadbuffer = 0x00004000; + + getrows->ulclientbase = client_buf_addr; + if (is_64bit) { + uint32_t hi = client_buf_addr >> 32; + request->header.ulreserved2 = hi; + } + getrows->cbreserved = cbreserved; + getrows->fbwdfetch = fbwdfetch; + getrows->etype = EROWSEEKAT; + getrows->chapt = 0; + getrows->seekdescription.crowseekat.bmkoffset = bookmark; + getrows->seekdescription.crowseekat.cskip = skip; + getrows->seekdescription.crowseekat.hregion = 0; +} + +static bool dummy_pull(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + return true; +} + +static bool pull_1byte_value(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + if (len < 1) { + return false; + } + val->vtype = type; + val->vvalue.vt_ui1 = (uint8_t)*(address); + DBG_INFO("\tval 0x%x\n", val->vvalue.vt_ui1); + return true; +} + +static bool pull_2byte_value(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + if (len < 2) { + return false; + } + val->vtype = type; + val->vvalue.vt_ui2 = SVALS(address, 0); + if (type == VT_BOOL) { + DBG_INFO("\tval %s (0x%x)\n",val->vvalue.vt_bool == 0xFFFF ? "true" : "false", val->vvalue.vt_bool ); + } else { + DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i2); + } + return true; +} + +static bool pull_4byte_value(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + if (len < 4) { + return false; + } + val->vtype = type; + val->vvalue.vt_ui4 = IVALS(address, 0); + DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i4); + return true; +} + +static bool pull_8byte_value(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + struct wsp_hyper *p_hyper = &val->vvalue.vt_ui8; + uint64_t hyper; + + if (len < 8) { + return false; + } + + val->vtype = type; + hyper = BVAL(address, 0); + uint64_to_wsp_hyper(hyper, p_hyper); + DBG_INFO("\tval 0x%" PRIx64 "\n", hyper); + return true; +} + +static bool pull_nullterm_string_value(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val) +{ + enum ndr_err_code err; + struct ndr_pull *ndr_pull; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + const char *string; + DATA_BLOB variant_blob = data_blob_null; + variant_blob.data = address; + variant_blob.length = len; + ndr_pull = ndr_pull_init_blob(&variant_blob, ctx); + ndr_set_flags(&ndr_pull->flags, LIBNDR_FLAG_STR_NULLTERM); + err = ndr_pull_string(ndr_pull, ndr_flags, &string); + if (err) { + DBG_ERR("error unmarshalling string from %p\n", variant_blob.data ); + return false; + } else { + DBG_INFO("\tstring val ->>>%s<<<-\n", string ); + val->vtype = type; + val->vvalue.vt_lpwstr.value = string; + } + return true; +} + +static struct value_only_handler { + uint16_t vtype; + bool (*pull_fn)(TALLOC_CTX *ctx, + uint16_t type, + uint8_t *address, + uint32_t len, + struct wsp_cbasestoragevariant *val); +} variant_value_only_handlers[] = { + {VT_EMPTY, dummy_pull}, + {VT_NULL, dummy_pull}, + {VT_I1, pull_1byte_value}, + {VT_UI1, pull_1byte_value}, + {VT_I2, pull_2byte_value}, + {VT_UI2, pull_2byte_value}, + {VT_BOOL, pull_2byte_value}, + {VT_I4, pull_4byte_value}, + {VT_UI4, pull_4byte_value}, + {VT_R4, pull_4byte_value}, + {VT_INT, pull_4byte_value}, + {VT_UINT, pull_4byte_value}, + {VT_ERROR, pull_4byte_value}, + {VT_DATE, pull_8byte_value}, + {VT_R8, pull_8byte_value}, + {VT_UI8, pull_8byte_value}, + {VT_I8, pull_8byte_value}, + {VT_FILETIME, pull_8byte_value}, + {VT_LPWSTR, pull_nullterm_string_value}, +}; + +static struct value_only_handler *get_value_handler(uint16_t vtype) +{ + int i; + for (i = 0; i < ARRAY_SIZE(variant_value_only_handlers); i++) { + if (variant_value_only_handlers[i].vtype == vtype) { + return &variant_value_only_handlers[i]; + } + } + return NULL; +} + +static enum ndr_err_code process_fixed_area_val(TALLOC_CTX *ctx, + struct value_only_handler *pull_handler, + bool is_64bit, + uint16_t vtype, + uint32_t len, + uint8_t *rowbuf_start, + uint8_t *value_start, + uint64_t client_buf_base_addr, + struct wsp_cbasestoragevariant *col) +{ + uint8_t *pull_address; + uint8_t buf_offset; + uint32_t pos = 0; + enum ndr_err_code err = NDR_ERR_SUCCESS; + + if (is_variable_size(vtype)) { + if (is_64bit) { + buf_offset = BVAL(value_start, pos); + pos +=8; + } else { + buf_offset = IVAL(value_start, pos); + pos +=4; + } + pull_address = + rowbuf_start + (buf_offset - client_buf_base_addr); + } else { + /* + * read a fixed type from the column fixed size area + * at the begining of the rows buffer + */ + pull_address = value_start; + } + if (!pull_handler->pull_fn(ctx, vtype, pull_address, len, col)) { + err = NDR_ERR_VALIDATE; + DBG_ERR("failed to extract variant value only for type %s\n", + get_vtype_name(vtype)); + } + return err; +} + +static enum ndr_err_code process_variant_val( + TALLOC_CTX *ctx, + struct value_only_handler *pull_handler, + bool is_64bit, + uint16_t vtype, + uint32_t len, + uint8_t *rows_buf_start, + uint32_t rows_buf_len, + uint8_t *value_start, + uint64_t client_buf_base_addr, + struct wsp_cbasestoragevariant *col) +{ + uint8_t *pull_address; + uint64_t buf_offset; + enum ndr_err_code err = NDR_ERR_SUCCESS; + uint32_t pos = 0; + /* + * variant column where the 'real' type is contained in the + * CTableVariant like structure + */ + DBG_INFO("\n"); + DBG_INFO("\tcrowvariant contains %s \n", + get_vtype_name(vtype)); + + if (is_variable_size(vtype)) { + if (is_64bit) { + buf_offset = BVAL(value_start, pos); + pos +=8; + } else { + buf_offset = IVAL(value_start, pos); + pos +=4; + } + /* + * In 32 bit mode len as calculated from the len + * (and this is what the MS-WSP example shows) + * In 64 bit mode len seems sometimes to have the value 0x32 :/ + * and therefore not the lenght we need. + * However these strings are NULL terminated so we + * should be ok. (#TODO investigate more) + */ + if (vtype == VT_LPWSTR) { + /* make sure we don't overrun end of the buff */ + len = rows_buf_len - (buf_offset - client_buf_base_addr); + } + pull_address = rows_buf_start + (buf_offset - client_buf_base_addr); + + } else if ((vtype & VT_VECTOR) == VT_VECTOR) { + int count; + uint64_t addr; + /* + * instead of just an offset we have a count followed + * by an array of uint32_t | unint64_t offsets + */ + uint64_t vec_count; + + /* currently we only support vector of strings */ + if (vtype != (VT_LPWSTR | VT_VECTOR)) { + err = NDR_ERR_VALIDATE; + DBG_ERR("unsupported type %s\n", + get_vtype_name(vtype)); + goto out; + } + /* address of offsets */ + if (is_64bit) { + /* + * maybe this isn't a 64bit value but instead + * due to alignment ? + */ + vec_count = BVAL(value_start, pos); + pos += 8; + + buf_offset = BVAL(value_start, pos); + pos +=8; + } else { + vec_count = IVAL(value_start, pos); + pos += 4; + + buf_offset = IVAL(value_start, pos); + pos +=4; + } + + col->vtype = vtype; + col->vvalue.vt_lpwstr_v.vvector_data = + talloc_zero_array(ctx, + struct vt_lpwstr, + vec_count); + col->vvalue.vt_lpwstr_v.vvector_elements = vec_count; + + addr = buf_offset - client_buf_base_addr; + for (count= 0; count < vec_count; count++) { + uint64_t vec_item_offset; + struct wsp_cbasestoragevariant tmp; + if (is_64bit) { + vec_item_offset = + BVAL(rows_buf_start, + addr); + addr += 8; + } else { + vec_item_offset = + IVAL(rows_buf_start, + addr); + addr += 4; + } + pull_address = + rows_buf_start + (vec_item_offset - client_buf_base_addr); + len = rows_buf_len - (vec_item_offset - client_buf_base_addr); + if (!pull_handler->pull_fn(ctx, + vtype & ~VT_VECTOR, + pull_address, + len, + &tmp)) { + err = NDR_ERR_VALIDATE; + DBG_ERR("failed to extract vector variant value only for type %s\n", get_vtype_name(vtype & ~VT_VECTOR)); + goto out; + } + col->vvalue.vt_lpwstr_v.vvector_data[count] = + tmp.vvalue.vt_lpwstr; + } + goto out; + } else { + uint32_t remaining; + pull_address = value_start; + /* make sure we have enough space in buffer */ + //if (rows_buf_len - (pull_address - rows_buf_start) < len) { + remaining = rows_buf_len - (pull_address - rows_buf_start); + if (remaining < len) { + err = NDR_ERR_VALIDATE; + DBG_ERR("not enough bytes left to extract %s from.\n", get_vtype_name(vtype & ~VT_VECTOR)); + goto out; + + } + } + if (!pull_handler->pull_fn(ctx, vtype, pull_address, len, col)) { + err = NDR_ERR_VALIDATE; + DBG_ERR("failed to extract variant value only for type %s\n", + get_vtype_name(vtype)); + } +out: + return err; +} + +static enum ndr_err_code process_value(TALLOC_CTX *ctx, + DATA_BLOB *rows_buf, + uint32_t nrow_offset, + struct wsp_ctablecolumn *tab_col, + bool is_64bit, + uint32_t cbreserved, + uint64_t client_buf_addr, + struct wsp_cbasestoragevariant *col) +{ + uint64_t client_buf_base_addr = client_buf_addr + cbreserved; + int32_t len = 0; + enum ndr_err_code err = NDR_ERR_SUCCESS; + + uint16_t vtype; + uint8_t *rowbuf_start; + uint32_t pos = 0; + struct value_only_handler *pull_handler = NULL; + + DBG_INFO("\n\tvalueoffset:valuesize 0x%x:0x%x crowvariant address = 0x%x",tab_col->valueoffset.value, + tab_col->valuesize.value, + (tab_col->valueoffset.value + nrow_offset)); + + rowbuf_start = rows_buf->data + nrow_offset; + + pos = tab_col->valueoffset.value; + + if (tab_col->lengthused) { + len = IVAL(rowbuf_start, tab_col->lengthoffset.value); + } + + if (tab_col->vtype != VT_VARIANT) { + pull_handler = get_value_handler(tab_col->vtype & ~VT_VECTOR); + if (!pull_handler) { + /* ignoring, maybe we should error out */ + DBG_ERR("no handler for type %s\n", + get_vtype_name(tab_col->vtype)); + goto out; + } + len -= pos; + err = process_fixed_area_val( + ctx, pull_handler, is_64bit, tab_col->vtype, + len, rowbuf_start, rowbuf_start + pos, + client_buf_base_addr, col); + + } else { + vtype = SVAL(rowbuf_start, pos); + + pos +=2; + pos +=6; /* reserved1 & reserved2 */ + + pull_handler = get_value_handler(vtype & ~VT_VECTOR); + if (!pull_handler) { + /* ignoring, maybe we should error out */ + DBG_ERR("no handler for type %s\n", + get_vtype_name(vtype)); + goto out; + } + if (is_variable_size(vtype)) { + /* for a variable size value (like a string) + * it seems the size is what's at + * lengthoffset - tab_col->valuesize.value + * Note: however this only seems to be true in the + * 32 bit mode case + */ + len = len - tab_col->valuesize.value; + } else { + len = len - (pos - tab_col->valueoffset.value); + } + err = process_variant_val(ctx, pull_handler, is_64bit, vtype, + len, rows_buf->data, rows_buf->length, + rowbuf_start + pos, + client_buf_base_addr, col); + } +out: + return err; +} + +static enum ndr_err_code process_columns(TALLOC_CTX *ctx, + bool is_64bit, + uint32_t cbreserved, + uint64_t client_buf_addr, + struct wsp_cpmsetbindingsin *bindingin, + DATA_BLOB *rows_buf, + uint32_t nrow, + struct wsp_cbasestoragevariant *cols) +{ + int i; + enum ndr_err_code err = NDR_ERR_SUCCESS; + uint32_t nrow_offset = nrow * bindingin->brow; +#if 0 + if (is_64bit) { + DBG_ERR("We don't handle 64 bit mode yet\n"); + err = NDR_ERR_VALIDATE; + goto out; + } +#endif + /* process columns */ + for (i = 0; i < bindingin->ccolumns; i++) { + struct wsp_ctablecolumn *tab_col = &bindingin->acolumns[i]; + uint8_t store_status = 0; /* StoreStatusOk */ + DBG_INFO("\nRow[%d]Col[%d] property %s type %s",nrow, i, + prop_from_fullprop(ctx, &tab_col->propspec), + get_vtype_name(tab_col->vtype)); + if (tab_col->statusused) { + store_status = ((uint8_t)*(rows_buf->data + nrow_offset + tab_col->statusoffset.value)); + DBG_INFO("\n\tstatusoffset 0x%x status is %s", + tab_col->statusoffset.value, + get_store_status(store_status)); + } + if (tab_col->lengthused) { + DBG_INFO("\n\tlengthoffset 0x%x value at length is 0x%x",tab_col->lengthoffset.value, IVAL(rows_buf->data, nrow_offset + tab_col->lengthoffset.value)); + } + if (tab_col->valueused && store_status == 0) { + err = process_value(ctx, rows_buf, nrow_offset, + tab_col, is_64bit, cbreserved, + client_buf_addr, &cols[i]); + if (err) { + goto out; + } + } + } +out: + return err; +} + +enum ndr_err_code extract_rowsarray( + TALLOC_CTX * ctx, + DATA_BLOB *rows_buf, + bool is_64bit, + struct wsp_cpmsetbindingsin *bindingsin, + uint32_t cbreserved, + uint64_t ulclientbase, + uint32_t rows, + struct wsp_cbasestoragevariant **rowsarray) +{ + int i; + enum ndr_err_code err; + + for (i = 0; i < rows; i++ ) { + struct wsp_cbasestoragevariant *cols = + talloc_zero_array(ctx, + struct wsp_cbasestoragevariant, + bindingsin->ccolumns); + err = process_columns(ctx, + is_64bit, + cbreserved, + ulclientbase, + bindingsin, + rows_buf, + i, + cols); + if (err) { + break; + } + rowsarray[i] = cols; + } + return err; +} + +static void process_query_node(TALLOC_CTX *ctx, + struct wsp_crestriction *crestriction, + t_query *node); + +static void process_andornot_node(TALLOC_CTX *ctx, + struct wsp_crestriction *crestr, + t_query *node, + struct wsp_crestriction **left, + struct wsp_crestriction **right) +{ + struct wsp_cnoderestriction *restriction_node; + + *left = NULL; + *right = NULL; + + restriction_node = + &crestr->restriction.cnoderestriction; + + crestr->weight = 1000; + + if (node->type == eAND || node->type == eOR) { + if (node->type == eAND) { + crestr->ultype = RTAND; + } else { + crestr->ultype = RTOR; + } + create_noderestriction(ctx, restriction_node, 2); + *left = &restriction_node->panode[0]; + *right = &restriction_node->panode[1]; + } else { + crestr->ultype = RTNOT; + crestr->restriction.restriction.restriction = + talloc_zero(ctx, struct wsp_crestriction); + crestr= + crestr->restriction.restriction.restriction; + } + if (*left == NULL) { + *left = crestr; + } + if (*right == NULL) { + *right = crestr; + } +} + +static void process_value_node(TALLOC_CTX *ctx, + struct wsp_crestriction *crestriction, + t_query *node) +{ + *crestriction = *node->restriction; +} + +static void process_query_node(TALLOC_CTX *ctx, + struct wsp_crestriction *crestriction, + t_query *node) +{ + struct wsp_crestriction *left = NULL, *right = NULL; + if (node == NULL) { + return; + } + switch (node->type) { + case eAND: + case eOR: + case eNOT: + process_andornot_node(ctx, crestriction, node, + &left, &right); + break; + case eVALUE: + process_value_node(ctx, crestriction, node); + default: + break; + } + process_query_node(ctx, left, node->left); + process_query_node(ctx, right, node->right); +} + +void create_querysearch_request(TALLOC_CTX * ctx, + struct wsp_request* request, + t_select_stmt *sql) +{ + struct wsp_cpmcreatequeryin *createquery = &request->message.cpmcreatequery; + uint32_t indices[sql->cols->num_cols]; + int i; + + for (i = 0; i < sql->cols->num_cols; i++) { + indices[i] = i; + } + + request->header.msg = CPMCREATEQUERY; + createquery->ccolumnsetpresent = 1; + createquery->columnset.columnset.count = sql->cols->num_cols; + fill_uint32_vec(ctx, &createquery->columnset.columnset.indexes, + indices, + sql->cols->num_cols); + + /* handle restrictions */ + createquery->crestrictionpresent = 1; + createquery->restrictionarray.restrictionarray.count = 1; + createquery->restrictionarray.restrictionarray.ispresent = 1; + + create_restriction_array(ctx, + &createquery->restrictionarray.restrictionarray.restrictions, + createquery->restrictionarray.restrictionarray.count); + + process_query_node(ctx, &createquery->restrictionarray.restrictionarray.restrictions[0], sql->where); + + + /* handle rest */ + createquery->csortsetpresent = 1; + if (createquery->csortsetpresent) { + struct wsp_csortset *sortset = &createquery->sortset.sortset; + /* sort on first column */ + struct wsp_csort data[] = { + {0x00000000, 0x00000000, 0x00000000, 0x00001809}, + }; + /* + * not sure if this is important of not, exists in captured + * messages, seems like it is, if we don't have the values below we get an error + */ + sortset->unknown1 = 1; + sortset->unknown2 = 0; + sortset->count = ARRAY_SIZE(data); + fill_sortarray(ctx, &sortset->sortarray, data,sortset->count); + } + + createquery->ccategorizationsetpresent = 0; + + createquery->rowsetproperties.ubooleanoptions = 0x00000203; + createquery->rowsetproperties.ulmaxopenrows = 0x00000000; + createquery->rowsetproperties.ulmemoryusage = 0x00000000; + createquery->rowsetproperties.cmaxresults = 0x00000000; + createquery->rowsetproperties.ccmdtimeout = 0x00000005; + + createquery->pidmapper.count = sql->cols->num_cols; + createquery->pidmapper.apropspec = talloc_zero_array(ctx, + struct wsp_cfullpropspec, + createquery->pidmapper.count); + for(i = 0; i < sql->cols->num_cols; i++) { + struct wsp_cfullpropspec *prop = + &createquery->pidmapper.apropspec[i]; + if (!set_fullpropspec(ctx, + prop, sql->cols->cols[i], + PRSPEC_PROPID)) { + /* #FIXME error handling */ + DBG_ERR("Failed to handle property named %s\n", + sql->cols->cols[i]); + continue; + } + } + createquery->lcid = 0x00001809; +} + +static uint32_t alignval(uint32_t num, int32_t align) { + num = num + (align - (num % align)) % align; + return num; +} + +static int32_t getNextAddress(int32_t value_off, + int32_t status_off, + int32_t len_off, + bool is_64bit) +{ + return MAX(MAX(value_off + get_value_size(is_64bit), status_off + 1), len_off + 2); +} + +static void create_binding_offsets(struct binding *binding, + int no_cols, + bool is_64bit) +{ + uint32_t buf_addr = 0x0; + uint32_t i; + + uint32_t value_off = 0; + uint32_t len_off = 0; + + /* initial state, seems weird but can't handle it any other way */ + uint32_t status_off = 0x1; /* this will get incremented to the desired 0x2 */ + uint32_t avail = 0x4; + int status_remain = 0x2; + int len_remain = -1; + + const static uint32_t WINDOW = 0x8; + const static uint32_t LEN_STAT_SIZE = 0x4; + for (i = 0; i < no_cols; i++) { + buf_addr = buf_addr + WINDOW; + value_off = buf_addr; + + if (status_remain <= 0) { + if (avail) { + status_off = avail; + status_remain = LEN_STAT_SIZE; + avail = 0; + } else { + /* + * we prepare the address to allocate + * another block from here. It will + * be allocated automatically when we + * re-enter the loop */ + status_off = getNextAddress(value_off, + status_off, + len_off, + is_64bit) + WINDOW; + status_remain = LEN_STAT_SIZE; + buf_addr = status_off; + avail = buf_addr + LEN_STAT_SIZE; + } + } else { + status_off++; + buf_addr = getNextAddress(value_off, + status_off, + len_off, + is_64bit); + } + + if (len_remain <= 0) { + if (avail) { + len_off = avail; + len_remain = LEN_STAT_SIZE; + avail = 0; + } else { + /* + * we prepare the address to allocate + * another block from here. It will + * be allocated automatically when we + * re-enter the loop */ + len_off = getNextAddress(value_off, + status_off, + len_off, + is_64bit) + WINDOW; + len_remain = LEN_STAT_SIZE; + buf_addr = len_off; + avail = buf_addr + LEN_STAT_SIZE; + } + } else { + len_off += 0x4; + buf_addr = getNextAddress(value_off, + status_off, + len_off, + is_64bit); + } + status_remain--; + len_remain -= LEN_STAT_SIZE; + binding[i].value_off = value_off; + binding[i].status_off = status_off; + binding[i].len_off = len_off; +#if 0 + printf("Col[%d]\n", i); + printf("\t value offset: 0x%x\n", value_off); + printf("\t status offset: 0x%x remain(%d)\n", status_off, status_remain); + printf("\t length offset: 0x%x remain(%d)\n", len_off, len_remain ); + printf("current buff addr 0x%x\n", buf_addr); +#endif + } +} + +static void fill_bindings(TALLOC_CTX *ctx, + struct wsp_cpmsetbindingsin *bindingsin, + char **col_names, + bool is_64bit) +{ + uint32_t i; + struct binding *offsets; + uint32_t num_cols; + struct wsp_ctablecolumn *tablecols = bindingsin->acolumns; + bindingsin->brow = 0x0; + num_cols = bindingsin->ccolumns; + + offsets = talloc_zero_array(ctx, struct binding, num_cols); + create_binding_offsets(offsets, num_cols, is_64bit); + for (i = 0; i < num_cols; i++) { + uint32_t max_off; + if (!set_ctablecolumn(ctx, &tablecols[i], col_names[i], + &offsets[i], is_64bit)) { + DBG_ERR("Failed to handle property named %s\n", + col_names[i]); + continue; + } + max_off = MAX(offsets[i].value_off + get_value_size(is_64bit), + offsets[i].status_off + 1); + max_off = MAX(max_off, offsets[i].len_off + 2); + if (max_off > bindingsin->brow) { + bindingsin->brow = max_off; + } + } + /* important */ + bindingsin->brow = alignval(bindingsin->brow,4); +} + +void create_setbindings_request(TALLOC_CTX * ctx, + struct wsp_request* request, + bool is_64bit, + t_select_stmt *sql, + uint32_t cursor) +{ + struct wsp_cpmsetbindingsin *bindingsin = &request->message.cpmsetbindings; + request->header.msg = CPMSETBINDINGSIN; + bindingsin->hcursor = cursor; + bindingsin->ccolumns = sql->cols->num_cols; + + bindingsin->acolumns = talloc_zero_array(ctx, struct wsp_ctablecolumn, + bindingsin->ccolumns); + fill_bindings(ctx, bindingsin, sql->cols->cols, is_64bit); + +} + +enum search_kind get_kind(const char* kind_str) +{ + enum search_kind result = Unknown; + int i; + const static struct { + const char* str; + enum search_kind search_kind; + } kind_map[] = { + {"Calendar", Calendar}, + {"Communication", Communication}, + {"Contact", Contact}, + {"Document", Document}, + {"Email", Email}, + {"Feed", Feed}, + {"Folder", Folder}, + {"Game", Game}, + {"InstantMessage", InstantMessage}, + {"Journal", Journal}, + {"Link", Link}, + {"Movie", Movie}, + {"Music", Music}, + {"Note", Note}, + {"Picture", Picture}, + {"Program", Program}, + {"RecordedTV", RecordedTV}, + {"SearchFolder", SearchFolder}, + {"Task", Task}, + {"Video", Video}, + {"WebHistory", WebHistory}, + }; + for (i = 0; i < ARRAY_SIZE(kind_map); i++) { + if (strequal(kind_str, kind_map[i].str)) { + result = kind_map[i].search_kind; + break; + } + } + return result; +} + +struct wsp_client_ctx +{ + struct dcerpc_pipe *p; + struct smbcli_state *cli; + struct smb2_tree *tree; +}; + +static NTSTATUS connect_server_smb(TALLOC_CTX *mem_ctx, + const char *host, + struct tevent_context *ev_ctx, + struct cli_credentials *credentials, + struct smbcli_state **cli) +{ + NTSTATUS status; + struct smbcli_options options; + struct smbcli_session_options session_options; + lpcfg_smbcli_options(cmdline_lp_ctx, &options); + + lpcfg_smbcli_session_options(cmdline_lp_ctx, &session_options); + + status = smbcli_full_connection(mem_ctx, + cli, + host, + lpcfg_smb_ports(cmdline_lp_ctx), + "IPC$", NULL, + lpcfg_socket_options(cmdline_lp_ctx), + credentials, + lpcfg_resolve_context(cmdline_lp_ctx), + ev_ctx, &options, &session_options, + lpcfg_gensec_settings(mem_ctx, + cmdline_lp_ctx)); + return status; +} + +static NTSTATUS connect_server_smb2(TALLOC_CTX *mem_ctx, + const char *host, + struct tevent_context *ev_ctx, + struct cli_credentials *credentials, + struct smb2_tree **tree) +{ + NTSTATUS status; + struct smbcli_options options; + struct smbcli_session_options session_options; + lpcfg_smbcli_options(cmdline_lp_ctx, &options); + + lpcfg_smbcli_session_options(cmdline_lp_ctx, &session_options); + + status = smb2_connect(mem_ctx, + host, + lpcfg_smb_ports(cmdline_lp_ctx), + "IPC$", + lpcfg_resolve_context(cmdline_lp_ctx), + credentials, + tree, + ev_ctx, + &options, + lpcfg_socket_options(cmdline_lp_ctx), + lpcfg_gensec_settings(mem_ctx, + cmdline_lp_ctx) + ); + return status; +} + +static NTSTATUS wait_for_pipe(TALLOC_CTX *mem_ctx, + struct tevent_context *ev_ctx, + bool smb2_or_greater, + struct wsp_client_ctx *ctx, + const char *pipe_name) +{ + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + + if (smb2_or_greater) { + struct smb2_tree *tree = ctx->tree; + req = + smb2cli_wait_pipe_send( + mem_ctx, + ev_ctx, + tree->session->transport->conn, + tree->session->transport->options.request_timeout, + tree->session->smbXcli, + tree->smbXcli, + 0, + pipe_name); + } else { + struct smbcli_state *cli = ctx->cli; + struct smbcli_tree *tree = cli->tree; + /* WHY isn't the tid set already here ? */ + smb1cli_tcon_set_id(tree->smbXcli, tree->tid); + req = smb1cli_wait_named_pipe_send(mem_ctx, ev_ctx, + tree->session->transport->conn, + cli->options.request_timeout, + tree->session->pid, + tree->session->smbXcli, + tree->smbXcli, + pipe_name); + } + if (req == NULL) { + return status; + } + + if (!tevent_req_poll_ntstatus(req, ev_ctx, &status)) { + goto done; + } + + if (smb2_or_greater) { + status = smb2cli_wait_pipe_recv(req); + } else { + status = smb1cli_wait_named_pipe_recv(req); + } +done: + return status; +} + +NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx, + const char *servername, + struct tevent_context *ev_ctx, + struct cli_credentials *credentials, + struct wsp_client_ctx **wsp_ctx) +{ + struct wsp_client_ctx *ctx = talloc_zero(mem_ctx, + struct wsp_client_ctx); + struct dcerpc_pipe *p; + struct dcerpc_binding_handle *h; + NTSTATUS status; + bool smb2_or_greater = + (lpcfg_client_max_protocol(cmdline_lp_ctx) >= PROTOCOL_SMB2_02); + if (smb2_or_greater) { + status = connect_server_smb2(mem_ctx, + servername, + ev_ctx, + credentials, + &ctx->tree); + } else { + status = connect_server_smb(mem_ctx, + servername, + ev_ctx, + credentials, + &ctx->cli); + } + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to server status: %s)\n", + nt_errstr(status)); + return status; + } + p = dcerpc_pipe_init(mem_ctx, ev_ctx); + + if (!p) { + DBG_ERR("failed to int the pipe)\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + status = wait_for_pipe(mem_ctx, + ev_ctx, + smb2_or_greater, + ctx, + "MsFteWds"); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("wait for pipe failed: %s)\n", + nt_errstr(status)); + return status; + } + if (smb2_or_greater) { + status = dcerpc_pipe_open_smb2(p, ctx->tree, "MsFteWds"); + } else { + status = dcerpc_pipe_open_smb(p, ctx->cli->tree, "MsFteWds"); + } + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to server status: %s)\n", + nt_errstr(status)); + return status; + } + + h = create_rawpipe_handle(p); + + if (!h) { + DBG_ERR("failed to create the pipe handle)\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + p->binding_handle = h; + ctx->p = p; + *wsp_ctx = ctx; + + return status; +} + +static NTSTATUS write_something(TALLOC_CTX* ctx, + struct dcerpc_pipe *p, + DATA_BLOB *blob_in, + DATA_BLOB *blob_out) +{ + uint32_t outflags; + struct tstream_context *stream; + struct dcerpc_binding_handle *handle = p->binding_handle; + + NTSTATUS status; + + stream = p->conn->transport.stream; + status = tstream_smbXcli_np_use_trans(stream); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to set trans mode on pipe status: %s)\n", + nt_errstr(status)); + return status; + } + + /* set large data */ + tstream_smbXcli_np_set_max_data(stream, 42800); + + status = dcerpc_binding_handle_raw_call(handle, + NULL, + 0, + 0, + blob_in->data, + blob_in->length, + ctx, + &blob_out->data, + &blob_out->length, + &outflags); + return status; +} + +static enum ndr_err_code parse_blob(TALLOC_CTX *ctx, DATA_BLOB *blob, + struct wsp_request *request, struct wsp_response *response, + DATA_BLOB *unread) +{ + struct ndr_pull *ndr = NULL; + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + uint32_t status = 0; + + ndr = ndr_pull_init_blob(blob, ctx); + + /* peek at the status */ + status = IVAL(blob->data, 4); + + /* is hard error ?*/ + if (status & 0x80000000 && blob->length == MSG_HDR_SIZE) { + /* just pull the header */ + err = ndr_pull_wsp_header(ndr, ndr_flags, &response->header); + DBG_ERR("error: %s\n", nt_errstr(NT_STATUS(status))); + goto out; + } + err = ndr_pull_wsp_response(ndr, ndr_flags, response); + if (err) { + DBG_ERR("Failed to pull header from response blob error %d\n", err); + goto out; + } + if (DEBUGLEVEL >=6) { + NDR_PRINT_DEBUG(wsp_response, response); + } + if (response->header.msg == CPMGETROWS) { + if (request) { + /* point to rows buffer */ + ndr->offset = request->message.cpmgetrows.cbreserved; + } + } + + if (ndr->offset < blob->length) { + int bytes = blob->length - ndr->offset; + *unread = data_blob_named(blob->data + ndr->offset, + bytes, "UNREAD"); + DBG_WARNING("\nThere are unprocessed bytes (len 0x%x) at end of message\n", bytes); + } + +out: + return err; +} + +static void set_msg_checksum(DATA_BLOB *blob, struct wsp_header *hdr) +{ + /* point at payload */ + uint32_t i; + uint8_t *buffer = blob->data + MSG_HDR_SIZE; + uint32_t buf_size = blob->length - MSG_HDR_SIZE; + uint32_t nwords = buf_size/4; + uint32_t offset = 0; + uint32_t checksum = 0; + + static const uint32_t xor_const = 0x59533959; + for(i = 0; i < nwords; i++) { + checksum += IVAL(buffer, offset); + offset += 4; + } + + checksum ^= xor_const; + checksum -= hdr->msg; + hdr->checksum = checksum; +} + +static enum ndr_err_code insert_header_and_checksum(TALLOC_CTX *ctx, DATA_BLOB* blob, + struct wsp_request *request) +{ + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + struct ndr_push *header_ndr = ndr_push_init_ctx(ctx); + + if (request->header.msg == CPMCONNECT + || request->header.msg == CPMCREATEQUERY + || request->header.msg == CPMSETBINDINGSIN + || request->header.msg == CPMGETROWS + || request->header.msg == CPMFETCHVALUE) { + + set_msg_checksum(blob, &request->header); + } + err = ndr_push_wsp_header(header_ndr, ndr_flags, &request->header); + if (err) { + DBG_ERR("Failed to push header, error %d\n", err); + return err; + } + memcpy(blob->data, header_ndr->data, MSG_HDR_SIZE); + return err; +} + +NTSTATUS wsp_request_response(TALLOC_CTX* ctx, + struct wsp_client_ctx *wsp_ctx, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *unread) +{ + struct dcerpc_pipe *p = wsp_ctx->p; + NTSTATUS status = NT_STATUS_OK; + + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + struct ndr_push* push_ndr; + enum ndr_err_code err; + + DATA_BLOB req_blob; + DATA_BLOB resp_blob; + + ZERO_STRUCT(req_blob); + ZERO_STRUCT(resp_blob); + + push_ndr = ndr_push_init_ctx(ctx); + + /* write message payload first */ + push_ndr->offset = MSG_HDR_SIZE; + DBG_INFO("\n"); + + switch(request->header.msg) { + case CPMCONNECT: + err = ndr_push_wsp_cpmconnectin(push_ndr, ndr_flags, + &request->message.cpmconnect); + break; + case CPMCREATEQUERY: + { + err = ndr_push_wsp_cpmcreatequeryin(push_ndr, ndr_flags, + &request->message.cpmcreatequery); + req_blob = ndr_push_blob(push_ndr); + /* we need to set cpmcreatequery.size */ + request->message.cpmcreatequery.size = req_blob.length - MSG_HDR_SIZE; + SIVAL(req_blob.data, MSG_HDR_SIZE, + request->message.cpmcreatequery.size); + + break; + } + case CPMSETBINDINGSIN: + err = ndr_push_wsp_cpmsetbindingsin(push_ndr, ndr_flags, + &request->message.cpmsetbindings); + req_blob = ndr_push_blob(push_ndr); + /* we need to set cpmsetbindings.bbindingdesc (size) */ + request->message.cpmsetbindings.bbindingdesc = + req_blob.length - MSG_HDR_SIZE - 16; + SIVAL(req_blob.data, MSG_HDR_SIZE + 8, + request->message.cpmsetbindings.bbindingdesc); + break; + case CPMGETROWS: + err = ndr_push_wsp_cpmgetrowsin(push_ndr, ndr_flags, + &request->message.cpmgetrows); + req_blob = ndr_push_blob(push_ndr); + request->message.cpmgetrows.cbseek = req_blob.length - MSG_HDR_SIZE - 32; + /* we need to set cpmgetrowsin.cbseek (size) */ + SIVAL(req_blob.data, MSG_HDR_SIZE + 12, + request->message.cpmgetrows.cbseek); + SIVAL(req_blob.data, MSG_HDR_SIZE + 16, + request->message.cpmgetrows.cbreserved); + break; + case CPMGETQUERYSTATUS: + err = ndr_push_wsp_cpmgetquerystatusin(push_ndr, ndr_flags, + &request->message.cpmgetquerystatus); + break; + case CPMGETQUERYSTATUSEX: + err = ndr_push_wsp_cpmgetquerystatusexin(push_ndr, ndr_flags, + &request->message.cpmgetquerystatusex); + break; + case CPMFREECURSOR: + err = ndr_push_wsp_cpmfreecursorin(push_ndr, ndr_flags, + &request->message.cpmfreecursor); + break; +/* + case CPMFREECURSOR: + status = push_wsp_cpmfreecursorin(buffer, request, + &offset); + break; + case CPMDISCONNECT: + push_wsp_cpmdisconnect(buffer, request, &offset); + break; +*/ + default: + status = NT_STATUS_MESSAGE_NOT_FOUND; + goto out; + break; + } + if (err) { + DBG_ERR("failed to serialise message! (%d)\n", err); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + if (!req_blob.data) { + req_blob = ndr_push_blob(push_ndr); + } + err = insert_header_and_checksum(ctx, &req_blob, request); + + DBG_NOTICE("\nsending raw message from client len %d\n", (int)req_blob.length); + DBG_NOTICE("\nsending raw message from client\n"); + DBG_NOTICE( "===============================\n"); + + dump_data(5, req_blob.data, req_blob.length); + + status = write_something(ctx, p, &req_blob, &resp_blob); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to write message\n"); + goto out; + } + DBG_NOTICE("\nraw response from server\n"); + DBG_NOTICE( "========================\n"); + dump_data(5, resp_blob.data, resp_blob.length); + + err = parse_blob(ctx, &resp_blob, request, response, unread); + if (err) { + DBG_ERR("Failed to parse response error %d\n", err); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + DBG_NOTICE("response status is 0x%x\n", response->header.status); + /* propagate error status to return status */ + if (response->header.status & 0x80000000) { + status = NT_STATUS_UNSUCCESSFUL; + } +out: + return status; +} + +/* + * tmp accessors, hoping we can remove the need for clients to know about + */ + +struct dcerpc_pipe * get_wsp_pipe(struct wsp_client_ctx *ctx) +{ + return ctx->p; +} + +struct smbcli_state * get_wsp_clistate(struct wsp_client_ctx *ctx) +{ + return ctx->cli; +} diff --git a/source4/libcli/wsp/wsp_cli.h b/source4/libcli/wsp/wsp_cli.h new file mode 100644 index 0000000..496af64 --- /dev/null +++ b/source4/libcli/wsp/wsp_cli.h @@ -0,0 +1,110 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ +#ifndef __LIBCLI_WSP_WSP_CLI +#define __LIBCLI_WSP_WSP_CLI + +#include "libcli/wsp/wsp_aqs.h" + +enum search_kind { + Calendar, + Communication, + Contact, + Document, + Email, + Feed, + Folder, + Game, + InstantMessage, + Journal, + Link, + Movie, + Music, + Note, + Picture, + Program, + RecordedTV, + SearchFolder, + Task, + Video, + WebHistory, + None, + Unknown, +}; + +enum search_kind get_kind(const char* kind_str); + +void init_connectin_request(TALLOC_CTX *ctx, + struct wsp_request* request, + const char* clientmachine, + const char* clientuser, + const char* server); + +void create_querysearch_request(TALLOC_CTX * ctx, + struct wsp_request* request, + t_select_stmt *sql); + +void create_setbindings_request(TALLOC_CTX * ctx, + struct wsp_request* request, + bool is_64bit, + t_select_stmt *sql, + uint32_t cursor); + +void create_seekat_getrows_request(TALLOC_CTX * ctx, + struct wsp_request* request, + bool is_64bit, + uint32_t cursor, + uint32_t bookmark, + uint32_t skip, + uint32_t rows, + uint32_t cbreserved, + uint64_t client_buf_addr, + uint32_t cbrowwidth, + uint32_t fbwdfetch); + +enum ndr_err_code extract_rowsarray(TALLOC_CTX * ctx, + DATA_BLOB *rows_buf, + bool is_64bit, + struct wsp_cpmsetbindingsin *bindingsin, + uint32_t cbreserved, + uint64_t client_buf_addr, + uint32_t rows, + struct wsp_cbasestoragevariant **rowsarray); + +struct wsp_client_ctx; +struct cli_credentials; + +NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx, + const char *servername, + struct tevent_context *ev_ctx, + struct cli_credentials *credential, + struct wsp_client_ctx **ctx); + +/* simple sync api */ +NTSTATUS wsp_request_response(TALLOC_CTX* ctx, + struct wsp_client_ctx *wsp_ctx, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *unread); + +/* tmp accessors */ +struct dcerpc_pipe * get_wsp_pipe(struct wsp_client_ctx *ctx); +struct smbcli_state * get_wsp_clistate(struct wsp_client_ctx *ctx); +#endif -- 2.10.2 >From 18be27a006cf9ce87c3d39e9c980ef71ce950c8d Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 21 Jul 2016 16:53:17 +0100 Subject: [PATCH 10/21] s4/utils: Add search client Simple cli client for doing a basic windows search. example: wspsearch -U$(USER)%$(PASSWD) //$(SERVER)/$(SHARE) --search='DSC' --kind=Picture Signed-off-by: Noel Power --- source4/utils/wscript_build | 6 + source4/utils/wspsearch.c | 732 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 738 insertions(+) create mode 100644 source4/utils/wspsearch.c diff --git a/source4/utils/wscript_build b/source4/utils/wscript_build index 046e237..193864c 100644 --- a/source4/utils/wscript_build +++ b/source4/utils/wscript_build @@ -17,3 +17,9 @@ bld.SAMBA_BINARY('oLschema2ldif', deps='samdb POPT_SAMBA' ) +bld.SAMBA_BINARY('wspsearch', + source='wspsearch.c', + deps='popt POPT_SAMBA POPT_CREDENTIALS dcerpc LIBSAMBA_WSP LIBCLI_SMB SMBREADLINE NDR_WSP NDR_WSP_DATA ', + enabled=bld.env.with_wsp + ) + diff --git a/source4/utils/wspsearch.c b/source4/utils/wspsearch.c new file mode 100644 index 0000000..ee0617d --- /dev/null +++ b/source4/utils/wspsearch.c @@ -0,0 +1,732 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ +#include "includes.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp_data.h" +#include "librpc/rpc/wsp_helper.h" +#include "libcli/wsp/wsp_cli.h" +#include "lib/cmdline/popt_common.h" +#include "lib/events/events.h" +#include "auth/gensec/gensec.h" +#include "util/tevent_ntstatus.h" +#include "util/debug.h" +#include "dcerpc.h" +#include "credentials.h" +#include "param/param.h" +#include "libcli/wsp/wsp_aqs.h" + +#define CLIENT_BUF_ADDR 0xDEAbd860 + +#if DEVELOPER +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; + 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 void print_basic_query(struct wsp_crestriction *restriction) +{ + TALLOC_CTX *ctx = talloc_init("print_basic_query"); + 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); + printf("%s %s %s", prop_name, op_str ? op_str : "", val_str); + TALLOC_FREE(ctx); +} + +static void print_node(t_query *node, bool is_rpn) +{ + switch(node->type) { + case eAND: + case eOR: + case eNOT: + printf(" %s ", nodetype_as_string(node->type)); + break; + case eVALUE: + default: + print_basic_query(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 + * 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 void infix(t_query *tree) +{ + if (tree == NULL) { + return; + } + if (is_operator_node(tree)) { + printf("("); + } + infix(tree->left); + print_node(tree, false); + infix(tree->right); + if (is_operator_node(tree)) { + printf(")"); + } +} + +static void dump_cols(t_select_stmt *select) +{ + t_col_list *cols = select->cols; + if (cols) { + int i; + for (i = 0; i < cols->num_cols; i++) { + if (i == 0) { + printf("%s", cols->cols[i]); + } else { + printf(", %s", cols->cols[i]); + } + } + } +} + +static void dump_sql(t_select_stmt *select) +{ + if (!select) { + return; + } + printf("selecting the following columns\n"); + dump_cols(select); + printf("\n"); + printf("parsed query:\n"); + infix(select->where); + printf("\n"); +} + +static int test_query(TALLOC_CTX *ctx, t_select_stmt *select) +{ + struct wsp_request request; + struct wsp_crestrictionarray *restrictionset = NULL; + const char *restrictionset_expr = NULL; + NTSTATUS status; + ZERO_STRUCT(request); + dump_sql(select); + create_querysearch_request(ctx, &request, select); + + restrictionset = + &request.message.cpmcreatequery.restrictionarray.restrictionarray; + + restrictionset_expr = + raw_restriction_to_string(ctx, + &restrictionset->restrictions[0]); + if (restrictionset_expr == NULL) { + status = NT_STATUS_UNSUCCESSFUL; + } else { + status = NT_STATUS_OK; + } + if (NT_STATUS_IS_OK(status)) { + printf("restriction expression =>%s<=\n", restrictionset_expr); + } else { + printf("error status %s\n", + nt_errstr(status)); + } + return 0; +} +#endif + +/* send connectin message */ +static NTSTATUS connect(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + const char* clientmachine, + const char* clientuser, + const char* server, + bool *is_64bit) +{ + struct wsp_request *request; + struct wsp_response *response; + uint32_t client_ver; + uint32_t server_ver; + DATA_BLOB unread; + NTSTATUS status; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + ZERO_STRUCT(unread); + + request = talloc_zero(local_ctx, struct wsp_request); + response = talloc_zero(local_ctx, struct wsp_response); + + init_connectin_request(local_ctx, request, + clientmachine, clientuser, server); + + status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); + if (NT_STATUS_IS_OK(status)) { + client_ver = request->message.cpmconnect.iclientversion; + server_ver = response->message.cpmconnect.server_version; + *is_64bit = (server_ver & 0xffff0000) && (client_ver & 0xffff0000); + } + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + +static NTSTATUS create_query(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + t_select_stmt *select, + uint32_t *single_cursor) +{ + struct wsp_request *request; + struct wsp_response *response; + NTSTATUS status; + DATA_BLOB unread; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + ZERO_STRUCT(unread); + request = talloc_zero(local_ctx, struct wsp_request); + response = talloc_zero(local_ctx, struct wsp_response); + + create_querysearch_request(ctx, request, select); + status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); + if (NT_STATUS_IS_OK(status)) { + if (unread.length == 4) { + *single_cursor = IVAL(unread.data, 0); + } + } + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + +static NTSTATUS create_bindings(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + bool is_64bit, + t_select_stmt *select, + uint32_t cursor, + struct wsp_cpmsetbindingsin *bindings_out) +{ + struct wsp_request *request; + struct wsp_response *response; + NTSTATUS status; + DATA_BLOB unread; + + ZERO_STRUCT(unread); + + request = talloc_zero(ctx, struct wsp_request); + response = talloc_zero(ctx, struct wsp_response); + create_setbindings_request(ctx, request, is_64bit, select, cursor); + status = wsp_request_response(ctx, wsp_ctx, request, response, &unread); + if (NT_STATUS_IS_OK(status)) { + *bindings_out = request->message.cpmsetbindings; + } + data_blob_free(&unread); + return status; +} + +static NTSTATUS create_querystatusex(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + uint32_t cursor, + uint32_t *nrows) +{ + struct wsp_request *request; + struct wsp_response *response; + NTSTATUS status; + DATA_BLOB unread; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + ZERO_STRUCT(unread); + + request = talloc_zero(local_ctx, struct wsp_request); + response = talloc_zero(local_ctx, struct wsp_response); + request->header.msg = CPMGETQUERYSTATUSEX; + request->message.cpmgetquerystatusex.hcursor = cursor; + request->message.cpmgetquerystatusex.bmk = 0xfffffffc; + status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); + if (NT_STATUS_IS_OK(status)) { + *nrows = response->message.cpmgetquerystatusex.resultsfound;; + } + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + + + +static NTSTATUS print_rowsreturned( + TALLOC_CTX *ctx, + DATA_BLOB *buffer, + bool is_64bit, + bool disp_all_cols, + struct wsp_cpmsetbindingsin *bindings, + uint32_t cbreserved, + uint64_t address, + uint32_t rowsreturned, + uint32_t *rows_processed) +{ + NTSTATUS status; + int row = 0; + TALLOC_CTX *local_ctx = talloc_init("results"); + struct wsp_cbasestoragevariant **rowsarray = + talloc_zero_array(local_ctx, + struct wsp_cbasestoragevariant*, + rowsreturned); + + enum ndr_err_code err = extract_rowsarray(rowsarray, + buffer, + is_64bit, + bindings, + cbreserved, + address, + rowsreturned, + rowsarray); + + if (err) { + DBG_ERR("failed to extract rows from getrows response\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + for(; row < rowsreturned; row++) { + TALLOC_CTX *row_ctx = talloc_init("row"); + const char *col_str = NULL; + if (disp_all_cols) { + int i; + for (i = 0; i < bindings->ccolumns; i++){ + col_str = + variant_as_string( + row_ctx, + &rowsarray[row][i], + true); + if (col_str) { + printf("%s%s", + i ? ", " : "", col_str); + } else { + printf("%sN/A", + i ? ", " : ""); + } + } + } else { + col_str = variant_as_string( + row_ctx, + &rowsarray[row][0], + true); + printf("%s", col_str); + } + printf("\n"); + TALLOC_FREE(row_ctx); + } + TALLOC_FREE(local_ctx); + status = NT_STATUS_OK; +out: + *rows_processed = row; + return status; +} + +static NTSTATUS create_getrows(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + struct wsp_cpmsetbindingsin *bindings, + uint32_t cursor, + uint32_t nrows, + bool disp_all_cols, + bool is_64bit) +{ + struct wsp_request *request; + struct wsp_response *response; + NTSTATUS status; + DATA_BLOB unread; + uint32_t bmk = 0; + uint32_t skip = 0; + uint32_t requested_rows = 0; + uint32_t total_rows = 0; + uint32_t INITIAL_ROWS = 32; + uint32_t rows_printed; + uint32_t current_row = 0; + TALLOC_CTX *row_ctx; + ZERO_STRUCT(unread); + + while (total_rows != nrows) { + row_ctx = talloc_new(ctx); + if (!row_ctx) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + request = talloc_zero(row_ctx, struct wsp_request); + response = talloc_zero(request, struct wsp_response); + if (requested_rows == 0) { + uint32_t remaining_rows = nrows - total_rows; + if ( remaining_rows < INITIAL_ROWS) { + requested_rows = remaining_rows; + } else { + requested_rows = INITIAL_ROWS; + } + bmk = 0xfffffffc; + skip = total_rows; + } + + create_seekat_getrows_request(request, + request, + is_64bit, + cursor, + bmk, + skip, + requested_rows, + 40, + CLIENT_BUF_ADDR, + bindings->brow, + 0); + + status = wsp_request_response(request, wsp_ctx, request, response, &unread); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + total_rows += response->message.cpmgetrows.rowsreturned; + if (response->message.cpmgetrows.rowsreturned + != requested_rows) { + if (response->message.cpmgetrows.etype == EROWSEEKAT) { + struct wsp_cpmgetrowsout *resp; + struct wsp_crowseekat *seekat; + resp = &response->message.cpmgetrows; + seekat = + &resp->seekdescription.crowseekat; + bmk = seekat->bmkoffset; + skip = seekat->cskip; + requested_rows = + requested_rows - response->message.cpmgetrows.rowsreturned; + } + } else { + requested_rows = 0; + } + status = print_rowsreturned(request, &unread, + is_64bit, + disp_all_cols, + bindings, 40, + CLIENT_BUF_ADDR, + response->message.cpmgetrows.rowsreturned, + &rows_printed); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + current_row += rows_printed; + data_blob_free(&unread); + TALLOC_FREE(row_ctx); + } +out: + TALLOC_FREE(row_ctx); + return status; +} + + +const char *default_column = "System.ItemUrl"; + +static char * build_default_sql(TALLOC_CTX *ctx, + const char *kind, + const char *phrase, + const char *location) +{ + char *sql = NULL; + + sql = talloc_asprintf(ctx, + "Scope:\"%s\" AND NOT System.Shell.SFGAOFlagsStrings:hidden" + " AND NOT System.Shell.OmitFromView:true", location); + + if (kind) { + sql = talloc_asprintf(ctx, "System.Kind:%s AND %s", + kind, sql); + } + + if (phrase) { + sql = talloc_asprintf(ctx, + "All:$=\"%s\" OR All:$<\"%s\"" + " AND %s", phrase, phrase, sql); + } + sql = talloc_asprintf(ctx, "SELECT %s" + " WHERE %s", default_column, sql); + return sql; +} + +int main(int argc, const char *argv[]) +{ + int opt; + int result = 0; + NTSTATUS status = NT_STATUS_OK; + poptContext pc; + char* server = NULL; + char* share = NULL; + char* path = NULL; + char* location = NULL; + char* query = NULL; + bool custom_query = false; + const char* phrase = NULL; + const char* kind = NULL; + uint32_t limit = 500; + uint32_t nrows = 0; + struct wsp_cpmsetbindingsin bindings_used; + bool is_64bit = false; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "limit", 0, POPT_ARG_INT, &limit, 0, "limit results", "default is 500, specifying 0 means unlimited" }, + { "search", 0, POPT_ARG_STRING, &phrase, 0, "Search phrase", "phrase" }, + { "kind", 0, POPT_ARG_STRING, &kind, 0, "Kind of thing to search for [Calendar|Communication|Contact|Document|Email|Feed|Folder|Game|InstantMessage|Journal|Link|Movie|Music|Note|Picture|Program|RecordedTV|SearchFolder|Task|Video|WebHistory]", "kind" }, + { "query", 0, POPT_ARG_STRING, &query, 0, "specify a more complex query", "query" }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev_ctx + = s4_event_context_init(talloc_tos()); + struct dcerpc_pipe *p; + uint32_t cursor; + struct wsp_client_ctx *wsp_ctx; + t_select_stmt *select_stmt; + + gensec_init(); + + pc = poptGetContext("wspsearch", argc, argv, long_options, 0); + poptSetOtherOptionHelp(pc, "//server1/share1"); + + while ((opt = poptGetNextOpt(pc)) != -1) ; + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + result = -1; + goto out; + } + + path = talloc_strdup(talloc_tos(), poptGetArg(pc)); + if (!path || limit < 0) { + DBG_ERR("Invalid argument\n"); + result = -1; + goto out; + } + + string_replace(path,'/','\\'); + server = talloc_strdup(talloc_tos(), path+2); + if (!server) { + DBG_ERR("Invalid argument\n"); + return -1; + } + + if (server) { + /* + * if we specify --query then we don't need actually need the + * share part, if it is specified then we don't care as we + * expect the scope to be part of the query (and if it isn't + * then it will probably fail anyway) + */ + share = strchr_m(server,'\\'); + if (!query && !share) { + DBG_ERR("Invalid argument\n"); + return -1; + } + if (share) { + *share = 0; + share++; + } + } + + + DBG_INFO("server name is %s\n", server ? server : "N/A"); + DBG_INFO("share name is %s\n", share ? share : "N/A"); + DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A"); + DBG_INFO("search kind is %s\n", kind ? kind : "N/A"); + + if (!query && (kind == NULL && phrase == NULL)) { + poptPrintUsage(pc, stderr, 0); + result = -1; + goto out; + } + + if (!query) { + location = talloc_asprintf(talloc_tos(), + "FILE://%s/%s", server, share); + query = build_default_sql(talloc_tos(), kind, phrase, location); + if (!query) { + result = -1; + goto out; + } + } else { + custom_query = true; + } + + select_stmt = get_wsp_sql_tree(query); + + poptFreeContext(pc); + + if (select_stmt == NULL) { + printf("query failed\n"); + result = -1; + goto out; + } + + if (select_stmt->cols == NULL) { + select_stmt->cols = talloc_zero(select_stmt, t_col_list); + select_stmt->cols->num_cols = 1; + select_stmt->cols->cols = + talloc_zero_array(select_stmt->cols, char*, 1); + select_stmt->cols->cols[0] = + talloc_strdup(select_stmt->cols, default_column); + } + +#if DEVELOPER + if (query) { + result = test_query(talloc_tos(), select_stmt); + } +#endif + status = wsp_server_connect(talloc_tos(), + server, + ev_ctx, + cmdline_credentials, + &wsp_ctx); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to wsp service status: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + + p = get_wsp_pipe(wsp_ctx); + dcerpc_binding_handle_set_timeout(p->binding_handle, + DCERPC_REQUEST_TIMEOUT * 1000); + + /* connect */ + DBG_INFO("sending connect\n"); + status = connect(talloc_tos(), + wsp_ctx, + lpcfg_netbios_name(cmdline_lp_ctx), + cli_credentials_get_username(cmdline_credentials), + server, + &is_64bit); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to wsp: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + + DBG_INFO("sending query\n"); + + status = create_query(talloc_tos(), wsp_ctx, select_stmt, &cursor); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to send query: %s)\n", + nt_errstr(status)); + result = -1; + goto out; + } + + DBG_INFO("sending createbindings\n"); + /* set bindings */ + status = create_bindings(talloc_tos(), wsp_ctx, is_64bit, select_stmt, cursor, &bindings_used); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to setbindings: %s)\n", + nt_errstr(status)); + result = -1; + goto out; + } + + status = create_querystatusex(talloc_tos(), + wsp_ctx, + bindings_used.hcursor, + &nrows); + if (!nrows) { + result = 0; + printf("no results found\n"); + goto out; + } + + printf("found %d results, returning %d \n", nrows, limit ? MIN(nrows, limit) : nrows); + status = create_getrows(talloc_tos(), + wsp_ctx, + &bindings_used, + bindings_used.hcursor, + limit ? MIN(nrows, limit) : nrows, + custom_query, + is_64bit); + result = 0; +out: + TALLOC_FREE(frame); + return result; +} -- 2.10.2 >From 34cde0071fe6d37279bcd90d76f1ce5a1d286fe7 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 28 Aug 2014 17:54:52 +0100 Subject: [PATCH 11/21] s3/smbd: Ensure WaitNamedPipe for MsFteWds succeeds (smb1) Signed-off-by: Noel Power --- source3/smbd/ipc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index f1c8ea0..ab21ee2 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -553,6 +553,7 @@ static void named_pipe(connection_struct *conn, uint64_t vuid, strequal(name,"SRVSVC") || strequal(name,"WINREG") || strequal(name,"SAMR") || + strequal(name, "MsFteWds") || strequal(name,"LSARPC")) { DEBUG(4,("named pipe command from Win95 (wow!)\n")); -- 2.10.2 >From 68efef699a5f4a0365d1ec94d163886c463a2a78 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Mon, 1 Sep 2014 15:27:36 +0100 Subject: [PATCH 12/21] s3/smbd: Handle smb2 FSCTL_PIPE_WAIT message Signed-off-by: Noel Power --- source3/smbd/smb2_ioctl_named_pipe.c | 71 +++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/source3/smbd/smb2_ioctl_named_pipe.c b/source3/smbd/smb2_ioctl_named_pipe.c index 13c4982..5373963 100644 --- a/source3/smbd/smb2_ioctl_named_pipe.c +++ b/source3/smbd/smb2_ioctl_named_pipe.c @@ -30,6 +30,69 @@ static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq); static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq); +struct pipe_wait_req_data +{ + int64_t timeout; + uint32_t name_len; + uint8_t timeout_specified; + char* pipe_name; +}; + +static bool read_pipe_wait_request_data(TALLOC_CTX * ctx, + struct pipe_wait_req_data *wait_request, + DATA_BLOB *input) +{ + uint8_t *buf = input->data; + uint32_t pos = 0; + /* lenght of data up to pipe name */ + uint32_t min_len = 14; + bool result = false; + + if (input->length < min_len) { + result = false; + goto out; + } + + wait_request->timeout = BVALS(buf, pos); + pos += 8; + + wait_request->name_len = IVAL(buf, pos); + pos +=4; + + if (min_len + wait_request->name_len > input->length) { + DEBUG(0,("incorrect buffer len, buffer size is %zu but we require %d\n", input->length, min_len + wait_request->name_len)); + result =false; + goto out; + } + + wait_request->timeout_specified = *(buf + pos); + pos++; + + pos++; /* skip padding */ + + wait_request->pipe_name = talloc_array(ctx, char, wait_request->name_len/2 + 1); + pull_string(wait_request->pipe_name, (buf + pos), + wait_request->name_len/2 + 1, wait_request->name_len, + STR_UNICODE | STR_NOALIGN); + result = true; +out: + return result; +} + +static bool can_handle_wait(DATA_BLOB *input) +{ + TALLOC_CTX * ctx = talloc_init(NULL); + struct pipe_wait_req_data req_data; + ZERO_STRUCT(req_data); + bool result = false; + + if (read_pipe_wait_request_data(ctx, &req_data, input)) { + result = strequal(req_data.pipe_name, "MsFteWds"); + } + TALLOC_FREE(ctx); + return result; +} + struct tevent_req *smb2_ioctl_named_pipe(uint32_t ctl_code, struct tevent_context *ev, struct tevent_req *req, @@ -39,7 +102,13 @@ struct tevent_req *smb2_ioctl_named_pipe(uint32_t ctl_code, uint8_t *out_data = NULL; uint32_t out_data_len = 0; - if (ctl_code == FSCTL_PIPE_TRANSCEIVE) { + if (ctl_code == FSCTL_PIPE_WAIT) { + if (can_handle_wait(&state->in_input)) { + DEBUG(0,("should be returning STATUS_OK for PIPE_WAIT\n")); + tevent_req_done(req); + return tevent_req_post(req, ev); + } + } else if (ctl_code == FSCTL_PIPE_TRANSCEIVE) { struct tevent_req *subreq; if (!IS_IPC(state->smbreq->conn)) { -- 2.10.2 >From 8d4a2c1a94cfb238f386eb197fc05f00bab66614 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Feb 2016 10:42:52 +0100 Subject: [PATCH 13/21] s3/build: seperate out check for Gnome Tracker from Spotlight Signed-off-by: Ralph Boehme --- source3/wscript | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/source3/wscript b/source3/wscript index ca8102f..ea001d9 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1643,6 +1643,18 @@ main() { conf.fatal('AFS headers not available, but --with-fake-kaserver was specified') conf.env['libtracker']='' + tracker_versions = ['1.0', '0.16', '0.14'] + + for version in tracker_versions: + testlib = 'tracker-sparql-' + version + if conf.CHECK_CFG(package=testlib, + args='--cflags --libs', + mandatory=False): + conf.SET_TARGET_TYPE(testlib, 'SYSLIB') + conf.env['libtracker'] = testlib + conf.DEFINE('HAVE_TRACKER', '1') + break + conf.env.with_spotlight = False conf.env.with_wsp = False if Options.options.with_wsp: @@ -1650,21 +1662,12 @@ main() { conf.DEFINE('WITH_WSP', '1') conf.env.with_wsp = True if Options.options.with_spotlight: - versions = ['1.0', '0.16', '0.14'] - for version in versions: - testlib = 'tracker-sparql-' + version - if conf.CHECK_CFG(package=testlib, - args='--cflags --libs', - mandatory=False): - conf.SET_TARGET_TYPE(testlib, 'SYSLIB') - conf.env['libtracker'] = testlib - conf.env.with_spotlight = True - conf.DEFINE('WITH_SPOTLIGHT', '1') - break - - if not conf.env.with_spotlight: - conf.fatal("Spotlight support requested but tracker-sparql library missing") + if not conf.CONFIG_SET('HAVE_TRACKER'): + conf.fatal('Missing Gnome Tracker development files') + Logs.info("building with Spotlight support") + conf.DEFINE('WITH_SPOTLIGHT', '1') + conf.env.with_spotlight = True default_static_modules.extend(TO_LIST('rpc_mdssvc_module')) forced_static_modules.extend(TO_LIST('auth_domain auth_builtin auth_sam auth_winbind')) -- 2.10.2 >From a4de9aa6fdc3c9748c82f927bb381a09d32342a0 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 20 Jan 2016 15:08:31 +0100 Subject: [PATCH 14/21] s3/lib: new tevent_glib_glue subsystem tevent_glib_glue_create() takes glib GMainContext and adds its event sources to a tevent context. tevent will poll the sources and run handlers for pending events as detailed in the glib documentation: https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html If Samba was built without glib support, the function will always return NULL with an error number ENOSYS. Signed-off-by: Ralph Boehme --- source3/lib/tevent_glib_glue.c | 694 +++++++++++++++++++++++++++++++++++++++++ source3/lib/tevent_glib_glue.h | 70 +++++ source3/wscript | 17 + source3/wscript_build | 6 + 4 files changed, 787 insertions(+) create mode 100644 source3/lib/tevent_glib_glue.c create mode 100644 source3/lib/tevent_glib_glue.h diff --git a/source3/lib/tevent_glib_glue.c b/source3/lib/tevent_glib_glue.c new file mode 100644 index 0000000..3e2ed67 --- /dev/null +++ b/source3/lib/tevent_glib_glue.c @@ -0,0 +1,694 @@ +/* + Unix SMB/CIFS implementation. + Integration of a glib g_main_context into a tevent_context + Copyright (C) Stefan Metzmacher 2016 + Copyright (C) Ralph Boehme 2016 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include "replace.h" +#include "tevent_glib_glue.h" +#include "system/filesys.h" +#include "system/select.h" +#include "lib/util/debug.h" +#include + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_TEVENT + +#ifdef HAVE_GLIB + +#include + +struct tevent_fd_map { + int fd; + struct tevent_fd *fd_event; +}; + +struct tevent_glib_glue { + struct tevent_context *ev; + GMainContext *gmain_ctx; + bool quit; + + struct tevent_timer *retry_timer; + gint gtimeout; + gint gpriority; + GPollFD *gpollfds; + gint num_gpollfds; + GPollFD *prev_gpollfds; + gint num_prev_gpollfds; + + struct tevent_fd_map *fd_map; + size_t num_maps; + struct tevent_timer *timer; + struct tevent_immediate *im; + bool scheduled_im; + struct pollfd *pollfds; +}; + +static bool tevent_glib_prepare(struct tevent_glib_glue *glue); +static bool tevent_glib_process(struct tevent_glib_glue *glue); +static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue); +static void tevent_glib_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2); +typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue, + const GPollFD *new, const GPollFD *old); +typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue, const GPollFD *fd); +typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue, const GPollFD *fd); + +/** + * Compare two GPollFD arrays + * + * For every element that exists in gfds and prev_gfds found_fn() is called. + * For every element in gfds but not in prev_gfds, new_fn() is called. + * For every element in prev_gfds but not in gfds removed_fn() is called. + **/ +static bool cmp_gfds(struct tevent_glib_glue *glue, + GPollFD *gfds, GPollFD *prev_gfds, + size_t num_gfds, size_t num_prev_gfds, + gfds_cmp_cb cmp_cb, + gfds_found_cb found_cb, + gfds_new_cb new_cb, + gfds_removed_cb removed_cb) +{ + bool ok; + size_t i = 0, j = 0; + int cmp; + + while (i < num_gfds && j < num_prev_gfds) { + cmp = cmp_cb(&gfds[i], &prev_gfds[j]); + if (cmp == 0) { + ok = found_cb(glue, &gfds[i], &prev_gfds[j]); + if (!ok) { + return false; + } + i++; + j++; + } else if (cmp < 0) { + ok = new_cb(glue, &gfds[i]); + if (!ok) { + return false; + } + i++; + } else { + ok = removed_cb(glue, &prev_gfds[j]); + if (!ok) { + return false; + } + j++; + } + } + + while (i < num_gfds) { + ok = new_cb(glue, &gfds[i++]); + if (!ok) { + return false; + } + } + + while (j < num_prev_gfds) { + ok = removed_cb(glue, &prev_gfds[j++]); + if (!ok) { + return false; + } + } + + return true; +} + +static int glib_fd_cmp_func(const void *p1, const void *p2) +{ + const GPollFD *lhs = p1; + const GPollFD *rhs = p2; + + if (lhs->fd < rhs->fd) { + return -1; + } else if (lhs->fd > rhs->fd) { + return 1; + } + + return 0; +} + +static bool match_gfd_cb(struct tevent_glib_glue *glue, + const GPollFD *new_gfd, + const GPollFD *old_gfd) +{ + size_t i; + struct tevent_fd *fd_event = NULL; + + if (new_gfd->events == old_gfd->events) { + return true; + } + + for (i = 0; i < glue->num_maps; i++) { + if (glue->fd_map[i].fd == new_gfd->fd) { + break; + } + } + + if (i == glue->num_maps) { + DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd); + return false; + } + + fd_event = glue->fd_map[i].fd_event; + if (fd_event == NULL) { + DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd); + return false; + } + + tevent_fd_set_flags(fd_event, 0); + + if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { + TEVENT_FD_READABLE(fd_event); + } + if (new_gfd->events & G_IO_OUT) { + TEVENT_FD_WRITEABLE(fd_event); + } + + return true; +} + +static bool add_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd) +{ + struct tevent_fd *fd_event = NULL; + uint16_t events; + + events = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) ? + TEVENT_FD_READ : 0; + events |= (gfd->events & G_IO_OUT) ? TEVENT_FD_WRITE : 0; + + fd_event = tevent_add_fd(glue->ev, glue->fd_map, + gfd->fd, + events, + tevent_glib_fd_handler, + glue); + if (fd_event == NULL) { + DBG_ERR("tevent_add_fd failed\n"); + return false; + } + + glue->fd_map = talloc_realloc(glue, glue->fd_map, + struct tevent_fd_map, + glue->num_maps + 1); + if (glue->fd_map == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + + glue->fd_map[glue->num_maps].fd = gfd->fd; + glue->fd_map[glue->num_maps].fd_event = fd_event; + glue->num_maps++; + + DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd); + + return true; +} + +static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd) +{ + size_t i; + + for (i = 0; i < glue->num_maps; i++) { + if (glue->fd_map[i].fd == gfd->fd) { + break; + } + } + + if (i == glue->num_maps) { + DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd); + return false; + } + + TALLOC_FREE(glue->fd_map[i].fd_event); + + if (i + 1 < glue->num_maps) { + memmove(&glue->fd_map[i], &glue->fd_map[i+1], + (glue->num_maps - (i + 1)) * sizeof(struct tevent_fd_map)); + } + + glue->num_maps--; + + glue->fd_map = talloc_realloc(glue, glue->fd_map, + struct tevent_fd_map, + glue->num_maps); + if (glue->num_maps > 0 && glue->fd_map == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + + return true; +} + +static void tevent_glib_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + tevent_glib_process(glue); + + return; +} + +static void tevent_glib_timer_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->timer = NULL; + tevent_glib_process(glue); + + return; +} + +static void tevent_glib_im_handler(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->scheduled_im = false; + tevent_glib_process(glue); + + return; +} + +static bool save_current_fdset(struct tevent_glib_glue *glue) +{ + /* Save old glib fds, we only grow the prev array */ + if (glue->num_prev_gpollfds < glue->num_gpollfds) { + glue->prev_gpollfds = talloc_realloc(glue, + glue->prev_gpollfds, + GPollFD, + glue->num_gpollfds); + if (glue->prev_gpollfds == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + } + glue->num_prev_gpollfds = glue->num_gpollfds; + if (glue->num_gpollfds > 0) { + memcpy(glue->prev_gpollfds, glue->gpollfds, + sizeof(GPollFD) * glue->num_gpollfds); + memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds); + } + + return true; +} + +static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue) +{ + bool ok; + gint num_fds; + + ok = save_current_fdset(glue); + if (!ok) { + return false; + } + + while (true) { + num_fds = g_main_context_query(glue->gmain_ctx, + glue->gpriority, + &glue->gtimeout, + glue->gpollfds, + glue->num_gpollfds); + if (num_fds == glue->num_gpollfds) { + break; + } + glue->gpollfds = talloc_realloc(glue, + glue->gpollfds, + GPollFD, + num_fds); + if (num_fds > 0 && glue->gpollfds == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + glue->num_gpollfds = num_fds; + }; + + if (glue->num_gpollfds > 0) { + qsort(glue->gpollfds, num_fds, sizeof(GPollFD), glib_fd_cmp_func); + } + + DBG_DEBUG("get_glib_fds_and_timeout: num fds: %d, timeout: %d ms\n", + num_fds, glue->gtimeout); + + return true; +} + +static bool tevent_glib_update_events(struct tevent_glib_glue *glue) +{ + bool ok; + + ok = cmp_gfds(glue, + glue->gpollfds, + glue->prev_gpollfds, + glue->num_gpollfds, + glue->num_prev_gpollfds, + glib_fd_cmp_func, + match_gfd_cb, + add_gfd_cb, + remove_gfd_cb); + if (!ok) { + return false; + } + + TALLOC_FREE(glue->timer); + if ((glue->gtimeout == 0) && (!glue->scheduled_im)) { + /* + * Schedule an immediate event. We use a immediate event and not + * an immediate timer event, because the former can be reused. + * + * We may be called in a loop in tevent_glib_process() and only + * want to schedule this once, so we remember the fact. + * + * Doing this here means we occasionally schedule an unneeded + * immediate event, but it avoids leaking abstraction into upper + * layers. + */ + tevent_schedule_immediate(glue->im, glue->ev, + tevent_glib_im_handler, + glue); + glue->scheduled_im = true; + } else if (glue->gtimeout > 0) { + uint64_t microsec = glue->gtimeout * 1000; + struct timeval tv = tevent_timeval_current_ofs(microsec / 1000000, + microsec % 1000000); + + glue->timer = tevent_add_timer(glue->ev, glue, + tv, + tevent_glib_timer_handler, + glue); + if (glue->timer == NULL) { + DBG_ERR("tevent_add_timer failed\n"); + return false; + } + } + + return true; +} + +static void tevent_glib_retry_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->retry_timer = NULL; + (void)tevent_glib_prepare(glue); +} + +static bool tevent_glib_prepare(struct tevent_glib_glue *glue) +{ + bool ok; + gboolean gok, source_ready; + + gok = g_main_context_acquire(glue->gmain_ctx); + if (!gok) { + DBG_ERR("couldn't acquire g_main_context\n"); + + tevent_glib_glue_cleanup(glue); + + glue->retry_timer = tevent_add_timer( + glue->ev, glue, + tevent_timeval_current_ofs(0, 1000), + tevent_glib_retry_timer, + glue); + if (glue->retry_timer == NULL) { + DBG_ERR("tevent_add_timer failed\n"); + return false; + } + return true; + } + + source_ready = g_main_context_prepare(glue->gmain_ctx, &glue->gpriority); + if (source_ready) { + g_main_context_dispatch(glue->gmain_ctx); + } + + ok = get_glib_fds_and_timeout(glue); + if (!ok) { + DBG_ERR("get_glib_fds_and_timeout failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + + tevent_glib_update_events(glue); + + return true; +} + +static short gpoll_to_poll_event(gushort gevent) +{ + short pevent = 0; + + if (gevent & G_IO_IN) { + pevent |= POLLIN; + } + if (gevent & G_IO_OUT) { + pevent |= POLLOUT; + } + if (gevent & G_IO_HUP) { + pevent |= POLLHUP; + } + if (gevent & G_IO_ERR) { + pevent |= POLLERR; + } + + return pevent; +} + +static gushort poll_to_gpoll_event(short pevent) +{ + gushort gevent = 0; + + if (pevent & POLLIN) { + gevent |= G_IO_IN; + } + if (pevent & POLLOUT) { + gevent |= G_IO_OUT; + } + if (pevent & POLLHUP) { + gevent |= G_IO_HUP; + } + if (pevent & POLLERR) { + gevent |= G_IO_ERR; + } + + return gevent; +} + +static bool gpoll_to_poll_fds(struct tevent_glib_glue *glue) +{ + size_t i; + + TALLOC_FREE(glue->pollfds); + + glue->pollfds = talloc_zero_array(glue, struct pollfd, + glue->num_gpollfds); + if (glue->pollfds == NULL) { + DBG_ERR("talloc_zero_array failed\n"); + return false; + } + + for (i = 0; i < glue->num_gpollfds; i++) { + glue->pollfds[i].fd = glue->gpollfds[i].fd; + glue->pollfds[i].events = gpoll_to_poll_event( + glue->gpollfds[i].events); + } + + return true; +} + +static void poll_to_gpoll_revents(struct tevent_glib_glue *glue) +{ + size_t i; + + for (i = 0; i < glue->num_gpollfds; i++) { + glue->gpollfds[i].revents = poll_to_gpoll_event( + glue->pollfds[i].revents); + } +} + +static bool tevent_glib_process(struct tevent_glib_glue *glue) +{ + bool ok; + int num_ready; + + ok = gpoll_to_poll_fds(glue); + if (!ok) { + DBG_ERR("gpoll_to_poll_fds failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + + num_ready = poll(glue->pollfds, glue->num_gpollfds, 0); + if (num_ready == -1) { + DBG_ERR("poll: %s\n", strerror(errno)); + } + + if (num_ready > 0) { + poll_to_gpoll_revents(glue); + } + + DBG_DEBUG("tevent_glib_process: num_ready: %d\n", num_ready); + + do { + bool sources_ready; + + sources_ready = g_main_context_check(glue->gmain_ctx, + glue->gpriority, + glue->gpollfds, + glue->num_gpollfds); + if (!sources_ready) { + break; + } + + g_main_context_dispatch(glue->gmain_ctx); + + if (glue->quit) { + /* Set via tevent_glib_glue_quit() */ + g_main_context_release(glue->gmain_ctx); + return true; + } + + /* + * This is an optimisation for the following case: + * + * If g_main_context_query() returns a timeout value of 0 this + * implicates that there may be more glib event sources ready. + * This avoids sheduling an immediate event and going through + * tevent_loop_once(). + */ + if (glue->gtimeout != 0) { + break; + } + + /* + * Give other glib threads a chance to grab the context, + * tevent_glib_prepare() will then re-acquire it + */ + g_main_context_release(glue->gmain_ctx); + + ok = tevent_glib_prepare(glue); + if (!ok) { + samba_tevent_glib_glue_quit(glue); + return false; + } + } while (true); + + /* + * Give other glib threads a chance to grab the context, + * tevent_glib_prepare() will then re-acquire it + */ + g_main_context_release(glue->gmain_ctx); + + ok = tevent_glib_prepare(glue); + if (!ok) { + samba_tevent_glib_glue_quit(glue); + return false; + } + + return true; +} + +static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue) +{ + size_t n = talloc_array_length(glue->fd_map); + size_t i; + + for (i = 0; i < n; i++) { + TALLOC_FREE(glue->fd_map[i].fd_event); + } + + TALLOC_FREE(glue->fd_map); + TALLOC_FREE(glue->gpollfds); + TALLOC_FREE(glue->prev_gpollfds); + TALLOC_FREE(glue->timer); + TALLOC_FREE(glue->retry_timer); + TALLOC_FREE(glue->im); + glue->num_gpollfds = 0; + glue->num_prev_gpollfds = 0; +} + +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue) +{ + tevent_glib_glue_cleanup(glue); + glue->quit = true; + return; +} + +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx) +{ + bool ok; + struct tevent_glib_glue *glue = NULL; + + glue = talloc_zero(mem_ctx, struct tevent_glib_glue); + if (glue == NULL) { + DBG_ERR("talloc_zero failed\n"); + return NULL; + } + + *glue = (struct tevent_glib_glue) { + .ev = ev, + .gmain_ctx = gmain_ctx, + }; + + glue->im = tevent_create_immediate(glue); + + ok = tevent_glib_prepare(glue); + if (!ok) { + TALLOC_FREE(glue); + return NULL; + } + + return glue; +} + +#else /* HAVE_GLIB */ + +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx) +{ + errno = ENOSYS; + return NULL; +} + +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue) +{ + return; +} +#endif /* HAVE_GLIB */ diff --git a/source3/lib/tevent_glib_glue.h b/source3/lib/tevent_glib_glue.h new file mode 100644 index 0000000..94f53cb --- /dev/null +++ b/source3/lib/tevent_glib_glue.h @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + Poll glib event loop from tevent + + Copyright (C) Ralph Boehme 2016 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#ifndef _TEVENT_GLIB_GLUE_H +#define _TEVENT_GLIB_GLUE_H + +#include +#include + +typedef struct _GMainContext GMainContext; + +/** + * @brief Add a glib GmainContext to a tevent context + * + * tevent will poll the glib event sources and run handlers for + * pending events as detailed in the glib documentation: + * + * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html + * + * If tevent was built without glib support, this function will always return + * NULL with an error number ENOSYS. + * + * @param[in] mem_ctx Memory context to use + * + * @param[in] ev Event context to use + * + * @param[in] gmain_ctx GMainContext that will be added to tevent + * + * @return A handle on the glue context that binds the + * the GMainContext to tevent. Pass the glue handle to + * tevent_glib_glue_quit() in a callback when you want + * stop processing glib events. + * You must not call talloc_free() on the handle while + * the loop is still in use and attached to tevent. + */ +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx); + +/** + * @brief Stop polling a GMainContext + * + * Used in a callback when you want to stop processing glib events. + * + * @param[in] glue And tevent_glib_glue handle + */ +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue); + +#endif diff --git a/source3/wscript b/source3/wscript index ea001d9..735a602 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1642,6 +1642,17 @@ main() { else: conf.fatal('AFS headers not available, but --with-fake-kaserver was specified') + if conf.CHECK_CFG(package='glib-2.0', args='--cflags --libs', + msg='Checking for glib-2.0', uselib_store="GLIB-2.0"): + if (conf.CHECK_HEADERS('glib.h', lib='glib-2.0') and conf.CHECK_LIB('glib-2.0', shlib=True)): + conf.DEFINE('HAVE_GLIB', 1) + else: + # define an empty subsystem to allow it to be used as an empty dependency + conf.SET_TARGET_TYPE('glib-2.0', 'EMPTY') + else: + # define an empty subsystem to allow it to be used as an empty dependency + conf.SET_TARGET_TYPE('glib-2.0', 'EMPTY') + conf.env['libtracker']='' tracker_versions = ['1.0', '0.16', '0.14'] @@ -1670,6 +1681,12 @@ main() { conf.env.with_spotlight = True default_static_modules.extend(TO_LIST('rpc_mdssvc_module')) + # Check for components that need tevent_glib_glue + if conf.CONFIG_SET('WITH_SPOTLIGHT'): + if not conf.CONFIG_SET('HAVE_GLIB'): + conf.fatal('Missing glib-2.0 development files') + conf.DEFINE('WITH_TEVENT_GLIB_GLUE', '1') + forced_static_modules.extend(TO_LIST('auth_domain auth_builtin auth_sam auth_winbind')) default_static_modules.extend(TO_LIST('''pdb_smbpasswd pdb_tdbsam pdb_wbc_sam auth_unix auth_wbc diff --git a/source3/wscript_build b/source3/wscript_build index d6d2be2..9fd97c4 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -862,6 +862,12 @@ bld.SAMBA3_SUBSYSTEM('SPOOLSSD', RPC_SOCK_HELPER ''') +bld.SAMBA3_SUBSYSTEM('tevent-glib-glue', + source='lib/tevent_glib_glue.c', + deps='glib-2.0', + enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'), +) + ########################## BINARIES ################################# bld.SAMBA3_BINARY('smbd/smbd', -- 2.10.2 >From 6b2711f8e487d8e24070e598f0a328f22e7c6622 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 28 Jan 2016 08:29:28 +0100 Subject: [PATCH 15/21] s3/lib: add a tevent_glib_glue subsystem test Tests adapted from glib2 glib/tests/mainloop.c. Signed-off-by: Ralph Boehme --- source3/lib/tevent_glib_glue_tests.c | 359 ++++++++++++++++++++++++++ source3/script/tests/test_tevent_glib_glue.sh | 20 ++ source3/selftest/tests.py | 3 + source3/wscript_build | 11 + 4 files changed, 393 insertions(+) create mode 100644 source3/lib/tevent_glib_glue_tests.c create mode 100755 source3/script/tests/test_tevent_glib_glue.sh diff --git a/source3/lib/tevent_glib_glue_tests.c b/source3/lib/tevent_glib_glue_tests.c new file mode 100644 index 0000000..bbba465 --- /dev/null +++ b/source3/lib/tevent_glib_glue_tests.c @@ -0,0 +1,359 @@ +/* + Unix SMB/CIFS implementation. + + testing of the tevent glib glue subsystem + + Copyright (C) Ralph Boehme 2016 + + glib tests adapted from glib2 glib/tests/mainloop.c + Copyright (C) 2011 Red Hat Inc., Matthias Clasen + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include "replace.h" +#include "lib/tevent_glib_glue.h" +#include +#include + +/* + * Unfortunately the glib test suite runner doesn't pass args to tests + * so we must keep a few globals here. + */ +static struct tevent_context *ev; + +static gboolean cb (gpointer data) +{ + return FALSE; +} + +static gboolean prepare (GSource *source, gint *time) +{ + return FALSE; +} +static gboolean check (GSource *source) +{ + return FALSE; +} +static gboolean dispatch (GSource *source, GSourceFunc cb_in, gpointer date) +{ + return FALSE; +} + +static GSourceFuncs funcs = { + prepare, + check, + dispatch, + NULL +}; + +static void test_maincontext_basic(void) +{ + GMainContext *ctx; + struct tevent_glib_glue *glue; + GSource *source; + guint id; + gpointer data = &funcs; + + ctx = g_main_context_new (); + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert (glue != NULL); + + g_assert (!g_main_context_pending (ctx)); + g_assert (!g_main_context_iteration (ctx, FALSE)); + + source = g_source_new (&funcs, sizeof (GSource)); + g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_DEFAULT); + g_assert (!g_source_is_destroyed (source)); + + g_assert (!g_source_get_can_recurse (source)); + g_assert (g_source_get_name (source) == NULL); + + g_source_set_can_recurse (source, TRUE); + g_source_set_name (source, "d"); + + g_assert (g_source_get_can_recurse (source)); + g_assert_cmpstr (g_source_get_name (source), ==, "d"); + + g_assert (g_main_context_find_source_by_user_data (ctx, NULL) == NULL); + g_assert (g_main_context_find_source_by_funcs_user_data (ctx, &funcs, NULL) == NULL); + + id = g_source_attach (source, ctx); + g_assert_cmpint (g_source_get_id (source), ==, id); + g_assert (g_main_context_find_source_by_id (ctx, id) == source); + + g_source_set_priority (source, G_PRIORITY_HIGH); + g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_HIGH); + + g_source_destroy (source); + g_assert (g_source_get_context (source) == ctx); + g_assert (g_main_context_find_source_by_id (ctx, id) == NULL); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); + g_main_context_unref (ctx); + + if (g_test_undefined ()) + { + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*source->context != NULL*failed*"); + g_assert (g_source_get_context (source) == NULL); + g_test_assert_expected_messages (); + } + + g_source_unref (source); + + ctx = g_main_context_default (); + + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert (glue != NULL); + + source = g_source_new (&funcs, sizeof (GSource)); + g_source_set_funcs (source, &funcs); + g_source_set_callback (source, cb, data, NULL); + id = g_source_attach (source, ctx); + g_source_unref (source); + g_source_set_name_by_id (id, "e"); + g_assert_cmpstr (g_source_get_name (source), ==, "e"); + g_assert (g_source_get_context (source) == ctx); + g_assert (g_source_remove_by_funcs_user_data (&funcs, data)); + + source = g_source_new (&funcs, sizeof (GSource)); + g_source_set_funcs (source, &funcs); + g_source_set_callback (source, cb, data, NULL); + id = g_source_attach (source, ctx); + g_source_unref (source); + g_assert (g_source_remove_by_user_data (data)); + g_assert (!g_source_remove_by_user_data ((gpointer)0x1234)); + + g_idle_add (cb, data); + g_assert (g_idle_remove_by_data (data)); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); +} + +static gboolean count_calls (gpointer data) +{ + gint *i = data; + + (*i)++; + + return TRUE; +} + +static gboolean quit_loop (gpointer data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort(data, struct tevent_glib_glue); + + samba_tevent_glib_glue_quit(glue); + + return G_SOURCE_REMOVE; +} + +static void test_timeouts (void) +{ + GMainContext *ctx; + struct tevent_glib_glue *glue; + GSource *source; + static gint a; + static gint b; + static gint c; + + a = b = c = 0; + + ctx = g_main_context_new (); + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert (glue != NULL); + + source = g_timeout_source_new (100); + g_source_set_callback (source, count_calls, &a, NULL); + g_source_attach (source, ctx); + g_source_unref (source); + + source = g_timeout_source_new (250); + g_source_set_callback (source, count_calls, &b, NULL); + g_source_attach (source, ctx); + g_source_unref (source); + + source = g_timeout_source_new (330); + g_source_set_callback (source, count_calls, &c, NULL); + g_source_attach (source, ctx); + g_source_unref (source); + + source = g_timeout_source_new (1050); + g_source_set_callback (source, quit_loop, glue, NULL); + g_source_attach (source, ctx); + g_source_unref (source); + + g_assert (tevent_loop_wait(ev) == 0); + + /* We may be delayed for an arbitrary amount of time - for example, + * it's possible for all timeouts to fire exactly once. + */ + g_assert_cmpint (a, >, 0); + g_assert_cmpint (a, >=, b); + g_assert_cmpint (b, >=, c); + + g_assert_cmpint (a, <=, 10); + g_assert_cmpint (b, <=, 4); + g_assert_cmpint (c, <=, 3); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); + g_main_context_unref (ctx); +} + + +static gchar zeros[1024]; + +static gsize fill_a_pipe (gint fd) +{ + gsize written = 0; + GPollFD pfd; + + pfd.fd = fd; + pfd.events = G_IO_OUT; + while (g_poll (&pfd, 1, 0) == 1) + /* we should never see -1 here */ + written += write (fd, zeros, sizeof zeros); + + return written; +} + +static gboolean write_bytes (gint fd, + GIOCondition condition, + gpointer user_data) +{ + gssize *to_write = user_data; + gint limit; + + if (*to_write == 0) + return FALSE; + + /* Detect if we run before we should */ + g_assert (*to_write >= 0); + + limit = MIN (*to_write, sizeof zeros); + *to_write -= write (fd, zeros, limit); + + return TRUE; +} + +static gboolean read_bytes (gint fd, + GIOCondition condition, + gpointer user_data) +{ + static gchar buffer[1024]; + gssize *to_read = user_data; + + *to_read -= read (fd, buffer, sizeof buffer); + + /* The loop will exit when there is nothing else to read, then we will + * use g_source_remove() to destroy this source. + */ + return TRUE; +} + +static void test_unix_fd(void) +{ + gssize to_write = -1; + gssize to_read; + gint fds[2]; + gint a, b; + gint s; + GSource *source_a; + GSource *source_b; + struct tevent_glib_glue *glue; + + glue = samba_tevent_glib_glue_create(ev, ev, g_main_context_default()); + g_assert (glue != NULL); + + s = pipe (fds); + g_assert (s == 0); + + to_read = fill_a_pipe (fds[1]); + /* write at higher priority to keep the pipe full... */ + a = g_unix_fd_add_full (G_PRIORITY_HIGH, fds[1], G_IO_OUT, write_bytes, &to_write, NULL); + source_a = g_source_ref (g_main_context_find_source_by_id (NULL, a)); + /* make sure no 'writes' get dispatched yet */ + while (tevent_loop_once(ev)); + + to_read += 128 * 1024 * 1024; + to_write = 128 * 1024 * 1024; + b = g_unix_fd_add (fds[0], G_IO_IN, read_bytes, &to_read); + source_b = g_source_ref (g_main_context_find_source_by_id (NULL, b)); + + /* Assuming the kernel isn't internally 'laggy' then there will always + * be either data to read or room in which to write. That will keep + * the loop running until all data has been read and written. + */ + while (to_write > 0 || to_read > 0) + { + gssize to_write_was = to_write; + gssize to_read_was = to_read; + + if (tevent_loop_once(ev) != 0) + break; + + /* Since the sources are at different priority, only one of them + * should possibly have run. + */ + g_assert (to_write == to_write_was || to_read == to_read_was); + } + + g_assert (to_write == 0); + g_assert (to_read == 0); + + /* 'a' is already removed by itself */ + g_assert (g_source_is_destroyed (source_a)); + g_source_unref (source_a); + g_source_remove (b); + g_assert (g_source_is_destroyed (source_b)); + g_source_unref (source_b); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); + + close (fds[1]); + close (fds[0]); +} + +int main(int argc, const char *argv[]) +{ + int test_argc = 3; + char *test_argv[] = { + discard_const("test_glib_glue"), + discard_const("-m"), + discard_const("no-undefined") + }; + char **argvp = test_argv; + + g_test_init(&test_argc, &argvp, NULL); + + ev = tevent_context_init(NULL); + if (ev == NULL) { + exit(1); + } + + g_test_add_func ("/maincontext/basic", test_maincontext_basic); + g_test_add_func ("/mainloop/timeouts", test_timeouts); + g_test_add_func ("/mainloop/unix-fd", test_unix_fd); + + return g_test_run(); +} diff --git a/source3/script/tests/test_tevent_glib_glue.sh b/source3/script/tests/test_tevent_glib_glue.sh new file mode 100755 index 0000000..6754494 --- /dev/null +++ b/source3/script/tests/test_tevent_glib_glue.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +incdir=`dirname $0`/../../../testprogs/blackbox +. $incdir/subunit.sh + +if [ ! -x $BINDIR/tevent_glib_glue_test ] ; then + # Some machines don't have /bin/true, simulate it + cat >$BINDIR/tevent_glib_glue_test <From 8c4b69d1188345ce0773d14e4912b4374826cfe2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 22 Jan 2016 15:38:39 +0100 Subject: [PATCH 16/21] s3/lib: tevent-glib-glue test utiltity with Tracker A small utilitly useful for tesing the tevent_glib_glue code. It runs a tracker-sparql search query against your local tracker store that must be setup and running. Signed-off-by: Ralph Boehme --- source3/utils/async-tracker.c | 286 ++++++++++++++++++++++++++++++++++++++++++ source3/wscript_build | 11 ++ 2 files changed, 297 insertions(+) create mode 100644 source3/utils/async-tracker.c diff --git a/source3/utils/async-tracker.c b/source3/utils/async-tracker.c new file mode 100644 index 0000000..4b674af --- /dev/null +++ b/source3/utils/async-tracker.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2011, Nokia + * Copyright (C) 2015, Noel Power + * Copyright (C) 2016, Ralph Boehme + * + * This library 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 2 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "includes.h" +#include "lib/util/debug.h" +#include "popt_common.h" +#include "param.h" +#include "lib/tevent_glib_glue.h" + +/* + * glib uses TRUE and FALSE which was redefined by "includes.h" to be + * unusable, undefine so glib can establish its own working + * replacement. + */ +#undef TRUE +#undef FALSE +#include + +enum loop_type {TEVENT_LOOP, GLIB_LOOP}; + +struct test_state { + enum loop_type loop_type; + TrackerSparqlConnection *connection; + GCancellable *cancellable; + GTimer *timer; + GMainLoop *loop; + struct tevent_context *ev; + struct tevent_glib_glue *glue; +}; + +static void cleanup(struct test_state *state) +{ + g_cancellable_cancel(state->cancellable); + g_object_unref(state->cancellable); + g_timer_destroy(state->timer); + if (state->connection) { + g_object_unref(state->connection); + state->connection = NULL; + } + if (state->loop_type == GLIB_LOOP) { + g_main_loop_quit(state->loop); + } else { + samba_tevent_glib_glue_quit(state->glue); + } +} + +static void cursor_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GError *error = NULL; + gboolean more_results; + struct test_state *state = talloc_get_type_abort(user_data, struct test_state); + + cursor = TRACKER_SPARQL_CURSOR(object); + more_results = tracker_sparql_cursor_next_finish(cursor, + res, + &error); + + if (!error) { + static gint i = 0; + + if (more_results) { + if (i++ < 5) { + int num_cols = tracker_sparql_cursor_get_n_columns(cursor); + int col; + if (i == 1) { + g_print("Printing first 5 results:\n"); + } + for (col = 0; col < num_cols; col++) { + g_print(" %s ", tracker_sparql_cursor_get_string( + cursor, col, NULL)); + if (col == num_cols -1 ) { + g_print("\n"); + } + } + + if (i == 5) { + g_print(" ...\n"); + g_print(" Printing nothing for remaining results\n"); + } + } + + tracker_sparql_cursor_next_async(cursor, + state->cancellable, + cursor_cb, + state); + } else { + g_print("\n"); + g_print("Async cursor next took: %.6f (for all %d results)\n", + g_timer_elapsed (state->timer, NULL), i); + + g_object_unref(cursor); + cleanup(state); + } + } else { + g_critical("Could not run cursor next: %s", error->message); + + if (cursor) { + g_object_unref(cursor); + } + + g_error_free(error); + cleanup(state); + } +} + +static void query_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GError *error = NULL; + struct test_state *state = talloc_get_type_abort( + user_data, struct test_state); + + cursor = tracker_sparql_connection_query_finish( + TRACKER_SPARQL_CONNECTION (object), + res, + &error); + + g_print("Async query took: %.6f\n", g_timer_elapsed(state->timer, NULL)); + + g_timer_start(state->timer); + + if (!error) { + tracker_sparql_cursor_next_async(cursor, + state->cancellable, + cursor_cb, + state); + } else { + g_critical("Could not run query: %s", error->message); + + if (cursor) { + g_object_unref(cursor); + } + + g_error_free(error); + cleanup(state); + } +} + +static void connection_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct test_state *state = talloc_get_type_abort( + user_data, struct test_state); + GError *error = NULL; + + state->connection = tracker_sparql_connection_get_finish(res, &error); + g_print("Async connection took: %.6f\n", + g_timer_elapsed(state->timer, NULL)); + + g_timer_start(state->timer); + + if (!error) { + tracker_sparql_connection_query_async( + state->connection, + "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) " + "WHERE { {?s nie:url ?name}}", + state->cancellable, + query_cb, + state); + } else { + g_critical("Could not connect: %s", error->message); + g_error_free(error); + cleanup(state); + } +} + +static void debug_fn(void *private_data, + enum tevent_debug_level level, + const char *fmt, + va_list ap) +{ + dbgtext_va(fmt, ap); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx = NULL; + struct test_state *state = NULL; + int c; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"tevent", 't', POPT_ARG_NONE, NULL, 't', "Use tevent loop" }, + {"glib", 'g', POPT_ARG_NONE, NULL, 'g', "Use glib loop" }, + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + exit(1); + } + + state = talloc_zero(mem_ctx, struct test_state); + if (state == NULL) { + exit(1); + } + + state->loop_type = GLIB_LOOP; + + setup_logging(argv[0], DEBUG_STDERR); + smb_init_locale(); + + if (!lp_load_client(get_dyn_CONFIGFILE())) { + fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", + get_dyn_CONFIGFILE()); + exit(1); + } + + pc = poptGetContext(NULL, argc, argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) { + case 'g': + state->loop_type = GLIB_LOOP; + break; + case 't': + state->loop_type = TEVENT_LOOP; + break; + } + } + + if (state->loop_type == GLIB_LOOP) { + state->loop = g_main_loop_new(NULL, false); + } else { + state->ev = tevent_context_init(mem_ctx); + if (CHECK_DEBUGLVL(10)) { + tevent_set_debug(state->ev, debug_fn, NULL); + } + state->glue = samba_tevent_glib_glue_create( + mem_ctx, state->ev, g_main_context_default()); + if (state->glue == NULL) { + printf("tevent_glib_glue_create failed\n"); + exit(1); + } + } + + state->timer = g_timer_new(); + state->cancellable = g_cancellable_new(); + tracker_sparql_connection_get_async(state->cancellable, + connection_cb, + state); + + if (state->loop_type == GLIB_LOOP) { + printf("entering g_main_loop_run\n"); + g_main_loop_run(state->loop); + } else { + printf("entering tevent_loop_wait\n"); + tevent_loop_wait(state->ev); + + DBG_DEBUG("freeing glue\n"); + TALLOC_FREE(state->glue); + DBG_DEBUG("freeing event context\n"); + TALLOC_FREE(state->ev); + } + + TALLOC_FREE(mem_ctx); + poptFreeContext(pc); + + return 0; +} diff --git a/source3/wscript_build b/source3/wscript_build index 167cbb8..3c383d9 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1043,6 +1043,17 @@ bld.SAMBA3_BINARY('tevent_glib_glue_test', enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'), install=False) +bld.SAMBA3_BINARY('tevent_glib_tracker', + source='utils/async-tracker.c', + deps=''' + talloc + libsmb + popt_samba3 + param + tevent-glib-glue ''' + bld.env['libtracker'], + enabled=bld.CONFIG_SET('HAVE_TRACKER') and bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'), + install=False) + bld.SAMBA3_BINARY('pdbtest', source='torture/pdbtest.c', deps=''' -- 2.10.2 >From aeed6bb7a0cbbf256450ada1ab3787d6a7ae34a2 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Mon, 22 Feb 2016 14:20:46 +0000 Subject: [PATCH 17/21] s3/lib: Rewrite glib code to use TEVENT_TRACE_[BEFORE|AFTER]_LOOP_ONCE events. Each loop iteration we need to evaluate any available glib event sources, this patch modifies the previous code in the following ways a) simplify calling sequence, in normal loop processing no immediate events are used, the exception is when glib_loop quit is detected in the 'glib_prepare' phase. In that case we need the event loop to cycle to avoid blocking on a timeout. b) simplify the optimisation for 0 timeout handling, detect this scenario in the prepare phase and drain events there. This results in the 'glib_process' phase also being simplified, previously it confusingly did a) the 0 timeout optimisation b) normal 'poll, check & dispatch' c) calling glib_prepare now 'glib_process' does just the normal 'poll, check & dispatch', nothing else c) every loop iteration results in glib_prepare/glib_process regardless of whether a tevent event fires Signed-off-by: Noel Power --- source3/lib/tevent_glib_glue.c | 216 ++++++++++++++++++++++++++--------------- 1 file changed, 137 insertions(+), 79 deletions(-) diff --git a/source3/lib/tevent_glib_glue.c b/source3/lib/tevent_glib_glue.c index 3e2ed67..d576645 100644 --- a/source3/lib/tevent_glib_glue.c +++ b/source3/lib/tevent_glib_glue.c @@ -45,6 +45,7 @@ struct tevent_glib_glue { struct tevent_context *ev; GMainContext *gmain_ctx; bool quit; + bool skip_glue_trace_event; struct tevent_timer *retry_timer; gint gtimeout; @@ -58,8 +59,10 @@ struct tevent_glib_glue { size_t num_maps; struct tevent_timer *timer; struct tevent_immediate *im; - bool scheduled_im; + bool draining_events; struct pollfd *pollfds; + tevent_trace_callback_t prev_trace_cb; + void *prev_trace_data; }; static bool tevent_glib_prepare(struct tevent_glib_glue *glue); @@ -270,8 +273,12 @@ static void tevent_glib_fd_handler(struct tevent_context *ev, private_data, struct tevent_glib_glue); tevent_glib_process(glue); - - return; + /* + * we have already called tevent_glib_process, we wish to inhibit + * the next TEVENT_TRACE_AFTER_LOOP_ONCE event from calling process + * a second time (out of order) + */ + glue->skip_glue_trace_event = true; } static void tevent_glib_timer_handler(struct tevent_context *ev, @@ -284,21 +291,81 @@ static void tevent_glib_timer_handler(struct tevent_context *ev, glue->timer = NULL; tevent_glib_process(glue); - + /* + * we have already called tevent_glib_process, we wish to inhibit + * the next TEVENT_TRACE_AFTER_LOOP_ONCE event from calling process + * a second time (out of order) + */ + glue->skip_glue_trace_event = true; return; } -static void tevent_glib_im_handler(struct tevent_context *ev, - struct tevent_immediate *im, - void *private_data) +/* noop to force loop processing */ +static void tevent_im_tickle_loop_handler(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) { - struct tevent_glib_glue *glue = talloc_get_type_abort( - private_data, struct tevent_glib_glue); + TALLOC_FREE(im); +} - glue->scheduled_im = false; - tevent_glib_process(glue); +/* + * This function preforms an optimisation for the following case: + * + * If g_main_context_query() returns a timeout value of 0 this + * implies that there maybe more glib event sources ready. + * Here we loop through the prepare, check & dispatch glib phases while + * the returned timeout is 0, this avoids scheduling an immediate event and + * going through tevent_loop_once(). +*/ +static void drain_glib_events( struct tevent_glib_glue *glue) +{ + bool sources_ready, ok; + do { + sources_ready = g_main_context_check(glue->gmain_ctx, + glue->gpriority, + glue->gpollfds, + glue->num_gpollfds); + if (sources_ready) { + g_main_context_dispatch(glue->gmain_ctx); + } - return; + g_main_context_release(glue->gmain_ctx); + + if (glue->quit) { + /* Set via tevent_glib_glue_quit() */ + struct tevent_immediate *im = + tevent_create_immediate(NULL); + /* + * as we are called from glib_prepare if we fall out + * here more than likely there is a timeout that poll + * will block for, an immediate event will force the + * event loop to cycle and terminate if no more events. + */ + tevent_schedule_immediate(im, glue->ev, + tevent_im_tickle_loop_handler, glue); + DEBUG(0,("quitting....\n")); + return; + } + + + /* + * Give other glib threads a chance to grab the context, + * tevent_glib_prepare() will then re-acquire it + */ + + ok = tevent_glib_prepare(glue); + if (!ok) { + samba_tevent_glib_glue_quit(glue); + return; + } + + if (glue->gtimeout != 0) { + break; + } + + } while (true); + + glue->draining_events = false; } static bool save_current_fdset(struct tevent_glib_glue *glue) @@ -382,22 +449,9 @@ static bool tevent_glib_update_events(struct tevent_glib_glue *glue) } TALLOC_FREE(glue->timer); - if ((glue->gtimeout == 0) && (!glue->scheduled_im)) { - /* - * Schedule an immediate event. We use a immediate event and not - * an immediate timer event, because the former can be reused. - * - * We may be called in a loop in tevent_glib_process() and only - * want to schedule this once, so we remember the fact. - * - * Doing this here means we occasionally schedule an unneeded - * immediate event, but it avoids leaking abstraction into upper - * layers. - */ - tevent_schedule_immediate(glue->im, glue->ev, - tevent_glib_im_handler, - glue); - glue->scheduled_im = true; + if ((glue->gtimeout == 0) && (!glue->draining_events)) { + glue->draining_events = true; + drain_glib_events(glue); } else if (glue->gtimeout > 0) { uint64_t microsec = glue->gtimeout * 1000; struct timeval tv = tevent_timeval_current_ofs(microsec / 1000000, @@ -432,7 +486,6 @@ static bool tevent_glib_prepare(struct tevent_glib_glue *glue) { bool ok; gboolean gok, source_ready; - gok = g_main_context_acquire(glue->gmain_ctx); if (!gok) { DBG_ERR("couldn't acquire g_main_context\n"); @@ -544,7 +597,7 @@ static bool tevent_glib_process(struct tevent_glib_glue *glue) { bool ok; int num_ready; - + bool sources_ready; ok = gpoll_to_poll_fds(glue); if (!ok) { DBG_ERR("gpoll_to_poll_fds failed\n"); @@ -563,49 +616,13 @@ static bool tevent_glib_process(struct tevent_glib_glue *glue) DBG_DEBUG("tevent_glib_process: num_ready: %d\n", num_ready); - do { - bool sources_ready; - - sources_ready = g_main_context_check(glue->gmain_ctx, - glue->gpriority, - glue->gpollfds, - glue->num_gpollfds); - if (!sources_ready) { - break; - } - + sources_ready = g_main_context_check(glue->gmain_ctx, + glue->gpriority, + glue->gpollfds, + glue->num_gpollfds); + if (sources_ready) { g_main_context_dispatch(glue->gmain_ctx); - - if (glue->quit) { - /* Set via tevent_glib_glue_quit() */ - g_main_context_release(glue->gmain_ctx); - return true; - } - - /* - * This is an optimisation for the following case: - * - * If g_main_context_query() returns a timeout value of 0 this - * implicates that there may be more glib event sources ready. - * This avoids sheduling an immediate event and going through - * tevent_loop_once(). - */ - if (glue->gtimeout != 0) { - break; - } - - /* - * Give other glib threads a chance to grab the context, - * tevent_glib_prepare() will then re-acquire it - */ - g_main_context_release(glue->gmain_ctx); - - ok = tevent_glib_prepare(glue); - if (!ok) { - samba_tevent_glib_glue_quit(glue); - return false; - } - } while (true); + } /* * Give other glib threads a chance to grab the context, @@ -613,12 +630,6 @@ static bool tevent_glib_process(struct tevent_glib_glue *glue) */ g_main_context_release(glue->gmain_ctx); - ok = tevent_glib_prepare(glue); - if (!ok) { - samba_tevent_glib_glue_quit(glue); - return false; - } - return true; } @@ -631,6 +642,7 @@ static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue) TALLOC_FREE(glue->fd_map[i].fd_event); } + tevent_set_trace_callback(glue->ev, glue->prev_trace_cb, glue->prev_trace_data); TALLOC_FREE(glue->fd_map); TALLOC_FREE(glue->gpollfds); TALLOC_FREE(glue->prev_gpollfds); @@ -648,6 +660,43 @@ void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue) return; } +static void tevent_glib_glue_trace_callback(enum tevent_trace_point point, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + bool call_trace_handler; + /* + * don't call prepare/process if explicity told to skip or if + * we are still waiting to acquire the glib context + */ + call_trace_handler = (!glue->skip_glue_trace_event && !glue->retry_timer); + + switch (point) { + case TEVENT_TRACE_BEFORE_LOOP_ONCE: + case TEVENT_TRACE_AFTER_LOOP_ONCE: + if (call_trace_handler) { + if (point == TEVENT_TRACE_BEFORE_LOOP_ONCE) { + tevent_glib_prepare(glue); + } else { + tevent_glib_process(glue); + } + } + glue->skip_glue_trace_event = false; + break; + case TEVENT_TRACE_BEFORE_WAIT: + case TEVENT_TRACE_AFTER_WAIT: + default: + break; + } + + + /* chain previous handler */ + if (glue->prev_trace_cb) { + glue->prev_trace_cb(point, glue->prev_trace_data); + } +} + struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev, GMainContext *gmain_ctx) @@ -668,12 +717,21 @@ struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, glue->im = tevent_create_immediate(glue); + tevent_get_trace_callback(glue->ev, &glue->prev_trace_cb, &glue->prev_trace_data); + tevent_set_trace_callback(ev, tevent_glib_glue_trace_callback, + glue); + ok = tevent_glib_prepare(glue); if (!ok) { TALLOC_FREE(glue); return NULL; } - + /* + * we have already called tevent_glib_prepare, we wish to inhibit + * the next TEVENT_TRACE_BEFORE_LOOP_ONCE event from calling prepare + * a second time (out of order) + */ + glue->skip_glue_trace_event = true; return glue; } -- 2.10.2 >From 353f2ff6e3850e990657be3b3f5db95667c0753b Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 16 Jun 2016 14:48:50 +0100 Subject: [PATCH 18/21] s3/utils: Adjust test to include glib event sources created from tevent context. Glib event sources when created/deleted/modified via code called from a tevent event loop handler should be detected and processed. If glib_prepare is NOT called after such a tevent handler as described previously has fired then the affected glib event sources will not be monitored by tevent, consequently those glib event sources with ready events will not be dispatched. Signed-off-by: Noel Power --- source3/utils/async-tracker.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/source3/utils/async-tracker.c b/source3/utils/async-tracker.c index 4b674af..cca2b4b 100644 --- a/source3/utils/async-tracker.c +++ b/source3/utils/async-tracker.c @@ -159,6 +159,19 @@ static void query_cb(GObject *object, } } +static void query_im_handler(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct test_state *state = talloc_get_type_abort(private_data, struct test_state); + tracker_sparql_connection_query_async(state->connection, + "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) WHERE { {?s nie:url ?name}}", + state->cancellable, + query_cb, + state); + +} + static void connection_cb(GObject *object, GAsyncResult *res, gpointer user_data) @@ -174,13 +187,28 @@ static void connection_cb(GObject *object, g_timer_start(state->timer); if (!error) { - tracker_sparql_connection_query_async( - state->connection, - "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) " - "WHERE { {?s nie:url ?name}}", - state->cancellable, - query_cb, - state); + if (state->loop_type == TEVENT_LOOP) { + /* + * set up query to run in the context of a + * tevent 'event' + */ + struct tevent_immediate *im = + tevent_create_immediate(state);; + if (im == NULL) { + g_critical("Could not create immediate event"); + cleanup(state); + } + tevent_schedule_immediate(im, state->ev, + query_im_handler, + state); + } else { + tracker_sparql_connection_query_async(state->connection, + "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) WHERE { {?s nie:url ?name}}", + state->cancellable, + query_cb, + state); + + } } else { g_critical("Could not connect: %s", error->message); g_error_free(error); -- 2.10.2 >From 5962ffdf76bf0e149798c9490b9b26beeacf852a Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 17 Jun 2016 10:38:17 +0100 Subject: [PATCH 19/21] s3/build: Adjust configure script to cater for WSP service tracker requirement. Signed-off-by: Noel Power --- source3/wscript | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/source3/wscript b/source3/wscript index 735a602..18ac579 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1668,10 +1668,6 @@ main() { conf.env.with_spotlight = False conf.env.with_wsp = False - if Options.options.with_wsp: - Logs.info("building with WSP support") - conf.DEFINE('WITH_WSP', '1') - conf.env.with_wsp = True if Options.options.with_spotlight: if not conf.CONFIG_SET('HAVE_TRACKER'): conf.fatal('Missing Gnome Tracker development files') @@ -1681,8 +1677,15 @@ main() { conf.env.with_spotlight = True default_static_modules.extend(TO_LIST('rpc_mdssvc_module')) + if Options.options.with_wsp: + if not conf.CONFIG_SET('HAVE_TRACKER'): + conf.fatal('Missing Gnome Tracker development files') + Logs.info("building with WSP support") + conf.DEFINE('WITH_WSP', '1') + conf.env.with_wsp = True + # Check for components that need tevent_glib_glue - if conf.CONFIG_SET('WITH_SPOTLIGHT'): + if conf.CONFIG_SET('WITH_SPOTLIGHT') or conf.CONFIG_SET('WITH_WSP'): if not conf.CONFIG_SET('HAVE_GLIB'): conf.fatal('Missing glib-2.0 development files') conf.DEFINE('WITH_TEVENT_GLIB_GLUE', '1') -- 2.10.2 >From 33f925b96c596895f14de6de43d3be6f5e286c67 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 4 Dec 2014 15:36:19 +0000 Subject: [PATCH 20/21] s3/rpc_server: WSP server implementation Adds a basic WSP service, caters for the basic queries encountered when using the search ribbon from windows explorer. This version doesn't have any rawpipe implementation and can only be run as an external service. Signed-off-by: Noel Power --- docs-xml/smbdotconf/misc/wspenable.xml | 55 + docs-xml/smbdotconf/misc/wspresultlimit.xml | 12 + source3/param/loadparm.c | 1 + source3/rpc_server/rpc_config.c | 1 + source3/rpc_server/rpc_config.h | 1 + source3/rpc_server/srv_pipe.c | 4 +- source3/rpc_server/srv_pipe.h | 1 + source3/rpc_server/wscript_build | 10 + source3/rpc_server/wsp/wsp_gss.c | 2233 +++++++++++++++++++++++ source3/rpc_server/wsp/wsp_gss.h | 254 +++ source3/rpc_server/wsp/wsp_sparql_conv.c | 2115 +++++++++++++++++++++ source3/rpc_server/wsp/wsp_sparql_conv.h | 99 + source3/rpc_server/wsp/wsp_srv_tracker-sparql.c | 831 +++++++++ source3/rpc_server/wsp/wsp_srv_tracker-sparql.h | 105 ++ source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c | 1627 +++++++++++++++++ source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h | 29 + source3/rpc_server/wspd.c | 766 ++++++++ source3/smbd/server.c | 9 +- source3/wscript | 1 + source3/wscript_build | 2 + 20 files changed, 8152 insertions(+), 4 deletions(-) create mode 100644 docs-xml/smbdotconf/misc/wspenable.xml create mode 100644 docs-xml/smbdotconf/misc/wspresultlimit.xml create mode 100644 source3/rpc_server/wsp/wsp_gss.c create mode 100644 source3/rpc_server/wsp/wsp_gss.h create mode 100644 source3/rpc_server/wsp/wsp_sparql_conv.c create mode 100644 source3/rpc_server/wsp/wsp_sparql_conv.h create mode 100644 source3/rpc_server/wsp/wsp_srv_tracker-sparql.c create mode 100644 source3/rpc_server/wsp/wsp_srv_tracker-sparql.h create mode 100644 source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c create mode 100644 source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h create mode 100644 source3/rpc_server/wspd.c diff --git a/docs-xml/smbdotconf/misc/wspenable.xml b/docs-xml/smbdotconf/misc/wspenable.xml new file mode 100644 index 0000000..9c42447 --- /dev/null +++ b/docs-xml/smbdotconf/misc/wspenable.xml @@ -0,0 +1,55 @@ + + + + This parameter controls whether Samba allows WSP + queries on a share. For controlling indexing of filesystems + you also have to use Tracker's own configuration system. + + + + WSP has several prerequisites: + + + + + Samba must be configured and built with WSP support. + + + + The wsp RPC service must be + enabled, see below. + + + Tracker intergration must be setup and the + share must be indexed by Tracker. + + + For a detailed set of instructions please see https://wiki.samba.org/index.php/wsp. + + + + The Spotlight RPC service can either be enabled as embedded + RPC service: + + + + +embedded + + + + Or it can be run in a seperate RPC service daemon: + + + + +fork + + + +no + diff --git a/docs-xml/smbdotconf/misc/wspresultlimit.xml b/docs-xml/smbdotconf/misc/wspresultlimit.xml new file mode 100644 index 0000000..432b331 --- /dev/null +++ b/docs-xml/smbdotconf/misc/wspresultlimit.xml @@ -0,0 +1,12 @@ + + + This parameter allows the number of results returned from a WSP + query on a share to be controlled. + + A value of 0 indicates no unlimited results will be returned. + +0 + diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 21073c6..2c1293d 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -185,6 +185,7 @@ static struct loadparm_service sDefault = .available = true, .read_only = true, .spotlight = false, + .wsp = false, .guest_only = false, .administrative_share = false, .guest_ok = false, diff --git a/source3/rpc_server/rpc_config.c b/source3/rpc_server/rpc_config.c index 23c6f88..7d450d9 100644 --- a/source3/rpc_server/rpc_config.c +++ b/source3/rpc_server/rpc_config.c @@ -37,6 +37,7 @@ struct rpc_service_defaults { /* { "samr", "embedded" }, */ /* { "netlogon", "embedded" }, */ { "fssagentrpc", "external" }, + { "MsFteWds", "external" }, { NULL, NULL } }; diff --git a/source3/rpc_server/rpc_config.h b/source3/rpc_server/rpc_config.h index 5091704..5de7e31 100644 --- a/source3/rpc_server/rpc_config.h +++ b/source3/rpc_server/rpc_config.h @@ -69,4 +69,5 @@ enum rpc_daemon_type_e rpc_daemon_type(const char *name); #define rpc_fss_daemon() rpc_daemon_type("fssd") #define rpc_mdssd_daemon() rpc_daemon_type("mdssd") +#define rpc_wsp_daemon() rpc_daemon_type("wspd") #endif /* _RPC_CONFIG_H */ diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index 0633b5f..a498cf8 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -235,8 +235,6 @@ bool create_next_pdu(struct pipes_struct *p) } -static bool pipe_init_outgoing_data(struct pipes_struct *p); - /******************************************************************* Marshall a bind_nak pdu. *******************************************************************/ @@ -1510,7 +1508,7 @@ static bool api_rpcTNP(struct pipes_struct *p, struct ncacn_packet *pkt, Initialise an outgoing packet. ****************************************************************************/ -static bool pipe_init_outgoing_data(struct pipes_struct *p) +bool pipe_init_outgoing_data(struct pipes_struct *p) { output_data *o_data = &p->out_data; diff --git a/source3/rpc_server/srv_pipe.h b/source3/rpc_server/srv_pipe.h index 453cca1..843673e 100644 --- a/source3/rpc_server/srv_pipe.h +++ b/source3/rpc_server/srv_pipe.h @@ -29,5 +29,6 @@ bool create_next_pdu(struct pipes_struct *p); bool api_pipe_bind_auth3(struct pipes_struct *p, struct ncacn_packet *pkt); bool setup_fault_pdu(struct pipes_struct *p, NTSTATUS status); bool is_known_pipename(const char *cli_filename, struct ndr_syntax_id *syntax); +bool pipe_init_outgoing_data(struct pipes_struct *p); #endif /* _RPC_SERVER_SRV_PIPE_H_ */ diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build index 1d0facb..42689ed 100755 --- a/source3/rpc_server/wscript_build +++ b/source3/rpc_server/wscript_build @@ -144,6 +144,11 @@ bld.SAMBA3_MODULE('rpc_mdssvc_module', internal_module=bld.SAMBA3_IS_STATIC_MODULE('rpc_mdssvc_module'), enabled=bld.SAMBA3_IS_ENABLED_MODULE('rpc_mdssvc_module')) +bld.SAMBA3_SUBSYSTEM('WSPSVC', + source='''wsp/wsp_gss.c wsp/wsp_sparql_conv.c wsp/wsp_srv_tracker_abs_if.c wsp/wsp_srv_tracker-sparql.c''', + deps='LIBNET tevent-glib-glue ' + bld.env['libtracker'], + enabled=bld.env.with_wsp) + # RPC_SERVICE bld.SAMBA3_SUBSYSTEM('RPC_SERVER_REGISTER', source='rpc_ep_register.c ../librpc/rpc/dcerpc_ep.c', @@ -179,6 +184,11 @@ bld.SAMBA3_SUBSYSTEM('RPC_SOCK_HELPER', source='rpc_sock_helper.c', deps='RPC_SERVER_REGISTER') +bld.SAMBA3_SUBSYSTEM('WSPD', + source='wspd.c', + deps='samba-util', + enabled=bld.env.with_wsp) + bld.SAMBA3_SUBSYSTEM('EPMD', source='epmd.c', deps='samba-util') diff --git a/source3/rpc_server/wsp/wsp_gss.c b/source3/rpc_server/wsp/wsp_gss.c new file mode 100644 index 0000000..ce3b970 --- /dev/null +++ b/source3/rpc_server/wsp/wsp_gss.c @@ -0,0 +1,2233 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include +#include +#include +#include +#include +#include "wsp_gss.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "wsp_sparql_conv.h" +#include "serverid.h" +#include "messages.h" +#include "rpc_server/rpc_pipes.h" +#include "rpc_server/rpc_server.h" +#include "wsp_srv_tracker_abs_if.h" +#include "util/tevent_ntstatus.h" + +#define MSG_HEADER_SIZE 16 + +static struct client_info *find_client_info(uint32_t handle, + struct gss_state *gss_state); + +struct dummy_async_state +{ +}; + +enum wsp_server_state { + NOT_INITIALISED, + RUNNING, +}; + +struct uint32_list +{ + struct uint32_list *prev, *next; + uint32_t number; +}; + +struct client_version_map { + struct client_version_map *prev, *next; + uint32_t fid_handle; + uint32_t version; +}; + +struct client_info { + struct client_info *prev, *next; + uint32_t handle; + uint32_t rowstart_index; /* index where 'rows' starts */ + uint32_t total_rows; /* num rows stored*/ + bool nomorerowstoreturn; /* num rows stored*/ + /*includes those processed already*/ + struct wsp_cbasestoragevariant **rows; +}; + +struct wsp_client_data +{ + struct gss_state *gss_state; + struct named_pipe_client *npc; + uint32_t fid; + bool is_64bit; +}; + +struct gss_state { + + struct uint32_list *ConnectedClientsIdentifiers; + struct client_version_map *ConnectedClientVersions; + struct client_info *client_info_map; + enum wsp_server_state wsp_server_state; + struct tevent_context *ev; + struct messaging_context *msg_ctx; + struct wsp_abstract_state *wsp_abstract_state; +}; + +/* fake some indirection for future backend support */ +static struct wsp_abstract_interface *get_impl(void) +{ + return tracker_wsp_abs_interace(); +} + +static struct uint32_list *get_connected_client_entry(uint32_t handle, + struct gss_state *gss_state) +{ + struct uint32_list *item = gss_state->ConnectedClientsIdentifiers; + for (; item; item = item->next) { + DBG_INFO("compare 0x%x with 0x%x\n", (uint32_t)handle, (uint32_t)item->number); + if (handle == item->number) { + return item; + } + } + return NULL; +} +static bool has_connected_client(uint32_t handle, struct gss_state *state) +{ + return get_connected_client_entry(handle, state) != NULL; +} + +static bool extract_connectin_propsets(TALLOC_CTX *ctx, + struct wsp_request *request, + struct connectin_propsets *propset) +{ + struct ndr_pull *ndr = NULL; + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + bool result = false; + DATA_BLOB blob; + blob.length = request->message.cpmconnect.cbblob1; + blob.data = request->message.cpmconnect.propsets; + ndr = ndr_pull_init_blob(&blob, ctx); + err = ndr_pull_connectin_propsets(ndr, ndr_flags, propset); + if (err) { + DBG_ERR("Failed to pull propset from propset blob, error %d\n", err); + goto out; + } + result = true; + +out: + return result; +} + +static bool get_property(uint32_t propid, struct wsp_cdbpropset *props, + struct wsp_cdbprop **prop_result) +{ + bool result = false; + int i; + for (i = 0; i < props->cproperties; i++) { + if (props->aprops[i].dbpropid == propid) { + *prop_result = &props->aprops[i]; + result = true; + break; + } + } + return result; +} + +/* stub for getting lcid */ +static uint32_t get_lcid(void) +{ + /* en-us */ + return 0x00000409; +} + +static uint32_t calculate_checksum(DATA_BLOB *blob, struct wsp_header *hdr) +{ + uint32_t i; + /* point at payload */ + uint8_t *buffer = blob->data + MSG_HEADER_SIZE; + uint32_t buf_size = blob->length - MSG_HEADER_SIZE; + uint32_t nwords = buf_size/4; + uint32_t offset = 0; + uint32_t checksum = 0; + + for(i = 0; i < nwords; i++) { + checksum += IVAL(buffer, offset); + offset += 4; + } + + checksum ^= XOR_CONST; + checksum -= hdr->msg; + return checksum; +} + +static bool verify_checksum(DATA_BLOB *blob, struct wsp_header *hdr) +{ + return calculate_checksum(blob, hdr) == hdr->checksum; +} + +/* MS-WSP 2.2.3.2, MS-WSP 3.1.5.2.1 */ +static struct tevent_req *handle_connect(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + NTSTATUS status; + uint32_t handle = client->client_data->fid; + struct connectin_propsets propsets; + struct wsp_cpmconnectin *client_info; + struct wsp_cdbprop *catalog_name; + struct uint32_list *item; + struct client_version_map *version_info; + uint32_t dwwinvermajor = 0; + uint32_t dwwinverminor = 0; + uint32_t dwnlsvermajor = 0; + uint32_t dwnlsverminor = 0; + uint32_t serverversion = 0; + bool supportsversioninginfo = false; + struct wsp_cpmconnectout *msg_out = &response->message.cpmconnect; + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create( + ctx, + &state, + struct dummy_async_state); + struct gss_state *gss_state = client->client_data->gss_state; + ZERO_STRUCT(propsets); + if (has_connected_client(handle, gss_state)) { + DBG_ERR("error client %d is already connected\n", handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!extract_connectin_propsets(state, request, &propsets)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!get_property(DBPROP_CI_CATALOG_NAME, &propsets.propertyset1, + &catalog_name)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (catalog_name->vvalue.vtype != VT_LPWSTR) { + DBG_ERR("incorrect type %d for DBPROP_CI_CATALOG_NAME \n", + catalog_name->vvalue.vtype); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->IsCatalogAvailable(client, + catalog_name->vvalue.vvalue.vt_lpwstr.value)){ + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (request->message.cpmconnect.iclientversion > 0x00000109) { + if (!verify_checksum(in_data, &request->header)) { + DBG_ERR("invalid checksum 0x%x\n", + request->header.checksum); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + } + item = talloc_zero(gss_state, + struct uint32_list); + item->number = handle; + DLIST_ADD_END(gss_state->ConnectedClientsIdentifiers, item); + + /* + * TODO not quite sure about the queryidentifier, documentation + * is not clear to me, for the moment I use the handle as the + * query identifier (e.g. only one query is possible per client) + */ + abs_interface->StoreClientInformation(client, + (uint32_t)handle, + &request->message.cpmconnect, + handle); + client_info = abs_interface->GetClientInformation(client, + handle); + + if (!client_info) { + DBG_ERR("error, no client info for handle %d available\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + version_info = talloc_zero(gss_state, + struct client_version_map); + version_info->fid_handle = handle; + version_info->version = request->message.cpmconnect.iclientversion; + + DLIST_ADD_END(gss_state->ConnectedClientVersions, version_info); + /* we need to hold onto the cpmconnect message */ + talloc_steal(get_connected_client_entry(handle, gss_state), + request); + + abs_interface->GetServerVersions(client, + &dwwinvermajor, &dwwinverminor, + &dwnlsvermajor, + &dwnlsverminor, + &serverversion, + &supportsversioninginfo); + + msg_out->server_version = serverversion; + client->client_data->is_64bit = + (serverversion & 0xffff0000) && (version_info->version & 0xffff0000); + + if (supportsversioninginfo) { + msg_out->version_dependant.version_info.dwwinvermajor = + dwwinvermajor; + msg_out->version_dependant.version_info.dwwinverminor = + dwwinverminor; + msg_out->version_dependant.version_info.dwnlsvermajor = + dwnlsvermajor; + msg_out->version_dependant.version_info.dwnlsverminor = + dwnlsverminor; + } + + status = NT_STATUS_OK; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +struct create_query_state +{ + uint32_t query_params_error; + uint32_t num_cursor_handles; + uint32_t *cursor_handles; + bool ftrueseq; + bool fworkid_unique; + bool can_query_now; + struct wsp_response *response; + DATA_BLOB *extra_blob; +}; + +static void handle_createquery_done(struct tevent_req *subreq); + +/* MS-WSP 2.2.3.4, MS-WSP 3.1.5.2.2 */ +static struct tevent_req *handle_createquery(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + struct wsp_cpmcreatequeryin *query = &request->message.cpmcreatequery; + struct wsp_ccolumnset *projected_col_offsets = NULL; + struct wsp_crestrictionarray *restrictionset = NULL; + struct wsp_csortset *sort_orders = NULL; + struct wsp_ccategorizationset *groupings = NULL; + struct wsp_crowsetproperties *rowsetproperties = + &query->rowsetproperties; + struct wsp_cpidmapper *pidmapper = &query->pidmapper; + struct wsp_ccolumngrouparray *grouparray = &query->grouparray; + + struct client_info *info = find_client_info(handle, gss_state); + struct tevent_req *req, *subreq = NULL; + struct create_query_state *state = NULL; + NTSTATUS status; + + if (!info) { + info = talloc_zero(gss_state, struct client_info); + info->handle = handle; + DLIST_ADD_END(gss_state->client_info_map, info); + } + + req = tevent_req_create(gss_state, &state, struct create_query_state); + if (!req) { + return NULL; + } + + if (!verify_checksum(in_data, &request->header)) { + DBG_ERR("invalid checksum 0x%x\n", + request->header.checksum); + status = NT_STATUS_INVALID_PARAMETER; + goto error_out; + } + state->extra_blob = extra_out_blob; + state->response = response; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto error_out; + } + + if (query->ccolumnsetpresent) { + projected_col_offsets = &query->columnset.columnset; + } + if (query->crestrictionpresent) { + restrictionset = &query->restrictionarray.restrictionarray; + } + if (query->csortsetpresent) { + sort_orders = &query->sortset.sortset; + } + if (query->ccategorizationsetpresent) { + groupings = &query->ccategorizationset.ccategorizationset; + if (groupings->size > 1) { + DBG_WARNING("can't yet handle multiple categories\n"); + status = NT_STATUS_INVALID_PARAMETER; + goto error_out; + } + } + + state->num_cursor_handles = groupings ? groupings->size + 1 : 1; + subreq = abs_interface->RunNewQuery_send(ctx, + client, + handle, + projected_col_offsets, + restrictionset, + sort_orders, + groupings, + rowsetproperties, + pidmapper, + grouparray, + get_lcid(), + /* out */ + &state->query_params_error, + &state->cursor_handles, + &state->ftrueseq, + &state->fworkid_unique, + &state->can_query_now); + if (!subreq) { + goto error_out; + } + + tevent_req_set_callback(subreq, handle_createquery_done, req); + return req; +error_out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static void handle_createquery_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct create_query_state *state = tevent_req_data(req, + struct create_query_state); + struct wsp_response *response = state->response; + uint32_t *pcursors = state->cursor_handles; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + int i; + int pos = 0; + talloc_free(subreq); + + if (has_error || !state->can_query_now || state->query_params_error) { + if (has_error == false) { + if(state->query_params_error) { + status = NT_STATUS(state->query_params_error); + } else { + status = NT_STATUS_INVALID_PARAMETER; + } + } + tevent_req_nterror(req, status); + return; + } + /* + * extra_blob is for tacking on typically dynamic content at the + * end of the message buffer that isn't easily (or at all) + * expressible in idl + */ + state->extra_blob->length = + sizeof(uint32_t) * state->num_cursor_handles; + state->extra_blob->data = talloc_zero_array(state, + uint8_t, + state->extra_blob->length); + for (i = 0; i < state->num_cursor_handles; i++) { + SIVAL(state->extra_blob->data, pos, pcursors[i]); + pos += sizeof(uint32_t); + } + + status = NT_STATUS_OK; + response->header.status = NT_STATUS_V(status); + tevent_req_done(req); +} + +struct query_status_state +{ + uint32_t status; + struct wsp_response *response; +}; + +static void handle_querystatus_done(struct tevent_req *subreq); + + +/* MS-WSP 2.2.3.6, MS-WSP 3.1.5.2.3 */ +static struct tevent_req *handle_querystatus(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + NTSTATUS status; + uint32_t handle = client->client_data->fid; + uint32_t hcursor = request->message.cpmgetquerystatus.hcursor; + struct tevent_req *req, *subreq = NULL; + struct query_status_state *state; + struct gss_state *gss_state = client->client_data->gss_state; + req = tevent_req_create(gss_state, &state, struct query_status_state); + if (!req) { + return NULL; + } + state->status = NT_STATUS_V(NT_STATUS_OK); + state->response = response; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->ClientQueryHasCursorHandle(client, + handle, hcursor)) { + DBG_ERR("no cursor %d for handle %d\n", hcursor, handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + subreq = abs_interface->GetQueryStatus_send(state, + client, + handle, + &state->status); + tevent_req_set_callback(subreq, handle_querystatus_done, req); + return req; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static void handle_querystatus_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct query_status_state *state = + tevent_req_data(req, struct query_status_state); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + state->response->message.cpmgetquerystatus.qstatus = state->status; + tevent_req_done(req); +} + +struct handle_querystatusex_state +{ + struct wspd_client_state *client; + struct wsp_response *response; + struct wsp_cpmcistateinout cistate; + struct wsp_abstract_interface *abs_interface; + uint32_t rows; + uint32_t handle; + uint32_t has_newrows; + uint32_t hcursor; + uint32_t bmk; +}; + +static void handle_querystatusex_done(struct tevent_req *subreq); + +/* MS-WSP 2.2.3.7, MS-WSP 3.1.5.2.4 */ +static struct tevent_req *handle_querystatusex(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + uint32_t hcursor = request->message.cpmgetquerystatusex.hcursor; + uint32_t bmk = request->message.cpmgetquerystatusex.bmk; + NTSTATUS status; + struct tevent_req *req, *subreq = NULL; + struct handle_querystatusex_state *state; + struct gss_state *gss_state = client->client_data->gss_state; + + req = tevent_req_create(gss_state, &state, + struct handle_querystatusex_state); + ZERO_STRUCTP(state); + state->client = client; + state->response = response; + state->abs_interface = abs_interface; + state->handle = handle; + state->hcursor = hcursor; + state->bmk = bmk; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->ClientQueryHasCursorHandle(client, + handle, hcursor)) { + DBG_ERR("no cursor %d for handle %d\n", hcursor, handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + + subreq = abs_interface->GetState_send(state, client, + &state->cistate); + if (!subreq) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + tevent_req_set_callback(subreq, handle_querystatusex_done, req); + return req; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static void getquerystatus_done(struct tevent_req *subreq); +static void handle_querystatusex_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct handle_querystatusex_state *state = + tevent_req_data(req, struct handle_querystatusex_state); + + struct wsp_cpmgetquerystatusexout *out = + &state->response->message.cpmgetquerystatusex; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + + if (has_error) { + tevent_req_nterror(req, status); + return; + } + TALLOC_FREE(subreq); + out->cfiltereddocuments = state->cistate.cfiltereddocuments; + out->cdocumentstofilter = state->cistate.ctotaldocuments; + + subreq = state->abs_interface->GetQueryStatus_send(state, + state->client, + state->handle, + &out->qstatus); + if (!subreq) { + status = NT_STATUS_INVALID_PARAMETER; + tevent_req_nterror(req, status); + return; + } + tevent_req_set_callback(subreq, getquerystatus_done, req); +} + +static void getratiofinished_done(struct tevent_req *subreq); +static void getquerystatus_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct handle_querystatusex_state *state = + tevent_req_data(req, struct handle_querystatusex_state); + + struct wsp_cpmgetquerystatusexout *out = + &state->response->message.cpmgetquerystatusex; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + subreq = state->abs_interface->GetRatioFinishedParams_send(state, + state->client, + state->handle, + state->hcursor, + &out->dwratiofinisheddenominator, + &out->dwratiofinishednumerator, + &state->rows, + &state->has_newrows); + if (!subreq) { + status = NT_STATUS_INVALID_PARAMETER; + tevent_req_nterror(req, status); + return; + } + tevent_req_set_callback(subreq, getratiofinished_done, req); +} + +static void getexpensiveprops_done(struct tevent_req *subreq); +static void getratiofinished_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct handle_querystatusex_state *state = + tevent_req_data(req, struct handle_querystatusex_state); + + struct wsp_cpmgetquerystatusexout *out = + &state->response->message.cpmgetquerystatusex; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + out->irowbmk = + state->abs_interface->GetApproximatePosition(state->client, + state->handle, + state->hcursor, + state->bmk); + out->whereid = state->abs_interface->GetWhereid(state->client, + state->handle); + + subreq = state->abs_interface->GetExpensiveProperties_send( + state, + state->client, + state->handle, + state->hcursor, + &out->crowstotal, + &out->resultsfound, + &out->maxrank); + + if (!subreq) { + status = NT_STATUS_INVALID_PARAMETER; + tevent_req_nterror(req, status); + return; + } + tevent_req_set_callback(subreq, getexpensiveprops_done, req); +} + +static void getexpensiveprops_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +/* MS-WSP 2.2.3.13, MS-WSP 3.1.5.2.5 */ +static struct tevent_req *handle_getratiofinishedin(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + NTSTATUS status; + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + struct tevent_req *req = NULL; + struct wsp_cpmratiofinishedin *ratio_in = + &request->message.wsp_cpmratiofinished; + struct wsp_cpmratiofinishedout *ratio_out = + &response->message.wsp_cpmratiofinished; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + status = NT_STATUS_OK; + req = abs_interface->GetRatioFinishedParams_send(ctx, + client, + handle, + ratio_in->hcursor, + &ratio_out->uldenominator, + &ratio_out->ulnumerator, + &ratio_out->crows, + &ratio_out->fnewrows); + return req; +out: + if (req) { + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + req = tevent_req_post(req, gss_state->ev); + } + return req; +} + +static bool push_string_value(TALLOC_CTX *ctx, + bool is_64bit, + struct wsp_cbasestoragevariant *col_val, + uint8_t *buf_start, + uint8_t *value_buf, + uint32_t *cur_rowbuf_end_pos, + uint32_t *value_length, + uint64_t address_adjustment, + uint32_t row_boundry) +{ + uint32_t address = 0; + uint32_t offset = 8; + const char *str_val; + *value_length = 0; + str_val = col_val->vvalue.vt_lpwstr.value; + DBG_DEBUG("string value %s\n", str_val); + /* lenght is num bytes (including null term) */ + *value_length += + ((strlen(str_val) + 1) * 2); + *cur_rowbuf_end_pos = + *cur_rowbuf_end_pos - *value_length; + if (row_boundry >= *cur_rowbuf_end_pos) { + /* + * col variant value about to corrupt + * fixed buffer + */ + DBG_NOTICE("col value overlapping fixed buffer " + "area\n"); + return false; + } + push_string(buf_start + *cur_rowbuf_end_pos, + str_val, ((strlen(str_val) + 1) * 2), + STR_UNICODE | STR_TERMINATE); + + address = *cur_rowbuf_end_pos + address_adjustment; + + if (is_64bit) { + *value_length += 0x18; /* variant */ + SBVAL(value_buf, offset, address); + } else { + *value_length += 0x10; /* variant */ + SIVAL(value_buf, offset, address); + } + return true; +} + +static bool push_vecstring_values( + TALLOC_CTX *ctx, + bool is_64bit, + struct wsp_cbasestoragevariant *col_val, + uint8_t *buf_start, + uint8_t *value_buf, + uint32_t *cur_rowbuf_end_pos, + uint32_t *value_length, + uint64_t address_adjustment, + uint32_t row_boundry) +{ + uint32_t offset = 8; + uint32_t increment = is_64bit ? 8 : 4; + uint32_t i; + uint32_t count = + col_val->vvalue.vt_lpwstr_v.vvector_elements; + uint64_t *addresses = talloc_zero_array(ctx, + uint64_t, count); + uint64_t address_of_address_vec; + if (addresses == NULL) { + DBG_ERR("out of memory\n"); + return false; + } + + + DBG_DEBUG("string value (vector) with %d strings\n", count); + + *value_length += (count * increment); + + *cur_rowbuf_end_pos = + *cur_rowbuf_end_pos - *value_length; + + address_of_address_vec = + *cur_rowbuf_end_pos + address_adjustment; + /* + * write out the strings and record the address + * they are written to. + * lenght is num bytes (including null term) + */ + for (i = 0; i < count; i++) { + const char *str_val = + col_val->vvalue.vt_lpwstr_v.vvector_data[i].value; + *value_length += + ((strlen(str_val) + 1) * 2); + /* pos of string */ + *cur_rowbuf_end_pos = + *cur_rowbuf_end_pos - *value_length; + if (row_boundry >= *cur_rowbuf_end_pos) { + /* + * col variant value about to corrupt + * fixed buffer + */ + DBG_NOTICE("col value overlapping " + "fixed buffer area\n"); + return false; + } + push_string(buf_start + *cur_rowbuf_end_pos, + str_val, ((strlen(str_val) + 1) * 2), + STR_UNICODE | STR_TERMINATE); + /* save the addresses */ + addresses[i] = + *cur_rowbuf_end_pos + address_adjustment; + } + + /* write out the address vector */ + for (i = 0; i < count; i++) { + if (is_64bit) { + SBVAL(buf_start, + (address_of_address_vec - address_adjustment) + (i * increment), + addresses[i]); /* 1 item */ + } else { + SIVAL(buf_start, + (address_of_address_vec - address_adjustment) + (i * increment), + addresses[i]); /* 1 item */ + } + } + + /* write out count and address of vector of addresses */ + if (is_64bit) { + SBVAL(value_buf, offset, count); + offset += increment; + SBVAL(value_buf, offset, address_of_address_vec); + *value_length += 0x18; /* variant */ + } else { + SIVAL(value_buf, offset, count); + offset += increment; + SIVAL(value_buf, offset, address_of_address_vec); + *value_length += 0x10; /* variant */ + } + TALLOC_FREE(addresses); + return true; +} + +static bool push_column_value(TALLOC_CTX *ctx, + bool is_64bit, + struct wsp_cbasestoragevariant *col_val, + uint8_t *buf_start, + uint8_t *value_buf, uint8_t *row_buf, + uint32_t *cur_rowbuf_end_pos, + uint32_t *value_length, + uint64_t address_adjustment, + uint32_t row_boundry) +{ + switch (col_val->vtype) { + case VT_BOOL: + case VT_I2: + case VT_UI2: + SSVAL(value_buf, 8, col_val->vvalue.vt_ui2); + break; + case VT_UI4: + case VT_I4: + case VT_INT: + case VT_UINT: + SIVAL(value_buf, 8, col_val->vvalue.vt_ui4); + break; + case VT_FILETIME: + case VT_UI8: + case VT_I8: + case VT_R8: { + uint64_t hyper_val; + struct wsp_hyper *phyper = &col_val->vvalue.vt_ui8; + wsp_hyper_to_uint64(phyper, &hyper_val); + SBVAL(value_buf, 8, hyper_val); + break; + } + case VT_VECTOR | VT_LPWSTR: + if (!push_vecstring_values(ctx, is_64bit, col_val, + buf_start, value_buf, + cur_rowbuf_end_pos, + value_length, address_adjustment, + row_boundry)) { + return false; + } + break; + case VT_LPWSTR: + if (!push_string_value(ctx, is_64bit, col_val, + buf_start, value_buf, + cur_rowbuf_end_pos, + value_length, address_adjustment, + row_boundry)) { + return false; + } + break; + } + return true; +} + +static bool push_column(TALLOC_CTX *ctx, + struct wsp_ctablecolumn *tab_col, + struct wsp_cbasestoragevariant *col_val, + DATA_BLOB *blob, uint32_t *cur_rowbuf_end_pos, + int row, uint64_t address_adjustment, + uint32_t row_width, bool is_64bit) +{ + uint8_t *row_buff = blob->data + (row * row_width); + uint32_t row_boundry = (row * row_width) + row_width; + if (row_boundry >= *cur_rowbuf_end_pos) { + /* + * abandon row processing, variant and fixed portions about + * to collide + */ + DBG_NOTICE("row too big to fit...\n"); + return false; + } + SSVAL(row_buff, 0, 0xdead); + if (tab_col->statusused) { + if (col_val->vtype == VT_NULL) { + *(row_buff + tab_col->statusoffset.value) = + STORESTATUSNULL; + } else { + *(row_buff + tab_col->statusoffset.value) = + STORESTATUSOK; + } + } + /* default value.. adjusted below if necessary when processing values */ + if (tab_col->lengthused) { + SIVAL(row_buff, tab_col->lengthoffset.value, 0x10); + } + + if (tab_col->valueused) { + uint32_t new_length = 0; + uint8_t *value_buf = row_buff + tab_col->valueoffset.value; + SSVAL(value_buf, 0, col_val->vtype); /* vtype */ + if (col_val->vtype != VT_NULL) { + bool ok; + SSVAL(value_buf, 2, 0); /* reserved1 */ + SIVAL(value_buf, 4, 0); /* reserved2 */ + ok = push_column_value(ctx, is_64bit, + col_val, blob->data, + value_buf, row_buff, + cur_rowbuf_end_pos, + &new_length, address_adjustment, + row_boundry); + if (!ok) { + return false; + } + if (tab_col->lengthused && new_length) { + SIVAL(row_buff, + tab_col->lengthoffset.value, new_length); + } + + } else { + SSVAL(value_buf, 0, VT_EMPTY); /* vtype */ + } + } + return true; +} + +struct seekratio_data +{ + uint32_t crowstotal; + uint32_t resultsfound; + uint32_t maxrank; +}; + +struct getrows_state +{ + struct client_info *info; + struct wspd_client_state *client; + DATA_BLOB *extra_out_blob; + struct wsp_cpmgetrowsin *rowsin; + struct wsp_cpmgetrowsout *rowsout; + struct wsp_abstract_interface *abs_interface; + struct gss_state *gss_state; + + uint32_t pad_adjust; + uint8_t* row_buff; + uint32_t buf_size; + uint32_t error; + uint32_t ncols; + uint32_t rowsreturned; + uint32_t rows_requested; + uint32_t cur_rowbuf_end_pos; + uint64_t address_adjustment; + uint32_t index; + uint32_t handle; + uint32_t hibufbits; + struct wsp_ctablecolumn *binding; + bool nomorerowstoreturn; + bool is_64bit; /* #TODO need to also check 64k mode */ + uint32_t *resp_status; + struct seekratio_data *seekratio; +}; + +static void handle_getrows_done(struct tevent_req *subreq); + +static void fill_rows_buffer(struct getrows_state *state, + struct gss_state *gss_state, + struct wsp_cbasestoragevariant **rowsarray) +{ + int i, j; + uint32_t nrows = 0; + uint32_t hcursor; + uint32_t chapter; + struct wsp_cbasestoragevariant *row; + + DATA_BLOB rows_blob; + + rows_blob.data = state->row_buff; + rows_blob.length = state->buf_size; + + hcursor = state->rowsin->hcursor; + chapter = state->rowsin->chapt; + if (state->error) { + *state->resp_status = NT_STATUS_V(NT_STATUS_INVALID_PARAMETER); + return; + } + + for (i = 0, nrows = 0; i < state->rowsreturned; i++) { + row = rowsarray[i]; + for (j = 0; j < state->ncols; j++) { + bool ok; + struct wsp_cbasestoragevariant *col_val = + &row[j]; + DBG_INFO("processing col[%d]\n", j); + ok = push_column(state, + &state->binding[j], + col_val, &rows_blob, + &state->cur_rowbuf_end_pos, i, + state->address_adjustment, + state->rowsin->cbrowWidth, + state->is_64bit); + if (!ok) { + DBG_NOTICE("readjusting rows returned from %d " + "to %d\n", state->rowsreturned, nrows); + state->rowsreturned = nrows; + /* + * reset nomorerowstoreturn if it's already set + * as we no longer are returning all rows + */ + if (state->nomorerowstoreturn) { + state->nomorerowstoreturn = false; + } + break; + } + } + nrows++; + } + + state->abs_interface->SetNextGetRowsPosition( + state->client, + state->handle, + hcursor, chapter, + state->index + i); + state->rowsout->rowsreturned = state->rowsreturned; + state->rowsout->etype = 0; + state->rowsout->chapt = state->rowsin->chapt; + + if (!state->nomorerowstoreturn + && (state->rows_requested != state->rowsreturned)) { + if (state->rowsin->etype == EROWSEEKAT) { + /* follow what windows seems to do, use a skip of 1 */ + uint32_t skip = 1; + /* + * 3.1.5.2.6 Receiving a CPMGetRowsIn Request + * (bullet 5 - 10) + * MS-WSP is confusing here again, but.. at least by + * observation with SeekDescription etype EROWSEEKAT + * we set the response eType to be the eType from + * GetRowsIn *BUT* not copy it, instead we populate the + * bookmark offset with the index and skip values to + * allow the client restart the search. + * Note: we only seem to need to do this when we + * haven't been able to fill the buffer with the + * requested number of rows + * #FIXME not sure how we hande the other types + */ + state->rowsout->etype = state->rowsin->etype; + state->rowsout->seekdescription.crowseekat.cskip = skip; + + state->rowsout->seekdescription.crowseekat.bmkoffset = + state->index + i - 1 - skip; + } + } + /* + * assuming no seekdescription we rewind row_buf back the max padding + */ + if (!state->rowsout->etype) { + state->row_buff -= state->pad_adjust; + state->buf_size += state->pad_adjust; + } + + rows_blob.data = state->row_buff; + rows_blob.length = state->buf_size; + + /* set up out param */ + *state->extra_out_blob = rows_blob; + + if (state->nomorerowstoreturn) { + *state->resp_status = DB_S_ENDOFROWSET; + } +} + +static struct tevent_req *process_rows_for_index(TALLOC_CTX *ctx, + struct tevent_req *req, + struct getrows_state *state) +{ + uint32_t hcursor; + uint32_t chapter; + uint32_t handle; + uint32_t fetchforward; + struct wsp_abstract_interface *abs_interface = state->abs_interface; + struct client_info *info = state->info; + struct wsp_cpmgetrowsin *rowsin = state->rowsin; + struct tevent_req* subreq; + + hcursor = state->rowsin->hcursor; + chapter = state->rowsin->chapt; + handle = state->handle; + fetchforward = rowsin->fbwdfetch; + if (state->index < 1) { + /* something odd gone wrong */ + DBG_ERR("illegal value for index %d\n", state->index); + goto error_out; + } + + abs_interface->SetNextGetRowsPosition(state->client, + handle, hcursor, + chapter, state->index); + state->binding = abs_interface->GetBindings(state->client, + handle, hcursor, + &state->ncols); + /* + * allocate the full amount of possible padding (rowsin->cbreserved) + * note: cbreserved includes the header size (16) plus size of + * message (not including seekdescription) (12) + any possible + * seekdescription ( variable 0 - 12 bytes ) + * e.g. if cbreserved is 40 then we increase the size of the + * buffer by 12 (thats the max padding), if the response message + * contains a seek description we need discard the seek description + * bytes from the start of the buffer. + * + */ + state->pad_adjust = (rowsin->cbreserved - (MSG_HEADER_SIZE + 12)); + + state->buf_size = + rowsin->cbreadbuffer - rowsin->cbreserved + state->pad_adjust; + state->row_buff = talloc_zero_array(ctx, uint8_t, state->buf_size); + + /* position buffer to write into after max padding */ + state->row_buff = state->row_buff + state->pad_adjust; + /* similary adjust size */ + state->buf_size = state->buf_size - state->pad_adjust; + state->cur_rowbuf_end_pos = state->buf_size; + + info->rows = talloc_zero_array(info, struct wsp_cbasestoragevariant*, + rowsin->crowstotransfer); + subreq = abs_interface->GetRows_send(state, state->client, + handle, hcursor, + rowsin->crowstotransfer, + fetchforward, info->rows, + &info->nomorerowstoreturn, + &info->total_rows, &state->error); + if (!subreq) { + goto error_out; + } + tevent_req_set_callback(subreq, handle_getrows_done, req); + return req; +error_out: + if (!tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER)) { + tevent_req_done(req); + } + return tevent_req_post(req, state->gss_state->ev); +} + +static void get_expensive_props_done(struct tevent_req *subreq); + +/* MS-WSP 2.2.3.2, MS-WSP 3.1.5.2.6 */ +static struct tevent_req *handle_getrows(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + NTSTATUS status; + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + struct wsp_cpmgetrowsin *rowsin = &request->message.cpmgetrows; + struct wsp_cpmgetrowsout *rowsout = &response->message.cpmgetrows; + uint32_t hcursor; + uint32_t chapter; + uint32_t oldindex; + + struct client_info *info = find_client_info(handle, gss_state); + struct tevent_req *req = NULL, *subreq = NULL; + struct getrows_state *state = NULL; + + if (!info) { + DBG_ERR("no cached data for query with handle %d\n", handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + req = tevent_req_create(gss_state, &state, struct getrows_state); + + if (!req) { + return NULL; + } + + ZERO_STRUCTP(state); + state->client = client; + state->gss_state = client->client_data->gss_state; + state->extra_out_blob = extra_out_blob; + state->nomorerowstoreturn = true; + state->resp_status = &response->header.status; + state->is_64bit = client->client_data->is_64bit; + state->abs_interface = abs_interface; + state->rowsout = rowsout; + state->handle = handle; + state->info = info; + state->rowsin = rowsin; + state->rowsout = rowsout; + + hcursor = rowsin->hcursor; + chapter = rowsin->chapt; + state->rows_requested = rowsin->crowstotransfer; + if (state->is_64bit) { + state->address_adjustment = request->header.ulreserved2; + state->address_adjustment <<= 32; + state->address_adjustment |= rowsin->ulclientbase; + } else { + state->address_adjustment = rowsin->ulclientbase; + } + state->address_adjustment += rowsin->cbreserved; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!verify_checksum(in_data, &request->header)) { + DBG_ERR("invalid checksum 0x%x\n", + request->header.checksum); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->ClientQueryHasCursorHandle(client, + handle, hcursor)) { + DBG_ERR("no cursor %d for handle %d\n", hcursor, handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->HasBindings(client, handle, hcursor)) { + DBG_ERR("no bindings for handle %d and cursor %d\n", + handle, hcursor); + status = NT_STATUS(E_UNEXPECTED); + goto out; + } + + oldindex = abs_interface->GetNextGetRowsPosition(client, + handle, hcursor, + chapter); + switch (rowsin->etype) { + case EROWSEEKNONE: + state->index = oldindex; + break; + case EROWSEEKNEXT: + state->index = + oldindex + rowsin->seekdescription.crowseeknext.cskip; + break; + case EROWSEEKAT: { + uint32_t cskip = + rowsin->seekdescription.crowseekat.cskip; + uint32_t bmkoffset = + rowsin->seekdescription.crowseekat.bmkoffset; + state->index = abs_interface->GetBookmarkPosition( + client, + handle, + hcursor, + bmkoffset); + state->index += cskip; + break; + } + case EROWSEEKATRATIO: { + uint32_t ulnumerator = + rowsin->seekdescription.crowseekatratio.ulnumerator; + uint32_t uldenominator = + rowsin->seekdescription.crowseekatratio.uldenominator; + state->seekratio = talloc_zero(state, + struct seekratio_data); + if (!uldenominator || uldenominator > ulnumerator) { + /* DB_E_BADRATIO */ + response->header.status = 0x80040E12; + goto out; + } + + + subreq = abs_interface->GetExpensiveProperties_send( + state, + client, + handle, + hcursor, + &state->seekratio->crowstotal, + &state->seekratio->resultsfound, + &state->seekratio->maxrank); + + if (!subreq) { + goto out; + } + tevent_req_set_callback(subreq, + get_expensive_props_done, + req); + return req; + break; + } + case EROWSEEKBYBOOKMARK: + + DBG_ERR("etype EROWSEEKBYBOOKMARK is unsupported for " + "GetRowsIn message"); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + break; + default: + DBG_ERR("illegal value for etype %d\n", + rowsin->etype); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + break; + } + + return process_rows_for_index(state, req, state); +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static void get_expensive_props_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct getrows_state *state = + tevent_req_data(req, struct getrows_state); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + if (state->seekratio) { + struct wsp_cpmgetrowsin *rowsin = state->rowsin; + uint32_t ulnumerator = + rowsin->seekdescription.crowseekatratio.ulnumerator; + uint32_t uldenominator = + rowsin->seekdescription.crowseekatratio.uldenominator; + uint32_t crowstotal = state->seekratio->crowstotal; + state->index = (ulnumerator/uldenominator) * crowstotal; + } + TALLOC_FREE(subreq); + + if (has_error) { + tevent_req_nterror(req, status); + return; + } + process_rows_for_index(state, req, state); +} + +static void handle_getrows_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct getrows_state *state = + tevent_req_data(req, struct getrows_state); + struct wsp_cbasestoragevariant **rows; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + rows = state->info->rows; + state->info->rowstart_index = state->index; + + state->nomorerowstoreturn = state->info->nomorerowstoreturn; + if (state->info->total_rows >= state->rows_requested) { + /* + * if total_rows we got is what we asked for it's + * possible that there are no more rows to get + */ + if (state->info->total_rows != state->rows_requested) { + state->nomorerowstoreturn = false; + } + state->rowsreturned = state->rows_requested; + } else { + state->rowsreturned = state->info->total_rows; + } + fill_rows_buffer(state, state->gss_state, rows); + tevent_req_done(req); +} + + +/* MS-WSP 2.2.3.10, MS-WSP 3.1.5.2.8 */ +static struct tevent_req *handle_setbindings(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + struct wsp_cpmsetbindingsin *bindings = + &request->message.cpmsetbindings; + uint32_t handle = client->client_data->fid; + NTSTATUS status; + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + struct gss_state *gss_state = client->client_data->gss_state; + int i; + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!verify_checksum(in_data, &request->header)) { + DBG_ERR("invalid checksum 0x%x\n", + request->header.checksum); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + /* + * #FIXME we should ideally be checking the integrity of + * bindings->acolumns. At the very least we filter out any + * non-variant columns as we just wont support these right now + * (documentation is unclear and windows explorer client doesn't + * use non-variant col values either) + */ + for (i = 0; i < bindings->ccolumns; i++) { + if (bindings->acolumns[i].vtype != VT_VARIANT) { + DBG_ERR("Illegal column value type %s\n", + get_vtype_name(bindings->acolumns[i].vtype)); + status = NT_STATUS(0x80070057); + goto out; + } + } + abs_interface->SetBindings(client, + handle, bindings->hcursor, + bindings->acolumns, + bindings->ccolumns); + /* keep the binding columns info */ + talloc_steal(get_connected_client_entry(handle, gss_state), + bindings->acolumns); + status = NT_STATUS_OK; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static struct client_info *find_client_info(uint32_t handle, + struct gss_state *gss_state) +{ + struct client_info *item; + for (item = gss_state->client_info_map; item; item = item->next){ + if (item->handle == handle) { + return item; + } + } + return NULL; +} + +static struct tevent_req *handle_getscopestats(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + NTSTATUS status; + struct wsp_cpmgetscopestatisticsout *statsout = + &response->message.cpmgetscopestatistics; + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + status = abs_interface->GetQueryStatistics( + client, + handle, + &statsout->dwindexeditems, + &statsout->dwoutstandingadds, + &statsout->dwoustandingmodifies); +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + + +static struct tevent_req *handle_rowsetnotify(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + NTSTATUS status; + bool more_events; + uint64_t data1; + uint64_t data2; + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + + struct wsp_cpmgetrowsetnotifyout *out = + &response->message.cpmgetrowsetnotifyout; + + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + abs_interface->GetLastUnretrievedEvent(client, + handle, &out->wid, + &out->eventinfo, + &more_events, + &out->rowitemstate, + &out->changeditemstate, + &out->rowsetevent, + &data1, + &data2); + out->eventinfo = (out->eventinfo << 1); + if (more_events) { + out->eventinfo = out->eventinfo | 0x1; + } else { + out->eventinfo = out->eventinfo & 0xFE; + } + memcpy(&out->rowseteventdata1, &data1, sizeof(data1)); + memcpy(&out->rowseteventdata2, &data2, sizeof(data2)); + status = NT_STATUS_OK; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static struct tevent_req *handle_freecursor(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + struct wsp_cpmfreecursorin *in = &request->message.cpmfreecursor; + struct wsp_cpmfreecursorout *out = &response->message.cpmfreecursor; + NTSTATUS status; + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + if (!has_connected_client(handle, gss_state)) { + DBG_ERR("no record of connected client for handle %d\n", + handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!abs_interface->ClientQueryHasCursorHandle(client, + handle, in->hcursor)) { + DBG_ERR("no cursor %d for handle %d\n", in->hcursor, handle); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + out->ccursorsremaining = abs_interface->ReleaseCursor(client, + handle, + in->hcursor); + + if (!out->ccursorsremaining) { + req = abs_interface->ReleaseQuery_send(state, client, handle); + } + status = NT_STATUS_OK; +out: + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +static void release_client_resources(struct wspd_client_state *client) +{ + uint32_t handle = client->client_data->fid; + struct gss_state *gss_state = client->client_data->gss_state; + struct uint32_list *id_item = gss_state->ConnectedClientsIdentifiers; + struct client_version_map *version_item = + gss_state->ConnectedClientVersions; + struct client_info *info = gss_state->client_info_map; + while (id_item) { + struct uint32_list *next_id = id_item->next; + if (id_item->number == handle) { + DLIST_REMOVE(gss_state->ConnectedClientsIdentifiers, + id_item); + TALLOC_FREE(id_item); + } + id_item = next_id; + } + while(version_item) { + struct client_version_map *next_version = version_item->next; + if (version_item->fid_handle == handle) { + DLIST_REMOVE(gss_state->ConnectedClientVersions, + version_item); + TALLOC_FREE(version_item); + } + version_item = next_version; + } + while(info) { + struct client_info *next_info = info->next; + if (info->handle == handle) { + DLIST_REMOVE(gss_state->client_info_map, + info); + TALLOC_FREE(info); + } + info = next_info; + } +} + +static int destroy_client_state(struct wspd_client_state *client) +{ + release_client_resources(client); + return 0; +} + +static int destroy_gss_state(struct gss_state *gss_state) +{ + TALLOC_FREE(gss_state->wsp_abstract_state); + return 0; +} + +static struct tevent_req *handle_disconnect(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + release_client_resources(client); + return abs_interface->ReleaseQuery_send(ctx, client, handle); +} + +static struct tevent_req *handle_setcope(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + uint32_t handle = client->client_data->fid; + struct dummy_async_state *state; + struct gss_state *gss_state = client->client_data->gss_state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + + /* #FIXME '0' isn't correct */ + NTSTATUS status = + abs_interface->SetScopePriority(client, handle,0); + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, gss_state->ev); +} + +/* + * dummy handler to just return a blank response message with 'success' + * status. + */ +static struct tevent_req *handle_dummy_handler(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface) +{ + struct dummy_async_state *state; + struct gss_state *gss_state = client->client_data->gss_state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + tevent_req_done(req); + return tevent_req_post(req, gss_state->ev); +} + +typedef struct tevent_req *(*msg_handler_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct wsp_request *request, + struct wsp_response *response, + DATA_BLOB *in_data, + DATA_BLOB *extra_out_blob, + struct wsp_abstract_interface *abs_interface); + +static struct { + uint32_t msgid; + msg_handler_fn msg_handler; +} msg_handlers [] = { + {CPMCONNECT, handle_connect}, + {CPMCREATEQUERY, handle_createquery}, + {CPMSETBINDINGSIN, handle_setbindings}, + {CPMGETQUERYSTATUS, handle_querystatus}, + {CPMGETQUERYSTATUSEX, handle_querystatusex}, + {CPMGETROWS, handle_getrows}, + {CPMGETSCOPESTATISTICS, handle_getscopestats}, + {CPMGETROWSETNOTIFY, handle_rowsetnotify}, + {CPMFREECURSOR, handle_freecursor}, + {CPMDISCONNECT, handle_disconnect}, + {CPMSETSCOPEPRIORITIZATION, handle_setcope}, + {CPMRESTARTPOSITIONIN, handle_dummy_handler}, + {CPMRATIOFINISHED, handle_getratiofinishedin}, + + /* start unimplemented messages */ + {CPMCOMPAREBMK, NULL}, + {CPMGETAPPROXIMATEPOSITION, NULL}, + {CPMGETNOTIFY, NULL}, + {CPMSENDNOTIFYOUT, NULL}, + {CPMCISTATEOUT, NULL}, + {CPMFETCHVALUE, NULL}, + {CPMSETCATSTATEIN, NULL}, + {CPMFINDINDICES, NULL}, +}; + +static struct { + uint32_t msgid; + const char *msg_name; +} msg_id_name_map [] = { + {CPMCONNECT, "CPMCONNECT"}, + {CPMCREATEQUERY, "CPMCREATEQUERY"}, + {CPMSETBINDINGSIN, "CPMSETBINDINGSIN"}, + {CPMGETQUERYSTATUS, "CPMGETQUERYSTATUS"}, + {CPMGETQUERYSTATUSEX, "CPMGETQUERYSTATUSEX"}, + {CPMDISCONNECT, "CPMDISCONNECT"}, + {CPMFREECURSOR, "CPMFREECURSOR"}, + {CPMGETROWS, "CPMGETROWS"}, + {CPMRATIOFINISHED, "CPMRATIOFINISHED"}, + {CPMCOMPAREBMK, "CPMCOMPAR"}, + {CPMGETAPPROXIMATEPOSITION, "CPMGETAPPROXIMATEPOSITION"}, + {CPMGETNOTIFY, "CPMGETNOTIFY"}, + {CPMSENDNOTIFYOUT, "CPMSENDNOTIFYOUT"}, + {CPMCISTATEOUT, "CPMCISTATEOUT"}, + {CPMFETCHVALUE, "CPMFETCHVALUE"}, + {CPMRESTARTPOSITIONIN, "CPMRESTARTPOSITIONIN"}, + {CPMSETCATSTATEIN, "CPMSETCATSTATEIN"}, + {CPMGETROWSETNOTIFY, "CPMGETROWSETNOTIFY"}, + {CPMFINDINDICES, "CPMFINDINDICES"}, + {CPMSETSCOPEPRIORITIZATION, "CPMSETSCOPEPRIORITIZATION"}, + {CPMGETSCOPESTATISTICS, "CPMGETSCOPESTATISTICS"}, +}; + +static const char *msgid_to_string(uint32_t msgid) +{ + int i; + const char *result = "UNKNOWN"; + for (i = 0; i < ARRAY_SIZE(msg_id_name_map); i++) { + if (msgid == msg_id_name_map[i].msgid ){ + result = msg_id_name_map[i].msg_name; + break; + } + } + return result; +}; +static msg_handler_fn get_wsp_msg_handler(uint32_t msgid) +{ + int i; + for(i = 0; i < ARRAY_SIZE(msg_handlers); i++) { + if (msg_handlers[i].msgid == msgid) { + if (!msg_handlers[i].msg_handler) { + DBG_WARNING("unhandled msgid 0x%x\n", msgid); + break; + } + return msg_handlers[i].msg_handler; + } + } + DBG_ERR("no handler for unknown msgid 0x%x\n", msgid); + return NULL; +} + +static bool extract_wsp_request(TALLOC_CTX *ctx, + DATA_BLOB *wsp_blob, + struct wsp_request *wsp_request) +{ + struct ndr_pull *ndr = NULL; + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + + DBG_DEBUG("got wsp message blob of size %d\n", (int)wsp_blob->length); + ndr = ndr_pull_init_blob(wsp_blob, ctx); + err = ndr_pull_wsp_request(ndr, ndr_flags, wsp_request); + if (err) { + return false; + } + return true; +} + +static void set_msg_checksum(DATA_BLOB *blob, struct wsp_header *hdr) +{ + uint32_t checksum = calculate_checksum(blob, hdr); + hdr->checksum = checksum; +} + +static enum ndr_err_code insert_checksum_into_msg_and_hdr(DATA_BLOB* blob, + struct wsp_header *header) +{ + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + TALLOC_CTX *ctx = talloc_init("insert"); + struct ndr_push *header_ndr = ndr_push_init_ctx(ctx); + + if ((blob->length > MSG_HEADER_SIZE) && (header->msg == CPMCONNECT + || header->msg == CPMCREATEQUERY + || header->msg == CPMSETBINDINGSIN + || header->msg == CPMFETCHVALUE)) { + + set_msg_checksum(blob, header); + } else { + DBG_DEBUG("Warning, trying to set checksum on message " + "that doesn't support a checksum\n"); + err = NDR_ERR_SUCCESS; + goto out; + } + /* + * alternatively we could just shove in the checksum at the + * appropriate offset. Safer though I think to use the standard + * routines, also it's probably an advantage to be able to + * rewrite out the msg header (in case of late setting of some status) + */ + err = ndr_push_wsp_header(header_ndr, ndr_flags, header); + if (err) { + DBG_ERR("Failed to push header, error %d\n", err); + goto out; + } + memcpy(blob->data, header_ndr->data, MSG_HEADER_SIZE); +out: + TALLOC_FREE(ctx); + return err; +} + +static bool insert_wsp_response(TALLOC_CTX *ctx, struct wsp_response *response, + DATA_BLOB *out_blob, DATA_BLOB *extra_out_blob) +{ + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + struct ndr_push* push_ndr; + enum ndr_err_code err; + bool header_only = false; + push_ndr = ndr_push_init_ctx(ctx); + + /* + * We don't send a response for CPMDISCONNECT + * When response->header.status == DB_S_ENDOFROWSET, this is a + * informational error and the rest of the message is expected to be + * filled out. + */ + if (response->header.msg != CPMDISCONNECT + &&( !response->header.status + || response->header.status == DB_S_ENDOFROWSET)) { + err = ndr_push_wsp_response(push_ndr, ndr_flags, + response); + } else { + err = ndr_push_wsp_header(push_ndr, ndr_flags, + &response->header); + header_only = true; + } + + if (err) { + DBG_ERR("failed to marshall response\n"); + return false; + } + *out_blob = ndr_push_blob(push_ndr); + if (!header_only && extra_out_blob->length) { + out_blob->data = talloc_realloc( + ctx, + out_blob->data, + uint8_t, + out_blob->length + extra_out_blob->length); + memcpy(out_blob->data + out_blob->length, + extra_out_blob->data, + extra_out_blob->length); + out_blob->length = out_blob->length + extra_out_blob->length; + } + err = insert_checksum_into_msg_and_hdr(out_blob, &response->header); + if (err) { + DBG_ERR("failed to insert checksum\n"); + return false; + } + return true; +} + +struct handle_wsp_state +{ + struct wsp_request *wsp_request; + struct wsp_response *response; + DATA_BLOB *out_blob; + DATA_BLOB extra_out_blob; + struct named_pipe_client *npc; +}; + +static void handle_wsp_done(struct tevent_req *subreq); +static struct tevent_req *handle_wsp_send(TALLOC_CTX *ctx, + struct wspd_client_state *client, + struct named_pipe_client *npc, + DATA_BLOB *in_blob, DATA_BLOB *out_blob) +{ + struct wsp_request *wsp_request; + struct wsp_response *response; + struct tevent_req *req, *subreq = NULL; + struct handle_wsp_state *state = NULL; + struct gss_state *gss_state = client->client_data->gss_state; + msg_handler_fn msg_handler; + NTSTATUS status; + + req = tevent_req_create(ctx, &state, struct handle_wsp_state); + if (!req) { + return NULL; + } + + response = talloc_zero(state, struct wsp_response); + wsp_request = talloc_zero(state, struct wsp_request); + ZERO_STRUCTP(out_blob); + + if (!extract_wsp_request(wsp_request, in_blob, wsp_request)) { + DBG_ERR("error extracting WSP message\n"); + goto err_out; + } + + state->wsp_request = wsp_request; + state->response = response; + state->out_blob = out_blob; + ZERO_STRUCT(state->extra_out_blob); + state->npc = npc; + response->header.msg = wsp_request->header.msg; + msg_handler = get_wsp_msg_handler(wsp_request->header.msg); + DBG_NOTICE("received %s message from handle %d\n", + msgid_to_string(wsp_request->header.msg), + client->client_data->fid); + if (msg_handler) { + subreq = msg_handler(state, client, wsp_request, + response, in_blob, &state->extra_out_blob, + get_impl()); + } else { + status = NT_STATUS_UNHANDLED_EXCEPTION; + goto err_out; + } + if (!subreq) { + /* better status ?? */ + status = NT_STATUS_UNHANDLED_EXCEPTION; + goto err_out; + } + tevent_req_set_callback(subreq, handle_wsp_done, req); + return req; +err_out: + response->header.status = NT_STATUS_V(status); + if (!insert_wsp_response(wsp_request, response, out_blob, + &state->extra_out_blob)) { + DBG_ERR("error inserting WSP response for msg %s\n", + msgid_to_string(state->wsp_request->header.msg)); + } + if (state->npc) { + talloc_steal(state->npc->p->mem_ctx, state->out_blob->data); + } + tevent_req_done(req); + return tevent_req_post(req, gss_state->ev); +} + +static void handle_wsp_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct handle_wsp_state *state = + tevent_req_data(req, struct handle_wsp_state); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + if (has_error) { + DBG_ERR("detected some async processing error %s 0x%x " + "for request %p\n", nt_errstr(status), + NT_STATUS_V(status), subreq); + state->response->header.status = NT_STATUS_V(status); + } + if (state->wsp_request->header.msg != CPMDISCONNECT) { + if (!insert_wsp_response(state->wsp_request, state->response, + state->out_blob, &state->extra_out_blob)) { + DBG_ERR("error inserting WSP response for msg %s\n", + msgid_to_string(state->wsp_request->header.msg)); + } + } else { + DBG_INFO("no message payload set for CPMDISCONNECT\n"); + } + if (state->npc) { + DBG_DEBUG("stealing response blob %p state->out_blob->data " + "with %d bytes\n", + state->out_blob->data, + (int)state->out_blob->length); + talloc_steal(state->npc->p->mem_ctx, state->out_blob->data); + } + TALLOC_FREE(state->wsp_request); + TALLOC_FREE(subreq); + tevent_req_done(req); +} + +struct gss_state *gss_state_create(struct tevent_context *event_ctx, + struct messaging_context *msg_ctx) + +{ + struct gss_state *state = talloc_zero(NULL, struct gss_state); + if (!state) { + DBG_ERR("Out of memory\n"); + return NULL; + } + DBG_NOTICE("wsp gss_start\n"); + state->ConnectedClientsIdentifiers = talloc_zero(state, + struct uint32_list); + state->ConnectedClientVersions = talloc_zero(state, + struct client_version_map); + state->wsp_server_state = NOT_INITIALISED; + + state->ev = event_ctx; + state->msg_ctx = msg_ctx; + talloc_set_destructor(state, destroy_gss_state); + return state; +} + +bool gss_init(struct gss_state *state) +{ + struct wsp_abstract_interface *abs_if = get_impl(); + if (state->wsp_server_state != NOT_INITIALISED) { + DBG_DEBUG("GSS_STATE is already initialised\n"); + return true; + } + state->wsp_abstract_state = abs_if->Initialise(state->ev, + state->msg_ctx); + + if (!state->wsp_abstract_state) { + DBG_ERR("failure initialise abstract interface\n"); + return false; + } + state->wsp_server_state = RUNNING; + return true; +} + +struct handle_wsp_req_state +{ + DATA_BLOB *out_resp; +}; + +struct tevent_req *do_wsp_request_send(TALLOC_CTX *ctx, + struct wspd_client_state *client) +{ + struct named_pipe_client *npc = client->client_data->npc; + struct pipes_struct *p = npc->p; + + return handle_wsp_send(ctx, client, npc, &p->in_data.pdu, + &p->out_data.rdata); + +} + +struct wspd_client_state * create_client_state(struct named_pipe_client *npc, + struct gss_state *gss_state) +{ + static int gen_id = 1; + struct wspd_client_state *state = talloc_zero(NULL, + struct wspd_client_state); + state->client_data = talloc_zero(state, struct wsp_client_data); + state->client_data->fid = gen_id++; + state->client_data->npc = npc; + state->client_data->gss_state = gss_state; + state->wsp_abstract_state = gss_state->wsp_abstract_state; + talloc_set_destructor(state, destroy_client_state); + return state; +} + +struct client_disconnected_state +{ + struct wspd_client_state *client_state; +}; + +static void client_disconnected_done(struct tevent_req *subreq); + +void client_disconnected(struct wspd_client_state *client_state) +{ + struct tevent_req *req, *subreq; + struct client_disconnected_state *state; + DBG_NOTICE("got disconnect for handle %d\n", + client_state->client_data->fid); + req = tevent_req_create(client_state->client_data->gss_state, &state, + struct client_disconnected_state); + if (!req) { + DBG_ERR("out of memory\n"); + goto error; + } + state->client_state = client_state; + subreq = get_impl()->ReleaseQuery_send(state, client_state, + client_state->client_data->fid); + if (!subreq) { + DBG_ERR("failed to create subrequest to release query " + "for handle %d\n", client_state->client_data->fid); + goto error; + } + tevent_req_set_callback(subreq, client_disconnected_done, req); + return; +error: + TALLOC_FREE(state->client_state); +} + +static void client_disconnected_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct client_disconnected_state *state = + tevent_req_data(req, struct client_disconnected_state); + TALLOC_FREE(state->client_state); + TALLOC_FREE(subreq); + TALLOC_FREE(req); +} + +struct pipes_struct *get_pipe(struct wspd_client_state *state) +{ + return state->client_data->npc->p; +} diff --git a/source3/rpc_server/wsp/wsp_gss.h b/source3/rpc_server/wsp/wsp_gss.h new file mode 100644 index 0000000..bdf8f51 --- /dev/null +++ b/source3/rpc_server/wsp/wsp_gss.h @@ -0,0 +1,254 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#ifndef __WSP_SRV_SPARQL__ +#define __WSP_SRV_SPARQL__ +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" + +struct messaging_context; +struct tevent_context; +struct named_pipe_client; +struct wspd_client_state; +struct gss_state; +struct wsp_client_data; + + +struct wspd_client_state +{ + struct wsp_abstract_state *wsp_abstract_state; + struct wsp_client_data *client_data; +}; + +struct tevent_req *do_wsp_request_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state); +void client_disconnected(struct wspd_client_state *state); +struct wspd_client_state *create_client_state(struct named_pipe_client *npc, + struct gss_state *gss_state); +struct gss_state *gss_state_create(struct tevent_context *event_ctx, + struct messaging_context *msg_ctx); +bool gss_init(struct gss_state *state); +/* not specified by MS-WSP */ +typedef struct wsp_abstract_state *(*Init_fn)(struct tevent_context *event_ctx, + struct messaging_context *msg_ctx); +/* not specified by MS-WSP */ +typedef struct wsp_ctablecolumn *(*GetBindings_fn)(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t *ncols); +typedef bool (*IsCatalogAvailable_fn)(struct wspd_client_state *client_state, + const char *CatalogName); +typedef void (*GetServerVersions_fn)(struct wspd_client_state* client_state, + uint32_t *dwWinVerMajor, + uint32_t *dwWinVerMinor, + uint32_t *dwNLSVerMajor, + uint32_t *dwNLSVerMinor, + uint32_t *serverVersion, + bool *supportsVersioningInfo); +typedef struct tevent_req *(*GetState_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + struct wsp_cpmcistateinout *out); +typedef void (*StoreClientInformation_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + struct wsp_cpmconnectin *ConnectMessage, + uint32_t NamedPipeHandle); + +typedef struct wsp_cpmconnectin *(*GetClientInformation_fn)(struct wspd_client_state *wsp_client, uint32_t QueryIdentifier); +typedef struct tevent_req *(*RunNewQuery_fn)( + TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + struct wsp_ccolumnset *ProjectionColumnsOffsets, + struct wsp_crestrictionarray *RestrictionSet, + struct wsp_csortset *SortOrders, + struct wsp_ccategorizationset *Groupings, + struct wsp_crowsetproperties *RowSetProperties, + struct wsp_cpidmapper *PidMapper, + struct wsp_ccolumngrouparray *GroupArray, + uint32_t Lcid, uint32_t *QueryParametersError, + uint32_t **CursorHandlesList, + bool *fTrueSequential, bool *fWorkidUnique, + bool *CanQueryNew); +typedef bool (*ClientQueryHasCursorHandle_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle); +typedef struct tevent_req *(*GetQueryStatus_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t *QueryStatus); + +typedef struct tevent_req *(*GetRatioFinishedParams_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t *rdwRatioFinishedDenominator, + uint32_t *rdwRatioFinishedNumerator, + uint32_t *cRows, + uint32_t *fNewRows); +typedef uint32_t (*GetApproximatePosition_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Bmk); +typedef uint32_t (*GetWhereid_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier); +typedef struct tevent_req *(*GetExpensiveProperties_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + /* out */ + uint32_t *rcRowsTotal, + uint32_t *rdwResultCount, + uint32_t *Maxrank); +typedef bool (*HasBindings_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle); +typedef uint32_t (*GetBookmarkPosition_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t bmkHandle); +typedef void (*SetNextGetRowsPosition_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Chapter, + uint32_t Index); + +typedef uint32_t (*GetNextGetRowsPosition_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Chapter); +typedef struct tevent_req *(*GetRows_fn)(TALLOC_CTX *ctx, + struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t NumRowsRequested, + uint32_t FetchForward, + /* out */ + struct wsp_cbasestoragevariant **RowsArray, + bool *NoMoreRowsToReturn, + uint32_t *NumRowsReturned, + uint32_t *Error); +typedef bool (*HasAccessToWorkid_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t Workid); +typedef bool (*HasAccessToProperty_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + struct wsp_cfullpropspec *PropSpec); + +typedef void (*GetPropertyValueForWorkid_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t Workid, + struct wsp_cfullpropspec *PropSpec, + struct wsp_serializedpropertyvalue *property, + bool *ValueExists); +typedef void (*SetBindings_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + struct wsp_ctablecolumn *Columns, + uint32_t nColumns); +typedef void (*GetQueryStatusChanges_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + /* out */ + uint32_t *LatestChange, + bool *ChangesPresent); +typedef uint32_t (*ReleaseCursor_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle); +typedef struct tevent_req *(*ReleaseQuery_fn)(TALLOC_CTX* ctx, + struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier); +typedef bool (*FindNextOccurrenceIndex_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t *PrevOccCoordinatesList, + uint32_t numPrevItems, + /* out */ + uint32_t *NextOccCoordinatesList, + uint32_t *numNextItems); + +typedef void (*GetLastUnretrievedEvent_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + /* out */ + uint32_t *Wid, + uint8_t *EventType, + bool *MoreEvents, + uint8_t *RowsetItemState, + uint8_t *ChangedItemState, + uint8_t *RowsetEvent, + uint64_t *RowsetEventData1, + uint64_t *RowsetEventData2); +typedef NTSTATUS (*GetQueryStatistics_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t *NumIndexedItems, + uint32_t *NumOutstandingAdds, + uint32_t *NumOutstandingModifies); +typedef NTSTATUS (*SetScopePriority_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier, + uint32_t Priority); + +typedef void (*FilterOutScopeStatisticsMessages_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier); +typedef void (*Inflect_fn)(struct wspd_client_state *wspd_client_state, + const char* phrase, + /* out */ + const char **inflections, + uint32_t inflectionsCount); +typedef void (*GenerateScopeStatisticsEvent_fn)(struct wspd_client_state *wspd_client_state, + uint32_t QueryIdentifier); + + +struct wsp_abstract_interface +{ + Init_fn Initialise; + IsCatalogAvailable_fn IsCatalogAvailable; + GetServerVersions_fn GetServerVersions; + GetState_fn GetState_send; + StoreClientInformation_fn StoreClientInformation; + GetClientInformation_fn GetClientInformation; + RunNewQuery_fn RunNewQuery_send; + ClientQueryHasCursorHandle_fn ClientQueryHasCursorHandle; + GetQueryStatus_fn GetQueryStatus_send; + GetRatioFinishedParams_fn GetRatioFinishedParams_send; + GetApproximatePosition_fn GetApproximatePosition; + GetWhereid_fn GetWhereid; + GetExpensiveProperties_fn GetExpensiveProperties_send; + HasBindings_fn HasBindings; + GetBookmarkPosition_fn GetBookmarkPosition; + SetNextGetRowsPosition_fn SetNextGetRowsPosition; + GetNextGetRowsPosition_fn GetNextGetRowsPosition; + GetRows_fn GetRows_send; + HasAccessToWorkid_fn HasAccessToWorkid; + HasAccessToProperty_fn HasAccessToProperty; + GetPropertyValueForWorkid_fn GetPropertyValueForWorkid; + GetQueryStatusChanges_fn GetQueryStatusChanges; + SetBindings_fn SetBindings; + GetBindings_fn GetBindings; + ReleaseCursor_fn ReleaseCursor; + ReleaseQuery_fn ReleaseQuery_send; + FindNextOccurrenceIndex_fn FindNextOccurrenceIndex; + GetLastUnretrievedEvent_fn GetLastUnretrievedEvent; + GetQueryStatistics_fn GetQueryStatistics; + SetScopePriority_fn SetScopePriority; + FilterOutScopeStatisticsMessages_fn FilterOutScopeStatisticsMessages; + Inflect_fn Inflect; + GenerateScopeStatisticsEvent_fn GenerateScopeStatisticsEvent; +}; + +struct pipes_struct *get_pipe(struct wspd_client_state* client_state); +#endif// __WSP_SRV_SPARQL__ diff --git a/source3/rpc_server/wsp/wsp_sparql_conv.c b/source3/rpc_server/wsp/wsp_sparql_conv.c new file mode 100644 index 0000000..491061b --- /dev/null +++ b/source3/rpc_server/wsp/wsp_sparql_conv.c @@ -0,0 +1,2115 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include "includes.h" +#include "wsp_sparql_conv.h" +#include "smbd/proto.h" +#include "libcli/security/security.h" +#include "wsp_srv_tracker-sparql.h" +#include "smbd/smbd.h" + +#define IMPLEMENTED 0 + +struct filter_data { + const char *share_scope; + uint32_t where_id; +}; + +static const char scheme[] = "file://"; +static NTSTATUS get_relative_share_path(TALLOC_CTX *ctx, const char *url, + const char **relative_share_path, + struct share_params *params); + +struct tracker_detail; + +static NTSTATUS scope_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS author_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS album_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS itemtype_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS item_folder_path_filter(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS all_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS kind_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS omit_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data); + +static NTSTATUS convert_entryid(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + out_val->vtype = VT_I4; + out_val->vvalue.vt_i4 = *(uint32_t*)(tracker_val); + status = NT_STATUS_OK; +out: + return status; +} + + +static NTSTATUS convert_filetime(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + /* + * Tracker time is a string with format xsd:dateTime + * [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] + */ + const char* datetime = (const char*)tracker_val; + struct tm utc_time; + time_t unixtime; + uint64_t filetime; + struct wsp_hyper *p_hyper = &out_val->vvalue.vt_filetime; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + if (!strptime(datetime, "%FT%TZ", &utc_time)) { + char *tmp = talloc_strdup(ctx, datetime); + char *dot; //, *; + /* + * it's possible the millsecs are tagged on after the time + * and before the timezone, we'll ignore them if that is + * the case. + */ + dot = strrchr(tmp,'.'); + if (dot) { + *dot = 'Z'; + if (!strptime(tmp, "%FT%TZ", &utc_time)) { + DBG_ERR("failed to convert date-time %s\n", + datetime); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + } else { + DBG_ERR("failed to convert date-time %s\n", datetime); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + } + + unixtime = timegm(&utc_time); + + /* https://support.microsoft.com/en-us/kb/167296 */ + filetime = ((unixtime * 10000000) + 116444736000000000); + out_val->vtype = VT_FILETIME; + uint64_to_wsp_hyper(filetime, p_hyper); + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_path(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + const char *tracker_url = (const char*)tracker_val; + const char *wsp_url; + struct row_conv_data *data = (struct row_conv_data*)private_data; + NTSTATUS status; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + if (data->tracker_row_url == NULL) { + tracker_url = (const char*)tracker_val; + data->tracker_row_url = tracker_url; + } else { + tracker_url = data->tracker_row_url; + } + + /* + * use the mangled url (with share name replacing share path) is we + * already have it + */ + if (data->row_relative_share_path == NULL) { + if (!NT_STATUS_IS_OK(get_relative_share_path(ctx, tracker_url, + &data->row_relative_share_path, + data->conn->params))) { + // #FIXME + DBG_ERR("error getting share path but %s\n", + data->row_relative_share_path); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + } + + if (data->row_relative_share_path != NULL) { + wsp_url = talloc_asprintf(ctx, "%s%s", scheme, + data->row_relative_share_path); + out_val->vtype = VT_LPWSTR; + out_val->vvalue.vt_lpwstr.value = wsp_url; + } + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_filesize(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + uint64_t size = 0; + struct wsp_hyper *p_hyper = &out_val->vvalue.vt_ui8; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + size = *(uint64_t*)tracker_val; + out_val->vtype = VT_UI8; + uint64_to_wsp_hyper(size, p_hyper); + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_kind(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + const char *val; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + val = (const char*)(tracker_val); + if (strstr(val, "image/") == val) {/* startswith */ + out_val->vtype = VT_VECTOR | VT_LPWSTR; + out_val->vvalue.vt_lpwstr_v.vvector_elements = 1; + out_val->vvalue.vt_lpwstr_v.vvector_data = + talloc_zero_array(ctx, struct vt_lpwstr, + out_val->vvalue.vt_lpwstr_v.vvector_elements); + out_val->vvalue.vt_lpwstr_v.vvector_data[0].value = + talloc_strdup(ctx, "picture"); + } + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_itemtype(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + const char *filename; + const char *ext; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + filename = (const char*)(tracker_val); + ext = strrchr(filename, '.'); + if (ext) { + out_val->vtype = VT_LPWSTR; + out_val->vvalue.vt_lpwstr.value = talloc_strdup(ctx, ext); + } + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_stringval(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + const char *filename; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out;; + } + filename = (const char*)(tracker_val); + out_val->vtype = VT_LPWSTR; + out_val->vvalue.vt_lpwstr.value = talloc_strdup(ctx, filename); + status = NT_STATUS_OK; +out: + return status; +} + +struct { + const char *ext; /* extension to match */ + const char *item_type; /* type string to return */ +} item_type_map [] = { + {"png", "PNG Image"}, + {"jpg", "JPEG image"}, + {"bmp", "Bitmap image"}, +}; + +static NTSTATUS convert_itemtypetext(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_val) +{ + NTSTATUS status; + const char *filename; + const char *ext; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + filename = (const char*)(tracker_val); + if (filename == NULL) { + DBG_ERR("Error no filename from tracker\n"); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + ext = strrchr(filename, '.'); + if (ext) { + int i; + out_val->vtype = VT_LPWSTR; + ext++; + for (i = 0; i < ARRAY_SIZE(item_type_map); i++) { + if (strequal(ext, item_type_map->ext)) { + out_val->vvalue.vt_lpwstr.value = + talloc_strdup(ctx, + item_type_map[i].item_type); + } + if (out_val->vvalue.vt_lpwstr.value == NULL) + out_val->vvalue.vt_lpwstr.value = + talloc_asprintf(ctx, "%s File", ext); + } + status = NT_STATUS_OK; + } else { + DBG_ERR("couldn't get extension from filename %s\n", + filename); + status = NT_STATUS_INVALID_PARAMETER; + } +out: + return status; +} + +static NTSTATUS get_sharename_from_path(TALLOC_CTX *ctx, const char *path, + char **share_name, char **share_path) +{ + int i; + int matchlen = 0; + int snum = -1; + int num_services = lp_numservices(); + if (!share_name) { + return NT_STATUS_INVALID_PARAMETER; + } + for (i = 0; i < num_services; i++) { + char *service_path = lp_path(ctx, i); + /* if the path starts with share path */ + if (strstr(path, service_path) == path) { + /* find the share that most (best) matches path */ + if (strlen(service_path) > matchlen) { + snum = i; + matchlen = strlen(service_path); + } + } + TALLOC_FREE(service_path); + } + if (snum == -1) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (share_path) { + *share_path = lp_path(ctx, snum); + } + *share_name = lp_servicename(ctx, snum); + return NT_STATUS_OK; +} + +static const char * mangle_path(TALLOC_CTX *ctx, const char *relative_path, + struct share_params *params) +{ + /* step through each segment and assess if it needs mangling */ + const char *sep = "/"; + char *copy = talloc_strdup(ctx, relative_path); + char *curr = copy; + char *prev = NULL; + const char *result = NULL; + char *p = NULL; + const char *end_of_string = copy + strlen(copy); + const char *end_last_seg = NULL; + struct segment_info + { + struct segment_info *prev, *next; + const char *startpos; + int num_chars; + }; + struct segment_info *infos = NULL; + struct segment_info *info = NULL; + int max_size = 0; + bool mangled = false; + + /* see if the path starts with '/' */ + while ((curr = strstr(curr, sep)) != NULL) { + max_size++; + if (prev) { + int num_chars = curr - prev - 1; + if (num_chars) { + bool needs_mangle = false; + info = talloc_zero(ctx, struct segment_info); + info->startpos = prev + 1; + info->num_chars = num_chars; + end_last_seg = info->startpos + num_chars; + + *curr = '\0'; + needs_mangle = + mangle_must_mangle(prev + 1, params); + + if (needs_mangle) { + char mname[13]; + name_to_8_3(prev + 1, mname, false, + params); + info->num_chars = strlen(mname); + info->startpos = talloc_strdup(info, + mname); + mangled = true; + } + *curr = '/'; + max_size += info->num_chars; + DLIST_ADD_END(infos, info); + } + } + prev = curr; + curr++; + } + + if (!mangled) { + /* + * if no path seqments were mangled then just return the + * path we were passed + */ + result = relative_path; + goto out; + } + + max_size += (end_of_string - end_last_seg); + p = talloc_zero_array(ctx, char, max_size + 1); + + result = p; + /* build path from existing segments or mangled ones */ + for (info = infos; info; info = info->next) { + *p = '/'; + p++; + memcpy(p, info->startpos, info->num_chars); + p += info->num_chars; + } + memcpy(p, end_last_seg, end_of_string - end_last_seg); +out: + for (info = infos; info;) { + struct segment_info *next = info->next; + TALLOC_FREE(info); + info = next; + } + TALLOC_FREE(copy); + return result; +} + +/* return NETBIOSNAME/SHARE/xyz for file:///local_share_path/xyz */ +static NTSTATUS get_relative_share_path(TALLOC_CTX *ctx, const char *url, + const char **relative_share_path, + struct share_params *params) +{ + char *s = NULL; + char *local_share_path = NULL; + char *share_name = NULL; + char *share_path = NULL; + if (!url) { + return NT_STATUS_INVALID_PARAMETER; + } + + s = strcasestr(url, scheme); + if (!s) { + return NT_STATUS_INVALID_PARAMETER; + } + + local_share_path = talloc_strdup(ctx, s + strlen(scheme)); + + /* find share name */ + if (!NT_STATUS_IS_OK(get_sharename_from_path(ctx, + local_share_path, + &share_name, + &share_path))) { + return NT_STATUS_INVALID_PARAMETER; + } + + s = local_share_path + strlen(share_path); + if (params) { + TALLOC_CTX *tmp_ctx = talloc_new(ctx); + /* attempt to mangle the relative share path part */ + const char *tmp = mangle_path(tmp_ctx, s, params); + *relative_share_path = talloc_asprintf(ctx, + "%s/%s%s", + lp_netbios_name(), + share_name, + tmp); + TALLOC_FREE(tmp_ctx); + } else { + *relative_share_path = talloc_asprintf(ctx, + "%s/%s%s", + lp_netbios_name(), + share_name, + s); + } + return NT_STATUS_OK; +} + +static NTSTATUS convert_folderpath(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + char *result = NULL; + const char* url; + struct row_conv_data *data = (struct row_conv_data *)private_data; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + if (data->tracker_row_url == NULL) { + url = (const char*)tracker_val; + data->tracker_row_url = url; + /* get the NETBIOS/SHARENAME/xyz path associate with this url */ + status = get_relative_share_path(ctx, url, + &data->row_relative_share_path, + data->conn->params); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } else { + url = data->tracker_row_url; + } + + if (data->row_relative_share_path != NULL) { + result = talloc_asprintf(ctx, "//%s", + data->row_relative_share_path); + } + + if (result) { + /* strip final '/' */ + char *slash = strrchr(result, '/'); + if (slash) { + *slash = 0; + /* replace '/' with '\' */ + string_replace(result, '/', '\\'); + } + out_val->vtype = VT_LPWSTR; + out_val->vvalue.vt_lpwstr.value = result; + } + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_folderpath_narrow(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + char* result = NULL; + char *tmp = NULL; + const char* url; + struct row_conv_data *data = (struct row_conv_data *)private_data; + if (vtype != VT_VARIANT) { + DBG_ERR("currently not handling non-VARIANT row results\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + if (data->tracker_row_url == NULL) { + url = (const char*)tracker_val; + data->tracker_row_url = url; + } else { + url = data->tracker_row_url; + } + /* get the NETBIOS/SHARENAME/xyz path associate with this url */ + if (data->row_relative_share_path == NULL) { + status = get_relative_share_path(ctx, + url, + &data->row_relative_share_path, + data->conn->params); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + if (data->row_relative_share_path != NULL) { + /* strip final '/' */ + char *remainder = NULL; + char *slash; + tmp = talloc_strdup(ctx, data->row_relative_share_path); + slash = strrchr(tmp, '/'); + if (slash) { + *slash = '\0'; + } + /* split NETBIOS/SHARENAME and xyz portions */ + slash = strchr(tmp, '/'); + if (!slash) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + slash = strchr(slash + 1, '/'); + /* tmp points to NETBIOS/SHARENAME */ + if (slash) { + char *last_slash = strrchr(slash, '/'); + if (last_slash) { + slash = last_slash; + } + *slash = '\0'; + remainder = (slash + 1); + } else { + /* + * there is no subdirectory, make remainder poing to + * empty string, e.g. just point at end of tmp + */ + remainder = tmp + strlen(tmp); + } + result = talloc_asprintf(ctx, "%s (//%s)", + remainder, tmp); + } + if (result) { + /* replace '/' with '\' */ + string_replace(result, '/', '\\'); + out_val->vtype = VT_LPWSTR; + out_val->vvalue.vt_lpwstr.value = result; + } else { + status = NT_STATUS_NO_MEMORY; + goto out; + } + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS convert_fileattrs(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + struct smb_filename *smb_fname = NULL; + struct row_conv_data *data = (struct row_conv_data *)private_data; + const char *tracker_row_url = (const char*)tracker_val; + uint32_t dosmode = 0; + + if (tracker_row_url) { + const char *path = tracker_row_url + strlen("file://"); + int ret; + smb_fname = + synthetic_smb_fname(ctx, path, NULL, NULL, 0); + if (!smb_fname) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + ret = SMB_VFS_STAT(data->conn, smb_fname); + if ((ret == -1) && (errno == ENOENT)) { + status = NT_STATUS_UNSUCCESSFUL; + DBG_ERR("vfs_stat failed for %s\n", path); + goto out; + } + dosmode = dos_mode(data->conn, smb_fname); + } else { + DBG_ERR("no url to get fileattributes from\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + /* + * somehow from the file's unix permission we need to + * get something representitive of FileAttributes values + */ + /* for testing hard code a value */ + out_val->vtype = VT_UI4; + out_val->vvalue.vt_ui4 = dosmode; + status = NT_STATUS_OK; +out: + return status; +} + +#if IMPLEMENTED +static NTSTATUS convert_gaoflags(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + /* + * somehow from the file's unix permission we need to + * get something representitive of SFGAOFlags + */ + /* for testing hard code a value */ + out_val->vtype = VT_UI4; + out_val->vvalue.vt_ui4 = 0x40400177; + status = NT_STATUS_OK; + return status; +} + +static NTSTATUS convert_rank(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + out_val->vtype = VT_I4; + out_val->vvalue.vt_i4 = 980; + status = NT_STATUS_OK; + return status; +} + +static NTSTATUS convert_cacheid(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data) +{ + NTSTATUS status; + /* just insert bogus value, is it needed ?*/ + static uint64_t gen = 0x1cf6a0d61d11bba; + + uint64_t id = gen++; + struct wsp_hyper *p_hyper = &out_val->vvalue.vt_ui8; + out_val->vtype = VT_UI8; + uint64_to_wsp_hyper(id, p_hyper); + status = NT_STATUS_OK; + return status; +} + +#endif + +struct tracker_detail { + const char *wsp_id; + const char *tracker_id; + NTSTATUS (*filt_helper)(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *private_data); + tracker_to_wsp_fn convert_fn; +} prop_to_tracker_iri_map[] = { + /* is a free text search thingy */ + {"All", NULL, all_filter_helper, NULL}, + /* + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx + * indicates that this property set is the Summary Information Property + * Set, but... no indication of what those property ids(s) are + */ + {"f29f85e0-4ff9-1068-ab91-08002b27b3d9/24", NULL, NULL}, + {"f29f85e0-4ff9-1068-ab91-08002b27b3d9/26", NULL, NULL}, + {"Path", "nie:url", NULL, convert_path}, + /* + * scope is not determined by a relationship (or one I can see) but + * could be determined by an extra 'where' clause doing a regrex + * on the nie:url value above, perhaps there is another way. + */ + + {"Scope", "nie:url", scope_filter_helper, NULL}, + {"System.Author", NULL, author_filter_helper, NULL}, + {"System.Music.AlbumTitle", NULL, album_filter_helper, NULL}, + {"System.Contact.FullName", NULL, NULL}, + {"System.DateAccessed", "nfo:fileLastAccessed", NULL, convert_filetime}, + {"System.DateModified", "nfo:fileLastModified", NULL, convert_filetime}, + + {"System.FileName", "nfo:fileName", NULL, convert_stringval }, + {"System.ItemAuthors", NULL, author_filter_helper, NULL}, + {"System.ItemFolderPathDisplay", "nie:url", item_folder_path_filter, convert_folderpath }, + /* + * needs to be synthesized, format is + * "\\NETBIOS\SHARE\path.." + */ + {"System.ItemFolderPathDisplayNarrow", "nie:url", NULL, convert_folderpath_narrow}, + {"System.ItemDate", "nfo:fileLastModified", NULL, convert_filetime }, + {"System.ItemNameDisplay", "nfo:fileName", NULL, convert_stringval }, +// {"System.ItemNamePrefix", "nfo:fileName", NULL }, + /* needs to be synthesised (is the extension) */ + {"System.ItemType", "nfo:fileName", itemtype_filter_helper, convert_itemtype }, + {"00000000-0000-0000-0000-000000000000/#SYSTEM.STRUCTUREDQUERY.VIRTUAL.TYPE", "nfo:fileName", itemtype_filter_helper, convert_itemtype }, + /* needs to be synthesised (is something like "JPEG image" etc) */ + {"System.ItemTypeText", "nfo:fileName", NULL, convert_itemtypetext }, + {"System.ItemName", "nfo:fileName", NULL, convert_stringval }, + {"System.ItemURL", "nie:url", NULL, convert_path}, + {"System.Keywords", NULL, NULL, NULL}, + /* + * again needs to be synthesized, actually is a vector and has a value + * like 'picture' when returned + */ + {"System.Kind", "nie:mimeType", kind_filter_helper, convert_kind }, + {"System.MIMEType", "nie:mimeType", NULL, convert_stringval }, + {"System.Message.FromName", "nmo:from", NULL, NULL}, + {"System.Music.AlbumTitle", "nmm:musicAlbum", NULL, NULL}, + {"System.Music.Genre", "nmm:genre", NULL, NULL}, + {"System.ParsingName", "nfo:fileName", NULL, convert_stringval }, + /* we need to synthesize this */ + {"System.Search.EntryID", "nie:isStoredAs", NULL, convert_entryid}, + /* + * needs to be synthesized (needs file url) on windows + * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762589%28v=vs.85%29.aspx + * describes the flags, (usually retrieved from + * IShellFolder::GetAttributesOf, and additionally the + * SFGAO_PKEYSFGAOMASK = 0x81044000 values are masked out + */ + /* has no relevence afaik on linux */ + {"System.Shell.OmitFromView", NULL, omit_filter_helper}, + {"System.Size", "nfo:fileSize", NULL, convert_filesize }, + {"System.Subject", "nmo:messageSubject", NULL, convert_stringval}, + {"System.Title", "nie:title", NULL, convert_stringval}, + {"System.Important.Unknown", "nie:url", NULL, convert_entryid}, + /* + * FileAttributes values are described by + * http://msdn.microsoft.com/en-us/library/windows/desktop/gg258117%28v=vs.85%29.aspx, doesn't seem to be returned by tracker but we could + */ + {"System.FileAttributes", "nie:url", NULL, convert_fileattrs}, +#if IMPLEMENTED + {"System.ThumbnailCacheId", "nie:url", NULL, convert_cacheid}, + {"System.SFGAOFlags", "nie:url", NULL, convert_gaoflags}, + {"System.DateCreated", "nfo:fileLastAccessed", NULL, convert_filetime}, + {"System.Search.Rank", "nie:url", NULL, convert_rank}, +#endif +}; + +static struct tracker_detail *get_tracker_detail(const char *wsp_id) +{ + int i; + for (i = 0; i < ARRAY_SIZE(prop_to_tracker_iri_map); i++) { + if (strequal(prop_to_tracker_iri_map[i].wsp_id, wsp_id)) { + return &prop_to_tracker_iri_map[i]; + } + } + return NULL; +} + +static NTSTATUS get_share_path(TALLOC_CTX *ctx, const char *share, + char **share_path) +{ + int snum; + char *service = NULL; + char *path = NULL; + if (share_path == NULL || share == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + snum = find_service(ctx, share, &service); + if ((snum == -1) || (service == NULL)) { + return NT_STATUS_INVALID_PARAMETER; + } + path = lp_path(ctx, snum); + if (path == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + *share_path = path; + return NT_STATUS_OK; +} + +static NTSTATUS get_share(TALLOC_CTX *ctx, + const char *win_url, + const char **shareout) +{ + char *share; + char *s; + if (win_url == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + s = strcasestr(win_url, scheme); + + if (s == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + s = s + strlen(scheme); + + share = talloc_strdup(ctx, s); + s = strchr(share, '/'); + + if (s == NULL) { + return NT_STATUS_INVALID_PARAMETER; + }; + + share = talloc_strdup(ctx, s + 1); + s = strchr(share, '/'); + if (s) { + *s = '\0'; + } + *shareout = share; + return NT_STATUS_OK; +} + +/* need to replace file://NETBIOS/SHARENAME with file:///local-path */ +static NTSTATUS replace_share_in_url(TALLOC_CTX *ctx, const char* path, + const char **local_path) +{ + const char *result = NULL; + const char *share; + char *share_path; + const char *tmp; + NTSTATUS status = get_share(ctx, path, &share); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = get_share_path(ctx, share, &share_path); + if (!NT_STATUS_IS_OK(get_share_path(ctx, share, &share_path))) { + return status; + } + + tmp = strcasestr(path, share); + if (tmp) { + result = talloc_asprintf(ctx, "file://%s%s", + share_path, tmp + strlen(share)); + } + status = NT_STATUS_OK; + *local_path = result; + return status; +} + +static NTSTATUS item_folder_path_filter(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + char *lower = NULL; + NTSTATUS status; + if (restric->ultype == RTCONTENT) { + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + } else if (restric->ultype == RTPROPERTY) { + prop_val = variant_as_string(ctx, + &restric->restriction.cpropertyrestriction.prval, + false); + } else { + DBG_ERR("helper failed for restriction type %d\n", + restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + lower = talloc_strdup(ctx, prop_val); + if (!strlower_m(lower)) { + /* if we can't convert, warn and soldier on */ + DBG_ERR("couldn't convert %s to lower case\n", prop_val); + } + + filter_str = talloc_asprintf(ctx, + "fn:contains(fn:lower-case(nie:url(?u))," + "\'/%s\')", + lower); + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + + +static NTSTATUS album_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + char *lower = NULL; + NTSTATUS status; + if (restric->ultype == RTCONTENT) { + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + } else if (restric->ultype == RTPROPERTY) { + prop_val = variant_as_string(ctx, + &restric->restriction.cpropertyrestriction.prval, + false); + } else { + DBG_ERR("helper failed for restriction type %d\n", + restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + lower = talloc_strdup(ctx, prop_val); + if (!strlower_m(lower)) { + /* if we can't convert, warn and soldier on */ + DBG_ERR("couldn't convert %s to lower case\n", prop_val); + } + + filter_str = + talloc_asprintf(ctx, + "fn:contains(fn:lower-case(nie:title(" + "nmm:musicAlbum(?u))),\'%s\')", + lower); + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + +static NTSTATUS author_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + char *lower = NULL; + NTSTATUS status; + if (restric->ultype == RTCONTENT) { + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + } else if (restric->ultype == RTPROPERTY) { + prop_val = variant_as_string(ctx, + &restric->restriction.cpropertyrestriction.prval, + false); + } else { + DBG_ERR("helper failed for restriction type %d\n", + restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + lower = talloc_strdup(ctx, prop_val); + if (!strlower_m(lower)) { + /* if we can't convert, warn and soldier on */ + DBG_ERR("couldn't convert %s to lower case\n", prop_val); + } + + filter_str = talloc_asprintf(ctx, + "fn:starts-with(fn:lower-case(nco:" + "fullname(nco:publisher(?u))), \'%s\')", + lower); + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + +static NTSTATUS itemtype_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + char *lower = NULL; + NTSTATUS status; + if (restric->ultype == RTCONTENT) { + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + } else if (restric->ultype == RTPROPERTY) { + prop_val = variant_as_string(ctx, + &restric->restriction.cpropertyrestriction.prval, + false); + } else { + DBG_ERR("helper failed for restriction type %d\n", restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + lower = talloc_strdup(ctx, prop_val); + if (!strlower_m(lower)) { + /* if we can't convert, warn and soldier on */ + DBG_ERR("couldn't convert %s to lower case\n", prop_val); + } + + filter_str = talloc_asprintf(ctx, "fn:ends-with(fn:lower-case(nfo:fileName(?u)), \'.%s\')", lower); + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + +static NTSTATUS scope_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + struct filter_data *data = (struct filter_data *)priv_data; + + NTSTATUS status; + + if (restric->ultype != RTPROPERTY) { + DBG_ERR("scope_filter_helper failed for restriction type %d\n", + restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + prop_val = variant_as_string(ctx, &restric->restriction.cpropertyrestriction.prval, false); + + if (!data->share_scope) { + status = get_share(ctx, prop_val, &data->share_scope); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + } + status = replace_share_in_url(ctx, prop_val, &prop_val); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + filter_str = talloc_asprintf(ctx, "tracker:uri-is-descendant " + "('%s', nie:url (?u))", + prop_val); + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + +/* + * This is pretty lame, really would be better to additionally use + * fts:match terms, but... I can only see how to use these in the WHERE + * clause which isn't afaics flexible enough when used in addition to other + * 'restrictions' + */ +static NTSTATUS all_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = NULL; + const char *prop_val = NULL; + char *lower; + NTSTATUS status; + + if (restric->ultype != RTCONTENT) { + DBG_ERR("scope_filter_helper failed for restriction type %d\n", + restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + prop_val = restric->restriction.ccontentrestriction.pwcsphrase; + lower = talloc_strdup(ctx, prop_val); + if (!strlower_m(lower)) { + /* if we can't convert, warn and soldier on */ + DBG_ERR("couldn't convert %s to lower case\n", prop_val); + } + if (restric->restriction.ccontentrestriction.ulgeneratemethod) { + filter_str = + talloc_asprintf( + ctx, + "fn:contains(fn:lower-case(" + "nie:plainTextContent(?u)), \'%s\')" + " || fn:contains(fn:lower-case(" + "nie:title(?u)),\'%s\') ||" + " fn:starts-with(fn:lower-case(" + "nfo:fileName(?u)), \'%s\')", + lower, + lower, + lower); + } else { + filter_str = + talloc_asprintf(ctx, + "nie:plainTextContent(?u) = \'%s\' ||" + " nie:title(?f) = \'%s\'", + prop_val, + prop_val); + } + *filter = filter_str; + status = NT_STATUS_OK; +done: + TALLOC_FREE(lower); + return status; +} + +static NTSTATUS omit_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = talloc_strdup(ctx,""); + const char *prop_val = NULL; + const char *tmp = NULL; + struct wsp_cpropertyrestriction *cprop; + struct wsp_ccontentrestriction *ccont; + NTSTATUS status; + /* + * assume OmitFromView always is 'false' since there is no such thing + * afaik as ommiting files from view in 'nix-land + */ + switch(restric->ultype) { + case RTPROPERTY: { + cprop = &restric->restriction.cpropertyrestriction, + tmp = variant_as_string(ctx, &cprop->prval, false); + prop_val = talloc_strdup(ctx, tmp); + filter_str = talloc_asprintf(ctx, + "(false %s %s)", + op_as_string(restric), + prop_val); + break; + } + case RTCONTENT: { + ccont = &restric->restriction.ccontentrestriction; + prop_val = + talloc_strdup(ctx,ccont->pwcsphrase); + filter_str = talloc_asprintf(ctx, + "(false = %s)", + prop_val); + break; + } + default: + DBG_ERR("omit_filter_helper failed for restriction" + " type %d\n", restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} +const static struct { + const char *kind; + const char *rdf_type; +} kind_rdf_type_map []= { + {"Calendar", NULL}, + {"Communication", NULL}, + {"Contact", NULL}, + /* + * note nfo:TextDocument will match spreadsheets even, nfo:Spreadsheet + * wont match libreoffice calc spreadsheets though + */ + {"Document", "nfo:TextDocument"}, + {"Email", NULL}, + {"Feed", NULL}, + {"Folder", "nfo:Folder"}, + {"Game", NULL}, + {"InstantMessage", NULL}, + {"Journal", NULL}, + {"Link", NULL}, + {"Movie", NULL}, + {"Music", "nmm:MusicPiece"}, + {"Note", NULL}, + {"Picture", "nfo:Image"}, + {"Program", NULL}, + {"RecordedTV", NULL}, + {"SearchFolder", NULL}, + {"Task", NULL}, + {"Video", "nfo:Video"}, + {"WebHistory", NULL}, +}; + +static const char * get_rdftype_for_kind(const char *kind) +{ + int i; + for (i = 0; i < ARRAY_SIZE(kind_rdf_type_map); i++) { + if (strequal(kind, kind_rdf_type_map[i].kind)) { + return kind_rdf_type_map[i].rdf_type; + } + } + return NULL; +} + +static NTSTATUS kind_filter_helper(TALLOC_CTX *ctx, + struct tracker_detail *detail, + struct wsp_crestriction *restric, + const char **filter, + void *priv_data) +{ + const char *filter_str = talloc_strdup(ctx,""); + char *prop_val = NULL; + const char *rdf_type; + struct wsp_cpropertyrestriction *cprop; + struct wsp_ccontentrestriction *ccont; + const char *tmp; + NTSTATUS status; + switch(restric->ultype) { + case RTPROPERTY: { + cprop = &restric->restriction.cpropertyrestriction; + tmp = variant_as_string(ctx, &cprop->prval, false); + prop_val = talloc_strdup(ctx, tmp); + break; + } + case RTCONTENT: { + ccont = &restric->restriction.ccontentrestriction; + prop_val = talloc_strdup(ctx, ccont->pwcsphrase); + break; + } + default: + DBG_ERR("scope_filter_helper failed for restriction" + " type %d\n", restric->ultype); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + break; + } + +#if 0 +/* could alernatively use media type */ + if (strequal(prop_val, "Picture")) { + filter_str = talloc_asprintf(ctx, "fn:starts-with(nie:mimeType(?u), 'image/')"); + } else if (strequal(prop_val, "Music")) { + filter_str = talloc_asprintf(ctx, "fn:starts-with(nie:mimeType(?u), 'audio/')"); + } +#endif + if (!strlen(filter_str)) { + /* + * using the type is one one of handling 'Kind' maybe + * media type is better + */ + rdf_type = get_rdftype_for_kind(prop_val); + + /* convert */ + if (rdf_type) { + filter_str = talloc_asprintf(ctx, + "?type IN (%s)", rdf_type); + } + } + *filter = filter_str; + status = NT_STATUS_OK; +done: + return status; +} + +struct tracker_pair_list +{ + struct tracker_pair_list *next, *prev; + const char* key; + struct tracker_detail * value; +}; + +struct sparql_conv +{ + struct tracker_pair_list *select; +}; + +static bool list_contains_value(struct sparql_conv *sparql_conv, + struct tracker_detail *value) +{ + struct tracker_pair_list *item = sparql_conv->select; + for (; item; item = item->next) { + if (item->value && strequal(item->value->tracker_id, + value->tracker_id)) { + return true; + } + } + return false; +} + +static void add_select_term_from_prop(TALLOC_CTX *ctx, + struct sparql_conv *sparql_conv, + const char *prop) +{ + struct tracker_detail *detail = get_tracker_detail(prop); + struct tracker_pair_list *item; + if (detail && detail->tracker_id) { + /* limit tracker select items so columns are unique */ + if (list_contains_value(sparql_conv, detail)) { + return; + } + item = talloc_zero(sparql_conv, struct tracker_pair_list); + item->key = talloc_strdup(item, prop); + item->value = detail; + DLIST_ADD_END(sparql_conv->select, item); + } +} + +static void get_select(TALLOC_CTX *ctx, + struct wsp_ccolumnset *columnset, + struct wsp_cpidmapper *pidmapper, + struct sparql_conv *sparql_conv) +{ + int i; + if (!columnset) { + return; + } + + /* + * always insert url into select (and also into the where clause), + * this gives us a starting point to filter from, also it allows + * to use the property 'method' in select for retrieval (which + * avoids the penalties imposed by using 'OPTIONAL' in sparql query) + */ + add_select_term_from_prop(ctx, sparql_conv, "Path"); + for (i=0; i < columnset->count; i++) { + int pid_index = columnset->indexes[i]; + struct wsp_cfullpropspec *prop_spec = + &pidmapper->apropspec[pid_index]; + char *prop = prop_from_fullprop(ctx, prop_spec); + add_select_term_from_prop(ctx, sparql_conv, prop); + TALLOC_FREE(prop); + } +} + +static NTSTATUS rtcontent_to_string(TALLOC_CTX *ctx, + struct wsp_ccontentrestriction *content, + struct tracker_detail *detail, + const char **presult) +{ + const char *result = talloc_strdup(ctx, ""); + NTSTATUS status = NT_STATUS_OK; + if (content->ulgeneratemethod) { + result = talloc_asprintf(ctx, "regex(%s(?u),\'^%s\')", + detail->tracker_id, + content->pwcsphrase); + } else { + /* exact match */ + result = talloc_asprintf(ctx, "%s(?u) =\'%s\'", + detail->tracker_id, + content->pwcsphrase); + } + *presult = result; + return status; +} + +static NTSTATUS rtproperty_to_string(TALLOC_CTX *ctx, + const char *operator, + struct wsp_cpropertyrestriction *restrict_prop, + struct tracker_detail *detail, + const char **presult) +{ + const char *value = variant_as_string(ctx, &restrict_prop->prval, true); + const char *result = talloc_strdup(ctx, ""); + NTSTATUS status = NT_STATUS_OK; + result = talloc_asprintf(ctx, "%s(?u) %s %s", detail->tracker_id, + operator, + value); + *presult = result; + return status; +} + +static NTSTATUS rtpropertycontainer_to_string(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + void *priv_data, + const char **presult) +{ + struct wsp_cfullpropspec *prop_spec = get_full_prop(restriction); + const char *prop = prop_from_fullprop(ctx, prop_spec); + struct tracker_detail *detail = get_tracker_detail(prop); + const char *result = talloc_strdup(ctx, ""); + NTSTATUS status = NT_STATUS_OK; + if (detail && detail->tracker_id && !detail->filt_helper) { + if (detail->filt_helper) { + status = detail->filt_helper(ctx, detail, + restriction, + &result, + priv_data); + } else { + if (restriction->ultype == RTPROPERTY) { + struct wsp_cpropertyrestriction *cprop; + const char *operator = + op_as_string(restriction); + cprop = &restriction->restriction.cpropertyrestriction; + status = rtproperty_to_string(ctx, + operator, + cprop, + detail, + &result); + } else if (restriction->ultype == RTCONTENT) { + struct wsp_ccontentrestriction *ccont; + ccont = &restriction->restriction.ccontentrestriction; + status = rtcontent_to_string(ctx, + ccont, + detail, + &result); + } + } + } else if (detail && detail->filt_helper) { + status = detail->filt_helper(ctx, detail, restriction, + &result, + priv_data); + } + *presult = result; + return status; +} + +static NTSTATUS rtnatlang_to_string(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + const char **presult) +{ + struct wsp_cfullpropspec *prop_spec = get_full_prop(restriction); + const char *prop = prop_from_fullprop(ctx, prop_spec); + struct tracker_detail *detail = get_tracker_detail(prop); + const char *result = talloc_strdup(ctx, ""); + struct wsp_cnatlanguagerestriction *cnat; + NTSTATUS status; + prop = prop_from_fullprop(ctx, prop_spec); + detail = get_tracker_detail(prop); + if (restriction->ultype != RTNATLANGUAGE) { + DBG_ERR("unexpected type %d, expected RTNATLANGUAGE\n", + restriction->ultype); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + cnat = &restriction->restriction.cnatlanguagerestriction; + if (detail && detail->tracker_id) { + result = talloc_asprintf(ctx, "%s(?u)=%s", + detail->tracker_id, + cnat->pwcsphrase); + } + status = NT_STATUS_OK; +done: + *presult = result; + return status; +} + +static NTSTATUS rtreusewhere_to_string(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + const char **result) +{ + int where_id; + const char *tmp; + NTSTATUS status; + *result = talloc_strdup(ctx, ""); + where_id = restriction->restriction.reusewhere.whereid; + DBG_DEBUG("SHARE reusewhereid %d\n", where_id); + /* + * Try get a previously built whereid string, + * It's quite possible that a whereid points to a + * restrictions set associated with a whereid that no + * longer exists (e.g. the associated query has been + * released). That's why we don't search for the + * restriction array, instead we expect the + * restriction string to be stored. + * Note: the documentation is ambiguous about this, + * it states the whereid refers to an open queries + * restriction set, that's true but it failes to point + * out that the restriction set (of the open query) + * itself could have been built using a whereid that + * is now 'released' thus we won't find the associated + * restriction set of that 'nested' whereid + */ + tmp = get_where_restriction_string(glob_data, where_id); + if (tmp && strlen(tmp)) { + *result = talloc_strdup(ctx, tmp); + DBG_NOTICE("detected a where id RTREUSEWHERE id=%d result = %s\n", + restriction->restriction.reusewhere.whereid, + tmp ? tmp : "None"); + } else { + /* + * this assumes the reason we have + * no whereid string is because there is no + * index, it's a pretty valid assumption + * but I think getting the status from + * maybe get_where_restriction_string might + * be better + */ + DBG_ERR("no whereid => this share is not indexed\n"); + tmp = talloc_asprintf(ctx, "insert expression for WHEREID = %d", + restriction->restriction.reusewhere.whereid); + /* + * if glob_data == NULL then we are more than likely being + * called from wsp_to_sparql and we don't want to propagate the + * status for this case + */ + if (glob_data != NULL) { + status = NT_STATUS(0x80070003); + goto done; + } + } + status = NT_STATUS_OK; +done: + *result = tmp; + return status; +} + +static const char* get_sort_string(TALLOC_CTX *ctx, + struct wsp_cpidmapper *pidmapper, + struct wsp_csortset *sorting) +{ + int i; + int sort_index= 0; + const char * sort_by[sorting->count]; + struct wsp_csort *order = sorting->sortarray; + const char* sort_str = NULL; + ZERO_STRUCT(sort_by); + for (i = 0; i < sorting->count; i++) { + int pid_index = order[i].pidcolimn; + struct wsp_cfullpropspec *prop_spec = + &pidmapper->apropspec[pid_index]; + char *prop = prop_from_fullprop(ctx, prop_spec); + struct tracker_detail *detail = + get_tracker_detail(prop); + + /* search sparql_conv for a match to the sort term */ + if (detail && detail->tracker_id) { + int j; + if (order[i].dworder != QUERY_SORTASCEND && + order[i].dworder != QUERY_DESCEND) { + DBG_ERR("Uknown sort order %d\n", order[i].dworder); + return NULL; + } + if (sort_str == NULL) { + sort_str = + talloc_asprintf(ctx, + "ORDER BY"); + } + if (order[i].dworder) { + sort_str = talloc_asprintf(ctx, + "%s ASC(%s(?u))", + sort_str, + detail->tracker_id); + } else { + sort_str = talloc_asprintf(ctx, + "%s DESC(%s(?u))", + sort_str, + detail->tracker_id); + } + /* don't try and order by the same col again */ + if (sort_index) { + for (j = 0; j < sort_index; j++) { + if (strequal(detail->tracker_id, + sort_by[j])) { + DBG_INFO("### Already sorting by %s\n", detail->tracker_id); + break; + } + else { + sort_by[sort_index++] = + detail->tracker_id; + } + } + } else { + sort_by[sort_index++] = + detail->tracker_id; + } + } + } + return sort_str; +} +/* + * convert_props if false won't attempt to convert wsp restriction + * properties to tracker properties + */ +NTSTATUS build_tracker_query(TALLOC_CTX *ctx, + struct wsp_ccolumnset *select_cols, + const char* restriction_expr, + struct wsp_cpidmapper *pidmapper, + struct tracker_selected_cols *tracker_cols, + struct wsp_csortset *sorting, + bool convert_props, + const char **query) +{ + const char *select_str = NULL; + const char *where_str = NULL; + const char *filter_str = restriction_expr; + const char *query_str = NULL; + const char *sort_str = NULL; + + struct tracker_pair_list *item = NULL; + struct sparql_conv *sparql_conv = talloc_zero(ctx, struct sparql_conv); + bool has_kind = false; + NTSTATUS status = NT_STATUS_OK; + + get_select(ctx,select_cols, pidmapper, sparql_conv); + item = sparql_conv->select; + + /* add 1 for the default Path item we always add */ + tracker_cols->tracker_ids = talloc_zero_array(ctx, const char *, select_cols->count + 1); + if (item) { + select_str = talloc_strdup(ctx, "SELECT"); + for (; item; item = item->next) { + /* skip those we can't handle */ + if (item->key) { + int i = tracker_cols->cols; + select_str = + talloc_asprintf( + ctx, + ("%s %s(?u)"), + select_str, + item->value->tracker_id); + tracker_cols->tracker_ids[i] = + talloc_strdup(ctx, + item->value->tracker_id); + tracker_cols->cols++; + } + } + talloc_realloc(ctx, tracker_cols->tracker_ids, char *, + tracker_cols->cols); + } + if (filter_str && strlen(filter_str)) { + filter_str = talloc_asprintf(ctx, ("FILTER%s"), + filter_str); + } + + if (strstr(filter_str, "?type IN ")) { + /* System.Kind is used in restrictions */ + has_kind = true; + } + if (has_kind) { + where_str = talloc_strdup(ctx, "WHERE{?u nie:url ?url . ?u rdf:type ?type"); + } else { + where_str = talloc_strdup(ctx, "WHERE{?u nie:url ?url"); + } + + if (sorting) { + sort_str = get_sort_string(ctx, pidmapper, sorting); + if (sort_str == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + } + + where_str = talloc_asprintf(ctx, "%s %s}", where_str, + strlen(filter_str) ? filter_str : ""); + query_str = talloc_asprintf(ctx, "%s %s %s", select_str, where_str, + sort_str ? sort_str : ""); + *query = query_str; + return status; +} + +bool build_mapper(TALLOC_CTX *ctx, struct wsp_ctablecolumn *columns, uint32_t ncols, struct tracker_selected_cols *tracker_cols, + struct binding_result_mapper *mapper) +{ + int i, j; + /* + * walk through the bindings and find if any returned tracker cols + * match + * + */ + mapper->map_data = talloc_zero_array(ctx, struct map_data, ncols); + mapper->ncols = ncols; + for (i = 0; i < ncols; i++) { + const char *wsp_id = prop_from_fullprop(ctx, + &columns[i].propspec); + + struct tracker_detail *conv = get_tracker_detail(wsp_id); + mapper->map_data[i].vtype = VT_NULL; + if (conv && conv->tracker_id) { + for (j = 0; j < tracker_cols->cols; j++) { + if (strequal(conv->tracker_id, + tracker_cols->tracker_ids[j]) + && conv->convert_fn) { + /* + * store the col where we can find + * the tracker value we can use to + * convert or synthesize into wsp + * property value + */ + if (strequal(wsp_id, + "System.Search.EntryID") + ||(strequal(wsp_id, + "System.Important.Unknown"))) { + DBG_DEBUG("special handling for %s\n", wsp_id); + mapper->map_data[i].col_with_value = tracker_cols->cols; + } else { + mapper->map_data[i].col_with_value = j; + } + mapper->map_data[i].convert_fn = + conv->convert_fn; + mapper->map_data[i].vtype = + columns[i].vtype; + DBG_DEBUG("mapping binding[%d] %s to tracker returned col %d %s\n", i, conv->wsp_id, j, tracker_cols->tracker_ids[j]); + break; + } + } + } + } + return true; +} + + +static NTSTATUS infix(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + void *priv_data, + const char **result); + +static NTSTATUS print_restriction(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + const char** presult, + void *priv_data) +{ + const char *result = NULL; + const char *tmp; + NTSTATUS status = NT_STATUS_OK; + + if (is_operator(restriction)) { + result = op_as_string(restriction); + } else { + switch(restriction->ultype) { + case RTCONTENT: + case RTPROPERTY: { + status = rtpropertycontainer_to_string(ctx, + glob_data, + restriction, + priv_data, + &tmp); + if (strlen(tmp)) { + result = tmp; + } + break; + } + case RTNATLANGUAGE: + status = rtnatlang_to_string(ctx, + glob_data, + restriction, + &tmp); + if (strlen(tmp)) { + result = tmp; + } + break; + case RTCOERCE_ABSOLUTE: { + struct wsp_crestriction *child_restrict = + restriction->restriction.ccoercionrestriction_abs.childres; + status = infix(ctx, glob_data, child_restrict, priv_data, &result); + break; + } + case RTREUSEWHERE: { + tmp = NULL; + status = rtreusewhere_to_string(ctx, + glob_data, + restriction, + &tmp); + if (tmp && strlen(tmp)) { + result = tmp; + } + break; + } + } + } + *presult = result; + return status; +} + +static bool is_andor(struct wsp_crestriction *restriction) +{ + if (restriction->ultype == RTAND || restriction->ultype == RTOR) { + return true; + } + return false; +} + +static NTSTATUS infix(TALLOC_CTX *ctx, struct wsp_abstract_state *glob_data, + struct wsp_crestriction *restriction, + void *priv_data, + const char **result) +{ + const char *tmp = NULL; + const char *token = NULL; + const char *left_node = NULL; + const char *right_node = NULL; + struct wsp_crestriction *left = NULL; + struct wsp_crestriction *right = NULL; + NTSTATUS status; + if (!restriction) { + return NT_STATUS_UNSUCCESSFUL; + } + if (is_operator(restriction)) { + if (is_andor(restriction)) { + struct wsp_cnoderestriction *cnodes = + &restriction->restriction.cnoderestriction; + if (cnodes->cnode) { + left = &cnodes->panode[0]; + if (cnodes->cnode > 1) { + right = &cnodes->panode[1]; + } + } + } else { + right = restriction->restriction.restriction.restriction; + } + } + if (left) { + status = infix(ctx, glob_data, left, priv_data, &left_node); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + status = print_restriction(ctx, glob_data, restriction, &token, priv_data); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (right) { + status = infix(ctx, glob_data, right, priv_data, &right_node); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (is_operator(restriction)) { + if (is_andor(restriction) == false && right_node && strlen(right_node)) { + tmp = talloc_asprintf(ctx, "(%s%s)", + token, + right_node); + } else if (left_node && strlen(left_node) && right_node && strlen(right_node)) { + tmp = talloc_asprintf(ctx, "(%s%s%s)", left_node, + token, right_node); + } else { + tmp = talloc_asprintf(ctx, "(%s%s)", + left_node ? left_node : "", + right_node ? right_node : ""); + } + } else { + tmp = talloc_asprintf(ctx, "%s%s%s", + left_node ? left_node : "", + token ? token : "", + right_node ? right_node : ""); + } + + if (strequal(tmp, "()")) { + tmp = NULL; + } + *result = tmp; + return status; +} + +NTSTATUS build_restriction_expression(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestrictionarray *restrictarray, + bool convert_props, + const char **restrict_expr, + const char** share_scope, + uint32_t *where_id) +{ + const char * query = NULL; + NTSTATUS status = NT_STATUS_OK; + struct filter_data data; + + ZERO_STRUCT(data); + if (restrictarray->count) { + if (convert_props == false) { + query = + raw_restriction_to_string(ctx, + &restrictarray->restrictions[0]); + if (!query) { + status = NT_STATUS_INVALID_PARAMETER; + } + } else { + status = infix(ctx, + glob_data, + &restrictarray->restrictions[0], + &data, + &query); + } + } + *restrict_expr = query; + *share_scope = data.share_scope; + *where_id = data.where_id; + return status; +} + +static bool can_open_url(struct connection_struct *conn, const char* url) +{ + const char* local_share_path = NULL; + struct smb_filename *smb_fname = NULL; + bool result; + files_struct *fsp = NULL; + int info; + TALLOC_CTX *ctx = talloc_init("url"); + NTSTATUS status; + local_share_path = talloc_strdup(ctx, url + strlen(scheme)); +#if 0 + /* + * #TODO in smbd the relative path is used here (afaik), however trying + * that here fails (get an OBJECT_NOT_FOUND) it seems to expect + * the parents of the url to be cached I think, + * (but I am guessing I am not tapping into the correct way of doing + * things). + * if we use filename_convert perhaps we could cache the mangled name + * that I am guessing is returned instead of manually doing that later + * in the code + */ + status = filename_convert(ctx, conn, 0, + local_share_path, ucf_flags, + NULL, /* ppath_contains_wcards */ + &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed converting %s %s\n", local_share_path, nt_errstr(status)); + result = false; + goto out; + } +#else + smb_fname = synthetic_smb_fname(ctx, local_share_path, NULL, NULL, 0); + if (!smb_fname) { + result = false; + goto out; + } + if ((SMB_VFS_STAT(conn, smb_fname) == -1) && (errno == ENOENT)) { + result = false; + goto out; + } + +#endif + + status = SMB_VFS_CREATE_FILE(conn, + NULL, + 0, /* root_dir_fid */ + smb_fname, + /* 0x120089 stolen from debug of share access, need to breakdown the flags */ + 0x120089, + 5, + 1, + 64, + 0, + INTERNAL_OPEN_ONLY, + NULL, + 0, + 0, /* private_flags */ + NULL, + NULL, + &fsp, + &info, + NULL, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to open file %s %s\n", url, nt_errstr(status)); + result = false; + goto out; + } + result = true; + close_file(NULL, fsp, NORMAL_CLOSE); +out: + TALLOC_FREE(ctx); + return result; +} + +bool +can_access_url(struct connection_struct *conn, const char* url) +{ + /* + * seems the easiest way to test if we have permission is to try open + * file pointed by the url for reading. + */ + return can_open_url(conn, url); +} + +void filter_tracker_rows(struct tracker_row **rows, + struct connection_struct *conn, + struct auth_session_info *session_info, + uint32_t rows_limit, + uint32_t *num_rows) +{ + struct tracker_row *list = *rows; + struct tracker_row *item = list; + TALLOC_CTX *frame = talloc_stackframe(); + int id = 1; + become_user_by_session(conn, session_info); + /* + * #TODO think about limiting the number of returned rows here + * maybe we should even think about limiting the number of unfiltered + * rows we read (before filtering) + */ + while (item) { + struct tracker_row *next = item->next; + struct tracker_col_data *col = + &item->columns[0]; + const char *url = col->type.string; + if (!can_access_url(conn, url)) { + DLIST_REMOVE(list, item); + TALLOC_FREE(item); + (*num_rows)--; + } else { + /* populate EntryId in extra 'hidden' column*/ + item->columns[item->ncols - 1].tracker_type = + TRACKER_INTEGER; + item->columns[item->ncols - 1].type.integer = id++; + if (rows_limit && (id - 1 >= rows_limit)) { + item = next; + while(item) { + next = item->next; + DLIST_REMOVE(list, item); + TALLOC_FREE(item); + (*num_rows)--; + item = next; + } + goto out; + } + } + + item = next; + } +out: + *rows = list; + unbecome_user(); + TALLOC_FREE(frame); +} diff --git a/source3/rpc_server/wsp/wsp_sparql_conv.h b/source3/rpc_server/wsp/wsp_sparql_conv.h new file mode 100644 index 0000000..94899f5 --- /dev/null +++ b/source3/rpc_server/wsp/wsp_sparql_conv.h @@ -0,0 +1,99 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#ifndef __WSP_SPARQL_CONV_H__ +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" + +struct wsp_abstract_state; +struct connection_struct; +struct auth_session_info; +struct tracker_row; +/* + * per row data, like mangled path name for row enty + * to avoid recalculating that every time + */ +struct row_conv_data +{ + struct connection_struct *conn; + const char *tracker_row_url; /* url from tracker */ + /* url with segments after the netbios name mangled if nesessary */ + const char *row_relative_share_path; +}; + +struct tracker_selected_cols +{ + int cols; + const char **tracker_ids; +}; + +typedef NTSTATUS (*tracker_to_wsp_fn)(TALLOC_CTX *ctx, + struct wsp_cbasestoragevariant *out_val, + uint32_t vtype, /* wsp col type */ + int type, /*TrackerSparqlValueType*/ + void *tracker_val, + void *private_data); +struct map_data +{ + uint32_t col_with_value; + uint32_t vtype; + tracker_to_wsp_fn convert_fn; +}; + +struct binding_result_mapper +{ + uint32_t ncols; + struct map_data *map_data; +}; + +bool build_mapper(TALLOC_CTX *ctx, struct wsp_ctablecolumn *columns, + uint32_t ncols, struct tracker_selected_cols *tracker_cols, + struct binding_result_mapper *mapper); + +NTSTATUS build_tracker_query(TALLOC_CTX *ctx, + struct wsp_ccolumnset *select_cols, + const char* restriction_expr, + struct wsp_cpidmapper *pidmapper, + struct tracker_selected_cols *tracker_cols, + struct wsp_csortset *sorting, + bool convert_props, + const char **query); + + +const char * get_where_restriction_string(struct wsp_abstract_state *wsp_abstract_state, uint32_t id); + +NTSTATUS build_restriction_expression(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + struct wsp_crestrictionarray *restrictions, + bool convert_props, + const char **restrict_expr, + const char **share_scope, + uint32_t *where_id + ); + +void filter_tracker_rows(struct tracker_row **rows, + struct connection_struct *conn, + struct auth_session_info *session_info, + uint32_t rows_limit, + uint32_t *num_removed); + +bool +can_access_url(struct connection_struct *conn, const char* url); +#endif /*__WSP_SPARQL_CONV_H__*/ diff --git a/source3/rpc_server/wsp/wsp_srv_tracker-sparql.c b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.c new file mode 100644 index 0000000..21adbe8 --- /dev/null +++ b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.c @@ -0,0 +1,831 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include "wsp_srv_tracker-sparql.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "util/tevent_ntstatus.h" +#include "util/samba_util.h" +#include "lib/tevent_glib_glue.h" + +#include +#include +#include "talloc.h" +#include "tevent/tevent_util.h" +#include "util/talloc_stack.h" +#include "util/debug.h" +#include "wsp_sparql_conv.h" + +struct sparql_ctx; + +struct tracker_query_info +{ + enum sparql_server_query_state state; + int query_id; + GTimer *timer; + struct sparql_ctx *sparq_ctx; + GCancellable *cancellable; + TrackerSparqlCursor *cur_cursor; + struct tracker_row *row_cursor; + uint32_t cur_index; + uint32_t num_rows; + uint32_t num_cols; + uint32_t results_limit; + bool cache_results; + struct connection_struct *conn; + struct auth_session_info *session_info; + struct tracker_row *rows; +}; + +struct tracker_connect_info { + TrackerSparqlConnection *connection; + GTimer *timer; + GCancellable *cancellable; + struct sparql_ctx *sparql_ctx; +}; + +struct sparql_ctx +{ + struct tracker_connect_info *conn; + + int nqueries; + struct tevent_context *event_ctx; + struct tevent_glib_glue *glue; +}; + +struct query_state +{ + struct tracker_query_info *query; + struct tevent_req *req; + struct tevent_context *event_ctx; + GCancellable *cancellable; +}; + +#if ASYNC_CONNECTION +static void +connection_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct tracker_connect_info *conn = user_data; + GError *error = NULL; + + conn->connection = tracker_sparql_connection_get_direct_finish(res, + &error); + if (error) { + DBG_ERR("Unexpected error getting connection %s\n", + error->message); + goto out; + } + DBG_NOTICE("Async connection took: %.6f\n", g_timer_elapsed (conn->timer, NULL)); + + if (!conn->connection) { + DBG_ERR("Async connection error, %s\n", + error ? error->message : "Unknown error"); + /* + * #FIXME send an error back to server/parent + * to do that we need to tie the connectection to some + * existing request from the client (maybe some lazy + * initialisation that is triggered from *any* client message) + */ + goto out; + } +out: + if (error) { + g_error_free(error); + } + if (conn->cancellable) { + g_clear_object(&conn->cancellable); + } + if (conn->timer) { + g_timer_destroy (conn->timer); + } + TALLOC_FREE(conn); +} +#endif + +static uint8_t get_tracker_col_type(TrackerSparqlValueType type) +{ + uint8_t result; + switch(type) + { + case TRACKER_SPARQL_VALUE_TYPE_URI: + case TRACKER_SPARQL_VALUE_TYPE_STRING: + case TRACKER_SPARQL_VALUE_TYPE_DATETIME: + result = TRACKER_STRING; + break; + case TRACKER_SPARQL_VALUE_TYPE_INTEGER: + result = TRACKER_INTEGER; + break; + case TRACKER_SPARQL_VALUE_TYPE_DOUBLE: + result = TRACKER_DOUBLE; + break; + case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN: + result = TRACKER_BOOLEAN; + break; + case TRACKER_SPARQL_VALUE_TYPE_UNBOUND: + case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE: + default: + result = TRACKER_NULL; + break; + } + return result; +} + +static void create_tracker_col_data(TALLOC_CTX *ctx, + TrackerSparqlCursor *cursor, + int col, + struct tracker_col_data *result) +{ + const char *tracker_str; + char *escaped = NULL; + TrackerSparqlValueType type = + tracker_sparql_cursor_get_value_type(cursor, col); + result->tracker_type = get_tracker_col_type(type); + switch (type) + { + case TRACKER_SPARQL_VALUE_TYPE_URI: + case TRACKER_SPARQL_VALUE_TYPE_STRING: + case TRACKER_SPARQL_VALUE_TYPE_DATETIME: + tracker_str = tracker_sparql_cursor_get_string(cursor, + col, + NULL); + /* + * #TODO #FIXME we should escape further up the chain + * and not here. e.g. we could unintentionally + * unescape normal valid characters in a literal + * string here. + * Instead we should unescape in the handlers for + * properties that we know contain a 'real' url. + */ + if (type == TRACKER_SPARQL_VALUE_TYPE_STRING) { + escaped = g_uri_unescape_string(tracker_str, + NULL); + result->type.string = talloc_strdup(ctx, + escaped); + } else { + result->type.string = + talloc_strdup(ctx, tracker_str); + } + if (escaped) { + g_free(escaped); + } + break; + case TRACKER_SPARQL_VALUE_TYPE_INTEGER: + result->type.integer = + tracker_sparql_cursor_get_integer(cursor, col); + break; + case TRACKER_SPARQL_VALUE_TYPE_DOUBLE: + result->type.double_val = + tracker_sparql_cursor_get_double(cursor, col); + break; + case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN: + result->type.boolean = + tracker_sparql_cursor_get_boolean(cursor, col); + break; + case TRACKER_SPARQL_VALUE_TYPE_UNBOUND: + case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE: + default: + break; + } +} + + +static int destroy_query(struct tracker_query_info *query) +{ + g_timer_destroy (query->timer); + if (query->cancellable) { + /* + * query->cancellable is used for not just the query but + * any cursor iteration associated with the query + * If we get here we are in the middle of some async + * operation so we should signal that that operation to + * cancel. We don't unref the cancel object here, that needs + * to be done when the async operation finished + */ + g_cancellable_cancel(query->cancellable); + } + if (query->cur_cursor) { + g_object_unref(query->cur_cursor); + } + query->sparq_ctx->nqueries--; + return 0; +} + +static void rewind_cursor_fully(struct tracker_query_info *query) +{ + tracker_sparql_cursor_rewind(query->cur_cursor); + query->cur_index = 0; + query->row_cursor = query->rows; +} + +static void +filter_rows(struct tracker_query_info *query) +{ + filter_tracker_rows(&query->rows, + query->conn, + query->session_info, + query->results_limit, + &query->num_rows); +} + +static void +cursor_cb(GObject *object, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + gboolean more_results; + TrackerSparqlCursor *cursor; + struct tracker_query_info *query_info; + struct query_state *state = talloc_get_type_abort(user_data, + struct query_state); + + cursor = TRACKER_SPARQL_CURSOR(object); + more_results = tracker_sparql_cursor_next_finish(cursor, + res, + &error); + if (error) { + DBG_ERR("Could not run cursor next: %s", error->message); + g_error_free (error); + /* + * We should only get a cancel from a query that is + * being torn down/deleted thus the query object + * itself will not exist and should not be dereferenced. + * For an ordinary error we can clear the cancellable + * of the query object + */ + if (state->cancellable) { + if (!g_cancellable_is_cancelled(state->cancellable)) { + state->query->cancellable = NULL; + } + g_clear_object(&state->cancellable); + } + tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL); + return; + + } + + query_info = state->query; + query_info->cur_cursor = TRACKER_SPARQL_CURSOR (object); + if (query_info->num_cols == 0) { + query_info->num_cols = + tracker_sparql_cursor_get_n_columns(query_info->cur_cursor); + } + DEBUG(0,("more_results %d limit %d %d\n", more_results, query_info->results_limit, !(query_info->results_limit && query_info->num_rows >= query_info->results_limit))); + if (more_results && + !(query_info->cache_results == false && query_info->results_limit && + query_info->num_rows >= query_info->results_limit)) { + if (query_info->cache_results) { + struct tracker_row *row; + int i; + row = talloc_zero(query_info, + struct tracker_row); + row->columns = talloc_zero_array(row, + struct tracker_col_data, + query_info->num_cols + 1); + row->ncols = query_info->num_cols + 1; + query_info->cur_index = query_info->num_rows; + for (i = 0; i < query_info->num_cols; i++) { + struct tracker_col_data *col = + &row->columns[i]; + create_tracker_col_data(row, cursor, i, col); + } + DLIST_ADD_END(query_info->rows, row); + } + query_info->num_rows++; + tracker_sparql_cursor_next_async(query_info->cur_cursor, + query_info->cancellable, + cursor_cb, + user_data); + return; + } + if (query_info->cache_results) { + /* we need to acl filter the results */ + filter_rows(query_info); + } + query_info->state = QUERY_COMPLETE; + DBG_NOTICE("\nAsync cursor next took: %.6f (for all results %d rows for " + "tracker query %d)\n", + g_timer_elapsed(query_info->timer, NULL), + query_info->num_rows, query_info->query_id); + if (query_info->num_rows) { + rewind_cursor_fully(query_info); + } + state->query->cancellable = NULL; + if (state->cancellable) { + g_clear_object(&state->cancellable); + } + tevent_req_done(state->req); +} + +static void +query_cb(GObject *object, GAsyncResult *res, gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GError *error = NULL; + struct tracker_query_info *query; + struct query_state *state = talloc_get_type_abort(user_data, + struct query_state); + + cursor = + tracker_sparql_connection_query_finish( + TRACKER_SPARQL_CONNECTION (object), + res, + &error); + if (error) { + DBG_ERR("Could not run query: %s", error->message); + + g_error_free (error); + /* + * we can't deference the query info, it's possible that + * that we have gotten here as a result of the query + * being cancelled + */ + tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL); + return; + } + + query = state->query; + + DBG_NOTICE("Async query took: %.6f\n", g_timer_elapsed(query->timer, + NULL)); + + g_timer_start(query->timer); + + query->cur_cursor = cursor; + tracker_sparql_cursor_next_async(cursor, + query->cancellable, + cursor_cb, + user_data); +} + +#if FOR_DEBUG +static void dump_val(struct tracker_col_data *col) +{ + switch(col->tracker_type) { + case TRACKER_STRING: + DBG_DEBUG("tracker_string %s\n", col->type.string); + break; + case TRACKER_INTEGER: + DBG_DEBUG("tracker_int %lu\n", col->type.integer); + break; + case TRACKER_BOOLEAN: + DBG_DEBUG("tracker_boolean %d\n", col->type.boolean); + break; + case TRACKER_DOUBLE: + DBG_DEBUG("tracker_double %f\n", + (double)col->type.double_val); + break; + default: + DBG_DEBUG("tracker unknown type %d\n", + col->tracker_type); + break; + } +} +#endif + + +struct dummy_get_rows_state +{ +}; + +static void mov_cursor_fwd(struct tracker_query_info* query, uint32_t new_pos) +{ + struct tracker_row *row_cursor = query->row_cursor; + while (row_cursor && query->cur_index < new_pos) { + row_cursor = row_cursor->next; + query->cur_index++; + query->row_cursor = row_cursor; + } +} + +struct stats_state +{ + struct wsp_cpmcistateinout *cistateinout; + struct tevent_req *req; + struct sparql_ctx *sparq_ctx; + GCancellable *cancellable; +}; + +static void stats_cursor_cb(GObject *object, GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GError *error = NULL; + gboolean more_results; + struct stats_state *state = talloc_get_type_abort(user_data, + struct stats_state); + + cursor = TRACKER_SPARQL_CURSOR (object); + more_results = tracker_sparql_cursor_next_finish(cursor, + res, + &error); + + if (error) { + DBG_ERR("got error iterating statistics cursor, error %s\n", + error->message); + g_error_free(error); + /* #FIXME not the common shared query_info cancellable */ + if (state->cancellable) { + g_cancellable_cancel(state->cancellable); + g_object_unref(state->cancellable); + } + tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL); + return; + } + + if (more_results) { + if (strequal(tracker_sparql_cursor_get_string (cursor, 0, NULL), + "nie:InformationElement")) { + state->cistateinout->cwordlist = + tracker_sparql_cursor_get_integer(cursor, 1); + } else { + tracker_sparql_cursor_next_async(cursor, + state->cancellable, + stats_cursor_cb, + state); + return; + } + } + //state->cistateinout->cpersistentindex = ?; + state->cistateinout->cqueries = state->sparq_ctx->nqueries; + + //out->cfreshtest = ?; + /* not sure if this is a good default */ + state->cistateinout->dwmergeprogress = 100; + state->cistateinout->estate = 0; + state->cistateinout->cfiltereddocuments = + state->cistateinout->cwordlist; + state->cistateinout->ctotaldocuments = + state->cistateinout->cwordlist; + //state->cistateinout->cpendingscans = ?; + //state->cistateinout->dwindexsize = ?; + //state->cistateinout->cuniquekeys = ?; + //state->cistateinout->csecqdocuments = ?; + //state->cistateinout->dwpropcachesize = ?; + if (cursor) { + g_object_unref (cursor); + } + /* #FIXME not the common query_info share cancellable */ + if (state->cancellable) { + g_cancellable_cancel(state->cancellable); + g_object_unref(state->cancellable); + } + + tevent_req_done(state->req); +} + +static void statistics_cb(GObject *object, GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + TrackerSparqlCursor *cursor; + struct stats_state *state = talloc_get_type_abort(user_data, + struct stats_state); + cursor = tracker_sparql_connection_statistics_finish( + TRACKER_SPARQL_CONNECTION (object), + res, + &error); + + if (error) { + DBG_ERR("spaql_statistics failed, reason %s\n", + error->message); + if (cursor) { + g_object_unref (cursor); + } + if (state->cancellable) { + g_clear_object(&state->cancellable); + } + g_error_free(error); + tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL); + return; + } + + tracker_sparql_cursor_next_async(cursor, state->cancellable, + stats_cursor_cb, + state); + return; +} + +struct glib_query_state +{ + uint32_t *rows; + uint32_t *status; +}; + +struct tevent_req *glib_tracker_query_status( + TALLOC_CTX *ctx, + struct tracker_query_info* query_info, + uint32_t *status, + uint32_t *nrows) +{ + struct glib_query_state *state; + struct tevent_req *req; + + req = tevent_req_create(ctx, &state, struct glib_query_state); + + if (!req) { + return NULL; + } + + state->rows = nrows; + state->status = status; + *state->status = query_info->state; + *state->rows = query_info->num_rows; + tevent_req_done(req); + return tevent_req_post(req, query_info->sparq_ctx->event_ctx); +} + +struct tevent_req *glib_tracker_new_query(TALLOC_CTX *ctx, + struct sparql_ctx* sparql_ctx, + uint32_t queryid, + const char *query, + bool cache_results, + uint32_t results_limit, + struct connection_struct *conn, + struct auth_session_info *session_info, + struct tracker_query_info **query_ctx) +{ + struct tracker_query_info *query_info; + struct tevent_req *req; + struct query_state *state; + + req = tevent_req_create(ctx, &state, struct query_state); + + if (!state) { + return NULL; + } + + query_info = talloc_zero(sparql_ctx, struct tracker_query_info); + + if (!query_info) { + return NULL; + } + + talloc_set_destructor(query_info, destroy_query); + + query_info->timer = g_timer_new (); + query_info->sparq_ctx = sparql_ctx; + query_info->state = QUERY_IN_PROGRESS; + query_info->query_id = queryid; + query_info->cancellable = g_cancellable_new(); + query_info->cache_results = cache_results; + query_info->results_limit = results_limit; + query_info->conn = conn; + query_info->session_info = session_info; + + *query_ctx = query_info; + + state->event_ctx = query_info->sparq_ctx->event_ctx; + state->req = req; + state->query = query_info; + state->cancellable = query_info->cancellable; + sparql_ctx->nqueries++; + + tracker_sparql_connection_query_async(sparql_ctx->conn->connection, + query, + query_info->cancellable, + query_cb, state); + return req; +} + +struct tevent_req *glib_tracker_getrows(TALLOC_CTX *ctx, + struct sparql_ctx* sparql_ctx, + struct tracker_query_info* query_info, + uint32_t index, uint32_t nrows, + bool reverse_fetch, + struct tracker_getrowsout *rowsout) +{ + struct get_rows_state *state; + + struct tevent_req *req; + + uint32_t i,j; + + uint32_t remaining_rows; + struct tracker_row *row_cursor; + + if (query_info->state != QUERY_COMPLETE) { + DBG_ERR("shouldn't get here query %d is still " + "returning results\n", + query_info->query_id); + return NULL; + } + + req = tevent_req_create(ctx, &state, struct dummy_get_rows_state); + if (!req) { + DBG_ERR("out of memory error\n"); + return NULL; + } + + remaining_rows = reverse_fetch ? (index - 1) : (query_info->num_rows - index); + if (nrows > remaining_rows) { + DBG_ERR("failed to get %d rows, cur index %d " + "total rows %d num rows available, " + "clipping rows returned to %d\n", + nrows, query_info->cur_index, query_info->num_rows, + remaining_rows); + nrows = remaining_rows; + } + if (query_info->cur_index != index) { + if (query_info->cur_index > index) { + rewind_cursor_fully(query_info); + } + DBG_ERR("moving cursor forward to new index %d\n", + index); + mov_cursor_fwd(query_info, index); + } + + rowsout->nrows = nrows; + rowsout->rows = talloc_zero_array(ctx, + struct tracker_row, + nrows); + + row_cursor = query_info->row_cursor; + i = 0; + while(row_cursor && i < nrows) { + struct tracker_col_data *src_cols = row_cursor->columns; + struct tracker_col_data *dest_columns = + talloc_zero_array(rowsout->rows, + struct tracker_col_data, + row_cursor->ncols); + rowsout->rows[i].ncols = row_cursor->ncols; + rowsout->rows[i].columns = dest_columns; + for (j = 0; j < row_cursor->ncols; j++) { + dest_columns[j] = src_cols[j]; + } + if (reverse_fetch) { + row_cursor = row_cursor->prev; + query_info->cur_index--; + } + else { + row_cursor = row_cursor->next; + query_info->cur_index++; + } + query_info->row_cursor = row_cursor; + i++; + } + rowsout->nrows_remaining = reverse_fetch ? (query_info->cur_index - 1) : + (query_info->num_rows - query_info->cur_index ); + DBG_NOTICE("retrieved %d rows from index is now %d rows remaining %d\n", + nrows, query_info->cur_index, rowsout->nrows_remaining); + tevent_req_done(req); + tevent_req_post(req, query_info->sparq_ctx->event_ctx); + return req; +} + +struct tevent_req *glib_tracker_getstate(TALLOC_CTX *ctx, + struct sparql_ctx* sparql_ctx, + struct wsp_cpmcistateinout *out) +{ + struct tevent_req *req; + struct stats_state *state; + + req = tevent_req_create(ctx, &state, struct stats_state); + if (!req) { + DBG_ERR("oom failed to create request\n"); + return NULL; + } + state->req = req; + state->cistateinout = out; + state->cancellable = g_cancellable_new(); + state->sparq_ctx = sparql_ctx; + tracker_sparql_connection_statistics_async(sparql_ctx->conn->connection, + state->cancellable, + statistics_cb, + state); + return req; +} + +bool can_access_workid(struct tracker_query_info* query_ctx, uint32_t workid) +{ + /* + * with this specific tracker implementation the workid gives the + * row position of the entry we want + */ + uint32_t index = workid - 1; + uint32_t i; + struct tracker_row *item = query_ctx->rows; + const char *url = NULL; + + /* get row at index */ + for(i = 0; i < index && item; i++, item = item->next); + + url = item->columns[0].type.string; + + return can_access_url(query_ctx->conn, url); +} + +/* + * Handle the following error from glibc + * "GLib-WARNING **: In call to g_spawn_sync(), exit status of a + * child process was requested but ECHILD was received by waitpid(). + * Most likely the process is ignoring SIGCHLD, or some other thread is + * invoking waitpid() with a nonpositive first argument; either behavior + * can break applications that use g_spawn_sync either directly or indirectly." + * We don't spawn (afaik) any other child processes from this one so + * resetting the signal handler to default should get rid of the error + * and associated race it warns about + */ +static void fix_glib_weirdness(void) +{ + signal(SIGCHLD, SIG_DFL); +} + +static int destroy_sparql_ctx(struct sparql_ctx *ctx) +{ + struct tracker_connect_info *conn = ctx->conn; + if (conn) { + if (conn->timer) { + g_timer_destroy (conn->timer); + conn->timer = NULL; + } + if (conn->cancellable) { + g_clear_object(&conn->cancellable); + } + if (conn->connection) { + g_clear_object(&conn->connection); + } + } + TALLOC_FREE(ctx->glue); + return 0; +} + +struct sparql_ctx * init_sparql(struct tevent_context *event_ctx) +{ + struct tracker_connect_info *conn; + struct sparql_ctx *sparql_ctx; + GError *error = NULL; + + + sparql_ctx = talloc_zero(NULL, struct sparql_ctx); + if (!sparql_ctx) { + return NULL; + } + talloc_set_destructor(sparql_ctx, destroy_sparql_ctx); + sparql_ctx->event_ctx = event_ctx; + + sparql_ctx->conn = talloc_zero(sparql_ctx, struct tracker_connect_info); + conn = sparql_ctx->conn; + conn->timer = g_timer_new (); + conn->sparql_ctx = sparql_ctx; + conn->cancellable = g_cancellable_new(); + fix_glib_weirdness(); +#if ASYNC_CONNECTION + tracker_sparql_connection_get_direct_async(conn->cancellable, + connection_cb, + conn); +#else + DBG_NOTICE("Sync connection took: %.6f\n", + g_timer_elapsed(conn->timer, NULL)); + /* for the moment lets connect the tracker on startup */ + conn->connection = + tracker_sparql_connection_get(conn->cancellable, + &error); + if (error || conn->connection == NULL) { + if (error) { + DBG_ERR("Unexpected error getting connection %s\n", + error->message); + g_error_free (error); + } + if (conn->connection == NULL) { + DBG_ERR("no connection to tracker\n"); + } + TALLOC_FREE(sparql_ctx); + return NULL; + } + if (conn->timer) { + g_timer_destroy (conn->timer); + conn->timer = NULL; + } + if (conn->cancellable) { + g_clear_object(&conn->cancellable); + } +#endif + sparql_ctx->glue = + samba_tevent_glib_glue_create(sparql_ctx, + sparql_ctx->event_ctx, + g_main_context_default()); + if (!sparql_ctx->glue) { + DBG_ERR("failed to create glib/tevent integration\n"); + TALLOC_FREE(sparql_ctx); + } + return sparql_ctx; +} + diff --git a/source3/rpc_server/wsp/wsp_srv_tracker-sparql.h b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.h new file mode 100644 index 0000000..5c8a7dd --- /dev/null +++ b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.h @@ -0,0 +1,105 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#ifndef __WSP_SRV_TRACKER_SPARQL__ +#define __WSP_SRV_TRACKER_SPARQL__ +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "tevent.h" + +struct tracker_query_info; +struct connection_struct; +struct auth_session_info; + +#define TRACKER_NULL ( 0x00000000 ) +#define TRACKER_STRING ( 0x00000001 ) +#define TRACKER_INTEGER ( 0x00000002 ) +#define TRACKER_BOOLEAN ( 0x00000003 ) +#define TRACKER_DOUBLE ( 0x00000004 ) + +union tracker_type { + const char * string; + uint64_t integer; + uint16_t boolean; + uint64_t double_val; +}; + +struct tracker_col_data { + uint8_t tracker_type; + union tracker_type type; +}; + +struct tracker_row { + struct tracker_row *prev, *next; + uint32_t ncols; + struct tracker_col_data *columns; +}; + +struct tracker_getrowsout { + uint32_t nrows; + uint32_t nrows_remaining; + struct tracker_row *rows; +}; + +struct tracker_row_list +{ + int nrows; + struct tracker_row_data *items; +}; + +enum sparql_server_query_state +{ + IDLE, + QUERY_ERROR, + QUERY_IN_PROGRESS, + QUERY_COMPLETE, +}; + +struct sparql_ctx *init_sparql(struct tevent_context *event_ctx); + +struct tevent_req *glib_tracker_query_status(TALLOC_CTX *ctx, + struct tracker_query_info* query_ctx, + uint32_t *status, uint32_t *nrows); + +struct tevent_req *glib_tracker_getstate(TALLOC_CTX *ctx, + struct sparql_ctx *sparql_ctx, + struct wsp_cpmcistateinout *out); + + +struct tevent_req *glib_tracker_getrows(TALLOC_CTX *ctx, + struct sparql_ctx* sparql_ctx, + struct tracker_query_info* query_ctx, + uint32_t index, + uint32_t nrows, + bool reverse_fetch, + struct tracker_getrowsout *rowsout); + +struct tevent_req *glib_tracker_new_query(TALLOC_CTX *ctx, + struct sparql_ctx* sparql_ctx, + uint32_t queryid, + const char *query, + bool cache_results, + uint32_t results_limit, + struct connection_struct *conn, + struct auth_session_info *session_info, + struct tracker_query_info** query_ctx); + +bool can_access_workid(struct tracker_query_info* query_ctx, uint32_t workid); +#endif diff --git a/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c b/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c new file mode 100644 index 0000000..e46c416 --- /dev/null +++ b/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c @@ -0,0 +1,1627 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#include +#include "wsp_srv_tracker_abs_if.h" +#include "wsp_gss.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "wsp_sparql_conv.h" +#include "serverid.h" +#include "messages.h" +#include "rpc_server/rpc_pipes.h" +#include "rpc_server/rpc_server.h" +#include "wsp_srv_tracker-sparql.h" +#include "smbd/proto.h" +#include "util/tevent_ntstatus.h" +#include "libcli/security/security.h" + +struct dummy_async_state +{ + uint32_t dummy; +}; + +struct client_info { + struct client_info *prev, *next; + struct wsp_cpmconnectin *connectin; + uint32_t query_id; + uint32_t handle; +}; + +struct query_list { + int nqueries; + struct query_data *items; +}; + +struct wsp_abstract_state { + struct client_info *client_info_map; + struct query_list queries; + uint64_t current_request_id; + struct tevent_context *ev; + struct sparql_ctx *sparql_ctx; +}; + +struct binding_data +{ + struct binding_data *prev, *next; + struct wsp_ctablecolumn *columns; + struct binding_result_mapper *result_converter; + uint32_t ncols; + uint32_t cursor_hndl; +}; + +struct binding_list { + int nbindings; + struct binding_data *items; +}; + +struct next_cursor_data { + struct next_cursor_data *prev, *next; + uint32_t cursor; + uint32_t chapter; + uint32_t index; +}; + +struct next_cursor_list { + struct next_cursor_data *items; +}; + +struct query_data +{ + struct query_data *prev, *next; + struct tracker_query_info *tracker_ctx; + struct wsp_abstract_state *glob_data; + int query_id; + struct wsp_crestrictionarray restrictionset; + enum sparql_server_query_state state; + struct tracker_selected_cols cols_to_convert; + struct binding_list bindings; + struct next_cursor_list next_cursors; + struct wsp_crowsetproperties rowsetproperties; + struct connection_struct *vfs_conn; + const char* where_filter; + const char* share; + bool no_index; + bool wsp_enabled; + /* + * current index set from index passed from last call to + * SetNextGetRowsPosition. (maybe we should just store the + * last chapter...) + */ + uint32_t current_index; + uint32_t ncursors; + uint32_t nrows; /* only set when query is finished */ + /* + * #FIXME current_url & current workid should be replaced by + * some sort of cache/hash of workid -> url + */ + const char* current_url; + uint32_t workid; +}; + +static struct client_info* find_client_info( + uint32_t handle, + struct wsp_abstract_state *glob_data) +{ + struct client_info *item = NULL; + for (item = glob_data->client_info_map; item; item = item->next) { + if (item->handle == handle) { + break; + } + } + return item; +} + +static struct next_cursor_data *find_next_cursor_index( + struct query_data *query_info, + uint32_t cursor, + uint32_t chapter) +{ + struct next_cursor_data *item = query_info->next_cursors.items; + for (; item; item = item->next) { + if (item->chapter == chapter && item->cursor == cursor) { + return item; + } + } + return NULL; +} + +static struct query_data *find_query_info(uint32_t query_id, + struct wsp_abstract_state *globals) +{ + struct query_data *item; + for (item = globals->queries.items; item; item = item->next) { + if (item->query_id == query_id) { + return item; + } + } + return NULL; +} + +static struct binding_data *find_bindings(uint32_t QueryIdentifier, + uint32_t CursorHandle, + struct wsp_abstract_state *globals) +{ + struct binding_data *item = NULL; + struct query_data *query_data = find_query_info(QueryIdentifier, + globals); + if (query_data) { + for (item = query_data->bindings.items; item; + item = item->next) { + if (item->cursor_hndl == CursorHandle) { + return item; + } + } + } + return NULL; +} + +/* release all data associated with query_info */ +static int destroy_query_data(struct query_data *query_info) +{ + struct client_info* cli_item = + query_info->glob_data->client_info_map; + struct wsp_abstract_state *glob_data = query_info->glob_data; + + TALLOC_FREE(query_info->cols_to_convert.tracker_ids); + DLIST_REMOVE(glob_data->queries.items, query_info); + glob_data->queries.nqueries--; + if (query_info->vfs_conn) { + SMB_VFS_DISCONNECT(query_info->vfs_conn); + conn_free(query_info->vfs_conn); + } + if (query_info->tracker_ctx) { + TALLOC_FREE(query_info->tracker_ctx); + } else { + DBG_ERR("failed to retrieve tracker_ctx for handle %d\n", + query_info->query_id); + } + while(cli_item) { + struct client_info *next_cli = cli_item->next; + if (cli_item->handle == query_info->query_id) { + DLIST_REMOVE(glob_data->client_info_map, + cli_item); + TALLOC_FREE(cli_item); + } + cli_item = next_cli; + } + return 0; +} + +const char * get_where_restriction_string(struct wsp_abstract_state *glob_data, + uint32_t id) +{ + /* search all open queries for where id */ + struct query_data *item = NULL; + if (glob_data) { + item = glob_data->queries.items; + } + for (;item;item = item->next) { + if (item->query_id == id && item->where_filter) { + return item->where_filter; + } + } + return NULL; +} + +static bool is_catalog_available(struct wspd_client_state *client_data, + const char *CatalogName) +{ + return strequal(CatalogName, "Windows\\SYSTEMINDEX"); +} + +static void store_client_information(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + struct wsp_cpmconnectin *ConnectMessage, + uint32_t NamedPipeHandle) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct client_info *client_info = talloc_zero(glob_data, + struct client_info); + client_info->handle = NamedPipeHandle; + client_info->query_id = QueryIdentifier; + client_info->connectin = ConnectMessage; + DLIST_ADD_END(glob_data->client_info_map, client_info); +} + +static void get_server_versions(struct wspd_client_state *client_state, + uint32_t *dwWinVerMajor, + uint32_t *dwWinVerMinor, + uint32_t *dwNLSVerMajor, + uint32_t *dwNLSVerMinor, + uint32_t *serverVersion, + bool *supportsVersioningInfo) +{ + *supportsVersioningInfo = false; +#if __WORDSIZE == 64 + /* 64 bit win7 */ + *serverVersion = 0x00010700; +#else + /* 32 bit win7 */ + *serverVersion = 0x00000700; +#endif +} + +struct run_new_query_state +{ + uint32_t *QueryParametersError; + bool *CanQueryNow; +}; + +static void run_new_query_done(struct tevent_req *subreq); +static struct tevent_req *run_new_query_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + struct wsp_ccolumnset *ProjectionColumnsOffsets, + struct wsp_crestrictionarray *RestrictionSet, + struct wsp_csortset *SortOrders, + struct wsp_ccategorizationset *Groupings, + struct wsp_crowsetproperties *RowSetProperties, + struct wsp_cpidmapper *PidMapper, + struct wsp_ccolumngrouparray *GroupArray, + uint32_t Lcid, uint32_t *QueryParametersError, + uint32_t **CursorHandlesList, + bool *fTrueSequential, bool *fWorkidUnique, + bool *CanQueryNow) +{ + struct query_data *query_info; + const char *sparql_query; + bool can_query_now = true; + int i; + const char *restriction_expr; + const char *share = NULL; + bool no_index = false; + struct tevent_req *req, *subreq = NULL; + struct run_new_query_state *state = NULL; + uint32_t where_id; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct pipes_struct *p = get_pipe(client_state); + NTSTATUS status; + *QueryParametersError = 0; + + req = tevent_req_create(ctx, &state, struct run_new_query_state); + if (!req) + { + DBG_ERR("out of memory\n"); + return NULL; + } + + state->QueryParametersError = QueryParametersError; + state->CanQueryNow = CanQueryNow; + if (!RestrictionSet) { + status = NT_STATUS_INVALID_PARAMETER; + goto err_out; + } + + query_info = talloc_zero(glob_data, struct query_data); + if (!query_info) { + DBG_ERR("out of memory\n"); + return NULL; + } + + query_info->state = QUERY_IN_PROGRESS; + query_info->query_id = QueryIdentifier; + query_info->ncursors = Groupings ? Groupings->size +1 : 1; + query_info->glob_data = glob_data; + + talloc_set_destructor(query_info, destroy_query_data); + + DLIST_ADD_END(glob_data->queries.items, query_info); + glob_data->queries.nqueries++; + + /* might need to keep these for later use (e.g. whereid processing) */ + status = build_restriction_expression(query_info, + glob_data, + RestrictionSet, + true, + &restriction_expr, + &share, + &where_id); + if (NT_STATUS_IS_OK(status)) { + query_info->where_filter = restriction_expr; + } else { + DBG_ERR("error %s when creating filter string\n", + nt_errstr(status)); + /* + * let this special error through, just don't + * actually run the query + */ + if (NT_STATUS_EQUAL(status,NT_STATUS(WIN_UPDATE_ERR))) { + no_index = true; + query_info->no_index = true; + } else { + *QueryParametersError = NT_STATUS_V(status); + can_query_now = false; + goto err_out; + } + } + + if (!share) { + if (where_id) { + struct query_data *tmp_data = + find_query_info(where_id, + glob_data); + if (tmp_data) { + share = tmp_data->share; + } + } + } + + *CursorHandlesList = talloc_zero_array(query_info, uint32_t, + query_info->ncursors); + /* allocate cursor id(s) */ + for (i = 0; i < query_info->ncursors; i++) { + struct next_cursor_data *item = talloc_zero(query_info, + struct next_cursor_data); + *CursorHandlesList[i] = i + 1; + + /* initial index (with unchaptered chapter) */ + item->chapter = 0; + item->cursor = *CursorHandlesList[i]; + item->index = 0; + + DLIST_ADD_END(query_info->next_cursors.items, item); + } + + if (!share) { + status = NT_STATUS_INVALID_PARAMETER; + can_query_now = false; + goto err_out; + } else { + char *service; + int snum = find_service(query_info, share, &service); + DBG_INFO("SHARE %s has indexing = %s\n", share, + lp_wsp(snum) ? "enabled" : "disabled"); + query_info->share = talloc_strdup(query_info,share); + query_info->wsp_enabled = lp_wsp(snum); + if (query_info->wsp_enabled == false) { + status = NT_STATUS_OK; + goto err_out; + } + if ((snum == -1) || (service == NULL)) { + DBG_ERR("share %s not found\n", share); + status = NT_STATUS_INVALID_PARAMETER; + can_query_now = false; + goto err_out; + } + + status = create_conn_struct(query_info, server_event_context(), + p->msg_ctx, &query_info->vfs_conn, + snum, lp_path(query_info, snum), + p->session_info); + + DBG_INFO("CONNECTION status = %s\n", nt_errstr(status)); + } + + talloc_steal(query_info, RestrictionSet->restrictions); + + status = build_tracker_query(query_info, + ProjectionColumnsOffsets, + restriction_expr, + PidMapper, + &query_info->cols_to_convert, + SortOrders, + true, + &sparql_query); + + if (!NT_STATUS_IS_OK(status)) { + can_query_now = false; + goto err_out; + } + + query_info->rowsetproperties = *RowSetProperties; + query_info->restrictionset = *RestrictionSet; + + *fWorkidUnique = false; + if (!no_index && can_query_now) { + uint32_t bool_opts = + query_info->rowsetproperties.ubooleanoptions; + bool cache_results = + !(bool_opts & EDONOTCOMPUTEEXPENSIVEPROPS); + subreq = glib_tracker_new_query(state, + glob_data->sparql_ctx, + QueryIdentifier, + sparql_query, + cache_results, + lp_wsp_result_limit(), + query_info->vfs_conn, + p->session_info, + &query_info->tracker_ctx); + if (!subreq) { + can_query_now = false; + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + /* kick off query */ + DBG_INFO("tracker-sparql query is \"%s\"\n", sparql_query); + tevent_req_set_callback(subreq, run_new_query_done, req); + } + /* + * we don't keep the unique ids around, if we wanted to we would + * need to somehow map the tracker internal uuid to a 32 bit + * identifier, that seems a bit heavyweight + */ + status = NT_STATUS_OK; + return req; +err_out: + *CanQueryNow = can_query_now; + *QueryParametersError = NT_STATUS_V(status); + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, glob_data->ev); +} + +static void run_new_query_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + NTSTATUS status = NT_STATUS_OK; + struct run_new_query_state *state = + tevent_req_data(req, struct run_new_query_state); + if (tevent_req_is_nterror(subreq, &status)) { + *state->CanQueryNow = false; + *state->QueryParametersError = NT_STATUS_V(status); + } else { + *state->CanQueryNow = true; + *state->QueryParametersError = NT_STATUS_V(NT_STATUS_OK); + } + TALLOC_FREE(subreq); + tevent_req_done(req); +} + +static void set_bindings(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + struct wsp_ctablecolumn *Columns, + uint32_t nColumns) +{ + struct query_data * query_data; + struct binding_data *binding; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + query_data = find_query_info(QueryIdentifier, + glob_data); + DBG_INFO("got bindings for %d columns %p\n", nColumns, Columns); + binding = talloc_zero(query_data, struct binding_data); + binding->columns = Columns; + binding->ncols = nColumns; + binding->cursor_hndl = CursorHandle; + /* + * need to create a mapping between the tracker cols returned + * and the binding columns requested + */ + binding->result_converter = talloc_zero(binding, + struct binding_result_mapper); + + build_mapper(binding, Columns, nColumns, + &query_data->cols_to_convert, binding->result_converter); + DLIST_ADD_END(query_data->bindings.items, binding); + query_data->bindings.nbindings++; +} + +static bool has_bindings(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle) +{ + struct binding_data *item = NULL; + struct wsp_abstract_state *glob_data = + client_state->wsp_abstract_state; + item = find_bindings(QueryIdentifier, CursorHandle, glob_data); + if (item) { + return true; + } + return false; +} + +struct get_tracker_query_state +{ + uint32_t *status; + uint32_t *nrows; +}; + +static void get_tracker_query_done(struct tevent_req *subreq); +static struct tevent_req *get_tracker_query_status(TALLOC_CTX *ctx, + struct wsp_abstract_state *glob_data, + uint32_t QueryIdentifier, + uint32_t *status, + uint32_t *nrows) +{ + bool no_index; + struct get_tracker_query_state *state = NULL; + struct query_data *query_info = find_query_info(QueryIdentifier, + glob_data); + struct tevent_req *req, *subreq; + NTSTATUS ntstatus = NT_STATUS_OK; + if (!query_info) { + return NULL; + } + + req = tevent_req_create(ctx, &state, + struct get_tracker_query_state); + + if (!req) { + return NULL; + } + + state->status = status; + state->nrows = nrows; + + no_index = query_info->no_index; + + + if (!no_index) { + subreq = glib_tracker_query_status(state, + query_info->tracker_ctx, + state->status, + state->nrows); + if (!subreq) { + DBG_ERR("communication with tracker server failed\n"); + ntstatus = NT_STATUS_UNSUCCESSFUL; + goto out; + } + tevent_req_set_callback(subreq, get_tracker_query_done, req); + } else { + *status = QUERY_COMPLETE; + *nrows = 0; + goto out; + } + return req; +out: + if (!tevent_req_nterror(req, ntstatus)) { + tevent_req_done(req); + } + return tevent_req_post(req, glob_data->ev); +} + +static void get_tracker_query_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + } else { + tevent_req_done(req); + } +} + +struct get_query_status_state +{ + uint32_t *QueryStatus; + uint32_t nrows; + struct query_data *query_data; +}; + + +static void get_query_status_done(struct tevent_req *subreq); +static struct tevent_req *get_query_status_send( + TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t *QueryStatus) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + struct tevent_req *req, *subreq = NULL; + struct get_query_status_state *state = NULL; + + req = tevent_req_create(ctx, &state, + struct get_query_status_state); + if (!req) { + return NULL; + } + state->QueryStatus = QueryStatus; + state->query_data = query_data; + + if (query_data->wsp_enabled == false) { + *QueryStatus = 2; + tevent_req_nterror(req, NT_STATUS_OK); + tevent_req_done(req); + return tevent_req_post(req, glob_data->ev); + } + subreq = get_tracker_query_status(state, glob_data, QueryIdentifier, + &query_data->state, &state->nrows); + if (!subreq) { + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, glob_data->ev); + } + tevent_req_set_callback(subreq, get_query_status_done, req); + return req; +} + +static void get_query_status_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct get_query_status_state *state = tevent_req_data(req, + struct get_query_status_state); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + switch(state->query_data->state) { + case QUERY_IN_PROGRESS: + *state->QueryStatus = STAT_BUSY; + break; + case QUERY_COMPLETE: + *state->QueryStatus = STAT_DONE; + state->query_data->nrows = state->nrows; + break; + case QUERY_ERROR: + case IDLE: + default: + *state->QueryStatus = STAT_ERROR; + break; + } + tevent_req_done(req); +} + +struct get_ratiofinished_state +{ + struct query_data *query_data; + uint32_t *rdwRatioFinishedDenominator; + uint32_t *rdwRatioFinishedNumerator; + uint32_t *cRows; + uint32_t *fNewRows; + uint32_t rows; +}; + +static void get_ratiofinished_params_done(struct tevent_req *subreq); +static struct tevent_req *get_ratiofinished_params_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t *rdwRatioFinishedDenominator, + uint32_t *rdwRatioFinishedNumerator, + uint32_t *cRows, + uint32_t *fNewRows) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + int rows = 0; + struct tevent_req *req, *subreq = NULL; + struct get_ratiofinished_state *state = NULL; + NTSTATUS status; + req = tevent_req_create(ctx, &state, + struct get_ratiofinished_state); + + state->query_data = query_data; + state->rdwRatioFinishedDenominator = rdwRatioFinishedDenominator; + state->rdwRatioFinishedNumerator = rdwRatioFinishedNumerator; + state->cRows = cRows; + state->fNewRows = fNewRows; + if (query_data) { + if (query_data->wsp_enabled == false) { + status = NT_STATUS_OK; + goto early_out; + } + if (query_data->state == QUERY_COMPLETE) { + rows = query_data->nrows; + status = NT_STATUS_OK; + } else { + subreq = get_tracker_query_status(state, + glob_data, + QueryIdentifier, + &query_data->state, + &state->rows); + if (!subreq) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + tevent_req_set_callback(req, + get_ratiofinished_params_done, + req); + return req; + } + } +early_out: + *rdwRatioFinishedDenominator = 0; + *rdwRatioFinishedDenominator = rows; + *cRows = rows; /* MS-WSP says client dont use it */ + *fNewRows = (rows > 0) ? 1 : 0; +out: + if (!tevent_req_nterror(req, status)) { + + tevent_req_done(req); + } + return tevent_req_post(req, glob_data->ev); +} + +static void get_ratiofinished_params_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct get_ratiofinished_state *state = + tevent_req_data(req, struct get_ratiofinished_state); + + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + + if (has_error) { + tevent_req_nterror(req, status); + return; + } + + if (state->query_data->state == QUERY_COMPLETE) { + state->query_data->nrows = state->rows; + } + if (state->query_data->wsp_enabled == false) { + tevent_req_done(req); + return; + } + *state->rdwRatioFinishedDenominator = 0; + *state->rdwRatioFinishedDenominator = state->rows; + *state->cRows = state->rows; /* MS-WSP says client dont use it */ + *state->fNewRows = (state->rows > 0) ? 1 : 0;; + tevent_req_done(req); +} + +static uint32_t get_approximate_position(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Bmk) +{ + /* + * not sure how to handle this yet, somehow it seems bookmarks + * must be exposed to the client from the row results, but... + * I am not sure how yet. + */ + return 0; +} + +static uint32_t get_whereid(struct wspd_client_state *client_state, + uint32_t QueryIdentifier) +{ + return QueryIdentifier; +} + +struct get_expensive_properties_state +{ + struct query_data *query_data; + uint32_t *rcRowsTotal; + uint32_t *rdwResultCount; + uint32_t *Maxrank; + uint32_t num_rows; +}; + +static void get_expensive_properties_done(struct tevent_req *subreq); +static struct tevent_req *get_expensive_properties_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t *rcRowsTotal, + uint32_t *rdwResultCount, + uint32_t *Maxrank) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + uint32_t boolean_options; + uint32_t num_rows = 0; + struct tevent_req *req, *subreq = NULL; + struct get_expensive_properties_state *state = NULL; + NTSTATUS status; + *rcRowsTotal = 0; + *rdwResultCount = 0; + *Maxrank = 0; + + req = tevent_req_create(ctx, &state, + struct get_expensive_properties_state); + if (!req) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (query_data->wsp_enabled == false) { + status = NT_STATUS_OK; + goto out; + } + state->query_data = query_data; + state->rcRowsTotal = rcRowsTotal; + state->rdwResultCount = rdwResultCount; + state->Maxrank = Maxrank; + + if (!query_data) { + DBG_ERR("failed to find query for %d\n", QueryIdentifier); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + if (query_data->state == QUERY_COMPLETE) { + num_rows = query_data->nrows; + status = NT_STATUS_OK; + } else { + subreq = get_tracker_query_status(state, glob_data, + QueryIdentifier, + &query_data->state, + &state->num_rows); + if (!subreq) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + tevent_req_set_callback(subreq, get_expensive_properties_done, + req); + return req; + } +out: + if (!tevent_req_nterror(req, status)) { + boolean_options = query_data->rowsetproperties.ubooleanoptions; + if (!(boolean_options & EDONOTCOMPUTEEXPENSIVEPROPS)) { + *rdwResultCount = num_rows; + *rcRowsTotal = num_rows; + /* how does one fake the makrank ? */ + } + tevent_req_done(req); + } + return tevent_req_post(req, glob_data->ev); +} + +static void get_expensive_properties_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct get_expensive_properties_state *state = + tevent_req_data(req, struct get_expensive_properties_state); + bool boolean_options; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + + if (has_error) { + tevent_req_nterror(req, status); + return; + } + + if (state->query_data->state == QUERY_COMPLETE) { + state->query_data->nrows = state->num_rows; + } + boolean_options = state->query_data->rowsetproperties.ubooleanoptions; + if (!(boolean_options & EDONOTCOMPUTEEXPENSIVEPROPS)) { + *state->rdwResultCount = state->num_rows; + *state->rcRowsTotal = state->num_rows; + /* how does one fake the makrank ? */ + } + tevent_req_done(req); +} + +struct get_state_state +{ + struct wsp_cpmcistateinout *out; +}; + +static void get_state_done(struct tevent_req *subreq); +static struct tevent_req *get_state_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + struct wsp_cpmcistateinout *out) +{ + struct tevent_req *req, *subreq = NULL; + struct get_state_state *state = NULL; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + + req = tevent_req_create(ctx, &state, struct get_state_state); + if (!req) { + return NULL; + } + + state->out = out; + + subreq = glib_tracker_getstate(state, glob_data->sparql_ctx, out); + if (!subreq) { + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, glob_data->ev); + } + tevent_req_set_callback(subreq, get_state_done, req); + return req; +} + +static void get_state_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + if (has_error) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +struct get_rows_data_state +{ + TALLOC_CTX *ctx; + struct tracker_getrowsout *rows; + uint32_t *rows_left; +}; + +static void get_rows_data_done(struct tevent_req *subreq); +static struct tevent_req *get_rows_data_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + struct tracker_query_info *tracker_ctx, + struct tracker_getrowsout *rows, uint32_t index, + uint32_t rows_to_get, uint32_t fbwdfetch, uint32_t *rows_left) +{ + struct tevent_req *req, *subreq = NULL; + struct get_rows_data_state *state = NULL; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + bool reverse_fetch = (fbwdfetch == 1); + req = tevent_req_create(ctx, &state, struct get_rows_data_state); + if (!req) { + return NULL; + } + + state->rows_left = rows_left; + state->rows = rows; + state->ctx = ctx; + + subreq = glib_tracker_getrows(state, glob_data->sparql_ctx, + tracker_ctx, index, rows_to_get, reverse_fetch, rows); + + if (!subreq) { + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + req = tevent_req_post(req, glob_data->ev); + return req; + } + + tevent_req_set_callback(subreq, get_rows_data_done, req); + return req; +} + +void get_rows_data_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct get_rows_data_state *state = + tevent_req_data(req, struct get_rows_data_state); + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_FREE(subreq); + + if (has_error) { + tevent_req_nterror(req, status); + return; + } + *state->rows_left = state->rows->nrows_remaining; + talloc_steal(state->ctx, state->rows->rows); + tevent_req_done(req); +} + +static void *get_value(int i, int j, struct tracker_col_data *col_val) +{ + void *value = NULL; + switch (col_val->tracker_type) { + case TRACKER_STRING: + value = discard_const_p(void, col_val->type.string); + DBG_DEBUG("String value[%d][%d] is %s\n", + i, j, col_val->type.string); + break; + case TRACKER_INTEGER: + value = (void*)&col_val->type.integer; + DBG_DEBUG("Integer value[%d][%d] is %lu\n", + i, j, col_val->type.integer); + break; + case TRACKER_BOOLEAN: + value = (void*)&col_val->type.boolean; + DBG_DEBUG("Boolean value[%d][%d] is %d(bool)\n", + i, j, col_val->type.boolean); + break; + case TRACKER_DOUBLE: + value = (void*)&col_val->type.double_val; + DBG_DEBUG("Double value[%d][%d] is %f(float)\n", + i, j, (float)col_val->type.double_val); + break; + } + return value; +} + +struct get_rows_state +{ + struct wsp_cbasestoragevariant **RowsArray; + struct tracker_getrowsout *rows; + struct auth_session_info *session_info; + struct connection_struct *conn; + struct wspd_client_state *client_state; + uint32_t queryid; + bool *NoMoreRowsToReturn; + uint32_t *NumRowsReturned; + uint32_t *Error; + uint32_t cmaxresults; + uint32_t remaining_rows; + struct binding_data *binding; + uint32_t nbinding_cols; + struct map_data *map_data; + uint32_t index; +}; + +static void get_rows_done(struct tevent_req *subreq); +static struct tevent_req *get_rows_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t NumRowsRequested, + uint32_t FetchForward, + /* out */ + struct wsp_cbasestoragevariant **RowsArray, + bool *NoMoreRowsToReturn, + uint32_t *NumRowsReturned, + uint32_t *Error) +{ + struct query_data *query_data; + struct tevent_req *req = NULL, *subreq = NULL; + struct get_rows_state *state; + struct wsp_abstract_state *glob_data = + client_state->wsp_abstract_state; + struct pipes_struct *p = get_pipe(client_state); + *NoMoreRowsToReturn = false; + *NumRowsReturned = 0; + *Error = 0; + + req = tevent_req_create(ctx, &state, struct get_rows_state); + if (!req) { + return NULL; + } + + /* find the bindings associated with this query */ + state->client_state = client_state; + state->queryid = QueryIdentifier; + state->binding = find_bindings(QueryIdentifier, + CursorHandle, + glob_data); + + if (!state->binding) { + *Error = E_UNEXPECTED; + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, glob_data->ev); + } + + + state->RowsArray = RowsArray; + state->NoMoreRowsToReturn = NoMoreRowsToReturn; + state->NumRowsReturned = NumRowsReturned; + state->Error = Error; + state->cmaxresults = 0; + state->map_data = state->binding->result_converter->map_data; + state->nbinding_cols = state->binding->ncols; + + query_data = find_query_info(QueryIdentifier, glob_data); + if (query_data->state == QUERY_ERROR) { + *Error = E_UNEXPECTED; + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, glob_data->ev); + } + + if (query_data->wsp_enabled == false) { + *NoMoreRowsToReturn = true; + tevent_req_nterror(req, NT_STATUS_OK); + tevent_req_done(req); + return tevent_req_post(req, glob_data->ev); + } + + state->conn = query_data->vfs_conn; + state->cmaxresults = query_data->rowsetproperties.cmaxresults; + state->session_info = p->session_info; + if (state->cmaxresults && + NumRowsRequested > query_data->rowsetproperties.cmaxresults) { + NumRowsRequested = query_data->rowsetproperties.cmaxresults; + } + + /* current index was set from last call to SetNextGetRowsPosition */ + state->index = query_data->current_index; + state->rows = talloc_zero(state, struct tracker_getrowsout); + subreq = get_rows_data_send(state, client_state, + query_data->tracker_ctx, + state->rows, + state->index, NumRowsRequested, + FetchForward, + &state->remaining_rows); + if (!subreq) { + DBG_ERR("unexpected failure trying to return row data\n"); + *Error = E_UNEXPECTED; + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, glob_data->ev); + } + + tevent_req_set_callback(subreq, get_rows_done, req); + return req; +} + +static bool has_access_toworkid(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t Workid) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + return can_access_workid(query_data->tracker_ctx, Workid); +} + +static void get_rows_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct get_rows_state *state = + tevent_req_data(req, struct get_rows_state); + uint32_t rows_to_try = state->rows->nrows; /* this is how many rows we got */ + int i; + int j; + + struct wsp_cbasestoragevariant **RowsArray = state->RowsArray; + NTSTATUS status = NT_STATUS_OK; + bool has_error = tevent_req_is_nterror(subreq, &status); + TALLOC_CTX *frame; + if (has_error) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } + + frame = talloc_stackframe(); + /* try retrieve next 'rows_to_try' rows */ + for (i = 0; i < rows_to_try; i++) { + /* + * try and convert the colums of stuff returned from tracker + * to the columns required for the bindings + */ + struct tracker_row *row_item = &state->rows->rows[i]; + struct row_conv_data *row_private_data = + talloc_zero(subreq, struct row_conv_data); + struct wsp_cbasestoragevariant *row; + /* + * Here we deviate from the spec, we *don't* acl filter + * results before passing back to the client as we have + * already acl checked the results returned (and cached) from + * the query. Note: MS-WSP documentation is very ambiguous + * about how this filtering should work, the description to + * me seems to contradict itself and is very unclear. + * Also in practise with win8 at least it seems that the + * filtering is already done when the query returns + * (reflected in the num results contained in + * CPMGETQUERYSTATUSEX _cResultsFound response field. + */ + row = RowsArray[i]; + row_private_data->conn = state->conn; + row = talloc_zero_array(state->RowsArray, + struct wsp_cbasestoragevariant, + state->nbinding_cols); + + RowsArray[i] = row; + for (j = 0; j < state->nbinding_cols; j++) { + struct wsp_ctablecolumn *bind_desc = + &state->binding->columns[j]; + struct wsp_cbasestoragevariant *col_val = + &row[j]; + DBG_DEBUG("about to process row[%d]col[%d] by using " + "tracker_col %d with result_converter %p\n", + i, j, state->map_data[j].col_with_value, + state->map_data[j].convert_fn); + if (state->map_data[j].vtype != VT_NULL) { + NTSTATUS conv_status; + uint32_t val_col = + state->map_data[j].col_with_value; + struct tracker_col_data *trker_col; + void *val; + trker_col = &row_item->columns[val_col]; + val = get_value(i, j, trker_col); + conv_status = state->map_data[j].convert_fn( + RowsArray, col_val, + bind_desc->vtype, + trker_col->tracker_type, + val, + row_private_data); + if (!NT_STATUS_IS_OK(conv_status)) { + /* mark column as unprocessable */ + DBG_DEBUG("failed to process row " + "%d col %d, error: %s\n", + i, j, + nt_errstr(conv_status)); + col_val->vtype = VT_NULL; + } + } else { + /* mark column as unprocessable or missing... */ + col_val->vtype = VT_NULL; + } + } + (*state->NumRowsReturned)++; + } + + /* + * if we returned the maxresults for a query, then say no-more + * available + */ + if (!state->remaining_rows + || (state->cmaxresults + && (state->index + *state->NumRowsReturned) >= state->cmaxresults)) { + *state->NoMoreRowsToReturn = true; + } + TALLOC_FREE(frame); + TALLOC_FREE(subreq); + tevent_req_done(req); +} + +static void set_nextgetrowsposition(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Chapter, + uint32_t Index) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + + /* + * we store 0 based indices ala array indices but the GSS descriptions + * are 1 based. + */ + Index--; + if (query_data) { + struct next_cursor_data *next_cursor = + find_next_cursor_index(query_data, + CursorHandle, + Chapter); + if (next_cursor) { + next_cursor->index = Index; + } + query_data->current_index = Index; + } +} + +static uint32_t get_nextgetrowsposition(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t Chapter) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + uint32_t index = 0; + if (query_data) { + struct next_cursor_data *next_cursor = + find_next_cursor_index(query_data, + CursorHandle, + Chapter); + if (next_cursor) { + index = next_cursor->index; + index--; + } + return index; + } + DBG_ERR("couldn't get index for queryid 0x%x cursor " + "handle 0x%x chapter 0x%x\n", + QueryIdentifier, CursorHandle, Chapter); + /* shouldn't get here */ + return 0; +} + +static uint32_t get_bookmarkpos(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t bmkHandle) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + uint32_t result = 0; + if (bmkHandle == DBBMK_FIRST) { + result = 1; + } else if (bmkHandle == DBBMK_LAST) { + struct query_data *query_data = + find_query_info(QueryIdentifier, glob_data); + if (!query_data) { + DBG_ERR("no query_data for query id, something " + "pretty major wrong :/\n"); + /* #TODO perhaps we should abort */ + result = bmkHandle; + } else { + result = query_data->nrows; + } + } else { + DBG_INFO("bmkHandle 0x%x\n", bmkHandle); + result = bmkHandle; + } + return result; +} + +static bool clientquery_has_cursorhandle(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle) +{ + bool result; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_data = find_query_info(QueryIdentifier, + glob_data); + struct next_cursor_data *item; + if (!query_data) { + DBG_ERR("no query_data for query id, something " + "pretty major wrong :/\n"); + result = false; + goto out; + } + + item = query_data->next_cursors.items; + for (; item; item = item->next) { + if (item->cursor == CursorHandle) { + result = true; + goto out; + } + } + result = false; +out: + return result; +} + +static NTSTATUS set_scope_prio(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t Priority) +{ + NTSTATUS status; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data * query_info = find_query_info(QueryIdentifier, + glob_data); + if (!query_info) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } else if (query_info->no_index){ + status = NT_STATUS(WIN_UPDATE_ERR); + goto done; + } + status = NT_STATUS_OK; +done: + return status; +} + +static NTSTATUS get_query_stats(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t *NumIndexedItems, + uint32_t *NumOutstandingAdds, + uint32_t *NumOutstandingModifies) +{ + NTSTATUS status; + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data * query_info = find_query_info(QueryIdentifier, + glob_data); + /* don't believe we can handle this, just init all to 0 */ + *NumIndexedItems = 0; + *NumOutstandingAdds = 0; + *NumOutstandingModifies = 0; + if (!query_info) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } else if (query_info->wsp_enabled == false) { + DBG_ERR("indexing not available for share %s\n", + query_info->share); + /* + * On windows we see that an indexed share that has + * indexing turned off seems to return zero results + * until such time as the client requests scope statistics + * if we sent the error below then the client will fall back + * to searching via smb. + */ + status = NT_STATUS(0x80070003); + goto done; + } else if (query_info->no_index){ + status = NT_STATUS(WIN_UPDATE_ERR); + goto done; + } + status = NT_STATUS_OK; +done: + return status; +} + +static void get_last_unretrieved_evt(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + /* out */ + uint32_t *Wid, + uint8_t *EventType, + bool *MoreEvents, + uint8_t *RowsetItemState, + uint8_t *ChangedItemState, + uint8_t *RowsetEvent, + uint64_t *RowsetEventData1, + uint64_t *RowsetEventData2) +{ + /* can't handle this */ + *Wid = 0; + *EventType = 0; + *MoreEvents = false; + *RowsetItemState = 0; + *ChangedItemState = 0; + *RowsetEvent = 0; + *RowsetEventData1 = 0; + *RowsetEventData2 = 0; +} + +static void remove_cursors_data(uint32_t cursor_handle, + struct query_data *query_info) +{ + struct next_cursor_data *item = query_info->next_cursors.items; + while (item) { + struct next_cursor_data *tmp = item->next; + if (item->cursor == cursor_handle) { + DLIST_REMOVE(query_info->next_cursors.items, item); + TALLOC_FREE(item); + query_info->ncursors--; + item = tmp; + } else { + item = item->next; + } + } +} + +static uint32_t release_cursor(struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_info = find_query_info(QueryIdentifier, + glob_data); + struct binding_data *binding = find_bindings(QueryIdentifier, + CursorHandle, + glob_data); + uint32_t ncursors = 0; + if (binding && query_info) { + DLIST_REMOVE(query_info->bindings.items, binding); + TALLOC_FREE(binding); + query_info->bindings.nbindings--; + remove_cursors_data(CursorHandle, query_info); + } + return ncursors; +} + +static struct tevent_req *release_query_send(TALLOC_CTX *ctx, + struct wspd_client_state *client_state, + uint32_t QueryIdentifier) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct query_data *query_info = find_query_info(QueryIdentifier, + glob_data); + struct dummy_async_state *state; + struct tevent_req *req = tevent_req_create(ctx, &state, + struct dummy_async_state); + if (!req) { + return NULL; + } + + if (query_info) { + TALLOC_FREE(query_info); + } else { + DBG_ERR("failed to retrieve query associated with handle %d\n", QueryIdentifier); + + } + return tevent_req_post(req, glob_data->ev); + +} + +static int destroy_wsp_abstract_state(struct wsp_abstract_state *glob_data) +{ + struct query_data *item = NULL; + for (item = glob_data->queries.items; item;){ + struct query_data *next = item->next; + TALLOC_FREE(item); + item = next; + } + TALLOC_FREE(glob_data->sparql_ctx); + return 0; +} + +static struct wsp_abstract_state *initialise(struct tevent_context *event_ctx, + struct messaging_context *msg_ctx) +{ + struct wsp_abstract_state *wsp_abstract_state = + talloc_zero(NULL, struct wsp_abstract_state); + if (!wsp_abstract_state) { + DBG_ERR("Out of memory\n"); + return NULL; + } + + talloc_set_destructor(wsp_abstract_state, destroy_wsp_abstract_state); + + wsp_abstract_state->client_info_map = talloc_zero(wsp_abstract_state, + struct client_info); + if (!wsp_abstract_state->client_info_map) { + DBG_ERR("Out of memory\n"); + return NULL; + } + wsp_abstract_state->ev = event_ctx; + wsp_abstract_state->sparql_ctx = init_sparql(event_ctx); + if (!wsp_abstract_state->sparql_ctx) { + DBG_ERR("failed to initialise tracker\n"); + return NULL; + } + return wsp_abstract_state; +} + +static struct wsp_cpmconnectin *get_client_information( + struct wspd_client_state *client_state, + uint32_t QueryIdentifier) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct client_info* client_info = find_client_info(QueryIdentifier, + glob_data); + if (client_info) { + return client_info->connectin; + } + return NULL; +} + +static struct wsp_ctablecolumn* get_binding( + struct wspd_client_state *client_state, + uint32_t QueryIdentifier, + uint32_t CursorHandle, + uint32_t *ncols) +{ + struct wsp_abstract_state *glob_data = client_state->wsp_abstract_state; + struct binding_data *binding_info = find_bindings(QueryIdentifier, + CursorHandle, + glob_data); + + if (binding_info) { + *ncols = binding_info->ncols; + return binding_info->columns; + } + return NULL; +} + +static struct wsp_abstract_interface concrete_impl = { + .Initialise = initialise, + .IsCatalogAvailable = is_catalog_available, + .StoreClientInformation = store_client_information, + .GetClientInformation = get_client_information, + .GetServerVersions = get_server_versions, + .GetState_send = get_state_send, + .RunNewQuery_send = run_new_query_send, + .ClientQueryHasCursorHandle = clientquery_has_cursorhandle, + .GetQueryStatus_send = get_query_status_send, + .GetRatioFinishedParams_send = get_ratiofinished_params_send, + .GetApproximatePosition = get_approximate_position, + .GetWhereid = get_whereid, + .GetExpensiveProperties_send = get_expensive_properties_send, + .HasBindings = has_bindings, + .GetBookmarkPosition = get_bookmarkpos, + .SetNextGetRowsPosition = set_nextgetrowsposition, + .GetNextGetRowsPosition = get_nextgetrowsposition, + .GetRows_send = get_rows_send, + .HasAccessToWorkid = has_access_toworkid, + .HasAccessToProperty = NULL, + .GetPropertyValueForWorkid = NULL, + .GetQueryStatusChanges = NULL, + .SetBindings = set_bindings, + .GetBindings = get_binding, + .ReleaseCursor = release_cursor, + .ReleaseQuery_send = release_query_send, + .FindNextOccurrenceIndex = NULL, + .GetLastUnretrievedEvent = get_last_unretrieved_evt, + .GetQueryStatistics = get_query_stats, + .SetScopePriority = set_scope_prio, + .FilterOutScopeStatisticsMessages = NULL, + .Inflect = NULL, + .GenerateScopeStatisticsEvent = NULL, +}; + +struct wsp_abstract_interface *tracker_wsp_abs_interace(void) +{ + return &concrete_impl; +} diff --git a/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h b/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h new file mode 100644 index 0000000..597aa1d --- /dev/null +++ b/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h @@ -0,0 +1,29 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) 2016 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 . + */ + +#ifndef __WSP_SRV_TRACKER_ABS_IF__ +#define __WSP_SRV_TRACKER_ABS_IF__ +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" + +struct wsp_abstract_interface; + +struct wsp_abstract_interface *tracker_wsp_abs_interace(void); +#endif diff --git a/source3/rpc_server/wspd.c b/source3/rpc_server/wspd.c new file mode 100644 index 0000000..366d2a3 --- /dev/null +++ b/source3/rpc_server/wspd.c @@ -0,0 +1,766 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (C) + * + * Based on fssd.c: + * Copyright (c) 2012 David Disseldorp + * Copyright (c) 2016 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 . + */ + +#include "includes.h" + +#include "serverid.h" +#include "ntdomain.h" +#include "messages.h" + +#include "rpc_server/srv_pipe.h" +#include "rpc_server/rpc_server.h" +#include "lib/tsocket/tsocket.h" +#include "lib/util/tevent_ntstatus.h" +#include "rpc_server/wsp/wsp_gss.h" +#include "rpc_server/wsp/wsp_srv_tracker-sparql.h" +#include "libcli/named_pipe_auth/npa_tstream.h" +#include "librpc/gen_ndr/auth.h" + +#include + +#define DAEMON_NAME "wspd" + +void start_wspd(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx); + +struct wspd_state +{ + struct gss_state *gss_state; + struct wspd_client_state *client_state; +}; + +struct listen_state { + int fd; + char *name; + struct tevent_context *ev_ctx; + struct messaging_context *msg_ctx; + void *private_data; +}; + +struct wsp_pipe_send_state { + DATA_BLOB buffer; + struct iovec in; + struct tstream_context *stream; + struct tevent_context *ev; +}; + +static int named_pipe_destructor(struct named_pipe_client *npc) +{ + if (npc->term_fn) { + npc->term_fn(npc->private_data); + } + return 0; +} + +static void named_pipe_wsp_accept_done(struct tevent_req *subreq); + +static void named_pipe_wsp_accept_function(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const char *pipe_name, int fd, + named_pipe_termination_fn *term_fn, + void *private_data) +{ + struct named_pipe_client *npc; + struct tstream_context *plain; + struct tevent_req *subreq; + int ret; + + npc = talloc_zero(ev_ctx, struct named_pipe_client); + if (!npc) { + DBG_ERR("Out of memory!\n"); + close(fd); + return; + } + + npc->pipe_name = talloc_strdup(npc, pipe_name); + if (npc->pipe_name == NULL) { + DBG_ERR("Out of memory!\n"); + TALLOC_FREE(npc); + close(fd); + return; + } + npc->ev = ev_ctx; + npc->msg_ctx = msg_ctx; + npc->term_fn = term_fn; + npc->private_data = private_data; + talloc_set_destructor(npc, named_pipe_destructor); + + /* make sure socket is in NON blocking state */ + ret = set_blocking(fd, false); + if (ret != 0) { + DBG_ERR("Failed to make socket non-blocking\n"); + TALLOC_FREE(npc); + close(fd); + return; + } + + ret = tstream_bsd_existing_socket(npc, fd, &plain); + if (ret != 0) { + DBG_ERR("Failed to create tstream socket\n"); + TALLOC_FREE(npc); + close(fd); + return; + } + + npc->file_type = FILE_TYPE_MESSAGE_MODE_PIPE; + npc->device_state = 0xff | 0x0400 | 0x0100; + npc->allocation_size = 4096; + + subreq = tstream_npa_accept_existing_send(npc, npc->ev, plain, + npc->file_type, + npc->device_state, + npc->allocation_size); + if (!subreq) { + DBG_ERR("Failed to start async accept procedure\n"); + TALLOC_FREE(npc); + close(fd); + return; + } + tevent_req_set_callback(subreq, named_pipe_wsp_accept_done, npc); +} + +static void* wsp_pipe_opened(struct named_pipe_client *npc, + void *private_data); +static void wsp_pipe_destroyed(void *private_data); + +static NTSTATUS wsp_pipe_read_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *buffer) +{ + struct wsp_pipe_send_state *state = tevent_req_data(req, + struct wsp_pipe_send_state); + NTSTATUS status; + + DBG_DEBUG("wsp_pipe_read_recv\n"); + if (tevent_req_is_nterror(req, &status)) { + DBG_ERR("wsp_pipe_read_recv nterror %s\n", nt_errstr(status)); + tevent_req_received(req); + return status; + } + + if (buffer) { + buffer->data = talloc_move(mem_ctx, &state->buffer.data); + buffer->length = state->buffer.length; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +static void read_wsp_pipe_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct wsp_pipe_send_state *state = + tevent_req_data(req, struct wsp_pipe_send_state); + int ret; + int sys_errno; + int to_read; + ssize_t ofs = 0; + NTSTATUS status; + + DBG_DEBUG("read_wsp_pipe_done\n"); + ret = tstream_readv_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + status = map_nt_error_from_unix_common(sys_errno); + tevent_req_nterror(req, status); + return; + } + /* + * initial read is just for waiting for at lest 1 byte, see if + * there is any additional bytes to read. + */ + to_read = tstream_pending_bytes(state->stream); + if (!to_read) { + /* we're done */ + tevent_req_done(req); + return; + } + + ofs = state->buffer.length; + state->buffer.data = talloc_realloc(state, + state->buffer.data, + uint8_t, + to_read + ofs); + state->buffer.length = to_read + state->buffer.length; + state->in.iov_base = (void *) (state->buffer.data + ofs); + state->in.iov_len = state->buffer.length - ofs; + subreq = tstream_readv_send(state, + state->ev, + state->stream, + &state->in, + 1); + if (tevent_req_nomem(subreq, req)) { + tevent_req_post(req, state->ev); + return; + } + + tevent_req_set_callback(subreq, read_wsp_pipe_done, req); +} + +static struct tevent_req *read_wsp_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream) +{ + struct tevent_req *req; + struct wsp_pipe_send_state *state; + struct tevent_req *subreq; + req = tevent_req_create(mem_ctx, &state, + struct wsp_pipe_send_state); + if (req == NULL) { + return NULL; + } + + state->buffer.length = 1; + state->buffer.data = talloc_zero_array(mem_ctx, + uint8_t, + state->buffer.length); + state->stream = stream; + state->in.iov_base = state->buffer.data; + state->in.iov_len = state->buffer.length; + state->ev = ev; + DBG_DEBUG("read_wsp_pipe_send stream %p\n", stream); + subreq = tstream_readv_send(state, ev, + stream, + &state->in, + 1); + tevent_req_set_callback(subreq, read_wsp_pipe_done, req); + return req; +} + +void wsp_pipe_process(struct tevent_req *subreq); +static struct tevent_req *wsp_server_loop(struct named_pipe_client *npc) +{ + struct tevent_req *subreq; + + subreq = read_wsp_pipe_send(npc, npc->ev, npc->tstream); + if (!subreq) { + DBG_ERR("Failed to start receving packets\n"); + goto fail; + } + tevent_req_set_callback(subreq, wsp_pipe_process, npc); +fail: + return subreq; +} + +static void process_wsp_pipe_request_done(struct tevent_req *subreq); +static struct tevent_req *process_wsp_pipe_request(TALLOC_CTX *mem_ctx, + struct named_pipe_client *npc); + +void wsp_pipe_process(struct tevent_req *subreq) +{ + struct named_pipe_client *npc = + tevent_req_callback_data(subreq, struct named_pipe_client); + DATA_BLOB recv_buffer = data_blob_null; + NTSTATUS status; + + DBG_DEBUG("wsp_pipe_process \n"); + status = wsp_pipe_read_recv(subreq, npc, &recv_buffer); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + npc->p->in_data.pdu_needed_len = 0; + npc->p->in_data.pdu = recv_buffer; + + subreq = process_wsp_pipe_request(npc->p->mem_ctx, npc); + tevent_req_set_callback(subreq, process_wsp_pipe_request_done, npc); + talloc_free(recv_buffer.data); + return; +fail: + DBG_ERR("Fatal error(%s). " + "Terminating client(%s) connection!\n", + nt_errstr(status), npc->client_name); + /* terminate client connection */ + talloc_free(npc); + return; +} + +static void wsp_pipe_process_done(struct tevent_req *subreq); +static void process_wsp_pipe_request_done(struct tevent_req *subreq) +{ + uint32_t to_send; + struct named_pipe_client *npc; + struct _output_data *out; + NTSTATUS status; + DBG_DEBUG("process_wsp_pipe_done\n"); + npc = tevent_req_callback_data(subreq, struct named_pipe_client); + out = &npc->p->out_data; + to_send = out->rdata.length; + TALLOC_FREE(subreq); + if (to_send) { + npc->iov = talloc_zero(npc, struct iovec); + if (!npc->iov) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + npc->count = 1; + + npc->iov[0].iov_len = to_send; + npc->iov[0].iov_base = out->rdata.data; + DBG_DEBUG("sending %lu bytes to tstream !!\n", + npc->iov[0].iov_len ); + subreq = tstream_writev_queue_send(npc, npc->ev, npc->tstream, + npc->write_queue, + npc->iov, + 1); + if (!subreq) { + DBG_ERR("Failed to send response for raw pipe\n"); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(subreq, wsp_pipe_process_done, npc); + } else { + /* + * we don't respond to some messages (e.g. CPMDisconnect from + * MS-WSP), make sure we restart the server loop in anycase. + */ + subreq = wsp_server_loop(npc); + if (!subreq) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + return; +fail: + DBG_ERR("Fatal error(%s). " + "Terminating client(%s) connection!\n", + nt_errstr(status), npc->client_name); + /* terminate client connection */ + talloc_free(npc); + return; +} + +static void wsp_pipe_process_done(struct tevent_req *subreq) +{ + struct named_pipe_client *npc = + tevent_req_callback_data(subreq, struct named_pipe_client); + int sys_errno; + int ret; + + DBG_DEBUG("wsp_pipe_process_done \n"); + ret = tstream_writev_queue_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + DBG_ERR("Writev failed!\n"); + goto fail; + } + if (tevent_queue_length(npc->write_queue) > 0) { + return; + } + + npc->count = 0; + TALLOC_FREE(npc->iov); + data_blob_free(&npc->p->in_data.data); + data_blob_free(&npc->p->out_data.frag); + data_blob_free(&npc->p->out_data.rdata); + + talloc_free_children(npc->p->mem_ctx); + subreq = wsp_server_loop(npc); + if (!subreq) { + goto fail; + } + return; +fail: + DBG_ERR("Fatal error(%s). " + "Terminating client(%s) connection!\n", + strerror(sys_errno), npc->client_name); + /* terminate client connection */ + talloc_free(npc); + return; +} + +static struct tevent_req *process_wsp_pipe_request(TALLOC_CTX *mem_ctx, + struct named_pipe_client *npc) +{ + struct tevent_req *subreq; + struct pipes_struct *p = npc->p; + TALLOC_CTX *frame = talloc_stackframe(); + struct wspd_state *wspd_state = + talloc_get_type_abort(npc->private_data, + struct wspd_state); + if (!pipe_init_outgoing_data(p)) { + subreq = NULL; + goto done; + } + + subreq = do_wsp_request_send(mem_ctx, wspd_state->client_state); + if (!subreq) { + goto done; + } +done: + TALLOC_FREE(frame); + return subreq; +} + +static void named_pipe_wsp_accept_done(struct tevent_req *subreq) +{ + struct auth_session_info_transport *session_info_transport; + struct named_pipe_client *npc = + tevent_req_callback_data(subreq, struct named_pipe_client); + int error; + int ret; + + ret = tstream_npa_accept_existing_recv(subreq, &error, npc, + &npc->tstream, + &npc->client, + &npc->client_name, + &npc->server, + &npc->server_name, + &session_info_transport); + + npc->session_info = talloc_move(npc, &session_info_transport->session_info); + + TALLOC_FREE(subreq); + if (ret != 0) { + DBG_ERR("Failed to accept named pipe connection! (%s)\n", + strerror(error)); + TALLOC_FREE(npc); + return; + } + + ret = make_server_pipes_struct(npc, + npc->msg_ctx, + npc->pipe_name, NCACN_NP, + npc->server, + npc->client, + npc->session_info, + &npc->p, &error); + if (ret != 0) { + DBG_ERR("Failed to create pipes_struct! (%s)\n", + strerror(error)); + goto fail; + } + + npc->write_queue = tevent_queue_create(npc, "np_server_write_queue"); + if (!npc->write_queue) { + DBG_ERR("Failed to set up write queue!\n"); + goto fail; + } + /* + * replace previous contents of private_data (gss_state) + * with wspd_state + */ + npc->private_data = wsp_pipe_opened(npc, npc->private_data); + + if (!npc->private_data) { + DBG_ERR("failed after opening pipe\n"); + goto fail; + } + + npc->term_fn = wsp_pipe_destroyed; + + subreq = wsp_server_loop(npc); + if (!subreq) { + DBG_ERR("Failed to start receving packets\n"); + goto fail; + } + + return; + +fail: + DBG_ERR("Fatal error. Terminating client(%s) connection!\n", + npc->client_name); + /* terminate client connection */ + talloc_free(npc); + return; +} + +static void named_pipe_listener(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct listen_state *state = + talloc_get_type_abort(private_data, + struct listen_state); + struct sockaddr_un sunaddr; + socklen_t len; + int sd = -1; + + /* TODO: should we have a limit to the number of clients ? */ + + len = sizeof(sunaddr); + + sd = accept(state->fd, + (struct sockaddr *)(void *)&sunaddr, &len); + + if (sd == -1) { + if (errno != EINTR) { + DBG_INFO("Failed to get a valid socket [%s]\n", + strerror(errno)); + } + return; + } + + DBG_INFO("Accepted socket %d\n", sd); + + named_pipe_wsp_accept_function(state->ev_ctx, + state->msg_ctx, + state->name, + sd, NULL, state->private_data); +} + +static bool setup_wsp_named_pipe_socket(const char *pipe_name, + struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + void *private_data) +{ + struct listen_state *state; + struct tevent_fd *fde; + int rc; + + state = talloc(ev_ctx, struct listen_state); + if (!state) { + DBG_ERR("Out of memory\n"); + return false; + } + state->name = talloc_strdup(state, pipe_name); + if (state->name == NULL) { + DBG_ERR("Out of memory\n"); + goto out; + } + state->fd = create_named_pipe_socket(pipe_name); + if (state->fd == -1) { + goto out; + } + state->private_data = private_data; + rc = listen(state->fd, 5); + if (rc < 0) { + DBG_ERR("Failed to listen on pipe socket %s: %s\n", + pipe_name, strerror(errno)); + goto out; + } + + state->ev_ctx = ev_ctx; + state->msg_ctx = msg_ctx; + + DBG_DEBUG("Openened pipe socket fd %d for %s\n", + state->fd, pipe_name); + + fde = tevent_add_fd(ev_ctx, + state, state->fd, TEVENT_FD_READ, + named_pipe_listener, state); + if (!fde) { + DBG_ERR("Failed to add event handler!\n"); + goto out; + } + + tevent_fd_set_auto_close(fde); + return true; + +out: + if (state->fd != -1) { + close(state->fd); + } + TALLOC_FREE(state); + return false; +} + +static void wspd_reopen_logs(void) +{ + char *lfile = lp_logfile(NULL); + int rc; + + if (lfile == NULL || lfile[0] == '\0') { + rc = asprintf(&lfile, "%s/log.%s", get_dyn_LOGFILEBASE(), DAEMON_NAME); + if (rc > 0) { + lp_set_logfile(lfile); + SAFE_FREE(lfile); + } + } else { + if (strstr(lfile, DAEMON_NAME) == NULL) { + rc = asprintf(&lfile, "%s.%s", lp_logfile(NULL), DAEMON_NAME); + if (rc > 0) { + lp_set_logfile(lfile); + SAFE_FREE(lfile); + } + } + } + + reopen_logs(); +} + +static void wspd_smb_conf_updated(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + DBG_DEBUG("Got message saying smb.conf was updated. Reloading.\n"); + change_to_root_user(); + lp_load_with_shares(get_dyn_CONFIGFILE()); + wspd_reopen_logs(); +} + +static void wspd_sig_term_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + DBG_ERR("got sigterm!!\n"); + exit_server_cleanly("terminationstruct tevent_context *ev_ctx signal"); +} + +static void wspd_setup_sig_term_handler(struct tevent_context *ev_ctx) +{ + struct tevent_signal *se; + + se = tevent_add_signal(ev_ctx, + ev_ctx, + SIGTERM, 0, + wspd_sig_term_handler, + NULL); + if (se == NULL) { + exit_server("failed to setup SIGTERM handler"); + } +} + +static void wspd_sig_hup_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + change_to_root_user(); + + DBG_NOTICE("reopening logs after SIGHUP\n"); + wspd_reopen_logs(); +} + +static void wspd_setup_sig_hup_handler(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx) +{ + struct tevent_signal *se; + + se = tevent_add_signal(ev_ctx, + ev_ctx, + SIGHUP, 0, + wspd_sig_hup_handler, + msg_ctx); + if (se == NULL) { + exit_server("failed to setup SIGHUP handler"); + } +} + +static void* wsp_pipe_opened(struct named_pipe_client *npc, + void *private_data) +{ + struct gss_state *gss_state = talloc_get_type_abort(private_data, + struct gss_state); + struct wspd_state *wsp_state = talloc_zero(gss_state, struct wspd_state); + DBG_NOTICE("starting wsp server loop \n"); + + wsp_state->gss_state = gss_state; + + if (!gss_init(gss_state)) { + DBG_ERR("Failed to initialise the gss\n"); + return NULL; + } + wsp_state->client_state = create_client_state(npc, gss_state); + return wsp_state; +} + +static void wsp_pipe_destroyed(void *private_data) +{ + struct wspd_state *wsp_state = + talloc_get_type_abort(private_data, struct wspd_state); + client_disconnected(wsp_state->client_state); + TALLOC_FREE(wsp_state); +} + +void start_wspd(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx) +{ + NTSTATUS status; + struct gss_state *gss_state; + pid_t pid; + bool ok; + int rc; + + gss_state = gss_state_create(ev_ctx, msg_ctx); + DBG_NOTICE("Forking WSP Daemon\n"); + + pid = fork(); + + if (pid == -1) { + DBG_ERR("failed to fork wsp daemon [%s], " + "aborting ...\n", strerror(errno)); + exit(1); + } + + if (pid) { + /* parent */ + return; + } + + /* child */ + status = reinit_after_fork(msg_ctx, + ev_ctx, + true, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("reinit_after_fork() failed\n"); + smb_panic("reinit_after_fork() failed"); + } + + wspd_reopen_logs(); + + wspd_setup_sig_term_handler(ev_ctx); + wspd_setup_sig_hup_handler(ev_ctx, msg_ctx); + + ok = serverid_register(messaging_server_id(msg_ctx), + FLAG_MSG_GENERAL | + FLAG_MSG_PRINT_GENERAL); + if (!ok) { + DBG_ERR("Failed to register serverid in wspd!\n"); + exit(1); + } + + messaging_register(msg_ctx, + ev_ctx, + MSG_SMB_CONF_UPDATED, + wspd_smb_conf_updated); + + ok = setup_wsp_named_pipe_socket("msftewds", ev_ctx, msg_ctx, gss_state); + if (!ok) { + DBG_ERR("Failed to open wsp named pipe!\n"); + exit(1); + } + DBG_NOTICE("WSP Daemon Started (%d)\n", getpid()); + /* loop forever */ + rc = tevent_loop_wait(ev_ctx); + + TALLOC_FREE(gss_state); + /* should not be reached */ + DBG_ERR("tevent_loop_wait() exited with %d - %s\n", + rc, (rc == 0) ? "out of events" : strerror(errno)); + DBG_ERR("server exiting\n"); + exit(1); +} diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 3cbd089..97f2ccb 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -102,6 +102,8 @@ extern void start_fssd(struct tevent_context *ev_ctx, extern void start_mdssd(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx); +extern void start_wspd(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx); /******************************************************************* What to do when smb.conf is updated. @@ -1951,7 +1953,12 @@ extern void build_options(bool screen); if (rpc_fss_daemon() == RPC_DAEMON_FORK) { start_fssd(ev_ctx, msg_ctx); } - +#ifdef WITH_WSP + if (rpc_wsp_daemon() == RPC_DAEMON_FORK) { + DEBUG(0,("### about to start wsp\n")); + start_wspd(ev_ctx, msg_ctx); + } +#endif if (!lp__disable_spoolss() && (rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) { bool bgq = lp_parm_bool(-1, "smbd", "backgroundqueue", true); diff --git a/source3/wscript b/source3/wscript index 18ac579..9d363f5 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1673,6 +1673,7 @@ main() { conf.fatal('Missing Gnome Tracker development files') Logs.info("building with Spotlight support") + default_static_modules.extend(TO_LIST('rpc_mdssvc_module')) conf.DEFINE('WITH_SPOTLIGHT', '1') conf.env.with_spotlight = True default_static_modules.extend(TO_LIST('rpc_mdssvc_module')) diff --git a/source3/wscript_build b/source3/wscript_build index 3c383d9..7b3dc51 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -642,6 +642,7 @@ bld.SAMBA3_LIBRARY('smbd_base', netapi NDR_IOCTL notifyd + WSPSVC ''' + bld.env['dmapi_lib'] + bld.env['legacy_quota_libs'] + @@ -879,6 +880,7 @@ bld.SAMBA3_BINARY('smbd/smbd', FSSD MDSSD SPOOLSSD + WSPD ''', install_path='${SBINDIR}') -- 2.10.2 >From 3937a5f59f737a7891498fdb71f25136a18cbfa3 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 27 Jul 2016 15:03:00 +0100 Subject: [PATCH 21/21] s3/utils Add wsp-to-tracker utility. 'wsp-to-tracker' is a developer tool that can be used introspect the expression tree of the binary CPMCREATEQUERY message and convert it to the equivalent tracker-sparq query that the WSP server/daemon will use. Additionally it can dump the raw binary expression converted into a stringified representation of the query. Signed-off-by: Noel Power --- source3/utils/wsp-to-tracker.c | 295 +++++++++++++++++++++++++++++++++++++++++ source3/wscript_build | 10 ++ 2 files changed, 305 insertions(+) create mode 100644 source3/utils/wsp-to-tracker.c diff --git a/source3/utils/wsp-to-tracker.c b/source3/utils/wsp-to-tracker.c new file mode 100644 index 0000000..72bf545 --- /dev/null +++ b/source3/utils/wsp-to-tracker.c @@ -0,0 +1,295 @@ +#include "includes.h" +#include "popt_common.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp.h" +#include "bin/default/librpc/gen_ndr/ndr_wsp_data.h" +#include "rpc_server/wsp/wsp_sparql_conv.h" +#include + +static const uint32_t BUFFER_SIZE = 20000; + +static bool get_blob_from_file(TALLOC_CTX *ctx, const char *message_bytes_file, + DATA_BLOB *blob) +{ + bool result = true; + FILE *f=NULL; + int i = 0; + + uint8_t *buffer = talloc_array(ctx, uint8_t, BUFFER_SIZE); + /* cheap and nasty read */ + f = fopen(message_bytes_file,"rb"); + + if (!f) { + DBG_ERR("Failed to open %s for reading\n", message_bytes_file); + result = false; + goto out; + } + while (!feof(f)) { + if (i > BUFFER_SIZE) { + DBG_ERR("buffer too small read %d bytes from %s\n", i, message_bytes_file); + } + fread(buffer+i,1,1,f); + i++; + + } + i--; + DBG_ERR("%d bytes from %s\n", i, message_bytes_file); +out: + if (f) { + fclose(f); + } + blob->data = buffer; + blob->length = i; + return result; +} + +static enum ndr_err_code parse_blob(TALLOC_CTX *ctx, DATA_BLOB *blob, + struct wsp_request *request, struct wsp_response *response, + bool is_request) +{ + struct ndr_pull *ndr = NULL; + enum ndr_err_code err; + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + + ndr = ndr_pull_init_blob(blob, ctx); + + if (is_request) { + err = ndr_pull_wsp_request(ndr, ndr_flags, request); + } + else { + err = ndr_pull_wsp_response(ndr, ndr_flags, response); + } + return err; +} + +static bool synthesize_bindings(TALLOC_CTX *ctx, + struct wsp_ccolumnset *columnset, + struct wsp_cpidmapper *pidmapper, + struct wsp_ctablecolumn **columns, + uint32_t *ncols) +{ + int i; + struct wsp_ctablecolumn *tab_cols = + talloc_zero_array(ctx, + struct wsp_ctablecolumn, + columnset->count); + *ncols = columnset->count; + for (i=0; i < columnset->count; i++) { + int pid_index = columnset->indexes[i]; + struct wsp_cfullpropspec *prop_spec = + &pidmapper->apropspec[pid_index]; + tab_cols[i].propspec = *prop_spec; + } + *columns = tab_cols; + return true; +} + +int main(int argc, const char *argv[]) +{ + DATA_BLOB blob; + int result; + TALLOC_CTX *ctx = talloc_init(NULL); + struct wsp_request *request; + struct wsp_response *response; + enum ndr_err_code err; + const char *query_str = NULL; + const char *share = NULL; + const char *restrictionset_expr = NULL; + struct wsp_cpmcreatequeryin *query; + struct wsp_ccolumnset *projected_col_offsets = NULL; + struct wsp_crestrictionarray *restrictionset = NULL; + struct wsp_cpidmapper *pidmapper = NULL; + struct tracker_selected_cols tracker_cols; + uint32_t where_id; + int i = 0; + int c = 0; + bool raw = false; + bool full = false; + bool restriction = false; + const char *infile; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"full", 'f', POPT_ARG_NONE, NULL, 'f', "prints out the full sparql query" }, + {"restriction", 'r', POPT_ARG_NONE, NULL, 'r', "prints out the restriction expression only" }, + {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "doesn't do any conversion to tracker properties, doesn't drop any part of the expression, just prints out what it can" }, + POPT_TABLEEND + }; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + if (!frame) { + DBG_ERR("failed to allocate stack frame\n"); + return -1; + } + + setup_logging(argv[0], DEBUG_STDERR); + smb_init_locale(); + + if (!lp_load_client(get_dyn_CONFIGFILE())) { + fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", + get_dyn_CONFIGFILE()); + exit(1); + } + + pc = poptGetContext("wsp-to-sparql", argc, argv, long_options, + 0); + + poptSetOtherOptionHelp(pc, "binary msg file"); + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) + { + case 'v': + raw = true; + break; + case 'f': + full = true; + break; + case 'r': + restriction = true; + break; + } + } + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + infile = talloc_strdup(frame, poptGetArg(pc)); + if (!infile) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + if (full && restriction) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + if (!full && !restriction) { + /* default to full */ + full = true; + } + if (!lp_load_with_shares(get_dyn_CONFIGFILE())) { + DBG_ERR("failed to load %s\n",get_dyn_CONFIGFILE()); + return -1; + } + poptFreeContext(pc); + + request = talloc(ctx, struct wsp_request); + response = talloc(ctx, struct wsp_response); + ZERO_STRUCTP(request); + ZERO_STRUCTP(response); + ZERO_STRUCT(tracker_cols); + + + if (!get_blob_from_file(ctx, infile, &blob)) { + DBG_ERR("failed to process %s\n", infile); + result = 1; + goto out; + } + + err = parse_blob(ctx, &blob, request, response, true); + if (err) { + DBG_ERR("failed to parse blob error %d\n", err); + result = 1; + goto out; + } + if (request->header.msg != CPMCREATEQUERY) { + DBG_ERR("wrong msg request type was expecting CPMCREATEQUERY, got %d\n", request->header.msg); + } + + query = &request->message.cpmcreatequery; + pidmapper = &query->pidmapper; + + if (query->ccolumnsetpresent) { + projected_col_offsets = &query->columnset.columnset; + } + if (query->crestrictionpresent) { + restrictionset = &query->restrictionarray.restrictionarray; + } + + status = + build_restriction_expression(ctx, + NULL, + restrictionset, + !raw, + &restrictionset_expr, + &share, + &where_id); + + if (!restrictionset_expr || strlen(restrictionset_expr) == 0) { + DBG_ERR("failed to generate restriction expression\n"); + goto out; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("status failure detected %s\n", + nt_errstr(status)); + goto out; + } + + if (full) { + struct binding_result_mapper *result_converter; + struct map_data *map_data; + struct wsp_ctablecolumn *columns; + uint32_t ncolumns; + struct wsp_csortset *sortset; + if (query->csortsetpresent) { + sortset = &query->sortset.sortset; + } + result_converter = talloc_zero(ctx, + struct binding_result_mapper); + if (!result_converter) { + goto out; + } + /* + * currently the tool doesn't have access to the bindings so + * we synthesise them here from the columnset & pidmapper info + * from the query message. + * #TODO allow bindings be specified on the commandline also + * to be used here. + */ + if (!synthesize_bindings(ctx, projected_col_offsets, pidmapper, + &columns, &ncolumns)) { + goto out; + } + + status = build_tracker_query(ctx, + projected_col_offsets, + restrictionset_expr, + pidmapper, + &tracker_cols, + sortset, + !raw, + &query_str); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + DBG_ERR("tracker-sparql query is:\n\"%s\"\n", query_str); + DBG_ERR("selected columns:\n"); + if (!build_mapper(ctx, columns, ncolumns, &tracker_cols, + result_converter)) { + goto out; + } + map_data = result_converter->map_data; + for (i=0; i < result_converter->ncols; i++) { + int pid_index = projected_col_offsets->indexes[i]; + struct wsp_cfullpropspec *prop_spec = + &pidmapper->apropspec[pid_index]; + char *prop = prop_from_fullprop(ctx, prop_spec); + if (map_data[i].convert_fn) { + DBG_ERR("Col[%d] %s is mapped/converted from tracker col[%d] %s\n", i, prop, map_data[i].col_with_value, tracker_cols.tracker_ids[map_data[i].col_with_value]); + } else { + DBG_ERR("Col[%d] %s Will not return a value\n", i, prop); + } + } + } + if (restriction) { + DBG_ERR("tracker-sparql restriction expression\n\"%s\"\n", + restrictionset_expr); + } + result = 0; + status = NT_STATUS_OK; +out: + return result; +} diff --git a/source3/wscript_build b/source3/wscript_build index 7b3dc51..0ad2afb 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1034,6 +1034,16 @@ bld.SAMBA3_BINARY('msg_source', param''', install=False) +bld.SAMBA3_BINARY('wsp-to-sparql', + source='utils/wsp-to-tracker.c rpc_server/wsp/wsp_sparql_conv.c', + deps=''' + talloc + popt_samba3 + smbd_base + NDR_WSP + NDR_WSP_DATA''', + enabled=bld.env.with_wsp) + bld.SAMBA3_BINARY('tevent_glib_glue_test', source='lib/tevent_glib_glue_tests.c', deps=''' -- 2.10.2