/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Inter-process communication and named pipe handling
   Copyright (C) Andrew Tridgell 1992-1995
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
/*
   This file handles the named pipe and mailslot calls
   in the SMBtrans protocol
   */

#include "includes.h"
#include "loadparm.h"
#include "pcap.h"

#ifdef CHECK_TYPES
#undef CHECK_TYPES
#endif
#define CHECK_TYPES 0

extern int DEBUGLEVEL;
extern int maxxmit;
extern files_struct Files[];

extern fstring local_machine;
extern BOOL lpq_cache_reset;

#define NERR_Success 0
#define NERR_badpass 86
#define NERR_notsupported 50

#define NERR_BASE (2100)
#define NERR_BufTooSmall (NERR_BASE+23)
#define NERR_JobNotFound (NERR_BASE+51)
#define NERR_DestNotFound (NERR_BASE+52)
#define ERROR_INVALID_LEVEL 124
#define ERROR_MORE_DATA 234

#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))

#define ACCESS_READ 0x01
#define ACCESS_WRITE 0x02
#define ACCESS_CREATE 0x04

#define STYPE_DISKTREE	0	/* Disk drive */
#define STYPE_PRINTQ	1	/* Spooler queue */
#define STYPE_DEVICE	2	/* Serial device */
#define STYPE_IPC	3	/* Interprocess communication (IPC) */

#define SHPWLEN 8		/* share password length */
#define NNLEN 12		/* 8.3 net name length */
#define SNLEN 15		/* service name length */
#define QNLEN 12		/* queue name maximum length */

static int CopyExpanded(int cnum, int snum, char** dst, char* src, int* n)
{
  pstring buf;
  int l;
  strcpy(buf,src);
  string_sub(buf,"%S",lp_servicename(snum));
  standard_sub(cnum,buf);
  StrnCpy(*dst,buf,*n);
  l = strlen(*dst) + 1;
  *dst += l;
  *n -= l;
  return l;
}

static int CopyAndAdvance(char** dst, char* src, int* n)
{
  int l;
  StrnCpy(*dst,src,*n);
  l = strlen(*dst) + 1;
  *dst += l;
  *n -= l;
  return l;
}

static int StrlenExpanded(int cnum, int snum, char* s)
{
  pstring buf;
  strcpy(buf,s);
  string_sub(buf,"%S",lp_servicename(snum));
  standard_sub(cnum,buf);
  return strlen(buf) + 1;
}

static char* Expand(int cnum, int snum, char* s)
{
  static pstring buf;
  strcpy(buf,s);
  string_sub(buf,"%S",lp_servicename(snum));
  standard_sub(cnum,buf);
  return buf;
}

/****************************************************************************
  send a trans reply
  ****************************************************************************/
static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup,
			     int ldata,int lparam,int lsetup)
{
  int i;
  int this_ldata,this_lparam;
  int tot_data=0,tot_param=0;
  int align;

  this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */
  this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam));

  align = (this_lparam%4);

  set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True);
  if (this_lparam)
    memcpy(smb_buf(outbuf),param,this_lparam);
  if (this_ldata)
    memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata);

  SSVAL(outbuf,smb_vwv0,lparam);
  SSVAL(outbuf,smb_vwv1,ldata);
  SSVAL(outbuf,smb_vwv3,this_lparam);
  SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
  SSVAL(outbuf,smb_vwv5,0);
  SSVAL(outbuf,smb_vwv6,this_ldata);
  SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
  SSVAL(outbuf,smb_vwv8,0);
  SSVAL(outbuf,smb_vwv9,lsetup);
  for (i=0;i<lsetup;i++)
    SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]);

  show_msg(outbuf);
  send_smb(outbuf);

  tot_data = this_ldata;
  tot_param = this_lparam;

  while (tot_data < ldata || tot_param < lparam)
    {
      this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */
      this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam));

      align = (this_lparam%4);

      set_message(outbuf,10,this_ldata+this_lparam+align,False);
      if (this_lparam)
	memcpy(smb_buf(outbuf),param+tot_param,this_lparam);
      if (this_ldata)
	memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata);

      SSVAL(outbuf,smb_vwv3,this_lparam);
      SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
      SSVAL(outbuf,smb_vwv5,tot_param);
      SSVAL(outbuf,smb_vwv6,this_ldata);
      SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
      SSVAL(outbuf,smb_vwv8,tot_data);
      SSVAL(outbuf,smb_vwv9,0);

      show_msg(outbuf);
      send_smb(outbuf);

      tot_data += this_ldata;
      tot_param += this_lparam;
    }
}



/****************************************************************************
  get a print queue
  ****************************************************************************/

struct pack_desc {
  char* format;			/* formatstring for structure */
  char* subformat;		/* subformat for structure */
  char* base;			/* baseaddress of buffer */
  int buflen;			/* remaining size for fixed part; on init: length of base */
  int subcount;			/* count of substructures */
  char* structbuf;		/* pointer into buffer for remaining fixed part */
  int stringlen;		/* remaining size for variable part */		
  char* stringbuf;		/* pointer into buffer for remaining variable part */
  int neededlen;		/* total needed size */
  int usedlen;			/* total used size (usedlen <= neededlen and usedlen <= buflen) */
  char* curpos;			/* current position; pointer into format or subformat */
  int errcode;
};

static int get_counter(char** p)
{
  int i, n;
  if (!isdigit(**p)) return 1;
  for (n = 0;;) {
    i = **p;
    if (isdigit(i))
      n = 10 * n + (i - '0');
    else
      return n;
    (*p)++;
  }
}

static int getlen(char* p)
{
  int n = 0;
  while (*p) {
    switch( *p++ ) {
    case 'W':			/* word (2 byte) */
      n += 2;
      break;
    case 'N':			/* count of substructures (word) at end */
      n += 2;
      break;
    case 'D':			/* double word (4 byte) */
    case 'z':			/* offset to zero terminated string (4 byte) */
    case 'l':			/* offset to user data (4 byte) */
      n += 4;
      break;
    case 'b':			/* offset to data (with counter) (4 byte) */
      n += 4;
      get_counter(&p);
      break;
    case 'B':			/* byte (with optional counter) */
      n += get_counter(&p);
      break;
    }
  }
  return n;
}

static void init_package(struct pack_desc* p, int count, int subcount)
{
  int n = p->buflen;
  int i = count * getlen(p->format);
  if (p->subformat) i += subcount * getlen(p->subformat);
  p->structbuf = p->base;
  p->neededlen = 0;
  p->usedlen = 0;
  p->subcount = 0;
  p->curpos = p->format;
  if (i > n) {
    i = n = 0;
    p->errcode = NERR_BufTooSmall;
  }
  else
    p->errcode = NERR_Success;
  p->buflen = i;
  n -= i;
  p->stringbuf = p->base + i;
  p->stringlen = n;
}

