/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version
 * 2.1 of the License.
 *                                                                                  
 * This library 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
 * Lesser General Public License for more details.
 *                                                                                  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * END COPYRIGHT BLOCK **/
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <prio.h>
#include "psetc_pvt.h"
#include "libadminutil/admutil.h"
#include "libadminutil/distadm.h"
#include "libadminutil/srvutil.h"
#include "dbtadmutil.h"

#ifdef XP_WIN32
#define strcasecmp stricmp
#define strncasecmp _strnicmp
#endif
 
/* Max size for a pathname */
#ifndef PATH_MAX
#define PATH_MAX 512
#endif

#ifndef FILE_PATHSEP
#define FILE_PATHSEP '/'
#endif

extern Resource *admutil_i18nResource;
extern char *admutil_acceptLang;

/* returns true if the given path is a valid directory, false otherwise */
static int
is_dir_ok(const char *path)
{
	PRFileInfo prinfo;
	int ret = 0;

	if (path && *path &&
		(PR_SUCCESS == PR_GetFileInfo(path, &prinfo)) &&
		prinfo.type == PR_FILE_DIRECTORY) {
		ret = 1;
	}

	return ret;
}

/* returns full path and file name if the file was found somewhere, false otherwise
   file may not yet exist, but we will create it if the dir exists */
static char *
find_file_in_paths(
	const char *filename, /* the base filename to look for */
	const char *path /* path given by caller */
)
{
	char *retval = NULL;
    char *adminutilConfDir = getenv(ADMINUTIL_CONFDIR_ENV_VAR);

	/* try given path */
    if (!is_dir_ok(path)) {
        if (is_dir_ok(adminutilConfDir)) {
            path = adminutilConfDir;
        } else {
            return retval;
        }
    }
	retval = PR_smprintf("%s/%s", path, filename);

	return retval;
}

/*
 * PsetNode related data structure and methods
 */
PsetNodePtr
psetNodeCreate(char* attrName, LDAPMessage *ldapEntry)
{
  PsetNodePtr  psetNode;
  
  psetNode = (PsetNodePtr)PR_Malloc(sizeof(PsetNode));
  memset((void*)psetNode, 0, sizeof(PsetNode));
  if (*attrName == '\0') psetNode->attrName = "";
  else psetNode->attrName = PL_strdup(attrName);
  psetNode->attrLDAP = ldapEntry;
  psetNode->ldapHolder = NULL;
  psetNode->ldapDumpYard = NULL;
  psetNode->attrFile = NULL;
  psetNode->children = NULL;

  return psetNode;

}

void
psetNodeDestroy(PsetNodePtr target)
{
  ListNodePtr p;

  if (target->attrName && *(target->attrName) != '\0') PR_Free(target->attrName);
  if (target->ldapHolder) ldap_msgfree(target->ldapHolder);
  if (target->attrFile) treeRemoveTree(target->attrFile);
  if (target->children) {
    p = target->children;
    while(p) {
      psetNodeDestroy((PsetNodePtr)p->val);
      p = p->next;
    }
    listDestroy(target->children);
  }

  if (target->ldapDumpYard) {
    p = target->ldapDumpYard;
    while(p) {
      ldap_msgfree((LDAPMessage*)p->val);
      p = p->next;
    }
    listDestroy(target->ldapDumpYard);
  }

  PR_Free(target);
}

static void
psetDeletePtr(PsetPtr psetp)
{
  if (psetp) {
    if (psetp->info) psetNodeDestroy(psetp->info);
    if (psetp->ldapFilter) PR_Free(psetp->ldapFilter);
    if (psetp->ldunbindf) {
      if (psetp->ld) ldap_unbind_ext(psetp->ld, NULL, NULL);
    }
    if (psetp->configFile) PR_Free(psetp->configFile);
    if (psetp->sieDN) PR_Free(psetp->sieDN);
    if (psetp->binddn) PR_Free(psetp->binddn);
    if (psetp->bindpw) {
      memset(psetp->bindpw, 0, strlen(psetp->bindpw));
      PR_Free(psetp->bindpw);
    }
  
    PR_Free(psetp);
  }
}

void
psetNodeLDAPDestroy(PsetNodePtr target, LDAP *ld)
{
  ListNodePtr p;
  char        *nodeDN;
  int         ldaperror;
  
  if (target->children) {
    p = target->children;
    while(p) {
      psetNodeLDAPDestroy((PsetNodePtr)p->val, ld);
      p = p->next;
    }
    listDestroy(target->children);
  }

  if (ld) {
    nodeDN = ldap_get_dn(ld, target->attrLDAP);
    if (nodeDN) {
      if ( (ldaperror = ldap_delete_ext_s(ld, nodeDN, NULL, NULL)) != LDAP_SUCCESS ) {
#ifdef LDAP_DEBUG
        fprintf(stderr, "ldap_delete_s [%s] error %d:%s\n",
                nodeDN, ldaperror, ldap_err2string(ldaperror));
#endif
        ldap_memfree(nodeDN);
        /* Well, the destructor CAN NOT return status  */
        /*
          if (ldaperror == LDAP_INSUFFICIENT_ACCESS) return PSET_ACCESS_FAIL;
          else return PSET_SYSTEM_ERR;
          */
      }
      ldap_memfree(nodeDN);
    }
  }
  
  if (target->attrName && *(target->attrName) != '\0') PR_Free(target->attrName);
  if (target->ldapHolder) ldap_msgfree(target->ldapHolder);
  if (target->attrFile) treeRemoveTree(target->attrFile);

  if (target->ldapDumpYard) {
    p = target->ldapDumpYard;
    while(p) {
      ldap_msgfree((LDAPMessage*)p->val);
      p = p->next;
    }
    listDestroy(target->ldapDumpYard);
  }

}

int
psetNodeAddNameValue(PsetNodePtr target, char* name, char* val)
{
  TreeNodePtr targetNode;

  if (target->attrFile) {
    targetNode = treeAddNameValue(target->attrFile, name, val);
    if (!targetNode) return PSET_OP_FAIL;
    else return PSET_OP_OK;
  }
  else target->attrFile = createTreeNode(name, val);

  return PSET_OP_OK;
}

int
psetNodeAddNameMultiValue(PsetNodePtr target, char* name, char** vals)
{
  TreeNodePtr targetNode;
  char*       val = *vals++;

  if (target->attrFile) {
    targetNode = treeAddNameValue(target->attrFile, name, val);
    if (!targetNode) return PSET_OP_FAIL;
    else return PSET_OP_OK;
  }
  else {
    target->attrFile = createTreeNode(name, val);
    targetNode = target->attrFile;
  }

  while ((val = *vals++)) treeAddValue(targetNode, val);

  return PSET_OP_OK;
}

int
psetNodeModNameValue(PsetNodePtr target, char* name, char* val)
{
  TreeNodePtr targetNode, resultNode;

  if (target->attrFile) {
    if ((targetNode = treeFindNode(target->attrFile, name))) {
      listDestroy(targetNode->val);
      targetNode->val = NULL;
      resultNode = treeAddValue(targetNode, val);
      if (!resultNode) return PSET_OP_FAIL;
      else return PSET_OP_OK;
    }
    else {
      return psetNodeAddNameValue(target,
                                  name,
                                  val);
    }
  }
  else {
    target->attrFile = createTreeNode(name, val);
  }
  
  return PSET_OP_OK;
}

int
psetNodeModNameMultiValue(PsetNodePtr target, char* name, char** vals)
{
  TreeNodePtr targetNode, resultNode;
  char*       val;

  if ((target->attrFile) &&
      (targetNode = treeFindNode(target->attrFile, name))) {
    listDestroy(targetNode->val);
    targetNode->val = NULL;
    while ((val = *vals++)) treeAddValue(targetNode, val);
  }
  else {
    val = *vals++;
    resultNode = createTreeNode(name, val);
    if (resultNode) {
      while ((val = *vals++)) treeAddValue(resultNode, val);
      if (target->attrFile) {
        treeAddNode(target->attrFile, resultNode);
      }
      else target->attrFile = resultNode;
    }
    else return PSET_OP_FAIL;
  }
  return PSET_OP_OK;
}

int
psetNodeDelAttr(PsetNodePtr target, char* name)
{
  int remove, result = PSET_OP_OK;

  if (target->attrFile) {
    result = treeRemoveNode(target->attrFile, name, &remove);
    if (remove) {
      PR_Free(target->attrFile);
      target->attrFile = NULL;
    }
  }

  return result;
}

int
psetNodeAddChild(PsetNodePtr target, PsetNodePtr child)
{
  ListNodePtr node;

  node = createListNode(child->attrName, child, 0);
  if (!target->children) target->children = node;
  else target->children = listCat(target->children, node);

  return PSET_OP_OK;
}

int
psetNodeRemoveChild(PsetNodePtr target, char*  childName)
{
  if (!target->children) return PSET_NO_DATA;
  else target->children = listDelete(target->children, childName);
  return PSET_OP_OK;
}

AttrNameList
psetNodeGetChildren(PsetNodePtr target, int* errorcode)
{
  ListNodePtr node;
  AttrNameList nl;
  int         i;

  *errorcode = PSET_OP_OK;
  node = target->children;
  nl = createAttrNameList(listCount(node));
  i = 0;
  while (node) {
    addName(nl, i++, node->name);
    node = node->next;
  }
  return nl;
}

int
psetNodeSetLDAP(PsetNodePtr target, LDAPMessage *entry)
{
  ListNodePtr   ldapHolder;


  if (target->ldapHolder) {
    ldapHolder = createListNode("dummy", (void*)(target->ldapHolder), 0);
    if (target->ldapDumpYard) 
      target->ldapDumpYard = listCat(target->ldapDumpYard, ldapHolder);
    else target->ldapDumpYard = ldapHolder;
  }

  target->ldapHolder = entry;

  return PSET_OP_OK;
}

