/* nwconn.c 18-Apr-00       */
/* one process / connection */

/* (C)opyright (C) 1993,2000  Martin Stover, Marburg, Germany
 *
 * 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.
 */

/* history since 21-Apr-00 
 *
 * mst:25-Apr-00: added routines for sending nwconn data to nwbind
 *
 */


#include "net.h"
#if 1
# define LOC_RW_BUFFERSIZE RW_BUFFERSIZE
#else
# define LOC_RW_BUFFERSIZE 512
#endif
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#if !CALL_NWCONN_OVER_SOCKET
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

#include "nwvolume.h"
#include "nwfile.h"
#include "connect.h"
#include "nwqconn.h"
#include "namspace.h"
#include "nwshare.h"
#include "nwconn.h"
#include "trustee.h"
#include "nwatalk.h"

int act_pid        = 0;

#define  FD_NCP_OUT    3

static int          father_pid    = -1;
static ipxAddr_t    from_addr;
static ipxAddr_t    my_addr;
static struct       t_unitdata ud;
static uint8        ipx_pack_typ  =  PACKT_CORE;
static int          last_sequence = -9999;

static IPX_DATA     ipxdata;
static NCPRESPONSE  *ncpresponse=(NCPRESPONSE*)&ipxdata;
static uint8        *responsedata=((uint8*)&ipxdata)+sizeof(NCPRESPONSE);

static int          requestlen;
static uint8        readbuff[IPX_MAX_DATA];

static uint8        saved_readbuff[IPX_MAX_DATA];
static int          saved_sequence=-1;
static int rw_buffer_size = LOC_RW_BUFFERSIZE;  /* default */

static NCPREQUEST   *ncprequest  = (NCPREQUEST*)readbuff;
static uint8        *requestdata = readbuff + sizeof(NCPREQUEST);
static int          ncp_type;
static int          sock_nwbind=-1;
static int          sock_echo  =-1;
static char         *prog_title;

static int req_printed=0;


static int scan_volume_user_disk_restrictions(uint8 volnr, uint32 sequence,
                                             uint8 *response)
{
  struct XDATA {
    uint8 entries;
  } *xdata = (struct XDATA *)response;
  int result = nw_get_volume_name(volnr, NULL, 0);

  (void)sequence;

  if (result < 0)
    return result;

  /*
   * Keep the scan reply empty for now.  The per-object quota query can be
   * routed through nwbind because it has a concrete object id to resolve,
   * but this scan call would need bindery iteration plus UNIX_USER property
   * lookups from nwbind-side code.  nwconn does not link the bindery database
   * helpers, and pulling them into this process would break the existing
   * process split.
   */
  xdata->entries = 0;
  return 1;
}

static int trustee_v3_to_ncp22_rights(int rights)
{
  int ncp22 = 0;

  /*
   * NetWare trustee/access masks use bit 0x0100 for Supervisor.
   * Bit 0x0004 is the old Open right, not Supervisor.
   *
   * MARS keeps the same bit positions internally, including TRUSTEE_O
   * at 0x0004.  Do not translate TRUSTEE_S down to 0x0004 here; doing
   * so makes clients see Open instead of Supervisor and RIGHTS prints
   * [ RWCEMFA] instead of [SRWCEMFA].
   */
  if (rights & TRUSTEE_R) ncp22 |= 0x0001; /* Read */
  if (rights & TRUSTEE_W) ncp22 |= 0x0002; /* Write */
  if (rights & TRUSTEE_O) ncp22 |= 0x0004; /* Open */
  if (rights & TRUSTEE_C) ncp22 |= 0x0008; /* Create */
  if (rights & TRUSTEE_E) ncp22 |= 0x0010; /* Erase */
  if (rights & TRUSTEE_A) ncp22 |= 0x0020; /* Access Control */
  if (rights & TRUSTEE_F) ncp22 |= 0x0040; /* File Scan */
  if (rights & TRUSTEE_M) ncp22 |= 0x0080; /* Modify */
  if (rights & TRUSTEE_S) ncp22 |= 0x0100; /* Supervisor */

  return(ncp22);
}

static int trustee_ncp22_to_v3_rights(int ncp22)
{
  int rights = 0;

  if (ncp22 & 0x0001) rights |= TRUSTEE_R; /* Read */
  if (ncp22 & 0x0002) rights |= TRUSTEE_W; /* Write */
  if (ncp22 & 0x0004) rights |= TRUSTEE_O; /* Open */
  if (ncp22 & 0x0008) rights |= TRUSTEE_C; /* Create */
  if (ncp22 & 0x0010) rights |= TRUSTEE_E; /* Erase */
  if (ncp22 & 0x0020) rights |= TRUSTEE_A; /* Access Control */
  if (ncp22 & 0x0040) rights |= TRUSTEE_F; /* File Scan */
  if (ncp22 & 0x0080) rights |= TRUSTEE_M; /* Modify */
  if (ncp22 & 0x0100) rights |= TRUSTEE_S; /* Supervisor */

  return(rights);
}


static void ncp23_debug_dump(char *what, uint8 *data, int len)
{
  char hex[3 * 64 + 1];
  char asc[65];
  int i;
  int n = len;
  char *hp = hex;

  if (n < 0) n = 0;
  if (n > 64) n = 64;

  for (i = 0; i < n; i++) {
    sprintf(hp, "%02x ", (int)data[i]);
    hp += 3;
    asc[i] = (data[i] >= 32 && data[i] < 127) ? (char)data[i] : '.';
  }
  *hp = '\0';
  asc[n] = '\0';

  XDPRINTF((5,0,
    "%s: payload_len=%d dump_len=%d hex=`%s` ascii=`%s`",
    what, len, n, hex, asc));
}

static int ncp22_component_path_to_dos(uint8 *src, int count,
                                       uint8 *dst, int maxlen,
                                       int *used_out)
{
  int used = 0;
  int out = 0;
  int i;

  if (used_out)
    *used_out = 0;

  if (!dst || maxlen < 1 || count < 0)
    return(-0x9c);

  for (i = 0; i < count; i++) {
    int len = (int)src[used++];

    if (len < 0 || len > 255)
      return(-0x9c);
    if (out + len + ((i > 0) ? 1 : 0) >= maxlen)
      return(-0x9c);

    if (i > 0)
      dst[out++] = '\\';
    memcpy(dst + out, src + used, len);
    out += len;
    used += len;
  }

  dst[out] = '\0';
  if (used_out)
    *used_out = used;
  return(out);
}

#if !CALL_NWCONN_OVER_SOCKET
static  char*      nwconn_state; /* shared memory segment will be 
                                  * attached to this pointer */
#endif

#if ENABLE_BURSTMODE
typedef struct {
  BURSTPACKET *sendburst;   /* buffer for sending burstpacket
                             * allocated and prefilled by response to
                             * func 0x65
                             * max. max_packet_size
                             */

  uint8   *send_buf;        /* we look from servers side
                             * complete data buf of burst reply
                             * file read status + file read buf
                             * sendbuff must be 8 byte more allocated
                             * than max_send_size !
                             */

  int     max_send_size;    /* send_buf size, complete Burst DATA size */

  uint32  packet_sequence;  /* -> packet_sequence
                             * will be increased after every
                             * packet
                             */
  int     burst_sequence;

  struct t_unitdata ud;
  ipxAddr_t to_addr;

  uint8   *recv_buf;        /* complete data buf for burst read requests
                             * must be 24 byte more allocated
                             * than max_recv_size !
                             */

  int     max_recv_size;    /* allocated size of recv_buf */

  int     max_burst_data_size;  /* size of BURSTDATA, max. IPX_DATA - BURSTHEADER */
  uint8   ipx_pack_typ;
} BURST_W;

static BURST_W *burst_w=NULL;
#endif

void nwconn_set_program_title(char *s)
{
  memset(prog_title, 0, 49);
  if (s&&*s)
    strmaxcpy(prog_title, s, 48);
  else
    strcpy(prog_title, "()");
}


static int ncp_response(int sequence, int task,
                int completition, int data_len)
{
  ncpresponse->sequence       = (uint8) sequence;
  ncpresponse->task           = (uint8) task;
  ncpresponse->completition   = (uint8) completition;
  last_sequence               = sequence;

  if (req_printed) {
    XDPRINTF((0,0, "NWCONN NCP_RESP seq:%d, conn:%d,  compl=0x%x task=%d TO %s",
        (int)ncpresponse->sequence,
        (int)ncpresponse->connection
          | (((int)ncpresponse->high_connection) << 8),
        (int)completition,
        (int)ncpresponse->task, visable_ipx_adr((ipxAddr_t *) ud.addr.buf)));
  }
  ud.udata.len = ud.udata.maxlen = sizeof(NCPRESPONSE) + data_len;
  if (t_sndudata(FD_NCP_OUT, &ud) < 0){
    if (nw_debug) t_error("t_sndudata in NWCONN !OK");
    return(-1);
  }
  return(0);
}

static int call_nwbind(int mode)
/* modes 0:  'standard' call
 *       1:  activate wdog
 */
{
  ipxAddr_t to_addr;
  int result;
  memcpy(&to_addr, &my_addr, sizeof(ipxAddr_t));
  U16_TO_BE16(sock_nwbind, to_addr.sock);
  ud.addr.buf  = (char*)&to_addr;
  if (mode==1) {  /* reset wdogs */
    NCPREQUEST buf;
    buf.type[0]    = buf.type[1]=0x22;
    buf.sequence   = ncprequest->sequence;
    buf.connection = ncprequest->connection;
    buf.task       = ncprequest->task;
    buf.high_connection = ncprequest->high_connection;
    buf.function = 0;
    ud.udata.len = ud.udata.maxlen = sizeof(buf);
    ud.udata.buf = (char*)&buf;
    XDPRINTF((3, 0, "send wdog reset"));
    result=t_sndudata(FD_NCP_OUT, &ud);
  } else {
    ud.udata.len = ud.udata.maxlen = sizeof(NCPREQUEST) + requestlen;
    ud.udata.buf = (char*)&readbuff;
    result=t_sndudata(FD_NCP_OUT, &ud);
  }
  ud.addr.buf   = (char*)&from_addr;
  ud.udata.buf  = (char*)&ipxdata;
  if (result< 0){
    if (nw_debug) t_error("t_sndudata in NWCONN !OK");
    return(-1);
  }
  return(0);
}

static void pr_debug_request()
{
  if (req_printed++) return;
  if (ncp_type == 0x2222) {
    int ufunc = 0;
    switch (ncprequest->function) {
      case 0x16 :
      case 0x17 : ufunc = (int) *(requestdata+2); break;
      case 0x57 : ufunc = (int) *(requestdata);   break;
      default   : break;
    } /* switch */
    XDPRINTF((1, 0,  "NCP REQUEST: func=0x%02x, ufunc=0x%02x, seq:%03d, task:%02d",
                      (int)ncprequest->function, ufunc,
                      (int)ncprequest->sequence,
                      (int)ncprequest->task));
  } else {
     XDPRINTF((1, 0, "Got NCP:type:0x%x, seq:%d, task:%d, func=0x%x",
                      ncp_type,
                      (int)ncprequest->sequence,
                      (int)ncprequest->task,
                      (int)ncprequest->function));
  }
  if (requestlen > 0){
    int    j = requestlen;
    uint8  *p=requestdata;
    XDPRINTF((0, 2, "len %d, DATA:", j));
    while (j--) {
      int c = *p++;
      if (c > 32 && c < 127)  XDPRINTF((0, 3,",\'%c\'", (char) c));
      else XDPRINTF((0,3, ",0x%x", c));
    }
    XDPRINTF((0,1,NULL));
  }
}
#if TEST_FNAME
static int test_handle = -1;
#endif

static void handle_nwbind_request(void)    /* mst:25-Apr-00 */
{
  int result = 0;
  char buf[IPX_MAX_DATA];
  char *data = &(buf[sizeof(NCPRESPONSE)]);
  int data_len = 0;
  
  switch (ncprequest->function) {
    case 0x1: {   /* get number and handles of open files */
      struct INPUT {
        uint8   header[7];       /* Requestheader */
        uint8   offset[4];       /* handle offset */
      } *input = (struct INPUT *) (ncprequest);
      struct XDATA {
        uint8 count_handles[4];
        uint8 handles[4];
      } *xdata = (struct XDATA*) data;
      int handles = nw_get_count_open_files(xdata->handles, GET_BE32(input->offset));
      U32_TO_BE32(handles, xdata->count_handles);
      data_len = (handles+1) * 4;
    }
    break;

    default :
      result = 0xfb;
  }
  
  {
    NCPRESPONSE *resp = (NCPRESPONSE*)&buf;
    ipxAddr_t to_addr;
    memcpy(&to_addr, &my_addr, sizeof(ipxAddr_t));
    U16_TO_BE16(sock_nwbind, to_addr.sock);
    ud.addr.buf  = (char*)&to_addr;
    
    resp->type[0]     = resp->type[1]=0x32;
    resp->sequence    = ncprequest->sequence;
    resp->connection  = ncprequest->connection;
    resp->task        = ncprequest->task;
    resp->high_connection = ncprequest->high_connection;
    resp->completition    = result;
    resp->connect_status  = 0;
    ud.udata.len  = ud.udata.maxlen = sizeof(NCPRESPONSE) + data_len;
    ud.udata.buf  = (char*)resp;
    XDPRINTF((3, 0, "send reply to nwbind"));
    result        = t_sndudata(FD_NCP_OUT, &ud);
    ud.addr.buf   = (char*)&from_addr;
    ud.udata.buf  = (char*)&ipxdata;
  }
}


static const char *afp_call_name(int ufunc)
{
  switch (ufunc) {
    case 0x03: return("AFP Delete");
    case 0x04: return("AFP Get Entry ID From Name");
    case 0x06: return("AFP Get Entry ID From NetWare Handle");
    case 0x07: return("AFP Rename");
    case 0x08: return("AFP Open File Fork");
    case 0x0b: return("AFP Alloc Temporary Dir Handle");
    case 0x0c: return("AFP Get Entry ID From Path Name");
    case 0x0d: return("AFP 2.0 Create Directory");
    case 0x0e: return("AFP 2.0 Create File");
    case 0x0f: return("AFP 2.0 Get File Information");
    case 0x10: return("AFP 2.0 Set File Information");
    case 0x11: return("AFP 2.0 Scan File Information");
    default:   return("unknown AFP call");
  }
}

static int afp_request_offset(void)
/*
 * AFP NCP 0x23 calls are documented with a two byte high-low request
 * length followed by the AFP subfunction.  Some old debug paths treated the
 * first payload byte as the subfunction.  Accept both layouts so diagnostics
 * and the first implemented AFP call work with either requester encoding.
 */
{
  if (requestlen >= 3) {
    int plen = GET_BE16(requestdata);
    if (plen == requestlen - 2)
      return(2);
  }
  return(0);
}

static uint32 afp_fallback_entry_id(int volume, const struct stat *stb)
/*
 * Build a stable local AFP entry id from Unix identity data when libatalk has
 * no stored CNID/AppleDouble id yet.  This is not a NetWare-internal directory
 * base number; it is only the AFP id returned by the path-name probe.
 */
{
  uint32 hash = 2166136261U;
  const uint8 *p;
  size_t len;

#define AFP_HASH_BYTES(ptr, size) do { \
    p = (const uint8 *)(ptr); \
    len = (size); \
    while (len--) { hash ^= *p++; hash *= 16777619U; } \
  } while (0)

  AFP_HASH_BYTES(&volume, sizeof(volume));
  AFP_HASH_BYTES(&stb->st_dev, sizeof(stb->st_dev));
  AFP_HASH_BYTES(&stb->st_ino, sizeof(stb->st_ino));
#undef AFP_HASH_BYTES

  hash &= 0x7fffffffU;
  if (!hash) hash = 1;
  return(hash);
}

static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len,
                                           uint8 *response)
{
  uint8 dir_handle;
  int path_len;
  int volume;
  char unixname[PATH_MAX];
  struct stat stbuff;
  uint32 entry_id = 0;
  int result;

  if (afp_len < 3) {
    XDPRINTF((2,0, "AFP Get Entry ID From Path Name rejected: short request len=%d",
              afp_len));
    return(-0x7e); /* NCP Boundary Check Failed */
  }

  dir_handle = afp_req[1];
  path_len   = (int)afp_req[2];
  if (path_len < 0 || afp_len < 3 + path_len) {
    XDPRINTF((2,0, "AFP Get Entry ID From Path Name rejected: boundary check len=%d path_len=%d",
              afp_len, path_len));
    return(-0x7e);
  }

  if (!nwatalk_backend_available()) {
    XDPRINTF((3,0, "AFP Get Entry ID From Path Name rejected: libatalk backend unavailable"));
    return(-0xbf); /* invalid namespace */
  }

  volume = conn_get_kpl_unxname(unixname, sizeof(unixname),
                                (int)dir_handle, afp_req + 3, path_len);
  if (volume < 0) {
    XDPRINTF((2,0, "AFP Get Entry ID From Path Name path resolve failed: dh=%d path='%s' result=-0x%x",
              (int)dir_handle, visable_data(afp_req + 3, path_len), -volume));
    return(volume);
  }

  if (stat(unixname, &stbuff)) {
    XDPRINTF((2,0, "AFP Get Entry ID From Path Name stat failed: dh=%d path='%s' unix='%s' errno=%d",
              (int)dir_handle, visable_data(afp_req + 3, path_len),
              unixname, errno));
    return(-0x9c); /* Invalid Path */
  }

  result = nwatalk_get_entry_id(unixname, &entry_id);
  if (result < 0 || !entry_id)
    entry_id = afp_fallback_entry_id(volume, &stbuff);

  U32_TO_BE32(entry_id, response);
  XDPRINTF((3,0, "AFP Get Entry ID From Path Name: dh=%d path='%s' entry=0x%08x%s",
            (int)dir_handle, visable_data(afp_req + 3, path_len), entry_id,
            (result < 0) ? " fallback" : ""));
  return(4);
}

