s3:utils: Remove condition that cannot be true (CID 1548341)
[Samba.git] / source3 / utils / wspsearch.c
blob2c56c97736b9a4eb8b76520471689970e0e07fc9
1 /*
2 * Unix SMB/CIFS implementation.
4 * Window Search Service
6 * Copyright (c) Noel Power
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.
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.
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/>.
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"
37 #define WIN_VERSION_64 0x10000
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)
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);
56 if (local_ctx == NULL) {
57 status = NT_STATUS_NO_MEMORY;
58 goto out;
61 response = talloc_zero(local_ctx, struct wsp_response);
62 if (!response) {
63 status = NT_STATUS_NO_MEMORY;
64 goto out;
67 request = talloc_zero(local_ctx, struct wsp_request);
68 if (!request) {
69 status = NT_STATUS_NO_MEMORY;
70 goto out;
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;
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);
90 out:
91 data_blob_free(&unread);
92 TALLOC_FREE(local_ctx);
93 return status;
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)
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);
108 if (local_ctx == NULL) {
109 status = NT_STATUS_NO_MEMORY;
110 goto out;
113 request = talloc_zero(local_ctx, struct wsp_request);
114 if (!request) {
115 status = NT_STATUS_NO_MEMORY;
116 goto out;
119 response = talloc_zero(local_ctx, struct wsp_response);
120 if (!response) {
121 status = NT_STATUS_NO_MEMORY;
122 goto out;;
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;
131 request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
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);
144 out:
145 data_blob_free(&unread);
146 TALLOC_FREE(local_ctx);
147 return status;
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)
157 struct wsp_request *request = NULL;
158 struct wsp_response *response = NULL;
159 NTSTATUS status;
160 DATA_BLOB unread = data_blob_null;
162 request = talloc_zero(ctx, struct wsp_request);
163 if (!request) {
164 status = NT_STATUS_NO_MEMORY;
165 goto out;
168 response = talloc_zero(ctx, struct wsp_response);
169 if (!response) {
170 status = NT_STATUS_NO_MEMORY;
171 goto out;
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;
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;
193 out:
194 data_blob_free(&unread);
195 return status;
198 static NTSTATUS create_querystatusex(TALLOC_CTX *ctx,
199 struct wsp_client_ctx *wsp_ctx,
200 uint32_t cursor,
201 uint32_t *nrows)
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);
210 if (local_ctx == NULL) {
211 status = NT_STATUS_NO_MEMORY;
212 goto out;
215 request = talloc_zero(local_ctx, struct wsp_request);
216 if (!request) {
217 status = NT_STATUS_NO_MEMORY;
218 goto out;
221 response = talloc_zero(local_ctx, struct wsp_response);
222 if (!response) {
223 status = NT_STATUS_NO_MEMORY;
224 goto out;
227 statusexin = &request->message.cpmgetquerystatusex;
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;
241 out:
242 data_blob_free(&unread);
243 TALLOC_FREE(local_ctx);
244 return status;
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)
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;
264 local_ctx = talloc_init("results");
265 if (local_ctx == NULL) {
266 status = NT_STATUS_NO_MEMORY;
267 goto out;
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;
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;
292 for(row = 0; row < rowsreturned; row++) {
293 TALLOC_CTX *row_ctx = NULL;
294 const char *col_str = NULL;
296 row_ctx = talloc_init("row");
297 if (row_ctx == NULL) {
298 status = NT_STATUS_NO_MEMORY;
299 goto out;
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 ? ", " : "");
318 } else {
319 col_str = variant_as_string(
320 row_ctx,
321 &rowsarray[row][0],
322 true);
323 printf("%s", col_str);
325 printf("\n");
326 TALLOC_FREE(row_ctx);
328 status = NT_STATUS_OK;
329 out:
330 TALLOC_FREE(local_ctx);
331 *rows_processed = row;
332 return status;
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)
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 TALLOC_CTX *row_ctx;
354 bool loop_again;
356 do {
357 row_ctx = talloc_new(NULL);
358 if (!row_ctx) {
359 status = NT_STATUS_UNSUCCESSFUL;
360 goto out;
362 request = talloc_zero(row_ctx, struct wsp_request);
363 if (!request) {
364 status = NT_STATUS_NO_MEMORY;
365 goto out;
367 response = talloc_zero(row_ctx, struct wsp_response);
368 if (!response) {
369 status = NT_STATUS_NO_MEMORY;
370 goto out;
373 create_seekat_getrows_request(request,
374 request,
375 cursor,
376 bmk,
377 skip,
378 requested_rows,
380 0xDEAbd860,
381 bindings->brow,
384 status = wsp_request_response(request,
385 wsp_ctx,
386 request,
387 response,
388 &unread);
389 if (!NT_STATUS_IS_OK(status)) {
390 goto out;
393 total_rows += response->message.cpmgetrows.rowsreturned;
394 if (response->message.cpmgetrows.rowsreturned
395 != requested_rows) {
396 uint32_t rowsreturned =
397 response->message.cpmgetrows.rowsreturned;
398 if (response->message.cpmgetrows.etype == EROWSEEKAT) {
399 struct wsp_cpmgetrowsout *resp;
400 struct wsp_crowseekat *seekat;
401 resp = &response->message.cpmgetrows;
402 seekat =
403 &resp->seekdescription.crowseekat;
404 bmk = seekat->bmkoffset;
405 skip = seekat->cskip;
406 } else {
407 bmk = 0xfffffffc;
408 skip = total_rows;
410 requested_rows = requested_rows - rowsreturned;
411 } else {
412 requested_rows = INITIAL_ROWS;
413 bmk = 0xfffffffc;
414 skip = total_rows;
417 if (response->message.cpmgetrows.rowsreturned) {
418 status = print_rowsreturned(row_ctx, &unread,
419 is_64bit,
420 disp_all_cols,
421 bindings, 40,
422 0xDEAbd860,
423 response->message.cpmgetrows.rowsreturned,
424 &rows_printed);
425 if (!NT_STATUS_IS_OK(status)) {
426 goto out;
428 data_blob_free(&unread);
432 * response is a talloc child of row_ctx so we need to
433 * assign loop_again before we delete row_ctx
435 loop_again = response->message.cpmgetrows.rowsreturned;
437 TALLOC_FREE(row_ctx);
438 if (nrows && total_rows > nrows) {
439 DBG_ERR("Something is wrong, results returned %d "
440 "exceed expected number of results %d\n",
441 total_rows, nrows);
442 status = NT_STATUS_UNSUCCESSFUL;
443 goto out;
445 } while (loop_again);
446 out:
447 data_blob_free(&unread);
448 TALLOC_FREE(row_ctx);
449 return status;
452 const char *default_column = "System.ItemUrl";
454 static bool is_valid_kind(const char *kind)
456 const char* kinds[] = {"calendar",
457 "communication",
458 "contact",
459 "document",
460 "email",
461 "feed",
462 "folder",
463 "game",
464 "instantMessage",
465 "journal",
466 "link",
467 "movie",
468 "music",
469 "note",
470 "picture",
471 "program",
472 "recordedtv",
473 "searchfolder",
474 "task",
475 "video",
476 "webhistory"};
477 char* search_kind = NULL;
478 int i;
479 bool found = false;
481 search_kind = strlower_talloc(NULL, kind);
482 if (search_kind == NULL) {
483 DBG_ERR("couldn't convert %s to lower case\n",
484 kind);
485 return NULL;
488 for (i=0; i<ARRAY_SIZE(kinds); i++) {
489 if (strequal(search_kind, kinds[i])) {
490 found = true;
491 break;
495 if (found == false) {
496 DBG_ERR("Invalid kind %s\n", kind);
498 TALLOC_FREE(search_kind);
499 return found;
502 static char * build_default_sql(TALLOC_CTX *ctx,
503 const char *kind,
504 const char *phrase,
505 const char *location)
507 char *sql = NULL;
508 /* match what windows clients do */
509 sql = talloc_asprintf(ctx,
510 "Scope:\"%s\" AND NOT System.Shell.SFGAOFlagsStrings:hidden"
511 " AND NOT System.Shell.OmitFromView:true", location);
513 if (kind) {
514 if (!is_valid_kind(kind)) {
515 return NULL;
517 sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
518 kind, sql);
521 if (phrase) {
522 sql = talloc_asprintf(ctx,
523 "All:$=\"%s\" OR All:$<\"%s\""
524 " AND %s", phrase, phrase, sql);
526 sql = talloc_asprintf(ctx, "SELECT %s"
527 " WHERE %s", default_column, sql);
528 return sql;
531 int main(int argc, char **argv)
533 int opt;
534 int result = 0;
535 NTSTATUS status = NT_STATUS_OK;
536 poptContext pc;
537 char* server = NULL;
538 char* share = NULL;
539 char* path = NULL;
540 char* location = NULL;
541 char* query = NULL;
542 bool custom_query = false;
543 const char* phrase = NULL;
544 const char* kind = NULL;
545 uint32_t limit = 500;
546 uint32_t nrows = 0;
547 struct wsp_cpmsetbindingsin bindings_used = {0};
548 bool is_64bit = false;
549 struct poptOption long_options[] = {
550 POPT_AUTOHELP
551 { "limit",
553 POPT_ARG_INT,
554 &limit,
556 "limit results",
557 "default is 500, specifying 0 means unlimited" },
558 { "search",
560 POPT_ARG_STRING,
561 &phrase,
563 "Search phrase",
564 "phrase" },
565 { "kind", 0, POPT_ARG_STRING, &kind, 0,
566 "Kind of thing to search for [Calendar|Communication|"
567 "Contact|Document|Email|Feed|Folder|Game|"
568 "InstantMessage|Journal|Link|Movie|Music|Note|Picture|"
569 "Program|RecordedTV|SearchFolder|Task|Video"
570 "|WebHistory]",
571 "kind" },
572 { "query",
574 POPT_ARG_STRING,
575 &query,
577 "specify a more complex query",
578 "query" },
579 POPT_COMMON_SAMBA
580 POPT_COMMON_CONNECTION
581 POPT_COMMON_CREDENTIALS
582 POPT_TABLEEND
584 TALLOC_CTX *frame = talloc_stackframe();
585 struct tevent_context *ev_ctx
586 = samba_tevent_context_init(talloc_tos());
587 uint32_t cursor = 0;
588 struct wsp_client_ctx *wsp_ctx = NULL;
589 t_select_stmt *select_stmt = NULL;
590 const char **const_argv = discard_const_p(const char *, argv);
591 struct dcerpc_binding_handle *h = NULL;
592 struct cli_state *c = NULL;
593 uint32_t flags = CLI_FULL_CONNECTION_IPC;
594 bool ok;
596 ok = samba_cmdline_init(frame,
597 SAMBA_CMDLINE_CONFIG_CLIENT,
598 false /* require_smbconf */);
599 if (!ok) {
600 DBG_ERR("Failed to set up cmdline parser\n");
601 result = -1;
602 goto out;
605 pc = samba_popt_get_context("wspsearch",
606 argc,
607 const_argv,
608 long_options,
610 poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1");
612 while ((opt = poptGetNextOpt(pc)) != -1) ;
614 if(!poptPeekArg(pc)) {
615 poptPrintUsage(pc, stderr, 0);
616 result = -1;
617 goto out;
620 path = talloc_strdup(talloc_tos(), poptGetArg(pc));
621 if (!path) {
622 DBG_ERR("Invalid argument\n");
623 result = -1;
624 goto out;
627 string_replace(path,'/','\\');
628 server = talloc_strdup(talloc_tos(), path+2);
629 if (!server) {
630 DBG_ERR("Invalid argument\n");
631 return -1;
634 if (server) {
636 * if we specify --query then we don't need actually need the
637 * share part, if it is specified then we don't care as we
638 * expect the scope to be part of the query (and if it isn't
639 * then it will probably fail anyway)
641 share = strchr_m(server,'\\');
642 if (!query && !share) {
643 DBG_ERR("Invalid argument\n");
644 return -1;
646 if (share) {
647 *share = 0;
648 share++;
652 DBG_INFO("server name is %s\n", server ? server : "N/A");
653 DBG_INFO("share name is %s\n", share ? share : "N/A");
654 DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A");
655 DBG_INFO("search kind is %s\n", kind ? kind : "N/A");
657 if (!query && (kind == NULL && phrase == NULL)) {
658 poptPrintUsage(pc, stderr, 0);
659 result = -1;
660 goto out;
663 if (!query) {
664 location = talloc_asprintf(talloc_tos(),
665 "FILE://%s/%s", server, share);
666 query = build_default_sql(talloc_tos(), kind, phrase, location);
667 if (!query) {
668 result = -1;
669 goto out;
671 } else {
672 custom_query = true;
675 printf("custom_query %d\n", custom_query);
676 select_stmt = get_wsp_sql_tree(query);
678 poptFreeContext(pc);
680 if (select_stmt == NULL) {
681 DBG_ERR("query failed\n");
682 result = -1;
683 goto out;
686 if (select_stmt->cols == NULL) {
687 select_stmt->cols = talloc_zero(select_stmt, t_col_list);
688 if (select_stmt->cols == NULL) {
689 DBG_ERR("out of memory\n");
690 result = -1;
691 goto out;
693 select_stmt->cols->num_cols = 1;
694 select_stmt->cols->cols =
695 talloc_zero_array(select_stmt->cols, char*, 1);
696 if (select_stmt->cols->cols == NULL) {
697 DBG_ERR("out of memory\n");
698 result = -1;
699 goto out;
701 select_stmt->cols->cols[0] =
702 talloc_strdup(select_stmt->cols, default_column);
705 status = cli_full_connection_creds(&c,
706 lp_netbios_name(),
707 server,
708 NULL,
710 "IPC$",
711 "IPC",
712 samba_cmdline_get_creds(),
713 flags);
715 if (!NT_STATUS_IS_OK(status)) {
716 DBG_ERR("failed to connect to IPC$: %s\n",
717 nt_errstr(status));
718 result = -1;
719 goto out;
722 status = wsp_server_connect(talloc_tos(),
723 server,
724 ev_ctx,
725 samba_cmdline_get_lp_ctx(),
726 samba_cmdline_get_creds(),
728 &wsp_ctx);
730 if (!NT_STATUS_IS_OK(status)) {
731 DBG_ERR("failed to connect to wsp: %s\n",
732 nt_errstr(status));
733 result = -1;
734 goto out;
737 h = get_wsp_pipe(wsp_ctx);
738 if (h == NULL) {
739 DBG_ERR("Failed to communicate with server, no pipe\n");
740 result = -1;
741 goto out;
744 dcerpc_binding_handle_set_timeout(h,
745 DCERPC_REQUEST_TIMEOUT * 1000);
747 /* connect */
748 DBG_INFO("sending connect\n");
749 status = wsp_connect(talloc_tos(),
750 wsp_ctx,
751 lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
752 cli_credentials_get_username(
753 samba_cmdline_get_creds()),
754 server,
755 &is_64bit);
757 if (!NT_STATUS_IS_OK(status)) {
758 DBG_ERR("failed to connect to wsp: %s\n",
759 nt_errstr(status));
760 result = -1;
761 goto out;
764 DBG_INFO("sending query\n");
766 status = create_query(talloc_tos(),
767 wsp_ctx,
768 limit,
769 select_stmt,
770 &cursor);
772 if (!NT_STATUS_IS_OK(status)) {
773 DBG_ERR("failed to send query: %s)\n",
774 nt_errstr(status));
775 result = -1;
776 goto out;
779 DBG_INFO("sending createbindings\n");
780 /* set bindings */
781 status = create_bindings(talloc_tos(),
782 wsp_ctx,
783 select_stmt,
784 cursor,
785 &bindings_used,
786 is_64bit);
787 if (!NT_STATUS_IS_OK(status)) {
788 DBG_ERR("failed to setbindings: %s)\n",
789 nt_errstr(status));
790 result = -1;
791 goto out;
794 status = create_querystatusex(talloc_tos(),
795 wsp_ctx,
796 bindings_used.hcursor,
797 &nrows);
798 if (!nrows) {
799 result = 0;
800 DBG_ERR("no results found\n");
801 goto out;
804 printf("found %d results, returning %d \n",
805 nrows,
806 limit ? MIN(nrows, limit) : nrows);
807 status = create_getrows(talloc_tos(),
808 wsp_ctx,
809 &bindings_used,
810 bindings_used.hcursor,
811 limit ? MIN(nrows, limit) : nrows,
812 custom_query,
813 is_64bit);
814 if (!NT_STATUS_IS_OK(status)) {
815 DBG_ERR("Failed to retrieve rows, error: %s\n",
816 nt_errstr(status));
817 result = -1;
818 goto out;
820 result = 0;
821 out:
822 TALLOC_FREE(frame);
823 return result;