r12275: Fix memory leak found by Mikhail Kshevetskiy <kl@laska.dorms.spbu.ru>
[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 #include "includes.h"
22
23 extern file_info def_finfo;
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 size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
33                                         uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
34 {
35         file_info finfo2;
36         int len;
37         char *base = p;
38
39         if (!finfo) {
40                 finfo = &finfo2;
41         }
42
43         if (p_resume_key) {
44                 *p_resume_key = 0;
45         }
46         memcpy(finfo,&def_finfo,sizeof(*finfo));
47
48         switch (level) {
49                 case 1: /* OS/2 understands this */
50                         /* these dates are converted to GMT by
51                            make_unix_date */
52                         finfo->ctime = cli_make_unix_date2(cli, p+4);
53                         finfo->atime = cli_make_unix_date2(cli, p+8);
54                         finfo->mtime = cli_make_unix_date2(cli, p+12);
55                         finfo->size = IVAL(p,16);
56                         finfo->mode = CVAL(p,24);
57                         len = CVAL(p, 26);
58                         p += 27;
59                         p += clistr_align_in(cli, p, 0);
60                         /* the len+2 below looks strange but it is
61                            important to cope with the differences
62                            between win2000 and win9x for this call
63                            (tridge) */
64                         p += clistr_pull(cli, finfo->name, p,
65                                          sizeof(finfo->name),
66                                          len+2, 
67                                          STR_TERMINATE);
68                         return PTR_DIFF(p, base);
69
70                 case 2: /* this is what OS/2 uses mostly */
71                         /* these dates are converted to GMT by
72                            make_unix_date */
73                         finfo->ctime = cli_make_unix_date2(cli, p+4);
74                         finfo->atime = cli_make_unix_date2(cli, p+8);
75                         finfo->mtime = cli_make_unix_date2(cli, p+12);
76                         finfo->size = IVAL(p,16);
77                         finfo->mode = CVAL(p,24);
78                         len = CVAL(p, 30);
79                         p += 31;
80                         /* check for unisys! */
81                         p += clistr_pull(cli, finfo->name, p,
82                                          sizeof(finfo->name),
83                                          len, 
84                                          STR_NOALIGN);
85                         return PTR_DIFF(p, base) + 1;
86                         
87                 case 260: /* NT uses this, but also accepts 2 */
88                 {
89                         size_t namelen, slen;
90                         p += 4; /* next entry offset */
91
92                         if (p_resume_key) {
93                                 *p_resume_key = IVAL(p,0);
94                         }
95                         p += 4; /* fileindex */
96                                 
97                         /* these dates appear to arrive in a
98                            weird way. It seems to be localtime
99                            plus the serverzone given in the
100                            initial connect. This is GMT when
101                            DST is not in effect and one hour
102                            from GMT otherwise. Can this really
103                            be right??
104                            
105                            I suppose this could be called
106                            kludge-GMT. Is is the GMT you get
107                            by using the current DST setting on
108                            a different localtime. It will be
109                            cheap to calculate, I suppose, as
110                            no DST tables will be needed */
111                         
112                         finfo->ctime = interpret_long_date(p);
113                         p += 8;
114                         finfo->atime = interpret_long_date(p);
115                         p += 8;
116                         finfo->mtime = interpret_long_date(p);
117                         p += 8;
118                         p += 8;
119                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
120                         p += 8;
121                         p += 8; /* alloc size */
122                         finfo->mode = CVAL(p,0);
123                         p += 4;
124                         namelen = IVAL(p,0);
125                         p += 4;
126                         p += 4; /* EA size */
127                         slen = SVAL(p, 0);
128                         p += 2; 
129                         {
130                                 /* stupid NT bugs. grr */
131                                 int flags = 0;
132                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
133                                 clistr_pull(cli, finfo->short_name, p,
134                                             sizeof(finfo->short_name),
135                                             slen, flags);
136                         }
137                         p += 24; /* short name? */        
138                         clistr_pull(cli, finfo->name, p,
139                                     sizeof(finfo->name),
140                                     namelen, 0);
141
142                         /* To be robust in the face of unicode conversion failures
143                            we need to copy the raw bytes of the last name seen here.
144                            Namelen doesn't include the terminating unicode null, so
145                            copy it here. */
146
147                         if (p_last_name_raw && p_last_name_raw_len) {
148                                 if (namelen + 2 > p_last_name_raw->length) {
149                                         memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
150                                         *p_last_name_raw_len = 0;
151                                 } else {
152                                         memcpy(p_last_name_raw->data, p, namelen);
153                                         SSVAL(p_last_name_raw->data, namelen, 0);
154                                         *p_last_name_raw_len = namelen + 2;
155                                 }
156                         }
157                         return (size_t)IVAL(base, 0);
158                 }
159         }
160         
161         DEBUG(1,("Unknown long filename format %d\n",level));
162         return (size_t)IVAL(base,0);
163 }
164
165 /****************************************************************************
166  Do a directory listing, calling fn on each file found.
167 ****************************************************************************/
168
169 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
170                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
171 {
172 #if 1
173         int max_matches = 1366; /* Match W2k - was 512. */
174 #else
175         int max_matches = 512;
176 #endif
177         int info_level;
178         char *p, *p2;
179         pstring mask;
180         file_info finfo;
181         int i;
182         char *tdl, *dirlist = NULL;
183         int dirlist_len = 0;
184         int total_received = -1;
185         BOOL First = True;
186         int ff_searchcount=0;
187         int ff_eos=0;
188         int ff_lastname=0;
189         int ff_dir_handle=0;
190         int loop_count = 0;
191         char *rparam=NULL, *rdata=NULL;
192         unsigned int param_len, data_len;       
193         uint16 setup;
194         pstring param;
195         const char *mnt;
196         uint32 resume_key = 0;
197         uint32 last_name_raw_len = 0;
198         DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
199
200         /* NT uses 260, OS/2 uses 2. Both accept 1. */
201         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
202         
203         /* when getting a directory listing from a 2k dfs root share, 
204            we have to include the full path (\server\share\mask) here */
205            
206         if ( cli->dfsroot )
207                 pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
208         else
209                 pstrcpy(mask,Mask);
210         
211         while (ff_eos == 0) {
212                 loop_count++;
213                 if (loop_count > 200) {
214                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
215                         break;
216                 }
217
218                 if (First) {
219                         setup = TRANSACT2_FINDFIRST;
220                         SSVAL(param,0,attribute); /* attribute */
221                         SSVAL(param,2,max_matches); /* max count */
222                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
223                         SSVAL(param,6,info_level); 
224                         SIVAL(param,8,0);
225                         p = param+12;
226                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
227                                          STR_TERMINATE);
228                 } else {
229                         setup = TRANSACT2_FINDNEXT;
230                         SSVAL(param,0,ff_dir_handle);
231                         SSVAL(param,2,max_matches); /* max count */
232                         SSVAL(param,4,info_level); 
233                         /* For W2K servers serving out FAT filesystems we *must* set the
234                            resume key. If it's not FAT then it's returned as zero. */
235                         SIVAL(param,6,resume_key); /* ff_resume_key */
236                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
237                            can miss filenames. Use last filename continue instead. JRA */
238                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
239                         p = param+12;
240                         if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
241                                 memcpy(p, last_name_raw.data, last_name_raw_len);
242                                 p += last_name_raw_len;
243                         } else {
244                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
245                         }
246                 }
247
248                 param_len = PTR_DIFF(p, param);
249
250                 if (!cli_send_trans(cli, SMBtrans2, 
251                                     NULL,                   /* Name */
252                                     -1, 0,                  /* fid, flags */
253                                     &setup, 1, 0,           /* setup, length, max */
254                                     param, param_len, 10,   /* param, length, max */
255                                     NULL, 0, 
256 #if 0
257                                     /* w2k value. */
258                                     MIN(16384,cli->max_xmit) /* data, length, max. */
259 #else
260                                     cli->max_xmit           /* data, length, max. */
261 #endif
262                                     )) {
263                         break;
264                 }
265
266                 if (!cli_receive_trans(cli, SMBtrans2, 
267                                        &rparam, &param_len,
268                                        &rdata, &data_len) &&
269                     cli_is_dos_error(cli)) {
270                         /* we need to work around a Win95 bug - sometimes
271                            it gives ERRSRV/ERRerror temprarily */
272                         uint8 eclass;
273                         uint32 ecode;
274
275                         SAFE_FREE(rdata);
276                         SAFE_FREE(rparam);
277
278                         cli_dos_error(cli, &eclass, &ecode);
279                         if (eclass != ERRSRV || ecode != ERRerror)
280                                 break;
281                         smb_msleep(100);
282                         continue;
283                 }
284
285                 if (cli_is_error(cli) || !rdata || !rparam) {
286                         SAFE_FREE(rdata);
287                         SAFE_FREE(rparam);
288                         break;
289                 }
290
291                 if (total_received == -1)
292                         total_received = 0;
293
294                 /* parse out some important return info */
295                 p = rparam;
296                 if (First) {
297                         ff_dir_handle = SVAL(p,0);
298                         ff_searchcount = SVAL(p,2);
299                         ff_eos = SVAL(p,4);
300                         ff_lastname = SVAL(p,8);
301                 } else {
302                         ff_searchcount = SVAL(p,0);
303                         ff_eos = SVAL(p,2);
304                         ff_lastname = SVAL(p,6);
305                 }
306
307                 if (ff_searchcount == 0) {
308                         SAFE_FREE(rdata);
309                         SAFE_FREE(rparam);
310                         break;
311                 }
312
313                 /* point to the data bytes */
314                 p = rdata;
315
316                 /* we might need the lastname for continuations */
317                 for (p2=p,i=0;i<ff_searchcount;i++) {
318                         if ((info_level == 260) && (i == ff_searchcount-1)) {
319                                 /* Last entry - fixup the last offset length. */
320                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
321                         }
322                         p2 += interpret_long_filename(cli,info_level,p2,&finfo,
323                                                         &resume_key,&last_name_raw,&last_name_raw_len);
324
325                         if (!First && *mask && strcsequal(finfo.name, mask)) {
326                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
327                                         finfo.name));
328                                 ff_eos = 1;
329                                 break;
330                         }
331                 }
332
333                 if (ff_lastname > 0) {
334                         pstrcpy(mask, finfo.name);
335                 } else {
336                         pstrcpy(mask,"");
337                 }
338
339                 /* grab the data for later use */
340                 /* and add them to the dirlist pool */
341                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
342
343                 if (!tdl) {
344                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
345                         SAFE_FREE(rdata);
346                         SAFE_FREE(rparam);
347                         break;
348                 } else {
349                         dirlist = tdl;
350                 }
351
352                 memcpy(dirlist+dirlist_len,p,data_len);
353                 dirlist_len += data_len;
354
355                 total_received += ff_searchcount;
356
357                 SAFE_FREE(rdata);
358                 SAFE_FREE(rparam);
359
360                 DEBUG(3,("received %d entries (eos=%d)\n",
361                          ff_searchcount,ff_eos));
362
363                 if (ff_searchcount > 0)
364                         loop_count = 0;
365
366                 First = False;
367         }
368
369         mnt = cli_cm_get_mntpoint( cli );
370
371         for (p=dirlist,i=0;i<total_received;i++) {
372                 p += interpret_long_filename(cli,info_level,p,&finfo,NULL,NULL,NULL);
373                 fn( mnt,&finfo, Mask, state );
374         }
375
376         /* free up the dirlist buffer and last name raw blob */
377         SAFE_FREE(dirlist);
378         data_blob_free(&last_name_raw);
379         return(total_received);
380 }
381
382 /****************************************************************************
383  Interpret a short filename structure.
384  The length of the structure is returned.
385 ****************************************************************************/
386
387 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
388 {
389
390         *finfo = def_finfo;
391
392         finfo->mode = CVAL(p,21);
393         
394         /* this date is converted to GMT by make_unix_date */
395         finfo->ctime = cli_make_unix_date(cli, p+22);
396         finfo->mtime = finfo->atime = finfo->ctime;
397         finfo->size = IVAL(p,26);
398         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
399         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
400                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
401                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
402         }
403
404         return(DIR_STRUCT_SIZE);
405 }
406
407
408 /****************************************************************************
409  Do a directory listing, calling fn on each file found.
410  this uses the old SMBsearch interface. It is needed for testing Samba,
411  but should otherwise not be used.
412 ****************************************************************************/
413
414 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
415                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
416 {
417         char *p;
418         int received = 0;
419         BOOL first = True;
420         char status[21];
421         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
422         int num_received = 0;
423         int i;
424         char *tdl, *dirlist = NULL;
425         pstring mask;
426         
427         ZERO_ARRAY(status);
428
429         pstrcpy(mask,Mask);
430   
431         while (1) {
432                 memset(cli->outbuf,'\0',smb_size);
433                 memset(cli->inbuf,'\0',smb_size);
434
435                 set_message(cli->outbuf,2,0,True);
436
437                 SCVAL(cli->outbuf,smb_com,SMBsearch);
438
439                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
440                 cli_setup_packet(cli);
441
442                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
443                 SSVAL(cli->outbuf,smb_vwv1,attribute);
444   
445                 p = smb_buf(cli->outbuf);
446                 *p++ = 4;
447       
448                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
449                 *p++ = 5;
450                 if (first) {
451                         SSVAL(p,0,0);
452                         p += 2;
453                 } else {
454                         SSVAL(p,0,21);
455                         p += 2;
456                         memcpy(p,status,21);
457                         p += 21;
458                 }
459
460                 cli_setup_bcc(cli, p);
461                 cli_send_smb(cli);
462                 if (!cli_receive_smb(cli)) break;
463
464                 received = SVAL(cli->inbuf,smb_vwv0);
465                 if (received <= 0) break;
466
467                 first = False;
468
469                 tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
470
471                 if (!tdl) {
472                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
473                         SAFE_FREE(dirlist);
474                         return 0;
475                 }
476                 else dirlist = tdl;
477
478                 p = smb_buf(cli->inbuf) + 3;
479
480                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
481                        p,received*DIR_STRUCT_SIZE);
482                 
483                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
484                 
485                 num_received += received;
486                 
487                 if (cli_is_error(cli)) break;
488         }
489
490         if (!first) {
491                 memset(cli->outbuf,'\0',smb_size);
492                 memset(cli->inbuf,'\0',smb_size);
493
494                 set_message(cli->outbuf,2,0,True);
495                 SCVAL(cli->outbuf,smb_com,SMBfclose);
496                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
497                 cli_setup_packet(cli);
498
499                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
500                 SSVAL(cli->outbuf, smb_vwv1, attribute);
501
502                 p = smb_buf(cli->outbuf);
503                 *p++ = 4;
504                 fstrcpy(p, "");
505                 p += strlen(p) + 1;
506                 *p++ = 5;
507                 SSVAL(p, 0, 21);
508                 p += 2;
509                 memcpy(p,status,21);
510                 p += 21;
511                 
512                 cli_setup_bcc(cli, p);
513                 cli_send_smb(cli);
514                 if (!cli_receive_smb(cli)) {
515                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
516                 }
517         }
518
519         for (p=dirlist,i=0;i<num_received;i++) {
520                 file_info finfo;
521                 p += interpret_short_filename(cli, p,&finfo);
522                 fn("\\", &finfo, Mask, state);
523         }
524
525         SAFE_FREE(dirlist);
526         return(num_received);
527 }
528
529 /****************************************************************************
530  Do a directory listing, calling fn on each file found.
531  This auto-switches between old and new style.
532 ****************************************************************************/
533
534 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
535              void (*fn)(const char *, file_info *, const char *, void *), void *state)
536 {
537         if (cli->protocol <= PROTOCOL_LANMAN1)
538                 return cli_list_old(cli, Mask, attribute, fn, state);
539         return cli_list_new(cli, Mask, attribute, fn, state);
540 }