/* nwdbm.c  25-Apr-00 data base for mars_nwe */
/* (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 login control routines from Paolo Prandini
 * mst:25-Apr-00: removed bug from pl18 for passwords beginning with '-'
 *
 */

/*
 * This code is only called from the process 'nwbind'
 * So, there is no need for locking or something else.
 */
#include "net.h"
#include "nwdbm.h"
#include "nwcrypt.h"
#include "nwqueue.h"
#include "dirent.h"
#include "unxfile.h"

#ifdef LINUX
#  ifdef USE_GDBM
#    include <gdbm.h>
#  else
#    include <ndbm.h>
#  endif

#  ifndef  SHADOW_PWD
#    define SHADOW_PWD  0
#  endif
#else
#  include </usr/ucbinclude/ndbm.h>
#  undef  SHADOW_PWD
#  define SHADOW_PWD  1
#endif

#ifdef USE_GDBM
#  define DBM_FILE                      GDBM_FILE
#else
#  define DBM_FILE                      DBM *
#endif

#if SHADOW_PWD
#  include <shadow.h>
#endif

#define DBM_REMAINS_OPEN  1

int tells_server_version=1; /* default 1 since 12-Jan-97 */
int password_scheme=0;

uint8 *sys_unixname=NULL; /* Unixname of SYS: ends with '/'  */
int   sys_unixnamlen=0;   /* len of unixname     */
int   sys_downshift=0;    /* is SYS downshift    */
int   sys_has_trustee=0;  /* has SYS trustees ?  */
uint8 *sys_sysname=NULL;  /* Name of first Volume, normally SYS */

uint32 network_serial_nmbr=(uint32)NETWORK_SERIAL_NMBR;
uint16 network_appl_nmbr=(uint16)NETWORK_APPL_NMBR;
int entry8_flags  = 0;   /* used in nwbind too */

static int entry17_flags = 0;

static datum key;
static datum data;
static DBM_FILE  my_dbm=NULL;

#define FNPROP  0
#define FNVAL   1
#define FNOBJ   2

#define FNIOBJ  3     /* Index for  Object Names */

#define COUNT_DBM_FILES  4

static char  *dbm_fn[COUNT_DBM_FILES]  = {
 "nwprop", "nwval", "nwobj"
#if COUNT_DBM_FILES > 3
 ,"nwiobj"
#endif
 };

#if DBM_REMAINS_OPEN
static DBM_FILE  my_dbms[COUNT_DBM_FILES] = {
  NULL, NULL, NULL
#if COUNT_DBM_FILES > 3
, NULL
#endif
};
#endif

static int x_dbminit(char *s, int ro)
{
  char buff[256];
#ifdef USE_GDBM
  (void)get_div_pathes(buff, s, 1, ".pag");
  my_dbm = gdbm_open(buff, 0, ro ? GDBM_READER : GDBM_WRCREAT, 0600, NULL);
#else
  (void)get_div_pathes(buff, s, 1, NULL);
  my_dbm = dbm_open(buff,  ro ? O_RDONLY : O_RDWR|O_CREAT , 0600);
#endif
  return( (my_dbm == NULL) ? -1 : 0);
}

static int dbminit(int what_dbm)
{
  int result = 0;
#if DBM_REMAINS_OPEN
  if (NULL == my_dbms[what_dbm]) {
    result = x_dbminit(dbm_fn[what_dbm], 0);
    if (!result)  my_dbms[what_dbm] = my_dbm;
  } else my_dbm = my_dbms[what_dbm];
  return(result);
#else
  return(x_dbminit(dbm_fn[what_dbm], 0));
#endif
  if (result)
    errorp(0, "dbminit", "on %s", dbm_fn[what_dbm]);
  return(result);
}

static int dbminit_ro(int what_dbm)
{
  int result = 0;
#if DBM_REMAINS_OPEN
  if (NULL == my_dbms[what_dbm]) {
    result = x_dbminit(dbm_fn[what_dbm], 1);
    if (!result)  my_dbms[what_dbm] = my_dbm;
  } else my_dbm = my_dbms[what_dbm];
  return(result);
#else
  return(x_dbminit(dbm_fn[what_dbm], 1));
#endif
  if (result)
    errorp(0, "dbminit ro", "on %s", dbm_fn[what_dbm]);
  return(result);
}



static int dbmclose()
{
  if (my_dbm != NULL) {
#if !DBM_REMAINS_OPEN
#  ifdef USE_GDBM
    gdbm_close(my_dbm);
#  else
    dbm_close(my_dbm);
#  endif
#endif
    my_dbm = NULL;
  }
  return(0);
}

void sync_dbm()
{
#if DBM_REMAINS_OPEN
  int k = COUNT_DBM_FILES;
  dbmclose();
  while (k--) {
    if (NULL != my_dbms[k]) {
#  ifdef USE_GDBM
      gdbm_close(my_dbms[k]);
#  else
      dbm_close(my_dbms[k]);
#  endif
      my_dbms[k] = NULL;
    }
  }
#else
  dbmclose();
#endif
}

#ifdef USE_GDBM
static datum firstkey(void)
{
  static char *last_dptr=NULL;
  datum result=gdbm_firstkey(my_dbm);
  if (last_dptr) free(last_dptr);
  last_dptr=result.dptr;
  return(result);
}

static datum nextkey(datum key)
{
  static char *last_dptr=NULL;
  datum result=gdbm_nextkey(my_dbm, key);
  if (last_dptr) free(last_dptr);
  last_dptr=result.dptr;
  return(result);
}

static datum fetch(datum key)
{
  static char *last_dptr=NULL;
  datum result=gdbm_fetch(my_dbm, key);
  if (last_dptr) free(last_dptr);
  last_dptr=result.dptr;
  return(result);
}

#  define delete(key)         gdbm_delete(my_dbm, key)
#  define store(key, content) gdbm_store(my_dbm,  key, content, GDBM_REPLACE)
#else
#  define firstkey()          dbm_firstkey(my_dbm)
#  define nextkey(key)        dbm_nextkey(my_dbm)
#  define delete(key)         dbm_delete(my_dbm, key)
#  define fetch(key)          dbm_fetch(my_dbm,  key)
#  define store(key, content) dbm_store(my_dbm,  key, content, DBM_REPLACE)
#endif

static int handle_iobj(int mode, NETOBJ *o)
/* modes:
 * 0  = search/read
 * 1  = rewrite  ( not needed yet )
 * 2  = rewrite/creat
 * 3  = delete
 */
{
  int result=-0xff;
  if (!dbminit(FNIOBJ)){
    NETIOBJ  iobj;
    strncpy(iobj.name, o->name, sizeof(iobj.name));
    iobj.type = o->type;
    key.dsize = NETIOBJ_KEY_SIZE;
    key.dptr  = (char*)&iobj;
    result    = -0xfc; /* no Object */
    if (mode == 3) {
      if (!delete(key)) result=0;
    } else  {
      data = fetch(key);
      if (data.dptr != NULL) {
        NETIOBJ *piobj=(NETIOBJ*)data.dptr;
        XDPRINTF((3,0, "got index of OBJ name=%s, type=0x%x, id = 0x%x",
               visable_data(piobj->name, sizeof(piobj->name)), (int)piobj->type, piobj->id));
        if (!mode) {
          o->id  = piobj->id;
          result = 0;
        } else {  /* write back */
          piobj->id    = o->id;
          result=(store(key, data)) ? -0xff : 0;
        }
      } else if (mode == 2) { /* creat */
        data.dsize = sizeof(NETIOBJ);
        data.dptr  = (char*)&iobj;
        iobj.id    = o->id;
        result= (store(key, data)) ? -0xff : 0;
      }
    }
  }
  dbmclose();
  XDPRINTF((3, 0,"handle_iobj mode=%d, result=0x%x, OBJ=%s, type=0x%x",
      mode, -result,
      visable_data(o->name, sizeof(o->name)),(int)o->type));
  return(result);
}