#ifdef __STDC__
static int package(struct pack_desc* p, ...)
{
#else
static int package(va_alist)
va_dcl
{
  struct pack_desc* p;
#endif
  va_list args;
  int needed=0, stringneeded;
  char* str=NULL;
  int is_string=0, stringused;
  int32 temp;

#ifdef __STDC__
  va_start(args,p);
#else
  va_start(args);
  p = va_arg(args,struct pack_desc *);
#endif

  if (!*p->curpos) {
    if (!p->subcount)
      p->curpos = p->format;
    else {
      p->curpos = p->subformat;
      p->subcount--;
    }
  }
#if CHECK_TYPES
  str = va_arg(args,char*);
  if (strncmp(str,p->curpos,strlen(str)) != 0) {
    DEBUG(2,("type error in package: %s instead of %*s\n",str,
 	     strlen(str),p->curpos));
    va_end(args);
#if AJT
    ajt_panic();
#endif  
    return 0;
  }
#endif
  stringneeded = -1;
  switch( *p->curpos++ ) {
  case 'W':			/* word (2 byte) */
    needed = 2;
    temp = va_arg(args,int16);
    if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
    break;
  case 'N':			/* count of substructures (word) at end */
    needed = 2;
    p->subcount = va_arg(args,int16);
    if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
    break;
  case 'D':			/* double word (4 byte) */
    needed = 4;
    temp = va_arg(args,int32);
    if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
    break;
  case 'B':			/* byte (with optional counter) */
    needed = get_counter(&p->curpos);
    if (p->buflen >= needed) StrnCpy(p->structbuf,va_arg(args,char*),needed);
    break;
  case 'z':			/* offset to zero terminated string (4 byte) */
    str = va_arg(args,char*);
    stringneeded = (str ? strlen(str)+1 : 0);
    is_string = 1;
    break;
  case 'l':			/* offset to user data (4 byte) */
    str = va_arg(args,char*);
    stringneeded = va_arg(args,int);
    is_string = 0;
    break;
  case 'b':			/* offset to data (with counter) (4 byte) */
    str = va_arg(args,char*);
    stringneeded = get_counter(&p->curpos);
    is_string = 0;
    break;
  }
  va_end(args);
  if (stringneeded >= 0) {
    needed = 4;
    if (p->buflen >= needed) {
      stringused = stringneeded;
      if (stringused > p->stringlen) {
	stringused = (is_string ? p->stringlen : 0);
	if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA;
      }
      if (!stringused)
	SIVAL(p->structbuf,0,0);
      else {
	SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
	memcpy(p->stringbuf,str,stringused);
	if (is_string) p->stringbuf[stringused-1] = '\0';
	p->stringbuf += stringused;
	p->stringlen -= stringused;
	p->usedlen += stringused;
      }
    }
    p->neededlen += stringneeded;
  }
  p->neededlen += needed;
  if (p->buflen >= needed) {
    p->structbuf += needed;
    p->buflen -= needed;
    p->usedlen += needed;
  }
  else {
    if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall;
  }
  return 1;
}

#if CHECK_TYPES
#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
#else
#define PACK(desc,t,v) package(desc,v)
#define PACKl(desc,t,v,l) package(desc,v,l)
#endif

static void PackDriverData(struct pack_desc* desc)
{
  char drivdata[4+4+32];
  SIVAL(drivdata,0,sizeof drivdata); /* cb */
  SIVAL(drivdata,4,1000);	/* lVersion */
  memset(drivdata+8,0,32);	/* szDeviceName */
  strcpy(drivdata+8,"NULL");
  PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
}

static int check_printq_info(struct pack_desc* desc,
 			     int uLevel, const char* id1, const char* id2)
{
  desc->subformat = NULL;
  switch( uLevel ) {
  case 0:
    desc->format = "B13";
    break;
  case 1:
    desc->format = "B13BWWWzzzzzWW";
    break;
  case 2:
    desc->format = "B13BWWWzzzzzWN";
    desc->subformat = "WB21BB16B10zWWzDDz";
    break;
  case 3:
    desc->format = "zWWWWzzzzWWzzl";
    break;
  case 4:
    desc->format = "zWWWWzzzzWNzzl";
    desc->subformat = "WWzWWDDzz";
    break;
  case 5:
    desc->format = "z";
    break;
  default: return False;
  }
  if (strcmp(desc->format,id1) != 0) return False;
  if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
  return True;
}

static void fill_printjob_info(int cnum, int snum, int uLevel,
			       struct pack_desc* desc,
			       print_queue_struct* queue, int n)
{
  PACK(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */
  if (uLevel == 1) {
    PACK(desc,"B21",queue->user); /* szUserName */
    PACK(desc,"B","");		/* pad */
    PACK(desc,"B16","");	/* szNotifyName */
    PACK(desc,"B10","PM_Q_RAW"); /* szDataType */
    PACK(desc,"z","");		/* pszParms */
    PACK(desc,"W",n+1);		/* uPosition */
    PACK(desc,"W",queue->status); /* fsStatus */
    PACK(desc,"z","");		/* pszStatus */
    PACK(desc,"D",queue->time); /* ulSubmitted */
    PACK(desc,"D",queue->size); /* ulSize */
    PACK(desc,"z",queue->file); /* pszComment */
  }
  if (uLevel == 2 || uLevel == 3) {
    PACK(desc,"W",50);		/* uPriority */
    PACK(desc,"z",queue->user); /* pszUserName */
    PACK(desc,"W",n+1);		/* uPosition */
    PACK(desc,"W",queue->status); /* fsStatus */
    PACK(desc,"D",queue->time); /* ulSubmitted */
    PACK(desc,"D",queue->size); /* ulSize */
    PACK(desc,"z","Samba");	/* pszComment */
    PACK(desc,"z",queue->file); /* pszDocument */
    if (uLevel == 3) {
      PACK(desc,"z","");	/* pszNotifyName */
      PACK(desc,"z","PM_Q_RAW"); /* pszDataType */
      PACK(desc,"z","");	/* pszParms */
      PACK(desc,"z","");	/* pszStatus */
      PACK(desc,"z",SERVICE(snum)); /* pszQueue */
      PACK(desc,"z","lpd");	/* pszQProcName */
      PACK(desc,"z","");	/* pszQProcParms */
      PACK(desc,"z","NULL"); /* pszDriverName */
      PackDriverData(desc);	/* pDriverData */
      PACK(desc,"z","");	/* pszPrinterName */
    }
  }
}

static void fill_printq_info(int cnum, int snum, int uLevel,
 			     struct pack_desc* desc,
 			     int count, print_queue_struct* queue,
 			     print_status_struct* status)
{
  if (uLevel < 3)
    PACK(desc,"B13",SERVICE(snum));
  else
    PACK(desc,"z",Expand(cnum,snum,SERVICE(snum)));
  if (uLevel == 1 || uLevel == 2) {
    PACK(desc,"B","");		/* alignment */
    PACK(desc,"W",5);		/* priority */
    PACK(desc,"W",0);		/* start time */
    PACK(desc,"W",0);		/* until time */
    PACK(desc,"z","");		/* pSepFile */
    PACK(desc,"z","lpd");	/* pPrProc */
    PACK(desc,"z",SERVICE(snum)); /* pDestinations */
    PACK(desc,"z",0);		/* pParms */
    if (snum < 0) {
      PACK(desc,"z","UNKNOWN PRINTER");
      PACK(desc,"W",LPSTAT_ERROR);
    }
    else if (!status->message[0]) {
      PACK(desc,"z",Expand(cnum,snum,lp_comment(snum)));
      PACK(desc,"W",LPSTAT_OK); /* status */
    }
    else {
      PACK(desc,"z",status->message);
      PACK(desc,"W",status->status); /* status */
    }
    PACK(desc,(uLevel == 1 ? "W" : "N"),count);
  }
  if (uLevel == 3 || uLevel == 4) {
    PACK(desc,"W",5);		/* uPriority */
    PACK(desc,"W",0);		/* uStarttime */
    PACK(desc,"W",0);		/* uUntiltime */
    PACK(desc,"W",5);		/* pad1 */
    PACK(desc,"z","");		/* pszSepFile */
    PACK(desc,"z","lpd");	/* pszPrProc */
    PACK(desc,"z","");		/* pszParms */
    if (!status->message[0]) {
      PACK(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */
      PACK(desc,"W",LPSTAT_OK); /* fsStatus */
    }
    else {
      PACK(desc,"z",status->message); /* pszComment */
      PACK(desc,"W",status->status); /* fsStatus */
    }
    PACK(desc,(uLevel == 3 ? "W" : "N"),count);	/* cJobs */
    PACK(desc,"z",SERVICE(snum)); /* pszPrinters */
    PACK(desc,"z","NULL");	/* pszDriverName */
    PackDriverData(desc);	/* pDriverData */
  }
  if (uLevel == 2 || uLevel == 4) {
    int i;
    for (i=0;i<count;i++)
      fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
  }
 
  DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count));
}

static BOOL api_DosPrintQGetInfo(int cnum,char *param,char *data,
				 int mdrcnt,int mprcnt,
				 char **rdata,char **rparam,
				 int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  char *QueueName = p;
  int uLevel,cbBuf;
  int count;
  int snum;
  char* str3;
  struct pack_desc desc;
  print_queue_struct *queue=NULL;
  print_status_struct status;
  
  bzero(&status,sizeof(status));
 
  p = skip_string(p,1);
  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);
  str3 = p + 4;
 
  if ((p = strchr(QueueName,'%'))) *p = 0;
 
  DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName));
 
  /* check it's a supported varient */
  if (strcmp(str1,"zWrLh") != 0) return False;
  if (!check_printq_info(&desc,uLevel,str2,str3)) return False;
 
  snum = lp_servicenumber(QueueName);
  if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
    int pnum = lp_servicenumber(PRINTERS_NAME);
    if (pnum >= 0) {
      lp_add_printer(QueueName,pnum);
      snum = lp_servicenumber(QueueName);
    }
  }
  
  count = get_printqueue(snum,cnum,&queue,&status);
  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,1,count);
  desc.subcount = count;
  fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status);
  *rdata_len = desc.usedlen;
  
  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,desc.neededlen);
  
  DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));

  if (queue) free(queue);
  
  return(True);
}


