/* 
   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: samba,v 1.10 2000/04/17 16:46:01 david Exp $
 *
 * HISTORY
 * $Log: samba,v $
 * Revision 1.10  2000/04/17 16:46:01  david
 * updates after testing event poll word non zero stuff
 *
 * Revision 1.9  2000/04/02 16:57:08  david
 * Updates to use the Internet event, use wimp poll event,
 * use non blocking sockets correctly,
 * compile libs in format suitable for RISCOS module
 * new nmbd module
 *
 * Revision 1.8  2000/01/29 17:19:51  david
 * Updates to support passwords
 * upped to version 0.06
 *
 * Revision 1.7  2000/01/11 20:14:40  david
 * Fixed Disk Size and Free Space reporting.
 * Fixed Workgroup name reporting
 * Upped version to 0.05a
 *
 * Revision 1.6  1999/11/28 12:05:41  david
 * Changed SAFTEY_MARGIN to be the same as MAX_RECV so that smbserver
 * will always be able to receive a buffer's worth of data even if there
 * is a maximum size message (65539bytes) in the input buffer.
 *
 * Revision 1.5  1999/11/23 22:00:16  david
 * Updates for long filename support.
 * Added files for Coreplus, Lanman1, Lanman2 and NT protorocols.
 *
 * Revision 1.4  1999/11/07 15:11:53  david
 * version:updated!
 * wimp:no longer used -> now using toolbox lib instead
 *
 * Revision 1.3  1999/08/08 11:51:21  david
 * Upped version to 0.03a
 * Added Boolean to samba to switch off file type creation.
 *
 * Revision 1.2  1999/06/20 13:41:38  david
 * Various updates - moved some stuff to reply and process
 *
 * Revision 1.1  1999/05/16 12:00:07  david
 * Initial revision
 *
 *
 *****************************************************************************/

#include <time.h>


#include "includes.h"

int DEBUGLEVEL=10;

int max_send=MAX_SEND;
int max_recv=MAX_RECV;
int global_client_caps;
  
fstring local_machine;
fstring remote_machine;
fstring global_myworkgroup;
extern pstring global_myname;
extern pstring myhostname;
pstring sesssetup_user,samlogon_user;
int sam_logon_in_ssb=0;
int Client;
extern time_t smb_last_time/*=(time_t)0*/;
BOOL case_preserve=False;
BOOL short_case_preserve=False;
BOOL case_mangle=False;
extern BOOL case_sensitive;
BOOL use_mangled_map=False;
extern BOOL smbDontStripFileExt;
extern BOOL setFileType;
extern BOOL appendFileType;
extern BOOL stripExtension;
extern BOOL detectDosParts;
int  smb_read_error=0;
unsigned int smb_echo_count = 0;
int dontConvertName=0;
int last_message;
connection_struct * g_conn=NULL;

struct current_user current_user;

extern char *last_inbuf /*= NULL*/;
int Protocol=PROTOCOL_CORE; 


/****************************************************************************
report a possible attack via the password buffer overflow bug
****************************************************************************/
void overflow_attack(int len)
{
        if( DEBUGLVL( 0 ) )
          {
      dbgtext( "ERROR: Invalid password length %d.\n", len );
      dbgtext( "Your machine may be under attack by someone " );
          dbgtext( "attempting to exploit an old bug.\n" );
      dbgtext( "Attack was from IP = %s.\n", client_addr(Client) );
          }
    exit_server("possible attack");
}

/****************************************************************************
  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;
    int len;
    char name_type = 0;
    
    *name1 = *name2 = 0;
    
    memset(outbuf,'\0',smb_size);

    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 || 
            name_len(inbuf+4 + 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));      

        fstrcpy(remote_machine,name2);
        remote_machine[15] = 0;
        trim_string(remote_machine," "," ");
        strlower(remote_machine);

        fstrcpy(local_machine,name1);
        len = strlen(local_machine);
        if (len == 16) {
            name_type = local_machine[15];
            local_machine[15] = 0;
        }
        trim_string(local_machine," "," ");
        strlower(local_machine);

        if (name_type == 'R') {
            /* We are being asked for a pathworks session --- 
               no thanks! */
            CVAL(outbuf, 0) = 0x83;
            break;
        }

        add_session_user(remote_machine);

        reload_services(True);
        /*reopen_logs();*/

        if (lp_status(-1)) {
            claim_connection(NULL,"STATUS.",MAXSTATUS,True);
        }

        break;
        
    case 0x89: /* session keepalive request 
              (some old clients produce this?) */
        CVAL(outbuf,0) = 0x85;
        CVAL(outbuf,3) = 0;
        break;
        
    case 0x82: /* positive session response */
    case 0x83: /* negative session response */
    case 0x84: /* retarget session response */
        DEBUG(0,("Unexpected session response\n"));
        break;
        
    case 0x85: /* session keepalive */
    default:
        return(0);
    }
    
    DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n",
            msg_type, msg_flags));
    
    return(outsize);
}

BOOL check_conn(connection_struct *conn)
{
  BOOL result=True;

  if (conn==NULL)
  {              
    DEBUG(3,("Error check_conn - conn is NULL\n"));
    result=False;
  }              

  return result;
}

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


/****************************************************************************
  parse a share descriptor string
****************************************************************************/
static void parse_connect(char *p,char *service,char *user,
              char *password,int *pwlen,char *dev)
{
  char *p2;

  DEBUG(4,("parsing connect string %s\n",p));
    
  p2 = strrchr(p,'\\');
  if (p2 == NULL)
    fstrcpy(service,p);
  else
    fstrcpy(service,p2+1);
  
  p += strlen(p) + 2;
  
  fstrcpy(password,p);
  *pwlen = strlen(password);

  p += strlen(p) + 2;

  fstrcpy(dev,p);
  
  *user = 0;
  p = strchr(service,'%');
  if (p != NULL)
    {
      *p = 0;
      fstrcpy(user,p+1);
    }
}

int connection_error(char *inbuf,char *outbuf,int ecode)
{
    if (ecode == ERRnoipc) {
        return(ERROR(ERRDOS,ERRnoipc));
    }

    return(ERROR(ERRSRV,ecode));
}


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

  if (!CAN_WRITE(conn)) return(False);

  if (dos_lstat(fname,&sbuf) != 0) return(False);
  fmode = dos_mode(conn,fname,&sbuf);
  if (fmode & aDIR) return(False);
  if (!lp_delete_readonly(SNUM(conn))) {
    if (fmode & aRONLY) return(False);
  }
  if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
    return(False);
  if (!check_file_sharing(conn,fname,False)) return(False);
  return(True);
}

/****************************************************************************
Static function used by reply_rmdir to delete an entire directory
tree recursively.
****************************************************************************/

static BOOL recursive_rmdir(char *directory)
{
  char *dname = NULL;
  BOOL ret = False;
  void *dirptr = OpenDir(NULL, directory, False);

  if(dirptr == NULL)
    return True;

  while((dname = ReadDirName(dirptr)))
  {
    pstring fullname;
    SMB_STRUCT_STAT st;

    if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
      continue;

    /* Construct the full name. */
    if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname))
    {
      errno = ENOMEM;
      ret = True;
      break;
    }
    pstrcpy(fullname, directory);
    pstrcat(fullname, "/");
    pstrcat(fullname, dname);

    if(dos_lstat(fullname, &st) != 0)
    {
      ret = True;
      break;
    }

    if(st.st_mode & S_IFDIR)
    {
      if(recursive_rmdir(fullname)!=0)
      {
        ret = True;
        break;
      }
      if(dos_rmdir(fullname) != 0)
      {
        ret = True;
        break;
      }
    }
    else if(dos_unlink(fullname) != 0)
    {
      ret = True;
      break;
    }
  }
  CloseDir(dirptr);
  return ret;
}

/****************************************************************************
 The internals of the rmdir code - called elsewhere.
****************************************************************************/
        
#define ENOTEMPTY 100

