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