/****************************************************************************
  view list of all print jobs on all queues
  ****************************************************************************/
static BOOL api_DosPrintQEnum(int cnum, char* param, char* data,
 			      int mdrcnt, int mprcnt,
 			      char **rdata, char** rparam,
 			      int *rdata_len, int *rparam_len)
{
  char *param_format = param+2;
  char *output_format1 = skip_string(param_format,1);
  char *p = skip_string(output_format1,1);
  int uLevel = SVAL(p,0);
  char *output_format2 = p + 4;
  int services = lp_numservices();
  int i, n;
  struct pack_desc desc;
  print_queue_struct **queue = NULL;
  print_status_struct *status = NULL;
  int* subcntarr = NULL;
  int queuecnt, subcnt=0, succnt;
 
  DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
 
  if (strcmp(param_format,"WrLeh") != 0) return False;
  if (!check_printq_info(&desc,uLevel,output_format1,output_format2))
    return False;
  queuecnt = 0;
  for (i = 0; i < services; i++)
    if (lp_print_ok(i) && lp_browseable(i))
      queuecnt++;
  if (uLevel > 0) {
    queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*));
    memset(queue,0,queuecnt*sizeof(print_queue_struct*));
    status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct));
    memset(status,0,queuecnt*sizeof(print_status_struct));
    subcntarr = (int*)malloc(queuecnt*sizeof(int));
    subcnt = 0;
    n = 0;
    for (i = 0; i < services; i++)
      if (lp_print_ok(i) && lp_browseable(i)) {
 	subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]);
 	subcnt += subcntarr[n];
 	n++;
      }
  }
  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,queuecnt,subcnt);
  n = 0;
  succnt = 0;
  for (i = 0; i < services; i++)
    if (lp_print_ok(i) && lp_browseable(i)) {
      fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
      n++;
      if (desc.errcode == NERR_Success) succnt = n;
    }
  if (subcntarr) free(subcntarr);
 
  *rdata_len = desc.usedlen;
  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,queuecnt);
  
  for (i = 0; i < queuecnt; i++) {
    if (queue && queue[i]) free(queue[i]);
  }

  if (queue) free(queue);
  if (status) free(status);
  
  return True;
}

/****************************************************************************
  get info about a share
  ****************************************************************************/

static int check_share_info(int uLevel, const char* id)
{
  switch( uLevel ) {
  case 0:
    if (strcmp(id,"B13") != 0) return False;
    break;
  case 1:
    if (strcmp(id,"B13BWz") != 0) return False;
    break;
  case 2:
    if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
    break;
  case 91:
    if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
    break;
  default: return False;
  }
  return True;
}

static int fill_share_info(int cnum, int snum, int uLevel,
 			   char** buf, int* buflen,
 			   char** stringbuf, int* stringspace, char* baseaddr)
{
  int struct_len;
  char* p;
  char* p2;
  int l2;
  int len;
 
  switch( uLevel ) {
  case 0: struct_len = 13; break;
  case 1: struct_len = 20; break;
  case 2: struct_len = 40; break;
  case 91: struct_len = 68; break;
  default: return -1;
  }
  
 
  if (!buf)
    {
      len = 0;
      if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum));
      if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
      if (buflen) *buflen = struct_len;
      if (stringspace) *stringspace = len;
      return struct_len + len;
    }
  
  len = struct_len;
  p = *buf;
  if (*buflen < struct_len) return -1;
  if (stringbuf)
    {
      p2 = *stringbuf;
      l2 = *stringspace;
    }
  else
    {
      p2 = p + struct_len;
      l2 = *buflen - struct_len;
    }
  if (!baseaddr) baseaddr = p;
  
  StrnCpy(p,lp_servicename(snum),13);
  
  if (uLevel > 0)
    {
      int type;
      CVAL(p,13) = 0;
      type = STYPE_DISKTREE;
      if (lp_print_ok(snum)) type = STYPE_PRINTQ;
      if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC;
      SSVAL(p,14,type);		/* device type */
      SIVAL(p,16,PTR_DIFF(p2,baseaddr));
      len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2);
    }
  
  if (uLevel > 1)
    {
      SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
      SSVAL(p,22,-1);		/* max uses */
      SSVAL(p,24,1); /* current uses */
      SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
      len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
      memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
    }
  
  if (uLevel > 2)
    {
      memset(p+40,0,SHPWLEN+2);
      SSVAL(p,50,0);
      SIVAL(p,52,0);
      SSVAL(p,56,0);
      SSVAL(p,58,0);
      SIVAL(p,60,0);
      SSVAL(p,64,0);
      SSVAL(p,66,0);
    }
       
  if (stringbuf)
    {
      *buf = p + struct_len;
      *buflen -= struct_len;
      *stringbuf = p2;
      *stringspace = l2;
    }
  else
    {
      *buf = p2;
      *buflen -= len;
    }
  return len;
}

