summaryrefslogtreecommitdiffstats
path: root/source3/utils/wspsearch.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/utils/wspsearch.c')
-rw-r--r--source3/utils/wspsearch.c842
1 files changed, 842 insertions, 0 deletions
diff --git a/source3/utils/wspsearch.c b/source3/utils/wspsearch.c
new file mode 100644
index 0000000..063b952
--- /dev/null
+++ b/source3/utils/wspsearch.c
@@ -0,0 +1,842 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Window Search Service
+ *
+ * Copyright (c) Noel Power
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/cmdline_contexts.h"
+#include "param.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/wsp/wsp_util.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/wsp_cli.h"
+#include "libcli/wsp/wsp_aqs.h"
+#include "librpc/gen_ndr/ndr_wsp.h"
+#include "librpc/gen_ndr/ndr_wsp_data.h"
+#include "dcerpc.h"
+
+#define WIN_VERSION_64 0x10000
+
+/* send connectin message */
+static NTSTATUS wsp_connect(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ const char* clientmachine,
+ const char* clientuser,
+ const char* server,
+ bool *is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ uint32_t client_ver;
+ uint32_t server_ver;
+ DATA_BLOB unread = data_blob_null;
+ NTSTATUS status;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!init_connectin_request(local_ctx, request,
+ clientmachine, clientuser, server)) {
+ DBG_ERR("Failed in initialise connection message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = wsp_request_response(local_ctx, wsp_ctx,
+ request, response, &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ client_ver = request->message.cpmconnect.iclientversion;
+ server_ver = response->message.cpmconnect.server_version;
+ *is_64bit =
+ (server_ver & WIN_VERSION_64)
+ && (client_ver & WIN_VERSION_64);
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS create_query(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ uint32_t limit,
+ t_select_stmt *select,
+ uint32_t *single_cursor)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;;
+ }
+
+ if (!create_querysearch_request(ctx, request, select)) {
+ DBG_ERR("error setting up query request message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
+
+ 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);
+ }
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS create_bindings(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ t_select_stmt *select,
+ uint32_t cursor,
+ struct wsp_cpmsetbindingsin *bindings_out,
+ bool is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+
+ request = talloc_zero(ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!create_setbindings_request(ctx,
+ request,
+ select,
+ cursor,
+ is_64bit)) {
+ DBG_ERR("Failed to create setbindings message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = wsp_request_response(ctx,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ *bindings_out = request->message.cpmsetbindings;
+ }
+
+out:
+ 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 = NULL;
+ struct wsp_response *response = NULL;
+ struct wsp_cpmgetquerystatusexin *statusexin = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ statusexin = &request->message.cpmgetquerystatusex;
+
+ request->header.msg = CPMGETQUERYSTATUSEX;
+ statusexin->hcursor = cursor;
+ statusexin->bmk = 0xfffffffc;
+ status = wsp_request_response(local_ctx,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ *nrows = response->message.cpmgetquerystatusex.resultsfound;
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS print_rowsreturned(
+ TALLOC_CTX *ctx,
+ DATA_BLOB *buffer,
+ bool is_64bit,
+ bool disp_all_cols,
+ struct wsp_cpmsetbindingsin *bindings,
+ uint32_t cbreserved,
+ uint64_t address,
+ uint32_t rowsreturned,
+ uint32_t *rows_processed)
+{
+ NTSTATUS status;
+ uint32_t row = 0;
+ TALLOC_CTX *local_ctx = NULL;
+ struct wsp_cbasestoragevariant **rowsarray = NULL;
+ enum ndr_err_code err;
+
+ local_ctx = talloc_init("results");
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ rowsarray = talloc_zero_array(local_ctx,
+ struct wsp_cbasestoragevariant*,
+ rowsreturned);
+ if (rowsarray == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ err = extract_rowsarray(rowsarray,
+ buffer,
+ is_64bit,
+ bindings,
+ cbreserved,
+ address,
+ rowsreturned,
+ rowsarray);
+ if (err) {
+ DBG_ERR("failed to extract rows from getrows response\n");
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ for(row = 0; row < rowsreturned; row++) {
+ TALLOC_CTX *row_ctx = NULL;
+ const char *col_str = NULL;
+
+ row_ctx = talloc_init("row");
+ if (row_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (disp_all_cols) {
+ int i;
+ for (i = 0; i < bindings->ccolumns; i++){
+ col_str =
+ variant_as_string(
+ row_ctx,
+ &rowsarray[row][i],
+ true);
+ if (col_str) {
+ printf("%s%s",
+ i ? ", " : "", col_str);
+ } else {
+ printf("%sN/A",
+ i ? ", " : "");
+ }
+ }
+ } else {
+ col_str = variant_as_string(
+ row_ctx,
+ &rowsarray[row][0],
+ true);
+ printf("%s", col_str);
+ }
+ printf("\n");
+ TALLOC_FREE(row_ctx);
+ }
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(local_ctx);
+ *rows_processed = row;
+ return status;
+}
+
+static NTSTATUS create_getrows(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ struct wsp_cpmsetbindingsin *bindings,
+ uint32_t cursor,
+ uint32_t nrows,
+ bool disp_all_cols,
+ bool is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ uint32_t bmk = 0xfffffffc;
+ uint32_t skip = 0;
+ uint32_t total_rows = 0;
+ uint32_t INITIAL_ROWS = 32;
+ uint32_t requested_rows = INITIAL_ROWS;
+ uint32_t rows_printed;
+ uint64_t baseaddress;
+ uint32_t offset_lowbits = 0xdeabd860;
+ uint32_t offset_hibits = 0xfeeddeaf;
+
+ TALLOC_CTX *row_ctx;
+ bool loop_again;
+
+ do {
+ row_ctx = talloc_new(NULL);
+ if (!row_ctx) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ request = talloc_zero(row_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ response = talloc_zero(row_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ create_seekat_getrows_request(request,
+ request,
+ cursor,
+ bmk,
+ skip,
+ requested_rows,
+ 40,
+ offset_lowbits,
+ bindings->brow,
+ 0);
+
+ if (is_64bit) {
+ /*
+ * MS-WSP 2.2.2
+ * ulreservered holds the high 32-bits part of
+ * a 64-bit offset if 64-bit offsets are being used.
+ */
+ request->header.ulreserved2 = offset_hibits;
+ baseaddress = request->header.ulreserved2;
+ baseaddress <<= 32;
+ baseaddress += offset_lowbits;
+ } else {
+ baseaddress = offset_lowbits;
+ }
+
+ status = wsp_request_response(request,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ total_rows += response->message.cpmgetrows.rowsreturned;
+ if (response->message.cpmgetrows.rowsreturned
+ != requested_rows) {
+ uint32_t rowsreturned =
+ response->message.cpmgetrows.rowsreturned;
+ 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;
+ } else {
+ bmk = 0xfffffffc;
+ skip = total_rows;
+ }
+ requested_rows = requested_rows - rowsreturned;
+ } else {
+ requested_rows = INITIAL_ROWS;
+ bmk = 0xfffffffc;
+ skip = total_rows;
+ }
+
+ if (response->message.cpmgetrows.rowsreturned) {
+ status = print_rowsreturned(row_ctx, &unread,
+ is_64bit,
+ disp_all_cols,
+ bindings, 40,
+ baseaddress,
+ response->message.cpmgetrows.rowsreturned,
+ &rows_printed);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ data_blob_free(&unread);
+ }
+
+ /*
+ * response is a talloc child of row_ctx so we need to
+ * assign loop_again before we delete row_ctx
+ */
+ loop_again = response->message.cpmgetrows.rowsreturned;
+
+ TALLOC_FREE(row_ctx);
+ if (nrows && total_rows > nrows) {
+ DBG_ERR("Something is wrong, results returned %d "
+ "exceed expected number of results %d\n",
+ total_rows, nrows);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ } while (loop_again);
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(row_ctx);
+ return status;
+}
+
+const char *default_column = "System.ItemUrl";
+
+static bool is_valid_kind(const char *kind)
+{
+ const char* kinds[] = {"calendar",
+ "communication",
+ "contact",
+ "document",
+ "email",
+ "feed",
+ "folder",
+ "game",
+ "instantMessage",
+ "journal",
+ "link",
+ "movie",
+ "music",
+ "note",
+ "picture",
+ "program",
+ "recordedtv",
+ "searchfolder",
+ "task",
+ "video",
+ "webhistory"};
+ char* search_kind = NULL;
+ int i;
+ bool found = false;
+
+ search_kind = strlower_talloc(NULL, kind);
+ if (search_kind == NULL) {
+ DBG_ERR("couldn't convert %s to lower case\n",
+ kind);
+ return NULL;
+ }
+
+ for (i=0; i<ARRAY_SIZE(kinds); i++) {
+ if (strequal(search_kind, kinds[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ DBG_ERR("Invalid kind %s\n", kind);
+ }
+ TALLOC_FREE(search_kind);
+ return found;
+}
+
+static char * build_default_sql(TALLOC_CTX *ctx,
+ const char *kind,
+ const char *phrase,
+ const char *location)
+{
+ char *sql = NULL;
+ /* match what windows clients do */
+ sql = talloc_asprintf(ctx,
+ "Scope:\"%s\" AND NOT System.Shell.SFGAOFlagsStrings:hidden"
+ " AND NOT System.Shell.OmitFromView:true", location);
+
+ if (kind) {
+ if (!is_valid_kind(kind)) {
+ return NULL;
+ }
+ sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
+ kind, sql);
+ }
+
+ if (phrase) {
+ sql = talloc_asprintf(ctx,
+ "All:$=\"%s\" OR All:$<\"%s\""
+ " AND %s", phrase, phrase, sql);
+ }
+ sql = talloc_asprintf(ctx, "SELECT %s"
+ " WHERE %s", default_column, sql);
+ return sql;
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int result = 0;
+ NTSTATUS status = NT_STATUS_OK;
+ poptContext pc;
+ char* server = NULL;
+ char* share = NULL;
+ char* path = NULL;
+ char* location = NULL;
+ char* query = NULL;
+ bool custom_query = false;
+ const char* phrase = NULL;
+ const char* kind = NULL;
+ uint32_t limit = 500;
+ uint32_t nrows = 0;
+ struct wsp_cpmsetbindingsin bindings_used = {0};
+ bool is_64bit = false;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "limit",
+ 0,
+ POPT_ARG_INT,
+ &limit,
+ 0,
+ "limit results",
+ "default is 500, specifying 0 means unlimited" },
+ { "search",
+ 0,
+ POPT_ARG_STRING,
+ &phrase,
+ 0,
+ "Search phrase",
+ "phrase" },
+ { "kind", 0, POPT_ARG_STRING, &kind, 0,
+ "Kind of thing to search for [Calendar|Communication|"
+ "Contact|Document|Email|Feed|Folder|Game|"
+ "InstantMessage|Journal|Link|Movie|Music|Note|Picture|"
+ "Program|RecordedTV|SearchFolder|Task|Video"
+ "|WebHistory]",
+ "kind" },
+ { "query",
+ 0,
+ POPT_ARG_STRING,
+ &query,
+ 0,
+ "specify a more complex query",
+ "query" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev_ctx
+ = samba_tevent_context_init(talloc_tos());
+ uint32_t cursor = 0;
+ struct wsp_client_ctx *wsp_ctx = NULL;
+ t_select_stmt *select_stmt = NULL;
+ const char **const_argv = discard_const_p(const char *, argv);
+ struct dcerpc_binding_handle *h = NULL;
+ struct cli_state *c = NULL;
+ uint32_t flags = CLI_FULL_CONNECTION_IPC;
+ bool ok;
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to set up cmdline parser\n");
+ result = -1;
+ goto out;
+ }
+
+ pc = samba_popt_get_context("wspsearch",
+ argc,
+ const_argv,
+ long_options,
+ 0);
+ poptSetOtherOptionHelp(pc, "[OPTIONS] //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;
+ }
+
+ if (server) {
+ /*
+ * if we specify --query then we don't need actually need the
+ * share part, if it is specified then we don't care as we
+ * expect the scope to be part of the query (and if it isn't
+ * then it will probably fail anyway)
+ */
+ share = strchr_m(server,'\\');
+ if (!query && !share) {
+ DBG_ERR("Invalid argument\n");
+ return -1;
+ }
+ if (share) {
+ *share = 0;
+ share++;
+ }
+ }
+
+ DBG_INFO("server name is %s\n", server ? server : "N/A");
+ DBG_INFO("share name is %s\n", share ? share : "N/A");
+ DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A");
+ DBG_INFO("search kind is %s\n", kind ? kind : "N/A");
+
+ if (!query && (kind == NULL && phrase == NULL)) {
+ poptPrintUsage(pc, stderr, 0);
+ result = -1;
+ goto out;
+ }
+
+ if (!query) {
+ location = talloc_asprintf(talloc_tos(),
+ "FILE://%s/%s", server, share);
+ query = build_default_sql(talloc_tos(), kind, phrase, location);
+ if (!query) {
+ result = -1;
+ goto out;
+ }
+ } else {
+ custom_query = true;
+ }
+
+ printf("custom_query %d\n", custom_query);
+ select_stmt = get_wsp_sql_tree(query);
+
+ poptFreeContext(pc);
+
+ if (select_stmt == NULL) {
+ DBG_ERR("query failed\n");
+ result = -1;
+ goto out;
+ }
+
+ if (select_stmt->cols == NULL) {
+ select_stmt->cols = talloc_zero(select_stmt, t_col_list);
+ if (select_stmt->cols == NULL) {
+ DBG_ERR("out of memory\n");
+ result = -1;
+ goto out;
+ }
+ select_stmt->cols->num_cols = 1;
+ select_stmt->cols->cols =
+ talloc_zero_array(select_stmt->cols, char*, 1);
+ if (select_stmt->cols->cols == NULL) {
+ DBG_ERR("out of memory\n");
+ result = -1;
+ goto out;
+ }
+ select_stmt->cols->cols[0] =
+ talloc_strdup(select_stmt->cols, default_column);
+ }
+
+ status = cli_full_connection_creds(&c,
+ lp_netbios_name(),
+ server,
+ NULL,
+ 0,
+ "IPC$",
+ "IPC",
+ samba_cmdline_get_creds(),
+ flags);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to IPC$: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ status = wsp_server_connect(talloc_tos(),
+ server,
+ ev_ctx,
+ samba_cmdline_get_lp_ctx(),
+ samba_cmdline_get_creds(),
+ c,
+ &wsp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to wsp: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ h = get_wsp_pipe(wsp_ctx);
+ if (h == NULL) {
+ DBG_ERR("Failed to communicate with server, no pipe\n");
+ result = -1;
+ goto out;
+ }
+
+ dcerpc_binding_handle_set_timeout(h,
+ DCERPC_REQUEST_TIMEOUT * 1000);
+
+ /* connect */
+ DBG_INFO("sending connect\n");
+ status = wsp_connect(talloc_tos(),
+ wsp_ctx,
+ lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
+ cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ server,
+ &is_64bit);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to wsp: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ DBG_INFO("sending query\n");
+
+ status = create_query(talloc_tos(),
+ wsp_ctx,
+ limit,
+ select_stmt,
+ &cursor);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to send query: %s)\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ DBG_INFO("sending createbindings\n");
+ /* set bindings */
+ status = create_bindings(talloc_tos(),
+ wsp_ctx,
+ select_stmt,
+ cursor,
+ &bindings_used,
+ is_64bit);
+ 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;
+ DBG_ERR("no results found\n");
+ goto out;
+ }
+
+ printf("found %d results, returning %d \n",
+ nrows,
+ limit ? MIN(nrows, limit) : nrows);
+ status = create_getrows(talloc_tos(),
+ wsp_ctx,
+ &bindings_used,
+ bindings_used.hcursor,
+ limit ? MIN(nrows, limit) : nrows,
+ custom_query,
+ is_64bit);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to retrieve rows, error: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+ result = 0;
+out:
+ TALLOC_FREE(frame);
+ return result;
+}