/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Main SMB reply routines
   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 most of the reply_ calls that the server
   makes to handle specific protocols
*/


#include "includes.h"
#include "loadparm.h"
#include "trans2.h"

/* look in server.c for some explanation of these variables */
extern int Protocol;
extern int DEBUGLEVEL;
extern int chain_size;
extern int maxxmit;
extern int chain_fnum;
extern char magic_char;
extern connection_struct Connections[];
extern files_struct Files[];
extern BOOL case_sensitive;
extern pstring sesssetup_user;
extern int Client;

/* this macro should always be used to extract an fnum (smb_fid) from
a packet to ensure chaining works correctly */
#define GETFNUM(buf,where) (chain_fnum!=-1?chain_fnum:SVAL(buf,where))


/****************************************************************************
  reply to an special message 
****************************************************************************/
int reply_special(char *inbuf,char *outbuf)
{
  int outsize = 4;
  int msg_type = CVAL(inbuf,0);
  int msg_flags = CVAL(inbuf,1);
  pstring name1,name2;
  extern fstring remote_machine;
  extern fstring local_machine;
  char *p;

  *name1 = *name2 = 0;

  smb_setlen(outbuf,0);

  switch (msg_type)
    {
    case 0x81: /* session request */
      CVAL(outbuf,0) = 0x82;
      CVAL(outbuf,3) = 0;
      if (name_len(inbuf+4) > 50)
	{
	  DEBUG(0,("Invalid name length in session request\n"));
	  return(0);
	}
      name_extract(inbuf,4,name1);
      name_extract(inbuf,4 + name_len(inbuf + 4),name2);
      DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2));      

      strcpy(remote_machine,name2);
      trim_string(remote_machine," "," ");
      p = strchr(remote_machine,' ');
      strlower(remote_machine);
      if (p) *p = 0;

      strcpy(local_machine,name1);
      trim_string(local_machine," "," ");
      p = strchr(local_machine,' ');
      strlower(local_machine);
      if (p) *p = 0;

      add_session_user(remote_machine);

      reload_services(True);
      reopen_logs();

      break;
    case 0x85: /* session keepalive */
    default:
      return(0);
    }
  
  DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags));
  
  return(outsize);
}



/****************************************************************************
  reply to a tcon
****************************************************************************/
int reply_tcon(char *inbuf,char *outbuf)
{
  pstring service;
  pstring user;
  pstring password;
  pstring dev;
  int connection_num;
  int outsize = 0;
  int uid = SVAL(inbuf,smb_uid);
  int vuid;
  int pwlen;

  *service = *user = *password = *dev = 0;

  vuid = valid_uid(uid);
  
  parse_connect(inbuf,service,user,password,&pwlen,dev);

  connection_num = make_connection(service,user,password,pwlen,dev,vuid);
  
  if (connection_num < 0)
    switch (connection_num)
      {
      case -4:
	return(ERROR(ERRSRV,ERRaccess));
      case -3:
	return(ERROR(ERRDOS,ERRnoipc));
      case -2:
	return(ERROR(ERRSRV,ERRinvnetname));
      default:
	return(ERROR(ERRSRV,ERRbadpw));
      }
  
  outsize = set_message(outbuf,2,0,True);
  SSVAL(outbuf,smb_vwv0,maxxmit);
  SSVAL(outbuf,smb_vwv1,connection_num);
  SSVAL(outbuf,smb_tid,connection_num);
  
  DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
  
  return(outsize);
}


/****************************************************************************
  reply to a tcon and X
****************************************************************************/
int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize)
{
  pstring service;
  pstring user;
  pstring password;
  pstring devicename;
  int connection_num;
  int outsize = 0;
  int uid = SVAL(inbuf,smb_uid);
  int vuid;
  int smb_com2 = SVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int passlen = SVAL(inbuf,smb_vwv3);

  *service = *user = *password = *devicename = 0;

  /* we might have to close an old one */
  if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0)
    close_cnum(SVAL(inbuf,smb_tid));
  
  vuid = valid_uid(uid);
  
  {
    char *path;
    char *p;
    memcpy(password,smb_buf(inbuf),passlen);
    password[passlen]=0;
    path = smb_buf(inbuf) + passlen;
    DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen));
    strcpy(service,path+2);
    p = strchr(service,'\\');
    if (!p)
      return(ERROR(ERRSRV,ERRinvnetname));
    *p = 0;
    strcpy(service,p+1);
    p = strchr(service,'%');
    if (p)
      {
	*p++ = 0;
	strcpy(user,p);
      }
    StrnCpy(devicename,path + strlen(path) + 1,6);
    DEBUG(4,("Got device type %s\n",devicename));
  }

  connection_num = make_connection(service,user,password,passlen,devicename,vuid);
  
  if (connection_num < 0)
    switch (connection_num)
      {
      case -4:
	return(ERROR(ERRSRV,ERRaccess));
      case -3:
	return(ERROR(ERRDOS,ERRnoipc));
      case -2:
	return(ERROR(ERRSRV,ERRinvnetname));
      default:
	return(ERROR(ERRSRV,ERRbadpw));
      }
  
  outsize = set_message(outbuf,2,strlen(devicename)+1,True);
  
  DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
  
  /* set the incoming and outgoing tid to the just created one */
  SSVAL(inbuf,smb_tid,connection_num);
  SSVAL(outbuf,smb_tid,connection_num);

  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4);

  strcpy(smb_buf(outbuf),devicename);

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);

  return(outsize);
}


/****************************************************************************
  reply to an unknown type
****************************************************************************/
int reply_unknown(char *inbuf,char *outbuf)
{
  int cnum;
  int type;
  cnum = SVAL(inbuf,smb_tid);
  type = CVAL(inbuf,smb_com);
  
  DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n",
	timestring(),
	smb_fn_name(type),
	cnum,type,type));
  
  return(ERROR(ERRSRV,ERRunknownsmb));
}


/****************************************************************************
  reply to an ioctl
****************************************************************************/
int reply_ioctl(char *inbuf,char *outbuf)
{
  DEBUG(3,("ignoring ioctl\n"));
  
  return(ERROR(ERRSRV,ERRnosupport));
}


/****************************************************************************
reply to a session setup command
****************************************************************************/
int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize)
{
  int outsize = 0;
  int sess_uid;
  int   smb_com2;
  int   smb_off2;       
  int   smb_bufsize;    
  int   smb_mpxmax;     
  int   smb_vc_num;     
  uint32   smb_sesskey;    
  int   smb_apasslen;   
  pstring smb_apasswd;
  pstring user;
  BOOL guest=False;

  *smb_apasswd = 0;
  
  sess_uid = SVAL(inbuf,smb_uid);
  smb_com2 = CVAL(inbuf,smb_vwv0);
  smb_off2 = SVAL(inbuf,smb_vwv1);
  smb_bufsize = SVAL(inbuf,smb_vwv2);
  smb_mpxmax = SVAL(inbuf,smb_vwv3);
  smb_vc_num = SVAL(inbuf,smb_vwv4);
  smb_sesskey = IVAL(inbuf,smb_vwv5);

  if (Protocol < PROTOCOL_NT1) {
    smb_apasslen = SVAL(inbuf,smb_vwv7);
    memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
    StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1);
  } else {
    uint16 passlen1 = SVAL(inbuf,smb_vwv7);
    uint16 passlen2 = SVAL(inbuf,smb_vwv8);
    BOOL doencrypt = SMBENCRYPT();
    char *p = smb_buf(inbuf);
    if (passlen1 > 256) passlen1 = 0;
    if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird 
					lengths sometimes */
    if(doencrypt) {
      /* Use the lanman2 password for now until the NT md4 password
	 is worked out. */
      smb_apasslen = passlen1;
      memcpy(smb_apasswd,p,smb_apasslen);
    } else {
      /* for Win95 */
      if (passlen1 > passlen2) {
	smb_apasslen = passlen1;
	StrnCpy(smb_apasswd,p,smb_apasslen);
      } else {
	smb_apasslen = passlen2;
	StrnCpy(smb_apasswd,p + passlen1,smb_apasslen);
      }
    }
    p += passlen1 + passlen2;
    strcpy(user,p); p = skip_string(p,1);
    DEBUG(3,("Domain=[%s]  NativeOS=[%s] NativeLanMan=[%s]\n",
	     p,skip_string(p,1),skip_string(p,2)));
  }


  DEBUG(3,("sesssetupX:name=[%s]\n",user));

  if (!*user)
    strcpy(user,lp_guestaccount(-1));

  strlower(user);

  strcpy(sesssetup_user,user);

  reload_services(True);

  add_session_user(user);


  if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) &&
      !check_hosts_equiv(user))
    {

      if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0))
	guest = True;
  
      /* now check if it's a valid username/password */
      if (!guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL))
	{
#if !GUEST_SESSSETUP
	  if (lp_security() >= SEC_USER)
	    return(ERROR(ERRSRV,ERRbadpw));
#endif
 	  if (*smb_apasswd || !Get_Pwnam(user,True))
	    strcpy(user,lp_guestaccount(-1));
	  DEBUG(3,("Registered username %s for guest access\n",user));
	  guest = True;
	}
    }

  if (!Get_Pwnam(user,True)) {
    DEBUG(3,("No such user %s - using guest account\n",user));
    strcpy(user,lp_guestaccount(-1));
  }

  if (lp_servicenumber(user) < 0 && 
      !strequal(user,lp_guestaccount(-1)))
    {
      int homes = lp_servicenumber(HOMES_NAME);
      char *home = get_home_dir(user);
      if (homes >= 0 && home)
	lp_add_home(user,homes,home);
    }


  /* it's ok - setup a reply */
  if (Protocol < PROTOCOL_NT1) {
    outsize = set_message(outbuf,3,0,True);
  } else {
    char *p;
    outsize = set_message(outbuf,3,3,True);
    p = smb_buf(outbuf);
    strcpy(p,"Unix"); p = skip_string(p,1);
    strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1);
    strcpy(p,lp_workgroup()); p = skip_string(p,1);
    outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
    /* perhaps grab OS version here?? */
  }

  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);

  if (guest)
    SSVAL(outbuf,smb_vwv2,1);

  /* register the name and uid as being validated, so further connections
     to a uid can get through without a password, on the same VC */
  register_uid(SVAL(inbuf,smb_uid),user,guest);
  
  maxxmit = MIN(maxxmit,smb_bufsize);

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);

  return(outsize);
}