int find_obj_id(NETOBJ *o)
/* no wildcards allowed */
{
  int result;
  XDPRINTF((2, 0,"findobj_id OBJ=%s, type=0x%x", visable_data(o->name, sizeof(o->name)),(int)o->type));
  if ((result=handle_iobj(0, o)) == 0) {
    result    = -0xff;
    if (!dbminit(FNOBJ)){
      key.dsize = NETOBJ_KEY_SIZE;
      key.dptr  = (char*)o;
      data      = fetch(key);
      if (data.dptr != NULL){
        NETOBJ *obj=(NETOBJ*)data.dptr;
        XDPRINTF((3,0, "got OBJ name=%s, id = 0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->id));
        if ( (!strncmp(obj->name, o->name, sizeof(obj->name)))
            && obj->type == o->type) {
          memcpy(o, data.dptr, sizeof(NETOBJ));
          result=0;
        } else {
          XDPRINTF((1,0, "OBJ Index '%s',0x%x, clashes OBJ data '%s', 0x%x",
                       visable_data(o->name, sizeof(o->name)),   (int)o->type,
                       visable_data(obj->name, sizeof(obj->name)), (int)obj->type));
        }
      } else {
        XDPRINTF((1,0, "OBJ Index '%s',0x%x, id=0x%x not found in OBJ data",
                       visable_data(o->name, sizeof(o->name)), (int)o->type, o->id));
      }
    }
    dbmclose();
    if (!result)
      return(0);
  }
  result=scan_for_obj(o, 0, 1);
  if (!result) { /* was ok, we will rewrite/creat iobj record */
    XDPRINTF((1, 0,"findobj_id OBJ='%s', type=0x%x, id=0x%x not in Index File",
       visable_data(o->name, sizeof(o->name)),(int)o->type,o->id));
    handle_iobj(2, o);
  }
  return(result);
}

int scan_for_obj(NETOBJ *o, uint32 last_obj_id, int ignore_rights)
/*
 * scans for object,
 * wildcards in objectname allowed
 * wildcard (MAX_U16) in  objecttype allowed
 */
{
  int result = -0xfc; /* no Object */
  XDPRINTF((2, 0,"scan_for_obj OBJ=%s, type=0x%x, lastid=0x%x",
              visable_data(o->name, sizeof(o->name)), (int)o->type, (int)last_obj_id));

  if (!dbminit(FNOBJ)){
    key = firstkey();
    if (last_obj_id && (last_obj_id != MAX_U32)){
      int  flag = 0;
      while (key.dptr != NULL && !flag) {
        if ( ((NETOBJ*)(key.dptr))->id == last_obj_id) flag++;
        key = nextkey(key);
      }
    }
    while (key.dptr != NULL && result) {
      data = fetch(key);
      if (data.dptr != NULL){
        NETOBJ *obj = (NETOBJ*)data.dptr;
        if ( ( ((int)obj->type == (int)o->type) || o->type == MAX_U16) &&
           name_match(obj->name, o->name) &&
           ( ignore_rights ||
           (b_acc(obj->id, obj->security, 0x00)== 0)))  {
          XDPRINTF((2, 0, "found OBJ=%s, id=0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->id));
          result = 0;
          memcpy((char *)o, (char*)obj, sizeof(NETOBJ));
        } else {
          XDPRINTF((3,0,"not found,but NAME=%s, type=0x%x, id=0x%x",
                         visable_data(obj->name, sizeof(obj->name)), (int)obj->type, (int)obj->id));
        }
      }
      if (result) key = nextkey(key);
    } /* while */
  } else result = -0xff;
  dbmclose();
  return(result);
}

static int loc_delete_property(uint32 obj_id,
                               uint8 *prop_name,
                               uint8 prop_id,
                               int   ever)  /* ever means no access tests */
/* deletes Object property or properties */
/* wildcards allowed in property name  */
{
  uint8 xset[256];
  int result = -0xfb; /* no property */
  memset(xset, 0, sizeof(xset));
  if (!prop_id) {
    XDPRINTF((2,0, "loc_delete_property obj_id=0x%x, prop=%s", obj_id, prop_name ? (char*)prop_name : "(null)"));
    if (!dbminit(FNPROP)){
      for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
        NETPROP *p=(NETPROP*)key.dptr;
        if (p->obj_id == obj_id) {
          data = fetch(key);
          p = (NETPROP*)data.dptr;
          if (p != NULL && p->name[0] == '\0') {
            XDPRINTF((1,0, "ignoring empty property name for obj_id=0x%x, id=%d", obj_id, (int)p->id));
          } else if (p != NULL && (!prop_name || !*prop_name || name_match(p->name, prop_name))){
            XDPRINTF((2,0, "found prop: %s, id=%d for deleting", p->name, (int)p->id));
            if (ever || !b_acc(obj_id, p->security, 0x13)) {
              if ((int)(p->id) > result) result = (int)(p->id);
              xset[p->id]++;
            } else if (result < 0) result = -0xf6; /* no delete priv. */
          }
        }
      } /* for */
    } else result = -0xff;
    dbmclose();
  } else {
    XDPRINTF((2,0, "loc_delete_property obj_id=0x%x, prop_id=%d", obj_id, (int)prop_id));
    xset[prop_id]++;
    result = prop_id;
  }
  if (result > 0) {
    if (!dbminit(FNVAL)){
      int k;
      NETVAL val;
      key.dptr   = (char*)&val;
      key.dsize  = NETVAL_KEY_SIZE;
      val.obj_id = obj_id;
      for (k = 1; k <= result; k++){
        if (xset[k]){
          int l   = 0;
          val.prop_id  = (uint8)k;
          while (l++ < 255) {
            val.segment = (uint8)l;
            if (delete(key)) break;
          }
        }
      } /* for */
    } else result=-0xff;
    dbmclose();
    if (result > 0) {
      if (!dbminit(FNPROP)){  /* now delete properties */
        int k;
        NETPROP prop;
        key.dptr   = (char*)&prop;
        key.dsize  = NETPROP_KEY_SIZE;
        prop.obj_id = obj_id;
        for (k = (prop_id) ? prop_id : 1; k <= result; k++){
          if (xset[k]){
            prop.id  = (uint8)k;
            if (delete(key)) {
              result = -0xf6;
              break;
            }
          }
        } /* for */
        if (result > 0) result=0;
      } else result=-0xff;
      dbmclose();
    }
  }
  return(result);
}

static int prop_delete_member(uint32 obj_id, int prop_id, int prop_security,
                       uint32 member_id)
{
  int result;
  NETVAL  val;
  if (0 != (result=b_acc(obj_id, prop_security, 0x11))) return(result);
  else result = 0; /* we lie insteed of -0xea;  no such member */

  if (!dbminit(FNVAL)){
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop_id;
    val.segment = (uint8)0;
    while (val.segment++ < (uint8)255) {
      data         = fetch(key);
      if (data.dptr != NULL) {
        NETVAL  *v = (NETVAL*)data.dptr;
        uint8   *p = v->value;
        int      k = 0;
        while (k++ < 32){
          uint32 id=GET_BE32(p);
          if (id == member_id) {
#if 0
            memset(p, 0, 4);
            memcpy(&val, v, sizeof(NETVAL));
#else       /* new since 0.99.pl7 */
            memcpy(&val, v,  p - (uint8*)v);
            if (k<32)
              memcpy(&val.value[(k-1)*4], p+4, (32-k) * 4);
            memset(&val.value[124], 0, 4);
#endif
            data.dptr = (char*)&val;
            if (store(key, data)) result=-0xff;
            else result=0;
            goto L1;
          }
          p += 4;
        }
      } else break;
    }
  } else result = -0xff;
L1:
  dbmclose();
  return(result);
}

#define LOC_MAX_OBJS  10000  /* should be big enough ;) */

static int loc_delete_obj(uint32 objid, int security)
/* delete's obj completely from bindery */
{
  int result = b_acc(objid, 0x33, 0x03); /* only supervisor or intern */
  if (result)
    return(result); /* no object delete priv */

  /* now we delete all properties of this object */
  (void)loc_delete_property(objid, (uint8*)"*", 0, 1);

  /* and now we delete all references of object in other set properties */
  if (!dbminit(FNPROP)){
    int     anz=0;
    uint32  objs[LOC_MAX_OBJS];
    uint8   props[LOC_MAX_OBJS];
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETPROP *prop=(NETPROP*)data.dptr;
        if (prop->flags & P_FL_SET) { /* is set property */
          objs[anz]    = prop->obj_id;
          props[anz++] = prop->id;
          if (anz == LOC_MAX_OBJS) break;
        }
      }
    }
    while (anz--) /* now try to delete obj members */
      prop_delete_member(objs[anz], props[anz], 0,  objid);
  } else
    result=-0xff;
  dbmclose();
  if (!result) {
    NETOBJ obj;
    int filled=0;
    if (!dbminit(FNOBJ)){
      key.dptr  = (char*)&objid;
      key.dsize = NETOBJ_KEY_SIZE;
      data=fetch(key);
      if (data.dptr) {
        filled++;
        memcpy(&obj, data.dptr, sizeof(NETOBJ));
      }
      if (delete(key)) result = -0xff;
    } else result = -0xff;
    dbmclose();
    if (filled)
      handle_iobj(3, &obj); /* now delete iobj */;
  }
  return(result);
}

int nw_delete_obj(NETOBJ *obj)
{
  int result = find_obj_id(obj);
  XDPRINTF((2,0, "nw_delete_obj obj_id=%d, obj_name=%s", obj->id, visable_data(obj->name, sizeof(obj->name))));
  if (!result)
    result=loc_delete_obj(obj->id, obj->security);
  return(result);
}

int nw_rename_obj(NETOBJ *o, uint8 *newname)
/* rename object */
{
  int result = find_obj_id(o);
  if (!result) {
    result = b_acc(0, 0x33, 0x04); /* only supervisor */
    if (result) return(result); /* no obj rename priv */
    else result=-0xff;
    handle_iobj(3, o); /* delete old iobj */
    if (!dbminit(FNOBJ)){
      key.dsize = NETOBJ_KEY_SIZE;
      key.dptr  = (char*)o;
      data      = fetch(key);
      if (data.dptr != NULL){
        NETOBJ *obj=(NETOBJ*)data.dptr;
        XDPRINTF((2,0, "rename_obj:got OBJ name=%s, id = 0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->id));
        memset(obj->name, 0, sizeof(obj->name));
        strncpy((char*)obj->name, (char*)newname, sizeof(obj->name)-1);
        obj->name[sizeof(obj->name)-1] = '\0';
        if (!store(key, data)) {
          memcpy(o, obj, sizeof(NETOBJ));  /* for handle_iobj */
          result=0;
        }
      }
    }
    dbmclose();
    handle_iobj(2, o); /* creat new iobj */
  }
  return(result);
}

int nw_change_obj_security(NETOBJ *o, int newsecurity)
/* change Security of Object */
{
  int result = find_obj_id(o);
  if (!result) {
    result = b_acc(o->id, o->security, 0x05);
    if (result) return(result);
    else result=-0xff;
    if (!dbminit(FNOBJ)){
      key.dsize = NETOBJ_KEY_SIZE;
      key.dptr  = (char*)o;
      data      = fetch(key);
      if (data.dptr != NULL){
        NETOBJ *obj=(NETOBJ*)data.dptr;
        XDPRINTF((2,0, "change_obj_security:got OBJ name=%s, id = 0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->id));
        obj->security = (uint8) newsecurity;
        if (!store(key, data)) result=0;
      }
    }
    dbmclose();
  }
  return(result);
}

int nw_get_obj(NETOBJ *o)
{
  int result = -0xfc; /*  no Object */
  if (!dbminit(FNOBJ)){
    key.dsize = NETOBJ_KEY_SIZE;
    key.dptr  = (char*)o;
    data      = fetch(key);
    if (data.dptr != NULL){
      NETOBJ *obj=(NETOBJ*)data.dptr;
      result = b_acc(o->id, obj->security, 0x0);
      XDPRINTF((2,0, "got OBJ name=%s, id = 0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->id));
      if (!result) memcpy(o, data.dptr, sizeof(NETOBJ));
    }
  } else result = -0xff;
  dbmclose();
  XDPRINTF((2,0, "nw_get_obj von OBJ id = 0x%x, result=0x%x",
           (int)o->id, result));
  return(result);
}

static int find_prop_id(NETPROP *p, uint32 obj_id, int last_prop_id)
{
  int result = -0xfb; /* no Property */
  XDPRINTF((2,0, "find Prop id of name=0x%x:%s, lastid=%d",
           obj_id, p->name, last_prop_id));
  if (!dbminit(FNPROP)){
    int  flag = (last_prop_id) ? 0 : 1;
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      NETPROP *prop=(NETPROP*)key.dptr;
      if (prop->obj_id == obj_id) {
        if (!flag) flag = (last_prop_id == prop->id);
        else {
          data = fetch(key);
          prop = (NETPROP*)data.dptr;
          if (data.dptr != NULL && name_match(prop->name, p->name)
            && (b_acc(obj_id, prop->security, 0x00)== 0) ) {
            XDPRINTF((2,0, "found PROP %s, id=0x%x", prop->name, (int) prop->id));
            result = 0;
            memcpy(p, prop, sizeof(NETPROP));
            break;
          }
        }
      }
    }
  } else result = -0xff;
  dbmclose();
  return(result);
}

#define find_first_prop_id(p, obj_id)  \
        find_prop_id((p), (obj_id), 0)


static int loc_change_prop_security(NETPROP *p, uint32 obj_id)
{
  int result = -0xfb; /* no Property */
  XDPRINTF((2,0, "loc_change_prop_security prop id of name=0x%x:%s", obj_id, p->name));
  if (!dbminit(FNPROP)){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      NETPROP *prop=(NETPROP*)key.dptr;
      if (prop->obj_id == obj_id) {
        data = fetch(key);
        prop = (NETPROP*)data.dptr;
        if (data.dptr != NULL  && name_match(prop->name, p->name) )  {
          uint8 security = p->security;
          XDPRINTF((2,0, "found PROP %s, id=0x%x", prop->name, (int) prop->id));
          result = b_acc(obj_id, prop->security, 0x15);
          if (!result) {
            memcpy(p, prop, sizeof(NETPROP));
            p->security = security;
            data.dptr  = (char*)p;
            data.dsize = sizeof(NETPROP);
            key.dptr  = (char *)p;
            key.dsize = NETPROP_KEY_SIZE;
            if (store(key, data)) result=-0xff;
          }
          break;
        }
      }
    }
  } else result = -0xff;
  dbmclose();
  return(result);
}

static int loc_get_prop_val(uint32 obj_id, int prop_id, int prop_security,
             int segment, uint8 *property_value, uint8 *more_segments)
{
  int result;
  NETVAL  val;

  if (0 != (result=b_acc(obj_id, prop_security, 0x10))) return(result);
  else result = -0xec; /* no such Segment */

  if (!dbminit(FNVAL)){
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop_id;
    val.segment = (uint8)segment;
    data        = fetch(key);
    if (data.dptr != NULL){
      NETVAL  *v = (NETVAL*)data.dptr;
      if (NULL != property_value) memcpy(property_value, v->value, 128);
      XDPRINTF((2,0, "found VAL 0x%x, %d, %d", obj_id, prop_id, segment));
      result = 0;
      val.segment++;
      data  = fetch(key);
      if (NULL != more_segments)
        *more_segments = (data.dptr != NULL) ? 0xff : 0;
    }
  } else result = -0xff;
  dbmclose();
  return(result);
}


static int prop_find_member(uint32 obj_id, int prop_id, int prop_security,
                                     uint32 member_id)
{
  int result;
  NETVAL  val;

  if (0 != (result=b_acc(obj_id, prop_security, 0x10))) return(result);
  else result = -0xea; /* no such member */

  if (!dbminit(FNVAL)){
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop_id;
    val.segment = (uint8)0;
    while (val.segment++ < (uint8)255) {
      data      = fetch(key);
      if (data.dptr != NULL){
        NETVAL  *v = (NETVAL*)data.dptr;
        uint8   *p=v->value;
        int     k=0;
        XDPRINTF((2,0, "found VAL 0x%x, %d segment %d", obj_id, prop_id, val.segment));
        while (k++ < 32){
          uint32 id = GET_BE32(p);
          if (id == member_id) {
            result = 0;
            break;
          } else p += 4;
        }
      }
    }
  } else result = -0xff;
  dbmclose();
  return(result);
}

static int prop_add_member(uint32 obj_id, int prop_id,  int prop_security,
                          uint32 member_id)
{
  int result;
  NETVAL  val;

  if (0 != (result=b_acc(obj_id, prop_security, 0x11))) return(result);
  else result = 0; /* OK */

  if (!dbminit(FNVAL)){
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop_id;
    val.segment = (uint8)0;
    while (!result) {
      if (val.segment++ < (uint8)255) {
        data        = fetch(key);
        if (data.dptr != NULL){
          NETVAL  *v = (NETVAL*)data.dptr;
          uint8   *p = v->value;
          int      k = 0;
          while (k++ < 32){
            uint32 null_id = 0;
            if (!memcmp(p, (char*)&null_id, 4)) {
              U32_TO_BE32(member_id, p);
              memcpy(&val, v, sizeof(NETVAL));
              data.dptr = (char*)&val;
              if (store(key, data)) result=-0xff;
              goto L1;
            } else p += 4;
          }
        } else {
          memset(val.value, 0, 128);
          U32_TO_BE32(member_id, val.value);
          data.dptr  = (char*)&val;
          data.dsize = sizeof(NETVAL);
          if (store(key, data)) result=-0xff;
          goto L1;
        }
      } else
        /* no more free cells, perhaps we need better result code */
        result=-0xff;
    } /* while */
  } else result = -0xff;
L1:
  dbmclose();
  return(result);
}


static int ins_prop_val(uint32 obj_id, NETPROP *prop, int segment,
                 uint8 *property_value, int erase_segments)
{
  int result = b_acc(obj_id, prop->security, 0x11);
  if (result) return(result);
  if (!dbminit(FNVAL)){
    NETVAL  val;
    int flag    = 1;
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop->id;
    result = -0xec; /* no such Segment */
    if (segment > 1) {
      val.segment = segment-1;
      data        = fetch(key);
      if (data.dptr == NULL) flag = 0;
    }
    if (flag){
      val.segment = segment;
      memcpy(val.value, property_value, 128);
      data.dsize = sizeof(NETVAL);
      data.dptr  = (char*)&val;
      if (!store(key, data)) {
        result = 0;
        if (erase_segments == 0xff){
          while (val.segment++ < (uint8)255 && !delete(key));
        }
      }
    }
  } else result = -0xff;
  dbmclose();
  return(result);
}

int nw_get_prop_val_by_obj_id(uint32 obj_id,
                              int segment_nr,
                              uint8 *prop_name, int prop_namlen,
                              uint8 *property_value,
                              uint8 *more_segments,
                              uint8 *property_flags)
{
  NETPROP prop;
  int result=-0xff;
  strmaxcpy(prop.name, prop_name, prop_namlen);
  XDPRINTF((5,0, "nw_get_prop_val_by_obj_id,id=0x%x, prop=%s, segment=%d",
            obj_id, prop.name, segment_nr));

  if ((result=find_first_prop_id(&prop, obj_id))==0){
    if ((result=loc_get_prop_val(obj_id, prop.id, prop.security, segment_nr,
                  property_value, more_segments)) == 0){
      *property_flags = prop.flags;
    }
  }
  return(result);
}

int nw_get_prop_val(int object_type,
                uint8 *object_name, int object_namlen,
                int   segment_nr,
                uint8 *prop_name, int prop_namlen,
                uint8 *property_value,
                uint8 *more_segments,
                uint8 *property_flags)
{
  NETOBJ obj;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  obj.type    = (uint16) object_type;
  if ((result = find_obj_id(&obj)) == 0){
    result = nw_get_prop_val_by_obj_id(obj.id,
                              segment_nr,
                              prop_name, prop_namlen,
                              property_value,
                              more_segments,
                              property_flags);
  }
  return(result);
}

int nw_delete_property(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name,   int prop_namlen)
{
  NETOBJ  obj;
  uint8   prop_name_x[20];
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)prop_name_x, (char*)prop_name, prop_namlen);
  XDPRINTF((2,0, "nw_delete_property obj=%s, prop=%s, type=0x%x",
      visable_data(obj.name, sizeof(obj.name)), prop_name_x, object_type));
  obj.type    = (uint16) object_type;
  if ((result = find_obj_id(&obj)) == 0){
    result = loc_delete_property(obj.id, prop_name_x, 0, 0);
  }
  return(result);
}

int nw_is_member_in_set(uint32 obj_id, char *propname, uint32 member_id)
{
  NETPROP prop;
  int result;
  strmaxcpy(prop.name, propname, sizeof(prop.name));
  result=find_first_prop_id(&prop, obj_id);
  if (!result)
    result = prop_find_member(obj_id, (int)prop.id, prop.security, member_id);
  return(result);
}

int nw_is_obj_in_set(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                int   member_type,
                uint8 *member_name, int member_namlen)
{
  NETOBJ  obj;
  NETOBJ  mobj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)mobj.name,  (char*)member_name, member_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  XDPRINTF((2,0, "nw_is_obj_in_set obj=%s,0x%x, member=%s,0x%x, prop=%s",
      visable_data(obj.name, sizeof(obj.name)), object_type,
      visable_data(mobj.name, sizeof(mobj.name)), member_type, prop.name));
  obj.type    = (uint16) object_type;
  mobj.type   = (uint16) member_type;
  if ((result = find_obj_id(&obj)) == 0){
    result=find_first_prop_id(&prop, obj.id);
    if (!result)
      result = find_obj_id(&mobj);
    if (!result)
      result = prop_find_member(obj.id, (int)prop.id, prop.security,  mobj.id);
  }
  return(result);
}

int nw_add_obj_to_set(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                int   member_type,
                uint8 *member_name, int member_namlen)
{
  NETOBJ  obj;
  NETOBJ  mobj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)mobj.name,  (char*)member_name, member_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  XDPRINTF((2,0, "nw_add_obj_to_set obj=%s,0x%x, member=%s,0x%x, prop=%s",
      visable_data(obj.name, sizeof(obj.name)), object_type,
      visable_data(mobj.name, sizeof(mobj.name)), member_type, prop.name));
  obj.type    = (uint16) object_type;
  mobj.type   = (uint16) member_type;
  if ((result = find_obj_id(&obj)) == 0){
    result=find_first_prop_id(&prop, obj.id);
    if (!result)
      result = find_obj_id(&mobj);
    if (!result) {
      if (-0xea == (result=prop_find_member(obj.id, prop.id, prop.security,  mobj.id)))
        result = prop_add_member(obj.id, (int)prop.id, prop.security,  mobj.id);
      else if (!result)
        result=-0xe9; /* property already exist */
    }
  }
  return(result);
}

int nw_delete_obj_from_set(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                int   member_type,
                uint8 *member_name, int member_namlen)
{
  NETOBJ  obj;
  NETOBJ  mobj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)mobj.name,  (char*)member_name, member_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  XDPRINTF((2,0, "nw_delete_obj_from_set obj=%s,0x%x, member=%s,0x%x, prop=%s",
      visable_data(obj.name, sizeof(obj.name)), object_type,
      visable_data(mobj.name, sizeof(mobj.name)), member_type, prop.name));
  obj.type    = (uint16) object_type;
  mobj.type   = (uint16) member_type;
  if ((result = find_obj_id(&obj)) == 0){
    result=find_first_prop_id(&prop, obj.id);
    if (!result)
      result = find_obj_id(&mobj);
    if (!result)
      result = prop_delete_member(obj.id, (int)prop.id, prop.security, mobj.id);
  }
  return(result);
}


