Thu Jul 9 13:41:30 2009

Asterisk developer's documentation


res_phoneprov.c File Reference

Phone provisioning application for the asterisk internal http server. More...

#include "asterisk.h"
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include "asterisk/file.h"
#include "asterisk/paths.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/module.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
#include "asterisk/strings.h"
#include "asterisk/stringfields.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
#include "asterisk/acl.h"
#include "asterisk/astobj2.h"
#include "asterisk/ast_version.h"

Go to the source code of this file.

Data Structures

struct  http_route
 structure to hold http routes (valid URIs, and the files they link to) More...
struct  phone_profile
 structure to hold phone profiles read from phoneprov.conf More...
struct  phoneprov_file
 structure to hold file data More...
struct  pp_variable_lookup
 Lookup table to translate between users.conf property names and variables for use in phoneprov templates. More...
struct  user
 structure to hold users read from users.conf More...
struct  users
 list of users found in the config file More...

Defines

#define FORMAT   "%-40.40s %-30.30s\n"
#define MAX_PROFILE_BUCKETS   17
#define MAX_ROUTE_BUCKETS   563
#define VAR_BUF_SIZE   4096

Enumerations

enum  pp_variables {
  PP_MACADDRESS, PP_USERNAME, PP_FULLNAME, PP_SECRET,
  PP_LABEL, PP_CALLERID, PP_TIMEZONE, PP_VAR_LIST_LENGTH
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void build_profile (const char *name, struct ast_variable *v)
 Build a phone profile and add it to the list of phone profiles.
static void build_route (struct phoneprov_file *pp_file, struct user *user, char *uri)
 Build a route structure and add it to the list of available http routes.
static struct userbuild_user (struct ast_config *cfg, const char *name, const char *mac, struct phone_profile *profile)
 Build and return a user structure based on gathered config data.
static int build_user_routes (struct user *user)
 Add an http route for dynamic files attached to the profile of the user.
static void delete_file (struct phoneprov_file *file)
static void delete_profiles (void)
 Delete all phone profiles, freeing their memory.
static void delete_routes (void)
 Delete all http routes, freeing their memory.
static void delete_user (struct user *user)
 Free all memory associated with a user.
static void delete_users (void)
 Destroy entire user list.
static struct phone_profilefind_profile (const char *name)
 Return a phone profile looked up by name.
static char * ftype2mtype (const char *ftype)
 Return mime type based on extension.
static char * handle_show_routes (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command to list static and dynamic routes.
static int load_file (const char *filename, char **ret)
 Read a TEXT file into a string and return the length.
static int load_module (void)
static int lookup_iface (const char *iface, struct in_addr *address)
static struct ast_strphoneprov_callback (struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
 Callback that is executed everytime an http request is received by this module.
static int pp_each_user_exec (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 A dialplan function that can be used to print a string for each phoneprov user.
static int profile_cmp_fn (void *obj, void *arg, int flags)
static void profile_destructor (void *obj)
static int profile_hash_fn (const void *obj, const int flags)
static int reload (void)
static void route_destructor (void *obj)
static int routes_cmp_fn (void *obj, void *arg, int flags)
static int routes_hash_fn (const void *obj, const int flags)
static int set_config (void)
static void set_timezone_variables (struct varshead *headp, const char *zone)
 Set all timezone-related variables based on a zone (i.e. America/New_York).
static int unload_module (void)
static struct phone_profileunref_profile (struct phone_profile *prof)
static struct http_routeunref_route (struct http_route *route)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "HTTP Phone Provisioning" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, .reload = reload, }
static struct in_addr __ourip = { .s_addr = 0x00000000, }
 for use in lookup_iface
static const struct ast_module_infoast_module_info = &__mod_info
char global_default_profile [80] = ""
char global_server [80] = ""
char global_serverport [6] = ""
varshead global_variables
 List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH.
static struct ao2_containerhttp_routes
struct {
   char *   ext
   char *   mtype
mimetypes []
 Extensions whose mime types we think we know.
static struct ast_http_uri phoneprovuri
static struct ast_cli_entry pp_cli []
static struct ast_custom_function pp_each_user_function
static struct pp_variable_lookup pp_variable_list []
 Lookup table to translate between users.conf property names and variables for use in phoneprov templates.
static struct ao2_containerprofiles


Detailed Description

Phone provisioning application for the asterisk internal http server.

Author:
Matthew Brooks <mbrooks@digium.com>

Terry Wilson <twilson@digium.com>

Definition in file res_phoneprov.c.


Define Documentation

#define FORMAT   "%-40.40s %-30.30s\n"

#define MAX_PROFILE_BUCKETS   17

Definition at line 59 of file res_phoneprov.c.

Referenced by load_module().

#define MAX_ROUTE_BUCKETS   563

Definition at line 60 of file res_phoneprov.c.

Referenced by load_module().

#define VAR_BUF_SIZE   4096

Definition at line 63 of file res_phoneprov.c.


Enumeration Type Documentation

enum pp_variables

Enumerator:
PP_MACADDRESS 
PP_USERNAME 
PP_FULLNAME 
PP_SECRET 
PP_LABEL 
PP_CALLERID 
PP_TIMEZONE 
PP_VAR_LIST_LENGTH 

Definition at line 70 of file res_phoneprov.c.

00070                   {
00071    PP_MACADDRESS,
00072    PP_USERNAME,
00073    PP_FULLNAME,
00074    PP_SECRET,
00075    PP_LABEL,
00076    PP_CALLERID,
00077    PP_TIMEZONE,
00078    PP_VAR_LIST_LENGTH,  /* This entry must always be the last in the list */
00079 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1049 of file res_phoneprov.c.

static void __unreg_module ( void   )  [static]

Definition at line 1049 of file res_phoneprov.c.

static void build_profile ( const char *  name,
struct ast_variable v 
) [static]

Build a phone profile and add it to the list of phone profiles.

Parameters:
name the name of the profile
v ast_variable from parsing phoneprov.conf

Definition at line 490 of file res_phoneprov.c.

References ao2_alloc(), AST_APP_ARG, ast_calloc, AST_DECLARE_APP_ARGS, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_NONSTANDARD_APP_ARGS, ast_strdupa, ast_string_field_init, ast_string_field_set, ast_strip(), ast_strlen_zero(), ast_var_assign(), ast_var_t::entries, ast_variable::name, ast_variable::next, profile_destructor(), unref_profile(), ast_variable::value, and var.

Referenced by set_config().

00491 {
00492    struct phone_profile *profile;
00493    struct ast_var_t *var;
00494 
00495    if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor)))
00496       return;
00497 
00498    if (ast_string_field_init(profile, 32)) {
00499       profile = unref_profile(profile);
00500       return;
00501    }
00502    
00503    if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
00504       profile = unref_profile(profile);
00505       return;
00506    }
00507 
00508    AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
00509    AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
00510 
00511    ast_string_field_set(profile, name, name);
00512    for (; v; v = v->next) {
00513       if (!strcasecmp(v->name, "mime_type")) {
00514          ast_string_field_set(profile, default_mime_type, v->value);
00515       } else if (!strcasecmp(v->name, "setvar")) {
00516          struct ast_var_t *var;
00517          char *value_copy = ast_strdupa(v->value);
00518 
00519          AST_DECLARE_APP_ARGS(args,
00520             AST_APP_ARG(varname);
00521             AST_APP_ARG(varval);
00522          );
00523          
00524          AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
00525          do {
00526             if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00527                break;
00528             args.varname = ast_strip(args.varname);
00529             args.varval = ast_strip(args.varval);
00530             if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00531                break;
00532             if ((var = ast_var_assign(args.varname, args.varval)))
00533                AST_LIST_INSERT_TAIL(profile->headp, var, entries);
00534          } while (0);
00535       } else if (!strcasecmp(v->name, "staticdir")) {
00536          ast_string_field_set(profile, staticdir, v->value);
00537       } else {
00538          struct phoneprov_file *pp_file;
00539          char *file_extension;
00540          char *value_copy = ast_strdupa(v->value); 
00541 
00542          AST_DECLARE_APP_ARGS(args,
00543             AST_APP_ARG(filename);
00544             AST_APP_ARG(mimetype);
00545          );
00546 
00547          if (!(pp_file = ast_calloc(1, sizeof(*pp_file)))) {
00548             profile = unref_profile(profile);
00549             return;
00550          }
00551          if (ast_string_field_init(pp_file, 32)) {
00552             ast_free(pp_file);
00553             profile = unref_profile(profile);
00554             return;
00555          }
00556 
00557          if ((file_extension = strrchr(pp_file->format, '.')))
00558             file_extension++;
00559 
00560          AST_STANDARD_APP_ARGS(args, value_copy);
00561 
00562          /* Mime type order of preference
00563           * 1) Specific mime-type defined for file in profile
00564           * 2) Mime determined by extension
00565           * 3) Default mime type specified in profile
00566           * 4) text/plain
00567           */
00568          ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
00569 
00570          if (!strcasecmp(v->name, "static_file")) {
00571             ast_string_field_set(pp_file, format, args.filename);
00572             ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
00573             AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
00574             /* Add a route for the static files, as their filenames won't change per-user */
00575             build_route(pp_file, NULL, NULL);
00576          } else {
00577             ast_string_field_set(pp_file, format, v->name);
00578             ast_string_field_set(pp_file, template, args.filename);
00579             AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
00580          }
00581       }
00582    }
00583 
00584    /* Append the global variables to the variables list for this profile.
00585     * This is for convenience later, when we need to provide a single
00586     * variable list for use in substitution. */
00587    AST_LIST_TRAVERSE(&global_variables, var, entries) {
00588       struct ast_var_t *new_var;
00589       if ((new_var = ast_var_assign(var->name, var->value)))
00590          AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
00591    }
00592 
00593    ao2_link(profiles, profile);
00594 
00595    profile = unref_profile(profile);
00596 }

static void build_route ( struct phoneprov_file pp_file,
struct user user,
char *  uri 
) [static]

Build a route structure and add it to the list of available http routes.

Parameters:
pp_file File to link to the route
user User to link to the route (NULL means static route)
uri URI of the route

Definition at line 464 of file res_phoneprov.c.

References ao2_alloc(), ao2_link(), ast_log(), ast_string_field_init, ast_string_field_set, phoneprov_file::format, http_routes, LOG_ERROR, route_destructor(), S_OR, unref_route(), and user.

00465 {
00466    struct http_route *route;
00467    
00468    if (!(route = ao2_alloc(sizeof(*route), route_destructor)))
00469       return;
00470 
00471    if (ast_string_field_init(route, 32)) {
00472       ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
00473       route = unref_route(route);
00474       return;
00475    }
00476 
00477    ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
00478    route->user = user;
00479    route->file = pp_file;
00480 
00481    ao2_link(http_routes, route);
00482 
00483    route = unref_route(route);
00484 }

static struct user* build_user ( struct ast_config cfg,
const char *  name,
const char *  mac,
struct phone_profile profile 
) [static]

Build and return a user structure based on gathered config data.

Definition at line 684 of file res_phoneprov.c.

References ast_calloc, ast_free, AST_LIST_INSERT_TAIL, ast_string_field_init, ast_string_field_set, ast_var_assign(), ast_variable_retrieve(), delete_user(), ast_var_t::entries, PP_TIMEZONE, PP_USERNAME, PP_VAR_LIST_LENGTH, pp_variable_list, set_timezone_variables(), unref_profile(), user, and var.

00685 {
00686    struct user *user;
00687    struct ast_var_t *var;
00688    const char *tmp;
00689    int i;
00690    
00691    if (!(user = ast_calloc(1, sizeof(*user)))) {
00692       profile = unref_profile(profile);
00693       return NULL;
00694    }
00695    
00696    if (!(user->headp = ast_calloc(1, sizeof(*user->headp)))) {
00697       profile = unref_profile(profile);
00698       ast_free(user);
00699       return NULL;
00700    }
00701 
00702    if (ast_string_field_init(user, 32)) {
00703       profile = unref_profile(profile);
00704       delete_user(user);
00705       return NULL;
00706    }
00707 
00708    ast_string_field_set(user, name, name);
00709    ast_string_field_set(user, macaddress, mac);
00710    user->profile = profile; /* already ref counted by find_profile */
00711 
00712    for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
00713       tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
00714 
00715       /* If we didn't get a USERNAME variable, set it to the user->name */
00716       if (i == PP_USERNAME && !tmp) {
00717          if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, user->name))) {
00718             AST_LIST_INSERT_TAIL(user->headp, var, entries);
00719          }
00720          continue;
00721       } else if (i == PP_TIMEZONE) {
00722          /* perfectly ok if tmp is NULL, will set variables based on server's time zone */
00723          set_timezone_variables(user->headp, tmp);
00724       }
00725 
00726       if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp)))
00727          AST_LIST_INSERT_TAIL(user->headp, var, entries);
00728    }
00729 
00730    if (!ast_strlen_zero(global_server)) {
00731       if ((var = ast_var_assign("SERVER", global_server)))
00732          AST_LIST_INSERT_TAIL(user->headp, var, entries);
00733    }
00734 
00735    if (!ast_strlen_zero(global_serverport)) {
00736       if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
00737          AST_LIST_INSERT_TAIL(user->headp, var, entries);
00738    }
00739 
00740    /* Append profile variables here, and substitute variables on profile
00741     * setvars, so that we can use user specific variables in them */
00742    AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
00743       char expand_buf[VAR_BUF_SIZE] = {0,};
00744       struct ast_var_t *var2;
00745 
00746       pbx_substitute_variables_varshead(user->headp, var->value, expand_buf, sizeof(expand_buf));
00747       if ((var2 = ast_var_assign(var->name, expand_buf)))
00748          AST_LIST_INSERT_TAIL(user->headp, var2, entries);
00749    }
00750 
00751    return user;
00752 }

