[Fedora-directory-commits] adminserver/lib/libdsa dsalib_conf.c, NONE, 1.1 dsalib_confs.c, NONE, 1.1 dsalib_db.c, NONE, 1.1 dsalib_debug.c, NONE, 1.1 dsalib_dn.c, NONE, 1.1 dsalib_filename.c, NONE, 1.1 dsalib_ldif.c, NONE, 1.1 dsalib_location.c, NONE, 1.1 dsalib_pw.c, NONE, 1.1 dsalib_tailf.c, NONE, 1.1 dsalib_updown.c, NONE, 1.1 dsalib_util.c, NONE, 1.1

Noriko Hosoi (nhosoi) fedora-directory-commits at redhat.com
Wed Jun 13 01:37:19 UTC 2007


Author: nhosoi

Update of /cvs/dirsec/adminserver/lib/libdsa
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv11558/lib/libdsa

Added Files:
	dsalib_conf.c dsalib_confs.c dsalib_db.c dsalib_debug.c 
	dsalib_dn.c dsalib_filename.c dsalib_ldif.c dsalib_location.c 
	dsalib_pw.c dsalib_tailf.c dsalib_updown.c dsalib_util.c 
Log Message:
Resolves: #237356
Summary: Move DS Admin Code into Admin Server (Comment #54 -- adminserver side)
Description: moving DS task CGIs to the adminserver with libdsa.



--- NEW FILE dsalib_conf.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#include <process.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dsalib.h"
#include <ctype.h>

#include "nspr.h"

#define CONF_SUFFIX "cn=config"

DS_EXPORT_SYMBOL char *
ds_get_var_name(int varnum)
{
    if ( (varnum >= DS_CFG_MAX) || (varnum < 0) )
        return(NULL);                      /* failure */
    return(ds_cfg_info[varnum].dci_varname);
}

/*
 * Get config info.
 */
DS_EXPORT_SYMBOL char **
ds_get_config(int type)
{
    char        conffile[PATH_MAX];
    char        *configdir;
    FILE        *sf = NULL;
    char        **conf_list = NULL;

    if ( (type != DS_REAL_CONFIG) && (type != DS_TMP_CONFIG) ) {
        ds_send_error("Invalid config file type.", 0);
        return(NULL);
    }

    if ( (configdir = ds_get_config_dir()) == NULL ) {
        ds_send_error("Cannot find configuration directory.", 0);
        return(NULL);
    }

    PR_snprintf(conffile, PATH_MAX, "%s/%s", configdir, DS_CONFIG_FILE);

    if ( !(sf = fopen(conffile, "r")) )  {
        ds_send_error("could not read config file.", 1);
        return(NULL);
    }

    conf_list = ds_get_conf_from_file(sf);

    fclose(sf);
    if (!conf_list) {
        ds_send_error("failed to read the config file successfully.", 0);
        return(NULL);
    }
    return(conf_list);
}

/*
 * NOTE: the ordering of the following array elements must be kept in sync
 * with the ordering of the #defines in ../include/dsalib.h.
 */
struct ds_cfg_info ds_cfg_info[] = {
{"nsslapd-errorlog-level" },
{"nsslapd-referral" },
{"nsslapd-auditlog" },
{"nsslapd-localhost" },
{"nsslapd-port" },
{"nsslapd-security" },
{"nsslapd-secureport" },
{"nsslapd-ssl3ciphers"},
{"passwordstoragescheme"},
{"nsslapd-accesslog"},
{"nsslapd-errorlog"},
{"nsslapd-rootdn"},
{"nsslapd-rootpwstoragescheme"},
{"nsslapd-suffix"},
{"nsslapd-localuser"},
{0}
};

/*
 * Open the config file and look for option "option".  Return its
 * value, or NULL if the option was not found.
 */
DS_EXPORT_SYMBOL char *
ds_get_config_value( int option )
{
    char **all, *value;
    int i;
    char *attr = ds_get_var_name(option);

    if (attr == NULL)
	return NULL;

    all = ds_get_config( DS_REAL_CONFIG );
    if ( all == NULL ) {
	return NULL;
    }
    for ( i = 0; all[ i ] != NULL; i++ ) {
	if (( value = strchr( all[ i ], ':' )) != NULL ) {
	    *value = '\0';
	    ++value;
	    while (*value && isspace(*value))
		++value;
	}
	if ( !strcasecmp( attr, all[ i ] )) {
	    return strdup( value );
	}
    }
    return NULL;
}

static size_t
count_quotes (const char* s)
{
    size_t count = 0;
    const char* t = s;
    if (t) while ((t = strpbrk (t, "\"\\")) != NULL) {
	++count;
	++t;
    }
    return count;
}

DS_EXPORT_SYMBOL char*
ds_enquote_config_value (int paramnum, char* s)
{
    char* result;
    char* brkcharset = "\"\\ \t\r\n";
	char *encoded_quote = "22"; /* replace quote with \22 */
	int encoded_quote_len = strlen(encoded_quote);
	char *begin = s;
    if (*s && ! strpbrk (s, brkcharset) && 
		! (paramnum == DS_AUDITFILE || paramnum == DS_ACCESSLOG ||
#if defined( XP_WIN32 )
		   paramnum == DS_SUFFIX ||
#endif
		   paramnum == DS_ERRORLOG)) {
		result = s;
    } else {
		char* t = malloc (strlen (s) + count_quotes (s) + 3);
		result = t;
		*t++ = '"';
		while (*s) {
			switch (*s) {

			case '"':
				/* convert escaped quotes by replacing the quote with
				   escape code e.g. 22 so that \" is converted to \22 "*/
				if ((s > begin) && (*(s - 1) == '\\'))
				{
					strcpy(t, encoded_quote);
					t += encoded_quote_len;
				}
				else /* unescaped ", just replace with \22 "*/
				{
					*t++ = '\\';
					strcpy(t, encoded_quote);
					t += encoded_quote_len;					
				}
				++s;
				break;

			default: 
				*t++ = *s++; /* just copy it */
				break;
			}
		}
		*t++ = '"';
		*t = '\0';
    }
    return result;
}

DS_EXPORT_SYMBOL char*
ds_DNS_to_DN (char* DNS)
{
    static const char* const RDN = "dc=";
    char* DN;
    char* dot;
    size_t components;
    if (DNS == NULL || *DNS == '\0') {
	return strdup ("");
    }
    components = 1;
    for (dot = strchr (DNS, '.'); dot != NULL; dot = strchr (dot + 1, '.')) {
	++components;
    }
    DN = malloc (strlen (DNS) + (components * strlen(RDN)) + 1);
    strcpy (DN, RDN);
    for (dot = strchr (DNS, '.'); dot != NULL; dot = strchr (dot + 1, '.')) {
	*dot = '\0';
	strcat (DN, DNS);
	strcat (DN, ",");
	strcat (DN, RDN);
	DNS = dot + 1;
	*dot = '.';
    }
    strcat (DN, DNS);
    dn_normalize (DN);
    return DN;
}


--- NEW FILE dsalib_confs.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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 <ldif.h>
#include <ctype.h>
#include "nspr.h"
#include "plstr.h"

/*
 * Read the configuration info into a null-terminated list of strings.
 */
DS_EXPORT_SYMBOL char **
ds_get_conf_from_file(FILE *conf)
{
    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;
    int lineno = 0;

    while ((entry = ldif_get_entry(conf, &lineno))) {
	char *begin = entry;
	if (!PL_strncasecmp(entry, config_entry, cfg_ent_len)) {
	    char *line = entry;
	    while ((line = ldif_getline(&entry))) {
		char *type, *value;
		int vlen = 0;
		int rc;

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

		/* this call modifies line */
		rc = ldif_parse_line(line, &type, &value, &vlen);
		if (rc != 0)
		{
			ds_send_error("Unknown error processing config file", 0);
		    free(begin);
		    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, value);
		conf_list[listsize] = NULL;		/* always null terminated */
	    }
	}
	free(begin);
    }
			
    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 == (int)NULL )
        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.
		     */
		    char	*type = NULL, *value = NULL, *tmpvalue = NULL;
		    int		ldif_rc, tmpvlen = 0;
		    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 = ldif_parse_line( tmpline, &type, &tmpvalue, &tmpvlen );
		    if (ldif_rc < 0) {
			ds_send_error("Unknown error processing config file", 0);
		    } else if (ldif_rc == 0) {	/* value returned in place */
			value = strdup(tmpvalue);
		    } else {			/* malloc'd value */
			value = tmpvalue;
		    }
		    free(tmpline);
		    return value;
		}
        }
        line_num++;
        line = ds_config[line_num];
    }
    return(NULL);
}


--- NEW FILE dsalib_db.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#include <process.h>
#include <io.h>
#endif
#include "dsalib.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#if !defined( XP_WIN32 )
#include <dirent.h>
#include <unistd.h>
#else
#define popen _popen
#define pclose _pclose
#endif
#include "nspr.h"

/*
 * Get a listing of backup directories
 * Return NULL for errors  and a NULL list for an empty list.
 */
 
DS_EXPORT_SYMBOL char **
ds_get_bak_dirs()
{
    char    format_str[PATH_MAX];
    char    *root;
    int        i = 0;
    char    **bak_dirs = NULL;
    char    *bakdir = NULL;

    if ( (root = ds_get_install_root()) == NULL ) 
    {
        ds_send_error("Cannot find server root directory.", 0);
        return(bak_dirs);
    }

    if ( (bakdir = ds_get_bak_dir()) == NULL )
    {
        ds_send_error("Cannot find backup directory.", 0);
        return(bak_dirs);
    }

    PR_snprintf( format_str, PATH_MAX, "%s", bakdir );
    bak_dirs = ds_get_file_list( format_str );
    if( bak_dirs )
    {
        while( bak_dirs[i] != NULL )
        {
            /* Prepend the filename with the install root */
            char filename[PATH_MAX];
            PR_snprintf( filename, PATH_MAX, "%s%c%s",
                            bakdir, FILE_SEP, bak_dirs[i] );
            free( bak_dirs[i] );
            bak_dirs[i] = strdup( filename );
#if defined( XP_WIN32 )
            ds_dostounixpath( bak_dirs[i] );
#endif
            i++;
        }
    }

    return(bak_dirs);
}

/*
 * Restore a database based on a backup directory name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_bak2db(char *file)
{
    char        startup_line[BIG_LINE];
    char        statfile[PATH_MAX];
    char        *tmp_dir;
    char        *root;
    int         haderror = 0;
    int         error = -1;
    int         status;
    FILE        *sf = NULL;
    struct stat    fstats;

    if ( file == NULL ) {
        return DS_NULL_PARAMETER;
    }
    status = ds_get_updown_status();
    if ( status == DS_SERVER_UP ) {
        return DS_SERVER_MUST_BE_DOWN;
    }
    if ( (root = ds_get_install_root()) == NULL ) {
        return DS_NO_SERVER_ROOT;
    }

    if ( file[strlen(file) - 1] == '\n' )    /* strip out returns */
        file[strlen(file) - 1] = '\0';

    if( stat( file, &fstats ) == -1 && errno == ENOENT ) {
        return DS_CANNOT_OPEN_BACKUP_FILE;
    } else if( !(fstats.st_mode & S_IFDIR) ) {
        return DS_NOT_A_DIRECTORY;
    }

    tmp_dir = ds_get_tmp_dir();
    PR_snprintf(statfile, PATH_MAX, "%s%cbak2db.%d", tmp_dir, FILE_SEP, (int)getpid());
    PR_snprintf(startup_line, BIG_LINE,
            "%s%cbak2db "
            "%s%s%s > "
            "%s%s%s 2>&1",
            root, FILE_SEP, 
            ENQUOTE, file, ENQUOTE, 
            ENQUOTE, statfile, ENQUOTE );
    alter_startup_line(startup_line);
    fflush(0);
    error = system(startup_line);
    fflush(0);
    if ( error == -1 ) {
        return DS_CANNOT_EXEC;
    }
    fflush(0);
    if( !(sf = fopen(statfile, "r")) )  {
        return DS_CANNOT_OPEN_STAT_FILE;
    }

    while ( fgets(startup_line, BIG_LINE, sf) ) {
        if ((strstr(startup_line, "- Restoring file")) || 
            (strstr(startup_line, "- Checkpointing"))) {
            ds_show_message(startup_line);
        } else {
            haderror = 1;
            ds_send_error(startup_line, 0);
        }
    }

    fclose(sf);
    unlink(statfile);

    if ( haderror )
        return DS_UNKNOWN_ERROR;
    return 0;
}

/*
 * Create a backup based on a file name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_db2bak(char *file)
{
    char        startup_line[BIG_LINE];
    char        statfile[PATH_MAX];
    char        *tmp_dir;
    char        *root;
    int         haderror = 0;
    int         error = -1;
    FILE        *sf = NULL;
    int        lite = 0;
#ifdef XP_WIN32
    time_t    ltime;
#endif

    if ( (root = ds_get_install_root()) == NULL ) {
        return DS_NO_SERVER_ROOT;
    }

    if ( (file == NULL) || (strlen(file) == 0) )
        file = NULL;

    tmp_dir = ds_get_tmp_dir();
    PR_snprintf(statfile, PATH_MAX, "%s%cdb2bak.%d", tmp_dir, FILE_SEP, (int)getpid());
    
                    
#if defined( XP_WIN32 )
    if( file == NULL )
    {
        file = malloc( BIG_LINE );

        time( &ltime );
        PR_snprintf( file, BIG_LINE, "%s", ctime( &ltime ) );
        ds_timetofname( file );
    }

    /* Check if the directory exists or can be created */
    if ( !ds_file_exists( file ) ) {
        char *errmsg = ds_mkdir_p( file, NEWDIR_MODE );
        if( errmsg != NULL ) {
/*            ds_send_error(errmsg, 10);
 */
            return DS_CANNOT_CREATE_DIRECTORY;
        }
    }
#endif