static int handle_ncp_serv(void)
{
  int    function       = (int)ncprequest->function;
  int    completition   = 0;  /* first set      */
  int    org_nw_debug   = nw_debug;
  int    do_druck       = 0;
  int    data_len       = 0;

  if (last_sequence == (int)ncprequest->sequence
       && ncp_type != 0x1111){ /* send the same again */
    if (t_sndudata(FD_NCP_OUT, &ud) < 0) {
      if (nw_debug) t_error("t_sndudata !OK");
    }
    XDPRINTF((3,0, "Sequence %d is written twice", (int)ncprequest->sequence));
    return(0);
  }
  req_printed=0;

  if (nw_debug > 1){
    if (nw_debug < 10
       && (function==0x48 || function == 0x49))  /* read or write */
          nw_debug=1;
    if (nw_debug < 15
       && (function==0x48)) /* read */
          nw_debug=1;
  }

  if (nw_debug > 5) pr_debug_request();

  if (ncp_type == 0x2222) {
    
    switch (function) {
      
      /* 0.99.pl18,  25-Sep-99
       * these log/lock functions are not well tested and perhaps not
       * ok .
       */
      case 0x3 : { /* Log File */
        struct INPUT {
          uint8   header[7];       /* Requestheader */
          uint8   dir_handle;
          uint8   lock_flag;       /* 0 = log, 1 = lock excl */ 
                                   /* 3 = lock shared        */
          uint8   timeout[2];      /* HI LO */
          uint8   len;
          uint8   path[2];
        } *input = (struct INPUT *) (ncprequest);
        int result = nw_log_file( input->lock_flag,
                                GET_BE16(input->timeout),
                                input->dir_handle,
                                input->len,
                                input->path);
        if (result) completition = (uint8) -result;
      }
      break;

      case 0x4 :   /* Lock File Set (old) */
      case 0x6a : { /* Lock File Set       */
        /*
         * NCP 0x2222/04 Lock File Set (old) and NCP 0x2222/106 Lock
         * File Set lock all files logged by the calling client's current
         * task.
         *
         * SDK request: 2-byte Lock Timeout, in units of 1/18 second;
         * 0 means No Wait.
         * SDK reply: no reply data.
         * SDK completion: 0x00 success, 0xfe timeout, 0xff lock error.
         *
         * The newer 0x6a call replaces the old 0x04 call but has the same
         * request layout for the file set lock itself.  MARS-NWE records
         * file-set members through Log File (0x03), then uses the shared
         * set handler for Lock/Release/Clear File Set operations.
         */
        struct INPUT {
          uint8   header[7];       /* Requestheader */
          uint8   timeout[2];      /* HI LO         */
        } *input = (struct INPUT *) (ncprequest);
        int result = share_handle_lock_sets(
                                      1,   /* File Set */
                                      0,   /* Lock logged entries */
                                      GET_BE16(input->timeout));
        if (result) completition = (uint8) -result;
      }
      break;
      
      case 0x5 :    /* Release File  */
      case 0x7 : {  /* Clear File, removes file from logset */
        struct INPUT {
          uint8   header[7];       /* Requestheader */
          uint8   dir_handle;
          uint8   len;
          uint8   path[2];
        } *input = (struct INPUT *) (ncprequest);
        int result = nw_log_file( (function==0x7)
                                   ? -2   /* unlock and unlog */
                                   : -1,  /* unlock           */
                                   0,
                                   input->dir_handle,
                                   input->len,
                                   input->path);
        if (result) completition = (uint8) -result;
      } 
      break;

      case 0x6 :    /* Release File Set */
      case 0x8 :  { /* Clear File Set  */
        int result = share_handle_lock_sets(
                                      1,   /* File Set */
                               (function==0x8) 
                                   ? -2    /* Clear    */
                                   : -1,   /* Release  */
                                      0); 
        
        if (result) completition = (uint8) -result;
      } 
      break;
      
      case 0x9 :  { /* Log Logical Record */
        struct INPUT {
          uint8   header[7];       /* Requestheader      */
          uint8   lock_flag;       /* 0 = log            */ 
                                   /* 1 = exclusive lock */
                                   /* 3 = shared ro lock */
          uint8   timeout[2];      /* HI LO              */
          uint8   len;             /* synch name len     */
          uint8   name[2];         /* synch name         */
        } *input = (struct INPUT *) (ncprequest);
        int result = nw_log_logical_record(
                                (int) input->lock_flag,
                                (int) GET_BE16(input->timeout),
                                (int) input->len,
                                input->name);
        if (result) completition = (uint8) -result;
      } 
      break;

      case 0xa :  { /* Log Logical Record Set */
        struct INPUT {
          uint8   header[7];       /* Requestheader       */
          uint8   lock_flag;       /* 0 = log             */
                                   /* 1 = exclusive lock  */
                                   /* 3 = shared ro lock  */
          uint8   timeout[2];      /* HI LO               */
        } *input = (struct INPUT *) (ncprequest);
        int result = share_handle_lock_sets(
                                 2,   /* Logical Record Set */
                                (int) input->lock_flag,
                                (int) GET_BE16(input->timeout) );
        if (result) completition = (uint8) -result;
      } 
      break;

      case 0xb :     /* Clear Logical Record */
      case 0xc : {   /* Release Logical Record */
        /*
         * SDK: NCP 0x2222/11 Clear Logical Record removes one logical
         * record from the calling task's synchronization table.
         *
         * SDK: NCP 0x2222/12 Release Logical Record releases/unlocks one
         * synchronization string held by the calling client.  The request
         * is a one-byte Synch Name Length followed by the Synch Name
         * (up to 128 bytes), returns no reply data, and reports success or
         * Unlock Error through the completion code.
         *
         * Important semantic difference: Release Logical Record leaves the
         * string in the caller's synchronization string table so a later
         * Lock Logical Record Set can relock it.  Clear Logical Record
         * unlocks and removes it from the table.
         */
        struct INPUT {
          uint8   header[7];       /* Requestheader  */
          uint8   len;             /* synch name len */
          uint8   name[2];         /* synch name     */
        } *input = (struct INPUT *) (ncprequest);
        int result = nw_log_logical_record(
                                (function == 0xb)
                                 ? -2   /* unlock + unlog */
                                 : -1,  /* unlock         */
                                 0,
                                (int) input->len,
                                input->name);
        if (result) completition = (uint8) -result;
      } 
      break;

      case 0xe :   /* Clear Logical Record Set   */
      case 0xd : { /* Release Logical Record Set */
        int result = share_handle_lock_sets(
                                      2,   /* Logical Record Set */
                               (function==0xe) 
                                   ? -2    /* Clear    */
                                   : -1,   /* Release  */
                                      0);
        if (result) completition = (uint8) -result;
      } 
      break;

      case 0x12 : { /* Get Volume Info with Number */
        int volume = (int)*requestdata;
        struct XDATA {
          uint8 sec_per_block[2];
          uint8 total_blocks[2];
          uint8 avail_blocks[2];
          uint8 total_dirs[2];
          uint8 avail_dirs[2];
          uint8 name[16];
          uint8 removable[2];
        } *xdata = (struct XDATA*) responsedata;
        int result;
        memset(xdata, 0, sizeof(struct XDATA));
        if ((result = nw_get_volume_name(volume, xdata->name, sizeof(xdata->name)))>-1){
          struct fs_usage fsp;
          if (!nw_get_fs_usage(xdata->name, &fsp,
             entry8_flags&0x40  )) {
            int sector_scale=1;
            while (fsp.fsu_blocks/sector_scale > 0xffff)
                   sector_scale+=2;
            U16_TO_BE16(sector_scale, xdata->sec_per_block);
            U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks);
            U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks);
            U16_TO_BE16(fsp.fsu_files,  xdata->total_dirs);
            U16_TO_BE16(fsp.fsu_ffree,  xdata->avail_dirs);
            if ( get_volume_options(volume) & VOL_OPTION_REMOUNT) {
              U16_TO_BE16(1,  xdata->removable);
            } else {
              U16_TO_BE16(0,  xdata->removable);
            }
          }
          data_len = sizeof(struct XDATA);
        } else completition = (uint8) -result;
      } 
      break;

      case 0x13 : { /* Get Station Number */
        /*
         * This is the old one-byte connection number call used by
         * DOS requesters before the richer NCP 23 connection services.
         * The SDK describes the client-visible API as
         * NWGetConnectionNumber(); older low-level documentation names the
         * underlying simple NCP 0x13 "Get Station Number".
         */
        if (act_connection > 0 && act_connection < 256) {
          *responsedata=(uint8) act_connection;
          data_len = 1;
        } else {
          XDPRINTF((1, 0, "Get Station Number failed, conn=%d",
                    act_connection));
          completition = 0xff;
        }
      } 
      break;

      case 0x14 : { /* GET DATE und TIME */
        struct SERVER_DATE {
          uint8   year;
          uint8   mon;
          uint8   day;
          uint8   std;
          uint8   min;
          uint8   sec;
          uint8   day_of_week;
        } *mydate = (struct SERVER_DATE*) responsedata;
        struct tm  *s_tm;
        time_t      timer;
        time(&timer);
        s_tm = localtime(&timer);
        mydate->year  = (uint8) s_tm->tm_year;
        mydate->mon   = (uint8) s_tm->tm_mon+1;
        mydate->day   = (uint8) s_tm->tm_mday;

        mydate->std   = (uint8) s_tm->tm_hour;
        mydate->min   = (uint8) s_tm->tm_min;
        mydate->sec   = (uint8) s_tm->tm_sec;
        mydate->day_of_week = (uint8) s_tm->tm_wday; /* Wochentag  */
        data_len = sizeof(struct SERVER_DATE);
      }
      break;

      case 0x15 :
        return(-1); /* nwbind must do this call */

      case 0x16 : {
        /* uint8 len = *(requestdata+1); */
        uint8 *p  =  requestdata +2;
        
        switch (*p) {         
                 
            case 0 : {
             /******** SetDirektoryHandle *************/
                   struct INPUT {
                     uint8   header[7];         /* Requestheader    */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   target_dir_handle; /* to change        */
                     uint8   source_dir_handle;
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   completition =
                     (uint8)-nw_set_dir_handle((int)input->target_dir_handle,
                                               (int)input->source_dir_handle,
                                                    input->path,
                                               (int)input->pathlen,
                                               (int)(ncprequest->task));
                 } 
                 break;

            case 0x1 : {
             /******** GetDirektoryPATH ***************/
                   struct INPUT {
                     uint8   header[7];      /* Requestheader */
                     uint8   div[3];         /* 0x0, dlen, ufunc */
                     uint8   dir_handle;
                   } *input = (struct INPUT *) (ncprequest);
                   struct XDATA {
                     uint8 len;
                     uint8 name[256];
                   } *xdata = (struct XDATA*) responsedata;
                   int result = nw_get_directory_path((int)input->dir_handle,
                                                   xdata->name, sizeof(xdata->name));
                   if (result > -1){
                      xdata->len = (uint8) result;
                      data_len   = result + 1;
                      xdata->name[result] = '\0';
                      XDPRINTF((5,0, "GetDirektoryPATH=%s", xdata->name));
                   } else completition = (uint8)-result;
                 } 
                 break;

            case 0x2 : { /* Scan Direktory Information */
             /******** Scan Dir Info   ****************/
                   struct INPUT {
                     uint8   header[7];       /* Requestheader */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   dir_handle;      /* Verzeichnis Handle */
                     uint8   sub_dir_nmbr[2]; /* HI LOW */
                                              /* firsttime 1 */
                     uint8   len;             /* kann auch 0 sein */
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   struct XDATA {
                     uint8 sub_dir_name[16];
                     uint8 create_date_time[4];
                     uint8 owner_id[4];       /* HI LOW */
                     uint8 max_right_mask;    /* inherited right mask */
                     uint8 reserved;          /* Reserved by Novell   */
                     uint8 sub_dir_nmbr[2];   /* HI LOW */
                   } *xdata = (struct XDATA*) responsedata;
                   int result;
                   memcpy(xdata->sub_dir_nmbr, input->sub_dir_nmbr, 2);
                   result = nw_scan_dir_info((int)input->dir_handle,
                            input->path, (int)input->len,
                            xdata->sub_dir_nmbr, xdata->sub_dir_name,
                            xdata->create_date_time, xdata->owner_id);
                   if (result > -1){
                      xdata->max_right_mask = (uint8)result;
                      data_len              = sizeof(struct XDATA);
                      XDPRINTF((5,0,"Scan Dir Info max_right_mask=%d", (int)result));
                   } else completition = (uint8)-result;
                 } 
                 break;
                 
            case 0x3 : { /* Get Direktory Rights */
             /******** Get Eff Dir Rights ****************/
                   struct XDATA {
                     uint8 eff_right_mask;  /* Effektive Right to Dir, old! */
                   } *xdata = (struct XDATA*) responsedata;
                   int result = nw_get_eff_dir_rights(
                                  (int)*(p+1),
                                         p+3,
                                  (int)*(p+2), 0);
                   if (result > -1) {
                     int ncp22_rights = trustee_v3_to_ncp22_rights(result);
                     xdata->eff_right_mask = (uint8)ncp22_rights;
                     data_len = 1;
                     XDPRINTF((5,0,"NCP22/3 Get Dir Rights=%d ncp22=%d",
                               (int)result, (int)ncp22_rights));
                   } else completition = (uint8) -result;
                 } 
                 break;

            case 0x4 : { /* Modify Max Right Mask */
             /******** MODIFY MAX RIGHT MASK ****************/
                   /*
                    * Request (NetWare 2.x compatible NCP 22/4):
                    *   byte directory handle
                    *   byte grant rights mask
                    *   byte revoke rights mask
                    *   byte path length
                    *   path bytes
                    *
                    * New mask is: (old_mask | grant_mask) & ~revoke_mask.
                    *
                    * No reply data.
                    */
                   int result = nw_modify_max_right_mask(
                                  (int)*(p+1),
                                         p+5,
                                  (int)*(p+4),
                                  (int)*(p+2),
                                  (int)*(p+3));
                   if (result) completition = (uint8)-result;
                 } 
                 break;

            case 0x5 : { /* Get Volume Number 0 .. 31 */
             /******** GetVolume Number ***************/
                   /* p+1 = namelen */
                   /* p+2 = data z.b 'SYS' */
                   struct XDATA {
                     uint8 volume;  /* Nummer */
                   } *xdata = (struct XDATA*) responsedata;
                   int result = nw_get_volume_number(p+2, (int)*(p+1));
                   if (result > -1) {
                     xdata->volume = (uint8) result;
                     data_len = 1;
                   } else completition = (uint8) -result;
                 } 
                 break;

            case 0x6 : { /* Get Volume Name from 0 .. 31 */
             /******** Get Volume Name  ***************/
                   struct XDATA {
                     uint8 namelen;
                     uint8 name[16];
                   } *xdata = (struct XDATA*) responsedata;
                   int result = nw_get_volume_name((int)*(p+1),
                                 xdata->name, sizeof(xdata->name));
                   if (result > -1) {
                     xdata->namelen = (uint8) result;
                     data_len       = result+1;
                   } else completition = (uint8) -result;
                 } 
                 break;

            case 0xa : { /* create directory  */
             /******** Create Dir *********************/
                   int dir_handle  = (int) *(p+1);
#if 0
                   int rightmask   = (int) *(p+2);
#endif
                   int pathlen     = (int) *(p+3);
                   uint8 *path     =  p+4;
                   int code = nw_mk_rd_dir(dir_handle, path, pathlen, 1);
                   if (code) completition = (uint8) -code;
                 } 
                 break;
                 
            case 0xb : { /* delete dirrctory */
             /******** Delete DIR *********************/
                   int dir_handle  = (int) *(p+1);
#if 0
                   int reserved    = (int) *(p+2); /* Res. by NOVELL */
#endif
                   int pathlen     = (int) *(p+3);
                   uint8 *path     =  p+4;
                   int code = nw_mk_rd_dir(dir_handle, path, pathlen, 0);
                   if (code) completition = (uint8) -code;
                 } 
                 break;
                 
            case 0xd : {  /* Add Trustees to DIR  */
             /******** AddTrustesstoDir ***************/
                   struct INPUT {
                     uint8   header[7];      /* Requestheader      */
                     uint8   div[3];         /* 0x0, dlen, ufunc   */
                     uint8   dir_handle;     /* Verzeichnis Handle */
                     uint8   trustee_id[4];  /* Trustee Object ID  */
                     uint8   trustee_right_mask;
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   uint32 trustee_id = GET_BE32(input->trustee_id);
                   int ncp22_rights = (int)input->trustee_right_mask;
                   int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights);
                   int copylen = input->pathlen;
                   uint8 pathbuf[256];
                   int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
                   int result;

                   ncp23_debug_dump("NCP22/0D AddTrustee raw",
                       (uint8 *)&input->dir_handle, raw_len);

                   if (copylen > 255) copylen = 255;
                   memcpy(pathbuf, input->path, copylen);
                   pathbuf[copylen] = '\0';

                   result = nw_add_trustee(
                       input->dir_handle,
                       input->path, input->pathlen,
                       trustee_id,
                       trustee_rights,
                       0);
                   XDPRINTF((5,0,
                     "NCP22/0D AddTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%02x rights=0x%03x rc=%d",
                     input->dir_handle, input->pathlen, pathbuf,
                     (unsigned long)trustee_id, ncp22_rights, trustee_rights, result));
                   if (result) completition = (uint8) -result;
                 } 
                 break;
                 
            case 0xe : {  /* remove trustees */
                   struct INPUT {
                     uint8   header[7];         /* Requestheader     */
                     uint8   div[3];            /* 0x0, dlen, ufunc  */
                     uint8   dir_handle;        /* Handle            */
                     uint8   trustee_id[4];     /* Trustee Object ID */
                     uint8   reserved;
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   uint32 trustee_id = GET_BE32(input->trustee_id);
                   int copylen = input->pathlen;
                   uint8 pathbuf[256];
                   int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
                   int result;

                   ncp23_debug_dump("NCP22/0E DelTrustee raw",
                       (uint8 *)&input->dir_handle, raw_len);

                   if (copylen > 255) copylen = 255;
                   memcpy(pathbuf, input->path, copylen);
                   pathbuf[copylen] = '\0';

                   result = nw_del_trustee(
                       input->dir_handle,
                       input->path, input->pathlen,
                       trustee_id,
                       0); /* normal */
                   XDPRINTF((5,0,
                     "NCP22/0E DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d",
                     input->dir_handle, input->pathlen, pathbuf,
                     (unsigned long)trustee_id, result));
                   if (result) completition = (uint8) -result;
                 } 
                 break;
                 
            case 0xf : { /* rename dir */
             /******** Rename DIR *********************/
                   int dir_handle  = (int) *(p+1);
                   int oldpathlen  = (int) *(p+2);
                   uint8 *oldpath  =       p+3;
                   int newpathlen  = (int) *(oldpath + oldpathlen);
                   uint8 *newpath  =       oldpath + oldpathlen + 1;
                   int code = mv_dir(dir_handle,
                                        oldpath, oldpathlen,
                                        newpath, newpathlen);
                   if (code) completition = (uint8) -code;
                 } 
                 break;
                 
            case 0x12 : /* Allocate Permanent Dir Handle */
             /******** Allocate Permanent DIR Handle **/
            case 0x13 : /* Allocate Temp Dir Handle */
             /******** Allocate Temp DIR Handle **/
            case 0x16 : { /* Allocate Special Temp Dir Handle */
             /******** Allocate spez temp  DIR Handle **/
                   
                   struct XDATA {
                     uint8 dirhandle;   /* new Dir Handle   */
                     uint8 right_mask;  /* 0xff effektive Right MAsk ? */
                   } *xdata = (struct XDATA*) responsedata;
                   int eff_rights;
                   int dirhandle = nw_alloc_dir_handle(
                                      (int) *(p+1),
                                             p+4,
                                      (int)*(p+3),
                                      (int)*(p+2),
                                      (*p==0x12) ? 0
                                   : ((*p==0x13) ? 1 : 2),
                                   (int)(ncprequest->task),
                                   &eff_rights);
                   if (dirhandle > -1){
                     xdata->dirhandle  = (uint8) dirhandle;
                     xdata->right_mask = eff_rights;
                     data_len = sizeof(struct XDATA);
                   } else completition = (uint8) -dirhandle;

                 } 
                 break;

            case 0x14 : { /* deallocate Dir Handle */
             /******** Free DIR Handle ****************/
                   int err_code = nw_free_dir_handle((int)*(p+1),
                                   (int)(ncprequest->task));
                   if (err_code) completition = (uint8) -err_code;
                 } 
                 break;

            case 0x15 : { /* liefert Volume Information */
             /******** Get Volume Info with Handle ****/
                   struct XDATA {
                     uint8  sectors[2];
                     uint8  total_blocks[2];
                     uint8  avail_blocks[2];
                     uint8  total_dirs[2];  /* anz dirs  */
                     uint8  avail_dirs[2];  /* free dirs */
                     uint8  name[16];       /* SYS Name  */
                     uint8  removable[2];
                   } *xdata = (struct XDATA*)responsedata;
                   int result = nw_get_vol_number((int)*(p+1));
                   memset(xdata, 0, sizeof(struct XDATA));
                   if (result > -1) {
                     int volume = result;
                     result = nw_get_volume_name(volume,
                                        xdata->name, sizeof(xdata->name) );
                     if (result > -1) {
                       struct fs_usage fsp;
                       if (!nw_get_fs_usage(xdata->name, &fsp,
                            entry8_flags&0x40 )) {
                         int sector_scale=1;
                         while (fsp.fsu_blocks/sector_scale > 0xffff)
                            sector_scale+=2;
                         U16_TO_BE16(sector_scale, xdata->sectors);
                         U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks);
                         U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks);
                         U16_TO_BE16(fsp.fsu_files,  xdata->total_dirs);
                         U16_TO_BE16(fsp.fsu_ffree,  xdata->avail_dirs);
                         if (get_volume_options(volume) & VOL_OPTION_REMOUNT) {
                           U16_TO_BE16(1,  xdata->removable);
                         } else {
                           U16_TO_BE16(0,  xdata->removable);
                         }
                       }
                       data_len = sizeof(struct XDATA);
                       XDPRINTF((5,0,"GIVE VOLUME INFO from :%s:", xdata->name));
                       result = 0;
                     }
                   }
                   completition = (uint8)-result;
                 }
                 break;

            case 0x17 : { /* Extract a Base Handle */
                  /*
                   * NCP 0x2222/22/23 Extract a Base Handle.
                   *
                   * WebSDK: request carries DirectoryHandle.  Reply returns a
                   * 10-byte ServerNetworkAddress plus a 4-byte opaque
                   * HandleID.  The handle must have been allocated with Alloc
                   * Permanent Directory Handle.  The returned HandleID is used
                   * by NCP 0x16/0x18 Restore an Extracted Base Handle.
                   *
                   * MARS-NWE keeps the HandleID opaque to the client and maps
                   * it to the current connection's saved volume/path tuple.
                   */
                  struct XDATA {
                    uint8 server_network_address[10];
                    uint8 handle_id[4];
                  } *xdata = (struct XDATA*)responsedata;
                  int result;
                  memcpy(xdata->server_network_address, my_addr.net, 4);
                  memcpy(xdata->server_network_address+4, my_addr.node, 6);
                  result = nw_extract_base_handle((int)*(p+1), xdata->handle_id);
                  if (result) completition = (uint8)-result;
                  else data_len = sizeof(struct XDATA);
                 }
                 break;

            case 0x18 : {  /* Restore Directory Handle */
                  /*
                   * NCP 0x2222/22/24 Restore an Extracted Base Handle.
                   *
                   * WebSDK: request carries the 10-byte ServerNetworkAddress
                   * and 4-byte HandleID returned by Extract a Base Handle.
                   * Reply returns NewDirectoryHandle and AccessRightsMask.
                   *
                   * The old SDK API exposes this as NWRestoreDirectoryHandle()
                   * and uses a 14-byte save buffer produced by
                   * NWSaveDirectoryHandle().  Rust nwserver and lwared do not
                   * implement this older save/restore pair; newer clients
                   * typically use the ordinary allocate/set directory-handle
                   * calls instead.  Keep this implementation conservative and
                   * connection-local rather than guessing a global NetWare
                   * directory-base number.
                   */
                  struct INPUT {
                    uint8 server_network_address[10];
                    uint8 handle_id[4];
                  } *input = (struct INPUT*)(p+1);
                  struct XDATA {
                    uint8 dirhandle;
                    uint8 right_mask;
                  } *xdata = (struct XDATA*)responsedata;
                  int eff_rights = 0;
                  int result;
                  if (memcmp(input->server_network_address, my_addr.net, 4)
                      || memcmp(input->server_network_address+4, my_addr.node, 6)) {
                    completition = 0xfd; /* Bad Station Number */
                    break;
                  }
                  result = nw_restore_base_handle(input->handle_id,
                             (int)(ncprequest->task), &eff_rights);
                  if (result > -1) {
                    xdata->dirhandle = (uint8)result;
                    xdata->right_mask = (uint8)eff_rights;
                    data_len = sizeof(struct XDATA);
                  } else completition = (uint8)-result;
                 }
                 break;
            case 0x19 : {
                  /* Set Directory Information
                   * Modifies basic directory information as creation date and
                   * directory rights mask. DOS namespace.
                   */
                   struct INPUT {
                     uint8   header[7];         /* Requestheader */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   dir_handle;
                     uint8   creation_date[2];
                     uint8   creation_time[2];
                     uint8   owner_id[4];
                     uint8   new_max_rights;
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   int result = nw_set_dir_info(
                       input->dir_handle,
                       input->path, input->pathlen,
                       GET_BE32(input->owner_id),
                       (int)input->new_max_rights,
                       input->creation_date,
                       input->creation_time);
                   if (result<0) completition = (uint8) -result;
                   /* No REPLY  */
                 } 
                 break;

            case 0x1a : { /* Get Pathname of A Volume Dir Pair */
                   /*
                    * Old NetWare 2.x compatibility call:
                    *   byte  volume
                    *   word  directory entry number (Hi-Lo)
                    *
                    * Reply:
                    *   byte path length
                    *   byte path[path length]
                    *
                    * This is the older string form of the NCP23/F3 mapping.
                    * NCP23/F3 is preferred for 32-bit directory numbers and
                    * namespace-aware callers.
                    */
                   struct XDATA {
                     uint8  pathlen;
                     uint8  pathname[255];
                   } *xdata = (struct XDATA*)responsedata;
                   int volume = (int)*(p+1);
                   uint32 dirnum = (uint32)GET_BE16(p+2);
                   uint8 comps[256];
                   uint8 pathbuf[255];
                   int result;
                   int pos = 0;
                   int out = 0;
                   int first = 1;

                   result = map_directory_number_to_path(volume, dirnum, 0,
                                                         comps, sizeof(comps));
                   if (result > -1) {
                     while (pos < result) {
                       int l = (int)comps[pos++];

                       if (l <= 0 || pos + l > result) {
                         result = -0x9c;
                         break;
                       }

                       if (!first) {
                         if (out >= (int)sizeof(pathbuf)) {
                           result = -0x9c;
                           break;
                         }
                         pathbuf[out++] = '\\';
                       }

                       if (out + l > (int)sizeof(pathbuf)) {
                         result = -0x9c;
                         break;
                       }

                       memcpy(pathbuf + out, comps + pos, l);
                       out += l;
                       pos += l;
                       first = 0;
                     }
                   }

                   if (result > -1) {
                     xdata->pathlen = (uint8)out;
                     memcpy(xdata->pathname, pathbuf, out);
                     data_len = out + 1;
                   } else completition = (uint8)(-result);
                 }
                 break;
                 
            case 0x1e : { /* SCAN a Directory, e.g. used by ndir.exe */
                   struct INPUT {
                     uint8   header[7];        /* Requestheader */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   dir_handle;       /* Verzeichnis Handle */
                     uint8   attrib;           /* Search Attrib z.B. 0x6 */
                     uint8   searchsequence[4]; /* 32 bit */
                     uint8   len;
                     uint8   data[2];
                   } *input = (struct INPUT *) (ncprequest);
                   int result = nw_scan_a_directory(
                                responsedata,
                                input->dir_handle,
                                input->data,
                                input->len,
                                input->attrib,
                                GET_BE32(input->searchsequence));

                   if (result > -1) data_len = result;
                   else completition = (uint8) (-result);
                 } 
                 break;
                 
            case 0x1f : { /* SCAN a root dir ????  */
                   struct INPUT {
                     uint8   header[7];        /* Requestheader      */
                     uint8   div[3];           /* 0x0, dlen, ufunc */
                     uint8   dir_handle;       /* Verzeichnis Handle */
                     uint8   dont_know1;       /* ????  0xc0  */
                     uint8   dont_know2;       /* ????  0xfa  */
                   } *input = (struct INPUT *) (ncprequest);
                   int result = nw_scan_a_root_dir(
                                responsedata,
                                input->dir_handle);
                   if (result > -1) data_len = result;
                   else completition = (uint8) (-result);
                 } 
                 break;
                 
            case 0x20 : { /* Scan Volume's User Disk Restrictions */
                   /*
                    * NCP 0x2222/22/32 Scan Volume's User Disk Restrictions.
                    *
                    * WebSDK / headers:
                    * Request: Volume Number, Sequence.  Sequence starts at
                    * zero and is incremented by the number of entries returned
                    * by previous calls.
                    * Reply: Number Of Entries followed by up to twelve
                    * Object ID / Restriction pairs.  Restrictions are reported
                    * in 4K blocks.
                    *
                    * The Rust nwserver and lwared implementations both return
                    * an empty list after validating the volume.  MARS-NWE keeps
                    * that SDK-compatible unrestricted result for non-quota
                    * builds, and fills entries from the existing Linux quota
                    * backend when QUOTA_SUPPORT is enabled and a restricted
                    * bindery user maps to a Unix uid.
                    */
                   uint8  volnr    = *(p+1);
                   uint32 sequence = GET_BE32(p+2);
                   int result = scan_volume_user_disk_restrictions(
                                  volnr, sequence, responsedata);
                   if (result > -1) data_len = result;
                   else completition = (uint8) (-result);
                 }
                 break;
                 
            case 0x21 : { /* change Vol restrictions for Obj */
                   XDPRINTF((5, 0, "Change vol restrictions"));
                 } 
                 return(-2); /* nwbind must do prehandling */
                 
            case 0x22 : { /* remove Vol restrictions for Obj */
                   XDPRINTF((5, 0, "Remove vol restrictions"));
                 }
                 return(-2); /* nwbind must do prehandling */
                 
            case 0x25 : { /* Set Entry, Set Directory File Information 
                   * sets or changes the file or directory information to the
                   * values entered in 'Change Bits'.
                   * NO REPLY
                   * used by ncopy.exe, flag.exe
                   */
                   struct INPUT {
                     uint8   header[7];        /* Requestheader */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   dir_handle;
                     uint8   attrib;
                     NW_SET_DIR_INFO f;
                   } *input = (struct INPUT *) (ncprequest);
                   int result = nw_set_a_directory_entry(
                                input->dir_handle,
                                input->f.u.f.name,
                                input->f.u.f.namlen,
                                input->attrib,
                                GET_BE32(input->f.searchsequence),
                                &(input->f));
                   if (result < 0) {
                     completition = (uint8)(-result);
                     XDPRINTF((5,0,
                       "NCP22/25 SetDirFileInfo: dh=%d attr=0x%02x seq=0x%08lx result=%d cc=0x%02x",
                       (int)input->dir_handle,
                       (int)input->attrib,
                       (unsigned long)GET_BE32(input->f.searchsequence),
                       result,
                       (unsigned int)completition));
                   }
                 } 
                 break;
                 
            case 0x26 : { /* Scan file or Dir for ext trustees */
                   int sequence = (int)*(p+2); /* trustee sequence  */
                   struct XDATA {
                     uint8  entries;
                     uint8  ids[80];       /* 20 id's */
                     uint8  trustees[40];  /* 20 trustees's */
                   } *xdata = (struct XDATA*) responsedata;
                   uint32 ids[20];
                   int    trustees[20];
                   int result  = nw_scan_for_trustee(
                                 (int)*(p+1),   /* dir handle */
                                 sequence,
                                 p+4,           /* path */
                                 (int)*(p+3),   /* pathlen */
                                 20,            /* max entries */
                                 ids,
                                 trustees,
                                 1);  /* extended */
                   if (result > -1) {
                     int       i = -1;
                     uint8 *idsp = xdata->ids;
                     uint8 *trp  = xdata->trustees;
                     memset(xdata, 0, sizeof(*xdata));
                     xdata->entries = result;
                     while(++i < result) {
                       int ncp22_rights = trustee_v3_to_ncp22_rights(trustees[i]);
                       U32_TO_BE32(ids[i],      idsp);
                       idsp+=4;
                       U16_TO_16(ncp22_rights, trp); /* LO - HI */
                       trp+=2;
                     }
                     data_len = sizeof(struct XDATA);
                   } else completition = (uint8) (-result);
                 } 
                 break;
                 
            case 0x27 : { /* Add Ext Trustees to DIR or File */
                   struct INPUT {
                     uint8   header[7];         /* Requestheader     */
                     uint8   div[3];            /* 0x0, dlen, ufunc  */
                     uint8   dir_handle;        /* Handle            */
                     uint8   trustee_id[4];     /* Trustee Object ID */
                     uint8   trustee_rights[2]; /* lo - hi           */
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   uint32 trustee_id = GET_BE32(input->trustee_id);
                   int ncp22_rights = GET_16(input->trustee_rights);
                   int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights);
                   int copylen = input->pathlen;
                   uint8 pathbuf[256];
                   int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
                   int result;

                   ncp23_debug_dump("NCP22/27 SetTrustee raw",
                       (uint8 *)&input->dir_handle, raw_len);

                   if (copylen > 255) copylen = 255;
                   memcpy(pathbuf, input->path, copylen);
                   pathbuf[copylen] = '\0';

                   result = nw_add_trustee(
                       input->dir_handle,
                       input->path, input->pathlen,
                       trustee_id,
                       trustee_rights,
                       1); /* extended */
                   XDPRINTF((5,0,
                     "NCP22/27 SetTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%04x rights=0x%03x rc=%d",
                     input->dir_handle, input->pathlen, pathbuf,
                     (unsigned long)trustee_id, ncp22_rights, trustee_rights, result));
                   if (result) completition = (uint8) -result;
                 } 
                 break;
                 
            case 0x28 : { /* Scan File Physical  ??? */
                   struct INPUT {
                     uint8   header[7];         /* Requestheader */
                     uint8   div[3];            /* 0x0, dlen, ufunc */
                     uint8   dir_handle;        /* directory handle */
                     uint8   attrib;            /* Search Attrib ?? 0x2f */
                     uint8   searchsequence[4]; /* 32 bit */
                     uint8   len;
                     uint8   data[2];
                   } *input = (struct INPUT *) (ncprequest);
                   /* we try, whether this is ok  ????? */
                   int result = nw_scan_a_directory(
                                responsedata,
                                input->dir_handle,
                                input->data,
                                input->len,
                                input->attrib,
                                GET_BE32(input->searchsequence));
                   if (result > -1) data_len = result;
                   else completition = (uint8) (-result);
                 } 
                 break;
            
            /*
             * NCP 0x2222/22/41 Get Object Disk Usage And Restrictions.
             *
             * WebSDK / headers:
             *   Request:  Volume Number, Object ID (high-low)
             *   Reply:    Restriction and In Use, both 32-bit values in 4K
             *             blocks.
             *
             * A restriction value of 0x40000000 means that the object has no
             * disk restriction.  NetWare also treats unknown/invalid object
             * IDs as a successful unrestricted/no-use result.
             *
             * With QUOTA_SUPPORT enabled this call needs bindery prehandling:
             * nwbind maps the Object ID to a Unix uid before
             * handle_after_bind() calls nw_get_vol_restrictions().  Without
             * quota support, keep the SDK-compatible fallback local so builds
             * on hosts without quota support do not need the quota backend.
             */
            case 0x29 : { /* Get Object Disk Usage And Restrictions */
#if QUOTA_SUPPORT
                   XDPRINTF((5, 0, "Read vol restrictions"));
                   return(-2); /* nwbind must do prehandling */
#else
#if DO_DEBUG
                    uint8  volnr = *(p+1);
                    uint32 id    = GET_BE32(p+2);
#endif
                    struct XDATA {
                      uint8 restriction[4];
                      uint8 inuse[4];
                    } *xdata = (struct XDATA*) responsedata;
                    XDPRINTF((5,0, "Get vol restriction fallback vol=%d, id=0x%lx",
                             (int)volnr, id));
                    U32_TO_32(0x40000000, xdata->restriction);
                    U32_TO_32(0x0,        xdata->inuse);
                    data_len=sizeof(struct XDATA);
#endif
                 } 
                 break;

            case 0x2a : { /*  Get Eff. Rights of DIR's and Files */
                   struct XDATA {
                     uint8    eff_rights[2]; /* LO-HI */
                   } *xdata = (struct XDATA*) responsedata;
                   int dir_handle = (int)*(p+1);
                   int pathlen    = (int)*(p+2);
                   uint8 *path    = p+3;
                   int copylen    = pathlen;
                   uint8 pathbuf[256];
                   int result;

                   if (copylen > 255) copylen = 255;
                   memcpy(pathbuf, path, copylen);
                   pathbuf[copylen] = '\0';

                   result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
                   if (result > -1){
                     int ncp22_rights = trustee_v3_to_ncp22_rights(result);
                     U16_TO_16(ncp22_rights, xdata->eff_rights);
                     data_len            = sizeof(struct XDATA);
                     XDPRINTF((5,0,
                       "NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rights=0x%04x ncp22=0x%04x",
                       dir_handle, pathlen, pathbuf, result, ncp22_rights));
                   } else {
                     completition = (uint8) (-result);
                     XDPRINTF((5,0,
                       "NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rc=%d",
                       dir_handle, pathlen, pathbuf, result));
                   }
                 } 
                 break;
                 
            case 0x2b : { /* remove ext trustees */
                   struct INPUT {
                     uint8   header[7];         /* Requestheader     */
                     uint8   div[3];            /* 0x0, dlen, ufunc  */
                     uint8   dir_handle;        /* Handle            */
                     uint8   trustee_id[4];     /* Trustee Object ID */
                     uint8   reserved;
                     uint8   pathlen;
                     uint8   path[2];
                   } *input = (struct INPUT *) (ncprequest);
                   uint32 trustee_id = GET_BE32(input->trustee_id);
                   int copylen = input->pathlen;
                   uint8 pathbuf[256];
                   int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
                   int result;

                   ncp23_debug_dump("NCP22/2B DelTrustee raw",
                       (uint8 *)&input->dir_handle, raw_len);

                   if (copylen > 255) copylen = 255;
                   memcpy(pathbuf, input->path, copylen);
                   pathbuf[copylen] = '\0';

                   result = nw_del_trustee(
                       input->dir_handle,
                       input->path, input->pathlen,
                       trustee_id,
                       1); /* extended */
                   XDPRINTF((5,0,
                     "NCP22/2B DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d",
                     input->dir_handle, input->pathlen, pathbuf,
                     (unsigned long)trustee_id, result));
                   if (result) completition = (uint8) -result;
                 } 
                 break;
                 
            case 0x2c : { /* Get Volume and Purge Information */
                   /* new Call since V3.11 */
                   /* ncpfs need this call */
                   int volume   = (int) *(p+1);
                   struct XDATA {
                     uint8 total_blocks[4];        /* LOW-HI !! */
                     uint8 avail_blocks[4];
                     uint8 purgeable_blocks[4];
                     uint8 not_purgeable_blocks[4];
                     uint8 total_dirs[4];
                     uint8 avail_dirs[4];
                     uint8 reserved_by_novell[4];
                     uint8 sec_per_block;
                     uint8 namlen;
                     uint8 name[1];
                   } *xdata = (struct XDATA*) responsedata;
                   uint8 name[100];
                   int result = nw_get_volume_name(volume, name, sizeof(name));
                   if (result > -1){
                     struct fs_usage fsp;
                     memset(xdata, 0, sizeof(struct XDATA));
                     if (!nw_get_fs_usage(name, &fsp, 0)) {
                       xdata->sec_per_block = 8; /* hard coded */
                       U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks);
                       U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks);
                       U32_TO_32(fsp.fsu_files,  xdata->total_dirs);
                       U32_TO_32(fsp.fsu_ffree,  xdata->avail_dirs);
                     }
                     xdata->namlen   = strlen((char*)name);
                     strmaxcpy(xdata->name, name, xdata->namlen);
                     data_len = xdata->namlen + 30;
                   } else completition = (uint8) -result;
                 } 
                 break;
                 
            case 0x2d : { /* Get Direktory Information */
                   int dir_handle = (int) *(p+1);
                   struct XDATA {
                     uint8    total_blocks[4];
                     uint8    avail_blocks[4];
                     uint8    total_dirs[4];
                     uint8    avail_dirs[4];
                     uint8    reserved_by_novell[4];
                     uint8    sec_per_block;
                     uint8    namlen;
                     uint8    name[1];  /* Volume Name  */
                   } *xdata = (struct XDATA*) responsedata;
                   int result = nw_get_vol_number(dir_handle);
                   uint8 name[100];
                   if (result > -1)
                     result = nw_get_volume_name(result, name, sizeof(name));
                   if (result > -1) {
                     struct fs_usage fsp;
                     memset(xdata, 0, sizeof(struct XDATA));
                     if (!nw_get_fs_usage(name, &fsp, 0)) {
                       xdata->sec_per_block = 8; /* hard coded */
                       U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks);
                       U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks);
                       U32_TO_32(fsp.fsu_files,  xdata->total_dirs);
                       U32_TO_32(fsp.fsu_ffree,  xdata->avail_dirs);
                     }
                     xdata->namlen = strlen((char*)name);
                     strmaxcpy(xdata->name, name, xdata->namlen);
                     data_len = xdata->namlen + 22;
                   } else completition = (uint8) -result;
                 } 
                 break;
                 
            case 0x2e : { /* Rename or Move (old) */
                   /*
                    * NCP 22 / subfunction 46 (0x2e): Rename Or Move (old)
                    *
                    * Request:
                    *   byte source directory handle
                    *   byte search attributes
                    *   byte source path component count
                    *   source path components: byte len + bytes, repeated
                    *   byte destination directory handle
                    *   byte destination path component count
                    *   destination path components: byte len + bytes, repeated
                    *
                    * No reply data.  This old call uses component-counted
                    * paths, unlike the plain length-prefixed NCP 69 rename.
                    */
                   uint8 srcpath[256];
                   uint8 dstpath[256];
                   int src_handle = (int)*(p+1);
                   int searchattr = (int)*(p+2);
                   int src_count  = (int)*(p+3);
                   int used = 0;
                   int srclen;
                   int dstlen;
                   int result;
                   uint8 *q;

                   srclen = ncp22_component_path_to_dos(p+4, src_count,
                                                        srcpath, sizeof(srcpath),
                                                        &used);
                   if (srclen < 0) {
                     completition = (uint8)(-srclen);
                     break;
                   }

                   q = p + 4 + used;
                   dstlen = ncp22_component_path_to_dos(q+2, (int)*(q+1),
                                                        dstpath, sizeof(dstpath),
                                                        NULL);
                   if (dstlen < 0) {
                     completition = (uint8)(-dstlen);
                     break;
                   }

                   XDPRINTF((5,0,
                     "NCP22/2E RenameOrMove: src_h=%d dst_h=%d attr=0x%02x src=`%s` dst=`%s`",
                     src_handle, (int)*q, searchattr, srcpath, dstpath));

                   result = nw_mv_files(searchattr,
                                        src_handle, srcpath, srclen,
                                        (int)*q, dstpath, dstlen);

                   XDPRINTF((5,0, "NCP22/2E RenameOrMove: file_result=%d", result));

                   /*
                    * nw_mv_files() is correct for file rename/move.  For a
                    * directory source it may return either Invalid Path (0x9c)
                    * or "not found" (0xff), depending on the search attribute
                    * shape.  Directories need the directory mover, and
                    * NCP22/2E can provide different source/destination handles.
                    */
                   if (result == -0x9c || result == -0xff) {
                     int dir_result = nw_mv_dir_between_handles(src_handle,
                                                               srcpath, srclen,
                                                               (int)*q, dstpath, dstlen);
                     XDPRINTF((5,0,
                       "NCP22/2E RenameOrMove: directory fallback result=%d",
                       dir_result));
                     result = dir_result;
                   }

                   if (result < 0) completition = (uint8)(-result);
                 }
                 break;

#if WITH_NAME_SPACE_CALLS

            case 0x2f : { /* Fill namespace buffer */
                   /* ncopy use this call */
                   int volume     = (int) *(p+1);
                   /* (p+2) == 0xe4 or 0xe2 sometimes  ???? */
                   int result=fill_namespace_buffer(
                      volume, responsedata);
                   if (result > -1) {
                     data_len = result;
                   } else completition = (uint8) -result;
                 } 
                 break;
                 
            case 0x30 : { /* Get Name Space Directory Entry */
                   int volume          = (int) *(p+1);
                   uint32 basehandle   =  GET_32(p+2);
                   int    namespace    = (int) *(p+6);
                   int    result=get_namespace_dir_entry(
                           volume, basehandle, namespace,
                           responsedata);
                   if (result > -1) {
                     data_len = result;
                   } else completition = (uint8) -result;
                 } 
                 break;
#endif
            case 0x32 : { /* Get Object Effective Rights */
                   /*
                    * Client32/NWCalls export:
                    *   NWNCP22s50GetObjEffectRights
                    *
                    * Observed request layout from Client32/NCPWIN32:
                    *   byte  subfunction 0x32
                    *   dword object id      (big endian)
                    *   byte  directory handle
                    *   byte  path length
                    *   byte[] path
                    *
                    * Reply:
                    *   word effective rights mask, low/high.
                    *
                    * mars_nwe's trustee engine currently computes effective
                    * rights for the active connection object.  For the normal
                    * NWGetEffectiveRights/NWGetObjectEffectiveRights use from
                    * a logged-in client this object id is the current user.
                    */
                   struct XDATA {
                     uint8 eff_rights[2]; /* LO-HI */
                   } *xdata = (struct XDATA*) responsedata;
                   uint32 object_id = GET_BE32(p+1);
                   int dir_handle   = (int)*(p+5);
                   int pathlen      = (int)*(p+6);
                   uint8 *path      = p+7;
                   int result;

                   if (object_id && object_id != (uint32)act_obj_id) {
                     int save_act_obj_id = act_obj_id;

                     /*
                      * Best effort for explicit-object callers.  Group
                      * membership is still the connection's group list, which
                      * is correct for the usual current-user call and avoids
                      * changing bindery/group state in this low-level handler.
                      */
                     act_obj_id = (int)object_id;
                     result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
                     act_obj_id = save_act_obj_id;
                   } else {
                     result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
                   }

                   if (result > -1) {
                     int ncp22_rights = trustee_v3_to_ncp22_rights(result);
                     U16_TO_16(ncp22_rights, xdata->eff_rights);
                     data_len = sizeof(struct XDATA);
                   } else completition = (uint8)(-result);
                 }
                 break;



            case 0x33 : { /* Get Extended Volume Information */
              /*
               * NCP 0x2222/22/51 Get Extended Volume Information
               *
               * WebSDK / nwvol.h context:
               *   Request:
               *     byte VolumeNumber
               *   Reply:
               *     word VolInfoReplyLen          (Lo-Hi)
               *     NWVolExtendedInfo VolumeInfo  (VolInfoReplyLen bytes)
               *     byte VolNameLen
               *     byte VolumeName[VolNameLen]
               *
               * NWVolExtendedInfo contains 33 little-endian 32-bit fields:
               * volume type, status flags, sector/cluster geometry, total and
               * free cluster counts, suballocation/limbo/compression/migration
               * counters, directory-entry counters, EA counters, a Directory
               * Services object id, and the last-modified date/time.
               *
               * MARS-NWE maps the Unix filesystem data we already expose via
               * the older volume-information calls to the core geometry and
               * capacity fields.  NetWare-specific suballocation, limbo,
               * compression, migration, EA, and DS fields are reported as zero.
               */
              struct XDATA {
                uint8 vol_info_reply_len[2];
                uint8 vol_type[4];
                uint8 status_flag[4];
                uint8 sector_size[4];
                uint8 sectors_per_cluster[4];
                uint8 vol_size_in_clusters[4];
                uint8 free_clusters[4];
                uint8 sub_alloc_freeable_clusters[4];
                uint8 freeable_limbo_sectors[4];
                uint8 nonfreeable_limbo_sectors[4];
                uint8 avail_sub_alloc_sectors[4];
                uint8 nonuseable_sub_alloc_sectors[4];
                uint8 sub_alloc_clusters[4];
                uint8 num_data_streams[4];
                uint8 num_limbo_data_streams[4];
                uint8 oldest_del_file_age_in_ticks[4];
                uint8 num_compressed_data_streams[4];
                uint8 num_compressed_limbo_data_streams[4];
                uint8 num_noncompressible_data_streams[4];
                uint8 precompressed_sectors[4];
                uint8 compressed_sectors[4];
                uint8 num_migrated_data_streams[4];
                uint8 migrated_sectors[4];
                uint8 clusters_used_by_fat[4];
                uint8 clusters_used_by_dirs[4];
                uint8 clusters_used_by_ext_dirs[4];
                uint8 total_dir_entries[4];
                uint8 unused_dir_entries[4];
                uint8 total_ext_dir_extants[4];
                uint8 unused_ext_dir_extants[4];
                uint8 ext_attrs_defined[4];
                uint8 ext_attr_extants_used[4];
                uint8 directory_services_object_id[4];
                uint8 vol_last_modified_date_and_time[4];
                uint8 vol_name_len;
                uint8 vol_name[16];
              } *xdata = (struct XDATA*) responsedata;
              uint8 name[100];
              int volume = (int) *(p+1);
              int result = nw_get_volume_name(volume, name, sizeof(name));

              memset(xdata, 0, sizeof(struct XDATA));
              if (result > -1) {
                struct fs_usage fsp;
                int namlen = strlen((char*)name);

                if (namlen > 16) namlen = 16;
                U16_TO_16(33 * 4, xdata->vol_info_reply_len);
                U32_TO_32(3, xdata->vol_type);              /* VINetWare386v31 */
                U32_TO_32(512, xdata->sector_size);
                U32_TO_32(8, xdata->sectors_per_cluster);  /* 4 KiB clusters */

                if (!nw_get_fs_usage(name, &fsp, 0)) {
                  U32_TO_32(fsp.fsu_blocks/8, xdata->vol_size_in_clusters);
                  U32_TO_32(fsp.fsu_bavail/8, xdata->free_clusters);
                  U32_TO_32(fsp.fsu_files, xdata->total_dir_entries);
                  U32_TO_32(fsp.fsu_ffree, xdata->unused_dir_entries);
                }

                xdata->vol_name_len = namlen;
                strmaxcpy(xdata->vol_name, name, namlen);
                data_len = 2 + (33 * 4) + 1 + namlen;
                XDPRINTF((5,0,
                  "NCP22/33 GetExtendedVolumeInfo: vol=%d name=`%s` len=%d",
                  volume, name, data_len));
              } else {
                completition = (uint8) -result;
              }
            }
            break;


            default:
                 completition = 0xfb;  /* unkwown request */
               break;
        } /* switch *p */
      } 
      break;


      case 0x17 : {  /* FILE SERVER ENVIRONMENT */
        /* uint8 len   = *(requestdata+1); */
        uint8 ufunc    = *(requestdata+2);
        uint8 *rdata   = requestdata+3;

        switch (ufunc) {
#if FUNC_17_02_IS_DEBUG
          case 0x02 :  {
             /* I hope this call isn't used       */
             /* now missused as a debug switch :) */
            struct XDATA {
              uint8  nw_debug;   /* old level */
            } *xdata = (struct XDATA*) responsedata;
            if (*rdata == NWCONN) {
              xdata->nw_debug = (uint8)org_nw_debug;
              nw_debug = org_nw_debug = (int) *(rdata+1);
              data_len = 1;
            } else return(-1);
          }
          break;
#endif

          case 0x14: /* Login Objekt, unencrypted passwords */
          case 0x18: /* crypt_keyed LOGIN */
          return(-2); /* nwbind must do prehandling */


          case 0x0f: { /* Scan File Information  */
            struct INPUT {
              uint8   header[7];      /* Requestheader   */
              uint8   div[3];         /* 0, len + ufunc   */
              uint8   sequence[2];    /* z.B. 0xff, 0xff */
              uint8   dir_handle;
              uint8   search_attrib;  /*    0: NONE    */
                                      /*   02: HIDDEN  */
                                      /*   04: SYSTEM  */
                                      /*   06: BOTH    */
                                      /* 0x10: DIR     */
              uint8   len;
              uint8   data[2];        /* Name        */
            } *input = (struct INPUT *)ncprequest;

            struct XDATA {
              uint8          sequence[2];      /* next sequence */
              /* NW_FILE_INFO f; */
              uint8          f[sizeof(NW_FILE_INFO)];
              uint8          owner_id[4];
              uint8          archive_date[2];
              uint8          archive_time[2];
              uint8          reserved[56];
            } *xdata = (struct XDATA*)responsedata;
            int len = input->len;
            int searchsequence;
            NW_FILE_INFO f;
            uint32   owner;

            memset(xdata, 0, sizeof(struct XDATA));
            searchsequence = nw_search( (uint8*) &f,
                             &owner,
                             (int)input->dir_handle,
                             (int) GET_BE16(input->sequence),
                             (int) input->search_attrib & ~0x10,
             /* this routine is only for scanning files ^^^^^^ */
                             input->data, len);
            if (searchsequence > -1) {
              memcpy(xdata->f, &f, sizeof(NW_FILE_INFO));
              U16_TO_BE16((uint16) searchsequence, xdata->sequence);
              U32_TO_BE32(owner, xdata->owner_id);
              data_len = sizeof(struct XDATA);
            } else completition = (uint8) (- searchsequence);
          }
          break;

          case 0x10: { /* Set  File Information  */
            struct INPUT {
              uint8   header[7];      /* Requestheader   */
              uint8   div[3];         /* 0, len + ufunc   */
              uint8   f[sizeof(NW_FILE_INFO) - 14]; /* no name */
              uint8   owner_id[4];
              uint8   archive_date[2];
              uint8   archive_time[2];
              uint8   reserved[56];
              uint8   dir_handle;
              uint8   search_attrib;  /*    0: NONE    */
                                      /*   02: HIDDEN  */
                                      /*   04: SYSTEM  */
                                      /*   06: BOTH    */
                                      /* 0x10: DIR     */
              uint8   len;
              uint8   data[2];        /* Name        */
            } *input = (struct INPUT *)ncprequest;
            NW_FILE_INFO f;
            int result;
            memcpy(((uint8*)&f)+14, input->f, sizeof(NW_FILE_INFO)-14);
            result = nw_set_file_information((int)input->dir_handle,
                                            input->data,
                                            (int)input->len,
                                            (int)input->search_attrib, &f);
            /* no reply packet */
            if (result <0) completition = (uint8)-result;
          }
          break;

          case 0x47 :  { /* SCAN BINDERY OBJECT TRUSTEE PATH */
                 struct INPUT {
                   uint8   header[7];     /* Requestheader     */
                   uint8   div[3];        /* 0x0, dlen, ufunc  */
                   uint8   volume;
                   uint8   sequence[2];   /* trustee searchsequence */
                   uint8   id[4];         /* Trustee Object ID */
                 } *input = (struct INPUT *) (ncprequest);
                 struct XDATA {
                   uint8 nextsequence[2];
                   uint8 id[4];
                   uint8 access_mask;
                   uint8 pathlen;
                   uint8 path[1];
                 } *xdata = (struct XDATA*) responsedata;
                 int sequence=GET_BE16(input->sequence);
                 int access_mask=0;
                 uint32 id=GET_BE32(input->id);
                 int result=nw_scan_user_trustee(
                    input->volume, &sequence, id, &access_mask, xdata->path);
                 if (result > 0) {
                   U16_TO_BE16(sequence, xdata->nextsequence);
                   memcpy(xdata->id, input->id, 4);
                   xdata->access_mask=(uint8)access_mask;
                   xdata->pathlen=result;
                   data_len = 8+result;
                 } else if (!result) {
                   memset(xdata, 0, 8);
                   data_len = 8;
                 } else
                   completition = (uint8)(-result);
          }
          break;

          case 0x64:  { /* create queue */
#if 0
            int    q_typ      = GET_BE16(rdata);
#endif
            int    q_name_len = *(rdata+2);
#if 0
            uint8  *q_name    = rdata+3;
#endif
            uint8 *dirhandle  = rdata+3+q_name_len;
            int pathlen       = *(rdata+3+q_name_len+1);
            uint8  *path      = rdata+3+q_name_len+2;
            uint8  new_path[257];
            int result        = conn_get_full_path(*dirhandle,
                                 path, pathlen, new_path,
                                 sizeof(new_path));
            if (result > -1) {
              int diffsize = result - pathlen;
              *dirhandle   = 0;
              memcpy(path, new_path, result);
              if (diffsize)
                requestlen+=diffsize;  /* !!!!!! */
              return(-1);  /* nwbind must do the rest    */
            } else
              completition = (uint8)(-result);
          }
          break;

          case 0x68:   /* create queue job and file old */
          case 0x79:   /* create queue job and file     */
          return(-2);  /* nwbind must do prehandling    */

          case 0x6C:     /* Get Queue Job Entry old */
          case 0x7A:   { /* Read Queue Job Entry */
             uint32 q_id = GET_BE32(rdata);
             int job_id  = GET_BE16(rdata+4);
             uint32 fhandle = get_queue_job_fhandle(q_id, job_id);
             U32_TO_BE32(fhandle, rdata+8);
             requestlen+=6;  /* !!!!!! */
          }
          return(-1);  /* nwbind must do the rest    */

          case 0x69:    /* close file and start queue old ?? */
          case 0x7f: {  /* close file and start queue */
            struct INPUT {
              uint8   header[7];          /* Requestheader */
              uint8   packetlen[2];       /* lo - hi       */
              uint8   func;               /* 0x7f or 0x69  */
              uint8   queue_id[4];        /* Queue ID      */
              uint8   job_id[4];          /* result from creat queue    */
                                          /* if 0x69 then only first 2 byte ! */
            } *input = (struct INPUT *) (ncprequest);
            uint32 q_id = GET_BE32(input->queue_id);
            int  job_id = (ufunc==0x69) ? GET_BE16(input->job_id)
                                        : GET_BE16(input->job_id);
            int result  = close_queue_job(q_id, job_id);
            if (result < 0) {
              completition = (uint8)-result;
            } else {
              return(-2);  /* nwbind must do next    */
            }
          }
          break;

          case 0x71 :  /* service queue job (old) */
          case 0x7c :  /* service queue job */
          return(-2);  /* nwbind must do prehandling    */

          case 0x72 :  /* finish queue job (old) */
          case 0x73 :  /* abort queue job (old) */
          case 0x83 :  /* finish queue job */
          case 0x84 : { /* abort queue job */
            struct INPUT {
              uint8   header[7];          /* Requestheader */
              uint8   packetlen[2];       /* low high      */
              uint8   func;               /* 0x7f or 0x69  */
              uint8   queue_id[4];        /* Queue ID      */
              uint8   job_id[4];          /* result from creat queue    */
                                          /* if 0x69 then only first 2 byte ! */
            } *input = (struct INPUT *) (ncprequest);
            uint32 q_id = GET_BE32(input->queue_id);
            int  job_id = GET_BE16(input->job_id);
            int result  = finish_abort_queue_job(q_id, job_id);
            if (result <0)
              completition=(uint8) -result;
            else
             return(-1);  /* nwbind must do the rest */
          }
          break;

          case 0xf3: {  /* Map Directory Number TO PATH */
            int payload_len = requestlen - 3;
            int result;
            if (payload_len < 0) payload_len = 0;
            ncp23_debug_dump("NCP23/F3 Map Directory Number TO PATH", rdata, payload_len);

            /*
             * Request: volume byte, directory number dword LO-HI, namespace byte.
             * Reply: length-preceded path components, no volume component.
             */
            if (payload_len >= 6) {
              int volume = (int)rdata[0];
              uint32 dirnum = GET_32(rdata+1);
              int namspace = (int)rdata[5];

              result = map_directory_number_to_path(volume, dirnum, namspace,
                                                    responsedata,
                                                    sizeof(IPX_DATA) - sizeof(NCPRESPONSE));
              if (result > -1) data_len = result;
              else completition = (uint8)(-result);
            } else {
              completition = 0x9c;
            }
          }
          break;

          case 0xf4: {  /* Map PATH TO Dir Entry */
            int payload_len = requestlen - 3;
            int result;
            if (payload_len < 0) payload_len = 0;
            ncp23_debug_dump("NCP23/F4 Map PATH TO Dir Entry", rdata, payload_len);

            /*
             * Request: dir handle byte, path length byte, path bytes.
             * Reply: volume byte, directory number dword LO-HI.
             */
            if (payload_len >= 2 && payload_len >= 2 + (int)rdata[1]) {
              struct XDATA {
                uint8 volume;
                uint8 dirnum[4];
              } *xdata = (struct XDATA*)responsedata;

              result = conn_map_path_to_dir_entry((int)rdata[0],
                                                  rdata+2, (int)rdata[1],
                                                  &(xdata->volume),
                                                  xdata->dirnum);
              if (result) completition = (uint8)(-result);
              else data_len = sizeof(struct XDATA);
            } else {
              completition = 0x9c;
            }
          }
          break;

          default : return(-1);
          break;
        } /* switch (ufunc) */
      } /* case 0x17 */
      break;

      case 0x18 : /* End of Job */
                  if (!(entry8_flags&0x200)) /* pcz: 14-Apr-00 */
                    free_connection_task_jobs(ncprequest->task);
                  nw_free_handles(ncprequest->task);
                  return(-1); /* nwbind must do a little rest */
                  break;

      case 0x19 : /* logout, some of this call is handled in ncpserv. */
                  free_queue_jobs();
                  nw_free_handles(-1);
                  set_nw_user(-1, -1,
                               0,
                               0, NULL,
                              -1, NULL,
                               0, NULL);
                  return(-1); /* nwbind must do a little rest */
                  break;

      case 0x1a : /* Log Physical Record     */
      case 0x1c : /* Release Physical Record */
      case 0x1e : /* Clear Physical Record   */
                  {
                    /*
                     * SDK: NCP 0x2222/26 Log Physical Record records one
                     * byte range in the caller's logged data block table.
                     * Lock Flag 0 logs for future locking, 1 locks
                     * exclusive, and 3 locks shareable/read-only.
                     *
                     * SDK: NCP 0x2222/28 Release Physical Record releases
                     * one locked byte range but leaves it in the logged
                     * table so a later Lock Physical Record Set can relock
                     * it.  The request carries a reserved byte, a 6-byte
                     * NetWare file handle, Start Offset, and Record Length;
                     * it returns no reply data and reports Unlock Error
                     * through the completion code.
                     *
                     * SDK: NCP 0x2222/30 Clear Physical Record releases the
                     * range if it is locked and removes it from the logged
                     * table.
                     *
                     * The physical-record set table is maintained alongside
                     * the existing low-level byte-range lock implementation:
                     * log/lock records are added on successful 0x1a calls,
                     * release leaves the set entry intact, and clear removes
                     * the matching set entry after a successful unlock.
                     */
                    struct INPUT {
                      uint8   header[7];      /* Requestheader */
                      uint8   lock_flag;      /* 0=log/reserved, 1=excl */
                                              /* 3=shared              */
                      uint8   ext_fhandle[2]; /* all zero / high handle */
                      uint8   fhandle[4];     /* Filehandle            */
                      uint8   offset[4];
                      uint8   size[4];
                      uint8   timeout[2];
                    } *input = (struct INPUT *)ncprequest;
                    int fhandle  = GET_32  (input->fhandle);
                    uint32 offset= GET_BE32(input->offset);
                    uint32 size  = GET_BE32(input->size);
                    uint16 timeout = GET_BE16(input->timeout);
                    int result;
                    if (function == 0x1a) { /* log or lock */
                      result = nw_log_physical_record(
                                                  fhandle,
                                                   offset,
                                                   size,
                                                   timeout,
                                            (int)input->lock_flag);
                      if (!result)
                        result = share_set_physrec_add_rm(
                                            (int)input->lock_flag,
                                                   fhandle,
                                                   offset,
                                                   size,
                                                   timeout);
                    } else {
                      result = nw_log_physical_record(
                                                  fhandle,
                                                   offset,
                                                   size,
                                                   timeout,
                                           (function == 0x1c)
                                             ? -1  /* unlock only */
                                             : -2  /* unlock + unlog */
                                                   );
                      if (!result && function == 0x1e)
                        result = share_set_physrec_add_rm(
                                             -2,
                                             fhandle,
                                             offset,
                                             size,
                                             timeout);
                    }
                    if (result) completition = (uint8)-result;
                  }
                  break;

      case 0x1b :  /* Lock Physical Record Set (old) */
      case 0x6e : { /* Lock Physical Record Set       */
                    /*
                     * SDK: NCP 0x2222/27 Lock Physical Record Set (old)
                     * and NCP 0x2222/110 Lock Physical Record Set lock all
                     * byte ranges logged by the calling client.
                     *
                     * SDK request: one-byte Lock Flag followed by a 2-byte
                     * Lock Timeout in 1/18 second units.  Lock Flag bit 1
                     * selects shareable/read-only locks; otherwise the set is
                     * locked exclusive/read-write.  The reply carries no data
                     * and completion reports success, timeout, or lock error.
                     *
                     * MARS-NWE stores physical-record set entries from Log
                     * Physical Record and routes the set lock through the
                     * shared synchronization-set handler.
                     */
                    struct INPUT {
                      uint8 header[7];
                      uint8 lock_flag;
                      uint8 timeout[2];
                    } *input = (struct INPUT *)ncprequest;
                    int lock_flag = (input->lock_flag & 0x02) ? 3 : 1;
                    int result = share_handle_lock_sets(
                                             4,  /* Physical Record Set */
                                             lock_flag,
                                             GET_BE16(input->timeout));
                    if (result) completition = (uint8)-result;
                  }
                  break;

      case 0x1d :  /* Release Physical Record Set */
      case 0x1f : { /* Clear Physical Record Set   */
                    /*
                     * SDK: NCP 0x2222/29 Release Physical Record Set
                     * releases all byte ranges locked by the calling client
                     * but leaves them in the client's data byte range table
                     * for future Lock Physical Record Set calls.
                     *
                     * SDK: NCP 0x2222/31 Clear Physical Record Set releases
                     * all locked ranges and clears the caller's data byte
                     * range table.  Both calls have no reply data; Clear
                     * carries a one-byte Lock Flags field documented as
                     * 0 = Not Locked.
                     */
                    int result = share_handle_lock_sets(
                                      4,   /* Physical Record Set */
                               (function==0x1f)
                                   ? -2    /* Clear    */
                                   : -1,   /* Release  */
                                      0);
                    if (result) completition = (uint8)-result;
                  }
                  break;

      case 0x20 : /* Semaphore */
                  return(-1);   /* handled by nwbind */
      
      case 0x21 : { /* Negotiate Buffer Size,  Packetsize  */
                    uint8 *getsize=responsedata;
                    int buffer_size = (int) (GET_BE16((uint8*)requestdata));
                    /* Der Novell-Client der PAM's Net/E-Ethernetkarte
                       f�r Atari ST/TT meldet ein Packetsize von 0 wenn
                       nwserv NACH dem Novell Client NET_S1.PRG
                       gestartet wird. Da 0 in jedem Falle ein unsinniger
                       Wert ist, wird rw_buffer_size nicht verwendet.
                       Hayo Schmidt <100305.1424@compuserve.com>, 7-Dec-97
                    */
                    if (buffer_size >= 512) {
                      rw_buffer_size = min(LOC_RW_BUFFERSIZE, buffer_size);
                      XDPRINTF((3,0, "Negotiate Buffer size = 0x%04x,(%d)",
                           (int) rw_buffer_size, (int) rw_buffer_size));
                    } else {
                      XDPRINTF((1,0, "Invalid Packetsize = %d, "
                                      "Negotiate Buffer Size is set to %d",
                                   buffer_size, rw_buffer_size));
                    }
                    U16_TO_BE16(rw_buffer_size, getsize);
                    data_len = 2;
                  }
                  break;

      case 0x22 : { /* Transaction Tracking System (TTS) calls */
                    int ufunc = (int) *requestdata;
                    /*
                     * WebSDK / headers:
                     *   NCP 0x2222/34/00 TTS Is Available has no reply data.
                     *   Its completion code is the status value: 0x00 means
                     *   Transaction Tracking Unavailable, 0xfd means Disabled,
                     *   and 0xff means Available.  The SDK headers expose this
                     *   as NWTTSIsAvailable().
                     *
                     * MARS-NWE does not implement transaction tracking.  Report
                     * the documented unavailable status for the availability
                     * probe, but keep the state-changing TTS subfunctions
                     * unsupported rather than pretending to begin, end, or abort
                     * transactions without rollback semantics.
                     *
                     * Cross-check: lwared and the Rust nwserver code do not
                     * provide a fuller TTS transaction implementation to mirror.
                     */
                    if (!ufunc) completition=0;  /* TTS unavailable */
                    else completition=0xfb;      /* request not known */
                  } break;

      case 0x23 : { /* div AFP Calls */
                    int afp_off = afp_request_offset();
                    uint8 *afp_req = requestdata + afp_off;
                    int afp_len = requestlen - afp_off;
                    int ufunc = (afp_len > 0) ? (int)*afp_req : -1;
                    /*
                     * NetWare AFP calls are server-side NCP entry points for
                     * Mac namespace semantics: AFP entry IDs, Finder Info,
                     * AppleDouble metadata, resource forks, and per-volume Mac
                     * namespace state.
                     *
                     * WebSDK / headers identify the old NCP 0x2222/35 AFP
                     * subfunctions used by nwafp.h, including AFP Delete
                     * (0x03), Get Entry ID From Name (0x04), Get Entry ID From
                     * NetWare Handle (0x06), Rename (0x07), Open File Fork
                     * (0x08), Alloc Temporary Dir Handle (0x0b), Get Entry ID
                     * From Path Name (0x0c), the AFP 2.0 create calls
                     * (0x0d/0x0e), Get/Set File Information (0x0f/0x10), and
                     * Scan File Information (0x11).
                     *
                     * Implement the path-name entry-id probe first because the
                     * SDK helpers use it to test AFP support.  It still requires
                     * the optional libatalk backend to be present; without a Mac
                     * namespace backend, keep returning invalid namespace.
                     */
                    if (ufunc == 0x0c) {
                      int result = afp_get_entry_id_from_path_name(afp_req,
                                           afp_len, responsedata);
                      if (result > -1) data_len = result;
                      else completition = (uint8)-result;
                    } else {
                      XDPRINTF((3,0, "AFP call rejected: ufunc=0x%02x (%s), Mac namespace unavailable, libatalk backend=%s",
                                ufunc, afp_call_name(ufunc),
                                nwatalk_backend_available() ? "enabled" : "disabled"));
                      completition=0xbf;   /* we say invalid namespace here */
                    }
                  } break;

      case 0x3b :  /* commit file to disk        */
      case 0x3d :  /* commit file                   */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   reserve;
                      uint8   ext_fhandle[2]; /* all zero   */
                      uint8   fhandle[4];     /* filehandle */
                    } *input = (struct INPUT *)ncprequest;
                    uint32 fhandle = GET_32(input->fhandle);
                    int result=nw_commit_file(fhandle);
                    if (result<0)
                      completition=(uint8)-result;
                  }
                  break;

      case 0x3e : { /* FILE SEARCH INIT  */
                    /* returns dhandle for searchings */
                    int  dir_handle = (int)*requestdata;
                    int  len        = (int)*(requestdata+1); /* pathlen */
                    uint8 *p        = requestdata+2;          /* path */
                    struct XDATA {
                      uint8  volume;            /* Volume       */
                      uint8  dir_id[2];         /* Direktory ID */
                      uint8  searchsequence[2];
                      uint8  dir_rights;        /* Rights       */
                    } *xdata= (struct XDATA*) responsedata;
                    int volume;
                    int searchsequence;
                    int dir_id;
                    int rights = nw_open_dir_handle(dir_handle, p, len,
                                  &volume, &dir_id, &searchsequence);
                    if (rights >-1) {
                      xdata->volume = (uint8)volume;
                      U16_TO_BE16((uint16)dir_id,        xdata->dir_id);
                      U16_TO_BE16((uint16)searchsequence, xdata->searchsequence);
                      xdata->dir_rights = (uint8)rights;
                      data_len = sizeof(struct XDATA);
                    } else completition = (uint8) -rights;
                  } break;

      case 0x3f : {  /* file search continue */
                     /* Dir_id is from file search init */
                    struct INPUT {
                      uint8   header[7];         /* Requestheader */
                      uint8   volume;            /* Volume ID    */
                      uint8   dir_id[2];         /* from File Search Init */
                      uint8   searchsequence[2]; /* sequence FFFF = first entry */
                      uint8   search_attrib;     /* Attribute */
                          /*     0 none,
                                 2 HIDDEN,
                           *     4 System ,
                                 6 Both,
                           *  0x10 Dir
                           */
                      uint8   len;            /* fnname len */
                      uint8   data[2];        /* fnname with wildcards */
                    } *input = (struct INPUT *) ncprequest;
                      int     len=input->len  ; /* FN Length */

                    struct XDATA {
                      uint8   searchsequence[2]; /* same as request sequence  */
                      uint8   dir_id[2];         /* Direktory ID    */
                    /*  is correct !! */
                      union {
                        NW_DIR_INFO  d;
                        NW_FILE_INFO f;
                      } u;
                    } *xdata = (struct XDATA*)responsedata;

                    int searchsequence = nw_dir_search(
                                          (uint8*) &(xdata->u),
                                          (int) GET_BE16(input->dir_id),
                                          (int) GET_BE16(input->searchsequence),
                                          (int) input->search_attrib,
                                          input->data, len);
                    if (searchsequence > -1) {
                      U16_TO_BE16((uint16) searchsequence, xdata->searchsequence);
                      memcpy(xdata->dir_id, input->dir_id, 2);
                      data_len = sizeof(struct XDATA);
                    } else completition = (uint8) (- searchsequence);
                  }
                  break;

      case 0x40 : /* Search for a File */
                  {
                    struct INPUT {
                      uint8   header[7];      /* Requestheader   */
                      uint8   sequence[2];     /* z.B. 0xff, 0xff */
                      uint8   dir_handle;     /* z.B  0x1        */
                      uint8   search_attrib;  /* z.B. 0x6        */
                      uint8   len;
                      uint8   data[2];        /* Name          */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   sequence[2];    /* answer sequence */
                      uint8   reserved[2];   /* z.B  0x0   0x0  */
                      union {
                        NW_DIR_INFO  d;
                        NW_FILE_INFO f;
                      } u;
                    } *xdata = (struct XDATA*)responsedata;
                    int len = input->len;
                    uint8 my_sequence[2];
                    int searchsequence;
                    uint32 owner;
                    memcpy(my_sequence, input->sequence, 2);
                    searchsequence = nw_search( (uint8*) &(xdata->u),
                                          &owner,
                                          (int)input->dir_handle,
                                          (int) GET_BE16(my_sequence),
                                          (int) input->search_attrib,
                                          input->data, len);
                    if (searchsequence > -1) {
                      U16_TO_BE16((uint16) searchsequence, xdata->sequence);
                      data_len = sizeof(struct XDATA);
                    } else completition = (uint8) (- searchsequence);
                  }
                  break;

      case 0x41  : {  /* open file for reading */
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   dirhandle;     /* Dirhandle     */
                      uint8   attrib;        /* z.B. 0x6 od. 0x4e  */
                            /* O_RDWR|TRUNC 0x6, O_RDONLY 0x6 */
                      uint8   len;           /* namelaenge */
                      uint8   data[2];       /* Name       */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   ext_fhandle[2]; /* all zero       */
                      uint8   fhandle[4];     /* File Handle    */
                      uint8   reserved[2];    /* reserved by novell */
                      NW_FILE_INFO fileinfo;
                    } *xdata= (struct XDATA*)responsedata;
                    int  fhandle=nw_creat_open_file((int)input->dirhandle,
                            input->data, input->len,
                            &(xdata->fileinfo),
                            (int)input->attrib,
                            0x1,   /* read access */
                            0, 
                            (int)(ncprequest->task));

                    if (fhandle > -1){
                      U32_TO_32(fhandle, xdata->fhandle);
                      xdata->ext_fhandle[0]=0;
                      xdata->ext_fhandle[1]=0;
                      xdata->reserved[0]=0;
                      xdata->reserved[1]=0;
                      data_len = sizeof(struct XDATA);
                    } else completition = (uint8) (-fhandle);
                  }
                  break;

      case 0x42 : /* close file */
                  {
                    struct INPUT {
                      uint8   header[7];      /* Requestheader */
                      uint8   reserve;
                      uint8   ext_fhandle[2]; /* all zero   */
                      uint8   fhandle[4];     /* filehandle */
                    } *input = (struct INPUT *)ncprequest;
                    uint32 fhandle = GET_32(input->fhandle);
                    completition = (uint8)(-nw_close_file(fhandle,
                              0, (int)(ncprequest->task)));

#if TEST_FNAME
                    if (!completition && fhandle == test_handle) {
                      do_druck++;
                      test_handle = -1;
                    }
#endif
                  }
                  break;

      case 0x43 : /* creat file, overwrite if exist */
      case 0x4D : /* create new file                */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader   */
                      uint8   dirhandle;
                      uint8   attribute;     /* creat Attribute */
                      uint8   len;
                      uint8   data[1];       /* Name */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   ext_fhandle[2];
                      uint8   fhandle[4];   /* Filehandle         */
                      uint8   reserved[2];  /* reserved by NOVELL */
                      NW_FILE_INFO fileinfo;
                    } *xdata= (struct XDATA*)responsedata;
                    int  fhandle=nw_creat_open_file(
                              (int)input->dirhandle,
                                   input->data,
                                   (int)input->len,
                                   &(xdata->fileinfo),
                                   (int)input->attribute,
                                  /* 0,   0x2,  mst: 26-Sep-99 */
                                    0x13,  /* pcz: 14-Nov-99 */
                                   (function==0x43) ? 1 : 2,
                                   (int)(ncprequest->task));
                    if (fhandle > -1){
                      data_len = sizeof(struct XDATA);
                      U32_TO_32  (fhandle, xdata->fhandle);
                      xdata->ext_fhandle[0]=0;
                      xdata->ext_fhandle[1]=0;
                      xdata->reserved[0]=0;
                      xdata->reserved[1]=0;

#ifdef TEST_FNAME
                      input->data[input->len] = '\0';
                      if (strstr(input->data, TEST_FNAME)){
                        test_handle = fhandle;
                        do_druck++;
                      }
#endif
                    } else completition = (uint8) (-fhandle);
                  }
                  break;

      case 0x44 : /* file(s)   delete */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   dirhandle;     /*     0x0 */
                      uint8   searchattributes;
                      /* 0 none, 2 Hidden, 4 System, 6 Both */
                      uint8   len;
                      uint8   data[2];        /* Name */
                    } *input = (struct INPUT *)ncprequest;
                    int err_code = nw_delete_files((int)input->dirhandle,
                        (int)input->searchattributes,
                        input->data, (int)input->len);
                    if (err_code < 0)
                       completition = (uint8) -err_code;
                  }
                  break;

      case 0x45 : /* rename file */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   dir_handle;
                      uint8   searchattrib;
                      uint8   len;
                      uint8   data[2];        /* Name */
                    } *input = (struct INPUT *)ncprequest;
                    uint8 *p = input->data+input->len; /* reserve z.B. 0x1 */
                                              /* + 1  = len2 */
                                              /* + 1  = data2 */
                    int errcode = nw_mv_files(
                                    (int)input->searchattrib,
                                    (int)input->dir_handle, input->data,(int)input->len,
                                    (int)input->dir_handle, p+2, (int)*(p+1) );

                    if (errcode < 0) completition = (uint8) -errcode;
                  }
                  break;

      case 0x46 : /* set file attributes */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   access;        /*  0x80, od 0x0 */
                      /* 0x80 for example is shared */
                      uint8   dir_handle;
                      uint8   attrib;         /* search attrib */
                      uint8   len;
                      uint8   data[2];        /* filename */
                    } *input = (struct INPUT *)ncprequest;
                    completition =
                      (uint8) (-nw_set_file_attributes((int)input->dir_handle,
                                        input->data, (int)input->len,
                                        (int)input->attrib,
                                        (int)input->access));
                  }
                  break;

      case 0x47 : /* move pointer to end of file ???? */
                  /* and return filesize ?  */
                  {
                    struct INPUT {
                      uint8   header[7];         /* Requestheader */
                      uint8   filler;
                      uint8   ext_filehandle[2]; /* all zero */
                      uint8   fhandle[4];        /* Dateihandle */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   size[4];    /* Position ??? */
                    } *xdata=(struct XDATA*)responsedata;
                    int    fhandle  = GET_32(input->fhandle);
                    int    size     = nw_seek_file(fhandle, 0);
                    if (size > -1) {
                      data_len = sizeof(struct XDATA);
                      U32_TO_BE32(size, xdata->size);
                    }
                    else completition = (uint8) -size;
                  }
                  break;

      case 0x48 : /* read file */
                  {
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   filler;
                      uint8   ext_fhandle[2]; /* all zero */
                      uint8   fhandle[4];     /* filehandle */
                      uint8   offset[4];
                      uint8   max_size[2];    /* byte to readd */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   size[2]; /* read bytes  */
                      uint8   data[2]; /* read data   */
                    } *xdata=(struct XDATA*)responsedata;
                    int    fhandle  = GET_32  (input->fhandle);
                    int    max_size = GET_BE16(input->max_size);
                    off_t  offset   = GET_BE32(input->offset);
                    int    zusatz   = (offset & 1) ? 1 : 0;
                    int    size;
                    if (max_size > rw_buffer_size) {
                      XDPRINTF((1,0, "wanted read=%d byte > %d",
                        max_size, rw_buffer_size));
                      size = -0x88; /* we say wrong filehandle */
                    } else
                     size = nw_read_file(fhandle,
                                              xdata->data+zusatz,
                                              max_size,
                                              offset);
                    if (size > -1) {
                      U16_TO_BE16(size, xdata->size);
                      data_len=size+zusatz+2;
                    } else completition = (uint8) -size;
                  }
                  break;

      case 0x49 : {  /* write file */
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   filler;         /* 0 Filler ?? */
                      uint8   ext_handle[2];
                      uint8   fhandle[4];     /* Dateihandle   */
                      uint8   offset[4];      /* SEEK OFFSET    */
                      uint8   size[2];        /* Datasize       */
                      uint8   data[2];        /* Schreibdaten */
                    } *input = (struct INPUT *)ncprequest;
                    off_t  offset     = GET_BE32(input->offset);
                    int    fhandle    = GET_32  (input->fhandle);
                    int    input_size = GET_BE16(input->size);
                    int size          = nw_write_file(fhandle,
                                              input->data,
                                              input_size,
                                              offset);
                    if (size < 0)
                       completition = (uint8) -size;
                    else if (size < input_size)
                       completition = (uint8)0xff;
                  }
                  break;


      case 0x4a : {  /* File SERVER COPY  */
                    /* should be OK */
                    struct INPUT {
                      uint8   header[7];       /* Requestheader */
                      uint8   reserved;        /* Reserved by Novell */
                      uint8   qext_fhandle[2]; /* ext Filehandle */
                      uint8   qfhandle[4];     /* Quellfile */
                      uint8   zext_fhandle[2]; /* ext Filehandle */
                      uint8   zfhandle[4];     /* Zielfile */
                      uint8   qoffset[4];      /* SourceFile Offset */
                      uint8   zoffset[4];      /* DestFile Offset   */
                      uint8   size[4];         /* copysize          */
                    } *input = (struct INPUT *)ncprequest;
                    int    qfhandle   = GET_32  (input->qfhandle);
                    int    zfhandle   = GET_32  (input->zfhandle);
                    off_t  qoffset    = GET_BE32(input->qoffset);
                    off_t  zoffset    = GET_BE32(input->zoffset);
                    uint32 input_size = GET_BE32(input->size);
                    int size          = nw_server_copy(qfhandle, qoffset,
                                                 zfhandle, zoffset,
                                                 input_size);
                    if (size < 0) completition = (uint8) -size;
                    else {
                      struct XDATA {
                        uint8   zsize[4];   /* real transfered Bytes */
                      } *xdata= (struct XDATA*)responsedata;
                      U32_TO_BE32(size, xdata->zsize);
                      data_len = sizeof(struct XDATA);
                    }
                  }
                  break;


      case 0x4b : {  /* set date of file, file will be closed later */
                    struct INPUT {
                      uint8   header[7];  /* Requestheader */
                      uint8   filler;
                      uint8   reserve[2]; /* ext Filehandle  */
                      uint8   fhandle[4]; /* Dateihandle */
                      uint8   zeit[2];    /* time    */
                      uint8   datum[2];   /* date    */
                    } *input = (struct INPUT *)ncprequest;
                    int result = nw_set_fdate_time(GET_32(input->fhandle),
                                 input->datum, input->zeit);
                    if (result <0) completition = (uint8) -result;
                  }
                  break;

      case 0x4c  : {  /* open file */
                    struct INPUT {
                      uint8   header[7];     /* Requestheader */
                      uint8   dirhandle;     /* Dirhandle     */
                      uint8   attrib;        /* z.B. 0x6 od. 0x4e  */
                            /* O_RDWR|TRUNC 0x6, O_RDONLY 0x6 */

                      uint8   access; /* z.B. 0x9 od 0x11 od. 0x13 */
                            /* O_RDWR|TRUNC  0x13, O_RDONLY 0x11 */
                            /* O_RDWR|TRUNC|O_DENYNONE 0x3 */
                            /* 0x10 BINAERMODUS ??    */
                            /* 0x02 do write          */
                      uint8   len;           /* namelaenge */
                      uint8   data[2];       /* Name       */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   ext_fhandle[2]; /* all zero       */
                      uint8   fhandle[4];     /* Dateihandle    */
                      uint8   reserved[2];    /* reserved by Novell */
                      NW_FILE_INFO fileinfo;
                    } *xdata= (struct XDATA*)responsedata;
                    int  fhandle=nw_creat_open_file((int)input->dirhandle,
                            input->data, input->len,
                            &(xdata->fileinfo),
                            (int)input->attrib,
                            (int)input->access, 0,
                            (int)(ncprequest->task));

                    if (fhandle > -1){
                      U32_TO_32  (fhandle, xdata->fhandle);
                      xdata->ext_fhandle[0]=0;
                      xdata->ext_fhandle[1]=0;
                      xdata->reserved[0]=0;
                      xdata->reserved[1]=0;
                      data_len = sizeof(struct XDATA);
#ifdef TEST_FNAME
                      input->data[input->len] = '\0';
                      if (strstr(input->data, TEST_FNAME)){
                        test_handle = fhandle;
                        do_druck++;
                      }
#endif

                    } else completition = (uint8) (-fhandle);
                  }
                  break;