static int build_user_routes ( struct user user  )  [static]

Add an http route for dynamic files attached to the profile of the user.

Definition at line 755 of file res_phoneprov.c.

References AST_LIST_TRAVERSE, build_route(), phoneprov_file::entry, phoneprov_file::format, pbx_substitute_variables_varshead(), user, and VAR_BUF_SIZE.

00756 {
00757    struct phoneprov_file *pp_file;
00758 
00759    AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
00760       char expand_buf[VAR_BUF_SIZE] = { 0, };
00761 
00762       pbx_substitute_variables_varshead(user->headp, pp_file->format, expand_buf, sizeof(expand_buf));
00763       build_route(pp_file, user, expand_buf);
00764    }
00765 
00766    return 0;
00767 }

static void delete_file ( struct phoneprov_file file  )  [static]

Definition at line 242 of file res_phoneprov.c.

References ast_string_field_free_memory, and free.

Referenced by profile_destructor().

00243 {
00244    ast_string_field_free_memory(file);
00245    free(file);
00246 }

static void delete_profiles ( void   )  [static]

Delete all phone profiles, freeing their memory.

Definition at line 893 of file res_phoneprov.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_unlink(), profiles, and unref_profile().

Referenced by reload(), and unload_module().

00894 {
00895    struct ao2_iterator i;
00896    struct phone_profile *profile;
00897 
00898    i = ao2_iterator_init(profiles, 0);
00899    while ((profile = ao2_iterator_next(&i))) {
00900       ao2_unlink(profiles, profile);
00901       profile = unref_profile(profile);
00902    }
00903 }

