Skip to content
Snippets Groups Projects
res_config_ldap.c 54.8 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Asterisk -- An open source telephony toolkit.
    
     *
     * Copyright (C) 2005, Oxymium sarl
     * Manuel Guesdon <mguesdon@oxymium.net> - LDAP RealTime Driver Author/Adaptor
     *
     * Copyright (C) 2007, Digium, Inc.
     * Russell Bryant <russell@digium.com>
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     *
     */
    
    /*! \file
     *
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \brief LDAP plugin for portable configuration engine (ARA)
    
     *
     * \author Mark Spencer <markster@digium.com>
     * \author Manuel Guesdon
     * \author Carl-Einar Thorner <cthorner@voicerd.com>
     * \author Russell Bryant <russell@digium.com>
     *
    
     * OpenLDAP http://www.openldap.org
    
    /*! \li \ref res_config_ldap.c uses the configuration file \ref res_ldap.conf
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \addtogroup configuration_file Configuration Files
     */
    
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \page res_ldap.conf res_ldap.conf
     * \verbinclude res_ldap.conf.sample
     */
    
    
    /*** MODULEINFO
    	<depend>ldap</depend>
    
    	<support_level>extended</support_level>
    
     ***/
    
    #include "asterisk.h"
    
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <ldap.h>
    
    #include "asterisk/channel.h"
    #include "asterisk/logger.h"
    #include "asterisk/config.h"
    #include "asterisk/module.h"
    #include "asterisk/lock.h"
    #include "asterisk/options.h"
    #include "asterisk/cli.h"
    #include "asterisk/utils.h"
    #include "asterisk/strings.h"
    #include "asterisk/pbx.h"
    #include "asterisk/linkedlists.h"
    
    #define RES_CONFIG_LDAP_CONF "res_ldap.conf"
    
    #define RES_CONFIG_LDAP_DEFAULT_BASEDN "asterisk"
    
    
    AST_MUTEX_DEFINE_STATIC(ldap_lock);
    
    static LDAP *ldapConn;
    
    static char user[512];
    
    static char pass[512];
    
    static char base_distinguished_name[512];
    
    static int version;
    
    static time_t connect_time;
    
    static int parse_config(void);
    static int ldap_reconnect(void);
    static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    struct category_and_metric {
    	const char *name;
    	int metric;
    	const char *variable_name;
    	const char *variable_value;
    
    	int var_metric; /*!< For organizing variables (particularly includes and switch statements) within a context */
    
    /*! \brief Table configuration
    
    Andrew Latham's avatar
    Andrew Latham committed
     */
    
    struct ldap_table_config {
    
    	char *table_name;		 /*!< table name */
    	char *additional_filter;	  /*!< additional filter	*/
    
    	struct ast_variable *attributes;  /*!< attribute names conversion */
    	struct ast_variable *delimiters;  /*!< the current delimiter is semicolon, so we are not using this variable */
    	AST_LIST_ENTRY(ldap_table_config) entry;
    
    Alexander Traud's avatar
    Alexander Traud committed
    	/*! \todo: Make proxies work */
    
    /*! \brief Should be locked before using it
    
    Andrew Latham's avatar
    Andrew Latham committed
     */
    
    static AST_LIST_HEAD_NOLOCK_STATIC(table_configs, ldap_table_config);
    static struct ldap_table_config *base_table_config;
    static struct ldap_table_config *static_table_config;
    
    static struct ast_cli_entry ldap_cli[] = {
    	AST_CLI_DEFINE(realtime_ldap_status, "Shows connection information for the LDAP RealTime driver"),
    };
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Create a new table_config
     */
    
    static struct ldap_table_config *table_config_new(const char *table_name)
    {
    	struct ldap_table_config *p;
    
    	if (!(p = ast_calloc(1, sizeof(*p))))
    		return NULL;
    
    	if (table_name) {
    		if (!(p->table_name = ast_strdup(table_name))) {
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Find a table_config
     *
    
     * Should be locked before using it
    
    Andrew Latham's avatar
    Andrew Latham committed
     *
    
    Andrew Latham's avatar
    Andrew Latham committed
     *  \note This function assumes ldap_lock to be locked.
     */
    
    static struct ldap_table_config *table_config_for_table_name(const char *table_name)
    {
    	struct ldap_table_config *c = NULL;
    
    	AST_LIST_TRAVERSE(&table_configs, c, entry) {
    		if (!strcmp(c->table_name, table_name))
    			break;
    	}
    
    	return c;
    }
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Find variable by name
     */
    
    static struct ast_variable *variable_named(struct ast_variable *var, const char *name)
    {
    	for (; var; var = var->next) {
    		if (!strcasecmp(name, var->name))
    			break;
    	}
    
    	return var;
    }
    
    
    Alexander Traud's avatar
    Alexander Traud committed
    /*!
     * \brief Count semicolons in string
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \param somestr - pointer to a string
     *
     * \return number of occurances of the delimiter(semicolon)
    
     */
    static int semicolon_count_str(const char *somestr)
    {
    	int count = 0;
    
    	for (; *somestr; somestr++) {
    		if (*somestr == ';')
    			count++;
    	}
    
    	return count;
    
    Alexander Traud's avatar
    Alexander Traud committed
    /*!
     * \brief Count semicolons in variables
    
    Andrew Latham's avatar
    Andrew Latham committed
     * takes a linked list of \a ast_variable variables, finds the one with the name variable_value
    
     * and returns the number of semicolons in the value for that \a ast_variable
     */
    static int semicolon_count_var(struct ast_variable *var)
    {
    	struct ast_variable *var_value = variable_named(var, "variable_value");
    
    
    	ast_debug(2, "semicolon_count_var: %s\n", var_value->value);
    
    
    	return semicolon_count_str(var_value->value);
    }
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief add attribute to table config
     *
     * Should be locked before using it
     */
    
    static void ldap_table_config_add_attribute(struct ldap_table_config *table_config,
    	const char *attribute_name, const char *attribute_value)
    {
    	struct ast_variable *var;
    
    
    	if (ast_strlen_zero(attribute_name) || ast_strlen_zero(attribute_value)) {
    
    		return;
    
    	if (!(var = ast_variable_new(attribute_name, attribute_value, table_config->table_name))) {
    
    		return;
    
    		var->next = table_config->attributes;
    
    	table_config->attributes = var;
    }
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Free table_config
     *
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \note assumes ldap_lock to be locked
     */
    
    static void table_configs_free(void)
    {
    	struct ldap_table_config *c;
    
    	while ((c = AST_LIST_REMOVE_HEAD(&table_configs, entry))) {
    
    		if (c->table_name) {
    			ast_free(c->table_name);
    		}
    		if (c->additional_filter) {
    			ast_free(c->additional_filter);
    		}
    		if (c->attributes) {
    
    			ast_variables_destroy(c->attributes);
    
    	}
    
    	base_table_config = NULL;
    	static_table_config = NULL;
    }
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Convert variable name to ldap attribute name
     *
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \note Should be locked before using it
    
    Andrew Latham's avatar
    Andrew Latham committed
     */
    
    static const char *convert_attribute_name_to_ldap(struct ldap_table_config *table_config,
    	const char *attribute_name)
    {
    	int i = 0;
    	struct ldap_table_config *configs[] = { table_config, base_table_config };
    
    	for (i = 0; i < ARRAY_LEN(configs); i++) {
    		struct ast_variable *attribute;
    
    
    
    		attribute = configs[i]->attributes;
    		for (; attribute; attribute = attribute->next) {
    
    			if (!strcasecmp(attribute_name, attribute->name)) {
    
    				return attribute->value;
    
    /*! \brief Convert ldap attribute name to variable name
    
    Andrew Latham's avatar
    Andrew Latham committed
     *
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \note Should be locked before using it
     */
    
    static const char *convert_attribute_name_from_ldap(struct ldap_table_config *table_config,
    						    const char *attribute_name)
    {
    	int i = 0;
    	struct ldap_table_config *configs[] = { table_config, base_table_config };
    
    	for (i = 0; i < ARRAY_LEN(configs); i++) {
    		struct ast_variable *attribute;
    
    
    		attribute = configs[i]->attributes;
    		for (; attribute; attribute = attribute->next) {
    
    			if (strcasecmp(attribute_name, attribute->value) == 0) {
    
    				return attribute->name;
    
    /*! \brief Get variables from ldap entry attributes
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \note Should be locked before using it
     * \return a linked list of ast_variable variables.
    
    static struct ast_variable *realtime_ldap_entry_to_var(struct ldap_table_config *table_config,
    	LDAPMessage *ldap_entry)
    {
    	BerElement *ber = NULL;
    	struct ast_variable *var = NULL;
    	struct ast_variable *prev = NULL;
    
    	int is_delimited = 0;
    	int i = 0;
    
    	char *ldap_attribute_name;
    	struct berval *value;
    	int pos = 0;
    
    	ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
    
    	while (ldap_attribute_name) {
    		struct berval **values = NULL;
    
    		const char *attribute_name = convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
    
    		int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
    
    
    		values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name); /* these are freed at the end */
    
    				ast_debug(2, "attribute_name: %s LDAP value: %s\n", attribute_name, valptr);
    
    				if (is_realmed_password_attribute) {
    
    					if (!strncasecmp(valptr, "{md5}", 5)) {
    						valptr += 5;
    					}
    					ast_debug(2, "md5: %s\n", valptr);
    
    					/* ok, so looping through all delimited values except the last one (not, last character is not delimited...) */
    					if (is_delimited) {
    						i = 0;
    						pos = 0;
    
    						while (!ast_strlen_zero(valptr + i)) {
    
    									prev->next = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
    
    									if (prev->next) {
    										prev = prev->next;
    									}
    								} else {
    
    									prev = var = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
    
    					/* for the last delimited value or if the value is not delimited: */
    					if (prev) {
    
    						prev->next = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
    
    						if (prev->next) {
    							prev = prev->next;
    						}
    					} else {
    
    						prev = var = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
    
    		ldap_memfree(ldap_attribute_name);
    
    		ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
    	}
    	ber_free(ber, 0);
    
    	return var;
    }
    
    /*! \brief Get variables from ldap entry attributes - Should be locked before using it
     *
     * The results are freed outside this function so is the \a vars array.
    
     * \return \a vars - an array of ast_variable variables terminated with a null.
    
    Andrew Latham's avatar
    Andrew Latham committed
     */
    
    static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_config *table_config,
    
    	LDAPMessage *ldap_result_msg, unsigned int *entries_count_ptr)
    
    	struct ast_variable **vars;
    	int i = 0;
    	int tot_count = 0;
    	int entry_index = 0;
    
    	LDAPMessage *ldap_entry = NULL;
    	BerElement *ber = NULL;
    	struct ast_variable *var = NULL;
    	struct ast_variable *prev = NULL;
    
    	int is_delimited = 0;
    	char *delim_value = NULL;
    
    	int delim_tot_count = 0;
    	int delim_count = 0;
    
    
    Alexander Traud's avatar
    Alexander Traud committed
    	/*! \brief First find the total count
    
    Andrew Latham's avatar
    Andrew Latham committed
    	 */
    
    	ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
    
    	for (tot_count = 0; ldap_entry; tot_count++) {
    
    		struct ast_variable *tmp = realtime_ldap_entry_to_var(table_config, ldap_entry);
    		tot_count += semicolon_count_var(tmp);
    
    		ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
    
    		ast_variables_destroy(tmp);
    
    		*entries_count_ptr = tot_count;
    
    Andrew Latham's avatar
    Andrew Latham committed
    	/*! \note Now that we have the total count we allocate space and create the variables
    
    	 * Remember that each element in vars is a linked list that points to realtime variable.
    	 * If the we are dealing with a static realtime variable we create a new element in the \a vars array for each delimited
    	 * value in \a variable_value; otherwise, we keep \a vars static and increase the length of the linked list of variables in the array element.
    
    Andrew Latham's avatar
    Andrew Latham committed
    	 * This memory must be freed outside of this function.
    	 */
    
    	vars = ast_calloc(tot_count + 1, sizeof(struct ast_variable *));
    
    	ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
    
    Alexander Traud's avatar
    Alexander Traud committed
    	/*! \brief For each static realtime variable we may create several entries in the \a vars array if it's delimited
    
    Andrew Latham's avatar
    Andrew Latham committed
    	 */
    
    	for (entry_index = 0; ldap_entry; ) {
    
    		int pos = 0;
    		delim_value = NULL;
    		delim_tot_count = 0;
    		delim_count = 0;
    
    		do { /* while delim_count */
    
    			/* Starting new static var */
    
    			char *ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
    			struct berval *value;
    			while (ldap_attribute_name) {
    
    				const char *attribute_name = convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
    
    				int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
    				struct berval **values = NULL;
    
    				values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name);
    				if (values) {
    
    						if (is_realmed_password_attribute) {
    
    							if (strncasecmp(valptr, "{md5}", 5) == 0) {
    								valptr += 5;
    							}
    							ast_debug(2, "md5: %s\n", valptr);
    
    							if (delim_value == NULL && !is_realmed_password_attribute
    
    								&& (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0)) {
    
    
    								if ((delim_tot_count = semicolon_count_str(delim_value)) > 0) {
    
    									ast_debug(4, "is delimited %d times: %s\n", delim_tot_count, delim_value);
    
    							if (is_delimited != 0 && !is_realmed_password_attribute
    
    								&& (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0) ) {
    
    								/* for non-Static RealTime, first */
    
    								for (i = pos; !ast_strlen_zero(valptr + i); i++) {
    
    									ast_debug(4, "DELIM pos: %d i: %d\n", pos, i);
    
    									if (delim_value[i] == ';') {
    
    										delim_value[i] = '\0';
    
    
    										ast_debug(2, "DELIM - attribute_name: %s value: %s pos: %d\n", attribute_name, &delim_value[pos], pos);
    
    										if (prev) {
    											prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
    											if (prev->next) {
    												prev = prev->next;
    											}
    										} else {
    											prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
    										}
    										pos = i + 1;
    
    
    										if (static_table_config == table_config) {
    
    									ast_debug(4, "DELIM pos: %d i: %d delim_count: %d\n", pos, i, delim_count);
    
    									/* Last delimited value */
    
    									ast_debug(4, "DELIM - attribute_name: %s value: %s pos: %d\n", attribute_name, &delim_value[pos], pos);
    
    									if (prev) {
    										prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
    										if (prev->next) {
    											prev = prev->next;
    										}
    									} else {
    										prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
    									}
    									/* Remembering to free memory */
    									is_delimited = 0;
    
    									pos = 0;
    
    								ast_debug(4, "DELIM pos: %d i: %d\n", pos, i);
    
    							} else {
    								/* not delimited */
    								if (delim_value) {
    
    								ast_debug(2, "attribute_name: %s value: %s\n", attribute_name, valptr);
    
    									prev->next = ast_variable_new(attribute_name, valptr, table_config->table_name);
    
    									if (prev->next) {
    										prev = prev->next;
    									}
    								} else {
    
    									prev = var = ast_variable_new(attribute_name, valptr, table_config->table_name);
    
    				}/*!< if (values) */
    
    				ldap_memfree(ldap_attribute_name);
    
    				ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
    			} /*!< while (ldap_attribute_name) */
    			ber_free(ber, 0);
    
    			if (static_table_config == table_config) {
    
    					const struct ast_variable *tmpdebug = variable_named(var, "variable_name");
    					const struct ast_variable *tmpdebug2 = variable_named(var, "variable_value");
    					if (tmpdebug && tmpdebug2) {
    
    						ast_log(LOG_DEBUG, "Added to vars - %s = %s\n", tmpdebug->value, tmpdebug2->value);
    
    					}
    				}
    				vars[entry_index++] = var;
    				prev = NULL;
    			}
    
    			delim_count++;
    
    		} while (delim_count <= delim_tot_count && static_table_config == table_config);
    
    		if (static_table_config != table_config) {
    
    			ast_debug(3, "Added to vars - non static\n");
    
    			vars[entry_index++] = var;
    			prev = NULL;
    		}
    		ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
    	} /*!< end for loop over ldap_entry */
    
    	return vars;
    }
    
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Check if we have a connection error
     */
    
    static int is_ldap_connect_error(int err)
    {
    
    	return (err == LDAP_SERVER_DOWN || err == LDAP_TIMEOUT || err == LDAP_CONNECT_ERROR);
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief Get LDAP entry by dn and return attributes as variables
     *
    
     * Should be locked before using it
    
    Andrew Latham's avatar
    Andrew Latham committed
     *
     * This is used for setting the default values of an object
     * i.e., with accountBaseDN
    
    */
    static struct ast_variable *ldap_loadentry(struct ldap_table_config *table_config,
    					   const char *dn)
    {
    	if (!table_config) {
    		ast_log(LOG_ERROR, "No table config\n");
    		return NULL;
    	} else {
    		struct ast_variable **vars = NULL;
    		struct ast_variable *var = NULL;
    		int result = -1;
    
    		LDAPMessage *ldap_result_msg = NULL;
    
    		int tries = 0;
    
    		ast_debug(2, "ldap_loadentry dn=%s\n", dn);
    
    		do {
    			result = ldap_search_ext_s(ldapConn, dn, LDAP_SCOPE_BASE,
    
    					   "(objectclass=*)", NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &ldap_result_msg);
    
    			if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
    
    				ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
    
    				tries++;
    				if (tries < 3) {
    					usleep(500000L * tries);
    					if (ldapConn) {
    
    						ldap_unbind_ext_s(ldapConn, NULL, NULL);
    
    		} while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
    
    		if (result != LDAP_SUCCESS) {
    
    			ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
    
    			ast_debug(2, "dn=%s\n", dn);
    			ast_mutex_unlock(&ldap_lock);
    			return NULL;
    		} else {
    			int num_entry = 0;
    
    			unsigned int *entries_count_ptr = NULL; /*!< not using this */
    
    			if ((num_entry = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
    
    				ast_debug(3, "num_entry: %d\n", num_entry);
    
    
    				vars = realtime_ldap_result_to_vars(table_config, ldap_result_msg, entries_count_ptr);
    
    					ast_log(LOG_NOTICE, "More than one entry for dn=%s. Take only 1st one\n", dn);
    
    				ast_debug(2, "Could not find any entry dn=%s.\n", dn);
    
    		ldap_msgfree(ldap_result_msg);
    
    
    		/* Chopping \a vars down to one variable */
    
    		if (vars != NULL) {
    
    			struct ast_variable **p = vars;
    
    
    			/* Only take the first one. */
    			var = *vars;
    
    			/* Destroy the rest. */
    			while (*++p) {
    				ast_variables_destroy(*p);
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \note caller should free returned pointer
     */
    
    static char *substituted(struct ast_channel *channel, const char *string)
    {
    #define MAXRESULT	2048
    	char *ret_string = NULL;
    
    	if (!ast_strlen_zero(string)) {
    		ret_string = ast_calloc(1, MAXRESULT);
    		pbx_substitute_variables_helper(channel, string, ret_string, MAXRESULT - 1);
    	}
    
    	ast_debug(2, "substituted: string: '%s' => '%s' \n", string, ret_string);
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \note caller should free returned pointer
     */
    
    static char *cleaned_basedn(struct ast_channel *channel, const char *basedn)
    {
    	char *cbasedn = NULL;
    	if (basedn) {
    		char *p = NULL;
    		cbasedn = substituted(channel, basedn);
    		if (*cbasedn == '"') {
    			cbasedn++;
    			if (!ast_strlen_zero(cbasedn)) {
    				int len = strlen(cbasedn);
    				if (cbasedn[len - 1] == '"')
    					cbasedn[len - 1] = '\0';
    
    			}
    		}
    		p = cbasedn;
    		while (*p) {
    			if (*p == '|')
    				*p = ',';
    			p++;
    		}
    	}
    	ast_debug(2, "basedn: '%s' => '%s' \n", basedn, cbasedn);
    	return cbasedn;
    }
    
    
    /*! \brief Replace \<search\> by \<by\> in string.
    
    Andrew Latham's avatar
    Andrew Latham committed
     * \note No check is done on string allocated size !
     */
    
    static int replace_string_in_string(char *string, const char *search, const char *by)
    
    {
    	int search_len = strlen(search);
    	int by_len = strlen(by);
    	int replaced = 0;
    	char *p = strstr(string, search);
    
    	if (p) {
    		replaced = 1;
    		while (p) {
    
    				memcpy(p, by, by_len);
    
    			} else {
    				memmove(p + by_len, p + search_len, strlen(p + search_len) + 1);
    
    				memcpy(p, by, by_len);
    			}
    			p = strstr(p + by_len, search);
    		}
    	}
    	return replaced;
    }
    
    
    /*! \brief Append a name=value filter string. The filter string can grow.
    
    Andrew Latham's avatar
    Andrew Latham committed
     */
    
    static void append_var_and_value_to_filter(struct ast_str **filter,
    	struct ldap_table_config *table_config,
    	const char *name, const char *value)
    {
    	char *new_name = NULL;
    	char *new_value = NULL;
    	char *like_pos = strstr(name, " LIKE");
    
    	ast_debug(2, "name='%s' value='%s'\n", name, value);
    
    	if (like_pos) {
    
    		int len = like_pos - name;
    
    		name = new_name = ast_strdupa(name);
    		new_name[len] = '\0';
    
    		value = new_value = ast_strdupa(value);
    		replace_string_in_string(new_value, "\\_", "_");
    		replace_string_in_string(new_value, "%", "*");
    	}
    
    	name = convert_attribute_name_to_ldap(table_config, name);
    
    	ast_str_append(filter, 0, "(%s=%s)", name, value);
    }
    
    
    /*!
     * \internal
     * \brief Create an LDAP filter using search fields
     *
     * \param config the \c ldap_table_config for this search
     * \param fields the \c ast_variable criteria to include
     *
     * \returns an \c ast_str pointer on success, NULL otherwise.
     */
    static struct ast_str *create_lookup_filter(struct ldap_table_config *config, const struct ast_variable *fields)
    {
    	struct ast_str *filter;
    	const struct ast_variable *field;
    
    	filter = ast_str_create(80);
    	if (!filter) {
    		return NULL;
    	}
    
    	/*
    	 * Create the filter with the table additional filter and the
    	 * parameter/value pairs we were given
    	 */
    	ast_str_append(&filter, 0, "(&");
    	if (config && config->additional_filter) {
    		ast_str_append(&filter, 0, "%s", config->additional_filter);
    	}
    	if (config != base_table_config
    		&& base_table_config
    		&& base_table_config->additional_filter) {
    		ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
    	}
    	/* Append the lookup fields */
    	for (field = fields; field; field = field->next) {
    		append_var_and_value_to_filter(&filter, config, field->name, field->value);
    	}
    	ast_str_append(&filter, 0, ")");
    
    	return filter;
    }
    
    
    /*! \brief LDAP base function
    
     * \return a null terminated array of ast_variable (one per entry) or NULL if no entry is found or if an error occured
     * caller should free the returned array and ast_variables
     * \param entries_count_ptr is a pointer to found entries count (can be NULL)
     * \param basedn is the base DN
     * \param table_name is the table_name (used dor attribute convertion and additional filter)
    
     * \param fields contains list of pairs name/value
    
    static struct ast_variable **realtime_ldap_base_ap(unsigned int *entries_count_ptr,
    
    	const char *basedn, const char *table_name, const struct ast_variable *fields)
    
    {
    	struct ast_variable **vars = NULL;
    
    	const struct ast_variable *field = fields;
    
    	struct ldap_table_config *table_config = NULL;
    	char *clean_basedn = cleaned_basedn(NULL, basedn);
    	struct ast_str *filter = NULL;
    	int tries = 0;
    	int result = 0;
    
    	LDAPMessage *ldap_result_msg = NULL;
    
    		ast_log(LOG_ERROR, "No table_name specified.\n");
    
    		ast_free(clean_basedn);
    
    	if (!field) {
    
    		ast_log(LOG_ERROR, "Realtime retrieval requires at least 1 parameter"
    
    			" and 1 value to search on.\n");
    
    		ast_free(clean_basedn);
    
    		return NULL;
    	}
    
    	ast_mutex_lock(&ldap_lock);
    
    	/* We now have our complete statement; Lets connect to the server and execute it.  */
    	if (!ldap_reconnect()) {
    		ast_mutex_unlock(&ldap_lock);
    
    		ast_free(clean_basedn);
    
    	table_config = table_config_for_table_name(table_name);
    	if (!table_config) {
    		ast_log(LOG_WARNING, "No table named '%s'.\n", table_name);
    		ast_mutex_unlock(&ldap_lock);
    
    		ast_free(clean_basedn);
    
    	filter = create_lookup_filter(table_config, fields);
    	if (!filter) {
    		ast_mutex_unlock(&ldap_lock);
    		ast_free(clean_basedn);
    		return NULL;
    
    	}
    
    	do {
    		/* freeing ldap_result further down */
    		result = ldap_search_ext_s(ldapConn, clean_basedn,
    
    				  LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
    
    		if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
    
    			ast_debug(1, "Failed to query directory. Try %d/10\n", tries + 1);
    
    			if (++tries < 10) {
    				usleep(1);
    
    					ldap_unbind_ext_s(ldapConn, NULL, NULL);
    
    	} while (result != LDAP_SUCCESS && tries < 10 && is_ldap_connect_error(result));
    
    	if (result != LDAP_SUCCESS) {
    
    		ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
    
    		ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
    
    		/* this is where we create the variables from the search result
    
    		 * freeing this \a vars outside this function */
    
    		if (ldap_count_entries(ldapConn, ldap_result_msg) > 0) {
    
    			/* is this a static var or some other? they are handled different for delimited values */
    
    			vars = realtime_ldap_result_to_vars(table_config, ldap_result_msg, entries_count_ptr);
    
    			ast_debug(1, "Could not find any entry matching %s in base dn %s.\n", ast_str_buffer(filter), clean_basedn);
    
    		ldap_msgfree(ldap_result_msg);
    
    Alexander Traud's avatar
    Alexander Traud committed
    		/*! \todo get the default variables from the accountBaseDN, not implemented with delimited values
    
    Andrew Latham's avatar
    Andrew Latham committed
    		 */
    
    		if (vars) {
    			struct ast_variable **p = vars;
    			while (*p) {
    				struct ast_variable *append_var = NULL;
    				struct ast_variable *tmp = *p;
    				while (tmp) {
    					if (strcasecmp(tmp->name, "accountBaseDN") == 0) {
    						/* Get the variable to compare with for the defaults */
    						struct ast_variable *base_var = ldap_loadentry(table_config, tmp->value);
    
    						while (base_var) {
    							struct ast_variable *next = base_var->next;
    							struct ast_variable *test_var = *p;
    							int base_var_found = 0;
    
    							/* run throught the default values and fill it inn if it is missing */
    							while (test_var) {
    								if (strcasecmp(test_var->name, base_var->name) == 0) {
    									base_var_found = 1;
    									break;
    
    									test_var = test_var->next;
    
    							}
    							if (base_var_found) {
    								base_var->next = NULL;
    
    								ast_variables_destroy(base_var);
    
    								base_var = next;
    							} else {
    
    								/*!
    								 * \todo XXX The interactions with base_var and append_var may
    								 * cause a memory leak of base_var nodes.  Also the append_var
    								 * list and base_var list may get cross linked.
    								 */
    
    									base_var->next = append_var;
    
    									base_var->next = NULL;
    
    								append_var = base_var;
    								base_var = next;
    							}
    						}
    					}
    					if (!tmp->next && append_var) {
    						tmp->next = append_var;
    						tmp = NULL;
    
    	ast_free(filter);
    	ast_free(clean_basedn);
    
    
    	ast_mutex_unlock(&ldap_lock);
    
    	return vars;
    }
    
    
    static struct ast_variable *realtime_arguments_to_fields(va_list ap)
    {
    	struct ast_variable *fields = NULL;
    	const char *newparam, *newval;
    
    	while ((newparam = va_arg(ap, const char *))) {
    		struct ast_variable *field;
    
    		newval = va_arg(ap, const char *);
    		if (!(field = ast_variable_new(newparam, newval, ""))) {
    			ast_variables_destroy(fields);
    			return NULL;
    		}
    
    		field->next = fields;
    		fields = field;
    	}
    
    	return fields;
    }
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*! \brief same as realtime_ldap_base_ap but take variable arguments count list
     */
    
    static struct ast_variable **realtime_ldap_base(unsigned int *entries_count_ptr,
    
    	const char *basedn, const char *table_name, ...)
    {
    
    	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
    
    	struct ast_variable **vars = NULL;
    	va_list ap;
    
    	va_start(ap, table_name);
    
    	fields = realtime_arguments_to_fields(ap);
    
    	vars = realtime_ldap_base_ap(entries_count_ptr, basedn, table_name, fields);