#if WITH_NAME_SPACE_CALLS
      case 0x56 : /* some extended atrribute calls */
                  {
                    int result = handle_func_0x56(requestdata, responsedata, ncprequest->task);
                    if (result > -1) data_len = result;
                    else completition=(uint8)-result;
                  }
                  break;

      case 0x57 : /* some new namespace calls */
                  {
                    int result = handle_func_0x57(requestdata, responsedata, ncprequest->task);
                    if (result > -1) data_len = result;
                    else completition=(uint8)-result;
                  }
                  break;
#endif

#ifdef _MAR_TESTS_XX
      case 0x5f : { /* ????????????? UNIX Client */
                    /* a 4.1 Server also do not know this call */
                    struct INPUT {
                      uint8   header[7];  /* Requestheader */
                      uint8   unknown[4]; /* 0x10, 0,0,0  */
                    } *input = (struct INPUT *)ncprequest;
                    completition = 0;
                  }
                  break;

#endif

      case 0x61 :
#if ENABLE_BURSTMODE
                  if (server_version_flags&1) { /* enable Burstmode */
                    /* Negotiate Buffer Size,  Packetsize new ?  */
                    int   wantsize = GET_BE16((uint8*)requestdata);
                    /* wantsize is here max.
                     * phys. packet size without MAC-header
                     * e.g. 1500 if ethernet
                     */
                    int   flags    = (int)   *(requestdata+2);
                   /**** flags ***********************
                    * CHECKSUMMING_REQUESTED         1
                    * SIGNATURE_REQUESTED            2
                    * COMPLETE_SIGNATURES_REQUESTED  4
                    * ENCRYPTION_REQUESTED           8
                    * LIP_DISABLED                0x80
                    **********************************/
                    struct XDATA {
                      uint8   getsize[2];
                      uint8   socket[2];      /* echo socket */
                      uint8   flags;          /* zero        */
                    } *xdata= (struct XDATA*)responsedata;
                    memset(xdata, 0, sizeof(*xdata));
                    wantsize = min(IPX_MAX_DATA+30,     wantsize);
                    rw_buffer_size = min(RW_BUFFERSIZE, wantsize-64);

                    U16_TO_BE16(wantsize,  xdata->getsize);
                    U16_TO_BE16(sock_echo, xdata->socket);
                    data_len = sizeof(*xdata);
                    XDPRINTF((2,0, "Packet Burst negotiate buffer: want=0x%04x reply=0x%04x rw_buffer=0x%04x echo_socket=0x%04x flags=0x%x",
                           (int) GET_BE16((uint8*)requestdata),
                           (int) wantsize,
                           (int) rw_buffer_size,
                           (int) sock_echo,
                           flags));
                  } else