static void delete_routes ( void   )  [static]

Delete all http routes, freeing their memory.

Definition at line 880 of file res_phoneprov.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_unlink(), http_routes, and unref_route().

Referenced by reload(), and unload_module().

00881 {
00882    struct ao2_iterator i;
00883    struct http_route *route;
00884    
00885    i = ao2_iterator_init(http_routes, 0);
00886    while ((route = ao2_iterator_next(&i))) {
00887       ao2_unlink(http_routes, route);
00888       route = unref_route(route);
00889    }
00890 }

static void delete_user ( struct user user  )  [static]

Free all memory associated with a user.

Definition at line 599 of file res_phoneprov.c.

References ast_free, AST_LIST_REMOVE_HEAD, ast_string_field_free_memory, ast_var_delete(), ast_var_t::entries, free, unref_profile(), user, and var.

Referenced by build_user(), and delete_users().

00600 {
00601    struct ast_var_t *var;
00602 
00603    while ((var = AST_LIST_REMOVE_HEAD(user->headp, entries)))
00604       ast_var_delete(var);
00605 
00606    ast_free(user->headp);
00607    ast_string_field_free_memory(user);
00608    user->profile = unref_profile(user->profile);
00609    free(user);
00610 }

static void delete_users ( void   )  [static]

Destroy entire user list.

