improved session reestablishment
[samba.git] / source3 / libsmb / clientgen.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    SMB client generic functions
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 #include "trans2.h"
26
27
28 extern int DEBUGLEVEL;
29
30
31 /****************************************************************************
32   send an smb to a fd and re-establish if necessary
33 ****************************************************************************/
34 static BOOL cli_send_smb(struct cli_state *cli)
35 {
36         size_t len;
37         size_t nwritten=0;
38         ssize_t ret;
39         BOOL reestablished=False;
40
41         len = smb_len(cli->outbuf) + 4;
42
43         while (nwritten < len) {
44                 ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
45                 if (ret <= 0 && errno == EPIPE && !reestablished) {
46                         if (cli_reestablish_connection(cli)) {
47                                 reestablished = True;
48                                 nwritten=0;
49                                 continue;
50                         }
51                 }
52                 if (ret <= 0) {
53                         DEBUG(0,("Error writing %d bytes to client. %d. Exiting\n",
54                                  len,ret));
55                         close_sockets();
56                         exit(1);
57                 }
58                 nwritten += ret;
59         }
60         
61         return True;
62 }
63
64 /*****************************************************
65  RAP error codes - a small start but will be extended.
66 *******************************************************/
67
68 struct
69 {
70   int err;
71   char *message;
72 } rap_errmap[] =
73 {
74   {5,    "User has insufficient privilege" },
75   {86,   "The specified password is invalid" },
76   {2226, "Operation only permitted on a Primary Domain Controller"  },
77   {2242, "The password of this user has expired." },
78   {2243, "The password of this user cannot change." },
79   {2244, "This password cannot be used now (password history conflict)." },
80   {2245, "The password is shorter than required." },
81   {2246, "The password of this user is too recent to change."},
82   {0, NULL}
83 };  
84
85 /****************************************************************************
86   return a description of an SMB error
87 ****************************************************************************/
88 static char *cli_smb_errstr(struct cli_state *cli)
89 {
90         return smb_errstr(cli->inbuf);
91 }
92
93 /******************************************************
94  Return an error message - either an SMB error or a RAP
95  error.
96 *******************************************************/
97     
98 char *cli_errstr(struct cli_state *cli)
99 {   
100         static fstring error_message;
101         uint8 errclass;
102         uint32 errnum;
103         int i;      
104
105         /*  
106          * Errors are of three kinds - smb errors,
107          * dealt with by cli_smb_errstr, NT errors,
108          * whose code is in cli.nt_error, and rap
109          * errors, whose error code is in cli.rap_error.
110          */ 
111
112         cli_error(cli, &errclass, &errnum);
113
114         if (errclass != 0)
115         {
116                 return cli_smb_errstr(cli);
117         }
118
119         /*
120          * Was it an NT error ?
121          */
122
123         if (cli->nt_error)
124         {
125                 char *nt_msg = get_nt_error_msg(cli->nt_error);
126
127                 if (nt_msg == NULL)
128                 {
129                         slprintf(error_message, sizeof(fstring) - 1, "NT code %d", cli->nt_error);
130                 }
131                 else
132                 {
133                         fstrcpy(error_message, nt_msg);
134                 }
135
136                 return error_message;
137         }
138
139         /*
140          * Must have been a rap error.
141          */
142
143         slprintf(error_message, sizeof(error_message) - 1, "code %d", cli->rap_error);
144
145         for (i = 0; rap_errmap[i].message != NULL; i++)
146         {
147                 if (rap_errmap[i].err == cli->rap_error)
148                 {
149                         fstrcpy( error_message, rap_errmap[i].message);
150                         break;
151                 }
152         } 
153
154         return error_message;
155 }
156
157 /****************************************************************************
158 setup basics in a outgoing packet
159 ****************************************************************************/
160 static void cli_setup_packet(struct cli_state *cli)
161 {
162         cli->rap_error = 0;
163         cli->nt_error = 0;
164         SSVAL(cli->outbuf,smb_pid,cli->pid);
165         SSVAL(cli->outbuf,smb_uid,cli->vuid);
166         SSVAL(cli->outbuf,smb_mid,cli->mid);
167         if (cli->protocol > PROTOCOL_CORE) {
168                 SCVAL(cli->outbuf,smb_flg,0x8);
169                 SSVAL(cli->outbuf,smb_flg2,0x1);
170         }
171 }
172
173
174 /*****************************************************************************
175  Convert a character pointer in a cli_call_api() response to a form we can use.
176  This function contains code to prevent core dumps if the server returns 
177  invalid data.
178 *****************************************************************************/
179 static char *fix_char_ptr(unsigned int datap, unsigned int converter, 
180                           char *rdata, int rdrcnt)
181 {
182         if (datap == 0) {       /* turn NULL pointers into zero length strings */
183                 return "";
184         } else {
185                 unsigned int offset = datap - converter;
186
187                 if (offset >= rdrcnt) {
188                         DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>",
189                                  datap, converter, rdrcnt));
190                         return "<ERROR>";
191                 } else {
192                         return &rdata[offset];
193                 }
194         }
195 }
196
197 /****************************************************************************
198   send a SMB trans or trans2 request
199   ****************************************************************************/
200 static BOOL cli_send_trans(struct cli_state *cli, int trans, 
201                            char *name, int pipe_name_len, 
202                            int fid, int flags,
203                            uint16 *setup, int lsetup, int msetup,
204                            char *param, int lparam, int mparam,
205                            char *data, int ldata, int mdata)
206 {
207         int i;
208         int this_ldata,this_lparam;
209         int tot_data=0,tot_param=0;
210         char *outdata,*outparam;
211         char *p;
212
213         this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
214         this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
215
216         bzero(cli->outbuf,smb_size);
217         set_message(cli->outbuf,14+lsetup,0,True);
218         CVAL(cli->outbuf,smb_com) = trans;
219         SSVAL(cli->outbuf,smb_tid, cli->cnum);
220         cli_setup_packet(cli);
221
222         outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len+1 : 3);
223         outdata = outparam+this_lparam;
224
225         /* primary request */
226         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
227         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
228         SSVAL(cli->outbuf,smb_mprcnt,mparam);   /* mprcnt */
229         SSVAL(cli->outbuf,smb_mdrcnt,mdata);    /* mdrcnt */
230         SCVAL(cli->outbuf,smb_msrcnt,msetup);   /* msrcnt */
231         SSVAL(cli->outbuf,smb_flags,flags);     /* flags */
232         SIVAL(cli->outbuf,smb_timeout,0);               /* timeout */
233         SSVAL(cli->outbuf,smb_pscnt,this_lparam);       /* pscnt */
234         SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */
235         SSVAL(cli->outbuf,smb_dscnt,this_ldata);        /* dscnt */
236         SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
237         SCVAL(cli->outbuf,smb_suwcnt,lsetup);   /* suwcnt */
238         for (i=0;i<lsetup;i++)          /* setup[] */
239                 SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
240         p = smb_buf(cli->outbuf);
241         if (trans==SMBtrans) {
242                 memcpy(p,name, pipe_name_len + 1);  /* name[] */
243         } else {
244                 *p++ = 0;  /* put in a null smb_name */
245                 *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
246         }
247         if (this_lparam)                        /* param[] */
248                 memcpy(outparam,param,this_lparam);
249         if (this_ldata)                 /* data[] */
250                 memcpy(outdata,data,this_ldata);
251         set_message(cli->outbuf,14+lsetup,              /* wcnt, bcc */
252                     PTR_DIFF(outdata+this_ldata,smb_buf(cli->outbuf)),False);
253
254         show_msg(cli->outbuf);
255         cli_send_smb(cli);
256
257         if (this_ldata < ldata || this_lparam < lparam) {
258                 /* receive interim response */
259                 if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout) || 
260                     CVAL(cli->inbuf,smb_rcls) != 0) {
261                         return(False);
262                 }      
263
264                 tot_data = this_ldata;
265                 tot_param = this_lparam;
266                 
267                 while (tot_data < ldata || tot_param < lparam)  {
268                         this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
269                         this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
270
271                         set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
272                         CVAL(cli->outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2;
273                         
274                         outparam = smb_buf(cli->outbuf);
275                         outdata = outparam+this_lparam;
276                         
277                         /* secondary request */
278                         SSVAL(cli->outbuf,smb_tpscnt,lparam);   /* tpscnt */
279                         SSVAL(cli->outbuf,smb_tdscnt,ldata);    /* tdscnt */
280                         SSVAL(cli->outbuf,smb_spscnt,this_lparam);      /* pscnt */
281                         SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */
282                         SSVAL(cli->outbuf,smb_spsdisp,tot_param);       /* psdisp */
283                         SSVAL(cli->outbuf,smb_sdscnt,this_ldata);       /* dscnt */
284                         SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
285                         SSVAL(cli->outbuf,smb_sdsdisp,tot_data);        /* dsdisp */
286                         if (trans==SMBtrans2)
287                                 SSVALS(cli->outbuf,smb_sfid,fid);               /* fid */
288                         if (this_lparam)                        /* param[] */
289                                 memcpy(outparam,param,this_lparam);
290                         if (this_ldata)                 /* data[] */
291                                 memcpy(outdata,data,this_ldata);
292                         set_message(cli->outbuf,trans==SMBtrans?8:9, /* wcnt, bcc */
293                                     PTR_DIFF(outdata+this_ldata,smb_buf(cli->outbuf)),False);
294                         
295                         show_msg(cli->outbuf);
296                         cli_send_smb(cli);
297                         
298                         tot_data += this_ldata;
299                         tot_param += this_lparam;
300                 }
301         }
302
303         return(True);
304 }
305
306
307 /****************************************************************************
308   receive a SMB trans or trans2 response allocating the necessary memory
309   ****************************************************************************/
310 static BOOL cli_receive_trans(struct cli_state *cli,int trans,
311                               char **param, int *param_len,
312                               char **data, int *data_len)
313 {
314         int total_data=0;
315         int total_param=0;
316         int this_data,this_param;
317         
318         *data_len = *param_len = 0;
319
320         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
321                 return False;
322
323         show_msg(cli->inbuf);
324         
325         /* sanity check */
326         if (CVAL(cli->inbuf,smb_com) != trans) {
327                 DEBUG(0,("Expected %s response, got command 0x%02x\n",
328                          trans==SMBtrans?"SMBtrans":"SMBtrans2", 
329                          CVAL(cli->inbuf,smb_com)));
330                 return(False);
331         }
332
333         if (cli_error(cli, NULL, NULL))
334         {
335                 return(False);
336         }
337
338         /* parse out the lengths */
339         total_data = SVAL(cli->inbuf,smb_tdrcnt);
340         total_param = SVAL(cli->inbuf,smb_tprcnt);
341
342         /* allocate it */
343         *data = Realloc(*data,total_data);
344         *param = Realloc(*param,total_param);
345
346         while (1)  {
347                 this_data = SVAL(cli->inbuf,smb_drcnt);
348                 this_param = SVAL(cli->inbuf,smb_prcnt);
349
350                 if (this_data + *data_len > total_data ||
351                     this_param + *param_len > total_param) {
352                         DEBUG(1,("Data overflow in cli_receive_trans\n"));
353                         return False;
354                 }
355
356                 if (this_data)
357                         memcpy(*data + SVAL(cli->inbuf,smb_drdisp),
358                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff),
359                                this_data);
360                 if (this_param)
361                         memcpy(*param + SVAL(cli->inbuf,smb_prdisp),
362                                smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff),
363                                this_param);
364                 *data_len += this_data;
365                 *param_len += this_param;
366
367                 /* parse out the total lengths again - they can shrink! */
368                 total_data = SVAL(cli->inbuf,smb_tdrcnt);
369                 total_param = SVAL(cli->inbuf,smb_tprcnt);
370                 
371                 if (total_data <= *data_len && total_param <= *param_len)
372                         break;
373                 
374                 if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
375                         return False;
376
377                 show_msg(cli->inbuf);
378                 
379                 /* sanity check */
380                 if (CVAL(cli->inbuf,smb_com) != trans) {
381                         DEBUG(0,("Expected %s response, got command 0x%02x\n",
382                                  trans==SMBtrans?"SMBtrans":"SMBtrans2", 
383                                  CVAL(cli->inbuf,smb_com)));
384                         return(False);
385                 }
386                 if (cli_error(cli, NULL, NULL))
387                 {
388                         return(False);
389                 }
390         }
391         
392         return(True);
393 }
394
395 /****************************************************************************
396 Call a remote api on an arbitrary pipe.  takes param, data and setup buffers.
397 ****************************************************************************/
398 BOOL cli_api_pipe(struct cli_state *cli, char *pipe_name, int pipe_name_len,
399                   uint16 *setup, uint32 setup_count, uint32 max_setup_count,
400                   char *params, uint32 param_count, uint32 max_param_count,
401                   char *data, uint32 data_count, uint32 max_data_count,
402                   char **rparam, uint32 *rparam_count,
403                   char **rdata, uint32 *rdata_count)
404 {
405   if (pipe_name_len == 0)
406     pipe_name_len = strlen(pipe_name);
407
408   cli_send_trans(cli, SMBtrans, 
409                  pipe_name, pipe_name_len,
410                  0,0,                         /* fid, flags */
411                  setup, setup_count, max_setup_count,
412                  params, param_count, max_param_count,
413                  data, data_count, max_data_count);
414
415   return (cli_receive_trans(cli, SMBtrans, 
416                             rparam, (int *)rparam_count,
417                             rdata, (int *)rdata_count));
418 }
419
420 /****************************************************************************
421 call a remote api
422 ****************************************************************************/
423 BOOL cli_api(struct cli_state *cli,
424              char *param, int prcnt, int mprcnt,
425              char *data, int drcnt, int mdrcnt,
426              char **rparam, int *rprcnt,
427              char **rdata, int *rdrcnt)
428 {
429   cli_send_trans(cli,SMBtrans,
430                  PIPE_LANMAN,strlen(PIPE_LANMAN), /* Name, length */
431                  0,0,                             /* fid, flags */
432                  NULL,0,0,                /* Setup, length, max */
433                  param, prcnt, mprcnt,    /* Params, length, max */
434                  data, drcnt, mdrcnt      /* Data, length, max */ 
435                 );
436
437   return (cli_receive_trans(cli,SMBtrans,
438                             rparam, rprcnt,
439                             rdata, rdrcnt));
440 }
441
442
443 /****************************************************************************
444 perform a NetWkstaUserLogon
445 ****************************************************************************/
446 BOOL cli_NetWkstaUserLogon(struct cli_state *cli,char *user, char *workstation)
447 {
448         char *rparam = NULL;
449         char *rdata = NULL;
450         char *p;
451         int rdrcnt,rprcnt;
452         pstring param;
453
454         memset(param, 0, sizeof(param));
455         
456         /* send a SMBtrans command with api NetWkstaUserLogon */
457         p = param;
458         SSVAL(p,0,132); /* api number */
459         p += 2;
460         pstrcpy(p,"OOWb54WrLh");
461         p = skip_string(p,1);
462         pstrcpy(p,"WB21BWDWWDDDDDDDzzzD");
463         p = skip_string(p,1);
464         SSVAL(p,0,1);
465         p += 2;
466         pstrcpy(p,user);
467         strupper(p);
468         p += 21;
469         p++;
470         p += 15;
471         p++; 
472         pstrcpy(p, workstation); 
473         strupper(p);
474         p += 16;
475         SSVAL(p, 0, CLI_BUFFER_SIZE);
476         p += 2;
477         SSVAL(p, 0, CLI_BUFFER_SIZE);
478         p += 2;
479         
480         if (cli_api(cli, 
481                     param, PTR_DIFF(p,param),1024,  /* param, length, max */
482                     NULL, 0, CLI_BUFFER_SIZE,           /* data, length, max */
483                     &rparam, &rprcnt,               /* return params, return size */
484                     &rdata, &rdrcnt                 /* return data, return size */
485                    )) {
486                 cli->rap_error = SVAL(rparam,0);
487                 p = rdata;
488                 
489                 if (cli->rap_error == 0) {
490                         DEBUG(4,("NetWkstaUserLogon success\n"));
491                         cli->privilages = SVAL(p, 24);
492                         fstrcpy(cli->eff_name,p+2);
493                 } else {
494                         DEBUG(1,("NetwkstaUserLogon gave error %d\n", cli->rap_error));
495                 }
496         }
497         
498         if (rparam)
499       free(rparam);
500         if (rdata)
501       free(rdata);
502         return (cli->rap_error == 0);
503 }
504
505 /****************************************************************************
506 call a NetShareEnum - try and browse available connections on a host
507 ****************************************************************************/
508 BOOL cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, const char *))
509 {
510   char *rparam = NULL;
511   char *rdata = NULL;
512   char *p;
513   int rdrcnt,rprcnt;
514   pstring param;
515   int count = -1;
516
517   /* now send a SMBtrans command with api RNetShareEnum */
518   p = param;
519   SSVAL(p,0,0); /* api number */
520   p += 2;
521   pstrcpy(p,"WrLeh");
522   p = skip_string(p,1);
523   pstrcpy(p,"B13BWz");
524   p = skip_string(p,1);
525   SSVAL(p,0,1);
526   SSVAL(p,2,0xFFFF);
527   p += 4;
528
529   if (cli_api(cli, 
530               param, PTR_DIFF(p,param), 1024,  /* Param, length, maxlen */
531               NULL, 0, 0xFFFF,            /* data, length, maxlen */
532               &rparam, &rprcnt,                /* return params, length */
533               &rdata, &rdrcnt))                /* return data, length */
534     {
535       int res = SVAL(rparam,0);
536       int converter=SVAL(rparam,2);
537       int i;
538       
539       if (res == 0 || res == ERRmoredata) {
540               count=SVAL(rparam,4);
541               p = rdata;
542
543               for (i=0;i<count;i++,p+=20) {
544                       char *sname = p;
545                       int type = SVAL(p,14);
546                       int comment_offset = IVAL(p,16) & 0xFFFF;
547                       char *cmnt = comment_offset?(rdata+comment_offset-converter):"";
548                       fn(sname, type, cmnt);
549               }
550       } else {
551               DEBUG(4,("NetShareEnum res=%d\n", res));
552       }      
553     } else {
554               DEBUG(4,("NetShareEnum failed\n"));
555     }
556   
557   if (rparam)
558     free(rparam);
559   if (rdata)
560     free(rdata);
561
562   return count;
563 }
564
565
566 /****************************************************************************
567 call a NetServerEnum for the specified workgroup and servertype mask.
568 This function then calls the specified callback function for each name returned.
569
570 The callback function takes 3 arguments: the machine name, the server type and
571 the comment.
572 ****************************************************************************/
573 BOOL cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
574                        void (*fn)(const char *, uint32, const char *))
575 {
576         char *rparam = NULL;
577         char *rdata = NULL;
578         int rdrcnt,rprcnt;
579         char *p;
580         pstring param;
581         int uLevel = 1;
582         int count = -1;
583   
584         /* send a SMBtrans command with api NetServerEnum */
585         p = param;
586         SSVAL(p,0,0x68); /* api number */
587         p += 2;
588         pstrcpy(p,"WrLehDz");
589         p = skip_string(p,1);
590   
591         pstrcpy(p,"B16BBDz");
592   
593         p = skip_string(p,1);
594         SSVAL(p,0,uLevel);
595         SSVAL(p,2,CLI_BUFFER_SIZE);
596         p += 4;
597         SIVAL(p,0,stype);
598         p += 4;
599         
600         pstrcpy(p, workgroup);
601         p = skip_string(p,1);
602         
603         if (cli_api(cli, 
604                     param, PTR_DIFF(p,param), 8,        /* params, length, max */
605                     NULL, 0, CLI_BUFFER_SIZE,               /* data, length, max */
606                     &rparam, &rprcnt,                   /* return params, return size */
607                     &rdata, &rdrcnt                     /* return data, return size */
608                    )) {
609                 int res = SVAL(rparam,0);
610                 int converter=SVAL(rparam,2);
611                 int i;
612                         
613                 if (res == 0 || res == ERRmoredata) {
614                         count=SVAL(rparam,4);
615                         p = rdata;
616                                         
617                         for (i = 0;i < count;i++, p += 26) {
618                                 char *sname = p;
619                                 int comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
620                                 char *cmnt = comment_offset?(rdata+comment_offset):"";
621                                 if (comment_offset < 0 || comment_offset > rdrcnt) continue;
622
623                                 stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
624
625                                 fn(sname, stype, cmnt);
626                         }
627                 }
628         }
629   
630         if (rparam)
631       free(rparam);
632         if (rdata)
633       free(rdata);
634         
635         return(count > 0);
636 }
637
638
639
640
641 static  struct {
642     int prot;
643     char *name;
644   }
645 prots[] = 
646     {
647       {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
648       {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
649       {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
650       {PROTOCOL_LANMAN1,"LANMAN1.0"},
651       {PROTOCOL_LANMAN2,"LM1.2X002"},
652       {PROTOCOL_LANMAN2,"Samba"},
653       {PROTOCOL_NT1,"NT LANMAN 1.0"},
654       {PROTOCOL_NT1,"NT LM 0.12"},
655       {-1,NULL}
656     };
657
658
659 /****************************************************************************
660 send a session setup 
661 ****************************************************************************/
662 BOOL cli_session_setup(struct cli_state *cli, 
663                        char *user, 
664                        char *pass, int passlen,
665                        char *ntpass, int ntpasslen,
666                        char *workgroup)
667 {
668         char *p;
669         fstring pword, ntpword;
670
671         if (cli->protocol < PROTOCOL_LANMAN1)
672                 return True;
673
674         if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
675                 return False;
676         }
677
678         if (((passlen == 0) || (passlen == 1)) && (pass[0] == '\0')) {
679           /* Null session connect. */
680           pword[0] = '\0';
681           ntpword[0] = '\0';
682         } else {
683           if ((cli->sec_mode & 2) && passlen != 24) {
684             passlen = 24;
685             ntpasslen = 24;
686             SMBencrypt((uchar *)pass,(uchar *)cli->cryptkey,(uchar *)pword);
687             SMBNTencrypt((uchar *)ntpass,(uchar *)cli->cryptkey,(uchar *)ntpword);
688           } else {
689                   fstrcpy(pword, pass);
690                   fstrcpy(ntpword, "");
691                   ntpasslen = 0;
692           }
693         }
694
695         /* if in share level security then don't send a password now */
696         if (!(cli->sec_mode & 1)) {
697                 fstrcpy(pword, "");
698                 passlen=1;
699                 fstrcpy(ntpword, "");
700                 ntpasslen=1;
701         } 
702
703         /* send a session setup command */
704         bzero(cli->outbuf,smb_size);
705
706         if (cli->protocol < PROTOCOL_NT1)
707         {
708                 set_message(cli->outbuf,10,1 + strlen(user) + passlen,True);
709                 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
710                 cli_setup_packet(cli);
711
712                 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
713                 SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
714                 SSVAL(cli->outbuf,smb_vwv3,2);
715                 SSVAL(cli->outbuf,smb_vwv4,1);
716                 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
717                 SSVAL(cli->outbuf,smb_vwv7,passlen);
718                 p = smb_buf(cli->outbuf);
719                 memcpy(p,pword,passlen);
720                 p += passlen;
721                 pstrcpy(p,user);
722                 strupper(p);
723         }
724         else
725         {
726                 set_message(cli->outbuf,13,0,True);
727                 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
728                 cli_setup_packet(cli);
729                 
730                 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
731                 SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
732                 SSVAL(cli->outbuf,smb_vwv3,2);
733                 SSVAL(cli->outbuf,smb_vwv4,cli->pid);
734                 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
735                 SSVAL(cli->outbuf,smb_vwv7,passlen);
736                 SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
737                 SSVAL(cli->outbuf,smb_vwv11,0);
738                 p = smb_buf(cli->outbuf);
739                 memcpy(p,pword,passlen); 
740                 p += SVAL(cli->outbuf,smb_vwv7);
741                 memcpy(p,ntpword,ntpasslen); 
742                 p += SVAL(cli->outbuf,smb_vwv8);
743                 pstrcpy(p,user);
744                 strupper(p);
745                 p = skip_string(p,1);
746                 pstrcpy(p,workgroup);
747                 strupper(p);
748                 p = skip_string(p,1);
749                 pstrcpy(p,"Unix");p = skip_string(p,1);
750                 pstrcpy(p,"Samba");p = skip_string(p,1);
751                 set_message(cli->outbuf,13,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
752         }
753
754       cli_send_smb(cli);
755       if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
756               return False;
757
758       show_msg(cli->inbuf);
759
760       if (CVAL(cli->inbuf,smb_rcls) != 0) {
761               return False;
762       }
763
764       /* use the returned vuid from now on */
765       cli->vuid = SVAL(cli->inbuf,smb_uid);
766
767       fstrcpy(cli->user_name, user);
768
769       return True;
770 }
771
772 /****************************************************************************
773  Send a uloggoff.
774 *****************************************************************************/
775
776 BOOL cli_ulogoff(struct cli_state *cli)
777 {
778         bzero(cli->outbuf,smb_size);
779         set_message(cli->outbuf,2,0,True);
780         CVAL(cli->outbuf,smb_com) = SMBulogoffX;
781         cli_setup_packet(cli);
782         SSVAL(cli->outbuf,smb_vwv0,0xFF);
783         SSVAL(cli->outbuf,smb_vwv2,0);  /* no additional info */
784
785         cli_send_smb(cli);
786         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
787                 return False;
788
789         return CVAL(cli->inbuf,smb_rcls) == 0;
790 }
791
792 /****************************************************************************
793 send a tconX
794 ****************************************************************************/
795 BOOL cli_send_tconX(struct cli_state *cli, 
796                     char *share, char *dev, char *pass, int passlen)
797 {
798         fstring fullshare, pword;
799         char *p;
800         bzero(cli->outbuf,smb_size);
801         bzero(cli->inbuf,smb_size);
802
803         fstrcpy(cli->share, share);
804
805         /* in user level security don't send a password now */
806         if (cli->sec_mode & 1) {
807                 passlen = 1;
808                 pass = "";
809         }
810
811         if ((cli->sec_mode & 2) && *pass && passlen != 24) {
812                 passlen = 24;
813                 SMBencrypt((uchar *)pass,(uchar *)cli->cryptkey,(uchar *)pword);
814         } else {
815                 memcpy(pword, pass, passlen);
816         }
817
818         slprintf(fullshare, sizeof(fullshare)-1,
819                  "\\\\%s\\%s", cli->desthost, share);
820
821         set_message(cli->outbuf,4,
822                     2 + strlen(fullshare) + passlen + strlen(dev),True);
823         CVAL(cli->outbuf,smb_com) = SMBtconX;
824         cli_setup_packet(cli);
825
826         SSVAL(cli->outbuf,smb_vwv0,0xFF);
827         SSVAL(cli->outbuf,smb_vwv3,passlen);
828
829         p = smb_buf(cli->outbuf);
830         memcpy(p,pword,passlen);
831         p += passlen;
832         fstrcpy(p,fullshare);
833         p = skip_string(p,1);
834         pstrcpy(p,dev);
835
836         SCVAL(cli->inbuf,smb_rcls, 1);
837
838         cli_send_smb(cli);
839         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
840                 return False;
841
842         if (CVAL(cli->inbuf,smb_rcls) != 0) {
843                 return False;
844         }
845
846         fstrcpy(cli->dev, "A:");
847
848         if (cli->protocol >= PROTOCOL_NT1) {
849                 fstrcpy(cli->dev, smb_buf(cli->inbuf));
850         }
851
852         if (strcasecmp(share,"IPC$")==0) {
853                 fstrcpy(cli->dev, "IPC");
854         }
855
856         /* only grab the device if we have a recent protocol level */
857         if (cli->protocol >= PROTOCOL_NT1 &&
858             smb_buflen(cli->inbuf) == 3) {
859                 /* almost certainly win95 - enable bug fixes */
860                 cli->win95 = True;
861         }
862
863         cli->cnum = SVAL(cli->inbuf,smb_tid);
864         return True;
865 }
866
867
868 /****************************************************************************
869 send a tree disconnect
870 ****************************************************************************/
871 BOOL cli_tdis(struct cli_state *cli)
872 {
873         bzero(cli->outbuf,smb_size);
874         set_message(cli->outbuf,0,0,True);
875         CVAL(cli->outbuf,smb_com) = SMBtdis;
876         SSVAL(cli->outbuf,smb_tid,cli->cnum);
877         cli_setup_packet(cli);
878         
879         cli_send_smb(cli);
880         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
881                 return False;
882         
883         return CVAL(cli->inbuf,smb_rcls) == 0;
884 }
885
886 /****************************************************************************
887 rename a file
888 ****************************************************************************/
889 BOOL cli_rename(struct cli_state *cli, char *fname_src, char *fname_dst)
890 {
891         char *p;
892
893         bzero(cli->outbuf,smb_size);
894         bzero(cli->inbuf,smb_size);
895
896         set_message(cli->outbuf,1, 4 + strlen(fname_src) + strlen(fname_dst), True);
897
898         CVAL(cli->outbuf,smb_com) = SMBmv;
899         SSVAL(cli->outbuf,smb_tid,cli->cnum);
900         cli_setup_packet(cli);
901
902         SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN);
903
904         p = smb_buf(cli->outbuf);
905         *p++ = 4;
906         pstrcpy(p,fname_src);
907         p = skip_string(p,1);
908         *p++ = 4;
909         pstrcpy(p,fname_dst);
910
911         cli_send_smb(cli);
912         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
913                 return False;
914         }
915
916         if (CVAL(cli->inbuf,smb_rcls) != 0) {
917                 return False;
918         }
919
920         return True;
921 }
922
923 /****************************************************************************
924 delete a file
925 ****************************************************************************/
926 BOOL cli_unlink(struct cli_state *cli, char *fname)
927 {
928         char *p;
929
930         bzero(cli->outbuf,smb_size);
931         bzero(cli->inbuf,smb_size);
932
933         set_message(cli->outbuf,1, 2 + strlen(fname),True);
934
935         CVAL(cli->outbuf,smb_com) = SMBunlink;
936         SSVAL(cli->outbuf,smb_tid,cli->cnum);
937         cli_setup_packet(cli);
938
939         SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN);
940   
941         p = smb_buf(cli->outbuf);
942         *p++ = 4;      
943         pstrcpy(p,fname);
944
945         cli_send_smb(cli);
946         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
947                 return False;
948         }
949
950         if (CVAL(cli->inbuf,smb_rcls) != 0) {
951                 return False;
952         }
953
954         return True;
955 }
956
957 /****************************************************************************
958 create a directory
959 ****************************************************************************/
960 BOOL cli_mkdir(struct cli_state *cli, char *dname)
961 {
962         char *p;
963
964         bzero(cli->outbuf,smb_size);
965         bzero(cli->inbuf,smb_size);
966
967         set_message(cli->outbuf,0, 2 + strlen(dname),True);
968
969         CVAL(cli->outbuf,smb_com) = SMBmkdir;
970         SSVAL(cli->outbuf,smb_tid,cli->cnum);
971         cli_setup_packet(cli);
972
973         p = smb_buf(cli->outbuf);
974         *p++ = 4;      
975         pstrcpy(p,dname);
976
977         cli_send_smb(cli);
978         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
979                 return False;
980         }
981
982         if (CVAL(cli->inbuf,smb_rcls) != 0) {
983                 return False;
984         }
985
986         return True;
987 }
988
989 /****************************************************************************
990 remove a directory
991 ****************************************************************************/
992 BOOL cli_rmdir(struct cli_state *cli, char *dname)
993 {
994         char *p;
995
996         bzero(cli->outbuf,smb_size);
997         bzero(cli->inbuf,smb_size);
998
999         set_message(cli->outbuf,0, 2 + strlen(dname),True);
1000
1001         CVAL(cli->outbuf,smb_com) = SMBrmdir;
1002         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1003         cli_setup_packet(cli);
1004
1005         p = smb_buf(cli->outbuf);
1006         *p++ = 4;      
1007         pstrcpy(p,dname);
1008
1009         cli_send_smb(cli);
1010         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1011                 return False;
1012         }
1013
1014         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1015                 return False;
1016         }
1017
1018         return True;
1019 }
1020
1021
1022
1023 /****************************************************************************
1024 open a file
1025 ****************************************************************************/
1026 int cli_nt_create(struct cli_state *cli, char *fname)
1027 {
1028         char *p;
1029
1030         bzero(cli->outbuf,smb_size);
1031         bzero(cli->inbuf,smb_size);
1032
1033         set_message(cli->outbuf,24,1 + strlen(fname),True);
1034
1035         CVAL(cli->outbuf,smb_com) = SMBntcreateX;
1036         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1037         cli_setup_packet(cli);
1038
1039         SSVAL(cli->outbuf,smb_vwv0,0xFF);
1040         SIVAL(cli->outbuf,smb_ntcreate_Flags, 0x06);
1041         SIVAL(cli->outbuf,smb_ntcreate_RootDirectoryFid, 0x0);
1042         SIVAL(cli->outbuf,smb_ntcreate_DesiredAccess, 0x2019f);
1043         SIVAL(cli->outbuf,smb_ntcreate_FileAttributes, 0x0);
1044         SIVAL(cli->outbuf,smb_ntcreate_ShareAccess, 0x03);
1045         SIVAL(cli->outbuf,smb_ntcreate_CreateDisposition, 0x01);
1046         SIVAL(cli->outbuf,smb_ntcreate_CreateOptions, 0x0);
1047         SIVAL(cli->outbuf,smb_ntcreate_ImpersonationLevel, 0x02);
1048         SSVAL(cli->outbuf,smb_ntcreate_NameLength, strlen(fname));
1049
1050         p = smb_buf(cli->outbuf);
1051         pstrcpy(p,fname);
1052         p = skip_string(p,1);
1053
1054         cli_send_smb(cli);
1055         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1056                 return -1;
1057         }
1058
1059         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1060                 return -1;
1061         }
1062
1063         return SVAL(cli->inbuf,smb_vwv2 + 1);
1064 }
1065
1066
1067 /****************************************************************************
1068 open a file
1069 ****************************************************************************/
1070 int cli_open(struct cli_state *cli, char *fname, int flags, int share_mode)
1071 {
1072         char *p;
1073         unsigned openfn=0;
1074         unsigned accessmode=0;
1075
1076         /* you must open for RW not just write - otherwise getattrE doesn't
1077            work! */
1078         if ((flags & O_ACCMODE) == O_WRONLY) {
1079                 flags = (flags & ~O_ACCMODE) | O_RDWR;
1080         }
1081
1082         if (flags & O_CREAT)
1083                 openfn |= (1<<4);
1084         if (!(flags & O_EXCL)) {
1085                 if (flags & O_TRUNC)
1086                         openfn |= (1<<1);
1087                 else
1088                         openfn |= (1<<0);
1089         }
1090
1091         accessmode = (share_mode<<4);
1092
1093         if ((flags & O_ACCMODE) == O_RDWR) {
1094                 accessmode |= 2;
1095         } else if ((flags & O_ACCMODE) == O_WRONLY) {
1096                 accessmode |= 1;
1097         } 
1098
1099 #if defined(O_SYNC)
1100         if ((flags & O_SYNC) == O_SYNC) {
1101                 accessmode |= (1<<14);
1102         }
1103 #endif /* O_SYNC */
1104
1105         bzero(cli->outbuf,smb_size);
1106         bzero(cli->inbuf,smb_size);
1107
1108         set_message(cli->outbuf,15,1 + strlen(fname),True);
1109
1110         CVAL(cli->outbuf,smb_com) = SMBopenX;
1111         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1112         cli_setup_packet(cli);
1113
1114         SSVAL(cli->outbuf,smb_vwv0,0xFF);
1115         SSVAL(cli->outbuf,smb_vwv2,0);  /* no additional info */
1116         SSVAL(cli->outbuf,smb_vwv3,accessmode);
1117         SSVAL(cli->outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
1118         SSVAL(cli->outbuf,smb_vwv5,0);
1119         SSVAL(cli->outbuf,smb_vwv8,openfn);
1120   
1121         p = smb_buf(cli->outbuf);
1122         pstrcpy(p,fname);
1123         p = skip_string(p,1);
1124
1125         cli_send_smb(cli);
1126         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1127                 return -1;
1128         }
1129
1130         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1131                 return -1;
1132         }
1133
1134         return SVAL(cli->inbuf,smb_vwv2);
1135 }
1136
1137
1138
1139
1140 /****************************************************************************
1141   close a file
1142 ****************************************************************************/
1143 BOOL cli_close(struct cli_state *cli, int fnum)
1144 {
1145         bzero(cli->outbuf,smb_size);
1146         bzero(cli->inbuf,smb_size);
1147
1148         set_message(cli->outbuf,3,0,True);
1149
1150         CVAL(cli->outbuf,smb_com) = SMBclose;
1151         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1152         cli_setup_packet(cli);
1153
1154         SSVAL(cli->outbuf,smb_vwv0,fnum);
1155         SIVALS(cli->outbuf,smb_vwv1,-1);
1156
1157         cli_send_smb(cli);
1158         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1159                 return False;
1160         }
1161
1162         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1163                 return False;
1164         }
1165
1166         return True;
1167 }
1168
1169
1170 /****************************************************************************
1171   lock a file
1172 ****************************************************************************/
1173 BOOL cli_lock(struct cli_state *cli, int fnum, uint32 offset, uint32 len, int timeout)
1174 {
1175         char *p;
1176         int saved_timeout = cli->timeout;
1177
1178         bzero(cli->outbuf,smb_size);
1179         bzero(cli->inbuf,smb_size);
1180
1181         set_message(cli->outbuf,8,10,True);
1182
1183         CVAL(cli->outbuf,smb_com) = SMBlockingX;
1184         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1185         cli_setup_packet(cli);
1186
1187         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
1188         SSVAL(cli->outbuf,smb_vwv2,fnum);
1189         CVAL(cli->outbuf,smb_vwv3) = 0;
1190         SIVALS(cli->outbuf, smb_vwv4, timeout);
1191         SSVAL(cli->outbuf,smb_vwv6,0);
1192         SSVAL(cli->outbuf,smb_vwv7,1);
1193
1194         p = smb_buf(cli->outbuf);
1195         SSVAL(p, 0, cli->pid);
1196         SIVAL(p, 2, offset);
1197         SIVAL(p, 6, len);
1198
1199         cli_send_smb(cli);
1200
1201         cli->timeout = (timeout == -1) ? 0x7FFFFFFF : timeout;
1202
1203         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1204                 cli->timeout = saved_timeout;
1205                 return False;
1206         }
1207
1208         cli->timeout = saved_timeout;
1209
1210         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1211                 return False;
1212         }
1213
1214         return True;
1215 }
1216
1217 /****************************************************************************
1218   unlock a file
1219 ****************************************************************************/
1220 BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len, int timeout)
1221 {
1222         char *p;
1223
1224         bzero(cli->outbuf,smb_size);
1225         bzero(cli->inbuf,smb_size);
1226
1227         set_message(cli->outbuf,8,10,True);
1228
1229         CVAL(cli->outbuf,smb_com) = SMBlockingX;
1230         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1231         cli_setup_packet(cli);
1232
1233         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
1234         SSVAL(cli->outbuf,smb_vwv2,fnum);
1235         CVAL(cli->outbuf,smb_vwv3) = 0;
1236         SIVALS(cli->outbuf, smb_vwv4, timeout);
1237         SSVAL(cli->outbuf,smb_vwv6,1);
1238         SSVAL(cli->outbuf,smb_vwv7,0);
1239
1240         p = smb_buf(cli->outbuf);
1241         SSVAL(p, 0, cli->pid);
1242         SIVAL(p, 2, offset);
1243         SIVAL(p, 6, len);
1244
1245         cli_send_smb(cli);
1246         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1247                 return False;
1248         }
1249
1250         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1251                 return False;
1252         }
1253
1254         return True;
1255 }
1256
1257
1258
1259 /****************************************************************************
1260 issue a single SMBread and don't wait for a reply
1261 ****************************************************************************/
1262 static void cli_issue_read(struct cli_state *cli, int fnum, off_t offset, 
1263                            size_t size, int i)
1264 {
1265         bzero(cli->outbuf,smb_size);
1266         bzero(cli->inbuf,smb_size);
1267
1268         set_message(cli->outbuf,10,0,True);
1269                 
1270         CVAL(cli->outbuf,smb_com) = SMBreadX;
1271         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1272         cli_setup_packet(cli);
1273
1274         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
1275         SSVAL(cli->outbuf,smb_vwv2,fnum);
1276         SIVAL(cli->outbuf,smb_vwv3,offset);
1277         SSVAL(cli->outbuf,smb_vwv5,size);
1278         SSVAL(cli->outbuf,smb_vwv6,size);
1279         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
1280
1281         cli_send_smb(cli);
1282 }
1283
1284 /****************************************************************************
1285   read from a file
1286 ****************************************************************************/
1287 size_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
1288 {
1289         char *p;
1290         int total = -1;
1291         int issued=0;
1292         int received=0;
1293         int mpx = MAX(cli->max_mux-1, 1);
1294         int block = (cli->max_xmit - (smb_size+32)) & ~1023;
1295         int mid;
1296         int blocks = (size + (block-1)) / block;
1297
1298         if (size == 0) return 0;
1299
1300         while (received < blocks) {
1301                 int size2;
1302
1303                 while (issued - received < mpx && issued < blocks) {
1304                         int size1 = MIN(block, size-issued*block);
1305                         cli_issue_read(cli, fnum, offset+issued*block, size1, issued);
1306                         issued++;
1307                 }
1308
1309                 if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1310                         return total;
1311                 }
1312
1313                 received++;
1314                 mid = SVAL(cli->inbuf, smb_mid) - cli->mid;
1315                 size2 = SVAL(cli->inbuf, smb_vwv5);
1316
1317                 if (CVAL(cli->inbuf,smb_rcls) != 0) {
1318                         blocks = MIN(blocks, mid-1);
1319                         continue;
1320                 }
1321
1322                 if (size2 <= 0) {
1323                         blocks = MIN(blocks, mid-1);
1324                         /* this distinguishes EOF from an error */
1325                         total = MAX(total, 0);
1326                         continue;
1327                 }
1328
1329                 if (size2 > block) {
1330                         DEBUG(0,("server returned more than we wanted!\n"));
1331                         exit(1);
1332                 }
1333                 if (mid >= issued) {
1334                         DEBUG(0,("invalid mid from server!\n"));
1335                         exit(1);
1336                 }
1337                 p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
1338
1339                 memcpy(buf+mid*block, p, size2);
1340
1341                 total = MAX(total, mid*block + size2);
1342         }
1343
1344         while (received < issued) {
1345                 client_receive_smb(cli->fd,cli->inbuf,cli->timeout);
1346                 received++;
1347         }
1348         
1349         return total;
1350 }
1351
1352
1353 /****************************************************************************
1354 issue a single SMBwrite and don't wait for a reply
1355 ****************************************************************************/
1356 static void cli_issue_write(struct cli_state *cli, int fnum, off_t offset, uint16 mode, char *buf,
1357                             size_t size, int i)
1358 {
1359         char *p;
1360
1361         bzero(cli->outbuf,smb_size);
1362         bzero(cli->inbuf,smb_size);
1363
1364         set_message(cli->outbuf,12,size,True);
1365         
1366         CVAL(cli->outbuf,smb_com) = SMBwriteX;
1367         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1368         cli_setup_packet(cli);
1369         
1370         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
1371         SSVAL(cli->outbuf,smb_vwv2,fnum);
1372
1373         SIVAL(cli->outbuf,smb_vwv3,offset);
1374         SIVAL(cli->outbuf,smb_vwv5,IS_BITS_SET_ALL(mode, 0x0008) ? 0xFFFFFFFF : 0);
1375         SSVAL(cli->outbuf,smb_vwv7,mode);
1376
1377         SSVAL(cli->outbuf,smb_vwv8,IS_BITS_SET_ALL(mode, 0x0008) ? size : 0);
1378         SSVAL(cli->outbuf,smb_vwv10,size);
1379         SSVAL(cli->outbuf,smb_vwv11,
1380               smb_buf(cli->outbuf) - smb_base(cli->outbuf));
1381         
1382         p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11);
1383         memcpy(p, buf, size);
1384
1385         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
1386         
1387         show_msg(cli->outbuf);
1388         cli_send_smb(cli);
1389 }
1390
1391 /****************************************************************************
1392   write to a file
1393   write_mode: 0x0001 disallow write cacheing
1394               0x0002 return bytes remaining
1395               0x0004 use raw named pipe protocol
1396               0x0008 start of message mode named pipe protocol
1397 ****************************************************************************/
1398 size_t cli_write(struct cli_state *cli,
1399                                 int fnum, uint16 write_mode,
1400                                 char *buf, off_t offset, size_t size)
1401 {
1402         int total = -1;
1403         int issued=0;
1404         int received=0;
1405         int mpx = MAX(cli->max_mux-1, 1);
1406         int block = (cli->max_xmit - (smb_size+32)) & ~1023;
1407         int mid;
1408         int blocks = (size + (block-1)) / block;
1409
1410         if (size == 0) return 0;
1411
1412         while (received < blocks) {
1413                 int size2;
1414
1415                 while (issued - received < mpx && issued < blocks) {
1416                         int size1 = MIN(block, size-issued*block);
1417                         cli_issue_write(cli, fnum, offset+issued*block,
1418                                         write_mode,
1419                                         buf + issued*block,
1420                                         size1, issued);
1421                         issued++;
1422                 }
1423
1424                 if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1425                         return total;
1426                 }
1427
1428                 received++;
1429                 mid = SVAL(cli->inbuf, smb_mid) - cli->mid;
1430                 size2 = SVAL(cli->inbuf, smb_vwv2);
1431
1432                 if (CVAL(cli->inbuf,smb_rcls) != 0) {
1433                         blocks = MIN(blocks, mid-1);
1434                         continue;
1435                 }
1436
1437                 if (size2 <= 0) {
1438                         blocks = MIN(blocks, mid-1);
1439                         /* this distinguishes EOF from an error */
1440                         total = MAX(total, 0);
1441                         continue;
1442                 }
1443
1444                 total += size2;
1445
1446                 total = MAX(total, mid*block + size2);
1447         }
1448
1449         while (received < issued) {
1450                 client_receive_smb(cli->fd,cli->inbuf,cli->timeout);
1451                 received++;
1452         }
1453         
1454         return total;
1455 }
1456
1457
1458 /****************************************************************************
1459 do a SMBgetattrE call
1460 ****************************************************************************/
1461 BOOL cli_getattrE(struct cli_state *cli, int fd, 
1462                   uint32 *attr, size_t *size, 
1463                   time_t *c_time, time_t *a_time, time_t *m_time)
1464 {
1465         bzero(cli->outbuf,smb_size);
1466         bzero(cli->inbuf,smb_size);
1467
1468         set_message(cli->outbuf,2,0,True);
1469
1470         CVAL(cli->outbuf,smb_com) = SMBgetattrE;
1471         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1472         cli_setup_packet(cli);
1473
1474         SSVAL(cli->outbuf,smb_vwv0,fd);
1475
1476         cli_send_smb(cli);
1477         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1478                 return False;
1479         }
1480         
1481         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1482                 return False;
1483         }
1484
1485         if (size) {
1486                 *size = IVAL(cli->inbuf, smb_vwv6);
1487         }
1488
1489         if (attr) {
1490                 *attr = SVAL(cli->inbuf,smb_vwv10);
1491         }
1492
1493         if (c_time) {
1494                 *c_time = make_unix_date3(cli->inbuf+smb_vwv0);
1495         }
1496
1497         if (a_time) {
1498                 *a_time = make_unix_date3(cli->inbuf+smb_vwv2);
1499         }
1500
1501         if (m_time) {
1502                 *m_time = make_unix_date3(cli->inbuf+smb_vwv4);
1503         }
1504
1505         return True;
1506 }
1507
1508
1509 /****************************************************************************
1510 do a SMBgetatr call
1511 ****************************************************************************/
1512 BOOL cli_getatr(struct cli_state *cli, char *fname, 
1513                 uint32 *attr, size_t *size, time_t *t)
1514 {
1515         char *p;
1516
1517         bzero(cli->outbuf,smb_size);
1518         bzero(cli->inbuf,smb_size);
1519
1520         set_message(cli->outbuf,0,strlen(fname)+2,True);
1521
1522         CVAL(cli->outbuf,smb_com) = SMBgetatr;
1523         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1524         cli_setup_packet(cli);
1525
1526         p = smb_buf(cli->outbuf);
1527         *p = 4;
1528         pstrcpy(p+1, fname);
1529
1530         cli_send_smb(cli);
1531         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1532                 return False;
1533         }
1534         
1535         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1536                 return False;
1537         }
1538
1539         if (size) {
1540                 *size = IVAL(cli->inbuf, smb_vwv3);
1541         }
1542
1543         if (t) {
1544                 *t = make_unix_date3(cli->inbuf+smb_vwv1);
1545         }
1546
1547         if (attr) {
1548                 *attr = SVAL(cli->inbuf,smb_vwv0);
1549         }
1550
1551
1552         return True;
1553 }
1554
1555
1556 /****************************************************************************
1557 do a SMBsetatr call
1558 ****************************************************************************/
1559 BOOL cli_setatr(struct cli_state *cli, char *fname, int attr, time_t t)
1560 {
1561         char *p;
1562
1563         bzero(cli->outbuf,smb_size);
1564         bzero(cli->inbuf,smb_size);
1565
1566         set_message(cli->outbuf,8,strlen(fname)+4,True);
1567
1568         CVAL(cli->outbuf,smb_com) = SMBsetatr;
1569         SSVAL(cli->outbuf,smb_tid,cli->cnum);
1570         cli_setup_packet(cli);
1571
1572         SSVAL(cli->outbuf,smb_vwv0, attr);
1573         put_dos_date3(cli->outbuf,smb_vwv1, t);
1574
1575         p = smb_buf(cli->outbuf);
1576         *p = 4;
1577         pstrcpy(p+1, fname);
1578         p = skip_string(p,1);
1579         *p = 4;
1580
1581         cli_send_smb(cli);
1582         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout)) {
1583                 return False;
1584         }
1585         
1586         if (CVAL(cli->inbuf,smb_rcls) != 0) {
1587                 return False;
1588         }
1589
1590         return True;
1591 }
1592
1593 /****************************************************************************
1594 send a qpathinfo call
1595 ****************************************************************************/
1596 BOOL cli_qpathinfo(struct cli_state *cli, const char *fname, 
1597                    time_t *c_time, time_t *a_time, time_t *m_time, 
1598                    size_t *size, uint32 *mode)
1599 {
1600         int data_len = 0;
1601         int param_len = 0;
1602         uint16 setup = TRANSACT2_QPATHINFO;
1603         pstring param;
1604         char *rparam=NULL, *rdata=NULL;
1605         int count=8;
1606         BOOL ret;
1607         time_t (*date_fn)(void *);
1608
1609         param_len = strlen(fname) + 7;
1610
1611         memset(param, 0, param_len);
1612         SSVAL(param, 0, SMB_INFO_STANDARD);
1613         pstrcpy(&param[6], fname);
1614
1615         do {
1616                 ret = (cli_send_trans(cli, SMBtrans2, 
1617                                       NULL, 0,        /* Name, length */
1618                                       -1, 0,          /* fid, flags */
1619                                       &setup, 1, 0,   /* setup, length, max */
1620                                       param, param_len, 10, /* param, length, max */
1621                                       NULL, data_len, cli->max_xmit /* data, length, max */
1622                                       ) &&
1623                        cli_receive_trans(cli, SMBtrans2, 
1624                                          &rparam, &param_len,
1625                                          &rdata, &data_len));
1626                 if (!ret) {
1627                         /* we need to work around a Win95 bug - sometimes
1628                            it gives ERRSRV/ERRerror temprarily */
1629                         uint8 eclass;
1630                         uint32 ecode;
1631                         cli_error(cli, &eclass, &ecode);
1632                         if (eclass != ERRSRV || ecode != ERRerror) break;
1633                         msleep(100);
1634                 }
1635         } while (count-- && ret==False);
1636
1637         if (!ret || !rdata || data_len < 22) {
1638                 return False;
1639         }
1640
1641         if (cli->win95) {
1642                 date_fn = make_unix_date;
1643         } else {
1644                 date_fn = make_unix_date2;
1645         }
1646
1647         if (c_time) {
1648                 *c_time = date_fn(rdata+0);
1649         }
1650         if (a_time) {
1651                 *a_time = date_fn(rdata+4);
1652         }
1653         if (m_time) {
1654                 *m_time = date_fn(rdata+8);
1655         }
1656         if (size) {
1657                 *size = IVAL(rdata, 12);
1658         }
1659         if (mode) {
1660                 *mode = SVAL(rdata,l1_attrFile);
1661         }
1662
1663         if (rdata) free(rdata);
1664         if (rparam) free(rparam);
1665         return True;
1666 }
1667
1668 /****************************************************************************
1669 send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
1670 ****************************************************************************/
1671 BOOL cli_qpathinfo2(struct cli_state *cli, const char *fname, 
1672                     time_t *c_time, time_t *a_time, time_t *m_time, 
1673                     time_t *w_time, size_t *size, uint32 *mode)
1674 {
1675         int data_len = 0;
1676         int param_len = 0;
1677         uint16 setup = TRANSACT2_QPATHINFO;
1678         pstring param;
1679         char *rparam=NULL, *rdata=NULL;
1680
1681         param_len = strlen(fname) + 7;
1682
1683         memset(param, 0, param_len);
1684         SSVAL(param, 0, SMB_QUERY_FILE_ALL_INFO);
1685         pstrcpy(&param[6], fname);
1686
1687         if (!cli_send_trans(cli, SMBtrans2, 
1688                             NULL, 0,                      /* name, length */
1689                             -1, 0,                        /* fid, flags */
1690                             &setup, 1, 0,                 /* setup, length, max */
1691                             param, param_len, 10,         /* param, length, max */
1692                             NULL, data_len, cli->max_xmit /* data, length, max */
1693                            )) {
1694                 return False;
1695         }
1696
1697         if (!cli_receive_trans(cli, SMBtrans2,
1698                                &rparam, &param_len,
1699                                &rdata, &data_len)) {
1700                 return False;
1701         }
1702
1703         if (!rdata || data_len < 22) {
1704                 return False;
1705         }
1706
1707         if (c_time) {
1708                 *c_time = interpret_long_date(rdata+0) - cli->serverzone;
1709         }
1710         if (a_time) {
1711                 *a_time = interpret_long_date(rdata+8) - cli->serverzone;
1712         }
1713         if (m_time) {
1714                 *m_time = interpret_long_date(rdata+16) - cli->serverzone;
1715         }
1716         if (w_time) {
1717                 *w_time = interpret_long_date(rdata+24) - cli->serverzone;
1718         }
1719         if (size) {
1720                 *size = IVAL(rdata, 40);
1721         }
1722         if (mode) {
1723                 *mode = IVAL(rdata, 32);
1724         }
1725
1726         if (rdata) free(rdata);
1727         if (rparam) free(rparam);
1728         return True;
1729 }
1730
1731
1732 /****************************************************************************
1733 send a qfileinfo call
1734 ****************************************************************************/
1735 BOOL cli_qfileinfo(struct cli_state *cli, int fnum, 
1736                    uint32 *mode, size_t *size,
1737                    time_t *c_time, time_t *a_time, time_t *m_time)
1738 {
1739         int data_len = 0;
1740         int param_len = 0;
1741         uint16 setup = TRANSACT2_QFILEINFO;
1742         pstring param;
1743         char *rparam=NULL, *rdata=NULL;
1744
1745         param_len = 4;
1746
1747         memset(param, 0, param_len);
1748         SSVAL(param, 0, fnum);
1749         SSVAL(param, 2, SMB_INFO_STANDARD);
1750
1751         if (!cli_send_trans(cli, SMBtrans2, 
1752                             NULL, 0,                        /* name, length */
1753                             -1, 0,                          /* fid, flags */
1754                             &setup, 1, 0,                   /* setup, length, max */
1755                             param, param_len, 2,            /* param, length, max */
1756                             NULL, data_len, cli->max_xmit   /* data, length, max */
1757                            )) {
1758                 return False;
1759         }
1760
1761         if (!cli_receive_trans(cli, SMBtrans2,
1762                                &rparam, &param_len,
1763                                &rdata, &data_len)) {
1764                 return False;
1765         }
1766
1767         if (!rdata || data_len < 22) {
1768                 return False;
1769         }
1770
1771         if (c_time) {
1772                 *c_time = make_unix_date2(rdata+0);
1773         }
1774         if (a_time) {
1775                 *a_time = make_unix_date2(rdata+4);
1776         }
1777         if (m_time) {
1778                 *m_time = make_unix_date2(rdata+8);
1779         }
1780         if (size) {
1781                 *size = IVAL(rdata, 12);
1782         }
1783         if (mode) {
1784                 *mode = SVAL(rdata,l1_attrFile);
1785         }
1786
1787         if (rdata) free(rdata);
1788         if (rparam) free(rparam);
1789         return True;
1790 }
1791
1792
1793 /****************************************************************************
1794 interpret a long filename structure - this is mostly guesses at the moment
1795 The length of the structure is returned
1796 The structure of a long filename depends on the info level. 260 is used
1797 by NT and 2 is used by OS/2
1798 ****************************************************************************/
1799 static int interpret_long_filename(int level,char *p,file_info *finfo)
1800 {
1801         extern file_info def_finfo;
1802
1803         if (finfo)
1804                 memcpy(finfo,&def_finfo,sizeof(*finfo));
1805
1806         switch (level)
1807                 {
1808                 case 1: /* OS/2 understands this */
1809                         if (finfo) {
1810                                 /* these dates are converted to GMT by make_unix_date */
1811                                 finfo->ctime = make_unix_date2(p+4);
1812                                 finfo->atime = make_unix_date2(p+8);
1813                                 finfo->mtime = make_unix_date2(p+12);
1814                                 finfo->size = IVAL(p,16);
1815                                 finfo->mode = CVAL(p,24);
1816                                 pstrcpy(finfo->name,p+27);
1817                         }
1818                         return(28 + CVAL(p,26));
1819
1820                 case 2: /* this is what OS/2 uses mostly */
1821                         if (finfo) {
1822                                 /* these dates are converted to GMT by make_unix_date */
1823                                 finfo->ctime = make_unix_date2(p+4);
1824                                 finfo->atime = make_unix_date2(p+8);
1825                                 finfo->mtime = make_unix_date2(p+12);
1826                                 finfo->size = IVAL(p,16);
1827                                 finfo->mode = CVAL(p,24);
1828                                 pstrcpy(finfo->name,p+31);
1829                         }
1830                         return(32 + CVAL(p,30));
1831
1832                         /* levels 3 and 4 are untested */
1833                 case 3:
1834                         if (finfo) {
1835                                 /* these dates are probably like the other ones */
1836                                 finfo->ctime = make_unix_date2(p+8);
1837                                 finfo->atime = make_unix_date2(p+12);
1838                                 finfo->mtime = make_unix_date2(p+16);
1839                                 finfo->size = IVAL(p,20);
1840                                 finfo->mode = CVAL(p,28);
1841                                 pstrcpy(finfo->name,p+33);
1842                         }
1843                         return(SVAL(p,4)+4);
1844                         
1845                 case 4:
1846                         if (finfo) {
1847                                 /* these dates are probably like the other ones */
1848                                 finfo->ctime = make_unix_date2(p+8);
1849                                 finfo->atime = make_unix_date2(p+12);
1850                                 finfo->mtime = make_unix_date2(p+16);
1851                                 finfo->size = IVAL(p,20);
1852                                 finfo->mode = CVAL(p,28);
1853                                 pstrcpy(finfo->name,p+37);
1854                         }
1855                         return(SVAL(p,4)+4);
1856                         
1857                 case 260: /* NT uses this, but also accepts 2 */
1858                         if (finfo) {
1859                                 int ret = SVAL(p,0);
1860                                 int namelen;
1861                                 p += 4; /* next entry offset */
1862                                 p += 4; /* fileindex */
1863                                 
1864                                 /* these dates appear to arrive in a
1865                                    weird way. It seems to be localtime
1866                                    plus the serverzone given in the
1867                                    initial connect. This is GMT when
1868                                    DST is not in effect and one hour
1869                                    from GMT otherwise. Can this really
1870                                    be right??
1871
1872                                    I suppose this could be called
1873                                    kludge-GMT. Is is the GMT you get
1874                                    by using the current DST setting on
1875                                    a different localtime. It will be
1876                                    cheap to calculate, I suppose, as
1877                                    no DST tables will be needed */
1878
1879                                 finfo->ctime = interpret_long_date(p); p += 8;
1880                                 finfo->atime = interpret_long_date(p); p += 8;
1881                                 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
1882                                 finfo->size = IVAL(p,0); p += 8;
1883                                 p += 8; /* alloc size */
1884                                 finfo->mode = CVAL(p,0); p += 4;
1885                                 namelen = IVAL(p,0); p += 4;
1886                                 p += 4; /* EA size */
1887                                 p += 2; /* short name len? */
1888                                 p += 24; /* short name? */        
1889                                 StrnCpy(finfo->name,p,namelen);
1890                                 return(ret);
1891                         }
1892                         return(SVAL(p,0));
1893                 }
1894         
1895         DEBUG(1,("Unknown long filename format %d\n",level));
1896         return(SVAL(p,0));
1897 }
1898
1899
1900 /****************************************************************************
1901   do a directory listing, calling fn on each file found
1902   ****************************************************************************/
1903 int cli_list(struct cli_state *cli,char *Mask,int attribute,void (*fn)(file_info *))
1904 {
1905         int max_matches = 512;
1906         /* NT uses 260, OS/2 uses 2. Both accept 1. */
1907         int info_level = cli->protocol<PROTOCOL_NT1?1:260; 
1908         char *p, *p2;
1909         pstring mask;
1910         file_info finfo;
1911         int i;
1912         char *dirlist = NULL;
1913         int dirlist_len = 0;
1914         int total_received = -1;
1915         BOOL First = True;
1916         int ff_resume_key = 0;
1917         int ff_searchcount=0;
1918         int ff_eos=0;
1919         int ff_lastname=0;
1920         int ff_dir_handle=0;
1921         int loop_count = 0;
1922         char *rparam=NULL, *rdata=NULL;
1923         int param_len, data_len;
1924         
1925         uint16 setup;
1926         pstring param;
1927         
1928         pstrcpy(mask,Mask);
1929         
1930         while (ff_eos == 0) {
1931                 loop_count++;
1932                 if (loop_count > 200) {
1933                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
1934                         break;
1935                 }
1936
1937                 param_len = 12+strlen(mask)+1;
1938
1939                 if (First) {
1940                         setup = TRANSACT2_FINDFIRST;
1941                         SSVAL(param,0,attribute); /* attribute */
1942                         SSVAL(param,2,max_matches); /* max count */
1943                         SSVAL(param,4,8+4+2);   /* resume required + close on end + continue */
1944                         SSVAL(param,6,info_level); 
1945                         SIVAL(param,8,0);
1946                         pstrcpy(param+12,mask);
1947                 } else {
1948                         setup = TRANSACT2_FINDNEXT;
1949                         SSVAL(param,0,ff_dir_handle);
1950                         SSVAL(param,2,max_matches); /* max count */
1951                         SSVAL(param,4,info_level); 
1952                         SIVAL(param,6,ff_resume_key); /* ff_resume_key */
1953                         SSVAL(param,10,8+4+2);  /* resume required + close on end + continue */
1954                         pstrcpy(param+12,mask);
1955
1956                         DEBUG(5,("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
1957                                  ff_dir_handle,ff_resume_key,ff_lastname,mask));
1958                 }
1959
1960                 if (!cli_send_trans(cli, SMBtrans2, 
1961                                     NULL, 0,                /* Name, length */
1962                                     -1, 0,                  /* fid, flags */
1963                                     &setup, 1, 0,           /* setup, length, max */
1964                                     param, param_len, 10,   /* param, length, max */
1965                                     NULL, 0, 
1966                                     cli->max_xmit /* data, length, max */
1967                                     )) {
1968                         break;
1969                 }
1970
1971                 if (!cli_receive_trans(cli, SMBtrans2, 
1972                                        &rparam, &param_len,
1973                                        &rdata, &data_len)) {
1974                         /* we need to work around a Win95 bug - sometimes
1975                            it gives ERRSRV/ERRerror temprarily */
1976                         uint8 eclass;
1977                         uint32 ecode;
1978                         cli_error(cli, &eclass, &ecode);
1979                         if (eclass != ERRSRV || ecode != ERRerror) break;
1980                         msleep(100);
1981                         continue;
1982                 }
1983
1984                 if (total_received == -1) total_received = 0;
1985
1986                 /* parse out some important return info */
1987                 p = rparam;
1988                 if (First) {
1989                         ff_dir_handle = SVAL(p,0);
1990                         ff_searchcount = SVAL(p,2);
1991                         ff_eos = SVAL(p,4);
1992                         ff_lastname = SVAL(p,8);
1993                 } else {
1994                         ff_searchcount = SVAL(p,0);
1995                         ff_eos = SVAL(p,2);
1996                         ff_lastname = SVAL(p,6);
1997                 }
1998
1999                 if (ff_searchcount == 0) 
2000                         break;
2001
2002                 /* point to the data bytes */
2003                 p = rdata;
2004
2005                 /* we might need the lastname for continuations */
2006                 if (ff_lastname > 0) {
2007                         switch(info_level)
2008                                 {
2009                                 case 260:
2010                                         ff_resume_key =0;
2011                                         StrnCpy(mask,p+ff_lastname,
2012                                                 data_len-ff_lastname);
2013                                         break;
2014                                 case 1:
2015                                         pstrcpy(mask,p + ff_lastname + 1);
2016                                         ff_resume_key = 0;
2017                                         break;
2018                                 }
2019                 } else {
2020                         pstrcpy(mask,"");
2021                 }
2022   
2023                 /* and add them to the dirlist pool */
2024                 dirlist = Realloc(dirlist,dirlist_len + data_len);
2025
2026                 if (!dirlist) {
2027                         DEBUG(0,("Failed to expand dirlist\n"));
2028                         break;
2029                 }
2030
2031                 /* put in a length for the last entry, to ensure we can chain entries 
2032                    into the next packet */
2033                 for (p2=p,i=0;i<(ff_searchcount-1);i++)
2034                         p2 += interpret_long_filename(info_level,p2,NULL);
2035                 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
2036
2037                 /* grab the data for later use */
2038                 memcpy(dirlist+dirlist_len,p,data_len);
2039                 dirlist_len += data_len;
2040
2041                 total_received += ff_searchcount;
2042
2043                 if (rdata) free(rdata); rdata = NULL;
2044                 if (rparam) free(rparam); rparam = NULL;
2045                 
2046                 DEBUG(3,("received %d entries (eos=%d resume=%d)\n",
2047                          ff_searchcount,ff_eos,ff_resume_key));
2048
2049                 First = False;
2050         }
2051
2052         for (p=dirlist,i=0;i<total_received;i++) {
2053                 p += interpret_long_filename(info_level,p,&finfo);
2054                 fn(&finfo);
2055         }
2056
2057         /* free up the dirlist buffer */
2058         if (dirlist) free(dirlist);
2059         return(total_received);
2060 }
2061
2062
2063 /****************************************************************************
2064 Send a SamOEMChangePassword command
2065 ****************************************************************************/
2066
2067 BOOL cli_oem_change_password(struct cli_state *cli, char *user, char *new_password,
2068                              char *old_password)
2069 {
2070   char param[16+sizeof(fstring)];
2071   char data[532];
2072   char *p = param;
2073   fstring upper_case_old_pw;
2074   fstring upper_case_new_pw;
2075   unsigned char old_pw_hash[16];
2076   unsigned char new_pw_hash[16];
2077   int data_len;
2078   int param_len = 0;
2079   char *rparam = NULL;
2080   char *rdata = NULL;
2081   int rprcnt, rdrcnt;
2082
2083   if (strlen(user) >= sizeof(fstring)-1) {
2084     DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
2085     return False;
2086   }
2087
2088   SSVAL(p,0,214); /* SamOEMChangePassword command. */
2089   p += 2;
2090   pstrcpy(p, "zsT");
2091   p = skip_string(p,1);
2092   pstrcpy(p, "B516B16");
2093   p = skip_string(p,1);
2094   pstrcpy(p,user);
2095   p = skip_string(p,1);
2096   SSVAL(p,0,532);
2097   p += 2;
2098
2099   param_len = PTR_DIFF(p,param);
2100
2101   /*
2102    * Get the Lanman hash of the old password, we
2103    * use this as the key to make_oem_passwd_hash().
2104    */
2105   memset(upper_case_old_pw, '\0', sizeof(upper_case_old_pw));
2106   fstrcpy(upper_case_old_pw, old_password);
2107   strupper(upper_case_old_pw);
2108   E_P16((uchar *)upper_case_old_pw, old_pw_hash);
2109
2110         if (!make_oem_passwd_hash( data, new_password, old_pw_hash, False))
2111         {
2112                 return False;
2113         }
2114
2115   /* 
2116    * Now place the old password hash in the data.
2117    */
2118   memset(upper_case_new_pw, '\0', sizeof(upper_case_new_pw));
2119   fstrcpy(upper_case_new_pw, new_password);
2120   strupper(upper_case_new_pw);
2121
2122   E_P16((uchar *)upper_case_new_pw, new_pw_hash);
2123
2124   E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
2125
2126   data_len = 532;
2127     
2128   if (cli_send_trans(cli,SMBtrans,
2129                     PIPE_LANMAN,strlen(PIPE_LANMAN),      /* name, length */
2130                     0,0,                                  /* fid, flags */
2131                     NULL,0,0,                             /* setup, length, max */
2132                     param,param_len,2,                    /* param, length, max */
2133                     data,data_len,0                       /* data, length, max */
2134                    ) == False) {
2135     DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n",
2136               user ));
2137     return False;
2138   }
2139
2140   if (cli_receive_trans(cli,SMBtrans,
2141                        &rparam, &rprcnt,
2142                        &rdata, &rdrcnt)) {
2143     if (rparam)
2144       cli->rap_error = SVAL(rparam,0);
2145   }
2146
2147   if (rparam)
2148     free(rparam);
2149   if (rdata)
2150     free(rdata);
2151
2152   return (cli->rap_error == 0);
2153 }
2154
2155 /****************************************************************************
2156 send a negprot command
2157 ****************************************************************************/
2158 BOOL cli_negprot(struct cli_state *cli)
2159 {
2160         char *p;
2161         int numprots;
2162         int plength;
2163
2164         bzero(cli->outbuf,smb_size);
2165
2166         /* setup the protocol strings */
2167         for (plength=0,numprots=0;
2168              prots[numprots].name && prots[numprots].prot<=cli->protocol;
2169              numprots++)
2170                 plength += strlen(prots[numprots].name)+2;
2171     
2172         set_message(cli->outbuf,0,plength,True);
2173
2174         p = smb_buf(cli->outbuf);
2175         for (numprots=0;
2176              prots[numprots].name && prots[numprots].prot<=cli->protocol;
2177              numprots++) {
2178                 *p++ = 2;
2179                 pstrcpy(p,prots[numprots].name);
2180                 p += strlen(p) + 1;
2181         }
2182
2183         CVAL(cli->outbuf,smb_com) = SMBnegprot;
2184         cli_setup_packet(cli);
2185
2186         CVAL(smb_buf(cli->outbuf),0) = 2;
2187
2188         cli_send_smb(cli);
2189         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
2190                 return False;
2191
2192         show_msg(cli->inbuf);
2193
2194         if (CVAL(cli->inbuf,smb_rcls) != 0 || 
2195             ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
2196                 return(False);
2197         }
2198
2199         cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
2200
2201
2202         if (cli->protocol >= PROTOCOL_NT1) {    
2203                 /* NT protocol */
2204                 cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
2205                 cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
2206                 cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
2207                 cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
2208                 cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1)*60;
2209                 /* this time arrives in real GMT */
2210                 cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
2211                 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
2212                 cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
2213                 if (cli->capabilities & 1) {
2214                         cli->readbraw_supported = True;
2215                         cli->writebraw_supported = True;      
2216                 }
2217         } else if (cli->protocol >= PROTOCOL_LANMAN1) {
2218                 cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
2219                 cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
2220                 cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
2221                 cli->serverzone = SVALS(cli->inbuf,smb_vwv10)*60;
2222                 /* this time is converted to GMT by make_unix_date */
2223                 cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
2224                 cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
2225                 cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
2226                 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
2227         } else {
2228                 /* the old core protocol */
2229                 cli->sec_mode = 0;
2230                 cli->serverzone = TimeDiff(time(NULL));
2231         }
2232
2233         cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
2234
2235         return True;
2236 }
2237
2238
2239 /****************************************************************************
2240   send a session request.  see rfc1002.txt 4.3 and 4.3.2
2241 ****************************************************************************/
2242 BOOL cli_session_request(struct cli_state *cli,
2243                          struct nmb_name *calling, struct nmb_name *called)
2244 {
2245         char *p;
2246         int len = 4;
2247         /* send a session request (RFC 1002) */
2248
2249         memcpy(&(cli->calling), calling, sizeof(*calling));
2250         memcpy(&(cli->called ), called , sizeof(*called ));
2251   
2252         /* put in the destination name */
2253         p = cli->outbuf+len;
2254         name_mangle(cli->called .name, p, cli->called .name_type);
2255         len += name_len(p);
2256
2257         /* and my name */
2258         p = cli->outbuf+len;
2259         name_mangle(cli->calling.name, p, cli->calling.name_type);
2260         len += name_len(p);
2261
2262         /* setup the packet length */
2263         _smb_setlen(cli->outbuf,len);
2264         CVAL(cli->outbuf,0) = 0x81;
2265
2266 #ifdef WITH_SSL
2267 retry:
2268 #endif /* WITH_SSL */
2269
2270         cli_send_smb(cli);
2271         DEBUG(5,("Sent session request\n"));
2272
2273         if (!client_receive_smb(cli->fd,cli->inbuf,cli->timeout))
2274                 return False;
2275
2276 #ifdef WITH_SSL
2277     if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
2278         if (!sslutil_fd_is_ssl(cli->fd)){
2279             if (sslutil_connect(cli->fd) == 0)
2280                 goto retry;
2281         }
2282     }
2283 #endif /* WITH_SSL */
2284
2285         if (CVAL(cli->inbuf,0) != 0x82) {
2286                 /* This is the wrong place to put the error... JRA. */
2287                 cli->rap_error = CVAL(cli->inbuf,0);
2288                 return False;
2289         }
2290         return(True);
2291 }
2292
2293
2294 /****************************************************************************
2295 open the client sockets
2296 ****************************************************************************/
2297 BOOL cli_connect(struct cli_state *cli, char *host, struct in_addr *ip)
2298 {
2299         extern struct in_addr ipzero;
2300
2301         fstrcpy(cli->desthost, host);
2302         
2303         if (!ip || ip_equal(*ip, ipzero)) {
2304                 if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
2305                         return False;
2306                 }
2307                 if (ip) *ip = cli->dest_ip;
2308         } else {
2309                 cli->dest_ip = *ip;
2310         }
2311
2312
2313         cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
2314                                   139, cli->timeout);
2315         if (cli->fd == -1)
2316                 return False;
2317
2318         return True;
2319 }
2320
2321
2322 /****************************************************************************
2323 initialise a client structure
2324 ****************************************************************************/
2325 BOOL cli_initialise(struct cli_state *cli)
2326 {
2327         if (cli->initialised)
2328         {
2329                 cli_shutdown(cli);
2330         }
2331
2332         memset(cli, 0, sizeof(*cli));
2333
2334         cli->fd = -1;
2335         cli->cnum = -1;
2336         cli->pid = (uint16)getpid();
2337         cli->mid = 1;
2338         cli->vuid = UID_FIELD_INVALID;
2339         cli->protocol = PROTOCOL_NT1;
2340         cli->timeout = 20000;
2341         cli->bufsize = CLI_BUFFER_SIZE+4;
2342         cli->max_xmit = cli->bufsize;
2343         cli->outbuf = (char *)malloc(cli->bufsize);
2344         cli->inbuf = (char *)malloc(cli->bufsize);
2345         if (!cli->outbuf || !cli->inbuf)
2346         {
2347                 return False;
2348         }
2349
2350         cli->initialised = 1;
2351
2352         return True;
2353 }
2354
2355 /****************************************************************************
2356 shutdown a client structure
2357 ****************************************************************************/
2358 void cli_shutdown(struct cli_state *cli)
2359 {
2360         if (cli->outbuf)
2361         {
2362                 free(cli->outbuf);
2363         }
2364         if (cli->inbuf)
2365         {
2366                 free(cli->inbuf);
2367         }
2368 #ifdef WITH_SSL
2369     if (cli->fd != -1)
2370       sslutil_disconnect(cli->fd);
2371 #endif /* WITH_SSL */
2372         if (cli->fd != -1) 
2373       close(cli->fd);
2374         memset(cli, 0, sizeof(*cli));
2375 }
2376
2377
2378 /****************************************************************************
2379   return error codes for the last packet
2380   returns 0 if there was no error and the best approx of a unix errno
2381   otherwise
2382
2383   for 32 bit "warnings", a return code of 0 is expected.
2384
2385 ****************************************************************************/
2386 int cli_error(struct cli_state *cli, uint8 *eclass, uint32 *num)
2387 {
2388         int  flgs2 = SVAL(cli->inbuf,smb_flg2);
2389         char rcls;
2390         int code;
2391
2392         if (eclass) *eclass = 0;
2393         if (num   ) *num = 0;
2394
2395         if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
2396                 /* 32 bit error codes detected */
2397                 uint32 nt_err = IVAL(cli->inbuf,smb_rcls);
2398                 if (num) *num = nt_err;
2399                 DEBUG(10,("cli_error: 32 bit codes: code=%08x\n", nt_err));
2400                 if (!IS_BITS_SET_ALL(nt_err, 0xc0000000)) return 0;
2401
2402                 switch (nt_err & 0xFFFFFF) {
2403                 case NT_STATUS_ACCESS_VIOLATION: return EACCES;
2404                 case NT_STATUS_NO_SUCH_FILE: return ENOENT;
2405                 case NT_STATUS_NO_SUCH_DEVICE: return ENODEV;
2406                 case NT_STATUS_INVALID_HANDLE: return EBADF;
2407                 case NT_STATUS_NO_MEMORY: return ENOMEM;
2408                 case NT_STATUS_ACCESS_DENIED: return EACCES;
2409                 case NT_STATUS_OBJECT_NAME_NOT_FOUND: return ENOENT;
2410                 case NT_STATUS_SHARING_VIOLATION: return EBUSY;
2411                 case NT_STATUS_OBJECT_PATH_INVALID: return ENOTDIR;
2412                 case NT_STATUS_OBJECT_NAME_COLLISION: return EEXIST;
2413                 }
2414
2415                 /* for all other cases - a default code */
2416                 return EINVAL;
2417         }
2418
2419         rcls  = CVAL(cli->inbuf,smb_rcls);
2420         code  = SVAL(cli->inbuf,smb_err);
2421         if (rcls == 0) return 0;
2422
2423         if (eclass) *eclass = rcls;
2424         if (num   ) *num    = code;
2425
2426         if (rcls == ERRDOS) {
2427                 switch (code) {
2428                 case ERRbadfile: return ENOENT;
2429                 case ERRbadpath: return ENOTDIR;
2430                 case ERRnoaccess: return EACCES;
2431                 case ERRfilexists: return EEXIST;
2432                 case ERRrename: return EEXIST;
2433                 }
2434         }
2435         if (rcls == ERRSRV) {
2436                 switch (code) {
2437                 case ERRbadpw: return EPERM;
2438                 }
2439         }
2440         /* for other cases */
2441         return EINVAL;
2442 }
2443
2444 /****************************************************************************
2445 set socket options on a open connection
2446 ****************************************************************************/
2447 void cli_sockopt(struct cli_state *cli, char *options)
2448 {
2449         set_socket_options(cli->fd, options);
2450 }
2451
2452 /****************************************************************************
2453 set the PID to use for smb messages. Return the old pid.
2454 ****************************************************************************/
2455 uint16 cli_setpid(struct cli_state *cli, uint16 pid)
2456 {
2457         uint16 ret = cli->pid;
2458         cli->pid = pid;
2459         return ret;
2460 }
2461
2462 /****************************************************************************
2463 re-establishes a connection
2464 ****************************************************************************/
2465 BOOL cli_reestablish_connection(struct cli_state *cli)
2466 {
2467         struct nmb_name calling;
2468         struct nmb_name called;
2469         fstring dest_host;
2470         fstring share;
2471         fstring dev;
2472         BOOL do_tcon = False;
2473         int oldfd = cli->fd;
2474
2475         if (!cli->initialised || cli->fd == -1)
2476         {
2477                 DEBUG(3,("cli_reestablish_connection: not connected\n"));
2478                 return False;
2479         }
2480
2481         /* copy the parameters necessary to re-establish the connection */
2482
2483         if (cli->cnum != 0)
2484         {
2485                 fstrcpy(share, cli->share);
2486                 fstrcpy(dev  , cli->dev);
2487                 do_tcon = True;
2488         }
2489
2490         memcpy(&called , &(cli->called ), sizeof(called ));
2491         memcpy(&calling, &(cli->calling), sizeof(calling));
2492         fstrcpy(dest_host, cli->full_dest_host_name);
2493
2494         DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
2495                  namestr(&calling), namestr(&called), 
2496                  inet_ntoa(cli->dest_ip),
2497                  cli->user_name, cli->domain));
2498
2499         cli->fd = -1;
2500
2501         if (cli_establish_connection(cli,
2502                                      dest_host, &cli->dest_ip,
2503                                      &calling, &called,
2504                                      share, dev, False, do_tcon)) {
2505                 if (cli->fd != oldfd) {
2506                         if (dup2(cli->fd, oldfd) == oldfd) {
2507                                 close(cli->fd);
2508                         }
2509                 }
2510                 return True;
2511         }
2512         return False;
2513 }
2514
2515 /****************************************************************************
2516 establishes a connection right up to doing tconX, reading in a password.
2517 ****************************************************************************/
2518 BOOL cli_establish_connection(struct cli_state *cli, 
2519                                 char *dest_host, struct in_addr *dest_ip,
2520                                 struct nmb_name *calling, struct nmb_name *called,
2521                                 char *service, char *service_type,
2522                                 BOOL do_shutdown, BOOL do_tcon)
2523 {
2524         DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
2525                           namestr(calling), namestr(called), inet_ntoa(*dest_ip),
2526                       cli->user_name, cli->domain));
2527
2528         /* establish connection */
2529
2530         if ((!cli->initialised))
2531         {
2532                 return False;
2533         }
2534
2535         if (cli->fd == -1)
2536         {
2537                 if (!cli_connect(cli, dest_host, dest_ip))
2538                 {
2539                         DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
2540                                           namestr(calling), inet_ntoa(*dest_ip)));
2541                         return False;
2542                 }
2543         }
2544
2545         if (!cli_session_request(cli, calling, called))
2546         {
2547                 DEBUG(1,("failed session request\n"));
2548                 if (do_shutdown)
2549           cli_shutdown(cli);
2550                 return False;
2551         }
2552
2553         if (!cli_negprot(cli))
2554         {
2555                 DEBUG(1,("failed negprot\n"));
2556                 if (do_shutdown)
2557           cli_shutdown(cli);
2558                 return False;
2559         }
2560
2561         if (cli->pwd.cleartext || cli->pwd.null_pwd)
2562         {
2563                 fstring passwd;
2564                 int pass_len;
2565
2566                 if (cli->pwd.null_pwd)
2567                 {
2568                         /* attempt null session */
2569                         passwd[0] = 0;
2570                         pass_len = 1;
2571                 }
2572                 else
2573                 {
2574                         /* attempt clear-text session */
2575                         pwd_get_cleartext(&(cli->pwd), passwd);
2576                         pass_len = strlen(passwd);
2577                 }
2578
2579                 /* attempt clear-text session */
2580                 if (!cli_session_setup(cli, cli->user_name,
2581                                passwd, pass_len,
2582                                NULL, 0,
2583                                cli->domain))
2584                 {
2585                         DEBUG(1,("failed session setup\n"));
2586                         if (do_shutdown)
2587                         {
2588                                 cli_shutdown(cli);
2589                         }
2590                         return False;
2591                 }
2592                 if (do_tcon)
2593                 {
2594                         if (!cli_send_tconX(cli, service, service_type,
2595                                             (char*)passwd, strlen(passwd)))
2596                         {
2597                                 DEBUG(1,("failed tcon_X\n"));
2598                                 if (do_shutdown)
2599                                 {
2600                                         cli_shutdown(cli);
2601                                 }
2602                                 return False;
2603                         }
2604                 }
2605         }
2606         else
2607         {
2608                 /* attempt encrypted session */
2609                 unsigned char nt_sess_pwd[24];
2610                 unsigned char lm_sess_pwd[24];
2611
2612                 /* creates (storing a copy of) and then obtains a 24 byte password OWF */
2613                 pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
2614                 pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
2615
2616                 /* attempt encrypted session */
2617                 if (!cli_session_setup(cli, cli->user_name,
2618                                (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
2619                                (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
2620                                cli->domain))
2621                 {
2622                         DEBUG(1,("failed session setup\n"));
2623                         if (do_shutdown)
2624               cli_shutdown(cli);
2625                         return False;
2626                 }
2627
2628                 if (do_tcon)
2629                 {
2630                         if (!cli_send_tconX(cli, service, service_type,
2631                                             (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
2632                         {
2633                                 DEBUG(1,("failed tcon_X\n"));
2634                                 if (do_shutdown)
2635                   cli_shutdown(cli);
2636                                 return False;
2637                         }
2638                 }
2639         }
2640
2641         if (do_shutdown)
2642       cli_shutdown(cli);
2643
2644         return True;
2645 }
2646
2647
2648 /****************************************************************************
2649   cancel a print job
2650   ****************************************************************************/
2651 int cli_printjob_del(struct cli_state *cli, int job)
2652 {
2653         char *rparam = NULL;
2654         char *rdata = NULL;
2655         char *p;
2656         int rdrcnt,rprcnt, ret = -1;
2657         pstring param;
2658
2659         bzero(param,sizeof(param));
2660
2661         p = param;
2662         SSVAL(p,0,81);          /* DosPrintJobDel() */
2663         p += 2;
2664         pstrcpy(p,"W");
2665         p = skip_string(p,1);
2666         pstrcpy(p,"");
2667         p = skip_string(p,1);
2668         SSVAL(p,0,job);     
2669         p += 2;
2670         
2671         if (cli_api(cli, 
2672                     param, PTR_DIFF(p,param), 1024,  /* Param, length, maxlen */
2673                     NULL, 0, CLI_BUFFER_SIZE,            /* data, length, maxlen */
2674                     &rparam, &rprcnt,                /* return params, length */
2675                     &rdata, &rdrcnt)) {               /* return data, length */
2676                 ret = SVAL(rparam,0);
2677         }
2678
2679         if (rparam) free(rparam);
2680         if (rdata) free(rdata);
2681
2682         return ret;
2683 }
2684
2685
2686 /****************************************************************************
2687 call fn() on each entry in a print queue
2688 ****************************************************************************/
2689 int cli_print_queue(struct cli_state *cli, 
2690                     void (*fn)(struct print_job_info *))
2691 {
2692         char *rparam = NULL;
2693         char *rdata = NULL;
2694         char *p;
2695         int rdrcnt, rprcnt;
2696         pstring param;
2697         int result_code=0;
2698         int i = -1;
2699         
2700         bzero(param,sizeof(param));
2701
2702         p = param;
2703         SSVAL(p,0,76);         /* API function number 76 (DosPrintJobEnum) */
2704         p += 2;
2705         pstrcpy(p,"zWrLeh");   /* parameter description? */
2706         p = skip_string(p,1);
2707         pstrcpy(p,"WWzWWDDzz");  /* returned data format */
2708         p = skip_string(p,1);
2709         pstrcpy(p,cli->share);    /* name of queue */
2710         p = skip_string(p,1);
2711         SSVAL(p,0,2);   /* API function level 2, PRJINFO_2 data structure */
2712         SSVAL(p,2,1000); /* size of bytes of returned data buffer */
2713         p += 4;
2714         pstrcpy(p,"");   /* subformat */
2715         p = skip_string(p,1);
2716
2717         DEBUG(4,("doing cli_print_queue for %s\n", cli->share));
2718
2719         if (cli_api(cli, 
2720                     param, PTR_DIFF(p,param), 1024,  /* Param, length, maxlen */
2721                     NULL, 0, CLI_BUFFER_SIZE,            /* data, length, maxlen */
2722                     &rparam, &rprcnt,                /* return params, length */
2723                     &rdata, &rdrcnt)) {               /* return data, length */
2724                 int converter;
2725                 result_code = SVAL(rparam,0);
2726                 converter = SVAL(rparam,2);       /* conversion factor */
2727
2728                 if (result_code == 0) {
2729                         struct print_job_info job;
2730                         
2731                         p = rdata; 
2732
2733                         for (i = 0; i < SVAL(rparam,4); ++i) {
2734                                 job.id = SVAL(p,0);
2735                                 job.priority = SVAL(p,2);
2736                                 fstrcpy(job.user,
2737                                         fix_char_ptr(SVAL(p,4), converter, 
2738                                                      rdata, rdrcnt));
2739                                 job.t = make_unix_date3(p + 12);
2740                                 job.size = IVAL(p,16);
2741                                 fstrcpy(job.name,fix_char_ptr(SVAL(p,24), 
2742                                                               converter, 
2743                                                               rdata, rdrcnt));
2744                                 fn(&job);                               
2745                                 p += 28;
2746                         }
2747                 }
2748         }
2749
2750         /* If any parameters or data were returned, free the storage. */
2751         if(rparam) free(rparam);
2752         if(rdata) free(rdata);
2753
2754         return i;
2755 }