#endif
                  {
                    XDPRINTF((2,0, "Function '0x61' (Burst) not enabled"));
                    completition = 0xfb; /* unknown request */
                    nw_debug=0;
                  }
                  break;

      case 0x65 :  /* Packet Burst Connection Request */
#if ENABLE_BURSTMODE
                  if (server_version_flags&1) { /* enable burstmode */
                    struct INPUT {
                      uint8   header[7];          /* Requestheader   */
                      uint8   connid[4];          /* RANDOM ID       */
                                                  /* build by time() */
                      uint8   max_packet_size[4]; /* HI-LO */
                      /* max_packet_size is here max.
                       * phys. packet size without MAC-header
                       * e.g. 1500 if ethernet
                       */
                      uint8   target_socket[2];   /* HI-LO */
                      uint8   max_send_size[4];   /* HI-LO */
                      uint8   max_recv_size[4];   /* HI-LO */
                    } *input = (struct INPUT *)ncprequest;
                    struct XDATA {
                      uint8   server_id[4];       /* RANDOM ID       */
                                                  /* build by time() */
                      uint8   max_packet_size[4]; /* HI-LO        */
                      uint8   max_send_size[4];   /* HI-LO        */
                      uint8   max_recv_size[4];   /* HI-LO        */
                    } *xdata= (struct XDATA*) responsedata;
                    int client_socket=GET_BE16(input->target_socket);
                    uint32 max_packet_size=min(sizeof(IPX_DATA),
                              GET_BE32(input->max_packet_size)-30);
                    U32_TO_BE32(max_packet_size + 30,
                        xdata->max_packet_size);
                    if (!burst_w)
                      burst_w=(BURST_W*)xcmalloc(sizeof(BURST_W));
                    xfree(burst_w->sendburst);
                    xfree(burst_w->send_buf);
                    xfree(burst_w->recv_buf);

                    burst_w->max_burst_data_size=
                       max_packet_size-sizeof(BURSTPACKET);

                    burst_w->sendburst=
                      (BURSTPACKET*)xcmalloc(max_packet_size);

                    burst_w->ud.udata.buf = (char*)(burst_w->sendburst);

                    burst_w->sendburst->type[0]=0x77;
                    burst_w->sendburst->type[1]=0x77;
                    burst_w->sendburst->streamtyp=2; /* BIG_SEND_BURST */

                    U32_TO_BE32(time(NULL),   burst_w->sendburst->source_conn);
                    U16_TO_16(act_connection, burst_w->sendburst->source_conn);
                    /* we need to identify it */

                    memcpy(xdata->server_id,
                           burst_w->sendburst->source_conn, 4);
                    memcpy(burst_w->sendburst->dest_conn,
                           input->connid, 4);

                    burst_w->max_send_size=
                      min(max_burst_send_size,
                        GET_BE32(input->max_recv_size));
                    burst_w->send_buf=xcmalloc(burst_w->max_send_size+8);

                    burst_w->max_recv_size=
                      min(max_burst_recv_size,
                        GET_BE32(input->max_send_size));
#if 1  /* MUST BE REMOVED LATER !!! */
                    /* we don't want fragmented receive packets */
                    if (burst_w->max_recv_size >
                         burst_w->max_burst_data_size-24)
                       burst_w->max_recv_size
                         =burst_w->max_burst_data_size-24;
#endif
                    burst_w->recv_buf=xcmalloc(burst_w->max_recv_size+24);
#if 1
                    U32_TO_BE32(0x5ff22, burst_w->sendburst->delaytime);
#endif
                    U32_TO_BE32(burst_w->max_recv_size,   xdata->max_recv_size);
                    U32_TO_BE32(burst_w->max_send_size,   xdata->max_send_size);

                    burst_w->ipx_pack_typ     = PACKT_CORE;
                    burst_w->ud.opt.len       = sizeof(uint8);
                    burst_w->ud.opt.maxlen    = sizeof(uint8);
                    burst_w->ud.opt.buf       = (char*)&(burst_w->ipx_pack_typ);

                    memcpy(&(burst_w->to_addr), &from_addr, sizeof(ipxAddr_t));
                    U16_TO_BE16(client_socket, burst_w->to_addr.sock);
                    burst_w->ud.addr.len      = sizeof(ipxAddr_t);
                    burst_w->ud.addr.maxlen   = sizeof(ipxAddr_t);
                    burst_w->ud.addr.buf      = (char*)&(burst_w->to_addr);
                    data_len = sizeof(*xdata);
                    XDPRINTF((2,0, "Packet Burst connection accepted: client_socket=0x%04x max_packet=%u max_data=%u send=%u recv=%u",
                           client_socket,
                           (unsigned)(max_packet_size + 30),
                           (unsigned)burst_w->max_burst_data_size,
                           (unsigned)burst_w->max_send_size,
                           (unsigned)burst_w->max_recv_size));
                  } else