Definition at line 613 of file res_phoneprov.c.

References AST_LIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, delete_user(), phoneprov_file::entry, and user.

00614 {
00615    struct user *user;
00616 
00617    AST_RWLIST_WRLOCK(&users);
00618    while ((user = AST_LIST_REMOVE_HEAD(&users, entry)))
00619       delete_user(user);
00620    AST_RWLIST_UNLOCK(&users);
00621 }

static struct phone_profile* find_profile ( const char *  name  )  [static]

Return a phone profile looked up by name.

Definition at line 219 of file res_phoneprov.c.

References ao2_find(), phone_profile::name, OBJ_POINTER, and profiles.

00220 {
00221    struct phone_profile tmp = {
00222       .name = name,
00223    };
00224 
00225    return ao2_find(profiles, &tmp, OBJ_POINTER);
00226 }

static char* ftype2mtype ( const char *  ftype  )  [static]

Return mime type based on extension.

Definition at line 165 of file res_phoneprov.c.

References ARRAY_LEN, ast_strlen_zero(), ext, and mimetypes.

00166 {
00167    int x;
00168 
00169    if (ast_strlen_zero(ftype))
00170       return NULL;
00171 
00172    for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
00173       if (!strcasecmp(ftype, mimetypes[x].ext))
00174          return mimetypes[x].mtype;
00175    }
00176    
00177    return NULL;
00178 }

static char* handle_show_routes ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

CLI command to list static and dynamic routes.

Definition at line 946 of file res_phoneprov.c.

References ao2_iterator_init(), ao2_iterator_next(), ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, http_route::file, FORMAT, http_routes, phoneprov_file::template, unref_route(), http_route::uri, ast_cli_entry::usage, and http_route::user.

00947 {
00948 #define FORMAT "%-40.40s  %-30.30s\n"
00949    struct ao2_iterator i;
00950    struct http_route *route;
00951    
00952    switch(cmd) {
00953    case CLI_INIT:
00954       e->command = "phoneprov show routes";
00955       e->usage =
00956          "Usage: phoneprov show routes\n"
00957          "       Lists all registered phoneprov http routes.\n";
00958       return NULL;
00959    case CLI_GENERATE:
00960       return NULL;
00961    }
00962 
00963    /* This currently iterates over routes twice, but it is the only place I've needed
00964     * to really separate static an dynamic routes, so I've just left it this way. */
00965    ast_cli(a->fd, "Static routes\n\n");
00966    ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
00967    i = ao2_iterator_init(http_routes, 0);
00968    while ((route = ao2_iterator_next(&i))) {
00969       if (!route->user)
00970          ast_cli(a->fd, FORMAT, route->uri, route->file->template);
00971       route = unref_route(route);
00972    }
00973 
00974    ast_cli(a->fd, "\nDynamic routes\n\n");
00975    ast_cli(a->fd, FORMAT, "Relative URI", "Template");
00976 
00977    i = ao2_iterator_init(http_routes, 0);
00978    while ((route = ao2_iterator_next(&i))) {
00979       if (route->user)
00980          ast_cli(a->fd, FORMAT, route->uri, route->file->template);
00981       route = unref_route(route);
00982    }
00983 
00984    return CLI_SUCCESS;
00985 }

static int load_file ( const char *  filename,
char **  ret 
) [static]

Read a TEXT file into a string and return the length.

Definition at line 296 of file res_phoneprov.c.

References ast_malloc, f, free, and len().

Referenced by phoneprov_callback().

