Fixed crash bug when attempting to list contents of non-existent
[kai/samba.git] / source3 / libsmb / clilist.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    client directory list routines
5    Copyright (C) Andrew Tridgell 1994-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define NO_SYSLOG
23
24 #include "includes.h"
25
26
27 /****************************************************************************
28 interpret a long filename structure - this is mostly guesses at the moment
29 The length of the structure is returned
30 The structure of a long filename depends on the info level. 260 is used
31 by NT and 2 is used by OS/2
32 ****************************************************************************/
33 static int interpret_long_filename(struct cli_state *cli,
34                                    int level,char *p,file_info *finfo)
35 {
36         extern file_info def_finfo;
37         file_info finfo2;
38         int len;
39         char *base = p;
40
41         if (!finfo) finfo = &finfo2;
42
43         memcpy(finfo,&def_finfo,sizeof(*finfo));
44
45         switch (level)
46                 {
47                 case 1: /* OS/2 understands this */
48                         /* these dates are converted to GMT by
49                            make_unix_date */
50                         finfo->ctime = make_unix_date2(p+4);
51                         finfo->atime = make_unix_date2(p+8);
52                         finfo->mtime = make_unix_date2(p+12);
53                         finfo->size = IVAL(p,16);
54                         finfo->mode = CVAL(p,24);
55                         len = CVAL(p, 26);
56                         p += 27;
57                         p += clistr_align_in(cli, p, 0);
58                         p += clistr_pull(cli, finfo->name, p,
59                                     sizeof(finfo->name),
60                                     len, 
61                                     STR_TERMINATE);
62                         return PTR_DIFF(p, base);
63
64                 case 2: /* this is what OS/2 uses mostly */
65                         /* these dates are converted to GMT by
66                            make_unix_date */
67                         finfo->ctime = make_unix_date2(p+4);
68                         finfo->atime = make_unix_date2(p+8);
69                         finfo->mtime = make_unix_date2(p+12);
70                         finfo->size = IVAL(p,16);
71                         finfo->mode = CVAL(p,24);
72                         len = CVAL(p, 30);
73                         p += 31;
74                         p += clistr_pull(cli, finfo->name, p,
75                                          sizeof(finfo->name),
76                                          len, 
77                                          STR_NOALIGN);
78                         return PTR_DIFF(p, base) + 1;
79                         
80                 case 260: /* NT uses this, but also accepts 2 */
81                 {
82                         int namelen, slen;
83                         p += 4; /* next entry offset */
84                         p += 4; /* fileindex */
85                                 
86                         /* these dates appear to arrive in a
87                            weird way. It seems to be localtime
88                            plus the serverzone given in the
89                            initial connect. This is GMT when
90                            DST is not in effect and one hour
91                            from GMT otherwise. Can this really
92                            be right??
93                            
94                            I suppose this could be called
95                            kludge-GMT. Is is the GMT you get
96                            by using the current DST setting on
97                            a different localtime. It will be
98                            cheap to calculate, I suppose, as
99                            no DST tables will be needed */
100                         
101                         finfo->ctime = interpret_long_date(p); p += 8;
102                         finfo->atime = interpret_long_date(p); p += 8;
103                         finfo->mtime = interpret_long_date(p); p += 8; p += 8;
104                         finfo->size = IVAL(p,0); p += 8;
105                         p += 8; /* alloc size */
106                         finfo->mode = CVAL(p,0); p += 4;
107                         namelen = IVAL(p,0); p += 4;
108                         p += 4; /* EA size */
109                         slen = SVAL(p, 0);
110                         p += 2; 
111                         {
112                                 /* stupid NT bugs. grr */
113                                 int flags = 0;
114                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
115                                 clistr_pull(cli, finfo->short_name, p,
116                                             sizeof(finfo->short_name),
117                                             24, flags);
118                         }
119                         p += 24; /* short name? */        
120                         clistr_pull(cli, finfo->name, p,
121                                     sizeof(finfo->name),
122                                     namelen, 0);
123                         return SVAL(base, 0);
124                 }
125                 }
126         
127         DEBUG(1,("Unknown long filename format %d\n",level));
128         return(SVAL(p,0));
129 }
130
131
132 /****************************************************************************
133   do a directory listing, calling fn on each file found
134   ****************************************************************************/
135 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
136                  void (*fn)(file_info *, const char *, void *), void *state)
137 {
138         int max_matches = 512;
139         int info_level;
140         char *p, *p2;
141         pstring mask;
142         file_info finfo;
143         int i;
144         char *tdl, *dirlist = NULL;
145         int dirlist_len = 0;
146         int total_received = -1;
147         BOOL First = True;
148         int ff_searchcount=0;
149         int ff_eos=0;
150         int ff_lastname=0;
151         int ff_dir_handle=0;
152         int loop_count = 0;
153         char *rparam=NULL, *rdata=NULL;
154         int param_len, data_len;        
155         uint16 setup;
156         pstring param;
157
158         /* NT uses 260, OS/2 uses 2. Both accept 1. */
159         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
160
161         pstrcpy(mask,Mask);
162         
163         while (ff_eos == 0) {
164                 loop_count++;
165                 if (loop_count > 200) {
166                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
167                         break;
168                 }
169
170                 if (First) {
171                         setup = TRANSACT2_FINDFIRST;
172                         SSVAL(param,0,attribute); /* attribute */
173                         SSVAL(param,2,max_matches); /* max count */
174                         SSVAL(param,4,4+2);     /* resume required + close on end */
175                         SSVAL(param,6,info_level); 
176                         SIVAL(param,8,0);
177                         p = param+12;
178                         p += clistr_push(cli, param+12, mask, -1, 
179                                          STR_TERMINATE);
180                 } else {
181                         setup = TRANSACT2_FINDNEXT;
182                         SSVAL(param,0,ff_dir_handle);
183                         SSVAL(param,2,max_matches); /* max count */
184                         SSVAL(param,4,info_level); 
185                         SIVAL(param,6,0); /* ff_resume_key */
186                         SSVAL(param,10,8+4+2);  /* continue + resume required + close on end */
187                         p = param+12;
188                         p += clistr_push(cli, param+12, mask, -1, 
189                                          STR_TERMINATE);
190                 }
191
192                 param_len = PTR_DIFF(p, param);
193
194                 if (!cli_send_trans(cli, SMBtrans2, 
195                                     NULL,                   /* Name */
196                                     -1, 0,                  /* fid, flags */
197                                     &setup, 1, 0,           /* setup, length, max */
198                                     param, param_len, 10,   /* param, length, max */
199                                     NULL, 0, 
200                                     cli->max_xmit /* data, length, max */
201                                     )) {
202                         break;
203                 }
204
205                 if (!cli_receive_trans(cli, SMBtrans2, 
206                                        &rparam, &param_len,
207                                        &rdata, &data_len) &&
208                     cli_is_dos_error(cli)) {
209                         /* we need to work around a Win95 bug - sometimes
210                            it gives ERRSRV/ERRerror temprarily */
211                         uint8 eclass;
212                         uint32 ecode;
213                         cli_dos_error(cli, &eclass, &ecode);
214                         if (eclass != ERRSRV || ecode != ERRerror) break;
215                         msleep(100);
216                         continue;
217                 }
218
219                 if (cli_is_error(cli)) 
220                         return -1;
221
222                 if (total_received == -1) total_received = 0;
223
224                 /* parse out some important return info */
225                 p = rparam;
226                 if (First) {
227                         ff_dir_handle = SVAL(p,0);
228                         ff_searchcount = SVAL(p,2);
229                         ff_eos = SVAL(p,4);
230                         ff_lastname = SVAL(p,8);
231                 } else {
232                         ff_searchcount = SVAL(p,0);
233                         ff_eos = SVAL(p,2);
234                         ff_lastname = SVAL(p,6);
235                 }
236
237                 if (ff_searchcount == 0) 
238                         break;
239
240                 /* point to the data bytes */
241                 p = rdata;
242
243                 /* we might need the lastname for continuations */
244                 if (ff_lastname > 0) {
245                         switch(info_level)
246                                 {
247                                 case 260:
248                                         clistr_pull(cli, mask, p+ff_lastname,
249                                                     sizeof(mask), 
250                                                     data_len-ff_lastname,
251                                                     STR_TERMINATE);
252                                         break;
253                                 case 1:
254                                         clistr_pull(cli, mask, p+ff_lastname+1,
255                                                     sizeof(mask), 
256                                                     -1,
257                                                     STR_TERMINATE);
258                                         break;
259                                 }
260                 } else {
261                         pstrcpy(mask,"");
262                 }
263  
264                 /* and add them to the dirlist pool */
265                 tdl = Realloc(dirlist,dirlist_len + data_len);
266
267                 if (!tdl) {
268                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
269                         break;
270                 }
271                 else dirlist = tdl;
272
273                 /* put in a length for the last entry, to ensure we can chain entries 
274                    into the next packet */
275                 for (p2=p,i=0;i<(ff_searchcount-1);i++)
276                         p2 += interpret_long_filename(cli,info_level,p2,NULL);
277                 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
278
279                 /* grab the data for later use */
280                 memcpy(dirlist+dirlist_len,p,data_len);
281                 dirlist_len += data_len;
282
283                 total_received += ff_searchcount;
284
285                 if (rdata) free(rdata); rdata = NULL;
286                 if (rparam) free(rparam); rparam = NULL;
287                 
288                 DEBUG(3,("received %d entries (eos=%d)\n",
289                          ff_searchcount,ff_eos));
290
291                 if (ff_searchcount > 0) loop_count = 0;
292
293                 First = False;
294         }
295
296         for (p=dirlist,i=0;i<total_received;i++) {
297                 p += interpret_long_filename(cli,info_level,p,&finfo);
298                 fn(&finfo, Mask, state);
299         }
300
301         /* free up the dirlist buffer */
302         if (dirlist) free(dirlist);
303         return(total_received);
304 }
305
306
307
308 /****************************************************************************
309 interpret a short filename structure
310 The length of the structure is returned
311 ****************************************************************************/
312 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
313 {
314         extern file_info def_finfo;
315
316         *finfo = def_finfo;
317
318         finfo->mode = CVAL(p,21);
319         
320         /* this date is converted to GMT by make_unix_date */
321         finfo->ctime = make_unix_date(p+22);
322         finfo->mtime = finfo->atime = finfo->ctime;
323         finfo->size = IVAL(p,26);
324         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
325         if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
326                 fstrcpy(finfo->short_name,finfo->name);
327         
328         return(DIR_STRUCT_SIZE);
329 }
330
331
332 /****************************************************************************
333   do a directory listing, calling fn on each file found
334   this uses the old SMBsearch interface. It is needed for testing Samba,
335   but should otherwise not be used
336   ****************************************************************************/
337 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
338                  void (*fn)(file_info *, const char *, void *), void *state)
339 {
340         char *p;
341         int received = 0;
342         BOOL first = True;
343         char status[21];
344         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
345         int num_received = 0;
346         int i;
347         char *tdl, *dirlist = NULL;
348         pstring mask;
349         
350         ZERO_ARRAY(status);
351
352         pstrcpy(mask,Mask);
353   
354         while (1) {
355                 memset(cli->outbuf,'\0',smb_size);
356                 memset(cli->inbuf,'\0',smb_size);
357
358                 set_message(cli->outbuf,2,0,True);
359
360                 CVAL(cli->outbuf,smb_com) = SMBsearch;
361
362                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
363                 cli_setup_packet(cli);
364
365                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
366                 SSVAL(cli->outbuf,smb_vwv1,attribute);
367   
368                 p = smb_buf(cli->outbuf);
369                 *p++ = 4;
370       
371                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
372                 *p++ = 5;
373                 if (first) {
374                         SSVAL(p,0,0);
375                         p += 2;
376                 } else {
377                         SSVAL(p,0,21);
378                         p += 2;
379                         memcpy(p,status,21);
380                         p += 21;
381                 }
382
383                 cli_setup_bcc(cli, p);
384                 cli_send_smb(cli);
385                 if (!cli_receive_smb(cli)) break;
386
387                 received = SVAL(cli->inbuf,smb_vwv0);
388                 if (received <= 0) break;
389
390                 first = False;
391
392                 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
393
394                 if (!tdl) {
395                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
396                         if (dirlist) free(dirlist);
397                         return 0;
398                 }
399                 else dirlist = tdl;
400
401                 p = smb_buf(cli->inbuf) + 3;
402
403                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
404                        p,received*DIR_STRUCT_SIZE);
405                 
406                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
407                 
408                 num_received += received;
409                 
410                 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
411         }
412
413         if (!first) {
414                 memset(cli->outbuf,'\0',smb_size);
415                 memset(cli->inbuf,'\0',smb_size);
416
417                 set_message(cli->outbuf,2,0,True);
418                 CVAL(cli->outbuf,smb_com) = SMBfclose;
419                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
420                 cli_setup_packet(cli);
421
422                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
423                 SSVAL(cli->outbuf, smb_vwv1, attribute);
424
425                 p = smb_buf(cli->outbuf);
426                 *p++ = 4;
427                 fstrcpy(p, "");
428                 p += strlen(p) + 1;
429                 *p++ = 5;
430                 SSVAL(p, 0, 21);
431                 p += 2;
432                 memcpy(p,status,21);
433                 p += 21;
434                 
435                 cli_setup_bcc(cli, p);
436                 cli_send_smb(cli);
437                 if (!cli_receive_smb(cli)) {
438                         DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
439                 }
440         }
441
442         for (p=dirlist,i=0;i<num_received;i++) {
443                 file_info finfo;
444                 p += interpret_short_filename(cli, p,&finfo);
445                 fn(&finfo, Mask, state);
446         }
447
448         if (dirlist) free(dirlist);
449         return(num_received);
450 }
451
452
453 /****************************************************************************
454   do a directory listing, calling fn on each file found
455   this auto-switches between old and new style
456   ****************************************************************************/
457 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
458              void (*fn)(file_info *, const char *, void *), void *state)
459 {
460         if (cli->protocol <= PROTOCOL_LANMAN1) {
461                 return cli_list_old(cli, Mask, attribute, fn, state);
462         }
463         return cli_list_new(cli, Mask, attribute, fn, state);
464 }