/****************************************************************************
  reply to a chkpth
****************************************************************************/
int reply_chkpth(char *inbuf,char *outbuf)
{
  int outsize = 0;
  int cnum,mode;
  pstring name;
  BOOL ok = False;
  
  cnum = SVAL(inbuf,smb_tid);
  
  strcpy(name,smb_buf(inbuf) + 1);
  unix_convert(name,cnum);

  mode = SVAL(inbuf,smb_vwv0);

  if (check_name(name,cnum))
    ok = directory_exist(name);

  if (!ok)
    return(ERROR(ERRDOS,ERRbadpath));
  
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode));
  
  return(outsize);
}


/****************************************************************************
  reply to a getatr
****************************************************************************/
int reply_getatr(char *inbuf,char *outbuf)
{
  pstring fname;
  int cnum;
  int outsize = 0;
  struct stat sbuf;
  BOOL ok = False;
  int mode=0;
  uint32 size=0;
  time_t mtime=0;
  
  cnum = SVAL(inbuf,smb_tid);

  strcpy(fname,smb_buf(inbuf) + 1);
  unix_convert(fname,cnum);

  /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
     under WfWg - weird! */
  if (! (*fname))
    {
      mode = 0x12;
      size = 0;
      mtime = 0;
      ok = True;
    }
  else
    if (check_name(fname,cnum))
      {
	if (stat(fname,&sbuf) == 0)
	  {
	    mode = dos_mode(cnum,fname,&sbuf);
	    size = sbuf.st_size;
	    mtime = sbuf.st_mtime;
	    if (mode & aDIR)
	      size = 0;
	    ok = True;
	  }
	else
	  DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
    }
  
  if (!ok)
    return(UNIXERROR(ERRDOS,ERRbadfile));
  
  outsize = set_message(outbuf,10,0,True);

  SSVAL(outbuf,smb_vwv0,mode);
  SIVAL(outbuf,smb_vwv1,mtime + GMT_TO_LOCAL * TimeDiff(mtime));
  SIVAL(outbuf,smb_vwv3,size);

  if (Protocol >= PROTOCOL_NT1) {
    char *p = strrchr(fname,'/');
    uint16 flg2 = SVAL(outbuf,smb_flg2);
    if (!p) p = fname;
    if (!is_8_3(fname))
      SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
  }
  
  DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size));
  
  return(outsize);
}


/****************************************************************************
  reply to a setatr
****************************************************************************/
int reply_setatr(char *inbuf,char *outbuf)
{
  pstring fname;
  int cnum;
  int outsize = 0;
  BOOL ok=False;
  int mode;
  time_t mtime;
  
  cnum = SVAL(inbuf,smb_tid);
  
  strcpy(fname,smb_buf(inbuf) + 1);
  unix_convert(fname,cnum);

  mode = SVAL(inbuf,smb_vwv0);
  mtime = IVAL(inbuf,smb_vwv1);
  
  if (directory_exist(fname))
    mode |= aDIR;
  if (check_name(fname,cnum))
    ok =  (dos_chmod(cnum,fname,mode) == 0);
  if (ok && mtime != 0)
    ok = set_filetime(fname,mtime + LOCAL_TO_GMT * TimeDiff(mtime));
  
  if (!ok)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode));
  
  return(outsize);
}


/****************************************************************************
  reply to a dskattr
****************************************************************************/
int reply_dskattr(char *inbuf,char *outbuf)
{
  int cnum;
  int outsize = 0;
  int dfree,dsize,bsize;
  
  cnum = SVAL(inbuf,smb_tid);
  
  disk_free(".",&bsize,&dfree,&dsize);
  
  outsize = set_message(outbuf,5,0,True);
  
  SSVAL(outbuf,smb_vwv0,dsize);
  SSVAL(outbuf,smb_vwv1,bsize/512);
  SSVAL(outbuf,smb_vwv2,512);
  SSVAL(outbuf,smb_vwv3,dfree);
  
  DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree));
  
  return(outsize);
}


/****************************************************************************
  reply to a search
  Can be called from SMBsearch, SMBffirst or SMBfunique.
****************************************************************************/
int reply_search(char *inbuf,char *outbuf)
{
  pstring mask;
  pstring directory;
  pstring fname;
  int size,mode;
  time_t date;
  int dirtype;
  int cnum;
  int outsize = 0;
  int numentries = 0;
  BOOL finished = False;
  int maxentries;
  int i;
  char *p;
  BOOL ok = False;
  int status_len;
  char *path;
  char status[21];
  int dptr_num=-1;
  BOOL check_descend = False;
  BOOL expect_close = False;
  BOOL can_open = True;

  *mask = *directory = *fname = 0;

  /* If we were called as SMBffirst then we must expect close. */
  if(CVAL(inbuf,smb_com) == SMBffirst)
    expect_close = True;
  
  cnum = SVAL(inbuf,smb_tid);

  outsize = set_message(outbuf,1,3,True);
  maxentries = SVAL(inbuf,smb_vwv0); 
  dirtype = SVAL(inbuf,smb_vwv1);
  path = smb_buf(inbuf) + 1;
  status_len = SVAL(smb_buf(inbuf),3 + strlen(path));

  
  /* dirtype &= ~aDIR; */
  
  DEBUG(5,("path=%s status_len=%d\n",path,status_len));

  
  if (status_len == 0)
    {
      pstring dir2;

      strcpy(directory,smb_buf(inbuf)+1);
      strcpy(dir2,smb_buf(inbuf)+1);
      unix_convert(directory,cnum);
      unix_format(dir2);

      if (!check_name(directory,cnum))
	can_open = False;

      p = strrchr(dir2,'/');
      if (p == NULL) 
	{strcpy(mask,dir2);*dir2 = 0;}
      else
	{*p = 0;strcpy(mask,p+1);}

      p = strrchr(directory,'/');
      if (!p) 
	*directory = 0;
      else
	*p = 0;

      if (strlen(directory) == 0)
	strcpy(directory,"./");
      bzero(status,21);
      CVAL(status,0) = dirtype;
    }
  else
    {
      memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
      memcpy(mask,status+1,11);
      mask[11] = 0;
      dirtype = CVAL(status,0) & 0x1F;
      Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num);      
      if (!Connections[cnum].dirptr)
	goto SearchEmpty;
      string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
      if (!case_sensitive)
	strnorm(mask);
    }

  /* turn strings of spaces into a . */  
  {
    p = strrchr(mask,' ');
    if (p)
      {
	if (!p[1])
	  {
	    while (*p == ' ') {*p = 0;p--;}
	  }
	else
	  {
	    fstring ext;
	    strcpy(ext,p+1);
	    *p = 0;
	    trim_string(mask,NULL," ");
	    strcat(mask,".");
	    strcat(mask,ext);
	  }
      }
  }

  {
    for (p=mask; *p; p++)
      {
	if (*p != '?' && *p != '*' && !isdoschar(*p))
	  {
	    DEBUG(5,("Invalid char [%c] in search mask?\n",*p));
	    *p = '?';
	  }
      }
  }

  if (!strchr(mask,'.') && strlen(mask)>8)
    {
      fstring tmp;
      strcpy(tmp,&mask[8]);
      mask[8] = '.';
      mask[9] = 0;
      strcat(mask,tmp);
    }

  DEBUG(5,("mask=%s directory=%s\n",mask,directory));
  
  if (can_open)
    {
      p = smb_buf(outbuf) + 3;
      
      ok = True;
      
      if (status_len == 0)
	{
	  dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid));
	  if (dptr_num < 0)
	    return(ERROR(ERRDOS,ERRnofids));
	}

      DEBUG(4,("dptr_num is %d\n",dptr_num));

      if (ok)
	{
	  if ((dirtype&0x1F) == aVOLID)
	    {	  
	      memcpy(p,status,21);
	      make_dir_struct(p,"???????????",SERVICE(SNUM(cnum)),0,aVOLID,0);
	      dptr_fill(p+12,dptr_num);
	      if (dptr_zero(p+12) && (status_len==0))
		numentries = 1;
	      else
		numentries = 0;
	      p += DIR_STRUCT_SIZE;
	    }
	  else 
	    {
	      DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
	      if (in_list(Connections[cnum].dirpath,
			  lp_dontdescend(SNUM(cnum)),True))
		check_descend = True;

	      for (i=numentries;(i<maxentries) && !finished;i++)
		{
		  finished = 
		    !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend);
		  if (!finished)
		    {
		      memcpy(p,status,21);
		      make_dir_struct(p,mask,fname,size,mode,date);
		      dptr_fill(p+12,dptr_num);
		      numentries++;
		    }
		  p += DIR_STRUCT_SIZE;
		}
	    }
	}
    }


 SearchEmpty:

  if (numentries == 0 || !ok)
    {
      CVAL(outbuf,smb_rcls) = ERRDOS;
      SSVAL(outbuf,smb_err,ERRnofiles);
    }

  /* If we were called as SMBffirst with smb_search_id == NULL
     and no entries were found then return error and close dirptr 
     (X/Open spec) */

  if(ok && expect_close && numentries == 0 && status_len == 0)
    {
      CVAL(outbuf,smb_rcls) = ERRDOS;
      SSVAL(outbuf,smb_err,ERRnofiles);
      /* Also close the dptr - we know it's gone */
      dptr_close(dptr_num);
    }

  /* If we were called as SMBfunique, then we can close the dirptr now ! */
  if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
    dptr_close(dptr_num);

  SSVAL(outbuf,smb_vwv0,numentries);
  SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
  CVAL(smb_buf(outbuf),0) = 5;
  SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);

  if (Protocol >= PROTOCOL_NT1) {
    uint16 flg2 = SVAL(outbuf,smb_flg2);
    SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
  }
  
  outsize += DIR_STRUCT_SIZE*numentries;
  smb_setlen(outbuf,outsize - 4);
  
  if ((! *directory) && dptr_path(dptr_num))
    sprintf(directory,"(%s)",dptr_path(dptr_num));

  DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n",
	timestring(),
	smb_fn_name(CVAL(inbuf,smb_com)), 
	mask,directory,cnum,dirtype,numentries,maxentries));

  return(outsize);
}