00297 {
00298    int len = 0;
00299    FILE *f;
00300    
00301    if (!(f = fopen(filename, "r"))) {
00302       *ret = NULL;
00303       return -1;
00304    }
00305 
00306    fseek(f, 0, SEEK_END);
00307    len = ftell(f);
00308    fseek(f, 0, SEEK_SET);
00309    if (!(*ret = ast_malloc(len + 1)))
00310       return -2;
00311 
00312    if (len != fread(*ret, sizeof(char), len, f)) {
00313       free(*ret);
00314       *ret = NULL;
00315       return -3;
00316    }
00317 
00318    fclose(f);
00319 
00320    (*ret)[len] = '\0';
00321 
00322    return len;
00323 }

static int load_module ( void   )  [static]

Definition at line 998 of file res_phoneprov.c.

References ao2_container_alloc(), ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_http_uri_link(), AST_LIST_HEAD_INIT_NOLOCK, global_variables, http_routes, MAX_PROFILE_BUCKETS, MAX_ROUTE_BUCKETS, phoneprovuri, pp_cli, pp_each_user_function, profile_cmp_fn(), profile_hash_fn(), profiles, routes_cmp_fn(), routes_hash_fn(), and set_config().

static int lookup_iface ( const char *  iface,
struct in_addr *  address 
) [static]

Definition at line 181 of file res_phoneprov.c.

References ast_copy_string(), ast_log(), errno, LOG_ERROR, and LOG_WARNING.

Referenced by set_config().

00182 {
00183    int mysock, res = 0;
00184    struct ifreq ifr;
00185    struct sockaddr_in *sin;
00186 
00187    memset(&ifr, 0, sizeof(ifr));
00188    ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
00189 
00190    mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
00191    if (mysock < 0) {
00192       ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
00193       return -1;
00194    }
00195 
00196    res = ioctl(mysock, SIOCGIFADDR, &ifr);
00197 
00198    close(mysock);
00199 
00200    if (res < 0) {
00201       ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
00202       memcpy(address, &__ourip, sizeof(__ourip));
00203       return -1;
00204    } else {
00205       sin = (struct sockaddr_in *)&ifr.ifr_addr;
00206       memcpy(address, &sin->sin_addr, sizeof(*address));
00207       return 0;
00208    }
00209 }

static struct ast_str* phoneprov_callback ( struct ast_tcptls_session_instance ser,
const char *  uri,
struct ast_variable vars,
int *  status,
char **  title,
int *  contentlength 
) [static]

Callback that is executed everytime an http request is received by this module.

Definition at line 326 of file res_phoneprov.c.

References ao2_find(), ast_calloc, ast_config_AST_DATA_DIR, ast_debug, ast_free, ast_get_version(), ast_http_error(), ast_inet_ntoa(), AST_LIST_INSERT_TAIL, ast_localtime(), ast_log(), ast_str_append(), ast_str_create(), ast_strftime(), ast_strlen_zero(), ast_tvnow(), ast_var_assign(), buf, ast_var_t::entries, errno, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, http_route::file, user::headp, http_routes, len(), load_file(), LOG_WARNING, phoneprov_file::mime_type, OBJ_POINTER, pbx_substitute_variables_varshead(), strdup, phoneprov_file::template, unref_route(), http_route::uri, http_route::user, var, and VAR_BUF_SIZE.

00327 {
00328    struct http_route *route;
00329    struct http_route search_route = {
00330       .uri = uri,
00331    };
00332    struct ast_str *result = ast_str_create(512);
00333    char path[PATH_MAX];
00334    char *file = NULL;
00335    int len;
00336    int fd;
00337    char buf[256];
00338    struct timeval tv = ast_tvnow();
00339    struct ast_tm tm;
00340 
00341    if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER)))
00342       goto out404;
00343 
00344    snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
00345 
00346    if (!route->user) { /* Static file */
00347 
00348       fd = open(path, O_RDONLY);
00349       if (fd < 0)
00350          goto out500;
00351 
00352       len = lseek(fd, 0, SEEK_END);
00353       lseek(fd, 0, SEEK_SET);
00354       if (len < 0) {
00355          ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00356          close(fd);
00357          goto out500;
00358       }
00359 
00360       ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
00361        fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
00362          "Server: Asterisk/%s\r\n"
00363          "Date: %s\r\n"
00364          "Connection: close\r\n"
00365          "Cache-Control: no-cache, no-store\r\n"
00366          "Content-Length: %d\r\n"
00367          "Content-Type: %s\r\n\r\n",
00368          ast_get_version(), buf, len, route->file->mime_type);
00369       
00370       while ((len = read(fd, buf, sizeof(buf))) > 0)
00371          if (fwrite(buf, 1, len, ser->f) != len) {
00372             if (errno != EPIPE) {
00373                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00374             } else {
00375                ast_debug(3, "Requester closed the connection while downloading '%s'\n", path);
00376             }
00377             break;
00378          }
00379 
00380       close(fd);
00381       route = unref_route(route);
00382       return NULL;
00383    } else { /* Dynamic file */
00384       int bufsize;
00385       char *tmp;
00386 
00387       len = load_file(path, &file);
00388       if (len < 0) {
00389          ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00390          if (file)
00391             ast_free(file);
00392          goto out500;
00393       }
00394 
00395       if (!file)
00396          goto out500;
00397 
00398       /* XXX This is a hack -- maybe sum length of all variables in route->user->headp and add that? */
00399       bufsize = len + VAR_BUF_SIZE;
00400       
00401       /* malloc() instead of alloca() here, just in case the file is bigger than
00402        * we have enough stack space for. */
00403       if (!(tmp = ast_calloc(1, bufsize))) {
00404          if (file)
00405             ast_free(file);
00406          goto out500;
00407       }
00408 
00409       /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
00410        * the IP address we are listening on that the phone contacted for this config file */
00411       if (ast_strlen_zero(global_server)) {
00412          struct sockaddr name;
00413          socklen_t namelen = sizeof(name);
00414          int res;
00415 
00416          if ((res = getsockname(ser->fd, &name, &namelen)))
00417             ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00418          else {
00419             struct ast_var_t *var;
00420 
00421             if ((var = ast_var_assign("SERVER", ast_inet_ntoa(((struct sockaddr_in *)&name)->sin_addr))))
00422                AST_LIST_INSERT_TAIL(route->user->headp, var, entries);
00423          }
00424       }
00425 
00426       pbx_substitute_variables_varshead(route->user->headp, file, tmp, bufsize);
00427    
00428       if (file)
00429          ast_free(file);
00430 
00431       ast_str_append(&result, 0,
00432          "Content-Type: %s\r\n"
00433          "Content-length: %d\r\n"
00434          "\r\n"
00435          "%s", route->file->mime_type, (int) strlen(tmp), tmp);
00436 
00437       if (tmp)
00438          ast_free(tmp);
00439 
00440       route = unref_route(route);
00441 
00442       return result;
00443    }
00444 
00445 out404:
00446    *status = 404;
00447    *title = strdup("Not Found");
00448    *contentlength = 0;
00449    return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
00450 
00451 out500:
00452    route = unref_route(route);
00453    *status = 500;
00454    *title = strdup("Internal Server Error");
00455    *contentlength = 0;
00456    return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
00457 }

