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