/** 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 #endif #include #include #include #include #include #include #include #define BIG_LINE 1024 static const char *DB_DIRECTIVE = "directory"; static const int DB_DIRECTIVE_LEN = 9; /* strlen("DB_DIRECTIVE") */ static const char *ENCODED = "encoded"; static void insert_dbinfo_propval(DBConfDBInfo_t *db_info, DBPropVal_t *propval) { if (db_info->lastprop) { db_info->lastprop->next = propval; } else { db_info->firstprop = propval; } db_info->lastprop = propval; } static void insert_dbconf_dbinfo(DBConfInfo_t *conf_info, DBConfDBInfo_t *db_info) { if (conf_info->lastdb) { conf_info->lastdb->next = db_info; } else { conf_info->firstdb = db_info; } conf_info->lastdb = db_info; } void dbconf_free_propval (DBPropVal_t *propval) { if (propval) { if (propval->prop) free(propval->prop); if (propval->val) free(propval->val); memset((void *)propval, 0, sizeof(DBPropVal_t)); free(propval); } } NSAPI_PUBLIC void dbconf_free_dbinfo (DBConfDBInfo_t *db_info) { if (db_info) { DBPropVal_t *next; DBPropVal_t *cur; if (db_info->dbname) free(db_info->dbname); if (db_info->url) free(db_info->url); cur = db_info->firstprop; while(cur) { next = cur->next; dbconf_free_propval(cur); cur = next; } memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); free(db_info); } } NSAPI_PUBLIC void dbconf_free_confinfo (DBConfInfo_t *conf_info) { DBConfDBInfo_t *next; DBConfDBInfo_t *cur; if (conf_info) { cur = conf_info->firstdb; while (cur) { next = cur->next; dbconf_free_dbinfo(cur); cur = next; } memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); free(conf_info); } } static int skip_blank_lines_and_spaces(FILE *fp, char *buf, char **ptr_out, int *eof) { char *ptr = buf; char *end; while(buf && (*buf || fgets(buf, BIG_LINE, fp))) { ptr = buf; /* skip leading whitespace */ while(*ptr && isspace(*ptr)) ++ptr; /* skip blank line or comment */ if (!*ptr || *ptr == '#') { *buf = 0; /* to force reading of next line */ continue; } /* Non-blank line found */ break; } *ptr_out = ptr; if (!*ptr) { *eof = 1; } else { /* skip trailing whitespace */ end = ptr + strlen(ptr) - 1; while(isspace(*end)) *end-- = 0; } return LDAPU_SUCCESS; } static int dbconf_parse_propval (char *buf, char *ptr, DBConfDBInfo_t *db_info) { char *dbname = db_info->dbname; int dbname_len = strlen(dbname); char *prop; char *val; DBPropVal_t *propval; char *delimeter_chars = " \t"; char *lastchar; int end_of_prop; char *encval = 0; /* encoded value */ char *origprop = 0; if ((ptr - buf + dbname_len > BIG_LINE) || strncmp(ptr, dbname, dbname_len) || !(ptr[dbname_len] == ':' || isspace(ptr[dbname_len]))) { /* Not a prop-val for the current db but not an error */ return LDAPU_ERR_NOT_PROPVAL; } /* remove the last char if it is newline */ lastchar = strrchr(buf, '\n'); if (lastchar) *lastchar = '\0'; prop = ptr + dbname_len + 1; while(*prop && (isspace(*prop) || *prop == ':')) ++prop; if (!*prop) { return LDAPU_ERR_PROP_IS_MISSING; } end_of_prop = strcspn(prop, delimeter_chars); if (prop[end_of_prop] != '\0') { /* buf doesn't end here -- val is present */ prop[end_of_prop] = '\0'; val = &prop[end_of_prop + 1]; while(*val && isspace(*val)) ++val; if (*val == '\0') val = 0; } else { val = 0; } /* * The prop-val line could be one of the following: * ":prop val" OR ":encoded prop encval" * If (prop == "encoded") then the val has "prop encval". * Get the actual prop from val and get encval (i.e. encoded value) * and decode it. If it is encoded then the val part must be non-NULL. */ if (val && *val && !strcmp(prop, ENCODED)) { /* val has the actual prop followed by the encoded value */ origprop = prop; prop = val; while(*prop && (isspace(*prop) || *prop == ':')) ++prop; if (!*prop) { return LDAPU_ERR_PROP_IS_MISSING; } end_of_prop = strcspn(prop, delimeter_chars); if (prop[end_of_prop] != '\0') { /* buf doesn't end here -- encval is present */ prop[end_of_prop] = '\0'; encval = &prop[end_of_prop + 1]; while(*encval && isspace(*encval)) ++encval; if (*encval == '\0') encval = 0; } else { encval = 0; } if (!encval) { /* special case - if encval is null, "encoded" itself is a * property and what we have in prop now is the value. */ val = prop; prop = origprop; } else { /* decode the value */ val = dbconf_decodeval(encval); } } /* Success - we have prop & val */ propval = (DBPropVal_t *)malloc(sizeof(DBPropVal_t)); if (!propval) return LDAPU_ERR_OUT_OF_MEMORY; memset((void *)propval, 0, sizeof(DBPropVal_t)); propval->prop = strdup(prop); propval->val = val ? strdup(val) : 0; if (!propval->prop || (val && !propval->val)) { dbconf_free_propval(propval); return LDAPU_ERR_OUT_OF_MEMORY; } if (encval) free(val); /* val was allocated by dbconf_decodeval */ insert_dbinfo_propval(db_info, propval); return LDAPU_SUCCESS; } static int dbconf_read_propval (FILE *fp, char *buf, DBConfDBInfo_t *db_info, int *eof) { int rv; char *ptr = buf; while(buf && (*buf || fgets(buf, BIG_LINE, fp))) { ptr = buf; rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); if (rv != LDAPU_SUCCESS || *eof) return rv; /* We have a non-blank line which could be prop-val pair for the * dbname in the db_info. parse the prop-val pair and continue. */ rv = dbconf_parse_propval(buf, ptr, db_info); if (rv == LDAPU_ERR_NOT_PROPVAL) return LDAPU_SUCCESS; if (rv != LDAPU_SUCCESS) return rv; *buf = 0; /* to force reading of next line */ } if (!*buf) *eof = 1; return LDAPU_SUCCESS; } static int parse_directive(char *buf, const char *directive, const int directive_len, DBConfDBInfo_t **db_info_out) { DBConfDBInfo_t *db_info; char *dbname; char *url; int end_of_dbname; char *delimeter_chars = " \t"; char *lastchar; /* remove the last char if it is newline */ lastchar = strrchr(buf, '\n'); if (lastchar) *lastchar = '\0'; if (strncmp(buf, directive, directive_len) || !isspace(buf[directive_len])) { return LDAPU_ERR_DIRECTIVE_IS_MISSING; } dbname = buf + directive_len + 1; while(*dbname && isspace(*dbname)) ++dbname; if (!*dbname) { return LDAPU_ERR_DBNAME_IS_MISSING; } end_of_dbname = strcspn(dbname, delimeter_chars); if (dbname[end_of_dbname] != '\0') { /* buf doesn't end here -- url is present */ dbname[end_of_dbname] = '\0'; url = &dbname[end_of_dbname + 1]; while(*url && isspace(*url)) ++url; if (*url == '\0') url = 0; } else { url = 0; } /* Success - we have dbname & url */ db_info = (DBConfDBInfo_t *)malloc(sizeof(DBConfDBInfo_t)); if (!db_info) return LDAPU_ERR_OUT_OF_MEMORY; memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); db_info->dbname = strdup(dbname); db_info->url = url ? strdup(url) : 0; if (!db_info->dbname || (url && !db_info->url)) { dbconf_free_dbinfo(db_info); return LDAPU_ERR_OUT_OF_MEMORY; } *db_info_out = db_info; return LDAPU_SUCCESS; } /* Read the next database info from the file and put it in db_info_out. The * buf may contain first line of the database info. When this function * finishes, the buf may contain unprocessed information (which should be * passed to the next call to read_db_info). */ static int read_db_info (FILE *fp, char *buf, DBConfDBInfo_t **db_info_out, const char *directive, const int directive_len, int *eof) { char *ptr; DBConfDBInfo_t *db_info; int rv; *db_info_out = 0; rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); if (rv != LDAPU_SUCCESS || *eof) return rv; /* We possibly have a directive of the form "directory " */ rv = parse_directive(ptr, directive, directive_len, &db_info); if (rv != LDAPU_SUCCESS) return rv; /* We have parsed the directive successfully -- lets look for additional * property-value pairs for the database. */ if (!fgets(buf, BIG_LINE, fp)) { *eof = 1; rv = LDAPU_SUCCESS; } else { rv = dbconf_read_propval(fp, buf, db_info, eof); } if (rv != LDAPU_SUCCESS) { dbconf_free_dbinfo(db_info); *db_info_out = 0; } else { *db_info_out = db_info; } return rv; } int dbconf_read_config_file_sub (const char *file, const char *directive, const int directive_len, DBConfInfo_t **conf_info_out) { FILE *fp; DBConfInfo_t *conf_info; DBConfDBInfo_t *db_info; char buf[BIG_LINE]; int rv; int eof; buf[0] = 0; #ifdef XP_WIN32 if ((fp = fopen(file, "rt")) == NULL) #else if ((fp = fopen(file, "r")) == NULL) #endif { return LDAPU_ERR_CANNOT_OPEN_FILE; } /* Allocate DBConfInfo_t */ conf_info = (DBConfInfo_t *)malloc(sizeof(DBConfInfo_t)); if (!conf_info) { fclose(fp); return LDAPU_ERR_OUT_OF_MEMORY; } memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); /* Read each db info */ eof = 0; while(!eof && ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) { insert_dbconf_dbinfo(conf_info, db_info); } if (rv != LDAPU_SUCCESS) { dbconf_free_confinfo(conf_info); *conf_info_out = 0; } else { *conf_info_out = conf_info; } fclose(fp); return rv; } NSAPI_PUBLIC int dbconf_read_config_file (const char *file, DBConfInfo_t **conf_info_out) { return dbconf_read_config_file_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, conf_info_out); } int dbconf_read_default_dbinfo_sub (const char *file, const char *directive, const int directive_len, DBConfDBInfo_t **db_info_out) { FILE *fp; DBConfDBInfo_t *db_info; char buf[BIG_LINE]; int rv; int eof; buf[0] = 0; #ifdef XP_WIN32 if ((fp = fopen(file, "rt")) == NULL) #else if ((fp = fopen(file, "r")) == NULL) #endif { return LDAPU_ERR_CANNOT_OPEN_FILE; } /* Read each db info until eof or dbname == default*/ eof = 0; while(!eof && ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) { if (!strcmp(db_info->dbname, DBCONF_DEFAULT_DBNAME)) break; dbconf_free_dbinfo(db_info); db_info = NULL; } if (rv != LDAPU_SUCCESS) { *db_info_out = 0; } else { *db_info_out = db_info; } fclose(fp); return rv; } NSAPI_PUBLIC int dbconf_read_default_dbinfo (const char *file, DBConfDBInfo_t **db_info_out) { return dbconf_read_default_dbinfo_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, db_info_out); } /* * ldapu_strcasecmp - is like strcasecmp on UNIX but also accepts null strings. */ int ldapu_strcasecmp (const char *s1, const char *s2) { #ifdef XP_WIN32 int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */ #endif if (!s1) return !s2 ? 0 : 0-tolower(*s2); else if (!s2) return tolower(*s1); #ifdef XP_WIN32 while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; } if (!*s1) return *s2 ? 0-tolower(*s2) : 0; else if (!*s2) return tolower(*s1); else return ls1 - ls2; #else return strcasecmp(s1, s2); #endif } NSAPI_PUBLIC int ldapu_dbinfo_attrval (DBConfDBInfo_t *db_info, const char *attr, char **val) { /* Look for given attr in the db_info and return its value */ int rv = LDAPU_ATTR_NOT_FOUND; DBPropVal_t *next; *val = 0; if (db_info) { next = db_info->firstprop; while (next) { rv = ldapu_strcasecmp(attr, next->prop); if (!rv) { /* Found the property */ *val = next->val ? strdup(next->val) : 0; if (next->val && !*val) { rv = LDAPU_ERR_OUT_OF_MEMORY; } else { rv = LDAPU_SUCCESS; } break; } next = next->next; } } return rv; } void dbconf_print_propval (DBPropVal_t *propval) { if (propval) { fprintf(stderr, "\tprop: \"%s\"\tval: \"%s\"\n", propval->prop, propval->val ? propval->val : ""); } else { fprintf(stderr, "Null propval\n"); } } void dbconf_print_dbinfo (DBConfDBInfo_t *db_info) { DBPropVal_t *next; if (db_info) { fprintf(stderr, "dbname: \"%s\"\n", db_info->dbname); fprintf(stderr, "url: \t\"%s\"\n", db_info->url ? db_info->url : ""); next = db_info->firstprop; while (next) { dbconf_print_propval(next); next = next->next; } } else { fprintf(stderr, "Null db_info\n"); } } void dbconf_print_confinfo (DBConfInfo_t *conf_info) { DBConfDBInfo_t *next; if (conf_info) { next = conf_info->firstdb; while (next) { dbconf_print_dbinfo(next); next = next->next; } } else { fprintf(stderr, "Null conf_info\n"); } } NSAPI_PUBLIC int dbconf_output_db_directive (FILE *fp, const char *dbname, const char *url) { fprintf(fp, "%s %s %s\n", DB_DIRECTIVE, dbname, url); return LDAPU_SUCCESS; } NSAPI_PUBLIC int dbconf_output_propval (FILE *fp, const char *dbname, const char *prop, const char *val, const int encoded) { if (encoded && val && *val) { char *new_val = dbconf_encodeval(val); if (!new_val) return LDAPU_ERR_OUT_OF_MEMORY; fprintf(fp, "%s:%s %s %s\n", dbname, ENCODED, prop, new_val); free(new_val); } else { fprintf(fp, "%s:%s %s\n", dbname, prop, val ? val : ""); } return LDAPU_SUCCESS; } NSAPI_PUBLIC int dbconf_get_dbnames (const char *dbmap, char ***dbnames_out, int *cnt_out) { DBConfInfo_t *conf_info = 0; DBConfDBInfo_t *db = 0; int cnt = 0; char **dbnames = 0; char *heap = 0; int rv; *dbnames_out = 0; *cnt_out = 0; rv = dbconf_read_config_file(dbmap, &conf_info); if (rv != LDAPU_SUCCESS) return rv; db = conf_info->firstdb; dbnames = (char **)malloc(32*1024); heap = (char *)dbnames + 2*1024; if (!dbnames) { dbconf_free_confinfo(conf_info); return LDAPU_ERR_OUT_OF_MEMORY; } *dbnames_out = dbnames; while(db) { *dbnames++ = heap; strcpy(heap, db->dbname); heap += strlen(db->dbname)+1; db = db->next; cnt++; } *dbnames = NULL; *cnt_out = cnt; dbconf_free_confinfo(conf_info); return LDAPU_SUCCESS; } NSAPI_PUBLIC int dbconf_free_dbnames (char **dbnames) { if (dbnames) free(dbnames); return LDAPU_SUCCESS; }