1/*
2   Unix SMB/CIFS implementation.
3   client directory list routines
4   Copyright (C) Andrew Tridgell 1994-2003
5   Copyright (C) James Myers 2003 <myersjj@samba.org>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "includes.h"
22#include "libcli/libcli.h"
23#include "libcli/raw/libcliraw.h"
24#include "libcli/raw/raw_proto.h"
25
26struct search_private {
27	struct clilist_file_info *dirlist;
28	TALLOC_CTX *mem_ctx;
29	int dirlist_len;
30	int ff_searchcount;  /* total received in 1 server trip */
31	int total_received;  /* total received all together */
32	enum smb_search_data_level data_level;
33	const char *last_name;     /* used to continue trans2 search */
34	struct smb_search_id id;   /* used for old-style search */
35};
36
37
38/****************************************************************************
39 Interpret a long filename structure.
40****************************************************************************/
41static bool interpret_long_filename(enum smb_search_data_level level,
42				    const union smb_search_data *info,
43				    struct clilist_file_info *finfo)
44{
45	struct clilist_file_info finfo2;
46
47	if (!finfo) finfo = &finfo2;
48	ZERO_STRUCTP(finfo);
49
50	switch (level) {
51	case RAW_SEARCH_DATA_STANDARD:
52		finfo->size = info->standard.size;
53		finfo->mtime = info->standard.write_time;
54		finfo->attrib = info->standard.attrib;
55		finfo->name = info->standard.name.s;
56		finfo->short_name = info->standard.name.s;
57		break;
58
59	case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
60		finfo->size = info->both_directory_info.size;
61		finfo->mtime = nt_time_to_unix(info->both_directory_info.write_time);
62		finfo->attrib = info->both_directory_info.attrib;
63		finfo->short_name = info->both_directory_info.short_name.s;
64		finfo->name = info->both_directory_info.name.s;
65		break;
66
67	default:
68		DEBUG(0,("Unhandled level %d in interpret_long_filename\n", (int)level));
69		return false;
70	}
71
72	return true;
73}
74
75/* callback function used for trans2 search */
76static bool smbcli_list_new_callback(void *private_data, const union smb_search_data *file)
77{
78	struct search_private *state = (struct search_private*) private_data;
79	struct clilist_file_info *tdl;
80
81	/* add file info to the dirlist pool */
82	tdl = talloc_realloc(state,
83			     state->dirlist,
84			     struct clilist_file_info,
85			     state->dirlist_len + 1);
86	if (!tdl) {
87		return false;
88	}
89	state->dirlist = tdl;
90	state->dirlist_len++;
91
92	interpret_long_filename(state->data_level, file, &state->dirlist[state->total_received]);
93
94	state->last_name = state->dirlist[state->total_received].name;
95	state->total_received++;
96	state->ff_searchcount++;
97
98	return true;
99}
100
101int smbcli_list_new(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
102		    enum smb_search_data_level level,
103		    void (*fn)(struct clilist_file_info *, const char *, void *),
104		    void *caller_state)
105{
106	union smb_search_first first_parms;
107	union smb_search_next next_parms;
108	struct search_private state;  /* for callbacks */
109	int received = 0;
110	bool first = true;
111	int num_received = 0;
112	int max_matches = 512;
113	char *mask;
114	int ff_eos = 0, i, ff_searchcount;
115	int ff_dir_handle=0;
116
117	/* initialize state for search */
118	state.mem_ctx = talloc_init("smbcli_list_new");
119	state.dirlist_len = 0;
120	state.total_received = 0;
121
122	state.dirlist = talloc_array(state.mem_ctx,
123				     struct clilist_file_info, 0);
124	mask = talloc_strdup(state.mem_ctx, Mask);
125
126	if (level == RAW_SEARCH_DATA_GENERIC) {
127		if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) {
128			level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
129		} else {
130			level = RAW_SEARCH_DATA_STANDARD;
131		}
132	}
133	state.data_level = level;
134
135	while (1) {
136		state.ff_searchcount = 0;
137		if (first) {
138			NTSTATUS status;
139
140			first_parms.t2ffirst.level = RAW_SEARCH_TRANS2;
141			first_parms.t2ffirst.data_level = state.data_level;
142			first_parms.t2ffirst.in.max_count = max_matches;
143			first_parms.t2ffirst.in.search_attrib = attribute;
144			first_parms.t2ffirst.in.pattern = mask;
145			first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
146			first_parms.t2ffirst.in.storage_type = 0;
147
148			status = smb_raw_search_first(tree,
149						      state.mem_ctx, &first_parms,
150						      (void*)&state, smbcli_list_new_callback);
151			if (!NT_STATUS_IS_OK(status)) {
152				talloc_free(state.mem_ctx);
153				return -1;
154			}
155
156			ff_dir_handle = first_parms.t2ffirst.out.handle;
157			ff_searchcount = first_parms.t2ffirst.out.count;
158			ff_eos = first_parms.t2ffirst.out.end_of_search;
159
160			received = first_parms.t2ffirst.out.count;
161			if (received <= 0) break;
162			if (ff_eos) break;
163			first = false;
164		} else {
165			NTSTATUS status;
166
167			next_parms.t2fnext.level = RAW_SEARCH_TRANS2;
168			next_parms.t2fnext.data_level = state.data_level;
169			next_parms.t2fnext.in.max_count = max_matches;
170			next_parms.t2fnext.in.last_name = state.last_name;
171			next_parms.t2fnext.in.handle = ff_dir_handle;
172			next_parms.t2fnext.in.resume_key = 0;
173			next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
174
175			status = smb_raw_search_next(tree,
176						     state.mem_ctx,
177						     &next_parms,
178						     (void*)&state,
179						     smbcli_list_new_callback);
180
181			if (!NT_STATUS_IS_OK(status)) {
182				return -1;
183			}
184			ff_searchcount = next_parms.t2fnext.out.count;
185			ff_eos = next_parms.t2fnext.out.end_of_search;
186			received = next_parms.t2fnext.out.count;
187			if (received <= 0) break;
188			if (ff_eos) break;
189		}
190
191		num_received += received;
192	}
193
194	for (i=0;i<state.total_received;i++) {
195		fn(&state.dirlist[i], Mask, caller_state);
196	}
197
198	talloc_free(state.mem_ctx);
199
200	return state.total_received;
201}
202
203/****************************************************************************
204 Interpret a short filename structure.
205 The length of the structure is returned.
206****************************************************************************/
207static bool interpret_short_filename(enum smb_search_data_level level,
208				     const union smb_search_data *info,
209				     struct clilist_file_info *finfo)
210{
211	struct clilist_file_info finfo2;
212
213	if (!finfo) finfo = &finfo2;
214	ZERO_STRUCTP(finfo);
215
216	switch (level) {
217	case RAW_SEARCH_DATA_SEARCH:
218		finfo->mtime = info->search.write_time;
219		finfo->size = info->search.size;
220		finfo->attrib = info->search.attrib;
221		finfo->name = info->search.name;
222		finfo->short_name = info->search.name;
223		break;
224
225	default:
226		DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level));
227		return false;
228	}
229
230	return true;
231}
232
233/* callback function used for smb_search */
234static bool smbcli_list_old_callback(void *private_data, const union smb_search_data *file)
235{
236	struct search_private *state = (struct search_private*) private_data;
237	struct clilist_file_info *tdl;
238
239	/* add file info to the dirlist pool */
240	tdl = talloc_realloc(state,
241			     state->dirlist,
242			     struct clilist_file_info,
243			     state->dirlist_len + 1);
244
245	if (!tdl) {
246		return false;
247	}
248	state->dirlist = tdl;
249	state->dirlist_len++;
250
251	interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]);
252
253	state->total_received++;
254	state->ff_searchcount++;
255	state->id = file->search.id; /* return resume info */
256
257	return true;
258}
259
260int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
261		 void (*fn)(struct clilist_file_info *, const char *, void *),
262		 void *caller_state)
263{
264	union smb_search_first first_parms;
265	union smb_search_next next_parms;
266	struct search_private state;  /* for callbacks */
267	const int num_asked = 500;
268	int received = 0;
269	bool first = true;
270	int num_received = 0;
271	char *mask;
272	int i;
273
274	/* initialize state for search */
275	state.mem_ctx = talloc_init("smbcli_list_old");
276	state.dirlist_len = 0;
277	state.total_received = 0;
278	state.data_level = RAW_SEARCH_DATA_SEARCH;
279
280	state.dirlist = talloc_array(state.mem_ctx, struct clilist_file_info,
281				     0);
282	mask = talloc_strdup(state.mem_ctx, Mask);
283
284	while (1) {
285		state.ff_searchcount = 0;
286		if (first) {
287			NTSTATUS status;
288
289			first_parms.search_first.level = RAW_SEARCH_SEARCH;
290			first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
291			first_parms.search_first.in.max_count = num_asked;
292			first_parms.search_first.in.search_attrib = attribute;
293			first_parms.search_first.in.pattern = mask;
294
295			status = smb_raw_search_first(tree, state.mem_ctx,
296						      &first_parms,
297						      (void*)&state,
298						      smbcli_list_old_callback);
299
300			if (!NT_STATUS_IS_OK(status)) {
301				talloc_free(state.mem_ctx);
302				return -1;
303			}
304
305			received = first_parms.search_first.out.count;
306			if (received <= 0) break;
307			first = false;
308		} else {
309			NTSTATUS status;
310
311			next_parms.search_next.level = RAW_SEARCH_SEARCH;
312			next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
313			next_parms.search_next.in.max_count = num_asked;
314			next_parms.search_next.in.search_attrib = attribute;
315			next_parms.search_next.in.id = state.id;
316
317			status = smb_raw_search_next(tree, state.mem_ctx,
318						     &next_parms,
319						     (void*)&state,
320						     smbcli_list_old_callback);
321
322			if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
323				break;
324			}
325			if (!NT_STATUS_IS_OK(status)) {
326				talloc_free(state.mem_ctx);
327				return -1;
328			}
329			received = next_parms.search_next.out.count;
330			if (received <= 0) break;
331		}
332
333		num_received += received;
334	}
335
336	for (i=0;i<state.total_received;i++) {
337		fn(&state.dirlist[i], Mask, caller_state);
338	}
339
340	talloc_free(state.mem_ctx);
341
342	return state.total_received;
343}
344
345/****************************************************************************
346 Do a directory listing, calling fn on each file found.
347 This auto-switches between old and new style.
348****************************************************************************/
349
350int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute,
351		void (*fn)(struct clilist_file_info *, const char *, void *), void *state)
352{
353	if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1)
354		return smbcli_list_old(tree, Mask, attribute, fn, state);
355	return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state);
356}
357