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/>.
22 #include "lib/util/debug.h"
23 #include "lib/cmdline/cmdline.h"
24 #include "lib/cmdline_contexts.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"
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,
47 struct wsp_request *request = NULL;
48 struct wsp_response *response = NULL;
51 DATA_BLOB unread = data_blob_null;
53 TALLOC_CTX *local_ctx = talloc_new(ctx);
56 if (local_ctx == NULL) {
57 status = NT_STATUS_NO_MEMORY;
61 response = talloc_zero(local_ctx, struct wsp_response);
63 status = NT_STATUS_NO_MEMORY;
67 request = talloc_zero(local_ctx, struct wsp_request);
69 status = NT_STATUS_NO_MEMORY;
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;
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;
86 (server_ver & WIN_VERSION_64)
87 && (client_ver & WIN_VERSION_64);
91 data_blob_free(&unread);
92 TALLOC_FREE(local_ctx);
96 static NTSTATUS create_query(TALLOC_CTX *ctx,
97 struct wsp_client_ctx *wsp_ctx,
99 t_select_stmt *select,
100 uint32_t *single_cursor)
102 struct wsp_request *request = NULL;
103 struct wsp_response *response = NULL;
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;
113 request = talloc_zero(local_ctx, struct wsp_request);
115 status = NT_STATUS_NO_MEMORY;
119 response = talloc_zero(local_ctx, struct wsp_response);
121 status = NT_STATUS_NO_MEMORY;
125 if (!create_querysearch_request(ctx, request, select)) {
126 DBG_ERR("error setting up query request message\n");
127 status = NT_STATUS_INVALID_PARAMETER;
131 request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
133 status = wsp_request_response(local_ctx,
138 if (NT_STATUS_IS_OK(status)) {
139 if (unread.length == 4) {
140 *single_cursor = IVAL(unread.data, 0);
145 data_blob_free(&unread);
146 TALLOC_FREE(local_ctx);
150 static NTSTATUS create_bindings(TALLOC_CTX *ctx,
151 struct wsp_client_ctx *wsp_ctx,
152 t_select_stmt *select,
154 struct wsp_cpmsetbindingsin *bindings_out,
157 struct wsp_request *request = NULL;
158 struct wsp_response *response = NULL;
160 DATA_BLOB unread = data_blob_null;
162 request = talloc_zero(ctx, struct wsp_request);
164 status = NT_STATUS_NO_MEMORY;
168 response = talloc_zero(ctx, struct wsp_response);
170 status = NT_STATUS_NO_MEMORY;
174 if (!create_setbindings_request(ctx,
179 DBG_ERR("Failed to create setbindings message\n");
180 status = NT_STATUS_INVALID_PARAMETER;
184 status = wsp_request_response(ctx,
189 if (NT_STATUS_IS_OK(status)) {
190 *bindings_out = request->message.cpmsetbindings;
194 data_blob_free(&unread);
198 static NTSTATUS create_querystatusex(TALLOC_CTX *ctx,
199 struct wsp_client_ctx *wsp_ctx,
203 struct wsp_request *request = NULL;
204 struct wsp_response *response = NULL;
205 struct wsp_cpmgetquerystatusexin *statusexin = NULL;
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;
215 request = talloc_zero(local_ctx, struct wsp_request);
217 status = NT_STATUS_NO_MEMORY;
221 response = talloc_zero(local_ctx, struct wsp_response);
223 status = NT_STATUS_NO_MEMORY;
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,
237 if (NT_STATUS_IS_OK(status)) {
238 *nrows = response->message.cpmgetquerystatusex.resultsfound;
242 data_blob_free(&unread);
243 TALLOC_FREE(local_ctx);
247 static NTSTATUS print_rowsreturned(
252 struct wsp_cpmsetbindingsin *bindings,
255 uint32_t rowsreturned,
256 uint32_t *rows_processed)
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;
270 rowsarray = talloc_zero_array(local_ctx,
271 struct wsp_cbasestoragevariant*,
273 if (rowsarray == NULL) {
274 status = NT_STATUS_NO_MEMORY;
278 err = extract_rowsarray(rowsarray,
287 DBG_ERR("failed to extract rows from getrows response\n");
288 status = NT_STATUS_UNSUCCESSFUL;
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;
304 for (i = 0; i < bindings->ccolumns; i++){
312 i ? ", " : "", col_str);
319 col_str = variant_as_string(
323 printf("%s", col_str);
326 TALLOC_FREE(row_ctx);
328 status = NT_STATUS_OK;
330 TALLOC_FREE(local_ctx);
331 *rows_processed = row;
335 static NTSTATUS create_getrows(TALLOC_CTX *ctx,
336 struct wsp_client_ctx *wsp_ctx,
337 struct wsp_cpmsetbindingsin *bindings,
343 struct wsp_request *request = NULL;
344 struct wsp_response *response = NULL;
346 DATA_BLOB unread = data_blob_null;
347 uint32_t bmk = 0xfffffffc;
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;
358 row_ctx = talloc_new(NULL);
360 status = NT_STATUS_UNSUCCESSFUL;
363 request = talloc_zero(row_ctx, struct wsp_request);
365 status = NT_STATUS_NO_MEMORY;
368 response = talloc_zero(row_ctx, struct wsp_response);
370 status = NT_STATUS_NO_MEMORY;
374 create_seekat_getrows_request(request,
385 status = wsp_request_response(request,
390 if (!NT_STATUS_IS_OK(status)) {
394 total_rows += response->message.cpmgetrows.rowsreturned;
395 if (response->message.cpmgetrows.rowsreturned
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;
404 &resp->seekdescription.crowseekat;
405 bmk = seekat->bmkoffset;
406 skip = seekat->cskip;
411 requested_rows = requested_rows - rowsreturned;
413 requested_rows = INITIAL_ROWS;
418 if (response->message.cpmgetrows.rowsreturned) {
419 status = print_rowsreturned(row_ctx, &unread,
424 response->message.cpmgetrows.rowsreturned,
426 if (!NT_STATUS_IS_OK(status)) {
429 current_row += rows_printed;
430 data_blob_free(&unread);
434 * response is a talloc child of row_ctz so we need to
435 * assign loop_again before we delete row_ctx
437 loop_again = response->message.cpmgetrows.rowsreturned;
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",
444 status = NT_STATUS_UNSUCCESSFUL;
447 } while (loop_again);
449 data_blob_free(&unread);
450 TALLOC_FREE(row_ctx);
454 const char *default_column = "System.ItemUrl";
456 static bool is_valid_kind(const char *kind)
458 const char* kinds[] = {"calendar",
479 char* search_kind = NULL;
483 search_kind = strlower_talloc(NULL, kind);
484 if (search_kind == NULL) {
485 DBG_ERR("couldn't convert %s to lower case\n",
490 for (i=0; i<ARRAY_SIZE(kinds); i++) {
491 if (strequal(search_kind, kinds[i])) {
497 if (found == false) {
498 DBG_ERR("Invalid kind %s\n", kind);
500 TALLOC_FREE(search_kind);
504 static char * build_default_sql(TALLOC_CTX *ctx,
507 const char *location)
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);
516 if (!is_valid_kind(kind)) {
519 sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
524 sql = talloc_asprintf(ctx,
525 "All:$=\"%s\" OR All:$<\"%s\""
526 " AND %s", phrase, phrase, sql);
528 sql = talloc_asprintf(ctx, "SELECT %s"
529 " WHERE %s", default_column, sql);
533 int main(int argc, char **argv)
537 NTSTATUS status = NT_STATUS_OK;
542 char* location = NULL;
544 bool custom_query = false;
545 const char* phrase = NULL;
546 const char* kind = NULL;
547 uint32_t limit = 500;
549 struct wsp_cpmsetbindingsin bindings_used = {0};
550 bool is_64bit = false;
551 struct poptOption long_options[] = {
559 "default is 500, specifying 0 means unlimited" },
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"
579 "specify a more complex query",
582 POPT_COMMON_CONNECTION
583 POPT_COMMON_CREDENTIALS
586 TALLOC_CTX *frame = talloc_stackframe();
587 struct tevent_context *ev_ctx
588 = samba_tevent_context_init(talloc_tos());
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;
597 samba_cmdline_init(frame,
598 SAMBA_CMDLINE_CONFIG_CLIENT,
599 false /* require_smbconf */);
601 pc = samba_popt_get_context("wspsearch",
606 poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1");
608 while ((opt = poptGetNextOpt(pc)) != -1) ;
610 if(!poptPeekArg(pc)) {
611 poptPrintUsage(pc, stderr, 0);
616 path = talloc_strdup(talloc_tos(), poptGetArg(pc));
617 if (!path || limit < 0) {
618 DBG_ERR("Invalid argument\n");
623 string_replace(path,'/','\\');
624 server = talloc_strdup(talloc_tos(), path+2);
626 DBG_ERR("Invalid argument\n");
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)
637 share = strchr_m(server,'\\');
638 if (!query && !share) {
639 DBG_ERR("Invalid argument\n");
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");
653 if (!query && (kind == NULL && phrase == NULL)) {
654 poptPrintUsage(pc, stderr, 0);
660 location = talloc_asprintf(talloc_tos(),
661 "FILE://%s/%s", server, share);
662 query = build_default_sql(talloc_tos(), kind, phrase, location);
671 printf("custom_query %d\n", custom_query);
672 select_stmt = get_wsp_sql_tree(query);
676 if (select_stmt == NULL) {
677 DBG_ERR("query failed\n");
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");
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");
697 select_stmt->cols->cols[0] =
698 talloc_strdup(select_stmt->cols, default_column);
701 status = cli_full_connection_creds(&c,
708 samba_cmdline_get_creds(),
711 if (!NT_STATUS_IS_OK(status)) {
712 DBG_ERR("failed to connect to IPC$: %s\n",
718 status = wsp_server_connect(talloc_tos(),
721 samba_cmdline_get_lp_ctx(),
722 samba_cmdline_get_creds(),
726 if (!NT_STATUS_IS_OK(status)) {
727 DBG_ERR("failed to connect to wsp: %s\n",
733 h = get_wsp_pipe(wsp_ctx);
735 DBG_ERR("Failed to communicate with server, no pipe\n");
740 dcerpc_binding_handle_set_timeout(h,
741 DCERPC_REQUEST_TIMEOUT * 1000);
744 DBG_INFO("sending connect\n");
745 status = wsp_connect(talloc_tos(),
747 lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
748 cli_credentials_get_username(
749 samba_cmdline_get_creds()),
753 if (!NT_STATUS_IS_OK(status)) {
754 DBG_ERR("failed to connect to wsp: %s\n",
760 DBG_INFO("sending query\n");
762 status = create_query(talloc_tos(),
768 if (!NT_STATUS_IS_OK(status)) {
769 DBG_ERR("failed to send query: %s)\n",
775 DBG_INFO("sending createbindings\n");
777 status = create_bindings(talloc_tos(),
783 if (!NT_STATUS_IS_OK(status)) {
784 DBG_ERR("failed to setbindings: %s)\n",
790 status = create_querystatusex(talloc_tos(),
792 bindings_used.hcursor,
796 DBG_ERR("no results found\n");
800 printf("found %d results, returning %d \n",
802 limit ? MIN(nrows, limit) : nrows);
803 status = create_getrows(talloc_tos(),
806 bindings_used.hcursor,
807 limit ? MIN(nrows, limit) : nrows,
810 if (!NT_STATUS_IS_OK(status)) {
811 DBG_ERR("Failed to retrieve rows, error: %s\n",