/*
    nwclient.c - Client oriented calls
    Copyright (C) 2001  Patrick Pollet

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Revision history:


	1.00  2001, Feb 18		Patrick Pollet
		Initial release.

        1.01  2001, March 9		Patrick Pollet
                Removed NWDSScanConnsForTrees.Is now in nwnet.c
		Corrected NWCXGetPermConnInfo(NWCC_INFO_AUTHENT_STATE).
                Corrected NWCXSplitNameAndContext (had no return values)

	1.02  2001, March 12		Patrick Pollet
                #131:Corrected NWCXIsDSAuthenticated ( return 0 if not dxh) and not -1
                #137:__NWCXGetPermConnList made static ( now internal only ?)
                #341: added tests for null pointers in NWCXAttachToTreeByName,NWCXGetDefault*
                #389: better error code NWE_BIND_NO_SUCH_PROP
                NWCXGetNDSVolumeServerAndResourceName now allows null pointers for serverName/resourceName

	1.03  2001, March 16		Patrick Pollet
		#284 (NWDSCreateContextHandleMnt): added missing NWDSFreeContext
		Rewrote the attribute reading part
		Added NWCXGetIntAttributeValues, NWCXGetStringAttributeValues,
                	NWCXGetMultiStringAttributeValues, NWCXGetAttributeValuesAsString,
                Added reading the ~/.nwinfos file to fetch default tree, ctx, user if none in environnment
                Added NWCXGetDefaultServer call to NWCXAttachToTreeByName()

	1.04  2001, May 31		Petr Vandrovec <vandrove@vc.cvut.cz>
		Add include strings.h, use ncpt_atomic_*, not atomic_*,
		use size_t for size...

	1.05  2001, July 15		Petr Vandrovec <vandrove@vc.cvut.cz>
		Fixed NWCXSplitNameAndContext to honor ctx's settings.

	1.06  2001, October 21		Patrick.Pollet@insa-lyon.fr
		Fixed NWCXSplitNameAndContext to allow NULL name or context .
		Mofified NWCXAttachToTreeByName() to use the new NWCCOpenConnByname((,,NWCC_NAME_FORMAT_NDS_TREE,...)

	1.07  2001, December 7		Patrick.Pollet@insa-lyon.fr
		Reverted NWCXAttachToTreeByName() to use the old code since (see #define USE_OLD_CODE)
		new NWCCOpenConnByname((,,NWCC_NAME_FORMAT_NDS_TREE,...) is sometimes capricious

	1.08  2002, January 20		Petr Vandrovec <vandrove@vc.cvut.cz>
		Moved NWCC_INFO_MOUNT_POINT code from here to nwcalls.c.
		Compile portions need by ncpumount even when NDS_SUPPORT is disabled.

	1.09  2002, January 25		Patrick.Pollet@insa-lyon.fr
		Implemented LoginScript reading API calls
                          Removed NWDSAbbreviateName in  __docopy_string when retrieving a SYN_DIST_NAME 


*/

#include "config.h"


#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>

#include <ncp/nwcalls.h>
#include <ncp/nwnet.h>
#include <ncp/nwclient.h>
#include "nwnet_i.h"

#ifdef NCP_KERNEL_NCPFS_AVAILABLE
#include <mntent.h>
#endif
#include <pwd.h>
#include <sys/stat.h>

/*should be in ndslib.h ?*/
#define NWE_BIND_NO_SUCH_PROP NWE_NCP_NOT_SUPPORTED

#undef DEBUG_PRINT

#if 1
/*read hard degugging*/
 void printConn (NWCONN_HANDLE conn, int stage) {
        printf("%s\n",conn->mount_point);
        printf ("------ info from kernel--------------------------------\n");
        printf ("infoV2.version %d\n",conn->i.version);
	printf ("infoV2.mounted_uid %lu\n",conn->i.mounted_uid);
	printf ("infoV2.connection %d\n",conn->i.connection);
	printf ("infoV2.buffer_size %d\n",conn->i.buffer_size);
        printf ("infoV2.volume_number %d\n",conn->i.volume_number);
	printf ("infoV2.directory_id %d\n",conn->i.directory_id);

	printf ("infoV2.dummy1 %d\n",conn->i.dummy1);
	printf ("infoV2.dummy2 %d\n",conn->i.dummy2);
	printf ("infoV2.dummy3 %d\n",conn->i.dummy3);

        printf ("------ info from ncplib (recreated stage %d) ---------\n",stage);

        printf ("is_connected %d\n",conn->is_connected);
        printf ("nds_conn %p\n",conn->nds_conn);
	printf ("nds_ring %p/%p\n",conn->nds_ring.prev, conn->nds_ring.next);
        printf ("user %s\n",conn->user);
	printf ("user_id_is_valid %d\n",conn->user_id_valid);
	printf ("user_id %x\n",conn->user_id);

	printf ("mount_fid %d\n",conn->mount_fid);
        printf ("mount_point %s\n",conn->mount_point);
	printf ("ncpt_atomic_t store_count %d\n",ncpt_atomic_read(&conn->store_count));
        printf ("state %d\n",conn->state);

        printf ("connstate %x\n", conn->connState);
        printf ("sign_wanted %x\n", ncp_get_sign_wanted(conn));
	printf ("sign_active %x\n", ncp_get_sign_active(conn));

        printf ("serverInfo.valid %x\n",conn->serverInfo.valid);
        printf ("serverInfo.serverName %s\n",conn->serverInfo.serverName);
        printf ("serverInfo.version.major %x\n",conn->serverInfo.version.major);
        printf ("serverInfo.version.minor %x\n",conn->serverInfo.version.minor);
        printf ("serverInfo.version.revision %x\n",conn->serverInfo.version.revision);

        printf ("NET_ADDRESS_TYPE nt %x\n",conn->nt);


 }
#endif

static int NWCIsPermanentConnection (NWCONN_HANDLE conn) {
  return (ncp_get_conn_type(conn) == NCP_CONN_PERMANENT);
}

NWCCODE NWCXGetPermConnInfo(NWCONN_HANDLE conn, nuint info, size_t len, void* buffer) {
        if (!buffer) /*just in case*/
		return ERR_NULL_POINTER;

	if (!NWCIsPermanentConnection(conn))
		return NWE_REQUESTER_FAILURE;

        switch (info) {
                case  NWCC_INFO_AUTHENT_STATE:
                        { /* must be authenticated since it is permanently mounted ? */
#ifdef NDS_SUPPORT			
                            if (NWIsDSServer(conn,NULL))
                                return ncp_put_req_size_unsigned(buffer, len, NWCC_AUTHENT_STATE_NDS);
                            else
#endif
                                return ncp_put_req_size_unsigned(buffer, len, NWCC_AUTHENT_STATE_BIND);
                        }

                default:
			return NWCCGetConnInfo(conn,info,len,buffer);
	}
}

#ifdef NDS_SUPPORT
int NWCXIsDSServer ( NWCONN_HANDLE conn, NWDSChar * treeName) {
/* same as API return 1 if OK but removes the trailings '_____'*/
   if (!treeName)
        return NWIsDSServer(conn,NULL);

   if (NWIsDSServer(conn,treeName)) {
      char *p;

       p = strchr(treeName, '\0') - 1;
       while ((p >= treeName) && (*p == '_'))
           p--;
       treeName[ p - treeName + 1] = 0;
      return 1;
   }
   else {
        treeName[0]=0;
        return 0;
  }
}

int NWCXIsSameTree (NWCONN_HANDLE conn, const NWDSChar *treeName) {
/* return 1 if OK  */
 char treeName2[MAX_TREE_NAME_CHARS+1]="";

 if (NWCXIsDSServer(conn,treeName2))  {
   if (!treeName)
      return 0;
   return  (!strcasecmp(treeName,treeName2));
 }
 else return 0;
}
#endif

