Wed Oct 28 11:46:18 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"

Include dependency graph for res_phoneprov.c:

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 __fini_users (void)
static void __init_users (void)
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 = AST_BUILDOPT_SUM, .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_info
ast_module_info = &__mod_info
char global_default_profile [80] = ""
char global_server [80] = ""
char global_serverport [6] = ""
struct 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

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 __fini_users ( void   )  [static]

Definition at line 142 of file res_phoneprov.c.

00145 {

static void __init_users ( void   )  [static]

Definition at line 142 of file res_phoneprov.c.

00145 {

static void __reg_module ( void   )  [static]

Definition at line 1057 of file res_phoneprov.c.

static void __unreg_module ( void   )  [static]

Definition at line 1057 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 494 of file res_phoneprov.c.

References ao2_alloc(), ao2_link(), AST_APP_ARG, ast_calloc, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_NONSTANDARD_APP_ARGS, AST_STANDARD_APP_ARGS, ast_strdupa, ast_string_field_build, ast_string_field_init, ast_string_field_set, ast_strip(), ast_strlen_zero(), ast_var_assign(), build_route(), phone_profile::default_mime_type, phone_profile::dynamic_files, ast_var_t::entries, phoneprov_file::format, ftype2mtype(), global_variables, phone_profile::headp, phoneprov_file::mime_type, ast_var_t::name, ast_variable::name, ast_variable::next, profile_destructor(), S_OR, phone_profile::static_files, phone_profile::staticdir, unref_profile(), ast_var_t::value, ast_variable::value, and var.

Referenced by set_config().

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

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 468 of file res_phoneprov.c.

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

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

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

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

Definition at line 688 of file res_phoneprov.c.

References ast_calloc, ast_free, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_var_assign(), ast_variable_retrieve(), delete_user(), ast_var_t::entries, phone_profile::headp, user::headp, ast_var_t::name, user::name, pbx_substitute_variables_varshead(), PP_TIMEZONE, PP_USERNAME, PP_VAR_LIST_LENGTH, pp_variable_list, user::profile, set_timezone_variables(), unref_profile(), ast_var_t::value, var, and VAR_BUF_SIZE.

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

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 759 of file res_phoneprov.c.

References AST_LIST_TRAVERSE, build_route(), phone_profile::dynamic_files, phoneprov_file::format, user::headp, pbx_substitute_variables_varshead(), user::profile, and VAR_BUF_SIZE.

Referenced by set_config().

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

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 898 of file res_phoneprov.c.

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

Referenced by reload(), and unload_module().

00899 {
00900    struct ao2_iterator i;
00901    struct phone_profile *profile;
00902 
00903    i = ao2_iterator_init(profiles, 0);
00904    while ((profile = ao2_iterator_next(&i))) {
00905       ao2_unlink(profiles, profile);
00906       profile = unref_profile(profile);
00907    }
00908    ao2_iterator_destroy(&i);
00909 }

static void delete_routes ( void   )  [static]

Delete all http routes, freeing their memory.

Definition at line 884 of file res_phoneprov.c.

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

Referenced by reload(), and unload_module().

00885 {
00886    struct ao2_iterator i;
00887    struct http_route *route;
00888    
00889    i = ao2_iterator_init(http_routes, 0);
00890    while ((route = ao2_iterator_next(&i))) {
00891       ao2_unlink(http_routes, route);
00892       route = unref_route(route);
00893    }
00894    ao2_iterator_destroy(&i);
00895 }

static void delete_user ( struct user user  )  [static]

Free all memory associated with a user.

Definition at line 603 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, user::headp, user::profile, unref_profile(), and var.

Referenced by build_user(), delete_users(), and set_config().

00604 {
00605    struct ast_var_t *var;
00606 
00607    while ((var = AST_LIST_REMOVE_HEAD(user->headp, entries)))
00608       ast_var_delete(var);
00609 
00610    ast_free(user->headp);
00611    ast_string_field_free_memory(user);
00612    user->profile = unref_profile(user->profile);
00613    free(user);
00614 }

static void delete_users ( void   )  [static]

Destroy entire user list.

Definition at line 617 of file res_phoneprov.c.

References AST_LIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and delete_user().

00618 {
00619    struct user *user;
00620 
00621    AST_RWLIST_WRLOCK(&users);
00622    while ((user = AST_LIST_REMOVE_HEAD(&users, entry)))
00623       delete_user(user);
00624    AST_RWLIST_UNLOCK(&users);
00625 }

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

Return a phone profile looked up by name.

Definition at line 219 of file res_phoneprov.c.

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

Referenced by set_config().

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, mimetypes, and mtype.

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 952 of file res_phoneprov.c.

References ao2_iterator_destroy(), 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, phoneprov_file::template, unref_route(), http_route::uri, ast_cli_entry::usage, and http_route::user.

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

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]

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, read]

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, len(), load_file(), LOG_WARNING, phoneprov_file::mime_type, name, 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          union {
00413             struct sockaddr sa;
00414             struct sockaddr_in sa_in;
00415          } name;
00416          socklen_t namelen = sizeof(name.sa);
00417          int res;
00418 
00419          if ((res = getsockname(ser->fd, &name.sa, &namelen)))
00420             ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00421          else {
00422             struct ast_var_t *var;
00423 
00424             if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr))))
00425             if ((var = ast_var_assign("SERVER", ast_inet_ntoa(((struct sockaddr_in *)&name)->sin_addr))))
00426                AST_LIST_INSERT_TAIL(route->user->headp, var, entries);
00427          }
00428       }
00429 
00430       pbx_substitute_variables_varshead(route->user->headp, file, tmp, bufsize);
00431    
00432       if (file)
00433          ast_free(file);
00434 
00435       ast_str_append(&result, 0,
00436          "Content-Type: %s\r\n"
00437          "Content-length: %d\r\n"
00438          "\r\n"
00439          "%s", route->file->mime_type, (int) strlen(tmp), tmp);
00440 
00441       if (tmp)
00442          ast_free(tmp);
00443 
00444       route = unref_route(route);
00445 
00446       return result;
00447    }
00448 
00449 out404:
00450    *status = 404;
00451    *title = strdup("Not Found");
00452    *contentlength = 0;
00453    return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
00454 
00455 out500:
00456    route = unref_route(route);
00457    *status = 500;
00458    *title = strdup("Internal Server Error");
00459    *contentlength = 0;
00460    return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
00461 }

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 912 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::headp, user::macaddress, pbx_substitute_variables_varshead(), and VAR_BUF_SIZE.

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

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, 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 1043 of file res_phoneprov.c.

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

