/* 
   Unix SMB/Netbios implementation.
   
   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.
*/

/******************************************************************************
 *
 * RCS ID
 * $Id: lanman10,v 1.2 2000/02/08 20:16:24 david Exp $
 *
 * HISTORY
 * $Log: lanman10,v $
 * Revision 1.2  2000/02/08 20:16:24  david
 * Tidied up exit calls
 *
 * Revision 1.1  1999/11/23 22:00:15  david
 * Updates for long filename support.
 * Added files for Coreplus, Lanman1, Lanman2 and NT protorocols.
 *
 *
 *
 *****************************************************************************/

#include <time.h>


#include "includes.h"
#include "nterr.h"

extern fstring local_machine;
extern fstring remote_machine;
extern fstring global_myworkgroup;
extern pstring global_myname;
extern pstring myhostname;
extern pstring sesssetup_user,samlogon_user;
extern int sam_logon_in_ssb;
extern int Client;
extern time_t smb_last_time;
extern BOOL case_preserve;
extern BOOL short_case_preserve;
extern BOOL case_mangle;
extern BOOL case_sensitive;
extern BOOL use_mangled_map;
extern BOOL smbDontStripFileExt;
extern BOOL setFileType;
extern BOOL appendFileType;
extern BOOL stripExtension;
extern BOOL detectDosParts;
extern int  smb_read_error;
extern unsigned int smb_echo_count ;
extern int dontConvertName;
extern int last_message;
extern connection_struct * g_conn;
extern int global_oplock_break;
extern struct current_user current_user;

extern char *last_inbuf;
extern int Protocol; 
extern int max_send;
extern int global_client_caps;

/*
tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize
*/



/****************************************************************************
  reply to a SMBreadbmpx (read block multiplex) request
****************************************************************************/
int reply_readbmpx(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  ssize_t nread = -1;
  ssize_t total_read;
  char *data;
  SMB_OFF_T startpos;
  int outsize;
  size_t maxcount;
  int max_per_packet;
  size_t tcount;
  int pad;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

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

  CHECK_FSP(fsp,conn);
  CHECK_READ(fsp);
  CHECK_ERROR(fsp);

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

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

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

  if (is_locked(fsp,conn,maxcount,startpos, F_RDLCK))
    return(ERROR(ERRDOS,ERRlock));
    
  do
    {
      size_t N = MIN(max_per_packet,tcount-total_read);
  
      nread = read_file(fsp,data,startpos,N);

      if (nread <= 0) nread = 0;

      if (nread < (ssize_t)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(&Client,outbuf);

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

  return(-1);
}

/****************************************************************************
  reply to a SMBwritebmpx (write block multiplex primary) request
****************************************************************************/
int reply_writebmpx(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  size_t numtowrite;
  ssize_t nwritten = -1;
  int outsize = 0;
  SMB_OFF_T startpos;
  size_t tcount;
  BOOL write_through;
  int smb_doff;
  char *data;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

  CHECK_FSP(fsp,conn);
  CHECK_WRITE(fsp);
  CHECK_ERROR(fsp);

  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;

  if (is_locked(fsp,conn,tcount,startpos,F_WRLCK))
    return(ERROR(ERRDOS,ERRlock));

  if(seek_file(fsp,startpos) == -1)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  nwritten = write_file(fsp,data,numtowrite);

  if(lp_syncalways(SNUM(conn)) || write_through)
    sync_file(conn,fsp);
  
  if(nwritten < (ssize_t)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((ssize_t)tcount > nwritten) 
  {
    write_bmpx_struct *wbms;
    if(fsp->wbmpx_ptr != NULL)
      wbms = fsp->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;
    fsp->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, ( "writebmpx fnum=%d num=%d wrote=%d\n",
        fsp->fnum, numtowrite, nwritten ) );

  if (write_through && tcount==nwritten) {
    /* we need to send both a primary and a secondary response */
    smb_setlen(outbuf,outsize - 4);
    send_smb(&Client,outbuf);

    /* now the secondary */
    outsize = set_message(outbuf,1,0,True);
    CVAL(outbuf,smb_com) = SMBwritec;
    SSVAL(outbuf,smb_vwv0,nwritten);
  }

  return(outsize);
}


/****************************************************************************
  reply to a SMBwritebs (write block multiplex secondary) request
****************************************************************************/
int reply_writebs(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  size_t numtowrite;
  ssize_t nwritten = -1;
  int outsize = 0;
  SMB_OFF_T startpos;
  size_t tcount;
  BOOL write_through;
  int smb_doff;
  char *data;
  write_bmpx_struct *wbms;
  BOOL send_response = False; 
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

  CHECK_FSP(fsp,conn);
  CHECK_WRITE(fsp);

  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 = fsp->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 */

  if(seek_file(fsp,startpos) == -1)
  {
    if(write_through)
    {
      /* We are returning an error - we can delete the aux struct */
      if (wbms) free((char *)wbms);
      fsp->wbmpx_ptr = NULL;
      return(UNIXERROR(ERRDOS,ERRnoaccess));
    }
    return(CACHE_ERROR(wbms,ERRDOS,ERRnoaccess));
  } 

  nwritten = write_file(fsp,data,numtowrite);

  if(lp_syncalways(SNUM(conn)) || write_through)
    sync_file(conn,fsp);
  
  if (nwritten < (ssize_t)numtowrite)
  {
    if(write_through)
    {
      /* We are returning an error - we can delete the aux struct */
      if (wbms) free((char *)wbms);
      fsp->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);
    fsp->wbmpx_ptr = NULL;
  }

  if(send_response)
    return(outsize);

  return(-1);
}


/****************************************************************************
  reply to a SMBsetattrE
****************************************************************************/
int reply_setattrE(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  struct utimbuf unix_times;
  int outsize = 0;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

  CHECK_FSP(fsp,conn);
  CHECK_ERROR(fsp);

  /* Convert the DOS times into unix 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);
  
  /* 
   * Patch from Ray Frush <frush@engr.colostate.edu>
   * Sometimes times are sent as zero - ignore them.
   */

  if ((unix_times.actime == 0) && (unix_times.modtime == 0)) 
  {
    /* Ignore request */
    if( DEBUGLVL( 3 ) )
      {
      dbgtext( "reply_setattrE fnum=%d ", fsp->fnum);
      dbgtext( "ignoring zero request - not setting timestamps of 0\n" );
      }
    return(outsize);
  }
  else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) 
  {
    /* set modify time = to access time if modify time was 0 */
    unix_times.modtime = unix_times.actime;
  }

  /* Set the date on this file */
  if(file_utime(conn, fsp->fsp_name, &unix_times))
    return(ERROR(ERRDOS,ERRnoaccess));
  
  DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n",
            fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) );

  return(outsize);
}


