r6225: get rid of warnings from my compiler about nested externs
[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 extern file_info def_finfo;
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
34 static size_t interpret_long_filename(struct cli_state *cli,
35                                    int level,char *p,file_info *finfo)
36 {
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                 case 1: /* OS/2 understands this */
47                         /* these dates are converted to GMT by
48                            make_unix_date */
49                         finfo->ctime = make_unix_date2(p+4);
50                         finfo->atime = make_unix_date2(p+8);
51                         finfo->mtime = make_unix_date2(p+12);
52                         finfo->size = IVAL(p,16);
53                         finfo->mode = CVAL(p,24);
54                         len = CVAL(p, 26);
55                         p += 27;
56                         p += clistr_align_in(cli, p, 0);
57                         /* the len+2 below looks strange but it is
58                            important to cope with the differences
59                            between win2000 and win9x for this call
60                            (tridge) */
61                         p += clistr_pull(cli, finfo->name, p,
62                                          sizeof(finfo->name),
63                                          len+2, 
64                                          STR_TERMINATE);
65                         return PTR_DIFF(p, base);
66
67                 case 2: /* this is what OS/2 uses mostly */
68                         /* these dates are converted to GMT by
69                            make_unix_date */
70                         finfo->ctime = make_unix_date2(p+4);
71                         finfo->atime = make_unix_date2(p+8);
72                         finfo->mtime = make_unix_date2(p+12);
73                         finfo->size = IVAL(p,16);
74                         finfo->mode = CVAL(p,24);
75                         len = CVAL(p, 30);
76                         p += 31;
77                         /* check for unisys! */
78                         p += clistr_pull(cli, finfo->name, p,
79                                          sizeof(finfo->name),
80                                          len, 
81                                          STR_NOALIGN);
82                         return PTR_DIFF(p, base) + 1;
83                         
84                 case 260: /* NT uses this, but also accepts 2 */
85                 {
86                         size_t namelen, slen;
87                         p += 4; /* next entry offset */
88                         p += 4; /* fileindex */
89                                 
90                         /* these dates appear to arrive in a
91                            weird way. It seems to be localtime
92                            plus the serverzone given in the
93                            initial connect. This is GMT when
94                            DST is not in effect and one hour
95                            from GMT otherwise. Can this really
96                            be right??
97                            
98                            I suppose this could be called
99                            kludge-GMT. Is is the GMT you get
100                            by using the current DST setting on
101                            a different localtime. It will be
102                            cheap to calculate, I suppose, as
103                            no DST tables will be needed */
104                         
105                         finfo->ctime = interpret_long_date(p);
106                         p += 8;
107                         finfo->atime = interpret_long_date(p);
108                         p += 8;
109                         finfo->mtime = interpret_long_date(p);
110                         p += 8;
111                         p += 8;
112                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
113                         p += 8;
114                         p += 8; /* alloc size */
115                         finfo->mode = CVAL(p,0);
116                         p += 4;
117                         namelen = IVAL(p,0);
118                         p += 4;
119                         p += 4; /* EA size */
120                         slen = SVAL(p, 0);
121                         p += 2; 
122                         {
123                                 /* stupid NT bugs. grr */
124                                 int flags = 0;
125                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
126                                 clistr_pull(cli, finfo->short_name, p,
127                                             sizeof(finfo->short_name),
128                                             slen, flags);
129                         }
130                         p += 24; /* short name? */        
131                         clistr_pull(cli, finfo->name, p,
132                                     sizeof(finfo->name),
133                                     namelen, 0);
134                         return (size_t)IVAL(base, 0);
135                 }
136         }
137         
138         DEBUG(1,("Unknown long filename format %d\n",level));
139         return (size_t)IVAL(base,0);
140 }
141
142 /****************************************************************************
143  Do a directory listing, calling fn on each file found.
144 ****************************************************************************/
145
146 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
147                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
148 {
149 #if 0
150         int max_matches = 1366; /* Match W2k - was 512. */
151 #else
152         int max_matches = 512;
153 #endif
154         int info_level;
155         char *p, *p2;
156         pstring mask;
157         file_info finfo;
158         int i;
159         char *tdl, *dirlist = NULL;
160         int dirlist_len = 0;
161         int total_received = -1;
162         BOOL First = True;
163         int ff_searchcount=0;
164         int ff_eos=0;
165         int ff_lastname=0;
166         int ff_dir_handle=0;
167         int loop_count = 0;
168         char *rparam=NULL, *rdata=NULL;
169         unsigned int param_len, data_len;       
170         uint16 setup;
171         pstring param;
172         const char *mnt;
173
174         /* NT uses 260, OS/2 uses 2. Both accept 1. */
175         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
176         
177         /* when getting a directory listing from a 2k dfs root share, 
178            we have to include the full path (\server\share\mask) here */
179            
180         if ( cli->dfsroot )
181                 pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
182         else
183                 pstrcpy(mask,Mask);
184         
185         while (ff_eos == 0) {
186                 loop_count++;
187                 if (loop_count > 200) {
188                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
189                         break;
190                 }
191
192                 if (First) {
193                         setup = TRANSACT2_FINDFIRST;
194                         SSVAL(param,0,attribute); /* attribute */
195                         SSVAL(param,2,max_matches); /* max count */
196                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
197                         SSVAL(param,6,info_level); 
198                         SIVAL(param,8,0);
199                         p = param+12;
200                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
201                                          STR_TERMINATE);
202                 } else {
203                         setup = TRANSACT2_FINDNEXT;
204                         SSVAL(param,0,ff_dir_handle);
205                         SSVAL(param,2,max_matches); /* max count */
206                         SSVAL(param,4,info_level); 
207                         SIVAL(param,6,0); /* ff_resume_key */
208                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
209                            can miss filenames. Use last filename continue instead. JRA */
210                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
211                         p = param+12;
212                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
213                                          STR_TERMINATE);
214                 }
215
216                 param_len = PTR_DIFF(p, param);
217
218                 if (!cli_send_trans(cli, SMBtrans2, 
219                                     NULL,                   /* Name */
220                                     -1, 0,                  /* fid, flags */
221                                     &setup, 1, 0,           /* setup, length, max */
222                                     param, param_len, 10,   /* param, length, max */
223                                     NULL, 0, 
224 #if 0
225                                     /* w2k value. */
226                                     MIN(16384,cli->max_xmit) /* data, length, max. */
227 #else
228                                     cli->max_xmit           /* data, length, max. */
229 #endif
230                                     )) {
231                         break;
232                 }
233
234                 if (!cli_receive_trans(cli, SMBtrans2, 
235                                        &rparam, &param_len,
236                                        &rdata, &data_len) &&
237                     cli_is_dos_error(cli)) {
238                         /* we need to work around a Win95 bug - sometimes
239                            it gives ERRSRV/ERRerror temprarily */
240                         uint8 eclass;
241                         uint32 ecode;
242                         cli_dos_error(cli, &eclass, &ecode);
243                         if (eclass != ERRSRV || ecode != ERRerror)
244                                 break;
245                         smb_msleep(100);
246                         continue;
247                 }
248
249                 if (cli_is_error(cli) || !rdata || !rparam) 
250                         break;
251
252                 if (total_received == -1)
253                         total_received = 0;
254
255                 /* parse out some important return info */
256                 p = rparam;
257                 if (First) {
258                         ff_dir_handle = SVAL(p,0);
259                         ff_searchcount = SVAL(p,2);
260                         ff_eos = SVAL(p,4);
261                         ff_lastname = SVAL(p,8);
262                 } else {
263                         ff_searchcount = SVAL(p,0);
264                         ff_eos = SVAL(p,2);
265                         ff_lastname = SVAL(p,6);
266                 }
267
268                 if (ff_searchcount == 0) 
269                         break;
270
271                 /* point to the data bytes */
272                 p = rdata;
273
274                 /* we might need the lastname for continuations */
275                 for (p2=p,i=0;i<ff_searchcount;i++) {
276                         if ((info_level == 260) && (i == ff_searchcount-1)) {
277                                 /* Last entry - fixup the last offset length. */
278                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
279                         }
280                         p2 += interpret_long_filename(cli,info_level,p2,&finfo);
281                 }
282
283                 if (ff_lastname > 0) {
284                         pstrcpy(mask, finfo.name);
285                 } else {
286                         pstrcpy(mask,"");
287                 }
288
289                 /* grab the data for later use */
290                 /* and add them to the dirlist pool */
291                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
292
293                 if (!tdl) {
294                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
295                         break;
296                 } else {
297                         dirlist = tdl;
298                 }
299
300                 memcpy(dirlist+dirlist_len,p,data_len);
301                 dirlist_len += data_len;
302
303                 total_received += ff_searchcount;
304
305                 SAFE_FREE(rdata);
306                 SAFE_FREE(rparam);
307
308                 DEBUG(3,("received %d entries (eos=%d)\n",
309                          ff_searchcount,ff_eos));
310
311                 if (ff_searchcount > 0)
312                         loop_count = 0;
313
314                 First = False;
315         }
316
317         mnt = cli_cm_get_mntpoint( cli );
318
319         for (p=dirlist,i=0;i<total_received;i++) {
320                 p += interpret_long_filename(cli,info_level,p,&finfo);
321                 fn( mnt,&finfo, Mask, state );
322         }
323
324         /* free up the dirlist buffer */
325         SAFE_FREE(dirlist);
326         return(total_received);
327 }
328
329 /****************************************************************************
330  Interpret a short filename structure.
331  The length of the structure is returned.
332 ****************************************************************************/
333
334 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
335 {
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)(const char *, 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 = SMB_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)(const char *, 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 }