/* DBDB: note on the following line. 
 * Originally this had quotes round the directory name.
 * I found that this made the script not work becuase
 * a path of the form "foo"/bar/"baz" was passed to slapd.
 * the c runtime didn't like this. Perhaps there's a simple
 * solution, but for now I've modified this line here to 
 * not quote the directory name. This means that backup
 * directories can't have spaces in them.
 */


    PR_snprintf(startup_line, sizeof(startup_line),
            "%s%cdb2bak "
            "%s%s%s > "
            "%s%s%s 2>&1",
            root, FILE_SEP,
            ENQUOTE,
            (file == NULL) ? "" : file,
            ENQUOTE,
            ENQUOTE, statfile, ENQUOTE);

    PATH_FOR_PLATFORM( startup_line );
    alter_startup_line(startup_line);
    fflush(0);
    error = system(startup_line);
    if ( error == -1 ) {
        return DS_CANNOT_EXEC;
    }
    if( !(sf = fopen(statfile, "r")) )  {
        return DS_CANNOT_OPEN_STAT_FILE;
    }

    while ( fgets(startup_line, BIG_LINE, sf) ) {
        if (strstr(startup_line, " - Backing up file") ||
            strstr(startup_line, " - Checkpointing database")) {
            ds_show_message(startup_line);
        } else {
            haderror = 1;
            if (strstr ( startup_line, "restricted mode")) {
                lite = 1;
            }
            ds_send_error(startup_line, 0);
        }
    }
    fclose(sf);
    unlink(statfile);

    if ( lite && haderror )
    return DS_HAS_TOBE_READONLY_MODE;

    if ( haderror )
        return DS_UNKNOWN_ERROR;
    return 0;
}

static void
process_and_report( char *line, int line_size, FILE *cmd )
{
    while(fgets(line, line_size, cmd))  {
        /* Strip off line feeds */
        int ind = strlen( line ) - 1;
        while ( (ind >= 0) &&
                ((line[ind] == '\n') ||
                 (line[ind] == '\r')) ) {
            line[ind] = 0;
            ind--;
        }
        if ( ind < 1 ) {
            continue;
        }
        ds_send_status(line);
    }
}

static int exec_and_report( char *startup_line )
{
    FILE        *cmd = NULL;
    char        line[BIG_LINE];
    int         haderror = 0;

    PATH_FOR_PLATFORM( startup_line );
    alter_startup_line(startup_line);

    /*
      fprintf( stdout, "Launching <%s>\n", startup_line );
    */

    fflush(0);
    cmd = popen(startup_line, "r");
    if(!cmd) {
        return DS_CANNOT_EXEC;
    }
    process_and_report( line, sizeof(line), cmd );
    pclose(cmd);

    /*
    ** The VLV indexing code prints OK,
    ** if the index was successfully created.
    */
    if (strcmp(line,"OK")==0) {
        haderror = 0;
    } else {
        haderror = DS_UNKNOWN_ERROR;
    }

    return haderror;
}

/*
 * Create a vlv index
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_vlvindex(char **backendList, char **vlvList)
{
    char        startup_line[BIG_LINE];
    char        *root;
    char        *instroot;
    char        **vlvc = NULL;
            

    root = ds_get_server_root();
    instroot = ds_get_install_root();
    if ( (root == NULL) || (instroot == NULL) ) {
        return DS_NO_SERVER_ROOT;
    }

    PR_snprintf(startup_line, sizeof(startup_line), "%s/bin/slapd/server/%s db2index "
            "-D %s%s/%s "
            "-n %s ",
            root, SLAPD_NAME,            
            ENQUOTE, instroot, ENQUOTE,
            backendList[0]);


    /* Create vlv TAG */
    vlvc=vlvList;
    while( *vlvc != NULL ) {
        PR_snprintf( startup_line, sizeof(startup_line), "%s -T %s%s%s", startup_line,"\"",*vlvc,"\"" );
        vlvc++;
    }    
   
    return exec_and_report( startup_line );
}

/*
 * Create one or more indexes
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_addindex(char **attrList, char *backendName)
{
    char        startup_line[BIG_LINE];
    char        *root;
    char        *instroot;
 
    root = ds_get_server_root();
    instroot = ds_get_install_root();

    if ( (root == NULL) || (instroot == NULL) ) {
        return DS_NO_SERVER_ROOT;
    }

    PR_snprintf(startup_line, sizeof(startup_line), "%s/bin/slapd/server/%s db2index "
            "-D %s%s%s "
            "-n %s",
            root, SLAPD_NAME,            
            ENQUOTE, instroot, ENQUOTE,
            backendName);

    while( *attrList != NULL ) {
        PR_snprintf( startup_line, sizeof(startup_line), "%s -t %s", startup_line, *attrList );
        attrList++;
    }

    return exec_and_report( startup_line );
}


--- NEW FILE dsalib_debug.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#endif
#include "dsalib.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nspr.h"
#include "plstr.h"

#if defined( XP_WIN32 )
int ldap_debug = 0;
#endif

DS_EXPORT_SYMBOL void
ds_log_env(char **envp)
{
    FILE    *file;
	char	admin_logfile[PATH_MAX], *tmp_dir;
	
	tmp_dir = ds_get_tmp_dir();
	PL_strncpyz( admin_logfile, tmp_dir, sizeof(admin_logfile) );
#if defined( XP_WIN32 )
	if( tmp_dir )
	{
		free( tmp_dir );
		tmp_dir = NULL;
	}
#endif
	PL_strcatn( admin_logfile, sizeof(admin_logfile), "/admin.log");

    file = fopen(admin_logfile, "a+");
    if (file != NULL) {
        int     i;
        for ( i = 0; envp[i] != (char *) 0; i++ ) {
            char        envstr[200];
 
            PR_snprintf(envstr, sizeof(envstr), "%s\n", envp[i]);
            fwrite(envstr, strlen(envstr), 1, file);
        }
        fclose(file);
    }
}
 
DS_EXPORT_SYMBOL void
ds_log_debug_message(char *msg)
{
    FILE    *file;
	char	admin_logfile[PATH_MAX], *tmp_dir;
	
	tmp_dir = ds_get_tmp_dir();
	memset( admin_logfile, 0, sizeof( admin_logfile ) );
	PL_strncpyz( admin_logfile, tmp_dir, sizeof(admin_logfile) );
#if defined( XP_WIN32 )
	if( tmp_dir )
	{
		free( tmp_dir );
		tmp_dir = NULL;
	}
#endif
	PL_strcatn( admin_logfile, sizeof(admin_logfile), "/admin.log");
 
    file = fopen(admin_logfile, "a+");
    if (file != NULL) {
        fwrite(msg, strlen(msg), 1, file);
        fclose(file);
    }
}



--- NEW FILE dsalib_dn.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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


#if defined( XP_WIN32 )
#include <windows.h>
#else
#include <sys/types.h>
#endif
#include <string.h>
#include "dsalib.h"
#include <stdlib.h>

#define DNSEPARATOR(c)	(c == ',' || c == ';')
#define SEPARATOR(c)	(c == ',' || c == ';' || c == '+')
#define SPACE(c)	(c == ' ' || c == '\n')
#define NEEDSESCAPE(c)	(c == '\\' || c == '"')
#define B4TYPE		0
#define INTYPE		1
#define B4EQUAL		2
#define B4VALUE		3
#define INVALUE		4
#define INQUOTEDVALUE	5
#define B4SEPARATOR	6

DS_EXPORT_SYMBOL char*
dn_normalize( char *dn )
{
	char	*d, *s;
	int	state, gotesc;

	/* Debug( LDAP_DEBUG_TRACE, "=> dn_normalize \"%s\"\n", dn, 0, 0 ); */

	gotesc = 0;
	state = B4TYPE;
	for ( d = s = dn; *s; s++ ) {
		switch ( state ) {
		case B4TYPE:
			if ( ! SPACE( *s ) ) {
				state = INTYPE;
				*d++ = *s;
			}
			break;
		case INTYPE:
			if ( *s == '=' ) {
				state = B4VALUE;
				*d++ = *s;
			} else if ( SPACE( *s ) ) {
				state = B4EQUAL;
			} else {
				*d++ = *s;
			}
			break;
		case B4EQUAL:
			if ( *s == '=' ) {
				state = B4VALUE;
				*d++ = *s;
			} else if ( ! SPACE( *s ) ) {
				/* not a valid dn - but what can we do here? */
				*d++ = *s;
			}
			break;
		case B4VALUE:
			if ( *s == '"' ) {
				state = INQUOTEDVALUE;
				*d++ = *s;
			} else if ( ! SPACE( *s ) ) { 
				state = INVALUE;
				*d++ = *s;
			}
			break;
		case INVALUE:
			if ( !gotesc && SEPARATOR( *s ) ) {
				while ( SPACE( *(d - 1) ) )
					d--;
				state = B4TYPE;
				if ( *s == '+' ) {
					*d++ = *s;
				} else {
					*d++ = ',';
				}
			} else if ( gotesc && !NEEDSESCAPE( *s ) &&
			    !SEPARATOR( *s ) ) {
				*--d = *s;
				d++;
			} else {
				*d++ = *s;
			}
			break;
		case INQUOTEDVALUE:
			if ( !gotesc && *s == '"' ) {
				state = B4SEPARATOR;
				*d++ = *s;
			} else if ( gotesc && !NEEDSESCAPE( *s ) ) {
				*--d = *s;
				d++;
			} else {
				*d++ = *s;
			}
			break;
		case B4SEPARATOR:
			if ( SEPARATOR( *s ) ) {
				state = B4TYPE;
				if ( *s == '+' ) {
					*d++ = *s;
				} else {
					*d++ = ',';
				}
			}
			break;
		default:
			break;
		}
		if ( *s == '\\' ) {
			gotesc = 1;
		} else {
			gotesc = 0;
		}
	}
	*d = '\0';

	/* Debug( LDAP_DEBUG_TRACE, "<= dn_normalize \"%s\"\n", dn, 0, 0 ); */
	return( dn );
}

DS_EXPORT_SYMBOL char*
ds_dn_expand (char* dn)
{
    char* edn;
    size_t i = 0;
    char* s;
    int state = B4TYPE;
    int gotesc = 0;

    if (dn == NULL) return NULL;
    edn = strdup (dn);
	if (edn == NULL) return NULL;
    for (s = dn; *s != '\0'; ++s, ++i) {
	switch (state) {
	  case B4TYPE:
	    if ( ! SPACE (*s)) {
		state = INTYPE;
	    }
	    break;
	  case INTYPE:
	    if (*s == '=') {
		state = B4VALUE;
	    } else if (SPACE (*s)) {
		state = B4EQUAL;
	    }
	    break;
	  case B4EQUAL:
	    if (*s == '=') {
		state = B4VALUE;
	    }
	    break;
	  case B4VALUE:
	    if (*s == '"') {
		state = INQUOTEDVALUE;
	    } else if ( ! SPACE (*s)) { 
		state = INVALUE;
	    }
	    break;
	  case INQUOTEDVALUE:
	    if (gotesc) {
		if ( ! NEEDSESCAPE (*s)) {
		    --i;
		    memmove (edn+i, edn+i+1, strlen (edn+i));
		}
	    } else {
		if (*s == '"') {
		    state = B4SEPARATOR;
		}
	    }
	    break;
	  case INVALUE:
	    if (gotesc) {
		if ( ! NEEDSESCAPE (*s) && ! SEPARATOR (*s)) {
		    --i;
		    memmove (edn+i, edn+i+1, strlen (edn+i));
		}
		break;
	    }
	  case B4SEPARATOR:
	    if (SEPARATOR (*s)) {
		state = B4TYPE;
		if ( ! SPACE (s[1])) {
		    /* insert a space following edn[i] */
		    edn = (char*) realloc (edn, strlen (edn)+2);
		    ++i;
		    memmove (edn+i+1, edn+i, strlen (edn+i)+1);
		    edn[i] = ' ';
		}
	    }
	    break;
	  default:
	    break;
	}
	gotesc = (*s == '\\');
    }
    return edn;
}

int
hexchar2int( char c )
{
    if ( '0' <= c && c <= '9' ) {
	return( c - '0' );
    }
    if ( 'a' <= c && c <= 'f' ) {
	return( c - 'a' + 10 );
    }
    if ( 'A' <= c && c <= 'F' ) {
	return( c - 'A' + 10 );
    }
    return( -1 );
}

/*
 * substr_dn_normalize - map a DN to a canonical form.
 * The DN is read from *dn through *(end-1) and normalized in place.
 * The new end is returned; that is, the canonical form is in
 * *dn through *(the_return_value-1).
 */

/* The goals of this function are:
 * 1. be compatible with previous implementations.  Especially, enable
 *    a server running this code to find database index keys that were
 *    computed by Directory Server 3.0 with a prior version of this code.
 * 2. Normalize in place; that is, avoid allocating memory to contain
 *    the canonical form.
 * 3. eliminate insignificant differences; that is, any two DNs are
 *    not significantly different if and only if their canonical forms
 *    are identical (ignoring upper/lower case).
 * 4. handle a DN in the syntax defined by RFC 2253.
 * 5. handle a DN in the syntax defined by RFC 1779.
 *
 * Goals 3 through 5 are not entirely achieved by this implementation,
 * because it can't be done without violating goal 1.  Specifically,
 * DNs like cn="a,b" and cn=a\,b are not mapped to the same canonical form,
 * although they're not significantly different.  Likewise for any pair
 * of DNs that differ only in their choice of quoting convention.
 * A previous version of this code changed all DNs to the most compact
 * quoting convention, but that violated goal 1, since Directory Server
 * 3.0 did not.
 *
 * Also, this implementation handles the \xx convention of RFC 2253 and
 * consequently violates RFC 1779, according to which this type of quoting
 * would be interpreted as a sequence of 2 numerals (not a single byte).
 */

