few edits
[Samba.git] / source / libsmb / clilist.c
blob9bd508f1d7c3913b93d4668cc838d3d1193791ac
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 client directory list routines
5 Copyright (C) Andrew Tridgell 1994-1998
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 2 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define NO_SYSLOG
24 #include "includes.h"
27 /****************************************************************************
28 interpret a long filename structure - this is mostly guesses at the moment
29 The length of the structure is returned
30 The structure of a long filename depends on the info level. 260 is used
31 by NT and 2 is used by OS/2
32 ****************************************************************************/
33 static int interpret_long_filename(struct cli_state *cli,
34 int level,char *p,file_info *finfo)
36 extern file_info def_finfo;
37 file_info finfo2;
38 int len;
39 char *base = p;
41 if (!finfo) finfo = &finfo2;
43 memcpy(finfo,&def_finfo,sizeof(*finfo));
45 switch (level)
47 case 1: /* OS/2 understands this */
48 /* these dates are converted to GMT by
49 make_unix_date */
50 finfo->ctime = make_unix_date2(p+4);
51 finfo->atime = make_unix_date2(p+8);
52 finfo->mtime = make_unix_date2(p+12);
53 finfo->size = IVAL_TO_SMB_OFF_T(p,16);
54 finfo->mode = CVAL(p,24);
55 len = CVAL(p, 26);
56 p += 27;
57 p += clistr_align_in(cli, p, 0);
58 p += clistr_pull(cli, finfo->name, p,
59 sizeof(finfo->name),
60 len,
61 STR_TERMINATE);
62 return PTR_DIFF(p, base);
64 case 2: /* this is what OS/2 uses mostly */
65 /* these dates are converted to GMT by
66 make_unix_date */
67 finfo->ctime = make_unix_date2(p+4);
68 finfo->atime = make_unix_date2(p+8);
69 finfo->mtime = make_unix_date2(p+12);
70 finfo->size = IVAL_TO_SMB_OFF_T(p,16);
71 finfo->mode = CVAL(p,24);
72 len = CVAL(p, 30);
73 p += 31;
74 /* check for unisys! */
75 p += clistr_pull(cli, finfo->name, p,
76 sizeof(finfo->name),
77 len,
78 STR_NOALIGN);
79 return PTR_DIFF(p, base) + 1;
81 case 260: /* NT uses this, but also accepts 2 */
83 int namelen, slen;
84 p += 4; /* next entry offset */
85 p += 4; /* fileindex */
87 /* these dates appear to arrive in a
88 weird way. It seems to be localtime
89 plus the serverzone given in the
90 initial connect. This is GMT when
91 DST is not in effect and one hour
92 from GMT otherwise. Can this really
93 be right??
95 I suppose this could be called
96 kludge-GMT. Is is the GMT you get
97 by using the current DST setting on
98 a different localtime. It will be
99 cheap to calculate, I suppose, as
100 no DST tables will be needed */
102 finfo->ctime = interpret_long_date(p); p += 8;
103 finfo->atime = interpret_long_date(p); p += 8;
104 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
105 finfo->size = IVAL_TO_SMB_OFF_T(p,0); p += 8;
106 p += 8; /* alloc size */
107 finfo->mode = CVAL(p,0); p += 4;
108 namelen = IVAL(p,0); p += 4;
109 p += 4; /* EA size */
110 slen = SVAL(p, 0);
111 p += 2;
113 /* stupid NT bugs. grr */
114 int flags = 0;
115 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
116 clistr_pull(cli, finfo->short_name, p,
117 sizeof(finfo->short_name),
118 slen, flags);
120 p += 24; /* short name? */
121 clistr_pull(cli, finfo->name, p,
122 sizeof(finfo->name),
123 namelen, 0);
124 return SVAL(base, 0);
128 DEBUG(1,("Unknown long filename format %d\n",level));
129 return(SVAL(p,0));
133 /****************************************************************************
134 do a directory listing, calling fn on each file found
135 ****************************************************************************/
136 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
137 void (*fn)(file_info *, const char *, void *), void *state)
139 int max_matches = 512;
140 int info_level;
141 char *p, *p2;
142 pstring mask;
143 file_info finfo;
144 int i;
145 char *tdl, *dirlist = NULL;
146 int dirlist_len = 0;
147 int total_received = -1;
148 BOOL First = True;
149 int ff_searchcount=0;
150 int ff_eos=0;
151 int ff_lastname=0;
152 int ff_dir_handle=0;
153 int loop_count = 0;
154 char *rparam=NULL, *rdata=NULL;
155 int param_len, data_len;
156 uint16 setup;
157 pstring param;
159 /* NT uses 260, OS/2 uses 2. Both accept 1. */
160 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
162 pstrcpy(mask,Mask);
164 while (ff_eos == 0) {
165 loop_count++;
166 if (loop_count > 200) {
167 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
168 break;
171 if (First) {
172 setup = TRANSACT2_FINDFIRST;
173 SSVAL(param,0,attribute); /* attribute */
174 SSVAL(param,2,max_matches); /* max count */
175 SSVAL(param,4,4+2); /* resume required + close on end */
176 SSVAL(param,6,info_level);
177 SIVAL(param,8,0);
178 p = param+12;
179 p += clistr_push(cli, param+12, mask, -1,
180 STR_TERMINATE|STR_CONVERT);
181 } else {
182 setup = TRANSACT2_FINDNEXT;
183 SSVAL(param,0,ff_dir_handle);
184 SSVAL(param,2,max_matches); /* max count */
185 SSVAL(param,4,info_level);
186 SIVAL(param,6,0); /* ff_resume_key */
187 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
188 p = param+12;
189 p += clistr_push(cli, param+12, mask, -1,
190 STR_TERMINATE|STR_CONVERT);
193 param_len = PTR_DIFF(p, param);
195 if (!cli_send_trans(cli, SMBtrans2,
196 NULL, /* Name */
197 -1, 0, /* fid, flags */
198 &setup, 1, 0, /* setup, length, max */
199 param, param_len, 10, /* param, length, max */
200 NULL, 0,
201 cli->max_xmit /* data, length, max */
202 )) {
203 break;
206 if (!cli_receive_trans(cli, SMBtrans2,
207 &rparam, &param_len,
208 &rdata, &data_len) &&
209 cli_is_dos_error(cli)) {
210 /* we need to work around a Win95 bug - sometimes
211 it gives ERRSRV/ERRerror temprarily */
212 uint8 eclass;
213 uint32 ecode;
214 cli_dos_error(cli, &eclass, &ecode);
215 if (eclass != ERRSRV || ecode != ERRerror) break;
216 msleep(100);
217 continue;
220 if (cli_is_error(cli) || !rdata || !rparam)
221 break;
223 if (total_received == -1) total_received = 0;
225 /* parse out some important return info */
226 p = rparam;
227 if (First) {
228 ff_dir_handle = SVAL(p,0);
229 ff_searchcount = SVAL(p,2);
230 ff_eos = SVAL(p,4);
231 ff_lastname = SVAL(p,8);
232 } else {
233 ff_searchcount = SVAL(p,0);
234 ff_eos = SVAL(p,2);
235 ff_lastname = SVAL(p,6);
238 if (ff_searchcount == 0)
239 break;
241 /* point to the data bytes */
242 p = rdata;
244 /* we might need the lastname for continuations */
245 if (ff_lastname > 0) {
246 switch(info_level)
248 case 260:
249 clistr_pull(cli, mask, p+ff_lastname,
250 sizeof(mask),
251 data_len-ff_lastname,
252 STR_TERMINATE);
253 break;
254 case 1:
255 clistr_pull(cli, mask, p+ff_lastname+1,
256 sizeof(mask),
258 STR_TERMINATE);
259 break;
261 } else {
262 pstrcpy(mask,"");
265 /* and add them to the dirlist pool */
266 tdl = Realloc(dirlist,dirlist_len + data_len);
268 if (!tdl) {
269 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
270 break;
272 else dirlist = tdl;
274 /* put in a length for the last entry, to ensure we can chain entries
275 into the next packet */
276 for (p2=p,i=0;i<(ff_searchcount-1);i++)
277 p2 += interpret_long_filename(cli,info_level,p2,NULL);
278 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
280 /* grab the data for later use */
281 memcpy(dirlist+dirlist_len,p,data_len);
282 dirlist_len += data_len;
284 total_received += ff_searchcount;
286 SAFE_FREE(rdata);
287 SAFE_FREE(rparam);
289 DEBUG(3,("received %d entries (eos=%d)\n",
290 ff_searchcount,ff_eos));
292 if (ff_searchcount > 0) loop_count = 0;
294 First = False;
297 for (p=dirlist,i=0;i<total_received;i++) {
298 p += interpret_long_filename(cli,info_level,p,&finfo);
299 fn(&finfo, Mask, state);
302 /* free up the dirlist buffer */
303 SAFE_FREE(dirlist);
304 return(total_received);
309 /****************************************************************************
310 interpret a short filename structure
311 The length of the structure is returned
312 ****************************************************************************/
313 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
315 extern file_info def_finfo;
317 *finfo = def_finfo;
319 finfo->mode = CVAL(p,21);
321 /* this date is converted to GMT by make_unix_date */
322 finfo->ctime = make_unix_date(p+22);
323 finfo->mtime = finfo->atime = finfo->ctime;
324 finfo->size = IVAL_TO_SMB_OFF_T(p,26);
325 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
326 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
327 fstrcpy(finfo->short_name,finfo->name);
329 return(DIR_STRUCT_SIZE);
333 /****************************************************************************
334 do a directory listing, calling fn on each file found
335 this uses the old SMBsearch interface. It is needed for testing Samba,
336 but should otherwise not be used
337 ****************************************************************************/
338 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
339 void (*fn)(file_info *, const char *, void *), void *state)
341 char *p;
342 int received = 0;
343 BOOL first = True;
344 char status[21];
345 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
346 int num_received = 0;
347 int i;
348 char *tdl, *dirlist = NULL;
349 pstring mask;
351 ZERO_ARRAY(status);
353 pstrcpy(mask,Mask);
355 while (1) {
356 memset(cli->outbuf,'\0',smb_size);
357 memset(cli->inbuf,'\0',smb_size);
359 set_message(cli->outbuf,2,0,True);
361 SCVAL(cli->outbuf,smb_com,SMBsearch);
363 SSVAL(cli->outbuf,smb_tid,cli->cnum);
364 cli_setup_packet(cli);
366 SSVAL(cli->outbuf,smb_vwv0,num_asked);
367 SSVAL(cli->outbuf,smb_vwv1,attribute);
369 p = smb_buf(cli->outbuf);
370 *p++ = 4;
372 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE|STR_CONVERT);
373 *p++ = 5;
374 if (first) {
375 SSVAL(p,0,0);
376 p += 2;
377 } else {
378 SSVAL(p,0,21);
379 p += 2;
380 memcpy(p,status,21);
381 p += 21;
384 cli_setup_bcc(cli, p);
385 cli_send_smb(cli);
386 if (!cli_receive_smb(cli)) break;
388 received = SVAL(cli->inbuf,smb_vwv0);
389 if (received <= 0) break;
391 first = False;
393 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
395 if (!tdl) {
396 DEBUG(0,("cli_list_old: failed to expand dirlist"));
397 SAFE_FREE(dirlist);
398 return 0;
400 else dirlist = tdl;
402 p = smb_buf(cli->inbuf) + 3;
404 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
405 p,received*DIR_STRUCT_SIZE);
407 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
409 num_received += received;
411 if (cli_is_error(cli)) break;
414 if (!first) {
415 memset(cli->outbuf,'\0',smb_size);
416 memset(cli->inbuf,'\0',smb_size);
418 set_message(cli->outbuf,2,0,True);
419 SCVAL(cli->outbuf,smb_com,SMBfclose);
420 SSVAL(cli->outbuf,smb_tid,cli->cnum);
421 cli_setup_packet(cli);
423 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
424 SSVAL(cli->outbuf, smb_vwv1, attribute);
426 p = smb_buf(cli->outbuf);
427 *p++ = 4;
428 fstrcpy(p, "");
429 p += strlen(p) + 1;
430 *p++ = 5;
431 SSVAL(p, 0, 21);
432 p += 2;
433 memcpy(p,status,21);
434 p += 21;
436 cli_setup_bcc(cli, p);
437 cli_send_smb(cli);
438 if (!cli_receive_smb(cli)) {
439 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
443 for (p=dirlist,i=0;i<num_received;i++) {
444 file_info finfo;
445 p += interpret_short_filename(cli, p,&finfo);
446 fn(&finfo, Mask, state);
449 SAFE_FREE(dirlist);
450 return(num_received);
454 /****************************************************************************
455 do a directory listing, calling fn on each file found
456 this auto-switches between old and new style
457 ****************************************************************************/
458 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
459 void (*fn)(file_info *, const char *, void *), void *state)
461 if (cli->protocol <= PROTOCOL_LANMAN1) {
462 return cli_list_old(cli, Mask, attribute, fn, state);
464 return cli_list_new(cli, Mask, attribute, fn, state);