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