static BOOL api_RNetShareGetInfo(int cnum,char *param,char *data,
				 int mdrcnt,int mprcnt,
				 char **rdata,char **rparam,
				 int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *netname = skip_string(str2,1);
  char *p = skip_string(netname,1);
  int uLevel = SVAL(p,0);
  int snum = find_service(netname);
  
  if (snum < 0) return False;
  
  /* check it's a supported varient */
  if (strcmp(str1,"zWrLh") != 0) return False;
  if (!check_share_info(uLevel,str2)) return False;
 
  *rdata = REALLOC(*rdata,mdrcnt);
  p = *rdata;
  *rdata_len = fill_share_info(cnum,snum,uLevel,&p,&mdrcnt,0,0,0);
  if (*rdata_len < 0) return False;
 
  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */
  SSVAL(*rparam,4,*rdata_len);
 
  return(True);
}

/****************************************************************************
  view list of shares available
  ****************************************************************************/
static BOOL api_RNetShareEnum(int cnum,char *param,char *data,
  			      int mdrcnt,int mprcnt,
  			      char **rdata,char **rparam,
  			      int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel = SVAL(p,0);
  int buf_len = SVAL(p,2);
  char *p2;
  int count=lp_numservices();
  int total=0,counted=0;
  int i;
  int data_len, fixed_len, string_len;
  int f_len, s_len;
 
  if (strcmp(str1,"WrLeh") != 0) return False;
  if (!check_share_info(uLevel,str2)) return False;
  
  data_len = fixed_len = string_len = 0;
  for (i=0;i<count;i++)
    if (lp_browseable(i))
      {
  	total++;
 	data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0);
 	if (data_len <= buf_len)
 	  {
 	    counted++;
 	    fixed_len += f_len;
 	    string_len += s_len;
 	  }
      }
  *rdata_len = fixed_len + string_len;
  *rdata = REALLOC(*rdata,*rdata_len);
  memset(*rdata,0,*rdata_len);
  
  p2 = (*rdata) + fixed_len;	/* auxillery data (strings) will go here */
  p = *rdata;
  f_len = fixed_len;
  s_len = string_len;
  for (i = 0; i < count;i++)
    if (lp_browseable(i))
      if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0)
 	break;
  
  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,counted);
  SSVAL(*rparam,6,total);
  
  DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
 	   counted,total,uLevel,
  	   buf_len,*rdata_len,mdrcnt));
  return(True);
}



/****************************************************************************
  get the time of day info
  ****************************************************************************/
static BOOL api_NetRemoteTOD(int cnum,char *param,char *data,
			     int mdrcnt,int mprcnt,
			     char **rdata,char **rparam,
			     int *rdata_len,int *rparam_len)
{
  char *p;
  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 21;
  *rdata = REALLOC(*rdata,*rdata_len);

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  p = *rdata;

  {
    struct tm *t;
    time_t unixdate = time(NULL) + GMT_TO_LOCAL*TimeDiff(0);

    t = LocalTime(&unixdate,0);

    SIVAL(p,0,unixdate); 
    SIVAL(p,4,0);		/* msecs ? */
    CVAL(p,8) = t->tm_hour;
    CVAL(p,9) = t->tm_min;
    CVAL(p,10) = t->tm_sec;
    CVAL(p,11) = 0;		/* hundredths of seconds */
    SSVAL(p,12,TimeDiff(0)/60); /* timezone in minutes from GMT */
    SSVAL(p,14,10000);		/* timer interval in 0.0001 of sec */
    CVAL(p,16) = t->tm_mday;
    CVAL(p,17) = t->tm_mon + 1;
    SSVAL(p,18,1900+t->tm_year);
    CVAL(p,20) = t->tm_wday;
  }


  return(True);
}

/****************************************************************************
  set the user password
  ****************************************************************************/
static BOOL api_SetUserPassword(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *p = skip_string(param+2,2);
  fstring user;
  fstring pass1,pass2;

  strcpy(user,p);

  p = skip_string(p,1);

  StrnCpy(pass1,p,16);
  StrnCpy(pass2,p+16,16);

  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  DEBUG(3,("Set password for <%s>\n",user));

  if (!password_ok(user,pass1,strlen(pass1),NULL) || !chgpasswd(user,pass1,pass2))
    SSVAL(*rparam,0,NERR_badpass);

  bzero(pass1,sizeof(fstring));
  bzero(pass2,sizeof(fstring));	 
	 
  return(True);
}

/****************************************************************************
  delete a print job
  Form: <W> <> 
  ****************************************************************************/
static BOOL api_RDosPrintJobDel(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
				   by the print queue api */
  int snum = (SVAL(p,0)>>8);  

  lpq_cache_reset = True;

  /* check it's a supported varient */
  if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
    return(False);

  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  if (snum >= 0)
    {
      print_queue_struct *queue=NULL;
      int i;
      int count = get_printqueue(snum,cnum,&queue,NULL);

      for (i=0;i<count;i++)
	if ((queue[i].job%0xFF) == jobid)
	  {
	    DEBUG(3,("Deleting queue entry %d\n",queue[i].job));
	    del_printqueue(cnum,snum,queue[i].job);
	  }

      if (queue) free(queue);
    }

  DEBUG(3,("Print job delete job=%d snum=%s\n",jobid,snum));

  return(True);
}

static BOOL api_WPrintQueuePurge(int cnum,char *param,char *data,
				 int mdrcnt,int mprcnt,
				 char **rdata,char **rparam,
				 int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *QueueName = skip_string(str2,1);
  int snum;

  lpq_cache_reset = True;

  /* check it's a supported varient */
  if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
    return(False);

  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  snum = lp_servicenumber(QueueName);
  if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
    int pnum = lp_servicenumber(PRINTERS_NAME);
    if (pnum >= 0) {
      lp_add_printer(QueueName,pnum);
      snum = lp_servicenumber(QueueName);
    }
  }

  if (snum >= 0) {
    print_queue_struct *queue=NULL;
    int i;
    int count = get_printqueue(snum,cnum,&queue,NULL);

    for (i = 0; i < count; i++)
      del_printqueue(cnum,snum,queue[i].job);

    if (queue) free(queue);
  }

  DEBUG(3,("Print queue purge, queue=%s\n",QueueName));

  return(True);
}


/****************************************************************************
  set the name of a print job (undocumented?)
  ****************************************************************************/
static BOOL api_PrintJobInfo(int cnum,char *param,char *data,
			     int mdrcnt,int mprcnt,
			     char **rdata,char **rparam,
			     int *rdata_len,int *rparam_len)
{
  pstring name;
  char *s = data;
  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  lpq_cache_reset = True;

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  /* now try to extract the name. As I don't really understand this
     API this is mostly guesswork.
     */
  if (isalpha(*s))
    {
      int i,l = 0;
      while (l<64 && *s)
	{
	  if (isalnum(*s) || strchr("-._",*s))
	    name[l++] = *s;
	  s++;
	}      
      name[l] = 0;

      DEBUG(3,("Setting print name to %s\n",name));
      
      for (i=0;i<MAX_OPEN_FILES;i++)
	if (Files[i].open && Files[i].print_file)
	  {
	    pstring wd;
	    GetWd(wd);
	    unbecome_user();

	    if (!become_user(Files[i].cnum) || !become_service(Files[i].cnum,True))
	      break;

	    if (rename(Files[i].name,name) == 0)
	      string_set(&Files[i].name,name);
	    break;
	  }
    }

  return(True);
}


