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