diff options
Diffstat (limited to 'ldap/admin/lib/dsalib_util.c')
-rw-r--r-- | ldap/admin/lib/dsalib_util.c | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/ldap/admin/lib/dsalib_util.c b/ldap/admin/lib/dsalib_util.c new file mode 100644 index 00000000..a6cf4bee --- /dev/null +++ b/ldap/admin/lib/dsalib_util.c @@ -0,0 +1,1123 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#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> + +#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) { + sprintf(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; + + strcpy(szWildcardFileSpec, dir); + strcat(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) { + sprintf(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))) { + sprintf(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) { + sprintf(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) { + sprintf(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) { + sprintf(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 +} + +/* Returns a directory path used for tmp files. */ +DS_EXPORT_SYMBOL char * +ds_get_tmp_dir() +{ + static char tmpdir[] = "/tmp"; + static char tmp[256] = {0}; + unsigned ilen; + char pch; + char* instanceDir = ds_get_install_root(); + + if(instanceDir == NULL) + { + #if defined( XP_WIN32 ) + ilen = strlen(tmp); + GetTempPath( ilen +1, tmp ); + /* Remove trailing slash. */ + pch = tmp[ilen-1]; + if( pch == '\\' || pch == '/' ) + tmp[ilen-1] = '\0'; + return tmp; + #else + return( tmpdir ); + #endif + } + + sprintf(tmp,"%s/tmp",instanceDir); + +#if defined( XP_WIN32 ) + for(ilen=0;ilen < strlen(tmp); ilen++) + { + if(tmp[ilen]=='/') + tmp[ilen]='\\'; + } +#endif + + if(!ds_file_exists(tmp)) + ds_mkdir_p(tmp,00770); + + return ( tmp ); +} + +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" */ +DS_EXPORT_SYMBOL int +ds_search_file(char *filename, char *searchstring) +{ + 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 ) { + 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]; + + sprintf(temp_startup_line, "/bin/sh -c \"%s\"", startup_line); + strcpy(startup_line, temp_startup_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) + { + strcat(error, msg); + strcat(error, SEPARATOR); + } + if (details) + strcat(error, 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); + 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; +} + +/* get db path dir info from dse.ldif and remove it if it's not under path +*/ +#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 rmbuf[MAXPATHLEN]; + char *bufp, *nextbufp; + char *retp; + int readit = 0; + + if (fp == NULL) + { + ds_rm_rf_err_func(fullpath, "opening dse.ldif", 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); +} + +/* 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; + char *fullpath = NULL; + 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 { + /* if dse.ldif, check db dir is under the instance dir or not */ + if (0 == strcmp(dirent->name, "dse.ldif")) + 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 */ +} + +DS_EXPORT_SYMBOL int +ds_rm_rf(const char *dir, DS_RM_RF_ERR_FUNC ds_rm_rf_err_func, void *arg) +{ + int retval = 0; + 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; +} |