Thu Sep 7 01:03:03 2017

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1