Mon Jun 27 16:50:56 2011

Asterisk developer's documentation


res_phoneprov.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2008, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  * Matthew Brooks <mbrooks@digium.com>
00008  * Terry Wilson <twilson@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 /*! \file
00022  *
00023  * \brief Phone provisioning application for the asterisk internal http server
00024  *
00025  * \author Matthew Brooks <mbrooks@digium.com>
00026  * \author Terry Wilson <twilson@digium.com>
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 #include <sys/ioctl.h>
00032 #include <sys/socket.h>
00033 #include <net/if.h>
00034 #ifdef SOLARIS
00035 #include <sys/sockio.h>
00036 #endif
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 270660 $")
00038 
00039 #include "asterisk/channel.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/paths.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/http.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/strings.h"
00049 #include "asterisk/stringfields.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/acl.h"
00053 #include "asterisk/astobj2.h"
00054 #include "asterisk/ast_version.h"
00055 
00056 #ifdef LOW_MEMORY
00057 #define MAX_PROFILE_BUCKETS 1
00058 #define MAX_ROUTE_BUCKETS 1
00059 #define MAX_USER_BUCKETS 1
00060 #else
00061 #define MAX_PROFILE_BUCKETS 17
00062 #define MAX_ROUTE_BUCKETS 563
00063 #define MAX_USER_BUCKETS 563
00064 #endif /* LOW_MEMORY */
00065 
00066 #define VAR_BUF_SIZE 4096
00067 
00068 /*** DOCUMENTATION
00069    <function name="PP_EACH_EXTENSION" language="en_US">
00070       <synopsis>
00071          Execute specified template for each extension.
00072       </synopsis>
00073       <syntax>
00074          <parameter name="mac" required="true" />
00075          <parameter name="template" required="true" />
00076       </syntax>
00077       <description>
00078          <para>Output the specified template for each extension associated with the specified MAC address.</para>
00079       </description>
00080    </function>
00081    <function name="PP_EACH_USER" language="en_US">
00082       <synopsis>
00083          Generate a string for each phoneprov user.
00084       </synopsis>
00085       <syntax>
00086          <parameter name="string" required="true" />
00087          <parameter name="exclude_mac" required="true" />
00088       </syntax>
00089       <description>
00090          <para>Pass in a string, with phoneprov variables you want substituted in the format of
00091          %{VARNAME}, and you will get the string rendered for each user in phoneprov
00092          excluding ones with MAC address <replaceable>exclude_mac</replaceable>. Probably not
00093          useful outside of res_phoneprov.</para>
00094          <para>Example: ${PP_EACH_USER(&lt;item&gt;&lt;fn&gt;%{DISPLAY_NAME}&lt;/fn&gt;&lt;/item&gt;|${MAC})</para>
00095       </description>
00096    </function>
00097  ***/
00098 
00099 /*! \brief for use in lookup_iface */
00100 static struct in_addr __ourip = { .s_addr = 0x00000000, };
00101 
00102 /* \note This enum and the pp_variable_list must be in the same order or
00103  * bad things happen! */
00104 enum pp_variables {
00105    PP_MACADDRESS,
00106    PP_USERNAME,
00107    PP_FULLNAME,
00108    PP_SECRET,
00109    PP_LABEL,
00110    PP_CALLERID,
00111    PP_TIMEZONE,
00112    PP_LINENUMBER,
00113    PP_LINEKEYS,
00114    PP_VAR_LIST_LENGTH,  /* This entry must always be the last in the list */
00115 };
00116 
00117 /*! \brief Lookup table to translate between users.conf property names and
00118  * variables for use in phoneprov templates */
00119 static const struct pp_variable_lookup {
00120    enum pp_variables id;
00121    const char * const user_var;
00122    const char * const template_var;
00123 } pp_variable_list[] = {
00124    { PP_MACADDRESS, "macaddress", "MAC" },
00125    { PP_USERNAME, "username", "USERNAME" },
00126    { PP_FULLNAME, "fullname", "DISPLAY_NAME" },
00127    { PP_SECRET, "secret", "SECRET" },
00128    { PP_LABEL, "label", "LABEL" },
00129    { PP_CALLERID, "cid_number", "CALLERID" },
00130    { PP_TIMEZONE, "timezone", "TIMEZONE" },
00131    { PP_LINENUMBER, "linenumber", "LINE" },
00132    { PP_LINEKEYS, "linekeys", "LINEKEYS" },
00133 };
00134 
00135 /*! \brief structure to hold file data */
00136 struct phoneprov_file {
00137    AST_DECLARE_STRING_FIELDS(
00138       AST_STRING_FIELD(format);  /*!< After variable substitution, becomes route->uri */
00139       AST_STRING_FIELD(template); /*!< Template/physical file location */
00140       AST_STRING_FIELD(mime_type);/*!< Mime-type of the file */
00141    );
00142    AST_LIST_ENTRY(phoneprov_file) entry;
00143 };
00144 
00145 /*! \brief structure to hold phone profiles read from phoneprov.conf */
00146 struct phone_profile {
00147    AST_DECLARE_STRING_FIELDS(
00148       AST_STRING_FIELD(name); /*!< Name of phone profile */
00149       AST_STRING_FIELD(default_mime_type);   /*!< Default mime type if it isn't provided */
00150       AST_STRING_FIELD(staticdir);  /*!< Subdirectory that static files are stored in */
00151    );
00152    struct varshead *headp; /*!< List of variables set with 'setvar' in phoneprov.conf */
00153    AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files;  /*!< List of static files */
00154    AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files; /*!< List of dynamic files */
00155 };
00156 
00157 struct extension {
00158    AST_DECLARE_STRING_FIELDS(
00159       AST_STRING_FIELD(name);
00160    );
00161    int index;
00162    struct varshead *headp; /*!< List of variables to substitute into templates */
00163    AST_LIST_ENTRY(extension) entry;
00164 };
00165 
00166 /*! \brief structure to hold users read from users.conf */
00167 struct user {
00168    AST_DECLARE_STRING_FIELDS(
00169       AST_STRING_FIELD(macaddress); /*!< Mac address of user's phone */
00170    );
00171    struct phone_profile *profile;   /*!< Profile the phone belongs to */
00172    AST_LIST_HEAD_NOLOCK(, extension) extensions;
00173 };
00174 
00175 /*! \brief structure to hold http routes (valid URIs, and the files they link to) */
00176 struct http_route {
00177    AST_DECLARE_STRING_FIELDS(
00178       AST_STRING_FIELD(uri);  /*!< The URI requested */
00179    );
00180    struct phoneprov_file *file;  /*!< The file that links to the URI */
00181    struct user *user;   /*!< The user that has variables to substitute into the file
00182                    * NULL in the case of a static route */
00183 };
00184 
00185 static struct ao2_container *profiles;
00186 static struct ao2_container *http_routes;
00187 static struct ao2_container *users;
00188 
00189 static char global_server[80] = ""; /*!< Server to substitute into templates */
00190 static char global_serverport[6] = ""; /*!< Server port to substitute into templates */
00191 static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
00192 
00193 /*! \brief List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH */
00194 static struct varshead global_variables;
00195 static ast_mutex_t globals_lock;
00196 
00197 /* iface is the interface (e.g. eth0); address is the return value */
00198 static int lookup_iface(const char *iface, struct in_addr *address)
00199 {
00200    int mysock, res = 0;
00201    struct ifreq ifr;
00202    struct sockaddr_in *sin;
00203 
00204    memset(&ifr, 0, sizeof(ifr));
00205    ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
00206 
00207    mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
00208    if (mysock < 0) {
00209       ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
00210       return -1;
00211    }
00212 
00213    res = ioctl(mysock, SIOCGIFADDR, &ifr);
00214 
00215    close(mysock);
00216 
00217    if (res < 0) {
00218       ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
00219       memcpy(address, &__ourip, sizeof(__ourip));
00220       return -1;
00221    } else {
00222       sin = (struct sockaddr_in *)&ifr.ifr_addr;
00223       memcpy(address, &sin->sin_addr, sizeof(*address));
00224       return 0;
00225    }
00226 }
00227 
00228 static struct phone_profile *unref_profile(struct phone_profile *prof)
00229 {
00230    ao2_ref(prof, -1);
00231 
00232    return NULL;
00233 }
00234 
00235 /*! \brief Return a phone profile looked up by name */
00236 static struct phone_profile *find_profile(const char *name)
00237 {
00238    struct phone_profile tmp = {
00239       .name = name,
00240    };
00241 
00242    return ao2_find(profiles, &tmp, OBJ_POINTER);
00243 }
00244 
00245 static int profile_hash_fn(const void *obj, const int flags)
00246 {
00247    const struct phone_profile *profile = obj;
00248 
00249    return ast_str_case_hash(profile->name);
00250 }
00251 
00252 static int profile_cmp_fn(void *obj, void *arg, int flags)
00253 {
00254    const struct phone_profile *profile1 = obj, *profile2 = arg;
00255 
00256    return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH | CMP_STOP : 0;
00257 }
00258 
00259 static void delete_file(struct phoneprov_file *file)
00260 {
00261    ast_string_field_free_memory(file);
00262    free(file);
00263 }
00264 
00265 static void profile_destructor(void *obj)
00266 {
00267    struct phone_profile *profile = obj;
00268    struct phoneprov_file *file;
00269    struct ast_var_t *var;
00270 
00271    while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry)))
00272       delete_file(file);
00273 
00274    while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry)))
00275       delete_file(file);
00276 
00277    while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries)))
00278       ast_var_delete(var);
00279 
00280    ast_free(profile->headp);
00281    ast_string_field_free_memory(profile);
00282 }
00283 
00284 static struct http_route *unref_route(struct http_route *route)
00285 {
00286    ao2_ref(route, -1);
00287 
00288    return NULL;
00289 }
00290 
00291 static int routes_hash_fn(const void *obj, const int flags)
00292 {
00293    const struct http_route *route = obj;
00294 
00295    return ast_str_case_hash(route->uri);
00296 }
00297 
00298 static int routes_cmp_fn(void *obj, void *arg, int flags)
00299 {
00300    const struct http_route *route1 = obj, *route2 = arg;
00301 
00302    return !strcasecmp(route1->uri, route2->uri) ? CMP_MATCH | CMP_STOP : 0;
00303 }
00304 
00305 static void route_destructor(void *obj)
00306 {
00307    struct http_route *route = obj;
00308 
00309    ast_string_field_free_memory(route);
00310 }
00311 
00312 /*! \brief Read a TEXT file into a string and return the length */
00313 static int load_file(const char *filename, char **ret)
00314 {
00315    int len = 0;
00316    FILE *f;
00317 
00318    if (!(f = fopen(filename, "r"))) {
00319       *ret = NULL;
00320       return -1;
00321    }
00322 
00323    fseek(f, 0, SEEK_END);
00324    len = ftell(f);
00325    fseek(f, 0, SEEK_SET);
00326    if (!(*ret = ast_malloc(len + 1)))
00327       return -2;
00328 
00329    if (len != fread(*ret, sizeof(char), len, f)) {
00330       free(*ret);
00331       *ret = NULL;
00332       return -3;
00333    }
00334 
00335    fclose(f);
00336 
00337    (*ret)[len] = '\0';
00338 
00339    return len;
00340 }
00341 
00342 /*! \brief Set all timezone-related variables based on a zone (i.e. America/New_York)
00343    \param headp pointer to list of user variables
00344    \param zone A time zone. NULL sets variables based on timezone of the machine
00345 */
00346 static void set_timezone_variables(struct varshead *headp, const char *zone)
00347 {
00348    time_t utc_time;
00349    int dstenable;
00350    time_t dststart;
00351    time_t dstend;
00352    struct ast_tm tm_info;
00353    int tzoffset;
00354    char buffer[21];
00355    struct ast_var_t *var;
00356    struct timeval when;
00357 
00358    time(&utc_time);
00359    ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
00360    snprintf(buffer, sizeof(buffer), "%d", tzoffset);
00361    var = ast_var_assign("TZOFFSET", buffer);
00362    if (var)
00363       AST_LIST_INSERT_TAIL(headp, var, entries);
00364 
00365    if (!dstenable)
00366       return;
00367 
00368    if ((var = ast_var_assign("DST_ENABLE", "1")))
00369       AST_LIST_INSERT_TAIL(headp, var, entries);
00370 
00371    when.tv_sec = dststart;
00372    ast_localtime(&when, &tm_info, zone);
00373 
00374    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
00375    if ((var = ast_var_assign("DST_START_MONTH", buffer)))
00376       AST_LIST_INSERT_TAIL(headp, var, entries);
00377 
00378    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00379    if ((var = ast_var_assign("DST_START_MDAY", buffer)))
00380       AST_LIST_INSERT_TAIL(headp, var, entries);
00381 
00382    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00383    if ((var = ast_var_assign("DST_START_HOUR", buffer)))
00384       AST_LIST_INSERT_TAIL(headp, var, entries);
00385 
00386    when.tv_sec = dstend;
00387    ast_localtime(&when, &tm_info, zone);
00388 
00389    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
00390    if ((var = ast_var_assign("DST_END_MONTH", buffer)))
00391       AST_LIST_INSERT_TAIL(headp, var, entries);
00392 
00393    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00394    if ((var = ast_var_assign("DST_END_MDAY", buffer)))
00395       AST_LIST_INSERT_TAIL(headp, var, entries);
00396 
00397    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00398    if ((var = ast_var_assign("DST_END_HOUR", buffer)))
00399       AST_LIST_INSERT_TAIL(headp, var, entries);
00400 }
00401 
00402 /*! \brief Callback that is executed everytime an http request is received by this module */
00403 static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
00404 {
00405    struct http_route *route;
00406    struct http_route search_route = {
00407       .uri = uri,
00408    };
00409    struct ast_str *result;
00410    char path[PATH_MAX];
00411    char *file = NULL;
00412    int len;
00413    int fd;
00414    struct ast_str *http_header;
00415 
00416    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00417       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00418       return -1;
00419    }
00420 
00421    if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
00422       goto out404;
00423    }
00424 
00425    snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
00426 
00427    if (!route->user) { /* Static file */
00428 
00429       fd = open(path, O_RDONLY);
00430       if (fd < 0) {
00431          goto out500;
00432       }
00433 
00434       len = lseek(fd, 0, SEEK_END);
00435       lseek(fd, 0, SEEK_SET);
00436       if (len < 0) {
00437          ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00438          close(fd);
00439          goto out500;
00440       }
00441 
00442       http_header = ast_str_create(80);
00443       ast_str_set(&http_header, 0, "Content-type: %s",
00444          route->file->mime_type);
00445 
00446       ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
00447 
00448       close(fd);
00449       route = unref_route(route);
00450       return 0;
00451    } else { /* Dynamic file */
00452       struct ast_str *tmp;
00453 
00454       len = load_file(path, &file);
00455       if (len < 0) {
00456          ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00457          if (file) {
00458             ast_free(file);
00459          }
00460 
00461          goto out500;
00462       }
00463 
00464       if (!file) {
00465          goto out500;
00466       }
00467 
00468       if (!(tmp = ast_str_create(len))) {
00469          if (file) {
00470             ast_free(file);
00471          }
00472 
00473          goto out500;
00474       }
00475 
00476       /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
00477        * the IP address we are listening on that the phone contacted for this config file */
00478       if (ast_strlen_zero(global_server)) {
00479          union {
00480             struct sockaddr sa;
00481             struct sockaddr_in sa_in;
00482          } name;
00483          socklen_t namelen = sizeof(name.sa);
00484          int res;
00485 
00486          if ((res = getsockname(ser->fd, &name.sa, &namelen))) {
00487             ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00488          } else {
00489             struct ast_var_t *var;
00490             struct extension *exten_iter;
00491 
00492             if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr)))) {
00493                AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
00494                   AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries);
00495                }
00496             }
00497          }
00498       }
00499 
00500       ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
00501 
00502       if (file) {
00503          ast_free(file);
00504       }
00505 
00506       http_header = ast_str_create(80);
00507       ast_str_set(&http_header, 0, "Content-type: %s",
00508          route->file->mime_type);
00509 
00510       if (!(result = ast_str_create(512))) {
00511          ast_log(LOG_ERROR, "Could not create result string!\n");
00512          if (tmp) {
00513             ast_free(tmp);
00514          }
00515          ast_free(http_header);
00516          goto out500;
00517       }
00518       ast_str_append(&result, 0, "%s", ast_str_buffer(tmp)); 
00519 
00520       ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
00521       if (tmp) {
00522          ast_free(tmp);
00523       }
00524 
00525       route = unref_route(route);
00526 
00527       return 0;
00528    }
00529 
00530 out404:
00531    ast_http_error(ser, 404, "Not Found", "Nothing to see here.  Move along.");
00532    return -1;
00533 
00534 out500:
00535    route = unref_route(route);
00536    ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
00537    return -1;
00538 }
00539 
00540 /*! \brief Build a route structure and add it to the list of available http routes
00541    \param pp_file File to link to the route
00542    \param user User to link to the route (NULL means static route)
00543    \param uri URI of the route
00544 */
00545 static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
00546 {
00547    struct http_route *route;
00548 
00549    if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
00550       return;
00551    }
00552 
00553    if (ast_string_field_init(route, 32)) {
00554       ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
00555       route = unref_route(route);
00556       return;
00557    }
00558 
00559    ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
00560    route->user = user;
00561    route->file = pp_file;
00562 
00563    ao2_link(http_routes, route);
00564 
00565    route = unref_route(route);
00566 }
00567 
00568 /*! \brief Build a phone profile and add it to the list of phone profiles
00569    \param name the name of the profile
00570    \param v ast_variable from parsing phoneprov.conf
00571 */
00572 static void build_profile(const char *name, struct ast_variable *v)
00573 {
00574    struct phone_profile *profile;
00575    struct ast_var_t *var;
00576 
00577    if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
00578       return;
00579    }
00580 
00581    if (ast_string_field_init(profile, 32)) {
00582       profile = unref_profile(profile);
00583       return;
00584    }
00585 
00586    if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
00587       profile = unref_profile(profile);
00588       return;
00589    }
00590 
00591    AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
00592    AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
00593 
00594    ast_string_field_set(profile, name, name);
00595    for (; v; v = v->next) {
00596       if (!strcasecmp(v->name, "mime_type")) {
00597          ast_string_field_set(profile, default_mime_type, v->value);
00598       } else if (!strcasecmp(v->name, "setvar")) {
00599          struct ast_var_t *variable;
00600          char *value_copy = ast_strdupa(v->value);
00601 
00602          AST_DECLARE_APP_ARGS(args,
00603             AST_APP_ARG(varname);
00604             AST_APP_ARG(varval);
00605          );
00606 
00607          AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
00608          do {
00609             if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00610                break;
00611             args.varname = ast_strip(args.varname);
00612             args.varval = ast_strip(args.varval);
00613             if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00614                break;
00615             if ((variable = ast_var_assign(args.varname, args.varval)))
00616                AST_LIST_INSERT_TAIL(profile->headp, variable, entries);
00617          } while (0);
00618       } else if (!strcasecmp(v->name, "staticdir")) {
00619          ast_string_field_set(profile, staticdir, v->value);
00620       } else {
00621          struct phoneprov_file *pp_file;
00622          char *file_extension;
00623          char *value_copy = ast_strdupa(v->value);
00624 
00625          AST_DECLARE_APP_ARGS(args,
00626             AST_APP_ARG(filename);
00627             AST_APP_ARG(mimetype);
00628          );
00629 
00630          if (!(pp_file = ast_calloc_with_stringfields(1, struct phoneprov_file, 32))) {
00631             profile = unref_profile(profile);
00632             return;
00633          }
00634 
00635          if ((file_extension = strrchr(pp_file->format, '.')))
00636             file_extension++;
00637 
00638          AST_STANDARD_APP_ARGS(args, value_copy);
00639 
00640          /* Mime type order of preference
00641           * 1) Specific mime-type defined for file in profile
00642           * 2) Mime determined by extension
00643           * 3) Default mime type specified in profile
00644           * 4) text/plain
00645           */
00646          ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
00647             (S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
00648 
00649          if (!strcasecmp(v->name, "static_file")) {
00650             ast_string_field_set(pp_file, format, args.filename);
00651             ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
00652             AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
00653             /* Add a route for the static files, as their filenames won't change per-user */
00654             build_route(pp_file, NULL, NULL);
00655          } else {
00656             ast_string_field_set(pp_file, format, v->name);
00657             ast_string_field_set(pp_file, template, args.filename);
00658             AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
00659          }
00660       }
00661    }
00662 
00663    /* Append the global variables to the variables list for this profile.
00664     * This is for convenience later, when we need to provide a single
00665     * variable list for use in substitution. */
00666    ast_mutex_lock(&globals_lock);
00667    AST_LIST_TRAVERSE(&global_variables, var, entries) {
00668       struct ast_var_t *new_var;
00669       if ((new_var = ast_var_assign(var->name, var->value))) {
00670          AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
00671       }
00672    }
00673    ast_mutex_unlock(&globals_lock);
00674 
00675    ao2_link(profiles, profile);
00676 
00677    profile = unref_profile(profile);
00678 }
00679 
00680 static struct extension *delete_extension(struct extension *exten)
00681 {
00682    struct ast_var_t *var;
00683    while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) {
00684       ast_var_delete(var);
00685    }
00686    ast_free(exten->headp);
00687    ast_string_field_free_memory(exten);
00688 
00689    ast_free(exten);
00690 
00691    return NULL;
00692 }
00693 
00694 static struct extension *build_extension(struct ast_config *cfg, const char *name)
00695 {
00696    struct extension *exten;
00697    struct ast_var_t *var;
00698    const char *tmp;
00699    int i;
00700 
00701    if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) {
00702       return NULL;
00703    }
00704 
00705    ast_string_field_set(exten, name, name);
00706 
00707    if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) {
00708       ast_free(exten);
00709       exten = NULL;
00710       return NULL;
00711    }
00712 
00713    for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
00714       tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
00715 
00716       /* If we didn't get a USERNAME variable, set it to the user->name */
00717       if (i == PP_USERNAME && !tmp) {
00718          if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) {
00719             AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00720          }
00721          continue;
00722       } else if (i == PP_TIMEZONE) {
00723          /* perfectly ok if tmp is NULL, will set variables based on server's time zone */
00724          set_timezone_variables(exten->headp, tmp);
00725       } else if (i == PP_LINENUMBER) {
00726          if (!tmp) {
00727             tmp = "1";
00728          }
00729          exten->index = atoi(tmp);
00730       } else if (i == PP_LINEKEYS) {
00731          if (!tmp) {
00732             tmp = "1";
00733          }
00734       }
00735 
00736       if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) {
00737          AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00738       }
00739    }
00740 
00741    if (!ast_strlen_zero(global_server)) {
00742       if ((var = ast_var_assign("SERVER", global_server)))
00743          AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00744    }
00745 
00746    if (!ast_strlen_zero(global_serverport)) {
00747       if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
00748          AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00749    }
00750 
00751    return exten;
00752 }
00753 
00754 static struct user *unref_user(struct user *user)
00755 {
00756    ao2_ref(user, -1);
00757 
00758    return NULL;
00759 }
00760 
00761 /*! \brief Return a user looked up by name */
00762 static struct user *find_user(const char *macaddress)
00763 {
00764    struct user tmp = {
00765       .macaddress = macaddress,
00766    };
00767 
00768    return ao2_find(users, &tmp, OBJ_POINTER);
00769 }
00770 
00771 static int users_hash_fn(const void *obj, const int flags)
00772 {
00773    const struct user *user = obj;
00774 
00775    return ast_str_case_hash(user->macaddress);
00776 }
00777 
00778 static int users_cmp_fn(void *obj, void *arg, int flags)
00779 {
00780    const struct user *user1 = obj, *user2 = arg;
00781 
00782    return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0;
00783 }
00784 
00785 /*! \brief Free all memory associated with a user */
00786 static void user_destructor(void *obj)
00787 {
00788    struct user *user = obj;
00789    struct extension *exten;
00790 
00791    while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
00792       exten = delete_extension(exten);
00793    }
00794 
00795    if (user->profile) {
00796       user->profile = unref_profile(user->profile);
00797    }
00798 
00799    ast_string_field_free_memory(user);
00800 }
00801 
00802 /*! \brief Delete all users */
00803 static void delete_users(void)
00804 {
00805    struct ao2_iterator i;
00806    struct user *user;
00807 
00808    i = ao2_iterator_init(users, 0);
00809    while ((user = ao2_iterator_next(&i))) {
00810       ao2_unlink(users, user);
00811       user = unref_user(user);
00812    }
00813    ao2_iterator_destroy(&i);
00814 }
00815 
00816 /*! \brief Build and return a user structure based on gathered config data */
00817 static struct user *build_user(const char *mac, struct phone_profile *profile)
00818 {
00819    struct user *user;
00820 
00821    if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
00822       profile = unref_profile(profile);
00823       return NULL;
00824    }
00825 
00826    if (ast_string_field_init(user, 32)) {
00827       profile = unref_profile(profile);
00828       user = unref_user(user);
00829       return NULL;
00830    }
00831 
00832    ast_string_field_set(user, macaddress, mac);
00833    user->profile = profile; /* already ref counted by find_profile */
00834 
00835    return user;
00836 }
00837 
00838 /*! \brief Add an extension to a user ordered by index/linenumber */
00839 static int add_user_extension(struct user *user, struct extension *exten)
00840 {
00841    struct ast_var_t *var;
00842    struct ast_str *str = ast_str_create(16);
00843 
00844    if (!str) {
00845       return -1;
00846    }
00847 
00848    /* Append profile variables here, and substitute variables on profile
00849     * setvars, so that we can use user specific variables in them */
00850    AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
00851       struct ast_var_t *var2;
00852 
00853       ast_str_substitute_variables_varshead(&str, 0, exten->headp, var->value);
00854       if ((var2 = ast_var_assign(var->name, ast_str_buffer(str)))) {
00855          AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
00856       }
00857    }
00858 
00859    ast_free(str);
00860 
00861    if (AST_LIST_EMPTY(&user->extensions)) {
00862       AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
00863    } else {
00864       struct extension *exten_iter;
00865 
00866       AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
00867          if (exten->index < exten_iter->index) {
00868             AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
00869          } else if (exten->index == exten_iter->index) {
00870             ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
00871             return -1;
00872          } else if (!AST_LIST_NEXT(exten_iter, entry)) {
00873             AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
00874          }
00875       }
00876       AST_LIST_TRAVERSE_SAFE_END;
00877    }
00878 
00879    return 0;
00880 }
00881 
00882 /*! \brief Add an http route for dynamic files attached to the profile of the user */
00883 static int build_user_routes(struct user *user)
00884 {
00885    struct phoneprov_file *pp_file;
00886    struct ast_str *str;
00887 
00888    if (!(str = ast_str_create(16))) {
00889       return -1;
00890    }
00891 
00892    AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
00893       ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
00894       build_route(pp_file, user, ast_str_buffer(str));
00895    }
00896 
00897    ast_free(str);
00898    return 0;
00899 }
00900 
00901 /* \brief Parse config files and create appropriate structures */
00902 static int set_config(void)
00903 {
00904    struct ast_config *cfg, *phoneprov_cfg;
00905    char *cat;
00906    struct ast_variable *v;
00907    struct ast_flags config_flags = { 0 };
00908    struct ast_var_t *var;
00909 
00910    /* Try to grab the port from sip.conf.  If we don't get it here, we'll set it
00911     * to whatever is set in phoneprov.conf or default to 5060 */
00912    if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
00913       ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
00914       ast_config_destroy(cfg);
00915    }
00916 
00917    if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
00918       ast_log(LOG_WARNING, "Unable to load users.conf\n");
00919       return 0;
00920    }
00921 
00922    /* Go ahead and load global variables from users.conf so we can append to profiles */
00923    for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00924       if (!strcasecmp(v->name, "vmexten")) {
00925          if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value))) {
00926             ast_mutex_lock(&globals_lock);
00927             AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00928             ast_mutex_unlock(&globals_lock);
00929          }
00930       }
00931       if (!strcasecmp(v->name, "localextenlength")) {
00932          if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
00933             ast_mutex_lock(&globals_lock);
00934             AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00935             ast_mutex_unlock(&globals_lock);
00936       }
00937    }
00938 
00939    if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags)) || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
00940       ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
00941       ast_config_destroy(cfg);
00942       return -1;
00943    }
00944 
00945    cat = NULL;
00946    while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
00947       if (!strcasecmp(cat, "general")) {
00948          for (v = ast_variable_browse(phoneprov_cfg, cat); v; v = v->next) {
00949             if (!strcasecmp(v->name, "serveraddr"))
00950                ast_copy_string(global_server, v->value, sizeof(global_server));
00951             else if (!strcasecmp(v->name, "serveriface")) {
00952                struct in_addr addr;
00953                lookup_iface(v->value, &addr);
00954                ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
00955             } else if (!strcasecmp(v->name, "serverport"))
00956                ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
00957             else if (!strcasecmp(v->name, "default_profile"))
00958                ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
00959          }
00960       } else
00961          build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
00962    }
00963 
00964    ast_config_destroy(phoneprov_cfg);
00965 
00966    cat = NULL;
00967    while ((cat = ast_category_browse(cfg, cat))) {
00968       const char *tmp, *mac;
00969       struct user *user;
00970       struct phone_profile *profile;
00971       struct extension *exten;
00972 
00973       if (!strcasecmp(cat, "general")) {
00974          continue;
00975       }
00976 
00977       if (!strcasecmp(cat, "authentication"))
00978          continue;
00979 
00980       if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
00981          continue;
00982 
00983       if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
00984          ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
00985          continue;
00986       }
00987 
00988       tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
00989       if (ast_strlen_zero(tmp)) {
00990          ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
00991          continue;
00992       }
00993 
00994       if (!(user = find_user(mac))) {
00995          if (!(profile = find_profile(tmp))) {
00996             ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
00997             continue;
00998          }
00999 
01000          if (!(user = build_user(mac, profile))) {
01001             ast_log(LOG_WARNING, "Could not create user for '%s' - skipping\n", user->macaddress);
01002             continue;
01003          }
01004 
01005          if (!(exten = build_extension(cfg, cat))) {
01006             ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01007             user = unref_user(user);
01008             continue;
01009          }
01010 
01011          if (add_user_extension(user, exten)) {
01012             ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01013             user = unref_user(user);
01014             exten = delete_extension(exten);
01015             continue;
01016          }
01017 
01018          if (build_user_routes(user)) {
01019             ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->macaddress);
01020             user = unref_user(user);
01021             continue;
01022          }
01023 
01024          ao2_link(users, user);
01025          user = unref_user(user);
01026       } else {
01027          if (!(exten = build_extension(cfg, cat))) {
01028             ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01029             user = unref_user(user);
01030             continue;
01031          }
01032 
01033          if (add_user_extension(user, exten)) {
01034             ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01035             user = unref_user(user);
01036             exten = delete_extension(exten);
01037             continue;
01038          }
01039 
01040          user = unref_user(user);
01041       }
01042    }
01043 
01044    ast_config_destroy(cfg);
01045 
01046    return 0;
01047 }
01048 
01049 /*! \brief Delete all http routes, freeing their memory */
01050 static void delete_routes(void)
01051 {
01052    struct ao2_iterator i;
01053    struct http_route *route;
01054 
01055    i = ao2_iterator_init(http_routes, 0);
01056    while ((route = ao2_iterator_next(&i))) {
01057       ao2_unlink(http_routes, route);
01058       route = unref_route(route);
01059    }
01060    ao2_iterator_destroy(&i);
01061 }
01062 
01063 /*! \brief Delete all phone profiles, freeing their memory */
01064 static void delete_profiles(void)
01065 {
01066    struct ao2_iterator i;
01067    struct phone_profile *profile;
01068 
01069    i = ao2_iterator_init(profiles, 0);
01070    while ((profile = ao2_iterator_next(&i))) {
01071       ao2_unlink(profiles, profile);
01072       profile = unref_profile(profile);
01073    }
01074    ao2_iterator_destroy(&i);
01075 }
01076 
01077 /*! \brief A dialplan function that can be used to print a string for each phoneprov user */
01078 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
01079 {
01080    char *tmp;
01081    struct ao2_iterator i;
01082    struct user *user;
01083    struct ast_str *str;
01084    AST_DECLARE_APP_ARGS(args,
01085       AST_APP_ARG(string);
01086       AST_APP_ARG(exclude_mac);
01087    );
01088    AST_STANDARD_APP_ARGS(args, data);
01089 
01090    if (!(str = ast_str_create(16))) {
01091       return -1;
01092    }
01093 
01094    /* Fix data by turning %{ into ${ */
01095    while ((tmp = strstr(args.string, "%{")))
01096       *tmp = '$';
01097 
01098    i = ao2_iterator_init(users, 0);
01099    while ((user = ao2_iterator_next(&i))) {
01100       if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
01101          continue;
01102       }
01103       ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
01104       if (buf) {
01105          size_t slen = len;
01106          ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
01107       } else {
01108          ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
01109       }
01110       user = unref_user(user);
01111    }
01112    ao2_iterator_destroy(&i);
01113 
01114    ast_free(str);
01115    return 0;
01116 }
01117 
01118 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01119 {
01120    return pp_each_user_helper(chan, data, buf, NULL, len);
01121 }
01122 
01123 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01124 {
01125    return pp_each_user_helper(chan, data, NULL, buf, len);
01126 }
01127 
01128 static struct ast_custom_function pp_each_user_function = {
01129    .name = "PP_EACH_USER",
01130    .read = pp_each_user_read,
01131    .read2 = pp_each_user_read2,
01132 };
01133 
01134 /*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
01135 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
01136 {
01137    struct user *user;
01138    struct extension *exten;
01139    char path[PATH_MAX];
01140    char *file;
01141    int filelen;
01142    struct ast_str *str;
01143    AST_DECLARE_APP_ARGS(args,
01144       AST_APP_ARG(mac);
01145       AST_APP_ARG(template);
01146    );
01147 
01148    AST_STANDARD_APP_ARGS(args, data);
01149 
01150    if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
01151       ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
01152       return 0;
01153    }
01154 
01155    if (!(user = find_user(args.mac))) {
01156       ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
01157       return 0;
01158    }
01159 
01160    snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
01161    filelen = load_file(path, &file);
01162    if (filelen < 0) {
01163       ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
01164       if (file) {
01165          ast_free(file);
01166       }
01167       return 0;
01168    }
01169 
01170    if (!file) {
01171       return 0;
01172    }
01173 
01174    if (!(str = ast_str_create(filelen))) {
01175       return 0;
01176    }
01177 
01178    AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
01179       ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
01180       if (buf) {
01181          size_t slen = len;
01182          ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
01183       } else {
01184          ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
01185       }
01186    }
01187 
01188    ast_free(file);
01189    ast_free(str);
01190 
01191    user = unref_user(user);
01192 
01193    return 0;
01194 }
01195 
01196 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01197 {
01198    return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
01199 }
01200 
01201 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01202 {
01203    return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
01204 }
01205 
01206 static struct ast_custom_function pp_each_extension_function = {
01207    .name = "PP_EACH_EXTENSION",
01208    .read = pp_each_extension_read,
01209    .read2 = pp_each_extension_read2,
01210 };
01211 
01212 /*! \brief CLI command to list static and dynamic routes */
01213 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01214 {
01215 #define FORMAT "%-40.40s  %-30.30s\n"
01216    struct ao2_iterator i;
01217    struct http_route *route;
01218 
01219    switch(cmd) {
01220    case CLI_INIT:
01221       e->command = "phoneprov show routes";
01222       e->usage =
01223          "Usage: phoneprov show routes\n"
01224          "       Lists all registered phoneprov http routes.\n";
01225       return NULL;
01226    case CLI_GENERATE:
01227       return NULL;
01228    }
01229 
01230    /* This currently iterates over routes twice, but it is the only place I've needed
01231     * to really separate static an dynamic routes, so I've just left it this way. */
01232    ast_cli(a->fd, "Static routes\n\n");
01233    ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
01234    i = ao2_iterator_init(http_routes, 0);
01235    while ((route = ao2_iterator_next(&i))) {
01236       if (!route->user)
01237          ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01238       route = unref_route(route);
01239    }
01240    ao2_iterator_destroy(&i);
01241 
01242    ast_cli(a->fd, "\nDynamic routes\n\n");
01243    ast_cli(a->fd, FORMAT, "Relative URI", "Template");
01244 
01245    i = ao2_iterator_init(http_routes, 0);
01246    while ((route = ao2_iterator_next(&i))) {
01247       if (route->user)
01248          ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01249       route = unref_route(route);
01250    }
01251    ao2_iterator_destroy(&i);
01252 
01253    return CLI_SUCCESS;
01254 }
01255 
01256 static struct ast_cli_entry pp_cli[] = {
01257    AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
01258 };
01259 
01260 static struct ast_http_uri phoneprovuri = {
01261    .callback = phoneprov_callback,
01262    .description = "Asterisk HTTP Phone Provisioning Tool",
01263    .uri = "phoneprov",
01264    .has_subtree = 1,
01265    .data = NULL,
01266    .key = __FILE__,
01267 };
01268 
01269 static int load_module(void)
01270 {
01271    profiles = ao2_container_alloc(MAX_PROFILE_BUCKETS, profile_hash_fn, profile_cmp_fn);
01272 
01273    http_routes = ao2_container_alloc(MAX_ROUTE_BUCKETS, routes_hash_fn, routes_cmp_fn);
01274 
01275    users = ao2_container_alloc(MAX_USER_BUCKETS, users_hash_fn, users_cmp_fn);
01276 
01277    AST_LIST_HEAD_INIT_NOLOCK(&global_variables);
01278    ast_mutex_init(&globals_lock);
01279 
01280    ast_custom_function_register(&pp_each_user_function);
01281    ast_custom_function_register(&pp_each_extension_function);
01282    ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
01283 
01284    set_config();
01285    ast_http_uri_link(&phoneprovuri);
01286 
01287    return 0;
01288 }
01289 
01290 static int unload_module(void)
01291 {
01292    struct ast_var_t *var;
01293 
01294    ast_http_uri_unlink(&phoneprovuri);
01295    ast_custom_function_unregister(&pp_each_user_function);
01296    ast_custom_function_unregister(&pp_each_extension_function);
01297    ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
01298 
01299    delete_routes();
01300    delete_users();
01301    delete_profiles();
01302    ao2_ref(profiles, -1);
01303    ao2_ref(http_routes, -1);
01304    ao2_ref(users, -1);
01305 
01306    ast_mutex_lock(&globals_lock);
01307    while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01308       ast_var_delete(var);
01309    }
01310    ast_mutex_unlock(&globals_lock);
01311 
01312    ast_mutex_destroy(&globals_lock);
01313 
01314    return 0;
01315 }
01316 
01317 static int reload(void)
01318 {
01319    struct ast_var_t *var;
01320 
01321    delete_routes();
01322    delete_users();
01323    delete_profiles();
01324 
01325    ast_mutex_lock(&globals_lock);
01326    while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01327       ast_var_delete(var);
01328    }
01329    ast_mutex_unlock(&globals_lock);
01330 
01331    set_config();
01332 
01333    return 0;
01334 }
01335 
01336 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP Phone Provisioning",
01337       .load = load_module,
01338       .unload = unload_module,
01339       .reload = reload,
01340    );

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