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