/****************************************************************************
  reply to a SMBgetattrE
****************************************************************************/
int reply_getattrE(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  SMB_STRUCT_STAT sbuf;
  int outsize = 0;
  int mode;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

  CHECK_FSP(fsp,conn);
  CHECK_ERROR(fsp);

  /* Do an fstat on this file */
  if(sys_fstat(fsp->fd_ptr->fd, &sbuf))
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  mode = dos_mode(conn,fsp->fsp_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,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))));
  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,(uint32)sbuf.st_size);
      SIVAL(outbuf,smb_vwv8,SMB_ROUNDUP(sbuf.st_size,1024));
    }
  SSVAL(outbuf,smb_vwv10, mode);
  
  DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum));
  
  return(outsize);
}

/*******************************************************************
  copy a file as part of a reply_copy
  ******************************************************************/

static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
              int count,BOOL target_is_directory, int *err_ret)
{
  int Access,action;
  SMB_STRUCT_STAT st;
  int ret=-1;
  files_struct *fsp1,*fsp2;
  pstring dest;
  
  *err_ret = 0;

  pstrcpy(dest,dest1);
  if (target_is_directory) {
    char *p = strrchr(src,'/');
    if (p) 
      p++;
    else
      p = src;
    pstrcat(dest,"/");
    pstrcat(dest,p);
  }

  if (!dos_file_exist(src,&st))
    return(False);

  fsp1 = file_new();
  if (!fsp1)
    return(False);

  open_file_shared(fsp1,conn,src,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
           (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action);

  if (!fsp1->open) {
      file_free(fsp1);
      return(False);
  }

  if (!target_is_directory && count)
    ofun = 1;

  fsp2 = file_new();
  if (!fsp2) {
      close_file(fsp1,False);
      return(False);
  }
  open_file_shared(fsp2,conn,dest,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
           ofun,st.st_mode,0,&Access,&action);

  if (!fsp2->open) {
    close_file(fsp1,False);
    file_free(fsp2);
    return(False);
  }

  if ((ofun&3) == 1) {
    if(sys_lseek(fsp2->fd_ptr->fd,0,SEEK_END) == -1) {
      DEBUG(0,("copy_file: error - sys_lseek returned error %s\n",
               strerror(errno) ));
      /*
       * Stop the copy from occurring.
       */
      ret = -1;
      st.st_size = 0;
    }
  }
  
  if (st.st_size)
    ret = transfer_file(fsp1->fd_ptr->fd,
                        fsp2->fd_ptr->fd,st.st_size,NULL,0,0);

  close_file(fsp1,False);
  /*
   * As we are opening fsp1 read-only we only expect
   * an error on close on fsp2 if we are out of space.
   * Thus we don't look at the error return from the
   * close of fsp1.
   */
  *err_ret = close_file(fsp2,False);

  return(ret == st.st_size);
}



/****************************************************************************
  reply to a file copy.
  ****************************************************************************/
int reply_copy(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  int outsize = 0;
  pstring name;
  pstring directory;
  pstring mask,newname;
  char *p;
  int count=0;
  int error = ERRnoaccess;
  int err = 0;
  BOOL has_wild;
  BOOL exists=False;
  int tid2 = SVAL(inbuf,smb_vwv0);
  int ofun = SVAL(inbuf,smb_vwv1);
  int flags = SVAL(inbuf,smb_vwv2);
  BOOL target_is_directory=False;
  BOOL bad_path1 = False;
  BOOL bad_path2 = False;

  *directory = *mask = 0;

  pstrcpy(name,smb_buf(inbuf));
  pstrcpy(newname,smb_buf(inbuf) + 1 + strlen(name));
   
  DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
   
  if (tid2 != conn->cnum) {
    /* can't currently handle inter share copies XXXX */
    DEBUG(3,("Rejecting inter-share copy\n"));
    return(ERROR(ERRSRV,ERRinvdevice));
  }

  unix_convert(name,conn,0,&bad_path1,NULL);
  unix_convert(newname,conn,0,&bad_path2,NULL);

  target_is_directory = dos_directory_exist(newname,NULL);

  if ((flags&1) && target_is_directory) {
    return(ERROR(ERRDOS,ERRbadfile));
  }

  if ((flags&2) && !target_is_directory) {
    return(ERROR(ERRDOS,ERRbadpath));
  }

  if ((flags&(1<<5)) && dos_directory_exist(name,NULL)) {
    /* wants a tree copy! XXXX */
    DEBUG(3,("Rejecting tree copy\n"));
    return(ERROR(ERRSRV,ERRerror));    
  }

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

  if (is_mangled(mask))
    check_mangled_cache( mask );

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

  if (!has_wild) {
    pstrcat(directory,"/");
    pstrcat(directory,mask);
    if (resolve_wildcards(directory,newname) && 
    copy_file(directory,newname,conn,ofun,
          count,target_is_directory,&err)) count++;
    if(!count && err) {
        errno = err;
        return(UNIXERROR(ERRHRD,ERRgeneral));
    }
    if (!count) exists = dos_file_exist(directory,NULL);
  } else {
    void *dirptr = NULL;
    char *dname;
    pstring destname;

    if (check_name(directory,conn))
      dirptr = OpenDir(conn, directory, True);

    if (dirptr) {
    error = ERRbadfile;

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

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

        error = ERRnoaccess;
        slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
        pstrcpy(destname,newname);
        if (resolve_wildcards(fname,destname) && 
        copy_file(directory,newname,conn,ofun,
              count,target_is_directory,&err)) count++;
        DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
      }
    CloseDir(dirptr);
    }
  }
  
  if (count == 0) {
    if(err) {
      /* Error on close... */
      errno = err;
      return(UNIXERROR(ERRHRD,ERRgeneral));
    }

    if (exists)
      return(ERROR(ERRDOS,error));
    else
    {
      if((errno == ENOENT) && (bad_path1 || bad_path2))
      {
        unix_ERR_class = ERRDOS;
        unix_ERR_code = ERRbadpath;
      }
      return(UNIXERROR(ERRDOS,error));
    }
  }
  
  outsize = set_message(outbuf,1,0,True);
  SSVAL(outbuf,smb_vwv0,count);

  return(outsize);
}



