--- Makefile.orig 2007-06-25 02:13:15.000000000 +0200 +++ Makefile 2007-06-25 02:16:44.000000000 +0200 @@ -18,6 +18,7 @@ CFLAGS+=-fPIC CFLAGS+=-D_GNU_SOURCE +LDAPCFLAGS=-DLDAP_DEPRECATED=1 INSTALL=install INSTALL_PREFIX= ASTLIBDIR=$(INSTALL_PREFIX)/usr/lib/asterisk @@ -27,6 +28,7 @@ # MySQL stuff... Autoconf anyone?? # MODS+=$(shell if [ -d /usr/include/mysql ]; then echo "cdr_addon_mysql.so app_addon_sql_mysql.so res_config_mysql.so"; fi) +MODS+=$(shell if [ -f /usr/include/ldap.h ]; then echo "res_config_ldap.so"; fi) CFLAGS+=$(shell if [ -d /usr/include/mysql ]; then echo "-I/usr/include/mysql"; fi) MLFLAGS= MLFLAGS+=$(shell if [ -d /usr/lib64/mysql ]; then echo "-L/usr/lib64/mysql"; fi) @@ -69,6 +71,10 @@ res_config_mysql.so: res_config_mysql.o $(CC) $(SOLINK) -o $@ $< -lmysqlclient -lz $(MLFLAGS) +res_config_ldap.so: res_config_ldap.o + $(CC) $(LDAPCFLAGS) $(SOLINK) -o $@ $< -lldap_r -llber -lz $(MLFLAGS) + + app_addon_sql_mysql.so: app_addon_sql_mysql.o $(CC) $(SOLINK) -o $@ $< -lmysqlclient -lz $(MLFLAGS) --- res_config_ldap.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ res_config_ldap.c 2006-02-20 17:03:02.000000000 +0100 @@ -0,0 +1,1248 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * res_config_ldap.c + * + * Todo: + * - config_ldap for the static stuff + * - useful default base dn + * - testing / bugfixing :-) + * - check for mem leaks + * + * v0.7 - (12-12-05) - test res and strncat cleanup + * v0.6 - (25-11-05) - less verbose output + * v0.5 - (24-11-05) - makefile cleanups + * v0.4 - (22-09-05) - improved reconnect ( Norbert Graf ) + * v0.3 - (18-09-05) - sort by initfield + * - use ldap_initialize() + * v0.2 - (14-09-05) - prepend ast to config values + * - minor bugfixes & cleanups + * v0.1 - (11-09-05) - Initial version based on res_config_mysql from asterisk-addons + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *res_config_ldap_desc = "LDAP RealTime Configuration Driver"; + +AST_MUTEX_DEFINE_STATIC(ldap_lock); +#define RES_CONFIG_LDAP_CONF "res_ldap.conf" +#define QUERY_SIZE 512 +#define OPTION_SIZE 100 +static char ldapuri[80]; +static char ldapuser[80]; +static char ldappass[80]; +static char ldapbasedn[80]; +static int connected; +static time_t connect_time; +static int errorcount = 0; +static int parse_config(void); +static LDAP *ldap_reconnect(); +void ldap_disconnect(LDAP *ldap); +static int realtime_ldap_status(int fd, int argc, char **argv); +int ldap_get_value_s(LDAP *ldap, const char *ldapbasedn,const char filter[QUERY_SIZE], char *attrs[], char *attrvalues[]); + +typedef struct list +{ + char *string[QUERY_SIZE]; + char *members[QUERY_SIZE]; + struct list *next; +} context; + +context *anf = NULL; + +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 }; + +static struct ast_variable *realtime_ldap(const char *database, const char *table, va_list ap) +{ + int numFields; + char *chunk; + char *op; + struct ast_variable *var=NULL, *prev=NULL; + const char *newparam, *newval; + char query[QUERY_SIZE]; + char query2[QUERY_SIZE]; + int i=0; + int j=0; + int search; + LDAPMessage *res, *res2; + BerElement *ber; + char **values; + char *attr; + LDAP *ldap; + + ast_log(LOG_DEBUG, "realtime_ldap\n"); + + if(!table) { + ast_log(LOG_WARNING, "LDAP RealTime: No table 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, "LDAP RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); + return NULL; + } + + /* 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 */ + + if(!strchr(newparam, ' ')) op = "="; else op = ""; + + snprintf(query, sizeof(query), "(&(objectClass=ast%s)(ast%s%s%s))", table, newparam, op, newval); + while((newparam = va_arg(ap, const char *))) { + i++; + newval = va_arg(ap, const char *); + if(!strchr(newparam, ' ')) op = "="; else op = ""; + snprintf(query + strlen(query), sizeof(query) - strlen(query), "(ast%s%s%s))", newparam, op, newval); + } + va_end(ap); + query2[0] = '\0'; + for ( j=0; j 0 ) { + res2 = ldap_first_entry(ldap, res); + while(res2) { + attr = ldap_first_attribute(ldap, res, &ber); + while (attr) { + if ( attr && !strncmp(attr, "ast", 3) ) { + values = ldap_get_values(ldap, res, attr); + attr += 3; + i = 0; + while(values[i]) { + chunk = strsep((char **) &values[i], ";"); + if(chunk && !ast_strlen_zero(ast_strip(chunk))) { + // ast_log(LOG_WARNING, "%s-%s\n", attr, chunk); + if(prev) { + prev->next = ast_variable_new(attr, chunk); + if (prev->next) { + prev = prev->next; + } + } else { + prev = var = ast_variable_new(attr, chunk); + } + } + i++; + } + } + /* ldap_value_free(values); */ + /* ldap_memfree(attr-3); */ + attr = ldap_next_attribute(ldap, res, ber); + } + res = ldap_next_entry(ldap, res2); + ldap_memfree(res2); + res2 = res; + } + } else { + ast_log(LOG_DEBUG, "LDAP RealTime: Could not find any entries in table %s.\n", table); + } + ldap_msgfree(res); + ldap_disconnect(ldap); + ast_mutex_unlock(&ldap_lock); + + return var; +} + +static struct ast_config *realtime_multi_ldap(const char *database, const char *table, va_list ap) +{ + int numFields, i, j, search = 0; + char query[QUERY_SIZE]; + char query2[QUERY_SIZE]; + const char *initfield = NULL; + char *chunk; + char *op; + const char *newparam, *newval; + struct ast_realloca ra; + struct ast_variable *var=NULL; + struct ast_config *cfg = NULL; + struct ast_category *cat = NULL; + LDAPMessage *res, *res2; + BerElement *ber; + char **values; + char *attr; + LDAP *ldap; + + ast_log(LOG_DEBUG, "realtime_multi_ldap\n"); + if(!table) { + ast_log(LOG_WARNING, "LDAP RealTime: No table specified.\n"); + return NULL; + } + + memset(&ra, 0, sizeof(ra)); + + cfg = ast_config_new(); + if (!cfg) { + /* If I can't alloc memory at this point, why bother doing anything else? */ + ast_log(LOG_WARNING, "Out of memory!\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, "LDAP RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); + return NULL; + } + + initfield = ast_strdupa(newparam); + if(initfield && (op = strchr(initfield, ' '))) { + *op = '\0'; + } + + /* 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 */ + + if(!strchr(newparam, ' ')) op = "="; else op = ""; + snprintf(query, sizeof(query), "(&(objectClass=ast%s)(ast%s%s%s))", table, newparam, op, newval); + while((newparam = va_arg(ap, const char *))) { + i++; + newval = va_arg(ap, const char *); + if(!strchr(newparam, ' ')) op = "="; else op = ""; + snprintf(query + strlen(query), sizeof(query) - strlen(query), "(ast%s%s%s))", newparam, op, newval); + } + va_end(ap); + query2[0] = '\0'; + for ( j=0; j 0 ) { + res2 = ldap_first_entry(ldap, res); + while(res2) { + var = NULL; + cat = ast_category_new(""); + if(!cat) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + + attr = ldap_first_attribute(ldap, res, &ber); + while (attr) { + if ( !strncmp(attr, "ast", 3) ) { + values = ldap_get_values(ldap, res, attr); + attr += 3; + i = 0; + while(values[i]) { + chunk = strsep((char **) &values[i], ";"); + if(chunk && !ast_strlen_zero(ast_strip(chunk))) { + if(initfield && !strcmp(initfield, attr)) { + ast_category_rename(cat, chunk); + } + var = ast_variable_new(attr, chunk); + ast_variable_append(cat, var); + } + i++; + } + } + ldap_value_free(values); + attr = ldap_next_attribute(ldap, res, ber); + } + ast_category_append(cfg, cat); + res = ldap_next_entry(ldap, res2); + ldap_memfree(res2); + res2 = res; + } + } else { + ast_log(LOG_DEBUG, "LDAP RealTime: Could not find any rows in table %s.\n", table); + } + + ldap_msgfree(res); + ldap_disconnect(ldap); + ast_mutex_unlock(&ldap_lock); + return cfg; +} + +static int update_ldap(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) +{ + char query[QUERY_SIZE]; + //char query2[QUERY_SIZE]; + char buf[256]; + const char *newparam, *newval; + char *dn; + LDAPMessage *res, *res2; + LDAPMod mod_change; + LDAPMod *all_changes[2]; + char *mod_vals[] = {"", NULL}; + int search, numFields; + LDAP *ldap; + + all_changes[0] = &mod_change; + all_changes[1] = NULL; + + + ast_log(LOG_DEBUG, "update_ldap\n"); + + mod_change.mod_op = LDAP_MOD_REPLACE; + mod_change.mod_values = mod_vals; + mod_change.mod_type = buf; + + if(!table) { + ast_log(LOG_WARNING, "LDAP RealTime: No table specified.\n"); + return -1; + } + + /* 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"); + return -1; + } + + /* 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 */ + + + snprintf(query, sizeof(query), "(&(objectClass=ast%s)(ast%s=%s))", table, keyfield, lookup); + ast_log(LOG_WARNING, "LDAP RealTime: Retrieve LDAP: %s\n", query); + + /* We now have our complete statement; Lets connect to the server and execute it. */ + ldap = ldap_reconnect(); + if(!ldap) { + return -1; + } + + ast_mutex_lock(&ldap_lock); + search = ldap_search_s(ldap, ldapbasedn, LDAP_SCOPE_SUBTREE, query, NULL, 0, &res); + if(search != LDAP_SUCCESS) { + ast_log(LOG_WARNING, "LDAP RealTime: Failed to query database. Check debug for more info.\n"); + ast_log(LOG_DEBUG, "LDAP RealTime: Query: %s\n", query); + if ( res != NULL ) { + ast_log(LOG_WARNING, "LDAP RealTime: Query Failed because: %s\n", ldap_err2string(ldap_result2error(ldap, res, 1))); + } + ast_mutex_unlock(&ldap_lock); + ldap_disconnect(ldap); + return -1; + } + + numFields = ldap_count_entries(ldap, res); + if ( numFields > 0 ) { + res2 = ldap_first_entry(ldap, res); + while(res2) { + dn = ldap_get_dn(ldap, res2); + ast_log(LOG_DEBUG, "LDAP RealTime: --%s\n", dn); + do { + ast_log(LOG_WARNING, "LDAP RealTime update: --%s --%s\n", newparam, newval); + snprintf(buf, sizeof(buf), "ast%s", newparam); + mod_vals[0] = (char *) newval; + ldap_modify_s(ldap, dn, all_changes); + } while((newparam = va_arg(ap, const char *)) && (newval = va_arg(ap, const char *))); + res = ldap_next_entry(ldap, res2); + ldap_memfree(res2); + res2 = res; + } + } + ldap_disconnect(ldap); + ast_mutex_unlock(&ldap_lock); + + /* get better return value */ + return 1; +} + +context *get_config(LDAP *ldap, const char *ldapbasedn, const char filter[QUERY_SIZE], char *attrs[], char *doubleEntries) +{ + int numFields; + char *chunk, *dn, *attr, **values; + int i = 0; + int j = 0; + int k = 0; + long ldapSearchResult = 0; + LDAPMessage *res, *res2; + BerElement *ber; + context *start = NULL, *p = NULL; + + /* Is the ldapbasedn set? */ + if(!ldapbasedn) { + ast_log(LOG_DEBUG, "No BaseDN specified.\n"); + return NULL; + } + + /* Dose we have a filter */ + if(!filter || !strlen(filter)) { + ast_log(LOG_DEBUG, "No Filter specified.\n"); + return NULL; + } + + if(!ldap) { + ast_log(LOG_DEBUG, "No connection!\n"); + return NULL; + } + + /* lets search the statement */ + ldapSearchResult = ldap_search_s(ldap, ldapbasedn, LDAP_SCOPE_SUBTREE, filter, attrs , 0, &res); + if(ldapSearchResult != LDAP_SUCCESS) { + 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); + if ( res != NULL ) { + ast_log(LOG_WARNING, "LDAP RealTime: Query Failed because: %s\n", ldap_err2string(ldap_result2error(ldap, res, 1))); + } + return NULL; + } + + numFields = ldap_count_entries(ldap, res); + if ( numFields > 0 ) { + /* sort the result */ + ldap_sort_entries(ldap, &res, attrs[1], strcasecmp); + res2 = ldap_first_entry(ldap, res); + while(res2) { + attr = ldap_first_attribute(ldap, res, &ber); + /* initialize a new context */ + if((p = (context*)malloc(sizeof(context)))) { + /* initialize field */ + for(i=0; attrs[i] != NULL; i++) { + p->string[i] = strdup(""); + p->members[i] = strdup(""); + } + p->string[i] = NULL; + p->members[i] = NULL; + + /* set field values */ + dn = ldap_get_dn(ldap, res); + /* if we must look for a "dn" */ + for(i = 0; attrs[i] != NULL; i++) + if(!strcasecmp(attrs[i], "dn")) { + p->string[i] = strdup(dn); + break; + } + + k = 0; + while (attr) { + values = ldap_get_values(ldap, res, attr); + i = 0; + + while(values[i]) { + chunk = strsep((char **) &values[i], ";"); + if(chunk && !ast_strlen_zero(ast_strip(chunk))) { + for(j = 0; attrs[j] != NULL; j++) { + if(!strcmp(attr, attrs[j]) || !strcmp(chunk, attrs[0])) { + ast_log(LOG_DEBUG, "%s-%s\n", attr, chunk); + if(!strcmp(attr, doubleEntries)) { + p->members[k] = strdup(chunk); + k++; + break; + } else { + p->string[j] = strdup(chunk); + break; + } + } + } + } + i++; + } + ldap_value_free(values); + ldap_memfree(attr); + attr = ldap_next_attribute(ldap, res, ber); + } + /* set the last element */ + p->members[k] = NULL; + res = ldap_next_entry(ldap, res2); + ldap_memfree(res2); + res2 = res; + p->next=start; + start = p; + } else + ast_log(LOG_WARNING, "Out of memory!\n"); + } + } else { + ast_log(LOG_DEBUG, "LDAP RealTime: Could not find any entries in DN \'%s\' with filter \'%s\'.\n", ldapbasedn, filter); + } + /* free all stuff */ + ldap_msgfree(res); + free(chunk); + free(dn); + free(attr); + ast_mutex_unlock(&ldap_lock); + + return start; +} + +void rm_context() +{ + context *p; + + while(anf != NULL) { + p = anf; + anf = p->next; + free(p); + } +} + +context *del_akt_context(context *p) +{ + context *q; + + if(p == anf) { + anf = p->next; + return anf; + } else { + q = anf; + while(q->next != p) + q = q->next; + q->next = p->next; + } + free(p); + return q->next; +} + +context *search_context(const char *stringToSearch, int index) +{ + context *p = anf; + + while(p) { + if(!strcmp(p->string[index], stringToSearch)) + break; + p = p->next; + } + + return p; +} + + +static struct ast_config *config_ldap(const char *database, const char *table, const char *file, struct ast_config *cfg) +{ + int i, j, barrier; + char *attrs[OPTION_SIZE], *options[OPTION_SIZE], filter[QUERY_SIZE], *var_val = NULL, *old_context = NULL; + struct ast_variable *var=NULL; + struct ast_category *cat = NULL; + LDAP *ldap; + context *p = NULL; + FILE *datei; + char a[QUERY_SIZE], b[QUERY_SIZE]; + + /* Is it my config file ("res_ldap.conf")? */ + if(!file || !strcmp(file, RES_CONFIG_LDAP_CONF)) { + ast_log(LOG_WARNING, "LDAP RealTime: Cannot configure myself.\n"); + return NULL; + + } else { + if((datei = fopen(database, "rt"))) { + ast_log(LOG_WARNING, "LDAP RealTime: Reading LDAP-Fields from File: \'%s\'.\n", database); + + i = 0; + while(!feof(datei)) { + fscanf(datei, "%s\t%s\n", a, b); + if(i+1 >= OPTION_SIZE) { + ast_log(LOG_ERROR, "LDAP RealTime: Out of memory! %i\n",i); + fclose(datei); + /* free fields */ + for(j = 0; j < i; j++) + free(attrs[j]); + for(j = 0; j < i; j++) + free(options[j]); + return NULL; + } + attrs[i] = strdup(a); + options[i] = strdup(b); + i++; + } + fclose(datei); + attrs[i] = NULL; + options[i] = NULL; + + /* show the field entrys */ + for(i=0; options[i]; i++) + ast_log(LOG_DEBUG, "%s=%s\n", attrs[i], options[i]); + + } else { + ast_log(LOG_WARNING, "LDAP RealTime: Fail to open file \'%s\'. Using defaults!\n", database); + + if(!strcmp(file, "voicemail.conf")) { + /* fields we wont to get from ldap + We don't care about global options! + (need no new objct) */ + attrs[0] = strdup("astVoicemailGeneralConfig"); options[0] = strdup("objectClass"); // objectClass for the generals + attrs[1] = strdup("astVoicemailContext"); options[1] = strdup("Context"); + /* General options */ + attrs[2] = strdup("astVoicemailFormat"); options[2] = strdup("Format"); + attrs[3] = strdup("astVoicemailServeremail"); options[3] = strdup("Serveremail"); + attrs[4] = strdup("astVoicemailAttach"); options[4] = strdup("Attach"); // Is general and MailBox option + attrs[5] = strdup("astVoicemailMaxmsg"); options[5] = strdup("Maxmsg"); + attrs[6] = strdup("astVoicemailMaxmessage"); options[6] = strdup("Maxmessage"); + attrs[7] = strdup("astVoicemailMinmessage"); options[7] = strdup("Minmessage"); + attrs[8] = strdup("astVoicemailMaxgreet"); options[8] = strdup("Maxgreet"); + attrs[9] = strdup("astVoicemailSkipms"); options[9] = strdup("Skipms"); + attrs[10] = strdup("astVoicemailMaxsilence"); options[10] = strdup("Maxsilence"); + attrs[11] = strdup("astVoicemailMaxlogins"); options[11] = strdup("Maxlogins"); + attrs[12] = strdup("astVoicemailExternnotify"); options[12] = strdup("Externnotify"); + attrs[13] = strdup("astVoicemailExternpass"); options[13] = strdup("Externpass"); + attrs[14] = strdup("astVoicemailDirectoryintro"); options[14] = strdup("Directoryintro"); + attrs[15] = strdup("astVoicemailCharset"); options[15] = strdup("Charset"); + attrs[16] = strdup("astVoicemailAdsifdn"); options[16] = strdup("Adsifdn"); + attrs[17] = strdup("astVoicemailAdsisec"); options[17] = strdup("Adsisec"); + attrs[18] = strdup("astVoicemailAdsiver"); options[18] = strdup("Adsiver"); + attrs[19] = strdup("astVoicemailPbxskip"); options[19] = strdup("Pbxskip"); + attrs[20] = strdup("astVoicemailFromstring"); options[20] = strdup("Fromstring"); + attrs[21] = strdup("astVoicemailUsedirectory"); options[21] = strdup("Usedirectory"); + attrs[22] = strdup("astVoicemailEmailsubject"); options[22] = strdup("Emailsubject"); + attrs[23] = strdup("astVoicemailEmailbody"); options[23] = strdup("Emailbody"); + attrs[24] = strdup("astVoicemailEmaildateformat"); options[24] = strdup("Emaildateformat"); + attrs[25] = strdup("astVoicemailMailcmd"); options[25] = strdup("Mailcmd"); + attrs[26] = strdup("astVoicemailTz"); options[26] = strdup("Tz"); + /* MailBox spezific */ + attrs[27] = strdup("astVoicemailSaycid"); options[27] = strdup("Saycid"); + attrs[28] = strdup("astVoicemailCidinternalContexts"); options[28] = strdup("CidinternalContexts"); + attrs[29] = strdup("astVoicemailSayduration"); options[29] = strdup("Sayduration"); + attrs[30] = strdup("astVoicemailSaydurationm"); options[30] = strdup("Saydurationm"); + attrs[31] = strdup("astVoicemailDialout"); options[31] = strdup("Dialout"); + attrs[32] = strdup("astVoicemailSendvoicemail"); options[32] = strdup("Sendvoicemail"); + attrs[33] = strdup("astVoicemailSearchcontexts"); options[33] = strdup("Searchcontexts"); + attrs[34] = strdup("astVoicemailCallback"); options[34] = strdup("Callback"); + attrs[35] = strdup("astVoicemailReview"); options[35] = strdup("Review"); + attrs[36] = strdup("astVoicemailOperator"); options[36] = strdup("Operator"); + attrs[37] = strdup("astVoicemailEnvelope"); options[37] = strdup("Envelope"); + attrs[38] = strdup("astVoicemailDelete"); options[38] = strdup("Delete"); + attrs[39] = strdup("astVoicemailNextaftercmd"); options[39] = strdup("Nextaftercmd"); + attrs[40] = strdup("astVoicemailForcename"); options[40] = strdup("Forcename"); + attrs[41] = strdup("astVoicemailForcegreetings"); options[41] = strdup("Forcegreetings"); + attrs[42] = strdup("astVoicemailHidefromdir"); options[42] = strdup("Hidefromdir"); + /* Fix Mailbox identifiers */ + attrs[43] = strdup("astVoicemailVoiceboxNr"); options[43] = strdup("VoiceboxNr"); // => + attrs[44] = strdup("astVoicemailPassword"); options[44] = strdup("Password"); // + attrs[45] = strdup("cn"); options[45] = strdup("Mailbox"); // we get from somewhere + attrs[46] = strdup("mail"); options[46] = strdup("Email"); // we get from somewhere + attrs[47] = strdup("astVoicemailPager"); options[47] = strdup("Pager"); // + attrs[48] = NULL; options[48]= NULL; + + } else if(!strcmp(file, "queues.conf")) { + /* fields we wont to get from ldap + (need a new object) */ + attrs[0] = strdup("astQueueGeneralConfig"); options[0] = strdup("objectClass"); // objectClass for the generals + attrs[1] = strdup("astQueueContext"); options[1] = strdup("Context"); + /* General options */ + attrs[2] = strdup("astQueuePersistentmembers"); options[2] = strdup("astQueuePersistentmembers"); + /* Queue spezific */ + attrs[3] = strdup("astQueueMusiconhold"); options[3] = strdup("Musiconhold"); + attrs[4] = strdup("astQueueAnnounce"); options[4] = strdup("Announce"); + attrs[5] = strdup("astQueueStrategy"); options[5] = strdup("Strategy"); + attrs[6] = strdup("astQueueDervicelevel"); options[6] = strdup("Dervicelevel"); + attrs[7] = strdup("astQueueGoToContext"); options[7] = strdup("Context"); + attrs[8] = strdup("astQueueTimeout"); options[8] = strdup("Timeout"); + attrs[9] = strdup("astQueueRetry"); options[9] = strdup("Retry"); + attrs[10] = strdup("astQueueWeight"); options[10] = strdup("Weight"); + attrs[11] = strdup("astQueueWrapuptime"); options[11] = strdup("Wrapuptime"); + attrs[12] = strdup("astQueueMaxlen"); options[12] = strdup("Maxlen"); + attrs[13] = strdup("astQueueAnnounceFrequency"); options[13] = strdup("Announce-frequency"); + attrs[14] = strdup("astQueuePeriodicAnnounceFrequency"); options[14] = strdup("Periodic-announce-frequency"); + attrs[15] = strdup("astQueueAnnounceHoldtime"); options[15] = strdup("Announce-holdtime"); + attrs[16] = strdup("astQueueAnnounceRoundSeconds"); options[16] = strdup("Announce-round-seconds"); + attrs[17] = strdup("astQueueQueueYouarenext"); options[17] = strdup("Queue-youarenext"); + attrs[18] = strdup("astQueueQueueThereare"); options[18] = strdup("Queue-thereare"); + attrs[19] = strdup("astQueueQueueCallswaiting"); options[19] = strdup("Queue-callswaiting"); + attrs[20] = strdup("astQueueQueueHoldtime"); options[20] = strdup("Queue-holdtime"); + attrs[21] = strdup("astQueueQueueMinutes"); options[21] = strdup("Queue-minutes"); + attrs[22] = strdup("astQueueQueueSeconds"); options[22] = strdup("Queue-seconds"); + attrs[23] = strdup("astQueueQueueThankyou"); options[23] = strdup("Queue-thankyou"); + attrs[24] = strdup("astQueueQueueLessthan"); options[24] = strdup("Queue-lessthan"); + attrs[25] = strdup("astQueueQueueReporthold"); options[25] = strdup("Queue-reporthold"); + attrs[26] = strdup("astQueuePeriodicAnnounce"); options[26] = strdup("Periodic-announce"); + attrs[27] = strdup("astQueueMonitorFormat"); options[27] = strdup("Monitor-format"); + attrs[28] = strdup("astQueueMonitorJoin"); options[28] = strdup("Monitor-join"); + attrs[29] = strdup("astQueueJoinempty"); options[29] = strdup("Joinempty"); + attrs[30] = strdup("astQueueLeavewhenempty"); options[30] = strdup("Leavewhenempty"); + attrs[31] = strdup("astQueueEventwhencalled"); options[31] = strdup("Eventwhencalled"); + attrs[32] = strdup("astQueueEventmemberstatusoff"); options[32] = strdup("Eventmemberstatusoff"); + attrs[33] = strdup("astQueueReportholdtime"); options[33] = strdup("Reportholdtime"); + attrs[34] = strdup("astQueueMemberdelay"); options[34] = strdup("Memberdelay"); + attrs[35] = strdup("astQueueTimeoutrestart"); options[35] = strdup("Timeoutrestart"); + /* the QueueMembers */ + attrs[36] = strdup("astQueueMember"); options[36]= strdup("member"); // must be the last field element! + attrs[37] = NULL; options[37]= NULL; + + } else if(!strcmp(file, "agents.conf")) { + /* fields we wont to get from ldap + (need a new object) */ + attrs[0] = strdup("astAgentGeneralConfig"); options[0] = strdup("objectClass"); // objectClass for the generals + attrs[1] = strdup("astAgentContext"); options[1] = strdup("Context"); + /* General options */ + attrs[2] = strdup("astAgentPersistentagents"); options[2] = strdup("Persistentagents"); + /* Agent spezific */ + attrs[3] = strdup("astAgentAutologoff"); options[3] = strdup("Autologoff"); + attrs[4] = strdup("astAgentAckcall"); options[4] = strdup("Ackcall"); + attrs[5] = strdup("astAgentWrapuptime"); options[5] = strdup("Wrapuptime"); + attrs[6] = strdup("astAgentMusiconhold"); options[6] = strdup("Musiconhold"); + attrs[7] = strdup("astAgentUpdatecdr"); options[7] = strdup("Updatecdr"); + attrs[8] = strdup("astAgentGroup"); options[8] = strdup("Group"); + attrs[9] = strdup("astAgentRecordagentcalls"); options[9] = strdup("Recordagentcalls"); + attrs[10] = strdup("astAgentRecordformat"); options[10]= strdup("Recordformat"); + attrs[11] = strdup("astAgentCreatelink"); options[11]= strdup("Createlink"); + attrs[12] = strdup("astAgentUrlprefix"); options[12]= strdup("Urlprefix"); + attrs[13] = strdup("astAgentSavecallsin"); options[13]= strdup("Savecallsin"); + attrs[14] = strdup("astAgentCustombeep"); options[14]= strdup("Custom_beep"); + /* The agent parameters */ + attrs[15] = strdup("astAgentAgentID"); options[15]= strdup("AgentID"); + attrs[16] = strdup("astAgentAgentPassword"); options[16]= strdup("AgentPassword"); + attrs[17] = strdup("cn"); options[17]= strdup("AgentDiscription"); + attrs[18] = NULL; options[18]= NULL; + + } else if(!strcmp(file, "meetme.conf")) { + /* fields we wont to get from ldap */ + attrs[0] = strdup("astMeetMeGeneralConfig"); options[0] = strdup("objectClass"); // objectClass for the generals + attrs[1] = strdup("astMeetMeContext"); options[1] = strdup("Context"); + /* General options */ + attrs[2] = strdup("astMeetMeAudiobuffers"); options[2] = strdup("Audiobuffers"); + /* MeetMe spezific */ + /* The MeetMe options */ + attrs[3] = strdup("astMeetMeConfno"); options[3] = strdup("Confno"); // must value if is it not a emty context + attrs[4] = strdup("astMeetMePincode"); options[4] = strdup("Pincode"); + attrs[5] = strdup("astMeetMeAdminpin"); options[5] = strdup("Adminpin"); + attrs[6] = NULL; options[6] = NULL; + + } else { + ast_log(LOG_WARNING, "LDAP RealTime: Useing of %s not supported!.\n",file); + return NULL; + } + } + } + + /* generate the query */ + snprintf(filter, sizeof(filter), "(|(objectClass=%s)(objectClass=%s))", table, attrs[0]); + + /* We now have our complete statement; Lets connect to the server and execute it. */ + ast_mutex_lock(&ldap_lock); + ldap = ldap_reconnect(); + if(!ldap) { + /* free all stuff */ + for(i = 0; attrs[i]; i++) + free(attrs[i]); + for(i = 0; options[i]; i++) + free(options[i]); + ast_mutex_unlock(&ldap_lock); + return NULL; + } + + /* Seting up barrier */ + for(i = 0; options[i]; i++) + if(!strcasecmp(options[i], "VoiceboxNr") || !strcasecmp(options[i], "AgentID") || !strcasecmp(options[i], "Confno") || !strcasecmp(options[i], "member")) { + barrier = i; + break; + } + + /* Lets get available contexts */ + anf = get_config(ldap, ldapbasedn, filter, attrs, !strcmp(file, "queues.conf") ? attrs[barrier] : ""); + + /* Get the context "general" */ + if((p = search_context("general", 1))) { + if (!(cat = ast_category_new(p->string[1]))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + } else { + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + /* Add the options */ + for(i = 2; i < barrier; i++) { + if(strlen(p->string[i])) { + var = ast_variable_new(options[i], p->string[i]); + ast_variable_append(cat, var); + ast_verbose("\t'%s = %s'\n", options[i], p->string[i]); + } + } + /* We not longer need the akt. context */ + del_akt_context(p); + /* add the category "general" */ + ast_category_append(cfg, cat); + cat = NULL; + } + } else { + ast_log(LOG_WARNING, "LDAP RealTime: Couldn't finde the \"general\" part.\n"); + } + + if(!strcmp(file, "agents.conf") || !strcmp(file, "meetme.conf")) { + if(!strcmp(file, "agents.conf") && (p = search_context("agents", 1))) { + if (!(cat = ast_category_new(p->string[1]))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + } else { + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + /* A general config only for agents */ + if(strlen(p->string[0])) { + /* Add the options */ + for(i = 2; i < barrier; i++) { + if(strlen(p->string[i])) { + var = ast_variable_new(options[i], p->string[i]); + ast_variable_append(cat, var); + ast_verbose("\t'%s = %s'\n", options[i], p->string[i]); + } + } + /* We not longer need the list element */ + del_akt_context(p); + } + } + } + + /* clear all elements we don't need */ + p = anf; + while(p) { + if(!strcmp(attrs[0], p->string[0])) { + p = del_akt_context(p); + continue; + } + p = p->next; + } + p = anf; + + /* Add the contextes "rooms" / "agents" without global options */ + if(!cat && p) { + if(!strcmp(file, "meetme.conf")) p->string[1] = strdup("rooms"); else p->string[1] = strdup("agents"); + if (!(cat = ast_category_new(p->string[1]))) + ast_log(LOG_WARNING, "Out of memory!\n"); + else + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + } + + /* Add the spezific entries */ + while(p && cat) { + /* generate values */ + var_val = strdup(p->string[barrier]); + for(i=barrier+1; options[i]; i++){ + if(strlen(p->string[i]) != 0) { + if (!(var_val = (char *)realloc(var_val, strlen(var_val) + 1 + strlen(p->string[i]) + 1))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + var_val = strcat(strcat(var_val, ","), p->string[i]); + } else { + if (!(var_val = (char *)realloc(var_val, strlen(var_val) + 1 + 1))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + var_val = strcat(var_val, ","); + } + } + /* add a new entry to the category */ + var = ast_variable_new(!strcmp(file, "agents.conf") ? "agent" : "conf", var_val); + ast_variable_append(cat, var); + ast_verbose("\t'%s => %s'\n", !strcmp(file, "agents.conf") ? "agent" : "conf", var_val); + p = p->next; + } + /* add the category "agents/rooms" */ + if(cat) + ast_category_append(cfg, cat); + + } else if(!strcmp(file, "voicemail.conf") && (p = anf)) { + /* clear all elements we don't need */ + while(p) { + if(!strcmp(attrs[0], p->string[0])) { + p = del_akt_context(p); + continue; + } + p = p->next; + } + + /* Add the rest contexts without global options */ + if((p = anf)) { + if (!(cat = ast_category_new(p->string[1]))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + } + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + old_context = strdup(p->string[1]); + } + while(p) { + if(strcmp(old_context, p->string[1])) { + /* add the category */ + ast_category_append(cfg, cat); + if (!(cat = ast_category_new(p->string[1]))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + old_context = strdup(p->string[1]); + } + + /* generate the first part of the value */ + var_val = strdup( p->string[barrier+1]); + for(i=barrier+2; p->string[i]; i++) + if(strlen( p->string[i]) == 0) { + if (!(var_val = (char *)realloc(var_val, strlen(var_val) + 1 + 1))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + strcat(var_val, ","); + } else { + if (!(var_val = (char *)realloc(var_val, strlen(var_val) + 1 + strlen( p->string[i]) + 1))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + strcat(strcat(var_val, ","), p->string[i]); + } + + /* generate the second part of the value */ + for(i=2; i < barrier; i++) + if(strlen( p->string[i]) != 0) { + if (!(var_val = (char *)realloc(var_val, strlen(var_val) + 1 + strlen(options[i]) + 1 + strlen( p->string[i]) + 1))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + strcat(strcat(strcat(strcat(var_val, ","), options[i]), "="), p->string[i]); + } + /* add a new entry to the category */ + var = ast_variable_new(p->string[barrier], var_val); + ast_variable_append(cat, var); + ast_verbose("\t'%s => %s'\n", p->string[barrier], var_val); + p = p->next; + } + /* add the category */ + if(anf) + ast_category_append(cfg, cat); + + } else if(!strcmp(file, "queues.conf") && (p = anf)) { + while(p) { + if (!(cat = ast_category_new(p->string[1]))) { + ast_log(LOG_WARNING, "Out of memory!\n"); + } else { + ast_verbose(" -- Add context: '%s'\n", p->string[1]); + /* add options */ + for(i = 2; p->string[i]; i++) { + if(strlen(p->string[i]) != 0) { + var = ast_variable_new(options[i], p->string[i]); + ast_variable_append(cat, var); + ast_verbose("\t'%s = %s'\n", options[i], p->string[i]); + } + } + + /* Add QueueMembers */ + for(i = 0; p->members[i]; i++) { + var = ast_variable_new(options[barrier], p->members[i]); + ast_variable_append(cat, var); + ast_verbose("\t'%s = %s'\n", options[barrier], p->members[i]); + } + } + /* add the category */ + ast_category_append(cfg, cat); + p = p->next; + } + } + + /* free all stuff */ + rm_context(); + for(i = 0; attrs[i]; i++) + free(attrs[i]); + for(i = 0; options[i]; i++) + free(options[i]); + free(var_val); + free(old_context); + ldap_disconnect(ldap); + ast_mutex_unlock(&ldap_lock); + + 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 = update_ldap +}; + +int load_module (void) +{ + parse_config(); + + if(!ldap_reconnect()) { + ast_log(LOG_WARNING, "LDAP RealTime: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "LDAP RealTime: Cannot Connect\n"); + } + + ast_config_engine_register(&ldap_engine); + if(option_verbose) { + ast_verbose("LDAP RealTime driver loaded.\n"); + } + ast_cli_register(&cli_realtime_ldap_status); + + return 0; +} + +int unload_module (void) +{ + /* Aquire control before doing anything to the module itself. */ + ast_mutex_lock(&ldap_lock); + + 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; +} + +int reload (void) +{ + /* Aquire control before doing anything to the module itself. */ + ast_mutex_lock(&ldap_lock); + + connected = 0; + parse_config(); + + /* Need to unlock so that ldap_reconnect can regain the lock. */ + ast_mutex_unlock(&ldap_lock); + + if(!ldap_reconnect()) { + ast_log(LOG_WARNING, "LDAP RealTime: Couldn't establish connection. Check debug.\n"); + ast_log(LOG_DEBUG, "LDAP RealTime: Cannot Connect\n"); + } + + ast_verbose(VERBOSE_PREFIX_2 "LDAP RealTime reloaded.\n"); + + return 0; +} + +int parse_config (void) +{ + struct ast_config *config; + char *s; + + config = ast_config_load(RES_CONFIG_LDAP_CONF); + + if(config) { + if(!(s=ast_variable_retrieve(config, "general", "ldapuser"))) { + ast_log(LOG_WARNING, "LDAP RealTime: No database user found, using 'asterisk' as default.\n"); + strncpy(ldapuser, "asterisk", sizeof(ldapuser) - 1); + } else { + strncpy(ldapuser, s, sizeof(ldapuser) - 1); + } + + if(!(s=ast_variable_retrieve(config, "general", "ldappass"))) { + ast_log(LOG_WARNING, "LDAP RealTime: No database password found, using 'asterisk' as default.\n"); + strncpy(ldappass, "asterisk", sizeof(ldappass) - 1); + } else { + strncpy(ldappass, s, sizeof(ldappass) - 1); + } + + if(!(s=ast_variable_retrieve(config, "general", "ldapuri"))) { + ast_log(LOG_WARNING, "LDAP RealTime: No database host found, using localhost via socket.\n"); + strncpy(ldapuri, "ldap://localhost", sizeof(ldapuri) - 1); + } else { + strncpy(ldapuri, s, sizeof(ldapuri) - 1); + } + + if(!(s=ast_variable_retrieve(config, "general", "ldapbasedn"))) { + ast_log(LOG_WARNING, "LDAP RealTime: No base dn found, using 'asterisk' as default.\n"); + strncpy(ldapbasedn, "asterisk", sizeof(ldapbasedn) - 1); + } else { + strncpy(ldapbasedn, s, sizeof(ldapbasedn) - 1); + } + } + ast_config_destroy(config); + + ast_log(LOG_WARNING, "LDAP RealTime Host: %s\n", ldapuri); + ast_log(LOG_WARNING, "LDAP RealTime User: %s\n", ldapuser); + ast_log(LOG_WARNING, "LDAP RealTime Base DN: %s\n", ldapbasedn); + + return 1; +} + +char *description (void) +{ + return res_config_ldap_desc; +} + +int usecount (void) +{ + /* Try and get a lock. If unsuccessful, than that means another thread is using the ldap object. */ + if(ast_mutex_trylock(&ldap_lock)) { + ast_log(LOG_DEBUG, "LDAP RealTime: Module usage count is 1.\n"); + return 1; + } + ast_mutex_unlock(&ldap_lock); + return 0; +} + +char *key () +{ + return ASTERISK_GPL_KEY; +} + +void ldap_disconnect(LDAP *ldap) +{ + + ldap_unbind_s(ldap); +} + +static LDAP *ldap_reconnect() +{ + int ver = LDAP_VERSION3; + int deref = LDAP_DEREF_ALWAYS; + int ldap_err; + LDAP *ldap; + + ast_mutex_lock(&ldap_lock); + + //if(!connected) { + if(strlen(ldapuri) && strlen(ldapuser) && strlen(ldappass) && strlen(ldapbasedn)) { + ldap_err = ldap_initialize(&ldap, ldapuri); + if(ldap_err != LDAP_SUCCESS || !ldap) { + ast_log(LOG_WARNING, "LDAP connection failed (host=%s)!\n", ldapuri); + connected = 0; + ast_mutex_unlock(&ldap_lock); + return NULL; + } + ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ver); + ldap_err = ldap_simple_bind_s(ldap, ldapuser, ldappass); + if(ldap_err == LDAP_SUCCESS) { + ast_log(LOG_WARNING, "LDAP RealTime: Successfully connected to LDAP.\n"); + connected = 1; + connect_time = time(NULL); + ast_mutex_unlock(&ldap_lock); + errorcount = 0; + return ldap; + } else if(ldap_err == LDAP_PROTOCOL_ERROR) { + if(ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ver) == -1 || + ldap_set_option(ldap, LDAP_OPT_DEREF, &deref) == -1) { + ast_log(LOG_ERROR, "LDAP RealTime: Failed to connect LDAP server %s on %s. Check debug for more info.\n", ldapbasedn, ldapuri); + ast_log(LOG_DEBUG, "LDAP RealTime: Cannot Connect: %s\n", ldap_err2string(ldap_err)); + connected = 0; + ast_mutex_unlock(&ldap_lock); + return NULL; + } else { + ast_log(LOG_DEBUG, "LDAP RealTime: Using LDAP_VERSION3 but successfully connected to LDAP.\n"); + connected = 1; + errorcount = 0; + connect_time = time(NULL); + ast_mutex_unlock(&ldap_lock); + return ldap; + } + } else { + ast_log(LOG_ERROR, "LDAP failed to bind (host=%s, user=%s %s %d)!\n",ldapuri, ldapuser, ldap_err2string(ldap_err), errorcount); + ldap_unbind(ldap); + errorcount++; + ast_mutex_unlock(&ldap_lock); + if ( errorcount < 3 ) { + usleep(50000); + return ldap_reconnect(); + } + return NULL; + } + + } else { + ast_log(LOG_ERROR, "LDAP RealTime: Not all Parameters of \"res_ldap.conf\" are set!\n"); + connected = 0; + ast_mutex_unlock(&ldap_lock); + free(ldap); + return NULL; + } + + /*} else { + ldap_err = ldap_simple_bind_s(ldap, ldapuser, ldappass); + if(ldap_err != LDAP_SUCCESS) { + connected = 0; + ast_log(LOG_ERROR, "LDAP RealTime: Server Error: %s\n", ldap_err2string(ldap_err)); + ast_mutex_unlock(&ldap_lock); + return 0; + } + + connected = 1; + + ast_log(LOG_WARNING, "LDAP RealTime: Everything is fine.\n"); + ast_mutex_unlock(&ldap_lock); + return 1; + }*/ +} + +static int realtime_ldap_status(int fd, int argc, char **argv) +{ + char status[256], status2[100] = ""; + int ctime = time(NULL) - connect_time; + + if(ldap_reconnect()) { + if ( ldapuri && ldapbasedn ) { + snprintf(status, 255, "Connected to %s base dn=%s", ldapuri, ldapbasedn); + } + + if(ldapuser && *ldapuser) { + snprintf(status2, 99, " with username %s", ldapuser); + } + + 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; + } else { + return RESULT_FAILURE; + } +}