DS_EXPORT_SYMBOL char *
dn_normalize_convert( char *dn )
{
    /* \xx is changed to \c.
       \c is changed to c, unless this would change its meaning.
       All values that contain 2 or more separators are "enquoted";
       all other values are not enquoted.
     */
	char	*value, *value_separator;
	char	*d, *s;
	char    *end;

	int	gotesc = 0;
	int	state = B4TYPE;
	if (NULL == dn)
		return dn;

	end = dn + strlen(dn);
	for ( d = s = dn; s != end; s++ ) {
		switch ( state ) {
		case B4TYPE:
			if ( ! SPACE( *s ) ) {
				state = INTYPE;
				*d++ = *s;
			}
			break;
		case INTYPE:
			if ( *s == '=' ) {
				state = B4VALUE;
				*d++ = *s;
			} else if ( SPACE( *s ) ) {
				state = B4EQUAL;
			} else {
				*d++ = *s;
			}
			break;
		case B4EQUAL:
			if ( *s == '=' ) {
				state = B4VALUE;
				*d++ = *s;
			} else if ( ! SPACE( *s ) ) {
				/* not a valid dn - but what can we do here? */
				*d++ = *s;
			}
			break;
		case B4VALUE:
			if ( *s == '"' || ! SPACE( *s ) ) {
				value_separator = NULL;
				value = d;
				state = ( *s == '"' ) ? INQUOTEDVALUE : INVALUE;
				*d++ = *s;
			}
			break;
		case INVALUE:
			if ( gotesc ) {
			    if ( SEPARATOR( *s ) ) {
				if ( value_separator ) value_separator = dn;
				else value_separator = d;
			    } else if ( ! NEEDSESCAPE( *s ) ) {
				--d; /* eliminate the \ */
			    }
			} else if ( SEPARATOR( *s ) ) {
			    while ( SPACE( *(d - 1) ) )
				d--;
			    if ( value_separator == dn ) { /* 2 or more separators */
				/* convert to quoted value: */
				auto char *L = NULL;
				auto char *R;
				for ( R = value; (R = strchr( R, '\\' )) && (R < d); L = ++R ) {
				    if ( SEPARATOR( R[1] )) {
					if ( L == NULL ) {
					    auto const size_t len = R - value;
					    if ( len > 0 ) memmove( value+1, value, len );
					    *value = '"'; /* opening quote */
					    value = R + 1;
					} else {
					    auto const size_t len = R - L;
					    if ( len > 0 ) {
						memmove( value, L, len );
						value += len;
					    }
					    --d;
					}
				    }
				}
				memmove( value, L, d - L + 1 );
				*d++ = '"'; /* closing quote */
			    }
			    state = B4TYPE;
			    *d++ = (*s == '+') ? '+' : ',';
			    break;
			}
			*d++ = *s;
			break;
		case INQUOTEDVALUE:
			if ( gotesc ) {
			    if ( ! NEEDSESCAPE( *s ) ) {
				--d; /* eliminate the \ */
			    }
			} else if ( *s == '"' ) {
			    state = B4SEPARATOR;
			    if ( value_separator == dn /* 2 or more separators */
				|| SPACE( value[1] ) || SPACE( d[-1] ) ) {
				*d++ = *s;
			    } else {
				/* convert to non-quoted value: */
				if ( value_separator == NULL ) { /* no separators */
				    memmove ( value, value+1, (d-value)-1 );
				    --d;
				} else { /* 1 separator */
				    memmove ( value, value+1, (value_separator-value)-1 );
				    *(value_separator - 1) = '\\';
				}
			    }
			    break;
			}
			if ( SEPARATOR( *s )) {
			    if ( value_separator ) value_separator = dn;
			    else value_separator = d;
			}
			*d++ = *s;
			break;
		case B4SEPARATOR:
			if ( SEPARATOR( *s ) ) {
			    state = B4TYPE;
			    *d++ = (*s == '+') ? '+' : ',';
			}
			break;
		default:
/*			LDAPDebug( LDAP_DEBUG_ANY,
			    "slapi_dn_normalize - unknown state %d\n", state, 0, 0 );*/
			break;
		}
		if ( *s != '\\' ) {
			gotesc = 0;
		} else {
			gotesc = 1;
			if ( s+2 < end ) {
			    auto int n = hexchar2int( s[1] );
			    if ( n >= 0 ) {
				auto int n2 = hexchar2int( s[2] );
				if ( n2 >= 0 ) {
				    n = (n << 4) + n2;
				    if (n == 0) { /* don't change \00 */
					*d++ = *++s;
					*d++ = *++s;
					gotesc = 0;
				    } else { /* change \xx to a single char */
					++s;
					*(unsigned char*)(s+1) = n;
				    }
				}
			    }
			}
		}
	}

	/* Trim trailing spaces */
	while ( d != dn && *(d - 1) == ' ' ) d--;

	*d = 0;

	return( dn );
}

/* if dn contains an unescaped quote return true */
DS_EXPORT_SYMBOL int
ds_dn_uses_LDAPv2_quoting(const char *dn)
{
	const char ESC = '\\';
	const char Q = '"';
	int ret = 0;
	const char *p = 0;

	/* check dn for a even number (incl. 0) of ESC followed by Q */
	if (!dn)
		return ret;

	p = strchr(dn, Q);
	if (p)
	{
		int nESC = 0;
		for (--p; (p >= dn) && (*p == ESC); --p)
			++nESC;
		if (!(nESC % 2))
			ret = 1;
	}

	return ret;
}


--- NEW FILE dsalib_filename.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#endif
#include "dsalib.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "nspr.h"

static char *
get_month_str(int month)
{
    static char *month_str[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
	"Nov", "Dec"};

    if ( (month < 1) || (month > 12) )
        return("Unknown month");
    return(month_str[month - 1]);
}

/*
 * Returns a string describing the meaning of the filename.
 * Two different formats are supported.
 */
DS_EXPORT_SYMBOL char *
ds_get_file_meaning(char *file)
{
    static char meaning[BIG_LINE];
#define	FILE_EXPECTED_SIZE1	14
#define	FILE_EXPECTED_SIZE2	17
    char	*name;
    char	*tmp;
    int		i;
    int		month;
    int		day;
    int		hour;
    int		minute;
    int		sec;
    int		year;

    /*
     * Expect a file name in format 06041996123401 (FILE_EXPECTED_SIZE1)
     * which should return "Jun  4 12:34:01 1996"
     * OR 			    1996_06_04_123401
     * which should return "Jun  4 12:34:01 1996"
     */
 
    if ( file == NULL )
        return(NULL);
    name = strdup(file);
    if ( name == NULL )
        return(NULL);
    if ( (tmp = strrchr(name, '.')) != NULL )
	*tmp = '\0';
    if ( strlen(name) == FILE_EXPECTED_SIZE1 ) {
        for ( i = 0; i < FILE_EXPECTED_SIZE1; i++ )
	    if ( !isdigit(name[i]) )
                return(NULL);
        if ( (sscanf(name, "%2d%2d%4d%2d%2d%2d", &month, &day, &year, &hour,
	    &minute, &sec)) == -1 )
            return(NULL);
    } else if ( strlen(name) == FILE_EXPECTED_SIZE2 ) {
        for ( i = 0; i < FILE_EXPECTED_SIZE2; i++ )
	    if ( !isdigit(name[i]) )
	        if ( name[i] != '_' )
                    return(NULL);
        if ( (sscanf(name, "%4d_%2d_%2d_%2d%2d%2d", &year, &month, &day, &hour,
	    &minute, &sec)) == -1 )
            return(NULL);
    } else
        return(NULL);

    if ( (month < 1) || (month > 12) )
        return(NULL);
    if ( (day < 1) || (day > 31) )
        return(NULL);
    if ( (year < 1000) || (year > 2100) )
        return(NULL);
    if ( (hour < 0) || (hour > 24) )
        return(NULL);
    if ( (minute < 0) || (minute > 60) )
        return(NULL);
    if ( (sec < 0) || (sec > 60) )
        return(NULL);
    PR_snprintf(meaning, sizeof(meaning), "%s % 2d %02d:%02d:%02d %4d", get_month_str(month), 
	day, hour, minute, sec, year);
    return(meaning);
}


--- NEW FILE dsalib_ldif.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#include <process.h>
#include <malloc.h>
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h>
#endif
#include "dsalib.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "nspr.h"
#include "plstr.h"

#ifndef XP_WIN32
#define SCRIPT_SUFFIX "" /* shell scripts have no suffix */
#else
#define SCRIPT_SUFFIX ".bat" /* batch file suffix */
#endif


static int
process_and_report( char *line, int line_size, FILE *cmd )
{
    int err = 0;
    while(fgets(line, line_size, cmd))  {
        /* Strip off line feeds */
        int ind = strlen( line ) - 1;
#ifdef DEBUG_CGI
		fprintf(stderr, "read line=[%s] ind=%d\n", line, ind);
#endif /* DEBUG_CGI */
        fprintf( stdout, ": %s", line );
        fflush(0);
        while ( (ind >= 0) &&
                ((line[ind] == '\n') ||
                 (line[ind] == '\r')) ) {
            line[ind] = 0;
            ind--;
        }
        if ( ind < 1 ) {
            continue;
        }
        ds_send_status(line);
        if ( (strstr(line, "bad LDIF") != NULL) ) {
#ifdef DEBUG_CGI
			fprintf(stderr, "invalid ldif file\n");
#endif /* DEBUG_CGI */
            err = DS_INVALID_LDIF_FILE;
        } else if ( 0 == err ) {
            if ( (strstr(line, "err=") != NULL) ) {
#ifdef DEBUG_CGI
				fprintf(stderr, "unknown error\n");
#endif /* DEBUG_CGI */
                err = DS_UNKNOWN_ERROR;
            }
        }
    }
#ifdef DEBUG_CGI
	fprintf(stderr, "process_and_report finished err=%d\n", err);
#endif /* DEBUG_CGI */
    return err;
}

static int exec_and_report( char *startup_line )
{
	FILE        *cmd = NULL;
	char        line[BIG_LINE];
	int         haderror = 0;

	PATH_FOR_PLATFORM( startup_line );
	alter_startup_line(startup_line);

	fflush(stdout);
	cmd = popen(startup_line, "r");
	if(!cmd) {
		printf("could not open pipe [%s]: %d\n",
			startup_line, errno);
#ifdef DEBUG_CGI
		fprintf(stderr, "could not open pipe [%s]: %d\n",
				startup_line, errno);
#endif /* DEBUG_CGI */
		return DS_CANNOT_EXEC;
	}
	haderror = process_and_report( line, sizeof(line), cmd );
	pclose(cmd);

    return haderror;
}

/*
 * Execute a shell command.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_exec_and_report( char *startup_line )
{
	return exec_and_report( startup_line );
}

/*
 * Create a database based on a file name.
 * 0:             success
 * anything else: failure
 */
static int
importldif(char *file, int preserve, char *backend, char *subtree)
{
    char        startup_line[BIG_LINE];
    char        *root;
    int         haderror = 0;
    int         i = 0, error = -1;
    int         status;
    struct stat fstats;
    char	errbuf[ BIG_LINE ];
    char	**db_files = NULL, *changelogdir = NULL;
    int		rc;

    errbuf[ 0 ] = '\0';

    if ( file == NULL ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: null file\n");
#endif /* DEBUG_CGI */
        return DS_NULL_PARAMETER;
    }
    status = ds_get_updown_status();
    if ( status == DS_SERVER_UP ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: server is not down\n");
#endif /* DEBUG_CGI */
        return DS_SERVER_MUST_BE_DOWN;
    }
    if ( (root = ds_get_install_root()) == NULL ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: could not get server root\n");
#endif /* DEBUG_CGI */
        return DS_NO_SERVER_ROOT;
    }

    if ( file[strlen(file) - 1] == '\n' )	/* strip out returns */
		file[strlen(file) - 1] = '\0';

    /* Make sure the file exists and is not a directory: 34347 */
    if( stat( file, &fstats ) == -1 && errno == ENOENT ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: could not open %s\n", file);
#endif /* DEBUG_CGI */
        return DS_CANNOT_OPEN_LDIF_FILE;
    } else if( fstats.st_mode & S_IFDIR ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: not a file %s\n", file);
#endif /* DEBUG_CGI */
        return DS_IS_A_DIRECTORY;
    }

	if ( preserve ) {
		PR_snprintf(startup_line, BIG_LINE, "%s%cldif2db%s -i %s%s%s",
				root, FILE_SEP, SCRIPT_SUFFIX,
				ENQUOTE, file, ENQUOTE);
	} else if (backend) {
		PR_snprintf(startup_line, BIG_LINE, "%s%cldif2db%s -n %s%s%s -i %s%s%s",
				root, FILE_SEP, SCRIPT_SUFFIX,
				ENQUOTE, backend, ENQUOTE,
				ENQUOTE, file, ENQUOTE);
	} else if (subtree) {
		PR_snprintf(startup_line, BIG_LINE, "%s%cldif2db%s -s %s%s%s -i %s%s%s",
				root, FILE_SEP, SCRIPT_SUFFIX,
				ENQUOTE, subtree, ENQUOTE,
				ENQUOTE, file, ENQUOTE);
	} else {
		PR_snprintf(startup_line, BIG_LINE, "%s%cldif2db%s -i %s%s%s -noconfig",
				root, FILE_SEP, SCRIPT_SUFFIX,
				ENQUOTE, file, ENQUOTE);
	}
    alter_startup_line(startup_line);
    fflush(stdout);
#ifdef DEBUG_CGI
	fprintf(stderr, "importldif: executing %s\n", startup_line);
#endif /* DEBUG_CGI */
    error = exec_and_report(startup_line);
    /*error = system(startup_line);*/
    if ( error != 0 ) {
#ifdef DEBUG_CGI
		fprintf(stderr, "importldif: error=%d\n", error);
#endif /* DEBUG_CGI */
        return error;
    }

    /* Remove the changelog database, if present */
    changelogdir = ds_get_config_value(0xdeadbeef);
    if ( changelogdir != NULL ) {
		db_files = ds_get_file_list( changelogdir );
		if ( db_files != NULL ) {
			ds_send_status("Removing changelog database...");
		}
		for ( i = 0; db_files != NULL && db_files[ i ] != NULL; i++ ) {
			char sbuf[ BIG_LINE ];
			char filename[ BIG_LINE ];
			if ( strlen( db_files[ i ]) > 0 ) {
				PR_snprintf( filename, BIG_LINE, "%s%c%s", changelogdir,
						 FILE_SEP, db_files[ i ]);
				PR_snprintf(sbuf, BIG_LINE, "Removing %s", filename);
				ds_send_status( sbuf );
				rc = unlink( filename);
				if ( rc != 0 ) {
					PR_snprintf( errbuf, BIG_LINE, "Warning: some files in %s could not "
							 "be removed\n", changelogdir );
					haderror++;
				}
			}
		}
    }
    if ( strlen( errbuf ) > 0 ) {
		ds_send_error( errbuf, 0 );
    }

    return error;
}