/****************************************************************************
  reply to an open and X
****************************************************************************/
int reply_open_and_X(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  pstring fname;
  int smb_mode = SVAL(inbuf,smb_vwv3);
  int smb_attr = SVAL(inbuf,smb_vwv5);
  /* Breakout the oplock request bits so we can set the
     reply bits separately. */
  BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf);
  BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf);
  BOOL oplock_request = ex_oplock_request | core_oplock_request;
#if 0
  int open_flags = SVAL(inbuf,smb_vwv2);
  int smb_sattr = SVAL(inbuf,smb_vwv4); 
  uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
#endif
  int smb_ofun = SVAL(inbuf,smb_vwv8);
  mode_t unixmode;
  SMB_OFF_T size=0;
  int fmode=0,mtime=0,rmode=0;
  SMB_STRUCT_STAT sbuf;
  int smb_action = 0;
  BOOL bad_path = False;
  files_struct *fsp;

  /* If it's an IPC, pass off the pipe handler. */
  if (IS_IPC(conn) && lp_nt_pipe_support())
    return reply_open_pipe_and_X(aclient, conn, inbuf,outbuf,length,bufsize);

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

  pstrcpy(fname,smb_buf(inbuf));
  unix_convert(fname,conn,0,&bad_path,NULL);
    
  fsp = file_new();
  if (!fsp)
    return(ERROR(ERRSRV,ERRnofids));

  if (!check_name(fname,conn))
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    file_free(fsp);
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }

  unixmode = unix_mode(conn,smb_attr | aARCH);
      
  open_file_shared(fsp,conn,fname,smb_mode,smb_ofun,unixmode,
                   oplock_request, &rmode,&smb_action);
      
  if (!fsp->open)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    file_free(fsp);
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }

  if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
    close_file(fsp,False);
    return(ERROR(ERRDOS,ERRnoaccess));
  }

  size = sbuf.st_size;
  fmode = dos_mode(conn,fname,&sbuf);
  mtime = sbuf.st_mtime;
  if (fmode & aDIR) {
    close_file(fsp,False);
    return(ERROR(ERRDOS,ERRnoaccess));
  }

  /* If the caller set the extended oplock request bit
     and we granted one (by whatever means) - set the
     correct bit for extended oplock reply.
   */

  if (ex_oplock_request && lp_fake_oplocks(SNUM(conn))) {
    smb_action |= EXTENDED_OPLOCK_GRANTED;
  }

  if(ex_oplock_request && fsp->granted_oplock) {
    smb_action |= EXTENDED_OPLOCK_GRANTED;
  }

  /* If the caller set the core oplock request bit
     and we granted one (by whatever means) - set the
     correct bit for core oplock reply.
   */

  if (core_oplock_request && lp_fake_oplocks(SNUM(conn))) {
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  }

  if(core_oplock_request && fsp->granted_oplock) {
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  }

  set_message(outbuf,15,0,True);
  SSVAL(outbuf,smb_vwv2,fsp->fnum);
  SSVAL(outbuf,smb_vwv3,fmode);
  if(lp_dos_filetime_resolution(SNUM(conn)) )
    put_dos_date3(outbuf,smb_vwv4,mtime & ~1);
  else
    put_dos_date3(outbuf,smb_vwv4,mtime);
  SIVAL(outbuf,smb_vwv6,(uint32)size);
  SSVAL(outbuf,smb_vwv8,rmode);
  SSVAL(outbuf,smb_vwv11,smb_action);

  return chain_reply(aclient,inbuf,outbuf,length,bufsize);
}


