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