int NWCXIsSameServer(NWCONN_HANDLE conn, const NWDSChar* server) {
	/* return 1 if OK  */
	char server2[NCP_BINDERY_NAME_LEN + 1];
	NWCCODE err;

	if (!server) {
		return 0;
	}
	err = NWCCGetConnInfo(conn, NWCC_INFO_SERVER_NAME, sizeof(server2), server2);
	if (err) {
		return 0;
	}
	return !strcasecmp(server,server2);
}

#ifdef NDS_SUPPORT
NWDSCCODE NWCXIsDSAuthenticated(NWDSContextHandle ctx){
/* return non zero if a context has authentication keys*/
   NWDS_HANDLE dxh;

   dxh=ctx->ds_connection;
   if (! dxh)  /* should not be ???*/
      return 0;
   return
    (dxh->authinfo!=NULL);
}
#endif

static
NWCCODE __NWCXGetPermConnList (NWCONN_HANDLE* conns , int maxEntries, int* curEntries,
                               uid_t uid,const NWDSChar* treeName, const NWDSChar* serverName){
/* returns the list of permanent connexions belonging to user uid*/
/* if uid=-1 = all Permanent connections (Root only) */
/* if treeName !=NUll only them */
/*if serverName !=NULL only them*/

#ifdef NCP_KERNEL_NCPFS_AVAILABLE
        FILE* mtab;
        NWCONN_HANDLE conn;
        struct mntent* mnt_ent;

#if 0
        char aux[256];
/*        char aux2[256];*/
        char* user;
#endif
	
        uid_t callerUid=getuid();
	int alluid = 0;

        *curEntries=0;

        if ((uid == (uid_t)-1)) {
	        /*only root can scan all connections*/
		if (callerUid != 0) {
	               return EACCES;
		}
		alluid = 1;
	} else if (uid != callerUid) {
	        /*only root can scan other user connections*/
        	if (callerUid != 0) {
			return EACCES;
		}
	}

#ifdef NDS_SUPPORT
        if (treeName && serverName) {	/*should never happen on this internal function ? */
               return NWE_REQUESTER_FAILURE;
	}
#else
	if (treeName) {
		return ENOPKG;
	}
#endif

        if ((mtab = fopen(MOUNTED, "r")) == NULL) {
                return errno;
        }
        while ((*curEntries <maxEntries)&&(mnt_ent = getmntent(mtab))) {
/*                printf("%s %s %s\n ",mnt_ent->mnt_type,mnt_ent->mnt_fsname,mnt_ent->mnt_dir);*/
                if (strcmp(mnt_ent->mnt_type, "ncpfs") != 0) {
                        continue;
                }
#if 0		
                /*printf("%s %s %s\n ",mnt_ent->mnt_type,mnt_ent->mnt_fsname,mnt_ent->mnt_dir);*/
                if (strlen(mnt_ent->mnt_fsname) >= sizeof(aux)) {
                        continue;
                }
                strcpy(aux, mnt_ent->mnt_fsname);
                user = strchr(aux, '/');
                if (!user) {
                        continue;
                }
                *user++ = '\0';

/*                strcpy(aux2, mnt_ent->mnt_dir);
                printf("-->%s %s %s\n ",aux,user,aux2);*/
#endif
                if (ncp_open_mount(mnt_ent->mnt_dir, &conn)) {
/*                   printf("failed%s\n","");*/
                       continue;
                }

                if (alluid || (conn->i.mounted_uid == uid)) {
                        int addit;
#ifdef NDS_SUPPORT			
                        if (treeName)
                                addit = NWCXIsSameTree(conn, treeName);
                        else 
#endif			
			if (serverName)
                                addit = NWCXIsSameServer(conn, serverName);
			else
				addit = 1;
                        if (addit) {
                                *conns++ = conn;
                                (*curEntries)++;
/*                               printf("OK %d\n",*curEntries);*/
                        } else {
				NWCCCloseConn(conn); /*free memory only */
			}
                } else {
			NWCCCloseConn(conn); /*free memory only */
		}
        }
        fclose(mtab);
        return 0;
#else
        return ENOPKG;
#endif  /* NCP_KERNEL_NCPFS_AVAILABLE */
}


NWCCODE NWCXGetPermConnList (NWCONN_HANDLE* conns , int maxEntries, int* curEntries, uid_t uid){
/* returns the list of permanent connexions belonging to user uid*/
/* if uid=-1 = all Permanent connections (Root only) */
    return __NWCXGetPermConnList (conns,maxEntries,curEntries,uid,NULL,NULL);

}

NWCCODE NWCXGetPermConnListByTreeName (NWCONN_HANDLE* conns, int maxEntries,
                                      int* curEntries, uid_t uid,
                                      const NWDSChar *treeName){
   return __NWCXGetPermConnList (conns,maxEntries,curEntries,uid,treeName,NULL);
}

NWCCODE NWCXGetPermConnListByServerName (NWCONN_HANDLE* conns , int maxEntries,
                                      int* curEntries, uid_t uid,
                                      const NWDSChar *serverName){
   return __NWCXGetPermConnList (conns,maxEntries,curEntries,uid,NULL,serverName);
}

#ifdef NDS_SUPPORT
/* copied from ndslib.c */
static void strcpy_cw(wchar_t *w, const char* s) {
        while ((*w++ = *(const nuint8*)s++) != 0);
}

NWDSCCODE NWDSCreateContextHandleMnt(NWDSContextHandle* ctx, const NWDSChar * treeName){

#define MAXCONNS 64
  NWDSCCODE err;
  NWCONN_HANDLE conns[MAXCONNS];

  int curEntries=0;
  NWDS_HANDLE dxh;
  int i;
  union __NWDSAuthInfo * ndai;
  size_t authinfo_len;
  wchar_t wc_treeName[MAX_DN_CHARS+1];

  if (!treeName)
      return ERR_NULL_POINTER;

  strcpy_cw (wc_treeName,treeName);

  err=NWDSCreateContextHandle (ctx);
  if (err)
       return err;

  err= NWCXGetPermConnListByTreeName (conns,MAXCONNS,&curEntries,getuid(),treeName);
  if (err) {
     NWDSFreeContext(*ctx);
     return (err);
  }
/*  printf ("got %d connexion(s) to %s \n",curEntries,treeName);*/
  dxh=(*ctx)->ds_connection;
  if (! dxh){  /* should not be ???*/
           NWDSFreeContext(*ctx);
           return EINVAL;
   }
  for (i=0; i <curEntries;i++) {
     NWCONN_HANDLE conn = conns[i];
     err=NWDSAddConnection (*ctx,conn);

     if (err) {
/*            printf ("error adding connection \n");*/
            NWCCCloseConn(conn);
            continue;
     }
     if (!dxh->authinfo) {
/*        printf ("trying to get authinfo \n");*/
        err = ncp_get_private_key(conn, NULL, &authinfo_len);
        if (err)
                  continue;       /* kernel without private key support */
        if (!authinfo_len)
                   continue;       /* no private key on this connection */
        ndai = (union __NWDSAuthInfo*)malloc(authinfo_len);
        if (!ndai)
                continue;       /* not enough memory */
        err = ncp_get_private_key(conn, ndai, &authinfo_len);
        if (err) {
                free(ndai);
                continue;
        }
/*        printf ("got authinfo \n");*/
        mlock(ndai, authinfo_len);
        dxh->authinfo = ndai;
        if (dxh->dck.tree_name)
            free(dxh->dck.tree_name);
        dxh->dck.tree_name=wcsdup(wc_treeName);
     }

  }
  return 0;

}


//#define NOENV 1  <-- testing reading of .nwinfos file
#undef NOENV
#define NWINFOS ".nwinfos"