static void copy_bindery_packet_name(uint8 *dst, int dst_size,
                                    uint8 *src, int src_len)
{
  int len = min(src_len, dst_size - 1);
  if (len > 0) memcpy(dst, src, len);
  dst[len] = '\0';
}

static int bindery_packet_name_has_wildcards(uint8 *name, int namlen)
{
  while (namlen-- > 0) {
    if (*name == '*' || *name == '?') return(1);
    ++name;
  }
  return(0);
}

static int prop_list_members(uint32 obj_id, int prop_id, int prop_security,
                             uint32 last_seen,
                             uint32 *relations, int max_relations)
{
  NETVAL val;
  int result;
  int count = 0;
  int collect = (last_seen == (uint32)0xffffffff);

  if (0 != (result=b_acc(obj_id, prop_security, 0x10))) return(result);

  if (!dbminit(FNVAL)){
    key.dsize   = NETVAL_KEY_SIZE;
    key.dptr    = (char*)&val;
    val.obj_id  = obj_id;
    val.prop_id = (uint8)prop_id;
    val.segment = (uint8)0;

    while (val.segment++ < (uint8)255 && count < max_relations) {
      data = fetch(key);
      if (data.dptr != NULL){
        NETVAL *v = (NETVAL*)data.dptr;
        uint8  *p = v->value;
        int     k = 0;

        while (k++ < 32 && count < max_relations) {
          uint32 id = GET_BE32(p);

          if (id != 0) {
            if (collect) {
              relations[count++] = id;
            } else if (id == last_seen) {
              collect = 1;
            }
          }
          p += 4;
        }
      }
    }
    result = count;
  } else result = -0xff;

  dbmclose();
  return(result);
}


int nw_list_obj_relations(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                uint32 last_seen,
                uint32 *relations, int max_relations)
{
  NETOBJ  obj;
  NETPROP prop;
  int result=-0xff;

  memset(&obj, 0, sizeof(obj));
  memset(&prop, 0, sizeof(prop));

  if (bindery_packet_name_has_wildcards(object_name, object_namlen))
    return(-0xf0);

  copy_bindery_packet_name(obj.name, sizeof(obj.name),
                           object_name, object_namlen);
  copy_bindery_packet_name(prop.name, sizeof(prop.name),
                           prop_name, prop_namlen);
  obj.type = (uint16)object_type;

  XDPRINTF((2,0, "nw_list_obj_relations obj=%s,0x%x, prop=%s, last=0x%x",
      visable_data(obj.name, sizeof(obj.name)), object_type, prop.name, last_seen));

  if ((result = find_obj_id(&obj)) == 0) {
    result = find_first_prop_id(&prop, obj.id);
    if (!result) {
      if (!(prop.flags & P_FL_SET))
        result = -0xfb;
      else
        result = prop_list_members(obj.id, (int)prop.id, prop.security,
                                   last_seen, relations, max_relations);
    }
  }
  return(result);
}


int nw_write_prop_value(int object_type,
                uint8 *object_name, int object_namlen,
                int   segment_nr, int erase_segments,
                uint8 *prop_name, int prop_namlen,
                uint8 *property_value)
{
  NETOBJ  obj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  XDPRINTF((2,0, "nw_write_prop_value obj=%s, prop=%s, type=0x%x, segment=%d",
      visable_data(obj.name, sizeof(obj.name)), prop.name, object_type, segment_nr));
  obj.type    = (uint16) object_type;

  if ((result = find_obj_id(&obj)) == 0){
    if ((result=find_first_prop_id(&prop, obj.id))==0){
       result=ins_prop_val(obj.id, &prop, segment_nr,
            property_value, erase_segments);

    }
  }
  return(result);
}


int nw_change_prop_security(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                int   prop_security)
{
  NETOBJ  obj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  prop.security = (uint8)prop_security;
  XDPRINTF((2,0, "nw_change_prop_security obj=%s,0x%x, prop=%s",
      visable_data(obj.name, sizeof(obj.name)), object_type, prop.name));
  obj.type    = (uint16) object_type;
  if ((result = find_obj_id(&obj)) == 0)
    return(loc_change_prop_security(&prop, obj.id));
  return(-0xff);
}

int nw_scan_property(NETPROP *prop,
                     int      object_type,
                     uint8    *object_name,
                     int      object_namlen,
                     uint8    *prop_name,
                     int      prop_namlen,
                     uint32   *last_scan)
{
  NETOBJ  obj;
  int     result;
  strmaxcpy((char*)obj.name,   (char*)object_name, object_namlen);
  strmaxcpy((char*)prop->name, (char*)prop_name,   prop_namlen);
  XDPRINTF((2,0, "nw_scan_property obj=%s, prop=%s, type=0x%x, last_scan=0x%x",
      visable_data(obj.name, sizeof(obj.name)), prop->name, object_type, (int)*last_scan));
  obj.type    = (uint16) object_type;

  if ((result = find_obj_id(&obj)) == 0){
    int last_prop_id;
    if (*last_scan == MAX_U32) *last_scan = 0;
    last_prop_id =  *last_scan;
    if ((result=find_prop_id(prop, obj.id, last_prop_id))==0){
      *last_scan = prop->id;
      if (!loc_get_prop_val(obj.id, prop->id, prop->security,  1,
                           NULL, NULL))
        result = 0xff; /* Has prop Values */
    }
  }
  return(result);
}

int nw_get_prop_val_str(uint32 q_id, char *propname, uint8 *buff)
/* for simple prop val strings */
{
  uint8  more_segments;
  uint8  property_flags;
  uint8  loc_buff[200];
  int    result;
  if (NULL == buff) buff=loc_buff;
  result=nw_get_prop_val_by_obj_id(q_id, 1, propname, strlen(propname),
                  buff, &more_segments, &property_flags);

  if (result > -1) {
    result=strlen(buff);
    XDPRINTF((5,0, "nw_get_prop_val_str:%s strlen=%d", propname, result));
  } else {
    XDPRINTF((5,0, "nw_get_prop_val_str:%s, result=-0x%x", propname, -result));
  }
  return(result);
}

int nw_create_obj(NETOBJ *obj, uint32 wanted_id)
/*
 * Routine creates object
 * wants OBJ name and OBJ-Type,  returns  obj.id.
 * if wanted_id > 0 and object don't exist then
 * wanted_id should be taken as obj_id.
*/
{
  int result = b_acc(0, 0x33, 0x02);
  XDPRINTF((2,0, "creat OBJ=%s,type=0x%x", visable_data(obj->name, sizeof(obj->name)), (int)obj->type));
  if (result) return(result); /* no object creat rights */
  if (!dbminit(FNOBJ)){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr){
        NETOBJ *o=(NETOBJ*)data.dptr;
        if (o->type == obj->type && !strcmp(o->name, obj->name)){
          obj->id =  o->id; /* fill ID       */
          result  = -0xee;  /* already exist */
          break;
        }
      }
    }
    if (!result){
      obj->id    = (wanted_id) ? wanted_id -1 : (obj->type << 16) + 1;
         /* 1 is reserved for supervisor !!!! */
      key.dsize  = NETOBJ_KEY_SIZE;
      key.dptr   = (char*)obj;
      while(1) {
        obj->id++;
        data = fetch(key);
        if (data.dptr == NULL) break;
      }
      data.dsize = sizeof(NETOBJ);
      data.dptr = (char*)obj;
      if (store(key, data)) result = -0xff;
    }
  } else result = -0xff;
  dbmclose();
  if (!result)
    handle_iobj(2, obj);
  return(result);
}

int nw_obj_has_prop(NETOBJ *obj)
{
  int result = (dbminit(FNPROP)) ? -0xff : 0;
  if (!result){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      NETPROP *p=(NETPROP*)key.dptr;
      if (p->obj_id == obj->id) {
        result = 1;
        break;
      }
    }
  }
  dbmclose();
  return(result);
}

static int nw_create_obj_prop(uint32 obj_id, NETPROP *prop)
{
  int result=0;
  if (!dbminit(FNPROP)){
    uint8   founds[256];
    memset((char*)founds, 0, sizeof(founds) );
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      NETPROP *p=(NETPROP*)key.dptr;
      if (p->obj_id == obj_id) {
        data = fetch(key);
        p  = (NETPROP*)data.dptr;
        if (data.dptr != NULL  && !strcmp(prop->name, p->name)){
          prop->id     = p->id;
          prop->obj_id = obj_id;
          result       = -0xed;  /* Property exists */
          if (p->security != prop->security ||
              p->flags    != prop->flags) {
            /* added 16-Aug-97 0.99.pl1 */
            XDPRINTF((1, 0, "prop '%s' got new security/flag=0x%x / %d",
               p->name, p->security, p->flags));
            p->security=prop->security;
            p->flags=prop->flags;
            key.dsize     = NETPROP_KEY_SIZE;
            key.dptr      = (char *)p;
            data.dsize    = sizeof(NETPROP);
            data.dptr     = (char *)p;
            if (store(key, data)) result = -0xff;
          }
          break;
        } else founds[p->id]++;
      }
    }
    if (!result){
      int k = 0;
      while (++k < sizeof(founds) ) {
        if (!founds[k]) break;     /* free slot */
      }
      key.dsize     = NETPROP_KEY_SIZE;
      key.dptr      = (char *)prop;
      data.dsize    = sizeof(NETPROP);
      data.dptr     = (char *)prop;
      prop->obj_id   = obj_id;
      prop->id       = (uint8)k;
      if (store(key, data)) result = -0xff;
    }
  } else result = -0xff;
  dbmclose();
  XDPRINTF((3, 0, "create property='%s' objid=0x%x, result=0x%x",
          prop->name, obj_id, result));
  return(result);
}

int nw_create_prop(int object_type,
                uint8 *object_name, int object_namlen,
                uint8 *prop_name, int prop_namlen,
                int prop_flags, int prop_security)