/****************************************************************************
  reply to a read and X
****************************************************************************/
int reply_read_and_X(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  files_struct *fsp = file_fsp(inbuf,smb_vwv2);
  SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3);
  size_t smb_maxcnt = SVAL(inbuf,smb_vwv5);
  size_t smb_mincnt = SVAL(inbuf,smb_vwv6);
  ssize_t nread = -1;
  char *data;

  /* If it's an IPC, pass off the pipe handler. */
  if (IS_IPC(conn))
    return reply_pipe_read_and_X(aclient,inbuf,outbuf,length,bufsize);

  CHECK_FSP(fsp,conn);
  CHECK_READ(fsp);
  CHECK_ERROR(fsp);

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

  if(CVAL(inbuf,smb_wct) == 12) {
#ifdef LARGE_SMB_OFF_T
    /*
     * This is a large offset (64 bit) read.
     */
    startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32);

#else /* !LARGE_SMB_OFF_T */

    /*
     * Ensure we haven't been sent a >32 bit offset.
     */

    if(IVAL(inbuf,smb_vwv10) != 0) {
      DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \
64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) ));
      return(ERROR(ERRDOS,ERRbadaccess));
    }

#endif /* LARGE_SMB_OFF_T */

  }

  if (is_locked(fsp,conn,smb_maxcnt,startpos, F_RDLCK))
    return(ERROR(ERRDOS,ERRlock));
  nread = read_file(fsp,data,startpos,smb_maxcnt);
  
  if (nread < 0)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  SSVAL(outbuf,smb_vwv5,nread);
  SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
  SSVAL(smb_buf(outbuf),-2,nread);
  
  DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n",
          fsp->fnum, smb_mincnt, smb_maxcnt, nread ) );

  return chain_reply(aclient,inbuf,outbuf,length,bufsize);
}



/****************************************************************************
  reply to a write and X
****************************************************************************/
int reply_write_and_X(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  files_struct *fsp = file_fsp(inbuf,smb_vwv2);
  SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3);
  size_t numtowrite = SVAL(inbuf,smb_vwv10);
  BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
  ssize_t nwritten = -1;
  int smb_doff = SVAL(inbuf,smb_vwv11);
  char *data;

  /* If it's an IPC, pass off the pipe handler. */
  if (IS_IPC(conn))
    return reply_pipe_write_and_X(aclient,inbuf,outbuf,length,bufsize);

  CHECK_FSP(fsp,conn);
  CHECK_WRITE(fsp);
  CHECK_ERROR(fsp);

  data = smb_base(inbuf) + smb_doff;

  if(CVAL(inbuf,smb_wct) == 14) {
#ifdef LARGE_SMB_OFF_T
    /*
     * This is a large offset (64 bit) write.
     */
    startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32);

#else /* !LARGE_SMB_OFF_T */

    /*
     * Ensure we haven't been sent a >32 bit offset.
     */

    if(IVAL(inbuf,smb_vwv12) != 0) {
      DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \
64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) ));
      return(ERROR(ERRDOS,ERRbadaccess));
    }

#endif /* LARGE_SMB_OFF_T */
  }

  if (is_locked(fsp,conn,numtowrite,startpos, F_WRLCK))
    return(ERROR(ERRDOS,ERRlock));

  if(seek_file(fsp,startpos) == -1)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  /* 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(numtowrite == 0)
    nwritten = 0;
  else
    nwritten = write_file(fsp,data,numtowrite);
  
  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  set_message(outbuf,6,0,True);
  
  SSVAL(outbuf,smb_vwv2,nwritten);
  
  if (nwritten < (ssize_t)numtowrite) {
    CVAL(outbuf,smb_rcls) = ERRHRD;
    SSVAL(outbuf,smb_err,ERRdiskfull);      
  }

  DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n",
       fsp->fnum, numtowrite, nwritten));

  if (lp_syncalways(SNUM(conn)) || write_through)
    sync_file(conn,fsp);

  return chain_reply(aclient,inbuf,outbuf,length,bufsize);
}

/****************************************************************************
 Get a lock count, dealing with large count requests.
****************************************************************************/

SMB_OFF_T get_lock_count( char *data, int data_offset, BOOL large_file_format, BOOL *err)
{
  SMB_OFF_T count = 0;

  *err = False;

  if(!large_file_format) {
    count = (SMB_OFF_T)IVAL(data,SMB_LKLEN_OFFSET(data_offset));
  } else {
#ifdef LARGE_SMB_OFF_T
    count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) |
            ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)));