/*
 * Create a database based on a file name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_ldif2db(char *file)
{
	return importldif( file, 0, NULL, NULL );
}

/*
 * Create a database based on a file name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_ldif2db_preserve(char *file)
{
	return importldif( file, 1, NULL, NULL );
}

/*
 * import an ldif file into a named backend or subtree
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_ldif2db_backend_subtree(char *file, char *backend, char *subtree)
{
	return importldif( file, 0, backend, subtree );
}

/*
 * Create a LDIF file based on a file name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_db2ldif_subtree(char *file, char *subtree)
{
    char        startup_line[BIG_LINE];
    char        statfile[PATH_MAX];
    char        outfile[PATH_MAX];
    char        scriptfile[PATH_MAX];
    char        *tmp_dir;
    char        *root;
    int         haderror = 0;
    int         error = -1;
    FILE        *sf = NULL;

    if ( (root = ds_get_install_root()) == NULL ) {
        return DS_NO_SERVER_ROOT;
    }

    if ( (file == NULL) || (strlen(file) == 0) )
        file = NULL;

	tmp_dir = ds_get_tmp_dir();
	PR_snprintf(statfile, PATH_MAX, "%s%cdb2ldif.%d", tmp_dir, FILE_SEP, (int) getpid());

#if defined( XP_WIN32 )
	if( file == NULL )
	{
		time_t		ltime;
		file = malloc( BIG_LINE );

		time( &ltime );
		PR_snprintf( file, BIG_LINE, "%s", ctime( &ltime ) );
		ds_timetofname( file );
	}
#endif

	if ( file == NULL )
		*outfile = 0;
	else
		PL_strncpyz( outfile, file, sizeof(outfile) );

	PR_snprintf(scriptfile, PATH_MAX, "%s%cdb2ldif", root, FILE_SEP);

	PATH_FOR_PLATFORM( outfile );
	PATH_FOR_PLATFORM( scriptfile );

	if ( subtree == NULL ) {
		PR_snprintf(startup_line, sizeof(startup_line),
				"%s "
				"%s%s%s > "
				"%s%s%s 2>&1",
				scriptfile,
				ENQUOTE, outfile, ENQUOTE,
				ENQUOTE, statfile, ENQUOTE);
	} else {
		PR_snprintf(startup_line, sizeof(startup_line),
				"%s "
				"%s%s%s "
				"-s \"%s\" > "
				"%s%s%s 2>&1",
				scriptfile,
				ENQUOTE, outfile, ENQUOTE,
				subtree,
				ENQUOTE, statfile, ENQUOTE);
	}

    fflush(0);
    alter_startup_line(startup_line);
    error = system(startup_line);
    if ( error == -1 ) {
        return DS_CANNOT_EXEC;
    }
    sf = fopen(statfile, "r");
    if( sf ) {
        while ( fgets(startup_line, BIG_LINE, sf) ) {
            /*
              The db2ldif process will usually print out a summary at the
              end, but that is not an error
            */
            char *ptr = strstr(startup_line, "Processed");
            if (ptr && strstr(ptr, "entries."))
            {
                ds_show_message(startup_line);
            }
            else
            {
                haderror = 1;
                ds_send_error(startup_line, 0);
            }
        }
        fclose(sf);
        unlink(statfile);
    }

    if ( haderror )
        return DS_UNKNOWN_ERROR;
    return 0;
}

/*
 * Create a LDIF file based on a file name.
 * 0:             success
 * anything else: failure
 */
DS_EXPORT_SYMBOL int
ds_db2ldif(char *file)
{
	return ds_db2ldif_subtree(file, NULL);
}


--- NEW FILE dsalib_location.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#endif
#include "dsalib.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nspr.h"
#include "plstr.h"

/*
 * Returns the server root. Info is 
 * returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_server_root()
{
    char        *root;
 
    if ( (root = getenv("NETSITE_ROOT")) == NULL )
        return(NULL);

    /* WIN32: Needed to take care of embedded space, */
    /* otherwise system() call fails */
    root = ds_makeshort( root );

    return root;
}

/*
 * Returns the install location of the server. Info is 
 * returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_install_root()
{
    char        *root;
    char        *ds_name;
    static char install_root[PATH_MAX];
 
    if ( (root = ds_get_server_root()) == NULL )
        return(NULL);
    if ( (ds_name = ds_get_server_name()) == NULL )
        return(NULL);

    PR_snprintf(install_root, sizeof(install_root), "%s/%s", root, ds_name);
    return(install_root);
}

/*
 * Returns the config file location of the server. Info is 
 * returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_config_dir()
{
    return getenv("DS_CONFIG_DIR");
}

/*
 * set config_dir to environment variable DS_CONFIG_DIR
 * to retrieve the value using ds_get_config_dir later.
 */
DS_EXPORT_SYMBOL void
ds_set_config_dir(char *config_dir)
{
    static char env[PATH_MAX];
    PR_snprintf(env, sizeof(env), "DS_CONFIG_DIR=%s", config_dir);
    putenv(env);
}

/*
 * Returns the run dir of the server, where pid files are put.
 * Info is returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_run_dir()
{
    return getenv("DS_RUN_DIR");
}

/*
 * set run_dir to environment variable DS_RUN_DIR
 * to retrieve the value using ds_get_run_dir later.
 */
DS_EXPORT_SYMBOL void
ds_set_run_dir(char *run_dir)
{
    static char env[PATH_MAX];
    PR_snprintf(env, sizeof(env), "DS_RUN_DIR=%s", run_dir);
    putenv(env);
}

/*
 * Returns the bakup dir of the server, where db backup files are put.
 * Info is returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_bak_dir()
{
    return getenv("DS_BAK_DIR");
}

/*
 * set bak_dir to environment variable DS_BAK_DIR
 * to retrieve the value using ds_get_bak_dir later.
 */
DS_EXPORT_SYMBOL void
ds_set_bak_dir(char *bak_dir)
{
    static char env[PATH_MAX];
    PR_snprintf(env, sizeof(env), "DS_BAK_DIR=%s", bak_dir);
    putenv(env);
}

/*
 * Returns the tmp dir of the server, where tmp files are put.
 * Info is returned in a static area. The caller must copy it 
 * for reuse if needed.
 */
DS_EXPORT_SYMBOL char *
ds_get_tmp_dir()
{
    return getenv("DS_TMP_DIR");
}

/*
 * set bak_dir to environment variable DS_TMP_DIR
 * to retrieve the value using ds_get_tmp_dir later.
 */
DS_EXPORT_SYMBOL void
ds_set_tmp_dir(char *tmp_dir)
{
    static char env[PATH_MAX];
    PR_snprintf(env, sizeof(env), "DS_TMP_DIR=%s", tmp_dir);
    putenv(env);
}

/*
 * Returns the install location of the server under the admserv
 * directory.
 */
DS_EXPORT_SYMBOL char *
ds_get_admserv_based_root()
{
    char        *root;
    char        *ds_name;
    static char install_root[PATH_MAX];
 
    if ( (root = getenv("ADMSERV_ROOT")) == NULL )
        return(NULL);
    if ( (ds_name = ds_get_server_name()) == NULL )
        return(NULL);
    PR_snprintf(install_root, sizeof(install_root), "%s/%s", root, ds_name);
    return(install_root);
}

DS_EXPORT_SYMBOL char *
ds_get_server_name()
{
    if( getenv("SERVER_NAMES") )
        return( getenv("SERVER_NAMES") );
    else {
        static char logfile[PATH_MAX];
        char *buf;
        char *out = logfile;
        buf = getenv("SCRIPT_NAME");
        if ( buf && (*buf == '/') )
            buf++;
        while ( *buf && (*buf != '/') ) {
            *out++ = *buf++;
        }
        *out = 0;
        return logfile;
    }
}

DS_EXPORT_SYMBOL char *
ds_get_logfile_name(int config_type)
{
    char        *filename;
    char        **ds_config = NULL;
    static char logfile[PATH_MAX+1];
 
    if ( (ds_config = ds_get_config(DS_REAL_CONFIG)) == NULL ) {
        /* For DS 4.0, no error output if file doesn't exist - that's
           a normal situation */
        /* ds_send_error("ds_get_config(DS_REAL_CONFIG) == NULL", 0); */
        return(NULL);
    }
    filename = ds_get_value(ds_config, ds_get_var_name(config_type), 0, 1);

    if ( filename == NULL ) {
        /* For DS 4.0, no error output if file doesn't exist - that's
           a normal situation */
        /* ds_send_error("ds_get_logfile_name: filename == NULL", 0); */
        return(NULL);
    }
    if ( ((int) strlen(filename)) >= PATH_MAX ) {
        ds_send_error("ds_get_logfile_name: filename too long", 0);
        free(filename);
        return(NULL);
    }
    PL_strncpyz(logfile, filename, sizeof(logfile));
    free(filename);
    return(logfile);
}

DS_EXPORT_SYMBOL char *
ds_get_errors_name()
{
    return( ds_get_logfile_name(DS_ERRORLOG) );
}

DS_EXPORT_SYMBOL char *
ds_get_access_name()
{
    return( ds_get_logfile_name(DS_ACCESSLOG) );
}

DS_EXPORT_SYMBOL char *
ds_get_audit_name()
{
    return( ds_get_logfile_name(DS_AUDITFILE) );
}



--- NEW FILE dsalib_pw.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

/*
 * Adjust password policy management related variables.
 * 
 * Valerie Chu
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include "ldap.h"
#include "ldif.h"
#include "sechash.h"
#include "dsalib.h"
#include "dsalib_pw.h"

#include "prtime.h"
#include "prlong.h"
#include "prmem.h"

#include <pk11func.h>
#include <pk11pqg.h>

#define SHA1_SALT_LENGTH    8   /* number of bytes of data in salt */
#define PWD_HASH_PREFIX_START   '{'
#define PWD_HASH_PREFIX_END '}'

#define SALTED_SHA1_SCHEME_NAME "SSHA"
#define SALTED_SHA1_NAME_LEN        4

/*
  WARNING:  The following code is blatantly copied from the server
  pwdstorage ssha_pwd.c plugin.  It would be nice to share this
  code with the server.  The problem is that the server wants to
  use slapi_ch_malloc to allocate the memory for the returned
  password - this function is not available outside the server
  (as in the setup programs that also want to hash the password)
  We need to figure out a way to put this code into a library
  in such a way that the memory allocation functions to use
  can be passed in or set beforehand.
*/

static void
ssha_rand_array(void *randx, size_t len)
{
    PK11_RandomUpdate(randx, len);
    PK11_GenerateRandom((unsigned char *)randx, (int)len);
}

/*
 * A salted SHA1 hash
 * if salt is null, no salt is used (this is for backward compatibility)
*/
SECStatus
sha1_salted_hash(unsigned char *hash_out, char *pwd, struct berval *salt)
{
    PK11Context *ctx;
    unsigned int outLen;
    SECStatus rc;

    if (salt && salt->bv_len) {
        ctx = PK11_CreateDigestContext(SEC_OID_SHA1);
		if (ctx == NULL) {
			rc = SECFailure;
		}
		else {
        	PK11_DigestBegin(ctx);
        	PK11_DigestOp(ctx, (unsigned char*)pwd, strlen(pwd));
        	PK11_DigestOp(ctx, (unsigned char*)(salt->bv_val), salt->bv_len);
        	PK11_DigestFinal(ctx, hash_out, &outLen, SHA1_LENGTH);
        	PK11_DestroyContext(ctx, 1);
        	if (outLen == SHA1_LENGTH)
            	rc = SECSuccess;
        	else
            	rc = SECFailure;
		}
    }
    else {
        /*backward compatibility*/
        rc = PK11_HashBuf(SEC_OID_SHA1, hash_out, (unsigned char *)pwd, strlen(pwd));
    }

    return rc;
}

char *
salted_sha1_pw_enc( char *pwd )
{
    unsigned char hash[ SHA1_LENGTH + SHA1_SALT_LENGTH ];
    unsigned char *salt = hash + SHA1_LENGTH;
    struct berval saltval;
    char *enc;

    saltval.bv_val = (void*)salt;
    saltval.bv_len = SHA1_SALT_LENGTH;

    /* generate a new random salt */
	/* Note: the uninitialized salt array provides a little extra entropy
	 * to the random array generation, but it is not really needed since
	 * PK11_GenerateRandom takes care of seeding. In any case, it doesn't
	 * hurt. */
	ssha_rand_array( salt, SHA1_SALT_LENGTH );

    /* SHA1 hash the user's key */
    if ( sha1_salted_hash( hash, pwd, &saltval ) != SECSuccess ) {
        return( NULL );
    }

    if (( enc = PR_Malloc( 3 + SALTED_SHA1_NAME_LEN +
        LDIF_BASE64_LEN(sizeof(hash)))) == NULL ) {
        return( NULL );
    }

    sprintf( enc, "%c%s%c", PWD_HASH_PREFIX_START, SALTED_SHA1_SCHEME_NAME,
        PWD_HASH_PREFIX_END );
    (void)ldif_base64_encode( hash, enc + 2 + SALTED_SHA1_NAME_LEN,
        sizeof(hash), -1 );

    return( enc );
}

DS_EXPORT_SYMBOL char *
ds_salted_sha1_pw_enc (char* pwd)
{
	return( salted_sha1_pw_enc(pwd) );
}


--- NEW FILE dsalib_tailf.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "dsalib.h"
#include "prthread.h"
#include "plstr.h"

/*
 * Function: adjustFile
 * Property: Adjust the file offset to the "tail" of the file
 * Called by: DisplayTail
 * Return: -1 for error, else file size
 */	
static int
adjustFile(FILE *fp, int curSize)
{
    struct stat	statBuf;
    int		fd = fileno(fp);

    if ( fstat(fd, &statBuf) == -1 )
	return(-1);
    if ( statBuf.st_size < curSize )	/* file has shrunk! */
    {
        if ( fseek(fp, 0L, 0) == -1 )	/* get back to the beginning */
	    return(-1);
    }
    curSize = (int) statBuf.st_size;
    if ( !curSize )
	curSize = 1;
    return(curSize);
}

/*
 * Function: wrapLines
 * Property: wrap lines at 50 characters.  When a wrap point is encountered,
 *           insert the string "\n", since the buffer is going to be placed
 *           inside a JavaScript alert() call.
 * Called by: ds_display_tail
 * Return: pointer to wrapped buffer.  Caller should free.
 */
static char *
wrapLines( char *buf )
{
    char *src = buf;
    char *obuf, *dst;
    int lwidth = 0;

    obuf = malloc( strlen( buf ) * 2 ); /* conservative */
    if ( obuf == NULL ) {
	return NULL;
    }
    dst = obuf;
    while ( *src != '\0' ) {
	if (( ++lwidth > 50 ) && isspace( *src )) {
	    *dst++ = '\\';
	    *dst++ = 'n';
	    lwidth = 0;
	    src++;
	} else {
	    *dst++ = *src++;
	}
    }
    *dst = '\0';
    return obuf;
}

DS_EXPORT_SYMBOL int
ds_get_file_size(char *fileName)
{
    struct stat	statBuf;

    if ( fileName == NULL )
        return(0);

    if ( stat(fileName, &statBuf) == -1 )
	return(0);

    return(statBuf.st_size);
}

/*
 * Function: ds_display_tail
 * Property: follow the tail and display it for timeOut secs or until the line
 * read from the file contains the string doneMsg; the lastLine, if not null,
 * will be filled in with the last line read from the file; this is useful
 * for determining why the server failed to start e.g. port in use, ran out
 * of semaphores, database is corrupted, etc.
 * Calls:    adjustFile
 */