/****************************************************************************
  reply to a fclose (stop directory search)
****************************************************************************/
int reply_fclose(char *inbuf,char *outbuf)
{
  int cnum;
  int outsize = 0;
  int status_len;
  char *path;
  char status[21];
  int dptr_num=-1;

  cnum = SVAL(inbuf,smb_tid);

  outsize = set_message(outbuf,1,0,True);
  path = smb_buf(inbuf) + 1;
  status_len = SVAL(smb_buf(inbuf),3 + strlen(path));

  
  if (status_len == 0)
    return(ERROR(ERRSRV,ERRsrverror));

  memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);

  if(dptr_fetch(status+12,&dptr_num)) {
    /*  Close the dptr - we know it's gone */
    dptr_close(dptr_num);
  }

  SSVAL(outbuf,smb_vwv0,0);

  DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum));

  return(outsize);
}


/****************************************************************************
  reply to an open
****************************************************************************/
int reply_open(char *inbuf,char *outbuf)
{
  pstring fname;
  int cnum;
  int fnum = -1;
  int outsize = 0;
  int fmode=0;
  int share_mode;
  int size = 0;
  time_t mtime=0;
  int unixmode;
  int rmode=0;
  struct stat sbuf;
  
  cnum = SVAL(inbuf,smb_tid);

  share_mode = SVAL(inbuf,smb_vwv0);

  strcpy(fname,smb_buf(inbuf)+1);
  unix_convert(fname,cnum);
    
  fnum = find_free_file();
  if (fnum < 0)
    return(ERROR(ERRSRV,ERRnofids));

  if (!check_name(fname,cnum))
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  unixmode = unix_mode(cnum,aARCH);
      
  open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL);

  if (!Files[fnum].open)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  if (fstat(Files[fnum].fd,&sbuf) != 0) {
    close_file(fnum);
    return(ERROR(ERRDOS,ERRnoaccess));
  }
    
  size = sbuf.st_size;
  fmode = dos_mode(cnum,fname,&sbuf);
  mtime = sbuf.st_mtime;

  if (fmode & aDIR) {
    DEBUG(3,("attempt to open a directory %s\n",fname));
    close_file(fnum);
    return(ERROR(ERRDOS,ERRnoaccess));
  }
  
  outsize = set_message(outbuf,7,0,True);
  SSVAL(outbuf,smb_vwv0,fnum);
  SSVAL(outbuf,smb_vwv1,fmode);
  SIVAL(outbuf,smb_vwv2,mtime + GMT_TO_LOCAL * TimeDiff(mtime));
  SIVAL(outbuf,smb_vwv4,size);
  SSVAL(outbuf,smb_vwv6,rmode);
    
  return(outsize);
}


/****************************************************************************
  reply to an open and X
****************************************************************************/
int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
{
  pstring fname;
  int cnum = SVAL(inbuf,smb_tid);
  int fnum = -1;
  int outsize = 0;
  int openmode = 0;
  int smb_com2 = CVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int smb_mode = SVAL(inbuf,smb_vwv3);
  int smb_attr = SVAL(inbuf,smb_vwv5);
#if 0
  int open_flags = SVAL(inbuf,smb_vwv2);
  int smb_sattr = SVAL(inbuf,smb_vwv4); 
  uint32 smb_time = IVAL(inbuf,smb_vwv6);
#endif
  int smb_ofun = SVAL(inbuf,smb_vwv8);
  int unixmode;
  int size=0,fmode=0,mtime=0,rmode=0;
  struct stat sbuf;
  int smb_action = 0;

  /* XXXX we need to handle passed times, sattr and flags */

  strcpy(fname,smb_buf(inbuf));
  unix_convert(fname,cnum);
    
  /* now add create and trunc bits */
  if (smb_ofun & 0x10)
    openmode |= O_CREAT;
  if ((smb_ofun & 0x3) == 2)
    openmode |= O_TRUNC;
  
  fnum = find_free_file();
  if (fnum < 0)
    return(ERROR(ERRSRV,ERRnofids));

  if (!check_name(fname,cnum))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  unixmode = unix_mode(cnum,smb_attr);
      
  open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode,
		   &rmode,&smb_action);
      
  if (!Files[fnum].open)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  if (fstat(Files[fnum].fd,&sbuf) != 0) {
    close_file(fnum);
    return(ERROR(ERRDOS,ERRnoaccess));
  }

  size = sbuf.st_size;
  fmode = dos_mode(cnum,fname,&sbuf);
  mtime = sbuf.st_mtime;
  if (fmode & aDIR) {
    close_file(fnum);
    return(ERROR(ERRDOS,ERRnoaccess));
  }
  
  outsize = set_message(outbuf,15,0,True);
  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
  SSVAL(outbuf,smb_vwv2,fnum);
  SSVAL(outbuf,smb_vwv3,fmode);
  SIVAL(outbuf,smb_vwv4,mtime + GMT_TO_LOCAL * TimeDiff(mtime));
  SIVAL(outbuf,smb_vwv6,size);
  SSVAL(outbuf,smb_vwv8,rmode);
  SSVAL(outbuf,smb_vwv11,smb_action);

  chain_fnum = fnum;

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);

  chain_fnum = -1;
  
  return(outsize);
}


/****************************************************************************
  reply to a SMBulogoffX
****************************************************************************/
int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize)
{
  int outsize = 0;
  int smb_com2 = CVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int uid = SVAL(inbuf,smb_uid);

  invalidate_uid(uid);

  outsize = set_message(outbuf,2,0,True);
  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);

  DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid));

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);

  
  return(outsize);
}


/****************************************************************************
  reply to a mknew
****************************************************************************/
int reply_mknew(char *inbuf,char *outbuf)
{
  pstring fname;
  int cnum,com;
  int fnum = -1;
  int outsize = 0;
  int createmode;
  mode_t unixmode;
  
  com = SVAL(inbuf,smb_com);
  cnum = SVAL(inbuf,smb_tid);

  createmode = SVAL(inbuf,smb_vwv0);
  strcpy(fname,smb_buf(inbuf)+1);
  unix_convert(fname,cnum);

  if (createmode & aVOLID)
    {
      DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
    }
  
  unixmode = unix_mode(cnum,createmode);
  
  if (com == SMBmknew && file_exist(fname,NULL))
    return(ERROR(ERRDOS,ERRfilexists));
    
  fnum = find_free_file();
  if (fnum < 0)
    return(ERROR(ERRSRV,ERRnofids));

  if (!check_name(fname,cnum))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode);
  
  if (!Files[fnum].open)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize = set_message(outbuf,1,0,True);
  SSVAL(outbuf,smb_vwv0,fnum);
  
  DEBUG(2,("new file %s\n",fname));
  DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode));
  
  return(outsize);
}