#else /* !LARGE_SMB_OFF_T */
    /*
     * NT4.x seems to be broken in that it sends large file
     * lockingX calls even if the CAP_LARGE_FILES was *not*
     * negotiated. JRA.
     */
      
    if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0){
      /*
       * Before we error out, see if we can sensibly map the top bits
       * down to the lower bits - or lose the top bits if they are all 1's.
       * It seems that NT has this horrible bug where it will send 64 bit
       * lock requests even if told not to. JRA.
       */

      if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) == (uint32)0xFFFFFFFF)
        count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset));
      else if (IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) == (uint32)0xFFFFFFFF)
        count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset));
      else {

        DEBUG(0,("get_lock_count: Error : a large file count (%x << 32 | %x) was sent and we don't \
support large counts.\n", (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)),
            (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) ));

        *err = True;
        return (SMB_OFF_T)-1;
      }
    }
    else
      count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset));

#endif /* LARGE_SMB_OFF_T */
  }
  return count;
}

/****************************************************************************
 Get a lock offset, dealing with large offset requests.
****************************************************************************/

SMB_OFF_T get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err)
{
  SMB_OFF_T offset = 0;

  *err = False;

  if(!large_file_format) {
    offset = (SMB_OFF_T)IVAL(data,SMB_LKOFF_OFFSET(data_offset));
  } else {
#ifdef LARGE_SMB_OFF_T
    offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) |
            ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)));
#else /* !LARGE_SMB_OFF_T */
    /*
     * NT4.x seems to be broken in that it sends large file
     * lockingX calls even if the CAP_LARGE_FILES was *not*
     * negotiated. JRA.
     */
      
    if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0){
      /*
       * Before we error out, see if we can sensibly map the top bits
       * down to the lower bits - or lose the top bits if they are all 1's.
       * It seems that NT has this horrible bug where it will send 64 bit
       * lock requests even if told not to. JRA.
       */

      if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)) == (uint32)0xFFFFFFFF)
        offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset));
      else if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) == (uint32)0xFFFFFFFF)
        offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
      else {

        DEBUG(0,("get_lock_count: Error : a large file offset (%x << 32 | %x) was sent and we don't \
support large offsets.\n", (unsigned int)IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)),
            (unsigned int)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)) ));

        *err = True;
        return (SMB_OFF_T)-1;
      }
    }
    else
      offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));

#endif /* LARGE_SMB_OFF_T */
  }
  return offset;
}

/****************************************************************************
  reply to a lockingX request
****************************************************************************/

int reply_lockingX(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  files_struct *fsp = file_fsp(inbuf,smb_vwv2);
  unsigned char locktype = CVAL(inbuf,smb_vwv3);
#if 0
  unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
#endif
  uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
  uint16 num_locks = SVAL(inbuf,smb_vwv7);
  SMB_OFF_T count = 0, offset = 0;
  int32 lock_timeout = IVAL(inbuf,smb_vwv4);
  int i;
  char *data;
  uint32 ecode=0, dummy2;
  int eclass=0, dummy1;
  BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
  BOOL err1, err2;

  CHECK_FSP(fsp,conn);
  CHECK_ERROR(fsp);

  data = smb_buf(inbuf);

  /* Check if this is an oplock break on a file
     we have granted an oplock on.
   */
  if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE))
  {
    int token;
    SMB_DEV_T dev = fsp->fd_ptr->dev;
    SMB_INO_T inode = fsp->fd_ptr->inode;

    DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n",
              fsp->fnum));
    /*
     * Make sure we have granted an oplock on this file.
     */
    if(!fsp->granted_oplock)
    {
      DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \
no oplock granted on this file.\n", fsp->fnum));

      /* if this is a pure oplock break request then don't send a reply */
      if (num_locks == 0 && num_ulocks == 0)
        return -1;
      else
        return ERROR(ERRDOS,ERRlock);
    }

    /* Remove the oplock flag from the sharemode. */
    lock_share_entry(fsp->conn, dev, inode, &token);
    if(remove_share_oplock(token, fsp)==False) {
      DEBUG(0,("reply_lockingX: failed to remove share oplock for fnum %d, \
dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode));
    }

    release_file_oplock(fsp);
    unlock_share_entry(fsp->conn, dev, inode, token);

    /* if this is a pure oplock break request then don't send a reply */
    if (num_locks == 0 && num_ulocks == 0)
    {
      /* Sanity check - ensure a pure oplock break is not a
         chained request. */
      if(CVAL(inbuf,smb_vwv0) != 0xff)
        DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n",
                 (unsigned int)CVAL(inbuf,smb_vwv0) ));
      return -1;
    }
  }

  /* Data now points at the beginning of the list
     of smb_unlkrng structs */
  for(i = 0; i < (int)num_ulocks; i++) {
    count = get_lock_count( data, i, large_file_format, &err1);
    offset = get_lock_offset( data, i, large_file_format, &err2);

    /*
     * There is no error code marked "stupid client bug".... :-).
     */
    if(err1 || err2)
      return ERROR(ERRDOS,ERRnoaccess);

    DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for file %s\n",
          (double)offset, (double)count, fsp->fsp_name ));

    if(!do_unlock(fsp,conn,count,offset,&eclass, &ecode))
      return ERROR(eclass,ecode);
  }

  /* Setup the timeout in seconds. */
  lock_timeout = ((lock_timeout == -1) ? -1 : lock_timeout/1000);

  /* Now do any requested locks */
  data += ((large_file_format ? 20 : 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 = get_lock_count( data, i, large_file_format, &err1);
    offset = get_lock_offset( data, i, large_file_format, &err2);

    /*
     * There is no error code marked "stupid client bug".... :-).
     */
    if(err1 || err2)
      return ERROR(ERRDOS,ERRnoaccess);
 
    DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for file %s\n",
          (double)offset, (double)count, fsp->fsp_name ));

    if(!do_lock(fsp,conn,count,offset, ((locktype & 1) ? F_RDLCK : F_WRLCK),
                &eclass, &ecode)) {
      if((ecode == ERRlock) && (lock_timeout != 0) && lp_blocking_locks(SNUM(conn))) {
        /*
         * A blocking lock was requested. Package up
         * this smb into a queued request and push it
         * onto the blocking lock queue.
         */
        if(push_blocking_lock_request(inbuf, length, lock_timeout, i))
          return -1;
      }
      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 = get_lock_count( data, i, large_file_format, &err1);
      offset = get_lock_offset( data, i, large_file_format, &err2);

      /*
       * There is no error code marked "stupid client bug".... :-).
       */
      if(err1 || err2)
        return ERROR(ERRDOS,ERRnoaccess);
 
      do_unlock(fsp,conn,count,offset,&dummy1,&dummy2);
    }
    return ERROR(eclass,ecode);
  }

  set_message(outbuf,2,0,True);
  
  DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n",
    fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) );

  return chain_reply(aclient,inbuf,outbuf,length,bufsize);
}