BOOL rmdir_internals(connection_struct *conn, char *directory)
{
  BOOL ok;

  ok = (dos_rmdir(directory) == 0);
  if(!ok && (errno == ENOTEMPTY) && lp_veto_files(SNUM(conn)))
  {
    /* 
     * Check to see if the only thing in this directory are
     * vetoed files/directories. If so then delete them and
     * retry. If we fail to delete any of them (and we *don't*
     * do a recursive delete) then fail the rmdir.
     */
    BOOL all_veto_files = True;
    char *dname;
    void *dirptr = OpenDir(conn, directory, False);

    if(dirptr != NULL)
    {
      int dirpos = TellDir(dirptr);
      while ((dname = ReadDirName(dirptr)))
      {
        if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
          continue;
        if(!IS_VETO_PATH(conn, dname))
        {
          all_veto_files = False;
          break;
        }
      }
      if(all_veto_files)
      {
        SeekDir(dirptr,dirpos);
        while ((dname = ReadDirName(dirptr)))
        {
          pstring fullname;
          SMB_STRUCT_STAT st;

          if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
            continue;

          /* Construct the full name. */
          if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname))
          {
            errno = ENOMEM;
            break;
          }
          pstrcpy(fullname, directory);
          pstrcat(fullname, "/");
          pstrcat(fullname, dname);
                     
          if(dos_lstat(fullname, &st) != 0)
            break;
          if(st.st_mode & S_IFDIR)
          {
            if(lp_recursive_veto_delete(SNUM(conn)))
            {
              if(recursive_rmdir(fullname) != 0)
                break;
            }
            if(dos_rmdir(fullname) != 0)
              break;
          }
          else if(dos_unlink(fullname) != 0)
            break;
        }
        CloseDir(dirptr);
        /* Retry the rmdir */
        ok = (dos_rmdir(directory) == 0);
      }
      else
        CloseDir(dirptr);
    }
    else
      errno = ENOTEMPTY;
  }
          
  if (!ok)
    DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n",
          directory,strerror(errno)));

  return ok;
}


/****************************************************************************
  reply to a exit
****************************************************************************/
int reply_exit(tpClients aclient,
               connection_struct *conn,
               tpSmbHeader smbHeader, 
               char *inbuf,
               char *outbuf, 
               int dum_size, 
               int dum_buffsize)
{
    int outsize = set_message(outbuf,0,0,True);
    DEBUG(3,("exit\n"));

    return(outsize);
}


/****************************************************************************
  reply to an ioctl
****************************************************************************/
int reply_ioctl(tpClients aclient,
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf, 
                int dum_size, 
                int dum_buffsize)
{
    DEBUG(3,("ignoring ioctl\n"));
#if 0
    /* we just say it succeeds and hope its all OK. 
       some day it would be nice to interpret them individually */
    return set_message(outbuf,1,0,True); 
#else
    return(ERROR(ERRSRV,ERRnosupport));
#endif
}


/****************************************************************************
  reply to a echo
****************************************************************************/
int reply_echo(tpClients aclient,
               connection_struct *conn,
               tpSmbHeader smbHeader,
               char *inbuf,
               char *outbuf,
               int dum_size, 
               int dum_buffsize)
{
    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);
    
    /* 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(&Client,outbuf);
    }

    DEBUG(3,("echo %d times\n", smb_reverb));

    smb_echo_count++;

    return -1;
}


/****************************************************************************
  reply to a setatr
****************************************************************************/
int reply_setatr(tpClients aclient,
                 connection_struct *conn,
                 tpSmbHeader smbHeader,
                 char *inbuf,
                 char *outbuf,
                 int dum_size, 
                 int dum_buffsize)
{
  pstring fname;
  int outsize = 0;
  BOOL ok=False;
  int mode;
  time_t mtime;
  SMB_STRUCT_STAT st;
  BOOL bad_path = False;
 
  pstrcpy(fname,smb_buf(inbuf) + 1);
  unix_convert(fname,conn,0,&bad_path,&st);

  mode = SVAL(inbuf,smb_vwv0);
  mtime = make_unix_date3(inbuf+smb_vwv1);
  
  if (VALID_STAT_OF_DIR(st) || dos_directory_exist(fname,NULL))
    mode |= aDIR;
  if (check_name(fname,conn))
    ok =  (file_chmod(conn,fname,mode,NULL) == 0);
  if (ok)
    ok = set_filetime(conn,fname,mtime);
  
  if (!ok)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }

    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }
 
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) );
  
  return(outsize);
}

/****************************************************************************
  reply to a getatr
****************************************************************************/
int reply_getatr(tpClients aclient,
                 connection_struct *conn,
                 tpSmbHeader smbHeader,
                 char *inbuf,
                 char *outbuf, 
                 int dum_size, 
                 int dum_buffsize)
{
  pstring fname;
  int outsize = 0;
  SMB_STRUCT_STAT sbuf;
  BOOL ok = False;
  int mode=0;
  SMB_OFF_T size=0;
  time_t mtime=0;
  BOOL bad_path = False;
 
  pstrcpy(fname,smb_buf(inbuf) + 1);

  /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
     under WfWg - weird! */
  if (! (*fname))
  {
    mode = aHIDDEN | aDIR;
    if (!CAN_WRITE(conn)) mode |= aRONLY;
    size = 0;
    mtime = 0;
    ok = True;
  }
  else
  {
    unix_convert(fname,conn,0,&bad_path,&sbuf);
    if (check_name(fname,conn))
    {
      if (VALID_STAT(sbuf) || dos_stat(fname,&sbuf) == 0)
      {
        mode = dos_mode(conn,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)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }

    return(UNIXERROR(ERRDOS,ERRbadfile));
  }
 
  outsize = set_message(outbuf,10,0,True);

  SSVAL(outbuf,smb_vwv0,mode);
  if(lp_dos_filetime_resolution(SNUM(conn)) )
    put_dos_date3(outbuf,smb_vwv1,mtime & ~1);
  else
    put_dos_date3(outbuf,smb_vwv1,mtime);
  SIVAL(outbuf,smb_vwv3,(uint32)size);

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

/****************************************************************************
  reply to a dskattr
****************************************************************************/
int reply_dskattr(tpClients aclient,
                 connection_struct *conn,
                 tpSmbHeader smbHeader,
                 char *inbuf,
                 char *outbuf, 
                 int dum_size, 
                 int dum_buffsize)
{
  int outsize = 0;
  SMB_BIG_UINT dfree,dsize,bsize;
  
  sys_disk_free(".",True,&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,("dskattr dfree=%d, dsize=%d, bsize=%d\n", 
         (unsigned int)dfree, dsize, bsize)); 

  return(outsize);
}


/****************************************************************************
  reply to a search
  Can be called from SMBsearch, SMBffirst or SMBfunique.
****************************************************************************/
int reply_search(tpClients aclient,
                 connection_struct *conn,
                 tpSmbHeader smbHeader, 
                 char *inbuf,
                 char *outbuf, 
                 int dum_size, 
                 int dum_buffsize)
{
  pstring mask;
  pstring directory;
  pstring fname;
  SMB_OFF_T size;
  int mode;
  time_t date;
  int dirtype;
  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;
  BOOL bad_path = False;
   
  if (check_conn(conn)==False)
  {
     return(ERROR(ERRDOS,ERRnofids));
  }

  *mask = *directory = *fname = 0;

  /* If we were called as SMBffirst then we must expect close. */
  if(CVAL(inbuf,smb_com) == SMBffirst)
    expect_close = True;
  
  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,("reply_search: path=%s status_len=%d\n",path,status_len));

  
  if (status_len == 0)
  {
    pstring dir2;

    pstrcpy(directory,smb_buf(inbuf)+1);
    pstrcpy(dir2,smb_buf(inbuf)+1);
    unix_convert(directory,conn,0,&bad_path,NULL);
    unix_format(dir2);

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

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

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

    if (strlen(directory) == 0)
      pstrcpy(directory,"./");
    memset((char *)status,'\0',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;
    conn->dirptr = dptr_fetch(status+12,&dptr_num);      
    if (!conn->dirptr)
      goto SearchEmpty;
    string_set(&conn->dirpath,dptr_path(dptr_num));
    if (!case_sensitive)
      strnorm(mask);
  }

  /* turn strings of spaces into a . */  
  {
    trim_string(mask,NULL," ");
    if ((p = strrchr(mask,' ')))
    {
      fstring ext;
      fstrcpy(ext,p+1);
      *p = 0;
      trim_string(mask,NULL," ");
      pstrcat(mask,".");
      pstrcat(mask,ext);
    }
  }

  /* Convert the formatted mask. (This code lives in trans2.c) */
  mask_convert(mask);

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

  if (!strchr(mask,'.') && strlen(mask)>8)
  {
    fstring tmp;
    fstrcpy(tmp,&mask[8]);
    mask[8] = '.';
    mask[9] = 0;
    pstrcat(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(conn,directory,True,expect_close,SVAL(inbuf,smb_pid));
      if (dptr_num < 0)
      {
        if(dptr_num == -2)
        {
          if((errno == ENOENT) && bad_path)
          {
            unix_ERR_class = ERRDOS;
            unix_ERR_code = ERRbadpath;
          }
          return (UNIXERROR(ERRDOS,ERRnofids));
        }
        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,"???????????",volume_label(SNUM(conn)),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",
              conn->dirpath,lp_dontdescend(SNUM(conn))));
        if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True))
          check_descend = True;

        for (i=numentries;(i<maxentries) && !finished;i++)
        {
          finished = 
            !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend);
          if (!finished)
          {
            memcpy(p,status,21);
            make_dir_struct(p,mask,fname,size,mode,date); 
            DEBUG(5,("get_dir_entry returned: file %s, mask %s, size %d, mode %o, date %s",
                  fname, mask, size, mode, ctime(&date)));
            dptr_fill(p+12,dptr_num);
            numentries++;
          }
          p += DIR_STRUCT_SIZE;
        }
      }
    } /* if (ok ) */
  }


  SearchEmpty:

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

  /* 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))
    slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num));

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

  return(outsize);
}

/****************************************************************************
  reply to a tcon
****************************************************************************/
int reply_tcon(/*connection_struct *conn,
               char *inbuf,char *outbuf, int dum_size, int dum_buffsize*/  
               tpClients aclient, connection_struct *conn, 
               tpSmbHeader smbHeader,
               char *inbuf, char *outbuf, 
               int flags1, int flags2)
{
    pstring service;
    pstring user;
    pstring password;
    pstring dev;
    int outsize = 0;
    uint16 vuid = SVAL(inbuf,smb_uid);
    int pwlen=0;
    int ecode = -1;

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

    parse_connect(smb_buf(inbuf)+1,service,user,password,&pwlen,dev);

    /*
     * 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);*/


    conn = make_connection(service,user,password,pwlen,dev,vuid,&ecode);
  
    if (!conn) {
        return(connection_error(inbuf,outbuf,ecode));
    }
  
    outsize = set_message(outbuf,2,0,True);
    SSVAL(outbuf,smb_vwv0,/* BUFFER_SIZE/2 */ max_recv );
    SSVAL(outbuf,smb_vwv1,conn->cnum);
    SSVAL(outbuf,smb_tid,conn->cnum);
  
    DEBUG(3,("tcon service=%s user=%s cnum=%d\n", 
         service, user, conn->cnum));
  
    return(outsize);
}