#endif
                  {
                    XDPRINTF((2,0, "Packet Burst Connection Request not enabled"));
                    nw_debug=0;
                    completition = 0xfb; /* unknown request */
                  }
                  break;

      case 0x68 :  /* NDS NCP,  NDS Fragger Protokoll ??  */
                XDPRINTF((2,0, "NDS Fragger Protokoll not supportet"));
                nw_debug=0;
                completition = 0xfb; /* unknown request */
                break;

      default : completition = 0xfb; /* unknown request */
                break;

    } /* switch function */

  } else if (ncp_type == 0x1111) {
    free_queue_jobs();
    (void) nw_init_connect();
    last_sequence = -9999;
  } else {
    XDPRINTF((1,0, "WRONG TYPE:0x%x", ncp_type));
    completition = 0xfb;
  }

  if (nw_debug && (completition == 0xfb || (do_druck == 1))) { /* UNKWON FUNCTION od. TYPE */
    pr_debug_request();
    if (completition == 0xfb) {
      int req_len = requestlen - sizeof(NCPREQUEST);
      int subfunc = -1;
      if (req_len > 2 && (function == 0x15 || function == 0x16 || function == 0x17))
        subfunc = requestdata[2];
      else if (req_len > 0 && function == 0x20)
        subfunc = requestdata[0];

      if (subfunc > -1)
        XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x ufunc=0x%x",
                  ncp_type, function, subfunc));
      else
        XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x",
                  ncp_type, function));
    } else if (data_len){
      int j     = data_len;
      uint8  *p = responsedata;
      XDPRINTF((0,2, "RSPONSE: len %d, DATA:",  data_len));
      while (j--) {
        int c = *p++;
        if (c > 32 && c < 127)  XDPRINTF((0,3,",\'%c\'", (char) c));
        else XDPRINTF((0,3,",0x%x", c));
      }
      XDPRINTF((0,1, NULL));
    }
  }
  ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len);
  nw_debug = org_nw_debug;
  return(0);
}

