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