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