/****************************************************************************
  reply to a create temporary file
****************************************************************************/
int reply_ctemp(char *inbuf,char *outbuf)
{
  pstring fname;
  pstring fname2;
  int cnum;
  int fnum = -1;
  int outsize = 0;
  int createmode;
  mode_t unixmode;
  
  cnum = SVAL(inbuf,smb_tid);
  createmode = SVAL(inbuf,smb_vwv0);
  sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1);
  unix_convert(fname,cnum);
  
  unixmode = unix_mode(cnum,createmode);
  
  fnum = find_free_file();
  if (fnum < 0)
    return(ERROR(ERRSRV,ERRnofids));

  if (!check_name(fname,cnum))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  strcpy(fname2,(char *)mktemp(fname));

  open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode);

  if (!Files[fnum].open)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  outsize = set_message(outbuf,1,2 + strlen(fname2),True);
  SSVAL(outbuf,smb_vwv0,fnum);
  CVAL(smb_buf(outbuf),0) = 4;
  strcpy(smb_buf(outbuf) + 1,fname2);
  
  DEBUG(2,("created temp file %s\n",fname2));
  DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode));
  
  return(outsize);
}


/*******************************************************************
check if a user is allowed to delete a file
********************************************************************/
static BOOL can_delete(char *fname,int cnum,int dirtype)
{
  struct stat sbuf;
  int fmode;

  if (lstat(fname,&sbuf) != 0) return(False);
  fmode = dos_mode(cnum,fname,&sbuf);
  if (fmode & aDIR) return(False);
  if (fmode & aRONLY) return(False);
  if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
    return(False);
  return(True);
}

/****************************************************************************
  reply to a unlink
****************************************************************************/
int reply_unlink(char *inbuf,char *outbuf)
{
  int outsize = 0;
  pstring name;
  int cnum;
  int dirtype;
  pstring directory;
  pstring mask;
  char *p;
  int count=0;
  int error = ERRnoaccess;
  BOOL has_wild;

  *directory = *mask = 0;

  cnum = SVAL(inbuf,smb_tid);
  dirtype = SVAL(inbuf,smb_vwv0);
  
  strcpy(name,smb_buf(inbuf) + 1);
   
  DEBUG(3,("reply_unlink : %s\n",name));
   
  unix_convert(name,cnum);

  p = strrchr(name,'/');
  if (!p) {
    strcpy(directory,"./");
    strcpy(mask,name);
  } else {
    *p = 0;
    strcpy(directory,name);
    strcpy(mask,p+1);
  }

  if (is_mangled(mask))
    check_mangled_stack(mask,NULL);

  has_wild = strchr(mask,'*') || strchr(mask,'?');

  if (!has_wild) {
    strcat(directory,"/");
    strcat(directory,mask);
    if (can_delete(directory,cnum,dirtype) && !unlink(directory)) count++;
  } else {
    void *dirptr = NULL;
    char *dname;

    if (check_name(directory,cnum))
      dirptr = OpenDir(directory);

    if (dirptr)
      {
	error = ERRbadfile;

	if (strequal(mask,"????????.???"))
	  strcpy(mask,"*");

	while ((dname = ReadDirName(dirptr)))
	  {
	    pstring fname;
	    strcpy(fname,dname);
	    
	    if(!mask_match(fname, mask, case_sensitive, False)) continue;

	    error = ERRnoaccess;
	    sprintf(fname,"%s/%s",directory,dname);
	    if (!can_delete(fname,cnum,dirtype)) continue;
	    if (!unlink(fname)) count++;
	    DEBUG(3,("reply_unlink : doing unlink on %s\n",fname));
	  }
	CloseDir(dirptr);
      }
  }
  
  if (count == 0)
    return(UNIXERROR(ERRDOS,error));
  
  outsize = set_message(outbuf,0,0,True);
  
  return(outsize);
}


/****************************************************************************
   reply to a readbraw (core+ protocol)
****************************************************************************/
int reply_readbraw(char *inbuf, char *outbuf)
{
  int cnum,maxcount,mincount,fnum;
  int nread = 0;
  int startpos;
  char *header = outbuf;
  int ret=0;
  int fd;
  char *fname;
  int predict=0;

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  startpos = IVAL(inbuf,smb_vwv1);
  maxcount = SVAL(inbuf,smb_vwv3);
  mincount = SVAL(inbuf,smb_vwv4);

  /* ensure we don't overrun the packet size */
  maxcount = MIN(65535,maxcount);
  maxcount = MAX(mincount,maxcount);

  if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read)
    {
      DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum));
      _smb_setlen(header,0);
      transfer_file(0,Client,0,header,4,0);
      return(-1);
    }
  else
    {
      fd = Files[fnum].fd;
      fname = Files[fnum].name;
    }


  if (!is_locked(fnum,cnum,maxcount,startpos))
    {
      int size = Files[fnum].size;
      int sizeneeded = startpos + maxcount;
	    
      if (size < sizeneeded) {
	struct stat st;
	if (fstat(Files[fnum].fd,&st) == 0)
	  size = st.st_size;
	if (!Files[fnum].can_write) 
	  Files[fnum].size = size;
      }

      nread = MIN(maxcount,size - startpos);	  
    }

  if (nread < mincount)
    nread = 0;
  
  DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n",
	   timestring(),
	   fnum,cnum,startpos,
	   maxcount,mincount,nread));
  
  _smb_setlen(header,nread);

  if (!Files[fnum].can_write)
    predict = read_predict(fd,startpos,header+4,NULL,nread);

  if ((nread-predict) > 0)
    seek_file(fnum,startpos + predict);

  ret = transfer_file(fd,Client,nread-predict,header,4+predict,
		      startpos+predict);

  if (ret != nread+4)
    DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n",
	     fname,startpos,nread,ret));

  DEBUG(5,("readbraw finished\n"));
  return -1;
}


/****************************************************************************
  reply to a lockread (core+ protocol)
****************************************************************************/
int reply_lockread(char *inbuf,char *outbuf,int length,int bufsize)
{
  int cnum,fnum;
  int nread = -1;
  char *data;
  int outsize = 0;
  uint32 startpos, numtoread;
  int eclass;
  uint32 ecode;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_READ(fnum);
  CHECK_ERROR(fnum);

  numtoread = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  
  outsize = set_message(outbuf,5,3,True);
  numtoread = MIN(bufsize-outsize,numtoread);
  data = smb_buf(outbuf) + 3;
  
  if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode))
    return (ERROR(eclass,ecode));

  nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
  
  if (nread < 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize += nread;
  SSVAL(outbuf,smb_vwv0,nread);
  SSVAL(outbuf,smb_vwv5,nread+3);
  SSVAL(smb_buf(outbuf),1,nread);
  
  DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
  
  return(outsize);
}


/****************************************************************************
  reply to a read
****************************************************************************/
int reply_read(char *inbuf,char *outbuf,int length,int bufsize)
{
  int cnum,numtoread,fnum;
  int nread = 0;
  char *data;
  int startpos;
  int outsize = 0;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_READ(fnum);
  CHECK_ERROR(fnum);

  numtoread = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  
  outsize = set_message(outbuf,5,3,True);
  numtoread = MIN(bufsize-outsize,numtoread);
  data = smb_buf(outbuf) + 3;
  
  if (is_locked(fnum,cnum,numtoread,startpos))
    return(ERROR(ERRDOS,ERRlock));	

  if (numtoread > 0)
    nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
  
  if (nread < 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize += nread;
  SSVAL(outbuf,smb_vwv0,nread);
  SSVAL(outbuf,smb_vwv5,nread+3);
  CVAL(smb_buf(outbuf),0) = 1;
  SSVAL(smb_buf(outbuf),1,nread);
  
  DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
  
  return(outsize);
}


/****************************************************************************
  reply to a read and X
****************************************************************************/
int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
{
  int smb_com2 = CVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int fnum = GETFNUM(inbuf,smb_vwv2);
  uint32 smb_offs = IVAL(inbuf,smb_vwv3);
  int smb_maxcnt = SVAL(inbuf,smb_vwv5);
  int smb_mincnt = SVAL(inbuf,smb_vwv6);
  int cnum;
  int nread = -1;
  char *data;
  int outsize = 0;
  BOOL ok = False;

  cnum = SVAL(inbuf,smb_tid);

  CHECK_FNUM(fnum,cnum);
  CHECK_READ(fnum);
  CHECK_ERROR(fnum);

  outsize = set_message(outbuf,12,0,True);
  data = smb_buf(outbuf);

  if (is_locked(fnum,cnum,smb_maxcnt,smb_offs))
    return(ERROR(ERRDOS,ERRlock));
  nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False);
  ok = True;
  
  if (nread < 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize += nread;
  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
  SSVAL(outbuf,smb_vwv5,nread);
  SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size);
  SSVAL(smb_buf(outbuf),-2,nread);
  
  DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n",
	timestring(),fnum,cnum,
	smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2));

  chain_fnum = fnum;

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);
  
  chain_fnum = -1;
  
  return(outsize);
}