/****************************************************************************
  reply to a tcon and X
****************************************************************************/
int reply_tcon_and_X(tpClients aclient, 
                     connection_struct *conn, 
                     tpSmbHeader smbHeader,
                     char *inbuf, 
                     char *outbuf,
                     int length,
                     int bufsize)
{
    pstring service;
    pstring user;
    pstring password;
    pstring devicename;
    int ecode = -1;
    uint16 vuid = SVAL(inbuf,smb_uid);
    int passlen = SVAL(inbuf,smb_vwv3);
    char *path;
    char *p;
    
    *service = *user = *password = *devicename = 0;

    /* we might have to close an old one */
    if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) {
        close_cnum(conn,vuid);
    }

    if (passlen > MAX_PASS_LEN) {
        overflow_attack(passlen);
    }
  
    memcpy(password,smb_buf(inbuf),passlen);
    password[passlen]=0;    
    path = smb_buf(inbuf) + passlen;

    if (passlen != 24) {
        if (strequal(password," "))
            *password = 0;
        passlen = strlen(password);
    }
    
    fstrcpy(service,path+2);
    p = strchr(service,'\\');
    if (!p)
        return(ERROR(ERRSRV,ERRinvnetname));
    *p = 0;
    fstrcpy(service,p+1);
    p = strchr(service,'%');
    if (p) {
        *p++ = 0;
        fstrcpy(user,p);
    }
    StrnCpy(devicename,path + strlen(path) + 1,6);
    DEBUG(4,("Got device type %s\n",devicename));

    /*
     * 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);*/
    
    conn = make_connection(service,user,password,passlen,devicename,vuid,&ecode);
    
    if (!conn)
        return(connection_error(inbuf,outbuf,ecode));

    if (Protocol < PROTOCOL_NT1) {
        set_message(outbuf,2,strlen(devicename)+1,True);
        pstrcpy(smb_buf(outbuf),devicename);
    } else {
        char *fsname = lp_fstype(SNUM(conn));

        set_message(outbuf,3,3,True);

        p = smb_buf(outbuf);
        pstrcpy(p,devicename); p = skip_string(p,1); /* device name */
        pstrcpy(p,fsname); p = skip_string(p,1); /* filesystem type e.g NTFS */
        
        set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
        
        /* what does setting this bit do? It is set by NT4 and
           may affect the ability to autorun mounted cdroms */
        SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS); 
    }
  
    DEBUG(3,("tconX service=%s user=%s\n",
         service, user));
  
    /* set the incoming and outgoing tid to the just created one */
    SSVAL(inbuf,smb_tid,conn->cnum);
    SSVAL(outbuf,smb_tid,conn->cnum);

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


/****************************************************************************
  reply to a write
****************************************************************************/
int reply_write(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char *inbuf,
               char *outbuf,
               int dum_size,
               int dum_buffsize)
{
  size_t numtowrite;
  ssize_t nwritten = -1;
  SMB_OFF_T startpos;
  char *data;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);
  int outsize = 0;

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

  numtowrite = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  data = smb_buf(inbuf) + 3;
  
  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 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(fsp->fd_ptr->fd, (SMB_OFF_T)startpos);
  else
    nwritten = write_file(fsp,data,numtowrite);
  
  if (lp_syncalways(SNUM(conn)))
    sync_file(conn,fsp);

  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
  {
    DEBUG(5,("write fail NOACCESS fnum=%d num=%d wrote=%d\n",
       fsp->fnum, numtowrite, nwritten));
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }

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

  if (nwritten < (ssize_t)numtowrite) {
    DEBUG(5,("write DISKFULL  fnum=%d num=%d wrote=%d\n",
       fsp->fnum, numtowrite, nwritten));
    CVAL(outbuf,smb_rcls) = ERRHRD;
    SSVAL(outbuf,smb_err,ERRdiskfull);      
  }
  
  DEBUG(3,("write fnum=%d num=%d wrote=%d\n",
       fsp->fnum, numtowrite, nwritten));

  return(outsize);
}

/****************************************************************************
  reply to a read
****************************************************************************/
int reply_read(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char *inbuf,
               char *outbuf, 
               int dum_size, 
               int dum_buffsize)
{
  size_t numtoread;
  ssize_t nread = 0;
  char *data;
  SMB_OFF_T startpos;
  int outsize = 0;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

  numtoread = SVAL(inbuf,smb_vwv1);
  startpos = IVAL(inbuf,smb_vwv2);
  
  outsize = set_message(outbuf,5,3,True);
  numtoread = MIN(/*BUFFER_SIZE-outsize*/ max_send-outsize, numtoread);
  data = smb_buf(outbuf) + 3;
  
  if (is_locked(fsp,conn,numtoread,startpos, F_RDLCK))
    return(ERROR(ERRDOS,ERRlock));    

  if (numtoread > 0)
    nread = read_file(fsp,data,startpos,numtoread);
  
  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, ( "read fnum=%d num=%d nread=%d\n",
            fsp->fnum, numtoread, nread ) );

  return(outsize);
}

