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