/****************************************************************************
  reply to a fclose (stop directory search)
****************************************************************************/
int reply_fclose(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  int outsize = 0;
  int status_len;
  char *path;
  char status[21];
  int dptr_num= -2;

  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,("search close\n"));

  return(outsize);
}


/****************************************************************************
 always return an error: it's just a matter of which one...
 ****************************************************************************/
static int session_trust_account(connection_struct *conn, char *inbuf, char *outbuf, char *user,
                                char *smb_passwd, int smb_passlen,
                                char *smb_nt_passwd, int smb_nt_passlen)
{
  struct smb_passwd *smb_trust_acct = NULL; /* check if trust account exists */

#if 0
  if (lp_security() == SEC_USER)
  {
    smb_trust_acct = getsmbpwnam(user);
  }
  else
  {  
#endif

    DEBUG(0,("session_trust_account: Trust account %s only supported with security = user\n", user));
    SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
    return(ERROR(0, 0xc0000000|NT_STATUS_LOGON_FAILURE));
#if 0
  }

  if (smb_trust_acct == NULL)
  {
    /* lkclXXXX: workstation entry doesn't exist */
    DEBUG(0,("session_trust_account: Trust account %s user doesn't exist\n",user));
    SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
    return(ERROR(0, 0xc0000000|NT_STATUS_NO_SUCH_USER));
  }
  else
  {
    if ((smb_passlen != 24) || (smb_nt_passlen != 24))
    {
      DEBUG(0,("session_trust_account: Trust account %s - password length wrong.\n", user));
      SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
      return(ERROR(0, 0xc0000000|NT_STATUS_LOGON_FAILURE));
    }

    if (!smb_password_ok(smb_trust_acct, NULL, (unsigned char *)smb_passwd, (unsigned char *)smb_nt_passwd))
    {
      DEBUG(0,("session_trust_account: Trust Account %s - password failed\n", user));
      SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
      return(ERROR(0, 0xc0000000|NT_STATUS_LOGON_FAILURE));
    }

    if (IS_BITS_SET_ALL(smb_trust_acct->acct_ctrl, ACB_DOMTRUST))
    {
      DEBUG(0,("session_trust_account: Domain trust account %s denied by server\n",user));
      SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
      return(ERROR(0, 0xc0000000|NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT));
    }

    if (IS_BITS_SET_ALL(smb_trust_acct->acct_ctrl, ACB_SVRTRUST))
    {
      DEBUG(0,("session_trust_account: Server trust account %s denied by server\n",user));
      SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
      return(ERROR(0, 0xc0000000|NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT));
    }

    if (IS_BITS_SET_ALL(smb_trust_acct->acct_ctrl, ACB_WSTRUST))
    {
      DEBUG(4,("session_trust_account: Wksta trust account %s denied by server\n", user));
      SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
      return(ERROR(0, 0xc0000000|NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT));
    }
  }

  /* don't know what to do: indicate logon failure */
  SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
  return(ERROR(0, 0xc0000000|NT_STATUS_LOGON_FAILURE));
#endif

}

static BOOL check_server_security(char *orig_user, char *domain, char *unix_user,
                                  char *smb_apasswd, int smb_apasslen,
                                  char *smb_ntpasswd, int smb_ntpasslen)
{
  BOOL ret = False;

  if(lp_security() != SEC_SERVER)
    return False;

#if 0
  ret = server_validate(orig_user, domain, 
                            smb_apasswd, smb_apasslen, 
                            smb_ntpasswd, smb_ntpasslen);
  if(ret) {
    /*
     * User validated ok against Domain controller.
     * If the admin wants us to try and create a UNIX
     * user on the fly, do so.
     * Note that we can never delete users when in server
     * level security as we never know if it was a failure
     * due to a bad password, or the user really doesn't exist.
     */
    if(lp_adduser_script() && !Get_Pwnam(unix_user,True)) {
      smb_create_user(unix_user);
    }
  }
#endif

  return ret;
}