DS_EXPORT_SYMBOL void 
ds_display_tail(char *fileName, int timeOut, int startSeek, char *doneMsg,
		char *lastLine)
{
	FILE    	*fp = NULL;
	int		fd;
	char		msgBuf[BIG_LINE];
	struct stat	statBuf;
	int		curSize;
	int		i = timeOut;

	if (lastLine != NULL)
	    lastLine[0] = 0;

	if ( fileName == NULL )
	    return;
	/*
	 * Open the file.
	 * Try to keep reading it assuming that it may get truncated.
	 */
	while (i && !fp)
	{
	    fp = fopen(fileName, "r");
	    if (!fp)
	    {
		PR_Sleep(PR_SecondsToInterval(1));
		--i;
		/* need to print something so http connection doesn't
		   timeout and also to let the user know something is
		   happening . . .
		*/
		if (!(i % 10))
		{
		    ds_send_status("Attempting to obtain server status . . .");
		}
	    }
	}

	if (!i || !fp)
	    return;

	fd = fileno(fp);
	if ( fstat(fd, &statBuf) == -1 ) {
	    (void) fclose(fp);
	    return;
	}
	curSize = (int) statBuf.st_size;
	if ( startSeek < curSize )
	    curSize = startSeek;
	if ( curSize > 0 )
	    if ( fseek(fp, curSize, SEEK_SET) == -1 ) {
		(void) fclose(fp);
		return;
	    }
	if ( !curSize )
	    curSize = 1;		/* ensure minimum */

	while ( i )
	{
	    int	newCurSize;

	    newCurSize = curSize = adjustFile(fp, curSize);
	    if ( curSize == -1 ) {
		(void) fclose(fp);
		return;
	    }
	    while ( fgets(msgBuf, sizeof(msgBuf), fp) )
	    {
		char *tmp;
		if (lastLine != NULL)
		    PL_strncpyz(lastLine, msgBuf, BIG_LINE);
		if ( (tmp = strchr(msgBuf, ((int) '\n'))) != NULL )
		    *tmp = '\0';	/* strip out real newlines from here */
		ds_send_status(msgBuf);
		if ( (strstr(msgBuf, "WARNING: ") != NULL) ||
		     (strstr(msgBuf, "ERROR: ") != NULL) ) {
		    char *wrapBuf;

		    wrapBuf = wrapLines( msgBuf );
		    if ( wrapBuf != NULL ) {
			ds_send_error(wrapBuf, 5);
		    } else {
			ds_send_error(msgBuf, 5);
		    }
		}
		if ( (doneMsg != NULL) && (strstr(msgBuf, doneMsg)) ) {
		    (void) fclose(fp);
		    return;
		}
	        newCurSize = adjustFile(fp, newCurSize);
	        if ( newCurSize == -1 ) {
		    (void) fclose(fp);
		    return;
	        }
	    }
	    if ( ferror(fp) ) {
		(void) fclose(fp);
		return;
	    }
	    clearerr(fp);	/* clear eof condition */
	    PR_Sleep(PR_SecondsToInterval(1));
	    if ( newCurSize != curSize )
		i = timeOut;	/* keep going till no more changes */
	    else
	        i--;
	}
	(void) fclose(fp);
}


--- NEW FILE dsalib_updown.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#include <process.h>
#include "regparms.h"
#else
#include <signal.h>
#include <sys/signal.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "dsalib.h"
#include <string.h>
#include "nspr.h"

#if defined( XP_WIN32 )
SC_HANDLE schService;
SC_HANDLE schSCManager;

int StartServer(); 
int StopandRestartServer();
int StopServer();
 
int StopNetscapeProgram();
int StartNetscapeProgram();

int StopNetscapeService();
int StartNetscapeService();
void WaitForServertoStop();
#endif

/*
 * Get status for the Directory Server.
 * 0 -- down
 * 1 -- up
 * -1 -- unknown
 */
#if !defined( XP_WIN32 )
static pid_t       server_pid;

DS_EXPORT_SYMBOL int
ds_get_updown_status()
{
    char pid_file_name[BIG_LINE];
    char *rundir;
    FILE *pidfile;
    int  ipid = -1;
    int  status = 0;
 
    if ( (rundir = ds_get_run_dir()) == NULL ) {
      fprintf(stderr, "ds_get_updown_status: could not get install root\n");
        return(DS_SERVER_UNKNOWN);
    }
    PR_snprintf(pid_file_name, BIG_LINE, "%s/%s.pid", rundir, ds_get_server_name());
    pidfile = fopen(pid_file_name, "r");
    if ( pidfile == NULL ) {
/* 
      fprintf(stderr,
            "ds_get_updown_status: could not open pid file=%s errno=%d\n",
            pid_file_name, errno);
*/
        return(DS_SERVER_DOWN);
   }
   status = fscanf(pidfile, "%d\n", &ipid);
   fclose(pidfile);
    if ( status == -1 ) {
      fprintf(stderr,
            "ds_get_updown_status: pidfile=%s server_pid=%d errno=%d\n",
            pid_file_name, ipid, errno);
        unlink(pid_file_name);     /* junk in file? */
        return(DS_SERVER_DOWN);
    }
    server_pid = (pid_t) ipid;
    if ( (status = kill(server_pid, 0)) != 0 && errno != EPERM ) {
      /* we should get ESRCH if the server is down, anything else may be
         a real problem */
      if (errno != ESRCH) {
         fprintf(stderr,
               "ds_get_updown_status: pidfile=%s server_pid=%d status=%d errno=%d\n",
               pid_file_name, server_pid, status, errno);
      }
        unlink(pid_file_name);     /* pid does not exist! */
        return(DS_SERVER_DOWN);
    }
    return(DS_SERVER_UP);
}
#else
DS_EXPORT_SYMBOL int
ds_get_updown_status()
{
    char   *ds_name = ds_get_server_name();
    HANDLE hServerDoneEvent = NULL;

    /* watchdog.c creates a global event of this same name */
    if((hServerDoneEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, ds_name)) != NULL) 
   {
       CloseHandle(hServerDoneEvent);
       return(DS_SERVER_UP);
    }
    if(GetLastError() == ERROR_ACCESS_DENIED)  /* it exists */
       return(DS_SERVER_UP);

    /* assume it's not running. */
    return(DS_SERVER_DOWN);
}
#endif

/*
  This function does not require calling ds_get_config(), but requires
  that that information be passed in.  This is very useful for starting
  the server during installation, because we already have all of the
  configuration information in memory, we don't need to read it in
*/
DS_EXPORT_SYMBOL int
ds_bring_up_server_install(int verbose, char *root, char *errorlog)
{
#if !defined( XP_WIN32 )
    char   startup_line[BIG_LINE];
    char   statfile[PATH_MAX];
    char   *tmp_dir;
#endif
    int     error = -1;
    int      status = DS_SERVER_DOWN;
    int      cur_size = 0;
    FILE   *sf = NULL;
    char   msgBuf[BIG_LINE] = {0};
    int secondsToWaitForServer = 600;
    char *serverStartupString = "slapd started.";

    status = ds_get_updown_status();
    if ( status == DS_SERVER_UP )
        return(DS_SERVER_ALREADY_UP);
    if (!root || !errorlog)
        return(DS_SERVER_UNKNOWN);
 
    if (verbose) {
        ds_send_status("starting up server ...");
        cur_size = ds_get_file_size(errorlog);
   }

#if !defined( XP_WIN32 )
    tmp_dir = ds_get_tmp_dir();
    PR_snprintf(statfile, PATH_MAX, "%s%cstartup.%d", tmp_dir, FILE_SEP, (int)getpid());

    PR_snprintf(startup_line, BIG_LINE, "%s%c%s > %s 2>&1",
         root, FILE_SEP, START_SCRIPT, statfile);
    alter_startup_line(startup_line);
    error = system(startup_line);
    if (error == -1)
      error = DS_SERVER_DOWN; /* could not start server */
    else
      error = DS_SERVER_UP; /* started server */
#else
    error = StartServer();
#endif

   if (error != DS_SERVER_UP)
   {
#if !defined( XP_WIN32 )
      FILE* fp = fopen(statfile, "r");
      if (fp)
      {
         while(fgets(msgBuf, BIG_LINE, fp))
            ds_send_status(msgBuf);
         fclose(fp);
      }
#endif
      return DS_SERVER_COULD_NOT_START;
   }

    if (verbose)
    {
      /* 
       * Stop in N secs or whenever the startup message comes up.
       * Do whichever happens first.  msgBuf will contain the last
       * line read from the errorlog.
       */
      ds_display_tail(errorlog, secondsToWaitForServer, cur_size,
                  serverStartupString, msgBuf);
    }
    if ( error != DS_SERVER_UP ) {
      int retval = DS_SERVER_UNKNOWN;
      if (strstr(msgBuf, "semget"))
         retval = DS_SERVER_MAX_SEMAPHORES;
      else if (strstr(msgBuf, "Back-End Initialization Failed"))
         retval = DS_SERVER_CORRUPTED_DB;
      else if (strstr(msgBuf, "not initialized... exiting"))
         retval = DS_SERVER_CORRUPTED_DB;
      else if (strstr(msgBuf, "address is in use"))
         retval = DS_SERVER_PORT_IN_USE;
#if defined( XP_WIN32 )
      /* on NT, if we run out of resources, there will not even be an error
         log
         */
      else if (msgBuf[0] == 0) {
         retval = DS_SERVER_NO_RESOURCES;
      }
#endif
      if (verbose)
         ds_send_error("error in starting server.", 1);
      return(retval);
    }
    if (verbose) {
#if !defined( XP_WIN32 )
      if( !(sf = fopen(statfile, "r")) )  {
         ds_send_error("could not read status file.", 1);
         return(DS_SERVER_UNKNOWN);
      }

      while ( fgets(startup_line, BIG_LINE, sf) )
         ds_send_error(startup_line, 0);
      fclose(sf);
      unlink(statfile);
#endif
      status = DS_SERVER_UNKNOWN;
      if (strstr(msgBuf, "semget"))
         status = DS_SERVER_MAX_SEMAPHORES;
      else if (strstr(msgBuf, "Back-End Initialization Failed"))
         status = DS_SERVER_CORRUPTED_DB;
      else if (strstr(msgBuf, "not initialized... exiting"))
         status = DS_SERVER_CORRUPTED_DB;
      else if (strstr(msgBuf, "address is in use"))
         status = DS_SERVER_PORT_IN_USE;
#if defined( XP_WIN32 )
      /* on NT, if we run out of resources, there will not even be an error
         log
         */
      else if (msgBuf[0] == 0) {
         status = DS_SERVER_NO_RESOURCES;
      }
#endif
    } else {
      int tries;
      for (tries = 0; tries < secondsToWaitForServer; tries++) {
         if (ds_get_updown_status() == DS_SERVER_UP) break;
         PR_Sleep(PR_SecondsToInterval(1));
      }
      if (verbose) {
         char str[100];
         PR_snprintf(str, sizeof(str), "Had to retry %d times", tries);
         ds_send_status(str);
      }
    }

    if ( (status == DS_SERVER_DOWN) || (status == DS_SERVER_UNKNOWN) )
      status = ds_get_updown_status();

    return(status);
}

/*
 * Start the Directory Server and return status.
 * Do not start if the server is already started.
 * 0 -- down
 * 1 -- up
 * -1 -- unknown
 * -2 -- already up
 */
 
DS_EXPORT_SYMBOL int
ds_bring_up_server(int verbose)
{
    char        *root;
    int      status;
    char   *errorlog;
    status = ds_get_updown_status();
    if ( status == DS_SERVER_UP )
        return(DS_SERVER_ALREADY_UP);
    if ( (root = ds_get_install_root()) == NULL )
        return(DS_SERVER_UNKNOWN);
 
    errorlog = ds_get_config_value(DS_ERRORLOG);
    if ( errorlog == NULL ) {
      errorlog = ds_get_errors_name(); /* fallback */
    }
   return ds_bring_up_server_install(verbose, root, errorlog);
}

DS_EXPORT_SYMBOL int
ds_bring_down_server()
{
    char        *root;
    int      status;
    int      cur_size;
    char *errorlog;
 
    status = ds_get_updown_status();   /* set server_pid too! */
    if ( status != DS_SERVER_UP ) {
   ds_send_error("The server is not up.", 0);
        return(DS_SERVER_ALREADY_DOWN);
    }
    if ( (root = ds_get_install_root()) == NULL ) {
   ds_send_error("Could not get the server root directory.", 0);
        return(DS_SERVER_UNKNOWN);
    }
 
    ds_send_status("shutting down server ...");
    if (!(errorlog = ds_get_errors_name())) {
   ds_send_error("Could not get the error log filename.", 0);
   return DS_SERVER_UNKNOWN;
    }

    cur_size = ds_get_file_size(errorlog);
#if !defined( XP_WIN32 )
    if ( (kill(server_pid, SIGTERM)) != 0)  {
   if (errno == EPERM) {
       ds_send_error("Not permitted to kill server.", 0);
       fprintf (stdout, "[%s]: kill (%li, SIGTERM) failed with errno = EPERM.<br>\n",
           ds_get_server_name(), (long)server_pid);
   } else {
       ds_send_error("error in killing server.", 1);
   }
        return(DS_SERVER_UNKNOWN);
    }
#else
    if( StopServer() == DS_SERVER_DOWN )  
   {
      ds_send_status("shutdown: server shut down");
   }
   else
   {
        ds_send_error("error in killing server.", 1);
        return(DS_SERVER_UNKNOWN);
   }
#endif
    /* 
     * Wait up to SERVER_STOP_TIMEOUT seconds for the stopped message to
    * appear in the error log.
     */
    ds_display_tail(errorlog, SERVER_STOP_TIMEOUT, cur_size, "slapd stopped.", NULL);
    /* in some cases, the server will tell us it's down when it's really not,
       so give the OS a chance to remove it from the process table */
   PR_Sleep(PR_SecondsToInterval(1));
    return(ds_get_updown_status());
}

#if defined( XP_WIN32 )

static BOOLEAN
IsService()
{
#if 0
    CHAR ServerKey[512], *ValueString;
   HKEY hServerKey;
   DWORD dwType, ValueLength, Result;

   PR_snprintf(ServerKey,sizeof(ServerKey), "%s\\%s", COMPANY_KEY, PRODUCT_KEY);

   Result = RegOpenKey(HKEY_LOCAL_MACHINE, ServerKey, &hServerKey);
   if (Result != ERROR_SUCCESS) {
      return TRUE;
   }
   ValueLength = 512;
   ValueString = (PCHAR)malloc(ValueLength);

   Result = RegQueryValueEx(hServerKey, IS_SERVICE_KEY, NULL,
      &dwType, ValueString, &ValueLength);
   if (Result != ERROR_SUCCESS) {
      return TRUE;
   }
   if (strcmp(ValueString, "yes")) {
      return FALSE;
   }
   else {
      return TRUE;
   }
#else
   return TRUE;
#endif
}