/**********************************************/
#define NDS_USER                "NDS_USER"
#define NDS_GECOS               "NDS_GECOS"
#define NDS_SHELL               "NDS_SHELL"
#define NDS_HOME                "NDS_HOME"
#define NDS_UID                 "NDS_UID"
#define NDS_GID                 "NDS_GID"
#define NDS_HOME_SERVER         "NDS_HOME_SERVER"
#define NDS_HOME_VOLUME         "NDS_HOME_VOLUME"
#define NDS_HOME_PATH           "NDS_HOME_PATH"
#define NDS_HOME_MNT_PNT        "NDS_HOME_MNT_PNT"
#define NDS_EMAIL               "NDS_EMAIL"

#define NDS_PREFERRED_SERVER    "NDS_PREFERRED_SERVER"
#define NDS_PREFERRED_TREE      "NDS_PREFERRED_TREE"
#define NDS_PREFERRED_NAME_CTX  "NDS_PREFERRED_NAME_CTX"

/***********************************************/

static char* readnwinfosfile (char * user, const char * info, const char * forTree, long * err) {
/*  strongly inspired from ncplib.c: ncp_fopen_nwc and ncp_get_nwc_ent
    user (=NULL) current user
    info : variable name we are looking for
    if forTree= NULL any tree will do
    else forTree must be equals to NWCLIENT_PREFERRED_TREE=xxxx
    stored in ~/.nwinfos file */

        FILE *f;
        static char value[1024]=""; /*static value of matching line*/
        char concernTree[1024]=""; /*tree read from ~/.nwinfos file */
        char line[1024]="";
        char path[1024]="";
        char * home=NULL;
        char *p;
        int line_len;
        struct stat st;

        if (!user) {
                home = getenv("HOME");
        } else {
                struct passwd *pwd;

                if ((pwd = getpwnam(user)) != NULL) {
                        home = pwd->pw_dir;
                }
        }

        if ((home == NULL)
            || (strlen(home) + sizeof(NWINFOS) + 2 > sizeof(path))) {
                *err = ENAMETOOLONG;
                return NULL;
        }
        strcpy(path, home);
        strcat(path, "/");
        strcat(path, NWINFOS);
#ifdef NOENV
   printf ("searching %s\n",path);
#endif

        if (stat(path, &st) != 0) {
                *err = errno;
                return NULL;
        }
        if ((st.st_mode & (S_IRWXO | S_IRWXG)) != 0) {
                *err = NCPLIB_INVALID_MODE;
                return NULL;
        }
        f=fopen(path, "r");

        if (!f) {
                *err=errno;
                return NULL;
        }
        value[0]=0;      /*reset statics */
        concernTree[0]=0;
        while (fgets(line, sizeof(line), f) != NULL) {
                if ((line[0] == '\n')|| (line[0] == '#'))
                        continue;
                line_len = strlen(line);
                if (line[line_len - 1] == '\n') {
                        line[line_len - 1] = '\0';

                }

                p=strchr(line,'=');
                if (!p)
                        continue;
                *p=0;
                if (!strcasecmp(line,info)) {
                        strcpy(value,p+1);
#ifdef NOENV
  printf ("found required info %s=%s\n",info,value);
#endif
                }
                if (!strcasecmp(line,NDS_PREFERRED_TREE)) {
                        strcpy(concernTree,p+1);
#ifdef NOENV
  printf ("this file concerns tree %s\n",concernTree);
#endif

                }

                if (value[0] && concernTree[0])
                        break; /* got both */

        }
        fclose(f);
#ifdef NOENV
  printf ("found for tree %s :pref_tree = %s required info %s=%s\n",
           forTree,concernTree,info,value);
#endif

        if (!value[0]) return NULL; /* not found */
        if (!forTree) return value; /* Ok for any tree */
        return strcasecmp(forTree,concernTree)? NULL:value;
}


/* we are using the same env names as the Caldera client (hope they won't mind)
#define PREFER_TREE_ENV         "NWCLIENT_PREFERRED_TREE"
#define PREFER_CTX_ENV          "NWCLIENT_DEFAULT_NAME_CONTEXT"
#define PREFER_SRV_ENV          "NWCLIENT_PREFERRED_SERVER"
#define PREFER_USER_ENV         "NWCLIENT_DEFAULT_USER"

this one is an extra by PP for a possible autologon
#define PREFER_PWD_ENV          "NWCLIENT_DEFAULT_PASSWORD"
*/


NWDSCCODE NWCXGetPreferredDSTree    (NWDSChar * preferTree, size_t maxLen){

  char * res=NULL;
  long err;

  if (!preferTree)
      return ERR_NULL_POINTER;
#ifndef NOENV
  res =getenv (PREFER_TREE_ENV);
#endif
  if (!res)
        res=readnwinfosfile (NULL,NDS_PREFERRED_TREE,NULL, &err);
  if (!res)
    return -1;
  if (strlen (res)+1 >maxLen)
     return NWE_BUFFER_OVERFLOW;
  strcpy(preferTree,res);
  return 0;
}

NWDSCCODE NWCXGetDefaultNameContext (const NWDSChar* forTree,
                                     NWDSChar * nameContext, size_t maxLen){

   char * res=NULL;
   long err;

  if (!nameContext)
      return ERR_NULL_POINTER;
#ifndef NOENV
  res =getenv (PREFER_CTX_ENV);
#endif
   if (!res)
        res=readnwinfosfile (NULL,NDS_PREFERRED_NAME_CTX,forTree,&err);
  if (!res)
    return -1;
  if (strlen (res)+1 >maxLen)
     return NWE_BUFFER_OVERFLOW;
  strcpy(nameContext,res);
  return 0;

}

NWDSCCODE NWCXGetPreferredServer    (const NWDSChar * forTree,
                                     NWDSChar *preferedServer, size_t maxLen){
	char * res=NULL;

	if (!preferedServer)
		return ERR_NULL_POINTER;
#ifndef NOENV
	res = getenv(PREFER_SRV_ENV);
#endif
	if (!res) {
		long err;

		res=readnwinfosfile (NULL, NDS_PREFERRED_SERVER, forTree, &err);
		if (!res) {
			return -1;
		}
	}
	/* test that this server DO belongs to tree forTree*/
	/* NULL means any tree or even bindery (?)*/
	if (forTree) {
		NWCONN_HANDLE conn;
		NWCCODE err;

		err = NWCCOpenConnByName(NULL, res, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, &conn);
		if (err) {
			return -1;	/* better code for not available ? */
		}
		err = !NWCXIsSameTree(conn, forTree);
		NWCCCloseConn(conn);
		if (err) {
			return -1;	/* not same tree ignore it */
		}
	}

	if (strlen(res) + 1 > maxLen) {
		return NWE_BUFFER_OVERFLOW;
	}
	strcpy(preferedServer, res);
	return 0;
}


NWDSCCODE NWCXGetDefaultUserName (const NWDSChar * forTree,
                                   NWDSChar *defaultName, size_t maxLen){
/*may be we should return current user's Unix name if nothing found in env ? */
        char * res=NULL;
        long err;
        if (!defaultName)
                return ERR_NULL_POINTER;
#ifndef NOENV
        res =getenv (PREFER_USER_ENV);
#endif
         if (!res)
                res=readnwinfosfile (NULL,NDS_USER,forTree, &err);
         if (!res)
                return -1;
        if (strlen (res)+1 >maxLen)
                return NWE_BUFFER_OVERFLOW;
        strcpy(defaultName,res);
        return 0;
}