PsetNodePtr
psetNodeFindNode(PsetNodePtr nodePtr, LDAP *ld, char *name, int* nodeFlag, 
                 int* errorcode)
{
  ListNodePtr node;
  int         dummy, nodeNameLen = PL_strlen(nodePtr->attrName);
  int         nameLen;
  PsetNodePtr result;
  char        *attrName;
  struct berval        **vals;

   *errorcode = PSET_OP_OK;
   *nodeFlag = 1;
  
  if (!name) {
    *errorcode = PSET_OP_FAIL;
    return NULL;
  }
  if (*name == '\0') {
    if (nodePtr->attrName[0] == '\0') return nodePtr;
    else {
      *errorcode = PSET_OP_FAIL;
      return NULL;
    }
  }

  nameLen = PL_strlen(name);

  if (nameLen < nodeNameLen) {
    *errorcode = PSET_OP_FAIL;
    return NULL;
  }

  /* It's me!!!  */
  if (!strcasecmp(nodePtr->attrName, name)) return nodePtr;


  if (!strncasecmp(nodePtr->attrName, name, nodeNameLen)) {
    node = nodePtr->children;
    while (node) {
      if ((result = psetNodeFindNode((PsetNodePtr)(node->val), ld, name,
                                    nodeFlag, &dummy))) {
        *errorcode = PSET_OP_OK;
        return result;
      }
      node = node->next;
    }

    /* It is not children! Is it attribute of my entry? */
    attrName = name+nodeNameLen;
    if (*attrName == '.') attrName++;
    if (nodePtr->attrLDAP) {
      if ((vals = ldap_get_values_len(ld, nodePtr->attrLDAP, attrName)) != NULL ) {
        ldap_value_free_len(vals);
        *nodeFlag = 0;
        return nodePtr;
      }
    }
    else if (nodePtr->attrFile) {
      if (treeFindNode(nodePtr->attrFile, attrName)) {
        *nodeFlag = 0;
        return nodePtr;
      }
    }
    *errorcode = PSET_OP_FAIL;
    return NULL;
  }

  *errorcode = PSET_OP_FAIL;
  return NULL;
}


ValueType
psetNodeFindValue(PsetNodePtr nodePtr, LDAP *ld, char *name, int* errorcode)
{
  struct berval          **vals;
  ValueType     val;

  *errorcode = PSET_OP_OK;

  if (nodePtr->attrLDAP) {
    if (( vals = ldap_get_values_len(ld, nodePtr->attrLDAP, name )) != NULL ) {
      val = admutil_strsdup_berval(vals);
      ldap_value_free_len(vals);
      return val;
    }
    /* ldap value not available  */
    *errorcode = PSET_NO_VALUE;
    return NULL;
  }
  else if (nodePtr->attrFile) {
    val = treeFindValue(nodePtr->attrFile, name);

    if (val) return val;

    /* No value available in local cache */
    *errorcode = PSET_NO_VALUE;
    return NULL;
  }
  return NULL;
}


PsetNodePtr
psetNodeBuildNodes(PsetNodePtr nodePtr, char *nodeName, int* errorcode)
{
  ListNodePtr   node;
  int           attrNameLen = 0;
  PsetNodePtr   tmpNode = NULL;
  char          namebuf[PATH_MAX], *newName, *tmpPtr;

  *errorcode = PSET_OP_OK;

  PR_snprintf(namebuf, sizeof(namebuf), "%s", nodeName);
  
  if (nodePtr->attrName[0] != '\0') {
    /* Is this node my descendent?  */
    /* If not, return NULL */
    attrNameLen = PL_strlen(nodePtr->attrName);
    if (strncasecmp(nodePtr->attrName, nodeName, attrNameLen)) return NULL;
    newName = namebuf+attrNameLen;
    if (*newName != '.') return NULL;
    newName++;
  }
  else {
    /* I am root, everyone is my descendent */
    attrNameLen = 0;
    newName = namebuf;
  }

  /* figure out the possible name for child  */
  if ((tmpPtr = strchr(newName, '.'))) *tmpPtr = '\0';
      
  node = nodePtr->children;
  while (node) {
    if (!strcasecmp(node->name, namebuf)) {
      tmpNode = (PsetNodePtr)(node->val);
      break;
    }
    node = node->next;
  }

  if (!tmpNode) {
    tmpNode = psetNodeCreate(namebuf, NULL);
    psetNodeAddChild(nodePtr, tmpNode);
  }

  /* Do I need to move downward?  */
  if (!strcasecmp(nodeName, tmpNode->attrName)) return tmpNode;
  else return psetNodeBuildNodes(tmpNode, nodeName, errorcode);
}

PsetNodePtr
psetNodeFindParent(PsetNodePtr nodePtr, LDAP *ld, char *name, int* errorcode)
{
  char   parentName[PATH_MAX], *ptr;
  int    nodeFlag;

  *errorcode = PSET_OP_OK;

  PL_strncpyz(parentName, name, sizeof(parentName));
  
  ptr = strrchr(parentName, '.');

  if (!ptr) {
    /* Top level  */
    if (nodePtr->attrName[0] == '\0') return nodePtr;
    *errorcode = PSET_OP_FAIL;
    return NULL;
  }

  *ptr = '\0';

  return psetNodeFindNode(nodePtr, ld, parentName, &nodeFlag, errorcode);

}

ListNodePtr
psetNodeGetAll(PsetNodePtr psetNode, LDAP *ld, int deep, int* errorcode)
{
  BerElement          *ber;
  NameType            attrName;
  struct berval       **vals;
  ListNodePtr         resultList=NULL, tmpList, node;
  char                wholeName[PATH_MAX];

  *errorcode = PSET_OP_OK;

  if (psetNode->attrLDAP) {
    for (attrName = ldap_first_attribute(ld, psetNode->attrLDAP, &ber);
         attrName != NULL;
         attrName = ldap_next_attribute(ld, psetNode->attrLDAP, ber)) {
      if (strcmp( attrName, "objectclass") &&
          strcmp( attrName, "cn") &&
          strcmp( attrName, "aci") &&
          strcmp( attrName, "creatorsname") &&
          strcmp( attrName, "createtimestamp") &&
          strcmp( attrName, "modifytimestamp") &&
          strcmp( attrName, "modifiersname" )) {
        if ((vals = ldap_get_values_len(ld, psetNode->attrLDAP, attrName)) !=
            NULL) {
          if (psetNode->attrName[0] == '\0')
            PR_snprintf(wholeName, sizeof(wholeName), "%s", attrName);
          else
            PR_snprintf(wholeName, sizeof(wholeName), "%s.%s",
                        psetNode->attrName, attrName);
          if (vals[0] && vals[0]->bv_len && vals[0]->bv_val && *(vals[0]->bv_val) != '\0') {
            node = createListNode(wholeName,
                                  (void*)(admutil_strsdup_berval(vals)),
                                  0);
          }
          else {
            node = createListNode(wholeName, NULL, 0);
          }
          if (!resultList) resultList = node;
          else listCat(resultList, node);
          ldap_value_free_len( vals );
        }
      }
      ldap_memfree(attrName);
    }
    if ( ber != NULL ) ber_free( ber, 0 );
  }
  else if (psetNode->attrFile) {
    resultList = treeBuildAttrList(psetNode->attrName, psetNode->attrFile);
  }

  if (deep) {
    node = psetNode->children;
    while (node) {
      tmpList = psetNodeGetAll((PsetNodePtr)(node->val), ld, 1, errorcode);
      if (tmpList) {
        if (resultList)        listCat(resultList, tmpList);
        else resultList = tmpList;
      }
      node = node->next;
    }
  }
    
  return resultList;
}

/* just like psetNodeGetAll, but includes the "aci" attribute */
ListNodePtr
psetNodeGetAllACI(PsetNodePtr psetNode, LDAP *ld, int deep, int* errorcode)
{
  BerElement          *ber;
  NameType            attrName;
  struct berval       **vals;
  ListNodePtr         resultList=NULL, tmpList, node;
  char                wholeName[PATH_MAX];

  *errorcode = PSET_OP_OK;

  if (psetNode->attrLDAP) {
    for (attrName = ldap_first_attribute(ld, psetNode->attrLDAP, &ber);
         attrName != NULL;
         attrName = ldap_next_attribute(ld, psetNode->attrLDAP, ber)) {
      if (strcmp( attrName, "objectclass") &&
          strcmp( attrName, "cn") &&
          strcmp( attrName, "creatorsname") &&
          strcmp( attrName, "createtimestamp") &&
          strcmp( attrName, "modifytimestamp") &&
          strcmp( attrName, "modifiersname" )) {
        if ((vals = ldap_get_values_len(ld, psetNode->attrLDAP, attrName)) !=
            NULL) {
          if (psetNode->attrName[0] == '\0')
            PR_snprintf(wholeName, sizeof(wholeName), "%s", attrName);
          else
            PR_snprintf(wholeName, sizeof(wholeName), "%s.%s",
                        psetNode->attrName, attrName);
          if (vals[0] && vals[0]->bv_len && vals[0]->bv_val && *(vals[0]->bv_val) != '\0') {
            node = createListNode(wholeName,
                                  (void*)(admutil_strsdup_berval(vals)),
                                  0);
          }
          else {
            node = createListNode(wholeName, NULL, 0);
          }
          if (!resultList) resultList = node;
          else listCat(resultList, node);
          ldap_value_free_len( vals );
        }
      }
      ldap_memfree(attrName);
    }
    if ( ber != NULL ) ber_free( ber, 0 );
  }
  else if (psetNode->attrFile) {
    resultList = treeBuildAttrList(psetNode->attrName, psetNode->attrFile);
  }

  if (deep) {
    node = psetNode->children;
    while (node) {
      tmpList = psetNodeGetAll((PsetNodePtr)(node->val), ld, 1, errorcode);
      if (tmpList) {
        if (resultList)        listCat(resultList, tmpList);
        else resultList = tmpList;
      }
      node = node->next;
    }
  }
    
  return resultList;
}