/****************************************************************************
  reply to a mknew or a create
****************************************************************************/
int reply_mknew(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char *inbuf,
               char *outbuf,
               int dum_size,
               int dum_buffsize)
{
  pstring fname;
  int com;
  int outsize = 0;
  int createmode;
  mode_t unixmode;
  int ofun = 0;
  BOOL bad_path = False;
  files_struct *fsp;
  int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
 
  com = SVAL(inbuf,smb_com);

  createmode = SVAL(inbuf,smb_vwv0);
  pstrcpy(fname,smb_buf(inbuf)+1);
  unix_convert(fname,conn,0,&bad_path,NULL);

  if (createmode & aVOLID)
    {
      DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
    }
  
  unixmode = unix_mode(conn,createmode);
  
  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));
  }

  if(com == SMBmknew)
  {
    /* We should fail if file exists. */
    ofun = 0x10;
  }
  else
  {
    /* SMBcreate - Create if file doesn't exist, truncate if it does. */
    ofun = 0x12;
  }

  /* Open file in dos compatibility share mode. */
  open_file_shared(fsp,conn,fname,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), 
                   ofun, unixmode, oplock_request, NULL, NULL);
  
  if (!fsp->open)
  {
    if((errno == ENOENT) && bad_path) 
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    file_free(fsp);
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }
 
  outsize = set_message(outbuf,1,0,True);
  SSVAL(outbuf,smb_vwv0,fsp->fnum);

  if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  }
 
  if(fsp->granted_oplock)
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
 
  DEBUG( 2, ( "new file %s\n", fname ) );
  DEBUG( 3, ( "mknew %s fd=%d dmode=%d umode=%o\n",
        fname, fsp->fd_ptr->fd, createmode, (int)unixmode ) );

  return(outsize);
}

/****************************************************************************
 Reply to a close - has to deal with closing a directory opened by NT SMB's.
****************************************************************************/
int reply_close(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char *inbuf,
               char *outbuf,
               int dum_size, 
               int dum_buffsize)
{
    int outsize = 0;
    time_t mtime;
    int32 eclass = 0, err = 0;
    files_struct *fsp = NULL;

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

    /* If it's an IPC, pass off to the pipe handler. */
    if (IS_IPC(conn)) {
       /* return reply_pipe_close(conn, inbuf,outbuf);*/
       return(ERROR(ERRSRV,ERRnosupport));   
    }

    fsp = file_fsp(inbuf,smb_vwv0);

    DEBUG(5,("Close File: num %d, name %s, fd %d, smbvwv[0] %d, smb_vwv0 %d\n",
          fsp->fnum, fsp->fsp_name, fsp->fd_ptr->fd,
          smbHeader->smbvwv[0], SVAL(inbuf, smb_vwv0)));

    /*
     * We can only use CHECK_FSP if we know it's not a directory.
     */

    if(!fsp || !fsp->open || (fsp->conn != conn))
      return(ERROR(ERRDOS,ERRbadfid));

    if(HAS_CACHED_ERROR(fsp)) {
        eclass = fsp->wbmpx_ptr->wr_errclass;
        err = fsp->wbmpx_ptr->wr_error;
    }

    if(fsp->is_directory) {
        /*
         * Special case - close NT SMB directory
         * handle.
         */
        DEBUG(3,("close directory fnum=%d\n", fsp->fnum));
        close_directory(fsp,True);
    } else {
        /*
         * Close ordinary file.
         */
        int close_err;

        /*
         * If there was a modify time outstanding,
         * try and set it here.
         */
        if(fsp->pending_modtime)
            set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);

        /*
         * Now take care of any time sent in the close.
         */
        mtime = make_unix_date3(inbuf+smb_vwv1);
        
        /* try and set the date */
        set_filetime(conn, fsp->fsp_name,mtime);

        DEBUG(3,("close fd=%d fnum=%d (0x%x) (numopen=%d)\n",
             fsp->fd_ptr->fd, fsp->fnum, fsp->fnum,
             conn->num_files_open));
 
        /*
         * close_file() returns the unix errno if an error
         * was detected on close - normally this is due to
         * a disk full error. If not then it was probably an I/O error.
         */
 
        if((close_err = close_file(fsp,True)) != 0) {
            errno = close_err;
            return (UNIXERROR(ERRHRD,ERRgeneral));
        }
    }  

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

    return(outsize);
}

/****************************************************************************
  reply to an open
****************************************************************************/

int reply_open(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char *inbuf,
               char *outbuf, 
               int dum_size, 
               int dum_buffsize)
{
  pstring fname;
  int outsize = 0;
  int fmode=0;
  int share_mode;
  SMB_OFF_T size = 0;
  time_t mtime=0;
  mode_t unixmode;
  int rmode=0;
  SMB_STRUCT_STAT sbuf;
  BOOL bad_path = False;
  files_struct *fsp;
  int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
 
  share_mode = SVAL(inbuf,smb_vwv0);

  pstrcpy(fname,smb_buf(inbuf)+1);
  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,aARCH);
      
  open_file_shared(fsp,conn,fname,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
                   unixmode, oplock_request,&rmode,NULL);

  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));
  }
*/
  if (sys_stat(fname,&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) {
    DEBUG(3,("attempt to open a directory %s\n",fname));
    close_file(fsp,False);
    return(ERROR(ERRDOS,ERRnoaccess));
  }
  
  outsize = set_message(outbuf,7,0,True);
  SSVAL(outbuf,smb_vwv0,fsp->fnum);
  SSVAL(outbuf,smb_vwv1,fmode);
  if(lp_dos_filetime_resolution(SNUM(conn)) )
    put_dos_date3(outbuf,smb_vwv2,mtime & ~1);
  else
    put_dos_date3(outbuf,smb_vwv2,mtime);
  SIVAL(outbuf,smb_vwv4,(uint32)size);
  SSVAL(outbuf,smb_vwv6,rmode);

  if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  }
    
  if(fsp->granted_oplock)
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  return(outsize);
}

/****************************************************************************
  reply to a tdis
****************************************************************************/
int reply_tdis(tpClients         aclient,
               connection_struct *conn, 
               tpSmbHeader       smbHeader,
               char              *inbuf,
               char              *outbuf, 
               int               dum_size, 
               int               dum_buffsize)
{
    int outsize = set_message(outbuf,0,0,True);
    uint16 vuid;

    vuid = SVAL(inbuf,smb_uid);

    if (!conn) {
        DEBUG(4,("Invalid connection in tdis\n"));
        return(ERROR(ERRSRV,ERRinvnid));
    }

    conn->used = False;

    close_cnum(conn,vuid);
  
    return outsize;
}


/****************************************************************************
  reply to a chkpth
****************************************************************************/
int reply_chkpth(tpClients aclient, 
                 connection_struct *conn,
                 tpSmbHeader smbHeader,
                 char *inbuf,
                 char *outbuf, 
                 int dum_size, 
                 int dum_buffsize)
{
  int outsize = 0;
  int mode;
  pstring name;
  BOOL ok = False;
  BOOL bad_path = False;
  SMB_STRUCT_STAT st;
 
  pstrcpy(name,smb_buf(inbuf) + 1);
  unix_convert(name,conn,0,&bad_path,&st);

  mode = SVAL(inbuf,smb_vwv0);

  if (check_name(name,conn)) {
    if(VALID_STAT(st))
      ok = S_ISDIR(st.st_mode);
    else
      ok = dos_directory_exist(name,NULL);
  }

  if (!ok)
  {
    /* We special case this - as when a Windows machine
       is parsing a path is steps through the components
       one at a time - if a component fails it expects
       ERRbadpath, not ERRbadfile.
     */
    if(errno == ENOENT)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }

#if 0
    /* Ugly - NT specific hack - maybe not needed ? (JRA) */
    if((errno == ENOTDIR) && (Protocol >= PROTOCOL_NT1) &&
       (get_remote_arch() == RA_WINNT))
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbaddirectory;
    }
#endif

    return(UNIXERROR(ERRDOS,ERRbadpath));
  }

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

  DEBUG(3,("chkpth %s mode=%d\n", name, mode));

  return(outsize);
}


