r5577: get recurse; dir working across single level dfs referrals
[metze/samba/wip.git] / source3 / 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  Interpret a long filename structure - this is mostly guesses at the moment.
27  The length of the structure is returned
28  The structure of a long filename depends on the info level. 260 is used
29  by NT and 2 is used by OS/2
30 ****************************************************************************/
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                 case 1: /* OS/2 understands this */
46                         /* these dates are converted to GMT by
47                            make_unix_date */
48                         finfo->ctime = make_unix_date2(p+4);
49                         finfo->atime = make_unix_date2(p+8);
50                         finfo->mtime = make_unix_date2(p+12);
51                         finfo->size = IVAL(p,16);
52                         finfo->mode = CVAL(p,24);
53                         len = CVAL(p, 26);
54                         p += 27;
55                         p += clistr_align_in(cli, p, 0);
56                         /* the len+2 below looks strange but it is
57                            important to cope with the differences
58                            between win2000 and win9x for this call
59                            (tridge) */
60                         p += clistr_pull(cli, finfo->name, p,
61                                          sizeof(finfo->name),
62                                          len+2, 
63                                          STR_TERMINATE);
64                         return PTR_DIFF(p, base);
65
66                 case 2: /* this is what OS/2 uses mostly */
67                         /* these dates are converted to GMT by
68                            make_unix_date */
69                         finfo->ctime = make_unix_date2(p+4);
70                         finfo->atime = make_unix_date2(p+8);
71                         finfo->mtime = make_unix_date2(p+12);
72                         finfo->size = IVAL(p,16);
73                         finfo->mode = CVAL(p,24);
74                         len = CVAL(p, 30);
75                         p += 31;
76                         /* check for unisys! */
77                         p += clistr_pull(cli, finfo->name, p,
78                                          sizeof(finfo->name),
79                                          len, 
80                                          STR_NOALIGN);
81                         return PTR_DIFF(p, base) + 1;
82                         
83                 case 260: /* NT uses this, but also accepts 2 */
84                 {
85                         size_t namelen, slen;
86                         p += 4; /* next entry offset */
87                         p += 4; /* fileindex */
88                                 
89                         /* these dates appear to arrive in a
90                            weird way. It seems to be localtime
91                            plus the serverzone given in the
92                            initial connect. This is GMT when
93                            DST is not in effect and one hour
94                            from GMT otherwise. Can this really
95                            be right??
96                            
97                            I suppose this could be called
98                            kludge-GMT. Is is the GMT you get
99                            by using the current DST setting on
100                            a different localtime. It will be
101                            cheap to calculate, I suppose, as
102                            no DST tables will be needed */
103                         
104                         finfo->ctime = interpret_long_date(p);
105                         p += 8;
106                         finfo->atime = interpret_long_date(p);
107                         p += 8;
108                         finfo->mtime = interpret_long_date(p);
109                         p += 8;
110                         p += 8;
111                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
112                         p += 8;
113                         p += 8; /* alloc size */
114                         finfo->mode = CVAL(p,0);
115                         p += 4;
116                         namelen = IVAL(p,0);
117                         p += 4;
118                         p += 4; /* EA size */
119                         slen = SVAL(p, 0);
120                         p += 2; 
121                         {
122                                 /* stupid NT bugs. grr */
123                                 int flags = 0;
124                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
125                                 clistr_pull(cli, finfo->short_name, p,
126                                             sizeof(finfo->short_name),
127                                             slen, flags);
128                         }
129                         p += 24; /* short name? */        
130                         clistr_pull(cli, finfo->name, p,
131                                     sizeof(finfo->name),
132                                     namelen, 0);
133                         return SVAL(base, 0);
134                 }
135         }
136         
137         DEBUG(1,("Unknown long filename format %d\n",level));
138         return(SVAL(p,0));
139 }
140
141 /****************************************************************************
142  Do a directory listing, calling fn on each file found.
143 ****************************************************************************/
144
145 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
146                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
147 {
148 #if 0
149         int max_matches = 1366; /* Match W2k - was 512. */
150 #else
151         int max_matches = 512;
152 #endif
153         int info_level;
154         char *p, *p2;
155         pstring mask;
156         file_info finfo;
157         int i;
158         char *tdl, *dirlist = NULL;
159         int dirlist_len = 0;
160         int total_received = -1;
161         BOOL First = True;
162         int ff_searchcount=0;
163         int ff_eos=0;
164         int ff_lastname=0;
165         int ff_dir_handle=0;
166         int loop_count = 0;
167         char *rparam=NULL, *rdata=NULL;
168         unsigned int param_len, data_len;       
169         uint16 setup;
170         pstring param;
171
172         /* NT uses 260, OS/2 uses 2. Both accept 1. */
173         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
174         
175         /* when getting a directory listing from a 2k dfs root share, 
176            we have to include the full path (\server\share\mask) here */
177            
178         if ( cli->dfsroot )
179                 pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
180         else
181                 pstrcpy(mask,Mask);
182         
183         while (ff_eos == 0) {
184                 loop_count++;
185                 if (loop_count > 200) {
186                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
187                         break;
188                 }
189
190                 if (First) {
191                         setup = TRANSACT2_FINDFIRST;
192                         SSVAL(param,0,attribute); /* attribute */
193                         SSVAL(param,2,max_matches); /* max count */
194                         SSVAL(param,4,4+2);     /* resume required + close on end */
195                         SSVAL(param,6,info_level); 
196                         SIVAL(param,8,0);
197                         p = param+12;
198                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
199                                          STR_TERMINATE);
200                 } else {
201                         setup = TRANSACT2_FINDNEXT;
202                         SSVAL(param,0,ff_dir_handle);
203                         SSVAL(param,2,max_matches); /* max count */
204                         SSVAL(param,4,info_level); 
205                         SIVAL(param,6,0); /* ff_resume_key */
206                         SSVAL(param,10,8+4+2);  /* continue + resume required + close on end */
207                         p = param+12;
208                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
209                                          STR_TERMINATE);
210                 }
211
212                 param_len = PTR_DIFF(p, param);
213
214                 if (!cli_send_trans(cli, SMBtrans2, 
215                                     NULL,                   /* Name */
216                                     -1, 0,                  /* fid, flags */
217                                     &setup, 1, 0,           /* setup, length, max */
218                                     param, param_len, 10,   /* param, length, max */
219                                     NULL, 0, 
220 #if 0
221                                     /* w2k value. */
222                                     MIN(16384,cli->max_xmit) /* data, length, max. */
223 #else
224                                     cli->max_xmit           /* data, length, max. */
225 #endif
226                                     )) {
227                         break;
228                 }
229
230                 if (!cli_receive_trans(cli, SMBtrans2, 
231                                        &rparam, &param_len,
232                                        &rdata, &data_len) &&
233                     cli_is_dos_error(cli)) {
234                         /* we need to work around a Win95 bug - sometimes
235                            it gives ERRSRV/ERRerror temprarily */
236                         uint8 eclass;
237                         uint32 ecode;
238                         cli_dos_error(cli, &eclass, &ecode);
239                         if (eclass != ERRSRV || ecode != ERRerror)
240                                 break;
241                         smb_msleep(100);
242                         continue;
243                 }
244
245                 if (cli_is_error(cli) || !rdata || !rparam) 
246                         break;
247
248                 if (total_received == -1)
249                         total_received = 0;
250
251                 /* parse out some important return info */
252                 p = rparam;
253                 if (First) {
254                         ff_dir_handle = SVAL(p,0);
255                         ff_searchcount = SVAL(p,2);
256                         ff_eos = SVAL(p,4);
257                         ff_lastname = SVAL(p,8);
258                 } else {
259                         ff_searchcount = SVAL(p,0);
260                         ff_eos = SVAL(p,2);
261                         ff_lastname = SVAL(p,6);
262                 }
263
264                 if (ff_searchcount == 0) 
265                         break;
266
267                 /* point to the data bytes */
268                 p = rdata;
269
270                 /* we might need the lastname for continuations */
271                 if (ff_lastname > 0) {
272                         switch(info_level) {
273                                 case 260:
274                                         clistr_pull(cli, mask, p+ff_lastname,
275                                                     sizeof(mask), 
276                                                     data_len-ff_lastname,
277                                                     STR_TERMINATE);
278                                         break;
279                                 case 1:
280                                         clistr_pull(cli, mask, p+ff_lastname+1,
281                                                     sizeof(mask), 
282                                                     -1,
283                                                     STR_TERMINATE);
284                                         break;
285                                 }
286                 } else {
287                         pstrcpy(mask,"");
288                 }
289  
290                 /* and add them to the dirlist pool */
291                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
292
293                 if (!tdl) {
294                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
295                         break;
296                 } else {
297                         dirlist = tdl;
298                 }
299
300                 /* put in a length for the last entry, to ensure we can chain entries 
301                    into the next packet */
302                 for (p2=p,i=0;i<(ff_searchcount-1);i++)
303                         p2 += interpret_long_filename(cli,info_level,p2,NULL);
304                 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
305
306                 /* grab the data for later use */
307                 memcpy(dirlist+dirlist_len,p,data_len);
308                 dirlist_len += data_len;
309
310                 total_received += ff_searchcount;
311
312                 SAFE_FREE(rdata);
313                 SAFE_FREE(rparam);
314
315                 DEBUG(3,("received %d entries (eos=%d)\n",
316                          ff_searchcount,ff_eos));
317
318                 if (ff_searchcount > 0)
319                         loop_count = 0;
320
321                 First = False;
322         }
323
324         for (p=dirlist,i=0;i<total_received;i++) {
325                 const char *mnt = cli_cm_get_mntpoint( cli );
326                 
327                 p += interpret_long_filename(cli,info_level,p,&finfo);
328                 
329                 fn( mnt,&finfo, Mask, state );
330         }
331
332         /* free up the dirlist buffer */
333         SAFE_FREE(dirlist);
334         return(total_received);
335 }
336
337 /****************************************************************************
338  Interpret a short filename structure.
339  The length of the structure is returned.
340 ****************************************************************************/
341
342 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
343 {
344         extern file_info def_finfo;
345
346         *finfo = def_finfo;
347
348         finfo->mode = CVAL(p,21);
349         
350         /* this date is converted to GMT by make_unix_date */
351         finfo->ctime = make_unix_date(p+22);
352         finfo->mtime = finfo->atime = finfo->ctime;
353         finfo->size = IVAL(p,26);
354         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
355         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
356                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
357                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
358         }
359
360         return(DIR_STRUCT_SIZE);
361 }
362
363
364 /****************************************************************************
365  Do a directory listing, calling fn on each file found.
366  this uses the old SMBsearch interface. It is needed for testing Samba,
367  but should otherwise not be used.
368 ****************************************************************************/
369
370 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
371                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
372 {
373         char *p;
374         int received = 0;
375         BOOL first = True;
376         char status[21];
377         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
378         int num_received = 0;
379         int i;
380         char *tdl, *dirlist = NULL;
381         pstring mask;
382         
383         ZERO_ARRAY(status);
384
385         pstrcpy(mask,Mask);
386   
387         while (1) {
388                 memset(cli->outbuf,'\0',smb_size);
389                 memset(cli->inbuf,'\0',smb_size);
390
391                 set_message(cli->outbuf,2,0,True);
392
393                 SCVAL(cli->outbuf,smb_com,SMBsearch);
394
395                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
396                 cli_setup_packet(cli);
397
398                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
399                 SSVAL(cli->outbuf,smb_vwv1,attribute);
400   
401                 p = smb_buf(cli->outbuf);
402                 *p++ = 4;
403       
404                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
405                 *p++ = 5;
406                 if (first) {
407                         SSVAL(p,0,0);
408                         p += 2;
409                 } else {
410                         SSVAL(p,0,21);
411                         p += 2;
412                         memcpy(p,status,21);
413                         p += 21;
414                 }
415
416                 cli_setup_bcc(cli, p);
417                 cli_send_smb(cli);
418                 if (!cli_receive_smb(cli)) break;
419
420                 received = SVAL(cli->inbuf,smb_vwv0);
421                 if (received <= 0) break;
422
423                 first = False;
424
425                 tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
426
427                 if (!tdl) {
428                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
429                         SAFE_FREE(dirlist);
430                         return 0;
431                 }
432                 else dirlist = tdl;
433
434                 p = smb_buf(cli->inbuf) + 3;
435
436                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
437                        p,received*DIR_STRUCT_SIZE);
438                 
439                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
440                 
441                 num_received += received;
442                 
443                 if (cli_is_error(cli)) break;
444         }
445
446         if (!first) {
447                 memset(cli->outbuf,'\0',smb_size);
448                 memset(cli->inbuf,'\0',smb_size);
449
450                 set_message(cli->outbuf,2,0,True);
451                 SCVAL(cli->outbuf,smb_com,SMBfclose);
452                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
453                 cli_setup_packet(cli);
454
455                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
456                 SSVAL(cli->outbuf, smb_vwv1, attribute);
457
458                 p = smb_buf(cli->outbuf);
459                 *p++ = 4;
460                 fstrcpy(p, "");
461                 p += strlen(p) + 1;
462                 *p++ = 5;
463                 SSVAL(p, 0, 21);
464                 p += 2;
465                 memcpy(p,status,21);
466                 p += 21;
467                 
468                 cli_setup_bcc(cli, p);
469                 cli_send_smb(cli);
470                 if (!cli_receive_smb(cli)) {
471                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
472                 }
473         }
474
475         for (p=dirlist,i=0;i<num_received;i++) {
476                 file_info finfo;
477                 p += interpret_short_filename(cli, p,&finfo);
478                 fn("\\", &finfo, Mask, state);
479         }
480
481         SAFE_FREE(dirlist);
482         return(num_received);
483 }
484
485 /****************************************************************************
486  Do a directory listing, calling fn on each file found.
487  This auto-switches between old and new style.
488 ****************************************************************************/
489
490 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
491              void (*fn)(const char *, file_info *, const char *, void *), void *state)
492 {
493         if (cli->protocol <= PROTOCOL_LANMAN1)
494                 return cli_list_old(cli, Mask, attribute, fn, state);
495         return cli_list_new(cli, Mask, attribute, fn, state);
496 }