/****************************************************************************
  reply to a writebraw (core+ or LANMAN1.0 protocol)
****************************************************************************/
int reply_writebraw(char *inbuf,char *outbuf,int length,int bufsize)
{
  int nwritten=0;
  int total_written=0;
  int numtowrite=0;
  int cnum,fnum;
  int outsize = 0;
  long startpos;
  char *data=NULL;
  BOOL write_through;
  int tcount;

  /* this is just to stop silly warnings */
  length = bufsize = 0;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);
  
  tcount = IVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv3);
  write_through = BITSETW(inbuf+smb_vwv7,0);

  /* We have to deal with slightly different formats depending
     on whether we are using the core+ or lanman1.0 protocol */
  if(Protocol <= PROTOCOL_COREPLUS) {
    numtowrite = SVAL(smb_buf(inbuf),-2);
    data = smb_buf(inbuf);
  } else {
    numtowrite = SVAL(inbuf,smb_vwv10);
    data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
  }

  /* force the error type */
  CVAL(inbuf,smb_com) = SMBwritec;
  CVAL(outbuf,smb_com) = SMBwritec;

  if (is_locked(fnum,cnum,tcount,startpos))
    return(ERROR(ERRDOS,ERRlock));

  if (seek_file(fnum,startpos) != startpos)
    DEBUG(0,("couldn't seek to %d in writebraw\n",startpos));

  if (numtowrite>0)
    nwritten = write_data(Files[fnum].fd,data,numtowrite);
  
  DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n",
	   timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through));

  if (nwritten < numtowrite) 
    return(UNIXERROR(ERRHRD,ERRdiskfull));

  total_written = nwritten;

  /* Return a message to the redirector to tell it
     to send more bytes */
  CVAL(outbuf,smb_com) = SMBwritebraw;
  SSVALS(outbuf,smb_vwv0,-1);
  outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
  send_smb(outbuf);
  
  /* Now read the raw data into the buffer and write it */
  if(read_smb_length(Client,inbuf,0) == -1) {
    exit_server("secondary writebraw failed");
  }
  
  /* Even though this is not an smb message, smb_len
     returns the generic length of an smb message */
  numtowrite = smb_len(inbuf);

  if (tcount > nwritten+numtowrite) {
    DEBUG(3,("Client overestimated the write %d %d %d\n",
	     tcount,nwritten,numtowrite));
  }

  nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0,
			   startpos+nwritten);
  total_written += nwritten;
  
  /* Set up outbuf to return the correct return */
  outsize = set_message(outbuf,1,0,True);
  CVAL(outbuf,smb_com) = SMBwritec;
  SSVAL(outbuf,smb_vwv0,total_written);

  if (nwritten < numtowrite) {
    CVAL(outbuf,smb_rcls) = ERRHRD;
    SSVAL(outbuf,smb_err,ERRdiskfull);      
  }

  if (lp_syncalways(SNUM(cnum)) || write_through)
    sync_file(fnum);

  DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n",
	   timestring(),fnum,cnum,startpos,numtowrite,total_written));

  /* we won't return a status if write through is not selected - this 
     follows what WfWg does */
  if (!write_through && total_written==tcount)
    return(-1);

  return(outsize);
}


/****************************************************************************
  reply to a writeunlock (core+)
****************************************************************************/
int reply_writeunlock(char *inbuf,char *outbuf)
{
  int cnum,fnum;
  int nwritten = -1;
  int outsize = 0;
  int fd;
  char *data;
  uint32 numtowrite,startpos;
  int eclass;
  uint32 ecode;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  numtowrite = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  data = smb_buf(inbuf) + 3;
  
  fd = Files[fnum].fd;

  if (is_locked(fnum,cnum,numtowrite,startpos))
    return(ERROR(ERRDOS,ERRlock));

  seek_file(fnum,startpos);

  /* The special X/Open SMB protocol handling of
     zero length writes is *NOT* done for
     this call */
  if(numtowrite == 0)
    nwritten = 0;
  else
    nwritten = write_data(fd,data,numtowrite);
  
  if (lp_syncalways(SNUM(cnum)))
    sync_file(fnum);

  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode))
    return(ERROR(eclass,ecode));

  outsize = set_message(outbuf,1,0,True);
  
  SSVAL(outbuf,smb_vwv0,nwritten);
  
  DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n",
	   timestring(),fnum,cnum,numtowrite,nwritten));
  
  return(outsize);
}


/****************************************************************************
  reply to a write
****************************************************************************/
int reply_write(char *inbuf,char *outbuf,int dum1,int dum2)
{
  int cnum,numtowrite,fnum;
  int nwritten = -1;
  int outsize = 0;
  int startpos;
  int fd;
  char *data;

  dum1 = dum2 = 0;

  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  numtowrite = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  data = smb_buf(inbuf) + 3;
  
  fd = Files[fnum].fd;

  if (is_locked(fnum,cnum,numtowrite,startpos))
    return(ERROR(ERRDOS,ERRlock));

  seek_file(fnum,startpos);

  /* X/Open SMB protocol says that if smb_vwv1 is
     zero then the file size should be extended or
     truncated to the size given in smb_vwv[2-3] */
  if(numtowrite == 0)
    nwritten = set_filelen(fd, startpos);
  else
    nwritten = write_data(fd,data,numtowrite);
  
  if (lp_syncalways(SNUM(cnum)))
    sync_file(fnum);

  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  outsize = set_message(outbuf,1,0,True);
  
  SSVAL(outbuf,smb_vwv0,nwritten);

  if (nwritten < numtowrite) {
    CVAL(outbuf,smb_rcls) = ERRHRD;
    SSVAL(outbuf,smb_err,ERRdiskfull);      
  }
  
  DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten));
  
  return(outsize);
}


/****************************************************************************
  reply to a write and X
****************************************************************************/
int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
{
  int smb_com2 = CVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int fnum = GETFNUM(inbuf,smb_vwv2);
  uint32 smb_offs = IVAL(inbuf,smb_vwv3);
  int smb_dsize = SVAL(inbuf,smb_vwv10);
  int smb_doff = SVAL(inbuf,smb_vwv11);
  BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
  int cnum;
  int nwritten = -1;
  int outsize = 0;
  int fd;
  char *data;

  cnum = SVAL(inbuf,smb_tid);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  data = smb_base(inbuf) + smb_doff;

  fd = Files[fnum].fd;

  if (is_locked(fnum,cnum,smb_dsize,smb_offs))
    return(ERROR(ERRDOS,ERRlock));

  seek_file(fnum,smb_offs);
  
  /* X/Open SMB protocol says that, unlike SMBwrite
     if the length is zero then NO truncation is
     done, just a write of zero. To truncate a file,
     use SMBwrite. */
  if(smb_dsize == 0)
    nwritten = 0;
  else
    nwritten = write_data(fd,data,smb_dsize);
  
  if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  outsize = set_message(outbuf,6,0,True);
  
  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
  SSVAL(outbuf,smb_vwv2,nwritten);
  
  if (nwritten < smb_dsize) {
    CVAL(outbuf,smb_rcls) = ERRHRD;
    SSVAL(outbuf,smb_err,ERRdiskfull);      
  }

  DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten));

  chain_fnum = fnum;

  if (lp_syncalways(SNUM(cnum)) || write_through)
    sync_file(fnum);

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);
  
  chain_fnum = -1;
  
  return(outsize);
}


/****************************************************************************
  reply to a lseek
****************************************************************************/
int reply_lseek(char *inbuf,char *outbuf)
{
  int cnum,fnum;
  uint32 startpos;
  int32 res=-1;
  int mode,umode;
  int outsize = 0;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  mode = SVAL(inbuf,smb_vwv1) & 3;
  startpos = IVAL(inbuf,smb_vwv2);

  switch (mode & 3) 
    {
    case 0: umode = SEEK_SET; break;
    case 1: umode = SEEK_CUR; break;
    case 2: umode = SEEK_END; break;
    default:
      umode = SEEK_SET; break;
    }
  
  res = lseek(Files[fnum].fd,startpos,umode);
  Files[fnum].pos = res;
  
  outsize = set_message(outbuf,2,0,True);
  SIVALS(outbuf,smb_vwv0,res);
  
  DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode));
  
  return(outsize);
}


/****************************************************************************
  reply to a flush
****************************************************************************/
int reply_flush(char *inbuf,char *outbuf)
{
  int cnum, fnum;
  int outsize = set_message(outbuf,0,0,True);

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  if (fnum != 0xFFFF) {
    CHECK_FNUM(fnum,cnum);
    CHECK_ERROR(fnum);
  }

  if (fnum == 0xFFFF)
    {
      int i;
      for (i=0;i<MAX_OPEN_FILES;i++)
	if (OPEN_FNUM(i))
	  sync_file(i);
    }
  else
    sync_file(fnum);

  DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum));
  return(outsize);
}