int
psetNodeLDAPUpdate(PsetNodePtr nodePtr, LDAP *ld, char* ldFilter, LDAPMod **mods)
{
  char   *nodeDN, *filter;
  int    ldaperror;
  LDAPMessage *result, *e;

  if (!ld) return PSET_LOCAL_MODE;

  nodeDN = ldap_get_dn(ld, nodePtr->attrLDAP);
  if (!nodeDN) return PSET_OP_FAIL;


  if ( (ldaperror = ldap_modify_ext_s(ld, nodeDN, mods, NULL, NULL)) != LDAP_SUCCESS ) {
#ifdef LDAP_DEBUG
    fprintf(stderr, "ldap_modify_s: [%s] error %d:%s\n",
            nodeDN, ldaperror, ldap_err2string(ldaperror));
#endif
    ldap_memfree(nodeDN);
    if (ldaperror == LDAP_INSUFFICIENT_ACCESS) return PSET_ACCESS_FAIL;
    /* attempt to add an attribute not part of the entry's objectclasses/schema */
    else if (ldaperror == LDAP_OBJECT_CLASS_VIOLATION) return PSET_ATTR_NOT_ALLOWED;
    else return PSET_SYSTEM_ERR; /* error code return here */
  }

  if (ldFilter) filter = ldFilter;
  else filter = "(objectclass=*)";
  if ((ldaperror = ldap_search_ext_s(ld, nodeDN, LDAP_SCOPE_BASE,
                                     filter, NULL, 0, NULL, NULL, NULL, -1, &result)) 
       != LDAP_SUCCESS ) {
#ifdef LDAP_DEBUG
    fprintf(stderr, "ldap_search_s: [%s:%s] error %d:%s\n",
            nodeDN, filter, ldaperror, ldap_err2string(ldaperror));
#endif
    ldap_memfree(nodeDN);
    if (ldaperror == LDAP_INSUFFICIENT_ACCESS) return PSET_ACCESS_FAIL;
    return PSET_SYSTEM_ERR;
  }
  
  if (ldap_count_entries(ld, result) == 0) {
    /* error return : entry does not exist */
    ldap_memfree(nodeDN);
    return PSET_ENTRY_NOT_EXIST;
  }

  e = ldap_first_entry(ld, result);
  nodePtr->attrLDAP = e;
  psetNodeSetLDAP(nodePtr, result);
  ldap_memfree(nodeDN);
  return PSET_OP_OK;
}

int
psetNodeFileExport(PsetNodePtr nodePtr, LDAP *ld, FILE *fstream)
{
  ListNodePtr  resultList, listPtr;
  char         *cptr, *sptr, *valBuf = NULL;
  ValueType    valList;
  int          errorCode = PSET_OP_OK, valLen = 0, prevValLen = 0, i = 0;

  resultList = psetNodeGetAll(nodePtr, ld, 1, &errorCode);

  if (!errorCode) {
    listPtr = resultList;
    while (listPtr) {
      if (listPtr->val) {
        valList = (ValueType)(listPtr->val);
        i = 0;
        while (valList[i] != NULL) {
          fprintf(fstream, "%s: ", listPtr->name);
          valLen = PL_strlen(valList[i]);
          if (valLen > prevValLen) {
            valBuf = (char *)PR_Realloc(valBuf, valLen + 1); /*+1 for ending '\0'*/
            prevValLen = valLen;
          }
          if (!valBuf) {
            errorCode = PSET_SYSTEM_ERR;
            goto bail;
          }
          PR_snprintf(valBuf, valLen + 1, "%s", valList[i]);
          sptr = valBuf;
          while ((cptr = strchr(sptr, '\n'))) {
            *cptr++ = '\0';
            fprintf(fstream, "%s\n ", sptr);
            sptr=cptr;
          }
          fprintf(fstream, "%s\n", sptr);
          i++;
        }
      }
      listPtr = listPtr->next;
    }
bail:
    nvlistDestroy(resultList);
  }

  PR_Free(valBuf);

  return errorCode;
}
  
/* retrieve and populate the entries from LDAP server*/
int
psetLDAPRefresh(PsetPtr pset)
{
  int         ldaperror, errorCode = PSET_OP_OK;
  LDAPMessage *e, *result;
  char        *dn, *nodeName = NULL;
  PsetNodePtr tmpNode, target;
  int         nodeFlag;
  char        *filter;

  if (pset->info->ldapHolder) {
    /* Free up memory  */
    ldap_msgfree(pset->info->ldapHolder);
    pset->info->ldapHolder = NULL;
  }

  if (pset->ldapFilter) filter = pset->ldapFilter;
  else filter = "(objectclass=*)";

  if ((ldaperror = ldap_search_ext_s(pset->ld, pset->sieDN, LDAP_SCOPE_SUBTREE,
                                     filter, NULL, 0, NULL, NULL, NULL, -1, &result)) 
       != LDAP_SUCCESS ) {
#ifdef LDAP_DEBUG
    fprintf(stderr, "ldap_search_s: [%s:%s] error %d:%s\n",
            pset->sieDN, filter, ldaperror, ldap_err2string(ldaperror));
#endif
    ldap_msgfree(result);
    if (ldaperror == LDAP_INSUFFICIENT_ACCESS) return PSET_ACCESS_FAIL;
    return PSET_SYSTEM_ERR;
  }
  
  if (ldap_count_entries(pset->ld, result) == 0) {
    /* error return : entry does not exist */
    ldap_msgfree(result);
    return PSET_ENTRY_NOT_EXIST;
  }

  for (e = ldap_first_entry(pset->ld, result); e != NULL;
       e = ldap_next_entry(pset->ld, e)) {
    dn = ldap_get_dn(pset->ld, e);
    nodeName = dn2AttrName(dn, pset->sieDN);
    ldap_memfree(dn);

    if (!nodeName || (nodeName[0] == '\0')) {
      psetNodeSetLDAP(pset->info, result);
      pset->info->attrLDAP = e;
    } else {
      target = psetNodeFindNode(pset->info, pset->ld, nodeName, &nodeFlag,
                                &errorCode);
      if (target) {
        if (target->ldapHolder) ldap_msgfree(target->ldapHolder);
        target->attrLDAP = e;
      }
      else {
        /* Building the tree of PsetNodes */
        errorCode = PSET_OP_OK;
        tmpNode = psetNodeBuildNodes(pset->info, nodeName, &errorCode);
        if (tmpNode) tmpNode->attrLDAP = e;
      }
      if (errorCode != PSET_OP_OK) break;
    }
    if (nodeName) PR_Free(nodeName);
  }
  return errorCode;
}

int
psetFileImportP(PsetPtr pset)
{
  FILE                *fstream;
  int                 errorCode = PSET_OP_OK, status;
  int                 lineno = 0, nameLen = 0, valLen = 0;
#ifdef XP_UNIX
  int                 fd;
  struct flock        flock_data;
#endif
  char                linebuf[1024];
  char                *name, *val, namebuf[128], *valBuf = NULL;
  char                *nodeName = NULL, *attrName = NULL, *valptr = NULL;
  int                 valBuf_len = 0;
  PsetNodePtr         lastPsetNode = NULL, target;
  PRStatus            prst = 0;
  PRFileInfo          prinfo;

  if (!pset->configFile) return PSET_LOCAL_OPEN_FAIL;
  if (!(fstream = fopen(pset->configFile, "r"))) 
    return PSET_LOCAL_OPEN_FAIL;
#ifdef XP_UNIX
  fd = fileno(fstream);
  flock_data.l_type = F_RDLCK;
  flock_data.l_whence = 0;
  flock_data.l_start = 0;
  flock_data.l_len = 0;

  if (fcntl(fd, F_SETLK, &flock_data) == -1) {
    fclose(fstream);
    fstream = NULL;
    return PSET_LOCAL_OPEN_FAIL;
  }
  prst = PR_GetFileInfo(pset->configFile, &prinfo);
  if (PR_SUCCESS != prst) {
    fclose(fstream);
    fstream = NULL;
    return PSET_LOCAL_OPEN_FAIL;
  }
  valptr = valBuf = (char *)PR_Malloc(prinfo.size + 1);
  if (NULL == valBuf) {
    fclose(fstream);
    fstream = NULL;
    return PSET_SYSTEM_ERR;
  }
  valBuf_len = prinfo.size;
#endif
  namebuf[0] = '\0';

  while(1) {
    /* read line */
    status = admutil_getline(fstream, sizeof(linebuf), lineno++, linebuf);
    switch (status) {
    case -1:
      /* Error on reading, SET ERRORCODE */
      if (pset->info->attrFile) treeRemoveTree(pset->info->attrFile);
      pset->info->attrFile = NULL;
      errorCode = PSET_SYSTEM_ERR;
      goto bailout;
      break;
    case 1:
      /* EOF, out of here */
      if (namebuf[0] != '\0') {
        if (!nodeName || (nodeName[0] == '\0')) {
          target = pset->info;
        } else if (lastPsetNode && !strcasecmp(nodeName, lastPsetNode->attrName)) {
          /* Is the last node? -> strcasecmp node name */
          target = lastPsetNode;
        } else {  /* No, Locate and build node -> psetnodeBuildNodes  */
          target = psetNodeBuildNodes(pset->info, nodeName, &errorCode);
        }

        /* psetNodeAddNameValue  */
        if (target) {
          errorCode = psetNodeAddNameValue(target, attrName, valBuf);
          lastPsetNode = target;
        }
#ifdef XP_UNIX
        flock_data.l_type = F_UNLCK;
        fcntl(fd, F_SETLK, &flock_data);
#endif
        fclose(fstream);
        fstream = NULL;
        if (errorCode) {
          goto bailout;
        }
        namebuf[0] = '\0';
        valptr = valBuf;
        *valptr = '\0';
      }          
      
      if (pset->fileRW) {
        errorCode = PSET_OP_OK;
      } else {
        errorCode = PSET_LOCAL_MODE;
      }
      goto bailout;
      break;
    default:
      /* seperate node and attribute */
      if (linebuf[0] == ' ') {
        /* This is continue of value  */
        /* Append value to value buffer  */
        val = &linebuf[1];
        valLen= PL_strlen(val);
        /* put back LF */
        *valptr++ = '\n';
        valBuf_len -= 1;
        PR_snprintf(valptr, valBuf_len, "%s", val);
        valBuf_len -= valLen;
        valptr += valLen;
      }
      else {      
        if (namebuf[0] != '\0') {
          if (!nodeName || (nodeName[0] == '\0')) {
            target = pset->info;
          } else if (lastPsetNode && !strcasecmp(nodeName, lastPsetNode->attrName)) {
            /* Is the last node? -> strcasecmp node name */
            target = lastPsetNode;
          } else {  /* No, Locate and build node -> psetnodeBuildNodes  */
            target = psetNodeBuildNodes(pset->info, nodeName, &errorCode);
          }

          /* psetNodeAddNameValue  */
          if (target) {
            errorCode = psetNodeAddNameValue(target, attrName, valBuf);
            lastPsetNode = target;
          }
          if (errorCode) {
            goto bailout;
          }
          namebuf[0] = '\0';
          valptr = valBuf;
          *valptr = '\0';
        }          
        name = strtok(linebuf, ":");
        nameLen = PL_strlen(name);
        val = linebuf+nameLen+1;
        PR_snprintf(namebuf, sizeof(namebuf), "%s", name);

        attrName = strrchr(namebuf, '.');

        if (!attrName) {
          attrName = namebuf;
          nodeName = "";
        }
        else {
          *attrName = '\0';
          attrName++;
          nodeName = namebuf;
        }

        /* Skip over the whitesapce  */
        while (*val && isspace(*val)) val++;

        if (*val == '\0') {
          /* Empty value, Ingore it */
          break;
        }
        /* Put value to value buffer */
        valLen= PL_strlen(val);
        PR_snprintf(valptr, valBuf_len, "%s", val);
        valptr += valLen;
        valBuf_len -= valLen;
      }
    }
  }
bailout:
  PR_Free(valBuf);
  return errorCode;
}