#if 0
NSAPI_PUBLIC BOOLEAN
IsAdminService()
{
    CHAR AdminKey[512], *ValueString;
   HKEY hAdminKey;
   DWORD dwType, ValueLength, Result;

   PR_snprintf(AdminKey,sizeof(AdminKey), "%s\\%s", COMPANY_KEY, ADMIN_REGISTRY_ROOT_KEY);

   Result = RegOpenKey(HKEY_LOCAL_MACHINE, AdminKey, &hAdminKey);
   if (Result != ERROR_SUCCESS) {
      return TRUE;
   }
   ValueLength = 512;
   ValueString = (PCHAR)malloc(ValueLength);

   Result = RegQueryValueEx(hAdminKey, IS_SERVICE_KEY, NULL,
      &dwType, ValueString, &ValueLength);
   if (Result != ERROR_SUCCESS) {
      return TRUE;
   }
   if (strcmp(ValueString, "yes")) {
      return FALSE;
   } else {
      return TRUE;
   }
}
#endif

int
StartServer()
{
    CHAR ErrorString[512];
    BOOLEAN Service;

    /* Figure out if the server is a service or an exe */
    Service = IsService();

     if(Service) {
        if (!(schSCManager = OpenSCManager(
                NULL,                   // machine (NULL == local)
                NULL,                   // database (NULL == default)
                SC_MANAGER_ALL_ACCESS   // access required
                ))) {
         PR_snprintf(ErrorString, sizeof(ErrorString),
                "Error: Could not open the ServiceControlManager:%d "
                "Please restart the server %s from the Services Program Item "
                "in the Control Panel", ds_get_server_name(), GetLastError());
            ds_send_error(ErrorString, 0);
            return(DS_SERVER_UNKNOWN);  
        }
        return(StartNetscapeService());
    } else {
        return(StartNetscapeProgram());
    }
}
 
int
StopandRestartServer()
{
    CHAR ErrorString[512];
    BOOLEAN Service;


    /* First figure out if the server is a service or an exe */
    Service = IsService();
     if(Service) {
        if (!(schSCManager = OpenSCManager(
                NULL,                   // machine (NULL == local)
                NULL,                   // database (NULL == default)
                SC_MANAGER_ALL_ACCESS   // access required
                ))) {
            PR_snprintf(ErrorString, sizeof(ErrorString),
                "Error: Could not restart server."
                "Please restart the server %s from the Services Program Item "
                "in the Control Panel", ds_get_server_name());
            ds_send_error(ErrorString, 0);
            return(DS_SERVER_UNKNOWN);  
        }
        if (StopNetscapeService() != DS_SERVER_DOWN)
            return(DS_SERVER_UNKNOWN);

        return(StartNetscapeService());
    } else {
        if (StopNetscapeProgram() != DS_SERVER_DOWN)
            return(DS_SERVER_UNKNOWN);
        return(StartNetscapeProgram());
    }
 
}
 
int
StopServer()
{
    CHAR ErrorString[512];
    BOOLEAN Service;

    /* First figure out if the server is a service or an exe */
    Service = IsService();

    if(Service) {
        if (!(schSCManager = OpenSCManager(
                NULL,                   // machine (NULL == local)
                NULL,                   // database (NULL == default)
                SC_MANAGER_ALL_ACCESS   // access required
                ))) {
            PR_snprintf(ErrorString, sizeof(ErrorString),
                "Error: Could not open the ServiceControlManager:%d "
                "Please restart the server %s from the Services Program Item "
                "in the Control Panel", ds_get_server_name(), GetLastError());
         ds_send_error(ErrorString, 0);
            return(DS_SERVER_UNKNOWN);  
        }
        return(StopNetscapeService());
    } else {
        return(StopNetscapeProgram());
    }
}

int
StartNetscapeProgram()
{
    char line[BIG_LINE], cmd[BIG_LINE];
    char *tmp = ds_get_install_root();

   CHAR ErrorString[512];
   STARTUPINFO siStartInfo;
   PROCESS_INFORMATION piProcInfo;
    FILE *CmdFile;

    ZeroMemory(line, sizeof(line));

    PR_snprintf(line, BIG_LINE, "%s\\startsrv.bat", tmp);
    
    CmdFile = fopen(line, "r");
    if (!CmdFile) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Error:Tried to start server %s "
            ": Could not open the startup script %s :Error %d. Please "
            "run startsrv.bat from the server's root directory.",
            ds_get_server_name(), line, errno);
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_DOWN);
    }

    ZeroMemory(cmd, sizeof(cmd));
    if (!fread(cmd, 1, BIG_LINE, CmdFile)) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Error:Tried to start server %s "
            ": Could not read the startup script %s :Error %d. Please "
            "run startsrv.bat from the server's root directory.",
            ds_get_server_name(), line, errno);
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_DOWN);
    }
    
   ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
   siStartInfo.cb = sizeof(STARTUPINFO);
   siStartInfo.lpReserved = siStartInfo.lpReserved2 = NULL;
   siStartInfo.cbReserved2 = 0;
   siStartInfo.lpDesktop = NULL;

   if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE,
      0, NULL, NULL, &siStartInfo, &piProcInfo)) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Error:Tried to start server %s "
            ": Could not start up the startup script %s :Error %d. Please "
            "run startsrv.bat from the server's root directory.",
            ds_get_server_name(), line, GetLastError());
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_DOWN);
   } 

   CloseHandle(piProcInfo.hProcess);
   CloseHandle(piProcInfo.hThread);
    return(DS_SERVER_UP);
}

int
StopNetscapeProgram()
{
    HANDLE hEvent;
    CHAR ErrorString[512];
    char *servid = ds_get_server_name();

    hEvent = CreateEvent(NULL, TRUE, FALSE, servid);  
    if(!SetEvent(hEvent)) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Tried to stop existing server %s"
            ": Could not signal it to stop :Error %d",
            servid, GetLastError());
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_UNKNOWN);
    }

    return(DS_SERVER_DOWN);  
}

int
StopNetscapeService()
{
    BOOL ret;
    SERVICE_STATUS ServiceStatus;
    DWORD Error;
    CHAR ErrorString[512];
    char *serviceName = ds_get_server_name();
 
    schService = OpenService(schSCManager, serviceName, SERVICE_ALL_ACCESS);
 
    if (schService == NULL) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Tried to open service"
            " %s: Error %d (%s). Please"
            " stop the server from the Services Item in the Control Panel",
            serviceName, GetLastError(), ds_system_errmsg());
            ds_send_error(ErrorString, 0);
            return(DS_SERVER_UP);
    }
 
    ret = ControlService(schService, SERVICE_CONTROL_STOP, &ServiceStatus);
    Error = GetLastError();
    /* if ControlService returns with ERROR_SERVICE_CANNOT_ACCEPT_CTRL and
       the server status indicates that it is either shutdown or in the process
        of shutting down, then just wait for it to stop as usual */
    if (ret ||
        ((Error == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) &&
         ((ServiceStatus.dwCurrentState == SERVICE_STOPPED) ||
          (ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING))))
    {
        CloseServiceHandle(schService);
        /* We make sure that the service is stopped */
        WaitForServertoStop();
        return(DS_SERVER_DOWN);
    } 
    else if (Error != ERROR_SERVICE_NOT_ACTIVE) 
   {
        PR_snprintf(ErrorString, sizeof(ErrorString), "Tried to stop service"
            " %s: Error %d (%s)."
            " Please stop the server from the Services Item in the"
            " Control Panel", serviceName, Error, ds_system_errmsg());
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_UNKNOWN);
    }
    return(DS_SERVER_DOWN);
}
 
 
int
StartNetscapeService()
{
    CHAR ErrorString[512];
    int retries = 0;
    char *serviceName = ds_get_server_name();
 
    schService = OpenService(
                    schSCManager,               // SCManager database
                    serviceName,                // name of service
                    SERVICE_ALL_ACCESS); 
    if (schService == NULL) 
   {
        CloseServiceHandle(schService);
        PR_snprintf(ErrorString, sizeof(ErrorString),"Tried to start"
            " the service %s: Error %d. Please"
            " start the server from the Services Item in the Control Panel",
            serviceName, GetLastError());
            ds_send_error(ErrorString, 0);
        return(DS_SERVER_DOWN);
    }
 
    if (!StartService(schService, 0, NULL)) 
   {
        CloseServiceHandle(schService);
        PR_snprintf(ErrorString, sizeof(ErrorString), "StartService:Could not start "
            "the Directory service %s: Error %d. Please restart the server "
            "from the Services Item in the Control Panel",
            serviceName, GetLastError());
        ds_send_error(ErrorString, 0);
        return(DS_SERVER_DOWN);
    }

    CloseServiceHandle(schService);
    return(DS_SERVER_UP);
}

void
WaitForServertoStop()
{
    HANDLE hServDoneSemaphore;
    int result,retries = 0;
    char *serviceName = ds_get_server_name();
    char *newServiceName;

RETRY:

   newServiceName = PR_smprintf("NS_%s", serviceName);

    hServDoneSemaphore = CreateSemaphore(
        NULL,   // security attributes    
        0,      // initial count for semaphore
        1,      // maximum count for semaphore
        newServiceName);

    PR_smprintf_free(newServiceName);

    if ( hServDoneSemaphore == NULL) {
        result = GetLastError();
        if (result == ERROR_INVALID_HANDLE) {
             if (retries < SERVER_STOP_TIMEOUT) {
                retries++;
                Sleep(1000);
                goto RETRY;
             }
       } else {
            /* We aren't too interested in why the creation failed
             * if it is not because of another instance */
            return;
       }
    }  // hServDoneSemaphore == NULL
    CloseHandle(hServDoneSemaphore);
    return;
}
#endif


--- NEW FILE dsalib_util.c ---
/** 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.
 * 
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 * right to link the code of this Program with code not covered under the GNU
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 * permitted under this exception must only link to the code of this Program
 * through those well defined interfaces identified in the file named EXCEPTION
 * found in the source code files (the "Approved Interfaces"). The files of
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 * the Approved Interfaces without causing the resulting work to be covered by
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 * additions to the list of Approved Interfaces. You must obey the GNU General
 * Public License in all respects for all of the Program code and other code used
 * in conjunction with the Program except the Non-GPL Code covered by this
 * exception. If you modify this file, you may extend this exception to your
 * version of the file, but you are not obligated to do so. If you do not wish to
 * provide this exception without modification, you must delete this exception
 * statement from your version and license this file solely under the GPL without
 * exception. 
 * 
 * 
 * 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

#if defined( XP_WIN32 )
#include <windows.h>
#include <io.h>
#else /* XP_WIN32 */
#   if defined( AIXV4 )
#   include <fcntl.h>
#   else /* AIXV4 */
#   include <sys/fcntl.h>
#   endif /* AIXV4 */
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#endif /* XP_WIN3 */
#include "dsalib.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>

#include "nspr.h"
#include "plstr.h"

#define COPY_BUFFER_SIZE        4096
/* This is the separator string to use when outputting key/value pairs
   to be read by the non-HTML front end (Java console)
*/
static const char *SEPARATOR = ":"; /* from AdmTask.java */

#define LOGFILEENVVAR "DEBUG_LOGFILE" /* used for logfp */

static int internal_rm_rf(const char *path, DS_RM_RF_ERR_FUNC ds_rm_rf_err_func, void *arg);

/* return a FILE * opened in append mode to the log file
   caller must use fclose to close it
*/
static FILE *
get_logfp(void)
{
	FILE *logfp = NULL;
	char *logfile = getenv(LOGFILEENVVAR);

	if (logfile) {
		logfp = fopen(logfile, "a");
	}
	return logfp;
}

DS_EXPORT_SYMBOL int 
ds_file_exists(char *filename)
{
    struct stat finfo;

    if ( filename == NULL )
	return 0;

    if ( stat(filename, &finfo) == 0 )	/* successful */
        return 1;
    else
        return 0;
}

DS_EXPORT_SYMBOL int
ds_mkdir(char *dir, int mode)
{
    if(!ds_file_exists(dir)) {
#ifdef XP_UNIX
        if(mkdir(dir, mode) == -1)
#else  /* XP_WIN32 */
        if(!CreateDirectory(dir, NULL))
#endif /* XP_WIN32 */
            return -1;
    }
    return 0;
}


DS_EXPORT_SYMBOL char *
ds_mkdir_p(char *dir, int mode)
{
    static char errmsg[ERR_SIZE];
    struct stat fi;
    char *t;

#ifdef XP_UNIX
    t = dir + 1;
#else /* XP_WIN32 */
    t = dir + 3;
#endif /* XP_WIN32 */

    while(1) {
        t = strchr(t, FILE_PATHSEP);

        if(t) *t = '\0';
        if(stat(dir, &fi) == -1) {
            if(ds_mkdir(dir, mode) == -1) {
                PR_snprintf(errmsg, sizeof(errmsg), "mkdir %s failed (%s)", dir, ds_system_errmsg());
                return errmsg;
            }
        }
        if(t) *t++ = FILE_PATHSEP;
        else break;
    }
    return NULL;
}


/*
 * Given the name of a directory, return a NULL-terminated array of
 * the file names contained in that directory.  Returns NULL if the directory
 * does not exist or an error occurs, and returns an array with a
 * single NULL string if the directory exists but is empty.  The caller
 * is responsible for freeing the returned array of strings.
 * File names "." and ".." are not returned.
 */
#if !defined( XP_WIN32 )
DS_EXPORT_SYMBOL char **
ds_get_file_list( char *dir )
{
    DIR *dirp;
    struct dirent *direntp;
    char **ret = NULL;
    int nfiles = 0;

    if (( dirp = opendir( dir )) == NULL ) {
	return NULL;
    }
    
    if (( ret = malloc( sizeof( char * ))) == NULL ) {
	return NULL;
    };

    while (( direntp = readdir( dirp )) != NULL ) {
	if ( strcmp( direntp->d_name, "." ) &&
		strcmp( direntp->d_name, ".." )) {
	    if (( ret = (char **) realloc( ret,
		    sizeof( char * ) * ( nfiles + 2 ))) == NULL );
	    ret[ nfiles ] = strdup( direntp->d_name );
	    nfiles++;
	}
    }
    (void) closedir( dirp );

    ret[ nfiles ] = NULL;
    return ret;
}
#else
DS_EXPORT_SYMBOL char **
ds_get_file_list( char *dir )
{
	char szWildcardFileSpec[MAX_PATH];
	char **ret = NULL;
	long hFile;
	struct _finddata_t	fileinfo;
	int	nfiles = 0;

	if( ( dir == NULL ) || (strlen( dir ) == 0) )
		return NULL;

    if( ( ret = malloc( sizeof( char * ) ) ) == NULL ) 
		return NULL;

	PL_strncpyz(szWildcardFileSpec, dir, sizeof(szWildcardFileSpec));
	PL_strcatn(szWildcardFileSpec, sizeof(szWildcardFileSpec), "/*");

	hFile = _findfirst( szWildcardFileSpec, &fileinfo);
	if( hFile == -1 )
		return NULL;

	if( ( strcmp( fileinfo.name, "." ) != 0 ) &&
		( strcmp( fileinfo.name, ".." ) != 0 ) )
	{
	    ret[ nfiles++ ] = strdup( fileinfo.name );
	}

	while( _findnext( hFile, &fileinfo ) == 0 )
	{
		if( ( strcmp( fileinfo.name, "." ) != 0 ) &&
			( strcmp( fileinfo.name, ".." ) != 0 ) )
		{
			if( ( ret = (char **) realloc( ret, sizeof( char * ) * ( nfiles + 2 ) ) ) != NULL ) 
				ret[ nfiles++ ] = strdup( fileinfo.name);
		}
	}

	_findclose( hFile );
    ret[ nfiles ] = NULL;
	return ret;
}
#endif /* ( XP_WIN32 ) */