/****************************************************************************
  reply to a unlink
****************************************************************************/
int reply_unlink(tpClients aclient,
                 connection_struct *conn,
                 tpSmbHeader smbHeader,
                 char *inbuf,
                 char *outbuf,
                 int dum_size, 
                 int dum_buffsize)
{
  int outsize = 0;
  pstring name;
  int dirtype;
  pstring directory;
  pstring mask;
  char *p;
  int count=0;
  int error = ERRnoaccess;
  BOOL has_wild;
  BOOL exists=False;
  BOOL bad_path = False;

  *directory = *mask = 0;

  dirtype = SVAL(inbuf,smb_vwv0);
  
  pstrcpy(name,smb_buf(inbuf) + 1);
   
  DEBUG(3,("reply_unlink : %s\n",name));
   
  unix_convert(name,conn,0,&bad_path,NULL);

  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 (can_delete(directory,conn,dirtype) && !dos_unlink(directory))
      count++;
    if (!count)
      exists = dos_file_exist(directory,NULL);    
  } else {
    void *dirptr = NULL;
    char *dname;

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

    /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then
       the pattern matches against the long name, otherwise the short name 
       We don't implement this yet XXXX
       */

    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);
        if (!can_delete(fname,conn,dirtype)) continue;
        if (!dos_unlink(fname)) count++;
        DEBUG(3,("reply_unlink : doing unlink on %s\n",fname));
      }
    CloseDir(dirptr);
      }
  }
  
  if (count == 0) {
    if (exists)
      return(ERROR(ERRDOS,error));
    else
    {
      if((errno == ENOENT) && bad_path)
      {
        unix_ERR_class = ERRDOS;
        unix_ERR_code = ERRbadpath;
      }
      return(UNIXERROR(ERRDOS,error));
    }
  }
  
  outsize = set_message(outbuf,0,0,True);
  
  return(outsize);
}


/****************************************************************************
  reply to a lock
****************************************************************************/
int reply_lock(tpClients aclient, 
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf, 
                int length, 
                int dum_buffsize)
{
    int outsize = set_message(outbuf,0,0,True);
    SMB_OFF_T count,offset;
    int eclass;
    uint32 ecode;
    files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

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

    DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n",
         fsp->fd_ptr->fd, fsp->fnum, (double)offset, (double)count));

    if (!do_lock(fsp, conn, count, offset, F_WRLCK, &eclass, &ecode)) {
      if((ecode == ERRlock) && 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, -1, 0))
          return -1;
      }
      return (ERROR(eclass,ecode));
    }

    return(outsize);
}


/****************************************************************************
  reply to a unlock
****************************************************************************/
int reply_unlock(tpClients aclient, 
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf,
                int dum_size,
                int dum_buffsize)
{
  int outsize = set_message(outbuf,0,0,True);
  SMB_OFF_T count,offset;
  int eclass;
  uint32 ecode;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

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

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

  DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n",
        fsp->fd_ptr->fd, fsp->fnum, (double)offset, (double)count ) );
  
  return(outsize);
}


/****************************************************************************
  reply to a mkdir
****************************************************************************/
int reply_mkdir(tpClients aclient, 
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf, 
                int dum_size, 
                int dum_buffsize)
{
  pstring directory;
  int outsize,ret= -1;
  BOOL bad_path = False;
 
  pstrcpy(directory,smb_buf(inbuf) + 1);
  unix_convert(directory,conn,0,&bad_path,NULL);
  
  if (check_name(directory, conn))
    ret = dos_mkdir(directory,unix_mode(conn,aDIR));

  DEBUG(3, ("mkdir: %s dos_mkdir returned %d\n", directory, ret ) ); 
  
  if (ret < 0)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }

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

  DEBUG( 3, ( "mkdir %s ret=%d\n", directory, ret ) );

  return(outsize);
}

   

/****************************************************************************
 Reply to a rmdir.
****************************************************************************/

int reply_rmdir(tpClients aclient, 
                connection_struct *conn,
                tpSmbHeader smbHeader, 
                char *inbuf,
                char *outbuf,
                int dum_size, 
                int dum_buffsize)
{
  pstring directory;
  int outsize = 0;
  BOOL ok = False;
  BOOL bad_path = False;

  pstrcpy(directory,smb_buf(inbuf) + 1);
  unix_convert(directory,conn, NULL,&bad_path,NULL);
  
  if (check_name(directory,conn))
  {
    dptr_closepath(directory,SVAL(inbuf,smb_pid));
    ok = rmdir_internals(conn, directory);
  }
  
  if (!ok)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    return(UNIXERROR(ERRDOS,ERRbadpath));
  }
 
  outsize = set_message(outbuf,0,0,True);
  
  DEBUG( 3, ( "rmdir %s\n", directory ) );
  
  return(outsize);
}


/*******************************************************************
resolve wildcards in a filename rename
********************************************************************/
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);
  
  fstrcpy(root1,name1);
  fstrcpy(root2,name2);
  p = strrchr(root1,'.');
  if (p) {
    *p = 0;
    fstrcpy(ext1,p+1);
  } else {
    fstrcpy(ext1,"");    
  }
  p = strrchr(root2,'.');
  if (p) {
    *p = 0;
    fstrcpy(ext2,p+1);
  } else {
    fstrcpy(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++;
  }

  pstrcpy(name2,root2);
  if (ext2[0]) {
    pstrcat(name2,".");
    pstrcat(name2,ext2);
  }

  return(True);
}

/*******************************************************************
check if a user is allowed to rename a file
********************************************************************/
static BOOL can_rename(char *fname,connection_struct *conn)
{
  SMB_STRUCT_STAT sbuf;

  if (!CAN_WRITE(conn)) return(False);

  if (dos_lstat(fname,&sbuf) != 0) return(False);
  if (!check_file_sharing(conn,fname,True)) return(False);

  return(True);
}

/****************************************************************************
 The guts of the rename command, split out so it may be called by the NT SMB
 code. 
****************************************************************************/
int rename_internals(connection_struct *conn, 
             char *inbuf, char *outbuf, char *name, 
             char *newname, BOOL replace_if_exists)
{
    pstring directory;
    pstring mask;
    pstring newname_last_component;
    char *p;
    BOOL has_wild;
    BOOL bad_path1 = False;
    BOOL bad_path2 = False;
    int count=0;
    int error = ERRnoaccess;
    BOOL exists=False;

    *directory = *mask = 0;

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

    /*
     * Split the old name into directory and last component
     * strings. Note that unix_convert may have stripped off a 
     * leading ./ from both name and newname if the rename is 
     * at the root of the share. We need to make sure either both
     * name and newname contain a / character or neither of them do
     * as this is checked in resolve_wildcards().
     */
    
    p = strrchr(name,'/');
    if (!p) {
        pstrcpy(directory,".");
        pstrcpy(mask,name);
    } else {
        *p = 0;
        pstrcpy(directory,name);
        pstrcpy(mask,p+1);
        *p = '/'; /* Replace needed for exceptional test below. */
    }

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

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

    if (!has_wild) {
        /*
         * No wildcards - just process the one file.
         */
        BOOL is_short_name = is_8_3(name, True);

        /* Add a terminating '/' to the directory name. */
        pstrcat(directory,"/");
        pstrcat(directory,mask);
        
        /* Ensure newname contains a '/' also */
        if(strrchr(newname,'/') == 0) {
            pstring tmpstr;
            
            pstrcpy(tmpstr, "./");
            pstrcat(tmpstr, newname);
            pstrcpy(newname, tmpstr);
        }
        
        DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", 
             case_sensitive, case_preserve, short_case_preserve, directory, 
             newname, newname_last_component, is_short_name));

        /*
         * Check for special case with case preserving and not
         * case sensitive, if directory and newname are identical,
         * and the old last component differs from the original
         * last component only by case, then we should allow
         * the rename (user is trying to change the case of the
         * filename).
         */
        if((case_sensitive == False) && 
           (((case_preserve == True) && 
             (is_short_name == False)) || 
            ((short_case_preserve == True) && 
             (is_short_name == True))) &&
           strcsequal(directory, newname)) {
            pstring newname_modified_last_component;

            /*
             * Get the last component of the modified name.
             * Note that we guarantee that newname contains a '/'
             * character above.
             */
            p = strrchr(newname,'/');
            pstrcpy(newname_modified_last_component,p+1);
            
            if(strcsequal(newname_modified_last_component, 
                      newname_last_component) == False) {
                /*
                 * Replace the modified last component with
                 * the original.
                 */
                pstrcpy(p+1, newname_last_component);
            }
        }
        
        if(replace_if_exists) {
            /*
             * NT SMB specific flag - rename can overwrite
             * file with the same name so don't check for
             * dos_file_exist().
             */
            if(resolve_wildcards(directory,newname) &&
               can_rename(directory,conn) &&
               !dos_rename(directory,newname))
                count++;
        } else {
            if (resolve_wildcards(directory,newname) && 
                can_rename(directory,conn) && 
                !dos_file_exist(newname,NULL) &&
                !dos_rename(directory,newname))
                count++;
        }

        DEBUG(3,("rename_internals: %s doing rename on %s -> %s\n",(count != 0) ? "succeeded" : "failed",
                         directory,newname));
        
        if (!count) exists = dos_file_exist(directory,NULL);
        if (!count && exists && dos_file_exist(newname,NULL)) {
            exists = True;
            error = ERRrename;
        }
    } else {
        /*
         * Wildcards - process each file that matches.
         */
        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);
                if (!can_rename(fname,conn)) {
                    DEBUG(6,("rename %s refused\n", fname));
                    continue;
                }
                pstrcpy(destname,newname);
                
                if (!resolve_wildcards(fname,destname)) {
                    DEBUG(6,("resolve_wildcards %s %s failed\n", fname, destname));
                    continue;
                }
                
                if (!replace_if_exists && dos_file_exist(destname,NULL)) {
                    DEBUG(6,("dos_file_exist %s\n", destname));
                    error = 183;
                    continue;
                }
                
                if (!dos_rename(fname,destname))
                    count++;
                DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname));
            }
            CloseDir(dirptr);
        }
    }
    
    if (count == 0) {
        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));
        }
    }
    
    return 0;
}

