Wed Apr 6 11:29:46 2011

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

Generated on Wed Apr 6 11:29:46 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7