/* Export configuration to config file  */
int
psetFileExportP(PsetPtr pset)
{
  FILE                *fstream;
#ifdef XP_UNIX
  int                 fd;
  struct flock        flock_data;
#endif
  int                 errorCode = PSET_OP_OK;

  if (!(pset->configFile)) return PSET_LOCAL_OPEN_FAIL;
  if (!(fstream = fopen(pset->configFile, "w"))) {
    return PSET_LOCAL_OPEN_FAIL;
  }
#ifdef XP_UNIX
  fd = fileno(fstream);
  flock_data.l_type = F_WRLCK;
  flock_data.l_whence = 0;
  flock_data.l_start = 0;
  flock_data.l_len = 0;

  if (fcntl(fd, F_SETLK, &flock_data) == -1) {
    fclose(fstream);
    fstream = NULL;
    return PSET_LOCAL_OPEN_FAIL;
  }
#endif
  errorCode = psetNodeFileExport(pset->info, pset->ld, fstream);

#ifdef XP_UNIX
  flock_data.l_type = F_UNLCK;
  fcntl(fd, F_SETLK, &flock_data);
#endif
  fclose(fstream);
  fstream = NULL;

  return errorCode;
}


PsetNodePtr
psetRootFindNode(PsetRootPtr psetRoot, NameType name, PsetPtr* pset, 
                 int* nodeFlag, int* errorcode)
{

  PsetNodePtr   target = NULL;
  PsetPtr       *psets = psetRoot->fileInfo;
  PsetPtr       psetl;

  *errorcode = PSET_OP_FAIL;

  if (psetRoot->ldapInfo) {
    target = psetNodeFindNode(psetRoot->ldapInfo->info, 
                              psetRoot->ldapInfo->ld,
                              name,
                              nodeFlag,
                              errorcode);
    if (target) {
      *pset = psetRoot->ldapInfo;
      return target;
    }
  }

  if (psets) {
    while ((psetl = *psets++)) {
      target = psetNodeFindNode(psetl->info, 
                                NULL,
                                name,
                                nodeFlag,
                                errorcode);
      if (target) {
        *pset = psetl;
        return target;
      }
    }
  }

  return target;
}

int
psetRootModAttribute(PsetRootPtr psetRoot, int mode, NameType name,
                     ValueType val)
{
  PsetPtr     pset = NULL;
  LDAPMod     **mods;
  int         nodeFlag, errorcode;
  char        namebuf[256];
  char        *attrName, *nodeName;
  ValueType   valsptr;
  PsetNodePtr nodePtr;

  PR_snprintf(namebuf, sizeof(namebuf), "%s", name);
  attrName = strrchr(namebuf, '.');
  if (!attrName) {
    attrName = namebuf;
    nodeName = "";
  }
  else {
    *attrName = '\0';
    attrName++;
    nodeName = namebuf;
  }

  nodePtr = psetRootFindNode(psetRoot, nodeName, &pset, &nodeFlag, &errorcode); 
  if (!nodePtr) return errorcode;

  if (!(pset->info->ldapHolder) && 
      (pset->configFile) && 
      !(pset->fileRW)) 
    return PSET_LOCAL_MODE;
                               
  valsptr = psetNodeFindValue(nodePtr, pset->ld, attrName, &errorcode);

  switch (mode) {
  case LDAP_MOD_REPLACE:
  case LDAP_MOD_DELETE:
    if (!valsptr) return PSET_ATTR_NOT_EXIST;
    deleteValue (valsptr);
    break;
  case LDAP_MOD_ADD:
    if (valsptr) {
      deleteValue (valsptr);
      return PSET_ATTR_EXIST;
    }
    break;
  default:
    deleteValue (valsptr);
    return PSET_ILLEGAL_OP;
  }

  if (pset->info->ldapHolder) {
    /* Modify LDAP entry */
    /* construct the list of modifications to make */
    mods = (LDAPMod**)PR_Malloc(2*sizeof(LDAPMod*));
 
    mods[0] = createMod(attrName, val, mode);
    mods[1] = NULL;
  
    /* make the change */
    errorcode = psetNodeLDAPUpdate(nodePtr, pset->ld, pset->ldapFilter, mods);
    deleteMods(mods);
  }
  else {
    /* Modify Node entry directly (file mode) */
    switch (mode) {
    case LDAP_MOD_REPLACE:
      errorcode = psetNodeModNameMultiValue(nodePtr, attrName, val);
      break;
    case LDAP_MOD_DELETE:
      errorcode = psetNodeDelAttr(nodePtr, attrName);
      break;
    case LDAP_MOD_ADD:
      errorcode = psetNodeAddNameMultiValue(nodePtr, attrName, val);
    }
  }

  if (!errorcode) psetFileExportP(pset);
  return errorcode;
}

/* Add a list of attributes to the pset entry */
int
psetRootModAttrList(PsetRootPtr psetRoot, int mode, AttributeList nvl)
{
  PsetPtr       pset = NULL;
  int           entries = 0, i = 0, errorcode = PSET_OP_OK;
  LDAPMod       **mods;
  PsetNodePtr   target;
  ListNodePtr   updateList = NULL, attrList, nodePtr, attrPtr;
  int           nodeFlag;
  ValueType     val;

  updateList = createUpdateList(nvl);
  nodePtr = updateList;
  while  (nodePtr) {
    target = psetRootFindNode(psetRoot, nodePtr->name, &pset,
                              &nodeFlag, &errorcode);
    if ((pset) && !(pset->info->ldapHolder) && 
        (pset->configFile) && 
        !(pset->fileRW)) {
      destroyUpdateList(updateList);
      return PSET_LOCAL_MODE;
    }

    if (target && nodeFlag && pset) {
      attrList = (ListNodePtr)nodePtr->val;

      /* Check the existence of the attribute */
      attrPtr = attrList;
      while (attrPtr) {
        val = psetNodeFindValue(target, pset->ld, attrPtr->name,
                                &errorcode);
        switch (mode) {
        case LDAP_MOD_REPLACE:
        case LDAP_MOD_DELETE:
          if (!val) {
            /* Arrtibute does not exist */
            destroyUpdateList(updateList);
            return PSET_ATTR_NOT_EXIST;
          }
          else deleteValue (val);
          break;
        case LDAP_MOD_ADD:
          if (val) {
            /* Attribute already exist */
            deleteValue (val);
            destroyUpdateList(updateList);
            return PSET_ATTR_EXIST;
          }
          break;
        default:
          deleteValue (val);
          destroyUpdateList(updateList);
          return PSET_ILLEGAL_OP;
        }
        attrPtr = attrPtr->next;
      }

      if (pset->info->ldapHolder) {
        /* OK, make real LDAP update */
        entries = listCount(attrList);
        mods = (LDAPMod**)PR_Malloc((entries+1)*sizeof(LDAPMod*));
        i = 0;
        attrPtr = attrList;
        while (attrPtr) {
          mods[i++] = createMod(attrPtr->name, (ValueType)attrPtr->val, mode);
          attrPtr = attrPtr->next;
        }
        mods[i] = NULL;
        errorcode = psetNodeLDAPUpdate(target, pset->ld, pset->ldapFilter, mods);
        deleteMods(mods);
      }
      else {
        /* Modify Node entry directly (file mode) */
        attrPtr = attrList;
        while (attrPtr) {        
          switch (mode) {
          case LDAP_MOD_REPLACE:
            errorcode = psetNodeModNameMultiValue(target, 
                                                  attrPtr->name,
                                                  (ValueType)attrPtr->val);
            break;
          case LDAP_MOD_DELETE:
            errorcode = psetNodeDelAttr(target, attrPtr->name);
            break;
          case LDAP_MOD_ADD:
            errorcode = psetNodeAddNameMultiValue(target, 
                                                  attrPtr->name,
                                                  (ValueType)attrPtr->val);
          }
          attrPtr = attrPtr->next;
        }
      }
    }
    else {
      errorcode = PSET_NOT_ENTRY;
      destroyUpdateList(updateList);
      if (errorcode) {
        return errorcode;
      }
    }
    nodePtr=nodePtr->next;
  }
  if (pset) {
    psetFileExportP(pset);
  }
  destroyUpdateList(updateList);
  return errorcode;
}

/*
  PsetHndl
  psetCreate(char *serverID, char* configRoot, char* userDN, char* passwd,
           int* errorcode)
 */
/*
   The configRoot directory is expected to contain the adm.conf file
   which we use to bootstrap our connection to the ldap server, and
   the local.conf file which holds the read-only cached copy of our real
   config information stored in the directory.  If the local.conf file is
   not found there, look for it under NETSITE_ROOT/serverID/config.
 */