static void handle_after_bind()
{
  NCPREQUEST *ncprequest = (NCPREQUEST*) saved_readbuff;
  uint8 *requestdata  = saved_readbuff + sizeof(NCPREQUEST);
  uint8 *bindresponse = readbuff       + sizeof(NCPRESPONSE);
  int   data_len      = 0;
  int   completition  = 0;
  switch (ncprequest->function) {
      /*  QUOTA support from: Matt Paley  */
    case 0x16 : {
      uint8 ufunc    = *(requestdata+2);
      switch (ufunc) {
        case 0x21: {
          /* change Vol restrictions for Obj */
          uint8  volnr = *(requestdata+3);
          uint32 id     = GET_BE32(requestdata+4);
          uint32 blocks = GET_32(requestdata+8);
          int    gid    = ((int *) bindresponse)[0];
          int    uid    = ((int *) bindresponse)[1];
          int    perm   = ((int *) bindresponse)[2];
          int    result;
          XDPRINTF((5, 0,
               "Change vol rest id=%x vol=%d blocks=%d gid=%d uid=%d p=%d",
                    (int) id, (int) volnr, (int) blocks,
                    (int) gid, (int) uid, (int) perm));
          if (perm == 0) {
            result = nw_set_vol_restrictions(volnr, uid, blocks);
          } else {
            result = -0x85;
          }
          if (result < 0) completition = (uint8)-result;
        }
        break;
        case 0x22: {
          /* Remove Vol restrictions for Obj */
          uint8  volnr = *(requestdata+3);
          uint32 id     = GET_BE32(requestdata+4);
          int    gid    = ((int *) bindresponse)[0];
          int    uid    = ((int *) bindresponse)[1];
          int    perm   = ((int *) bindresponse)[2];
          int result;
          XDPRINTF((1, 0, "Remove vol rest id=%x vol=%d gid=%d uid=%d p=%d",
                    (int) id, (int) volnr, (int) gid, (int) uid, (int) perm));
          if (perm == 0) {
            result = nw_set_vol_restrictions(volnr, uid, 0);
          } else {
            result = -0x85;
          }
          if (result < 0) completition = (uint8)-result;
        }
        break;
        case 0x29: {
          /* Get Vol restrictions for Obj */
          uint8  volnr = *(requestdata+3);
          uint32 id     = GET_BE32(requestdata+4);
          int    gid    = ((int *) bindresponse)[0];
          int    uid    = ((int *) bindresponse)[1];
          int    perm   = ((int *) bindresponse)[2];
          uint32 quota, used;
          int result;
          struct XDATA {
            uint8 restriction[4];
            uint8 inuse[4];
          } *xdata = (struct XDATA*) responsedata;
          XDPRINTF((5, 0, "Get vol rest id=%x vol=%d gid=%d uid=%d p=%d",
                    (int) id, (int) volnr, (int) gid, (int) uid, (int) perm));
          if (perm == 0) {
            result = nw_get_vol_restrictions(volnr, uid, &quota, &used);
          } else {
            result = -0x85;
          }
          data_len = 8;
          if (result == 0) {
            U32_TO_32(quota, xdata->restriction);
            U32_TO_32(used,  xdata->inuse);
          } else {
            U32_TO_32(0x40000000, xdata->restriction);
            U32_TO_32(0x0,        xdata->inuse);
            completition = (uint8) -result;
          }
        }
        break;
        default : completition = 0xfb;
      }
      break;
    }
    case 0x17 : {  /* FILE SERVER ENVIRONMENT */
       uint8 ufunc    = *(requestdata+2);
       switch (ufunc) {
         case 0x14:   /* Login Objekt, unencrypted passwords */
         case 0x18: { /* crypt_keyed LOGIN */
           int    grpcount      = * (int*)(bindresponse    + 4 * sizeof(int));
           uint32 *grps         =   (uint32*)(bindresponse + 5 * sizeof(int));
           int    unxloginlen   =   (int)*(uint8*)(grps+grpcount);
           uint8  *unxloginname =   (uint8*)(grps+grpcount)+1;
           uint8 objname[48];
           /* ncpserv have changed the structure */
           if (ufunc==0x14) {
             xstrmaxcpy(objname, requestdata+6, (int) *(requestdata+5));
           } else if (ufunc==0x18){
             xstrmaxcpy(objname, requestdata+14, (int) *(requestdata+13));
           } else objname[0]='\0';
           set_nw_user(*((int*)bindresponse),               /* gid */
                       *((int*)(bindresponse+sizeof(int))), /* uid */
                       *((int*)(bindresponse    + 2 * sizeof(int))), /* id_flags */
                       *((uint32*)(bindresponse + 3 * sizeof(int))), /* id */
                        objname,           /* login name */
                        unxloginlen, unxloginname,
                        grpcount, grps);
         }
         break;

         case 0x68:   /* create queue job and file old */
         case 0x79: { /* create queue job and file     */
                      /* nwbind made prehandling       */
           struct INPUT {
             uint8   header[7];          /* Requestheader   */
             uint8   packetlen[2];       /* low high        */
             uint8   func;               /* 0x79 or 0x68    */
             uint8   queue_id[4];        /* Queue ID        */
             uint8   queue_job[280];     /* oldsize is 256  */
           } *input = (struct INPUT *) (ncprequest);
           uint32  q_id = GET_BE32(input->queue_id);
           uint8  *qjob = bindresponse;
           int result = creat_queue_job( (int) ncprequest->task,
                                         q_id, qjob,
                                         responsedata,
                                         (ufunc == 0x68)  );
           if (result > -1)
             data_len=result;
           else
             completition = (uint8) -result;
         }
         break;

         case 0x69:    /* close file and start queue old ?? */
         case 0x7f: {  /* close file and start queue */
           struct INPUT {
             uint8   header[7];          /* Requestheader */
             uint8   packetlen[2];       /* low high      */
             uint8   func;               /* 0x7f or 0x69  */
             uint8   queue_id[4];        /* Queue ID      */
             uint8   job_id[4];          /* result from creat queue    */
                                         /* if 0x69 then only 2 byte ! */
           } *input = (struct INPUT *) (ncprequest);
           struct RINPUT {
             uint8   client_area[152];
             uint8   prc_len;           /* len of printcommand */
             uint8   prc[1];            /* printcommand */
           } *rinput = (struct RINPUT *) (bindresponse);
           uint32 q_id = GET_BE32(input->queue_id);
           int  job_id = (ufunc==0x69) ? GET_BE16(input->job_id)
                                       : GET_BE16(input->job_id);

           int result = close_queue_job2(q_id, job_id,
                                            rinput->client_area,
                                            rinput->prc,
                                            rinput->prc_len);
           if (result < 0) completition = (uint8)-result;
         }
         break;

         case 0x71 :    /* service queue job (old) */
         case 0x7c : {  /* service queue job */
           struct INPUT {
             uint8   header[7];          /* Requestheader   */
             uint8   packetlen[2];       /* low high        */
             uint8   func;               /* 0x7c,0x71       */
             uint8   queue_id[4];        /* Queue ID        */
             uint8   job_typ[2];         /* service typ     */
           } *input = (struct INPUT *) (ncprequest);
           uint32  q_id = GET_BE32(input->queue_id);
           uint8  *qjob = bindresponse;
           int result   = service_queue_job((int)ncprequest->task,
                                      q_id, qjob,
                                      responsedata,
                                      ufunc==0x71);
           if (result > -1)
             data_len=result;
           else
             completition = (uint8) -result;
         }
         break;

         default : completition = 0xfb;
       }
    }
    break;
    default : completition = 0xfb;
  } /* switch */
  ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len);
}

