s3:utils: Initialize row variable in wspsearch
[samba.git] / source3 / utils / wspsearch.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *
4  *  Window Search Service
5  *
6  *  Copyright (c) Noel Power
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 #include "includes.h"
22 #include "lib/util/debug.h"
23 #include "lib/cmdline/cmdline.h"
24 #include "lib/cmdline_contexts.h"
25 #include "param.h"
26 #include "client.h"
27 #include "libsmb/proto.h"
28 #include "librpc/rpc/rpc_common.h"
29 #include "librpc/wsp/wsp_util.h"
30 #include "rpc_client/cli_pipe.h"
31 #include "rpc_client/wsp_cli.h"
32 #include "libcli/wsp/wsp_aqs.h"
33 #include "librpc/gen_ndr/ndr_wsp.h"
34 #include "librpc/gen_ndr/ndr_wsp_data.h"
35 #include "dcerpc.h"
36
37 #define WIN_VERSION_64 0x10000
38
39 /* send connectin message */
40 static NTSTATUS wsp_connect(TALLOC_CTX *ctx,
41                         struct wsp_client_ctx *wsp_ctx,
42                         const char* clientmachine,
43                         const char* clientuser,
44                         const char* server,
45                         bool *is_64bit)
46 {
47         struct wsp_request *request = NULL;
48         struct wsp_response *response = NULL;
49         uint32_t client_ver;
50         uint32_t server_ver;
51         DATA_BLOB unread = data_blob_null;
52         NTSTATUS status;
53         TALLOC_CTX *local_ctx = talloc_new(ctx);
54
55
56         if (local_ctx == NULL) {
57                 status = NT_STATUS_NO_MEMORY;
58                 goto out;
59         }
60
61         response = talloc_zero(local_ctx, struct wsp_response);
62         if (!response) {
63                 status = NT_STATUS_NO_MEMORY;
64                 goto out;
65         }
66
67         request = talloc_zero(local_ctx, struct wsp_request);
68         if (!request) {
69                 status = NT_STATUS_NO_MEMORY;
70                 goto out;
71         }
72
73         if (!init_connectin_request(local_ctx, request,
74                                clientmachine, clientuser, server)) {
75                 DBG_ERR("Failed in initialise connection message\n");
76                 status = NT_STATUS_INVALID_PARAMETER;
77                 goto out;
78         }
79
80         status =  wsp_request_response(local_ctx, wsp_ctx,
81                         request, response, &unread);
82         if (NT_STATUS_IS_OK(status)) {
83                 client_ver = request->message.cpmconnect.iclientversion;
84                 server_ver = response->message.cpmconnect.server_version;
85                 *is_64bit =
86                         (server_ver & WIN_VERSION_64)
87                         && (client_ver & WIN_VERSION_64);
88         }
89
90 out:
91         data_blob_free(&unread);
92         TALLOC_FREE(local_ctx);
93         return status;
94 }
95
96 static NTSTATUS create_query(TALLOC_CTX *ctx,
97                              struct wsp_client_ctx *wsp_ctx,
98                              uint32_t limit,
99                              t_select_stmt *select,
100                              uint32_t *single_cursor)
101 {
102         struct wsp_request *request = NULL;
103         struct wsp_response *response = NULL;
104         NTSTATUS status;
105         DATA_BLOB unread = data_blob_null;
106         TALLOC_CTX *local_ctx = talloc_new(ctx);
107
108         if (local_ctx == NULL) {
109                 status = NT_STATUS_NO_MEMORY;
110                 goto out;
111         }
112
113         request = talloc_zero(local_ctx, struct wsp_request);
114         if (!request) {
115                 status = NT_STATUS_NO_MEMORY;
116                 goto out;
117         }
118
119         response = talloc_zero(local_ctx, struct wsp_response);
120         if (!response) {
121                 status = NT_STATUS_NO_MEMORY;
122                 goto out;;
123         }
124
125         if (!create_querysearch_request(ctx, request, select)) {
126                 DBG_ERR("error setting up query request message\n");
127                 status = NT_STATUS_INVALID_PARAMETER;
128                 goto out;
129         }
130
131         request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
132
133         status = wsp_request_response(local_ctx,
134                         wsp_ctx,
135                         request,
136                         response,
137                         &unread);
138         if (NT_STATUS_IS_OK(status)) {
139                 if (unread.length == 4) {
140                         *single_cursor = IVAL(unread.data, 0);
141                 }
142         }
143
144 out:
145         data_blob_free(&unread);
146         TALLOC_FREE(local_ctx);
147         return status;
148 }
149
150 static NTSTATUS create_bindings(TALLOC_CTX *ctx,
151                                 struct wsp_client_ctx *wsp_ctx,
152                                 t_select_stmt *select,
153                                 uint32_t cursor,
154                                 struct wsp_cpmsetbindingsin *bindings_out,
155                                 bool is_64bit)
156 {
157         struct wsp_request *request = NULL;
158         struct wsp_response *response = NULL;
159         NTSTATUS status;
160         DATA_BLOB unread = data_blob_null;
161
162         request = talloc_zero(ctx, struct wsp_request);
163         if (!request) {
164                 status = NT_STATUS_NO_MEMORY;
165                 goto out;
166         }
167
168         response = talloc_zero(ctx, struct wsp_response);
169         if (!response) {
170                 status = NT_STATUS_NO_MEMORY;
171                 goto out;
172         }
173
174         if (!create_setbindings_request(ctx,
175                                 request,
176                                 select,
177                                 cursor,
178                                 is_64bit)) {
179                 DBG_ERR("Failed to create setbindings message\n");
180                 status = NT_STATUS_INVALID_PARAMETER;
181                 goto out;
182         }
183
184         status = wsp_request_response(ctx,
185                         wsp_ctx,
186                         request,
187                         response,
188                         &unread);
189         if (NT_STATUS_IS_OK(status)) {
190                 *bindings_out = request->message.cpmsetbindings;
191         }
192
193 out:
194         data_blob_free(&unread);
195         return status;
196 }
197
198 static NTSTATUS create_querystatusex(TALLOC_CTX *ctx,
199                                 struct wsp_client_ctx *wsp_ctx,
200                                 uint32_t cursor,
201                                 uint32_t *nrows)
202 {
203         struct wsp_request *request = NULL;
204         struct wsp_response *response = NULL;
205         struct wsp_cpmgetquerystatusexin *statusexin = NULL;
206         NTSTATUS status;
207         DATA_BLOB unread = data_blob_null;
208         TALLOC_CTX *local_ctx = talloc_new(ctx);
209
210         if (local_ctx == NULL) {
211                 status = NT_STATUS_NO_MEMORY;
212                 goto out;
213         }
214
215         request = talloc_zero(local_ctx, struct wsp_request);
216         if (!request) {
217                 status = NT_STATUS_NO_MEMORY;
218                 goto out;
219         }
220
221         response = talloc_zero(local_ctx, struct wsp_response);
222         if (!response) {
223                 status = NT_STATUS_NO_MEMORY;
224                 goto out;
225         }
226
227         statusexin = &request->message.cpmgetquerystatusex;
228
229         request->header.msg = CPMGETQUERYSTATUSEX;
230         statusexin->hcursor = cursor;
231         statusexin->bmk = 0xfffffffc;
232         status = wsp_request_response(local_ctx,
233                         wsp_ctx,
234                         request,
235                         response,
236                         &unread);
237         if (NT_STATUS_IS_OK(status)) {
238                 *nrows = response->message.cpmgetquerystatusex.resultsfound;
239         }
240
241 out:
242         data_blob_free(&unread);
243         TALLOC_FREE(local_ctx);
244         return status;
245 }
246
247 static NTSTATUS print_rowsreturned(
248                                 TALLOC_CTX *ctx,
249                                 DATA_BLOB *buffer,
250                                 bool is_64bit,
251                                 bool disp_all_cols,
252                                 struct wsp_cpmsetbindingsin *bindings,
253                                 uint32_t cbreserved,
254                                 uint64_t address,
255                                 uint32_t rowsreturned,
256                                 uint32_t *rows_processed)
257 {
258         NTSTATUS status;
259         uint32_t row = 0;
260         TALLOC_CTX *local_ctx = NULL;
261         struct wsp_cbasestoragevariant **rowsarray = NULL;
262         enum ndr_err_code err;
263
264         local_ctx = talloc_init("results");
265         if (local_ctx == NULL) {
266                 status = NT_STATUS_NO_MEMORY;
267                 goto out;
268         }
269
270         rowsarray = talloc_zero_array(local_ctx,
271                         struct wsp_cbasestoragevariant*,
272                         rowsreturned);
273         if (rowsarray == NULL) {
274                 status = NT_STATUS_NO_MEMORY;
275                 goto out;
276         }
277
278         err = extract_rowsarray(rowsarray,
279                         buffer,
280                         is_64bit,
281                         bindings,
282                         cbreserved,
283                         address,
284                         rowsreturned,
285                         rowsarray);
286         if (err) {
287                 DBG_ERR("failed to extract rows from getrows response\n");
288                 status = NT_STATUS_UNSUCCESSFUL;
289                 goto out;
290         }
291
292         for(row = 0; row < rowsreturned; row++) {
293                 TALLOC_CTX *row_ctx = NULL;
294                 const char *col_str = NULL;
295
296                 row_ctx = talloc_init("row");
297                 if (row_ctx == NULL) {
298                         status = NT_STATUS_NO_MEMORY;
299                         goto out;
300                 }
301
302                 if (disp_all_cols) {
303                         int i;
304                         for (i = 0; i < bindings->ccolumns; i++){
305                                 col_str =
306                                         variant_as_string(
307                                                 row_ctx,
308                                                 &rowsarray[row][i],
309                                                 true);
310                                 if (col_str) {
311                                         printf("%s%s",
312                                                 i ? ", " : "", col_str);
313                                 } else {
314                                         printf("%sN/A",
315                                                 i ? ", " : "");
316                                 }
317                         }
318                 } else {
319                         col_str = variant_as_string(
320                                         row_ctx,
321                                         &rowsarray[row][0],
322                                         true);
323                         printf("%s", col_str);
324                 }
325                 printf("\n");
326                 TALLOC_FREE(row_ctx);
327         }
328         status = NT_STATUS_OK;
329 out:
330         TALLOC_FREE(local_ctx);
331         *rows_processed = row;
332         return status;
333 }
334
335 static NTSTATUS create_getrows(TALLOC_CTX *ctx,
336                                struct wsp_client_ctx *wsp_ctx,
337                                struct wsp_cpmsetbindingsin *bindings,
338                                uint32_t cursor,
339                                uint32_t nrows,
340                                bool disp_all_cols,
341                                bool is_64bit)
342 {
343         struct wsp_request *request = NULL;
344         struct wsp_response *response = NULL;
345         NTSTATUS status;
346         DATA_BLOB unread = data_blob_null;
347         uint32_t bmk = 0xfffffffc;
348         uint32_t skip = 0;
349         uint32_t total_rows = 0;
350         uint32_t INITIAL_ROWS = 32;
351         uint32_t requested_rows = INITIAL_ROWS;
352         uint32_t rows_printed;
353         uint32_t current_row = 0;
354         TALLOC_CTX *row_ctx;
355         bool loop_again;
356
357         do {
358                 row_ctx = talloc_new(NULL);
359                 if (!row_ctx) {
360                         status = NT_STATUS_UNSUCCESSFUL;
361                         goto out;
362                 }
363                 request = talloc_zero(row_ctx, struct wsp_request);
364                 if (!request) {
365                         status = NT_STATUS_NO_MEMORY;
366                         goto out;
367                 }
368                 response = talloc_zero(row_ctx, struct wsp_response);
369                 if (!response) {
370                         status = NT_STATUS_NO_MEMORY;
371                         goto out;
372                 }
373
374                 create_seekat_getrows_request(request,
375                                         request,
376                                         cursor,
377                                         bmk,
378                                         skip,
379                                         requested_rows,
380                                         40,
381                                         0xDEAbd860,
382                                         bindings->brow,
383                                         0);
384
385                 status = wsp_request_response(request,
386                                 wsp_ctx,
387                                 request,
388                                 response,
389                                 &unread);
390                 if (!NT_STATUS_IS_OK(status)) {
391                         goto out;
392                 }
393
394                 total_rows += response->message.cpmgetrows.rowsreturned;
395                 if (response->message.cpmgetrows.rowsreturned
396                    != requested_rows) {
397                         uint32_t rowsreturned =
398                                 response->message.cpmgetrows.rowsreturned;
399                         if (response->message.cpmgetrows.etype == EROWSEEKAT) {
400                                 struct wsp_cpmgetrowsout *resp;
401                                 struct wsp_crowseekat *seekat;
402                                 resp = &response->message.cpmgetrows;
403                                 seekat =
404                                         &resp->seekdescription.crowseekat;
405                                 bmk = seekat->bmkoffset;
406                                 skip = seekat->cskip;
407                         } else {
408                                 bmk = 0xfffffffc;
409                                 skip = total_rows;
410                         }
411                         requested_rows = requested_rows - rowsreturned;
412                 } else {
413                         requested_rows = INITIAL_ROWS;
414                         bmk = 0xfffffffc;
415                         skip = total_rows;
416                 }
417
418                 if (response->message.cpmgetrows.rowsreturned) {
419                         status = print_rowsreturned(row_ctx, &unread,
420                                 is_64bit,
421                                 disp_all_cols,
422                                 bindings, 40,
423                                 0xDEAbd860,
424                                 response->message.cpmgetrows.rowsreturned,
425                                 &rows_printed);
426                         if (!NT_STATUS_IS_OK(status)) {
427                                 goto out;
428                         }
429                         current_row += rows_printed;
430                         data_blob_free(&unread);
431                 }
432
433                 /*
434                  * response is a talloc child of row_ctz so we need to
435                  * assign loop_again before we delete row_ctx
436                  */
437                 loop_again = response->message.cpmgetrows.rowsreturned;
438
439                 TALLOC_FREE(row_ctx);
440                 if (nrows && total_rows > nrows) {
441                         DBG_ERR("Something is wrong, results returned %d "
442                                 "exceed expected number of results %d\n",
443                                 total_rows, nrows);
444                         status = NT_STATUS_UNSUCCESSFUL;
445                         goto out;
446                 }
447         } while (loop_again);
448 out:
449         data_blob_free(&unread);
450         TALLOC_FREE(row_ctx);
451         return status;
452 }
453
454 const char *default_column = "System.ItemUrl";
455
456 static bool is_valid_kind(const char *kind)
457 {
458         const char* kinds[] = {"calendar",
459                 "communication",
460                 "contact",
461                 "document",
462                 "email",
463                 "feed",
464                 "folder",
465                 "game",
466                 "instantMessage",
467                 "journal",
468                 "link",
469                 "movie",
470                 "music",
471                 "note",
472                 "picture",
473                 "program",
474                 "recordedtv",
475                 "searchfolder",
476                 "task",
477                 "video",
478                 "webhistory"};
479         char* search_kind = NULL;
480         int i;
481         bool found = false;
482
483         search_kind = strlower_talloc(NULL, kind);
484         if (search_kind == NULL) {
485                 DBG_ERR("couldn't convert %s to lower case\n",
486                                 kind);
487                 return NULL;
488         }
489
490         for (i=0; i<ARRAY_SIZE(kinds); i++) {
491                 if (strequal(search_kind, kinds[i])) {
492                         found = true;
493                         break;
494                 }
495         }
496
497         if (found == false) {
498                 DBG_ERR("Invalid kind %s\n", kind);
499         }
500         TALLOC_FREE(search_kind);
501         return found;
502 }
503
504 static char * build_default_sql(TALLOC_CTX *ctx,
505                                 const char *kind,
506                                 const char *phrase,
507                                 const char *location)
508 {
509         char *sql = NULL;
510         /* match what windows clients do */
511         sql = talloc_asprintf(ctx,
512                 "Scope:\"%s\"  AND NOT System.Shell.SFGAOFlagsStrings:hidden"
513                 "  AND NOT System.Shell.OmitFromView:true", location);
514
515         if (kind) {
516                 if (!is_valid_kind(kind)) {
517                         return NULL;
518                 }
519                 sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
520                                         kind, sql);
521         }
522
523         if (phrase) {
524                 sql = talloc_asprintf(ctx,
525                                 "All:$=\"%s\" OR All:$<\"%s\""
526                                 " AND %s", phrase, phrase, sql);
527         }
528         sql =  talloc_asprintf(ctx, "SELECT %s"
529                                 " WHERE %s", default_column, sql);
530         return sql;
531 }
532
533 int main(int argc, char **argv)
534 {
535         int opt;
536         int result = 0;
537         NTSTATUS status = NT_STATUS_OK;
538         poptContext pc;
539         char* server = NULL;
540         char* share = NULL;
541         char* path = NULL;
542         char* location = NULL;
543         char* query = NULL;
544         bool custom_query = false;
545         const char* phrase = NULL;
546         const char* kind = NULL;
547         uint32_t limit = 500;
548         uint32_t nrows = 0;
549         struct wsp_cpmsetbindingsin bindings_used = {0};
550         bool is_64bit = false;
551         struct poptOption long_options[] = {
552                 POPT_AUTOHELP
553                 { "limit",
554                         0,
555                         POPT_ARG_INT,
556                         &limit,
557                         0,
558                         "limit results",
559                         "default is 500, specifying 0 means unlimited" },
560                 { "search",
561                         0,
562                         POPT_ARG_STRING,
563                         &phrase,
564                         0,
565                         "Search phrase",
566                         "phrase" },
567                 { "kind", 0, POPT_ARG_STRING, &kind, 0,
568                         "Kind of thing to search for [Calendar|Communication|"
569                         "Contact|Document|Email|Feed|Folder|Game|"
570                         "InstantMessage|Journal|Link|Movie|Music|Note|Picture|"
571                         "Program|RecordedTV|SearchFolder|Task|Video"
572                         "|WebHistory]",
573                         "kind" },
574                 { "query",
575                         0,
576                         POPT_ARG_STRING,
577                         &query,
578                         0,
579                         "specify a more complex query",
580                         "query" },
581                 POPT_COMMON_SAMBA
582                 POPT_COMMON_CONNECTION
583                 POPT_COMMON_CREDENTIALS
584                 POPT_TABLEEND
585         };
586         TALLOC_CTX *frame = talloc_stackframe();
587         struct tevent_context *ev_ctx
588                 =  samba_tevent_context_init(talloc_tos());
589         uint32_t cursor = 0;
590         struct wsp_client_ctx *wsp_ctx = NULL;
591         t_select_stmt *select_stmt = NULL;
592         const char **const_argv = discard_const_p(const char *, argv);
593         struct dcerpc_binding_handle *h = NULL;
594         struct cli_state *c = NULL;
595         uint32_t flags = CLI_FULL_CONNECTION_IPC;
596
597         samba_cmdline_init(frame,
598                            SAMBA_CMDLINE_CONFIG_CLIENT,
599                            false /* require_smbconf */);
600
601         pc = samba_popt_get_context("wspsearch",
602                         argc,
603                         const_argv,
604                         long_options,
605                         0);
606         poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1");
607
608         while ((opt = poptGetNextOpt(pc)) != -1) ;
609
610         if(!poptPeekArg(pc)) {
611                 poptPrintUsage(pc, stderr, 0);
612                 result = -1;
613                 goto out;
614         }
615
616         path = talloc_strdup(talloc_tos(), poptGetArg(pc));
617         if (!path || limit < 0) {
618                 DBG_ERR("Invalid argument\n");
619                 result = -1;
620                 goto out;
621         }
622
623         string_replace(path,'/','\\');
624         server = talloc_strdup(talloc_tos(), path+2);
625         if (!server) {
626                 DBG_ERR("Invalid argument\n");
627                 return -1;
628         }
629
630         if (server) {
631                 /*
632                  * if we specify --query then we don't need actually need the
633                  * share part, if it is specified then we don't care as we
634                  * expect the scope to be part of the query (and if it isn't
635                  * then it will probably fail anyway)
636                  */
637                 share = strchr_m(server,'\\');
638                 if (!query && !share) {
639                         DBG_ERR("Invalid argument\n");
640                         return -1;
641                 }
642                 if (share) {
643                         *share = 0;
644                         share++;
645                 }
646         }
647
648         DBG_INFO("server name is %s\n", server ? server : "N/A");
649         DBG_INFO("share name is %s\n", share ? share : "N/A");
650         DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A");
651         DBG_INFO("search kind is %s\n", kind ? kind : "N/A");
652
653         if (!query && (kind == NULL && phrase == NULL)) {
654                 poptPrintUsage(pc, stderr, 0);
655                 result = -1;
656                 goto out;
657         }
658
659         if (!query) {
660                 location = talloc_asprintf(talloc_tos(),
661                                 "FILE://%s/%s", server, share);
662                 query = build_default_sql(talloc_tos(), kind, phrase, location);
663                 if (!query) {
664                         result = -1;
665                         goto out;
666                 }
667         } else {
668                 custom_query = true;
669         }
670
671         printf("custom_query %d\n", custom_query);
672         select_stmt = get_wsp_sql_tree(query);
673
674         poptFreeContext(pc);
675
676         if (select_stmt == NULL) {
677                 DBG_ERR("query failed\n");
678                 result = -1;
679                 goto out;
680         }
681
682         if (select_stmt->cols == NULL) {
683                 select_stmt->cols = talloc_zero(select_stmt, t_col_list);
684                 if (select_stmt->cols == NULL) {
685                         DBG_ERR("out of memory\n");
686                         result = -1;
687                         goto out;
688                 }
689                 select_stmt->cols->num_cols = 1;
690                 select_stmt->cols->cols =
691                         talloc_zero_array(select_stmt->cols, char*, 1);
692                 if (select_stmt->cols->cols == NULL) {
693                         DBG_ERR("out of memory\n");
694                         result = -1;
695                         goto out;
696                 }
697                 select_stmt->cols->cols[0] =
698                         talloc_strdup(select_stmt->cols, default_column);
699         }
700
701         status =  cli_full_connection_creds(&c,
702                                    lp_netbios_name(),
703                                    server,
704                                    NULL,
705                                    0,
706                                    "IPC$",
707                                    "IPC",
708                                    samba_cmdline_get_creds(),
709                                    flags);
710
711         if (!NT_STATUS_IS_OK(status)) {
712                 DBG_ERR("failed to connect to IPC$: %s\n",
713                       nt_errstr(status));
714                 result = -1;
715                 goto out;
716         }
717
718         status = wsp_server_connect(talloc_tos(),
719                         server,
720                         ev_ctx,
721                         samba_cmdline_get_lp_ctx(),
722                         samba_cmdline_get_creds(),
723                         c,
724                         &wsp_ctx);
725
726         if (!NT_STATUS_IS_OK(status)) {
727                 DBG_ERR("failed to connect to wsp: %s\n",
728                       nt_errstr(status));
729                 result = -1;
730                 goto out;
731         }
732
733         h = get_wsp_pipe(wsp_ctx);
734         if (h == NULL) {
735                 DBG_ERR("Failed to communicate with server, no pipe\n");
736                 result = -1;
737                 goto out;
738         }
739
740         dcerpc_binding_handle_set_timeout(h,
741                                           DCERPC_REQUEST_TIMEOUT * 1000);
742
743         /* connect */
744         DBG_INFO("sending connect\n");
745         status = wsp_connect(talloc_tos(),
746                          wsp_ctx,
747                          lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
748                          cli_credentials_get_username(
749                                  samba_cmdline_get_creds()),
750                          server,
751                          &is_64bit);
752
753         if (!NT_STATUS_IS_OK(status)) {
754                 DBG_ERR("failed to connect to wsp: %s\n",
755                       nt_errstr(status));
756                 result = -1;
757                 goto out;
758         }
759
760         DBG_INFO("sending query\n");
761
762         status = create_query(talloc_tos(),
763                         wsp_ctx,
764                         limit,
765                         select_stmt,
766                         &cursor);
767
768         if (!NT_STATUS_IS_OK(status)) {
769                 DBG_ERR("failed to send query: %s)\n",
770                       nt_errstr(status));
771                 result = -1;
772                 goto out;
773         }
774
775         DBG_INFO("sending createbindings\n");
776         /* set bindings */
777         status = create_bindings(talloc_tos(),
778                         wsp_ctx,
779                         select_stmt,
780                         cursor,
781                         &bindings_used,
782                         is_64bit);
783         if (!NT_STATUS_IS_OK(status)) {
784                 DBG_ERR("failed to setbindings: %s)\n",
785                       nt_errstr(status));
786                 result = -1;
787                 goto out;
788         }
789
790         status = create_querystatusex(talloc_tos(),
791                                       wsp_ctx,
792                                       bindings_used.hcursor,
793                                       &nrows);
794         if (!nrows) {
795                 result = 0;
796                 DBG_ERR("no results found\n");
797                 goto out;
798         }
799
800         printf("found %d results, returning %d \n",
801                         nrows,
802                         limit ? MIN(nrows, limit) : nrows);
803         status = create_getrows(talloc_tos(),
804                                 wsp_ctx,
805                                 &bindings_used,
806                                 bindings_used.hcursor,
807                                 limit ? MIN(nrows, limit) : nrows,
808                                 custom_query,
809                                 is_64bit);
810         if (!NT_STATUS_IS_OK(status)) {
811                 DBG_ERR("Failed to retrieve rows, error: %s\n",
812                         nt_errstr(status));
813                 result = -1;
814                 goto out;
815         }
816         result = 0;
817 out:
818         TALLOC_FREE(frame);
819         return result;
820 }