PR_IMPLEMENT(PsetHndl)
psetCreate(char* serverID, char* configRoot, char* user, char* passwd,
           int* errorcode)
{
  PsetHndl      pset = NULL;
  AdmldapInfo   ldapInfo= NULL;
  char          *path = NULL, *ldapHost=NULL, *sieDN = NULL;
  char          *userDN = NULL;
  char          *bindPasswd = NULL;
  int           ldapPort = -1;
  int           useLocalConf = 1;
  
  ldapInfo = admldapBuildInfo(configRoot, errorcode);

  if (!ldapInfo) return NULL;

  /* get LDAP info, default is localhost:389 */
  ldapHost = admldapGetHost(ldapInfo);
  ldapPort = admldapGetPort(ldapInfo);

  *errorcode = PSET_OP_OK;

  if (!ldapHost) ldapHost = PL_strdup("localhost");
  if (ldapPort < 0) ldapPort = 389;

  /* if server is admin-serv, then the ldapInfo sieDN is the correct sieDN */
  if (!serverID || !PL_strncasecmp(serverID, "admin-serv", strlen("admin-serv"))) {
    sieDN = admldapGetSIEDN(ldapInfo);
  } else { /* given other serverID */
    sieDN = findSIEDNByID(ldapInfo, serverID);
    if (!sieDN) {
      *errorcode = PSET_ENTRY_NOT_EXIST;
      goto done;
    }
    useLocalConf = 0; /* disallow local.conf creation - will overwrite admin server one */
    /* use psetRealCreate to pass in a conf file */
  }

  /* Get user dn and password  */
  if (!user) {
    ADM_GetUserDNString(errorcode, &user);
  }
  if (!user) {
    ADM_GetCurrentUsername(errorcode, &user);
  }
  /* if user is just attr val, get dn */
  userDN = admldapGetUserDN(ldapInfo, user);
  if (passwd) {
    bindPasswd = passwd;
  } else { /* passwd is NULL */
    bindPasswd = admldapGetSIEPWD(ldapInfo); /* duplicated; need to free */
    if (!bindPasswd) {
      ADM_GetCurrentPassword(errorcode, &bindPasswd); /* should not free */
      passwd = bindPasswd; /* setting this not to free bindPasswd */
    }
  }

  /* find local.conf file */
  if (useLocalConf &&
      !(path = find_file_in_paths("local.conf", configRoot))) {
      /* error - no valid file or dir could be found */
      *errorcode = PSET_ENV_ERR;
  }

  pset = psetRealCreate(ldapInfo, ldapHost, ldapPort, sieDN, userDN, bindPasswd, path,
                        errorcode);
done:
  PR_Free(ldapHost);
  PR_Free(sieDN);
  PR_smprintf_free(path);
  PR_Free(userDN);
  if (!passwd) {
    if (bindPasswd) {
      memset(bindPasswd, '\0', strlen(bindPasswd));
      PR_Free(bindPasswd);
      bindPasswd = NULL;
    }
  }
  destroyAdmldap(ldapInfo);
  return pset;
}


PR_IMPLEMENT(PsetHndl)
psetRealCreate(AdmldapInfo ldapInfo, char* ldapHost, int ldapPort, char* sieDN, char* userDN,
               char* passwd, char* configFile, int* errorcode)
{

  return psetRealLDAPImport(ldapInfo, NULL, ldapHost, ldapPort, sieDN, userDN,
                            passwd, configFile, NULL, errorcode);
}


PR_IMPLEMENT(PsetHndl)
psetFullCreate(LDAP *ld, char* sieDN, char* cacheFile, int ldunbindFlag,
               int* errorcode)
{
  return psetFullLDAPImportRef(NULL, ld, sieDN, cacheFile, NULL, ldunbindFlag,
                            errorcode, NULL, NULL);
}

PR_IMPLEMENT(PsetHndl)
psetFullCreateRef(LDAP *ld, char* sieDN, char* cacheFile, int ldunbindFlag,
                  int* errorcode, char *userDN, char *passwd)
{
  return psetFullLDAPImportRef(NULL, ld, sieDN, cacheFile, NULL, ldunbindFlag,
                            errorcode, userDN, passwd);
}

PR_IMPLEMENT(PsetHndl)
psetFileCreate(char* configFile, char* filter, int* errorcode)
{
  return psetFileImport(NULL, configFile, filter, 1, errorcode);
}

PR_IMPLEMENT(PsetHndl)
psetRealLDAPImport(AdmldapInfo ldapInfo, PsetHndl pseth, char* ldapHost, int ldapPort, char* sieDN, 
                   char* userDN, char* passwd, char* cacheFile, char* filter,
                   int* errorcode)
{
  LDAP          *ld = NULL;
  int           ldapError, unbindF = 0;
  PsetHndl      pset = NULL;

  if ((!ldapHost) || (ldapPort < 1) || (!sieDN)) {
    /* set error code to SIEDN not available */
    *errorcode = PSET_ARGS_ERROR;
    return pseth;
  }

  if (NULL != passwd) {
    ld = admutil_ldap_init(ldapInfo, NULL, ldapHost, ldapPort, 0, 0, NULL);
    /* authenticate to LDAP server*/
    if ((ldapError = admutil_ldap_bind(ld, userDN, passwd, LDAP_SASL_SIMPLE, NULL, NULL, NULL, NULL))
        != LDAP_SUCCESS ) {
      switch (ldapError) {
      case LDAP_INAPPROPRIATE_AUTH:
      case LDAP_INVALID_CREDENTIALS:
      case LDAP_INSUFFICIENT_ACCESS:
        /* authenticate failed: Should not continue */
  #ifdef LDAP_DEBUG
    fprintf(stderr, "ldap_bind: [%s] error %d:%s\n",
            userDN, ldapError, ldap_err2string(ldapError));
  #endif
        ldap_unbind_ext(ld, NULL, NULL);
        *errorcode = PSET_AUTH_FAIL;
        return pset;
      case LDAP_NO_SUCH_OBJECT:
      case LDAP_ALIAS_PROBLEM:
      case LDAP_INVALID_DN_SYNTAX:
        /* Not a good DN */
        ldap_unbind_ext(ld, NULL, NULL);
        *errorcode = PSET_ENTRY_NOT_EXIST;
        return pset;
      default:
        ldap_unbind_ext(ld, NULL, NULL);
        unbindF = 0;
        ld = NULL;
      }
    }
    else unbindF = 1;
  }

  /* Connect to the LDAP server  */
  pset = psetFullLDAPImportRef(pseth,
                            ld,
                            sieDN,
                            cacheFile,
                            filter,
                            unbindF,
                            errorcode, userDN, passwd);
  return pset;

}

PR_IMPLEMENT(PsetHndl)
psetFullLDAPImport(PsetHndl pseth, struct ldap *ld, char* sieDN,
                   char* cacheFile, char* filter, int ldunbindFlag,
                   int* errorcode) 
{
  return psetFullLDAPImportRef(pseth, ld, sieDN, cacheFile, filter, ldunbindFlag, errorcode, NULL, NULL);
}

PR_IMPLEMENT(PsetHndl)
psetFullLDAPImportRef(PsetHndl pseth, struct ldap *ld, char* sieDN,
                   char* cacheFile, char* filter, int ldunbindFlag,
                   int* errorcode, char *userDN, char *passwd)
{
  PsetRootPtr   psetRoot;
  PsetPtr       pset;

  if (!pseth) {
    psetRoot= (PsetRootPtr)PR_Malloc(sizeof(PsetRoot));
    memset((void*)psetRoot, 0, sizeof(PsetRoot));
  }
  else psetRoot = (PsetRootPtr) pseth;
  pset = (PsetPtr)PR_Malloc(sizeof(Pset));
  memset((void*)pset, 0, sizeof(Pset));
  psetRoot->ldapInfo = pset;

  if (cacheFile) pset->configFile = PL_strdup(cacheFile);
  if (sieDN) pset->sieDN = PL_strdup(sieDN);
  if (filter) pset->ldapFilter = PL_strdup(filter);
  pset->fileRW = 0;
  pset->ldunbindf = ldunbindFlag;

  if (ld && sieDN) {
    pset->ld = ld;

    if (userDN!=NULL)
      {
        psetSetLDAPReferalInfo(psetRoot, userDN, passwd);
      }

    /* Retrieve data  */
    pset->info = psetNodeCreate("", NULL);
    if ((*errorcode = psetLDAPRefresh(pset))) {
      /* error on retrieving data, can I use local config data? 
         Maybe not!!
         */
      psetDelete((PsetHndl)psetRoot);
      return NULL;
    }
    if (pset->info->ldapHolder) *errorcode = psetFileExportP(pset);
  }
    
  /* If LDAP is not available, get from localcache */
  if (!(pset->info) || !(pset->info->ldapHolder)) {
    /* Read the content of config file in case LDAP is not available */
    if (pset->configFile) {
      pset->info = psetNodeCreate("", NULL);
      *errorcode = psetFileImportP(pset);
    }
    else *errorcode = PSET_LOCAL_OPEN_FAIL;

    if (*errorcode && *errorcode != PSET_LOCAL_MODE) {
        psetDeletePtr(psetRoot->ldapInfo);        
        psetRoot->ldapInfo = NULL;
    }
  }

  return (PsetHndl)psetRoot;

}

PR_IMPLEMENT(PsetHndl)
psetFileImport(PsetHndl pseth, char* configFile, char* filter, int rw, int* errorcode)
{
  PsetRootPtr   psetRoot;
  PsetPtr       pset, *psets;
  int           n=0;

  if (!pseth) {
    psetRoot= (PsetRootPtr)PR_Malloc(sizeof(PsetRoot));
    memset((void*)psetRoot, 0, sizeof(PsetRoot));
  }
  else psetRoot= (PsetRootPtr)pseth;

  pset = (PsetPtr)PR_Malloc(sizeof(Pset));
  memset((void*)pset, 0, sizeof(Pset));

  if (psetRoot->fileInfo) {
    psets = psetRoot->fileInfo;
    while (psets++) n++;
  }
  psetRoot->fileInfo = (PsetPtr*)realloc(psetRoot->fileInfo,
                                         (n+2)*sizeof(PsetPtr));
  psetRoot->fileInfo[n] = pset;
  psetRoot->fileInfo[n+1] = NULL;

  if (configFile) {
    pset->configFile = PL_strdup(configFile);
    pset->fileRW = rw;
    pset->info = psetNodeCreate("", NULL);
    *errorcode = psetFileImportP(pset);
  }
  else *errorcode = PSET_ARGS_ERROR;

  if (*errorcode) {
    psetDelete((PsetHndl)psetRoot);
    psetRoot = NULL;
  }
  return (PsetHndl)psetRoot;
}

