Sat Feb 11 06:33:21 2012

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: 332615 $")
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       ast_debug(3, "category: %s\n", category->value);
01096       ast_debug(3, "var_name: %s\n", var_name->value);
01097       ast_debug(3, "var_val: %s\n", var_val->value);
01098       ast_debug(3, "cat_metric: %s\n", cat_metric->value);
01099 
01100       if (!category) {
01101          ast_log(LOG_ERROR, "No category name in entry '%s'  for file '%s'.\n",
01102                (dn ? dn->value : "?"), file);
01103       } else if (!cat_metric) {
01104          ast_log(LOG_ERROR, "No category metric in entry '%s'(category: %s) for file '%s'.\n",
01105                (dn ? dn->value : "?"), category->value, file);
01106       } else if (!var_metric) {
01107          ast_log(LOG_ERROR, "No variable metric in entry '%s'(category: %s) for file '%s'.\n",
01108                (dn ? dn->value : "?"), category->value, file);
01109       } else if (!var_name) {
01110          ast_log(LOG_ERROR, "No variable name in entry '%s' (category: %s metric: %s) for file '%s'.\n",
01111                (dn ? dn->value : "?"), category->value,
01112                cat_metric->value, file);
01113       } else if (!var_val) {
01114          ast_log(LOG_ERROR, "No variable value in entry '%s' (category: %s metric: %s variable: %s) for file '%s'.\n",
01115                (dn ? dn->value : "?"), category->value,
01116                cat_metric->value, var_name->value, file);
01117       } else {
01118          categories[vars_count].name = category->value;
01119          categories[vars_count].metric = atoi(cat_metric->value);
01120          categories[vars_count].variable_name = var_name->value;
01121          categories[vars_count].variable_value = var_val->value;
01122          categories[vars_count].var_metric = atoi(var_metric->value);
01123          vars_count++;
01124       }
01125    }
01126 
01127    qsort(categories, vars_count, sizeof(*categories), compare_categories);
01128 
01129    for (i = 0; i < vars_count; i++) {
01130       if (!strcmp(categories[i].variable_name, "#include")) {
01131          struct ast_flags flags = { 0 };
01132          if (!ast_config_internal_load(categories[i].variable_value, cfg, flags, "", who_asked)) {
01133             break;
01134          }
01135          continue;
01136       }
01137 
01138       if (!last_category || strcmp(last_category, categories[i].name) ||
01139          last_category_metric != categories[i].metric) {
01140 
01141          cur_cat = ast_category_new(categories[i].name, table_name, -1);
01142          if (!cur_cat) {
01143             break;
01144          }
01145          last_category = categories[i].name;
01146          last_category_metric = categories[i].metric;
01147          ast_category_append(cfg, cur_cat);
01148       }
01149 
01150       if (!(new_v = ast_variable_new(categories[i].variable_name, categories[i].variable_value, table_name))) {
01151          break;
01152       }
01153 
01154       ast_variable_append(cur_cat, new_v);
01155    }
01156 
01157    ast_free(vars);
01158    ast_free(categories);
01159 
01160    return cfg;
01161 }
01162 
01163 /* \brief Function to update a set of values in ldap static mode
01164 */
01165 static int update_ldap(const char *basedn, const char *table_name, const char *attribute,
01166    const char *lookup, va_list ap)
01167 {
01168    int error = 0;
01169    LDAPMessage *ldap_entry = NULL;
01170    LDAPMod **ldap_mods;
01171    const char *newparam = NULL;
01172    const char *newval = NULL;
01173    char *dn;
01174    int num_entries = 0;
01175    int i = 0;
01176    int mods_size = 0;
01177    int mod_exists = 0;
01178    struct ldap_table_config *table_config = NULL;
01179    char *clean_basedn = NULL;
01180    struct ast_str *filter = NULL;
01181    int tries = 0;
01182    int result = 0;
01183    LDAPMessage *ldap_result_msg = NULL;
01184 
01185    if (!table_name) {
01186       ast_log(LOG_ERROR, "No table_name specified.\n");
01187       return -1;
01188    } 
01189 
01190    if (!(filter = ast_str_create(80))) {
01191       return -1;
01192    }
01193 
01194    if (!attribute || !lookup) {
01195       ast_log(LOG_WARNING, "LINE(%d): search parameters are empty.\n", __LINE__);
01196       return -1;
01197    }
01198    ast_mutex_lock(&ldap_lock);
01199 
01200    /* We now have our complete statement; Lets connect to the server and execute it.  */
01201    if (!ldap_reconnect()) {
01202       ast_mutex_unlock(&ldap_lock);
01203       return -1;
01204    }
01205 
01206    table_config = table_config_for_table_name(table_name);
01207    if (!table_config) {
01208       ast_log(LOG_ERROR, "No table named '%s'.\n", table_name);
01209       ast_mutex_unlock(&ldap_lock);
01210       return -1;
01211    }
01212 
01213    clean_basedn = cleaned_basedn(NULL, basedn);
01214 
01215    /* Create the filter with the table additional filter and the parameter/value pairs we were given */
01216    ast_str_append(&filter, 0, "(&");
01217    if (table_config && table_config->additional_filter) {
01218       ast_str_append(&filter, 0, "%s", table_config->additional_filter);
01219    }
01220    if (table_config != base_table_config && base_table_config && base_table_config->additional_filter) {
01221       ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
01222    }
01223    append_var_and_value_to_filter(&filter, table_config, attribute, lookup);
01224    ast_str_append(&filter, 0, ")");
01225 
01226    /* Create the modification array with the parameter/value pairs we were given, 
01227     * if there are several parameters with the same name, we collect them into 
01228     * one parameter/value pair and delimit them with a semicolon */
01229    newparam = va_arg(ap, const char *);
01230    newparam = convert_attribute_name_to_ldap(table_config, newparam);
01231    newval = va_arg(ap, const char *);
01232    if (!newparam || !newval) {
01233       ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
01234       return -1;
01235    }
01236 
01237    mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
01238    ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
01239    ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
01240 
01241    ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
01242    ldap_mods[0]->mod_type = ast_strdup(newparam);
01243 
01244    ldap_mods[0]->mod_values = ast_calloc(sizeof(char *), 2);
01245    ldap_mods[0]->mod_values[0] = ast_strdup(newval);
01246 
01247    while ((newparam = va_arg(ap, const char *))) {
01248       newparam = convert_attribute_name_to_ldap(table_config, newparam);
01249       newval = va_arg(ap, const char *);
01250       mod_exists = 0;
01251 
01252       for (i = 0; i < mods_size - 1; i++) {
01253          if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
01254             /* We have the parameter allready, adding the value as a semicolon delimited value */
01255             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));
01256             strcat(ldap_mods[i]->mod_values[0], ";");
01257             strcat(ldap_mods[i]->mod_values[0], newval);
01258             mod_exists = 1;   
01259             break;
01260          }
01261       }
01262 
01263       /* create new mod */
01264       if (!mod_exists) {
01265          mods_size++;
01266          ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
01267          ldap_mods[mods_size - 1] = NULL;
01268          
01269          ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
01270 
01271          ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
01272          strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
01273 
01274          if (strlen(newval) == 0) {
01275             ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_DELETE;
01276          } else {
01277             ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
01278 
01279             ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
01280             ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
01281             strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
01282          }
01283       }
01284    }
01285    /* freeing ldap_mods further down */
01286 
01287    do {
01288       /* freeing ldap_result further down */
01289       result = ldap_search_ext_s(ldapConn, clean_basedn,
01290               LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
01291               &ldap_result_msg);
01292       if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
01293          ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
01294          tries++;
01295          if (tries < 3) {
01296             usleep(500000L * tries);
01297             if (ldapConn) {
01298                ldap_unbind_ext_s(ldapConn, NULL, NULL);
01299                ldapConn = NULL;
01300             }
01301             if (!ldap_reconnect())
01302                break;
01303          }
01304       }
01305    } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
01306 
01307    if (result != LDAP_SUCCESS) {
01308       ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
01309       ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
01310 
01311       ast_mutex_unlock(&ldap_lock);
01312       free(filter);
01313       free(clean_basedn);
01314       ldap_msgfree(ldap_result_msg);
01315       ldap_mods_free(ldap_mods, 0);
01316       return -1;
01317    }
01318    /* Ready to update */
01319    if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
01320       ast_debug(3, "LINE(%d) Modifying %s=%s hits: %d\n", __LINE__, attribute, lookup, num_entries);
01321       for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
01322          if (ldap_mods[i]->mod_op != LDAP_MOD_DELETE) {
01323             ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
01324          } else {
01325             ast_debug(3, "LINE(%d) deleting %s \n", __LINE__, ldap_mods[i]->mod_type);
01326          }
01327       }
01328       ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
01329 
01330       for (i = 0; ldap_entry; i++) { 
01331          dn = ldap_get_dn(ldapConn, ldap_entry);
01332          if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS)  {
01333             ast_log(LOG_ERROR, "Couldn't modify '%s'='%s', dn:%s because %s\n",
01334                   attribute, lookup, dn, ldap_err2string(error));
01335          }
01336          ldap_memfree(dn);
01337          ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
01338       }
01339    }
01340 
01341    ast_mutex_unlock(&ldap_lock);
01342    ast_free(filter);
01343    ast_free(clean_basedn);
01344    ldap_msgfree(ldap_result_msg);
01345    ldap_mods_free(ldap_mods, 0);
01346    return num_entries;
01347 }
01348 
01349 static int update2_ldap(const char *basedn, const char *table_name, va_list ap)
01350 {
01351    int error = 0;
01352    LDAPMessage *ldap_entry = NULL;
01353    LDAPMod **ldap_mods;
01354    const char *newparam = NULL;
01355    const char *newval = NULL;
01356    char *dn;
01357    int num_entries = 0;
01358    int i = 0;
01359    int mods_size = 0;
01360    int mod_exists = 0;
01361    struct ldap_table_config *table_config = NULL;
01362    char *clean_basedn = NULL;
01363    struct ast_str *filter = NULL;
01364    int tries = 0;
01365    int result = 0;
01366    LDAPMessage *ldap_result_msg = NULL;
01367 
01368    if (!table_name) {
01369       ast_log(LOG_ERROR, "No table_name specified.\n");
01370       return -1;
01371    } 
01372 
01373    if (!(filter = ast_str_create(80))) {
01374       return -1;
01375    }
01376 
01377    ast_mutex_lock(&ldap_lock);
01378 
01379    /* We now have our complete statement; Lets connect to the server and execute it.  */
01380    if (!ldap_reconnect()) {
01381       ast_mutex_unlock(&ldap_lock);
01382       ast_free(filter);
01383       return -1;
01384    }
01385 
01386    table_config = table_config_for_table_name(table_name);
01387    if (!table_config) {
01388       ast_log(LOG_ERROR, "No table named '%s'.\n", table_name);
01389       ast_mutex_unlock(&ldap_lock);
01390       ast_free(filter);
01391       return -1;
01392    }
01393 
01394    clean_basedn = cleaned_basedn(NULL, basedn);
01395 
01396    /* Create the filter with the table additional filter and the parameter/value pairs we were given */
01397    ast_str_append(&filter, 0, "(&");
01398    if (table_config && table_config->additional_filter) {
01399       ast_str_append(&filter, 0, "%s", table_config->additional_filter);
01400    }
01401    if (table_config != base_table_config && base_table_config
01402       && base_table_config->additional_filter) {
01403       ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
01404    }
01405 
01406    /* Get multiple lookup keyfields and values */
01407    while ((newparam = va_arg(ap, const char *))) {
01408       newval = va_arg(ap, const char *);
01409       append_var_and_value_to_filter(&filter, table_config, newparam, newval);
01410    }
01411    ast_str_append(&filter, 0, ")");
01412 
01413    /* Create the modification array with the parameter/value pairs we were given, 
01414     * if there are several parameters with the same name, we collect them into 
01415     * one parameter/value pair and delimit them with a semicolon */
01416    newparam = va_arg(ap, const char *);
01417    newparam = convert_attribute_name_to_ldap(table_config, newparam);
01418    newval = va_arg(ap, const char *);
01419    if (!newparam || !newval) {
01420       ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
01421       ast_free(filter);
01422       ast_free(clean_basedn);
01423       return -1;
01424    }
01425 
01426    mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
01427    ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
01428    ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
01429 
01430    ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
01431    ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
01432    strcpy(ldap_mods[0]->mod_type, newparam);
01433 
01434    ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2);
01435    ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
01436    strcpy(ldap_mods[0]->mod_values[0], newval);
01437 
01438    while ((newparam = va_arg(ap, const char *))) {
01439       newparam = convert_attribute_name_to_ldap(table_config, newparam);
01440       newval = va_arg(ap, const char *);
01441       mod_exists = 0;
01442 
01443       for (i = 0; i < mods_size - 1; i++) {
01444          if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
01445             /* We have the parameter allready, adding the value as a semicolon delimited value */
01446             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));
01447             strcat(ldap_mods[i]->mod_values[0], ";");
01448             strcat(ldap_mods[i]->mod_values[0], newval);
01449             mod_exists = 1;   
01450             break;
01451          }
01452       }
01453 
01454       /* create new mod */
01455       if (!mod_exists) {
01456          mods_size++;
01457          ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
01458          ldap_mods[mods_size - 1] = NULL;
01459          ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
01460 
01461          ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
01462 
01463          ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
01464          strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
01465 
01466          ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
01467          ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
01468          strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
01469       }
01470    }
01471    /* freeing ldap_mods further down */
01472 
01473    do {
01474       /* freeing ldap_result further down */
01475       result = ldap_search_ext_s(ldapConn, clean_basedn,
01476               LDAP_SCOPE_SUBTREE, ast_str_buffer(filter), NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
01477               &ldap_result_msg);
01478       if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
01479          ast_log(LOG_WARNING, "Failed to query directory. Try %d/3\n", tries + 1);
01480          tries++;
01481          if (tries < 3) {
01482             usleep(500000L * tries);
01483             if (ldapConn) {
01484                ldap_unbind_ext_s(ldapConn, NULL, NULL);
01485                ldapConn = NULL;
01486             }
01487             if (!ldap_reconnect()) {
01488                break;
01489             }
01490          }
01491       }
01492    } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
01493 
01494    if (result != LDAP_SUCCESS) {
01495       ast_log(LOG_WARNING, "Failed to query directory. Error: %s.\n", ldap_err2string(result));
01496       ast_log(LOG_WARNING, "Query: %s\n", ast_str_buffer(filter));
01497 
01498       ast_mutex_unlock(&ldap_lock);
01499       ast_free(filter);
01500       ast_free(clean_basedn);
01501       ldap_msgfree(ldap_result_msg);
01502       ldap_mods_free(ldap_mods, 0);
01503       return -1;
01504    }
01505    /* Ready to update */
01506    if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
01507       for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
01508          ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
01509       }
01510 
01511       ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
01512 
01513       for (i = 0; ldap_entry; i++) {
01514          dn = ldap_get_dn(ldapConn, ldap_entry);
01515          if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS)  {
01516             ast_log(LOG_ERROR, "Couldn't modify dn:%s because %s", dn, ldap_err2string(error));
01517          }
01518          ldap_memfree(dn);
01519          ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
01520       }
01521    }
01522 
01523    ast_mutex_unlock(&ldap_lock);
01524    if (filter) {
01525       ast_free(filter);
01526    }
01527    if (clean_basedn) {
01528       ast_free(clean_basedn);
01529    }
01530    ldap_msgfree(ldap_result_msg);
01531    ldap_mods_free(ldap_mods, 0);
01532    return num_entries;
01533 }
01534 
01535 static struct ast_config_engine ldap_engine = {
01536    .name = "ldap",
01537    .load_func = config_ldap,
01538    .realtime_func = realtime_ldap,
01539    .realtime_multi_func = realtime_multi_ldap,
01540    .update_func = update_ldap,
01541    .update2_func = update2_ldap,
01542 };
01543 
01544 static int load_module(void)
01545 {
01546    if (parse_config() < 0) {
01547       ast_log(LOG_ERROR, "Cannot load LDAP RealTime driver.\n");
01548       return 0;
01549    }
01550 
01551    ast_mutex_lock(&ldap_lock);
01552 
01553    if (!ldap_reconnect())  {
01554       ast_log(LOG_WARNING, "Couldn't establish connection to LDAP directory. Check debug.\n");
01555    }
01556 
01557    ast_config_engine_register(&ldap_engine);
01558    ast_verb(1, "LDAP RealTime driver loaded.\n");
01559    ast_cli_register_multiple(ldap_cli, ARRAY_LEN(ldap_cli));
01560 
01561    ast_mutex_unlock(&ldap_lock);
01562 
01563    return 0;
01564 }
01565 
01566 static int unload_module(void)
01567 {
01568    /* Aquire control before doing anything to the module itself. */
01569    ast_mutex_lock(&ldap_lock);
01570 
01571    table_configs_free();
01572 
01573    if (ldapConn) {
01574       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01575       ldapConn = NULL;
01576    }
01577    ast_cli_unregister_multiple(ldap_cli, ARRAY_LEN(ldap_cli));
01578    ast_config_engine_deregister(&ldap_engine);
01579    ast_verb(1, "LDAP RealTime driver unloaded.\n");
01580 
01581    /* Unlock so something else can destroy the lock. */
01582    ast_mutex_unlock(&ldap_lock);
01583 
01584    return 0;
01585 }
01586 
01587 static int reload(void)
01588 {
01589    /* Aquire control before doing anything to the module itself. */
01590    ast_mutex_lock(&ldap_lock);
01591 
01592    if (ldapConn) {
01593       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01594       ldapConn = NULL;
01595    }
01596 
01597    if (parse_config() < 0) {
01598       ast_log(LOG_NOTICE, "Cannot reload LDAP RealTime driver.\n");
01599       ast_mutex_unlock(&ldap_lock);
01600       return 0;
01601    }     
01602 
01603    if (!ldap_reconnect())  {
01604       ast_log(LOG_WARNING, "Couldn't establish connection to your directory server. Check debug.\n");
01605    }
01606 
01607    ast_verb(2, "LDAP RealTime driver reloaded.\n");
01608 
01609    /* Done reloading. Release lock so others can now use driver. */
01610    ast_mutex_unlock(&ldap_lock);
01611 
01612    return 0;
01613 }
01614 
01615 /*! \brief parse the configuration file */
01616 static int parse_config(void)
01617 {
01618    struct ast_config *config;
01619    struct ast_flags config_flags = {0};
01620    const char *s, *host;
01621    int port;
01622    char *category_name = NULL;
01623 
01624    /* Make sure that global variables are reset */
01625    url[0] = '\0';
01626    user[0] = '\0';
01627    pass[0] = '\0';
01628    base_distinguished_name[0] = '\0';
01629    version = 3;
01630 
01631    config = ast_config_load(RES_CONFIG_LDAP_CONF, config_flags);
01632    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01633       ast_log(LOG_ERROR, "Cannot load configuration file: %s\n", RES_CONFIG_LDAP_CONF);
01634       return -1;
01635    }
01636 
01637    if (!(s = ast_variable_retrieve(config, "_general", "user"))) {
01638       ast_log(LOG_NOTICE, "No directory user found, anonymous binding as default.\n");
01639       user[0] = '\0';
01640    } else {
01641       ast_copy_string(user, s, sizeof(user));
01642    }
01643 
01644    if (!ast_strlen_zero(user)) {
01645       if (!(s = ast_variable_retrieve(config, "_general", "pass"))) {
01646          ast_log(LOG_WARNING, "No directory password found, using 'asterisk' as default.\n");
01647          ast_copy_string(pass, "asterisk", sizeof(pass));
01648       } else {
01649          ast_copy_string(pass, s, sizeof(pass));
01650       }
01651    }
01652 
01653    /* URL is preferred, use host and port if not found */
01654    if ((s = ast_variable_retrieve(config, "_general", "url"))) {
01655       ast_copy_string(url, s, sizeof(url));
01656    } else if ((host = ast_variable_retrieve(config, "_general", "host"))) {
01657       if (!(s = ast_variable_retrieve(config, "_general", "port")) || sscanf(s, "%5d", &port) != 1 || port > 65535) {
01658          ast_log(LOG_NOTICE, "No directory port found, using 389 as default.\n");
01659          port = 389;
01660       }
01661 
01662       snprintf(url, sizeof(url), "ldap://%s:%d", host, port);
01663    } else {
01664       ast_log(LOG_ERROR, "No directory URL or host found.\n");
01665       ast_config_destroy(config);
01666       return -1;
01667    }
01668 
01669    if (!(s = ast_variable_retrieve(config, "_general", "basedn"))) {
01670       ast_log(LOG_ERROR, "No LDAP base dn found, using '%s' as default.\n", RES_CONFIG_LDAP_DEFAULT_BASEDN);
01671       ast_copy_string(base_distinguished_name, RES_CONFIG_LDAP_DEFAULT_BASEDN, sizeof(base_distinguished_name));
01672    } else 
01673       ast_copy_string(base_distinguished_name, s, sizeof(base_distinguished_name));
01674 
01675    if (!(s = ast_variable_retrieve(config, "_general", "version")) && !(s = ast_variable_retrieve(config, "_general", "protocol"))) {
01676       ast_log(LOG_NOTICE, "No explicit LDAP version found, using 3 as default.\n");
01677    } else if (sscanf(s, "%30d", &version) != 1 || version < 1 || version > 6) {
01678       ast_log(LOG_WARNING, "Invalid LDAP version '%s', using 3 as default.\n", s);
01679       version = 3;
01680    }
01681 
01682    table_configs_free();
01683 
01684    while ((category_name = ast_category_browse(config, category_name))) {
01685       int is_general = (strcasecmp(category_name, "_general") == 0);
01686       int is_config = (strcasecmp(category_name, "config") == 0); /*!< using the [config] context for Static RealTime */
01687       struct ast_variable *var = ast_variable_browse(config, category_name);
01688       
01689       if (var) {
01690          struct ldap_table_config *table_config =
01691             table_config_for_table_name(category_name);
01692          if (!table_config) {
01693             table_config = table_config_new(category_name);
01694             AST_LIST_INSERT_HEAD(&table_configs, table_config, entry);
01695             if (is_general)
01696                base_table_config = table_config;
01697             if (is_config)
01698                static_table_config = table_config;
01699          }
01700          for (; var; var = var->next) {
01701             if (!strcasecmp(var->name, "additionalFilter")) {
01702                table_config->additional_filter = ast_strdup(var->value);
01703             } else {
01704                ldap_table_config_add_attribute(table_config, var->name, var->value);
01705             }
01706          }
01707       }
01708    }
01709 
01710    ast_config_destroy(config);
01711 
01712    return 1;
01713 }
01714 
01715 /*! \note ldap_lock should have been locked before calling this function. */
01716 static int ldap_reconnect(void)
01717 {
01718    int bind_result = 0;
01719    struct berval cred;
01720 
01721    if (ldapConn) {
01722       ast_debug(2, "Everything seems fine.\n");
01723       return 1;
01724    }
01725 
01726    if (ast_strlen_zero(url)) {
01727       ast_log(LOG_ERROR, "Not enough parameters to connect to ldap directory\n");
01728       return 0;
01729    }
01730 
01731    if (LDAP_SUCCESS != ldap_initialize(&ldapConn, url)) {
01732       ast_log(LOG_ERROR, "Failed to init ldap connection to '%s'. Check debug for more info.\n", url);
01733       return 0;
01734    }
01735 
01736    if (LDAP_OPT_SUCCESS != ldap_set_option(ldapConn, LDAP_OPT_PROTOCOL_VERSION, &version)) {
01737       ast_log(LOG_WARNING, "Unable to set LDAP protocol version to %d, falling back to default.\n", version);
01738    }
01739 
01740    if (!ast_strlen_zero(user)) {
01741       ast_debug(2, "bind to '%s' as user '%s'\n", url, user);
01742       cred.bv_val = (char *) pass;
01743       cred.bv_len = strlen(pass);
01744       bind_result = ldap_sasl_bind_s(ldapConn, user, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
01745    } else {
01746       ast_debug(2, "bind %s anonymously\n", url);
01747       cred.bv_val = NULL;
01748       cred.bv_len = 0;
01749       bind_result = ldap_sasl_bind_s(ldapConn, NULL, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
01750    }
01751    if (bind_result == LDAP_SUCCESS) {
01752       ast_debug(2, "Successfully connected to directory.\n");
01753       connect_time = time(NULL);
01754       return 1;
01755    } else {
01756       ast_log(LOG_WARNING, "bind failed: %s\n", ldap_err2string(bind_result));
01757       ldap_unbind_ext_s(ldapConn, NULL, NULL);
01758       ldapConn = NULL;
01759       return 0;
01760    }
01761 }
01762 
01763 static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01764 {
01765    char status[256], credentials[100] = "";
01766    int ctimesec = time(NULL) - connect_time;
01767 
01768    switch (cmd) {
01769    case CLI_INIT:
01770       e->command = "realtime show ldap status";
01771       e->usage =
01772          "Usage: realtime show ldap status\n"
01773          "         Shows connection information for the LDAP RealTime driver\n";
01774       return NULL;
01775    case CLI_GENERATE:
01776       return NULL;
01777    }
01778 
01779    if (!ldapConn)
01780       return CLI_FAILURE;
01781 
01782    if (!ast_strlen_zero(url)) 
01783       snprintf(status, sizeof(status), "Connected to '%s', baseDN %s", url, base_distinguished_name);
01784 
01785    if (!ast_strlen_zero(user))
01786       snprintf(credentials, sizeof(credentials), " with username %s", user);
01787 
01788    if (ctimesec > 31536000) {
01789       ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01790             status, credentials, ctimesec / 31536000,
01791             (ctimesec % 31536000) / 86400, (ctimesec % 86400) / 3600,
01792             (ctimesec % 3600) / 60, ctimesec % 60);
01793    } else if (ctimesec > 86400) {
01794       ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n",
01795             status, credentials, ctimesec / 86400, (ctimesec % 86400) / 3600,
01796             (ctimesec % 3600) / 60, ctimesec % 60);
01797    } else if (ctimesec > 3600) {
01798       ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n",
01799             status, credentials, ctimesec / 3600, (ctimesec % 3600) / 60,
01800             ctimesec % 60);
01801    } else if (ctimesec > 60) {
01802       ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials,
01803                ctimesec / 60, ctimesec % 60);
01804    } else {
01805       ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01806    }
01807 
01808    return CLI_SUCCESS;
01809 }
01810 
01811 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "LDAP realtime interface",
01812    .load = load_module,
01813    .unload = unload_module,
01814    .reload = reload,
01815    .load_pri = AST_MODPRI_REALTIME_DRIVER,
01816 );

Generated on Sat Feb 11 06:33:21 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6