/* creats property for an object */
{
  NETOBJ  obj;
  NETPROP prop;
  int result=-0xff;
  strmaxcpy((char*)obj.name,  (char*)object_name, object_namlen);
  strmaxcpy((char*)prop.name, (char*)prop_name,   prop_namlen);
  obj.type    = (uint16) object_type;
  if (   0 == (result = find_obj_id(&obj))
     &&  0 == (result = b_acc(obj.id, obj.security, 0x12)) ) {
    prop.flags    = (uint8)prop_flags;
    prop.security = (uint8)prop_security;
    result        = nw_create_obj_prop(obj.id, &prop);
  }
  XDPRINTF((2,0, "nw_create_prop obj=%s, prop=%s, type=0x%x, result=0x%x",
      visable_data(obj.name, sizeof(obj.name)), prop.name, object_type, result));
  return(result);
}

static int nw_new_obj(uint32 *wanted_id,
                      char *objname, int objtype,
                      int objflags, int objsecurity)
{
  NETOBJ obj;
  int result;
  xstrcpy(obj.name, objname);
  obj.type      =  (uint16) objtype;
  obj.flags     =  (uint8)  objflags;
  obj.security  =  (uint8)  objsecurity;
  obj.id        =  0L;
  result = nw_create_obj(&obj, *wanted_id);
  *wanted_id = obj.id;
  return(result);
}

uint32 nw_new_obj_prop(uint32 wanted_id,
                  char *objname, int objtype, int objflags, int objsecurity,
                  char *propname, int propflags, int propsecurity,
                  char *value, int valuesize, int ever)
/*
 * creats new property value, if needed creats Object
 * and the property,
 * if propname == NULL only object will be created.
 * if valuesize == 0, then only obj or property
 * will be created, returns obj->id
 */
{
  NETOBJ  obj;
  NETPROP prop;
  if (objname && *objname)
    nw_new_obj(&wanted_id, objname, objtype,
                         objflags, objsecurity);
  obj.id       = wanted_id;
  obj.security = objsecurity;
  if (propname && *propname && !b_acc(obj.id, obj.security, 0x12)) {
    int result;
    strmaxcpy(prop.name, propname, sizeof(prop.name));
    prop.flags    =   (uint8)propflags;
    prop.security =   (uint8)propsecurity;
    result=nw_create_obj_prop(obj.id, &prop);
    if (valuesize && (!result || (result == -0xed && ever)) ){
      uint8  locvalue[128];
      memset(locvalue, 0, sizeof(locvalue));
      memcpy(locvalue, value, min(sizeof(locvalue), valuesize));
      ins_prop_val(obj.id, &prop, 1, locvalue, 0xff);
    }
  }
  return(obj.id);
}

/* some property names */
/*STANDARD NOVELL properties */
static uint8 *pn_password=(uint8*)        "PASSWORD";
static uint8 *pn_login_control=(uint8*)   "LOGIN_CONTROL";
static uint8 *pn_security_equals=(uint8*) "SECURITY_EQUALS";
static uint8 *pn_groups_i_m_in=(uint8*)   "GROUPS_I'M_IN";
static uint8 *pn_group_members=(uint8*)   "GROUP_MEMBERS";


/* OWN properties */
static uint8 *pn_unix_user=(uint8*)     "UNIX_USER";
static uint8 *pn_special_flags=(uint8*)"SP_SU_FLAGS"; /* flag */

typedef struct {
  int    pw_uid;
  int    pw_gid;
  char   pw_passwd[80];
  uint8  pw_dir[257];
  uint8  pw_name[20];
} MYPASSWD;


static MYPASSWD *nw_getpwnam(uint32 obj_id)
{
  static MYPASSWD pwstat;
  char buff[200];
  if (nw_get_prop_val_str(obj_id, pn_unix_user, buff) > 0){
    struct passwd *pw;
    endpwent(); /* tnx to Leslie */
    pw = getpwnam(buff);
    if (NULL != pw) {
      if (obj_id != 1 && !pw->pw_uid)
        return(NULL);  /* only supervisor -> root */
      pwstat.pw_uid = pw->pw_uid;
      pwstat.pw_gid = pw->pw_gid;
      xstrcpy(pwstat.pw_passwd, pw->pw_passwd);
      xstrcpy(pwstat.pw_name,   pw->pw_name);
      xstrcpy(pwstat.pw_dir,    pw->pw_dir);
#if SHADOW_PWD
      if (pwstat.pw_passwd[0] == 'x' && pwstat.pw_passwd[1]=='\0') {
        struct spwd *spw;
        endspent(); /* tnx to Leslie */
        spw=getspnam(buff);
        if (spw) xstrcpy(pwstat.pw_passwd, spw->sp_pwdp);
      }
#endif
      XDPRINTF((2,0, "FOUND obj_id=0x%x, pwnam=%s, gid=%d, uid=%d",
                 obj_id, buff, pw->pw_gid, pw->pw_uid));
      return(&pwstat);
    }
  }
  XDPRINTF((2,0, "NOT FOUND PWNAM of obj_id=0x%x",  obj_id));
  return(NULL);
}

int nw_is_security_equal(uint32 id1,  uint32 id2)
/* returns 0 if id2 has same security as id1 */
{
  return(nw_is_member_in_set(id2, pn_security_equals, id1));
}

int get_groups_i_m_in(uint32 id, uint32 *gids)
/* returns max. 32 groups */
{
  uint8 buff[128];
  uint8 more_segments=0;
  uint8 property_flags;
  int   result=nw_get_prop_val_by_obj_id(id, 1,
                  pn_groups_i_m_in, strlen(pn_groups_i_m_in),
                  buff, &more_segments, &property_flags);
  if (!result) {
    uint8   *p=buff;
    int     k=-1;
    while (++k < 32) {
      *gids=GET_BE32(p);
      p+=4;
      if (*gids) {
        gids++;
        result++;
      }
    }
  } else result=0;
  return(result);
}

int get_guid(int *gid, int *uid, uint32 obj_id, uint8 *name)
/* searched for gid und uid of actual obj */
{
  MYPASSWD *pw = nw_getpwnam(obj_id);
  if (NULL != pw) {
    *gid    = pw->pw_gid;
    *uid    = pw->pw_uid;
    if (name) strmaxcpy(name, pw->pw_name, 20);
    return(0);
  } else {
    *gid = -1;
    *uid = -1;
    if (name) strcpy(name, "UNKNOWN");
    return(-0xff);
  }
}

static int crypt_pw_ok(uint8 *password, char *passwd)
/* returns 0 if not ok */
{
  char pnul[2] = {'\0', '\0'};
  char *pp    = (password) ? (char*)password : pnul;
  char *p     =  crypt(pp, passwd);
  return( (strcmp(p, passwd)) ? 0 : 1 );
}

static int loc_nw_test_passwd(uint8 *keybuff, uint8 *stored_passwd,
                              uint32 obj_id, uint8 *vgl_key, uint8 *akt_key)
{
  if (nw_get_prop_val_str(obj_id, pn_password, stored_passwd) > 0) {
    nw_encrypt(vgl_key, stored_passwd, keybuff);
    return (memcmp(akt_key, keybuff, 8) ? -0xff : 0);
  } else { /* now we build an empty password */
    uint8 buf[8];
    uint8 s_uid[4];
    U32_TO_BE32(obj_id, s_uid);
    shuffle(s_uid, buf, 0, stored_passwd);
    nw_encrypt(vgl_key, stored_passwd, keybuff);
    return(1);
  }
}


int nw_test_passwd(uint32 obj_id, uint8 *vgl_key, uint8 *akt_key)
/* returns 0, if password ok and -0xff if not ok */
{
  uint8 keybuff[8];
  uint8 stored_passwd[200];
  int result=loc_nw_test_passwd(keybuff, stored_passwd,
                                obj_id, vgl_key, akt_key);
  if (result < 1) return(result);

  if (obj_id == 1) return(-0xff);
  /* the real SUPERVISOR must use netware passwords */

  if (password_scheme & PW_SCHEME_LOGIN) {
    if (!(password_scheme & PW_SCHEME_ALLOW_EMPTY_PW)) {
      MYPASSWD *pw = nw_getpwnam(obj_id);
      if (pw && *(pw->pw_passwd) && !crypt_pw_ok(NULL, pw->pw_passwd))
        return(-0xff);
    }
  }
  return(0); /* no password */
}

int nw_test_unenpasswd(uint32 obj_id, uint8 *password)
{
  uint8 passwordu[100];
  uint8 passwd[200];
  uint8 stored_passwd[200];
  MYPASSWD *pw;
  if (password && *password
     && nw_get_prop_val_str(obj_id, pn_password, stored_passwd) > 0 ) {
    uint8 s_uid[4];
    U32_TO_BE32(obj_id, s_uid);
    xstrcpy(passwordu, password);
    upstr(passwordu);
    shuffle(s_uid, passwordu, strlen(passwordu), passwd);
    memset(passwordu, 0, 100);
    if (!memcmp(passwd, stored_passwd, 16)) return(0);
  }
  if (NULL != (pw = nw_getpwnam(obj_id))) {
    int pwok = crypt_pw_ok(password, pw->pw_passwd);
    if (!pwok) {
      xstrcpy(passwordu, password);
      downstr(passwordu);
      pwok = crypt_pw_ok(passwordu, pw->pw_passwd);
      memset(passwordu, 0, 100);
    }
    return((pwok) ? 0 : -0xff);
  } else return(-0xff);
}

/* added Routines from Paolo Prandini: mst:25-Apr-00 */
static int nw_update_oldpasswd(uint32 obj_id, char *pwd)
{
  char  *pn_control="OLD_PASSWORDS"; 
  uint8 more_segments;
  uint8 property_flags;
  int   segment = 1;
  int   result;
  uint8 segm[128];

  // Read OLD_PASSWORDS bindery object
  result=nw_get_prop_val_by_obj_id(obj_id, segment,
           pn_control, strlen(pn_control),
           segm, &more_segments, &property_flags);
  if (result < 0) {
     // Here if OLD_PASSWORDS object not found
     return 0;
  } 

  // Check if password already present in data object
  for (segment=0;segment<8;segment++) {
    if (memcmp(pwd,segm+segment*16,16)==0)return 1;
  }
  // Update record
  memmove(segm+16, segm, 128-16);
  memcpy(segm,pwd,16);

  nw_new_obj_prop(obj_id, NULL, 0, 0, 0,
                  pn_control, P_FL_STAT|P_FL_ITEM,  0x32,
                  segm, 128, 1);
  return(0);
}

int nw_set_login_control(uint32 obj_id, LOGIN_CONTROL *plc)
{
  char *pn_login_control="LOGIN_CONTROL"; 
  nw_new_obj_prop(obj_id, NULL, 0, 0, 0,
      pn_login_control, P_FL_STAT|P_FL_ITEM,  0x32,
      (uint8 *)plc, sizeof(LOGIN_CONTROL), 1);
  return(0);
}

int nw_get_login_control(uint32 obj_id, LOGIN_CONTROL *plc)
{
  uint8   more_segments;
  uint8   property_flags;
  int     segment = 1;
  char    *pn_login_control="LOGIN_CONTROL"; 
  int     result;
  uint8   segm[128];

  // Read LOGIN_CONTROL bindery object
  result=nw_get_prop_val_by_obj_id(obj_id, segment,
             pn_login_control, strlen(pn_login_control),
             segm, &more_segments, &property_flags);
  
  if (result < 0) {
    // Here if LOGIN_CONTROL object not found 
    char *pn_user_defaults="USER_DEFAULTS";
    USER_DEFAULTS ud;
    XDPRINTF((5, 0, "Reading USER_DEFAULTS for user %x",obj_id));
    // Try reading USER_DEFAULTS object
    segment=1;
    result=nw_get_prop_val_by_obj_id(1, segment,
               pn_user_defaults, strlen(pn_user_defaults),
               segm, &more_segments, &property_flags);
    if (result < 0) return -1;
    XDPRINTF((5, 0, "Read USER_DEFAULTS"));
    memcpy(&ud,segm,sizeof(ud));
    // Ok, now fill LOGIN_CONTROL with data from USER_DEFAULTS;
    memset(plc,0,sizeof(LOGIN_CONTROL));
    plc->accountExpiresYear  = ud.accountExpiresYear;
    plc->accountExpiresMonth = ud.accountExpiresMonth;
    plc->accountExpiresDay   = ud.accountExpiresDay;
    plc->restrictionFlags    = ud.restrictionFlags;
    memcpy(plc->expirationInterval, ud.expirationInterval, 
           sizeof(plc->expirationInterval));
    plc->graceReset          = ud.graceReset;
    plc->minimumPasswordLength = ud.minimumPasswordLength;
    memcpy(plc->maxConcurrentConnections, ud.maxConcurrentConnections,
           sizeof(plc->maxConcurrentConnections));
    memcpy(plc->timeBitMap, ud.timeBitMap, sizeof(ud.timeBitMap));
    memcpy(plc->maxDiskBlocks, ud.maxDiskBlocks, sizeof(plc->maxDiskBlocks));
  } else 
    memcpy(plc,segm,sizeof(LOGIN_CONTROL));

  return 0;
}

// Check if the new crypted password is acceptable or not
static int nw_valid_keynewpasswd(uint32 obj_id, int pwdlen, char *newpasswd)
{
  LOGIN_CONTROL lc;
  int result;

  XDPRINTF((5, 0, "Change password for user %x with crypted pwd",obj_id));

  result = nw_get_login_control(obj_id, &lc);
  if (result < 0) return(0); /* No restrictions available */

  XDPRINTF((2, 0, "CHPWD:len(pwd)=%d limit=%d", pwdlen, lc.minimumPasswordLength));

  if (pwdlen < lc.minimumPasswordLength)
    return (-0xd8); // PASSWORD TOO SHORT
  return 0;
}

