r5975: Re-arrange code and comments to make more sense.
[idra/samba.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,(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                 for (p2=p,i=0;i<ff_searchcount;i++) {
274                         p2 += interpret_long_filename(cli,info_level,p2,&finfo);
275                 }
276
277                 if (ff_lastname > 0) {
278                         pstrcpy(mask, finfo.name);
279                 } else {
280                         pstrcpy(mask,"");
281                 }
282
283                 /* grab the data for later use */
284                 /* and add them to the dirlist pool */
285                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
286
287                 if (!tdl) {
288                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
289                         break;
290                 } else {
291                         dirlist = tdl;
292                 }
293
294                 memcpy(dirlist+dirlist_len,p,data_len);
295                 dirlist_len += data_len;
296
297                 total_received += ff_searchcount;
298
299                 SAFE_FREE(rdata);
300                 SAFE_FREE(rparam);
301
302                 DEBUG(3,("received %d entries (eos=%d)\n",
303                          ff_searchcount,ff_eos));
304
305                 if (ff_searchcount > 0)
306                         loop_count = 0;
307
308                 First = False;
309         }
310
311         for (p=dirlist,i=0;i<total_received;i++) {
312                 const char *mnt = cli_cm_get_mntpoint( cli );
313                 
314                 p += interpret_long_filename(cli,info_level,p,&finfo);
315                 
316                 fn( mnt,&finfo, Mask, state );
317         }
318
319         /* free up the dirlist buffer */
320         SAFE_FREE(dirlist);
321         return(total_received);
322 }
323
324 /****************************************************************************
325  Interpret a short filename structure.
326  The length of the structure is returned.
327 ****************************************************************************/
328
329 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
330 {
331         extern file_info def_finfo;
332
333         *finfo = def_finfo;
334
335         finfo->mode = CVAL(p,21);
336         
337         /* this date is converted to GMT by make_unix_date */
338         finfo->ctime = make_unix_date(p+22);
339         finfo->mtime = finfo->atime = finfo->ctime;
340         finfo->size = IVAL(p,26);
341         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
342         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
343                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
344                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
345         }
346
347         return(DIR_STRUCT_SIZE);
348 }
349
350
351 /****************************************************************************
352  Do a directory listing, calling fn on each file found.
353  this uses the old SMBsearch interface. It is needed for testing Samba,
354  but should otherwise not be used.
355 ****************************************************************************/
356
357 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
358                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
359 {
360         char *p;
361         int received = 0;
362         BOOL first = True;
363         char status[21];
364         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
365         int num_received = 0;
366         int i;
367         char *tdl, *dirlist = NULL;
368         pstring mask;
369         
370         ZERO_ARRAY(status);
371
372         pstrcpy(mask,Mask);
373   
374         while (1) {
375                 memset(cli->outbuf,'\0',smb_size);
376                 memset(cli->inbuf,'\0',smb_size);
377
378                 set_message(cli->outbuf,2,0,True);
379
380                 SCVAL(cli->outbuf,smb_com,SMBsearch);
381
382                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
383                 cli_setup_packet(cli);
384
385                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
386                 SSVAL(cli->outbuf,smb_vwv1,attribute);
387   
388                 p = smb_buf(cli->outbuf);
389                 *p++ = 4;
390       
391                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
392                 *p++ = 5;
393                 if (first) {
394                         SSVAL(p,0,0);
395                         p += 2;
396                 } else {
397                         SSVAL(p,0,21);
398                         p += 2;
399                         memcpy(p,status,21);
400                         p += 21;
401                 }
402
403                 cli_setup_bcc(cli, p);
404                 cli_send_smb(cli);
405                 if (!cli_receive_smb(cli)) break;
406
407                 received = SVAL(cli->inbuf,smb_vwv0);
408                 if (received <= 0) break;
409
410                 first = False;
411
412                 tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
413
414                 if (!tdl) {
415                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
416                         SAFE_FREE(dirlist);
417                         return 0;
418                 }
419                 else dirlist = tdl;
420
421                 p = smb_buf(cli->inbuf) + 3;
422
423                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
424                        p,received*DIR_STRUCT_SIZE);
425                 
426                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
427                 
428                 num_received += received;
429                 
430                 if (cli_is_error(cli)) break;
431         }
432
433         if (!first) {
434                 memset(cli->outbuf,'\0',smb_size);
435                 memset(cli->inbuf,'\0',smb_size);
436
437                 set_message(cli->outbuf,2,0,True);
438                 SCVAL(cli->outbuf,smb_com,SMBfclose);
439                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
440                 cli_setup_packet(cli);
441
442                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
443                 SSVAL(cli->outbuf, smb_vwv1, attribute);
444
445                 p = smb_buf(cli->outbuf);
446                 *p++ = 4;
447                 fstrcpy(p, "");
448                 p += strlen(p) + 1;
449                 *p++ = 5;
450                 SSVAL(p, 0, 21);
451                 p += 2;
452                 memcpy(p,status,21);
453                 p += 21;
454                 
455                 cli_setup_bcc(cli, p);
456                 cli_send_smb(cli);
457                 if (!cli_receive_smb(cli)) {
458                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
459                 }
460         }
461
462         for (p=dirlist,i=0;i<num_received;i++) {
463                 file_info finfo;
464                 p += interpret_short_filename(cli, p,&finfo);
465                 fn("\\", &finfo, Mask, state);
466         }
467
468         SAFE_FREE(dirlist);
469         return(num_received);
470 }
471
472 /****************************************************************************
473  Do a directory listing, calling fn on each file found.
474  This auto-switches between old and new style.
475 ****************************************************************************/
476
477 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
478              void (*fn)(const char *, file_info *, const char *, void *), void *state)
479 {
480         if (cli->protocol <= PROTOCOL_LANMAN1)
481                 return cli_list_old(cli, Mask, attribute, fn, state);
482         return cli_list_new(cli, Mask, attribute, fn, state);
483 }