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
;
357 row_ctx
= talloc_new(NULL
);
359 status
= NT_STATUS_UNSUCCESSFUL
;
362 request
= talloc_zero(row_ctx
, struct wsp_request
);
364 status
= NT_STATUS_NO_MEMORY
;
367 response
= talloc_zero(row_ctx
, struct wsp_response
);
369 status
= NT_STATUS_NO_MEMORY
;
373 create_seekat_getrows_request(request
,
384 status
= wsp_request_response(request
,
389 if (!NT_STATUS_IS_OK(status
)) {
393 total_rows
+= response
->message
.cpmgetrows
.rowsreturned
;
394 if (response
->message
.cpmgetrows
.rowsreturned
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
;
403 &resp
->seekdescription
.crowseekat
;
404 bmk
= seekat
->bmkoffset
;
405 skip
= seekat
->cskip
;
410 requested_rows
= requested_rows
- rowsreturned
;
412 requested_rows
= INITIAL_ROWS
;
417 if (response
->message
.cpmgetrows
.rowsreturned
) {
418 status
= print_rowsreturned(row_ctx
, &unread
,
423 response
->message
.cpmgetrows
.rowsreturned
,
425 if (!NT_STATUS_IS_OK(status
)) {
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",
442 status
= NT_STATUS_UNSUCCESSFUL
;
445 } while (loop_again
);
447 data_blob_free(&unread
);
448 TALLOC_FREE(row_ctx
);
452 const char *default_column
= "System.ItemUrl";
454 static bool is_valid_kind(const char *kind
)
456 const char* kinds
[] = {"calendar",
477 char* search_kind
= NULL
;
481 search_kind
= strlower_talloc(NULL
, kind
);
482 if (search_kind
== NULL
) {
483 DBG_ERR("couldn't convert %s to lower case\n",
488 for (i
=0; i
<ARRAY_SIZE(kinds
); i
++) {
489 if (strequal(search_kind
, kinds
[i
])) {
495 if (found
== false) {
496 DBG_ERR("Invalid kind %s\n", kind
);
498 TALLOC_FREE(search_kind
);
502 static char * build_default_sql(TALLOC_CTX
*ctx
,
505 const char *location
)
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
);
514 if (!is_valid_kind(kind
)) {
517 sql
= talloc_asprintf(ctx
, "System.Kind:%s AND %s",
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
);
531 int main(int argc
, char **argv
)
535 NTSTATUS status
= NT_STATUS_OK
;
540 char* location
= NULL
;
542 bool custom_query
= false;
543 const char* phrase
= NULL
;
544 const char* kind
= NULL
;
545 uint32_t limit
= 500;
547 struct wsp_cpmsetbindingsin bindings_used
= {0};
548 bool is_64bit
= false;
549 struct poptOption long_options
[] = {
557 "default is 500, specifying 0 means unlimited" },
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"
577 "specify a more complex query",
580 POPT_COMMON_CONNECTION
581 POPT_COMMON_CREDENTIALS
584 TALLOC_CTX
*frame
= talloc_stackframe();
585 struct tevent_context
*ev_ctx
586 = samba_tevent_context_init(talloc_tos());
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
;
596 ok
= samba_cmdline_init(frame
,
597 SAMBA_CMDLINE_CONFIG_CLIENT
,
598 false /* require_smbconf */);
600 DBG_ERR("Failed to set up cmdline parser\n");
605 pc
= samba_popt_get_context("wspsearch",
610 poptSetOtherOptionHelp(pc
, "[OPTIONS] //server1/share1");
612 while ((opt
= poptGetNextOpt(pc
)) != -1) ;
614 if(!poptPeekArg(pc
)) {
615 poptPrintUsage(pc
, stderr
, 0);
620 path
= talloc_strdup(talloc_tos(), poptGetArg(pc
));
622 DBG_ERR("Invalid argument\n");
627 string_replace(path
,'/','\\');
628 server
= talloc_strdup(talloc_tos(), path
+2);
630 DBG_ERR("Invalid argument\n");
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");
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);
664 location
= talloc_asprintf(talloc_tos(),
665 "FILE://%s/%s", server
, share
);
666 query
= build_default_sql(talloc_tos(), kind
, phrase
, location
);
675 printf("custom_query %d\n", custom_query
);
676 select_stmt
= get_wsp_sql_tree(query
);
680 if (select_stmt
== NULL
) {
681 DBG_ERR("query failed\n");
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");
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");
701 select_stmt
->cols
->cols
[0] =
702 talloc_strdup(select_stmt
->cols
, default_column
);
705 status
= cli_full_connection_creds(&c
,
712 samba_cmdline_get_creds(),
715 if (!NT_STATUS_IS_OK(status
)) {
716 DBG_ERR("failed to connect to IPC$: %s\n",
722 status
= wsp_server_connect(talloc_tos(),
725 samba_cmdline_get_lp_ctx(),
726 samba_cmdline_get_creds(),
730 if (!NT_STATUS_IS_OK(status
)) {
731 DBG_ERR("failed to connect to wsp: %s\n",
737 h
= get_wsp_pipe(wsp_ctx
);
739 DBG_ERR("Failed to communicate with server, no pipe\n");
744 dcerpc_binding_handle_set_timeout(h
,
745 DCERPC_REQUEST_TIMEOUT
* 1000);
748 DBG_INFO("sending connect\n");
749 status
= wsp_connect(talloc_tos(),
751 lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
752 cli_credentials_get_username(
753 samba_cmdline_get_creds()),
757 if (!NT_STATUS_IS_OK(status
)) {
758 DBG_ERR("failed to connect to wsp: %s\n",
764 DBG_INFO("sending query\n");
766 status
= create_query(talloc_tos(),
772 if (!NT_STATUS_IS_OK(status
)) {
773 DBG_ERR("failed to send query: %s)\n",
779 DBG_INFO("sending createbindings\n");
781 status
= create_bindings(talloc_tos(),
787 if (!NT_STATUS_IS_OK(status
)) {
788 DBG_ERR("failed to setbindings: %s)\n",
794 status
= create_querystatusex(talloc_tos(),
796 bindings_used
.hcursor
,
800 DBG_ERR("no results found\n");
804 printf("found %d results, returning %d \n",
806 limit
? MIN(nrows
, limit
) : nrows
);
807 status
= create_getrows(talloc_tos(),
810 bindings_used
.hcursor
,
811 limit
? MIN(nrows
, limit
) : nrows
,
814 if (!NT_STATUS_IS_OK(status
)) {
815 DBG_ERR("Failed to retrieve rows, error: %s\n",