* fix to display correct form information in REG_BINARY information
[Samba.git] / source / libsmb / clilist.c
blob17a759f9e39371455fdb39ea116e64425eddbfbb
1 /*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #define NO_SYSLOG
23 #include "includes.h"
26 /****************************************************************************
27 interpret a long filename structure - this is mostly guesses at the moment
28 The length of the structure is returned
29 The structure of a long filename depends on the info level. 260 is used
30 by NT and 2 is used by OS/2
31 ****************************************************************************/
32 static int interpret_long_filename(struct cli_state *cli,
33 int level,char *p,file_info *finfo)
35 extern file_info def_finfo;
36 file_info finfo2;
37 int len;
38 char *base = p;
40 if (!finfo) finfo = &finfo2;
42 memcpy(finfo,&def_finfo,sizeof(*finfo));
44 switch (level)
46 case 1: /* OS/2 understands this */
47 /* these dates are converted to GMT by
48 make_unix_date */
49 finfo->ctime = make_unix_date2(p+4);
50 finfo->atime = make_unix_date2(p+8);
51 finfo->mtime = make_unix_date2(p+12);
52 finfo->size = IVAL(p,16);
53 finfo->mode = CVAL(p,24);
54 len = CVAL(p, 26);
55 p += 27;
56 p += clistr_align_in(cli, p, 0);
57 /* the len+2 below looks strange but it is
58 important to cope with the differences
59 between win2000 and win9x for this call
60 (tridge) */
61 p += clistr_pull(cli, finfo->name, p,
62 sizeof(finfo->name),
63 len+2,
64 STR_TERMINATE);
65 return PTR_DIFF(p, base);
67 case 2: /* this is what OS/2 uses mostly */
68 /* these dates are converted to GMT by
69 make_unix_date */
70 finfo->ctime = make_unix_date2(p+4);
71 finfo->atime = make_unix_date2(p+8);
72 finfo->mtime = make_unix_date2(p+12);
73 finfo->size = IVAL(p,16);
74 finfo->mode = CVAL(p,24);
75 len = CVAL(p, 30);
76 p += 31;
77 /* check for unisys! */
78 p += clistr_pull(cli, finfo->name, p,
79 sizeof(finfo->name),
80 len,
81 STR_NOALIGN);
82 return PTR_DIFF(p, base) + 1;
84 case 260: /* NT uses this, but also accepts 2 */
86 int namelen, slen;
87 p += 4; /* next entry offset */
88 p += 4; /* fileindex */
90 /* these dates appear to arrive in a
91 weird way. It seems to be localtime
92 plus the serverzone given in the
93 initial connect. This is GMT when
94 DST is not in effect and one hour
95 from GMT otherwise. Can this really
96 be right??
98 I suppose this could be called
99 kludge-GMT. Is is the GMT you get
100 by using the current DST setting on
101 a different localtime. It will be
102 cheap to calculate, I suppose, as
103 no DST tables will be needed */
105 finfo->ctime = interpret_long_date(p); p += 8;
106 finfo->atime = interpret_long_date(p); p += 8;
107 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
108 finfo->size = IVAL(p,0); p += 8;
109 p += 8; /* alloc size */
110 finfo->mode = CVAL(p,0); p += 4;
111 namelen = IVAL(p,0); p += 4;
112 p += 4; /* EA size */
113 slen = SVAL(p, 0);
114 p += 2;
116 /* stupid NT bugs. grr */
117 int flags = 0;
118 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
119 clistr_pull(cli, finfo->short_name, p,
120 sizeof(finfo->short_name),
121 slen, flags);
123 p += 24; /* short name? */
124 clistr_pull(cli, finfo->name, p,
125 sizeof(finfo->name),
126 namelen, 0);
127 return SVAL(base, 0);
131 DEBUG(1,("Unknown long filename format %d\n",level));
132 return(SVAL(p,0));
136 /****************************************************************************
137 do a directory listing, calling fn on each file found
138 ****************************************************************************/
139 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
140 void (*fn)(file_info *, const char *, void *), void *state)
142 int max_matches = 512;
143 int info_level;
144 char *p, *p2;
145 pstring mask;
146 file_info finfo;
147 int i;
148 char *tdl, *dirlist = NULL;
149 int dirlist_len = 0;
150 int total_received = -1;
151 BOOL First = True;
152 int ff_searchcount=0;
153 int ff_eos=0;
154 int ff_lastname=0;
155 int ff_dir_handle=0;
156 int loop_count = 0;
157 char *rparam=NULL, *rdata=NULL;
158 int param_len, data_len;
159 uint16 setup;
160 pstring param;
162 /* NT uses 260, OS/2 uses 2. Both accept 1. */
163 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
165 pstrcpy(mask,Mask);
167 while (ff_eos == 0) {
168 loop_count++;
169 if (loop_count > 200) {
170 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
171 break;
174 if (First) {
175 setup = TRANSACT2_FINDFIRST;
176 SSVAL(param,0,attribute); /* attribute */
177 SSVAL(param,2,max_matches); /* max count */
178 SSVAL(param,4,4+2); /* resume required + close on end */
179 SSVAL(param,6,info_level);
180 SIVAL(param,8,0);
181 p = param+12;
182 p += clistr_push(cli, param+12, mask, -1,
183 STR_TERMINATE);
184 } else {
185 setup = TRANSACT2_FINDNEXT;
186 SSVAL(param,0,ff_dir_handle);
187 SSVAL(param,2,max_matches); /* max count */
188 SSVAL(param,4,info_level);
189 SIVAL(param,6,0); /* ff_resume_key */
190 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
191 p = param+12;
192 p += clistr_push(cli, param+12, mask, -1,
193 STR_TERMINATE);
196 param_len = PTR_DIFF(p, param);
198 if (!cli_send_trans(cli, SMBtrans2,
199 NULL, /* Name */
200 -1, 0, /* fid, flags */
201 &setup, 1, 0, /* setup, length, max */
202 param, param_len, 10, /* param, length, max */
203 NULL, 0,
204 cli->max_xmit /* data, length, max */
205 )) {
206 break;
209 if (!cli_receive_trans(cli, SMBtrans2,
210 &rparam, &param_len,
211 &rdata, &data_len) &&
212 cli_is_dos_error(cli)) {
213 /* we need to work around a Win95 bug - sometimes
214 it gives ERRSRV/ERRerror temprarily */
215 uint8 eclass;
216 uint32 ecode;
217 cli_dos_error(cli, &eclass, &ecode);
218 if (eclass != ERRSRV || ecode != ERRerror) break;
219 msleep(100);
220 continue;
223 if (cli_is_error(cli) || !rdata || !rparam)
224 break;
226 if (total_received == -1) total_received = 0;
228 /* parse out some important return info */
229 p = rparam;
230 if (First) {
231 ff_dir_handle = SVAL(p,0);
232 ff_searchcount = SVAL(p,2);
233 ff_eos = SVAL(p,4);
234 ff_lastname = SVAL(p,8);
235 } else {
236 ff_searchcount = SVAL(p,0);
237 ff_eos = SVAL(p,2);
238 ff_lastname = SVAL(p,6);
241 if (ff_searchcount == 0)
242 break;
244 /* point to the data bytes */
245 p = rdata;
247 /* we might need the lastname for continuations */
248 if (ff_lastname > 0) {
249 switch(info_level)
251 case 260:
252 clistr_pull(cli, mask, p+ff_lastname,
253 sizeof(mask),
254 data_len-ff_lastname,
255 STR_TERMINATE);
256 break;
257 case 1:
258 clistr_pull(cli, mask, p+ff_lastname+1,
259 sizeof(mask),
261 STR_TERMINATE);
262 break;
264 } else {
265 pstrcpy(mask,"");
268 /* and add them to the dirlist pool */
269 tdl = Realloc(dirlist,dirlist_len + data_len);
271 if (!tdl) {
272 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
273 break;
275 else dirlist = tdl;
277 /* put in a length for the last entry, to ensure we can chain entries
278 into the next packet */
279 for (p2=p,i=0;i<(ff_searchcount-1);i++)
280 p2 += interpret_long_filename(cli,info_level,p2,NULL);
281 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
283 /* grab the data for later use */
284 memcpy(dirlist+dirlist_len,p,data_len);
285 dirlist_len += data_len;
287 total_received += ff_searchcount;
289 SAFE_FREE(rdata);
290 SAFE_FREE(rparam);
292 DEBUG(3,("received %d entries (eos=%d)\n",
293 ff_searchcount,ff_eos));
295 if (ff_searchcount > 0) loop_count = 0;
297 First = False;
300 for (p=dirlist,i=0;i<total_received;i++) {
301 p += interpret_long_filename(cli,info_level,p,&finfo);
302 fn(&finfo, Mask, state);
305 /* free up the dirlist buffer */
306 SAFE_FREE(dirlist);
307 return(total_received);
312 /****************************************************************************
313 interpret a short filename structure
314 The length of the structure is returned
315 ****************************************************************************/
316 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
318 extern file_info def_finfo;
320 *finfo = def_finfo;
322 finfo->mode = CVAL(p,21);
324 /* this date is converted to GMT by make_unix_date */
325 finfo->ctime = make_unix_date(p+22);
326 finfo->mtime = finfo->atime = finfo->ctime;
327 finfo->size = IVAL(p,26);
328 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
329 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
330 fstrcpy(finfo->short_name,finfo->name);
332 return(DIR_STRUCT_SIZE);
336 /****************************************************************************
337 do a directory listing, calling fn on each file found
338 this uses the old SMBsearch interface. It is needed for testing Samba,
339 but should otherwise not be used
340 ****************************************************************************/
341 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
342 void (*fn)(file_info *, const char *, void *), void *state)
344 char *p;
345 int received = 0;
346 BOOL first = True;
347 char status[21];
348 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
349 int num_received = 0;
350 int i;
351 char *tdl, *dirlist = NULL;
352 pstring mask;
354 ZERO_ARRAY(status);
356 pstrcpy(mask,Mask);
358 while (1) {
359 memset(cli->outbuf,'\0',smb_size);
360 memset(cli->inbuf,'\0',smb_size);
362 set_message(cli->outbuf,2,0,True);
364 SCVAL(cli->outbuf,smb_com,SMBsearch);
366 SSVAL(cli->outbuf,smb_tid,cli->cnum);
367 cli_setup_packet(cli);
369 SSVAL(cli->outbuf,smb_vwv0,num_asked);
370 SSVAL(cli->outbuf,smb_vwv1,attribute);
372 p = smb_buf(cli->outbuf);
373 *p++ = 4;
375 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
376 *p++ = 5;
377 if (first) {
378 SSVAL(p,0,0);
379 p += 2;
380 } else {
381 SSVAL(p,0,21);
382 p += 2;
383 memcpy(p,status,21);
384 p += 21;
387 cli_setup_bcc(cli, p);
388 cli_send_smb(cli);
389 if (!cli_receive_smb(cli)) break;
391 received = SVAL(cli->inbuf,smb_vwv0);
392 if (received <= 0) break;
394 first = False;
396 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
398 if (!tdl) {
399 DEBUG(0,("cli_list_old: failed to expand dirlist"));
400 SAFE_FREE(dirlist);
401 return 0;
403 else dirlist = tdl;
405 p = smb_buf(cli->inbuf) + 3;
407 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
408 p,received*DIR_STRUCT_SIZE);
410 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
412 num_received += received;
414 if (cli_is_error(cli)) break;
417 if (!first) {
418 memset(cli->outbuf,'\0',smb_size);
419 memset(cli->inbuf,'\0',smb_size);
421 set_message(cli->outbuf,2,0,True);
422 SCVAL(cli->outbuf,smb_com,SMBfclose);
423 SSVAL(cli->outbuf,smb_tid,cli->cnum);
424 cli_setup_packet(cli);
426 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
427 SSVAL(cli->outbuf, smb_vwv1, attribute);
429 p = smb_buf(cli->outbuf);
430 *p++ = 4;
431 fstrcpy(p, "");
432 p += strlen(p) + 1;
433 *p++ = 5;
434 SSVAL(p, 0, 21);
435 p += 2;
436 memcpy(p,status,21);
437 p += 21;
439 cli_setup_bcc(cli, p);
440 cli_send_smb(cli);
441 if (!cli_receive_smb(cli)) {
442 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
446 for (p=dirlist,i=0;i<num_received;i++) {
447 file_info finfo;
448 p += interpret_short_filename(cli, p,&finfo);
449 fn(&finfo, Mask, state);
452 SAFE_FREE(dirlist);
453 return(num_received);
457 /****************************************************************************
458 do a directory listing, calling fn on each file found
459 this auto-switches between old and new style
460 ****************************************************************************/
461 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
462 void (*fn)(file_info *, const char *, void *), void *state)
464 if (cli->protocol <= PROTOCOL_LANMAN1) {
465 return cli_list_old(cli, Mask, attribute, fn, state);
467 return cli_list_new(cli, Mask, attribute, fn, state);