PR_IMPLEMENT(int)
psetDelete(PsetHndl pseth)
{
  PsetRootPtr psetRoot;
  PsetPtr pset, *psets;

  if (!pseth) return PSET_NULL_HANDLE;

  psetRoot = (PsetRootPtr)pseth;

  psetDeletePtr(psetRoot->ldapInfo);
  psetRoot->ldapInfo = NULL;

  psets = psetRoot->fileInfo;

  if (psets) {
    while ((pset= *psets++)) {
      if (pset->info) psetNodeDestroy(pset->info);
      if (pset->configFile) PR_Free(pset->configFile);
    }
  }

  PR_Free(psetRoot);
  
  return PSET_OP_OK;
}

/* Check the existence of the attribute name, it can be LDAP entry or LDAP
   attribute */
PR_IMPLEMENT(int)
psetCheckAttribute(PsetHndl pseth, NameType name)
{
  PsetRootPtr psetRoot;
  PsetPtr     pset;
  PsetNodePtr nodePtr;
  int         errorcode, nodeFlag;

  if (!pseth) return PSET_NULL_HANDLE;
  psetRoot = (PsetRootPtr)pseth;

  nodePtr = psetRootFindNode(psetRoot, name, &pset, &nodeFlag, &errorcode);

  if (errorcode || !nodePtr) return PSET_OP_FAIL;

  if (nodeFlag) return PSET_ENTRY_EXIST;
  else return PSET_ATTR_EXIST;

}

/* Retrieve the value of the atrribute based on the given attribute name */
PR_IMPLEMENT(ValueType)
psetGetAttrValue(PsetHndl pseth, NameType name, int* errorcode)
{
  PsetRootPtr psetRoot;
  PsetPtr     pset;
  PsetNodePtr nodePtr;
  int         nodeFlag;
  char*       attrName;
  ValueType   val;

  *errorcode = PSET_OP_OK;

  if (!pseth) {
    *errorcode = PSET_NULL_HANDLE;
    return NULL;
  }
  psetRoot = (PsetRootPtr)pseth;

  attrName = strrchr(name, '.');

  if (!attrName) attrName = name;
  else attrName++;

  nodePtr = psetRootFindNode(psetRoot, name, &pset, &nodeFlag, errorcode);

  if (*errorcode) return NULL;

  if (nodeFlag) {
    *errorcode = PSET_NOT_ATTR;
    return NULL;
  }

  val = psetNodeFindValue(nodePtr, pset->ld, attrName, errorcode);
  return val;
}


/* Find out what type of object the node described by "name" is */
PR_IMPLEMENT(ValueType)
psetGetObjectClass(PsetHndl pseth, NameType name, int* errorcode)
{
  NameType objectclass;
  ValueType value;
  int needFree = 0;

  if(strstr(name, ".objectclass")) {
    objectclass = name;
  } else {
    objectclass = (NameType)PR_smprintf("%s.objectclass", name);
    needFree = 1;
  }
  value = psetGetAttrValue(pseth, objectclass, errorcode);

  if (needFree) {
      PR_smprintf_free(objectclass);
  }

  return value;
}


/* Retrieve the value of the atrribute based on the given attribute name */
PR_IMPLEMENT(char*)
psetGetAttrSingleValue(PsetHndl pseth, NameType name, int* errorcode)
{
  char*       attrVal = NULL;
  ValueType   vals;

  *errorcode = PSET_OP_OK;

  vals = psetGetAttrValue(pseth, name, errorcode);

  if (*errorcode == PSET_OP_OK) {
    if (vals[0]) attrVal = PL_strdup(vals[0]);
    admutil_strsFree(vals);
  }
  return attrVal;

}

/* Retrieve the values of the atrributes based on the given attribute name list */
PR_IMPLEMENT(AttributeList)
psetGetAttrList(PsetHndl pseth, AttrNameList nl, int* errorcode)
{
  PsetRootPtr  psetRoot;
  PsetPtr      pset;
  PsetNodePtr  target;
  NameType     name;
  ListNodePtr  resultList = NULL, tmpList;
  AttributeList resultAttrList = NULL;
  int           nodeFlag, dummyError;
  char          *attrName;
  ValueType     val;

  *errorcode = PSET_OP_OK;
  
  if (!pseth) {
    *errorcode = PSET_NULL_HANDLE;
    return NULL;
  }
  psetRoot = (PsetRootPtr)pseth;

  while ((name = *nl++)) {
    target = psetRootFindNode(psetRoot, name, &pset, &nodeFlag, 
                              &dummyError);
    if (target) {
      if (nodeFlag) {
        tmpList = psetNodeGetAll(target, pset->ld, 1, errorcode);
        if (resultList) listCat(resultList, tmpList);
        else resultList = tmpList;
      }
      else {
        attrName = strrchr(name, '.');
        if (attrName) attrName++;
        else attrName = name;
        val = psetNodeFindValue(target, pset->ld, attrName, errorcode);
        if (resultList) listAppend(resultList, name, (void*)(val), 0);
        else resultList = createListNode(name, (void*)(val), 0);
      }
    }
    else {
      *errorcode = PSET_PARTIAL_OP;
    }
  }
      
  resultAttrList = nvlistConvert(resultList);
  nvlistDestroy(resultList);
  return resultAttrList;
}

/* Retrieve the values of the all atrributes */
PR_IMPLEMENT(AttributeList)
psetGetAllAttrs(PsetHndl pseth, NameType nodeName, int* errorcode)
{
  PsetRootPtr         psetRoot;
  PsetPtr             pset;
  PsetNodePtr         psetNode;
  ListNodePtr         tmpList;
  AttributeList       resultList;
  int                 nodeFlag;

  *errorcode = PSET_OP_OK;

  if (!pseth) {
    *errorcode = PSET_NULL_HANDLE;
    return NULL;
  }
  psetRoot = (PsetRootPtr)pseth;

  psetNode = psetRootFindNode(psetRoot, nodeName, &pset,
                              &nodeFlag, errorcode);

  if (!psetNode) return NULL;

  if (!nodeFlag) {
    *errorcode = PSET_NOT_ENTRY;
    return NULL;
  }

  tmpList = psetNodeGetAll(psetNode, pset->ld, 0, errorcode);

  resultList = nvlistConvert(tmpList);

  nvlistDestroy(tmpList);

  return resultList;
}

/* Retrieve the values of the all atrributes, including ACI's */
PR_IMPLEMENT(AttributeList)
psetGetAllAttrsACI(PsetHndl pseth, NameType nodeName, int* errorcode)
{
  PsetRootPtr         psetRoot;
  PsetPtr             pset;
  PsetNodePtr         psetNode;
  ListNodePtr         tmpList;
  AttributeList       resultList;
  int                 nodeFlag;

  *errorcode = PSET_OP_OK;

  if (!pseth) {
    *errorcode = PSET_NULL_HANDLE;
    return NULL;
  }
  psetRoot = (PsetRootPtr)pseth;

  psetNode = psetRootFindNode(psetRoot, nodeName, &pset,
                              &nodeFlag, errorcode);

  if (!psetNode) return NULL;

  if (!nodeFlag) {
    *errorcode = PSET_NOT_ENTRY;
    return NULL;
  }

  tmpList = psetNodeGetAllACI(psetNode, pset->ld, 0, errorcode);

  resultList = nvlistConvert(tmpList);

  nvlistDestroy(tmpList);

  return resultList;
}


/* Retrieve the values of the all children */
PR_IMPLEMENT(AttrNameList)
psetGetChildren(PsetHndl pseth, NameType nodeName, int* errorcode)
{
  PsetRootPtr         psetRoot;
  PsetPtr             pset;
  PsetNodePtr         psetNode;
  int                 nodeFlag;

  *errorcode = PSET_OP_OK;

  if (!pseth) {
    *errorcode = PSET_NULL_HANDLE;
    return NULL;
  }
  psetRoot = (PsetRootPtr)pseth;

  psetNode = psetRootFindNode(psetRoot, nodeName, &pset,
                              &nodeFlag, errorcode);

  if (!psetNode) return NULL;
  if (!nodeFlag) {
    *errorcode = PSET_NOT_ENTRY;
    return NULL;
  }

  return psetNodeGetChildren(psetNode, errorcode);
}

/* Set the value of given attribute */
PR_IMPLEMENT(int)
psetSetAttr(PsetHndl pseth, NameType name, ValueType val)
{
  PsetRootPtr     psetRoot;

  if (!pseth) return PSET_NULL_HANDLE;

  psetRoot = (PsetRootPtr)pseth;

  return psetRootModAttribute(psetRoot, LDAP_MOD_REPLACE, name, val);
}

/* Set the value of given single-valued attribute */
PR_IMPLEMENT(int)
psetSetSingleValueAttr(PsetHndl pseth, NameType name, char*  val)
{
  int result;
  ValueType vals;

  vals = (ValueType)PR_Malloc(2*sizeof(char*));
  vals[0] = val;
  vals[1] = NULL;

  result = psetSetAttr(pseth, name, vals);
  PR_Free (vals);

  return result;
}

/* Set the value of given attribute list */
PR_IMPLEMENT(int)
psetSetAttrList(PsetHndl pseth, AttributeList nvl)
{
  PsetRootPtr       psetRoot;

  if (!pseth) {
    return PSET_NULL_HANDLE;
  }
  psetRoot = (PsetRootPtr)pseth;

  return psetRootModAttrList(psetRoot, LDAP_MOD_REPLACE, nvl);
}
/* These functions can dynamically create/delete attributes from LDAP server,
   Not quite sure the usefulness of these functions  */

/* Add an attribute to the pset entry */
PR_IMPLEMENT(int)
psetAddAttribute(PsetHndl pseth, NameType name, ValueType val)
{
  PsetRootPtr     psetRoot;

  if (!pseth) return PSET_NULL_HANDLE;
  psetRoot = (PsetRootPtr)pseth;
  return psetRootModAttribute(psetRoot, LDAP_MOD_ADD, name, val);
}

/* Add a single valued attribute to the pset entry */
PR_IMPLEMENT(int)
psetAddSingleValueAttribute(PsetHndl pseth, NameType name, char* val)
{
  ValueType vals;
  int       result;

  vals = (ValueType)PR_Malloc(2*sizeof(char*));
  if (NULL == vals)
    return PSET_SYSTEM_ERR;
  vals[0] = val;
  vals[1] = NULL;

  result = psetAddAttribute(pseth, name, vals);
  PR_Free (vals);
  return result;
}

