/** BEGIN COPYRIGHT BLOCK
 * 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; version 2 of the License.
 * 
 * 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.
 * 
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*
 * Some of the simple conf stuff here. Must not call any
 * libadmin functions! This is needed by ds_config.c
 */
#if defined( XP_WIN32 )
#include <windows.h>
#endif
#include "dsalib.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lber.h>
#include <ldif.h>
#include <ctype.h>
#include "nspr.h"
#include "plstr.h"

/* ldif_read_record lineno argument type depends on openldap version */
#if defined(USE_OPENLDAP)
#include <ldap_features.h>
#if LDAP_VENDOR_VERSION >= 20434 /* changed in 2.4.34 */
typedef unsigned long int ldif_record_lineno_t;
#else
typedef int ldif_record_lineno_t;
#endif
#else
typedef int ldif_record_lineno_t;
#endif

int
dsalib_ldif_parse_line(
    char *line,
    struct berval *type,
    struct berval *value,
    int *freeval
)
{
    int rc;
#if defined(USE_OPENLDAP)
    rc = ldif_parse_line2(line, type, value, freeval);
    /* check that type and value are null terminated */
#else
    int vlen;
    rc = ldif_parse_line(line, &type->bv_val, &value->bv_val, &vlen);
    type->bv_len = type->bv_val ? strlen(type->bv_val) : 0;
    value->bv_len = vlen;
#endif
    *freeval = 0; /* always returns in place */
    return rc;
}

/*
 * Read the configuration info into a null-terminated list of strings.
 */
DS_EXPORT_SYMBOL char **
#if defined(USE_OPENLDAP)
ds_get_conf_from_file(LDIFFP *conf)
#else
ds_get_conf_from_file(FILE *conf)
#endif
{
    static char config_entry[] = "dn: cn=config";
    static int cfg_ent_len = sizeof(config_entry)-1;
    int		listsize = 0;
    char        **conf_list = NULL;
    char *entry = 0;
#if defined(USE_OPENLDAP)
    int buflen = 0;
#endif
    ldif_record_lineno_t lineno;
    int i = 0;

#if defined(USE_OPENLDAP)
    while (ldif_read_record(conf, &lineno, &entry, &buflen)) {
#else
    while ((entry = ldif_get_entry(conf, &lineno))) {
#endif
	char *begin = entry;
	if (!PL_strncasecmp(entry, config_entry, cfg_ent_len)) {
	    char *line = entry;
	    while ((line = ldif_getline(&entry))) {
		struct berval type, value;
		int freeval = 0;
		int rc;

		if ( *line == '\n' || *line == '\0' ) {
		    break;
		}

		/* this call modifies line */
		rc = dsalib_ldif_parse_line(line, &type, &value, &freeval);
		if (rc != 0)
		{
		    ds_send_error("Unknown error processing config file", 0);
		    free(begin);
		    for (i=0; conf_list && conf_list[i]; i++) {
		        PR_smprintf_free(conf_list[i]);
		    }
		    free((void *)conf_list);
		    return NULL;
		}
		listsize++;
		conf_list = (char **) realloc(conf_list, 
					      ((listsize + 1) * sizeof(char *)));
		/* this is the format expected by ds_get_config_value */
		conf_list[listsize - 1] = PR_smprintf("%.*s:%.*s",
						      type.bv_len, type.bv_val, value.bv_len, value.bv_val);
		conf_list[listsize] = NULL;		/* always null terminated */
		if (freeval) {
		    PL_strfree(value.bv_val);
		}
	    }
	}
	free(begin);
	entry = NULL;
#if defined(USE_OPENLDAP)
	buflen = 0;
#endif
    }
			
    return(conf_list);
}

/*
 * Returns 1 if parm is in confline else 0
 */
static int
ds_parm_in_line(char *confline, char *parm)
{
    int parm_size;
 
    if ( confline == NULL )
        return(0);
    if ( parm == NULL )
        return(0);
    parm_size = strlen(parm);
    if ( parm_size == 0 )
        return(0);
    if ( PL_strncasecmp(confline, parm, parm_size) == 0 )
        if ( ((int) strlen(confline)) > parm_size )
            if ( confline[parm_size] == ':' )
                return(1);
    return(0);
}
 
/*
 * Gets the string that corresponds to the parameter supplied from the
 * list of config lines.  Returns a malloc'd string.
 */
DS_EXPORT_SYMBOL char *
ds_get_value(char **ds_config, char *parm, int phase, int occurance)
{
    char        *line; 
    int         line_num = 0;
    int         cur_phase = 0;
    int         cur_occurance = 0;
 
    if ( (parm == NULL) || (ds_config == NULL) )
        return(NULL);
    if ( (phase < 0) || (occurance < 1) )
        return(NULL);
    line = ds_config[line_num];
    while ( line != NULL ) {
	if ( ds_parm_in_line(line, "database") )
	    cur_phase++;
        if ( ds_parm_in_line(line, parm) ) {    /* found it */
	    if ( phase == cur_phase )
		if ( ++cur_occurance == occurance ) {
		    /*
		     * Use ldif_parse_line() so continuation markers are
		     * handled correctly, etc.
		     */
		    struct berval	type, tmpvalue;
		    char	*value = NULL;
		    int		freeval = 0;
		    int		ldif_rc;
		    char	*tmpline = strdup(line);

		    if ( NULL == tmpline ) {
			ds_send_error(
				"ds_get_value() failed: strdup() returned NULL\n",
				1 /* print errno */ );
			return(NULL);
		    }

		    ldif_rc = dsalib_ldif_parse_line( tmpline, &type, &tmpvalue, &freeval );
		    if (ldif_rc) {
			ds_send_error("Unknown error processing config file", 0);
		    } else {
			if (freeval) {
			    value = tmpvalue.bv_val;
			} else {
			    value = PL_strndup(tmpvalue.bv_val, tmpvalue.bv_len);
			}
		    }
		    free(tmpline);
		    return value;
		}
        }
        line_num++;
        line = ds_config[line_num];
    }
    return(NULL);
}