/****************************************************************************
 Check for a valid username and password in security=domain mode.
****************************************************************************/

static BOOL check_domain_security(char *orig_user, char *domain, char *unix_user, 
                                  char *smb_apasswd, int smb_apasslen,
                                  char *smb_ntpasswd, int smb_ntpasslen)
{
  BOOL ret = False;
  BOOL user_exists = True;

  if(lp_security() != SEC_DOMAIN)
    return False;
#if 0
  ret = domain_client_validate(orig_user, domain,
                                smb_apasswd, smb_apasslen,
                                smb_ntpasswd, smb_ntpasslen,
                                &user_exists);

  if(ret) {
    /*
     * User validated ok against Domain controller.
     * If the admin wants us to try and create a UNIX
     * user on the fly, do so.
     */
    if(user_exists && lp_adduser_script() && !Get_Pwnam(unix_user,True)) {
      smb_create_user(unix_user);
    }
  } else {
    /*
     * User failed to validate ok against Domain controller.
     * If the failure was "user doesn't exist" and admin 
     * wants us to try and delete that UNIX user on the fly,
     * do so.
     */
    if(!user_exists && lp_deluser_script() && Get_Pwnam(unix_user,True)) {
      smb_delete_user(unix_user);
    }
  }
#endif
  return ret;
}

/****************************************************************************
reply to a session setup command
****************************************************************************/

