[asterisk-commits] branch oej/test-this-branch r12468 - in /team/oej/test-this-branch: ./ config...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Wed Mar 8 12:37:47 MST 2006


Author: oej
Date: Wed Mar  8 13:37:42 2006
New Revision: 12468

URL: http://svn.digium.com/view/asterisk?rev=12468&view=rev
Log:
Issue #5768 - Realtime LDAP driver. (mguesdon)

Added:
    team/oej/test-this-branch/configs/res_ldap.conf.sample   (with props)
    team/oej/test-this-branch/doc/rt-ldap.txt   (with props)
    team/oej/test-this-branch/res/res_config_ldap.c   (with props)
Modified:
    team/oej/test-this-branch/README.test-this-branch
    team/oej/test-this-branch/res/Makefile

Modified: team/oej/test-this-branch/README.test-this-branch
URL: http://svn.digium.com/view/asterisk/team/oej/test-this-branch/README.test-this-branch?rev=12468&r1=12467&r2=12468&view=diff
==============================================================================
--- team/oej/test-this-branch/README.test-this-branch (original)
+++ team/oej/test-this-branch/README.test-this-branch Wed Mar  8 13:37:42 2006
@@ -43,6 +43,9 @@
 - Show threads CLI command (rizzo, #6053)
 - IFMODULE dialplan function (oej, #6671)
 - End CDR before 'h' extension (russellb, #6193)
+- LDAP realtime driver (mguesdon, #5768)
+  See doc/rt_ldap.txt!
+
 
 Coming here soon:
 - siptransfer: Improved SIP transfer support
@@ -50,6 +53,7 @@
 All of these exist in the bug tracker. Please report your findings
 in each open issue report, so that we get the feedback for each
 patch. Your opinion, good or bad, is important for us.
+http://bugs.digium.com
 
 * Open BUGS (fixes welcome!)
 ----------------------------

Added: team/oej/test-this-branch/configs/res_ldap.conf.sample
URL: http://svn.digium.com/view/asterisk/team/oej/test-this-branch/configs/res_ldap.conf.sample?rev=12468&view=auto
==============================================================================
--- team/oej/test-this-branch/configs/res_ldap.conf.sample (added)
+++ team/oej/test-this-branch/configs/res_ldap.conf.sample Wed Mar  8 13:37:42 2006
@@ -1,0 +1,134 @@
+; Sample Asterisk config file for res_config_ldap
+; in extconfig.conf you can use it like this:
+; sipusers => ldap,"dc=myDomain,dc=myDomainExt",sip
+; sippeers => ldap,"dc=myDomain,dc=myDomainExt",sip
+; extensions => ldap,"dc=myDomain,dc=myDomainExt",extensions
+; sip.conf => ldap,"dc=myDomain,dc=myDomainExt",config
+
+
+[_general]
+dbhost=192.168.1.1,ldap.mydomain.com	; LDAP host(s)
+dbbasedn=MyRootDN	; Base DN
+dbpass=MyPassword	; Bind password
+dbuser=MyDN		; Bind DN
+
+; Configuration Table
+[config]
+; Attributes mapping (asterisk variable name => ldap attribute name)
+attribute = filename => oxyPBXConfigFilename
+attribute = category => oxyPBXConfigCategory
+attribute = variable_name => oxyPBXConfigVariableName
+attribute = variable_value => oxyPBXConfigVariableValue
+attribute = cat_metric => oxyPBXConfigCategoryMetric
+attribute = commented => oxyPBXConfigCommented
+
+; addtional filter
+additionalFilter=(objectClass=oxyPBXConfig)
+
+; Extensions Table
+[extensions]
+attribute = context  =>  oxyPBXExtensionContext
+attribute = exten  =>  oxyPBXExtensionExten
+attribute = priority => oxyPBXExtensionPriority
+attribute = app => oxyPBXExtensionApplication
+attribute = appdata => oxyPBXExtensionApplicationData
+additionalFilter=(objectClass=oxyPBXExtension)
+
+// Sip Users Table
+[sip]
+attribute = name => uid
+attribute = amaflags => oxyPBXAccountAMAFlags
+attribute = callgroup => oxyPBXAccountCallGroup
+attribute = callerid => oxyPBXAccountCallerID
+attribute = canreinvite => oxyPBXAccountCanReinvite
+attribute = context => oxyPBXAccountContext
+attribute = dtmfmode => oxyPBXAccountDTMFMode
+attribute = fromuser => oxyPBXAccountFromUser
+attribute = fromdomain => oxyPBXAccountFromDomain
+attribute = fullcontact => oxyPBXAccountFullContact
+attribute = fullcontact => gecos
+attribute = host => oxyPBXAccountHost
+attribute = insecure => oxyPBXAccountInsecure
+attribute = mailbox => oxyPBXAccountMailbox
+attribute = md5secret => realmedPassword
+attribute = nat => oxyPBXAccountNAT
+attribute = deny => oxyPBXAccountDeny
+attribute = permit => oxyPBXAccountPermit
+attribute = pickupgroup => oxyPBXAccountPickupGroup
+attribute = port => oxyPBXAccountPort
+attribute = qualify => oxyPBXAccountQualify
+attribute = restrictcid => oxyPBXAccountRestrictCID
+attribute = rtptimeout => oxyPBXAccountRTPTimeout
+attribute = rtpholdtimeout => oxyPBXAccountRTPHoldTimeout
+attribute = type => oxyPBXAccountType
+attribute = disallow => oxyPBXAccountDisallowedCodec
+attribute = allow => oxyPBXAccountAllowedCodec
+attribute = MusicOnHold => oxyPBXAccountMusicOnHold
+attribute = regseconds => oxyPBXAccountExpirationTimestamp
+attribute = regcontext => oxyPBXAccountRegistrationContext
+attribute = regexten => oxyPBXAccountRegistrationExten
+attribute = CanCallForward => oxyPBXAccountCanCallForward
+additionalFilter=(objectClass=oxyPBXAccountSIP)
+
+; IAX Users Table
+[iax]
+attribute = amaflags => oxyPBXAccountAMAFlags
+attribute = callerid => oxyPBXAccountCallerID
+attribute = context => oxyPBXAccountContext
+attribute = fullcontact => oxyPBXAccountFullContact
+attribute = fullcontact => gecos
+attribute = host => oxyPBXAccountHost
+attribute = mailbox => oxyPBXAccountMailbox
+attribute = md5secret => realmedPassword
+attribute = deny => oxyPBXAccountDeny
+attribute = permit => oxyPBXAccountPermit
+attribute = port => oxyPBXAccountPort
+attribute = qualify => oxyPBXAccountQualify
+attribute = type => oxyPBXAccountType
+attribute = disallow => oxyPBXAccountDisallowedCodec
+attribute = allow => oxyPBXAccountAllowedCodec
+attribute = regseconds => oxyPBXAccountExpirationTimestamp
+attribute = regcontext => oxyPBXAccountRegistrationContext
+attribute = regexten => oxyPBXAccountRegistrationExten
+attribute = notransfer => oxyPBXAccountNoTransfer
+additionalFilter=(objectClass=oxyPBXAccountIAX)
+
+; A Test Family
+[testfamily]
+attribute = MyUSERID => uid
+additionalFilter=(objectClass=*)
+
+[accounts]
+attribute = amaflags => oxyPBXAccountAMAFlags
+attribute = callgroup => oxyPBXAccountCallGroup
+attribute = callerid => oxyPBXAccountCallerID
+attribute = canreinvite => oxyPBXAccountCanReinvite
+attribute = context => oxyPBXAccountContext
+attribute = dtmfmode => oxyPBXAccountDTMFMode
+attribute = fromuser => oxyPBXAccountFromUser
+attribute = fromdomain => oxyPBXAccountFromDomain
+attribute = fullcontact => oxyPBXAccountFullContact
+attribute = fullcontact => gecos
+attribute = host => oxyPBXAccountHost
+attribute = insecure => oxyPBXAccountInsecure
+attribute = mailbox => oxyPBXAccountMailbox
+attribute = md5secret => realmedPassword
+attribute = nat => oxyPBXAccountNAT
+attribute = deny => oxyPBXAccountDeny
+attribute = permit => oxyPBXAccountPermit
+attribute = pickupgroup => oxyPBXAccountPickupGroup
+attribute = port => oxyPBXAccountPort
+attribute = qualify => oxyPBXAccountQualify
+attribute = restrictcid => oxyPBXAccountRestrictCID
+attribute = rtptimeout => oxyPBXAccountRTPTimeout
+attribute = rtpholdtimeout => oxyPBXAccountRTPHoldTimeout
+attribute = type => oxyPBXAccountType
+attribute = disallow => oxyPBXAccountDisallowedCodec
+attribute = allow => oxyPBXAccountAllowedCodec
+attribute = MusicOnHold => oxyPBXAccountMusicOnHold
+attribute = regseconds => oxyPBXAccountExpirationTimestamp
+attribute = regcontext => oxyPBXAccountRegistrationContext
+attribute = regexten => oxyPBXAccountRegistrationExten
+attribute = CanCallForward => oxyPBXAccountCanCallForward
+additionalFilter=(objectClass=oxyPBXAccount)
+

Propchange: team/oej/test-this-branch/configs/res_ldap.conf.sample
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/oej/test-this-branch/configs/res_ldap.conf.sample
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/oej/test-this-branch/configs/res_ldap.conf.sample
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/oej/test-this-branch/doc/rt-ldap.txt
URL: http://svn.digium.com/view/asterisk/team/oej/test-this-branch/doc/rt-ldap.txt?rev=12468&view=auto
==============================================================================
--- team/oej/test-this-branch/doc/rt-ldap.txt (added)
+++ team/oej/test-this-branch/doc/rt-ldap.txt Wed Mar  8 13:37:42 2006
@@ -1,0 +1,65 @@
+Asterisk Realtim LDAP Driver
+---------------------------
+
+With this driver Asterisk can retrieve information from a LDAP drectory, including 
+sip/iax users, extensions and configuration.
+
+See configs/res_ldap.conf.sample for a configuration file sample
+
+
+Here is a LDAP dif sample:
+
+# Base SIP Phones Entry
+dn: uid=phone-base,dc=myDomain,dc=myDomainExt
+objectClass: top
+objectClass: oxyPBXAccount
+objectClass: oxyPBXAccountSIP
+uid: phone-base
+oxyPBXAccountAccountingCode: baseacccode
+oxyPBXAccountHost: dynamic
+preferredLanguage: FR
+oxyPBXAccountAMAFlags: billing
+oxyPBXAccountContext: ldaptest
+
+
+# A Phone. realmedPassword md5 hash should be the result of 
+#  echo -n "UID:SIPRealm:Password" | md5sum
+dn: uid=phone-test,dc=myDomain,dc=myDomainExt
+objectClass: top
+objectClass: oxyPBXAccount
+objectClass: oxyPBXAccountSIP
+uid: phone-test
+oxyPBXAccountAccountingCode: acc-phone-base
+oxyPBXAccountFullContact: Noone <1234>
+oxyPBXAccountCallerID: 1234
+oxyPBXAccountBaseDN: uid=phone-base,dc=myDomain,dc=myDomainExt
+realmedPassword: {MD5}f67965da780bf9c70d6e337f938cee6f
+
+
+# extensions, 
+dn: ou=extensions,dc=myDomain,dc=myDomainExt
+ou: extensions
+objectClass: top
+objectClass: organizationalUnit
+
+# Extension 100 Priority 1 in context ldaptest
+dn: cn=100-1,ou=extensions,dc=myDomain,dc=myDomainExt
+oxyPBXExtensionApplication: NoOp
+oxyPBXExtensionApplicationData: TEST LDAP
+objectClass: top
+objectClass: oxyPBXExtension
+oxyPBXExtensionExten: 100
+oxyPBXExtensionContext: ldaptest
+cn: 100-1
+oxyPBXExtensionPriority: 1
+
+# Extension 100 Priority 1 in context ldaptest
+dn: cn=100-2,ou=extensions,dc=myDomain,dc=myDomainExt
+objectClass: top
+objectClass: oxyPBXExtension
+oxyPBXExtensionExten: 100
+oxyPBXExtensionContext: ldaptest
+cn: 100-2
+oxyPBXExtensionPriority: 2
+oxyPBXExtensionApplication: hangup
+

Propchange: team/oej/test-this-branch/doc/rt-ldap.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/oej/test-this-branch/doc/rt-ldap.txt
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/oej/test-this-branch/doc/rt-ldap.txt
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/oej/test-this-branch/res/Makefile
URL: http://svn.digium.com/view/asterisk/team/oej/test-this-branch/res/Makefile?rev=12468&r1=12467&r2=12468&view=diff
==============================================================================
--- team/oej/test-this-branch/res/Makefile (original)
+++ team/oej/test-this-branch/res/Makefile Wed Mar  8 13:37:42 2006
@@ -41,6 +41,16 @@
   ifeq ($(findstring BSD,$(OSARCH)),BSD)
     MODS:=$(filter-out res_snmp.so,$(MODS))
   endif
+endif
+
+ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/ldap.h),)
+  MODS:=$(filter-out res_ldap.so,$(MODS))
+endif
+
+ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/net-snmp/net-snmp-config.h),)
+  MODS:=$(filter-out res_snmp.so,$(MODS))
+else
+  SNMP_LDLIBS+=$(shell net-snmp-config --agent-libs)
 endif
 
 ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/net-snmp/net-snmp-config.h),)
