/** 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 "libadminutil/admutil.h"
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "ldap.h"
#include "libadminutil/distadm.h"
#include "libadmsslutil/psetcssl.h"
#include "libadmsslutil/srvutilssl.h"
#include "libadminutil/admutil-int.h"

#ifdef XP_WIN32
#define strcasecmp stricmp
#define strncasecmp _strnicmp
#endif

#ifndef PATH_MAX
#define PATH_MAX 256
#endif

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

PR_IMPLEMENT(PsetHndl)
psetRealLDAPImportSSL(AdmldapInfo ldapInfo, PsetHndl pseth, char* ldapHost, int ldapPort,
                      int secure, char* sieDN, char* userDN, char* passwd, 
                      char* configFile, char* filter, int* errorcode)
{
  LDAP          *ld = NULL;
  PsetHndl      psethndl = NULL;
  int           ldapError, unbindF = 0;

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

  /* Connect to the LDAP server  */
  if (NULL == passwd) { /* if passwd is not given, try info from client */
    ADM_GetCurrentPassword(errorcode, &passwd);
  }
  if (NULL != passwd) { /* if passwd is not given, let's use cache data */
    if (secure) ld = admutil_ldap_init(ldapInfo, NULL, ldapHost, ldapPort, 1, 0, NULL);
    else  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 */
        /* ldap_perror( pset->ld, "ldap_simple_bind_s" );  */
        ldap_unbind_ext(ld, NULL, NULL);
        *errorcode = PSET_AUTH_FAIL;
        return NULL;
      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 NULL;
      default:
        ldap_unbind_ext(ld, NULL, NULL);
        unbindF = 0;
        ld = NULL;
      }
    }
    else unbindF = 1;
  }

  psethndl = psetFullLDAPImportRef(pseth,
                                ld,
                                sieDN,
                                configFile,
                                filter,
                                unbindF,
                                errorcode, userDN, passwd);
  return psethndl;
}


PR_IMPLEMENT(PsetHndl)
psetRealCreateSSL(AdmldapInfo ldapInfo, char* ldapHost, int ldapPort, int secure, char* sieDN, 
                  char* userDN, char* passwd, char* configFile, 
                  int* errorcode)
{
  return psetRealLDAPImportSSL(ldapInfo, NULL, ldapHost, ldapPort, secure, sieDN,
                               userDN, passwd, configFile, NULL, errorcode);
}


PR_IMPLEMENT(PsetHndl)
psetCreateSSL(char* serverID, char* configRoot, char* user, char* passwd,
              int* errorcode)
{
  PsetHndl      pset = NULL;
  AdmldapInfo   ldapInfo= NULL;
  char          *path = NULL, *ldapHost=NULL, *sieDN = NULL, *bindPasswd = NULL;
  char          *userDN = NULL;
  int           ldapPort = -1, secure = 0;
  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");
  secure = admldapGetSecurity(ldapInfo);
  if (secure) {
    if (ldapPort < 0) ldapPort = 636;
  }
  else {
    if (ldapPort < 0) ldapPort = 389;
  }

  /* Get SIE */
  /* 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 = findSIEDNByIDSSL(ldapInfo, serverID);
    if (!sieDN) {
      *errorcode = PSET_ENTRY_NOT_EXIST;
      goto done;
    }
    useLocalConf = 0; /* disallow local.conf creation - will overwrite admin server one */
    /* use psetRealCreateSSL 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; /* 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 = psetRealCreateSSL(ldapInfo, ldapHost, ldapPort, secure, sieDN, userDN,
                           bindPasswd, path, errorcode);
done:
  destroyAdmldap(ldapInfo);
  PR_Free(userDN);
  PR_Free(ldapHost);
  PR_Free(sieDN);
  PR_smprintf_free(path);
  if (!passwd) {
    if (bindPasswd) {
      memset(bindPasswd, '\0', strlen(bindPasswd));
      PR_Free(bindPasswd);
    }
  }
  return pset;
}

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