WIP WSP patches

Noel Power nopower at suse.com
Fri Jul 29 16:20:58 UTC 2016


Dear List,

As promised here is the current set of patches for (work in progress)
WSP (Windows Search protocol) It would be really great to be able to
have this as an experimental feature. There are alot patches involved,
some of them you have seen before but they never really got progressed.
I hope I can get your help to push on further with this -)

patches [1,2,3,4] are basically the rawpipe patches I posted a couple of
weeks ago (with some minor changes)

patches [5,6] splits out the binding handle backend code used by the
torture test to be used by other code (e.g. any future clients) and
adjusts the torture test. (these could be easily squashed with the above)

patches [7,8] were patches submitted with the rawpipe patches mentioned
previously (and also sometime even much longer ago) These patches also
include updates to the torture test.

patches [8,9 & 19] again some patches from the past submitted a long
time ago but submitting here again because I need them for WSP, idl
compiler spins without them.

patches [11] WSP idl (it depends on the previous pidl patches)

patches [12,13,14,15] Provide '--enable-WSP' configure option, some
common helper code (e.g. common wsp property definitions) and a basic
windows search cli program

patches [16] SMB1 support for MsFteWds WaitNamedPipe

patches [17] SMB2 handle FSCTL_PIPE_WAIT message for MsFteWds pipe

patches [18, 19, 20, 21] are patches from Ralph that enable the glib
integration (rewritten version of my attempt to do same) Note: these
patches don't include the parts of that patchset that modify the
spotlight service (these didn't apply cleanly for me).

patches [22, 23] are the patches I sent previously as part of the
discussion about Ralfs rewrite of the glib integration work above. They
address problems I found and additionally modify some the test client
code. The WSP server implementation depends on these patches. Yes it is
possible to write a standalone glib WSP server or even use some other
technique to e.g. separate out the glib parts into a different process
or thread. I neither want to paint myself into a glib corner or get into
the business or rewritting code already present in the samba libraries
or managing a separate thread or process. The glib glue code allows glib
and tevent code to co-exist seamlessly without awkward and strange
'hacks' in the application code. Note: I've been using the glib/tevent
code for quite a while now and experienced no negative issues with it at
all.

patches [24, 25] The WSP code implementation itself, true it is not
complete, there are still many holes in the implementation, but.... it
works pretty well with the basic searches you can do with the normal
windows api [1]. I hope we I can get it upstream as an experimental
feature. At least there I (or others) can improve it without it bit
rotting, with so many patches it is hard to keep it applying on current
master.

patches [26] tracker-to-sparql. Developer tool for converting the binary
query into tracker-sparql (also can be used to see a stringified version
of the raw binary expression restrictions prior to conversion)

Trying it out
=============

The current version of the patches attached here are available from
https://github.com/noelpower/samba/tree/WSP-WIP

  configure with '--enable-wsp'

To deploy you need to follow the instructions as per spotlight wrt
tracker e.g.
https://wiki.samba.org/index.php/Spotlight#Setup

Windows search of Linux share
=============================

On a windows machine navigate to a linux samba share that is indexed
with the tracker user as described above and start searching and running
smbd (enabled with wsp)

Linux cli
=========

To test out the client cli it's quite simple

example 1. Search for all pictures in $(share)

    wspsearch -U$(USER)%$(PASSWD) //$(SERVER)/$(SHARE) --kind=Picture

example 2. Search for all pictures is $(share) whose name contain 'DSC'

    wspsearch -U$(USER)%$(PASSWD) //$(SERVER)/$(SHARE) --kind=Picture
--phrase='DSC'

Usage: wspsearch //server1/share1
      --search=phrase                         Search phrase
      --kind=kind                             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]

Examining the protocol
======================
Recent versions of wireshark (I know that at least version 2.0.4) have
the dissector for WSP that myself and Gregor Beck wrote. (note: still
some tweaks needed to that due to recent changes to the MS-WSP doc
triggered by some discussions I had on dochelp at microsoft.com)


[1] - There are known problems like
  a) if you search against a kind e,g, 'Movies' that tracker (afaics)
doesn't understand you get 'all' results back, I just haven't thought
about how to handle some of the scenarios long or hard enough. So, in
the example about we could just return any 'Video' but there is already
a 'Video' kind in windows, or we could just realise we can't deal with
it and just return no results etc. Similar situations exist for some
unknown properties that can happen in the query.
  b) sorting of results isn't handled
  c) there are lots of exotic restriction types that don't seem to be
used by the search ui that honestly I don't even understand the text in
the protocol documents
  d) and so on and so on....
-------------- next part --------------
From 08b1c8d83344d0218b3e9dd06fb095d46141f18b Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 4 Dec 2014 19:25:02 +0000
Subject: [PATCH 01/26] s3: Add for very basic loop integration infrastructure
 for non-rpc (raw) pipes.

Add a simple infrastructure that gives the ability to add a wellknown
pipe name and an associated callback for the server loop.
Support for both embedded and external service is provided.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/rpc_server/rpc_ncacn_np.c | 23 ++++++++++---
 source3/rpc_server/rpc_server.c   | 71 ++++++++++++++++++++++++++++++++++++---
 source3/rpc_server/rpc_server.h   | 14 ++++++++
 source3/rpc_server/srv_pipe.c     |  7 ++--
 4 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/source3/rpc_server/rpc_ncacn_np.c b/source3/rpc_server/rpc_ncacn_np.c