@@ -128,6 +138,10 @@
 res_snmp.so: res_snmp.o snmp/agent.o
 	$(CC) $(SOLINK) ${SNMP_LDFLAGS} -o $@ ${CYGSOLINK} res_snmp.o snmp/agent.o ${CYGSOLIB} ${SNMP_LDLIBS}
 
+res_config_ldap.so: res_config_ldap.o
+	$(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lldap
+
+
 ifneq ($(wildcard .depend),)
   include .depend
 endif

Added: team/oej/test-this-branch/res/res_config_ldap.c
URL: http://svn.digium.com/view/asterisk/team/oej/test-this-branch/res/res_config_ldap.c?rev=12468&view=auto
==============================================================================
--- team/oej/test-this-branch/res/res_config_ldap.c (added)
+++ team/oej/test-this-branch/res/res_config_ldap.c Wed Mar  8 13:37:42 2006
@@ -1,0 +1,1292 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2005, Oxymium sarl
+ * 
+ * Manuel Guesdon <mguesdon at oxymium.net> - LDAP RealTime Driver Author/Adaptor
+ *
+ * res_config_ldap.c <LDAP plugin for RealTime configuration engine>
+ *
+ * v0.5   - (11-16-2005) - Initial version based
+ * v0.7   - (03-08-2006) - Fixes, confg file changes
+ */
+
+#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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <ldap.h>
+#include <stdio.h>
+
+static char *res_config_ldap_desc = "LDAP RealTime Configuration Driver";
+AST_MUTEX_DEFINE_STATIC(ldap_lock);
+#define RES_CONFIG_LDAP_CONF "res_ldap.conf"
+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 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);
+
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+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
+};
+
+// Log variable
+static void ast_variable_log(const char *log_prefix, struct ast_variable *var)
+{
+	ast_log(LOG_DEBUG, "LOG Variable: %s\n", log_prefix);
+	while (var) {
+		ast_log(LOG_DEBUG, "%s => %s\n", var->name, var->value);
+		var = var->next;
+	};
+	ast_log(LOG_DEBUG, "END LOG Variable: %s\n", log_prefix);
+}
+
+// Log config
+static void ast_config_log(const char *log_prefix, struct ast_config *config)
+{
+	ast_log(LOG_DEBUG, "LOG Config: %s\n", log_prefix);
+	char *category_name = NULL;
+	while ((category_name = ast_category_browse(config, category_name))) {
+		ast_log(LOG_DEBUG, "========= category: %s ==========\n",
+				category_name);
+		struct ast_variable *var = ast_variable_browse(config, category_name);
+		if (var) {
+			while (var) {
+				ast_log(LOG_DEBUG, "%s=%s\n", var->name, var->value);
+				var = var->next;
+			}
+		}
+	}
+	ast_log(LOG_DEBUG, "END LOG Config: %s\n", log_prefix);
+};
+
+// Table configuration
+struct ldap_table_config {
+	char *table_name;			// table name
+	char *additional_filter;	// additional filter       
+	struct ast_variable *attributes;	// attribute names conversion
+	struct ldap_table_config *next;	// next table
+};
+
+// Should be locked before using it
+static struct ldap_table_config *table_configs = NULL;
+static struct ldap_table_config *base_table_config = NULL;
+
+// Create a new table_config
+static struct ldap_table_config *table_config_new(const char *table_name)
+{
+	struct ldap_table_config *p = malloc(sizeof(struct ldap_table_config));
+	memset(p, 0, sizeof(struct ldap_table_config));
+	if (table_name)
+		p->table_name = strdup(table_name);
+	return p;
+};
+
+// 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;
+};
+
+/*
+// add attributes to table config - Should be locked before using it
+static void ldap_table_config_add_attributes(struct ldap_table_config* table_config,const char* attributes_string)
+{
+  if (attributes_string && *attributes_string)
+    {      
+      char* string=strdup(attributes_string);
+      char* string_end=string+strlen(string);
+      char* start=string;
+      //ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: start: %s\n",start);
+      while(start<string_end)
+        {
+          char* conv_sep=NULL;
+          //ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: start: %s\n",start);
+          char* attr_sep=strchr(start,',');
+          //ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: attr_sep: %s\n",attr_sep);
+          if (!attr_sep)
+            attr_sep=string_end;
+          else
+            *attr_sep='\0';
+          conv_sep=strchr(start,'=');
+          //ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: conv_sep: %s\n",conv_sep);
+          if (conv_sep>=attr_sep)
+            conv_sep=NULL;
+
+          if (!conv_sep)
+            {
+       	 ast_log(LOG_WARNING, "LDAP RealTime: Missing '=' in attributes conversion list: %s\n",attributes_string);
+            }
+          else
+            {
+              *conv_sep='\0';
+              struct ast_variable* var=ast_variable_new(start,conv_sep+1);
+              //ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: VAR %s => %s\n",var->name,var->value);
+              if (table_config->attributes)
+       	 var->next=table_config->attributes;
+              table_config->attributes=var;
+            }
+          start=attr_sep+1;
+        };
+      free(string);
+    };
+};
+*/
+// 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;
+		ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: start: %s\n",
+				start);
+		char *p = strstr(start, "=>");
+		if (!p) {
+			ast_log(LOG_WARNING,
+					"LDAP RealTime: 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 (*start == '\0') {
+				ast_log(LOG_WARNING,
+						"LDAP RealTime: Empty variable name in attribute: %s in %s\n",
+						attribute_string, table_config->table_name);
+			} else if (*value == '\0') {
+				ast_log(LOG_WARNING,
+						"LDAP RealTime: 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);
+				//ast_log(LOG_DEBUG, "LDAP RealTime: Add attribute: VAR %s => %s\n",var->name,var->value);
+				if (table_config->attributes)
+					var->next = table_config->attributes;
+				table_config->attributes = var;
+				ast_log(LOG_DEBUG,
+						"LDAP RealTime (%d): Added attribute in %s: %s -> %s\n",
+						__LINE__, table_config->table_name, start, value);
+			}
+		};
+		free(string);
+	};
+};
+
+// 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;
+}
+
+// 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;
+};
+
+// 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;
+};
+
+// 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;
+}
+
+// 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) {
+		} else {
+			char **v = values;
+
+			while (*v) {
+				char *value = *v;
+				ast_log(LOG_DEBUG,
+						"LDAP RealTime (%d): attribute_name: %s value: %s\n",
+						__LINE__, attribute_name, value);
+				if (is_realmed_password_attribute) {
+					if (strncasecmp(value, "{md5}", 5) == 0)
+						value += 5;
+					else
+						value = NULL;
+					ast_log(LOG_DEBUG, "LDAP RealTime (%d): md5: %s\n",
+							__LINE__, 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);
+}
+
+// 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, "LDAP RealTime: Not table config\n");
+		return NULL;
+	} else {
+		struct ast_variable *var = NULL;
+		int result = -1;
+		LDAPMessage *ldap_result = NULL;
+		int tries = 0;
+
+		ast_log(LOG_DEBUG, "LDAP RealTime (%d): ldap_loadentry dn=%s\n",
+				__LINE__, 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,
+						"LDAP RealTime: 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,
+					"LDAP RealTime: Failed to query database. Check debug for more info.\n");
+			ast_log(LOG_DEBUG, "LDAP RealTime: dn=%s\n", dn);
+			ast_log(LOG_DEBUG, "LDAP RealTime: 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;
+				ast_log(LOG_DEBUG, "LDAP RealTime (%d): num_entry: %d\n",
+						__LINE__, 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,
+							"LDAP RealTime: More than one entry for dn=%s. Take only 1st one\n",
+							dn);
+				}
+			} else {
+				ast_log(LOG_WARNING,
+						"LDAP RealTime: Could not find any entry dn=%s.\n",
+						dn);
+			}
+		}
+		ldap_msgfree(ldap_result);
+
+		ast_variable_log("LDAP RealTime: Found Entry", var);
+		return var;
+	}
+}
+
+// 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 = malloc(MAXRESULT);
+		if (!ret_string) {
+			ast_log(LOG_WARNING, "Out of memory\n");
+		} else {
+			memset(ret_string, 0, MAXRESULT);
+			pbx_substitute_variables_helper(channel, string, ret_string, MAXRESULT - 1);
+		}
+	}
+	ast_log(LOG_DEBUG, "LDAP RealTime: substituted: string: '%s' => '%s' \n",
+			string, ret_string);
+	return ret_string;
+}
+
+// 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 (*cbasedn != '\0') {
+				int len = strlen(cbasedn);
+				if (cbasedn[len - 1] == '"')
+					cbasedn[len - 1] = '\0';
+
+			}
+		}
+		p = cbasedn;
+		while (*p) {
+			if (*p == '|')
+				*p = ',';
+			p++;
+		};
+	}
+	ast_log(LOG_DEBUG, "LDAP RealTime (%d): basedn: '%s' => '%s' \n",
+			__LINE__, basedn, cbasedn);
+	return cbasedn;
+}
+
+// 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);
+}
+
+// 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;
+};
+
+// Append a name=value filter string. The filter string can grow.
+// 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");
+	//ast_log(LOG_DEBUG, "LDAP RealTime: 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);
+}
+
+/* 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)
+{
+	ast_log(LOG_DEBUG,
+			"LDAP RealTime (%d): realtime_ldap_base: basedn: %s table_name: %s\n",
+			__LINE__, basedn, table_name);
+	struct ast_variable **vars = NULL;
+	if (!table_name) {
+		ast_log(LOG_WARNING, "LDAP RealTime: No table_name specified.\n");
+	} else {
+		const char *newparam = NULL;
+		const char *newval = 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,
+					"LDAP RealTime: 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()) {
+			} else {
+				struct ldap_table_config *table_config = NULL;
+
+				table_config = table_config_for_table_name(table_name);
+				if (!table_config) {
+					ast_log(LOG_WARNING,
+							"LDAP RealTime: 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, ")");
+
+					ast_log(LOG_DEBUG, "LDAP RealTime (%d): filter: %s\n",
+							__LINE__, 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,
+									"LDAP RealTime: 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,
+								"LDAP RealTime: Failed to query database. Check debug for more info.\n");
+						ast_log(LOG_WARNING, "LDAP RealTime: Query: %s\n",
+								filter);
+						ast_log(LOG_WARNING,
+								"LDAP RealTime: 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 =
+								malloc(sizeof(struct ast_variable *) *
+									   (num_entry + 1));
+							memset(vars, 0,
+								   sizeof(struct ast_variable *) *
+								   (num_entry + 1));
+							ast_log(LOG_DEBUG,
+									"LDAP RealTime (%d): num_entry: %d\n",
+									__LINE__, 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);
+								ast_variable_log("LDAP RealTime: Found Entry",
+												 vars[entry_index]);
+								ldap_entry =
+									ldap_next_entry(ldapConn, ldap_entry);
+								ast_log(LOG_DEBUG,
+										"LDAP RealTime (%d): next entry: %p\n",
+										__LINE__, ldap_entry);
+							};
+						} else {
+							ast_log(LOG_WARNING,
+									"LDAP RealTime: 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);
+										ast_log(LOG_DEBUG,
+												"LDAP RealTime (%d): Loaded base: %s\n",
+												__LINE__, tmp->value);
+										ast_variable_log("Base Variables",
+														 base_var);
+										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;
+}
+
+// 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;
+}
+
+// See Asterisk doc
+static struct ast_variable *realtime_ldap(const char *basedn,
+					  const char *table_name, va_list ap)
+{
+	ast_log(LOG_DEBUG,
+			"LDAP RealTime (%d): realtime_ldap: basedn: %s table_name: %s\n",
+			__LINE__, basedn, table_name);
+	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);
+	}
+	ast_variable_log("LDAP RealTime: ==> Found Entry", var);
+	return var;
+}
+
+// See Asterisk doc
+static struct ast_config *realtime_multi_ldap(const char *basedn,
+					      const char *table_name,
+					      va_list ap)
+{
+	ast_log(LOG_DEBUG,
+			"LDAP RealTime (%d): realtime_multi_ldap: basedn: %s table_name: %s\n",
+			__LINE__, basedn, table_name);
+	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;
+
+}
+struct category_and_metric {
+	char *name;
+	int metric;
+	char *variable_name;
+	char *variable_value;
+};
+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);
+}
+
+// See Asterisk doc
+static struct ast_config *config_ldap(const char *basedn,
+				      const char *table_name,
+				      const char *file,
+				      struct ast_config *cfg)
+{
+	ast_log(LOG_DEBUG,
+			"LDAP RealTime (%d): config_ldap: basedn: %s table_name: %s\n",
+			__LINE__, basedn, table_name);
+	if (!file || !strcmp(file, RES_CONFIG_LDAP_CONF)) {
+		ast_log(LOG_WARNING, "LDAP RealTime: 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 =

[... 384 lines stripped ...]


More information about the asterisk-commits mailing list