/****************************************************************************
  reply to a exit
****************************************************************************/
int reply_exit(char *inbuf,char *outbuf)
{
  int outsize = set_message(outbuf,0,0,True);
  DEBUG(3,("%s exit\n",timestring()));
  
  return(outsize);
}


/****************************************************************************
  reply to a close
****************************************************************************/
int reply_close(char *inbuf,char *outbuf)
{
  int fnum,cnum;
  int outsize = 0;
  time_t mtime;
  int32 eclass = 0, err = 0;

  outsize = set_message(outbuf,0,0,True);

  cnum = SVAL(inbuf,smb_tid);

  fnum = GETFNUM(inbuf,smb_vwv0);
  CHECK_FNUM(fnum,cnum);

  if(HAS_CACHED_ERROR(fnum)) {
    eclass = Files[fnum].wbmpx_ptr->wr_errclass;
    err = Files[fnum].wbmpx_ptr->wr_error;
  }

  mtime = IVAL(inbuf,smb_vwv1);

  close_file(fnum);

  /* try and set the date */
  if (mtime == 0 || mtime == -1)
    DEBUG(5,("not setting null date\n"));
  else
    set_filetime(Files[fnum].name,mtime + LOCAL_TO_GMT * TimeDiff(mtime));

  /* We have a cached error */
  if(eclass || err)
    return(ERROR(eclass,err));

  DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n",
	   timestring(),Files[fnum].fd,fnum,cnum,
	   Connections[cnum].num_files_open));
  
  return(outsize);
}


/****************************************************************************
  reply to a writeclose (Core+ protocol)
****************************************************************************/
int reply_writeclose(char *inbuf,char *outbuf)
{
  int cnum,numtowrite,fnum;
  int nwritten = -1;
  int outsize = 0;
  int startpos;
  char *data;
  time_t mtime;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  numtowrite = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  mtime = IVAL(inbuf,smb_vwv4);
  data = smb_buf(inbuf) + 1;
  
  if (is_locked(fnum,cnum,numtowrite,startpos))
    return(ERROR(ERRDOS,ERRlock));
      
  seek_file(fnum,startpos);
      
  nwritten = write_data(Files[fnum].fd,data,numtowrite);

  close_file(fnum);

  if (mtime == 0 || mtime == -1)
    DEBUG(5,("not setting null date\n"));
  else
    set_filetime(Files[fnum].name,mtime + LOCAL_TO_GMT * TimeDiff(mtime));
  
  DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n",
	   timestring(),fnum,cnum,numtowrite,nwritten,
	   Connections[cnum].num_files_open));
  
  if (nwritten <= 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize = set_message(outbuf,1,0,True);
  
  SSVAL(outbuf,smb_vwv0,nwritten);
  return(outsize);
}


/****************************************************************************
  reply to a lock
****************************************************************************/
int reply_lock(char *inbuf,char *outbuf)
{
  int fnum,cnum;
  int outsize = set_message(outbuf,0,0,True);
  uint32 count,offset;
  int eclass;
  uint32 ecode;

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  count = IVAL(inbuf,smb_vwv1);
  offset = IVAL(inbuf,smb_vwv3);

  DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));

  if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode))
    return (ERROR(eclass,ecode));
  
  return(outsize);
}


/****************************************************************************
  reply to a unlock
****************************************************************************/
int reply_unlock(char *inbuf,char *outbuf)
{
  int fnum,cnum;
  int outsize = set_message(outbuf,0,0,True);
  uint32 count,offset;
  int eclass;
  uint32 ecode;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  count = IVAL(inbuf,smb_vwv1);
  offset = IVAL(inbuf,smb_vwv3);

  if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode))
    return (ERROR(eclass,ecode));

  DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
  
  return(outsize);
}


/****************************************************************************
  reply to a tdis
****************************************************************************/
int reply_tdis(char *inbuf,char *outbuf)
{
  int cnum;
  int outsize = set_message(outbuf,0,0,True);
  
  cnum = SVAL(inbuf,smb_tid);

  Connections[cnum].used = False;

  close_cnum(cnum);
  
  DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum));

  return outsize;
}



/****************************************************************************
  reply to a echo
****************************************************************************/
int reply_echo(char *inbuf,char *outbuf)
{
  int cnum;
  int smb_reverb = SVAL(inbuf,smb_vwv0);
  int seq_num;
  int data_len = smb_buflen(inbuf);
  int outsize = set_message(outbuf,1,data_len,True);

  cnum = SVAL(inbuf,smb_tid);

  if (cnum != 0xFFFF && !OPEN_CNUM(cnum))
    {
      DEBUG(4,("Invalid cnum in echo (%d)\n",cnum));
      return(ERROR(ERRSRV,ERRinvnid));
    }

  /* copy any incoming data back out */
  if (data_len > 0)
    memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);

  if (smb_reverb > 100)
    {
      DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
      smb_reverb = 100;
    }

  for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++)
    {
      SSVAL(outbuf,smb_vwv0,seq_num);

      smb_setlen(outbuf,outsize - 4);

      send_smb(outbuf);
    }

  DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum));

  return -1;
}


/****************************************************************************
  reply to a printopen
****************************************************************************/
int reply_printopen(char *inbuf,char *outbuf)
{
  pstring fname;
  pstring fname2;
  int cnum;
  int fnum = -1;
  int outsize = 0;

  *fname = *fname2 = 0;

  cnum = SVAL(inbuf,smb_tid);

  if (!CAN_PRINT(cnum))
    return(ERROR(ERRDOS,ERRnoaccess));

  {
    pstring s;
    char *p;
    StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1);
    p = s;
    while (*p)
      {
	if (!(isalnum(*p) || strchr("._-",*p)))
	  *p = 'X';
	p++;
      }

    if (strlen(s) > 10) s[10] = 0;

    sprintf(fname,"%s.XXXXXX",s);  
  }

  fnum = find_free_file();
  if (fnum < 0)
    return(ERROR(ERRSRV,ERRnofids));

  strcpy(fname2,(char *)mktemp(fname));

  if (!check_name(fname2,cnum))
    return(ERROR(ERRDOS,ERRnoaccess));

  open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC,
	    unix_mode(cnum,0));

  if (!Files[fnum].open)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  /* force it to be a print file */
  Files[fnum].print_file = True;
  
  outsize = set_message(outbuf,1,0,True);
  SSVAL(outbuf,smb_vwv0,fnum);
  
  DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum));
  
  return(outsize);
}


/****************************************************************************
  reply to a printclose
****************************************************************************/
int reply_printclose(char *inbuf,char *outbuf)
{
  int fnum,cnum;
  int outsize = set_message(outbuf,0,0,True);
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  if (!CAN_PRINT(cnum))
    return(ERROR(ERRDOS,ERRnoaccess));
  
  close_file(fnum);
  
  DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum));
  
  return(outsize);
}


/****************************************************************************
  reply to a printqueue
****************************************************************************/
int reply_printqueue(char *inbuf,char *outbuf)
{
  int cnum;
  int outsize = set_message(outbuf,2,3,True);
  int max_count = SVAL(inbuf,smb_vwv0);
  int start_index = SVAL(inbuf,smb_vwv1);

  cnum = SVAL(inbuf,smb_tid);

/* allow checking the queue for anyone */
#if 0
  if (!CAN_PRINT(cnum))
    return(ERROR(ERRDOS,ERRnoaccess));
#endif

  SSVAL(outbuf,smb_vwv0,0);
  SSVAL(outbuf,smb_vwv1,0);
  CVAL(smb_buf(outbuf),0) = 1;
  SSVAL(smb_buf(outbuf),1,0);
  
  DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n",
	timestring(),cnum,start_index,max_count));

  if (!OPEN_CNUM(cnum) || !Connections[cnum].printer)
    {
      int i;
      cnum = -1;

      for (i=0;i<MAX_CONNECTIONS;i++)
	if (CAN_PRINT(i) && Connections[i].printer)
	  cnum = i;

      if (cnum == -1)
	for (i=0;i<MAX_CONNECTIONS;i++)
	  if (OPEN_CNUM(i))
	    cnum = i;

      if (!OPEN_CNUM(cnum))
	return(ERROR(ERRSRV,ERRinvnid));

      DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum));
    }

  if (!become_user(cnum))
    return(ERROR(ERRSRV,ERRinvnid));

  {
    print_queue_struct *queue = NULL;
    char *p = smb_buf(outbuf) + 3;
    int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL);
    int num_to_get = ABS(max_count);
    int first = (max_count>0?start_index:start_index+max_count+1);
    int i;

    if (first >= count)
      num_to_get = 0;
    else
      num_to_get = MIN(num_to_get,count-first);
    

    for (i=first;i<first+num_to_get;i++)
      {
	put_dos_date2(p,0,queue[i].time);
	CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3);
	SSVAL(p,5,queue[i].job);
	SIVAL(p,7,queue[i].size);
	CVAL(p,11) = 0;
	StrnCpy(p+12,queue[i].user,16);
	p += 28;
      }

    if (count > 0)
      {
	outsize = set_message(outbuf,2,28*count+3,False);	  
	SSVAL(outbuf,smb_vwv0,count);
	SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
	CVAL(smb_buf(outbuf),0) = 1;
	SSVAL(smb_buf(outbuf),1,28*count);
      }

    if (queue) free(queue);
	  
    DEBUG(3,("%d entries returned in queue\n",count));
  }
  
  return(outsize);
}


