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

Generated on Mon Jun 27 16:50:55 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7