#if ENABLE_BURSTMODE


static int send_burst(int offset, int datasize, int flags)
{
  BURSTPACKET *sb=burst_w->sendburst;
  U32_TO_BE32(burst_w->packet_sequence++, sb->packet_sequence);
  U32_TO_BE32(offset,   sb->burstoffset);
  U16_TO_BE16(datasize, sb->datasize);
  U16_TO_BE16(0,        sb->missing);
  sb->flags = (uint8)flags;
  memcpy(sb+1, burst_w->send_buf+offset, datasize);
  burst_w->ud.udata.len =
     burst_w->ud.udata.maxlen = datasize+sizeof(BURSTPACKET);
  XDPRINTF((8, 0, "Packet Burst send packet: seq=%u off=%d size=%d flags=0x%x",
        (unsigned)(burst_w->packet_sequence-1), offset, datasize, flags));
  if (t_sndudata(FD_NCP_OUT, &(burst_w->ud)) < 0){
    if (nw_debug) t_error("t_sndudata in NWCONN !OK");
    return(-1);
  }
  return(0);
}

#include <sys/time.h>
static void sleep_mu(int mu)
{
  struct timeval t;
  t.tv_sec  = 0;
  t.tv_usec = mu;
  (void) select(1, NULL, NULL, NULL, &t);
}