/****************************************************************************
  reply to a printwrite
****************************************************************************/
int reply_printwrite(char *inbuf,char *outbuf)
{
  int cnum,numtowrite,fnum;
  int outsize = set_message(outbuf,0,0,True);
  char *data;
  
  cnum = SVAL(inbuf,smb_tid);

  if (!CAN_PRINT(cnum))
    return(ERROR(ERRDOS,ERRnoaccess));

  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  numtowrite = SVAL(smb_buf(inbuf),1);
  data = smb_buf(inbuf) + 3;
  
  if (write_data(Files[fnum].fd,data,numtowrite) != numtowrite)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite));
  
  return(outsize);
}


/****************************************************************************
  reply to a mkdir
****************************************************************************/
int reply_mkdir(char *inbuf,char *outbuf)
{
  pstring directory;
  int cnum;
  int outsize,ret=-1;
  
  strcpy(directory,smb_buf(inbuf) + 1);
  cnum = SVAL(inbuf,smb_tid);
  unix_convert(directory,cnum);
  
  if (check_name(directory,cnum))
    ret = mkdir(directory,unix_mode(cnum,aDIR));
  
  if (ret < 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret));
  
  return(outsize);
}


/****************************************************************************
  reply to a rmdir
****************************************************************************/
int reply_rmdir(char *inbuf,char *outbuf)
{
  pstring directory;
  int cnum;
  int outsize = 0;
  BOOL ok = False;
  
  cnum = SVAL(inbuf,smb_tid);
  strcpy(directory,smb_buf(inbuf) + 1);
  unix_convert(directory,cnum);
  
  if (check_name(directory,cnum))
    {
      dptr_closepath(directory,SVAL(inbuf,smb_pid));
      ok = (rmdir(directory) == 0);
      if (!ok)
	DEBUG(3,("couldn't remove directory %s : %s\n",
		 directory,strerror(errno)));
    }
  
  if (!ok)
    return(UNIXERROR(ERRDOS,ERRbadpath));
  
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG(3,("%s rmdir %s\n",timestring(),directory));
  
  return(outsize);
}


/*******************************************************************
resolve wildcards in a filename rename
********************************************************************/
static BOOL resolve_wildcards(char *name1,char *name2)
{
  fstring root1,root2;
  fstring ext1,ext2;
  char *p,*p2;

  name1 = strrchr(name1,'/');
  name2 = strrchr(name2,'/');

  if (!name1 || !name2) return(False);
  
  strcpy(root1,name1);
  strcpy(root2,name2);
  p = strrchr(root1,'.');
  if (p) {
    *p = 0;
    strcpy(ext1,p+1);
  } else {
    strcpy(ext1,"");    
  }
  p = strrchr(root2,'.');
  if (p) {
    *p = 0;
    strcpy(ext2,p+1);
  } else {
    strcpy(ext2,"");    
  }

  p = root1;
  p2 = root2;
  while (*p2) {
    if (*p2 == '?') {
      *p2 = *p;
      p2++;
    } else {
      p2++;
    }
    if (*p) p++;
  }

  p = ext1;
  p2 = ext2;
  while (*p2) {
    if (*p2 == '?') {
      *p2 = *p;
      p2++;
    } else {
      p2++;
    }
    if (*p) p++;
  }

  strcpy(name2,root2);
  if (ext2[0]) {
    strcat(name2,".");
    strcat(name2,ext2);
  }

  return(True);
}

/*******************************************************************
check if a user is allowed to rename a file
********************************************************************/
static BOOL can_rename(char *fname,int cnum)
{
  struct stat sbuf;
  int fmode;

  if (lstat(fname,&sbuf) != 0) return(False);
  fmode = dos_mode(cnum,fname,&sbuf);
  if (fmode & aRONLY) return(False);
  return(True);
}

/****************************************************************************
  reply to a mv
****************************************************************************/
int reply_mv(char *inbuf,char *outbuf)
{
  int outsize = 0;
  pstring name;
  int cnum;
  pstring directory;
  pstring mask,newname;
  char *p;
  int count=0;
  int error = ERRnoaccess;
  BOOL has_wild;

  *directory = *mask = 0;

  cnum = SVAL(inbuf,smb_tid);
  
  strcpy(name,smb_buf(inbuf) + 1);
  strcpy(newname,smb_buf(inbuf) + 3 + strlen(name));
   
  DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
   
  unix_convert(name,cnum);
  unix_convert(newname,cnum);

  p = strrchr(name,'/');
  if (!p) {
    strcpy(directory,"./");
    strcpy(mask,name);
  } else {
    *p = 0;
    strcpy(directory,name);
    strcpy(mask,p+1);
  }

  if (is_mangled(mask))
    check_mangled_stack(mask,NULL);

  has_wild = strchr(mask,'*') || strchr(mask,'?');

  if (!has_wild) {
    strcat(directory,"/");
    strcat(directory,mask);
    if (resolve_wildcards(directory,newname) && 
	can_rename(directory,cnum) && 
	!rename(directory,newname)) count++;
  } else {
    void *dirptr = NULL;
    char *dname;
    pstring destname;

    if (check_name(directory,cnum))
      dirptr = OpenDir(directory);

    if (dirptr)
      {
	error = ERRbadfile;

	if (strequal(mask,"????????.???"))
	  strcpy(mask,"*");

	while ((dname = ReadDirName(dirptr)))
	  {
	    pstring fname;
	    strcpy(fname,dname);
	    
	    if(!mask_match(fname, mask, case_sensitive, False)) continue;

	    error = ERRnoaccess;
	    sprintf(fname,"%s/%s",directory,dname);
	    if (!can_rename(fname,cnum)) continue;
	    strcpy(destname,newname);
	    if (resolve_wildcards(fname,destname) && 
		!rename(fname,destname)) count++;
	    DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname));
	  }
	CloseDir(dirptr);
      }
  }
  
  if (count == 0)
    return(UNIXERROR(ERRDOS,error));
  
  outsize = set_message(outbuf,0,0,True);
  
  return(outsize);
}


/****************************************************************************
  reply to a setdir
****************************************************************************/
int reply_setdir(char *inbuf,char *outbuf)
{
  int cnum,snum;
  int outsize = 0;
  BOOL ok = False;
  pstring newdir;
  
  cnum = SVAL(inbuf,smb_tid);
  
  snum = Connections[cnum].service;
  if (!CAN_SETDIR(snum))
    return(ERROR(ERRDOS,ERRnoaccess));
  
  strcpy(newdir,smb_buf(inbuf) + 1);
  strlower(newdir);
  
  if (strlen(newdir) == 0)
    ok = True;
  else
    {
      ok = directory_exist(newdir);
      if (ok)
	string_set(&Connections[cnum].connectpath,newdir);
    }
  
  if (!ok)
    return(ERROR(ERRDOS,ERRbadpath));
  
  outsize = set_message(outbuf,0,0,True);
  CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh);
  
  DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum));
  
  return(outsize);
}


/****************************************************************************
  reply to a lockingX request
****************************************************************************/
int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
{
  int smb_com2 = CVAL(inbuf,smb_vwv0);
  int smb_off2 = SVAL(inbuf,smb_vwv1);
  int fnum = GETFNUM(inbuf,smb_vwv2);
  uint16 locktype = SVAL(inbuf,smb_vwv3);
  uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
  uint16 num_locks = SVAL(inbuf,smb_vwv7);
  uint32 count, offset;

  int cnum;
  int i;
  char *data;
  uint32 ecode=0, dummy2;
  int outsize, eclass=0, dummy1;

  cnum = SVAL(inbuf,smb_tid);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  data = smb_buf(inbuf);
  /* Data now points at the beginning of the list
     of smb_unlkrng structs */
  for(i = 0; i < (int)num_ulocks; i++) {
    count = IVAL(data,SMB_LKLEN_OFFSET(i));
    offset = IVAL(data,SMB_LKOFF_OFFSET(i));
    if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode))
      return ERROR(eclass,ecode);
  }

  /* Now do any requested locks */
  data += 10*num_ulocks;
  /* Data now points at the beginning of the list
     of smb_lkrng structs */
  for(i = 0; i < (int)num_locks; i++) {
    count = IVAL(data,SMB_LKLEN_OFFSET(i)); 
    offset = IVAL(data,SMB_LKOFF_OFFSET(i)); 
    if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode))
      break;
  }

  /* If any of the above locks failed, then we must unlock
     all of the previous locks (X/Open spec). */
  if(i != num_locks && num_locks != 0) {
    for(; i >= 0; i--) {
      count = IVAL(data,SMB_LKLEN_OFFSET(i));  
      offset = IVAL(data,SMB_LKOFF_OFFSET(i)); 
      do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2);
    }
    return ERROR(eclass,ecode);
  }

  outsize = set_message(outbuf,2,0,True);
  
  CVAL(outbuf,smb_vwv0) = smb_com2;
  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
  
  DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n",
	timestring(),fnum,cnum,locktype,num_locks,num_ulocks));

  chain_fnum = fnum;

  if (smb_com2 != 0xFF)
    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
			   outbuf,outbuf+outsize,
			   length,bufsize);
  
  chain_fnum = -1;
  
  return(outsize);
}