/* Add a list of attributes to the pset entry */
PR_IMPLEMENT(int)
psetAddAttrList(PsetHndl pseth, AttributeList nvl)
{
  PsetRootPtr       psetRoot;

  if (!pseth) {
    return PSET_NULL_HANDLE;
  }
  psetRoot = (PsetRootPtr)pseth;

  return psetRootModAttrList(psetRoot, LDAP_MOD_REPLACE, nvl);
}

/* Remove an attribute from pset entry */
PR_IMPLEMENT(int)
psetDeleteteAttribute(PsetHndl pseth, NameType name)
{
  PsetRootPtr     psetRoot;

  if (!pseth) return PSET_NULL_HANDLE;
  psetRoot = (PsetRootPtr)pseth;

  return psetRootModAttribute(psetRoot, LDAP_MOD_DELETE, name, NULL);
}

/* Remove a list of attributes from pset entry */
PR_IMPLEMENT(int)
psetDeleteteAttrList(PsetHndl pseth, AttrNameList nl)
{
  PsetRootPtr   psetRoot;
  int           i, cnt = 0;
  char          *name;
  AttrNameList  nlPtr;
  AttributeList nvl;

  if (!pseth) return PSET_NULL_HANDLE;
  psetRoot = (PsetRootPtr)pseth;

  nlPtr = nl;
  while (*nlPtr++) cnt++;

  nvl = createAttributeList(cnt);
  nlPtr = nl;
  i = 0;
  while ((name = *nlPtr++)) addAttribute(nvl, i++, name, NULL);
  return psetRootModAttrList(psetRoot, LDAP_MOD_DELETE, nvl);
}

PR_IMPLEMENT(int)
psetAddEntry(PsetHndl pseth, NameType parent, NameType name,
             AttrNameList objectclasses, AttributeList initList)
{
  PsetRootPtr  psetRoot;
  PsetPtr      pset;
  PsetNodePtr  parentPtr, nodePtr;
  LDAPMessage  *result, *e;
  LDAPMod      **mods, *obclsMod;
  char         *dn;
  int          ldaperror, i, cnt, nodeFlag, errorCode;
  char         absAttrName[PATH_MAX], **names, *filter;
  AttributePtr nv;
  AttributeList nvlPtr;

  
  if (!pseth) return PSET_NULL_HANDLE;
  psetRoot = (PsetRootPtr)pseth;
  if (!parent) return PSET_OP_FAIL;
  if (*parent != '\0')
    PR_snprintf(absAttrName, sizeof(absAttrName), "%s.%s", parent, name);
  else
    PR_snprintf(absAttrName, sizeof(absAttrName), "%s", name);

  parentPtr = psetRootFindNode(psetRoot, parent, &pset, &nodeFlag,
                               &errorCode);

  if (!parentPtr) return PSET_NO_PARENT;

  if (!nodeFlag) return PSET_NO_PARENT;

  if (errorCode) return PSET_NO_PARENT;

  if (!(pset->info->ldapHolder) &&
      !(pset->fileRW)) return PSET_LOCAL_MODE;

  if ((nodePtr = psetNodeFindNode(parentPtr, pset->ld, absAttrName, &nodeFlag,
                                 &errorCode))) {
    return PSET_ENTRY_EXIST;
  }

  errorCode = PSET_OP_OK;

  if (pset->ld) {
    int adderr;

    dn = attrName2dn(absAttrName, pset->sieDN);

    /* Build initial list */
    nvlPtr = initList;
    cnt = 0;
    while (*nvlPtr++) cnt++;
    mods = (LDAPMod**)PR_Malloc((cnt+3)*sizeof(LDAPMod*));
    names = (char**)PR_Malloc(2*sizeof(char*));
    if (NULL == mods || NULL == names)
        return PSET_SYSTEM_ERR;
    names[0] = PL_strdup(name);
    names[1] = NULL;
    mods[0] = createMod("cn", names, 0);
    PR_Free(names);
  
    obclsMod = (LDAPMod*)PR_Malloc(sizeof(LDAPMod));
    if (NULL == obclsMod)
        return PSET_SYSTEM_ERR;
    obclsMod->mod_op = 0;
    obclsMod->mod_type = "objectClass";
    obclsMod->mod_values = objectclasses;

    mods[1] = obclsMod;

    nvlPtr = initList;
    i = 2;
    while ((nv = *nvlPtr++))
      mods[i++] = createMod(nv->attrName, nv->attrVal, 0);
    mods[i] = NULL;

    if ( (adderr = ldap_add_ext_s (pset->ld, dn, mods, NULL, NULL)) == LDAP_SUCCESS ) {
      /* Refresh this node  */
      if (pset->ldapFilter) filter = pset->ldapFilter;
      else filter = "(objectclass=*)";
      if ((ldaperror = ldap_search_ext_s(pset->ld, dn, LDAP_SCOPE_BASE,
                                         filter, NULL, 0, NULL, NULL, NULL, -1, &result)) 
          != LDAP_SUCCESS ) {
#ifdef LDAP_DEBUG
        fprintf(stderr, "ldap_search_s: [%s:%s] error %d:%s\n",
                dn, filter, ldaperror, ldap_err2string(ldaperror));
#endif
        if (ldaperror == LDAP_INSUFFICIENT_ACCESS) return PSET_ACCESS_FAIL;
        return PSET_SYSTEM_ERR;
      }
  
      if (ldap_count_entries(pset->ld, result) == 0) {
        /* error return : entry does not exist */
        return PSET_ENTRY_NOT_EXIST;
      }
      /* create a PsetNode */
      e = ldap_first_entry(pset->ld, result);
      nodePtr = psetNodeCreate(absAttrName, e);
      psetNodeSetLDAP(nodePtr, result);

      /* add to the parent */
      psetNodeAddChild(parentPtr, nodePtr);
    }
    else {
#ifdef LDAP_DEBUG
      fprintf(stderr, "ldap_add_s: [%s] error %d:%s\n",
              dn, adderr, ldap_err2string(adderr));
#endif
      errorCode = PSET_OP_FAIL;
    }

    deleteMod(mods[0]);
    PR_Free(obclsMod);
    i=2;
    while (mods[i])  deleteMod(mods[i++]);

    PR_Free(mods);
  }
  else {
    nodePtr = psetNodeCreate(absAttrName, NULL);
    nvlPtr = initList;
    while ((nv = *nvlPtr++) && !errorCode) {
      errorCode = psetNodeAddNameMultiValue(nodePtr, 
                                            nv->attrName, 
                                            nv->attrVal);
    }
    /* add to the parent */
    psetNodeAddChild(parentPtr, nodePtr);
  }

  if (errorCode) return errorCode;
  else return psetFileExportP(pset);
}

PR_IMPLEMENT(int)
psetDeleteEntry(PsetHndl pseth, NameType name)
{
  PsetRootPtr  psetRoot;
  PsetPtr      pset;
  PsetNodePtr  target, parentPtr;
  int          nodeFlag, errorCode  = PSET_OP_OK;  

  if (!pseth) return PSET_NULL_HANDLE;

  psetRoot = (PsetRootPtr)pseth;

  target = psetRootFindNode(psetRoot, name, &pset, &nodeFlag,
                            &errorCode);
  if (!target || !nodeFlag) 
    {
      return PSET_ENTRY_NOT_EXIST;
    }

  if (pset->ld) {
    psetNodeLDAPDestroy(target, pset->ld);
  }
  else {
    if (!(pset->fileRW)) return PSET_LOCAL_MODE;
  }

  parentPtr = psetNodeFindParent(pset->info, pset->ld, name, &errorCode);

  errorCode = psetNodeRemoveChild(parentPtr, name);

  if (errorCode) return errorCode;

  return psetFileExportP(pset);
}

PR_IMPLEMENT(char*)
psetErrorString(int errorNum, char* lang, char *buffer, size_t bufsize, int *rc)
{
  char *acceptLang = lang;
  char* errorStr = NULL;

  if (buffer) {
      *buffer = '\0';
  }

  if (!acceptLang) acceptLang = admutil_acceptLang;

  switch (errorNum) {
  case PSET_OP_OK:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_OP_OK, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Operation OK";
    break;
  case  PSET_OP_FAIL:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_OP_FAIL, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Operation failed";
    break;
  case  PSET_SYSTEM_ERR:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_SYSTEM_ERR, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "System (OS/LDAP) related error";
    break;
  case  PSET_ENV_ERR:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ENV_ERR, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Environment variable error";
    break;
  case  PSET_ARGS_ERROR:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ARGS_ERROR, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Arguments error";
    break;
  case  PSET_NULL_HANDLE:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NULL_HANDLE, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Null handle";
    break;
  case  PSET_LOCAL_MODE:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_LOCAL_MODE, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Operated in local cache mode";
    break;
  case  PSET_LOCAL_OPEN_FAIL:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_LOCAL_OPEN_FAIL, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Failed to open local cache";
    break;
  case  PSET_AUTH_FAIL:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_AUTH_FAIL, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Authentication failed";
    break;
  case  PSET_ACCESS_FAIL:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ACCESS_FAIL, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Access failed - improper permission";
    break;
  case  PSET_ENTRY_NOT_EXIST:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ENTRY_NOT_EXIST, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Entry does not exist";
    break;
  case  PSET_ATTR_NOT_EXIST:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ATTR_NOT_EXIST, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Attribute does not exist";
    break;
  case  PSET_ENTRY_EXIST:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ENTRY_EXIST, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Entry exist";
    break;
  case  PSET_ATTR_EXIST:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ATTR_EXIST, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Attribute exist";
    break;
  case  PSET_NOT_ENTRY:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NOT_ENTRY, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Not an entry";
    break;
  case  PSET_NOT_ATTR:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NOT_ATTR, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Not an attribute";
    break;
  case  PSET_NO_LDAP_HNDL:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NO_LDAP_HNDL, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "NULL LDAP Handler";
    break;
  case  PSET_NO_DN:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NO_DN, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "No Distinguished Name";
    break;
  case  PSET_NO_DATA:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NO_DATA, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "No data";
    break;
  case  PSET_NO_VALUE:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NO_VALUE, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "No value";
    break;
  case  PSET_NO_PARENT:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NO_PARENT, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "No parent node";
    break;
  case  PSET_PARTIAL_GET:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_PARTIAL_GET, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Only get partial data";
    break;
  case  PSET_PARTIAL_OP:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_PARTIAL_OP, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Operation only success partially, some failed";
    break;
  case  PSET_ILLEGAL_OP:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ILLEGAL_OP, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Illegal operation";
    break;
  case  PSET_NOT_IMPLEMENT:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_NOT_IMPLEMENT, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Not implemented yet";
    break;
  case  PSET_ATTR_NOT_ALLOWED:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_ATTR_NOT_ALLOWED, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Attribute disallowed by entry objectclasses";
    break;
  default:
    if (admutil_i18nResource) 
      errorStr = res_getstring(admutil_i18nResource, DBT_pset_UNKNOWN_ERROR_NO, acceptLang, buffer, bufsize, rc);
    if (errorStr) return errorStr;
    else errorStr = "Unknown Error Number";
  }

  if (buffer) {
      PL_strncpyz(buffer, errorStr, bufsize);
      return buffer;
  }

  return PL_strdup(errorStr);
}

