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