Sat Apr 26 2014 22:01:40

Asterisk developer's documentation


res_config_ldap.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 2005, Oxymium sarl
00005  * Manuel Guesdon <mguesdon@oxymium.net> - LDAP RealTime Driver Author/Adaptor
00006  *
00007  * Copyright (C) 2007, Digium, Inc.
00008  * Russell Bryant <russell@digium.com>
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  *
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief ldap plugin for portable configuration engine (ARA)
00025  *
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author Manuel Guesdon
00028  * \author Carl-Einar Thorner <cthorner@voicerd.com>
00029  * \author Russell Bryant <russell@digium.com>
00030  *
00031  * \extref OpenLDAP http://www.openldap.org
00032  */
00033 
00034 /*** MODULEINFO
00035    <depend>ldap</depend>
00036    <support_level>extended</support_level>
00037  ***/
00038 
00039 #include "asterisk.h"
00040 
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <ctype.h>
00044 #include <stdio.h>
00045 #include <ldap.h>
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 385199 $")
00048 
00049 #include "asterisk/channel.h"
00050 #include "asterisk/logger.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/lock.h"
00054 #include "asterisk/options.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/strings.h"
00058 #include "asterisk/pbx.h"
00059 #include "asterisk/linkedlists.h"
00060 
00061 #define RES_CONFIG_LDAP_CONF "res_ldap.conf"
00062 #define RES_CONFIG_LDAP_DEFAULT_BASEDN "asterisk"
00063 
00064 AST_MUTEX_DEFINE_STATIC(ldap_lock);
00065 
00066 static LDAP *ldapConn;
00067 static char url[512];
00068 static char user[512];
00069 static char pass[512];
00070 static char base_distinguished_name[512];
00071 static int version;
00072 static time_t connect_time;
00073 
00074 static int parse_config(void);
00075 static int ldap_reconnect(void);
00076 static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00077 
00078 struct category_and_metric {
00079    const char *name;
00080    int metric;
00081    const char *variable_name;
00082    const char *variable_value;
00083    int var_metric; /*!< For organizing variables (particularly includes and switch statments) within a context */
00084 };
00085 
00086 /*! \brief Table configuration */
00087 struct ldap_table_config {
00088    char *table_name;     /*!< table name */
00089    char *additional_filter;     /*!< additional filter   */
00090    struct ast_variable *attributes;  /*!< attribute names conversion */
00091    struct ast_variable *delimiters;  /*!< the current delimiter is semicolon, so we are not using this variable */
00092    AST_LIST_ENTRY(ldap_table_config) entry;
00093    /* TODO: Make proxies work */
00094 };
00095 
00096 /*! \brief Should be locked before using it */
00097 static AST_LIST_HEAD_NOLOCK_STATIC(table_configs, ldap_table_config);
00098 static struct ldap_table_config *base_table_config;
00099 static struct ldap_table_config *static_table_config;
00100 
00101 static struct ast_cli_entry ldap_cli[] = {
00102    AST_CLI_DEFINE(realtime_ldap_status, "Shows connection information for the LDAP RealTime driver"),
00103 };
00104 
00105 /*! \brief Create a new table_config */
00106 static struct ldap_table_config *table_config_new(const char *table_name)
00107 {
00108    struct ldap_table_config *p;
00109 
00110    if (!(p = ast_calloc(1, sizeof(*p))))
00111       return NULL;
00112 
00113    if (table_name) {
00114       if (!(p->table_name = ast_strdup(table_name))) {
00115          free(p);
00116          return NULL;
00117       }
00118    }
00119 
00120    return p;
00121 }
00122 
00123 /*! \brief Find a table_config - Should be locked before using it 
00124  *  \note This function assumes ldap_lock to be locked. */
00125 static struct ldap_table_config *table_config_for_table_name(const char *table_name)
00126 {
00127    struct ldap_table_config *c = NULL;
00128 
00129    AST_LIST_TRAVERSE(&table_configs, c, entry) {
00130       if (!strcmp(c->table_name, table_name))
00131          break;
00132    }
00133 
00134    return c;
00135 }
00136 
00137 /*! \brief Find variable by name */
00138 static struct ast_variable *variable_named(struct ast_variable *var, const char *name)
00139 {
00140    for (; var; var = var->next) {
00141       if (!strcasecmp(name, var->name))
00142          break;
00143    }
00144 
00145    return var;
00146 }
00147 
00148 /*! \brief for the semicolon delimiter
00149    \param somestr - pointer to a string
00150 
00151    \return number of occurances of the delimiter(semicolon)
00152  */
00153 static int semicolon_count_str(const char *somestr)
00154 {
00155    int count = 0;
00156 
00157    for (; *somestr; somestr++) {
00158       if (*somestr == ';')
00159          count++;
00160    }
00161 
00162    return count;
00163 }
00164 
00165 /* takes a linked list of \a ast_variable variables, finds the one with the name variable_value
00166  * and returns the number of semicolons in the value for that \a ast_variable
00167  */
00168 static int semicolon_count_var(struct ast_variable *var)
00169 {
00170    struct ast_variable *var_value = variable_named(var, "variable_value");
00171 
00172    if (!var_value) {
00173       return 0;
00174    }
00175 
00176    ast_debug(2, "LINE(%d) semicolon_count_var: %s\n", __LINE__, var_value->value);
00177 
00178    return semicolon_count_str(var_value->value);
00179 }
00180 
00181 /*! \brief add attribute to table config - Should be locked before using it */
00182 static void ldap_table_config_add_attribute(struct ldap_table_config *table_config,
00183    const char *attribute_name, const char *attribute_value)
00184 {
00185    struct ast_variable *var;
00186 
00187    if (ast_strlen_zero(attribute_name) || ast_strlen_zero(attribute_value)) {
00188       return;
00189    }
00190 
00191    if (!(var = ast_variable_new(attribute_name, attribute_value, table_config->table_name))) {
00192       return;
00193    }
00194 
00195    if (table_config->attributes) {
00196       var->next = table_config->attributes;
00197    }
00198    table_config->attributes = var;
00199 }
00200 
00201 /*! \brief Free table_config 
00202  *  \note assumes ldap_lock to be locked */
00203 static void table_configs_free(void)
00204 {
00205    struct ldap_table_config *c;
00206 
00207    while ((c = AST_LIST_REMOVE_HEAD(&table_configs, entry))) {
00208       if (c->table_name) {
00209          ast_free(c->table_name);
00210       }
00211       if (c->additional_filter) {
00212          ast_free(c->additional_filter);
00213       }
00214       if (c->attributes) {
00215          ast_variables_destroy(c->attributes);
00216       }
00217       free(c);
00218    }
00219 
00220    base_table_config = NULL;
00221    static_table_config = NULL;
00222 }
00223 
00224 /*! \brief Convert variable name to ldap attribute name - Should be locked before using it */
00225 static const char *convert_attribute_name_to_ldap(struct ldap_table_config *table_config,
00226    const char *attribute_name)
00227 {
00228    int i = 0;
00229    struct ldap_table_config *configs[] = { table_config, base_table_config };
00230 
00231    for (i = 0; i < ARRAY_LEN(configs); i++) {
00232       struct ast_variable *attribute;
00233 
00234       if (!configs[i]) {
00235          continue;
00236       }
00237 
00238       attribute = configs[i]->attributes;
00239       for (; attribute; attribute = attribute->next) {
00240          if (!strcasecmp(attribute_name, attribute->name)) {
00241             return attribute->value;
00242          }
00243       }
00244    }
00245 
00246    return attribute_name;
00247 }
00248 
00249 /*! \brief Convert ldap attribute name to variable name 
00250    \note Should be locked before using it */
00251 static const char *convert_attribute_name_from_ldap(struct ldap_table_config *table_config,
00252                       const char *attribute_name)
00253 {
00254    int i = 0;
00255    struct ldap_table_config *configs[] = { table_config, base_table_config };
00256 
00257    for (i = 0; i < ARRAY_LEN(configs); i++) {
00258       struct ast_variable *attribute;
00259 
00260       if (!configs[i]) {
00261          continue;
00262       }
00263 
00264       attribute = configs[i]->attributes;
00265       for (; attribute; attribute = attribute->next) {
00266          if (strcasecmp(attribute_name, attribute->value) == 0) {
00267             return attribute->name;
00268          }
00269       }
00270    }
00271 
00272    return attribute_name;
00273 }
00274 
00275 /*! \brief Get variables from ldap entry attributes 
00276    \note Should be locked before using it
00277    \return a linked list of ast_variable variables.
00278  */
00279 static struct ast_variable *realtime_ldap_entry_to_var(struct ldap_table_config *table_config,
00280    LDAPMessage *ldap_entry)
00281 {
00282    BerElement *ber = NULL;
00283    struct ast_variable *var = NULL;
00284    struct ast_variable *prev = NULL;
00285    int is_delimited = 0;
00286    int i = 0;
00287    char *ldap_attribute_name;
00288    struct berval *value;
00289    int pos = 0;
00290 
00291    ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
00292 
00293    while (ldap_attribute_name) {
00294       struct berval **values = NULL;
00295       const char *attribute_name = convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
00296       int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
00297 
00298       values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name); /* these are freed at the end */
00299       if (values) {
00300          struct berval **v;
00301          char *valptr;
00302 
00303          for (v = values; *v; v++) {
00304             value = *v;
00305             valptr = value->bv_val;
00306             ast_debug(2, "LINE(%d) attribute_name: %s LDAP value: %s\n", __LINE__, attribute_name, valptr);
00307             if (is_realmed_password_attribute) {
00308                if (!strncasecmp(valptr, "{md5}", 5)) {
00309                   valptr += 5;
00310                }
00311                ast_debug(2, "md5: %s\n", valptr);
00312             }
00313             if (valptr) {
00314                /* ok, so looping through all delimited values except the last one (not, last character is not delimited...) */
00315                if (is_delimited) {
00316                   i = 0;
00317                   pos = 0;
00318                   while (!ast_strlen_zero(valptr + i)) {
00319                      if (valptr[i] == ';') {
00320                         valptr[i] = '\0';
00321                         if (prev) {
00322                            prev->next = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
00323                            if (prev->next) {
00324                               prev = prev->next;
00325                            }
00326                         } else {
00327                            prev = var = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
00328                         }
00329                         pos = i + 1;
00330                      }
00331                      i++;
00332                   }
00333                }
00334                /* for the last delimited value or if the value is not delimited: */
00335                if (prev) {
00336                   prev->next = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
00337                   if (prev->next) {
00338                      prev = prev->next;
00339                   }
00340                } else {
00341                   prev = var = ast_variable_new(attribute_name, &valptr[pos], table_config->table_name);
00342                }
00343             }
00344          }
00345          ldap_value_free_len(values);
00346       }
00347       ldap_memfree(ldap_attribute_name);
00348       ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
00349    }
00350    ber_free(ber, 0);
00351 
00352    return var;
00353 }
00354 
00355 /*! \brief Get variables from ldap entry attributes - Should be locked before using it
00356  *
00357  * The results are freed outside this function so is the \a vars array.
00358  * 
00359  * \return \a vars - an array of ast_variable variables terminated with a null.
00360  **/
00361 static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_config *table_config,
00362    LDAPMessage *ldap_result_msg, unsigned int *entries_count_ptr)
00363 {
00364    struct ast_variable **vars;
00365    int i = 0;
00366    int tot_count = 0;
00367    int entry_index = 0;
00368    LDAPMessage *ldap_entry = NULL;
00369    BerElement *ber = NULL;
00370    struct ast_variable *var = NULL;
00371    struct ast_variable *prev = NULL;
00372    int is_delimited = 0;
00373    char *delim_value = NULL;
00374    int delim_tot_count = 0;
00375    int delim_count = 0;
00376 
00377    /* First find the total count */
00378    ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
00379 
00380    for (tot_count = 0; ldap_entry; tot_count++) { 
00381       struct ast_variable *tmp = realtime_ldap_entry_to_var(table_config, ldap_entry);
00382       tot_count += semicolon_count_var(tmp);
00383       ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
00384       ast_variables_destroy(tmp);
00385    }
00386 
00387    if (entries_count_ptr) {
00388       *entries_count_ptr = tot_count;
00389    }
00390 
00391    /* Now that we have the total count we allocate space and create the variables
00392     * Remember that each element in vars is a linked list that points to realtime variable.
00393     * If the we are dealing with a static realtime variable we create a new element in the \a vars array for each delimited
00394     * value in \a variable_value; otherwise, we keep \a vars static and increase the length of the linked list of variables in the array element.
00395     * This memory must be freed outside of this function. */
00396    vars = ast_calloc(sizeof(struct ast_variable *), tot_count + 1);
00397 
00398    ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
00399 
00400    i = 0;
00401 
00402    /* For each static realtime variable we may create several entries in the \a vars array if it's delimited */
00403    for (entry_index = 0; ldap_entry; ) {
00404       int pos = 0;
00405       delim_value = NULL;
00406       delim_tot_count = 0;
00407       delim_count = 0;
00408 
00409       do { /* while delim_count */
00410 
00411          /* Starting new static var */
00412          char *ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
00413          struct berval *value;
00414          while (ldap_attribute_name) {
00415             const char *attribute_name = convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
00416             int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
00417             struct berval **values = NULL;
00418 
00419             values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name);
00420             if (values) {
00421                struct berval **v;
00422                char *valptr;
00423 
00424                for (v = values; *v; v++) {
00425                   value = *v;
00426                   valptr = value->bv_val;
00427                   if (is_realmed_password_attribute) {
00428                      if (strncasecmp(valptr, "{md5}", 5) == 0) {
00429                         valptr += 5;
00430                      }
00431                      ast_debug(2, "md5: %s\n", valptr);
00432                   }
00433                   if (valptr) {
00434                      if (delim_value == NULL && !is_realmed_password_attribute
00435                         && (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0)) {
00436 
00437                         delim_value = ast_strdup(valptr);
00438 
00439                         if ((delim_tot_count = semicolon_count_str(delim_value)) > 0) {
00440                            ast_debug(4, "LINE(%d) is delimited %d times: %s\n", __LINE__, delim_tot_count, delim_value);
00441                            is_delimited = 1;
00442                         }
00443                      }
00444 
00445                      if (is_delimited != 0 && !is_realmed_password_attribute
00446                         && (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0) ) {
00447                         /* for non-Static RealTime, first */
00448 
00449                         for (i = pos; !ast_strlen_zero(valptr + i); i++) {
00450                            ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
00451                            if (delim_value[i] == ';') {
00452                               delim_value[i] = '\0';
00453 
00454                               ast_debug(2, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
00455 
00456                               if (prev) {
00457                                  prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
00458                                  if (prev->next) {
00459                                     prev = prev->next;
00460                                  }
00461                               } else {
00462                                  prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
00463                               }
00464                               pos = i + 1;
00465 
00466                               if (static_table_config == table_config) {
00467                                  break;
00468                               }
00469                            }
00470                         }
00471                         if (ast_strlen_zero(valptr + i)) {
00472                            ast_debug(4, "LINE(%d) DELIM pos: %d i: %d delim_count: %d\n", __LINE__, pos, i, delim_count);
00473                            /* Last delimited value */
00474                            ast_debug(4, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
00475                            if (prev) {
00476                               prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
00477                               if (prev->next) {
00478                                  prev = prev->next;
00479                               }
00480                            } else {
00481                               prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
00482                            }
00483                            /* Remembering to free memory */
00484                            is_delimited = 0;
00485                            pos = 0;
00486                         }
00487                         free(delim_value);
00488                         delim_value = NULL;
00489 
00490                         ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
00491                      } else {
00492                         /* not delimited */
00493                         if (delim_value) {
00494                            free(delim_value);
00495                            delim_value = NULL;
00496                         }
00497                         ast_debug(2, "LINE(%d) attribute_name: %s value: %s\n", __LINE__, attribute_name, valptr);
00498 
00499                         if (prev) {
00500                            prev->next = ast_variable_new(attribute_name, valptr, table_config->table_name);
00501                            if (prev->next) {
00502                               prev = prev->next;
00503                            }
00504                         } else {
00505                            prev = var = ast_variable_new(attribute_name, valptr, table_config->table_name);
00506                         }
00507                      }
00508                   }
00509                } /*!< for (v = values; *v; v++) */
00510                ldap_value_free_len(values);
00511             }/*!< if (values) */
00512             ldap_memfree(ldap_attribute_name);
00513             ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
00514          } /*!< while (ldap_attribute_name) */
00515          ber_free(ber, 0);
00516          if (static_table_config == table_config) {
00517             if (option_debug > 2) {
00518                const struct ast_variable *tmpdebug = variable_named(var, "variable_name");
00519                const struct ast_variable *tmpdebug2 = variable_named(var, "variable_value");
00520                if (tmpdebug && tmpdebug2) {
00521                   ast_debug(3, "LINE(%d) Added to vars - %s = %s\n", __LINE__, tmpdebug->value, tmpdebug2->value);
00522                }
00523             }
00524             vars[entry_index++] = var;
00525             prev = NULL;
00526          }
00527 
00528          delim_count++;
00529       } while (delim_count <= delim_tot_count && static_table_config == table_config);
00530 
00531       if (static_table_config != table_config) {
00532          ast_debug(3, "LINE(%d) Added to vars - non static\n", __LINE__);
00533 
00534          vars[entry_index++] = var;
00535          prev = NULL;
00536       }
00537       ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
00538    } /*!< end for loop over ldap_entry */
00539 
00540    return vars;
00541 }
00542 
00543 
00544 /*! \brief Check if we have a connection error */
00545 static int is_ldap_connect_error(int err)
00546 {
00547    return (err == LDAP_SERVER_DOWN || err == LDAP_TIMEOUT || err == LDAP_CONNECT_ERROR);
00548 }
00549 
00550 /*! \brief Get LDAP entry by dn and return attributes as variables  - Should be locked before using it 
00551    This is used for setting the default values of an object(i.e., with accountBaseDN)
00552 */
00553 static struct ast_variable *ldap_loadentry(struct ldap_table_config *table_config,
00554                   const char *dn)
00555 {
00556    if (!table_config) {
00557       ast_log(LOG_ERROR, "No table config\n");
00558       return NULL;
00559    } else {
00560       struct ast_variable **vars = NULL;
00561       struct ast_variable *var = NULL;
00562       int result = -1;
00563       LDAPMessage *ldap_result_msg = NULL;
00564       int tries = 0;
00565 
00566       ast_debug(2, "ldap_loadentry dn=%s\n", dn);
00567 
00568       do {
00569          result = ldap_search_ext_s(ldapConn, dn, LDAP_SCOPE_BASE,
00570                   "(objectclass=*)", NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &ldap_result_msg);
00571          if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
00572             ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
00573             tries++;
00574             if (tries < 3) {
00575                usleep(500000L * tries);
00576                if (ldapConn) {
00577                   ldap_unbind_ext_s(ldapConn, NULL, NULL);
00578                   ldapConn = NULL;
00579                }
00580                if (!ldap_reconnect()) {
00581                   break;
00582                }
00583             }
00584          }
00585       } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
00586 
00587       if (result != LDAP_SUCCESS) {
00588          ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
00589          ast_debug(2, "dn=%s\n", dn);
00590          ast_mutex_unlock(&ldap_lock);
00591          return NULL;
00592       } else {
00593          int num_entry = 0;
00594          unsigned int *entries_count_ptr = NULL; /*!< not using this */
00595 
00596          if ((num_entry = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
00597             ast_debug(3, "num_entry: %d\n", num_entry);
00598 
00599             vars = realtime_ldap_result_to_vars(table_config, ldap_result_msg, entries_count_ptr);
00600             if (num_entry > 1) {
00601                ast_log(LOG_NOTICE, "More than one entry for dn=%s. Take only 1st one\n", dn);
00602             }
00603          } else {
00604             ast_debug(2, "Could not find any entry dn=%s.\n", dn);
00605          }
00606       }
00607       ldap_msgfree(ldap_result_msg);
00608 
00609       /* Chopping \a vars down to one variable */
00610       if (vars != NULL) {
00611          struct ast_variable **p = vars;
00612 
00613          /* Only take the first one. */
00614          var = *vars;
00615 
00616          /* Destroy the rest. */
00617          while (*++p) {
00618             ast_variables_destroy(*p);
00619          }
00620          ast_free(vars);
00621       }
00622 
00623       return var;
00624    }
00625 }
00626 
00627 /*! \note caller should free returned pointer */
00628 static char *substituted(struct ast_channel *channel, const char *string)
00629 {
00630 #define MAXRESULT 2048
00631    char *ret_string = NULL;
00632 
00633    if (!ast_strlen_zero(string)) {
00634       ret_string = ast_calloc(1, MAXRESULT);
00635       pbx_substitute_variables_helper(channel, string, ret_string, MAXRESULT - 1);
00636    }
00637    ast_debug(2, "substituted: string: '%s' => '%s' \n", string, ret_string);
00638    return ret_string;
00639 }
00640 
00641 /*! \note caller should free returned pointer */
00642 static char *cleaned_basedn(struct ast_channel *channel, const char *basedn)
00643 {
00644    char *cbasedn = NULL;
00645    if (basedn) {
00646       char *p = NULL;
00647       cbasedn = substituted(channel, basedn);
00648       if (*cbasedn == '"') {
00649          cbasedn++;
00650          if (!ast_strlen_zero(cbasedn)) {
00651             int len = strlen(cbasedn);
00652             if (cbasedn[len - 1] == '"')
00653                cbasedn[len - 1] = '\0';
00654 
00655          }
00656       }
00657       p = cbasedn;
00658       while (*p) {
00659          if (*p == '|')
00660             *p = ',';
00661          p++;
00662       }
00663    }
00664    ast_debug(2, "basedn: '%s' => '%s' \n", basedn, cbasedn);
00665    return cbasedn;
00666 }
00667 
00668 /*! \brief Replace <search> by <by> in string. 
00669    \note No check is done on string allocated size ! */
00670 static int replace_string_in_string(char *string, const char *search, const char *by)
00671 {
00672    int search_len = strlen(search);
00673    int by_len = strlen(by);
00674    int replaced = 0;
00675    char *p = strstr(string, search);
00676 
00677    if (p) {
00678       replaced = 1;
00679       while (p) {
00680          if (by_len == search_len) {
00681             memcpy(p, by, by_len);
00682          } else {
00683             memmove(p + by_len, p + search_len, strlen(p + search_len) + 1);
00684             memcpy(p, by, by_len);
00685          }
00686          p = strstr(p + by_len, search);
00687       }
00688    }
00689    return replaced;
00690 }
00691 
00692 /*! \brief Append a name=value filter string. The filter string can grow. */
00693 static void append_var_and_value_to_filter(struct ast_str **filter,
00694    struct ldap_table_config *table_config,
00695    const char *name, const char *value)
00696 {
00697    char *new_name = NULL;
00698    char *new_value = NULL;
00699    char *like_pos = strstr(name, " LIKE");
00700 
00701    ast_debug(2, "name='%s' value='%s'\n", name, value);
00702 
00703    if (like_pos) {
00704       int len = like_pos - name;
00705 
00706       name = new_name = ast_strdupa(name);
00707       new_name[len] = '\0';
00708       value = new_value = ast_strdupa(value);
00709       replace_string_in_string(new_value, "\\_", "_");
00710       replace_string_in_string(new_value, "%", "*");
00711    }
00712 
00713    name = convert_attribute_name_to_ldap(table_config, name);
00714 
00715    ast_str_append(filter, 0, "(%s=%s)", name, value);
00716 }
00717 
00718 /*! \brief LDAP base function 
00719  * \return a null terminated array of ast_variable (one per entry) or NULL if no entry is found or if an error occured
00720  * caller should free the returned array and ast_variables
00721  * \param entries_count_ptr is a pointer to found entries count (can be NULL)
00722  * \param basedn is the base DN
00723  * \param table_name is the table_name (used dor attribute convertion and additional filter)
00724  * \param ap contains null terminated list of pairs name/value
00725 */
00726 static struct ast_variable **realtime_ldap_base_ap(unsigned int *entries_count_ptr,
00727    const char *basedn, const char *table_name, va_list ap)
00728 {
00729    struct ast_variable **vars = NULL;
00730    const char *newparam = NULL;
00731    const char *newval = NULL;
00732    struct ldap_table_config *table_config = NULL;
00733    char *clean_basedn = cleaned_basedn(NULL, basedn);
00734    struct ast_str *filter = NULL;
00735    int tries = 0;
00736    int result = 0;
00737    LDAPMessage *ldap_result_msg = NULL;
00738 
00739    if (!table_name) {
00740       ast_log(LOG_ERROR, "No table_name specified.\n");
00741       ast_free(clean_basedn);
00742       return NULL;
00743    } 
00744 
00745    if (!(filter = ast_str_create(80))) {
00746       ast_log(LOG_ERROR, "Can't initialize data structures.n");
00747       ast_free(clean_basedn);
00748       return NULL;
00749    }
00750 
00751    /* Get the first parameter and first value in our list of passed paramater/value pairs  */
00752    newparam = va_arg(ap, const char *);
00753    newval = va_arg(ap, const char *);
00754 
00755    if (!newparam || !newval) {
00756       ast_log(LOG_ERROR, "Realtime retrieval requires at least 1 parameter"
00757          " and 1 value to search on.\n");
00758       ast_free(filter);
00759       ast_free(clean_basedn);
00760       return NULL;
00761    }
00762 
00763    ast_mutex_lock(&ldap_lock);
00764 
00765    /* We now have our complete statement; Lets connect to the server and execute it.  */
00766    if (!ldap_reconnect()) {
00767       ast_mutex_unlock(&ldap_lock);
00768       ast_free(filter);
00769       ast_free(clean_basedn);
00770       return NULL;
00771    }
00772 
00773    table_config = table_config_for_table_name(table_name);
00774    if (!table_config) {
00775       ast_log(LOG_WARNING, "No table named '%s'.\n", table_name);
00776       ast_mutex_unlock(&ldap_lock);
00777       ast_free(filter);
00778       ast_free(clean_basedn);
00779       return NULL;
00780    }
00781 
00782    ast_str_append(&filter, 0, "(&");
00783 
00784    if (table_config && table_config->additional_filter) {
00785       ast_str_append(&filter, 0, "%s", table_config->additional_filter);
00786    }
00787    if (table_config != base_table_config && base_table_config && 
00788       base_table_config->additional_filter) {
00789       ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
00790    }
00791 
00792    /* Create the first part of the query using the first parameter/value pairs we just extracted */
00793    /*   If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00794 
00795    append_var_and_value_to_filter(&filter, table_config, newparam, newval);
00796    while ((newparam = va_arg(ap, const char *))) {
00797       newval = va_arg(ap, const char *);
00798       append_var_and_value_to_filter(&filter, table_config, newparam, newval);
00799    }
00800    ast_str_append(&filter, 0, ")");
00801 
00802    do {
00803       /* freeing ldap_result further down */
00804       result = ldap_search_ext_s(ldapConn, clean_basedn,
00805               LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
00806               &ldap_result_msg);
00807       if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
00808          ast_debug(1, "Failed to query directory. Try %d/10\n", tries + 1);
00809          if (++tries < 10) {
00810             usleep(1);
00811             if (ldapConn) {
00812                ldap_unbind_ext_s(ldapConn, NULL, NULL);
00813                ldapConn = NULL;
00814             }
00815             if (!ldap_reconnect()) {
00816                break;
00817             }
00818          }
00819       }
00820    } while (result != LDAP_SUCCESS && tries < 10 && is_ldap_connect_error(result));
00821 
00822    if (result != LDAP_SUCCESS) {
00823       ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
00824       ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
00825    } else {
00826       /* this is where we create the variables from the search result 
00827        * freeing this \a vars outside this function */
00828       if (ldap_count_entries(ldapConn, ldap_result_msg) > 0) {
00829          /* is this a static var or some other? they are handled different for delimited values */
00830          vars = realtime_ldap_result_to_vars(table_config, ldap_result_msg, entries_count_ptr);
00831       } else {
00832          ast_debug(1, "Could not find any entry matching %s in base dn %s.\n", ast_str_buffer(filter), clean_basedn);
00833       }
00834 
00835       ldap_msgfree(ldap_result_msg);
00836 
00837       /* TODO: get the default variables from the accountBaseDN, not implemented with delimited values */
00838       if (vars) {
00839          struct ast_variable **p = vars;
00840          while (*p) {
00841             struct ast_variable *append_var = NULL;
00842             struct ast_variable *tmp = *p;
00843             while (tmp) {
00844                if (strcasecmp(tmp->name, "accountBaseDN") == 0) {
00845                   /* Get the variable to compare with for the defaults */
00846                   struct ast_variable *base_var = ldap_loadentry(table_config, tmp->value);
00847                   
00848                   while (base_var) {
00849                      struct ast_variable *next = base_var->next;
00850                      struct ast_variable *test_var = *p;
00851                      int base_var_found = 0;
00852 
00853                      /* run throught the default values and fill it inn if it is missing */
00854                      while (test_var) {
00855                         if (strcasecmp(test_var->name, base_var->name) == 0) {
00856                            base_var_found = 1;
00857                            break;
00858                         } else {
00859                            test_var = test_var->next;
00860                         }
00861                      }
00862                      if (base_var_found) {
00863                         base_var->next = NULL;
00864                         ast_variables_destroy(base_var);
00865                         base_var = next;
00866                      } else {
00867                         /*!
00868                          * \todo XXX The interactions with base_var and append_var may
00869                          * cause a memory leak of base_var nodes.  Also the append_var
00870                          * list and base_var list may get cross linked.
00871                          */
00872                         if (append_var) {
00873                            base_var->next = append_var;
00874                         } else {
00875                            base_var->next = NULL;
00876                         }
00877                         append_var = base_var;
00878                         base_var = next;
00879                      }
00880                   }
00881                }
00882                if (!tmp->next && append_var) {
00883                   tmp->next = append_var;
00884                   tmp = NULL;
00885                } else {
00886                   tmp = tmp->next;
00887                }
00888             }
00889             p++;
00890          }
00891       }
00892    }
00893 
00894    if (filter) {
00895       ast_free(filter);
00896    }
00897 
00898    if (clean_basedn) {
00899       ast_free(clean_basedn);
00900    }
00901 
00902    ast_mutex_unlock(&ldap_lock);
00903 
00904    return vars;
00905 }
00906 
00907 /*! \brief same as realtime_ldap_base_ap but take variable arguments count list */
00908 static struct ast_variable **realtime_ldap_base(unsigned int *entries_count_ptr,
00909    const char *basedn, const char *table_name, ...)
00910 {
00911    struct ast_variable **vars = NULL;
00912    va_list ap;
00913 
00914    va_start(ap, table_name);
00915    vars = realtime_ldap_base_ap(entries_count_ptr, basedn, table_name, ap);
00916    va_end(ap);
00917 
00918    return vars;
00919 }
00920 
00921 /*! \brief See Asterisk doc
00922 *
00923 * For Realtime Dynamic(i.e., switch, queues, and directory) -- I think
00924 */
00925 static struct ast_variable *realtime_ldap(const char *basedn,
00926                  const char *table_name, va_list ap)
00927 {
00928    struct ast_variable **vars = realtime_ldap_base_ap(NULL, basedn, table_name, ap);
00929    struct ast_variable *var = NULL;
00930 
00931    if (vars) {
00932       struct ast_variable *last_var = NULL;
00933       struct ast_variable **p = vars;
00934 
00935       /* Chain the vars array of lists into one list to return. */
00936       while (*p) {
00937          if (last_var) {
00938             while (last_var->next) {
00939                last_var = last_var->next;
00940             }
00941             last_var->next = *p;
00942          } else {
00943             var = *p;
00944             last_var = var;
00945          }
00946          p++;
00947       }
00948       free(vars);
00949    }
00950    return var;
00951 }
00952 
00953 /*! \brief See Asterisk doc
00954 *
00955 * this function will be called for the switch statment if no match is found with the realtime_ldap function(i.e. it is a failover);
00956 * however, the ast_load_realtime wil match on wildcharacters also depending on what the mode is set to
00957 * this is an area of asterisk that could do with a lot of modification
00958 * I think this function returns Realtime dynamic objects
00959 */
00960 static struct ast_config *realtime_multi_ldap(const char *basedn,
00961       const char *table_name, va_list ap)
00962 {
00963    char *op;
00964    const char *initfield = NULL;
00965    const char *newparam, *newval;
00966    struct ast_variable **vars =
00967       realtime_ldap_base_ap(NULL, basedn, table_name, ap);
00968    struct ast_config *cfg = NULL;
00969 
00970    newparam = va_arg(ap, const char *);
00971    newval = va_arg(ap, const char *);
00972    if (!newparam || !newval) {
00973        ast_log(LOG_WARNING, "realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00974        return NULL;
00975    }
00976    initfield = ast_strdupa(newparam);
00977    if ((op = strchr(initfield, ' '))) {
00978       *op = '\0';
00979    }
00980 
00981    if (vars) {
00982       cfg = ast_config_new();
00983       if (!cfg) {
00984          ast_log(LOG_ERROR, "Unable to create a config!\n");
00985       } else {
00986          struct ast_variable **p = vars;
00987 
00988          while (*p) {
00989             struct ast_category *cat = NULL;
00990             cat = ast_category_new("", table_name, -1);
00991             if (!cat) {
00992                ast_log(LOG_ERROR, "Unable to create a new category!\n");
00993                break;
00994             } else {
00995                struct ast_variable *var = *p;
00996                while (var) {
00997                   struct ast_variable *next = var->next;
00998                   if (initfield && !strcmp(initfield, var->name)) {
00999                      ast_category_rename(cat, var->value);
01000                   }
01001                   var->next = NULL;
01002                   ast_variable_append(cat, var);
01003                   var = next;
01004                }
01005             }
01006             ast_category_append(cfg, cat);
01007             p++;
01008          }
01009       }
01010       free(vars);
01011    }
01012    return cfg;
01013 
01014 }
01015 
01016 /*! 
01017  * \brief Sorting alogrithm for qsort to find the order of the variables \a a and \a b
01018  * \param a pointer to category_and_metric struct
01019  * \param b pointer to category_and_metric struct
01020  *
01021  * \retval -1 for if b is greater
01022  * \retval 0 zero for equal
01023  * \retval 1 if a is greater
01024  */
01025 static int compare_categories(const void *a, const void *b)
01026 {
01027    const struct category_and_metric *as = a;
01028    const struct category_and_metric *bs = b;
01029 
01030    if (as->metric < bs->metric) {
01031       return -1;
01032    } else if (as->metric > bs->metric) {
01033       return 1;
01034    } else if (as->metric == bs->metric && strcmp(as->name, bs->name) != 0) {
01035       return strcmp(as->name, bs->name);
01036    } 
01037    /* if the metric and the category name is the same, we check the variable metric */
01038    if (as->var_metric < bs->var_metric) {
01039       return -1;
01040    } else if (as->var_metric > bs->var_metric) {
01041       return 1;
01042    }
01043 
01044    return 0;
01045 }
01046 
01047 /*! \brief See Asterisk doc
01048  *
01049 *  This is for Static Realtime (again: I think...)
01050 *  
01051 *  load the configuration stuff for the .conf files
01052 *  called on a reload
01053 */
01054 static struct ast_config *config_ldap(const char *basedn, const char *table_name,
01055    const char *file, struct ast_config *cfg, struct ast_flags config_flags, const char *sugg_incl, const char *who_asked)
01056 {
01057    unsigned int vars_count = 0;
01058    struct ast_variable **vars;
01059    int i = 0;
01060    struct ast_variable *new_v = NULL;
01061    struct ast_category *cur_cat = NULL;
01062    const char *last_category = NULL;
01063    int last_category_metric = 0;
01064    struct category_and_metric *categories;
01065    struct ast_variable **p;
01066 
01067    if (ast_strlen_zero(file) || !strcasecmp(file, RES_CONFIG_LDAP_CONF)) {
01068       ast_log(LOG_ERROR, "Missing configuration file: %s. Can't configure myself.\n", RES_CONFIG_LDAP_CONF);
01069       return NULL;
01070    }
01071 
01072    vars = realtime_ldap_base(&vars_count, basedn, table_name, "filename", file, "commented", "FALSE", NULL);
01073 
01074    if (!vars) {
01075       ast_log(LOG_WARNING, "Could not find config '%s' in directory.\n", file);
01076       return NULL;
01077    }
01078 
01079    /*!\note Since the items come back in random order, they need to be sorted
01080     * first, and since the data could easily exceed stack size, this is
01081     * allocated from the heap.
01082     */
01083    if (!(categories = ast_calloc(sizeof(*categories), vars_count))) {
01084       return NULL;
01085    }
01086 
01087    for (vars_count = 0, p = vars; *p; p++) {
01088       struct ast_variable *category = variable_named(*p, "category");
01089       struct ast_variable *cat_metric = variable_named(*p, "cat_metric");
01090       struct ast_variable *var_name = variable_named(*p, "variable_name");
01091       struct ast_variable *var_val = variable_named(*p, "variable_value");
01092       struct ast_variable *var_metric = variable_named(*p, "var_metric");
01093       struct ast_variable *dn = variable_named(*p, "dn");
01094 
01095       if (!category) {
01096          ast_log(LOG_ERROR, "No category name in entry '%s'  for file '%s'.\n",
01097                (dn ? dn->value : "?"), file);
01098       } else if (!cat_metric) {
01099          ast_log(LOG_ERROR, "No category metric in entry '%s'(category: %s) for file '%s'.\n",
01100                (dn ? dn->value : "?"), category->value, file);
01101       } else if (!var_metric) {
01102          ast_log(LOG_ERROR, "No variable metric in entry '%s'(category: %s) for file '%s'.\n",
01103                (dn ? dn->value : "?"), category->value, file);
01104       } else if (!var_name) {
01105          ast_log(LOG_ERROR, "No variable name in entry '%s' (category: %s metric: %s) for file '%s'.\n",
01106                (dn ? dn->value : "?"), category->value,
01107                cat_metric->value, file);
01108       } else if (!var_val) {
01109          ast_log(LOG_ERROR, "No variable value in entry '%s' (category: %s metric: %s variable: %s) for file '%s'.\n",
01110                (dn ? dn->value : "?"), category->value,
01111                cat_metric->value, var_name->value, file);
01112       } else {
01113          categories[vars_count].name = category->value;
01114          categories[vars_count].metric = atoi(cat_metric->value);
01115          categories[vars_count].variable_name = var_name->value;
01116          categories[vars_count].variable_value = var_val->value;
01117          categories[vars_count].var_metric = atoi(var_metric->value);
01118          vars_count++;
01119       }
01120 
01121       ast_debug(3, "category: %s\n", category->value);
01122       ast_debug(3, "var_name: %s\n", var_name->value);
01123       ast_debug(3, "var_val: %s\n", var_val->value);
01124       ast_debug(3, "cat_metric: %s\n", cat_metric->value);
01125 
01126    }
01127 
01128    qsort(categories, vars_count, sizeof(*categories), compare_categories);
01129 
01130    for (i = 0; i < vars_count; i++) {
01131       if (!strcmp(categories[i].variable_name, "#include")) {
01132          struct ast_flags flags = { 0 };
01133          if (!ast_config_internal_load(categories[i].variable_value, cfg, flags, "", who_asked)) {
01134             break;
01135          }
01136          continue;
01137       }
01138 
01139       if (!last_category || strcmp(last_category, categories[i].name) ||
01140          last_category_metric != categories[i].metric) {
01141 
01142          cur_cat = ast_category_new(categories[i].name, table_name, -1);
01143          if (!cur_cat) {
01144             break;
01145          }
01146          last_category = categories[i].name;
01147          last_category_metric = categories[i].metric;
01148          ast_category_append(cfg, cur_cat);
01149       }
01150 
01151       if (!(new_v = ast_variable_new(categories[i].variable_name, categories[i].variable_value, table_name))) {
01152          break;
01153       }
01154 
01155       ast_variable_append(cur_cat, new_v);
01156    }
01157 
01158    ast_free(vars);
01159    ast_free(categories);
01160 
01161    return cfg;
01162 }
01163 
01164 /* \brief Function to update a set of values in ldap static mode
01165 */
01166 static int update_ldap(const char *basedn, const char *table_name, const char *attribute,
01167    const char *lookup, va_list ap)
01168 {
01169    int error = 0;
01170    LDAPMessage *ldap_entry = NULL;
01171    LDAPMod **ldap_mods;
01172    const char *newparam = NULL;
01173    const char *newval = NULL;
01174    char *dn;
01175    int num_entries = 0;
01176    int i = 0;
01177    int mods_size = 0;
01178    int mod_exists = 0;
01179    struct ldap_table_config *table_config = NULL;
01180    char *clean_basedn = NULL;
01181    struct ast_str *filter = NULL;
01182    int tries = 0;
01183    int result = 0;
01184    LDAPMessage *ldap_result_msg = NULL;
01185 
01186    if (!table_name) {
01187       ast_log(LOG_ERROR, "No table_name specified.\n");
01188       return -1;
01189    } 
01190 
01191    if (!(filter = ast_str_create(80))) {
01192       return -1;
01193    }
01194 
01195    if (!attribute || !lookup) {
01196       ast_log(LOG_WARNING, "LINE(%d): search parameters are empty.\n", __LINE__);
01197       return -1;
01198    }
01199    ast_mutex_lock(&ldap_lock);
01200 
01201    /* We now have our complete statement; Lets connect to the server and execute it.  */
01202    if (!ldap_reconnect()) {
01203       ast_mutex_unlock(&ldap_lock);
01204       return -1;
01205    }
01206 
01207    table_config = table_config_for_table_name(table_name);
01208    if (!table_config) {
01209       ast_log(LOG_ERROR, "No table named '%s'.\n", table_name);
01210       ast_mutex_unlock(&ldap_lock);
01211       return -1;
01212    }
01213 
01214    clean_basedn = cleaned_basedn(NULL, basedn);
01215 
01216    /* Create the filter with the table additional filter and the parameter/value pairs we were given */
01217    ast_str_append(&filter, 0, "(&");
01218    if (table_config && table_config->additional_filter) {
01219       ast_str_append(&filter, 0, "%s", table_config->additional_filter);
01220    }
01221    if (table_config != base_table_config && base_table_config && base_table_config->additional_filter) {
01222       ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
01223    }
01224    append_var_and_value_to_filter(&filter, table_config, attribute, lookup);
01225    ast_str_append(&filter, 0, ")");
01226 
01227    /* Create the modification array with the parameter/value pairs we were given, 
01228     * if there are several parameters with the same name, we collect them into 
01229     * one parameter/value pair and delimit them with a semicolon */
01230    newparam = va_arg(ap, const char *);
01231    newparam = convert_attribute_name_to_ldap(table_config, newparam);
01232    newval = va_arg(ap, const char *);
01233    if (!newparam || !newval) {
01234       ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
01235       return -1;
01236    }
01237 
01238    mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
01239    ldap_mods = ldap_memcalloc(sizeof(LDAPMod *), mods_size);
01240    ldap_mods[0] = ldap_memcalloc(1, sizeof(LDAPMod));
01241 
01242    ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
01243    ldap_mods[0]->mod_type = ldap_strdup(newparam);
01244 
01245    ldap_mods[0]->mod_values = ast_calloc(sizeof(char *), 2);
01246    ldap_mods[0]->mod_values[0] = ldap_strdup(newval);
01247 
01248    while ((newparam = va_arg(ap, const char *))) {
01249       newparam = convert_attribute_name_to_ldap(table_config, newparam);
01250       newval = va_arg(ap, const char *);
01251       mod_exists = 0;
01252 
01253       for (i = 0; i < mods_size - 1; i++) {
01254          if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
01255             /* We have the parameter allready, adding the value as a semicolon delimited value */
01256             ldap_mods[i]->mod_values[0] = ldap_memrealloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(newval) + 2));
01257             strcat(ldap_mods[i]->mod_values[0], ";");
01258             strcat(ldap_mods[i]->mod_values[0], newval);
01259             mod_exists = 1;   
01260             break;
01261          }
01262       }
01263 
01264       /* create new mod */
01265       if (!mod_exists) {
01266          mods_size++;
01267          ldap_mods = ldap_memrealloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
01268          ldap_mods[mods_size - 1] = NULL;
01269          
01270          ldap_mods[mods_size - 2] = ldap_memcalloc(1, sizeof(LDAPMod));
01271 
01272          ldap_mods[mods_size - 2]->mod_type = ldap_memcalloc(sizeof(char), strlen(newparam) + 1);
01273          strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
01274 
01275          if (strlen(newval) == 0) {
01276             ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_DELETE;
01277          } else {
01278             ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
01279 
01280             ldap_mods[mods_size - 2]->mod_values = ldap_memcalloc(sizeof(char *), 2);
01281             ldap_mods[mods_size - 2]->mod_values[0] = ldap_memcalloc(sizeof(char), strlen(newval) + 1);
01282             strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
01283          }
01284       }
01285    }
01286    /* freeing ldap_mods further down */
01287 
01288    do {
01289       /* freeing ldap_result further down */
01290       result = ldap_search_ext_s(ldapConn, clean_basedn,
01291               LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
01292               &ldap_result_msg);
01293       if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
01294          ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
01295          tries++;
01296          if (tries < 3) {
01297             usleep(500000L * tries);
01298             if (ldapConn) {
01299                ldap_unbind_ext_s(ldapConn, NULL, NULL);
01300                ldapConn = NULL;
01301             }
01302             if (!ldap_reconnect())
01303                break;
01304          }
01305       }
01306    } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
01307 
01308    if (result != LDAP_SUCCESS) {
01309       ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
01310       ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
01311 
01312       ast_mutex_unlock(&ldap_lock);
01313       free(filter);
01314       free(clean_basedn);
01315       ldap_msgfree(ldap_result_msg);
01316       ldap_mods_free(ldap_mods, 0);
01317       return -1;
01318    }
01319    /* Ready to update */
01320    if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
01321       ast_debug(3, "LINE(%d) Modifying %s=%s hits: %d\n", __LINE__, attribute, lookup, num_entries);
01322       for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
01323          if (ldap_mods[i]->mod_op != LDAP_MOD_DELETE) {
01324             ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
01325          } else {
01326             ast_debug(3, "LINE(%d) deleting %s \n", __LINE__, ldap_mods[i]->mod_type);
01327          }
01328       }
01329       ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
01330 
01331       for (i = 0; ldap_entry; i++) { 
01332          dn = ldap_get_dn(ldapConn, ldap_entry);
01333          if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS)  {
01334             ast_log(LOG_ERROR, "Couldn't modify '%s'='%s', dn:%s because %s\n",
01335                   attribute, lookup, dn, ldap_err2string(error));
01336          }
01337          ldap_memfree(dn);
01338          ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
01339       }
01340    }
01341 
01342    ast_mutex_unlock(&ldap_lock);
01343    ast_free(filter);
01344    ast_free(clean_basedn);
01345    ldap_msgfree(ldap_result_msg);
01346    ldap_mods_free(ldap_mods, 0);
01347    return num_entries;
01348 }
01349 
01350 static int update2_ldap(const char *basedn, const char *table_name, va_list ap)
01351 {
01352    int error = 0;
01353    LDAPMessage *ldap_entry = NULL;
01354    LDAPMod **ldap_mods;
01355    const char *newparam = NULL;
01356    const char *newval = NULL;
01357    char *dn;
01358    int num_entries = 0;
01359    int i = 0;
01360    int mods_size = 0;
01361    int mod_exists = 0;
01362    struct ldap_table_config *table_config = NULL;
01363    char *clean_basedn = NULL;
01364    struct ast_str *filter = NULL;
01365    int tries = 0;
01366    int result = 0;
01367    LDAPMessage *ldap_result_msg = NULL;
01368 
01369    if (!table_name) {
01370       ast_log(LOG_ERROR, "No table_name specified.\n");
01371       return -1;
01372    } 
01373 
01374    if (!(filter = ast_str_create(80))) {
01375       return -1;
01376    }
01377 
01378    ast_mutex_lock(&ldap_lock);
01379 
01380    /* We now have our complete statement; Lets connect to the server and execute it.  */
01381    if (!ldap_reconnect()) {
01382       ast_mutex_unlock(&ldap_lock);
01383       ast_free(filter);
01384       return -1;
01385    }
01386 
01387    table_config = table_config_for_table_name(table_name);
01388    if (!table_config) {
01389       ast_log(LOG_ERROR, "No table named '%s'.\n", table_name);
01390       ast_mutex_unlock(&ldap_lock);
01391       ast_free(filter);
01392       return -1;
01393    }
01394 
01395    clean_basedn = cleaned_basedn(NULL, basedn);
01396 
01397    /* Create the filter with the table additional filter and the parameter/value pairs we were given */
01398    ast_str_append(&filter, 0, "(&");
01399    if (table_config && table_config->additional_filter) {
01400       ast_str_append(&filter, 0, "%s", table_config->additional_filter);
01401    }
01402    if (table_config != base_table_config && base_table_config
01403       && base_table_config->additional_filter) {
01404       ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
01405    }
01406 
01407    /* Get multiple lookup keyfields and values */
01408    while ((newparam = va_arg(ap, const char *))) {
01409       newval = va_arg(ap, const char *);
01410       append_var_and_value_to_filter(&filter, table_config, newparam, newval);
01411    }
01412    ast_str_append(&filter, 0, ")");
01413 
01414    /* Create the modification array with the parameter/value pairs we were given, 
01415     * if there are several parameters with the same name, we collect them into 
01416     * one parameter/value pair and delimit them with a semicolon */
01417    newparam = va_arg(ap, const char *);
01418    newparam = convert_attribute_name_to_ldap(table_config, newparam);
01419    newval = va_arg(ap, const char *);
01420    if (!newparam || !newval) {
01421       ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
01422       ast_free(filter);
01423       ast_free(clean_basedn);
01424       return -1;
01425    }
01426 
01427    mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
01428    ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
01429    ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
01430 
01431    ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
01432    ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
01433    strcpy(ldap_mods[0]->mod_type, newparam);
01434 
01435    ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2);
01436    ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
01437    strcpy(ldap_mods[0]->mod_values[0], newval);
01438 
01439    while ((newparam = va_arg(ap, const char *))) {
01440       newparam = convert_attribute_name_to_ldap(table_config, newparam);
01441       newval = va_arg(ap, const char *);
01442       mod_exists = 0;
01443 
01444       for (i = 0; i < mods_size - 1; i++) {
01445          if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
01446             /* We have the parameter allready, adding the value as a semicolon delimited value */
01447             ldap_mods[i]->mod_values[0] = ast_realloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(newval) + 2));
01448             strcat(ldap_mods[i]->mod_values[0], ";");
01449             strcat(ldap_mods[i]->mod_values[0], newval);
01450             mod_exists = 1;   
01451             break;
01452          }
01453       }
01454 
01455       /* create new mod */
01456       if (!mod_exists) {
01457          mods_size++;
01458          ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
01459          ldap_mods[mods_size - 1] = NULL;
01460          ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
01461 
01462          ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
01463 
01464          ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
01465          strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
01466 
01467          ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
01468          ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
01469          strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
01470       }
01471    }
01472    /* freeing ldap_mods further down */
01473 
01474    do {
01475       /* freeing ldap_result further down */
01476       result = ldap_search_ext_s(ldapConn, clean_basedn,
01477               LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
01478               &ldap_result_msg);
01479       if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
01480          ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
01481          tries++;
01482          if (tries < 3) {
01483             usleep(500000L * tries);
01484             if (ldapConn) {
01485                ldap_unbind_ext_s(ldapConn, NULL, NULL);
01486                ldapConn = NULL;
01487             }
01488             if (!ldap_reconnect()) {
01489                break;
01490             }
01491          }
01492       }
01493    } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
01494 
01495    if (result != LDAP_SUCCESS) {
01496       ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
01497       ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
01498 
01499       ast_mutex_unlock(&ldap_lock);
01500       ast_free(filter);
01501       ast_free(clean_basedn);
01502       ldap_msgfree(ldap_result_msg);
01503       ldap_mods_free(ldap_mods, 0);
01504       return -1;
01505    }
01506    /* Ready to update */
01507    if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
01508       for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
01509          ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
01510       }
01511 
01512       ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
01513 
01514       for (i = 0; ldap_entry; i++) {
01515          dn = ldap_get_dn(ldapConn, ldap_entry);
01516          if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS)  {
01517             ast_log(LOG_ERROR, "Couldn't modify dn:%s because %s", dn, ldap_err2string(error));
01518          }
01519          ldap_memfree(dn);
01520          ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
01521       }
01522    }
01523 
01524    ast_mutex_unlock(&ldap_lock);
01525    if (filter) {
01526       ast_free(filter);
01527    }
01528    if (clean_basedn) {
01529       ast_free(clean_basedn);
01530    }
01531    ldap_msgfree(ldap_result_msg);
01532    ldap_mods_free(ldap_mods, 0);
01533    return num_entries;
01534 }
01535 
01536 static struct ast_config_engine ldap_engine = {
01537    .name = "ldap",
01538    .load_func = config_ldap,
01539    .realtime_func = realtime_ldap,
01540    .realtime_multi_func = realtime_multi_ldap,
01541    .update_func = update_ldap,
01542    .update2_func = update2_ldap,
01543 };
01544 
01545 static int load_module(void)
01546 {
01547    if (parse_config() < 0) {
01548       ast_log(LOG_ERROR, "Cannot load LDAP RealTime driver.\n");
01549       return 0;
01550    }
01551 
01552    ast_mutex_lock(&ldap_lock);
01553 
01554    if (!ldap_reconnect())  {
01555       ast_log(LOG_WARNING, "Couldn't establish connection to LDAP directory. Check debug.\n");
01556    }
01557 
01558    ast_config_engine_register(&ldap_engine);
01559    ast_verb(1, "LDAP RealTime driver loaded.\n");
01560    ast_cli_register_multiple(ldap_cli, ARRAY_LEN(ldap_cli));
01561 
01562    ast_mutex_unlock(&ldap_lock);
01563 
01564    return 0;
01565 }
01566 
01567 static int unload_module(void)
01568 {
01569    /* Aquire control before doing anything to the module itself. */
01570    ast_mutex_lock(&ldap_lock);
01571 
01572    table_configs_free();
01573 
01574    if (ldapConn) {
01575       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01576       ldapConn = NULL;
01577    }
01578    ast_cli_unregister_multiple(ldap_cli, ARRAY_LEN(ldap_cli));
01579    ast_config_engine_deregister(&ldap_engine);
01580    ast_verb(1, "LDAP RealTime driver unloaded.\n");
01581 
01582    /* Unlock so something else can destroy the lock. */
01583    ast_mutex_unlock(&ldap_lock);
01584 
01585    return 0;
01586 }
01587 
01588 static int reload(void)
01589 {
01590    /* Aquire control before doing anything to the module itself. */
01591    ast_mutex_lock(&ldap_lock);
01592 
01593    if (ldapConn) {
01594       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01595       ldapConn = NULL;
01596    }
01597 
01598    if (parse_config() < 0) {
01599       ast_log(LOG_NOTICE, "Cannot reload LDAP RealTime driver.\n");
01600       ast_mutex_unlock(&ldap_lock);
01601       return 0;
01602    }     
01603 
01604    if (!ldap_reconnect())  {
01605       ast_log(LOG_WARNING, "Couldn't establish connection to your directory server. Check debug.\n");
01606    }
01607 
01608    ast_verb(2, "LDAP RealTime driver reloaded.\n");
01609 
01610    /* Done reloading. Release lock so others can now use driver. */
01611    ast_mutex_unlock(&ldap_lock);
01612 
01613    return 0;
01614 }
01615 
01616 /*! \brief parse the configuration file */
01617 static int parse_config(void)
01618 {
01619    struct ast_config *config;
01620    struct ast_flags config_flags = {0};
01621    const char *s, *host;
01622    int port;
01623    char *category_name = NULL;
01624 
01625    /* Make sure that global variables are reset */
01626    url[0] = '\0';
01627    user[0] = '\0';
01628    pass[0] = '\0';
01629    base_distinguished_name[0] = '\0';
01630    version = 3;
01631 
01632    config = ast_config_load(RES_CONFIG_LDAP_CONF, config_flags);
01633    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01634       ast_log(LOG_ERROR, "Cannot load configuration file: %s\n", RES_CONFIG_LDAP_CONF);
01635       return -1;
01636    }
01637 
01638    if (!(s = ast_variable_retrieve(config, "_general", "user"))) {
01639       ast_log(LOG_NOTICE, "No directory user found, anonymous binding as default.\n");
01640       user[0] = '\0';
01641    } else {
01642       ast_copy_string(user, s, sizeof(user));
01643    }
01644 
01645    if (!ast_strlen_zero(user)) {
01646       if (!(s = ast_variable_retrieve(config, "_general", "pass"))) {
01647          ast_log(LOG_WARNING, "No directory password found, using 'asterisk' as default.\n");
01648          ast_copy_string(pass, "asterisk", sizeof(pass));
01649       } else {
01650          ast_copy_string(pass, s, sizeof(pass));
01651       }
01652    }
01653 
01654    /* URL is preferred, use host and port if not found */
01655    if ((s = ast_variable_retrieve(config, "_general", "url"))) {
01656       ast_copy_string(url, s, sizeof(url));
01657    } else if ((host = ast_variable_retrieve(config, "_general", "host"))) {
01658       if (!(s = ast_variable_retrieve(config, "_general", "port")) || sscanf(s, "%5d", &port) != 1 || port > 65535) {
01659          ast_log(LOG_NOTICE, "No directory port found, using 389 as default.\n");
01660          port = 389;
01661       }
01662 
01663       snprintf(url, sizeof(url), "ldap://%s:%d", host, port);
01664    } else {
01665       ast_log(LOG_ERROR, "No directory URL or host found.\n");
01666       ast_config_destroy(config);
01667       return -1;
01668    }
01669 
01670    if (!(s = ast_variable_retrieve(config, "_general", "basedn"))) {
01671       ast_log(LOG_ERROR, "No LDAP base dn found, using '%s' as default.\n", RES_CONFIG_LDAP_DEFAULT_BASEDN);
01672       ast_copy_string(base_distinguished_name, RES_CONFIG_LDAP_DEFAULT_BASEDN, sizeof(base_distinguished_name));
01673    } else 
01674       ast_copy_string(base_distinguished_name, s, sizeof(base_distinguished_name));
01675 
01676    if (!(s = ast_variable_retrieve(config, "_general", "version")) && !(s = ast_variable_retrieve(config, "_general", "protocol"))) {
01677       ast_log(LOG_NOTICE, "No explicit LDAP version found, using 3 as default.\n");
01678    } else if (sscanf(s, "%30d", &version) != 1 || version < 1 || version > 6) {
01679       ast_log(LOG_WARNING, "Invalid LDAP version '%s', using 3 as default.\n", s);
01680       version = 3;
01681    }
01682 
01683    table_configs_free();
01684 
01685    while ((category_name = ast_category_browse(config, category_name))) {
01686       int is_general = (strcasecmp(category_name, "_general") == 0);
01687       int is_config = (strcasecmp(category_name, "config") == 0); /*!< using the [config] context for Static RealTime */
01688       struct ast_variable *var = ast_variable_browse(config, category_name);
01689       
01690       if (var) {
01691          struct ldap_table_config *table_config =
01692             table_config_for_table_name(category_name);
01693          if (!table_config) {
01694             table_config = table_config_new(category_name);
01695             AST_LIST_INSERT_HEAD(&table_configs, table_config, entry);
01696             if (is_general)
01697                base_table_config = table_config;
01698             if (is_config)
01699                static_table_config = table_config;
01700          }
01701          for (; var; var = var->next) {
01702             if (!strcasecmp(var->name, "additionalFilter")) {
01703                table_config->additional_filter = ast_strdup(var->value);
01704             } else {
01705                ldap_table_config_add_attribute(table_config, var->name, var->value);
01706             }
01707          }
01708       }
01709    }
01710 
01711    ast_config_destroy(config);
01712 
01713    return 1;
01714 }
01715 
01716 /*! \note ldap_lock should have been locked before calling this function. */
01717 static int ldap_reconnect(void)
01718 {
01719    int bind_result = 0;
01720    struct berval cred;
01721 
01722    if (ldapConn) {
01723       ast_debug(2, "Everything seems fine.\n");
01724       return 1;
01725    }
01726 
01727    if (ast_strlen_zero(url)) {
01728       ast_log(LOG_ERROR, "Not enough parameters to connect to ldap directory\n");
01729       return 0;
01730    }
01731 
01732    if (LDAP_SUCCESS != ldap_initialize(&ldapConn, url)) {
01733       ast_log(LOG_ERROR, "Failed to init ldap connection to '%s'. Check debug for more info.\n", url);
01734       return 0;
01735    }
01736 
01737    if (LDAP_OPT_SUCCESS != ldap_set_option(ldapConn, LDAP_OPT_PROTOCOL_VERSION, &version)) {
01738       ast_log(LOG_WARNING, "Unable to set LDAP protocol version to %d, falling back to default.\n", version);
01739    }
01740 
01741    if (!ast_strlen_zero(user)) {
01742       ast_debug(2, "bind to '%s' as user '%s'\n", url, user);
01743       cred.bv_val = (char *) pass;
01744       cred.bv_len = strlen(pass);
01745       bind_result = ldap_sasl_bind_s(ldapConn, user, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
01746    } else {
01747       ast_debug(2, "bind %s anonymously\n", url);
01748       cred.bv_val = NULL;
01749       cred.bv_len = 0;
01750       bind_result = ldap_sasl_bind_s(ldapConn, NULL, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
01751    }
01752    if (bind_result == LDAP_SUCCESS) {
01753       ast_debug(2, "Successfully connected to directory.\n");
01754       connect_time = time(NULL);
01755       return 1;
01756    } else {
01757       ast_log(LOG_WARNING, "bind failed: %s\n", ldap_err2string(bind_result));
01758       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01759       ldapConn = NULL;
01760       return 0;
01761    }
01762 }
01763 
01764 static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01765 {
01766    char status[256], credentials[100] = "";
01767    int ctimesec = time(NULL) - connect_time;
01768 
01769    switch (cmd) {
01770    case CLI_INIT:
01771       e->command = "realtime show ldap status";
01772       e->usage =
01773          "Usage: realtime show ldap status\n"
01774          "         Shows connection information for the LDAP RealTime driver\n";
01775       return NULL;
01776    case CLI_GENERATE:
01777       return NULL;
01778    }
01779 
01780    if (!ldapConn)
01781       return CLI_FAILURE;
01782 
01783    if (!ast_strlen_zero(url)) 
01784       snprintf(status, sizeof(status), "Connected to '%s', baseDN %s", url, base_distinguished_name);
01785 
01786    if (!ast_strlen_zero(user))
01787       snprintf(credentials, sizeof(credentials), " with username %s", user);
01788 
01789    if (ctimesec > 31536000) {
01790       ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01791             status, credentials, ctimesec / 31536000,
01792             (ctimesec % 31536000) / 86400, (ctimesec % 86400) / 3600,
01793             (ctimesec % 3600) / 60, ctimesec % 60);
01794    } else if (ctimesec > 86400) {
01795       ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n",
01796             status, credentials, ctimesec / 86400, (ctimesec % 86400) / 3600,
01797             (ctimesec % 3600) / 60, ctimesec % 60);
01798    } else if (ctimesec > 3600) {
01799       ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n",
01800             status, credentials, ctimesec / 3600, (ctimesec % 3600) / 60,
01801             ctimesec % 60);
01802    } else if (ctimesec > 60) {
01803       ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials,
01804                ctimesec / 60, ctimesec % 60);
01805    } else {
01806       ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01807    }
01808 
01809    return CLI_SUCCESS;
01810 }
01811 
01812 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "LDAP realtime interface",
01813    .load = load_module,
01814    .unload = unload_module,
01815    .reload = reload,
01816    .load_pri = AST_MODPRI_REALTIME_DRIVER,
01817 );