NWDSCCODE NWCXGetDefaultPassword (const NWDSChar * forTree,
                                   NWDSChar *defaultPWD, size_t maxLen){
        char * res=NULL;
        if (!defaultPWD)
                return ERR_NULL_POINTER;
        (void) forTree; /* not yet used */
        res =getenv (PREFER_PWD_ENV);
        if (!res)
                return -1;
        if (strlen (res)+1 >maxLen)
                return NWE_BUFFER_OVERFLOW;
        strcpy(defaultPWD,res);
        return 0;
}


#define USE_OLD_CODE

NWDSCCODE NWCXAttachToTreeByName( NWCONN_HANDLE* conn, const NWDSChar * treeName){

#ifdef USE_OLD_CODE
        struct ncp_conn *connInit;
	struct ncp_bindery_object obj;
        long err;
        char prefered_server[MAX_DN_BYTES+1];

        if (!conn || !treeName)
                return ERR_NULL_POINTER;

        /* NULL  nearest server ????*/
	if ((connInit = ncp_open(NULL, &err)) == NULL){
                  return err;
        }
#if 0
  printf ("searching for default server in tree %s\n",treeName);
#endif
        /* see if there is a default server in env */
        err=NWCXGetPreferredServer(treeName,prefered_server,sizeof(prefered_server));
        if (!err) {
#if 0
  printf ("found default server %s\n",prefered_server);
#endif

           err=NWCCOpenConnByName(connInit,prefered_server,NWCC_NAME_FORMAT_BIND,
                               0, NWCC_RESERVED,conn);
           /*we already checked in NWCXGetPreferredServer that server belongs to the target tree */
           if (!err) {
#if 0
  printf ("OK on server %s\n",prefered_server);
  printConn (*conn,1);
#endif

                NWCCCloseConn(connInit);
                return 0;
           }
        }

/* try to speed up search of all servers but first checking that treeName exists */
        {
          nuint32 scanIndex=-1;
          char myTreeName [MAX_DN_CHARS+1];
          NWDSContextHandle ctx;
          int found=0;

          err= NWDSCreateContextHandle (&ctx);
          if (err)
            return err;

          while (!found && (err=NWDSScanForAvailableTrees(ctx,connInit,"*",&scanIndex,myTreeName)==0)){
                /*printf (" comparing %s and %s \n",treeName,myTreeName);*/
             found=!strcasecmp(treeName,myTreeName);
          }
          NWDSFreeContext(ctx);
          /*printf ("found=%d\n",err);*/
          if (!found) /* 0x89FC*/
            {
               NWCCCloseConn(connInit);
               return NWE_BIND_NO_SUCH_PROP;
            }
        }

	obj.object_id = 0xffffffff;

        err=NWE_BIND_NO_SUCH_PROP;
        while (ncp_scan_bindery_object(connInit, obj.object_id,
				       NCP_BINDERY_FSERVER, "*",
				       &obj) == 0){
                // do not ask AXIS CD ROM TOWERS they are bugged in opening conns !!!!
                 /*printf ("trying server %s\n",obj.object_name);*/
                 if (!strncmp ("AXIS",obj.object_name,4)) {
                        continue;
                 }
                 err=NWCCOpenConnByName(connInit,obj.object_name,NWCC_NAME_FORMAT_BIND,
                               0, NWCC_RESERVED,conn);
                 if (err)
                    continue;
                 if (NWCXIsSameTree (*conn,treeName))
                     break; /* gotcha*/
                  NWCCCloseConn(*conn);
                  err=NWE_BIND_NO_SUCH_PROP;
	}
	NWCCCloseConn(connInit);
        return err;

#else
 /* much shorter !!!  but does not always works , when nearest server does not belong to the target tree*/
 /* we get a Server unknown error 0x89FC */
 /* printf ("using new code\n");*/
  return  NWCCOpenConnByName(NULL, treeName, NWCC_NAME_FORMAT_NDS_TREE,
                        NWCC_OPEN_NEW_CONN,
                        NWCC_RESERVED, conn);
#endif

}


NWDSCCODE NWCXSplitNameAndContext (NWDSContextHandle ctx, const NWDSChar * dn,
                                   NWDSChar * name, NWDSChar* context) {
	NWDSCCODE err;
	wchar_t wdn[MAX_DN_CHARS + 1];
	wchar_t* cptr;
	wchar_t* wctx;

	err = NWDSXlateFromCtx(ctx, wdn, sizeof(wdn), dn);
	if (err) {
		return err;
	}
	cptr = wdn;
	while (*cptr && *cptr != L'.') {
		if (*cptr == L'\\') {
			if (!*++cptr) {
				return ERR_INVALID_OBJECT_NAME;
			}
		}
		cptr++;
	}
	if (*cptr) {
		wctx = cptr + 1;
		*cptr = 0;
	} else {
		wctx = cptr;
	}

	if (name) {
		err = NWDSXlateToCtx(ctx, name, MAX_DN_BYTES, wdn, NULL);
		if (err) {
			return err;
		}
	}
	if (context) {
		err = NWDSXlateToCtx(ctx, context, MAX_DN_BYTES, wctx, NULL);
		if (err) {
			return err;
		}
	}
	return 0;
}


/* internal only !!!!
 called with "real buffer" for single valued
    or a temp buffer for with multivalued attributes
 we process attributes with variable size (CI_LIST, CI_OCTET..
         and return them as strings with comma separators
attributes with fields are emitted on a single line with "," as separator*/