static int nw_set_enpasswd(uint32 obj_id, uint8 *passwd, int dont_ch)
{
  uint8 *prop_name=pn_password;
  LOGIN_CONTROL lc;
  int result;

  if (passwd && *passwd) {
    if ((!dont_ch) || (nw_get_prop_val_str(obj_id, prop_name, NULL) < 1)) {
      result=nw_update_oldpasswd(obj_id,passwd);
      if (nw_get_login_control(obj_id,&lc)==0) {
        if (lc.restrictionFlags&2) {
          if (result) return -0xd7;
        }
      }
      nw_new_obj_prop(obj_id, NULL, 0, 0, 0,
                       prop_name, P_FL_STAT|P_FL_ITEM,  0x44,
                       passwd, 16, 1);
    }
  } else if (!dont_ch)
    (void)loc_delete_property(obj_id, prop_name, 0, 1);
  
  // Here we must update LOGIN_CONTROL properties
  if (nw_get_login_control(obj_id,&lc)==0) {
    struct tm exptm,*tm;
    time_t t,expiry;

    if (lc.passwordExpiresYear) {
      exptm.tm_year = lc.passwordExpiresYear;
      exptm.tm_mon  = lc.passwordExpiresMonth-1;
      exptm.tm_mday = lc.passwordExpiresDay;
      exptm.tm_hour = 23;
      exptm.tm_min = 59;
      exptm.tm_sec = 59;
      expiry = mktime(&exptm);
    } else expiry = 0;

    if (expiry>0) { /* if expiry is enabled */
      if (lc.passwordGraceLogins!=255)
              lc.passwordGraceLogins=lc.graceReset;

      // Now renew expiration date
      time(&t);
      t += ( GET_BE16(lc.expirationInterval) *24L*3600L );
      tm = localtime(&t);

      lc.passwordExpiresYear = tm->tm_year;
      lc.passwordExpiresMonth = tm->tm_mon+1;
      lc.passwordExpiresDay = tm->tm_mday;

      nw_set_login_control(obj_id,&lc);
    }
  }
  return(0);
}

int nw_set_passwd(uint32 obj_id, char *password, int dont_ch)
{
  if (password && *password) {
    uint8 passwd[200];
    uint8 s_uid[4];
    U32_TO_BE32(obj_id, s_uid);
    shuffle(s_uid, password, strlen(password), passwd);
    return(nw_set_enpasswd(obj_id, passwd, dont_ch));
  } else
    return(nw_set_enpasswd(obj_id, NULL, dont_ch));
}


/* main work from Guntram Blohm
 * no chance for unix password support here - can't get real password
 * from ncp request
 */
int nw_keychange_passwd(uint32 obj_id, uint8 *cryptkey, uint8 *oldpass,
                         int cryptedlen, uint8 *newpass, int id_flags)
/* returns 1 if new password is zero */
{
  uint8 storedpass[200];
  uint8 keybuff[8];
  char  buf[100];
  uint8 s_uid[4];

  int   len;
  int   result = loc_nw_test_passwd(keybuff, storedpass,
                                  obj_id, cryptkey, oldpass);

  XDPRINTF((5, 0, "Crypted change password: id=0x%x, oldpresult=0x%x",
                    (int)obj_id, result));

  len=(cryptedlen ^ storedpass[0] ^ storedpass[1])&0x3f;
  XDPRINTF((5, 0, "real len of new pass = %d", len));

  XDPRINTF((5, 0, "stored:  %s", hex_str(buf, storedpass,  16)));
  XDPRINTF((5, 0, "crypted: %s", hex_str(buf, keybuff,      8)));
  XDPRINTF((5, 0, "ncp old: %s", hex_str(buf, oldpass,      8)));

  if (result < 0) {     /* wrong passwd */
    if (id_flags&1) {  /* supervisor (equivalence) is changing passwd   */
      U32_TO_BE32(obj_id, s_uid);
      shuffle(s_uid, buf, 0, storedpass);
      nw_encrypt(cryptkey, storedpass, keybuff);
      len=(cryptedlen ^ storedpass[0] ^ storedpass[1])&0x3f;
      XDPRINTF((5, 0, "N real len of new pass = %d", len));
      XDPRINTF((5, 0, "N stored:  %s", hex_str(buf, storedpass,  16)));
      XDPRINTF((5, 0, "N crypted: %s", hex_str(buf, keybuff,  8)));
      if (memcmp(oldpass, keybuff, 8))
         return(-0xff);  /* if not BLANK then error */
    } else return(-0xff);
  }
  result=nw_valid_keynewpasswd(obj_id, len, storedpass);
  if (result<0) return result;

  XDPRINTF((5, 0, "ncp new: %s", hex_str(buf,newpass,     16)));
  nw_decrypt_newpass(storedpass,   newpass,   newpass);
  nw_decrypt_newpass(storedpass+8, newpass+8, newpass+8);
  XDPRINTF((5, 0, "realnew: %s", hex_str(buf,newpass,     16)));
  result=nw_set_enpasswd(obj_id, newpass, 0);
  if (result<0) return result;
  /* testing for zero password */
  U32_TO_BE32(obj_id, s_uid);
  shuffle(s_uid, buf, 0, storedpass);
  return(memcmp(newpass, storedpass, 16) ? 0 : 1);
}

static int nw_new_add_prop_member(uint32 obj_id, char *propname,
                                  int propflags, int propsecurity,
                                  uint32 member_id)
/* add member to set, if member not in set */
{
  NETPROP prop;
  int result;
  strmaxcpy(prop.name, propname, sizeof(prop.name));
  prop.flags    = (uint8) (propflags | P_FL_SET); /* always SET */
  prop.security = (uint8) propsecurity;
  result = nw_create_obj_prop(obj_id, &prop);

  if (!result || result == -0xed) {  /* created or exists */
    if (-0xea == (result=prop_find_member(obj_id, prop.id, prop.security,  member_id)))
      return(prop_add_member(obj_id, prop.id, prop.security,  member_id));
    else if (!result) result = -0xee;  /* already exist */
  }

  return(result);
}


int nwdbm_mkdir(char *unixname, int mode, int flags)
/* flags & 1 = set x permiss flag in upper dirs */
{
  char *p=unixname;
  while (NULL != (p=strchr(p+1, '/'))) {
    *p = '\0';
    if (!mkdir(unixname, mode))
       chmod(unixname, mode);
    else if (flags&1){
      struct stat stb;
      if (!stat(unixname, &stb))
        chmod(unixname, stb.st_mode|0111);
    }
    *p='/';
  }
  if (!mkdir(unixname, mode)) {
    chmod(unixname, mode);
    return(0);
  }
  return(-1);
}

int nwdbm_rmdir(char *path)
/* removes full directory, without subdirs */
{
  DIR   *f=opendir(path);
  if (f) {
    int pathlen=strlen(path);
    char *gpath=xmalloc(pathlen+300);
    char *p=gpath+pathlen;
    struct dirent* dirbuff;
    memcpy(gpath, path, pathlen);
    *p++ = '/';
    while ((dirbuff = readdir(f)) != (struct dirent*)NULL){
      if (dirbuff->d_ino) {
        strmaxcpy(p, dirbuff->d_name, 255);
        unlink(gpath);
      }
    }
    xfree(gpath);
    closedir(f);
    return(rmdir(path));
  }
  return(-1);
}