DS_EXPORT_SYMBOL time_t 
ds_get_mtime(char *filename)
{
    struct stat fi;

    if ( stat(filename, &fi) )
        return 0;
    return fi.st_mtime;
}

/*
 * Copy files: return is
 *    1: success
 *    0: failure
 * Print errors as needed.
 */
DS_EXPORT_SYMBOL int 
ds_cp_file(char *sfile, char *dfile, int mode)
{
#if defined( XP_WIN32 )
	return( CopyFile( sfile, dfile, FALSE ) ); /* Copy even if dfile exists */
#else
    int sfd, dfd, len;
    struct stat fi;
    char copy_buffer[COPY_BUFFER_SIZE];
    unsigned long read_len;
    char error[BIG_LINE];

/* Make sure we're in the right umask */
    umask(022);

    if( (sfd = open(sfile, O_RDONLY)) == -1) {
        PR_snprintf(error, sizeof(error), "Can't open file %s for reading.", sfile);
        ds_send_error(error, 1);
	return(0);
    }

    fstat(sfd, &fi);
    if (!(S_ISREG(fi.st_mode))) {
        PR_snprintf(error, sizeof(error), "File %s is not a regular file.", sfile);
        ds_send_error(error, 1);
        close(sfd);
	return(0);
    }
    len = fi.st_size;

    if( (dfd = open(dfile, O_RDWR | O_CREAT | O_TRUNC, mode)) == -1) {
        PR_snprintf(error, sizeof(error), "can't write to file %s", dfile);
        ds_send_error(error, 1);
        close(sfd);
	return(0);
    }
    while (len) {
        read_len = len>COPY_BUFFER_SIZE?COPY_BUFFER_SIZE:len;

        if ( (read_len = read(sfd, copy_buffer, read_len)) == -1) {
            PR_snprintf(error, sizeof(error), "Error reading file %s for copy.", sfile);
            ds_send_error(error, 1);
            close(sfd);
            close(dfd);
	    return(0);
        }

        if ( write(dfd, copy_buffer, read_len) != read_len) {
            PR_snprintf(error, sizeof(error), "Error writing file %s for copy.", dfile);
            ds_send_error(error, 1);
            close(sfd);
            close(dfd);
	    return(0);
        }

        len -= read_len;
    }
    close(sfd);
    close(dfd);
    return(1);
#endif
}

DS_EXPORT_SYMBOL void 
ds_unixtodospath(char *szText)
{
	if(szText)
	{
		while(*szText)
   		{
   			if( *szText == '/' )
   				*szText = '\\';
   			szText++;
		}
	}
}

/* converts '\' chars to '/' */
DS_EXPORT_SYMBOL void 
ds_dostounixpath(char *szText)
{
	if(szText)
	{
		while(*szText)
   		{
   			if( *szText == '\\' )
   				*szText = '/';
   			szText++;
		}
	}
}

/* converts ':' chars to ' ' */
DS_EXPORT_SYMBOL void 
ds_timetofname(char *szText)
{
	if(szText)
	{
		/* Replace trailing newline */
		szText[ strlen( szText ) -1 ] = 0;
		while(*szText)
   		{
			if( *szText == ':' ||
			    *szText == ' ' )
   				*szText = '_';
   			szText++;
		}
	}
}

/* Effects a rename in 2 steps, needed on NT because if the 
target of a rename() already exists, the rename() will fail. */
DS_EXPORT_SYMBOL int 
ds_saferename(char *szSrc, char *szTarget)
{
#ifdef XP_WIN32
	int iRetVal;
	char *szTmpFile;
	struct stat buf;
#endif

	if( !szSrc || !szTarget )
		return 1;

#if defined( XP_WIN32 )

	szTmpFile = mktemp("slrnXXXXXX" );
	if( stat( szTarget, &buf ) == 0 )
	{
		/* Target file exists */
		if( !szTmpFile )
			return 1;

		if( !ds_cp_file( szTarget, szTmpFile, 0644) )
			return( 1 );		

		unlink( szTarget );
		if( (iRetVal = rename( szSrc, szTarget )) != 0 )
		{
			/* Failed to rename, copy back. */
			ds_cp_file( szTmpFile, szTarget, 0644);			
		}
		/* Now remove temp file */
		unlink( szTmpFile );
	}
	else
		iRetVal = rename(szSrc, szTarget);

	return iRetVal;	
#else
	return rename(szSrc, szTarget);
#endif

}

DS_EXPORT_SYMBOL char*
ds_encode_all (const char* s)
{
    char* r;
    size_t l;
    size_t i;
    if (s == NULL || *s == '\0') {
	return strdup ("");
    }
    l = strlen (s);
    r = malloc (l * 3 + 1);
    for (i = 0; *s != '\0'; ++s) {
	r[i++] = '%';
	sprintf (r + i, "%.2X", 0xFF & (unsigned int)*s);
	i += 2;
    }
    r[i] = '\0';
    return r;
}

DS_EXPORT_SYMBOL char*
ds_URL_encode (const char* s)
{
    char* r;
    size_t l;
    size_t i;
    if (s == NULL || *s == '\0') {
	return strdup ("");
    }
    l = strlen (s) + 1;
    r = malloc (l);
    for (i = 0; *s != '\0'; ++s) {
	if (*s >= 0x20 && *s <= 0x7E && strchr (" <>\"#%{}[]|\\^~`?,;=+\n", *s) == NULL) {
	    if (l - i <= 1) r = realloc (r, l *= 2);
	    r[i++] = *s;
	} else { /* encode *s */
	    if (l - i <= 3) r = realloc (r, l *= 2);
	    r[i++] = '%';
	    sprintf (r + i, "%.2X", 0xFF & (unsigned int)*s);
	    i += 2;
	}
    }
    r[i] = '\0';
    return r;
}

DS_EXPORT_SYMBOL char*
ds_URL_decode (const char* original)
{
    char* r = strdup (original);
    char* s;
    for (s = r; *s != '\0'; ++s) {
	if (*s == '+') {
	    *s = ' ';
	}
	else if (*s == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
	    memmove (s, s+1, 2);
	    s[2] = '\0';
	    *s = (char)strtoul (s, NULL, 16);
	    memmove (s+1, s+3, strlen (s+3) + 1);
	}
    }
    return r;
}

#if !defined( XP_WIN32 )
#include <errno.h> /* errno */
#include <pwd.h> /* getpwnam */

static int   saved_uid_valid = 0;
static uid_t saved_uid;
static int   saved_gid_valid = 0;
static gid_t saved_gid;

#if defined( HPUX )
#define SETEUID(id) setresuid((uid_t) -1, id, (uid_t) -1)
#else
#define SETEUID(id) seteuid(id)
#endif

#endif

DS_EXPORT_SYMBOL char*
ds_become_localuser_name (char *localuser)
{
#if !defined( XP_WIN32 )
    if (localuser != NULL) {
	struct passwd* pw = getpwnam (localuser);
	if (pw == NULL) {
	    fprintf (stderr, "getpwnam(%s) == NULL; errno %d",
		     localuser, errno);
		fprintf (stderr, "\n");
	    fflush (stderr);
	} else {
	    if ( ! saved_uid_valid) saved_uid = geteuid();
	    if ( ! saved_gid_valid) saved_gid = getegid();
	    if (setgid (pw->pw_gid) == 0) {
		saved_gid_valid = 1;
	    } else {
		fprintf (stderr, "setgid(%li) != 0; errno %d",
			 (long)pw->pw_gid, errno);
		fprintf (stderr, "\n");
		fflush (stderr);
	    }
	    if (SETEUID (pw->pw_uid) == 0) {
		saved_uid_valid = 1;
	    } else {
		fprintf (stderr, "seteuid(%li) != 0; errno %d",
			 (long)pw->pw_uid, errno);
		fprintf (stderr, "\n");
		fflush (stderr);
	    }
	}
    }
    return NULL;
#else
    return NULL;
#endif
}

DS_EXPORT_SYMBOL char*
ds_become_localuser (char **ds_config)
{
#if !defined( XP_WIN32 )
    char* localuser = ds_get_value (ds_config, ds_get_var_name(DS_LOCALUSER), 0, 1);
    if (localuser != NULL) {
	char	*rv = ds_become_localuser_name(localuser);

	free(localuser);
	return rv;
    }
    return NULL;
#else
    return NULL;
#endif
}

DS_EXPORT_SYMBOL char*
ds_become_original (char **ds_config)
{
#if !defined( XP_WIN32 )
    if (saved_uid_valid) {
	if (SETEUID (saved_uid) == 0) {
	    saved_uid_valid = 0;
	} else {
	    fprintf (stderr, "seteuid(%li) != 0; errno %d<br>n",
		     (long)saved_uid, errno);
	    fflush (stderr);
	}
    }
    if (saved_gid_valid) {
	if (setgid (saved_gid) == 0) {
	    saved_gid_valid = 0;
	} else {
	    fprintf (stderr, "setgid(%li) != 0; errno %d<br>\n",
		     (long)saved_gid, errno);
	    fflush (stderr);
	}
    }
    return NULL;
#else
    return NULL;
#endif
}

/*
 * When a path containing a long filename is passed to system(), the call
 * fails. Therfore, we need to use the short version of the path, when 
 * constructing the path to pass to system().
 */
DS_EXPORT_SYMBOL char*
ds_makeshort( char * filepath )
{
#if defined( XP_WIN32 )
	char *shortpath = malloc( MAX_PATH );
	DWORD dwStatus;
	if( shortpath )
	{
		dwStatus = GetShortPathName( filepath, shortpath, MAX_PATH );
		return( shortpath );
	}
#endif
	return filepath;
}

/* returns 1 if string "searchstring" found in file "filename" */
/* if found, returnstring is allocated and filled with the line */
/* caller should release the memory */
DS_EXPORT_SYMBOL int 
ds_search_file(char *filename, char *searchstring, char **returnstring)
{
    struct stat finfo;
	FILE * sf;
	char big_line[BIG_LINE];

    if( filename == NULL )
		return 0;

    if( stat(filename, &finfo) != 0 )	/* successful */
        return 0;

	if( !(sf = fopen(filename, "r")) )  
		return 0;

	while ( fgets(big_line, BIG_LINE, sf) ) {
		if( strstr( big_line, searchstring ) != NULL ) {
			*returnstring = (char *)malloc(strlen(big_line) + 1);
			if (NULL != *returnstring) {
				strcpy(*returnstring, big_line);
			}
		    fclose(sf);
			return 1;
		}
	}

	fclose(sf);

	return 0;
}

/*
 * on linux when running as root, doing something like 
 * system("date > out.log 2>&1") will fail, because of an 
 * ambigious redirect.  This works for /bin/sh, but not /bin/csh or /bin/tcsh
 *
 * using this would turn
 * 	system("date > out.log 2>&1");
 * into
 * 	system("/bin/sh/ -c \"date > out.log 2>&1\"")
 *
 */
DS_EXPORT_SYMBOL void
alter_startup_line(char *startup_line)
{
#if (defined Linux && !defined LINUX2_4)
        char temp_startup_line[BIG_LINE+40];
 
        PR_snprintf(temp_startup_line, sizeof(temp_startup_line), "/bin/sh -c \"%s\"", startup_line);
        PL_strncpyz(startup_line, temp_startup_line, BIG_LINE);
#else
	/* do nothing */
#endif /* Linux */
}

DS_EXPORT_SYMBOL void
ds_send_error(char *errstr, int print_errno)
{
	FILE *logfp;
	fprintf(stdout, "error%s%s\n", SEPARATOR, errstr);
	if (print_errno && errno)
		fprintf(stdout, "system_errno%s%d\n", SEPARATOR, errno);

    fflush(stdout);

	if ((logfp = get_logfp())) {
		fprintf(logfp, "error%s%s\n", SEPARATOR, errstr);
		if (print_errno && errno)
			fprintf(logfp, "system_errno%s%d\n", SEPARATOR, errno);
		fclose(logfp);
	}

}

DS_EXPORT_SYMBOL void
ds_send_status(char *str)
{
	FILE *logfp;
    fprintf(stdout, "[%s]: %s\n", ds_get_server_name(), str);
    fflush(stdout);

	if ((logfp = get_logfp())) {
	    fprintf(logfp, "[%s]: %s\n", ds_get_server_name(), str);
		fclose(logfp);
	}
}

/* type and doexit are unused
   I'm not sure what type is supposed to be used for
   removed the doexit code because we don't want to
   exit abruptly anymore, we must exit by returning an
   exit code from the return in main()
*/
static void
report_error(int type, char *msg, char *details, int doexit)
{
    char error[BIG_LINE*4] = {0};

	if (msg)
	{
		PL_strcatn(error, BIG_LINE*4, msg);
		PL_strcatn(error, BIG_LINE*4, SEPARATOR);
	}
	if (details)
		PL_strcatn(error, BIG_LINE*4, details);
	ds_send_error(error, 1);
}

DS_EXPORT_SYMBOL void
ds_report_error(int type, char *msg, char *details)
{
	/* richm - changed exit flag to 0 - we must not exit
	   abruptly, we should instead exit by returning a code
	   as the return value of main - this ensures that callers
	   are properly notified of the status
	*/
	report_error(type, msg, details, 0);
}

DS_EXPORT_SYMBOL void
ds_report_warning(int type, char *msg, char *details)
{
	report_error(type, msg, details, 0);
}

DS_EXPORT_SYMBOL void
ds_show_message(const char *message)
{
	FILE *logfp;
	printf("%s\n", message);
	fflush(stdout);

	if ((logfp = get_logfp())) {
		fprintf(logfp, "%s\n", message);
		fclose(logfp);
	}

	return;
}