/****************************************************************************
  get info about the server
  ****************************************************************************/

#define SV_TYPE_WORKSTATION	0x00000001
#define SV_TYPE_SERVER		0x00000002
#define SV_TYPE_SQLSERVER	0x00000004
#define SV_TYPE_DOMAIN_CTRL	0x00000008
#define SV_TYPE_DOMAIN_BAKCTRL	0x00000010
#define SV_TYPE_TIME_SOURCE	0x00000020
#define SV_TYPE_AFP		0x00000040
#define SV_TYPE_NOVELL		0x00000080

static BOOL api_RNetServerGetInfo(int cnum,char *param,char *data,
				  int mdrcnt,int mprcnt,
				  char **rdata,char **rparam,
				  int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel = SVAL(p,0);
  char *p2;
  int struct_len;

  /* check it's a supported varient */
  if (strcmp(str1,"WrLh") != 0) return False;
  switch( uLevel ) {
  case 0:
    if (strcmp(str2,"B16") != 0) return False;
    struct_len = 16;
    break;
  case 1:
    if (strcmp(str2,"B16BBDz") != 0) return False;
    struct_len = 26;
    break;
  case 2:
    if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
	!= 0) return False;
    struct_len = 134;
    break;
  case 3:
    if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
	!= 0) return False;
    struct_len = 144;
    break;
  case 20:
    if (strcmp(str2,"DN") != 0) return False;
    struct_len = 6;
    break;
  case 50:
    if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
    struct_len = 42;
    break;
  default: return False;
  }

  *rdata_len = mdrcnt;
  *rdata = REALLOC(*rdata,*rdata_len);

  p = *rdata;
  p2 = p + struct_len;
  if (uLevel != 20) StrnCpy(p,local_machine,16);
  p += 16;
  if (uLevel > 0)
    {
      pstring comment;
      SCVAL(p,0,1);		/* version_major */
      SCVAL(p,1,9);		/* version_minor */
      SIVAL(p,2,SV_TYPE_WORKSTATION|SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE
	    /*|SV_TYPE_DOMAIN_CTRL*/); /* type */
      SIVAL(p,6,PTR_DIFF(p2,*rdata));
      strcpy(comment,lp_serverstring());
      standard_sub(cnum,comment);
      StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
      p2 = skip_string(p2,1);
    }
  if (uLevel > 1)
    {
      return False;		/* not yet implemented */
    }

  *rdata_len = PTR_DIFF(p2,*rdata);

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */
  SSVAL(*rparam,4,*rdata_len);

  return(True);
}


/****************************************************************************
  get info about the server
  ****************************************************************************/
static BOOL api_NetWkstaGetInfo(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  char *p2;
  extern pstring sesssetup_user;
  int level = SVAL(p,0);

  DEBUG(4,("NetWkstaGetInfo level %d\n",level));

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);

  /* check it's a supported varient */
  if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
    return(False);

  *rdata_len = mdrcnt + 1024;
  *rdata = REALLOC(*rdata,*rdata_len);

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  p = *rdata;
  p2 = p + 22;

  SIVAL(p,0,PTR_DIFF(p2,*rdata));
  strcpy(p2,local_machine);
  p2 = skip_string(p2,1);
  p += 4;

  SIVAL(p,0,PTR_DIFF(p2,*rdata));
  strcpy(p2,sesssetup_user);
  p2 = skip_string(p2,1);
  p += 4;

  SIVAL(p,0,PTR_DIFF(p2,*rdata));
  strcpy(p2,lp_workgroup());
  p2 = skip_string(p2,1);
  p += 4;

  SCVAL(p,0,1);
  SCVAL(p,1,9);
  p += 2;

  SIVAL(p,0,PTR_DIFF(p2,*rdata));
  strcpy(p2,lp_workgroup());	/* login domain */
  p2 = skip_string(p2,1);
  p += 4;

  SIVAL(p,0,PTR_DIFF(p2,*rdata));
  strcpy(p2,"info 2");
  p2 = skip_string(p2,1);
  p += 4;

  *rdata_len = PTR_DIFF(p2,*rdata);

  SSVAL(*rparam,4,*rdata_len);

  return(True);
}


/****************************************************************************
  get info about a user
  ****************************************************************************/

#define USER_PRIV_GUEST 0
#define USER_PRIV_USER 1
#define USER_PRIV_ADMIN 2

static BOOL api_RNetUserGetInfo(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *UserName = skip_string(str2,1);
  char *p = skip_string(UserName,1);
  int uLevel = SVAL(p,0);
  char *p2;

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);

  /* check it's a supported varient */
  if (strcmp(str1,"zWrLh") != 0) return False;
  switch( uLevel ) {
  case 0: p2 = "B21"; break;
  case 1: p2 = "B21BB16DWzzWz"; break;
  case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
  case 10: p2 = "B21Bzzz"; break;
  case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
  default: return False;
  }
  if (strcmp(p2,str2) != 0) return False;

  *rdata_len = mdrcnt + 1024;
  *rdata = REALLOC(*rdata,*rdata_len);

  SSVAL(*rparam,0,NERR_Success);
  SSVAL(*rparam,2,0);		/* converter word */

  p = *rdata;
  p2 = p + 86;

  memset(p,0,21);
  strcpy(p,UserName);
  if (uLevel > 0) {
    SCVAL(p,21,0);
    *p2 = 0;
    if (uLevel >= 10) {
      SIVAL(p,22,PTR_DIFF(p2,*rdata)); /* comment */
      SIVAL(p,26,PTR_DIFF(p2,*rdata)); /* user_comment */
      SIVAL(p,30,PTR_DIFF(p2,*rdata)); /* full name */
      p2++;
    }
    if (uLevel == 11) {
      SSVAL(p,34,USER_PRIV_ADMIN); /* user privilege */
      SIVAL(p,36,0);		/* auth flags */
      SIVAL(p,40,0);		/* password age */
      SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
      strcpy(p2,"Y:\\GIGA\\C$\\PRJ\\USERS\\AD");
      p2 = skip_string(p2,1);
      SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* parms */
      strcpy(p2,"");
      p2 = skip_string(p2,1);
      SIVAL(p,52,0);		/* last logon */
      SIVAL(p,56,0);		/* last logoff */
      SSVAL(p,60,-1);		/* bad pw counts */
      SSVAL(p,62,-1);		/* num logons */
      SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* logon server */
      strcpy(p2,"\\\\GIGA");
      p2 = skip_string(p2,1);
      SSVAL(p,68,49);		/* country code */
      SIVAL(p,70,0);		/* workstations */

      SIVAL(p,74,-1L);		/* max storage */
      SSVAL(p,78,168);		/* units per week */
      SIVAL(p,80,PTR_DIFF(p2,*rdata)); /* logon hours */
      memset(p2,-1,21);
      p2 += 21;
      SSVAL(p,84,860);		/* code page */
    }
    if (uLevel == 1 || uLevel == 2) {
      memset(p+22,' ',16);	/* password */
      SIVAL(p,38,-1);		/* password age */
      SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */
      SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
      strcpy(p2,"\\\\__SAMBA__\\HOMES");
      p2 = skip_string(p2,1);
      SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
      *p2++ = 0;
      SSVAL(p,52,0);		/* flags */
      SIVAL(p,54,0);		/* script_path */
      if (uLevel == 2) {
	SIVAL(p,60,0);		/* auth_flags */
	SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
	strcpy(p2,"<Full Name>");
	p2 = skip_string(p2,1);
	SIVAL(p,68,0);		/* urs_comment */
	SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
	strcpy(p2,"");
	p2 = skip_string(p2,1);
	SIVAL(p,76,0);		/* workstations */
	SIVAL(p,80,0);		/* last_logon */
	SIVAL(p,84,0);		/* last_logoff */
	SIVAL(p,88,-1);		/* acct_expires */
	SIVAL(p,92,-1);		/* max_storage */
	SSVAL(p,96,168);	/* units_per_week */
	SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
	memset(p2,-1,21);
	p2 += 21;
	SSVAL(p,102,-1);	/* bad_pw_count */
	SSVAL(p,104,-1);	/* num_logons */
	SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
	strcpy(p2,"\\\\__SAMBA__");
	p2 = skip_string(p2,1);
	SSVAL(p,110,49);	/* country_code */
	SSVAL(p,112,860);	/* code page */
      }
    }
  }

  *rdata_len = PTR_DIFF(p2,*rdata);

  SSVAL(*rparam,4,*rdata_len);	/* is this right?? */

  return(True);
}