/****************************************************************************
  reply to a lseek
****************************************************************************/
int reply_lseek(tpClients aclient, 
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf,
                int dum_size, 
                int dum_buffsize)
{
  SMB_OFF_T startpos;
  SMB_OFF_T res= -1;
  int mode,umode;
  int outsize = 0;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

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

  writeLog("Read-lseek",aclient->clientFd,inbuf,64);

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


  DEBUG(5,("lseek mode %d, startpos 0x%x, smb_vwv2 %x,%d\n",mode,startpos, 
           smb_vwv2,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;
  }

  if((res = sys_lseek(fsp->fd_ptr->fd,startpos,umode)) == -1)
    return(UNIXERROR(ERRDOS,ERRnoaccess));

  fsp->pos = res;
  
  outsize = set_message(outbuf,2,0,True);
  SIVALS(outbuf,smb_vwv0,res);
  
  DEBUG(3,("lseek fnum=%d ofs=%.0f mode=%d\n",
       fsp->fnum, (double)startpos, mode));

  return(outsize);
}

/****************************************************************************
  reply to a flush
****************************************************************************/
int reply_flush(tpClients aclient,
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf, 
                int dum_size, 
                int dum_buffsize)
{
  int outsize = set_message(outbuf,0,0,True);
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);

  if (fsp) {
      CHECK_FSP(fsp,conn);
      CHECK_ERROR(fsp);
  }

  if (!fsp) {
      file_sync_all(conn);
  } else {
      sync_file(conn,fsp);
  }

  DEBUG(3,("flush\n"));
  return(outsize);
}


/****************************************************************************
  reply to a printopen
****************************************************************************/
int reply_printopen(tpClients aclient,
                    connection_struct *conn,
                    tpSmbHeader smbHeader,
                    char *inbuf,
                    char *outbuf,
                    int dum_size, 
                    int dum_buffsize)
{
    pstring fname;
    pstring fname2;
    int outsize = 0;
    files_struct *fsp;
    
    *fname = *fname2 = 0;
    
    if (!CAN_PRINT(conn))
        return(ERROR(ERRDOS,ERRnoaccess));

    {
        pstring s;
        char *p;
        pstrcpy(s,smb_buf(inbuf)+1);

        p = s;
        while (*p) {
            if (!(isalnum((int)*p) || strchr("._-",*p)))
                *p = 'X';
            p++;
        }

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

        slprintf(fname,sizeof(fname)-1, "%s.XXXXXX",s);  
    }

    fsp = file_new();
    if (!fsp)
        return(ERROR(ERRSRV,ERRnofids));
    
    pstrcpy(fname2,(char *)mktemp(fname));

    if (!check_name(fname2,conn)) {
        file_free(fsp);
        return(ERROR(ERRDOS,ERRnoaccess));
    }
    
    /* don't treat the .XXXXXX as a file type */
    smbDontStripFileExt = True;

    /* Open for exclusive use, write only. */
    open_file_shared(fsp,conn,fname2, SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
                     (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL), unix_mode(conn,0), 0, NULL, NULL);

    smbDontStripFileExt = False;

    if (!fsp->open) {
        file_free(fsp);
        return(UNIXERROR(ERRDOS,ERRnoaccess));
    }

    /* force it to be a print file */
    fsp->print_file = True;
  
    outsize = set_message(outbuf,1,0,True);
    SSVAL(outbuf,smb_vwv0,fsp->fnum);
  
    DEBUG(3,("openprint %s fd=%d fnum=%d\n",
           fname2, fsp->fd_ptr->fd, fsp->fnum));

    return(outsize);
}


/****************************************************************************
  reply to a printclose
****************************************************************************/
int reply_printclose(tpClients aclient,
                     connection_struct *conn,
                     tpSmbHeader smbHeader,
                     char *inbuf,
                     char *outbuf,
                     int dum_size,
                     int dum_buffsize)
{
    int outsize = set_message(outbuf,0,0,True);
    files_struct *fsp = file_fsp(inbuf,smb_vwv0);
    int close_err = 0;

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

    if (!CAN_PRINT(conn))
        return(ERROR(ERRDOS,ERRnoaccess));
  
    DEBUG(3,("printclose fd=%d fnum=%d\n",
         fsp->fd_ptr->fd,fsp->fnum));
  
    close_err = close_file(fsp,True);

    if(close_err != 0) {
        errno = close_err;
        return(UNIXERROR(ERRHRD,ERRgeneral));
    }

    return(outsize);
}


/****************************************************************************
  reply to a printqueue
****************************************************************************/
int reply_printqueue(tpClients aclient,
                     connection_struct *conn,
                     tpSmbHeader smbHeader,
                     char *inbuf,
                     char *outbuf,
                     int dum_size, 
                     int dum_buffsize)
{
    int outsize = set_message(outbuf,2,3,True);
    int max_count = SVAL(inbuf,smb_vwv0);
    int start_index = SVAL(inbuf,smb_vwv1);

    /* we used to allow the client to get the cnum wrong, but that
       is really quite gross and only worked when there was only
       one printer - I think we should now only accept it if they
       get it right (tridge) */
    if (!CAN_PRINT(conn))
        return(ERROR(ERRDOS,ERRnoaccess));

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

    {
        print_queue_struct *queue = NULL;
        char *p = smb_buf(outbuf) + 3;
        int count = get_printqueue(SNUM(conn), conn,&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,printjob_encode(SNUM(conn), 
                          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(tpClients aclient,
                     connection_struct *conn,
                     tpSmbHeader smbHeader,
                     char *inbuf,
                     char *outbuf,
                     int dum_size, 
                     int dum_buffsize)
{
  int numtowrite;
  int outsize = set_message(outbuf,0,0,True);
  char *data;
  files_struct *fsp = file_fsp(inbuf,smb_vwv0);
  
  if (!CAN_PRINT(conn))
    return(ERROR(ERRDOS,ERRnoaccess));

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

  numtowrite = SVAL(smb_buf(inbuf),1);
  data = smb_buf(inbuf) + 3;
  
  if (write_file(fsp,data,numtowrite) != numtowrite)
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  
  DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) );
  
  return(outsize);
}

/****************************************************************************
  reply to a create temporary file
****************************************************************************/
int reply_ctemp(tpClients aclient,
                connection_struct *conn,
                tpSmbHeader smbHeader,
                char *inbuf,
                char *outbuf, 
                int dum_size, 
                int dum_buffsize)
{
  pstring fname;
  pstring fname2;
  int outsize = 0;
  int createmode;
  mode_t unixmode;
  BOOL bad_path = False;
  files_struct *fsp;
  int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
 
  createmode = SVAL(inbuf,smb_vwv0);
  pstrcpy(fname,smb_buf(inbuf)+1);
  pstrcat(fname,"/TMXXXXXX");
  unix_convert(fname,conn,0,&bad_path,NULL);
  
  unixmode = unix_mode(conn,createmode);
  
  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));
  }

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

  /* Open file in dos compatibility share mode. */
  /* We should fail if file exists. */
  open_file_shared(fsp,conn,fname2,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), 
                   (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL), unixmode, oplock_request, NULL, NULL);

  if (!fsp->open)
  {
    if((errno == ENOENT) && bad_path)
    {
      unix_ERR_class = ERRDOS;
      unix_ERR_code = ERRbadpath;
    }
    file_free(fsp);
    return(UNIXERROR(ERRDOS,ERRnoaccess));
  }

  outsize = set_message(outbuf,1,2 + strlen(fname2),True);
  SSVAL(outbuf,smb_vwv0,fsp->fnum);
  CVAL(smb_buf(outbuf),0) = 4;
  pstrcpy(smb_buf(outbuf) + 1,fname2);

  if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
  }
  
  if(fsp->granted_oplock)
    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;

  DEBUG( 2, ( "created temp file %s\n", fname2 ) );
  DEBUG( 3, ( "ctemp %s fd=%d dmode=%d umode=%o\n",
        fname2, fsp->fd_ptr->fd, createmode, (int)unixmode ) );

  return(outsize);
}