#if defined(USE_OPENLDAP)
static int
pset_ldap_rebind_proc(
	LDAP *ld, LDAP_CONST char *url,
	ber_tag_t request, ber_int_t msgid,
	void *arg)
{
  PsetPtr pset = (PsetPtr)arg;

  return admutil_ldap_bind(ld, pset->binddn, pset->bindpw, LDAP_SASL_SIMPLE, NULL, NULL, NULL, NULL);
}
#else /* NOT OPENLDAP */
static int
pset_ldap_rebind_proc (LDAP *ld, char **whop, char **passwdp,
                       int *authmethodp, int freeit, void *arg)
{
  PsetPtr pset = (PsetPtr)arg;

  if (freeit == 0) {
    *whop = pset->binddn;
    *passwdp = pset->bindpw;
    *authmethodp = LDAP_AUTH_SIMPLE;
  }

  return LDAP_SUCCESS;
}
#endif /* OPENLDAP */

/* Setting  up LDAP referal */
PR_IMPLEMENT(int)
psetSetLDAPReferalInfo(PsetHndl pseth, char* userDN, char* passwd)
{
  PsetRootPtr  psetRoot;
  PsetPtr  pset;

  if (!pseth) return PSET_NULL_HANDLE;

  psetRoot = (PsetRootPtr)pseth;
  pset = psetRoot->ldapInfo;

  if (!pset) return PSET_NULL_HANDLE;

  if (pset->binddn) PR_Free(pset->binddn);
  if (userDN) pset->binddn = PL_strdup(userDN);
  else  pset->binddn = NULL;
  if (pset->bindpw) {
    memset(pset->bindpw, 0, strlen(pset->bindpw));
    PR_Free(pset->bindpw);
  }
  if (passwd) pset->bindpw = PL_strdup(passwd);
  else pset->bindpw = NULL;

  if (pset->ld)
    ldap_set_rebind_proc(pset->ld, pset_ldap_rebind_proc, (void *)pset);

  return PSET_OP_OK;
}


void replace_dn(char **attribute_list, char *source_groupDN, char *dest_groupDN)
{
  char replace_string[BIG_LINE];
  char *temp_string;
  char *start_ptr, *end_ptr;

  int i;

  if((!attribute_list) ||
     (!source_groupDN) ||
     (!dest_groupDN))
    return;

  for(i=0; attribute_list[i]; i++) {
    int rep_str_len = 0;

    /* no matches */
    if(!strstr(attribute_list[i], source_groupDN))
      continue;

    /* exact match */
    if(!strcmp(attribute_list[i], source_groupDN)) {
      PR_Free(attribute_list[i]);
      attribute_list[i] = PL_strdup(dest_groupDN);
      continue;
    }

    /* at least one substring */
    replace_string[0] = '\0';
    temp_string = PL_strdup(attribute_list[i]);
    start_ptr = temp_string;
    end_ptr = strstr(temp_string, source_groupDN);

    while((start_ptr && *start_ptr != '\0') && 
          (end_ptr && *end_ptr != '\0')) {
      end_ptr[0] = '\0';
      rep_str_len = BIG_LINE - PL_strlen(replace_string);
      PL_strncat(replace_string, start_ptr, rep_str_len);
      rep_str_len = BIG_LINE - PL_strlen(replace_string);
      PL_strncat(replace_string, dest_groupDN, rep_str_len);
      start_ptr = end_ptr + PL_strlen(source_groupDN);
      if(start_ptr && *start_ptr != '\0')
        end_ptr = strstr(start_ptr, source_groupDN);
    }
    if(start_ptr && *start_ptr!= '\0') {
      rep_str_len = BIG_LINE - PL_strlen(replace_string);
      PL_strncat(replace_string, start_ptr, rep_str_len);
    }

    PR_Free(temp_string);
    PR_Free(attribute_list[i]);
    attribute_list[i] = PL_strdup(replace_string);
  }

  return;
}

int
psetRecursiveReplicate(PsetHndl source, PsetHndl dest, NameType name, char *source_groupDN, char *dest_groupDN)
{
  AttributeList nodeAttrs;
  ValueType nodeObjectClass;

  AttrNameList nodeChildren;

  int errorcode, count, rv;

  AttrNameList listName;
  AttributeList initList;
  AttributeList temp_list;
  AttributePtr attrPtr;
  

  NameType parent;
  char *temp_node_name, *temp_attr_name;

  nodeAttrs = psetGetAllAttrsACI(source, name, &errorcode);
  nodeObjectClass = psetGetObjectClass(source, name, &errorcode);
                                    
  /* create the duplicate - even if it exists */

  count=0;
  while(nodeObjectClass[count] != NULL) count++;
  listName = createAttrNameList(count);

  for(count=0; nodeObjectClass[count] != NULL; count++)
    addName(listName, count, nodeObjectClass[count]);
  deleteValue(nodeObjectClass);
  nodeObjectClass = NULL;

  count=0;
  temp_list = nodeAttrs;
  while(temp_list && (*temp_list++)) count++;
  initList = createAttributeList(count);

  temp_list = nodeAttrs;
  count=0;
  while(temp_list && (attrPtr = *temp_list++)) {
    if ((temp_attr_name = strrchr(attrPtr->attrName, '.')))
      temp_attr_name++;
    else
      temp_attr_name = attrPtr->attrName;
    /* replace source_groupDN with dest_groupDN */
    replace_dn(attrPtr->attrVal, source_groupDN, dest_groupDN);
    addAttribute(initList, count, temp_attr_name, attrPtr->attrVal);
    count++;
  }

  /* install it in the dest tree */

  parent = PL_strdup(name);
  if ((temp_node_name = strrchr(parent, '.'))) {
    temp_node_name[0] = '\0';
    temp_node_name++;
  }
  else {
    temp_node_name = name;
    PR_Free(parent);
    parent = PL_strdup("");
  }

  rv = psetAddEntry(dest, parent, temp_node_name, listName, initList);
  PR_Free(parent);

  /*
   * mergeConfig CGI :
   * PSET_ENTRY_EXIST is ok as the top level entries are already added from
   * the mergeConfig CGI.
   * PSET_LOCAL_OPEN_FAIL is ok as we do not need to save the created entry
   * into the local cache file. The entry is created in the remote config DS,
   * not in the current one so pset->configFile == NULL.
   */
  if (rv != PSET_OP_OK &&
      rv != PSET_ENTRY_EXIST &&
      rv != PSET_LOCAL_OPEN_FAIL) {
      return rv;
  }

  /* process the children */

  nodeChildren = psetGetChildren(source, name, &errorcode);
  while ((temp_node_name = *nodeChildren++)) {
    rv = psetRecursiveReplicate(source, dest, temp_node_name, source_groupDN, dest_groupDN);
    if (rv != PSET_OP_OK) {
        return rv;
    }
  }

  return PSET_OP_OK;

}

/*
 * psetReplicateSIE(PsetHndl source, PsetHndl target, char *dest_groupDN)
 *
 * Replicates all nodes from the SIE tree described in "source"
 * to the the SIE tree described in "dest".
 *
 * If the value of any attribute contains the source program group DN,
 * make sure that it's changed to the dest_groupDN (if the DN's are different).
 *
 * Note: This method is to be used only by the mergeConfig admin CGI because of
 * the custom error handling in the auxiliary method psetRecursiveReplicate().
 */

PR_IMPLEMENT(int)
psetReplicateSIE(PsetHndl source, PsetHndl dest, char *source_groupDN, char *dest_groupDN)
{
  char *nodeName;
  AttributeList nodeAttrs;
  ValueType nodeObjectClass;

  AttrNameList nodeChildren;
  NameType name;

  int errorcode, count, rv=PSET_OP_OK;

  AttrNameList listName;
  AttributeList initList;
  AttributeList temp_list;
  AttributePtr attrPtr;

  /* get the root */
  nodeName = PL_strdup("");

  nodeAttrs = psetGetAllAttrsACI(source, nodeName, &errorcode);
  nodeObjectClass = psetGetObjectClass(source, nodeName, &errorcode);
                                    
  /* create the duplicate - even if it exists */

  count=0;
  while(nodeObjectClass[count] != NULL) count++;
  listName = createAttrNameList(count);

  for(count=0; nodeObjectClass[count] != NULL; count++)
    addName(listName, count, nodeObjectClass[count]);
  deleteValue(nodeObjectClass);
  nodeObjectClass = NULL;

  count=0;
  temp_list = nodeAttrs;
  while(temp_list && (*temp_list++)) count++;  
  initList = createAttributeList(count);

  temp_list = nodeAttrs;
  count=0;
  while(temp_list && (attrPtr = *temp_list++)) {
    /* replace source_groupDN with dest_groupDN */
    replace_dn(attrPtr->attrVal, source_groupDN, dest_groupDN);
    addAttribute(initList, count, attrPtr->attrName, attrPtr->attrVal);
    count++;
  }

  /* install it in the dest tree */

  psetAddEntry(dest, "", nodeName, listName, initList);

  /* process the children */

  nodeChildren = psetGetChildren(source, nodeName, &errorcode);
  while((rv == PSET_OP_OK) && (name = *nodeChildren++)) {
    rv = psetRecursiveReplicate(source, dest, name, source_groupDN, dest_groupDN);
  }

  PR_Free(nodeName);

  return rv;
}

/*
  emacs settings
  Local Variables:
  c-basic-offset: 2
  End:
*/