static int pp_each_user_exec ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

A dialplan function that can be used to print a string for each phoneprov user.

Definition at line 906 of file res_phoneprov.c.

References AST_APP_ARG, ast_build_string(), AST_DECLARE_APP_ARGS, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_strlen_zero(), user::entry, pbx_substitute_variables_varshead(), user, and VAR_BUF_SIZE.

00907 {
00908    char *tmp, expand_buf[VAR_BUF_SIZE] = {0,};
00909    struct user *user;
00910    AST_DECLARE_APP_ARGS(args,
00911       AST_APP_ARG(string);
00912       AST_APP_ARG(exclude_mac);
00913    );
00914    AST_STANDARD_APP_ARGS(args, data);
00915 
00916    /* Fix data by turning %{ into ${ */
00917    while ((tmp = strstr(args.string, "%{")))
00918       *tmp = '$';
00919 
00920    AST_RWLIST_RDLOCK(&users);
00921    AST_RWLIST_TRAVERSE(&users, user, entry) {
00922       if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac))
00923          continue;
00924       pbx_substitute_variables_varshead(user->headp, args.string, expand_buf, sizeof(expand_buf));
00925       ast_build_string(&buf, &len, "%s", expand_buf);
00926    }
00927    AST_RWLIST_UNLOCK(&users);
00928 
00929    return 0;
00930 }

static int profile_cmp_fn ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 235 of file res_phoneprov.c.

References CMP_MATCH, and phone_profile::name.

Referenced by load_module().

00236 {
00237    const struct phone_profile *profile1 = obj, *profile2 = arg;
00238 
00239    return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH : 0;
00240 }

static void profile_destructor ( void *  obj  )  [static]

Definition at line 248 of file res_phoneprov.c.

References AST_LIST_REMOVE_HEAD, ast_string_field_free_memory, ast_var_delete(), delete_file(), phone_profile::dynamic_files, ast_var_t::entries, phoneprov_file::entry, free, phone_profile::headp, phone_profile::static_files, and var.

Referenced by build_profile().

00249 {
00250    struct phone_profile *profile = obj;
00251    struct phoneprov_file *file;
00252    struct ast_var_t *var;
00253 
00254    while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry)))
00255       delete_file(file);
00256 
00257    while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry)))
00258       delete_file(file);
00259 
00260    while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries)))
00261       ast_var_delete(var);
00262 
00263    free(profile->headp);
00264    ast_string_field_free_memory(profile);
00265 }

static int profile_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 228 of file res_phoneprov.c.

References ast_str_hash(), and phone_profile::name.

Referenced by load_module().

00229 {
00230    const struct phone_profile *profile = obj;
00231    
00232    return ast_str_hash(profile->name);
00233 }

static int reload ( void   )  [static]

Definition at line 1035 of file res_phoneprov.c.

References delete_profiles(), delete_routes(), delete_users(), and set_config().

01036 {
01037    delete_routes();
01038    delete_users();
01039    delete_profiles();
01040    set_config();
01041 
01042    return 0;
01043 }

static void route_destructor ( void *  obj  )  [static]

Definition at line 288 of file res_phoneprov.c.

References ast_string_field_free_memory.

Referenced by build_route().

00289 {
00290    struct http_route *route = obj;
00291 
00292    ast_string_field_free_memory(route);
00293 }

static int routes_cmp_fn ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 281 of file res_phoneprov.c.