/****************************************************************************
 Reply to a mv.
****************************************************************************/

int reply_mv(tpClients aclient,
             connection_struct *conn,
             tpSmbHeader smbHeader,
             char *inbuf,
             char *outbuf, 
             int dum_size, 
             int dum_buffsize)
{
  int outsize = 0;
  pstring name;
  pstring newname;

  pstrcpy(name,smb_buf(inbuf) + 1);
  pstrcpy(newname,smb_buf(inbuf) + 3 + strlen(name));
   
  DEBUG(3,("reply_mv : %s -> %s\n",name,newname));

  outsize = rename_internals(conn, inbuf, outbuf, name, newname, False);
  if(outsize == 0) 
    outsize = set_message(outbuf,0,0,True);
  
  return(outsize);
}

#if 0
/* now in c.negprot */
int reply_negprot(tpClients aclient, connection_struct *conn,
                  tpSmbHeader smbHeader,
                  char *inbuf, char *outbuf, 
                  int flags1, int flags2)
{
   char *p=smbHeader->smbdata+2;
   char logs[100];
   int  index=-1,i=0;

   while (p<smbHeader->smbdata+smbHeader->smbbcc)
   {
      sprintf(logs,"%s",p); 
      writeLog(logs,0,NULL,0);  
      if (strcsequal(p,"PC NETWORK PROGRAM 1.0"))
      {
         index = i;
      }
      p += strlen(p) +2;
      i++; 
   }        
   
   SSVAL(outbuf,smb_vwv0,index);
   return set_message(outbuf,1,0,True);               
}
#endif
                                
void unpackHeader(tSmbHeader *smbHeader, char * inbuf)
{                                     
   int offset=0,i;         
   char logs[100]; 
   int tempdebuglevel=0;
   inbuf+=4;

   smbHeader->smbidf[0]=CVAL(inbuf,0);
   smbHeader->smbidf[1]=CVAL(inbuf,1);
   smbHeader->smbidf[2]=CVAL(inbuf,2);
   smbHeader->smbidf[3]=CVAL(inbuf,3);
   smbHeader->smbcom   =CVAL(inbuf,4);
   smbHeader->smbrcls  =CVAL(inbuf,5);
   smbHeader->smbreh   =CVAL(inbuf,6);
   smbHeader->smberr   =SVAL(inbuf,7);
   smbHeader->smbreb   =CVAL(inbuf,9);

   offset = 10;
   for (i=0;i<7;i++)
   {
      smbHeader->smbres[i]=SVAL(inbuf,offset);
      offset += 2;
   }             

   smbHeader->smbtid = SVAL(inbuf,offset); offset+=2;
   smbHeader->smbpid = SVAL(inbuf,offset); offset+=2;
   smbHeader->smbuid = SVAL(inbuf,offset); offset+=2;
   smbHeader->smbmid = SVAL(inbuf,offset); offset+=2;
   smbHeader->smbwct = CVAL(inbuf,offset); offset++;

   smbHeader->smbvwv = (WORD*) calloc(1, smbHeader->smbwct*
                                          sizeof(WORD));
   for (i=0;i<smbHeader->smbwct;i++)
   {
      smbHeader->smbvwv[i]=SVAL(inbuf,offset);
      offset += 2;
   }                    

   smbHeader->smbbcc = CVAL(inbuf,offset); offset++;

   smbHeader->smbdata = (char*)((int)inbuf+(int)offset);

   sprintf(logs,"smbidf %x %c%c%c",smbHeader->smbidf[0],smbHeader->smbidf[1],
                smbHeader->smbidf[2],smbHeader->smbidf[3]);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbcom 0x%x",smbHeader->smbcom);
#if 0    
/*
   if (DEBUGLEVEL==1)
   {
     DEBUG(1,("smbcom 0x%x\n",smbHeader->smbcom));  
   } 
*/     
   DEBUG(0,("smbcom 0x%x\n",smbHeader->smbcom));
      tempdebuglevel=DEBUGLEVEL;
      DEBUGLEVEL=10;                                        
   DEBUG(0,("smbcom 0x%x\n",smbHeader->smbcom));  
 DEBUGLEVEL=tempdebuglevel;   
                       
   if (smbHeader->smbcom==0x2d)
   { 
      tempdebuglevel=DEBUGLEVEL;
      DEBUGLEVEL=10;
   }
   else
   {
     DEBUGLEVEL=tempdebuglevel;   
   } 
#endif

   writeLog(logs,0,NULL,0);              
   sprintf(logs,"smbrcls 0x%x",smbHeader->smbrcls);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbreh 0x%x",smbHeader->smbreh);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smberr 0x%x",smbHeader->smberr);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbtid 0x%x",smbHeader->smbtid);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbpid 0x%x",smbHeader->smbpid);
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbuid 0x%x",smbHeader->smbuid); 
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbmid 0x%x",smbHeader->smbmid); 
   writeLog(logs,0,NULL,0);
   sprintf(logs,"smbwct %d",smbHeader->smbwct);  
   writeLog(logs,0,NULL,0);

   for (i=0;i<smbHeader->smbwct;i++)
   {
     sprintf(logs,"smbvwv[%d] 0%.4x",i,smbHeader->smbvwv[i]);
     writeLog(logs,0,NULL,0);
   }
/*   sprintf(logs,"smbvwv 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
           smbHeader->smbvwv[0],smbHeader->smbvwv[1],smbHeader->smbvwv[2],
           smbHeader->smbvwv[3],smbHeader->smbvwv[4],smbHeader->smbvwv[5],
           smbHeader->smbvwv[6]);  
   writeLog(logs,0,NULL,0);        */
   sprintf(logs,"smbbcc %d",smbHeader->smbbcc);  
   writeLog(logs,0,NULL,0);                    
   writeLog("smbdata",1,smbHeader->smbdata,smbHeader->smbbcc); 
}


/*
These flags determine some of the permissions required to do an operation 

Note that I don't set NEED_WRITE on some write operations because they
are used by some brain-dead clients when printing, and I don't want to
force write permissions on print services.
*/


/* 
   define a list of possible SMB messages and their corresponding
   functions. Any message that has a NULL function is unimplemented -
   please feel free to contribute implementations!
*/


/* moved to c.process */
#define AS_USER (1<<0)
#define NEED_WRITE (1<<1)
#define TIME_INIT (1<<2)
#define CAN_IPC (1<<3)
#define AS_GUEST (1<<5)
#define QUEUE_IN_OPLOCK (1<<6)