/****************************************************************************
  reply to a SMBreadbmpx (read block multiplex) request
****************************************************************************/
int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize)
{
  int cnum,fnum;
  int nread = -1;
  int total_read;
  char *data;
  int32 startpos;
  int outsize, mincount, maxcount;
  int max_per_packet;
  int tcount;
  int pad;

  /* this function doesn't seem to work - disable by default */
  if (!lp_readbmpx())
    return(ERROR(ERRSRV,ERRuseSTD));

  outsize = set_message(outbuf,8,0,True);

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_READ(fnum);
  CHECK_ERROR(fnum);

  startpos = IVAL(inbuf,smb_vwv1);
  maxcount = SVAL(inbuf,smb_vwv3);
  mincount = SVAL(inbuf,smb_vwv4);

  data = smb_buf(outbuf);
  pad = ((int)data)%4;
  if (pad) pad = 4 - pad;
  data += pad;

  max_per_packet = bufsize-(outsize+pad);
  tcount = maxcount;
  total_read = 0;

  if (is_locked(fnum,cnum,maxcount,startpos))
    return(ERROR(ERRDOS,ERRlock));
	
  do
    {
      int N = MIN(max_per_packet,tcount-total_read);
  
      nread = read_file(fnum,data,startpos,N,N,-1,False);

      if (nread <= 0) nread = 0;

      if (nread < N)
	tcount = total_read + nread;

      set_message(outbuf,8,nread,False);
      SIVAL(outbuf,smb_vwv0,startpos);
      SSVAL(outbuf,smb_vwv2,tcount);
      SSVAL(outbuf,smb_vwv6,nread);
      SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));

      send_smb(outbuf);

      total_read += nread;
      startpos += nread;
    }
  while (total_read < tcount);

  return(-1);
}


/****************************************************************************
  reply to a SMBwritebmpx (write block multiplex primary) request
****************************************************************************/
int reply_writebmpx(char *inbuf,char *outbuf)
{
  int cnum,numtowrite,fnum;
  int nwritten = -1;
  int outsize = 0;
  int32 startpos;
  int tcount, write_through, smb_doff;
  int fd;
  char *data;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);
  CHECK_ERROR(fnum);

  tcount = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv3);
  write_through = BITSETW(inbuf+smb_vwv7,0);
  numtowrite = SVAL(inbuf,smb_vwv10);
  smb_doff = SVAL(inbuf,smb_vwv11);

  data = smb_base(inbuf) + smb_doff;

  /* If this fails we need to send an SMBwriteC response,
     not an SMBwritebmpx - set this up now so we don't forget */
  CVAL(outbuf,smb_com) = SMBwritec;

  fd = Files[fnum].fd;

  if (is_locked(fnum,cnum,tcount,startpos))
    return(ERROR(ERRDOS,ERRlock));

  seek_file(fnum,startpos);
  nwritten = write_data(fd,data,numtowrite);

  if(lp_syncalways(SNUM(cnum)) || write_through)
    sync_file(fnum);
  
  if(nwritten < numtowrite)
    return(UNIXERROR(ERRHRD,ERRdiskfull));

  /* If the maximum to be written to this file
     is greater than what we just wrote then set
     up a secondary struct to be attached to this
     fd, we will use this to cache error messages etc. */
  if(tcount > nwritten) 
    {
      write_bmpx_struct *wbms;
      if(Files[fnum].wbmpx_ptr != NULL)
	wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */
      else
	wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
      if(!wbms)
	{
	  DEBUG(0,("Out of memory in reply_readmpx\n"));
	  return(ERROR(ERRSRV,ERRnoresource));
	}
      wbms->wr_mode = write_through;
      wbms->wr_discard = False; /* No errors yet */
      wbms->wr_total_written = nwritten;
      wbms->wr_errclass = 0;
      wbms->wr_error = 0;
      Files[fnum].wbmpx_ptr = wbms;
    }

  /* We are returning successfully, set the message type back to
     SMBwritebmpx */
  CVAL(outbuf,smb_com) = SMBwriteBmpx;
  
  outsize = set_message(outbuf,1,0,True);
  
  SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
  
  DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n",
	timestring(),fnum,cnum,numtowrite,nwritten));
  
  return(outsize);
}


/****************************************************************************
  reply to a SMBwritebs (write block multiplex secondary) request
****************************************************************************/
int reply_writebs(char *inbuf,char *outbuf)
{
  int cnum,numtowrite,fnum;
  int nwritten = -1;
  int outsize = 0;
  int32 startpos;
  int tcount, write_through, smb_doff;
  int fd;
  char *data;
  write_bmpx_struct *wbms;
  BOOL send_response = False;
  
  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);
  CHECK_FNUM(fnum,cnum);
  CHECK_WRITE(fnum);

  tcount = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  numtowrite = SVAL(inbuf,smb_vwv6);
  smb_doff = SVAL(inbuf,smb_vwv7);

  data = smb_base(inbuf) + smb_doff;

  /* We need to send an SMBwriteC response, not an SMBwritebs */
  CVAL(outbuf,smb_com) = SMBwritec;

  /* This fd should have an auxiliary struct attached,
     check that it does */
  wbms = Files[fnum].wbmpx_ptr;
  if(!wbms) return(-1);

  /* If write through is set we can return errors, else we must
     cache them */
  write_through = wbms->wr_mode;

  /* Check for an earlier error */
  if(wbms->wr_discard)
    return -1; /* Just discard the packet */

  fd = Files[fnum].fd;

  seek_file(fnum,startpos);
  nwritten = write_data(fd,data,numtowrite);

  if(lp_syncalways(SNUM(cnum)) || write_through)
    sync_file(fnum);
  
  if (nwritten < numtowrite)
    {
      if(write_through)	{
	/* We are returning an error - we can delete the aux struct */
	if (wbms) free((char *)wbms);
	Files[fnum].wbmpx_ptr = NULL;
	return(ERROR(ERRHRD,ERRdiskfull));
      }
      return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
    }

  /* Increment the total written, if this matches tcount
     we can discard the auxiliary struct (hurrah !) and return a writeC */
  wbms->wr_total_written += nwritten;
  if(wbms->wr_total_written >= tcount)
    {
      if (write_through) {
	outsize = set_message(outbuf,1,0,True);
	SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);    
	send_response = True;
      }

      free((char *)wbms);
      Files[fnum].wbmpx_ptr = NULL;
    }

  if(send_response)
    return(outsize);

  return(-1);
}


/****************************************************************************
  reply to a SMBsetattrE
****************************************************************************/
int reply_setattrE(char *inbuf,char *outbuf)
{
  int cnum,fnum;
  struct utimbuf unix_times;
  int outsize = 0;

  outsize = set_message(outbuf,0,0,True);

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  /* Convert the DOS times into dos times. Ignore create
     time as UNIX can't set this.
     */
  
  unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
  unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
  unix_times.actime += LOCAL_TO_GMT*TimeDiff(unix_times.actime);
  unix_times.modtime += LOCAL_TO_GMT*TimeDiff(unix_times.modtime);
  
  /* Set the date on this file */
  if(utime(Files[fnum].name, &unix_times))
    return(ERROR(ERRDOS,ERRnoaccess));
  
  DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));

  return(outsize);
}


/****************************************************************************
  reply to a SMBgetattrE
****************************************************************************/
int reply_getattrE(char *inbuf,char *outbuf)
{
  int cnum,fnum;
  struct stat sbuf;
  int outsize = 0;
  int mode;

  outsize = set_message(outbuf,11,0,True);

  cnum = SVAL(inbuf,smb_tid);
  fnum = GETFNUM(inbuf,smb_vwv0);

  CHECK_FNUM(fnum,cnum);
  CHECK_ERROR(fnum);

  /* Do an fstat on this file */
  if(fstat( Files[fnum].fd, &sbuf))
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  mode = dos_mode(cnum,Files[fnum].name,&sbuf);
  
  /* Convert the times into dos times. Set create
     date to be last modify date as UNIX doesn't save
     this */
  put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime);
  put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
  put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
  if (mode & aDIR)
    {
      SIVAL(outbuf,smb_vwv6,0);
      SIVAL(outbuf,smb_vwv8,0);
    }
  else
    {
      SIVAL(outbuf,smb_vwv6,sbuf.st_size);
      SIVAL(outbuf,smb_vwv8,sbuf.st_size);
    }
  SSVAL(outbuf,smb_vwv10, mode);
  
  DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
  
  return(outsize);
}