int reply_sesssetup_and_X(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int length, 
             int bufsize)
{
  uint16 sess_vuid;
  int gid;
  int uid;
  int   smb_bufsize;    
  int   smb_apasslen = 0;   
  pstring smb_apasswd;
  int   smb_ntpasslen = 0;   
  pstring smb_ntpasswd;
  BOOL valid_nt_password = False;
  pstring user;
  pstring orig_user;
  BOOL guest=False;
  static BOOL done_sesssetup = False;
  BOOL doencrypt = SMBENCRYPT();
  char *domain = "";

  *smb_apasswd = 0;
  *smb_ntpasswd = 0;
  
  smb_bufsize = SVAL(inbuf,smb_vwv2);

  if (Protocol < PROTOCOL_NT1) {
    smb_apasslen = SVAL(inbuf,smb_vwv7);
    if (smb_apasslen > MAX_PASS_LEN)
    {
        overflow_attack(smb_apasslen);
    }

    memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
    smb_apasswd[smb_apasslen] = 0;
    pstrcpy(user,smb_buf(inbuf)+smb_apasslen);

    if (!doencrypt && (lp_security() != SEC_SERVER)) {
        smb_apasslen = strlen(smb_apasswd);
    }
  } else {
    uint16 passlen1 = SVAL(inbuf,smb_vwv7);
    uint16 passlen2 = SVAL(inbuf,smb_vwv8);
    enum remote_arch_types ra_type = get_remote_arch();
    char *p = smb_buf(inbuf);    

    global_client_caps = IVAL(inbuf,smb_vwv11);

    /* client_caps is used as final determination if client is NT or Win95. 
       This is needed to return the correct error codes in some
       circumstances.
     */
    
    if(ra_type == RA_WINNT || ra_type == RA_WIN95)
    {
      if(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))
        set_remote_arch( RA_WINNT);
      else
        set_remote_arch( RA_WIN95);
    }

    if (passlen1 != 24 && passlen2 != 24)
      doencrypt = False;

    if (passlen1 > MAX_PASS_LEN) {
        overflow_attack(passlen1);
    }

    passlen1 = MIN(passlen1, MAX_PASS_LEN);
    passlen2 = MIN(passlen2, MAX_PASS_LEN);

    if(!doencrypt) {
       /* both Win95 and WinNT stuff up the password lengths for
          non-encrypting systems. Uggh. 
      
          if passlen1==24 its a win95 system, and its setting the
          password length incorrectly. Luckily it still works with the
          default code because Win95 will null terminate the password
          anyway 

          if passlen1>0 and passlen2>0 then maybe its a NT box and its
          setting passlen2 to some random value which really stuffs
          things up. we need to fix that one.  */

      if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1)
        passlen2 = 0;
    }

    if(doencrypt || ((lp_security() == SEC_SERVER) || (lp_security() == SEC_DOMAIN))) {
      /* Save the lanman2 password and the NT md4 password. */
      smb_apasslen = passlen1;
      memcpy(smb_apasswd,p,smb_apasslen);
      smb_apasswd[smb_apasslen] = 0;
      smb_ntpasslen = passlen2;
      memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen);
      smb_ntpasswd[smb_ntpasslen] = 0;
    } else {
      /* we use the first password that they gave */
      smb_apasslen = passlen1;
      StrnCpy(smb_apasswd,p,smb_apasslen);      
      
      /* trim the password */
      smb_apasslen = strlen(smb_apasswd);

      /* wfwg sometimes uses a space instead of a null */
      if (strequal(smb_apasswd," ")) {
    smb_apasslen = 0;
    *smb_apasswd = 0;
      }
    }
    
    p += passlen1 + passlen2;
    fstrcpy(user,p); p = skip_string(p,1);
    domain = p;

    fstrcpy(aclient->nativeOs,skip_string(p,1));
    fstrcpy(aclient->nativeLanMan,skip_string(p,2));  

    DEBUG(3,("Domain=[%s]  NativeOS=[%s] NativeLanMan=[%s]\n",
         domain,aclient->nativeOs,aclient->nativeLanMan));
  }


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

  /* If name ends in $ then I think it's asking about whether a */
  /* computer with that name (minus the $) has access. For now */
  /* say yes to everything ending in $. */
  if ((user[strlen(user) - 1] == '$') && (smb_apasslen == 24) && (smb_ntpasslen == 24))
  {
    return session_trust_account(conn, inbuf, outbuf, user, 
                                 smb_apasswd, smb_apasslen,
                                 smb_ntpasswd, smb_ntpasslen);
  }

  /* If no username is sent use the guest account */
  if (!*user)
  {
    pstrcpy(user,lp_guestaccount(-1));
    /* If no user and no password then set guest flag. */
    if( *smb_apasswd == 0)
      guest = True;
  }

  strlower(user);

  /*
   * In share level security, only overwrite sesssetup_use if
   * it's a non null-session share. Helps keep %U and %G
   * working.
   */

  if((lp_security() != SEC_SHARE) || (*user && !guest))
    pstrcpy(sesssetup_user,user);

  reload_services(True);

  /*
   * Save the username before mapping. We will use
   * the original username sent to us for security=server
   * and security=domain checking.
   */

  pstrcpy( orig_user, user);

  /*
   * Pass the user through the NT -> unix user mapping
   * function.
   */
   
  (void)map_username(user);

  /*
   * Do any UNIX username case mangling.
   */
  (void)Get_Pwnam( user, True);

  add_session_user(user);

  /*
   * Check if the given username was the guest user with no password.
   */

  if(!guest && strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0))
    guest = True;

  /* 
   * Check with orig_user for security=server and
   * security=domain.
   */

  if (!guest && 
      !check_server_security(orig_user, domain, user,
                             smb_apasswd, smb_apasslen,
                             smb_ntpasswd, smb_ntpasslen) &&
      !check_domain_security(orig_user, domain, user,
                             smb_apasswd, smb_apasslen,
                             smb_ntpasswd, smb_ntpasslen) &&
      !check_hosts_equiv(user)
     )
  {

    /* 
     * If we get here then the user wasn't guest and the remote
     * authentication methods failed. Check the authentication
     * methods on this local server.
     *
     * If an NT password was supplied try and validate with that
     * first. This is superior as the passwords are mixed case 
     * 128 length unicode.
      */

    if(smb_ntpasslen)
    {
      if(!password_ok(user, smb_ntpasswd,smb_ntpasslen,NULL))
        DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n"));
      else
        valid_nt_password = True;
    } 

    if (!valid_nt_password && !password_ok(user, smb_apasswd,smb_apasslen,NULL))
    {
      if (lp_security() >= SEC_USER) 
      {
        if (lp_map_to_guest() == NEVER_MAP_TO_GUEST)
          return(ERROR(ERRSRV,ERRbadpw));

        if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER)
        {
         if (Get_Pwnam(user,True))
            return(ERROR(ERRSRV,ERRbadpw));
        }

        /*
         * ..else if lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD
         * Then always map to guest account - as done below.
         */
      }

      if (*smb_apasswd || !Get_Pwnam(user,True))
         pstrcpy(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));
    pstrcpy(user,lp_guestaccount(-1));
    guest = True;
  }

  if (!strequal(user,lp_guestaccount(-1)) &&
      lp_servicenumber(user) < 0)      
  {
    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) {
    set_message(outbuf,3,0,True);
  } else {
    char *p;
    set_message(outbuf,3,3,True);
    p = smb_buf(outbuf);
    pstrcpy(p,"Unix"); p = skip_string(p,1);
    pstrcpy(p,"Samba "); pstrcat(p,VERSION); p = skip_string(p,1);
    pstrcpy(p,global_myworkgroup); p = skip_string(p,1);
    set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
    /* perhaps grab OS version here?? */
  }

  /* Set the correct uid in the outgoing and incoming packets
     We will use this on future requests to determine which
     user we should become.
     */
  {
    struct passwd *pw = Get_Pwnam(user,False);
    if (!pw) {
      DEBUG(1,("Username %s is invalid on this system\n",user));
      return(ERROR(ERRSRV,ERRbadpw));
    }
    gid = pw->pw_gid;
    uid = pw->pw_uid;
  }

  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 */
  sess_vuid = register_vuid(uid,gid,user,sesssetup_user,guest);
 
  SSVAL(outbuf,smb_uid,sess_vuid);
  SSVAL(inbuf,smb_uid,sess_vuid);

  if (!done_sesssetup)
    max_send = MIN(max_send,smb_bufsize);

  DEBUG(6,("Client requested max send size of %d\n", max_send));

  done_sesssetup = True;

  return chain_reply(aclient,inbuf,outbuf,length,bufsize);
}
                             



