Change size parameters from signed to unsigned to fix up warnings.
[kai/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 /****************************************************************************
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); p += 8;
105                         finfo->atime = interpret_long_date(p); p += 8;
106                         finfo->mtime = interpret_long_date(p); p += 8; p += 8;
107                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0); p += 8;
108                         p += 8; /* alloc size */
109                         finfo->mode = CVAL(p,0); p += 4;
110                         namelen = IVAL(p,0); p += 4;
111                         p += 4; /* EA size */
112                         slen = SVAL(p, 0);
113                         p += 2; 
114                         {
115                                 /* stupid NT bugs. grr */
116                                 int flags = 0;
117                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
118                                 clistr_pull(cli, finfo->short_name, p,
119                                             sizeof(finfo->short_name),
120                                             slen, flags);
121                         }
122                         p += 24; /* short name? */        
123                         clistr_pull(cli, finfo->name, p,
124                                     sizeof(finfo->name),
125                                     namelen, 0);
126                         return SVAL(base, 0);
127                 }
128         }
129         
130         DEBUG(1,("Unknown long filename format %d\n",level));
131         return(SVAL(p,0));
132 }
133
134 /****************************************************************************
135  Do a directory listing, calling fn on each file found.
136 ****************************************************************************/
137
138 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
139                  void (*fn)(file_info *, const char *, void *), void *state)
140 {
141         int max_matches = 512;
142         int info_level;
143         char *p, *p2;
144         pstring mask;
145         file_info finfo;
146         int i;
147         char *tdl, *dirlist = NULL;
148         int dirlist_len = 0;
149         int total_received = -1;
150         BOOL First = True;
151         int ff_searchcount=0;
152         int ff_eos=0;
153         int ff_lastname=0;
154         int ff_dir_handle=0;
155         int loop_count = 0;
156         char *rparam=NULL, *rdata=NULL;
157         unsigned int param_len, data_len;       
158         uint16 setup;
159         pstring param;
160
161         /* NT uses 260, OS/2 uses 2. Both accept 1. */
162         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
163
164         pstrcpy(mask,Mask);
165         
166         while (ff_eos == 0) {
167                 loop_count++;
168                 if (loop_count > 200) {
169                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
170                         break;
171                 }
172
173                 if (First) {
174                         setup = TRANSACT2_FINDFIRST;
175                         SSVAL(param,0,attribute); /* attribute */
176                         SSVAL(param,2,max_matches); /* max count */
177                         SSVAL(param,4,4+2);     /* resume required + close on end */
178                         SSVAL(param,6,info_level); 
179                         SIVAL(param,8,0);
180                         p = param+12;
181                         p += clistr_push(cli, param+12, mask, -1, 
182                                          STR_TERMINATE);
183                 } else {
184                         setup = TRANSACT2_FINDNEXT;
185                         SSVAL(param,0,ff_dir_handle);
186                         SSVAL(param,2,max_matches); /* max count */
187                         SSVAL(param,4,info_level); 
188                         SIVAL(param,6,0); /* ff_resume_key */
189                         SSVAL(param,10,8+4+2);  /* continue + resume required + close on end */
190                         p = param+12;
191                         p += clistr_push(cli, param+12, mask, -1, 
192                                          STR_TERMINATE);
193                 }
194
195                 param_len = PTR_DIFF(p, param);
196
197                 if (!cli_send_trans(cli, SMBtrans2, 
198                                     NULL,                   /* Name */
199                                     -1, 0,                  /* fid, flags */
200                                     &setup, 1, 0,           /* setup, length, max */
201                                     param, param_len, 10,   /* param, length, max */
202                                     NULL, 0, 
203                                     cli->max_xmit /* data, length, max */
204                                     )) {
205                         break;
206                 }
207
208                 if (!cli_receive_trans(cli, SMBtrans2, 
209                                        &rparam, &param_len,
210                                        &rdata, &data_len) &&
211                     cli_is_dos_error(cli)) {
212                         /* we need to work around a Win95 bug - sometimes
213                            it gives ERRSRV/ERRerror temprarily */
214                         uint8 eclass;
215                         uint32 ecode;
216                         cli_dos_error(cli, &eclass, &ecode);
217                         if (eclass != ERRSRV || ecode != ERRerror) break;
218                         msleep(100);
219                         continue;
220                 }
221
222                 if (cli_is_error(cli) || !rdata || !rparam) 
223                         break;
224
225                 if (total_received == -1) total_received = 0;
226
227                 /* parse out some important return info */
228                 p = rparam;
229                 if (First) {
230                         ff_dir_handle = SVAL(p,0);
231                         ff_searchcount = SVAL(p,2);
232                         ff_eos = SVAL(p,4);
233                         ff_lastname = SVAL(p,8);
234                 } else {
235                         ff_searchcount = SVAL(p,0);
236                         ff_eos = SVAL(p,2);
237                         ff_lastname = SVAL(p,6);
238                 }
239
240                 if (ff_searchcount == 0) 
241                         break;
242
243                 /* point to the data bytes */
244                 p = rdata;
245
246                 /* we might need the lastname for continuations */
247                 if (ff_lastname > 0) {
248                         switch(info_level)
249                                 {
250                                 case 260:
251                                         clistr_pull(cli, mask, p+ff_lastname,
252                                                     sizeof(mask), 
253                                                     data_len-ff_lastname,
254                                                     STR_TERMINATE);
255                                         break;
256                                 case 1:
257                                         clistr_pull(cli, mask, p+ff_lastname+1,
258                                                     sizeof(mask), 
259                                                     -1,
260                                                     STR_TERMINATE);
261                                         break;
262                                 }
263                 } else {
264                         pstrcpy(mask,"");
265                 }
266  
267                 /* and add them to the dirlist pool */
268                 tdl = Realloc(dirlist,dirlist_len + data_len);
269
270                 if (!tdl) {
271                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
272                         break;
273                 }
274                 else dirlist = tdl;
275
276                 /* put in a length for the last entry, to ensure we can chain entries 
277                    into the next packet */
278                 for (p2=p,i=0;i<(ff_searchcount-1);i++)
279                         p2 += interpret_long_filename(cli,info_level,p2,NULL);
280                 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
281
282                 /* grab the data for later use */
283                 memcpy(dirlist+dirlist_len,p,data_len);
284                 dirlist_len += data_len;
285
286                 total_received += ff_searchcount;
287
288                 SAFE_FREE(rdata);
289                 SAFE_FREE(rparam);
290
291                 DEBUG(3,("received %d entries (eos=%d)\n",
292                          ff_searchcount,ff_eos));
293
294                 if (ff_searchcount > 0) loop_count = 0;
295
296                 First = False;
297         }
298
299         for (p=dirlist,i=0;i<total_received;i++) {
300                 p += interpret_long_filename(cli,info_level,p,&finfo);
301                 fn(&finfo, Mask, state);
302         }
303
304         /* free up the dirlist buffer */
305         SAFE_FREE(dirlist);
306         return(total_received);
307 }
308
309 /****************************************************************************
310  Interpret a short filename structure.
311  The length of the structure is returned.
312 ****************************************************************************/
313
314 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
315 {
316         extern file_info def_finfo;
317
318         *finfo = def_finfo;
319
320         finfo->mode = CVAL(p,21);
321         
322         /* this date is converted to GMT by make_unix_date */
323         finfo->ctime = make_unix_date(p+22);
324         finfo->mtime = finfo->atime = finfo->ctime;
325         finfo->size = IVAL(p,26);
326         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
327         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
328                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
329                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
330         }
331
332         return(DIR_STRUCT_SIZE);
333 }
334
335
336 /****************************************************************************
337  Do a directory listing, calling fn on each file found.
338  this uses the old SMBsearch interface. It is needed for testing Samba,
339  but should otherwise not be used.
340 ****************************************************************************/
341
342 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
343                  void (*fn)(file_info *, const char *, void *), void *state)
344 {
345         char *p;
346         int received = 0;
347         BOOL first = True;
348         char status[21];
349         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
350         int num_received = 0;
351         int i;
352         char *tdl, *dirlist = NULL;
353         pstring mask;
354         
355         ZERO_ARRAY(status);
356
357         pstrcpy(mask,Mask);
358   
359         while (1) {
360                 memset(cli->outbuf,'\0',smb_size);
361                 memset(cli->inbuf,'\0',smb_size);
362
363                 set_message(cli->outbuf,2,0,True);
364
365                 SCVAL(cli->outbuf,smb_com,SMBsearch);
366
367                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
368                 cli_setup_packet(cli);
369
370                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
371                 SSVAL(cli->outbuf,smb_vwv1,attribute);
372   
373                 p = smb_buf(cli->outbuf);
374                 *p++ = 4;
375       
376                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
377                 *p++ = 5;
378                 if (first) {
379                         SSVAL(p,0,0);
380                         p += 2;
381                 } else {
382                         SSVAL(p,0,21);
383                         p += 2;
384                         memcpy(p,status,21);
385                         p += 21;
386                 }
387
388                 cli_setup_bcc(cli, p);
389                 cli_send_smb(cli);
390                 if (!cli_receive_smb(cli)) break;
391
392                 received = SVAL(cli->inbuf,smb_vwv0);
393                 if (received <= 0) break;
394
395                 first = False;
396
397                 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
398
399                 if (!tdl) {
400                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
401                         SAFE_FREE(dirlist);
402                         return 0;
403                 }
404                 else dirlist = tdl;
405
406                 p = smb_buf(cli->inbuf) + 3;
407
408                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
409                        p,received*DIR_STRUCT_SIZE);
410                 
411                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
412                 
413                 num_received += received;
414                 
415                 if (cli_is_error(cli)) break;
416         }
417
418         if (!first) {
419                 memset(cli->outbuf,'\0',smb_size);
420                 memset(cli->inbuf,'\0',smb_size);
421
422                 set_message(cli->outbuf,2,0,True);
423                 SCVAL(cli->outbuf,smb_com,SMBfclose);
424                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
425                 cli_setup_packet(cli);
426
427                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
428                 SSVAL(cli->outbuf, smb_vwv1, attribute);
429
430                 p = smb_buf(cli->outbuf);
431                 *p++ = 4;
432                 fstrcpy(p, "");
433                 p += strlen(p) + 1;
434                 *p++ = 5;
435                 SSVAL(p, 0, 21);
436                 p += 2;
437                 memcpy(p,status,21);
438                 p += 21;
439                 
440                 cli_setup_bcc(cli, p);
441                 cli_send_smb(cli);
442                 if (!cli_receive_smb(cli)) {
443                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
444                 }
445         }
446
447         for (p=dirlist,i=0;i<num_received;i++) {
448                 file_info finfo;
449                 p += interpret_short_filename(cli, p,&finfo);
450                 fn(&finfo, Mask, state);
451         }
452
453         SAFE_FREE(dirlist);
454         return(num_received);
455 }
456
457 /****************************************************************************
458  Do a directory listing, calling fn on each file found.
459  This auto-switches between old and new style.
460 ****************************************************************************/
461
462 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
463              void (*fn)(file_info *, const char *, void *), void *state)
464 {
465         if (cli->protocol <= PROTOCOL_LANMAN1)
466                 return cli_list_old(cli, Mask, attribute, fn, state);
467         return cli_list_new(cli, Mask, attribute, fn, state);
468 }