struct smb_message_struct
{
  int code;
  char *name;
  int (*fn)(tpClients, connection_struct *, tpSmbHeader,
            char *, char *, int, int);
  int flags;
}
 smb_messages[] = {

    /* CORE PROTOCOL */

   {SMBnegprot,"SMBnegprot",reply_negprot,0},
   {SMBtcon,"SMBtcon",reply_tcon,0},
   {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
   {SMBtdis,"SMBtdis",reply_tdis,0},
   {SMBsearch,"SMBsearch",reply_search,AS_USER | CAN_IPC},
   {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER | CAN_IPC},
   {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | CAN_IPC | NEED_WRITE},
   {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER | CAN_IPC},
   {SMBexit,"SMBexit",reply_exit,0},
   {SMBioctl,"SMBioctl",reply_ioctl,0},
   {SMBecho,"SMBecho",reply_echo,0},
   {SMBopen,"SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK | CAN_IPC},
   {SMBread,"SMBread",reply_read,AS_USER | CAN_IPC},
   {SMBwrite,"SMBwrite",reply_write,AS_USER | CAN_IPC},
   {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
   /* note that SMBmknew and SMBcreate are deliberately overloaded */   
   {SMBcreate,"SMBcreate",reply_mknew,AS_USER | CAN_IPC},
   {SMBmknew,"SMBmknew",reply_mknew,AS_USER | CAN_IPC},
   {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER | CAN_IPC},
   {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK | CAN_IPC},
   {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE | CAN_IPC},
   {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE | CAN_IPC},
   {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK | CAN_IPC}, 
   {SMBlseek,"SMBlseek",reply_lseek,AS_USER | CAN_IPC},
   {SMBflush,"SMBflush",reply_flush,AS_USER | CAN_IPC},
   {SMBlock,"SMBlock",reply_lock,AS_USER | CAN_IPC},
   {SMBunlock,"SMBunlock",reply_unlock,AS_USER | CAN_IPC}, 
   {SMBctemp,"SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK },
   {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
   {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
   {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
   {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},  
   {SMBtconX,"SMBtconX",reply_tcon_and_X,0},

   /* CORE+ PROTOCOL FOLLOWS */
   
   {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER},
   {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER},
   {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER},
   {SMBlockread,"SMBlockread",reply_lockread,AS_USER},
   {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER},

   /* LANMAN1.0 PROTOCOL FOLLOWS */

   {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
   {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER},
   {SMBreadBs,"SMBreadBs",NULL,AS_USER},
   {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
   {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
   {SMBwritec,"SMBwritec",NULL,AS_USER},
   {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
   {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
   {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
   {SMBioctls,"SMBioctls",NULL,AS_USER},
   {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
   {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
   
   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
   {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
   {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
   {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
   
   {SMBffirst,"SMBffirst",reply_search,AS_USER},
   {SMBfunique,"SMBfunique",reply_search,AS_USER},
   {SMBfclose,"SMBfclose",reply_fclose,AS_USER},

   /* LANMAN2.0 PROTOCOL FOLLOWS */
   {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
   {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
   {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER },
   {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},

   /* NT PROTOCOL FOLLOWS */
   {SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
   {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
   {SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
   {SMBntcancel, "SMBntcancel", reply_ntcancel, 0 },

   /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */
   
   {SMBsendb,"SMBsendb",NULL,AS_GUEST},
   {SMBfwdname,"SMBfwdname",NULL,AS_GUEST},
   {SMBcancelf,"SMBcancelf",NULL,AS_GUEST},
   {SMBgetmac,"SMBgetmac",NULL,AS_GUEST},
  
   {0," ",NULL,0}
};

int samba_message(tpClients aclient,
                   int type, 
                   int length, 
                   char *inbuf, 
                   char *outbuf,
                   int size,
                   int bufsize)
{                    
   int i,length2=0,msgNo;
   tSmbHeader smbHeader;
   BOOL foundMessage=False;
   connection_struct * conn;
   static int num_smb_messages = 
     sizeof(smb_messages) / sizeof(struct smb_message_struct);

  errno = 0;
  last_message = type;

  /* make sure this is an SMB packet */
  if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
  {
    DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
    return(-1);
  }

   unpackHeader(&smbHeader,inbuf);

   i=0;      
   while (i<num_smb_messages)
   {
      if (smb_messages[i].code == smbHeader.smbcom)
      {  
         msgNo=i;
         foundMessage=True;
         break;
      }
      i++;
   }            
          
   if (foundMessage==False)
   {
      length2 = reply_unknown(inbuf,outbuf);
   }
   else
   {
      int flags = smb_messages[msgNo].flags;
      static uint16 last_session_tag = UID_FIELD_INVALID;
      /* In share mode security we must ignore the vuid. */
      uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
      
      conn = conn_find(SVAL(inbuf,smb_tid)/*smbHeader.smbtid*/);


      /* Ensure this value is replaced in the incoming packet. */
      SSVAL(inbuf,smb_uid,session_tag);

     /*
       * Ensure the correct username is in sesssetup_user.
       * This is a really ugly bugfix for problems with
       * multiple session_setup_and_X's being done and
       * allowing %U and %G substitutions to work correctly.
       * There is a reason this code is done here, don't
       * move it unless you know what you're doing... :-).
       * JRA.
       */
      if (session_tag != last_session_tag) {
        user_struct *vuser = NULL;

        last_session_tag = session_tag;
        if(session_tag != UID_FIELD_INVALID)
          vuser = get_valid_user_struct(session_tag);           
        if(vuser != NULL)
          pstrcpy( sesssetup_user, vuser->requested_name);
      }

      /* does this protocol need to be run as root? */
      if (!(flags & AS_USER))
        unbecome_user();

      /* does this protocol need to be run as the connected user? */
      if ((flags & AS_USER) && !become_user(conn,session_tag)) {
        if (flags & AS_GUEST) 
          flags &= ~AS_USER;
        else
          return(ERROR(ERRSRV,ERRinvnid));
      }
      /* this code is to work around a bug is MS client 3 without
         introducing a security hole - it needs to be able to do
         print queue checks as guest if it isn't logged in properly */
      if (flags & AS_USER)
        flags &= ~AS_GUEST;

      /* does it need write permission? */
      if ((flags & NEED_WRITE) && !CAN_WRITE(conn))
        return(ERROR(ERRSRV,ERRaccess));

      /* ipc services are limited */
      if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
        return(ERROR(ERRSRV,ERRaccess));	    
      }

      /* load service specific parameters */
      if (conn && 
	  !become_service(conn,(flags & AS_USER)?True:False)) {
        return(ERROR(ERRSRV,ERRaccess));
      }

      /* does this protocol need to be run as guest? */
      if ((flags & AS_GUEST) && 
	  (!become_guest() || 
	   !check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1)))) {
        return(ERROR(ERRSRV,ERRaccess));
      }

      g_conn = conn;
      last_inbuf = inbuf;
      length2=smb_messages[msgNo].fn(aclient,conn,&smbHeader,inbuf,outbuf,size,bufsize);

   }

   free(smbHeader.smbvwv);
   return length2;
}

/****************************************************************************
return a string containing the function name of a SMB command
****************************************************************************/
char *smb_fn_name(int type)
{
	static char *unknown_name = "SMBunknown";
	static int num_smb_messages = 
		sizeof(smb_messages) / sizeof(struct smb_message_struct);
	int match;

	for (match=0;match<num_smb_messages;match++)
		if (smb_messages[match].code == type)
			break;

	if (match == num_smb_messages)
		return(unknown_name);

	return(smb_messages[match].name);
}


/****************************************************************************
  initialise connect, service and file structs
****************************************************************************/
void init_structs(void )
{
	get_myname(myhostname,NULL);

	/*
	 * Set the machine NETBIOS name if not already
	 * set from the config file.
	 */

	if (!*global_myname) {
		char *p;
		fstrcpy( global_myname, myhostname );
		p = strchr( global_myname, '.' );
		if (p) 
			*p = 0;
	}

	strupper( global_myname );

	conn_init();

	file_init();

	/* for RPC pipes */
	init_rpc_pipe_hnd();

	/* for LSA handles */
	init_lsa_policy_hnd();

	init_dptrs();
}

void samba_init(void)
{ 
  extern BOOL append_log;

  smbDontStripFileExt = False;

  append_log = False;
  dontConvertName = 0;

  TimeInit();
  charset_initialise();
  codepage_initialise(MSDOS_LATIN_1_CODEPAGE);

  if(!initialize_password_db()) 
  {
    DEBUG(0,("Can't setup password database vectors.\n")); 
  }


  lp_load(CONFIGFILE,False,True,True);

  service_info();

  init_structs();

  ftypes_init();

  fstrcpy(global_myworkgroup, lp_workgroup());

  reopen_logs();
}



void samba_timeout(void)
{
  int deadtime = lp_deadtime()*60;

  if (deadtime <= 0)
    deadtime = DEFAULT_SMBD_TIMEOUT;

  timeout_processing(deadtime);
}