static void create_nw_db(char *fn, int always)
{
  char   fname[300];
  struct stat stbuff;
  (void)get_div_pathes(fname, fn, 1, ".dir");
  if (stat(fname, &stbuff)){
    (void)get_div_pathes(fname, NULL, 1, NULL);
    nwdbm_mkdir(fname, 0700, 0);
    (void)get_div_pathes(fname, fn, 1, ".dir");
  }
  if (always || stat(fname, &stbuff)){
    int fd;
    if (always) { /* we save old dbm */
      char pa[300];
      char fna[300];
      (void)get_div_pathes(pa, "nwdbm.sav", 1, NULL);
      nwdbm_mkdir(pa, 0700, 0);
      snprintf(fna, sizeof(fna), "%s/%s.dir", pa, fn);
      unlink(fna);
      rename(fname, fna);
      snprintf(fna, sizeof(fna), "%s/%s.pag", pa, fn);
      unlink(fna);
      (void)get_div_pathes(pa, fn, 1, ".pag");
      rename(pa, fna);
    }
    fd = open(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
    if (fd > -1) close(fd);
  }
  chmod(fname, 0600);
  (void)get_div_pathes(fname, fn, 1, ".pag");
  if (always || stat(fname, &stbuff)){
    int fd = open(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
    if (fd > -1) close(fd);
  }
  chmod(fname, 0600);
}

static void add_pr_queue(uint32 q_id,
                         char *q_name, char *q_directory,
                         char *q_command,
                         uint32 su_id, uint32 ge_id)
{
  uint8 buf[300];
  nw_new_obj(&q_id, q_name, 0x3, O_FL_STAT, 0x31);

  if (!q_directory || !*q_directory) {
    q_directory=buf;
    sprintf(q_directory, "SYS:SYSTEM/%08X.QDR", q_id);
  }
  XDPRINTF((2,0, "ADD Q=%s, V=%s, C=%s", q_name, q_directory, q_command));
  nw_new_obj_prop(q_id, NULL,            0,     0,    0,
                     "Q_DIRECTORY",      P_FL_ITEM,   0x31,
                      q_directory,  strlen(q_directory), 1);

  /* this is mars_nwe own property to handle the print job direct !!! */
  if (q_command && *q_command) {
    nw_new_obj_prop(q_id ,NULL,              0  ,   0  ,   0   ,
                     "Q_UNIX_PRINT",     P_FL_ITEM| P_FL_DYNA,   0x31,
                      q_command,  strlen(q_command), 1);
  }
  nw_new_add_prop_member(q_id, "Q_USERS",      P_FL_STAT,  0x31,  ge_id);
  nw_new_add_prop_member(q_id, "Q_OPERATORS",  P_FL_STAT,  0x31,  su_id);
  nw_new_obj_prop(q_id , NULL,             0  ,   0  ,   0   ,
                     "Q_SERVERS",             P_FL_SET,  0x31,
                      NULL,  0, 0);
}

static void add_pr_server(uint32 ps_id,
                         char *ps_name,
                         char *ps_queue,
                         uint32 su_id, uint32 ge_id, int is_user)
{
  XDPRINTF((1,0, "ADD PS%s=%s, Q=%s", is_user?"(User)":"", ps_name, ps_queue));
  if (!is_user) {
    nw_new_obj(&ps_id, ps_name, 0x7, O_FL_STAT, 0x31);
    nw_new_add_prop_member(ps_id, "PS_OPERATORS", P_FL_STAT,  0x31,  su_id);
    nw_new_add_prop_member(ps_id, "PS_USERS", P_FL_STAT,      0x31,  ge_id);
  } else {
    NETOBJ  obj;
    strmaxcpy((char*)obj.name, (char*)ps_name, 47);
    obj.type = 0x1; /* USER */
    if (find_obj_id(&obj)) {
      XDPRINTF((1, 0, "add_pr_server:user=%s not exist", visable_data(obj.name, sizeof(obj.name))));
      return;
    }
    ps_id=obj.id;
  }
  if (ps_queue && *ps_queue) {
    NETOBJ  obj;
    strmaxcpy((char*)obj.name, (char*)ps_queue, 47);
    obj.type = 0x3; /* QUEUE */
    if (!find_obj_id(&obj)) {
      nw_new_add_prop_member(obj.id, "Q_SERVERS",  P_FL_STAT, 0x31, ps_id);
    }
  }
}

static void add_user_to_group(uint32 u_id,  uint32 g_id)
{
  nw_new_add_prop_member(u_id, pn_groups_i_m_in,   P_FL_STAT,  0x31,  g_id);
  nw_new_add_prop_member(u_id, pn_security_equals, P_FL_STAT,  0x32,  g_id);
  nw_new_add_prop_member(g_id, pn_group_members,   P_FL_STAT,  0x31,  u_id);
}

static void add_user_2_unx(uint32 u_id,  char  *unname)
{
  if (unname && *unname)
    nw_new_obj_prop(u_id, NULL,          0  ,   0  ,   0 ,
                     pn_unix_user,        P_FL_ITEM,    0x30,
                     (char*)unname,  strlen(unname), 1);
}

extern int test_allow_password_change(uint32 id)
{
  uint8  more_segments;
  uint8  property_flags;
  uint8  buff[200];
  int    segment = 1;
  int    result  = nw_get_prop_val_by_obj_id(id, segment,
                   pn_special_flags, strlen(pn_special_flags),
                   buff, &more_segments, &property_flags);
  if (result > -1 && (GET_BE32(buff) & 1))
    return(-0xff);

  /* hint from <root@cs.imi.udmurtia.su> (Mr. Charlie Root) */
  result=nw_get_prop_val_by_obj_id(id, segment,
                     pn_login_control, strlen(pn_login_control),
                     buff, &more_segments, &property_flags);

  /* can user change password ? */
  if (result > -1 && (buff[62] & 1) ) /* Restriction Mask */
    return(-0xff);

  return(0);
}

static void add_remove_special_flags(uint32 obj_id, int flags)
/* add special flags to User, 0x1 = fixed-password */
{
  if (flags) {
    uint8 buff[4];
    U32_TO_BE32(flags, buff);
    nw_new_obj_prop(obj_id, NULL, 0, 0, 0,
                        pn_special_flags, P_FL_STAT|P_FL_ITEM, 0x33,
                        buff, sizeof(buff), 1);
  } else
    (void)loc_delete_property(obj_id, pn_special_flags, 0, 1);
}

static void add_user_g(uint32 u_id,   uint32 g_id,
                  char   *name,  char  *unname,
                  char *password, int dont_ch,
                  int flags, int set_flags)
{
                                      /*   Typ    Flags  Security */
#if 0  /* perhaps Compiler BUG ? problem since gcc 2.7.2.3, 29-Jan-99 */
  dont_ch = (nw_new_obj(&u_id,  name,              0x1  , 0x0,  0x31)
              && dont_ch);
#else
  if (!nw_new_obj(&u_id,  name,              0x1  , 0x0,  0x31))
    dont_ch = 0;
#endif
  if (dont_ch) return;

  XDPRINTF((1, 0, "Add/Change User='%s', UnixUser='%s'",
                  name, unname));

  add_user_to_group(u_id, g_id);
  add_user_2_unx(u_id, unname);
  if (password && *password) {
    // if (*password == '-') *password='\0';
    // if (password[0] == '-' && password[1] != '\0')    
    if (password[0] == '-' && password[1] == '\0') /* mst: 25-Apr-00 */
       *password='\0';
    nw_set_passwd(u_id, password, dont_ch);
  }
  if (set_flags)
    add_remove_special_flags(u_id, flags);
}

static void add_group(char *name,  char  *unname, char *password)
{
                                       /*   Typ    Flags  Security */
  uint32 g_id  = 0L;
  (void) nw_new_obj(&g_id,  name,       0x2  , 0x0,   0x31);
  if (unname && *unname)
    nw_new_obj_prop(g_id, NULL,                0  ,   0  ,   0 ,
                     "UNIX_GROUP",         P_FL_ITEM,    0x33,
                     (char*)unname,  strlen(unname), 1);
}


static int get_sys_unixname(uint8 *unixname, uint8 *sysname, uint8 *sysentry)
{
  uint8 optionstr[256];
  int   founds = sscanf((char*)sysentry, "%s %s %s",sysname, unixname, optionstr);
  if (founds > 1 && *unixname) {
    struct stat statb;
    int result = strlen(sysname);
    uint8 *pp  = unixname + strlen(unixname);
    if (*(sysname+result-1) == ':')
       *(sysname+result-1) = '\0';
    upstr(sysname);
    result=0;
    if (founds > 2) {
      uint8 *p;
      for (p=optionstr; *p; p++) {
        if (*p=='k' || *p=='i') {
          /*
           * Keep the automatically created SYS subdirectories in the
           * canonical lower-case form when the volume is configured for
           * either explicit downshift ('k') or case-insensitive lookup ('i').
           * Without this, a SYS volume installed with lower-case directories
           * such as public/login/system/mail and configured as 'itO' will
           * recreate upper-case PUBLIC/LOGIN/SYSTEM/MAIL on every server
           * start, which then shows duplicate DOS-visible entries.
           */
          result|=1;  /* downshift */
        } else if (*p=='t') {
          result|=2;  /* trustees */
        }
      } /* for */
    } /* if */
    if (*(pp-1) != '/') *pp++ = '/';
    *pp     = '.';
    *(pp+1) = '\0';

    if (stat(unixname, &statb) < 0)
      nwdbm_mkdir(unixname, 0751, 1);

    if (stat(unixname, &statb) < 0 || !S_ISDIR(statb.st_mode)) {
      errorp(1, "No good SYS", "unix name='%s'", unixname);
      return(-1);
    }

    *pp  = '\0';
    return(result);
  } else return(-1);
}

static uint8 *test_add_dir(uint8 *unixname, uint8 *pp, int flags,
              int downshift, int permiss, int gid, int uid, char *fn)

/* flags & 1 = fn will be appended to unixname   */
/* flags & 2 = always modify uid/gid, permission */
/* flags & 4 = always add x   flag to upper dirs */
{
  struct stat stb;
  strcpy((char*)pp, fn);
  if (downshift) downstr(pp);
  else upstr(pp);
  if (stat(unixname, &stb) < 0) {
    if (nwdbm_mkdir(unixname, permiss, (flags&4) ? 1 : 0)< 0)
      errorp(1, "mkdir error", "fname='%s'", unixname);
    else {
      chmod(unixname, permiss);
      if (uid >-1  && gid > -1)
        if (chown(unixname, uid, gid)) {}
      XDPRINTF((1, 0, "Created dir '%s'", unixname));
    }
  } else {
    if (flags&4) { /* add 'x' flag */
      char *p=unixname;
      while (NULL != (p=strchr(p+1, '/'))) {
        struct stat stb;
        *p = '\0';
        if (!stat(unixname, &stb))
            chmod(unixname, stb.st_mode|0111);
        *p='/';
      }
    }
    if (flags&2) {
      chmod(unixname, permiss);
      if (uid >-1  && gid > -1)
        if (chown(unixname, uid, gid)) {}
    }
  }
  if (flags&1) {
    pp += strlen(pp);
    *pp++='/';
    *pp  = '\0';
  } else
    *pp='\0';
  return(pp);
}

static void correct_user_dirs(uint32 objid, uint8 *objname, int uid, int gid)
{
  uint8  fndir[512];
  uint8  buf1[20];
  uint8  *p = fndir+sys_unixnamlen;
  uint8  *pp;
  uint8  *p1;
  int    l;
  int    mask;
  DIR    *f;
  memcpy(fndir, sys_unixname, sys_unixnamlen);
  /* SYS/MAIL */
  memcpy(p,"/mail/", 6);
  p1=p+6;
  l=sprintf(buf1,"../%x", (int)objid)-3;
  memcpy(p1, buf1+3, l+1);
  pp=p1+l;
  *pp='\0';
  if (!sys_downshift) {
    upstr(p);
    upstr(buf1);
  }

  if (sys_has_trustee)
    mask = 0700; /* other rights may be given via trustees */
  else
    mask = 0733; /* everybody should be able to write */

  (void)mkdir(fndir, mask);
  (void)chmod(fndir, mask);
  if (chown(fndir, uid, gid)) {}

  if ((f=opendir(fndir)) != (DIR*)NULL) {
    struct dirent* dirbuff;
    *pp='/';
    if (entry17_flags&0x1) {  /* creat empty login script */
      struct stat statb;
      strcpy(pp+1, "login");
      if (!sys_downshift)
         upstr(pp+1);
      if (stat(fndir, &statb)) { /* no one exist */
        FILE *f=fopen(fndir, "w");
        if (f) {
          fprintf(f, "REM auto created by mars_nwe\r\n");
          fclose(f);
          if (chown(fndir, uid, gid)) {}
          chmod(fndir, 0600);
        }
      }
    }

    while ((dirbuff = readdir(f)) != (struct dirent*)NULL){
      if (dirbuff->d_ino) {
        struct stat lstatb;
        uint8 *name=(uint8*)(dirbuff->d_name);
        if (name[0] != '.' && name[1] != '.' && name[1] != '\0') {
          strcpy(pp+1, name);
          if (  !lstat(fndir, &lstatb)
             && !S_ISLNK(lstatb.st_mode) &&
              lstatb.st_uid != 0 && lstatb.st_gid != 0
              && lstatb.st_uid != uid) {
            if (chown(fndir, uid, gid)) {}
            if (sys_has_trustee) {
              chmod(fndir, S_ISDIR(lstatb.st_mode) ? 700 : 600);
            }
          }
        }
      }
    }
    closedir(f);
  }
  memcpy(p1, "user/", 5);
  strmaxcpy(p1+5, objname, 47);
  if (!sys_downshift)
    upstr(p1);
  else
    downstr(p1+5);
  unlink(fndir);
  if (symlink(buf1, fndir)) {}
}

void test_ins_unx_user(uint32 id)
{
  NETOBJ obj;
  obj.id = id;
  if (!nw_get_obj(&obj)){
    MYPASSWD *mpw=nw_getpwnam(id);
    if ((MYPASSWD*)NULL == mpw){
      struct passwd *pw;
      uint8 unxname[50];
      xstrcpy(unxname, obj.name);
      downstr(unxname);
      pw = getpwnam(unxname);
      if (NULL != pw && pw->pw_uid) { /* only non root user */
        add_user_2_unx(id, unxname);
        correct_user_dirs(id, obj.name, pw->pw_uid, pw->pw_gid);
      }
    } else if (mpw->pw_uid){
      correct_user_dirs(id, obj.name, mpw->pw_uid, mpw->pw_gid);
    }
  }
}

static int cmp_uint32(const void *e1, const void *e2)
{
  if (*((uint32*)e1) < *((uint32*)e2)) return(-1);
  if (*((uint32*)e1) > *((uint32*)e2)) return(1);
  return(0);
}

static void check_compress_bindery()
/* try to repair and compress bindery, added in 0.99.pl7 */
{
  char   *errstr   = "check_compress_bindery";
  int    propok    = 0;
  uint32 objs[LOC_MAX_OBJS];
  int    ocount    = 0;

  /* for deleting props */
  uint32 d_prop_oid[LOC_MAX_OBJS];
  uint8  d_props[LOC_MAX_OBJS];
  int    d_pcount  = 0;

  uint32 *prop_oid = NULL;
  uint8  *props    = NULL;
  uint8  *props_fl = NULL;
  int    pcount    = 0;
  long   tstart    = time(NULL);

  sync_dbm();
  XDPRINTF((1,0, "%s starts...", errstr));

  if (!dbminit(FNOBJ)){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETOBJ *obj=(NETOBJ*)data.dptr;
        if (ocount == LOC_MAX_OBJS) {
          errorp(10, errstr, "to many objs = %d.",  ocount);
          dbmclose();
          return;
        }
        objs[ocount++] = obj->id;
      }
    }
    qsort(objs, (size_t)ocount, (size_t)sizeof(uint32), cmp_uint32);
    XDPRINTF((1,0, "%s after qsort", errstr));

    /* we test whether qsort/bsearch will work fine */
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETOBJ *obj=(NETOBJ*)data.dptr;
        if (NULL == bsearch(&obj->id, objs,
             (size_t)ocount, (size_t)sizeof(uint32), cmp_uint32)) {
          errorp(10, errstr, "bsearch failed at id 0x%x.",
                            obj->id);

          dbmclose();
          return;
        }
      }
    }
    dbmclose();

    /* handle all properties */
    if (!dbminit(FNPROP)){
      propok++;
#define LOC_MAX_OBJS_PROPS  (LOC_MAX_OBJS * 20)
      prop_oid = (uint32*) xmalloc(LOC_MAX_OBJS_PROPS * sizeof(uint32));
      props    = (uint8*)  xmalloc(LOC_MAX_OBJS_PROPS * sizeof(uint8));
      props_fl = (uint8*)  xmalloc(LOC_MAX_OBJS_PROPS * sizeof(uint8));
      for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
        data = fetch(key);
        if (data.dptr) {
          NETPROP *prop=(NETPROP*)data.dptr;
          if (NULL == bsearch(&prop->obj_id, objs,
             (size_t)ocount, (size_t)sizeof(uint32), cmp_uint32)) {
            XDPRINTF((1,0, "will delete property %s for obj_id 0x%x",
                           prop->name, prop->obj_id));
            if (d_pcount == LOC_MAX_OBJS) break;
            d_prop_oid[d_pcount] = prop->obj_id;
            d_props[d_pcount++]  = prop->id;
          } else {
            if (pcount == LOC_MAX_OBJS_PROPS) {
              errorp(10, errstr, "to many props = %d.",  pcount);
              propok=0;
              break;
            }
            prop_oid[pcount]   = prop->obj_id;
            props[pcount]      = prop->id;
            props_fl[pcount++] = prop->flags;
          }
        }
      }  /* for */
    }
    dbmclose();
    /* now delete properties which aere not assigned to an object */
    while (d_pcount--)
       loc_delete_property(d_prop_oid[d_pcount], (char*)NULL,
                            d_props[d_pcount], 1);

    XDPRINTF((1,0, "%s after deleting props, propok=%d", errstr, propok));

    if (propok) {  /* correct/compress propertie values */
      int  fd=-1;
      char tmpfn[300];
      strcpy(tmpfn,"/tmp/nwvalXXXXXX");
#if 0      
      if (mktemp(tmpfn)) {
        unlink(tmpfn);
        fd=creat(tmpfn, 0600);
      }
#else /* mst: 04-Apr-00, patch from (Jukka Ukkonen) */
      fd = mkstemp (tmpfn);
#endif
      if (fd > -1) {
        if (!dbminit(FNVAL)){
          int     i   = -1;
          NETVAL  val;
          key.dsize   = NETVAL_KEY_SIZE;
          key.dptr    = (char*)&val;
          while (propok && ++i < pcount) {
            int is_set     = (props_fl[i] & P_FL_SET) ? 1:0;
            NETVAL  valexp;
            int     eitems = 0;
            uint8   *ep    = valexp.value;
            val.obj_id     = prop_oid[i];
            val.prop_id    = props[i];
            val.segment    = (uint8)0;
            if (is_set) {
              memset(&valexp, 0, sizeof(NETVAL));
              valexp.obj_id  = val.obj_id;
              valexp.prop_id = val.prop_id;
              valexp.segment = 1;
              d_pcount       = 0;
            }
            while (val.segment++ < (uint8)255) {
              data = fetch(key);
              if (data.dptr != NULL){
                NETVAL  *v = (NETVAL*)data.dptr;
                uint8   *p = v->value;
                if (is_set) {
                  int     k=0;
                  while (k++ < 32){
                    uint32 id = GET_BE32(p);
                    if (id) {
                      if (NULL != bsearch(&id, objs,
                        (size_t)ocount, (size_t)sizeof(uint32), cmp_uint32)) {
                        int l=-1;
                        while (++l < d_pcount) {
                          if (d_prop_oid[l] == id) {
                            id=(uint32)0;
                            break;
                          }
                        }
                      } else id=(uint32)0;
                    }
                    if (id) {
                      d_prop_oid[d_pcount++] = id;
                      if (eitems > 31) {
                        if (sizeof(NETVAL) != write(fd, &valexp, sizeof(NETVAL))){
                          errorp(1, errstr, "writeerror on %s", tmpfn);
                          propok=0;
                          break;
                        }
                        valexp.segment++;
                        eitems = 0;
                        ep     = valexp.value;
                        memset(ep, 0, 128);
                      }
                      memcpy(ep, p, 4);
                      eitems++;
                      ep += 4;
                    }
                    p += 4;
                  }
                } else {  /* ITEM property */
                  if (sizeof(NETVAL) != write(fd, v, sizeof(NETVAL))){
                    errorp(1, errstr, "writeerror on %s", tmpfn);
                    propok=0;
                    break;
                  }
                }
              }
            } /* while */

            if (is_set && eitems) {
              if (sizeof(NETVAL) != write(fd, &valexp, sizeof(NETVAL))){
                errorp(1, errstr, "writeerror on %s", tmpfn);
                propok=0;
                break;
              }
            }
          } /* while */
        }
        dbmclose();
        close(fd);
        if (propok)
          fd=open(tmpfn, O_RDONLY);
        else
          fd=-1;
      }
      if (fd > -1) {
        NETVAL val;
        sync_dbm();
        create_nw_db(dbm_fn[FNVAL], 1); /* creat new value.dbm */
        if (!dbminit(FNVAL)){
          while (sizeof(NETVAL) == read(fd, &val, sizeof(NETVAL))){
            key.dsize     = NETVAL_KEY_SIZE;
            key.dptr      = (char*)&val;
            data.dsize    = sizeof(NETVAL);
            data.dptr     = (char*)&val;
            if (store(key, data)) {
              errorp(0, errstr, "Cannot store obj_id=0x%8x, prop_id=0x%x",
               (int)val.obj_id, (int)val.prop_id);
            }
          } /* while */
        } else {
          errorp(1, errstr, "fatal error bindery file %s saved.",
             dbm_fn[FNVAL]);
          exit(1);
        }
        dbmclose();
        close(fd);
        unlink(tmpfn);
      }
    }
    dbmclose();
  }
  sync_dbm();
  xfree(prop_oid);
  xfree(props);
  xfree(props_fl);
  XDPRINTF((1,0, "%s ends after %ld seconds.", errstr,
            (long) time(NULL) - tstart));
}

