Thu Sep 7 01:03:04 2017

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

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