r5967: Fix typo bug where flags overwrote info level.
[jpeach/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  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,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* 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                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
207                            can miss filenames. Use last filename continue instead. JRA */
208                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
209                         p = param+12;
210                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
211                                          STR_TERMINATE);
212                 }
213
214                 param_len = PTR_DIFF(p, param);
215
216                 if (!cli_send_trans(cli, SMBtrans2, 
217                                     NULL,                   /* Name */
218                                     -1, 0,                  /* fid, flags */
219                                     &setup, 1, 0,           /* setup, length, max */
220                                     param, param_len, 10,   /* param, length, max */
221                                     NULL, 0, 
222 #if 0
223                                     /* w2k value. */
224                                     MIN(16384,cli->max_xmit) /* data, length, max. */
225 #else
226                                     cli->max_xmit           /* data, length, max. */
227 #endif
228                                     )) {
229                         break;
230                 }
231
232                 if (!cli_receive_trans(cli, SMBtrans2, 
233                                        &rparam, &param_len,
234                                        &rdata, &data_len) &&
235                     cli_is_dos_error(cli)) {
236                         /* we need to work around a Win95 bug - sometimes
237                            it gives ERRSRV/ERRerror temprarily */
238                         uint8 eclass;
239                         uint32 ecode;
240                         cli_dos_error(cli, &eclass, &ecode);
241                         if (eclass != ERRSRV || ecode != ERRerror)
242                                 break;
243                         smb_msleep(100);
244                         continue;
245                 }
246
247                 if (cli_is_error(cli) || !rdata || !rparam) 
248                         break;
249
250                 if (total_received == -1)
251                         total_received = 0;
252
253                 /* parse out some important return info */
254                 p = rparam;
255                 if (First) {
256                         ff_dir_handle = SVAL(p,0);
257                         ff_searchcount = SVAL(p,2);
258                         ff_eos = SVAL(p,4);
259                         ff_lastname = SVAL(p,8);
260                 } else {
261                         ff_searchcount = SVAL(p,0);
262                         ff_eos = SVAL(p,2);
263                         ff_lastname = SVAL(p,6);
264                 }
265
266                 if (ff_searchcount == 0) 
267                         break;
268
269                 /* point to the data bytes */
270                 p = rdata;
271
272                 /* we might need the lastname for continuations */
273  
274                 /* and add them to the dirlist pool */
275                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
276
277                 if (!tdl) {
278                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
279                         break;
280                 } else {
281                         dirlist = tdl;
282                 }
283
284                 /* put in a length for the last entry, to ensure we can chain entries 
285                    into the next packet */
286                 for (p2=p,i=0;i<(ff_searchcount-1);i++) {
287                         p2 += interpret_long_filename(cli,info_level,p2,&finfo);
288                 }
289                 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
290
291                 /* we might need the lastname for continuations */
292                 if (ff_lastname > 0) {
293                         pstrcpy(mask, finfo.name);
294                 } else {
295                         pstrcpy(mask,"");
296                 }
297
298                 /* grab the data for later use */
299                 memcpy(dirlist+dirlist_len,p,data_len);
300                 dirlist_len += data_len;
301
302                 total_received += ff_searchcount;
303
304                 SAFE_FREE(rdata);
305                 SAFE_FREE(rparam);
306
307                 DEBUG(3,("received %d entries (eos=%d)\n",
308                          ff_searchcount,ff_eos));
309
310                 if (ff_searchcount > 0)
311                         loop_count = 0;
312
313                 First = False;
314         }
315
316         for (p=dirlist,i=0;i<total_received;i++) {
317                 const char *mnt = cli_cm_get_mntpoint( cli );
318                 
319                 p += interpret_long_filename(cli,info_level,p,&finfo);
320                 
321                 fn( mnt,&finfo, Mask, state );
322         }
323
324         /* free up the dirlist buffer */
325         SAFE_FREE(dirlist);
326         return(total_received);
327 }
328
329 /****************************************************************************
330  Interpret a short filename structure.
331  The length of the structure is returned.
332 ****************************************************************************/
333
334 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
335 {
336         extern file_info def_finfo;
337
338         *finfo = def_finfo;
339
340         finfo->mode = CVAL(p,21);
341         
342         /* this date is converted to GMT by make_unix_date */
343         finfo->ctime = make_unix_date(p+22);
344         finfo->mtime = finfo->atime = finfo->ctime;
345         finfo->size = IVAL(p,26);
346         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
347         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
348                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
349                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
350         }
351
352         return(DIR_STRUCT_SIZE);
353 }
354
355
356 /****************************************************************************
357  Do a directory listing, calling fn on each file found.
358  this uses the old SMBsearch interface. It is needed for testing Samba,
359  but should otherwise not be used.
360 ****************************************************************************/
361
362 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
363                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
364 {
365         char *p;
366         int received = 0;
367         BOOL first = True;
368         char status[21];
369         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
370         int num_received = 0;
371         int i;
372         char *tdl, *dirlist = NULL;
373         pstring mask;
374         
375         ZERO_ARRAY(status);
376
377         pstrcpy(mask,Mask);
378   
379         while (1) {
380                 memset(cli->outbuf,'\0',smb_size);
381                 memset(cli->inbuf,'\0',smb_size);
382
383                 set_message(cli->outbuf,2,0,True);
384
385                 SCVAL(cli->outbuf,smb_com,SMBsearch);
386
387                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
388                 cli_setup_packet(cli);
389
390                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
391                 SSVAL(cli->outbuf,smb_vwv1,attribute);
392   
393                 p = smb_buf(cli->outbuf);
394                 *p++ = 4;
395       
396                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
397                 *p++ = 5;
398                 if (first) {
399                         SSVAL(p,0,0);
400                         p += 2;
401                 } else {
402                         SSVAL(p,0,21);
403                         p += 2;
404                         memcpy(p,status,21);
405                         p += 21;
406                 }
407
408                 cli_setup_bcc(cli, p);
409                 cli_send_smb(cli);
410                 if (!cli_receive_smb(cli)) break;
411
412                 received = SVAL(cli->inbuf,smb_vwv0);
413                 if (received <= 0) break;
414
415                 first = False;
416
417                 tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
418
419                 if (!tdl) {
420                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
421                         SAFE_FREE(dirlist);
422                         return 0;
423                 }
424                 else dirlist = tdl;
425
426                 p = smb_buf(cli->inbuf) + 3;
427
428                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
429                        p,received*DIR_STRUCT_SIZE);
430                 
431                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
432                 
433                 num_received += received;
434                 
435                 if (cli_is_error(cli)) break;
436         }
437
438         if (!first) {
439                 memset(cli->outbuf,'\0',smb_size);
440                 memset(cli->inbuf,'\0',smb_size);
441
442                 set_message(cli->outbuf,2,0,True);
443                 SCVAL(cli->outbuf,smb_com,SMBfclose);
444                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
445                 cli_setup_packet(cli);
446
447                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
448                 SSVAL(cli->outbuf, smb_vwv1, attribute);
449
450                 p = smb_buf(cli->outbuf);
451                 *p++ = 4;
452                 fstrcpy(p, "");
453                 p += strlen(p) + 1;
454                 *p++ = 5;
455                 SSVAL(p, 0, 21);
456                 p += 2;
457                 memcpy(p,status,21);
458                 p += 21;
459                 
460                 cli_setup_bcc(cli, p);
461                 cli_send_smb(cli);
462                 if (!cli_receive_smb(cli)) {
463                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
464                 }
465         }
466
467         for (p=dirlist,i=0;i<num_received;i++) {
468                 file_info finfo;
469                 p += interpret_short_filename(cli, p,&finfo);
470                 fn("\\", &finfo, Mask, state);
471         }
472
473         SAFE_FREE(dirlist);
474         return(num_received);
475 }
476
477 /****************************************************************************
478  Do a directory listing, calling fn on each file found.
479  This auto-switches between old and new style.
480 ****************************************************************************/
481
482 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
483              void (*fn)(const char *, file_info *, const char *, void *), void *state)
484 {
485         if (cli->protocol <= PROTOCOL_LANMAN1)
486                 return cli_list_old(cli, Mask, attribute, fn, state);
487         return cli_list_new(cli, Mask, attribute, fn, state);
488 }