static BOOL api_WWkstaUserLogon(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel,cbBuf;
  struct pack_desc desc;
  char* name;

  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);
  name = p + 4;

  DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));

  /* check it's a supported varient */
  if (strcmp(str1,"OOWb54WrLh") != 0) return False;
  if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  desc.subformat = NULL;
  desc.format = str2;
  init_package(&desc,1,0);
  PACK(&desc,"W",0);		/* code */
  PACK(&desc,"B21",name);	/* eff. name */
  PACK(&desc,"B","");		/* pad */
  PACK(&desc,"W",2);		/* priv */
  PACK(&desc,"D",0);		/* auth flags */
  PACK(&desc,"W",0);		/* num logons */
  PACK(&desc,"W",0);		/* bad pw count */
  PACK(&desc,"D",0);		/* last logon */
  PACK(&desc,"D",0);		/* last logoff */
  PACK(&desc,"D",-1);		/* logoff time */
  PACK(&desc,"D",-1);		/* kickoff time */
  PACK(&desc,"D",0);		/* password age */
  PACK(&desc,"D",0);		/* password can change */
  PACK(&desc,"D",-1);		/* password must change */
  PACK(&desc,"z",local_machine);/* computer */
  PACK(&desc,"z",lp_workgroup());/* domain */
  PACK(&desc,"z",0);		/* script path */
  PACK(&desc,"D",0);		/* reserved */
  *rdata_len = desc.usedlen;

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,desc.neededlen);

  DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
  return(True);
}


/****************************************************************************
  api_WAccessGetUserPerms
  ****************************************************************************/
static BOOL api_WAccessGetUserPerms(int cnum,char *param,char *data,
				    int mdrcnt,int mprcnt,
				    char **rdata,char **rparam,
				    int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *user = skip_string(str2,1);
  char *resource = skip_string(user,1);

  DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));

  /* check it's a supported varient */
  if (strcmp(str1,"zzh") != 0) return False;
  if (strcmp(str2,"") != 0) return False;

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,0);		/* errorcode */
  SSVAL(*rparam,2,0);		/* converter word */
  SSVAL(*rparam,4,0x7f);	/* permission flags */

  return(True);
}

/****************************************************************************
  api_WPrintJobEnumerate
  ****************************************************************************/

static int check_printjob_info(struct pack_desc* desc,
			       int uLevel, const char* id)
{
  desc->subformat = NULL;
  switch( uLevel ) {
  case 0: desc->format = "W"; break;
  case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
  case 2: desc->format = "WWzWWDDzz"; break;
  case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
  default: return False;
  }
  if (strcmp(desc->format,id) != 0) return False;
  return True;
}

static BOOL api_WPrintJobGetInfo(int cnum,char *param,char *data,
				 int mdrcnt,int mprcnt,
				 char **rdata,char **rparam,
				 int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uJobId = SVAL(p,0);
  int uLevel,cbBuf;
  int count;
  int i;
  int snum;
  int job;
  struct pack_desc desc;
  print_queue_struct *queue=NULL;
  print_status_struct status;

  uLevel = SVAL(p,2);
  cbBuf = SVAL(p,4);

  DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,uJobId));

  /* check it's a supported varient */
  if (strcmp(str1,"WWrLh") != 0) return False;
  if (!check_printjob_info(&desc,uLevel,str2)) return False;

  snum = (unsigned int)uJobId >> 8; /*## valid serice number??*/
  job = uJobId & 0xFF;

  count = get_printqueue(snum,cnum,&queue,&status);
  for (i = 0; i < count; i++) {
    if ((queue[i].job % 0xFF) == job) break;
  }
  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,1,0);
  if (i < count) {
    fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
    *rdata_len = desc.usedlen;
  }
  else {
    desc.errcode = NERR_JobNotFound;
    *rdata_len = 0;
  }

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,desc.neededlen);

  if (queue) free(queue);

  DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
  return(True);
}

static BOOL api_WPrintJobEnumerate(int cnum,char *param,char *data,
				   int mdrcnt,int mprcnt,
				   char **rdata,char **rparam,
				   int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  char* name = p;
  int uLevel,cbBuf;
  int count;
  int i, succnt;
  int snum;
  struct pack_desc desc;
  print_queue_struct *queue=NULL;
  print_status_struct status;

  p = skip_string(p,1);
  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));

  /* check it's a supported varient */
  if (strcmp(str1,"zWrLeh") != 0) return False;
  if (uLevel > 2) return False;	/* defined only for uLevel 0,1,2 */
  if (!check_printjob_info(&desc,uLevel,str2)) return False;

  snum = lp_servicenumber(name);
  if (snum < 0 && pcap_printername_ok(name,NULL)) {
    int pnum = lp_servicenumber(PRINTERS_NAME);
    if (pnum >= 0) {
      lp_add_printer(name,pnum);
      snum = lp_servicenumber(name);
    }
  }

  count = get_printqueue(snum,cnum,&queue,&status);
  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,count,0);

  succnt = 0;
  for (i = 0; i < count; i++) {
    fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
    if (desc.errcode == NERR_Success) succnt = i+1;
  }
  *rdata_len = desc.usedlen;

  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,count);

  if (queue) free(queue);

  DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
  return(True);
}

static int check_printdest_info(struct pack_desc* desc,
				int uLevel, const char* id)
{
  desc->subformat = NULL;
  switch( uLevel ) {
  case 0: desc->format = "B9"; break;
  case 1: desc->format = "B9B21WWzW"; break;
  case 2: desc->format = "z"; break;
  case 3: desc->format = "zzzWWzzzWW"; break;
  default: return False;
  }
  if (strcmp(desc->format,id) != 0) return False;
  return True;
}