References CMP_MATCH, and http_route::uri.

Referenced by load_module().

00282 {
00283    const struct http_route *route1 = obj, *route2 = arg;
00284 
00285    return !strcmp(route1->uri, route2->uri) ? CMP_MATCH : 0;
00286 }

static int routes_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 274 of file res_phoneprov.c.

References ast_str_hash(), and http_route::uri.

Referenced by load_module().

00275 {
00276    const struct http_route *route = obj;
00277    
00278    return ast_str_hash(route->uri);
00279 }

static int set_config ( void   )  [static]

Definition at line 770 of file res_phoneprov.c.

References ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_inet_ntoa(), ast_log(), ast_variable_browse(), ast_variable_retrieve(), build_profile(), LOG_ERROR, lookup_iface(), ast_variable::name, ast_variable::next, S_OR, and ast_variable::value.

00771 {
00772    struct ast_config *cfg;
00773    char *cat;
00774    struct ast_variable *v;
00775    struct ast_flags config_flags = { 0 };
00776 
00777    /* Try to grab the port from sip.conf.  If we don't get it here, we'll set it
00778     * to whatever is set in phoneprov.conf or default to 5060 */
00779    if ((cfg = ast_config_load("sip.conf", config_flags))) {
00780       ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
00781       ast_config_destroy(cfg);
00782    }
00783 
00784    if (!(cfg = ast_config_load("phoneprov.conf", config_flags))) {
00785       ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
00786       ast_config_destroy(cfg);
00787       return -1;
00788    }
00789 
00790    cat = NULL;
00791    while ((cat = ast_category_browse(cfg, cat))) {
00792       if (!strcasecmp(cat, "general")) {
00793          for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00794             if (!strcasecmp(v->name, "serveraddr"))
00795                ast_copy_string(global_server, v->value, sizeof(global_server));
00796             else if (!strcasecmp(v->name, "serveriface")) {
00797                struct in_addr addr;
00798                lookup_iface(v->value, &addr);
00799                ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
00800             } else if (!strcasecmp(v->name, "serverport"))
00801                ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
00802             else if (!strcasecmp(v->name, "default_profile"))
00803                ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
00804          }
00805       } else 
00806          build_profile(cat, ast_variable_browse(cfg, cat));
00807    }
00808 
00809    ast_config_destroy(cfg);
00810 
00811    if (!(cfg = ast_config_load("users.conf", config_flags))) {
00812       ast_log(LOG_WARNING, "Unable to load users.cfg\n");
00813       return 0;
00814    }
00815 
00816    cat = NULL;
00817    while ((cat = ast_category_browse(cfg, cat))) {
00818       const char *tmp, *mac;
00819       struct user *user;
00820       struct phone_profile *profile;
00821       struct ast_var_t *var;
00822 
00823       if (!strcasecmp(cat, "general")) {
00824          for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00825             if (!strcasecmp(v->name, "vmexten")) {
00826                if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value)))
00827                   AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00828             }
00829             if (!strcasecmp(v->name, "localextenlength")) {
00830                if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
00831                   AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00832             }
00833          }
00834       }
00835            
00836       if (!strcasecmp(cat, "authentication"))
00837          continue;
00838 
00839       if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))  
00840          continue;
00841 
00842       if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
00843          ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
00844          continue;
00845       }
00846 
00847       tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
00848       if (ast_strlen_zero(tmp)) {
00849          ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
00850          continue;
00851       }
00852 
00853       if (!(profile = find_profile(tmp))) {
00854          ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
00855          continue;
00856       }
00857 
00858       if (!(user = build_user(cfg, cat, mac, profile))) {
00859          ast_log(LOG_WARNING, "Could not create user %s - skipping.\n", cat);
00860          continue;
00861       }
00862 
00863       if (build_user_routes(user)) {
00864          ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->name);
00865          delete_user(user);
00866          continue;
00867       }
00868 
00869       AST_RWLIST_WRLOCK(&users);
00870       AST_RWLIST_INSERT_TAIL(&users, user, entry);
00871       AST_RWLIST_UNLOCK(&users);
00872    }
00873 
00874    ast_config_destroy(cfg);
00875 
00876    return 0;
00877 }

static void set_timezone_variables ( struct varshead headp,
const char *  zone 
) [static]

Set all timezone-related variables based on a zone (i.e. America/New_York).

Parameters:
headp pointer to list of user variables
zone A time zone. NULL sets variables based on timezone of the machine

Definition at line 627 of file res_phoneprov.c.

References ast_get_dst_info(), AST_LIST_INSERT_TAIL, ast_localtime(), ast_var_assign(), ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_mon, and var.

Referenced by build_user().