01044 {
01045    delete_routes();
01046    delete_users();
01047    delete_profiles();
01048    set_config();
01049 
01050    return 0;
01051 }

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 774 of file res_phoneprov.c.

References ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_inet_ntoa(), AST_LIST_INSERT_TAIL, ast_log(), AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_true(), ast_var_assign(), ast_variable_browse(), ast_variable_retrieve(), build_profile(), build_user(), build_user_routes(), delete_user(), ast_var_t::entries, find_profile(), global_variables, LOG_ERROR, LOG_WARNING, lookup_iface(), user::name, ast_variable::name, ast_variable::next, S_OR, ast_variable::value, and var.

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

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 631 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().

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

static int unload_module ( void   )  [static]

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

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, read]

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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1057 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 1057 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.

List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH.

Definition at line 162 of file res_phoneprov.c.

Referenced by build_profile(), load_module(), set_config(), and unload_module().

struct ao2_container* http_routes [static]

Definition at line 141 of file res_phoneprov.c.

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 999 of file res_phoneprov.c.

struct ast_cli_entry pp_cli[] [static]

Initial value:

 {
   AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
}

Definition at line 995 of file res_phoneprov.c.

Definition at line 938 of file res_phoneprov.c.

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.


Generated on Wed Oct 28 11:46:18 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6