static void fill_printdest_info(int cnum, int snum, int uLevel,
				struct pack_desc* desc)
{
  char buf[100];
  strcpy(buf,SERVICE(snum));
  strupper(buf);
  if (uLevel <= 1) {
    PACK(desc,"B9",buf);	/* szName */
    if (uLevel == 1) {
      PACK(desc,"B21","");	/* szUserName */
      PACK(desc,"W",0);		/* uJobId */
      PACK(desc,"W",0);		/* fsStatus */
      PACK(desc,"z","");	/* pszStatus */
      PACK(desc,"W",0);		/* time */
    }
  }
  if (uLevel == 2 || uLevel == 3) {
    PACK(desc,"z",buf);		/* pszPrinterName */
    if (uLevel == 3) {
      PACK(desc,"z","");	/* pszUserName */
      PACK(desc,"z",0);		/* pszLogAddr */
      PACK(desc,"W",0);		/* uJobId */
      PACK(desc,"W",0);		/* fsStatus */
      PACK(desc,"z","");	/* pszStatus */
      PACK(desc,"z","");	/* pszComment */
      PACK(desc,"z","NULL"); /* pszDrivers */
      PACK(desc,"W",0);		/* time */
      PACK(desc,"W",0);		/* pad1 */
    }
  }
}

static BOOL api_WPrintDestGetInfo(int cnum,char *param,char *data,
				  int mdrcnt,int mprcnt,
				  char **rdata,char **rparam,
				  int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  char* PrinterName = p;
  int uLevel,cbBuf;
  struct pack_desc desc;
  int snum;

  p = skip_string(p,1);
  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));

  /* check it's a supported varient */
  if (strcmp(str1,"zWrLh") != 0) return False;
  if (!check_printdest_info(&desc,uLevel,str2)) return False;

  snum = lp_servicenumber(PrinterName);
  if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
    int pnum = lp_servicenumber(PRINTERS_NAME);
    if (pnum >= 0) {
      lp_add_printer(PrinterName,pnum);
      snum = lp_servicenumber(PrinterName);
    }
  }

  if (snum < 0) {
    *rdata_len = 0;
    desc.errcode = NERR_DestNotFound;
    desc.neededlen = 0;
  }
  else {
    if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
    desc.base = *rdata;
    desc.buflen = mdrcnt;
    init_package(&desc,1,0);
    fill_printdest_info(cnum,snum,uLevel,&desc);
    *rdata_len = desc.usedlen;
  }

  *rparam_len = 6;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,desc.neededlen);

  DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
  return(True);
}

static BOOL api_WPrintDestEnum(int cnum,char *param,char *data,
			       int mdrcnt,int mprcnt,
			       char **rdata,char **rparam,
			       int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel,cbBuf;
  int queuecnt;
  int i, n, succnt;
  struct pack_desc desc;
  int services = lp_numservices();

  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));

  /* check it's a supported varient */
  if (strcmp(str1,"WrLeh") != 0) return False;
  if (!check_printdest_info(&desc,uLevel,str2)) return False;

  queuecnt = 0;
  for (i = 0; i < services; i++)
    if (lp_print_ok(i) && lp_browseable(i))
      queuecnt++;

  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,queuecnt,0);

  succnt = 0;
  n = 0;
  for (i = 0; i < services; i++) {
    if (lp_print_ok(i) && lp_browseable(i)) {
      fill_printdest_info(cnum,i,uLevel,&desc);
      n++;
      if (desc.errcode == NERR_Success) succnt = n;
    }
  }
  *rdata_len = desc.usedlen;

  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,queuecnt);

  DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
  return(True);
}

static BOOL api_WPrintDriverEnum(int cnum,char *param,char *data,
				 int mdrcnt,int mprcnt,
				 char **rdata,char **rparam,
				 int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel,cbBuf;
  int succnt;
  struct pack_desc desc;

  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));

  /* check it's a supported varient */
  if (strcmp(str1,"WrLeh") != 0) return False;
  if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;

  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,1,0);

  PACK(&desc,"B41","NULL");
  succnt = (desc.errcode == NERR_Success ? 1 : 0);

  *rdata_len = desc.usedlen;

  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,1);

  DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
  return(True);
}

static BOOL api_WPrintQProcEnum(int cnum,char *param,char *data,
				int mdrcnt,int mprcnt,
				char **rdata,char **rparam,
				int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel,cbBuf;
  int succnt;
  struct pack_desc desc;

  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));

  /* check it's a supported varient */
  if (strcmp(str1,"WrLeh") != 0) return False;
  if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;

  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,1,0);

  PACK(&desc,"B13","lpd");
  succnt = (desc.errcode == NERR_Success ? 1 : 0);

  *rdata_len = desc.usedlen;

  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,1);

  DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
  return(True);
}

static BOOL api_WPrintPortEnum(int cnum,char *param,char *data,
			       int mdrcnt,int mprcnt,
			       char **rdata,char **rparam,
			       int *rdata_len,int *rparam_len)
{
  char *str1 = param+2;
  char *str2 = skip_string(str1,1);
  char *p = skip_string(str2,1);
  int uLevel,cbBuf;
  int succnt;
  struct pack_desc desc;

  uLevel = SVAL(p,0);
  cbBuf = SVAL(p,2);

  DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));

  /* check it's a supported varient */
  if (strcmp(str1,"WrLeh") != 0) return False;
  if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;

  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
  desc.base = *rdata;
  desc.buflen = mdrcnt;
  init_package(&desc,1,0);

  PACK(&desc,"B13","lp0");
  succnt = (desc.errcode == NERR_Success ? 1 : 0);

  *rdata_len = desc.usedlen;

  *rparam_len = 8;
  *rparam = REALLOC(*rparam,*rparam_len);
  SSVALS(*rparam,0,desc.errcode);
  SSVAL(*rparam,2,0);
  SSVAL(*rparam,4,succnt);
  SSVAL(*rparam,6,1);

  DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
  return(True);
}

/****************************************************************************
  the buffer was too small
  ****************************************************************************/
static BOOL api_TooSmall(int cnum,char *param,char *data,
			 int mdrcnt,int mprcnt,
			 char **rdata,char **rparam,
			 int *rdata_len,int *rparam_len)
{
  *rparam_len = MIN(*rparam_len,mprcnt);
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_BufTooSmall);

  DEBUG(3,("Supplied buffer too small in API command\n"));

  return(True);
}


/****************************************************************************
  the request is not supported
  ****************************************************************************/
static BOOL api_Unsupported(int cnum,char *param,char *data,
			    int mdrcnt,int mprcnt,
			    char **rdata,char **rparam,
			    int *rdata_len,int *rparam_len)
{
  *rparam_len = 4;
  *rparam = REALLOC(*rparam,*rparam_len);

  *rdata_len = 0;

  SSVAL(*rparam,0,NERR_notsupported);
  SSVAL(*rparam,2,0);		/* converter word */

  DEBUG(3,("Unsupported API command\n"));

  return(True);
}




