[svn-commits] anthonyl: branch anthonyl/5768-ldap-redux r47979 - /team/anthonyl/5768-ldap-r...

svn-commits at lists.digium.com svn-commits at lists.digium.com
Thu Nov 23 17:59:32 MST 2006


Author: anthonyl
Date: Thu Nov 23 18:59:31 2006
New Revision: 47979

URL: http://svn.digium.com/view/asterisk?view=rev&rev=47979
Log:
ok

Added:
    team/anthonyl/5768-ldap-redux/res/res_config_ldap.c   (with props)

Added: team/anthonyl/5768-ldap-redux/res/res_config_ldap.c
URL: http://svn.digium.com/view/asterisk/team/anthonyl/5768-ldap-redux/res/res_config_ldap.c?view=auto&rev=47979
==============================================================================
--- team/anthonyl/5768-ldap-redux/res/res_config_ldap.c (added)
+++ team/anthonyl/5768-ldap-redux/res/res_config_ldap.c Thu Nov 23 18:59:31 2006
@@ -1,0 +1,1200 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2005, Oxymium sarl
+ * 
+ * Manuel Guesdon <mguesdon at oxymium.net> - LDAP RealTime Driver Author/Adaptor
+ *
+ * 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
+ *
+ * \brief ldap plugin for portable configuration engine (ARA)
+ *
+ * \author Mark Spencer <markster at digium.com>
+ * \author Manuel Guesdon
+ *
+ * \arg http://www.openldap.org
+ */
+
+/*** MODULEINFO
+	<depend>ldap</depend>
+ ***/
+
+#include "asterisk.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <ldap.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 47896 $")
+
+#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 LDAP_AUTH_SIMPLE 1
+#define LDAP_AUTH_SASL   2 
+
+static char *res_config_ldap_desc = "LDAP RealTime Configuration Driver";
+
+/* note to me: group these into a structure */
+AST_MUTEX_DEFINE_STATIC(ldap_lock);
+
+static LDAP *ldapConn = NULL;
+static char dbhost[512] = "";
+static char dbuser[512] = "";
+static char dbpass[50] = "";
+static char dbbasedn[512] = "";
+static int dbport = 389;
+static int ldapversion = LDAP_VERSION3;
+static int ldapauthtype = LDAP_AUTH_SIMPLE; /* default to simple authencation */
+static time_t connect_time = 0;
+
+static int parse_config(void);
+static int ldap_reconnect(void);
+static int realtime_ldap_status(int fd, int argc, char **argv);
+
+struct category_and_metric {
+	char *name;
+	int metric;
+	char *variable_name;
+	char *variable_value;
+};
+
+/*! \brief Table configuration */
+struct ldap_table_config {
+	char *table_name;
+	char *additional_filter;
+	struct ast_variable *attributes;
+	struct ldap_table_config *next;
+};
+static char cli_realtime_ldap_status_usage[] =
+	"Usage: realtime ldap status\n"
+	"       Shows connection information for the LDAP RealTime driver\n";
+
+static struct ast_cli_entry cli_realtime_ldap_status = {
+	{ "realtime", "ldap", "status", NULL }, realtime_ldap_status,
+	"Shows connection information for the LDAP RealTime driver",
+	cli_realtime_ldap_status_usage, NULL
+};
+
+/*! \brief Should be locked before using it */
+static struct ldap_table_config *table_configs = NULL;
+static struct ldap_table_config *base_table_config = NULL;
+
+/*! \brief Create a new table_config */
+static struct ldap_table_config *table_config_new(const char *table_name)
+{
+	struct ldap_table_config *p = ast_calloc(1, sizeof(*p));
+	
+	if (table_name)
+		p->table_name = strdup(table_name);
+	
+	return p;
+}
+
+/*! \brief Find a table_config - Should be locked before using it */
+static struct ldap_table_config *table_config_for_table_name(const char *table_name)
+{
+	struct ldap_table_config *c = table_configs;
+	while (c) {
+		if (strcmp(c->table_name, table_name) == 0)
+			return c;
+		else
+			c = c->next;
+	}
+	return NULL;
+}
+
+/*! \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_string)
+{
+	if (attribute_string && *attribute_string) {
+		char *string = strdup(attribute_string);
+		char *start = string;
+		char *p = strstr(start, "=>");
+
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Add attribute: start: %s\n", start);
+
+		if (!p) {
+			ast_log(LOG_WARNING,
+					"Missing '=>' in attribute: %s in %s\n",
+					attribute_string, table_config->table_name);
+		} else {
+			char *value = p + 2;	//skip =>      
+			/* trim ! */
+			while (isspace(*start))
+				start++;
+			p--;
+			while (p >= start && isspace(*p)) {
+				*p = '\0';
+				p--;
+			}
+			while (isspace(*value))
+				value++;
+			p = value + strlen(value) - 1;
+			while (p >= value && isspace(*p)) {
+				*p = '\0';
+				p--;
+			}
+			if (ast_strlen_zero(start)) {
+				ast_log(LOG_WARNING,
+						"Empty variable name in attribute: %s in %s\n",
+						attribute_string, table_config->table_name);
+			} else if (ast_strlen_zero(value)) {
+				ast_log(LOG_WARNING,
+						"Empty ldap attribute name in attribute: %s in %s\n",
+						attribute_string, table_config->table_name);
+			} else {
+				struct ast_variable *var = ast_variable_new(start, value);
+				if (option_debug > 2)
+					ast_log(LOG_DEBUG, "Add attribute: VAR %s => %s\n",var->name,var->value);
+				if (table_config->attributes)
+					var->next = table_config->attributes;
+				table_config->attributes = var;
+				if (option_debug > 2)
+					ast_log(LOG_DEBUG, "Added attribute in %s: %s -> %s\n",
+						table_config->table_name, start, value);
+			}
+		}
+		free(string);
+	}
+}
+
+/*! \brief Free table_config */
+static void table_configs_free(void)
+{
+	struct ldap_table_config *c = table_configs;
+
+	while (c) {
+		struct ldap_table_config *next = c->next;
+		if (c->table_name)
+			free(c->table_name);
+		if (c->additional_filter)
+			free(c->additional_filter);
+		if (c->attributes) {
+			ast_variables_destroy(c->attributes);
+		}
+		free(c);
+		c = next;
+	}
+	table_configs = NULL;
+	base_table_config = NULL;
+}
+
+/*! \brief Convert variable name to ldap attribute name - Should be locked before using it */
+static const char *convert_attribute_name_to_ldap(struct ldap_table_config *table_config,
+						  const char *attribute_name)
+{
+	int i = 0;
+	for (i = 0; i < 2; i++) {
+		if (table_config) {
+			struct ast_variable *attribute = table_config->attributes;
+			while (attribute) {
+				if (strcasecmp(attribute_name, attribute->name) == 0)
+					return attribute->value;
+				else
+					attribute = attribute->next;
+			}
+		}
+		if (table_config == base_table_config)
+			break;
+		else
+			table_config = base_table_config;
+	}
+	return attribute_name;
+}
+
+/*! \brief Convert ldap attribute name to variable name - 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;
+
+	for (i = 0; i < 2; i++) {
+		if (table_config) {
+			struct ast_variable *attribute = table_config->attributes;
+
+			while (attribute) {
+				if (strcasecmp(attribute_name, attribute->value) == 0)
+					return attribute->name;
+				else
+					attribute = attribute->next;
+			}
+		}
+		if (table_config == base_table_config)
+			break;
+		else
+			table_config = base_table_config;
+	}
+	return attribute_name;
+}
+
+/*! \brief Find variable by name */
+static struct ast_variable *variable_named(struct ast_variable *var,
+					   const char *name)
+{
+	while (var) {
+		if (strcasecmp(name, var->name) == 0)
+			return var;
+		else
+			var = var->next;
+	}
+	return NULL;
+}
+
+/*! \brief Get variables from ldap entry attributes - Should be locked before using it */
+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;
+
+	char *ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
+
+	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;
+		char **values = NULL;
+
+		values = ldap_get_values(ldapConn, ldap_entry, ldap_attribute_name);
+		if (values) {
+			char **v = values;
+
+			while (*v) {
+				char *value = *v;
+				if (option_debug > 1)
+					ast_log(LOG_DEBUG, "attribute_name: %s value: %s\n", attribute_name, value);
+				if (is_realmed_password_attribute) {
+					if (strncasecmp(value, "{md5}", 5) == 0)
+						value += 5;
+					else
+						value = NULL;
+					if (option_debug > 1)
+						ast_log(LOG_DEBUG, "md5: %s\n", value);
+				}
+				if (value) {
+					if (prev) {
+						prev->next = ast_variable_new(attribute_name, value);
+						if (prev->next) 
+							prev = prev->next;
+					} else {
+						prev = var = ast_variable_new(attribute_name, value);
+					}
+				}
+				v++;
+			}
+			ldap_value_free(values);
+		}
+		ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
+	}
+	ber_free(ber, 0);
+
+	return var;
+}
+
+static int is_ldap_connect_error(int err)
+{
+	return (err == LDAP_SERVER_DOWN
+			|| err == LDAP_TIMEOUT || err == LDAP_CONNECT_ERROR);
+}
+
+/*! \brief LGet LDAP entry by dn and return attributes as variables  - Should be locked before using it */
+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 *var = NULL;
+		int result = -1;
+		LDAPMessage *ldap_result = NULL;
+		int tries = 0;
+
+		if (option_debug > 1)
+			ast_log(LOG_DEBUG, "ldap_loadentry dn=%s\n", dn);
+
+		do {
+			result = ldap_search_s(ldapConn, dn, LDAP_SCOPE_BASE,
+					   "(objectclass=*)", NULL, 0, &ldap_result);
+			if (result < 0 && is_ldap_connect_error(result)) {
+				ast_log(LOG_WARNING,
+					"Failed to query database. Try %d/3\n",
+					tries + 1);
+				tries++;
+				if (tries < 3) {
+					usleep(500000L * tries);
+					if (ldapConn) {
+						ldap_unbind_s(ldapConn);
+						ldapConn = NULL;
+					}
+					if (!ldap_reconnect())
+						break;
+				}
+			}
+		} while (result < 0 && tries < 3 && is_ldap_connect_error(result));
+
+		if (result < 0) {
+			ast_log(LOG_WARNING,
+					"Failed to query database. Check debug for more info.\n");
+			if (option_debug > 1) {
+				ast_log(LOG_DEBUG, "dn=%s\n", dn);
+				ast_log(LOG_DEBUG, "Query Failed because: %s\n",
+					ldap_err2string(result));
+			}
+			ast_mutex_unlock(&ldap_lock);
+			return NULL;
+		} else {
+			int num_entry = 0;
+			if ((num_entry = ldap_count_entries(ldapConn, ldap_result)) > 0) {
+				LDAPMessage *ldap_entry = NULL;
+				if (option_debug)
+					ast_log(LOG_DEBUG, "num_entry: %d\n", num_entry);
+
+				ldap_entry = ldap_first_entry(ldapConn, ldap_result);
+				var = realtime_ldap_entry_to_var(table_config, ldap_entry);
+				if (num_entry > 1)
+					ast_log(LOG_WARNING, "More than one entry for dn=%s. Take only 1st one\n", dn);
+			} else {
+				ast_log(LOG_WARNING, "Could not find any entry dn=%s.\n", dn);
+			}
+		}
+		ldap_msgfree(ldap_result);
+
+		return var;
+	}
+}
+
+/*! \brief 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);
+	}
+	if (option_debug > 1)
+		ast_log(LOG_DEBUG, "substituted: string: '%s' => '%s' \n",
+			string, ret_string);
+	return ret_string;
+}
+
+/*! \brief 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++;
+		}
+	}
+	if (option_debug > 1)
+		ast_log(LOG_DEBUG, "basedn: '%s' => '%s' \n", basedn, cbasedn);
+	return cbasedn;
+}
+
+/*! \brief Append a string to a filter string. The filter string can grow */
+static void append_string_to_filter(char **filter_ptr, int *filter_size_ptr, const char *filter)
+{
+	int current_len = 0;
+	int needed_len = 0;
+	char *r_filter = NULL;
+
+	if (strchr(filter, '$')) {
+		r_filter = substituted(NULL, filter);
+		filter = r_filter;
+	}
+	current_len = (*filter_ptr ? strlen(*filter_ptr) : 0);
+	needed_len = current_len + strlen(filter);
+	if (*filter_size_ptr < (needed_len + 1)) {
+		if (*filter_size_ptr == 0)
+			*filter_size_ptr = (needed_len > 128 ? needed_len : 128);
+		else
+			*filter_size_ptr = (*filter_size_ptr) * 2;
+		*filter_ptr = realloc(*filter_ptr, *filter_size_ptr);
+	}
+	if (*filter_ptr) {
+		strcpy((*filter_ptr) + current_len, filter);
+	}
+	if (r_filter)
+		free(r_filter);
+}
+
+/*! \brief Replace search by by in string. 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) {
+			if (by_len == search_len)
+				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. */
+/*! \brief convert name and value if "LIKE' is used (see http://bugs.digium.com/view.php?id=5765) */
+static void append_var_and_value_to_filter(char **filter_ptr,
+					   int *filter_size_ptr,
+					   struct ldap_table_config
+					   *table_config, const char *name,
+					   const char *value)
+{
+	char *new_name = NULL;
+	char *new_value = NULL;
+	char *wsPos = strstr(name, " LIKE");
+	if (option_debug > 1)
+		ast_log(LOG_DEBUG, "name='%s' value='%s'\n", name, value);
+	if (wsPos) {
+		new_name = strndup(name, wsPos - name);
+		name = new_name;
+		new_value = strdup(value);
+		replace_string_in_string(new_value, "\\_", "_");
+		replace_string_in_string(new_value, "%", "*");
+		value = new_value;
+	}
+	name = convert_attribute_name_to_ldap(table_config, name);
+	append_string_to_filter(filter_ptr, filter_size_ptr, "(");
+	append_string_to_filter(filter_ptr, filter_size_ptr, name);
+	append_string_to_filter(filter_ptr, filter_size_ptr, "=");
+	append_string_to_filter(filter_ptr, filter_size_ptr, value);
+	append_string_to_filter(filter_ptr, filter_size_ptr, ")");
+	if (new_name)
+		free(new_name);
+	if (new_value)
+		free(new_value);
+}
+
+/*! \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
+ entries_count_ptr is a pointer to found entries count (can be NULL)
+ basedn is the base DN
+ table_name is the table_name (used dor attribute convertion and additional filter)
+ ap contains null terminated list of pairs name/value
+*/
+static struct ast_variable **realtime_ldap_base(unsigned int *entries_count_ptr,
+	const char *basedn, const char *table_name, va_list ap)
+{
+	struct ast_variable **vars = NULL;
+	const char *newparam = NULL;
+	const char *newval = NULL;
+
+	if (option_debug > 1)
+		ast_log(LOG_DEBUG,
+			"realtime_ldap_base: basedn: %s table_name: %s\n", basedn, table_name);
+	if (!table_name) {
+		ast_log(LOG_WARNING, "No table_name specified.\n");
+		return NULL;
+	} 
+
+	/* Get the first parameter and first value in our list of passed paramater/value pairs  */
+	newparam = va_arg(ap, const char *);
+	newval = va_arg(ap, const char *);
+	if (!newparam || !newval) {
+		ast_log(LOG_WARNING,
+				"Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
+	} else {
+		ast_mutex_lock(&ldap_lock);
+
+		/* We now have our complete statement; Lets connect to the server and execute it.  */
+		if (ldap_reconnect()) {
+			struct ldap_table_config *table_config = NULL;
+
+			table_config = table_config_for_table_name(table_name);
+			if (!table_config) {
+				ast_log(LOG_WARNING,
+						"No table named '%s'.\n",
+						table_name);
+			} else {
+				char *clean_basedn = cleaned_basedn(NULL, basedn);
+				char *filter = NULL;
+				int filter_size = 0;
+				int tries = 0;
+
+				int result = 0;
+				LDAPMessage *ldap_result = NULL;
+
+				append_string_to_filter(&filter, &filter_size, "(&");
+
+				if (table_config && table_config->additional_filter) {
+					append_string_to_filter(&filter, &filter_size,
+						table_config->additional_filter);
+				}
+				if (table_config != base_table_config && base_table_config
+					&& base_table_config->additional_filter) {
+						append_string_to_filter(&filter, &filter_size,
+							base_table_config->additional_filter);
+				}
+
+				/* Create the first part of the query using the first parameter/value pairs we just extracted */
+				/*   If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+
+				append_var_and_value_to_filter(&filter, &filter_size,
+				       table_config, newparam, newval);
+				while ((newparam = va_arg(ap, const char *))) {
+					newval = va_arg(ap, const char *);
+					append_var_and_value_to_filter(&filter, &filter_size,
+					       table_config, newparam, newval);
+				}
+				append_string_to_filter(&filter, &filter_size, ")");
+
+				if (option_debug)
+					ast_log(LOG_DEBUG, "filter: %s\n", filter);
+
+
+				do {
+					result = ldap_search_s(ldapConn, clean_basedn,
+							  LDAP_SCOPE_SUBTREE, filter, NULL, 0,
+							  &ldap_result);
+					if (result < 0 && is_ldap_connect_error(result)) {
+						ast_log(LOG_WARNING, "Failed to query database. Try %d/3\n",
+							tries + 1);
+						tries++;
+						if (tries < 3) {
+							usleep(500000L * tries);
+							if (ldapConn) {
+								ldap_unbind_s(ldapConn);
+								ldapConn = NULL;
+							}
+							if (!ldap_reconnect())
+								break;
+						}
+					}
+				} while (result < 0 && tries < 3 && is_ldap_connect_error(result));
+
+				if (result < 0) {
+					ast_log(LOG_WARNING,
+							"Failed to query database. Check debug for more info.\n");
+					ast_log(LOG_WARNING, "Query: %s\n",
+							filter);
+					ast_log(LOG_WARNING,
+							"Query Failed because: %s\n",
+								ldap_err2string(result));
+				} else {
+					int num_entry = 0;
+					if ((num_entry = ldap_count_entries(ldapConn, ldap_result)) > 0) {
+						LDAPMessage *ldap_entry = NULL;
+						int entry_index = 0;
+						if (entries_count_ptr)
+							*entries_count_ptr = num_entry;
+						vars = ast_calloc(1, sizeof(struct ast_variable *) *(num_entry + 1));
+						if (option_debug)
+							ast_log(LOG_DEBUG, "num_entry: %d\n", num_entry);
+
+						ldap_entry = ldap_first_entry(ldapConn, ldap_result);
+
+						for (entry_index = 0; ldap_entry; entry_index++) {
+							vars[entry_index] = realtime_ldap_entry_to_var(table_config,ldap_entry);
+							ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
+						}
+					} else {
+						ast_log(LOG_WARNING, "Could not find any entry matching %s in base dn %s.\n",
+							filter, clean_basedn);
+					}
+
+					ldap_msgfree(ldap_result);
+
+					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) {
+									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;
+
+										while (test_var) {
+											if (strcasecmp(test_var->name, base_var->name) == 0) {
+												base_var_found = 1;
+												break;
+											} else
+												test_var = test_var->next;
+										}
+										if (base_var_found) {
+											base_var->next = NULL;
+											ast_variables_destroy (base_var);
+											base_var = next;
+										} else {
+											if (append_var)
+												base_var->next = append_var;
+											else
+												base_var->next = NULL;
+											append_var = base_var;
+											base_var = next;
+										}
+									}
+								}
+								if (!tmp->next && append_var) {
+									tmp->next = append_var;
+									tmp = NULL;
+								} else
+									tmp = tmp->next;
+							}
+							p++;
+						}
+					}
+				}
+				if (filter)
+					free(filter);
+				if (clean_basedn)
+					free(clean_basedn);
+			}
+		}
+		ast_mutex_unlock(&ldap_lock);
+	}
+	return vars;
+}
+
+/*! \brief same as realtime_ldap_base_ 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, ...)
+{
+	struct ast_variable **vars = NULL;
+	va_list ap;
+
+	va_start(ap, table_name);
+	vars = realtime_ldap_base(entries_count_ptr, basedn, table_name, ap);
+	va_end(ap);
+	return vars;
+}
+
+/*! \brief See Asterisk doc */
+static struct ast_variable *realtime_ldap(const char *basedn,
+					  const char *table_name, va_list ap)
+{
+	struct ast_variable **vars = realtime_ldap_base(NULL, basedn, table_name, ap);
+	struct ast_variable *var = NULL;
+
+	if (vars) {
+		struct ast_variable *last_var = NULL;
+		struct ast_variable **p = vars;
+		while (*p) {
+			if (last_var) {
+				while (last_var->next)
+					last_var = last_var->next;
+				last_var->next = *p;
+			} else {
+				var = *p;
+				last_var = var;
+			}
+			p++;
+		}
+		free(vars);
+	}
+	return var;
+}
+
+/*! \brief See Asterisk doc */
+static struct ast_config *realtime_multi_ldap(const char *basedn,
+      const char *table_name, va_list ap)
+{
+	struct ast_variable **vars =
+		realtime_ldap_base(NULL, basedn, table_name, ap);
+	struct ast_config *cfg = NULL;
+
+	if (vars) {
+		cfg = ast_config_new();
+		if (!cfg) {
+			ast_log(LOG_WARNING, "Out of memory!\n");
+		} else {
+			struct ast_variable **p = vars;
+
+			while (*p) {
+				struct ast_category *cat = NULL;
+				cat = ast_category_new("");
+				if (!cat) {
+					ast_log(LOG_WARNING, "Out of memory!\n");
+					break;
+				} else {
+					struct ast_variable *var = *p;
+					while (var) {
+						struct ast_variable *next = var->next;
+						var->next = NULL;
+						ast_variable_append(cat, var);
+						var = next;
+					}
+				}
+				ast_category_append(cfg, cat);
+				p++;
+			}
+		}
+		free(vars);
+	}
+	return cfg;
+
+}
+
+static int compare_categories(const void *a, const void *b)
+{
+	struct category_and_metric *as = (struct category_and_metric *) a;
+	struct category_and_metric *bs = (struct category_and_metric *) b;
+	if (as->metric < bs->metric)
+		return -1;
+	else if (as->metric > bs->metric)
+		return 1;
+	else
+		return strcmp(as->name, bs->name);
+}
+
+/*! \brief See Asterisk doc */
+static struct ast_config *config_ldap(const char *basedn,
+				      const char *table_name,
+				      const char *file,
+				      struct ast_config *cfg,
+				      int withcomments)
+{
+	ast_log(LOG_DEBUG, "config_ldap: basedn: %s table_name: %s\n",	basedn, table_name);
+	if (!file || !strcmp(file, RES_CONFIG_LDAP_CONF)) {
+		ast_log(LOG_WARNING, "Cannot configure myself.\n");
+	} else {
+		unsigned int vars_count = 0;
+		struct ast_variable **vars =
+			realtime_ldap_base_(&vars_count, basedn, table_name, "filename",
+								file, "commented", "FALSE", NULL);
+
+		if (vars) {
+			int i = 0;
+			struct ast_variable *new_v = NULL;
+			struct ast_category *cur_cat = NULL;
+			char *last_category = NULL;
+			int last_category_metric = 0;
+
+			/* sort on metric and category */
+			struct category_and_metric *categories =
+				malloc(sizeof(struct category_and_metric) * vars_count);
+			struct ast_variable **p = vars;
+			vars_count = 0;
+			while (*p) {
+				struct ast_variable *category =
+					variable_named(*p, "category");
+				struct ast_variable *cat_metric =
+					variable_named(*p, "cat_metric");
+				struct ast_variable *var_name =
+					variable_named(*p, "variable_name");
+				struct ast_variable *var_val =
+					variable_named(*p, "variable_value");
+				if (option_debug) {
+					ast_log(LOG_DEBUG, "category: %s\n",
+						category->value);
+					ast_log(LOG_DEBUG, "var_name: %s\n",
+						var_name->value);
+					ast_log(LOG_DEBUG, "var_val: %s\n",
+						var_val->value);
+					ast_log(LOG_DEBUG, "cat_metric: %s\n",
+						cat_metric->value);
+				}
+				if (!category) {
+					struct ast_variable *dn = variable_named(*p, "dn");
+					ast_log(LOG_ERROR,
+							"No category name in entry '%s'  for file '%s'.\n",
+							(dn ? dn->value : "?"), file);
+				} else if (!cat_metric) {
+					struct ast_variable *dn = variable_named(*p, "dn");
+					ast_log(LOG_ERROR,
+							"No category metric in entry '%s'(category: %s) for file '%s'.\n",
+							(dn ? dn->value : "?"), category->value, file);
+				} else if (!var_name) {
+					struct ast_variable *dn = variable_named(*p, "dn");
+					ast_log(LOG_ERROR,
+							"No variable name in entry '%s' (category: %s metric: %s) for file '%s'.\n",
+							(dn ? dn->value : "?"), category->value,
+							cat_metric->value, file);
+				} else if (!var_val) {
+					struct ast_variable *dn = variable_named(*p, "dn");
+					ast_log(LOG_ERROR,
+							"No variable value in entry '%s' (category: %s metric: %s variable: %s) for file '%s'.\n",
+							(dn ? dn->value : "?"), category->value,
+							cat_metric->value, var_name->value, file);
+				} else {
+					categories[vars_count].name = category->value;
+					categories[vars_count].metric = atoi(cat_metric->value);
+					categories[vars_count].variable_name = var_name->value;
+					categories[vars_count].variable_value = var_val->value;
+					vars_count++;
+				}
+				p++;
+			}
+			qsort(categories, vars_count, sizeof(struct category_and_metric),
+				  compare_categories);
+
+			for (i = 0; i < vars_count; i++) {
+				if (strcmp(categories[i].variable_name, "#include") == 0) {
+					if (!ast_config_internal_load(categories[i].variable_value, cfg, 0)) {
+						break;
+						return NULL;
+					}
+				} else {
+					if (!last_category || strcmp(last_category, categories[i].name) != 0 ||
+						last_category_metric != categories[i].metric) {
+						cur_cat = ast_category_new(categories[i].name);
+						if (!cur_cat) {
+							ast_log(LOG_WARNING, "Out of memory!\n");
+							break;
+						}
+						last_category = categories[i].name;
+						last_category_metric = categories[i].metric;
+						ast_category_append(cfg, cur_cat);
+					}
+					new_v = ast_variable_new(categories[i].variable_name,
+							 categories[i].variable_value);
+					ast_variable_append(cur_cat, new_v);
+				}
+			}
+			free(vars);
+			free(categories);
+		} else {
+			ast_log(LOG_WARNING, "Could not find config '%s' in database.\n", file);
+		}
+	}
+	return cfg;
+
+}
+
+static struct ast_config_engine ldap_engine = {
+	.name = "ldap",
+	.load_func = config_ldap,
+	.realtime_func = realtime_ldap,
+	.realtime_multi_func = realtime_multi_ldap,
+	.update_func = NULL
+};
+
+static int load_module(void *mod)
+{
+	
+	if (parse_config() < 0) {
+		ast_log(LOG_NOTICE, "Cannot load LDAP RealTime driver.\n");
+		return 0;
+	}
+
+	ast_mutex_lock(&ldap_lock);
+
+	if (!ldap_reconnect()) 
+		ast_log(LOG_WARNING, "Couldn't establish connection. Check debug.\n");
+
+	ast_config_engine_register(&ldap_engine);
+	if (option_verbose) 
+		ast_verbose("LDAP RealTime driver loaded.\n");
+	ast_cli_register(&cli_realtime_ldap_status);
+
+	ast_mutex_unlock(&ldap_lock);
+
+	return 0;
+}
+
+static int unload_module(void *mod)
+{
+	/* Aquire control before doing anything to the module itself. */
+	ast_mutex_lock(&ldap_lock);
+
+	table_configs_free();
+
+	if (ldapConn) {
+		ldap_unbind_s(ldapConn);
+		ldapConn = NULL;
+	}
+	ast_cli_unregister(&cli_realtime_ldap_status);
+	ast_config_engine_deregister(&ldap_engine);
+	if (option_verbose)
+		ast_verbose("LDAP RealTime unloaded.\n");
+
+	STANDARD_HANGUP_LOCALUSERS;
+
+	/* Unlock so something else can destroy the lock. */
+	ast_mutex_unlock(&ldap_lock);
+
+	return 0;
+}
+
+static int reload(void *mod)
+{
+	/* Aquire control before doing anything to the module itself. */
+	ast_mutex_lock(&ldap_lock);
+
+	if (ldapConn) {
+		ldap_unbind_s(ldapConn);
+		ldapConn = NULL;
+	}
+	
+	if (parse_config() < 0) {
+		ast_log(LOG_NOTICE, "Cannot reload LDAP RealTime driver.\n");
+		return 0;
+	}		
+
+	if (!ldap_reconnect()) 
+		ast_log(LOG_WARNING, "Couldn't establish connection. Check debug.\n");
+
+	ast_verbose(VERBOSE_PREFIX_2 "LDAP RealTime reloaded.\n");
+
+	/* Done reloading. Release lock so others can now use driver. */
+	ast_mutex_unlock(&ldap_lock);
+
+	return 0;
+}
+
+int parse_config(void)
+{
+	struct ast_config *config;
+	char *s;
+
+	config = ast_config_load(RES_CONFIG_LDAP_CONF);
+
+	if (!config) {
+		ast_log(LOG_WARNING, "Cannot load configuration %s\n", RES_CONFIG_LDAP_CONF);
+		return -1;
+	}
+
+	if (!(s = ast_variable_retrieve(config, "_general", "dbuser"))) {
+		ast_log(LOG_WARNING, "No database user found, anonymous binding as default.\n");
+		dbuser[0] = '\0';
+	} else 
+		ast_copy_string(dbuser, s, sizeof(dbuser));
+	
+	if (!(s = ast_variable_retrieve(config, "_general", "dbpass"))) {
+		ast_log(LOG_WARNING, "No database password found, using 'asterisk' as default.\n");
+		ast_copy_string(dbpass, "asterisk", sizeof(dbpass) - 1);
+	} else
+		ast_copy_string(dbpass, s, sizeof(dbpass));
+
+	if (!(s = ast_variable_retrieve(config, "_general", "dbhost"))) {
+		ast_log(LOG_ERROR, "No directory host found.\n");
+		dbhost[0] = '\0';
+	} else 
+		ast_copy_string(dbhost, s, sizeof(dbhost));
+	
+	if (!(s = ast_variable_retrieve(config, "_general", "dbbasedn"))) {
+		ast_log(LOG_ERROR, "No LDAP base dn found, using 'asterisk' as default.\n");
+		dbbasedn[0] = '\0';
+	} else 
+		ast_copy_string(dbbasedn, s, sizeof(dbbasedn));
+	
+	if (!(s = ast_variable_retrieve(config, "_general", "dbport"))) {
+		ast_log(LOG_WARNING, "No directory port found, using 389 as default.\n");
+		dbport = 389;
+	} else 
+		dbport = atoi(s);
+	if (!(s = ast_variable_retrieve(config, "_general", "ldapversion"))) {
+		ldapversion = LDAP_VERSION3;
+	} else 
+		switch (atoi(s)) {
+			case '2':
+				ldapversion = LDAP_VERSION2;
+				break;
+			case '3':	
+			default:
+				ldapversion = LDAP_VERSION3;
+				break;
+		}
+	
+	if (!(s = ast_variable_retrieve(config, "_general", "auth"))) {	
+		if (!strcmp(s, "sasl")) {
+			ast_log(LOG_NOTICE, "enabling ldap tls authencation\n");
+			ldapauthtype = LDAP_AUTH_SASL;	
+		} else {
+			ast_log(LOG_NOTICE, "using the default simple authencation\n");
+			ldapauthtype = LDAP_AUTH_SIMPLE;
+		}
+	}
+	
+	table_configs_free();
+	
+	char *category_name = NULL;
+	while ((category_name = ast_category_browse(config, category_name))) {
+		int is_general = (strcasecmp(category_name, "_general") == 0);
+		struct ast_variable *var = ast_variable_browse(config, category_name);
+		
+		if (option_debug)
+			ast_log(LOG_DEBUG, "found: category_name=%s\n", category_name);
+		if (var) {
+			struct ldap_table_config *table_config =
+				table_config_for_table_name(category_name);
+			if (!table_config) {
+				table_config = table_config_new(category_name);
+				if (table_configs)
+					table_config->next = table_configs;
+				table_configs = table_config;
+				if (is_general)
+					base_table_config = table_config;
+			}
+			while (var) {
+				if (option_debug)
+					ast_log(LOG_DEBUG, "found: category_name=%s var->name=%s var->value=%s\n",
+						category_name, var->name, var->value);
+				if (strcasecmp(var->name, "attribute") == 0) 
+					ldap_table_config_add_attribute(table_config, var->value);
+				else if (strcasecmp(var->name, "additionalFilter") == 0)
+					table_config->additional_filter = strdup(var->value);
+				var = var->next;
+			}
+		}
+	}
+
+	ast_config_destroy(config);
+	if (option_debug > 3) {
+		ast_log(LOG_DEBUG, "LDAP RealTime Host: %s\n", dbhost);
+		ast_log(LOG_DEBUG, "LDAP RealTime Port: %i\n", dbport);
+		ast_log(LOG_DEBUG, "LDAP RealTime User: %s\n", dbuser);
+		ast_log(LOG_DEBUG, "LDAP RealTime Password: %s\n", dbpass);
+		ast_log(LOG_DEBUG, "LDAP RealTime BaseDN: %s\n", dbbasedn);
+	}
+	return 1;
+}
+
+static const char *description(void)
+{
+	return res_config_ldap_desc;
+}
+
+static const char *key(void)
+{
+	return ASTERISK_GPL_KEY;
+}
+
+static int ldap_reconnect(void)
+{
+	/* mutex lock should have been locked before calling this function. */
+	int bind_result = 0;
+
+	if (ldapConn) {
+		if (option_debug > 1)
+			ast_log(LOG_DEBUG, "Everything seems fine.\n");
+		return 1;
+	}
+
+	if (!dbhost) {
+		ast_log(LOG_ERROR, "Not enough parameters to connect to ldap database\n");
+		return 0;
+	}
+
+	if (!(ldapConn = ldap_init(dbhost, dbport))) {
+		ast_log(LOG_ERROR, "Failed to init ldap connection to %s. Check debug for more info.\n", dbhost);
+		return 0;
+	} 
+
+	ldap_set_option(ldapConn, LDAP_OPT_PROTOCOL_VERSION, &ldapversion);
+	
+	
+	if (dbuser && *dbuser) {
+		if (ldapauthtype == LDAP_AUTH_SIMPLE) {
+			bind_result = ldap_simple_bind_s(ldapConn, dbUser, dbPass);
+		else {
+			/* connect via sasl */
+		}
+	} else {
+		
+		if (ldapauthtype == LDAP_AUTH_SIMPLE) {
+			bind_result = ldap_simple_bind_s(ldapConn, NULL, NULL);
+		} else {
+			/* connect via sasl */
+		}
+	}
+	
+	if (bind_result == LDAP_SUCCESS) {
+		if (option_debug > 1)
+			ast_log(LOG_DEBUG, "Successfully connected to database.\n");
+		connect_time = time(NULL);
+		return 1;
+	} else {
+		ast_log(LOG_WARNING, "bind failed: %s\n", ldap_err2string(bind_result));
+		ldap_unbind(ldapConn);
+		ldapConn = NULL;
+		return 0;
+	}
+}
+
+static int realtime_ldap_status(int fd, int argc, char **argv)
+{
+	char status[256], status2[100] = "";
+	int ctime = time(NULL) - connect_time;
+
+	if (!ldapConn)
+		return RESULT_FAILURE;
+	if (dbhost) 
+		snprintf(status, 255, "Connected to %s, port %d baseDN %s", dbhost, dbport, dbbasedn);
+
+	if (dbuser && *dbuser)
+		snprintf(status2, 99, " with username %s", dbuser);
+
+	if (ctime > 31536000) {
+		ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
+				status, status2, ctime / 31536000,
+				(ctime % 31536000) / 86400, (ctime % 86400) / 3600,
+				(ctime % 3600) / 60, ctime % 60);
+	} else if (ctime > 86400) {
+		ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n",
+				status, status2, ctime / 86400, (ctime % 86400) / 3600,
+				(ctime % 3600) / 60, ctime % 60);
+	} else if (ctime > 3600) {
+		ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n",
+				status, status2, ctime / 3600, (ctime % 3600) / 60,
+				ctime % 60);
+	} else if (ctime > 60) {
+		ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2,
+					ctime / 60, ctime % 60);
+	} else {
+		ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
+	}
+
+	return RESULT_SUCCESS;
+}
+
+STD_MOD(MOD_1, reload, NULL, NULL);

Propchange: team/anthonyl/5768-ldap-redux/res/res_config_ldap.c
------------------------------------------------------------------------------
    svn::eol-style = native

Propchange: team/anthonyl/5768-ldap-redux/res/res_config_ldap.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/anthonyl/5768-ldap-redux/res/res_config_ldap.c
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: team/anthonyl/5768-ldap-redux/res/res_config_ldap.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain



More information about the svn-commits mailing list