int nw_fill_standard(char *servername, ipxAddr_t *adr)
/* fills the standardproperties */
{
  int    is_nwe_start = (NULL != servername && NULL != adr);
  char   serverna[MAX_SERVER_NAME+2];
  uint32 su_id    = 0x00000001;
  uint32 ge_id    = 0x01000001;
  uint32 server_id= 0x03000001;
  uint32 q1_id    = 0x0E000001;
  uint32 ps1_id   = 0x0F000001;
  int    entry18_flags=0;  /* for queue handling */
  FILE *f;
  int  auto_ins_user = 0;
  char auto_ins_passwd[100];
  int  make_tests    = 1;
  char sysentry[256];
  sysentry[0] = '\0';

  if (is_nwe_start) {
    int i = get_ini_int(16);
    if (i > -1) make_tests=i;
    if (make_tests > 1)
      check_compress_bindery();
  }

  ge_id = nw_new_obj_prop(ge_id, "EVERYONE",        0x2,   0x0,  0x31,
                           pn_group_members,          P_FL_SET,  0x31,
                              NULL, 0, 0);
  if (NULL != (f= open_nw_ini())){
    char buff[256];
    int  what;
    while (0 != (what =get_ini_entry(f, 0, (char*)buff, sizeof(buff)))) {
      if (1 == what && !*sysentry) {
        xstrcpy(sysentry, buff);
      } else if (6 == what) {  /* server Version */
        tells_server_version = atoi(buff);
      } else if (7 == what) {  /* password_scheme */
        int pwscheme     = atoi(buff);
        password_scheme  = 0;
        switch (pwscheme) {
          case  9 : password_scheme |= PW_SCHEME_GET_KEY_FAIL;
          case  8 : password_scheme |= PW_SCHEME_ALLOW_EMPTY_PW;
          case  7 : password_scheme |= PW_SCHEME_LOGIN;
          case  1 : password_scheme |= PW_SCHEME_CHANGE_PW;
                    break;
          default : password_scheme = 0;
                    break;
        } /* switch */

      } else if (8 == what) { /* entry8_flags */
        entry8_flags = hextoi((char*)buff);
      } else if (17 == what) { /* entry17_flags */
        entry17_flags = hextoi((char*)buff);
      } else if (18 == what) { /* entry18_flags */
        entry18_flags = hextoi((char*)buff);
      } else if (21 == what) {  /* QUEUES */
        char name[200];
        char directory[200];
        char command[200];
        char *p=buff;
        char *pp=name;
        char c;
        int  state=0;
        name[0]='\0';
        directory[0]='\0';
        command[0]='\0';
        while (0 != (c = *p++)) {
          if (c == 32 || c == '\t') {
            if (!(state & 1)) {
              *pp = '\0';
              state++;
            }
          } else {
            if (state & 1){
              if (state == 1) {
                pp=directory;
                state++;
              } else if (state==3) {
                strncpy(command, p-1, sizeof(command)-1);
                break;
              }
            }
            *pp++ = c;
          }
        }
        *pp='\0';
        if (*name) {
          upstr(name);
          if (directory[0]=='-' && directory[1]=='\0')
            directory[0]='\0';
          if (command[0]=='-' && command[1]=='\0')
            command[0]='\0';
          add_pr_queue(q1_id, name, directory, command, su_id, ge_id);
          q1_id++;
        }
      } else if (22 == what) {  /* PSERVER */
        char name[200];
        char queue[200];
        char sflags[200];
        int  flags=0;
        int  count=sscanf((char*)buff, "%s %s %s", name, queue, sflags);
        if (count > 2) flags=hextoi(sflags);
        if (count < 2) *queue=0;
        if (count < 1) *name=0;
        if (*name) {
          upstr(name);
          upstr(queue);
          add_pr_server(ps1_id, name, queue, su_id, ge_id, flags&1 ?1:0);
          if (!(flags&1)) ps1_id++;
        }
      } else if (12 == what || 13 == what || 14 == what) {
        /* SUPERVISOR, OTHERS  and GROUPS*/
        char nname[100];
        char uname[100];
        char password[100];
        char flagsstr[100];
        int  flags=0;
        int  set_flags=0;
        int  anz=sscanf((char*)buff, "%s %s %s %s", nname, uname, password, flagsstr);
        if (anz == 1) {
          strcpy(uname, nname);
          anz++;
        }
        if (anz > 1) {
          upstr(nname);
          if (anz > 2) {
            upstr(password);
            if (anz > 3) {
              flags=hextoi(flagsstr);
              set_flags++;
            } else if ( what == 13
                     && password[0] == '0'
                     && password[1] == 'X'
                     && password[2] >= '0'
                     && password[2] <= '9' ) {
              flags=hextoi(password);
              password[0] = '\0';
              set_flags++;
            }
          } else password[0] = '\0';
          if (what == 14)
            add_group(nname, uname, password);
          else
            add_user_g((12 == what) ? su_id : 0L, ge_id, nname,
                            uname, password, 0, flags, set_flags);
        }
        memset(password, 0, sizeof(password));
        memset(buff,     0, sizeof(buff));
      } else if (15 == what) {
        char buf[100];
        int  anz=sscanf((char*)buff, "%s %s", buf, auto_ins_passwd);
        auto_ins_user = ((anz == 2) && atoi(buf) && *auto_ins_passwd);
        if (auto_ins_user) auto_ins_user = atoi(buf);
      } else if (16 == what) {
        make_tests = atoi(buff);
      } else if (70 == what) {
        network_serial_nmbr=atou(buff);
      } else if (71 == what) {
        network_appl_nmbr=(uint16)atou(buff);
      }
    } /* while */
    fclose(f);
  }

  if (is_nwe_start) {
    strmaxcpy(serverna, servername, MAX_SERVER_NAME);
    upstr(serverna);
    nw_new_obj_prop(server_id, serverna,       0x4,      O_FL_DYNA,  0x40,
                       "NET_ADDRESS",         P_FL_ITEM | P_FL_DYNA, 0x40,
                        (char*)adr,  sizeof(ipxAddr_t), 1);

#ifdef _MAR_TESTS_1
    nw_new_obj_prop(pserv_id, serverna,      0x47,    O_FL_DYNA, 0x31,
                       "NET_ADDRESS",     P_FL_ITEM | P_FL_DYNA, 0x40,
                        (char*)adr,  sizeof(ipxAddr_t), 1);
#endif
  }

  if (auto_ins_user) {
    /* here Unix users will be inserted automaticly as mars_nwe users */
    struct passwd *pw;
    upstr(auto_ins_passwd);
    while (NULL != (pw=getpwent())) {
      if (pw->pw_uid) {
        int do_add = ( (pw->pw_passwd[0]!= '*' && pw->pw_passwd[0]!='x')
                      || pw->pw_passwd[1] != '\0');
#if SHADOW_PWD
        /*
        tip from: Herbert Rosmanith <herp@wildsau.idv.uni-linz.ac.at>
        */
        if (!do_add) {
          struct spwd *sp=getspnam(pw->pw_name);
          if (sp) {
            if  (  ((sp->sp_pwdp[0] != '*' && sp->sp_pwdp[0] != 'x')
                      || sp->sp_pwdp[1] !='\0')
                      &&
                   ((sp->sp_pwdp[0] != 'N' && sp->sp_pwdp[1] != 'P')
                      || sp->sp_pwdp[2] != '\0') )
               do_add++;
#if 0
            XDPRINTF((1,0, "Shadow pw of %s = `%s`", pw->pw_name, sp->sp_pwdp));
#endif
          } else {
            XDPRINTF((1,0, "cannot read shadow password"));
          }
        }
#endif
        if ( do_add) {
          char nname[100];
          xstrcpy(nname, pw->pw_name);
          upstr(nname);
          add_user_g(0L, ge_id, nname, pw->pw_name, auto_ins_passwd,
            (auto_ins_user == 99) ? 0 : 99, 0, 0);
        } else {
          XDPRINTF((1,0, "Unix User:'%s' not added because passwd='%s'",
             pw->pw_name, pw->pw_passwd));
        }
      } else {
        XDPRINTF((1,0, "Unix User:'%s' not added because uid=0 (root)",
                         pw->pw_name));
      }
    }
    endpwent();
  }
  memset(auto_ins_passwd, 0, sizeof(auto_ins_passwd));

  if (*sysentry) {
    uint8 unixname[512];
    uint8 sysname[256];
    int   result      = get_sys_unixname(unixname, sysname, sysentry);
    int   downshift   = (result & 1);
    int   has_trustee = (result & 2);
    int   unlen       = strlen(unixname);
    if (result < 0) return(-1);
    new_str(sys_unixname, unixname);
    new_str(sys_sysname,  sysname);
    sys_downshift   = downshift;
    sys_has_trustee = has_trustee;
    sys_unixnamlen  = unlen;

    if (make_tests) {
      uint32  objs[LOC_MAX_OBJS];
      uint8   maildir[512];
      int     ocount=0;
      uint8  *pp    = unixname+unlen;
      uint8  *ppp   = maildir+unlen;
      int    mask;
      memcpy(maildir, unixname, unlen+1);

      test_add_dir(unixname,     pp, 4, downshift,0755, 0,0, "LOGIN");
      test_add_dir(unixname,     pp, 0, downshift,0751, 0,0, "SYSTEM");
      mask = (sys_has_trustee) ? 0751 : 0755;
      test_add_dir(unixname,     pp, 0, downshift,mask, 0,0, "PUBLIC");
      /* ----- */
      ppp=test_add_dir(maildir, ppp, 1, downshift,0755, 0,0, "MAIL");
      test_add_dir(maildir,     ppp, 0, downshift,0755, 0,0, "USER");

      if (!dbminit(FNOBJ)){
        for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
          data = fetch(key);
          if (data.dptr) {
            NETOBJ *obj=(NETOBJ*)data.dptr;
            if (obj->type == 1 || obj->type == 3) {
              objs[ocount++] = obj->id;
              if (ocount == LOC_MAX_OBJS) break;
            }
          }
        }
      }
      dbmclose();

      while (ocount--) {
        NETOBJ obj;
        obj.id = objs[ocount];
        nw_get_obj(&obj);
        if (obj.type == 1) {
          int gid;
          int uid;
          if (!get_guid(&gid, &uid, obj.id, NULL))
            correct_user_dirs(obj.id, obj.name, uid, gid);
          else
            errorp(10, "Cannot get unix uid/gid", "User=`%s`", obj.name);
        } else if (obj.type == 3) { /* print queue */
          uint8 buff[300];
          char  *p;
          result=nw_get_q_dirname(obj.id, buff);
          upstr(buff);
          if (result > -1 && NULL != (p=strchr(buff, ':')) ) {
            *p++='\0';
            if (!strcmp(buff, sysname)) {
              mask = (sys_has_trustee) ? 0751 : 0755;
              test_add_dir(unixname, pp, 2|4, downshift, mask, 0, 0, p);
            } else
             errorp(10, "queue dir not on SYS",
                "Queue=%s, Volume=%s", obj.name, sysname);
          } else
            errorp(10, "Cannot get queue dir", "Queue=%s", obj.name);
        }
      }

    }

    if (is_nwe_start)  {
      /* do only init_queues when starting nwserv */
      init_queues(entry18_flags);  /* nwqueue.c */
    }

    return(0);
  }
  return(-1);
}

static void nw_init_dbm_1(char *servername, ipxAddr_t *adr)
/*
 * routine inits bindery
 * all dynamic objects and properties will be deleted.
 * and the always needed properties will be created
 * if not exist.
 */
{
  int     anz=0;
  uint32  objs[LOC_MAX_OBJS];
  uint8   props[LOC_MAX_OBJS];

  create_nw_db(dbm_fn[FNOBJ],  0);
  create_nw_db(dbm_fn[FNPROP], 0);
  create_nw_db(dbm_fn[FNVAL],  0);
  create_nw_db(dbm_fn[FNIOBJ], 0);

  if (!dbminit(FNOBJ)){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETOBJ *obj=(NETOBJ*)data.dptr;
        if ((obj->flags & O_FL_DYNA) || !obj->name[0]) {
          /* dynamic or without name */
          objs[anz++] = obj->id;
          if (anz == LOC_MAX_OBJS) break;
        } else if (obj->type == 1 /* && obj->id != 1 */ && obj->security != 0x31) {
          /* this is for correcting wrong obj security */
          obj->security=0x31;
          (void)store(key, data);
          XDPRINTF((1,0, "Correcting access obj_id=0x%x(%s)",
                  (int)obj->id, obj->name));
        }
      }
    }
  }
  dbmclose();
  while (anz--)
    loc_delete_obj(objs[anz], 0x44);  /* Now delete dynamic objects */
  anz = 0;
  if (!dbminit(FNPROP)){
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETPROP *prop=(NETPROP*)data.dptr;
        if (prop->flags & P_FL_DYNA) { /* dynamic */
          objs[anz]    = prop->obj_id;
          props[anz++] = prop->id;
          if (anz == LOC_MAX_OBJS) break;
        }
      }
    }
  }
  dbmclose();
  while (anz--) /* now delete dynamic properties */
    loc_delete_property(objs[anz], (char*)NULL, props[anz], 1);
}