struct
{
  char *name;
  int id;
  BOOL (*fn)();
  int flags;
} api_commands[] = {
  {"RNetShareEnum",	0,	api_RNetShareEnum,0},
  {"RNetShareGetInfo",	1,	api_RNetShareGetInfo,0},
  {"RNetServerGetInfo",	13,	api_RNetServerGetInfo,0},
  {"RNetUserGetInfo",	56,	api_RNetUserGetInfo,0},
  {"NetWkstaGetInfo",	63,	api_NetWkstaGetInfo,0},
  {"DosPrintQEnum",	69,	api_DosPrintQEnum,0},
  {"DosPrintQGetInfo",	70,	api_DosPrintQGetInfo,0},
  {"WPrintJobEnumerate",76,	api_WPrintJobEnumerate,0},
  {"WPrintJobGetInfo",	77,	api_WPrintJobGetInfo,0},
  {"RDosPrintJobDel",	81,	api_RDosPrintJobDel,0},
  {"WPrintDestEnum",	84,	api_WPrintDestEnum,0},
  {"WPrintDestGetInfo",	85,	api_WPrintDestGetInfo,0},
  {"NetRemoteTOD",	91,	api_NetRemoteTOD,0},
  {"WPrintQueuePurge",	103,	api_WPrintQueuePurge,0},
  {"WAccessGetUserPerms",105,	api_WAccessGetUserPerms,0},
  {"SetUserPassword",	115,	api_SetUserPassword,0},
  {"WWkstaUserLogon",	132,	api_WWkstaUserLogon,0},
  {"PrintJobInfo",	147,	api_PrintJobInfo,0},
  {"WPrintDriverEnum",	205,	api_WPrintDriverEnum,0},
  {"WPrintQProcEnum",	206,	api_WPrintQProcEnum,0},
  {"WPrintPortEnum",	207,	api_WPrintPortEnum,0},
  {NULL,		-1,	api_Unsupported,0}};


/****************************************************************************
  handle remote api calls
  ****************************************************************************/
static int api_reply(int cnum,char *outbuf,char *data,char *params,
		     int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
{
  int api_command = SVAL(params,0);
  char *rdata = NULL;
  char *rparam = NULL;
  int rdata_len = 0;
  int rparam_len = 0;
  BOOL reply=False;
  int i;

  DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
	   api_command,params+2,skip_string(params+2,1),
	   tdscnt,tpscnt,mdrcnt,mprcnt));

  for (i=0;api_commands[i].name;i++)
    if (api_commands[i].id == api_command && api_commands[i].fn)
      {
	DEBUG(3,("Doing %s\n",api_commands[i].name));
	break;
      }

  rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024);
  rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024);

  reply = api_commands[i].fn(cnum,params,data,mdrcnt,mprcnt,
			     &rdata,&rparam,&rdata_len,&rparam_len);


  if (rdata_len > mdrcnt ||
      rparam_len > mprcnt)
    {
      reply = api_TooSmall(cnum,params,data,mdrcnt,mprcnt,
			   &rdata,&rparam,&rdata_len,&rparam_len);
    }
	    

  /* if we get False back then it's actually unsupported */
  if (!reply)
    api_Unsupported(cnum,params,data,mdrcnt,mprcnt,
		    &rdata,&rparam,&rdata_len,&rparam_len);

      

  /* now send the reply */
  send_trans_reply(outbuf,rdata,rparam,NULL,rdata_len,rparam_len,0);

  if (rdata)
    free(rdata);
  if (rparam)
    free(rparam);
  
  return(-1);
}

/****************************************************************************
  handle named pipe commands
  ****************************************************************************/
static int named_pipe(int cnum,char *outbuf,char *name,
		      uint16 *setup,char *data,char *params,
		      int suwcnt,int tdscnt,int tpscnt,
		      int msrcnt,int mdrcnt,int mprcnt)
{

  if (strequal(name,"LANMAN"))
    return(api_reply(cnum,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt));

  DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n",
	   name,(int)setup[0],(int)setup[1]));
  
  return(0);
}


/****************************************************************************
  reply to a SMBtrans
  ****************************************************************************/
int reply_trans(char *inbuf,char *outbuf)
{
  fstring name;

  char *data=NULL,*params=NULL;
  uint16 *setup=NULL;

  int outsize = 0;
  int cnum = SVAL(inbuf,smb_tid);

  int tpscnt = SVAL(inbuf,smb_vwv0);
  int tdscnt = SVAL(inbuf,smb_vwv1);
  int mprcnt = SVAL(inbuf,smb_vwv2);
  int mdrcnt = SVAL(inbuf,smb_vwv3);
  int msrcnt = CVAL(inbuf,smb_vwv4);
  BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
  BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
  int pscnt = SVAL(inbuf,smb_vwv9);
  int psoff = SVAL(inbuf,smb_vwv10);
  int dscnt = SVAL(inbuf,smb_vwv11);
  int dsoff = SVAL(inbuf,smb_vwv12);
  int suwcnt = CVAL(inbuf,smb_vwv13);

  StrnCpy(name,smb_buf(inbuf),sizeof(name)-1);
  
  if (tdscnt)
    {
      data = (char *)malloc(tdscnt);
      memcpy(data,smb_base(inbuf)+dsoff,dscnt);
    }
  if (tpscnt)
    {
      params = (char *)malloc(tpscnt);
      memcpy(params,smb_base(inbuf)+psoff,pscnt);
    }

  if (suwcnt)
    {
      int i;
      setup = (uint16 *)malloc(suwcnt*sizeof(setup[0]));
      for (i=0;i<suwcnt;i++)
	setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
    }


  if (pscnt < tpscnt || dscnt < tdscnt)
    {
      /* We need to send an interim response then receive the rest
	 of the parameter/data bytes */
      outsize = set_message(outbuf,0,0,True);
      show_msg(outbuf);
      send_smb(outbuf);
    }

  /* receive the rest of the trans packet */
  while (pscnt < tpscnt || dscnt < tdscnt)
    {
      int pcnt,poff,dcnt,doff,pdisp,ddisp;

      receive_smb(inbuf, 0);
      show_msg(inbuf);
	  
      /* Ensure this is still a trans packet (sanity check) */
      if(CVAL(inbuf, smb_com) != SMBtrans)
	{
	  DEBUG(2,("Invalid secondary trans2 packet\n"));
	  if (params) free(params);
	  if (data) free(data);
	  if (setup) free(setup);
	  return(ERROR(ERRSRV,ERRerror));
	}
      
      tpscnt = SVAL(inbuf,smb_vwv0);
      tdscnt = SVAL(inbuf,smb_vwv1);

      pcnt = SVAL(inbuf,smb_vwv2);
      poff = SVAL(inbuf,smb_vwv3);
      pdisp = SVAL(inbuf,smb_vwv4);
      
      dcnt = SVAL(inbuf,smb_vwv5);
      doff = SVAL(inbuf,smb_vwv6);
      ddisp = SVAL(inbuf,smb_vwv7);
      
      pscnt += pcnt;
      dscnt += dcnt;

      if (pcnt)
	memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
      if (dcnt)
	memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);      
    }


  DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt));
  

  if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0)
    outsize = named_pipe(cnum,outbuf,name+strlen("\\PIPE\\"),setup,data,params,
			 suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);


  if (data) free(data);
  if (params) free(params);
  if (setup) free(setup);

  if (close_on_completion)
    close_cnum(cnum);

  if (one_way)
    return(-1);
  
  if (outsize == 0)
    return(ERROR(ERRSRV,ERRnosupport));

  return(outsize);
}