index f9c73de..6a4597a 100644
--- a/source3/rpc_server/rpc_ncacn_np.c
+++ b/source3/rpc_server/rpc_ncacn_np.c
@@ -85,6 +85,7 @@ NTSTATUS make_internal_rpc_pipe_socketpair(TALLOC_CTX *mem_ctx,
 	NTSTATUS status;
 	int error;
 	int rc;
+	struct name_pipe_server_details *pipe_details = NULL;
 
 	DEBUG(4, ("Create of internal pipe %s requested\n", pipe_name));
 
@@ -94,7 +95,13 @@ NTSTATUS make_internal_rpc_pipe_socketpair(TALLOC_CTX *mem_ctx,
 		goto out;
 	}
 
-	npa->file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+	pipe_details = get_pipe_server_details(pipe_name);
+
+	if (pipe_details) {
+		npa->file_type = pipe_details->msg_mode;
+	} else {
+		npa->file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+	}
 	npa->device_state = 0xff | 0x0400 | 0x0100;
 	npa->allocation_size = 4096;
 
@@ -161,14 +168,22 @@ NTSTATUS make_internal_rpc_pipe_socketpair(TALLOC_CTX *mem_ctx,
 		goto out;
 	}
 
-	subreq = dcerpc_read_ncacn_packet_send(npc, npc->ev, npc->tstream);
+	if (pipe_details) {
+		subreq = pipe_details->start_server_loop(npc,
+							 pipe_details->private_data);
+	} else {
+		subreq = dcerpc_read_ncacn_packet_send(npc,
+						       npc->ev,
+						       npc->tstream);
+		if (subreq) {
+			tevent_req_set_callback(subreq, named_pipe_packet_process, npc);
+		}
+	}
 	if (subreq == NULL) {
 		DEBUG(2, ("Failed to start receving packets\n"));
 		status = NT_STATUS_PIPE_BROKEN;
 		goto out;
 	}
-	tevent_req_set_callback(subreq, named_pipe_packet_process, npc);
-
 	*pnpa = talloc_steal(mem_ctx, npa);
 	status = NT_STATUS_OK;
 out:
diff --git a/source3/rpc_server/rpc_server.c b/source3/rpc_server/rpc_server.c
index 5effe66..a9487f5 100644
--- a/source3/rpc_server/rpc_server.c
+++ b/source3/rpc_server/rpc_server.c
@@ -37,6 +37,47 @@
 #define SERVER_TCP_LOW_PORT  1024
 #define SERVER_TCP_HIGH_PORT 1300
 
+static struct name_pipe_server_details *pipe_details_map = NULL;
+
+static void init_pipe_details_map(void)
+{
+	if (!pipe_details_map) {
+		pipe_details_map = talloc_zero(NULL, struct name_pipe_server_details);
+	}
+}
+
+struct name_pipe_server_details *get_pipe_server_details(const char* name) {
+	struct name_pipe_server_details *item;
+	init_pipe_details_map();
+	for(item = pipe_details_map; item; item = item->next) {
+		if (strequal(name, item->name)) {
+			return item;
+		}
+	}
+	return NULL;
+}
+
+void add_pipe_server_details(const char* name,  uint16_t msg_mode,
+			     server_loop_fn loop_fn, void *private_data)
+{
+	struct name_pipe_server_details * item = get_pipe_server_details(name);
+	if (item) {
+		/*update*/
+		item->start_server_loop = loop_fn;
+		item->msg_mode = msg_mode;
+		item->private_data = private_data;
+	} else {
+		struct name_pipe_server_details *new_item =
+			talloc_zero(pipe_details_map,
+				    struct name_pipe_server_details);
+		new_item->name = name;
+		new_item->start_server_loop = loop_fn;
+		new_item->msg_mode = msg_mode;
+		new_item->private_data = private_data;
+		DLIST_ADD_END(pipe_details_map, new_item);
+	}
+}
+
 /* Creates a pipes_struct and initializes it with the information
  * sent from the client */
 int make_server_pipes_struct(TALLOC_CTX *mem_ctx,
@@ -291,6 +332,8 @@ void named_pipe_accept_function(struct tevent_context *ev_ctx,
 	struct tstream_context *plain;
 	struct tevent_req *subreq;
 	int ret;
+	struct name_pipe_server_details *pipe_details =
+				get_pipe_server_details(pipe_name);
 
 	npc = talloc_zero(ev_ctx, struct named_pipe_client);
 	if (!npc) {
@@ -330,7 +373,11 @@ void named_pipe_accept_function(struct tevent_context *ev_ctx,
 		return;
 	}
 
-	npc->file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+	if (pipe_details) {
+		npc->file_type = pipe_details->msg_mode;
+	} else {
+		npc->file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+	}
 	npc->device_state = 0xff | 0x0400 | 0x0100;
 	npc->allocation_size = 4096;
 
@@ -347,13 +394,12 @@ void named_pipe_accept_function(struct tevent_context *ev_ctx,
 	tevent_req_set_callback(subreq, named_pipe_accept_done, npc);
 }
 
-static void named_pipe_packet_done(struct tevent_req *subreq);
-
 static void named_pipe_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);
+	struct name_pipe_server_details *pipe_details = NULL;
 	int error;
 	int ret;
 
@@ -395,12 +441,26 @@ static void named_pipe_accept_done(struct tevent_req *subreq)
 	}
 
 	/* And now start receiving and processing packets */
-	subreq = dcerpc_read_ncacn_packet_send(npc, npc->ev, npc->tstream);
+
+	pipe_details = get_pipe_server_details(npc->pipe_name);
+	/* has the named pipe special non-rpc treatment */
+	if (pipe_details) {
+		subreq =
+			pipe_details->start_server_loop(npc,
+							pipe_details->private_data);
+	} else {
+		subreq = dcerpc_read_ncacn_packet_send(npc,
+						       npc->ev,
+						       npc->tstream);
+		if (subreq) {
+			tevent_req_set_callback(subreq,
+						named_pipe_packet_process, npc);
+		}
+	}
 	if (!subreq) {
 		DEBUG(2, ("Failed to start receving packets\n"));
 		goto fail;
 	}
-	tevent_req_set_callback(subreq, named_pipe_packet_process, npc);
 	return;
 
 fail:
@@ -411,6 +471,7 @@ fail:
 	return;
 }
 
+static void named_pipe_packet_done(struct tevent_req *subreq);
 void named_pipe_packet_process(struct tevent_req *subreq)
 {
 	struct named_pipe_client *npc =
diff --git a/source3/rpc_server/rpc_server.h b/source3/rpc_server/rpc_server.h
index 2291350..33f0bd8 100644
--- a/source3/rpc_server/rpc_server.h
+++ b/source3/rpc_server/rpc_server.h
@@ -55,6 +55,8 @@ struct named_pipe_client {
 	void *private_data;
 };
 
+bool pipe_init_outgoing_data(struct pipes_struct *p);
+
 struct named_pipe_client *named_pipe_client_init(TALLOC_CTX *mem_ctx,
 						 struct tevent_context *ev_ctx,
 						 struct messaging_context *msg_ctx,
@@ -108,4 +110,16 @@ void dcerpc_ncacn_accept(struct tevent_context *ev_ctx,
 			 int s,
 			 dcerpc_ncacn_disconnect_fn fn);
 
+typedef struct tevent_req *(*server_loop_fn)(struct named_pipe_client *npc,
+					     void *private_data);
+struct name_pipe_server_details {
+	struct name_pipe_server_details *prev, *next;
+	const char* name;
+	uint16_t msg_mode;
+	server_loop_fn start_server_loop;
+	void *private_data;
+};
+
+void add_pipe_server_details(const char *name, uint16_t msg_mode, server_loop_fn loop, void *private_data);
+struct name_pipe_server_details *get_pipe_server_details(const char* name);
 #endif /* _PRC_SERVER_H_ */
diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c
index bcd7e5d..7972db1 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.
 *******************************************************************/
@@ -481,7 +479,8 @@ bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
 		return false;
 	}
 
-	if (rpc_srv_get_pipe_interface_by_cli_name(pipename, syntax)) {
+	if (rpc_srv_get_pipe_interface_by_cli_name(pipename, syntax)
+	    || get_pipe_server_details(pipename)) {
 		return true;
 	}
 
@@ -1510,7 +1509,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;
 
-- 
2.1.4


From 7197208c3dbce25edb79220048b85f6f2d57f8f7 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 10 May 2016 17:14:39 +0100
Subject: [PATCH 02/26] s3: add 'generic' rawpipe server loop and a reference
 impl for testing.

Provide a helper function 'common_rawpipe_register' that provides a server
loop (you can still provide your own loop if you want) to simplify
processing of 'rawpipe' messages. The reference implementation is built only
with '-DDEVELOPER' and will be used by smbtorture for testing.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/rpc_server/rawpipe.c     | 457 +++++++++++++++++++++++++++++++++++++++
 source3/rpc_server/rawpipe.h     |  50 +++++
 source3/rpc_server/rpc_config.h  |   3 +
 source3/rpc_server/wscript_build |   4 +
 4 files changed, 514 insertions(+)
 create mode 100644 source3/rpc_server/rawpipe.c
 create mode 100644 source3/rpc_server/rawpipe.h

diff --git a/source3/rpc_server/rawpipe.c b/source3/rpc_server/rawpipe.c
new file mode 100644
index 0000000..e6ba332
--- /dev/null
+++ b/source3/rpc_server/rawpipe.c
@@ -0,0 +1,457 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *
+ *  RawPipe server loop
+ *
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rawpipe.h"
+#include <tevent.h>
+#include "rpc_common.h"
+#include "rpc_server/srv_pipe.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_server/rpc_pipes.h"
+#include "rpc_server/rpc_config.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/tevent_ntstatus.h"
+
+struct common_rawpipe_ctx
+{
+	rawpipe_init init;
+	rawpipe_close dtor;
+	rawpipe_fn handler;
+	void *private_data;
+};
+
+struct common_rawpipe_loop_ctx
+{
+	void *init_ctx;
+	struct common_rawpipe_ctx *reg_ctx;
+};
+
+static struct tevent_req *process_rawpipe_request(TALLOC_CTX *mem_ctx,
+						   struct named_pipe_client *npc)
+{
+	struct tevent_req *subreq;
+	struct pipes_struct *p = npc->p;
+	struct common_rawpipe_loop_ctx *ctx =
+			talloc_get_type_abort(npc->private_data,
+					      struct common_rawpipe_loop_ctx);
+	TALLOC_CTX *frame = talloc_stackframe();
+
+	if (!pipe_init_outgoing_data(p)) {
+		goto done;
+	}
+
+	subreq = ctx->reg_ctx->handler(mem_ctx, npc, ctx->init_ctx);
+
+	if (!subreq) {
+		goto done;
+	}
+done:
+	TALLOC_FREE(frame);
+	return subreq;
+}
+
+struct rawpipe_send_state {
+	DATA_BLOB buffer;
+	struct iovec in;
+	struct tstream_context *stream;
+	struct tevent_context *ev;
+};
+
+static void read_rawpipe_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq,
+				 struct tevent_req);
+	struct rawpipe_send_state *state =
+		tevent_req_data(req, struct rawpipe_send_state);
+	int ret;
+	int sys_errno;
+	int to_read;
+	ssize_t ofs = 0;
+	NTSTATUS status;
+
+	DEBUG(10,("read_rawpipe_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_rawpipe_done, req);
+}
+
+static struct tevent_req *read_rawpipe_send(TALLOC_CTX *mem_ctx,
+				 struct tevent_context *ev,
+				 struct tstream_context *stream)
+{
+	struct tevent_req *req;
+	struct rawpipe_send_state *state;
+	struct tevent_req *subreq;
+	req = tevent_req_create(mem_ctx, &state,
+				struct rawpipe_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;
+	DEBUG(10,("read_rawpipe_send stream %p\n", stream));
+	subreq = tstream_readv_send(state, ev,
+					stream,
+					&state->in,
+					1);
+	tevent_req_set_callback(subreq, read_rawpipe_done, req);
+	return req;
+}
+
+static NTSTATUS rawpipe_read_recv(struct tevent_req *req,
+				  TALLOC_CTX *mem_ctx,
+				  DATA_BLOB *buffer)
+{
+	struct rawpipe_send_state *state = tevent_req_data(req,
+						struct rawpipe_send_state);
+	NTSTATUS status;
+
+	DEBUG(10,("rawpipe_read_recv\n"));
+	if (tevent_req_is_nterror(req, &status)) {
+		DEBUG(0,("rawpipe_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;
+}
+
+void rawpipe_process(struct tevent_req *subreq);
+
+static void common_destroy_pipe(void *private_data)
+{
+	struct common_rawpipe_loop_ctx *loop_ctx =
+			talloc_get_type_abort(private_data,
+					      struct common_rawpipe_loop_ctx);
+	loop_ctx->reg_ctx->dtor(loop_ctx->init_ctx);
+}
+
+static struct tevent_req *start_common_rawpipe_loop(struct named_pipe_client *npc,
+						void *private_data);
+static struct tevent_req *common_rawpipe_loop(struct named_pipe_client *npc);
+
+static void rawpipe_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;
+
+	DEBUG(10,("rawpipe_process_done \n"));
+	ret = tstream_writev_queue_recv(subreq, &sys_errno);
+	TALLOC_FREE(subreq);
+	if (ret == -1) {
+		DEBUG(2, ("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 = common_rawpipe_loop(npc);
+	if (!subreq) {
+		goto fail;
+	}
+	return;
+fail:
+	DEBUG(0, ("Fatal error(%s). "
+		  "Terminating client(%s) connection!\n",
+		  strerror(sys_errno), npc->client_name));
+	/* terminate client connection */
+	talloc_free(npc);
+	return;
+}
+
+static void process_rawpipe_request_done(struct tevent_req *subreq);
+void rawpipe_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;
+
+	DEBUG(10,("rawpipe_process \n"));
+	status = rawpipe_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_rawpipe_request(npc->p->mem_ctx, npc);
+	tevent_req_set_callback(subreq, process_rawpipe_request_done, npc);
+	talloc_free(recv_buffer.data);
+	return;
+fail:
+	DEBUG(0, ("Fatal error(%s). "
+		  "Terminating client(%s) connection!\n",
+		  nt_errstr(status), npc->client_name));
+	/* terminate client connection */
+	talloc_free(npc);
+	return;
+}
+
+static void process_rawpipe_request_done(struct tevent_req *subreq)
+{
+	uint32_t to_send;
+	struct named_pipe_client *npc;
+	struct _output_data *out;
+	NTSTATUS status;
+	DEBUG(10,("process_rawpipe_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;
+		DEBUG(10,("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) {
+			DEBUG(2, ("Failed to send response for raw pipe\n"));
+			status = NT_STATUS_NO_MEMORY;
+			goto fail;
+		}
+		tevent_req_set_callback(subreq, rawpipe_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 = common_rawpipe_loop(npc);
+		if (!subreq) {
+			status = NT_STATUS_NO_MEMORY;
+			goto fail;
+		}
+	}
+	return;
+fail:
+	DEBUG(0, ("Fatal error(%s). "
+		  "Terminating client(%s) connection!\n",
+		  nt_errstr(status), npc->client_name));
+	/* terminate client connection */
+	talloc_free(npc);
+	return;
+}
+
+static struct tevent_req *common_rawpipe_loop(struct named_pipe_client *npc)
+{
+	struct tevent_req *subreq;
+
+	subreq = read_rawpipe_send(npc, npc->ev, npc->tstream);
+	if (!subreq) {
+		DEBUG(0, ("Failed to start receving packets\n"));
+		goto fail;
+	}
+	tevent_req_set_callback(subreq, rawpipe_process, npc);
+fail:
+	return subreq;
+}
+
+static struct tevent_req *start_common_rawpipe_loop(struct named_pipe_client *npc,
+						void *private_data)
+{
+	struct common_rawpipe_ctx *ctx =
+			talloc_get_type_abort(private_data,
+					      struct common_rawpipe_ctx);
+	struct common_rawpipe_loop_ctx *loop_ctx =
+			talloc_zero(npc, struct common_rawpipe_loop_ctx);
+	loop_ctx->reg_ctx = ctx;
+	if (ctx->init) {
+		loop_ctx->init_ctx = ctx->init(npc, ctx->private_data);
+	}
+	if (ctx->dtor) {
+		npc->term_fn = common_destroy_pipe;
+	}
+	npc->private_data = loop_ctx;
+	return common_rawpipe_loop(npc);
+}
+
+void common_rawpipe_register(const char* pipename,
+			     uint16_t msg_mode,
+			     rawpipe_init init,
+			     rawpipe_close dtor,
+			     rawpipe_fn handler,
+			     void *private_data)
+{
+	struct common_rawpipe_ctx *ctx =
+		talloc(NULL, struct common_rawpipe_ctx);
+	ctx->init = init;
+	ctx->dtor = dtor;
+	ctx->handler = handler;
+	ctx->private_data = private_data;
+	add_pipe_server_details(pipename,
+				msg_mode,
+				start_common_rawpipe_loop,
+				ctx);
+}
+
+#ifdef DEVELOPER
+
+struct dummy_rawpipe_state
+{
+};
+
+/*
+ * Very simple test harness for raw pipes
+ * + If we recieve message 'large' we return a large message
+ *   that we know breaks the max response with a named pipe trans msg
+ *   (Max Ioctl is hard coded to 4280) (this should generate a BUFFER_OVERFLOW)
+ * + Any other message we recieve we just echo back
+ *
+ * Note: we can easily extend the test harness by defining some proper
+ * 	 message structures to exchange more complex instruction + payload
+ *	 combinations.
+ */
+static struct tevent_req *do_echo_send(TALLOC_CTX *ctx,
+				       struct named_pipe_client *npc,
+				       void *init_ctx)
+{
+	struct tevent_req *req;
+	struct pipes_struct *p = npc->p;
+	struct dummy_rawpipe_state *state;
+	const char* large = "large";
+	req = tevent_req_create(ctx, &state,
+				struct dummy_rawpipe_state);
+	DEBUG(10,("received %lu bytes mem_ctx = %p\n",
+	      p->in_data.pdu.length,
+	      npc->p->mem_ctx));
+
+	if ((p->in_data.pdu.length == strlen(large) + 1)
+	   && (memcmp(p->in_data.pdu.data, large, strlen(large) + 1) == 0)) {
+		uint32_t large_size = 6500;
+		p->out_data.rdata.data = talloc_zero_array(npc->p->mem_ctx,
+					       uint8_t,
+					       large_size);
+		p->out_data.rdata.length = large_size;
+		DEBUG(10,("LARGE message test sending back %d bytes\n",
+			large_size));
+	} else {
+		/* simple echo */
+		p->out_data.rdata.data = talloc_array(npc->p->mem_ctx,
+					       uint8_t,
+					       p->in_data.pdu.length);
+		p->out_data.rdata.length = p->in_data.pdu.length;
+	}
+
+	memcpy(p->out_data.rdata.data,
+		p->in_data.pdu.data,
+		p->in_data.pdu.length);
+
+	tevent_req_done(req);
+	return tevent_req_post(req, npc->ev);
+}
+
+void init_rawipe_echo(struct tevent_context *ev_ctx,
+		      struct messaging_context *msg_ctx)
+{
+	common_rawpipe_register("rawpipe", FILE_TYPE_MESSAGE_MODE_PIPE,
+				NULL, NULL, do_echo_send, NULL);
+	if (rpc_rawd_daemon()) {
+		pid_t pid = fork();
+		bool ok;
+		if (pid == -1) {
+			DEBUG(0, ("failed to fork rawd daemon [%s], "
+			      "aborting ...\n", strerror(errno)));
+			exit(1);
+		}
+
+		if (pid) {
+			/* parent */
+			return;
+		}
+		ok = setup_named_pipe_socket("rawpipe", ev_ctx, msg_ctx);
+		if (!ok) {
+			DEBUG(0, ("Failed to open rawpipe named pipe!\n"));
+			exit(1);
+		}
+		DEBUG(10,("rawd daemon started\n"));
+	}
+	DEBUG(10,("raw pipe echo loop started\n"));
+}
+#endif
diff --git a/source3/rpc_server/rawpipe.h b/source3/rpc_server/rawpipe.h
new file mode 100644
index 0000000..33f797f
--- /dev/null
+++ b/source3/rpc_server/rawpipe.h
@@ -0,0 +1,50 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *
+ *  RawPipe server loop
+ *
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RAWPIPE__
+#define __RAWPIPE__
+
+
+struct named_pipe_client;
+struct messaging_context;
+
+/* returns a context that is passed to the handler and close functions */
+typedef void* (*rawpipe_init)(struct named_pipe_client *npc,
+			    void *private_data);
+/* called when pipe is destoyed */
+typedef void (*rawpipe_close)(void *init_ctx);
+/* called when a message to respond to on the pipe is available */
+typedef struct tevent_req *(*rawpipe_fn)(TALLOC_CTX *ctx,
+					 struct named_pipe_client *npc,
+					 void *init_ctx);
+
+void common_rawpipe_register(const char* pipename,
+			     uint16_t msg_mode,
+			     rawpipe_init init,
+			     rawpipe_close dtor,
+			     rawpipe_fn handler,
+			     void *private_data);
+#ifdef DEVELOPER
+void init_rawipe_echo(struct tevent_context *ev_ctx,
+		      struct messaging_context *msg_ctx);
+#endif
+
+#endif /* __RAWPIPE__ */
diff --git a/source3/rpc_server/rpc_config.h b/source3/rpc_server/rpc_config.h
index 5091704..dd39f14 100644
--- a/source3/rpc_server/rpc_config.h
+++ b/source3/rpc_server/rpc_config.h
@@ -68,5 +68,8 @@ enum rpc_daemon_type_e rpc_daemon_type(const char *name);
 #define rpc_lsasd_daemon() rpc_daemon_type("lsasd")
 #define rpc_fss_daemon() rpc_daemon_type("fssd")
 #define rpc_mdssd_daemon() rpc_daemon_type("mdssd")
+#ifdef DEVELOPER
+#define rpc_rawd_daemon() rpc_daemon_type("rawd")
+#endif
 
 #endif /* _RPC_CONFIG_H */
diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build
index 1d0facb..3982064 100755
--- a/source3/rpc_server/wscript_build
+++ b/source3/rpc_server/wscript_build
@@ -1,6 +1,10 @@
 #!/usr/bin/env python
 
 ### RPC_SERVER
+bld.SAMBA3_SUBSYSTEM('RAWPIPE',
+                    source='rawpipe.c',
+                    deps='samba-util')
+
 bld.SAMBA3_SUBSYSTEM('rpc',
                     source='',
                     deps='RPC_PIPE_REGISTER')
-- 
2.1.4


From 62de600c58f5a3893b694d8c7ccff748d50c221b Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 10 May 2016 17:15:06 +0100
Subject: [PATCH 03/26] s3:smbd Add rawd (rawpipe echo test service) to the
 smbd process.

The new raw pipe service is conditionalised on --enable-developer.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/server.c | 7 ++++++-
 source3/wscript_build | 2 +-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 9e3c652..3fda247 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -51,7 +51,9 @@
 #include "smbd/notifyd/notifyd.h"
 #include "smbd/smbd_cleanupd.h"
 #include "lib/util/sys_rw.h"
-
+#ifdef DEVELOPER
+#include "rpc_server/rawpipe.h"
+#endif
 #ifdef CLUSTER_SUPPORT
 #include "ctdb_protocol.h"
 #endif
@@ -1715,6 +1717,9 @@ extern void build_options(bool screen);
 		daemon_ready("smbd");
 	}
 
+#ifdef DEVELOPER
+	init_rawipe_echo(ev_ctx, msg_ctx);
+#endif
 	/* only start other daemons if we are running as a daemon
 	 * -- bad things will happen if smbd is launched via inetd
 	 *  and we fork a copy of ourselves here */
diff --git a/source3/wscript_build b/source3/wscript_build
index b9a2ee6..f8ddf23 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -855,7 +855,7 @@ bld.SAMBA3_SUBSYSTEM('LIBLSA',
 
 bld.SAMBA3_BINARY('smbd/smbd',
                  source='smbd/server.c smbd/smbd_cleanupd.c',
-                 deps='smbd_base EPMD LSASD FSSD MDSSD',
+                 deps='smbd_base EPMD LSASD FSSD MDSSD RAWPIPE',
                  install_path='${SBINDIR}')
 
 bld.SAMBA3_BINARY('nmbd/nmbd',
-- 
2.1.4


From 4325e36369d70b50e0befa5b1f272d558d6fe3fa Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 18 May 2016 15:06:49 +0100
Subject: [PATCH 04/26] s4/torture: Add smbtorture tests for rawpipe
 infrastructure.

Some simple tests that interact with the reference (simple echo service)
implementation that uses the new rawpipe infrastructure.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/selftest/tests.py     |   2 +-
 source4/torture/raw/raw.c     |   1 +
 source4/torture/raw/rawpipe.c | 670 ++++++++++++++++++++++++++++++++++++++++++
 source4/torture/wscript_build |   2 +-
 4 files changed, 673 insertions(+), 2 deletions(-)
 create mode 100644 source4/torture/raw/rawpipe.c

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 4736ebc..6905ee9 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -514,5 +514,5 @@ for e in endianness_options:
             options = binding_string + " -U$USERNAME%$PASSWORD"
             plansmbtorture4testsuite(test, "nt4_dc", options, 'over ncacn_ip_tcp with [%s%s%s] ' % (a, s, e))
 
-plansmbtorture4testsuite('rpc.epmapper', 'nt4_dc:local', 'ncalrpc: -U$USERNAME%$PASSWORD', 'over ncalrpc')
 plansmbtorture4testsuite('rpc.fsrvp', 'nt4_dc:local', 'ncacn_np:$SERVER_IP[/pipe/FssagentRpc] -U$USERNAME%$PASSWORD', 'over ncacn_np')
+plansmbtorture4testsuite('raw.rawpipe', 'nt4_dc:local', '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
diff --git a/source4/torture/raw/raw.c b/source4/torture/raw/raw.c
index bda463b..21d2f08 100644
--- a/source4/torture/raw/raw.c
+++ b/source4/torture/raw/raw.c
@@ -58,6 +58,7 @@ NTSTATUS torture_raw_init(void)
 	torture_suite_add_suite(suite, torture_raw_context(suite));
 	torture_suite_add_suite(suite, torture_raw_session(suite));
 	torture_suite_add_suite(suite, torture_raw_rename(suite));
+	torture_suite_add_suite(suite, torture_raw_rawpipe(suite));
 	torture_suite_add_1smb_test(suite, "seek", torture_raw_seek);
 	torture_suite_add_1smb_test(suite, "eas", torture_raw_eas);
 	torture_suite_add_suite(suite, torture_raw_streams(suite));
diff --git a/source4/torture/raw/rawpipe.c b/source4/torture/raw/rawpipe.c
new file mode 100644
index 0000000..e15df3e
--- /dev/null
+++ b/source4/torture/raw/rawpipe.c
@@ -0,0 +1,670 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   test suite for Raw pipe implementation
+
+   Copyright (C) Noel Power 2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/rawpipe.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/libcli.h"
+#include "librpc/rpc/dcerpc_raw.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "torture/rpc/torture_rpc.h"
+#include "torture/raw/proto.h"
+#include "torture/util.h"
+#include "util/tevent_ntstatus.h"
+#include "param/param.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,
+};
+
+static 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;
+}
+
+struct rawpipe_test_data
+{
+	struct dcerpc_pipe *p;
+	struct dcerpc_binding_handle *h;
+};
+
+static NTSTATUS write_something(TALLOC_CTX* ctx,
+				struct dcerpc_binding_handle *handle,
+				DATA_BLOB *blob_in,
+				DATA_BLOB *blob_out)
+{
+	uint32_t outflags;
+	NTSTATUS 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 NTSTATUS connect_server_smb2(TALLOC_CTX *mem_ctx,
+			struct torture_context *tctx,
+			struct smb2_tree **tree)
+{
+	NTSTATUS status;
+	struct cli_credentials *credentials = cmdline_credentials;
+	struct smbcli_options options;
+	struct smbcli_session_options session_options;
+	const char *host = torture_setting_string(tctx, "host", NULL);
+
+	lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+	lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+	status = smb2_connect(mem_ctx,
+			      host,
+			      lpcfg_smb_ports(tctx->lp_ctx),
+			      "IPC$",
+			      lpcfg_resolve_context(tctx->lp_ctx),
+			      credentials,
+			      tree,
+			      tctx->ev,
+			      &options,
+			      lpcfg_socket_options(tctx->lp_ctx),
+			      lpcfg_gensec_settings(tctx, tctx->lp_ctx)
+			      );
+	return status;
+}
+
+static NTSTATUS connect_server_smb(TALLOC_CTX *mem_ctx,
+			struct torture_context *tctx,
+			struct smbcli_state **cli)
+{
+	NTSTATUS status;
+	struct cli_credentials *credentials = cmdline_credentials;
+	struct smbcli_options options;
+	struct smbcli_session_options session_options;
+	const char *host = torture_setting_string(tctx, "host", NULL);
+
+	lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+	lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+	status = smbcli_full_connection(mem_ctx,
+					cli,
+					host,
+					lpcfg_smb_ports(tctx->lp_ctx),
+					"IPC$", NULL,
+					lpcfg_socket_options(tctx->lp_ctx),
+					credentials,
+					lpcfg_resolve_context(tctx->lp_ctx),
+					tctx->ev, &options, &session_options,
+					lpcfg_gensec_settings(tctx,
+							      tctx->lp_ctx));
+	return status;
+}
+
+static bool test_rawpipe_simple_echo(struct torture_context *tctx,
+				     const void *data)
+{
+	NTSTATUS status;
+	bool ret = true;
+	DATA_BLOB out;
+	DATA_BLOB in;
+	const char *test_message = "hello";
+	struct rawpipe_test_data *test_data =
+			talloc_get_type(data,
+					struct rawpipe_test_data);
+	TALLOC_CTX *mem_ctx = talloc_init("test_rawpipe_simple_echo");
+
+	in.data = talloc_array(mem_ctx, uint8_t, strlen(test_message) + 1);
+	in.length = talloc_array_length(in.data);
+	memcpy(in.data, test_message, in.length);
+	status = write_something(mem_ctx, test_data->h, &in, &out);
+	torture_assert_ntstatus_ok(tctx, status, "failed to write to pipe\n");
+	torture_assert(tctx,
+		       in.length == out.length,
+		       "message sizes are different");
+	ret = memcmp(in.data, out.data, in.length) == 0;
+	torture_assert(tctx, ret, "messages differ");
+	TALLOC_FREE(mem_ctx);
+	return ret;
+}
+
+/*
+ * The idea here is to send a large message size that exceeds the normal
+ * hardcoded Max Ioctl hard coded limit of 4280 bytes, this will generate
+ * will result in a BUFFER_OVERFLOW error at the SMB layer in the response
+ * from the server [1].
+ *
+ * [1] It seems we don't see the SMB BUFFER_OVERFLOW status at this layer
+ *     however we can detect that the message is clipped to the current
+ *     limit of 4280 bytes.
+ */
+static bool test_rawpipe_large_message(struct torture_context *tctx,
+				     const void *data)
+{
+	NTSTATUS status;
+	bool ret = true;
+	const char * test_message = "large";
+	uint32_t limit = 4280;
+	uint32_t expected = 6500;
+	DATA_BLOB out;
+	DATA_BLOB in;
+	struct rawpipe_test_data *test_data =
+			talloc_get_type(data,
+					struct rawpipe_test_data);
+
+	TALLOC_CTX *mem_ctx = talloc_init("test_rawpipe_large_message");
+	in.data = talloc_array(mem_ctx, uint8_t, strlen(test_message) + 1);
+	in.length = talloc_array_length(in.data);
+	memcpy(in.data, test_message, in.length);
+
+	status = write_something(tctx, test_data->h, &in, &out);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "failed to write to pipe\n");
+	ret = (out.length != expected) && (out.length == limit);
+	torture_comment(tctx, "test_rawpipe_large_message test was %s (received %d bytes expected %d)\n", ret ? "successful" : "unsuccsessful", (uint32_t)out.length, limit);
+done:
+	return ret;
+}
+
+static bool raw_smb1_setup(struct torture_context *tctx,
+			  void **ppdata)
+{
+	struct rawpipe_test_data *data;
+	NTSTATUS status;
+	struct dcerpc_pipe *p;
+	struct smbcli_state *cli = NULL;
+	struct dcerpc_binding_handle *h;
+
+	data = talloc(NULL, struct rawpipe_test_data);
+	status = connect_server_smb(data, tctx, &cli);
+	torture_assert_ntstatus_ok(tctx, status, "failed to connect to server");
+
+	p = dcerpc_pipe_init(tctx, tctx->ev);
+
+	status = dcerpc_pipe_open_smb(p, cli->tree, "rawpipe");
+	torture_assert_ntstatus_ok(tctx, status, "could not open pipe\n");
+
+	h = create_rawpipe_handle(p);
+	torture_assert(tctx, h != NULL, "failed to create handle\n");
+
+	status = tstream_smbXcli_np_use_trans(p->conn->transport.stream);
+	torture_assert_ntstatus_ok(tctx,
+				   status,
+				   "failed to set trans mode on pipe\n");
+
+	data->p = p;
+	data->h = h;
+
+	*ppdata = data;
+	return true;
+}
+
+static bool raw_smb2_setup(struct torture_context *tctx,
+			  void **ppdata)
+{
+	struct rawpipe_test_data *data;
+	NTSTATUS status;
+	struct dcerpc_pipe *p;
+	struct smb2_tree *tree = NULL;
+	struct dcerpc_binding_handle *h;
+
+	data = talloc(NULL, struct rawpipe_test_data);
+
+	status = connect_server_smb2(data, tctx, &tree);
+	torture_assert_ntstatus_ok(tctx, status, "failed to connect to server");
+
+	p = dcerpc_pipe_init(tctx, tctx->ev);
+
+	status = dcerpc_pipe_open_smb2(p, tree, "rawpipe");
+	torture_assert_ntstatus_ok(tctx, status, "could not open pipe\n");
+
+	h = create_rawpipe_handle(p);
+	torture_assert(tctx, h != NULL, "failed to create handle\n");
+
+	status = tstream_smbXcli_np_use_trans(p->conn->transport.stream);
+	torture_assert_ntstatus_ok(tctx,
+				   status,
+				   "failed to set trans mode on pipe\n");
+
+	data->p = p;
+	data->h = h;
+
+	*ppdata = data;
+	return true;
+}
+
+
+static bool raw_smb1_teardown(struct torture_context *tctx,
+			  void *data)
+{
+	return true;
+}
+
+static bool raw_smb2_teardown(struct torture_context *tctx,
+			  void *data)
+{
+	return true;
+}
+
+static void add_raw_test(struct torture_suite *suite,
+			const char *name,
+			bool (*run) (struct torture_context *test,
+					const void *tcase_data),
+			bool use_smb2)
+{
+	struct torture_tcase *tcase = torture_suite_add_tcase(suite,
+							      name);
+	if (use_smb2) {
+		torture_tcase_set_fixture(tcase,
+					  raw_smb2_setup,
+					  raw_smb1_teardown);
+	} else {
+		torture_tcase_set_fixture(tcase,
+					  raw_smb1_setup,
+					  raw_smb2_teardown);
+	}
+	torture_tcase_add_simple_test_const(tcase, name, run);
+}
+
+struct torture_suite *torture_raw_rawpipe(TALLOC_CTX *mem_ctx)
+{
+	struct torture_suite *suite = torture_suite_create(mem_ctx, "rawpipe");
+	/* SMB1 tests */
+	add_raw_test(suite, "smb1_simple_echo", test_rawpipe_simple_echo, false);
+	add_raw_test(suite, "smb1_echo_large_message", test_rawpipe_large_message, false);
+	/* SMB2 tests */
+	add_raw_test(suite, "smb2_simple_echo", test_rawpipe_simple_echo, true);
+	add_raw_test(suite, "smb2_echo_large_message", test_rawpipe_large_message, true);
+	return suite;
+}
diff --git a/source4/torture/wscript_build b/source4/torture/wscript_build
index 382e2c6..e7797f2 100755
--- a/source4/torture/wscript_build
+++ b/source4/torture/wscript_build
@@ -19,7 +19,7 @@ bld.SAMBA_MODULE('TORTURE_BASIC',
 
 
 bld.SAMBA_MODULE('TORTURE_RAW',
-	source='raw/qfsinfo.c raw/qfileinfo.c raw/setfileinfo.c raw/search.c raw/close.c raw/open.c raw/mkdir.c raw/oplock.c raw/notify.c raw/mux.c raw/ioctl.c raw/chkpath.c raw/unlink.c raw/read.c raw/context.c raw/session.c raw/write.c raw/lock.c raw/pingpong.c raw/lockbench.c raw/lookuprate.c raw/tconrate.c raw/openbench.c raw/rename.c raw/eas.c raw/streams.c raw/acls.c raw/seek.c raw/samba3hide.c raw/samba3misc.c raw/composite.c raw/raw.c raw/offline.c',
+	source='raw/qfsinfo.c raw/qfileinfo.c raw/setfileinfo.c raw/search.c raw/close.c raw/open.c raw/mkdir.c raw/oplock.c raw/notify.c raw/mux.c raw/ioctl.c raw/chkpath.c raw/unlink.c raw/read.c raw/context.c raw/session.c raw/write.c raw/lock.c raw/pingpong.c raw/lockbench.c raw/lookuprate.c raw/tconrate.c raw/openbench.c raw/rename.c raw/eas.c raw/streams.c raw/acls.c raw/seek.c raw/samba3hide.c raw/samba3misc.c raw/composite.c raw/raw.c raw/offline.c raw/rawpipe.c',
 	autoproto='raw/proto.h',
 	subsystem='smbtorture',
 	init_function='torture_raw_init',
-- 
2.1.4


From 6cd1d5851a6c42e1a0f59dc28c376583697e2434 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 29 Jun 2016 12:47:18 +0100
Subject: [PATCH 05/26] 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 <noel.power at suse.com>
---
 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 a28669a..b4d3621 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.1.4


From 0128dd471e96f5ea3877a74a892f078a79ef6674 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 29 Jun 2016 18:01:43 +0100
Subject: [PATCH 06/26] s4/torture: Use common custom dcerpc_binding_handle
 code for rawpipe test.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source4/torture/raw/rawpipe.c | 375 +-----------------------------------------
 1 file changed, 2 insertions(+), 373 deletions(-)

diff --git a/source4/torture/raw/rawpipe.c b/source4/torture/raw/rawpipe.c
index e15df3e..28614fc 100644
--- a/source4/torture/raw/rawpipe.c
+++ b/source4/torture/raw/rawpipe.c
@@ -38,379 +38,6 @@
 #include "util/tevent_ntstatus.h"
 #include "param/param.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,
-};
-
-static 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;
-}
-
 struct rawpipe_test_data
 {
 	struct dcerpc_pipe *p;
@@ -660,11 +287,13 @@ static void add_raw_test(struct torture_suite *suite,
 struct torture_suite *torture_raw_rawpipe(TALLOC_CTX *mem_ctx)
 {
 	struct torture_suite *suite = torture_suite_create(mem_ctx, "rawpipe");
+#ifdef DEVELOPER
 	/* SMB1 tests */
 	add_raw_test(suite, "smb1_simple_echo", test_rawpipe_simple_echo, false);
 	add_raw_test(suite, "smb1_echo_large_message", test_rawpipe_large_message, false);
 	/* SMB2 tests */
 	add_raw_test(suite, "smb2_simple_echo", test_rawpipe_simple_echo, true);
 	add_raw_test(suite, "smb2_echo_large_message", test_rawpipe_large_message, true);
+#endif
 	return suite;
 }
-- 
2.1.4


From 0d91e6e743a87e1e9e3fca82639f8cbf2d2bb53a Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 5 Jun 2014 10:52:54 +0100
Subject: [PATCH 07/26] 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 <noel.power at suse.com>
---
 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.1.4


From 92585ac0e6a5d8c5c889d9b2194c10deeb122015 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 24 May 2016 14:41:03 +0100
Subject: [PATCH 08/26] s4/torture: Adjust torture test(s) for new
 tstream_smbXcli_np_set_max_data func.

Adjust tests to ensure tstream_smbXcli_np_set_max_data works as expected.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source4/torture/raw/rawpipe.c | 69 +++++++++++++++++++++++++++++++++----------
 1 file changed, 53 insertions(+), 16 deletions(-)

diff --git a/source4/torture/raw/rawpipe.c b/source4/torture/raw/rawpipe.c
index 28614fc..edd9e89 100644
--- a/source4/torture/raw/rawpipe.c
+++ b/source4/torture/raw/rawpipe.c
@@ -147,18 +147,9 @@ static bool test_rawpipe_simple_echo(struct torture_context *tctx,
 	return ret;
 }
 
-/*
- * The idea here is to send a large message size that exceeds the normal
- * hardcoded Max Ioctl hard coded limit of 4280 bytes, this will generate
- * will result in a BUFFER_OVERFLOW error at the SMB layer in the response
- * from the server [1].
- *
- * [1] It seems we don't see the SMB BUFFER_OVERFLOW status at this layer
- *     however we can detect that the message is clipped to the current
- *     limit of 4280 bytes.
- */
-static bool test_rawpipe_large_message(struct torture_context *tctx,
-				     const void *data)
+static bool test_rawpipe_large_message_impl(struct torture_context *tctx,
+					    const void *data,
+					    bool increase_max_ioctl)
 {
 	NTSTATUS status;
 	bool ret = true;
@@ -170,20 +161,64 @@ static bool test_rawpipe_large_message(struct torture_context *tctx,
 	struct rawpipe_test_data *test_data =
 			talloc_get_type(data,
 					struct rawpipe_test_data);
+	struct tstream_context *stream = NULL;
 
 	TALLOC_CTX *mem_ctx = talloc_init("test_rawpipe_large_message");
 	in.data = talloc_array(mem_ctx, uint8_t, strlen(test_message) + 1);
 	in.length = talloc_array_length(in.data);
 	memcpy(in.data, test_message, in.length);
 
+	if (increase_max_ioctl) {
+		stream = test_data->p->conn->transport.stream;
+		tstream_smbXcli_np_set_max_data(stream, expected);
+	}
 	status = write_something(tctx, test_data->h, &in, &out);
 	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "failed to write to pipe\n");
-	ret = (out.length != expected) && (out.length == limit);
-	torture_comment(tctx, "test_rawpipe_large_message test was %s (received %d bytes expected %d)\n", ret ? "successful" : "unsuccsessful", (uint32_t)out.length, limit);
+	if (increase_max_ioctl) {
+		ret = out.length == expected;
+	} else {
+		ret = (out.length != expected) && (out.length == limit);
+	}
+	torture_comment(tctx, "test_rawpipe_large_message test was %s (received %d bytes expected %d)\n", ret ? "successful" : "unsuccsessful", (uint32_t)out.length, increase_max_ioctl ? expected : limit);
 done:
 	return ret;
 }
 
+/*
+ * The idea here is to send a large message size that exceeds the normal
+ * hardcoded Max Ioctl hard coded limit of 4280 bytes, this will generate
+ * will result in a BUFFER_OVERFLOW error at the SMB layer in the response
+ * from the server [1].
+ *
+ * [1] It seems we don't see the SMB BUFFER_OVERFLOW status at this layer
+ *     however we can detect that the message is clipped to the current
+ *     limit of 4280 bytes.
+ */
+static bool test_rawpipe_large_message_clipped(struct torture_context *tctx,
+					       const void *data)
+{
+	return test_rawpipe_large_message_impl(tctx, data, false);
+}
+
+/*
+ * The idea here is to send a large message size that exceeds the normal
+ * hardcoded Max Ioctl hard coded limit of 4280 bytes, this would normally
+ * result in a BUFFER_OVERFLOW error at the SMB layer in the response
+ * from the server. However, now we test 'tstream_smbXcli_np_set_max_data'
+ * a new function which allows us to adjust the size limit so the message
+ * should no longer be clipped.
+ *
+ * [1] It seems we don't see the SMB BUFFER_OVERFLOW status at this layer
+ *     however we can detect that the message is clipped to the current
+ *     limit of 4280 bytes.
+ */
+
+static bool test_rawpipe_large_message_newmax(struct torture_context *tctx,
+					      const void *data)
+{
+	return test_rawpipe_large_message_impl(tctx, data, true);
+}
+
 static bool raw_smb1_setup(struct torture_context *tctx,
 			  void **ppdata)
 {
@@ -290,10 +325,12 @@ struct torture_suite *torture_raw_rawpipe(TALLOC_CTX *mem_ctx)
 #ifdef DEVELOPER
 	/* SMB1 tests */
 	add_raw_test(suite, "smb1_simple_echo", test_rawpipe_simple_echo, false);
-	add_raw_test(suite, "smb1_echo_large_message", test_rawpipe_large_message, false);
+	add_raw_test(suite, "smb1_echo_large_message_clipped", test_rawpipe_large_message_clipped, false);
+	add_raw_test(suite, "smb1_echo_large_message_newmax", test_rawpipe_large_message_newmax, false);
 	/* SMB2 tests */
 	add_raw_test(suite, "smb2_simple_echo", test_rawpipe_simple_echo, true);
-	add_raw_test(suite, "smb2_echo_large_message", test_rawpipe_large_message, true);
+	add_raw_test(suite, "smb2_echo_large_message_clipped", test_rawpipe_large_message_clipped, true);
+	add_raw_test(suite, "smb2_echo_large_message_newmax", test_rawpipe_large_message_newmax, true);
 #endif
 	return suite;
 }
-- 
2.1.4


From 2f1cb4618adc07b16bfef33f17a73fea87eaa006 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 2 Dec 2014 17:26:41 +0000
Subject: [PATCH 09/26] 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 <noel.power at suse.com>
---
 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 d65cbe7..2aaf525 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.1.4


From 69b08fbcdb98f9443c136225f90abe8e07dc3e45 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Mon, 25 Aug 2014 11:53:30 +0100
Subject: [PATCH 10/26] 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 <noel.power at suse.com>
---
 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.1.4


From 1856c7a4fb4d060852eb1583ff06c2fbc7261c45 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 3 Dec 2014 10:56:18 +0000
Subject: [PATCH 11/26] 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 <noel.power at suse.com>
---
 librpc/idl/wscript_build |    2 +-
 librpc/idl/wsp.idl       | 1344 ++++++++++++++++++++++++++++++++++++++++++++++
 librpc/idl/wsp_data.idl  |  298 ++++++++++
 librpc/rpc/wsp_helper.c  |   52 ++
 librpc/rpc/wsp_helper.h  |   31 ++
 librpc/wscript_build     |   12 +-
 6 files changed, 1737 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 47acc0d..0699d6f 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..86cbf79
--- /dev/null
+++ b/librpc/idl/wsp.idl
@@ -0,0 +1,1344 @@
+#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;
+
+	/*
+	 * variants stored in the ROWS buffer don't seem to quite
+	 * agree with the documentation, seems VT_VARIANT types (on 32bit)
+	 * systems consistently have a size of 0x10, additionally the layout
+	 * seems to be more like as follows
+	 */
+
+	typedef [public] struct {
+		uint32 count;
+		uint32 offsets_addr;
+	} row_offset32_vector;
+
+	typedef [public,nodiscriminant,switch_type(uint16)] union {
+		/*
+		 * variable sized types, only have 'real-life' examples of
+		 * VT_LPWSTR sofar, so the rest are guesses
+		 */
+		[case (VT_LPWSTR)] uint32 offset;
+		[case (VT_COMPRESSED_LPWSTR)] uint32 offset;
+		[case (VT_BSTR)] uint32 offset;
+		[case (VT_BLOB)] uint32 offset;
+		[case (VT_BLOB_OBJECT)] uint32 offset;
+		[case (VT_VARIANT)] uint32 offset;
+		/*
+		 * fixed size types, this is again more or less a guess
+		 * although have real-life examples of (VT_LPWSTR | VT_VECTOR)
+		 * and VT_FILETIME that can be verified.
+		 * Also examples of VT_BOOL,VT_I4 VT_LPWSTR, VT_R8, VT_UI8
+		 * but can't really verify the values extracted. Assuming
+		 * other fixed types behave similarly.
+		 */
+		[case(VT_I1)] int8 i1_value;
+		//[case(VT_I1 | VT_ARRAY)]  /*currently not handled*/
+		[case(VT_I1 | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_UI1)] uint8 ui1_value;
+		//[case(VT_UI1 | VT_ARRAY)] /*currently not handled*/
+		[case(VT_UI1 | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_I2)] int16 i2_value;
+		//[case(VT_I2 | VT_ARRAY)] /*currently not handled*/
+		[case(VT_I2 | VT_VECTOR)]  row_offset32_vector offset_vec;
+
+		[case(VT_UI2)] uint16 ui2_value;
+		//[case(VT_UI2 | VT_ARRAY)] /*currently not handled*/
+		[case(VT_UI2 | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_BOOL)] uint16 bool_value;
+		//[case(VT_BOOL | VT_ARRAY)]/*currently not handled*/
+		[case(VT_BOOL | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_I4)] int32 i4_value;
+		[case(VT_I4 | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_I4 | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_UI4)] uint32 ui4_value;
+		[case(VT_UI4 | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_UI4 | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_R4)] uint32 r4_value;
+		[case(VT_R4 | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_R4 | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_INT)] int32 int_value;
+		//[case(VT_INT | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_UINT)] uint32 uint_value;
+		//[case(VT_UINT | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_ERROR)] uint32 error_value;
+		[case(VT_ERROR | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_ERROR | VT_ARRAY)] /*currently not handled*/
+
+		/* can't seem to use unsigned hyper with pidl :/ */
+		[case(VT_I8)] wsp_hyper i8_value;
+		[case(VT_I8 | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_UI8)] wsp_hyper ui8_value;
+		[case(VT_UI8 | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_R8)] wsp_hyper r8_value;
+		[case(VT_R8 | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_R8 | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_CY)] wsp_hyper cy_value;
+		[case(VT_CY | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_CY | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_DATE)] wsp_hyper date_value;
+		[case(VT_DATE | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_DATE| VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_FILETIME)] wsp_hyper filetime_value;
+		[case(VT_FILETIME | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_BSTR | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_BSTR | VT_ARRAY)] /*currently not handled*/
+
+		[case(VT_LPWSTR | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_COMPRESSED_LPWSTR | VT_VECTOR)] row_offset32_vector offset_vec;
+		/* this one could be wrong and might very well be handled like a variable size */
+		[case(VT_DECIMAL)] vt_decimal decimal_value;
+		[case(VT_DECIMAL | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		/* this one could be wrong and might very well be handled like a variable size */
+		[case(VT_CLSID)] GUID clid_value;
+		[case(VT_CLSID | VT_VECTOR)] row_offset32_vector offset_vec;
+
+		[case(VT_NULL)];
+		[case(VT_EMPTY)];
+
+		[case(VT_VARIANT | VT_VECTOR)] row_offset32_vector offset_vec;
+		//[case(VT_VARIANT | VT_ARRAY)] /*currently not handled*/
+
+	} row_variant_32;
+
+	typedef [public] struct {
+		uint16 vtype;
+		uint16 reserved1;
+		uint32 reserved2;
+		[switch_is(vtype)] row_variant_32 content;
+	} wsp_crowvariant32_guess;
+
+	typedef [public] struct {
+		uint16 vtype;
+		uint16 reserved1;
+		uint32 reserved2;
+		wsp_hyper offset;
+	} wsp_crowvariant64;
+
+	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..4547a3c
--- /dev/null
+++ b/librpc/idl/wsp_data.idl
@@ -0,0 +1,298 @@
+#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;
+}
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 <http://www.gnu.org/licenses/>.
+ */
+#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 <http://www.gnu.org/licenses/>.
+ */
+#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 eceb0c4..657b0bc 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -325,6 +325,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'
@@ -675,7 +685,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.1.4


From fc4fd4538bf7732e41908c686c61f95d907ce8b6 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 26 Jul 2016 11:47:43 +0100
Subject: [PATCH 12/26] 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 497b673..ff45078 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -75,6 +75,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
@@ -1633,6 +1634,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.1.4


From f1195ece1e00ae5b484ea171fa3cce732fb84201 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 26 Jul 2016 12:23:38 +0100
Subject: [PATCH 13/26] librpc/rpc: Add windows propertyset info and associated
 accessor and helper api.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 librpc/rpc/wsp_helper.c | 1608 +++++++++++++++++++++++++++++++++++++++++++++++
 librpc/rpc/wsp_helper.h |   21 +-
 2 files changed, 1628 insertions(+), 1 deletion(-)

diff --git a/librpc/rpc/wsp_helper.c b/librpc/rpc/wsp_helper.c
index 3d88a88..ce1222e 100644
--- a/librpc/rpc/wsp_helper.c
+++ b/librpc/rpc/wsp_helper.c
@@ -50,3 +50,1611 @@ 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_prop_info(const char *prop_name)
+{
+	const struct full_propset_info *result = NULL;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_propertyset); i++) {
+		const struct full_propset_info *item =
+				full_propertyset[i].prop_info;
+		while (item->id) {
+			if (strequal(prop_name, item->name)) {
+				result = item;
+				break;
+			}
+			item++;
+		}
+	}
+	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;
+}
+
+
diff --git a/librpc/rpc/wsp_helper.h b/librpc/rpc/wsp_helper.h
index 76b5156..65369a1 100644
--- a/librpc/rpc/wsp_helper.h
+++ b/librpc/rpc/wsp_helper.h
@@ -22,10 +22,29 @@
 #define __LIBRPC_WSP_HELPER_H__
 
 struct safearraybound;
+struct wsp_cfullpropspec;
+struct wsp_cpmgetrowsin;
+struct wsp_cpmgetrowsout;
+struct wsp_cpmsetbindingsin;
 struct wsp_hyper;
 
 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 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);
 #endif // __LIBRPC_WSP_HELPER_H__
-- 
2.1.4


From 9e16b676b4657a72f1fbb0ce376241d157088cac Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 29 Jun 2016 11:29:54 +0100
Subject: [PATCH 14/26] s4/libcli: Add simple client api for wsp client code.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source4/libcli/wscript_build |    5 +
 source4/libcli/wsp/wsp_cli.c | 1727 ++++++++++++++++++++++++++++++++++++++++++
 source4/libcli/wsp/wsp_cli.h |  111 +++
 3 files changed, 1843 insertions(+)
 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..972a28c 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',
+	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_cli.c b/source4/libcli/wsp/wsp_cli.c
new file mode 100644
index 0000000..60c3fd1
--- /dev/null
+++ b/source4/libcli/wsp/wsp_cli.c
@@ -0,0 +1,1727 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 "smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/rpc/dcerpc_raw.h"
+
+#define MSG_HDR_SIZE 16
+
+static const uint32_t CLIENTVERSION = 0x00000109;
+
+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 void set_fullpropspec(TALLOC_CTX *ctx, struct wsp_cfullpropspec *prop,
+			     const char* guid_str, uint32_t kind,
+			     uint32_t prop_id, const char* propname)
+{
+	GUID_from_string(guid_str, &prop->guidpropset);
+	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_id;
+	}
+}
+
+static void set_ctablecolumn(TALLOC_CTX *ctx, struct wsp_ctablecolumn *tablecol,
+			const char* guid_str, uint32_t kind,
+			uint32_t prop_id, const char* propname,
+			uint32_t vtype, bool aggregateused,
+			uint8_t aggregatevalue, bool valueused,
+			uint16_t valueoffset, uint16_t valuesize,
+			bool statusused, uint16_t statusoffset,
+			bool lengthused, uint16_t lengthoffset)
+{
+	set_fullpropspec(ctx, &tablecol->propspec, guid_str, kind, prop_id,
+			 propname);
+
+	tablecol->vtype = vtype;
+
+	if (aggregateused) {
+		tablecol->aggregateused = 1;
+		tablecol->aggregatetype.value = aggregatevalue;
+	}
+
+	if (valueused) {
+		tablecol->valueused = 1;
+		tablecol->valueoffset.value = valueoffset;
+		tablecol->valuesize.value = valuesize;
+	}
+	if (statusused) {
+		tablecol->statusused = 1;
+		tablecol->statusoffset.value = statusoffset;
+	}
+	if (lengthused) {
+		tablecol->lengthused = 1;
+		tablecol->lengthoffset.value = lengthoffset;
+	}
+}
+
+void create_setbindings_request(TALLOC_CTX * ctx,
+				struct wsp_request* request,
+				uint32_t cursor)
+{
+	struct wsp_cpmsetbindingsin *bindingsin = &request->message.cpmsetbindings;
+	request->header.msg = CPMSETBINDINGSIN;
+	bindingsin->hcursor = cursor;
+	bindingsin->brow = 0x00000030;
+	bindingsin->ccolumns = 2;
+
+	bindingsin->acolumns = talloc_zero_array(ctx, struct wsp_ctablecolumn,
+						 bindingsin->ccolumns);
+
+	set_ctablecolumn(ctx, &bindingsin->acolumns[0],
+			"49691c90-7e17-101a-a91c-08002b2ecda9", PRSPEC_PROPID,
+			0x00000009, NULL, VT_VARIANT, true, 0, true, 8, 16,
+			true, 2, true, 4);
+	set_ctablecolumn(ctx, &bindingsin->acolumns[1],
+			"6b8da074-3b5c-43bc-886f-0a2cdce00b6f", PRSPEC_PROPID,
+			0x00000064, NULL, VT_VARIANT, true, 0, true, 24, 16,
+			true, 3, true, 40);
+}
+
+static 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);
+}
+
+static void set_variant_i4(TALLOC_CTX *ctx,
+			   struct wsp_cbasestoragevariant *vvalue,
+			   uint32_t val)
+{
+	vvalue->vtype = VT_I4;
+	vvalue->vvalue.vt_i4 = val;
+}
+
+static 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;
+}
+
+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 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;
+}
+
+static 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);
+}
+
+static 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);
+}
+
+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));
+}
+
+/* create single dim array of vt_i4 */
+static 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);
+}
+
+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);
+
+}
+
+/* create single dim array of bstr */
+static 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;
+	}
+}
+
+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,
+				   uint32_t cursor,
+				   uint32_t bookmark,
+				   uint32_t skip,
+				   uint32_t rows,
+				   uint32_t cbreserved,
+				   uint32_t ulclientbase,
+				   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 = ulclientbase;
+	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 void extract_crowvariant_fixed(struct wsp_crowvariant32_guess *colval,
+				   struct wsp_cbasestoragevariant *outval)
+{
+	switch (colval->vtype) {
+		case VT_I1:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_i1 = colval->content.i1_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.i1_value);
+			break;
+		case VT_UI1:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_ui1 = colval->content.ui1_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.ui1_value);
+			break;
+		case VT_I2:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_i2 = colval->content.i2_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.i2_value);
+			break;
+		case VT_UI2:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_ui2 = colval->content.ui2_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.ui2_value);
+			break;
+		case VT_I4:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_i4 = colval->content.i4_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.i4_value);
+			break;
+		case VT_UI4:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_ui4 = colval->content.ui4_value;
+			DBG_INFO("\tval 0x%x\n", colval->content.ui4_value);
+			break;
+		case VT_BOOL:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_bool = colval->content.bool_value;
+			DBG_INFO("\tval %s\n",colval->content.bool_value == 0xFFF ? "true" : "false" );
+			break;
+		case VT_UI8:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_ui8 = colval->content.ui8_value;
+			DBG_INFO("\tval hi 0x%x lo 0x%d\n", colval->content.ui8_value.hi, colval->content.ui8_value.lo);
+			break;
+		case VT_R8:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_r8 = colval->content.r8_value;
+			DBG_INFO("\tval hi 0x%x lo 0x%d\n", colval->content.r8_value.hi, colval->content.r8_value.lo);
+			break;
+		case VT_I8:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_i8 = colval->content.i8_value;
+			DBG_INFO("\tval hi 0x%x lo 0x%d\n", colval->content.i8_value.hi, colval->content.i8_value.lo);
+			break;
+		case VT_FILETIME:
+			outval->vtype = colval->vtype;
+			outval->vvalue.vt_filetime = colval->content.filetime_value;
+			DBG_INFO("\tval hi 0x%x lo 0x%d\n", colval->content.filetime_value.hi, colval->content.filetime_value.lo);
+			break;
+		default:
+			DBG_INFO("#FIXME unsupported type %s\n",
+				get_vtype_name(colval->vtype));
+			break;
+	}
+}
+
+static void extract_crowvariant_fixed_vec_item(TALLOC_CTX *ctx, uint16_t type,
+				       uint32_t offset, DATA_BLOB *rows_buf,
+				       struct wsp_cbasestoragevariant *val)
+{
+	switch (type) {
+		case VT_I1:
+			val->vvalue.vt_i1 = (int8_t)*(offset + rows_buf->data);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i1);
+			break;
+		case VT_UI1:
+			val->vvalue.vt_ui1 = (uint8_t)*(offset + rows_buf->data);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_ui1);
+			break;
+		case VT_I2:
+			val->vvalue.vt_i2 = SVALS(rows_buf->data, offset);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i2);
+			break;
+		case VT_UI2:
+			val->vvalue.vt_ui2 = SVALS(rows_buf->data, offset);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i2);
+			break;
+		case VT_BOOL:
+			val->vvalue.vt_bool =  SVAL(rows_buf->data, offset);
+			val->vtype = type;
+			DBG_INFO("\tval %s (0x%x)\n",val->vvalue.vt_bool == 0xFFFF ? "true" : "false", val->vvalue.vt_bool );
+			break;
+		case VT_I4:
+			val->vvalue.vt_i4 = IVALS(rows_buf->data, offset);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_i4);
+			break;
+		case VT_R4:
+		case VT_UINT:
+		case VT_UI4:
+		case VT_ERROR:
+			val->vvalue.vt_ui4 = IVALS(rows_buf->data, offset);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%x\n", val->vvalue.vt_ui4);
+			break;
+		case VT_DATE:
+		case VT_R8:
+		case VT_UI8:
+		case VT_FILETIME: {
+			struct wsp_hyper *p_hyper = &val->vvalue.vt_ui8;
+			uint64_t hyper = BVAL(rows_buf->data, offset);
+			uint64_to_wsp_hyper(hyper, p_hyper);
+			val->vtype = type;
+			DBG_INFO("\tval 0x%" PRIx64 "\n", hyper);
+			break;
+		}
+		case VT_DECIMAL: /* FIXME needs implementation */
+		default:
+			DBG_ERR("#FIXME Unhandled type %d\n", type);
+			break;
+	}
+}
+
+
+static void dump_rowbuf_variable_type(TALLOC_CTX *ctx, uint16_t type, uint32_t offset,
+				      DATA_BLOB *rows_buf, uint32_t len,
+				      struct wsp_cbasestoragevariant  *val)
+{
+	enum ndr_err_code err;
+	struct ndr_pull *ndr_pull;
+	int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+	DATA_BLOB variant_blob = data_blob_null;
+	variant_blob.data = rows_buf->data + offset;
+	variant_blob.length = len;
+	ndr_pull = ndr_pull_init_blob(&variant_blob, ctx);
+	switch (type) {
+		case VT_LPWSTR: {
+			const char *string;
+			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 );
+			} else {
+				DBG_INFO("\tstring val ->>>%s<<<-\n", string );
+				val->vtype = type;
+				val->vvalue.vt_lpwstr.value = string;
+			}
+			break;
+		}
+		default:
+			DBG_ERR("#FIXME Unhandled variant type %s\n", get_vtype_name(type));
+			break;
+	}
+}
+
+static void dump_crowvariant32(TALLOC_CTX *ctx,
+			       struct wsp_crowvariant32_guess *colval,
+			       uint32_t offset,
+			       DATA_BLOB *rows_buf, uint32_t len,
+			       struct wsp_cbasestoragevariant *val)
+{
+	int count;
+	uint32_t addr;
+	switch(colval->vtype) {
+		case VT_LPWSTR:
+		case VT_COMPRESSED_LPWSTR:
+		case VT_BSTR:
+		case VT_BLOB:
+		case VT_BLOB_OBJECT:
+		case VT_VARIANT:
+			addr = colval->content.offset - offset;
+			dump_rowbuf_variable_type(ctx, colval->vtype, addr,
+						  rows_buf, len, val);
+			break;
+		case VT_I1:
+		case VT_UI1:
+		case VT_I2:
+		case VT_UI2:
+		case VT_BOOL:
+		case VT_I4:
+		case VT_UI4:
+		case VT_R4:
+		case VT_INT:
+		case VT_UINT:
+		case VT_ERROR:
+		case VT_I8:
+		case VT_UI8:
+		case VT_R8:
+		case VT_CY:
+		case VT_DATE:
+		case VT_FILETIME:
+		case VT_DECIMAL: /* #TODO confirm this one */
+		case VT_CLSID: /* #TODO confirm this one */
+			extract_crowvariant_fixed(colval, val);
+			break;
+		case VT_I1 | VT_VECTOR:
+		case VT_I2 | VT_VECTOR:
+		case VT_UI2 | VT_VECTOR:
+		case VT_BOOL | VT_VECTOR:
+		case VT_I4 | VT_VECTOR:
+		case VT_UI4 | VT_VECTOR:
+		case VT_R4 | VT_VECTOR:
+		case VT_ERROR | VT_VECTOR:
+		case VT_I8 | VT_VECTOR:
+		case VT_UI8 | VT_VECTOR:
+		case VT_CY | VT_VECTOR:
+		case VT_DATE | VT_VECTOR:
+		case VT_FILETIME | VT_VECTOR:
+		case VT_BSTR | VT_VECTOR:
+		case VT_LPWSTR | VT_VECTOR:
+		case VT_COMPRESSED_LPWSTR | VT_VECTOR:
+		case VT_DECIMAL | VT_VECTOR:
+		case VT_CLSID | VT_VECTOR:
+		case VT_VARIANT | VT_VECTOR:
+			addr = colval->content.offset_vec.offsets_addr - offset;
+			DBG_INFO("\tunadjusted offset 0x%x adjusted 0x%x\n", colval->content.offset_vec.offsets_addr, addr);
+			for (count = 0; count < colval->content.offset_vec.count;
+			     count++) {
+				uint32_t vec_item_offset = IVAL(rows_buf->data, addr);
+				vec_item_offset = vec_item_offset - offset;
+				if (is_variable_size(colval->vtype & ~VT_VECTOR)) {
+					dump_rowbuf_variable_type(ctx,
+						colval->vtype & ~VT_VECTOR, vec_item_offset,
+						rows_buf, len, val);
+
+				} else {
+					extract_crowvariant_fixed_vec_item(ctx,
+						colval->vtype, vec_item_offset,
+						rows_buf, val);
+				}
+				addr += sizeof(vec_item_offset);
+			}
+	};
+}
+
+static enum ndr_err_code process_columns(TALLOC_CTX *ctx,
+					 uint32_t cbreserved,
+					 uint32_t ulclientbase,
+					 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;
+	struct ndr_pull *ndr_pull;
+	int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+	uint32_t nrow_offset = nrow * bindingin->brow;
+
+	/* process columns */
+	for (i = 0; i < bindingin->ccolumns; i++) {
+		struct wsp_ctablecolumn *tab_col = &bindingin->acolumns[i];
+		struct wsp_crowvariant32_guess colval;
+		DATA_BLOB col_val_blob = data_blob_null;
+		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) {
+			DBG_INFO("\n\tstatusoffset 0x%x status is %s",
+			      tab_col->statusoffset.value, get_store_status((uint8_t)*(rows_buf->data + nrow_offset + tab_col->statusoffset.value)));
+		}
+		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) {
+			int32_t offset = ulclientbase + cbreserved;
+			int32_t len = 0;
+			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));
+
+
+			col_val_blob.data = rows_buf->data + tab_col->valueoffset.value + nrow_offset;
+			col_val_blob.length = tab_col->valuesize.value;
+
+			if (tab_col->vtype != VT_VARIANT) {
+				if (is_variable_size(tab_col->vtype)) {
+					struct wsp_crowvariant32 var_col;
+					ndr_pull = ndr_pull_init_blob(&col_val_blob, ctx);
+					err = ndr_pull_wsp_crowvariant32(ndr_pull, ndr_flags, &var_col);
+					if (err) {
+						DBG_ERR("!!! failed to pull row (guess) variant for col data\n");
+						goto out;
+					}
+					if (var_col.vtype != tab_col->vtype) {
+						DBG_ERR("!!! column type expected 0x%x doesn't match 0x%x in row buffer\n", tab_col->vtype, var_col.vtype);
+						goto out;
+					}
+					dump_rowbuf_variable_type(ctx,
+						var_col.vtype,
+						var_col.offset - offset,
+						rows_buf,
+						tab_col->lengthoffset.value,
+						&cols[i]);
+				} else {
+					extract_crowvariant_fixed_vec_item(ctx,
+						tab_col->vtype,
+						tab_col->valueoffset.value + nrow_offset,
+						rows_buf,
+						&cols[i]);
+				}
+				continue;
+			}
+			ndr_pull = ndr_pull_init_blob(&col_val_blob, ctx);
+			err = ndr_pull_wsp_crowvariant32_guess(ndr_pull, ndr_flags, &colval);
+			if (err) {
+				DBG_ERR("!!! failed to pull row (guess) variant for col data\n");
+				goto out;
+			}
+			DBG_INFO("\n");
+			DBG_INFO("\tcrowvariant contains %s \n",
+				get_vtype_name(colval.vtype));
+			if (tab_col->lengthused) {
+				/* it seems the size is what's at
+				 * lengthoffset - tab_col->valuesize.value
+				 */
+				len = IVAL(rows_buf->data, nrow_offset + tab_col->lengthoffset.value);
+				len = len - tab_col->valuesize.value;
+			}
+
+			dump_crowvariant32(ctx, &colval, offset, rows_buf, len, &cols[i]);
+		}
+	}
+out:
+	return err;
+}
+
+enum ndr_err_code extract_rowsarray(
+			TALLOC_CTX * ctx,
+			DATA_BLOB *rows_buf,
+			struct wsp_cpmsetbindingsin *bindingsin,
+			uint32_t cbreserved,
+			uint32_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,
+				      cbreserved,
+				      ulclientbase,
+				      bindingsin,
+				      rows_buf,
+				      i,
+				      cols);
+		if (err) {
+			break;
+		}
+		rowsarray[i] = cols;
+	}
+	return err;
+}
+
+void create_querysearch_request(TALLOC_CTX * ctx,
+				struct wsp_request* request,
+				const char *location,
+				const char *phrase,
+				const char *kind)
+{
+	struct wsp_cpmcreatequeryin *createquery = &request->message.cpmcreatequery;
+	struct wsp_crestriction *crestriction;
+	struct wsp_cnoderestriction *restriction_node;
+
+	uint32_t indices[] = {0, 1};
+
+	bool has_kind;
+	enum search_kind search_kind = get_kind(kind);
+	int i;
+
+	if (search_kind == None || search_kind == Unknown) {
+		if (search_kind == Unknown) {
+			DBG_ERR("unknown kind %s\n", kind);
+		}
+		has_kind = false;
+	} else {
+		has_kind = true;
+	}
+
+	request->header.msg = CPMCREATEQUERY;
+	createquery->ccolumnsetpresent = 1;
+	createquery->columnset.columnset.count = ARRAY_SIZE(indices);
+	fill_uint32_vec(ctx, &createquery->columnset.columnset.indexes,
+			indices,
+			ARRAY_SIZE(indices));
+
+	createquery->crestrictionpresent = 1;
+	/*
+	 * the restriction array probably should be built from some sort
+	 * of textual representation of the expression tree ??. There must
+	 * be some way to better build this.
+	 */
+	createquery->restrictionarray.restrictionarray.count = 1;
+	createquery->restrictionarray.restrictionarray.ispresent = 1;
+	create_restriction_array(ctx,
+				 &createquery->restrictionarray.restrictionarray.restrictions,
+				 createquery->restrictionarray.restrictionarray.count);
+	crestriction = &createquery->restrictionarray.restrictionarray.restrictions[0];
+
+	crestriction->ultype = RTAND;
+	crestriction->weight = 1000;
+	restriction_node = &crestriction->restriction.cnoderestriction;
+	create_noderestriction(ctx, restriction_node,
+			       2);
+
+	crestriction = &restriction_node->panode[1];
+	/* panode[1] RTNot with CPropertyRestriction */
+	crestriction->ultype = RTNOT;
+	crestriction->weight = 1000;
+	/* #FIXME this is ugly, need to adjust the idl so this reads better */
+	crestriction->restriction.restriction.restriction = talloc_zero(ctx,
+						struct wsp_crestriction);
+
+	crestriction = crestriction->restriction.restriction.restriction;
+	crestriction->ultype = RTPROPERTY;
+	crestriction->weight  = 1000;
+	crestriction->restriction.cpropertyrestriction.relop = PREQ;
+	set_fullpropspec(ctx,
+			&crestriction->restriction.cpropertyrestriction.property,
+			"de35258c-c695-4cbc-b982-38b0ad24ced0", PRSPEC_PROPID,
+			0x00000002, NULL);
+	set_variant_lpwstr(ctx,
+			   &crestriction->restriction.cpropertyrestriction.prval,
+			   "true");
+	crestriction->restriction.cpropertyrestriction.lcid = 0x00001809;
+
+
+	/* panode[0] RTAnd */
+	crestriction = &restriction_node->panode[0];
+	crestriction->ultype = RTAND;
+	crestriction->weight = 1000;
+	restriction_node = &crestriction->restriction.cnoderestriction;
+	create_noderestriction(ctx, restriction_node,
+			       2);
+
+	/* RTNot with CCoercionRestriction (with ContentRestriction)*/
+	crestriction = &restriction_node->panode[1];
+	crestriction->ultype = RTNOT;
+	crestriction->weight = 1000;
+	/* #FIXME this is ugly, need to adjust the idl so this reads better */
+	crestriction->restriction.restriction.restriction = talloc_zero(ctx,
+						struct wsp_crestriction);
+
+	crestriction = crestriction->restriction.restriction.restriction;
+	crestriction->ultype = RTCOERCE_ABSOLUTE;
+	crestriction->weight = 1000;
+	/* #FIXME float support ? */
+	crestriction->restriction.ccoercionrestriction_abs.ffvalue = 0x447a0000;
+	crestriction->restriction.ccoercionrestriction_abs.childres =
+			talloc_zero(ctx, struct wsp_crestriction);
+	/* CContentRestriction (in CCoercionRestriction) */
+	crestriction = crestriction->restriction.ccoercionrestriction_abs.childres;
+	crestriction->ultype = RTCONTENT;
+	crestriction->weight = 1000;
+
+	set_fullpropspec(ctx,
+			&crestriction->restriction.ccontentrestriction.property,
+			"d6942081-d53b-443d-ad47-5e059d9cd27a", PRSPEC_PROPID,
+			0x00000002, NULL);
+	crestriction->restriction.ccontentrestriction.pwcsphrase =
+			talloc_strdup(ctx, "hidden");
+	crestriction->restriction.ccontentrestriction.cc =
+		strlen(crestriction->restriction.ccontentrestriction.pwcsphrase);
+	crestriction->restriction.ccontentrestriction.lcid = 0x00001809;
+	crestriction->restriction.ccontentrestriction.ulgeneratemethod = 0x00000000;
+
+
+	crestriction = &restriction_node->panode[0];
+
+	crestriction->ultype = RTAND;
+	crestriction->weight = 1000;
+
+	restriction_node = &crestriction->restriction.cnoderestriction;
+	create_noderestriction(ctx, restriction_node,
+			       2);
+	/* RTProperty */
+	crestriction = &restriction_node->panode[1];
+	crestriction->ultype = RTPROPERTY;
+	crestriction->weight = 1000;
+	crestriction->restriction.cpropertyrestriction.relop = PREQ;
+	set_fullpropspec(ctx,
+			&crestriction->restriction.cpropertyrestriction.property,
+			"b725f130-47ef-101a-a5f1-02608c9eebac", PRSPEC_PROPID,
+			0x00000016, NULL);
+
+	set_variant_lpwstr(ctx,
+			   &crestriction->restriction.cpropertyrestriction.prval,
+			   location);
+	crestriction->restriction.cpropertyrestriction.lcid = 0x00001809;
+
+	if (has_kind) {
+		/* RTAnd */
+		crestriction = &restriction_node->panode[0];
+		if (phrase) {
+			crestriction->ultype = RTAND;
+			crestriction->weight = 1000;
+			restriction_node = &crestriction->restriction.cnoderestriction;
+			create_noderestriction(ctx, restriction_node,
+					       2);
+			/* RTCoerceAbsolute */
+			crestriction = &restriction_node->panode[1];
+		}
+		crestriction->ultype = RTCOERCE_ABSOLUTE;
+		crestriction->weight = 1000;
+		/* #FIXME float support ? */
+		crestriction->restriction.ccoercionrestriction_abs.ffvalue = 0x447a0000;
+		crestriction->restriction.ccoercionrestriction_abs.childres =
+			talloc_zero(ctx, struct wsp_crestriction);
+		/* CContentRestriction (in CCoercionRestriction) */
+		crestriction = crestriction->restriction.ccoercionrestriction_abs.childres;
+		crestriction->ultype = RTCONTENT;
+		crestriction->weight = 1000;
+
+		set_fullpropspec(ctx,
+				&crestriction->restriction.ccontentrestriction.property,
+			"1e3ee840-bc2b-476c-8237-2acd1a839b22", PRSPEC_PROPID,
+			0x00000003, NULL);
+		crestriction->restriction.ccontentrestriction.pwcsphrase =
+			talloc_strdup(ctx, kind);
+		crestriction->restriction.ccontentrestriction.cc =
+			strlen(crestriction->restriction.ccontentrestriction.pwcsphrase);
+		crestriction->restriction.ccontentrestriction.lcid = 0x00001809;
+		crestriction->restriction.ccontentrestriction.ulgeneratemethod = 0x00000000;
+	}
+
+	if (phrase) {
+		/* RTOr */
+		crestriction = &restriction_node->panode[0];
+		crestriction->ultype = RTOR;
+		crestriction->weight = 1000;
+
+		restriction_node = &crestriction->restriction.cnoderestriction;
+		create_noderestriction(ctx, restriction_node,
+				       2);
+
+		/* CCoercionRestriction */
+		crestriction = &restriction_node->panode[0];
+		crestriction->ultype = RTCOERCE_ABSOLUTE;
+		crestriction->weight = 1000;
+		/* #FIXME float support ? */
+		/* 	CContentRestriction (in CCoercionRestriction) */
+		crestriction->restriction.ccoercionrestriction_abs.ffvalue = 0x3f800000;
+		crestriction->restriction.ccoercionrestriction_abs.childres =
+				talloc_zero(ctx, struct wsp_crestriction);
+		crestriction = crestriction->restriction.ccoercionrestriction_abs.childres;
+		crestriction->ultype = RTCONTENT;
+		crestriction->weight = 1000;
+
+		set_fullpropspec(ctx,
+				&crestriction->restriction.ccontentrestriction.property,
+				"49691c90-7e17-101a-a91c-08002b2ecda9", PRSPEC_PROPID,
+				0x00000006, NULL);
+		crestriction->restriction.ccontentrestriction.pwcsphrase =
+				talloc_strdup(ctx, phrase);
+		crestriction->restriction.ccontentrestriction.cc =
+			strlen(crestriction->restriction.ccontentrestriction.pwcsphrase);
+		crestriction->restriction.ccontentrestriction.lcid = 0x00000409;
+		crestriction->restriction.ccontentrestriction.ulgeneratemethod = 0x00000001;
+
+
+		/* RTAnd */
+		crestriction = &restriction_node->panode[1];
+		crestriction->ultype = RTAND;
+		crestriction->weight = 1000;
+
+		restriction_node = &crestriction->restriction.cnoderestriction;
+		create_noderestriction(ctx, restriction_node,
+				       2);
+
+		crestriction = &restriction_node->panode[0];
+		/* panode[0] RTNaturalLanguage */
+		crestriction->ultype = RTNATLANGUAGE;
+		crestriction->weight = 1000;
+		set_fullpropspec(ctx,
+				 &crestriction->restriction.cnatlanguagerestriction.property,
+				 "00000000-0000-0000-0000-000000000000", PRSPEC_LPWSTR,
+				 0x00000000, "52360260");
+
+		crestriction->restriction.cnatlanguagerestriction.pwcsphrase =
+			talloc_strdup(ctx, phrase);
+
+		crestriction->restriction.cnatlanguagerestriction.cc =
+			strlen(crestriction->restriction.cnatlanguagerestriction.pwcsphrase);
+		crestriction->restriction.cnatlanguagerestriction.lcid = 0x00000409;
+
+		crestriction = &restriction_node->panode[1];
+		/* panode[1] RTContent */
+		crestriction->ultype = RTCONTENT;
+		crestriction->weight = 1000;
+		set_fullpropspec(ctx,
+				&crestriction->restriction.ccontentrestriction.property,
+				"49691c90-7e17-101a-a91c-08002b2ecda9", PRSPEC_PROPID,
+				0x00000006, NULL);
+		crestriction->restriction.ccontentrestriction.pwcsphrase =
+				talloc_strdup(ctx, phrase);
+		crestriction->restriction.ccontentrestriction.cc =
+			strlen(crestriction->restriction.ccontentrestriction.pwcsphrase);
+		crestriction->restriction.ccontentrestriction.lcid = 0x00000409;
+		crestriction->restriction.ccontentrestriction.ulgeneratemethod = 0x00000000;
+	}
+
+	createquery->csortsetpresent = 1;
+	if (createquery->csortsetpresent) {
+		struct wsp_csortset *sortset = &createquery->sortset.sortset;
+		struct wsp_csort data[] = {
+			{0x00000001, 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 = 2;
+	createquery->pidmapper.apropspec = talloc_zero_array(ctx,
+						struct wsp_cfullpropspec,
+						createquery->pidmapper.count);
+
+	i = 0;
+	set_fullpropspec(ctx, &createquery->pidmapper.apropspec[i++],
+			 "49691c90-7e17-101a-a91c-08002b2ecda9", PRSPEC_PROPID,
+			 0x00000009, NULL);
+	set_fullpropspec(ctx, &createquery->pidmapper.apropspec[i++],
+			 "6b8da074-3b5c-43bc-886f-0a2cdce00b6f", PRSPEC_PROPID,
+			 0x00000064, NULL);
+	createquery->pidmapper.count = i;
+	createquery->lcid = 0x00001809;
+}
+
+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;
+}
+
+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;
+	}
+
+	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..ce4e6c0
--- /dev/null
+++ b/source4/libcli/wsp/wsp_cli.h
@@ -0,0 +1,111 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+#ifndef __LIBCLI_WSP_WSP_CLI
+#define __LIBCLI_WSP_WSP_CLI
+
+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,
+				const char *location,
+				const char *phrase,
+				const char *kind);
+
+/*
+ * #FIXME perhaps we need a more expressive api here for this,
+ * createquery msg and setbindings are related, we should be maybe specifiying
+ * maybe a list of property names
+ */
+void create_setbindings_request(TALLOC_CTX * ctx,
+				struct wsp_request* request,
+				uint32_t cursor);
+
+void create_seekat_getrows_request(TALLOC_CTX * ctx,
+				   struct wsp_request* request,
+				   uint32_t cursor,
+				   uint32_t bookmark,
+				   uint32_t skip,
+				   uint32_t rows,
+				   uint32_t cbreserved,
+				   uint32_t ulclientbase,
+				   uint32_t cbrowwidth,
+				   uint32_t fbwdfetch);
+
+enum ndr_err_code extract_rowsarray(TALLOC_CTX * ctx,
+				    DATA_BLOB *rows_buf,
+				    struct wsp_cpmsetbindingsin *bindingsin,
+				    uint32_t cbreserved,
+				    uint32_t ulclientbase,
+				    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.1.4


From 6f50bcdf470754a7a98c7c00573b17b17b68975b Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 21 Jul 2016 16:53:17 +0100
Subject: [PATCH 15/26] 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 <noel.power at suse.com>
---
 source4/utils/wscript_build |   6 +
 source4/utils/wspsearch.c   | 390 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 396 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..cf6c3c9
--- /dev/null
+++ b/source4/utils/wspsearch.c
@@ -0,0 +1,390 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+#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"
+
+/* send connectin message */
+static NTSTATUS connect(TALLOC_CTX *ctx,
+			struct wsp_client_ctx *wsp_ctx,
+			const char* clientmachine,
+			const char* clientuser,
+			const char* server)
+{
+	struct wsp_request *request;
+	struct wsp_response *response;
+	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);
+	data_blob_free(&unread);
+	TALLOC_FREE(local_ctx);
+	return status;
+}
+
+static NTSTATUS create_query(TALLOC_CTX *ctx,
+			     struct wsp_client_ctx *wsp_ctx,
+			     const char* location,
+			     const char* phrase,
+			     const char* kind,
+			     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(local_ctx, request, location, phrase, kind);
+	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,
+				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, 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 create_getrows(TALLOC_CTX *ctx,
+			       struct wsp_client_ctx *wsp_ctx,
+			       struct wsp_cpmsetbindingsin *bindings,
+			       uint32_t cursor,
+			       uint32_t nrows)
+{
+	struct wsp_request *request;
+	struct wsp_response *response;
+	NTSTATUS status;
+	DATA_BLOB unread;
+	struct wsp_cbasestoragevariant **rowsarray = NULL;
+	enum ndr_err_code err;
+	TALLOC_CTX *local_ctx = talloc_new(ctx);
+	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 row;
+	uint32_t current_row = 0;
+	ZERO_STRUCT(unread);
+
+	while (total_rows != nrows) {
+		request = talloc_zero(local_ctx, struct wsp_request);
+		response = talloc_zero(local_ctx, 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(local_ctx,
+					request,
+					cursor,
+					bmk,
+					skip,
+					requested_rows,
+					40,
+					0xDEAbd860,
+					bindings->brow,
+					0);
+
+		status = wsp_request_response(local_ctx, 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;
+		}
+		rowsarray =
+			talloc_zero_array(local_ctx,
+				struct wsp_cbasestoragevariant*,
+				response->message.cpmgetrows.rowsreturned);
+		err = extract_rowsarray(rowsarray,
+				&unread,
+				bindings,
+				40,
+				0xDEAbd860,
+				response->message.cpmgetrows.rowsreturned,
+				rowsarray);
+		if (err) {
+			DBG_ERR("failed to extract rows from getrows response\n");
+		}
+		data_blob_free(&unread);
+		for(row = 0;
+		    row < response->message.cpmgetrows.rowsreturned; row++, current_row++) {
+			if (rowsarray[row][0].vtype != VT_EMPTY) {
+				printf("%s\n", rowsarray[row][0].vvalue.vt_lpwstr.value );
+			} else {
+				printf("problem with row[%d]\n", current_row);
+			}
+		}
+	}
+out:
+	TALLOC_FREE(rowsarray);
+	TALLOC_FREE(local_ctx);
+	return status;
+}
+
+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;
+	const char* phrase = NULL;
+	const char* kind = NULL;
+	uint32_t nrows = 0;
+	struct wsp_cpmsetbindingsin bindings_used;
+	struct poptOption long_options[] = {
+                POPT_AUTOHELP
+		{ "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" },
+                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;
+
+	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) {
+		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;
+        }
+
+	share = strchr_m(server,'\\');
+	if (!share) {
+		DBG_ERR("Invalid argument\n");
+                return -1;
+	}
+
+	*share = 0;
+	share++;
+
+	DBG_INFO("server name is %s\n", server);
+	DBG_INFO("share name is %s\n", share);
+	DBG_INFO("search phrase is %s\n", phrase);
+	DBG_INFO("search kind is %s\n", kind);
+
+	if (kind == NULL && phrase == NULL) {
+		poptPrintUsage(pc, stderr, 0);
+		result = -1;
+		goto out;
+	}
+	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);
+
+	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");
+
+	location = talloc_asprintf(talloc_tos(), "FILE://%s/%s", server, share);
+	status = create_query(talloc_tos(), wsp_ctx, location, phrase, kind, &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, 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\n", nrows);
+	status = create_getrows(talloc_tos(),
+				wsp_ctx,
+				&bindings_used,
+				bindings_used.hcursor,
+				nrows);
+	result = 0;
+out:
+	TALLOC_FREE(frame);
+	return result;
+}
-- 
2.1.4


From a79b359337215432c9afa50a521f96c687337629 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 28 Aug 2014 17:54:52 +0100
Subject: [PATCH 16/26] s3/smbd: Ensure WaitNamedPipe for MsFteWds succeeds
 (smb1)

Signed-off-by: Noel Power <noel.power at suse.com>
---
 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.1.4


From 7d2409e2ad8a1df81c2b62bf3d723d5849fad603 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Mon, 1 Sep 2014 15:27:36 +0100
Subject: [PATCH 17/26] s3/smbd: Handle smb2 FSCTL_PIPE_WAIT message

Signed-off-by: Noel Power <noel.power at suse.com>
---
 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.1.4


From d4697ad4f2d83c58a80be7c72e9536246a8a981b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 15 Feb 2016 10:42:52 +0100
Subject: [PATCH 18/26] s3/build: seperate out check for Gnome Tracker from
 Spotlight

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/wscript | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/source3/wscript b/source3/wscript
index ff45078..82fcd19 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1633,6 +1633,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:
@@ -1640,21 +1652,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.1.4


From cea16809f0a553488c717fade9e80db460ce4b43 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 20 Jan 2016 15:08:31 +0100
Subject: [PATCH 19/26] 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 <slow at samba.org>
---
 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "tevent_glib_glue.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "lib/util/debug.h"
+#include <tevent.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_TEVENT
+
+#ifdef HAVE_GLIB
+
+#include <glib.h>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TEVENT_GLIB_GLUE_H
+#define _TEVENT_GLIB_GLUE_H
+
+#include <talloc.h>
+#include <tevent.h>
+
+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 82fcd19..c825a7f 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1632,6 +1632,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']
 
@@ -1660,6 +1671,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 f8ddf23..c0e5c63 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -851,6 +851,12 @@ bld.SAMBA3_SUBSYSTEM('INIT_SAMR',
 bld.SAMBA3_SUBSYSTEM('LIBLSA',
                      source='lib/lsa.c')
 
+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.1.4


From 10f7f54ae456e4963f091e3564ad25d9a7998497 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 28 Jan 2016 08:29:28 +0100
Subject: [PATCH 20/26] s3/lib: add a tevent_glib_glue subsystem test

Tests adapted from glib2 glib/tests/mainloop.c.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/tevent_glib_glue.h"
+#include <glib.h>
+#include <glib-unix.h>
+
+/*
+ * 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 <<EOF
+#!/bin/sh
+exit 0
+EOF
+    chmod +x $BINDIR/tevent_glib_glue_test
+fi
+
+failed=0
+
+testit "tevent_glib_glue_test" $VALGRIND $BINDIR/tevent_glib_glue_test ||
+	failed=`expr $failed + 1`
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 6905ee9..50693b3 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -264,6 +264,9 @@ plantestsuite(
     "samba3.pthreadpool", "nt4_dc",
     [os.path.join(samba3srcdir, "script/tests/test_pthreadpool.sh")])
 
+plantestsuite("samba3.tevent_glib_glue", "nt4_dc",
+    [os.path.join(samba3srcdir, "script/tests/test_tevent_glib_glue.sh")])
+
 plantestsuite("samba3.async_req", "nt4_dc",
               [os.path.join(samba3srcdir, "script/tests/test_async_req.sh")])
 
diff --git a/source3/wscript_build b/source3/wscript_build
index c0e5c63..079656a 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1372,6 +1372,17 @@ bld.SAMBA3_BINARY('eventlogadm',
                  param
                  LIBEVENTLOG''')
 
+bld.SAMBA3_BINARY('tevent_glib_glue_test',
+                 source='lib/tevent_glib_glue_tests.c',
+                 deps='''
+                 talloc
+                 libsmb
+                 popt_samba3
+                 param
+                 tevent-glib-glue''',
+                 enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'),
+                 install=False)
+
 bld.SAMBA3_BINARY('sharesec',
                  source='utils/sharesec.c lib/util_sd.c',
                  deps='''
-- 
2.1.4


From 617fef1c9529ba2ee7179bed8a851999f82e1855 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 22 Jan 2016 15:38:39 +0100
Subject: [PATCH 21/26] 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 <slow at samba.org>
---
 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 <ivan.frade at nokia.com>
+ * Copyright (C) 2015, Noel Power <nopower at suse.com>
+ * Copyright (C) 2016, Ralph Boehme <slow at samba.org.>
+ *
+ * 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 <libtracker-sparql/tracker-sparql.h>
+
+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 079656a..1893e28 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1383,6 +1383,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('sharesec',
                  source='utils/sharesec.c lib/util_sd.c',
                  deps='''
-- 
2.1.4


From 34df6159034227d53260fab21f26adc2a7a7ae8a Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Mon, 22 Feb 2016 14:20:46 +0000
Subject: [PATCH 22/26] 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 <noel.power at suse.com>
---
 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.1.4


From a0c2d17fc15d8eaa29af56fae5b538363778d025 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 16 Jun 2016 14:48:50 +0100
Subject: [PATCH 23/26] 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 <noel.power at suse.com>
---
 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.1.4


From 28ae40d48cdaa4ec6fd4136b6c49a4957b8f4de3 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 17 Jun 2016 10:38:17 +0100
Subject: [PATCH 24/26] s3/build: Adjust configure script to cater for WSP
 service tracker requirement.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/wscript | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/source3/wscript b/source3/wscript
index c825a7f..ba331f5 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1658,10 +1658,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')
@@ -1671,8 +1667,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.1.4


From 126abdf8b135797e50ad3f38cd9148c4403dfff2 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 4 Dec 2014 15:36:19 +0000
Subject: [PATCH 25/26] 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.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/rpc_server/rpc_config.c                 |    1 +
 source3/rpc_server/rpc_config.h                 |    1 +
 source3/rpc_server/wscript_build                |   10 +
 source3/rpc_server/wsp/wsp_gss.c                | 2110 +++++++++++++++++++
 source3/rpc_server/wsp/wsp_gss.h                |  254 +++
 source3/rpc_server/wsp/wsp_sparql_conv.c        | 2445 +++++++++++++++++++++++
 source3/rpc_server/wsp/wsp_sparql_conv.h        |  102 +
 source3/rpc_server/wsp/wsp_srv_tracker-sparql.c |  823 ++++++++
 source3/rpc_server/wsp/wsp_srv_tracker-sparql.h |  104 +
 source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c | 1572 +++++++++++++++
 source3/rpc_server/wsp/wsp_srv_tracker_abs_if.h |   29 +
 source3/rpc_server/wspd.c                       |  271 +++
 source3/smbd/server.c                           |   11 +-
 source3/wscript                                 |    1 +
 source3/wscript_build                           |    3 +-
 15 files changed, 7735 insertions(+), 2 deletions(-)
 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/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 dd39f14..48dfd12 100644
--- a/source3/rpc_server/rpc_config.h
+++ b/source3/rpc_server/rpc_config.h
@@ -72,4 +72,5 @@ enum rpc_daemon_type_e rpc_daemon_type(const char *name);
 #define rpc_rawd_daemon() rpc_daemon_type("rawd")
 #endif
 
+#define rpc_wsp_daemon() rpc_daemon_type("wspd")
 #endif /* _RPC_CONFIG_H */
diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build
index 3982064..2c6cfb7 100755
--- a/source3/rpc_server/wscript_build
+++ b/source3/rpc_server/wscript_build
@@ -148,6 +148,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',
@@ -183,6 +188,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..b94e3f6
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_gss.c
@@ -0,0 +1,2110 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <includes.h>
+#include <tevent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.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_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;
+};
+
+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;
+	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_column_value(TALLOC_CTX *ctx,
+			       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,
+			       uint32_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:
+		case VT_LPWSTR: {
+			uint32_t address = 0;
+			uint32_t offset = 8;
+			bool is_vector = (col_val->vtype & VT_VECTOR);
+			const char *str_val;
+			*value_length = 0;
+			/*
+			 * if vector for testing at the moment just assume 1
+			 * entry
+			 */
+			if (is_vector) {
+				*value_length = 4; /* for 1 X 32bit address*/
+				str_val = col_val->vvalue.vt_lpwstr_v.vvector_data[0].value;
+			} else {
+				str_val = col_val->vvalue.vt_lpwstr.value;
+			}
+			DBG_DEBUG("string value (vector = %s) is %s\n",
+				is_vector ? "true" : "false", 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_vector) {
+				/* #FIXME this is FSCK-ugly */
+				/* store item1 address */
+				SIVAL(buf_start,
+				      *cur_rowbuf_end_pos + *value_length - 4,
+				      address); /* 1 item */
+				/*
+				 * address where address of string vector item
+				 * is stored
+				 */
+				address += *value_length - 4;
+			}
+
+			*value_length += 0x10; /* variant */
+
+			if (is_vector) {
+				SIVAL(value_buf, offset, 1); /* 1 item */
+				offset += 4;
+			}
+			/* for 32 bit addressing */
+			SIVAL(value_buf, offset, address);
+			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, uint32_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, 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;
+	uint32_t address_adjustment;
+	uint32_t index;
+	uint32_t handle;
+	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;
+	state->address_adjustment = rowsin->ulclientbase + rowsin->cbreserved;
+
+	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 = false; /* #TODO need to also check 64k mode */
+	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 (!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;
+	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.
+	 */
+	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 <http://www.gnu.org/licenses/>.
+ */
+
+#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..71bfcd4
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_sparql_conv.c
@@ -0,0 +1,2445 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+static const char *relop(uint32_t nrelop)
+{
+	const char *result;
+	switch (nrelop&0xF) {
+		case PRLT:
+			result = "<";
+			break;
+		case PRLE:
+			result = "<=";
+			break;
+		case PRGT:
+			result = ">";
+			break;
+		case PRGE:
+			result = ">=";
+			break;
+		case PREQ:
+			result = "=";
+			break;
+		case PRNE:
+			result = "!=";
+			break;
+#if 0
+		case PRRE:
+			result = "regex"; /*<sigh>*/
+			break;
+		case PRALLBITS:
+			result = "&";
+			break;
+		case PRSOMEBITS:
+			result = "|";
+			break;
+#endif
+		default:
+			result = "=";
+			break;
+	}
+	return result;
+}
+
+static const char *property_restriction_val(
+			TALLOC_CTX *ctx,
+			struct wsp_cpropertyrestriction* restriction_prop,
+			bool quote)
+{
+	const char* result = talloc_strdup(ctx, "");
+	struct wsp_cbasestoragevariant *prval;
+	prval = &restriction_prop->prval;
+	switch (prval->vtype) {
+		case VT_LPWSTR:
+			if (quote) {
+				result = talloc_asprintf(ctx, "\'%s\'",
+						prval->vvalue.vt_lpwstr.value);
+			}
+			else {
+				result = talloc_strdup(ctx,
+						prval->vvalue.vt_lpwstr.value);
+			}
+			break;
+		case VT_LPWSTR | VT_VECTOR: {
+			int num_elems =
+				prval->vvalue.vt_lpwstr_v.vvector_elements;
+			int i;
+			for(i = 0; i < num_elems; i++) {
+				struct vt_lpwstr_vec *vec;
+				const char *val;
+				vec = &prval->vvalue.vt_lpwstr_v;
+				val = vec->vvector_data[i].value;
+				if (i) {
+					if (quote) {
+						result =
+							talloc_asprintf(ctx,
+								"%s,\'%s\'",
+								result,
+								val);
+					} else {
+						result =
+							talloc_asprintf(ctx,
+								"%s,%s",
+								result,
+								val);
+					}
+				} else {
+					if (quote) {
+						result = talloc_asprintf(
+								ctx,
+								"\'%s\'",
+								val);
+					} else {
+						result = talloc_strdup(
+								ctx,
+								val);
+					}
+				}
+			}
+			break;
+		}
+		case VT_I4:
+			result = talloc_asprintf(ctx,
+						 "%d",
+						 prval->vvalue.vt_i4);
+			break;
+		case VT_UI8: {
+			struct wsp_hyper *phyper = &prval->vvalue.vt_ui8;
+			uint64_t val = 0;
+			wsp_hyper_to_uint64(phyper, &val);
+			result = talloc_asprintf(ctx, "%lu", val);
+			break;
+		}
+		default:
+			result = talloc_asprintf(
+					ctx,
+					"UNHANDLED property type %s #FIXME",
+					get_vtype_name(prval->vtype));
+			break;
+	}
+	return result;
+}
+
+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 = property_restriction_val(ctx,
+				&restric->restriction.cpropertyrestriction,
+				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 = property_restriction_val(ctx,
+				&restric->restriction.cpropertyrestriction,
+				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 = property_restriction_val(ctx,
+				&restric->restriction.cpropertyrestriction,
+				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 = property_restriction_val(ctx,
+				&restric->restriction.cpropertyrestriction,
+				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 = property_restriction_val(ctx,
+				&restric->restriction.cpropertyrestriction,
+				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, "regex(%s(?u),\'^%s\')",
+						detail->tracker_id,
+						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 = property_restriction_val(ctx, cprop, false);
+			prop_val = talloc_strdup(ctx, tmp);
+			filter_str = talloc_asprintf(ctx,
+						"(false %s %s)",
+						relop(cprop->relop),
+						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 = property_restriction_val(ctx, cprop, 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 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;
+}
+
+static const char* rtcontent_to_string(TALLOC_CTX *ctx,
+				       struct wsp_ccontentrestriction *content,
+				       struct tracker_detail *detail)
+{
+	const char *result = talloc_strdup(ctx, "");
+	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);
+	}
+	return result;
+}
+
+static const char* rtproperty_to_string(TALLOC_CTX *ctx,
+			       struct wsp_cpropertyrestriction *restrict_prop,
+			       struct tracker_detail *detail)
+{
+	const char *operator = relop(restrict_prop->relop);
+	const char *value = property_restriction_val(ctx, restrict_prop, true);
+	const char *result = talloc_strdup(ctx, "");
+	result = talloc_asprintf(ctx, "%s(?u) %s %s", detail->tracker_id,
+				 operator,
+				 value);
+	return result;
+}
+
+static const char* rtpropertycontainer_to_string(TALLOC_CTX *ctx,
+					struct wsp_abstract_state *glob_data,
+					struct wsp_crestriction *restriction,
+					void *priv_data)
+{
+	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;
+	if (detail && detail->tracker_id && !detail->filt_helper) {
+		if (detail->filt_helper) {
+			status = detail->filt_helper(ctx, detail,
+						     restriction,
+						     &result,
+						     priv_data);
+			if (!NT_STATUS_IS_OK(status)) {
+					goto done;
+			}
+		} else {
+			if (restriction->ultype == RTPROPERTY) {
+				struct wsp_cpropertyrestriction *cprop;
+				cprop = &restriction->restriction.cpropertyrestriction;
+				result = rtproperty_to_string(ctx,
+							      cprop,
+							      detail);
+			} else if (restriction->ultype == RTCONTENT) {
+				struct wsp_ccontentrestriction *ccont;
+				ccont =	&restriction->restriction.ccontentrestriction;
+				result = rtcontent_to_string(ctx,
+							     ccont,
+							     detail);
+			}
+		}
+	} else if (detail && detail->filt_helper) {
+		status = detail->filt_helper(ctx, detail, restriction,
+					     &result,
+					     priv_data);
+		if (!NT_STATUS_IS_OK(status)) {
+			goto done;
+		}
+	}
+done:
+	return result;
+}
+
+static 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;
+}
+
+static const char* rtpropertycontainer_to_string_raw(TALLOC_CTX *ctx,
+					struct wsp_abstract_state *glob_data,
+					struct wsp_crestriction *restriction,
+					void *priv_data)
+{
+	struct wsp_cfullpropspec *prop_spec = get_full_prop(restriction);
+	const char *prop = prop_from_fullprop(ctx, prop_spec);
+	const char *result = talloc_strdup(ctx, "");
+	const char *operator = NULL;
+	const char *value = NULL;
+	const char *type = NULL;
+	if (restriction->ultype == RTPROPERTY) {
+		struct wsp_cpropertyrestriction *restrict_prop =
+			&restriction->restriction.cpropertyrestriction;
+		operator = relop(restrict_prop->relop);
+		value = property_restriction_val(ctx, restrict_prop, true);
+		type = "RTPROPERTY";
+	} else if (restriction->ultype == RTCONTENT) {
+		struct wsp_ccontentrestriction *content =
+			&restriction->restriction.ccontentrestriction;
+		operator = genmeth_to_string(content->ulgeneratemethod);
+		value = content->pwcsphrase;
+		type = "RTCONTENT";
+	}
+	if (operator && value) {
+		result = talloc_asprintf(ctx, "%s %s %s %s", type, prop, operator,
+					 value);
+	}
+	return result;
+}
+
+static const char * rtnatlang_to_string(TALLOC_CTX *ctx,
+					struct wsp_abstract_state *glob_data,
+					struct wsp_crestriction *restriction)
+{
+	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;
+	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);
+		return result;
+	}
+	cnat = &restriction->restriction.cnatlanguagerestriction;
+	if (detail && detail->tracker_id) {
+		result = talloc_asprintf(ctx, "%s(?u)=%s",
+					 detail->tracker_id,
+					 cnat->pwcsphrase);
+	}
+	return result;
+}
+
+static const char * rtnatlang_to_string_raw(TALLOC_CTX *ctx,
+					struct wsp_abstract_state *glob_data,
+					struct wsp_crestriction *restriction)
+{
+	struct wsp_cfullpropspec *prop_spec = get_full_prop(restriction);
+	const char *prop = prop_from_fullprop(ctx, prop_spec);
+	const char *result = talloc_strdup(ctx, "");
+	struct wsp_cnatlanguagerestriction *cnat;
+	if (restriction->ultype != RTNATLANGUAGE) {
+		DBG_ERR("unexpected type %d, expected RTNATLANGUAGE\n",
+		      restriction->ultype);
+		return result;
+	}
+	cnat = &restriction->restriction.cnatlanguagerestriction;
+	result = talloc_asprintf(ctx,
+				 "RTNATLANGUAGE %s = %s",
+				 prop,
+				 cnat->pwcsphrase);
+	return result;
+}
+
+static NTSTATUS filter_term_from_restriction(TALLOC_CTX *ctx,
+					     struct wsp_abstract_state *glob_data,
+					     struct wsp_crestriction *restriction,
+					     uint32_t parent_type,
+					     const char **filter,
+					     void *priv_data);
+
+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 possibly 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);
+		status = NT_STATUS(0x80070003);
+		goto done;
+	}
+	status = NT_STATUS_OK;
+done:
+	*result = tmp;
+	return status;
+}
+
+
+/*
+ * #FIXME we need to return a status here (perhaps for the others too)
+ *        its necessary in order to pass back the error regarding whether
+ *        the share is indexed or not.
+ */
+typedef const char * (*rtprop_cntr_to_str_fn)(TALLOC_CTX *ctx,
+					  struct wsp_abstract_state *glob_data,
+					  struct wsp_crestriction *restriction,
+					  void *priv_data);
+typedef const char * (*natlang_to_str_fn)(TALLOC_CTX *ctx,
+					  struct wsp_abstract_state *glob_data,
+					  struct wsp_crestriction *restriction);
+typedef NTSTATUS (*reusewhere_to_str_fn)(TALLOC_CTX *ctx,
+					 struct wsp_abstract_state *glob_data,
+					 struct wsp_crestriction *restriction,
+					 const char **result);
+struct wsp_conv_ops
+{
+	rtprop_cntr_to_str_fn rtprop_cntr_to_str;
+	natlang_to_str_fn natlang_to_str;
+	reusewhere_to_str_fn reusewhere_to_str;
+};
+
+static NTSTATUS rtreusewhere_to_string_raw(TALLOC_CTX *ctx,
+					struct wsp_abstract_state *glob_data,
+					struct wsp_crestriction *restriction,
+					const char **result)
+{
+	int where_id;
+	NTSTATUS status;
+	where_id = restriction->restriction.reusewhere.whereid;
+	*result = talloc_asprintf(ctx, "insert expression for WHEREID = %d",
+				  where_id);
+	status = NT_STATUS_OK;
+	return status;
+}
+
+static struct wsp_conv_ops *conv_ops = NULL;
+
+/*
+ * if dump is set then no attempt to convert wsp properties is made,
+ * instead the 'raw' expression is generated (e.g. no conversion and no
+ * terms 'dropped' because they are not supported and/or we don't know how
+ * to handle them.
+ */
+static void init_conv_ops(bool dump)
+{
+	if (!conv_ops) {
+		/* this will leak, is there some global state I could hook off */
+		conv_ops = talloc_zero(NULL, struct wsp_conv_ops);
+	}
+
+	if (!dump) {
+		conv_ops->rtprop_cntr_to_str = rtpropertycontainer_to_string;
+		conv_ops->natlang_to_str = rtnatlang_to_string;
+		conv_ops->reusewhere_to_str = rtreusewhere_to_string;
+	} else {
+		conv_ops->rtprop_cntr_to_str =
+			rtpropertycontainer_to_string_raw;
+		conv_ops->natlang_to_str = rtnatlang_to_string_raw;
+		conv_ops->reusewhere_to_str = rtreusewhere_to_string_raw;
+	}
+}
+
+static NTSTATUS filter_term_from_restriction(TALLOC_CTX *ctx,
+					     struct wsp_abstract_state *glob_data,
+					     struct wsp_crestriction *restriction,
+					     uint32_t parent_type,
+					     const char **filter,
+					     void *priv_data)
+{
+	int i;
+	const char* filter_str = talloc_strdup(ctx, "");
+	NTSTATUS status;
+
+	switch(restriction->ultype) {
+		case RTAND:
+		case RTOR: {
+			struct wsp_cnoderestriction *cnodes =
+				&restriction->restriction.cnoderestriction;
+			const char* str = talloc_strdup(ctx, "");
+			const char* andor = (restriction->ultype == RTAND) ?
+						"&&" : "||";
+			const char *tmp;
+			for (i = 0; i < cnodes->cnode; i++) {
+				status =
+					filter_term_from_restriction(ctx,
+							glob_data,
+							&cnodes->panode[i],
+							restriction->ultype,
+							&tmp,
+							priv_data);
+				if (!NT_STATUS_IS_OK(status)) {
+					goto done;
+				}
+				if (strlen(str) && strlen(tmp)) {
+					str = talloc_asprintf(ctx,
+							"%s %s %s",
+				 			str, andor, tmp);
+				} else {
+					str = talloc_asprintf(ctx,
+							"%s%s",
+				 			 str, tmp);
+				}
+			}
+			filter_str = talloc_asprintf(ctx, "(%s)", str);
+			break;
+		}
+		case RTNOT: {
+			struct wsp_crestriction *not =
+				restriction->restriction.restriction.restriction;
+			const char *tmp;
+			status =
+				filter_term_from_restriction(ctx,
+							      glob_data,
+							      not,
+							      restriction->ultype,
+							      &tmp,
+							      priv_data);
+			if (!NT_STATUS_IS_OK(status)) {
+				goto done;
+			}
+			if (strlen(tmp)) {
+				filter_str = talloc_asprintf(ctx, "!%s", tmp);
+			}
+			break;
+		}
+		case RTCONTENT:
+		case RTPROPERTY:
+			filter_str = conv_ops->rtprop_cntr_to_str(ctx,
+								  glob_data,
+								  restriction,
+								  priv_data);
+			break;
+		case RTNATLANGUAGE:
+			filter_str = conv_ops->natlang_to_str(ctx, glob_data,
+							      restriction);
+			break;
+		case RTCOERCE_ABSOLUTE: {
+			struct wsp_crestriction *child_restrict =
+					restriction->restriction.ccoercionrestriction_abs.childres;
+			status = filter_term_from_restriction(ctx,
+							      glob_data,
+							      child_restrict,
+							      restriction->ultype,
+							      &filter_str,
+							      priv_data);
+			if (!NT_STATUS_IS_OK(status)) {
+				goto done;
+			}
+			break;
+		}
+		case RTREUSEWHERE: {
+			if (priv_data) {
+				struct filter_data *data =
+					(struct filter_data *)priv_data;
+				data->where_id =
+					restriction->restriction.reusewhere.whereid;
+			}
+			status = conv_ops->reusewhere_to_str(ctx, glob_data,
+							     restriction,
+							     &filter_str);
+			break;
+		}
+	}
+	*filter = filter_str;
+	status = NT_STATUS_OK;
+done:
+	return status;
+}
+
+NTSTATUS get_filter(TALLOC_CTX *ctx,
+		    struct wsp_abstract_state *glob_data,
+		    struct wsp_crestrictionarray *restrictarray,
+		    bool convert_props,
+		    const char **filter_str,
+		    const char **share_scope,
+		    uint32_t *where_id)
+{
+	int i;
+	char *filter = talloc_strdup(ctx, "");
+	struct filter_data data;
+	NTSTATUS status;
+
+	ZERO_STRUCT(data);
+	init_conv_ops(!convert_props);
+	if (!restrictarray->count) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto out;
+	}
+	for (i = 0; i < restrictarray->count; i++) {
+		const char * result;
+		status = filter_term_from_restriction(ctx, glob_data,
+					    &restrictarray->restrictions[i],
+					    RTNONE, &result, &data);
+		if (!NT_STATUS_IS_OK(status)) {
+			goto out;
+		}
+		if (strlen(filter)) {
+			filter = talloc_asprintf(ctx, "%s %s", result,
+						 filter);
+		} else {
+			filter = talloc_strdup(ctx, result);
+		}
+	}
+	/*
+	 * it's possibly (but unusual) to have a single item restriction, make
+	 * sure it is properly enclosed with '(' & ')'
+	 */
+	if (strlen(filter)) {
+		if (filter[0] != '(' && filter[strlen(filter)] != ')') {
+			filter = talloc_asprintf(ctx, "(%s)", filter);
+		}
+	}
+	status = NT_STATUS_OK;
+out:
+	*filter_str = filter;
+	*share_scope = data.share_scope;
+	*where_id = data.where_id;
+	return status;
+}
+
+/*
+ * 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,
+			     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;
+
+	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");
+	}
+
+	where_str = talloc_asprintf(ctx, "%s %s}", where_str,
+		strlen(filter_str) ? filter_str : "");
+	query_str = talloc_asprintf(ctx, "%s %s", select_str, where_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 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;
+}
+
+static 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;
+		}
+	}
+	return op;
+}
+
+static const char *infix(TALLOC_CTX *ctx,
+			 struct wsp_abstract_state *glob_data,
+			 struct wsp_crestriction *restriction,
+			 void *priv_data);
+static const char *print_restriction(TALLOC_CTX *ctx,
+				     struct wsp_abstract_state *glob_data,
+				     struct wsp_crestriction *restriction,
+				     void *priv_data)
+{
+	const char *result = NULL;
+	const char *tmp;
+	NTSTATUS status;
+
+	if (is_operator(restriction)) {
+		result = talloc_strdup(ctx, op_as_string(restriction));
+	} else {
+		switch(restriction->ultype) {
+			case RTCONTENT:
+			case RTPROPERTY: {
+				tmp = conv_ops->rtprop_cntr_to_str(ctx,
+								   glob_data,
+								   restriction,
+								   priv_data);
+				if (strlen(tmp)) {
+					result = tmp;
+				}
+				break;
+			}
+			case RTNATLANGUAGE:
+				tmp = conv_ops->natlang_to_str(ctx, glob_data, restriction);
+				if (strlen(tmp)) {
+					result = tmp;
+				}
+				break;
+			case RTCOERCE_ABSOLUTE: {
+				struct wsp_crestriction *child_restrict =
+					restriction->restriction.ccoercionrestriction_abs.childres;
+				result = infix(ctx, glob_data, child_restrict, priv_data);
+				break;
+			}
+			case RTREUSEWHERE: {
+				status = conv_ops->reusewhere_to_str(ctx,
+								glob_data,
+								restriction,
+								&tmp);
+				if (NT_STATUS_IS_OK(status) && strlen(tmp)) {
+					result = tmp;
+				}
+				break;
+			}
+		}
+	}
+	return result;
+}
+
+/*
+ * This is incomplete WIP more or less the 'proper' algorithm for traversing
+ * a binary tree to produce an infix expression. It's not fully tested
+ * and buggy.
+ * TODO: eventually use this instead of homegrown algo in getfilter
+ */
+static const char *infix(TALLOC_CTX *ctx, struct  wsp_abstract_state *glob_data,
+			 struct wsp_crestriction *restriction,
+			 void *priv_data)
+{
+	const char *foo = 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;
+	if (!restriction) {
+		return NULL;
+	}
+	if (is_operator(restriction)) {
+		foo = talloc_strdup(ctx, "(");
+		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;
+		}
+	}
+	left_node = infix(ctx, glob_data, left, priv_data);
+	token = print_restriction(ctx, glob_data, restriction, priv_data);
+	right_node = infix(ctx, glob_data, right, priv_data);
+
+	if (right_node || left_node || token){
+		if (is_operator(restriction)) {
+			if (right_node && left_node) {
+				foo = talloc_asprintf(ctx, "%s%s %s %s", foo,
+					left_node,
+					token,
+					right_node);
+			} else  {
+				foo = talloc_asprintf(ctx, "%s%s%s", foo,
+					left_node ? left_node : "",
+					right_node ? right_node : "");
+			}
+		} else  {
+			foo = talloc_asprintf(ctx, "%s%s%s",
+					     left_node ? left_node : "",
+					     token ? token : "",
+					     right_node ? right_node : "");
+		}
+	}
+	if (is_operator(restriction)) {
+		foo = talloc_asprintf(ctx, "%s)", foo);
+	}
+	if (strequal(foo, "()")) {
+		foo = NULL;
+	}
+	return foo;
+}
+
+const char* build_restriction_expression(TALLOC_CTX *ctx,
+		       struct wsp_abstract_state *glob_data,
+		       struct wsp_crestrictionarray *restrictarray,
+		       bool convert_props,
+		       const char** share)
+{
+	const char * query = talloc_strdup(ctx, "");
+	init_conv_ops(!convert_props);
+	if (restrictarray->count) {
+		query = infix(ctx, glob_data, &restrictarray->restrictions[0], share);
+	}
+	return query;
+}
+
+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 *num_removed)
+{
+	uint32_t removed = 0;
+	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);
+			removed++;
+		} else {
+			/* populate EntryId in extra 'hidden' column*/
+			item->columns[item->ncols - 1].tracker_type =
+				TRACKER_INTEGER;
+			item->columns[item->ncols - 1].type.integer = id++;
+		}
+
+		item = next;
+	}
+	*num_removed = removed;
+	*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..f9a0adc
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_sparql_conv.h
@@ -0,0 +1,102 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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,
+			     bool convert_props,
+			     const char **query);
+
+
+const char * get_where_restriction_string(struct wsp_abstract_state *wsp_abstract_state, uint32_t id);
+
+NTSTATUS get_filter(TALLOC_CTX *ctx,
+		    struct wsp_abstract_state *glob_data,
+		    struct wsp_crestrictionarray *restrictarray,
+		    bool convert_props,
+		    const char **filter,
+		    const char **share_scope,
+		    uint32_t *where_id);
+
+const char *build_restriction_expression(TALLOC_CTX *ctx,
+		    			 struct wsp_abstract_state *glob_data,
+					 struct wsp_crestrictionarray *restrictions,
+					 bool convert_props,
+					 const char **share_scope);
+
+void filter_tracker_rows(struct tracker_row **rows,
+			 struct connection_struct *conn,
+			 struct auth_session_info *session_info,
+			 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..c223729
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.c
@@ -0,0 +1,823 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <glib-unix.h>
+#include <libtracker-sparql/tracker-sparql.h>
+#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;
+	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)
+{
+	uint32_t removed = 0;
+	filter_tracker_rows(&query->rows, query->conn, query->session_info, &removed);
+	query->num_rows = query->num_rows - removed;
+}
+
+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);
+	}
+	if (more_results) {
+		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,
+					  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->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..5fcc8cb
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_srv_tracker-sparql.h
@@ -0,0 +1,104 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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,
+					  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..7e1e427
--- /dev/null
+++ b/source3/rpc_server/wsp/wsp_srv_tracker_abs_if.c
@@ -0,0 +1,1572 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <includes.h>
+#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;
+	/*
+	 * 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;
+	/* 32 bit win7 */
+	*serverVersion = 0x00000700;
+}
+
+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 = get_filter(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;
+			}
+		}
+	} else {
+		query_info->share = share;
+	}
+
+	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);
+		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,
+				     true,
+				     &sparql_query);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		can_query_now = false;
+		goto err_out;
+	}
+
+	*CursorHandlesList = talloc_zero_array(query_info, uint32_t,
+					       query_info->ncursors);
+	query_info->rowsetproperties = *RowSetProperties;
+
+	/* 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);
+	}
+	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,
+						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;
+
+	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->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;
+		}
+	}
+	*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;
+	}
+	*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;
+	}
+
+	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);
+	}
+
+	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->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 <http://www.gnu.org/licenses/>.
+ */
+
+#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..405fdc3
--- /dev/null
+++ b/source3/rpc_server/wspd.c
@@ -0,0 +1,271 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 "rpc_server/rawpipe.h"
+#include <signal.h>
+
+#define DAEMON_NAME "wspd"
+
+struct wspd_state
+{
+	struct gss_state *gss_state;
+	struct wspd_client_state *client_state;
+};
+
+void start_wspd(struct tevent_context *ev_ctx,
+		struct messaging_context *msg_ctx, bool is_external);
+
+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();
+	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 pipe_destroyed(void *private_data);
+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)) {
+		/*
+		 * #FIXME should we return bool and have an out param or
+		 * abort or....
+		 */
+		DBG_ERR("Failed to initialise the gss\n");
+		exit(1);
+	}
+	wsp_state->client_state = create_client_state(npc, gss_state);
+	return wsp_state;
+}
+
+static struct tevent_req *process_wsp_pipe_request(TALLOC_CTX *ctx,
+				struct named_pipe_client *npc,
+				void *state)
+{
+	struct tevent_req *subreq;
+	struct wspd_state *wspd_state =
+			talloc_get_type_abort(state,
+					      struct wspd_state);
+        TALLOC_CTX *frame = talloc_stackframe();
+	subreq = do_wsp_request_send(ctx, wspd_state->client_state);
+
+	if (!subreq) {
+		goto done;
+	}
+done:
+        TALLOC_FREE(frame);
+	return subreq;
+}
+
+static void 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,
+		bool is_external)
+{
+	NTSTATUS status;
+	struct gss_state *gss_state;
+	pid_t pid;
+	bool ok;
+	int rc;
+
+	gss_state = gss_state_create(ev_ctx, msg_ctx);
+	common_rawpipe_register("msftewds", FILE_TYPE_MESSAGE_MODE_PIPE,
+				wsp_pipe_opened,
+				pipe_destroyed,
+				process_wsp_pipe_request,
+				gss_state);
+	if (is_external == false) {
+		DBG_NOTICE("WSP is internal\n");
+		return;
+	}
+	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);
+
+	/*
+	 * here is where the init and shutdown callbacks were handled
+	 * for rpc, need to figure out what similar thing I can do
+	 */
+	ok = setup_named_pipe_socket("msftewds", ev_ctx, msg_ctx);
+	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 3fda247..4454686 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -103,6 +103,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, bool is_external);
 
 /*******************************************************************
  What to do when smb.conf is updated.
@@ -1732,7 +1734,14 @@ 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, true);
+		} else {
+			start_wspd(ev_ctx, msg_ctx, false);
+		}
+#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 ba331f5..68eca29 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1663,6 +1663,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 1893e28..dca9965 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -634,6 +634,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    netapi
                    NDR_IOCTL
                    notifyd
+                   WSPSVC
                    ''' +
                    bld.env['dmapi_lib'] +
                    bld.env['legacy_quota_libs'] +
@@ -861,7 +862,7 @@ bld.SAMBA3_SUBSYSTEM('tevent-glib-glue',
 
 bld.SAMBA3_BINARY('smbd/smbd',
                  source='smbd/server.c smbd/smbd_cleanupd.c',
-                 deps='smbd_base EPMD LSASD FSSD MDSSD RAWPIPE',
+                 deps='smbd_base EPMD LSASD FSSD MDSSD RAWPIPE WSPD',
                  install_path='${SBINDIR}')
 
 bld.SAMBA3_BINARY('nmbd/nmbd',
-- 
2.1.4


From 95fa84f34da18876f9ac67ae74c08af8f6919613 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 27 Jul 2016 15:03:00 +0100
Subject: [PATCH 26/26] 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 <noel.power at suse.com>
---
 source3/utils/wsp-to-tracker.c | 276 +++++++++++++++++++++++++++++++++++++++++
 source3/wscript_build          |  10 ++
 2 files changed, 286 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..c7899cd
--- /dev/null
+++ b/source3/utils/wsp-to-tracker.c
@@ -0,0 +1,276 @@
+#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 <unistd.h>
+
+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:
+	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 void print_help(void)
+{
+	DBG_ERR("wsp-to-tracker [-f|-r] -v\n");
+	DBG_ERR("\t-f full prints out the full sparql query\n");
+	DBG_ERR("\t-r restriction, prints out the restriction expression\n");
+	DBG_ERR("\t-n uses new infix expression generator (note: only works with -r option)\n");
+	DBG_ERR("\t-v don't do any conversion to tracker properties from wsp properties\n");
+	DBG_ERR("\t   don't drop any part of the extression, just print it out as you can\n");
+}
+
+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, 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;
+	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;
+	int errflg = 0;
+	bool raw = false;
+	bool full = false;
+	bool restriction = false;
+	bool new_generator = false;
+
+	NTSTATUS status;
+	TALLOC_CTX *frame = talloc_stackframe();
+	if (!frame) {
+		DBG_ERR("failed to allocate stack frame\n");
+		return -1;
+	}
+	while ((c = getopt (argc, argv, "vfrhn")) != -1) {
+		switch (c)
+		{
+			case 'v':
+				raw = true;
+				break;
+			case 'f':
+				full = true;
+				break;
+			case 'h':
+				print_help();
+				exit(0);
+				break;
+			case 'r':
+				restriction = true;
+				break;
+			case 'n':
+				new_generator = true;
+				break;
+			case ':':       /* -f or -o without operand */
+				DBG_ERR("Option -%c requires an operand\n",
+					optopt);
+				errflg++;
+				break;
+			case '?':
+				DBG_ERR("Unrecognized option: '-%c'\n", optopt);
+				errflg++;
+				break;
+			default:
+				break;
+		}
+	}
+	if ( optind >= argc || errflg || ( full && restriction )) {
+		DBG_ERR("USAGE: wsp-to-tracker [-f|-r] -v -h message.bytes\n");
+		exit(2);
+	}
+
+	if (!full && !restriction) {
+		/* default to full */
+		full = true;
+	}
+	/* #TODO #FIXME do we really need this ? */
+	if (!lp_load_with_shares(get_dyn_CONFIGFILE())) {
+		DBG_ERR("failed to load %s\n",get_dyn_CONFIGFILE());
+		return -1;
+	}
+	request = talloc(ctx, struct wsp_request);
+	response = talloc(ctx, struct wsp_response);
+	ZERO_STRUCTP(request);
+	ZERO_STRUCTP(response);
+	ZERO_STRUCT(tracker_cols);
+
+	if (argc < 2) {
+		result = 1;
+		goto out;
+	}
+
+
+	if (!get_blob_from_file(ctx, argv[argc - 1], &blob)) {
+		DBG_ERR("failed to process %s\n", argv[1]);
+		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;
+	}
+
+
+	if (full) {
+		const char *restrictionset_expr = NULL;
+		struct binding_result_mapper *result_converter;
+		struct map_data *map_data;
+		struct wsp_ctablecolumn *columns;
+		uint32_t  ncolumns;
+		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 = get_filter(ctx, NULL, restrictionset, !raw,  &restrictionset_expr, &share, &where_id);
+		status = build_tracker_query(ctx,
+				     projected_col_offsets,
+				     restrictionset_expr,
+				     pidmapper,
+				     &tracker_cols,
+				     !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) {
+
+		if (new_generator) {
+			DBG_ERR("using new generator\n");
+			query_str = build_restriction_expression(ctx, NULL, restrictionset, !raw, &share);
+		} else {
+			status = get_filter(ctx, NULL, restrictionset, !raw,  &query_str, &share, &where_id);
+		}
+		DBG_ERR("tracker-sparql restrictione expression\n\"%s\"\n", query_str);
+	}
+	result = 0;
+	status = NT_STATUS_OK;
+out:
+	return result;
+}
diff --git a/source3/wscript_build b/source3/wscript_build
index dca9965..328552c 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1357,6 +1357,16 @@ bld.SAMBA3_BINARY('smbcacls',
                  libcli_lsa3
                  krb5samba''')
 
+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('smbcquotas',
                  source='utils/smbcquotas.c',
                  deps='''
-- 
2.1.4



More information about the samba-technical mailing list