DS_EXPORT_SYMBOL void
ds_show_key_value(char *key, char *value)
{
	FILE *logfp;
	printf("%s%s%s\n", key, SEPARATOR, value);

	if ((logfp = get_logfp())) {
		fprintf(logfp, "%s%s%s\n", key, SEPARATOR, value);
		fclose(logfp);
	}
	return;
}

/* Stolen from the Admin Server dsgw_escape_for_shell */
DS_EXPORT_SYMBOL char * 
ds_escape_for_shell( char *s ) 
{ 
    char        *escaped; 
    char        tmpbuf[4]; 
    size_t x,l; 
  
    if ( s == NULL ) { 
        return( s ); 
    } 
  
    l = 3 * strlen( s ) + 1; 
    escaped = malloc( l ); 
    memset( escaped, 0, l ); 
    for ( x = 0; s[x]; x++ ) { 
        if (( (unsigned char)s[x] & 0x80 ) == 0 ) { 
           strncat( escaped, &s[x], 1 ); 
        } else { 
           /* not an ASCII character - escape it */ 
           sprintf( tmpbuf, "\\%x", (unsigned)(((unsigned char)(s[x])) & 0xff) ); 
           strcat( escaped, tmpbuf ); 
        } 
    } 
    return( escaped ); 
}

DS_EXPORT_SYMBOL char *
ds_system_errmsg(void)
{
    static char static_error[BUFSIZ];
    char *lmsg = 0; /* Local message pointer */
    size_t msglen = 0;
    int sys_error = 0;
#ifdef XP_WIN32
    LPTSTR sysmsg = 0;
#endif

    /* Grab the OS error message */
#ifdef XP_WIN32
    sys_error = GetLastError();
#else
    sys_error = errno;
#endif

#if defined(XP_WIN32)
    msglen = FormatMessage(
	FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
	NULL, 
	GetLastError(), 
	LOCALE_SYSTEM_DEFAULT, 
	(LPTSTR)&sysmsg, 
	0, 
	0);
    if (msglen > 0)
	lmsg = sysmsg;
    SetLastError(0);
#else
    lmsg = strerror(errno);
    errno = 0;
#endif

    if (!lmsg)
	static_error[0] = 0;
    else
    {
	/* At this point lmsg points to something. */
	int min = 0;
	msglen = strlen(lmsg);

	min = msglen > BUFSIZ ? BUFSIZ : msglen;
	strncpy(static_error, lmsg, min-1);
	static_error[min-1] = 0;
    }

#ifdef XP_WIN32
    /* NT's FormatMessage() dynamically allocated the msg; free it */
    if (sysmsg)
        LocalFree(sysmsg);
#endif

    return static_error;
}

#ifndef MAXPATHLEN
#define MAXPATHLEN  1024
#endif

enum {
	DB_DIRECTORY = 0,
	DB_LOGDIRECTORY,
	DB_CHANGELOGDIRECTORY,
	DB_HOME_DIRECTORY
};

static int
is_fullpath(char *path)
{
	int len;
	if (NULL == path || '\0' == *path)
		return 0;

	if (FILE_PATHSEP == *path) /* UNIX */
		return 1;

	len = strlen(path);
	if (len > 2)
	{
		if (':' == path[1] && ('/' == path[2] || '\\' == path[2])) /* Windows */
			return 1;
	}
	return 0;
}

static void
rm_db_dirs(char *fullpath, DS_RM_RF_ERR_FUNC ds_rm_rf_err_func, void *arg)
{
	FILE *fp = fopen(fullpath, "r");
	char buf[2][MAXPATHLEN];
	char *bufp, *nextbufp;
	char *retp;
	int readit = 0;

	if (fp == NULL)
	{
		ds_rm_rf_err_func(fullpath, "opening the config file", arg);
		return;
	}

	bufp = buf[0]; *bufp = '\0';
	nextbufp = buf[1]; *nextbufp = '\0';

	while (readit || (retp = fgets(bufp, MAXPATHLEN, fp)) != NULL)
	{
		int len = strlen(bufp);
		int type = -1;
		char *p, *q;

		if (strstr(bufp, "nsslapd-directory"))
			type = DB_DIRECTORY;
		else if (strstr(bufp, "nsslapd-db-home-directory"))
			type = DB_HOME_DIRECTORY;
		else if (strstr(bufp, "nsslapd-db-logdirectory"))
			type = DB_LOGDIRECTORY;
		else if (strstr(bufp, "nsslapd-changelogdir"))
			type = DB_CHANGELOGDIRECTORY;
		else
		{
			readit = 0;
			continue;
		}

		p = bufp + len;

		while ((retp = fgets(nextbufp, MAXPATHLEN, fp)) != NULL)
		{
			int thislen;
			if (*nextbufp == ' ')
			{
				thislen = strlen(nextbufp);
				len += thislen;
				if (len < MAXPATHLEN)
				{
					strncpy(p, nextbufp, thislen);
					p += thislen;
				}
				/* else too long as a path. ignore it */
			}
			else
				break;
		}
		if (retp == NULL)	/* done */
			break;

		p = strchr(bufp, ':');
		if (p == NULL)
		{
			char *tmpp = bufp;
			bufp = nextbufp;
			nextbufp = tmpp;
			readit = 1;
			continue;
		}

		while (*(++p) == ' ') ;

		q = p + strlen(p) - 1;
		while (*q == ' ' || *q == '\t' || *q == '\n')
			q--;
		*(q+1) = '\0';

		switch (type)
		{
		case DB_DIRECTORY:
		case DB_LOGDIRECTORY:
		case DB_CHANGELOGDIRECTORY:
			if (is_fullpath(p))
				internal_rm_rf(p, ds_rm_rf_err_func, NULL);
			break;
		case DB_HOME_DIRECTORY:
			internal_rm_rf(p, ds_rm_rf_err_func, NULL);
			break;
		}
	}

	fclose(fp);
}

static char *
get_dir_from_startslapd(char *loc, char *keyword)
{
	char *returnstr = NULL; 
	char *ptr = NULL;
	char *confdir = NULL;
if (ds_search_file(loc, keyword, &returnstr) > 0 && returnstr) {
		ptr = strchr(returnstr, '=');
		if (NULL != ptr) {
			confdir = strdup(++ptr);
		}
		free(returnstr);
	}
	return confdir;
}

static char *
get_dir_from_config(char *config_dir, char *config_attr)
{
    char *configfile = NULL;
	char *returnstr = NULL; 
	char *ptr = NULL;
	char *dir = NULL;
	configfile = PR_smprintf("%s%c%s", config_dir, FILE_PATHSEP, DS_CONFIG_FILE);
	if (configfile && ds_search_file(configfile, config_attr, &returnstr) > 0
		&& returnstr) {
		ptr = strchr(returnstr, ':');
		if (NULL != ptr) {
			while (' ' == *ptr || '\t' == *ptr) ptr++;
			dir = strdup(ptr);
		}
		free(returnstr);
		PR_smprintf_free(configfile);
	}
	return dir;
}

/* this function will recursively remove a directory hierarchy from the file
   system, like "rm -rf"
   In order to handle errors, the user supplies a callback function.  When an
   error occurs, the callback function is called with the file or directory name
   and the system errno.  The callback function should return TRUE if it wants
   to continue or FALSE if it wants the remove aborted.
   The error callback should use PR_GetError and/or PR_GetOSError to
   determine the cause of the failure
*/
/* you could locate db dirs non standard location
   we should remove them, as well.
*/
static int
internal_rm_rf(const char *path, DS_RM_RF_ERR_FUNC ds_rm_rf_err_func, void *arg)
{
	struct PRFileInfo prfi;
	int retval = 0;

	if (PR_GetFileInfo(path, &prfi) != PR_SUCCESS) {
		if (!ds_rm_rf_err_func(path, "reading directory", arg)) {
			return 1;
		}
	}

	if (prfi.type == PR_FILE_DIRECTORY)
	{
		PRDir *dir;
		PRDirEntry *dirent;

		if (!(dir = PR_OpenDir(path))) {
			if (!ds_rm_rf_err_func(path, "opening directory", arg)) {
				return 1;
			}
			return 0;
		}
			
		while ((dirent = PR_ReadDir(dir, PR_SKIP_BOTH))) {
			char *fullpath = PR_smprintf("%s%c%s", path, FILE_PATHSEP, dirent->name);
			if (PR_GetFileInfo(fullpath, &prfi) != PR_SUCCESS) {
				if (!ds_rm_rf_err_func(fullpath, "reading file", arg)) {
					PR_smprintf_free(fullpath);
					PR_CloseDir(dir);
					return 1;
				} /* else just continue */
			} else if (prfi.type == PR_FILE_DIRECTORY) {
				retval = internal_rm_rf(fullpath, ds_rm_rf_err_func, arg);
				if (retval) { /* non zero return means stop */
					PR_smprintf_free(fullpath);
					break;
				}
			} else {
				/* FHS changes the directory structure.
				 * Config dir is no longer in the instance dir.
				 * The info should be found in start-slapd,
				 * therefore get the path from the file here.
				 */
				if (0 == strcmp(dirent->name, "start-slapd")) {
				    char *config_dir = ds_get_config_dir();
					char *run_dir = ds_get_run_dir();
					if (NULL == config_dir || '\0' == *config_dir) {
						config_dir = get_dir_from_startslapd(fullpath, DS_CONFIG_DIR);
					}
					if (NULL == run_dir || '\0' == *run_dir) {
						char *ptr = NULL;
						run_dir = get_dir_from_startslapd(fullpath, PIDFILE);
						ptr = strrchr(run_dir, FILE_PATHSEP);
						if (NULL != ptr) {
							*ptr = '\0';	/* equiv to dirname */
						}
					}
					if (NULL != run_dir) {
						internal_rm_rf(run_dir, ds_rm_rf_err_func, NULL);
						free(run_dir);
					}
					if (NULL != config_dir) {
						char *lock_dir = get_dir_from_config(config_dir, DS_CONFIG_LOCKDIR);
						char *err_log = get_dir_from_config(config_dir, DS_CONFIG_ERRLOG);

						if (NULL != lock_dir) {
							internal_rm_rf(lock_dir, ds_rm_rf_err_func, NULL);
							free(lock_dir);
						}
						if (NULL != err_log) {
							char *ptr = strrchr(err_log, FILE_PATHSEP);
							if (NULL != ptr) {
								*ptr = '\0'; /* equiv to 'dirname' */
								internal_rm_rf(err_log, ds_rm_rf_err_func, NULL);
							}
							free(err_log);
						}
						/* removing db dirs */
						rm_db_dirs(config_dir, ds_rm_rf_err_func, arg);

						/* removing config dir */
						internal_rm_rf(config_dir, ds_rm_rf_err_func, NULL);
					}
				}
				/* 
				 * When the file is the config file, 
				 * check if db dir is in the instance dir or not.
				 * If db dir exists in the instance dir, it's an old structure.
				 * Let's clean the old db here, as well.
				 */
				if (0 == strcmp(dirent->name, DS_CONFIG_FILE)) {
					rm_db_dirs(fullpath, ds_rm_rf_err_func, arg);
				}

				if (PR_Delete(fullpath) != PR_SUCCESS) {
					if (!ds_rm_rf_err_func(fullpath, "deleting file", arg)) {
						PR_smprintf_free(fullpath);
						PR_CloseDir(dir);
						return 1;
					}
				}
			}
			PR_smprintf_free(fullpath);
		}
		PR_CloseDir(dir);
		if (PR_RmDir(path) != PR_SUCCESS) {
			if (!ds_rm_rf_err_func(path, "removing directory", arg)) {
				retval = 1;
			}
		}
	}

	return retval;
}

static int
default_err_func(const char *path, const char *op, void *arg)
{
	PRInt32 errcode = PR_GetError();
	char *msg;
	const char *errtext;

	if (!errcode || (errcode == PR_UNKNOWN_ERROR)) {
		errcode = PR_GetOSError();
		errtext = ds_system_errmsg();
	} else {
		errtext = PR_ErrorToString(errcode, PR_LANGUAGE_I_DEFAULT);
	}

	msg = PR_smprintf("%s %s: error code %d (%s)", op, path, errcode, errtext);
	ds_send_error(msg, 0);
	PR_smprintf_free(msg);
	return 1; /* just continue */	
}

/* dir: instance dir, e.g.,  "$NETSITE_ROOT/slapd-<id>" */
DS_EXPORT_SYMBOL int
ds_rm_rf(const char *dir, DS_RM_RF_ERR_FUNC ds_rm_rf_err_func, void *arg)
{
	struct PRFileInfo prfi;

	if (!dir) {
		ds_send_error("Could not remove NULL directory name", 1);
		return 1;
	}

	if (!ds_rm_rf_err_func) {
		ds_rm_rf_err_func = default_err_func;
	}

	if (PR_GetFileInfo(dir, &prfi) != PR_SUCCESS) {
		if (ds_rm_rf_err_func(dir, "reading directory", arg)) {
			return 0;
		} else {
			return 1;
		}
	}
	if (prfi.type != PR_FILE_DIRECTORY) {
		char *msg = PR_smprintf("Cannot remove directory %s because it is not a directory", dir);
		ds_send_error(msg, 0);
		PR_smprintf_free(msg);
		return 1;
	}

	return internal_rm_rf(dir, ds_rm_rf_err_func, arg);
}

DS_EXPORT_SYMBOL int
ds_remove_reg_key(void *base, const char *format, ...)
{
	int rc = 0;
#ifdef XP_WIN32
	int retries = 3;
	HKEY hkey = (HKEY)base;
	char *key;
	va_list ap;

	va_start(ap, format);
	key = PR_vsmprintf(format, ap);
	va_end(ap);

	do {
		if (ERROR_SUCCESS != RegDeleteKey(hkey, key)) {
			rc = GetLastError();
			if (rc == ERROR_BADKEY || rc == ERROR_CANTOPEN ||
				rc == ERROR_CANTREAD ||
				rc == ERROR_CANTWRITE || rc == ERROR_KEY_DELETED ||
				rc == ERROR_ALREADY_EXISTS || rc == ERROR_NO_MORE_FILES) {
				rc = 0; /* key already deleted - no error */
			} else if ((retries > 1) && (rc == ERROR_IO_PENDING)) {
				/* the key is busy - lets wait and try again */
				PR_Sleep(PR_SecondsToInterval(3));
				retries--;
			} else {
				char *errmsg = PR_smprintf("Could not remove registry key %s - error %d (%s)",
									   	key, rc, ds_system_errmsg());
				ds_send_error(errmsg, 0);
				PR_smprintf_free(errmsg);
				break; /* no retry, just fail */
			}
		}
	} while (rc && retries);
	PR_smprintf_free(key);
#endif
	return rc;
}




More information about the Fedora-directory-commits mailing list