int nw_init_dbm(char *servername, ipxAddr_t *adr)
{
  int result;
  nw_init_dbm_1(servername, adr);
  result = nw_fill_standard(servername, adr);
  sync_dbm();
  return(result);
}

void nw_exit_dbm(void)
{
  exit_queues();
  sync_dbm();
}

#if 0
#define MAX_OBJ_IDS 100000 /* should be enough */
typedef struct {
  int     anz;
  uint32  obj_ids[MAX_OBJ_IDS];
}
#endif


static FILE *open_exp_file(char *path, int what_dbm, int mode)
/* mode 1 = export, 0 = import */
{
  char buf[300];
  char *err_str="open_exp_file";
  FILE *f;
  char *opmode=mode ? "w+" : "r";
  sprintf(buf, "%s/%s.exp", (path && *path) ? path : ".",
               dbm_fn[what_dbm] );
  if (NULL == (f=fopen(buf, opmode))) {
    errorp(0, err_str, "Open error `%s` mode=%s", buf, opmode);
  } else {
    if (!mode) {
      sync_dbm();
      create_nw_db(dbm_fn[what_dbm],  1);
    }
    if (!dbminit(what_dbm))
      return(f);
    else {
      errorp(0, err_str, "dbminit error `%s`", buf);
      fclose(f);
      dbmclose();
    }
  }
  return(NULL);
}



static int export_obj(char *path)
{
  int result = 1;
  FILE *f    = open_exp_file(path, FNOBJ, 1);
  if (f != NULL) {
    result=0;
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETOBJ *o=(NETOBJ*)data.dptr;
        fprintf(f, "0x%08x %-47s 0x%04x 0x%02x 0x%02x\n",
            (int) o->id,  o->name, (int) o->type,
            (int) o->flags, (int)o->security);
      }
    }
    fclose(f);
    dbmclose();
  }
  return(result);
}

static int export_prop(char *path)
{
  int result = 1;
  FILE *f    = open_exp_file(path, FNPROP, 1);
  if (f != NULL) {
    result=0;
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETPROP *p=(NETPROP*)data.dptr;
        fprintf(f, "0x%08x 0x%02x %-15s 0x%02x 0x%02x\n",
            (int) p->obj_id, (int)p->id, p->name,
            (int) p->flags, (int)p->security);
      }
    }
    fclose(f);
    dbmclose();
  }
  return(result);
}

static int export_val(char *path)
{
  int result = 1;
  FILE *f    = open_exp_file(path, FNVAL, 1);
  if (f != NULL) {
    result=0;
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETVAL *v=(NETVAL*)data.dptr;
        int k=128;
        uint8 *p=v->value;
        fprintf(f, "0x%08x 0x%02x 0x%02x ",
            (int) v->obj_id, (int)v->prop_id, (int) v->segment);
        while (k--) {
          fprintf(f, "%02x", (int)*p++);
        }
        fprintf(f, "\n");
      }
    }
    fclose(f);
    dbmclose();
  }
  return(result);
}

int do_export_dbm(char *path)
/* Builds ASCII export files */
{
  int result=export_obj(path);
  if (!result) result=export_prop(path);
  if (!result) result=export_val(path);
  sync_dbm();
  return(result);
}

static int import_obj(char *path)
{
  char *err_str="import_obj";
  int result=1;
  FILE *f = open_exp_file(path, FNOBJ, 0);
  if (f != NULL) {
    char   buff[300];
    int    line=0;
    result=0;
    while (fgets(buff, sizeof(buff), f) != NULL){
      NETOBJ obj;
      char   name[300];
      int    type;
      int    flags;
      int    security;
      int    obj_id;
      line++;
      if (sscanf(buff, "%x %s %x %x %x",
            &(obj_id),  name, &type,
            &flags, &security) == 5) {
        strmaxcpy(obj.name, name, 47);
        obj.id       = (uint32) obj_id;
        obj.type     = (uint16)type;
        obj.flags    = (uint8) flags;
        obj.security = (uint8) security;
        key.dsize  = NETOBJ_KEY_SIZE;
        key.dptr   = (char*)&obj;
        data.dsize = sizeof(NETOBJ);
        data.dptr  = (char*)&obj;
        if (store(key, data)) {
          errorp(0, err_str, "Cannot store `%s` type=0x%x",
             obj.name, (int)obj.type);
        }
      } else {
        errorp(0, err_str, "Wrong line=%d: `%s`",line, buff);
      }
    } /* while */
    XDPRINTF((0, 0, "%s:got %d lines", err_str, line));
    fclose(f);
    dbmclose();
  }
  return(result);
}

static int import_prop(char *path)
{
  char *err_str="import_prop";
  int result=1;
  FILE *f = open_exp_file(path, FNPROP, 0);
  if (f != NULL) {
    char   buff[300];
    int    line=0;
    result=0;
    while (fgets(buff, sizeof(buff), f) != NULL){
      NETPROP prop;
      int    id;
      char   name[300];
      int    obj_id;
      int    flags;
      int    security;
      line++;
      if (sscanf(buff, "%x %x %s %x %x",
            &(obj_id), &id,  name, &flags, &security) == 5) {
        prop.obj_id   = (uint32)obj_id;
        prop.id       = (uint8)id;
        strmaxcpy(prop.name, name, 15);
        prop.flags    = (uint8) flags;
        prop.security = (uint8) security;
        key.dsize  = NETPROP_KEY_SIZE;
        key.dptr   = (char*)&prop;
        data.dsize = sizeof(NETPROP);
        data.dptr  = (char*)&prop;
        if (store(key, data)) {
          errorp(0, err_str, "Cannot store `%s` obj_id=0x%x, prop_id=0x%x",
             prop.name, prop.obj_id, (int)prop.id);
        }
      } else {
        errorp(0, err_str, "Wrong line=%d: `%s`",line, buff);
      }
    } /* while */
    XDPRINTF((0, 0, "%s:got %d lines", err_str, line));
    fclose(f);
    dbmclose();
  }
  return(result);
}

static int import_val(char *path)
{
  char *err_str="import_val";
  int result=1;
  FILE *f = open_exp_file(path, FNVAL, 0);
  if (f != NULL) {
    char   buff[300];
    int    line=0;
    result=0;
    while (fgets(buff, sizeof(buff), f) != NULL){
      NETVAL val;
      int    prop_id;
      int    obj_id;
      int    segment;
      char   value[300];
      line++;
      if (sscanf(buff, "%x %x %x %s",
            &obj_id, &prop_id, &segment, value) == 4) {
        uint8 *p=val.value;
        uint8 *pp=value;
        char  smallbuf[3];
        int  k=128;
        smallbuf[2] = '\0';
        val.obj_id = (uint32) obj_id;
        while (k--) {
          int i;
          memcpy(smallbuf, pp, 2);
          pp+=2;
          sscanf(smallbuf, "%x", &i);
          *p++ = (uint8) i;
        }
        val.prop_id   = (uint8) prop_id;
        val.segment   = (uint8) segment;
        key.dsize     = NETVAL_KEY_SIZE;
        key.dptr      = (char*)&val;
        data.dsize    = sizeof(NETVAL);
        data.dptr     = (char*)&val;
        if (store(key, data)) {
          errorp(0, err_str, "Cannot store obj_id=0x%8x, prop_id=0x%x",
             (int)val.obj_id, (int)val.prop_id);
        }
      } else {
        errorp(0, err_str, "Wrong line=%d: `%s`",line, buff);
      }
    } /* while */
    XDPRINTF((0, 0, "%s:got %d lines", err_str, line));
    fclose(f);
    dbmclose();
  }
  return(result);
}

int do_import_dbm(char *path)
/* Imports ASCII export files */
{
  int result=import_obj(path);
  if (!result) result=import_prop(path);
  if (!result) result=import_val(path);
  return(result);
}


/* export functions to export bindery to directory entries */

static char *path_bindery   = "/var/nwserv/bind";

static void bcreate_obj(uint32 id, char *name, int type,
                        int flags, int security)

{
  char buf[300];
  char buf1[300];
  uint8 buf_uc[4];
  char id_buf[30];
  int  len = slprintf(buf, sizeof(buf)-1,"%s/%x", path_bindery, type);
  int  idlen;

  U32_TO_BE32(id, buf_uc);
  idlen = slprintf(id_buf, sizeof(id_buf)-1,"%x/%x/%x/%x",
                              (int) buf_uc[0],
                              (int) buf_uc[1],
                              (int) buf_uc[2],
                              (int) buf_uc[3]);

  /*  1 creat */
  nwdbm_mkdir(buf, 0755, 1);

  /*  1/mstover unlink */
  slprintf(buf+len, sizeof(buf)-1-len,"/%s", name);
  downstr(buf+len);
  unlink(buf);

  /*  1/mstover  ->  ../id/1/2/3/4 */
  slprintf(buf1,sizeof(buf1)-1, "../id/%s", id_buf);
  if (symlink(buf1, buf)) {}

  /* id/1/2/3/4   creat */
  len = slprintf(buf, sizeof(buf)-2,"%s/id/%s", path_bindery, id_buf);
  unx_xrmdir(buf); /* remove if exist */
  nwdbm_mkdir(buf, 0755, 1);
  buf[len++] = '/';
  /* ----------------------------------- */

  /* name  ->  mstover */
  strmaxcpy(buf+len, "name.o", sizeof(buf)-1-len);
  strmaxcpy(buf1, name, sizeof(buf1)-1);
  downstr(buf1);
  if (symlink(buf1, buf)) {}

  /* typ   ->  1 */
  strmaxcpy(buf+len, "typ.o", sizeof(buf)-1-len);
  slprintf(buf1, sizeof(buf1)-1,"%x", type);
  if (symlink(buf1, buf)) {}

  /* flags & security */
  strmaxcpy(buf+len, "f+s.o", sizeof(buf)-1-len);
  slprintf(buf1, sizeof(buf1)-1,"%02x%02x", flags&0xff, security&0xff);
  if (symlink(buf1, buf)) {}

}

static int export_obj_to_dir(void)
{
  int result = 0;
  if (!result) {
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETOBJ *o=(NETOBJ*)data.dptr;
        bcreate_obj( o->id, o->name, (int) o->type,
                          (int)o->flags, (int) o->security);
      }
    }
  }
  return(result);
}

static void bcreate_prop(uint32 id, int prop_id, char *name,
                        int propflags, int propsecurity)
{
  char buf[300];
  char buf1[300];
  uint8 buf_uc[4];

  int  len;
  int  len1;
  U32_TO_BE32(id, buf_uc);
  len = slprintf(buf, sizeof(buf)-1,"%s/id/%x/%x/%x/%x/",
                              path_bindery,
                              (int) buf_uc[0],
                              (int) buf_uc[1],
                              (int) buf_uc[2],
                              (int) buf_uc[3]);

  /* prop_id unlink */
  slprintf(buf+len, sizeof(buf)-1-len,"%x.p", prop_id);
  downstr(buf+len);
  unlink(buf);

  /*  id.p -> name */
  slprintf(buf1,sizeof(buf1)-1, "%s", name);
  downstr(buf1);
  if (symlink(buf1, buf)) {}


  /* x/name  creat */
  len1 = slprintf(buf+len, sizeof(buf)-1-len,"%s", name);
  downstr(buf+len);
  unx_xrmdir(buf); /* remove if exist */
  nwdbm_mkdir(buf, 0700, 1);
  len += len1;
  buf[len++] = '/';

  /* flags & security */
  strmaxcpy(buf+len, "f+s", sizeof(buf)-1-len);
  slprintf(buf1, sizeof(buf1)-1,"%02x%02x", propflags&0xff, propsecurity&0xff);
  if (symlink(buf1, buf)) {}

}

static int export_prop_to_dir(void)
{
  int result = 0;
  if (!result) {
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETPROP *p=(NETPROP*)data.dptr;
        bcreate_prop(p->obj_id, (int) p->id, p->name,
                  (int)p->flags, (int) p->security);
      }
    }
  }
  return(result);
}


static void bcreate_val(uint32 id, int prop_id, int segment,
                                       uint8 *value)
{
  char buf[300];
  char buf1[300];
  uint8 buf_uc[4];

  int  len;
  int k     = 128;
  uint8 *p  = value;
  uint8 *p1 = buf1;

  U32_TO_BE32(id, buf_uc);
  len = slprintf(buf, sizeof(buf)-1,"%s/id/%x/%x/%x/%x/%x.p/%x",
                              path_bindery,
                              (int) buf_uc[0],
                              (int) buf_uc[1],
                              (int) buf_uc[2],
                              (int) buf_uc[3],
                              prop_id,
                              segment);

  /* segment unlink */
  unlink(buf);
  while (k--) {
    sprintf(p1, "%02x", (int) *p++);
    p1+=2;
  }
  if (symlink(buf1, buf)) {}
}


static int export_val_to_dir(void)
{
  int result = 0;
  if (!result) {
    for  (key = firstkey(); key.dptr != NULL; key = nextkey(key)) {
      data = fetch(key);
      if (data.dptr) {
        NETVAL *v=(NETVAL*)data.dptr;
        bcreate_val(v->obj_id, (int)v->prop_id, (int) v->segment, v->value);
      }
    }
  }
  return(result);
}

int do_export_dbm_to_dir(void)
{
  int result =dbminit_ro(FNOBJ);
  if (!result) {
    export_obj_to_dir();
    result = dbminit_ro(FNPROP);
  }
  if (!result) {
    export_prop_to_dir();
    result = dbminit_ro(FNVAL);
  }
  if (!result)
     export_val_to_dir();
  return(result);
}




