This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
[samba.git] / source / libsmb / clilist.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client directory list routines
4    Copyright (C) Andrew Tridgell 1994-1998
5    
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.
10    
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.
15    
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.
19 */
20
21 #define NO_SYSLOG
22
23 #include "includes.h"
24
25
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)
34 {
35         extern file_info def_finfo;
36         file_info finfo2;
37         int len;
38         char *base = p;
39
40         if (!finfo) finfo = &finfo2;
41
42         memcpy(finfo,&def_finfo,sizeof(*finfo));
43
44         switch (level)
45                 {
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);
66
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;
83                         
84                 case 260: /* NT uses this, but also accepts 2 */
85                 {
86                         int namelen, slen;
87                         p += 4; /* next entry offset */
88                         p += 4; /* fileindex */
89                                 
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??
97                            
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 */
104                         
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; 
115                         {
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);
122                         }
123                         p += 24; /* short name? */        
124                         clistr_pull(cli, finfo->name, p,
125                                     sizeof(finfo->name),
126                                     namelen, 0);
127                         return SVAL(base, 0);
128                 }
129                 }
130         
131         DEBUG(1,("Unknown long filename format %d\n",level));
132         return(SVAL(p,0));
133 }
134
135
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)
141 {
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;
161
162         /* NT uses 260, OS/2 uses 2. Both accept 1. */
163         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
164
165         pstrcpy(mask,Mask);
166         
167         while (ff_eos == 0) {
168                 loop_count++;
169                 if (loop_count > 200) {
170                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
171                         break;
172                 }
173
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);
194                 }
195
196                 param_len = PTR_DIFF(p, param);
197
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;
207                 }
208
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;
221                 }
222
223                 if (cli_is_error(cli) || !rdata || !rparam) 
224                         break;
225
226                 if (total_received == -1) total_received = 0;
227
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);
239                 }
240
241                 if (ff_searchcount == 0) 
242                         break;
243
244                 /* point to the data bytes */
245                 p = rdata;
246
247                 /* we might need the lastname for continuations */
248                 if (ff_lastname > 0) {
249                         switch(info_level)
250                                 {
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), 
260                                                     -1,
261                                                     STR_TERMINATE);
262                                         break;
263                                 }
264                 } else {
265                         pstrcpy(mask,"");
266                 }
267  
268                 /* and add them to the dirlist pool */
269                 tdl = Realloc(dirlist,dirlist_len + data_len);
270
271                 if (!tdl) {
272                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
273                         break;
274                 }
275                 else dirlist = tdl;
276
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));
282
283                 /* grab the data for later use */
284                 memcpy(dirlist+dirlist_len,p,data_len);
285                 dirlist_len += data_len;
286
287                 total_received += ff_searchcount;
288
289                 SAFE_FREE(rdata);
290                 SAFE_FREE(rparam);
291
292                 DEBUG(3,("received %d entries (eos=%d)\n",
293                          ff_searchcount,ff_eos));
294
295                 if (ff_searchcount > 0) loop_count = 0;
296
297                 First = False;
298         }
299
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);
303         }
304
305         /* free up the dirlist buffer */
306         SAFE_FREE(dirlist);
307         return(total_received);
308 }
309
310
311
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)
317 {
318         extern file_info def_finfo;
319
320         *finfo = def_finfo;
321
322         finfo->mode = CVAL(p,21);
323         
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);
331         
332         return(DIR_STRUCT_SIZE);
333 }
334
335
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)
343 {
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;
353         
354         ZERO_ARRAY(status);
355
356         pstrcpy(mask,Mask);
357   
358         while (1) {
359                 memset(cli->outbuf,'\0',smb_size);
360                 memset(cli->inbuf,'\0',smb_size);
361
362                 set_message(cli->outbuf,2,0,True);
363
364                 SCVAL(cli->outbuf,smb_com,SMBsearch);
365
366                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
367                 cli_setup_packet(cli);
368
369                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
370                 SSVAL(cli->outbuf,smb_vwv1,attribute);
371   
372                 p = smb_buf(cli->outbuf);
373                 *p++ = 4;
374       
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;
385                 }
386
387                 cli_setup_bcc(cli, p);
388                 cli_send_smb(cli);
389                 if (!cli_receive_smb(cli)) break;
390
391                 received = SVAL(cli->inbuf,smb_vwv0);
392                 if (received <= 0) break;
393
394                 first = False;
395
396                 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
397
398                 if (!tdl) {
399                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
400                         SAFE_FREE(dirlist);
401                         return 0;
402                 }
403                 else dirlist = tdl;
404
405                 p = smb_buf(cli->inbuf) + 3;
406
407                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
408                        p,received*DIR_STRUCT_SIZE);
409                 
410                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
411                 
412                 num_received += received;
413                 
414                 if (cli_is_error(cli)) break;
415         }
416
417         if (!first) {
418                 memset(cli->outbuf,'\0',smb_size);
419                 memset(cli->inbuf,'\0',smb_size);
420
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);
425
426                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
427                 SSVAL(cli->outbuf, smb_vwv1, attribute);
428
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;
438                 
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)));
443                 }
444         }
445
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);
450         }
451
452         SAFE_FREE(dirlist);
453         return(num_received);
454 }
455
456
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)
463 {
464         if (cli->protocol <= PROTOCOL_LANMAN1) {
465                 return cli_list_old(cli, Mask, attribute, fn, state);
466         }
467         return cli_list_new(cli, Mask, attribute, fn, state);
468 }