00628 {
00629    time_t utc_time;
00630    int dstenable;
00631    time_t dststart;
00632    time_t dstend;
00633    struct ast_tm tm_info;
00634    int tzoffset;
00635    char buffer[21];
00636    struct ast_var_t *var;
00637    struct timeval tv;
00638 
00639    time(&utc_time);
00640    ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
00641    snprintf(buffer, sizeof(buffer), "%d", tzoffset);
00642    var = ast_var_assign("TZOFFSET", buffer);
00643    if (var)
00644       AST_LIST_INSERT_TAIL(headp, var, entries); 
00645 
00646    if (!dstenable)
00647       return;
00648 
00649    if ((var = ast_var_assign("DST_ENABLE", "1")))
00650       AST_LIST_INSERT_TAIL(headp, var, entries);
00651 
00652    tv.tv_sec = dststart; 
00653    ast_localtime(&tv, &tm_info, zone);
00654 
00655    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
00656    if ((var = ast_var_assign("DST_START_MONTH", buffer)))
00657       AST_LIST_INSERT_TAIL(headp, var, entries);
00658 
00659    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00660    if ((var = ast_var_assign("DST_START_MDAY", buffer)))
00661       AST_LIST_INSERT_TAIL(headp, var, entries);
00662 
00663    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00664    if ((var = ast_var_assign("DST_START_HOUR", buffer)))
00665       AST_LIST_INSERT_TAIL(headp, var, entries);
00666 
00667    tv.tv_sec = dstend;
00668    ast_localtime(&tv, &tm_info, zone);
00669 
00670    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
00671    if ((var = ast_var_assign("DST_END_MONTH", buffer)))
00672       AST_LIST_INSERT_TAIL(headp, var, entries);
00673 
00674    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00675    if ((var = ast_var_assign("DST_END_MDAY", buffer)))
00676       AST_LIST_INSERT_TAIL(headp, var, entries);
00677 
00678    snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00679    if ((var = ast_var_assign("DST_END_HOUR", buffer)))
00680       AST_LIST_INSERT_TAIL(headp, var, entries);
00681 }

static int unload_module ( void   )  [static]

Definition at line 1015 of file res_phoneprov.c.

References ao2_ref(), ARRAY_LEN, ast_cli_unregister_multiple(), ast_custom_function_unregister(), ast_http_uri_unlink(), AST_LIST_REMOVE_HEAD, ast_var_delete(), delete_profiles(), delete_routes(), delete_users(), ast_var_t::entries, global_variables, http_routes, phoneprovuri, pp_cli, pp_each_user_function, profiles, and var.

01016 {
01017    struct ast_var_t *var;
01018 
01019    ast_http_uri_unlink(&phoneprovuri);
01020    ast_custom_function_unregister(&pp_each_user_function);
01021    ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
01022 
01023    delete_routes();
01024    delete_users();
01025    delete_profiles();
01026    ao2_ref(profiles, -1);
01027    ao2_ref(http_routes, -1);
01028 
01029    while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries)))
01030       ast_var_delete(var);
01031 
01032    return 0;
01033 }

static struct phone_profile* unref_profile ( struct phone_profile prof  )  [static]

Definition at line 211 of file res_phoneprov.c.

References ao2_ref().

Referenced by build_profile(), build_user(), delete_profiles(), and delete_user().

00212 {
00213    ao2_ref(prof, -1);
00214 
00215    return NULL;
00216 }

static struct http_route* unref_route ( struct http_route route  )  [static]

Definition at line 267 of file res_phoneprov.c.

References ao2_ref().

Referenced by build_route(), delete_routes(), handle_show_routes(), and phoneprov_callback().

00268 {
00269    ao2_ref(route, -1);
00270 
00271    return NULL;
00272 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "HTTP Phone Provisioning" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1049 of file res_phoneprov.c.

struct in_addr __ourip = { .s_addr = 0x00000000, } [static]

for use in lookup_iface

Definition at line 66 of file res_phoneprov.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1049 of file res_phoneprov.c.

char* ext

Definition at line 146 of file res_phoneprov.c.

char global_default_profile[80] = ""

Default profile to use if one isn't specified

Definition at line 159 of file res_phoneprov.c.

char global_server[80] = ""

Server to substitute into templates

Definition at line 157 of file res_phoneprov.c.

char global_serverport[6] = ""

Server port to substitute into templates

Definition at line 158 of file res_phoneprov.c.

struct varshead global_variables

List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH.

Definition at line 162 of file res_phoneprov.c.

Referenced by load_module(), and unload_module().

struct ao2_container* http_routes [static]

Definition at line 141 of file res_phoneprov.c.

Referenced by build_route(), delete_routes(), handle_show_routes(), load_module(), phoneprov_callback(), and unload_module().

struct { ... } mimetypes[] [static]

Extensions whose mime types we think we know.

char* mtype

Definition at line 147 of file res_phoneprov.c.

struct ast_http_uri phoneprovuri [static]

Definition at line 991 of file res_phoneprov.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry pp_cli[] [static]

Initial value:

 {
   { .handler =  handle_show_routes , .summary =  "Show registered phoneprov http routes" ,__VA_ARGS__ },
}

Definition at line 987 of file res_phoneprov.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function pp_each_user_function [static]

Definition at line 932 of file res_phoneprov.c.

Referenced by load_module(), and unload_module().

struct pp_variable_lookup pp_variable_list[] [static]

Lookup table to translate between users.conf property names and variables for use in phoneprov templates.

Referenced by build_user().

struct ao2_container* profiles [static]

Definition at line 140 of file res_phoneprov.c.

Referenced by delete_profiles(), find_profile(), load_module(), and unload_module().


Generated on Thu Jul 9 13:41:31 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7