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