static NWDSCCODE __docopy_string (NWDSContextHandle ctx, const void* val,
                                   const enum SYNTAX synt, size_t currentSize,
                                   char* result, size_t maxSize){
  NWDSCCODE err;
  char tmpBuf [MAX_DN_BYTES+1];

#ifdef DEBUG_PRINT
  printf ("__docopy_string got :%s synt = %d cursize=%d maxsize= %d\n",(char *)val,synt,currentSize,maxSize );
#endif

  if (currentSize >maxSize) return NWE_BUFFER_OVERFLOW;
  if (!result) return ERR_NULL_POINTER;
  switch (synt) {
	case SYN_DIST_NAME:
              /*
	      err = NWDSAbbreviateName(ctx, val, tmpBuf);
              if (err)
                  return err;
              //strcpy (result,tmpBuf);
	      *************************/
	     strcpy(result,val);
              break;
	case SYN_CI_STRING:
	case SYN_CE_STRING:
        case SYN_PR_STRING:
	case SYN_NU_STRING:
	case SYN_TEL_NUMBER:
	case SYN_CLASS_NAME:
		strcpy(result,val);
	        break;
	case SYN_PATH:{
	        const Path_T* p = (const Path_T*)val;
                if (strlen(p->volumeName)+strlen(p->path)+2+2+1>=maxSize)
                        return NWE_BUFFER_OVERFLOW;
		sprintf(result,"%u,%s,%s", p->nameSpaceType,p->volumeName, p->path);
                }
		break;
	case SYN_TYPED_NAME:{
		const Typed_Name_T* tn = (const Typed_Name_T*)val;
                if (strlen(tn->objectName)+8+8+2+1>=maxSize)
                        return NWE_BUFFER_OVERFLOW;
		sprintf(result,"%u,%u,%s", tn->interval,tn->level,tn->objectName);
		}
		break;
	case SYN_FAX_NUMBER:{
	        const Fax_Number_T* fn = (const Fax_Number_T*)val;
                if (strlen(fn->telephoneNumber)+2+1+1>=maxSize)
                        return NWE_BUFFER_OVERFLOW;
		sprintf(result,"%s,%u", fn->telephoneNumber,fn->parameters.numOfBits);
		}
		break;
	case SYN_EMAIL_ADDRESS:{
		const EMail_Address_T* ea = (const EMail_Address_T*)val;
                /*change the SMTP:aaa@bbbb to SMTP,aaa@bbbb */
                char* p=strchr(ea->address,':');
                if (strlen(ea->address)+2+1+1>=maxSize)
                        return NWE_BUFFER_OVERFLOW;

                if (p) *p=',';
		sprintf(result,"%u,%s", ea->type,ea->address);
		}
		break;
	case SYN_PO_ADDRESS:{
	        const NWDSChar* const* pa = (const NWDSChar* const*)val;
                int n;
                size_t len=1;
                for (n=0;n <5;n++)
                  len +=strlen(pa[n]+1);
                if (len >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
		sprintf(result,"%s,%s,%s,%s,%s,%s",pa[0],pa[1],pa[2],pa[3],pa[4],pa[5]);
		}
		break;
	case SYN_HOLD:{
	        const Hold_T* h = (const Hold_T*)val;
                if (strlen(h->objectName)+8+1+1>=maxSize)
                        return NWE_BUFFER_OVERFLOW;
		sprintf(result,"%u,%s", h->amount, h->objectName);
		}
		break;
         case SYN_TIMESTAMP:{
                const TimeStamp_T* stamp = (const TimeStamp_T*)val;
                if (maxSize <=3*9)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%u,%u,%u",stamp->wholeSeconds, stamp->replicaNum,stamp->eventID);
                }
                break;
         case SYN_BACK_LINK:{
                const Back_Link_T* bl = (const Back_Link_T*)val;
                if (strlen(bl->objectName)+8+1+1 >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%08X,%s", bl->remoteID, bl->objectName);
                }
                break;
        case SYN_CI_LIST:{
	        const CI_List_T* ol = (const CI_List_T*)val;
                const CI_List_T* p;
                size_t len=1;
                char *aux=result;
                for(p=ol;p;p=p->next) {
                  len=+strlen(p->s)+1;
                }
                if (len >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                if (len==1) {
                        result[0]=0;
                        return 0;
                }
                for (p=ol;p;p=p->next) {
                        len=strlen(p->s);
                        memcpy(aux,p->s,len);
                        aux+=len;
                        *(aux++)=',';
                }
                *(--aux)=0;
                }
		break;

         case SYN_OCTET_LIST:{
                const Octet_List_T* ol = (const Octet_List_T*)val;
                size_t i;
                char aux [4];
                if ((ol->length+1)*3+1 >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%u", ol->length);
                for (i = 0; i < ol->length; i++) {
                        sprintf(aux,",%02X", ol->data[i]);
                        strcat(result,aux);
                }
                }
                break;
        case SYN_OCTET_STRING:{
                const Octet_String_T* os = (const Octet_String_T*)val;
                char aux [4];
                size_t i;
#ifdef DEBUG_PRINT
                printf ("len %d\n",os->length);
#endif
                if ((os->length+1)*3+1 >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%u", os->length);
                for (i = 0; i < os->length; i++) {
                        sprintf(aux,",%02X", os->data[i]);
                        strcat(result,aux);
                }
                }
                break;
        case SYN_NET_ADDRESS:{
                const Net_Address_T* na = (const Net_Address_T*)val;
                size_t z;
                char aux[4];
                z=na->addressLength;
                if (3*(z+2)+1  >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%u,%u", na->addressType,na->addressLength);
                for (z = 0; z < na->addressLength; z++) {
                        sprintf(aux,",%02X", na->address[z]);
                        strcat(result,aux);
                }
                }
                break;
        case SYN_OBJECT_ACL:{
                const Object_ACL_T* oacl = (const Object_ACL_T*)val;
                if (strlen(oacl->protectedAttrName)+strlen(oacl->subjectName)+8+2+1 >=maxSize)
                        return NWE_BUFFER_OVERFLOW;
                sprintf(result,"%s,%s,%08X",oacl->protectedAttrName,oacl->subjectName,oacl->privileges);
                }
                break;
	default:
		return EINVAL;
	}
        return 0;
}

static
NWDSCCODE docopy_string(NWDSContextHandle ctx, const void * val, const enum SYNTAX synt, size_t currentSize,
                                   void * result, size_t maxSize){

        return __docopy_string(ctx, val, synt, currentSize, (char*)result, maxSize);
}


static
NWDSCCODE docopy_int(UNUSED(NWDSContextHandle ctx), const void * val, const enum SYNTAX synt, size_t currentSize,
		     void * result, size_t maxSize) {
#ifdef DEBUG_PRINT
	printf ("docopy_int got :%s synt = %d cursize=%d maxsize= %d\n",(char *)val,synt,currentSize,maxSize );
#endif
	if (!result) {
		return ERR_NULL_POINTER;
	}
	switch (synt) {
		case SYN_BOOLEAN:
			return ncp_put_req_size_unsigned(result, maxSize, *(const Boolean_T*)val);
		case SYN_COUNTER:
			return ncp_put_req_size_unsigned(result, maxSize, *(const Counter_T*)val);
		case SYN_INTEGER:
			return ncp_put_req_size_unsigned(result, maxSize, *(const Integer_T*)val);
		case SYN_INTERVAL:
			return ncp_put_req_size_unsigned(result, maxSize, *(const Integer_T*)val);
		case SYN_TIME:
			return ncp_put_req_size_unsigned(result, maxSize, *(const Time_T*)val);
		default:
			return EINVAL;
	}
}

/***********************************************************************/

struct strlistnode {
   struct strlistnode* next;
   char * str;
};

struct strlist {
  struct strlistnode* first;
  struct strlistnode* last;
};

static struct strlist* initlist (void) {
  struct strlist* l;

  l=(struct strlist*) malloc(sizeof(*l));
  if (l) {
        l->first=l->last=NULL;
  }
  return l;
}

static void freelist ( struct strlist* l) {
  if (l) {
        struct strlistnode* p;
        struct strlistnode* q=NULL;
        for (p=l->first;p;p=q) {
                q=p->next;
                free(p->str);
                free(p);
        }
  }
}

static NWDSCCODE addstring (char * str, struct strlist * l) {

    char * v;
    struct strlistnode* newNode;

    if (!l)
        return ERR_NULL_POINTER;
    v= strdup(str);
    if (!v)
        return ENOMEM;
    newNode=malloc (sizeof (*newNode));
    if (!newNode)
        return ENOMEM;
    newNode->str=v;
    newNode->next=NULL;
    if (l->last)
        l->last->next=newNode;
    else
        l->first=newNode;
   l->last=newNode;
   return 0;

}

static NWDSCCODE strlist_to_string (struct strlist * l, char ** result, char sep) {
/*convert a linked list to a string with sep between strings */
        size_t ln;
        char* aux;
        struct strlistnode * p;

        *result = NULL;

        if (!l)
                return ERR_NULL_POINTER;

        /*compute needed memory */
        ln = 1;
        for (p = l->first; p; p = p->next)
                ln += strlen(p->str) + 1;
        if (ln == 1)
                return 0;

        aux = (char*)malloc(ln);
        if (!aux)
                return ENOMEM;
        *result = aux;
        /*transfert to result with commas between strings */
        for (p = l->first;p; p = p->next) {
                ln = strlen(p->str);
                memcpy(aux, p->str, ln);
                aux += ln;
                *aux++ = sep;
        }
        *--aux = 0; /* remove last comma */
        return 0;
}


/***********************************************************************/

/* append the new string to a linked list */
static NWDSCCODE docopy_multistring(NWDSContextHandle ctx, const void * val, const enum SYNTAX synt, size_t currentSize,
				    void * result, size_t maxSize) {
	NWDSCCODE err;
	char tmpBuf[MAX_DN_BYTES+1];
	struct strlist* l=(struct strlist *) result;

#ifdef DEBUG_PRINT
	printf ("docopy_multistring got :%s synt = %d cursize=%d maxsize= %d\n",(char *)val,synt,currentSize,maxSize );
#endif
	/*copy string to tmpBuffer */
	err = __docopy_string(ctx, val, synt, currentSize, tmpBuf, sizeof(tmpBuf));
	if (!err) {
		/* and add to list if Ok */
		err= addstring(tmpBuf,l);
	}
	return err;
}



/*-------------------------------------------------------------------------*/
/* Function : ReadAttributesValues                                         */
/*                                                                         */
/* Description :                                                           */
/*   Function for reading an set of attributes values                      */
/*   nearly same as the one used in pam_ncp_auth.c/pamncp.c snapin         */
/*    except a "more complex" copying call back                            */
/*-------------------------------------------------------------------------*/

// element of the table of attributes to read

struct attrop {
	const NWDSChar* attrname;
	NWDSCCODE (*copyval)(NWDSContextHandle, const void* val, const enum SYNTAX synt,
                             size_t currentSize, void* result, size_t maxSize);
	enum SYNTAX synt;
        int maxsize;
};



static NWDSCCODE ReadAttributesValues(NWDSContextHandle ctx,
			 const char* objname,
			 void* arg,
			 struct attrop* atlist) {


	Buf_T* inBuf=NULL;
	Buf_T* outBuf=NULL;
	NWDSCCODE dserr;
	struct attrop* ptr;
	nuint32 iterHandle;


	iterHandle = NO_MORE_ITERATIONS; // test at bailout point !!!

#ifdef DEBUG_PRINT
{
  char aux [8192];
  NWDSCanonicalizeName(ctx,objname,aux);
  printf ("readattr called for %s %s fqdn=%s \n",objname,atlist->attrname,aux);
}
#endif

        dserr = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &inBuf);
	if (dserr) {
		goto bailout;
	}
	dserr = NWDSInitBuf(ctx, DSV_READ, inBuf);
	if (dserr) {
		goto bailout;
	}
	for (ptr = atlist; ptr->attrname; ptr++) {
		dserr = NWDSPutAttrName(ctx, inBuf, ptr->attrname);
		if (dserr) {
			goto bailout;
		}
	}
	dserr = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &outBuf);
	if (dserr) {
		goto bailout;
	}
	do {
		nuint32 attrs;
		dserr = NWDSRead(ctx, objname, DS_ATTRIBUTE_VALUES, 0, inBuf, &iterHandle, outBuf);
		if (dserr) {
                        /* caller must complains or not upon */
			/*ERR_NO_SUCH_ATTRIBUTE*/
			/*ERR_NO_SUCH_ENTRY*/

			goto bailout;
		}
		dserr = NWDSGetAttrCount(ctx, outBuf, &attrs);
		if (dserr) {
			goto bailout;
		}
#ifdef DEBUG_PRINT
   printf ("attr count %d\n",attrs);
#endif
		while (attrs--) {
			char attrname[MAX_SCHEMA_NAME_CHARS+1];
			nuint32 synt;
			nuint32 vals;

			dserr = NWDSGetAttrName(ctx, outBuf, attrname, &vals, &synt);
			if (dserr) {
				goto bailout;
			}
#ifdef DEBUG_PRINT
   printf ("val count %d\n",vals);
#endif

                        while (vals--) {
				size_t sz;
				void* val;

				dserr = NWDSComputeAttrValSize(ctx, outBuf, synt, &sz);
				if (dserr) {
					goto bailout;
				}
				val = malloc(sz);
				if (!val) {
					goto bailout;
				}
				dserr = NWDSGetAttrVal(ctx, outBuf, synt, val);
				if (dserr) {
					free(val);
					goto bailout;
				}
				for (ptr = atlist; ptr->attrname; ptr++) {
#if 0
  printf ("%s:%d:%p\n",ptr->attrname,ptr->synt,ptr->copyval);
#endif

					if (!strcmp(ptr->attrname, attrname))
#if 0
  printf ("ok\n");
#endif

						break;
				}
#if 0
  printf ("%s:%d:%p\n",ptr->attrname,ptr->synt,ptr->copyval);
#endif


#ifdef DEBUG_PRINT
  printf (ptr->copyval? "non null\n":"null\n");
#endif
				if (ptr->copyval) {
					if (ptr->synt != synt) {
                                             dserr=ERR_NO_SUCH_ATTRIBUTE;
					} else {
#ifdef DEBUG_PRINT
  printf ("zy go\n");
#endif

						dserr= ptr->copyval(ctx,val,synt,sz, arg,ptr->maxsize);
					}
				}else {
                                        dserr=ERR_NO_SUCH_ATTRIBUTE;
                                         /* mistyped: happens with upcae/lowcase errors */
                                }
				free(val);
				if (dserr) {
					goto bailout;
				}
			}
		}
	} while (iterHandle != NO_MORE_ITERATIONS);
bailout:;
	if (iterHandle != NO_MORE_ITERATIONS) {
	    // let's keep the final dserr as the 'out of memory error' from ptr->getval()
		NWDSCloseIteration(ctx, DSV_READ, iterHandle);
	}
	if (inBuf) NWDSFreeBuf(inBuf);
	if (outBuf) NWDSFreeBuf(outBuf);
#ifdef DEBUG_PRINT
  printf ("readattr ended with code :%s\n",strnwerror(dserr));
#endif

	return dserr;
}



/* specific buffer extracting functions that replace docopy_* */
struct nw_home_info {
	char*	hs;
	char*  hrn;
};


static NWDSCCODE docopy_host_server(NWDSContextHandle ctx,
                                    const void* val, const enum SYNTAX synt,
                                    size_t currentSize, void* result, size_t maxSize){
	struct nw_home_info* hi = (struct nw_home_info*)result;
        char buf [MAX_DN_CHARS];
        char * dot;
        char *v;
#ifdef DEBUG_PRINT
  printf ("docopy_host_server got :%s synt = %d cursize=%d maxsize= %d\n",(char *)val,synt,currentSize,maxSize );
#endif

        strcpy(buf,(const char *) val);
        dot = strchr(buf, '.');  // trim out the context part ( it that good enough ???)
        if (dot) *dot=0;
        v = strdup(buf);
	if (!v) {
		return ENOMEM;
	}
	hi->hs = v;
	return 0;
}

static NWDSCCODE docopy_host_resource_name(NWDSContextHandle ctx,
                                    const void* val, const enum SYNTAX synt,
                                    size_t currentSize, void* result, size_t maxSize){
	struct nw_home_info* hi = (struct nw_home_info*)result;


        char* v = strdup((const char*)val);

#ifdef DEBUG_PRINT
  printf ("docopy_host_resource got :%s synt = %d cursize=%d maxsize= %d\n",(char *)val,synt,currentSize,maxSize );
#endif

        if (!v) {
		return ENOMEM;
	}
	hi->hrn =v;
	return 0;
}

static NWDSCCODE docopy_home_directory(NWDSContextHandle ctx,
                                        const void* val, const enum SYNTAX synt,
                                        size_t currentSize, void* result, size_t maxSize){


      const Path_T* pa = (const Path_T*) val;
      Path_T* result_int = (Path_T*)result;
      NWDSCCODE dserr;
      char * v;


      result_int->nameSpaceType=pa->nameSpaceType;
      v = strdup(pa->path);
      if (!v)
        return ENOMEM;
      result_int->path=v;

      v = strdup(pa->volumeName);
      if (!v)
        return ENOMEM;
      result_int->volumeName=v;

      return 0;
}

/******************************************************************************/
NWDSCCODE NWCXGetNDSVolumeServerAndResourceName(NWDSContextHandle ctx,
                                                const NWDSChar* ndsName,
                                                NWDSChar* serverName,
                                                size_t serverNameMaxLen,
                                                NWDSChar *resourceName,
                                                size_t resourceNameMaxLen){

	static  struct attrop atlist[] = {
		{ATTR_HOST_SERVER, 	docopy_host_server,	        SYN_DIST_NAME   ,9999},
                {ATTR_HOST_RN,          docopy_host_resource_name,      SYN_CI_STRING   ,9999},
		{ NULL,		        NULL,			        SYN_UNKNOWN     ,0 }};

        struct nw_home_info hi ={NULL,NULL};
        NWDSCCODE dserr;

        if (!ndsName )
                return ERR_NULL_POINTER;


	dserr=ReadAttributesValues(ctx,ndsName,&hi,atlist);
        if (!dserr) {
                if (serverName && hi.hs) {
                        if (strlen(hi.hs) <serverNameMaxLen)
                                 strcpy(serverName,hi.hs);
                        else
                                dserr=NWE_BUFFER_OVERFLOW;
                }

                if (resourceName &&hi.hrn) {
                        if (strlen(hi.hrn) <resourceNameMaxLen)
                                strcpy(resourceName,hi.hrn);
                        else
                                dserr= NWE_BUFFER_OVERFLOW;
               }

       }
#ifdef DEBUG_PRINT
  printf ("%s %s:\n",hi.hs,hi.hrn);
#endif

       if (hi.hs) free( hi.hs);
       if (hi.hrn) free( hi.hrn);
       return dserr;

}


NWDSCCODE NWCXGetObjectHomeDirectory(NWDSContextHandle ctx,
                                     const NWDSChar* ndsName,
                                     NWDSChar* serverName,
                                     size_t serverNameMaxLen,
                                     NWDSChar *resourceName,
                                     size_t resourceNameMaxLen,
                                     NWDSChar* NDSVolumeName,
                                     size_t NDSVolumeNameMaxLen,
                                     NWDSChar* pathName,
                                     size_t pathNameMaxLen) {

        Path_T reply={0,NULL,NULL};

        static  struct attrop atlist[] = {
	        { ATTR_HOME_NW , 	docopy_home_directory,   SYN_PATH   ,9999},
	        { NULL,		        NULL,			SYN_UNKNOWN     ,0 }};
         NWDSCCODE dserr;

        if (!ndsName )
                return ERR_NULL_POINTER;

	dserr=ReadAttributesValues(ctx,ndsName,&reply,atlist);

        if (!dserr) {
           if (NDSVolumeName && reply.volumeName) {
             if (strlen(reply.volumeName) <NDSVolumeNameMaxLen)
                                 strcpy(NDSVolumeName,reply.volumeName);
                        else
                                dserr=NWE_BUFFER_OVERFLOW;
           }

           if (pathName && reply.path) {
             if (strlen(reply.path) <pathNameMaxLen)
                                 strcpy(pathName,reply.path);
                        else
                                dserr=NWE_BUFFER_OVERFLOW;
           }

           if (serverName || resourceName){
                if (reply.volumeName)
                        dserr=NWCXGetNDSVolumeServerAndResourceName(ctx,reply.volumeName,
                                                serverName,serverNameMaxLen,
                                                resourceName,resourceNameMaxLen);
                       else dserr=ERR_NO_SUCH_ATTRIBUTE;

           }
       }
#ifdef DEBUG_PRINT
  printf ("%s %s\n",reply.volumeName,reply.path);
  printf ("%s %s %s %s\n",NDSVolumeName,pathName,serverName,resourceName);
#endif

        if (reply.path) free (reply.path);
        if (reply.volumeName) free (reply.volumeName);

#ifdef DEBUG_PRINT
  printf ("%s:\n",strnwerror(dserr));
#endif
        return dserr;

}


NWDSCCODE NWCXGetObjectLastLoginTime(NWDSContextHandle ctx,
                                     const NWDSChar* ndsName,
                                     time_t * tm){
	int int_tm;
	NWDSCCODE err;

        err = NWCXGetIntAttributeValue(ctx, ndsName, ATTR_LOGIN_TIME, &int_tm);
	if (!err) {
		*tm = int_tm;
	}
	return err;
}

NWDSCCODE NWCXGetObjectMessageServer(NWDSContextHandle ctx,
                                     const NWDSChar* ndsName,
                                     NWDSChar* serverName,
                                     int serverNameMaxLen){

        return NWCXGetStringAttributeValue(ctx,ndsName,ATTR_MESSAGE_SERVER,serverName,serverNameMaxLen);
}


/*** login script reading & writing ****************************************/

struct loginScriptInfo {
        const NWDSChar * user;
        char * buffer;
        int  bytesRead;
        int max;
};


static NWDSCCODE docopy_login_script(NWDSContextHandle ctx, const void * val, const enum SYNTAX synt, size_t currentSize,
                                   void * result, size_t maxSize){

// code from contrib/testing/dstream.c

        static char attrName[MAX_DN_CHARS + 1]=ATTR_LOGIN_SCRIPT;
        struct loginScriptInfo* lsi=result;
        nuint8 hndl[6];
        NWCONN_HANDLE fileConn;
        ncp_off64_t fileSize;

        NWDSCCODE err ;

        (void) val;(void) maxSize;(void) synt;(void) currentSize; // warnings out !

        if (!lsi->buffer) return ERR_NULL_POINTER;
#if 0
        printf("do_copy_login_script called for %s\n", lsi->user);
#endif
        //err=ERR_NO_SUCH_ATTRIBUTE;

         err= __NWDSOpenStream(ctx, lsi->user, attrName, 0, &fileConn, hndl, &fileSize);
         if(err==0){
                lsi->bytesRead=ncp_read ( fileConn,hndl,0, lsi->max-1,lsi->buffer);
                if (lsi->bytesRead <0 )   {// some error occured
		 err=-1;
		 lsi->bytesRead=0;
                }
                lsi->buffer[lsi->bytesRead]='\0';
                ncp_close_file(fileConn,hndl);
                NWCCCloseConn(fileConn);
#if  0
                 printf("%s:%d/%d bytes read from login script, err=%d\n",lsi->user, lsi->bytesRead,lsi->max,err);
#endif
          }
          return err;
}

NWDSCCODE NWCXGetObjectLoginScript(NWDSContextHandle ctx,const NWDSChar* objectName,
                                        char* buffer, int * len, int maxlen) {
       static  struct attrop atlist[] = {
	        { ATTR_LOGIN_SCRIPT , 	docopy_login_script,    SYN_STREAM      ,9999},
	        { NULL,		        NULL,			SYN_UNKNOWN     ,0 }};
       NWDSCCODE dserr;

  struct loginScriptInfo lsi;

  if (!objectName) return ERR_NULL_POINTER;

  lsi.user=objectName;
  lsi.buffer=buffer;
  lsi.max=maxlen;
  lsi.bytesRead=0;

  dserr= ReadAttributesValues(ctx,objectName,&lsi,atlist);
  if (!dserr)  {
     *len=lsi.bytesRead;
  }
        return dserr;
}

NWDSCCODE NWCXGetContextLoginScript(NWDSContextHandle ctx,const NWDSChar* objectName,
                                        char* buffer, int * len,int maxlen){
// PP: I am not too happy with this code, but it seems to works even if current user's have a
// Default Name context in environnment of a .nwclient file.

        char context[MAX_DN_BYTES+1];
        char fqdn[MAX_DN_BYTES+1];
        NWDSCCODE dserr;
        NWDSContextHandle ctx2;

        if (!objectName) return ERR_NULL_POINTER;
        // we must convert relative name to a FQDN and then climb up contexts up to root
         dserr= NWDSCanonicalizeName(ctx,objectName,fqdn);
        if (!dserr) {
                // we must duplicate context handle and have a [Root] name context there
                dserr= NWDSDuplicateContextHandle(ctx, &ctx2);
                if (dserr)
                        return dserr;
                dserr=NWDSSetContext(ctx2, DCK_NAME_CONTEXT, "[Root]");
                if (dserr)  {
                        NWDSFreeContext(ctx2);
                        return dserr;
                }
                // let's climb up until we found one
                dserr=NWCXSplitNameAndContext(ctx2,fqdn,NULL,context);
                if (!dserr) {
                         //printf ("trying to get context LS of %s from %s\n",fqdn,context);
                        dserr=ERR_NO_SUCH_ATTRIBUTE;
                        while (dserr==ERR_NO_SUCH_ATTRIBUTE && context[0]) {
	                 //printf ("trying to get context LS from %s\n",context);
                                dserr=NWCXGetObjectLoginScript(ctx2,context,buffer,len,maxlen);
                                if (dserr)
                                   NWCXSplitNameAndContext(ctx2,context,NULL,context);
                        }
                }
	       NWDSFreeContext(ctx2);
         }
        return dserr;
}

NWDSCCODE NWCXGetProfileLoginScript(NWDSContextHandle ctx,const NWDSChar* objectName,
				    char* buffer, int * len, int maxlen){
	char profile[MAX_DN_BYTES+1];
	NWDSCCODE dserr;

	if (!objectName) {
		return ERR_NULL_POINTER;
	}

	dserr = NWCXGetStringAttributeValue(ctx, objectName, ATTR_PROFILE, profile, sizeof(profile));
	if (!dserr) {
#if 0
		printf ("reading Login Script of profile  %s for object %s\n",profile,objectName);
#endif
		dserr = NWCXGetObjectLoginScript(ctx, profile, buffer, len, maxlen);
	}
	return dserr;
}



/* read a single valued string attribute */
NWDSCCODE NWCXGetStringAttributeValue (NWDSContextHandle ctx,
                                        const NWDSChar* objectName,
                                        const NWDSChar* attrName,
                                        char* buffer, int maxlen) {
       struct attrop atlist[] = {
	        { attrName, 	docopy_string,  SYN_UNKNOWN,	maxlen},
	        { NULL,		NULL,		SYN_UNKNOWN,	0 }};
       NWDSCCODE dserr;


       if (!objectName )
                return ERR_NULL_POINTER;

       dserr=NWDSGetSyntaxID(ctx, attrName, &atlist[0].synt);
#ifdef DEBUG_PRINT
  printf ("err NWDSGetSyntaxID %s for object %s\n",attrName,strnwerror(dserr));
#endif
        if (dserr)
               return dserr;
        switch (atlist[0].synt) {
        	case SYN_COUNTER:
	        case SYN_INTEGER:
        	case SYN_INTERVAL:
	        case SYN_BOOLEAN:
                case SYN_TIME:
			dserr=EINVAL;
			break;
                default:
			dserr=ReadAttributesValues(ctx,objectName,buffer,atlist);
                        break;

       }
        return dserr;
}


/* read a single valued numeric/boolean attribute */
NWDSCCODE NWCXGetIntAttributeValue     (NWDSContextHandle ctx,
                                        const NWDSChar* objectName,
                                        const NWDSChar* attrName,
                                        int * value){
       int aux=0;
       struct attrop atlist[] = {
	        { attrName, 	docopy_int,     SYN_UNKNOWN,	sizeof(aux) },
	        { NULL,		NULL,		SYN_UNKNOWN,	0 }};
       NWDSCCODE dserr;


       if (!objectName )
                return ERR_NULL_POINTER;

       dserr=NWDSGetSyntaxID(ctx, attrName, &atlist[0].synt);
#ifdef DEBUG_PRINT
  printf ("err NWDSGetSyntaxID %s for object %s\n",attrName,strnwerror(dserr));
#endif
        if (dserr)
               return dserr;
        switch (atlist[0].synt) {
        	case SYN_COUNTER:
	        case SYN_INTEGER:
        	case SYN_INTERVAL:
	        case SYN_BOOLEAN:
                case SYN_TIME:
                        dserr=ReadAttributesValues(ctx,objectName,&aux,atlist);
                        break;
                default: dserr=EINVAL;
       }
       if (!dserr)
                *value= aux;
        return dserr;
}

/* read a multi valued string attribute */
/*does check for syntax flag being DS_STRING and ! DS_SINGLE */
/* caller MUST free the buffer created here */
NWDSCCODE NWCXGetMultiStringAttributeValue (NWDSContextHandle ctx,
                                        const NWDSChar* objectName,
                                        const NWDSChar* attrName,
                                        char** buffer) {
	struct attrop atlist[] = {
		{ attrName, 	docopy_multistring,     SYN_UNKNOWN,	MAX_DN_BYTES + 1},
		{ NULL,		NULL,		        SYN_UNKNOWN     ,0 }};
	NWDSCCODE dserr;
	struct strlist *l=NULL;

	if (!objectName )
		return ERR_NULL_POINTER;

	dserr=NWDSGetSyntaxID(ctx, attrName, &atlist[0].synt);
#ifdef DEBUG_PRINT
  printf (" NWDSGetSyntaxID (%d) %s for object %s returned :%s\n",atlist[0].synt,attrName,objectName,strnwerror(dserr));
#endif
	if (dserr)
		return dserr;

	l = initlist();
	if (!l)
		return ENOMEM;

        switch (atlist[0].synt) {
        	case SYN_COUNTER:
	        case SYN_INTEGER:
        	case SYN_INTERVAL:
	        case SYN_BOOLEAN:
                case SYN_TIME:
			dserr=EINVAL;
			break;
                default:
                        dserr=ReadAttributesValues(ctx,objectName,l,atlist);
                        break;

	}
        if (!dserr) {
		dserr=strlist_to_string(l,buffer,',');
        }
        freelist(l);
        return dserr;
}



/* read & convert to string a single valued attribute */
NWDSCCODE NWCXGetAttributeValueAsString (NWDSContextHandle ctx,
                                        const NWDSChar* objectName,
                                        const NWDSChar* attrName,
                                        char* buffer, size_t maxlen) {

        enum SYNTAX synt;
        int auxint=0;
        NWDSCCODE dserr;
        char aux[128];

        dserr=NWDSGetSyntaxID(ctx, attrName, &synt);
#ifdef DEBUG_PRINT
  printf ("NWDSGetSyntaxID: %s %s %d\n",strnwerror(dserr),attrName,synt);
#endif

       if (dserr)
                return dserr;
       switch (synt) {
        	case SYN_COUNTER:
	        case SYN_INTEGER:
        	case SYN_INTERVAL:
	        case SYN_BOOLEAN:
                        dserr=NWCXGetIntAttributeValue(ctx,objectName,attrName,&auxint);
                        if (!dserr) {
                                if (synt == SYN_BOOLEAN) {
					sprintf(aux,"%s", (auxint == 0)?"false":"true");
				} else {
					sprintf(aux,"%u",auxint);
				}
                                if (strlen(aux)<maxlen)
                                        strcpy(buffer,aux);
                                else dserr=NWE_BUFFER_OVERFLOW;
                        }
                        break;
		case SYN_TIME:
                        dserr=NWCXGetIntAttributeValue(ctx,objectName,attrName,&auxint);
                        if (!dserr) {
				time_t t;
				
				t = auxint;
				sprintf(aux,"%s", ctime(&t));
                                /* remove LF added by ctime */
                                if (strlen(aux)>0 && aux[strlen(aux)-1]=='\n')
                                	aux[strlen(aux)-1]=0;
                                if (strlen(aux)<maxlen)
                                        strcpy(buffer,aux);
                                else dserr=NWE_BUFFER_OVERFLOW;
                        }
                        break;
                default:
                        dserr=NWCXGetStringAttributeValue(ctx,objectName,attrName,buffer,maxlen);
       }
       return dserr;
}
#endif	/* NDS_SUPPORT */