static void handle_burst_response(uint32 offset, int size)
{
  BURSTPACKET *sb=burst_w->sendburst;
  U16_TO_BE16(burst_w->burst_sequence,   sb->burst_seq);
  U16_TO_BE16(burst_w->burst_sequence+1, sb->ack_seq);
  U32_TO_BE32(size,  sb->burstsize);

  XDPRINTF((3, 0, "Packet Burst response: seq=%u offset=%u size=%d max_packet_data=%d",
        (unsigned)burst_w->burst_sequence, (unsigned)offset, size,
        burst_w->max_burst_data_size));

  while (size) {
    int sendsize=min(size, burst_w->max_burst_data_size);
    int flags=0;
    size-=sendsize;
    if (!size) flags|=0x10; /* EndOfBurst */
    send_burst(offset, sendsize, flags);
#if 0
    sleep_mu(2);
#endif
    offset+=sendsize;
  }

}

static void handle_burst(BURSTPACKET *bp, int len)
{
  if (burst_w) {
    uint32 burstoffset   = GET_BE32(bp->burstoffset);
    int    burstsequence = GET_BE16(bp->burst_seq);
    int    datasize      = GET_BE16(bp->datasize);

    XDPRINTF((6, 0, "Packet Burst recv packet: seq=%d flags=0x%x off=%u datasize=%d len=%d",
          burstsequence, bp->flags, (unsigned)burstoffset, datasize, len));

    if (datasize && !(bp->flags & 0x80)) {
      /* copy if no System Packet */
      if (datasize+burstoffset > burst_w->max_recv_size+24) {
        XDPRINTF((1, 0, "recv burstpacket offs=%d+size=%d > max_recv+24=%d",
         burstoffset, datasize, burst_w->max_recv_size+24));
        return;
      }
      memcpy(burst_w->recv_buf+burstoffset, bp+1, datasize);
    }

    if (bp->flags & 0x10) {  /* last packet, now action */
      /* 0x10 = EOB flag */
      struct REQ {
        uint8 function[4];           /* lo-hi    1=READ, 2=WRITE   */
        uint8 fhandle[4];            /* from open file             */
        uint8 reserved1[4];          /* all zero                   */
        uint8 reserved2[4];          /* ??? c8,0 od. c9,f0         */
        uint8 file_offset[4];        /* HI-LO  */
        uint8 file_size  [4];        /* HI-LO  */
        uint8 data[2];               /* only Write */
      } *req=(struct REQ*)(burst_w->recv_buf);
      int function=GET_32(req->function);

      if (function == 1 || function == 2) {  /* Read or Write */
        uint32 fhandle = GET_32(req->fhandle);
        uint32 foffset = GET_BE32(req->file_offset);
        uint32 fsize   = GET_BE32(req->file_size);
        if (function == 1) {  /* Read Request */
          struct XDATA {
            uint8 resultcode[4];  /* lo-hi ,
                             * 0=noerror=OK,
                             * 1=init-err,
                             * 2=IO-err,
                             * 3=no data
                             */
            uint8 readbytes[4];   /* hi-lo */
          } *xdata= (struct XDATA*)burst_w->send_buf;
          int zusatz = 0; /* (foffset & 1) ? 1 : 0; */
          int size;
          XDPRINTF((2, 0, "Packet Burst READ request: fh=0x%x off=%u size=%u seq=%d",
                (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize,
                burstsequence));
          size = nw_read_file(fhandle,
                              burst_w->send_buf+sizeof(struct XDATA),
                              fsize, foffset);
          if (zusatz) {
            XDPRINTF((1, 0, "foffset=%d, fsize=%d", foffset, fsize));
          }
          if (size > -1) {
            U32_TO_32(0,      xdata->resultcode);
            U32_TO_BE32(size, xdata->readbytes);
            XDPRINTF((2, 0, "Packet Burst READ reply: fh=0x%x off=%u requested=%u read=%d",
                  (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size));
          } else {
            U32_TO_32(3,      xdata->resultcode);
            U32_TO_BE32(0,    xdata->readbytes);
            size=0;
          }
          burst_w->burst_sequence = burstsequence;
          handle_burst_response(0, size+sizeof(struct XDATA));
        } else { /* Write Request */
          struct XDATA {
            uint8 resultcode[4];  /* lo-hi ,
                                   * 0=noerror=OK,
                                   * 4=write error
                                   */
          } *xdata= (struct XDATA*)burst_w->send_buf;
          int size;
          XDPRINTF((2, 0, "Packet Burst WRITE request: fh=0x%x off=%u size=%u seq=%d",
                (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize,
                burstsequence));
          size = nw_write_file(fhandle, req->data, fsize, foffset);
          U32_TO_32(size==fsize ? 0 : 4, xdata->resultcode);
          XDPRINTF((2, 0, "Packet Burst WRITE reply: fh=0x%x off=%u requested=%u written=%d result=%s",
                (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size,
                size==fsize ? "ok" : "error"));
          burst_w->burst_sequence = burstsequence;
          handle_burst_response(0, sizeof(struct XDATA));
        }
      } else {
        XDPRINTF((1, 0, "burst unknow function=0x%x", function));
      }
      req->function[0]=0;
    } else if (bp->flags & 0x80) {  /* System Flag */
      int missing=GET_BE16(bp->missing);
      uint8 *p=(uint8*)(bp+1);
      burst_w->burst_sequence = burstsequence;
      XDPRINTF((3, 0, "Packet Burst missing-list request: seq=%d missing=%d",
            burstsequence, missing));
      while (missing--){
        int offs=GET_BE32(p);
        int size=GET_BE16(p+4);
        XDPRINTF((3, 0, "Packet Burst resend range: off=%d size=%d", offs, size));
        handle_burst_response(offs, size);
        p+=6;
      }
    }
  } else {
    XDPRINTF((1, 0, "burst_w not allocated"));
  }
}
#endif

static void close_all(void)
{
  nw_exit_connect();
  close(0);
  close(FD_NCP_OUT);
}

static int  fl_get_int=0;
/* signals
 *   &01   sig_quit
 *   &02   sig_hup
 *   &04   sig_usr1
 *   &08   sig_usr2
 */

static void sig_quit(int rsig)
{
  XDPRINTF((2, 0, "Got Signal=%d", rsig));
  fl_get_int |= 1;
}

static void sig_pipe(int rsig)
{
  XDPRINTF((1, 0, "Got SIG_PIPE"));
  signal(SIGPIPE,  sig_pipe);
}

static void sig_hup(int rsig)
{
  fl_get_int |= 2;
  signal(SIGHUP,   sig_hup);
}

static void sig_usr1(int rsig)
{
  fl_get_int |= 4;
}

static void sig_usr2(int rsig)
{
  fl_get_int |= 8;
}

static void get_new_debug(void)
{
  get_ini_debug(3);
  fl_get_int &= ~2;
}

static void handle_extern_command(void)
{
  fl_get_int &= ~4;
  signal(SIGUSR1, sig_usr1);
}

static void handle_sigusr2(void)
{
  char fn[256];
  FILE *f;
  fl_get_int &= ~8;
  sprintf(fn, "/tmp/nwconn%04d.log", act_connection);
  if (seteuid(0)) {}
  unlink(fn); /* security: mst:18-Apr-00 */
  f=fopen(fn, "w");
  (void)reseteuid();
  if (f) {
    log_file_module(f);
    fclose(f);
  } else
    errorp(0, "handle_sigusr2", "cannot open %s for writing", fn);
  signal(SIGUSR2, sig_usr2);
}

static void set_sig(void)
{
  signal(SIGTERM,  sig_quit);
  signal(SIGQUIT,  sig_quit);
  signal(SIGINT,   sig_quit);
  signal(SIGPIPE,  sig_pipe);
  signal(SIGHUP,   sig_hup);
  signal(SIGUSR1,  sig_usr1);
  signal(SIGUSR2,  sig_usr2);
  if (use_mmap)
     signal(SIGBUS,   sig_bus_mmap);  /* in nwfile.c */
}
#include <sys/resource.h>

int main(int argc, char **argv)
{
#if !CALL_NWCONN_OVER_SOCKET  
  int shm_id;
#endif
  time_t last_time=time(NULL);
#if CALL_NWCONN_OVER_SOCKET  
  if (argc != 4 || 3!=sscanf(argv[3], "()INIT-:%x,%x,%x-",
     &father_pid, &sock_nwbind, &sock_echo)) {
    fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock-\n");
    exit(1);
  }
#else
  if (argc != 4 || 4!=sscanf(argv[3], "()INIT-:%x,%x,%x,%x-",
     &father_pid, &sock_nwbind, &sock_echo, &shm_id)) {
    fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock,shm_id-\n");
    exit(1);
  }
#endif
  prog_title=argv[3];
  if (setuid(0)) {}
  if (setgid(0)) {}
  act_connection = atoi(*(argv+1));
#if !CALL_NWCONN_OVER_SOCKET
  nwconn_state = shmat(shm_id, NULL, SHM_W);
  if (nwconn_state == (char *)-1) {
    errorp(0, "Can't attach shared memory segment", NULL);
    exit(1);
  }
#endif
  init_tools(NWCONN, 0);
  memset(saved_readbuff, 0, sizeof(saved_readbuff));
  XDPRINTF((3, 0, "FATHER PID=%d, ADDR=%s CON:%d",
                  father_pid, *(argv+2), act_connection));
  adr_to_ipx_addr(&from_addr,   *(argv+2));

  if (nw_init_connect()) exit(1);
  act_pid = getpid();

#ifdef LINUX
  set_emu_tli();
#endif
  last_sequence = -9999;
  if (get_ipx_addr(&my_addr)) exit(1);

#if CALL_NWCONN_OVER_SOCKET
# if 1
#  ifdef SIOCIPXNCPCONN
   {
     int conn   = act_connection;
     int result = ioctl(0, SIOCIPXNCPCONN, &conn);
     XDPRINTF((3, 0, "ioctl:SIOCIPXNCPCONN result=%d", result));
   }
#  endif
# endif
#endif

  set_default_guid();
  nwconn_set_program_title(NULL);

  ud.opt.len       = sizeof(uint8);
  ud.opt.maxlen    = sizeof(uint8);
  ud.opt.buf       = (char*)&ipx_pack_typ;

  ud.addr.len      = sizeof(ipxAddr_t);
  ud.addr.maxlen   = sizeof(ipxAddr_t);
  ud.addr.buf      = (char*)&from_addr;
  ud.udata.buf     = (char*)&ipxdata;

  U16_TO_BE16(0x3333, ncpresponse->type);
  ncpresponse->task            = (uint8) 1;    /* allways 1 */
  ncpresponse->connection      = (uint8)act_connection;
  ncpresponse->high_connection = (uint8)(act_connection >> 8);

  set_sig();

  while ( !(fl_get_int&1) ) {
    int data_len;
    /* We should reply 'Request Being Processed' if request arrived twice
     * or more and nwconn actually busy, if nwconn is free, we are simply
     * resend previous reply.
     * We are set the flag in shared memory indicating what nwconn is busy 
     * and check it later in ncpserv. /lenz */
#if !CALL_NWCONN_OVER_SOCKET
    nwconn_state[act_connection] = 0; /* nwconn is free */
#endif    
    data_len = read(0, readbuff, sizeof(readbuff));
#if !CALL_NWCONN_OVER_SOCKET
    nwconn_state[act_connection] = 1; /* nwconn is busy */
#endif
    /* this read is a pipe or a socket read,
     * depending on CALL_NWCONN_OVER_SOCKET
     */

    if (fl_get_int) {
      if (fl_get_int & 1) break;
      if (fl_get_int & 2)
        get_new_debug();
      if (fl_get_int & 4)
        handle_extern_command();
      if (fl_get_int & 8)
        handle_sigusr2();
    }

    if (data_len > 0) {
      XDPRINTF((99, 0,  "NWCONN GOT DATA len = %d",data_len));
      ncpresponse->connect_status = (uint8) 0;
      ncpresponse->task           = ncprequest->task;

      if ((ncp_type = (int)GET_BE16(ncprequest->type)) == 0x3333) {
        /* this is a response packet */
        data_len -= sizeof(NCPRESPONSE);
        if (saved_sequence > -1
            && ((int)(ncprequest->sequence) == saved_sequence)
            && !ncprequest->function) {
          /* comes from nwbind */
          handle_after_bind();
        } else {
          /* OK for direct sending */
          XDPRINTF((6,0, "NWCONN:direct sending:type 0x3333, completition=0x%x, len=%d",
                 (int)(ncprequest->function), data_len));
          if (data_len)
            memcpy(responsedata, readbuff+sizeof(NCPRESPONSE), data_len);
          ncpresponse->connect_status = ((NCPRESPONSE*)readbuff)->connect_status;
          ncp_response(ncprequest->sequence,
                       ncprequest->task,
                       ncprequest->function, data_len);
        }
        saved_sequence = -1;
      } else { /* this calls I must handle, it is a request */
        act_time=time(NULL);
        act_ncpsequence=(int)(ncprequest->sequence);

        if (act_time > last_time+60 && saved_sequence == -1) {
           /* ca. 0.5 min. reset wdogs, 5 min as in original is too long for me. /lenz*/
           call_nwbind(1);
           last_time=act_time;
        }

#if ENABLE_BURSTMODE
        if (ncp_type == 0x7777) { /* BURST-MODE */
          XDPRINTF((2, 0, "Packet Burst data packet received: len=%d", data_len));
          handle_burst((BURSTPACKET*)readbuff, data_len);
        } else
#endif
        if (ncp_type == 0x2121) { /* request from nwbind  */
          /* mst:25-Apr-00 */
          requestlen  = data_len - sizeof(NCPREQUEST);
          handle_nwbind_request();
        } else {
          int result;
          requestlen  = data_len - sizeof(NCPREQUEST);
          if (0 != (result = handle_ncp_serv()) ) {
            if (result == -2) {
              /* here the actual call must be saved
               * because we need it later, when the request to nwbind
               * returns.
               */
              memcpy(saved_readbuff, readbuff, data_len);
              saved_sequence = (int)(ncprequest->sequence);
            } else saved_sequence = -1;
            /* this call must go to nwbind */
            call_nwbind(0);
          }
        }
      }
    }
  } /* while */

  if (seteuid(0)) {}
#  ifdef SIOCIPXNCPCONN
   {
     int conn = -act_connection;
     (void)ioctl(0, SIOCIPXNCPCONN, &conn);
   }
#  endif
  close_all();
  XDPRINTF((3,0, "leave nwconn pid=%d", getpid()));
  return(0);
}
