Author: gtjoseph Date: Thu Oct 9 12:45:23 2014 New Revision: 424964 URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=424964 Log: res_phoneprov: Refactor phoneprov to allow pluggable config providers
This patch makes res_phoneprov more modular so other modules (like pjsip) can provide configuration information instead of res_phoneprov relying solely on users.conf and sip.conf. To accomplish this a new ast_phoneprov public API is now exposed which allows config providers to register themselves, set defaults (server profile, etc) and add user extensions. * ast_phoneprov_provider_register registers the provider and provides callbacks for loading default settings and loading users. * ast_phoneprov_provider_unregister clears the defaults and users. * ast_phoneprov_add_extension should be called once for each user/extension by the provider's load_users callback to add them. * ast_phoneprov_delete_extension deletes one extension. * ast_phoneprov_delete_extensions deletes all extensions for the provider. Tested-by: George Joseph Review: https://reviewboard.asterisk.org/r/3970/ ........ Merged revisions 424963 from http://svn.asterisk.org/svn/asterisk/branches/12 Added: branches/13/include/asterisk/phoneprov.h - copied unchanged from r424963, branches/12/include/asterisk/phoneprov.h branches/13/res/res_phoneprov.exports.in - copied unchanged from r424963, branches/12/res/res_phoneprov.exports.in Modified: branches/13/ (props changed) branches/13/configs/samples/phoneprov.conf.sample branches/13/include/asterisk/chanvars.h branches/13/main/chanvars.c branches/13/res/res_phoneprov.c Propchange: branches/13/ ------------------------------------------------------------------------------ Binary property 'branch-12-merged' - no diff available. Modified: branches/13/configs/samples/phoneprov.conf.sample URL: http://svnview.digium.com/svn/asterisk/branches/13/configs/samples/phoneprov.conf.sample?view=diff&rev=424964&r1=424963&r2=424964 ============================================================================== --- branches/13/configs/samples/phoneprov.conf.sample (original) +++ branches/13/configs/samples/phoneprov.conf.sample Thu Oct 9 12:45:23 2014 @@ -1,4 +1,7 @@ [general] +; This section applies only to the default sip.conf/users.conf config provider +; embedded in res_phoneprov. Other providers may provide their own default settings. + ; The default behavior of res_phoneprov will be to set the SERVER template variable to ; the IP address that the phone uses to contact the provisioning server and the ; SERVER_PORT variable to the bindport setting in sip.conf. Unless you have a very @@ -15,7 +18,8 @@ ; with the provisioning server. You can define either static files, or dynamically ; generated files that can have dynamic names and point to templates that variables ; can be substituted into. You can also set arbitrary variables for the profiles -; templates to have access to. Example: +; templates to have access to. Profiles are shared across all config providers. +; Example: ;[example] ;mime_type => application/octet-stream @@ -25,7 +29,9 @@ ;setvar => DB_CIDNAME=${ODBC_CID_NAME_LOOKUP(${USERNAME})} ; Dynamically generated files have a filename registered with variable substitution -; with variables obtained while reading users.conf. +; with variables obtained from various config providers. The default provider +; embedded in res_phoneprov reads users.conf. Other providers will have their own +; sources for the variables and may provide additional variables not listed here. ; Built in variables and the options in users.conf that they come from ; MAC (macaddress) Modified: branches/13/include/asterisk/chanvars.h URL: http://svnview.digium.com/svn/asterisk/branches/13/include/asterisk/chanvars.h?view=diff&rev=424964&r1=424963&r2=424964 ============================================================================== --- branches/13/include/asterisk/chanvars.h (original) +++ branches/13/include/asterisk/chanvars.h Thu Oct 9 12:45:23 2014 @@ -33,6 +33,8 @@ AST_LIST_HEAD_NOLOCK(varshead, ast_var_t); +struct varshead *ast_var_list_create(void); +void ast_var_list_destroy(struct varshead *head); #ifdef MALLOC_DEBUG struct ast_var_t *_ast_var_assign(const char *name, const char *value, const char *file, int lineno, const char *function); #define ast_var_assign(a,b) _ast_var_assign(a,b,__FILE__,__LINE__,__PRETTY_FUNCTION__) @@ -43,5 +45,21 @@ const char *ast_var_name(const struct ast_var_t *var); const char *ast_var_full_name(const struct ast_var_t *var); const char *ast_var_value(const struct ast_var_t *var); +char *ast_var_find(const struct varshead *head, const char *name); +struct varshead *ast_var_list_clone(struct varshead *head); + +#define AST_VAR_LIST_TRAVERSE(head, var) AST_LIST_TRAVERSE(head, var, entries) + +static inline void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var) { + if (var) { + AST_LIST_INSERT_TAIL(head, var, entries); + } +} + +static inline void AST_VAR_LIST_INSERT_HEAD(struct varshead *head, struct ast_var_t *var) { + if (var) { + AST_LIST_INSERT_HEAD(head, var, entries); + } +} #endif /* _ASTERISK_CHANVARS_H */ Modified: branches/13/main/chanvars.c URL: http://svnview.digium.com/svn/asterisk/branches/13/main/chanvars.c?view=diff&rev=424964&r1=424963&r2=424964 ============================================================================== --- branches/13/main/chanvars.c (original) +++ branches/13/main/chanvars.c Thu Oct 9 12:45:23 2014 @@ -90,4 +90,67 @@ return (var ? var->value : NULL); } +char *ast_var_find(const struct varshead *head, const char *name) +{ + struct ast_var_t *var; + AST_LIST_TRAVERSE(head, var, entries) { + if (!strcmp(name, var->name)) { + return var->value; + } + } + return NULL; +} + +struct varshead *ast_var_list_create(void) +{ + struct varshead *head; + + head = ast_calloc(1, sizeof(*head)); + if (!head) { + return NULL; + } + AST_LIST_HEAD_INIT_NOLOCK(head); + return head; +} + +void ast_var_list_destroy(struct varshead *head) +{ + struct ast_var_t *var; + + if (!head) { + return; + } + + while ((var = AST_LIST_REMOVE_HEAD(head, entries))) { + ast_var_delete(var); + } + + ast_free(head); +} + +struct varshead *ast_var_list_clone(struct varshead *head) +{ + struct varshead *clone; + struct ast_var_t *var, *newvar; + + if (!head) { + return NULL; + } + + clone = ast_var_list_create(); + if (!clone) { + return NULL; + } + + AST_VAR_LIST_TRAVERSE(head, var) { + newvar = ast_var_assign(var->name, var->value); + if (!newvar) { + ast_var_list_destroy(clone); + return NULL; + } + AST_VAR_LIST_INSERT_TAIL(clone, newvar); + } + + return clone; +} Modified: branches/13/res/res_phoneprov.c URL: http://svnview.digium.com/svn/asterisk/branches/13/res/res_phoneprov.c?view=diff&rev=424964&r1=424963&r2=424964 ============================================================================== --- branches/13/res/res_phoneprov.c (original) +++ branches/13/res/res_phoneprov.c Thu Oct 9 12:45:23 2014 @@ -2,10 +2,12 @@ * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2008, Digium, Inc. + * Copyright (C) 2014, Fairview 5 Engineering, LLC * * Mark Spencer <[email protected]> * Matthew Brooks <[email protected]> * Terry Wilson <[email protected]> + * George Joseph <[email protected]> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -24,7 +26,8 @@ * * \author Matthew Brooks <[email protected]> * \author Terry Wilson <[email protected]> - */ + * \author George Joseph <[email protected]> + */ /*! \li \ref res_phoneprov.c uses the configuration file \ref phoneprov.conf and \ref users.conf and \ref sip.conf * \addtogroup configuration_file Configuration Files @@ -38,6 +41,8 @@ /*** MODULEINFO <support_level>extended</support_level> ***/ + +#define AST_API_MODULE #include "asterisk.h" @@ -65,12 +70,15 @@ #include "asterisk/acl.h" #include "asterisk/astobj2.h" #include "asterisk/ast_version.h" +#include "asterisk/phoneprov.h" #ifdef LOW_MEMORY +#define MAX_PROVIDER_BUCKETS 1 #define MAX_PROFILE_BUCKETS 1 #define MAX_ROUTE_BUCKETS 1 #define MAX_USER_BUCKETS 1 #else +#define MAX_PROVIDER_BUCKETS 17 #define MAX_PROFILE_BUCKETS 17 #define MAX_ROUTE_BUCKETS 563 #define MAX_USER_BUCKETS 563 @@ -85,7 +93,7 @@ </synopsis> <syntax> <parameter name="mac" required="true" /> - <parameter name="template" required="true" /> + <parameter name="template_file" required="true" /> </syntax> <description> <para>Output the specified template for each extension associated with the specified MAC address.</para> @@ -109,41 +117,162 @@ </function> ***/ +/*! + * \brief Creates a hash function for a structure string field. + * \param fname The name to use for the function + * \param stype The structure type + * \param field The field in the structure to hash + * + * SIMPLE_HASH_FN(mystruct, myfield) will produce a function + * named mystruct_hash_fn which hashes mystruct->myfield. + */ +#define SIMPLE_HASH_FN(fname, stype, field) \ +static int fname(const void *obj, const int flags) \ +{ \ + const struct stype *provider = obj; \ + const char *key; \ + switch (flags & OBJ_SEARCH_MASK) { \ + case OBJ_SEARCH_KEY: \ + key = obj; \ + break; \ + case OBJ_SEARCH_OBJECT: \ + provider = obj; \ + key = provider->field; \ + break; \ + default: \ + ast_assert(0); \ + return 0; \ + } \ + return ast_str_hash(key); \ +} + +/*! + * \brief Creates a compare function for a structure string field. + * \param fname The name to use for the function + * \param stype The structure type + * \param field The field in the structure to compare + * + * SIMPLE_CMP_FN(mystruct, myfield) will produce a function + * named mystruct_cmp_fn which compares mystruct->myfield. + */ +#define SIMPLE_CMP_FN(fname, stype, field) \ +static int fname(void *obj, void *arg, int flags) \ +{ \ + const struct stype *object_left = obj, *object_right = arg; \ + const char *right_key = arg; \ + int cmp; \ + switch (flags & OBJ_SEARCH_MASK) { \ + case OBJ_SEARCH_OBJECT: \ + right_key = object_right->field; \ + case OBJ_SEARCH_KEY: \ + cmp = strcmp(object_left->field, right_key); \ + break; \ + case OBJ_SEARCH_PARTIAL_KEY: \ + cmp = strncmp(object_left->field, right_key, strlen(right_key)); \ + break; \ + default: \ + cmp = 0; \ + break; \ + } \ + if (cmp) { \ + return 0; \ + } \ + return CMP_MATCH; \ +} + +const char *ast_phoneprov_std_variable_lookup[] = { + [AST_PHONEPROV_STD_MAC] = "MAC", + [AST_PHONEPROV_STD_PROFILE] = "PROFILE", + [AST_PHONEPROV_STD_USERNAME] = "USERNAME", + [AST_PHONEPROV_STD_DISPLAY_NAME] = "DISPLAY_NAME", + [AST_PHONEPROV_STD_SECRET] = "SECRET", + [AST_PHONEPROV_STD_LABEL] = "LABEL", + [AST_PHONEPROV_STD_CALLERID] = "CALLERID", + [AST_PHONEPROV_STD_TIMEZONE] = "TIMEZONE", + [AST_PHONEPROV_STD_LINENUMBER] = "LINE", + [AST_PHONEPROV_STD_LINEKEYS] = "LINEKEYS", + [AST_PHONEPROV_STD_SERVER] = "SERVER", + [AST_PHONEPROV_STD_SERVER_PORT] = "SERVER_PORT", + [AST_PHONEPROV_STD_SERVER_IFACE] = "SERVER_IFACE", + [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = "VOICEMAIL_EXTEN", + [AST_PHONEPROV_STD_EXTENSION_LENGTH] = "EXTENSION_LENGTH", + [AST_PHONEPROV_STD_TZOFFSET] = "TZOFFSET", + [AST_PHONEPROV_STD_DST_ENABLE] = "DST_ENABLE", + [AST_PHONEPROV_STD_DST_START_MONTH] = "DST_START_MONTH", + [AST_PHONEPROV_STD_DST_START_MDAY] = "DST_START_MDAY", + [AST_PHONEPROV_STD_DST_START_HOUR] = "DST_START_HOUR", + [AST_PHONEPROV_STD_DST_END_MONTH] = "DST_END_MONTH", + [AST_PHONEPROV_STD_DST_END_MDAY] = "DST_END_MDAY", + [AST_PHONEPROV_STD_DST_END_HOUR] = "DST_END_HOUR", +}; + +/* Translate the standard variables to their users.conf equivalents. */ +const char *pp_user_lookup[] = { + [AST_PHONEPROV_STD_MAC] = "macaddress", + [AST_PHONEPROV_STD_PROFILE] = "profile", + [AST_PHONEPROV_STD_USERNAME] = "username", + [AST_PHONEPROV_STD_DISPLAY_NAME] = "fullname", + [AST_PHONEPROV_STD_SECRET] = "secret", + [AST_PHONEPROV_STD_LABEL] = "label", + [AST_PHONEPROV_STD_CALLERID] = "cid_number", + [AST_PHONEPROV_STD_TIMEZONE] = "timezone", + [AST_PHONEPROV_STD_LINENUMBER] = "linenumber", + [AST_PHONEPROV_STD_LINEKEYS] = "linekeys", + [AST_PHONEPROV_STD_SERVER] = NULL, + [AST_PHONEPROV_STD_SERVER_PORT] = NULL, + [AST_PHONEPROV_STD_SERVER_IFACE] = NULL, + [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = "vmexten", + [AST_PHONEPROV_STD_EXTENSION_LENGTH] = "localextenlength", + [AST_PHONEPROV_STD_TZOFFSET] = NULL, + [AST_PHONEPROV_STD_DST_ENABLE] = NULL, + [AST_PHONEPROV_STD_DST_START_MONTH] = NULL, + [AST_PHONEPROV_STD_DST_START_MDAY] = NULL, + [AST_PHONEPROV_STD_DST_START_HOUR] = NULL, + [AST_PHONEPROV_STD_DST_END_MONTH] = NULL, + [AST_PHONEPROV_STD_DST_END_MDAY] = NULL, + [AST_PHONEPROV_STD_DST_END_HOUR] = NULL, +}; + +/* Translate the standard variables to their phoneprov.conf [general] equivalents. */ +const char *pp_general_lookup[] = { + [AST_PHONEPROV_STD_MAC] = NULL, + [AST_PHONEPROV_STD_PROFILE] = "default_profile", + [AST_PHONEPROV_STD_USERNAME] = NULL, + [AST_PHONEPROV_STD_DISPLAY_NAME] = NULL, + [AST_PHONEPROV_STD_SECRET] = NULL, + [AST_PHONEPROV_STD_LABEL] = NULL, + [AST_PHONEPROV_STD_CALLERID] = NULL, + [AST_PHONEPROV_STD_TIMEZONE] = NULL, + [AST_PHONEPROV_STD_LINENUMBER] = NULL, + [AST_PHONEPROV_STD_LINEKEYS] = NULL, + [AST_PHONEPROV_STD_SERVER] = "serveraddr", + [AST_PHONEPROV_STD_SERVER_PORT] = "serverport", + [AST_PHONEPROV_STD_SERVER_IFACE] = "serveriface", + [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = NULL, + [AST_PHONEPROV_STD_EXTENSION_LENGTH] = NULL, + [AST_PHONEPROV_STD_TZOFFSET] = NULL, + [AST_PHONEPROV_STD_DST_ENABLE] = NULL, + [AST_PHONEPROV_STD_DST_START_MONTH] = NULL, + [AST_PHONEPROV_STD_DST_START_MDAY] = NULL, + [AST_PHONEPROV_STD_DST_START_HOUR] = NULL, + [AST_PHONEPROV_STD_DST_END_MONTH] = NULL, + [AST_PHONEPROV_STD_DST_END_MDAY] = NULL, + [AST_PHONEPROV_STD_DST_END_HOUR] = NULL, +}; + /*! \brief for use in lookup_iface */ static struct in_addr __ourip = { .s_addr = 0x00000000, }; -/* \note This enum and the pp_variable_list must be in the same order or - * bad things happen! */ -enum pp_variables { - PP_MACADDRESS, - PP_USERNAME, - PP_FULLNAME, - PP_SECRET, - PP_LABEL, - PP_CALLERID, - PP_TIMEZONE, - PP_LINENUMBER, - PP_LINEKEYS, - PP_VAR_LIST_LENGTH, /* This entry must always be the last in the list */ +/*! \brief structure to hold config providers */ +struct phoneprov_provider { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(provider_name); + ); + ast_phoneprov_load_users_cb load_users; }; - -/*! \brief Lookup table to translate between users.conf property names and - * variables for use in phoneprov templates */ -static const struct pp_variable_lookup { - enum pp_variables id; - const char * const user_var; - const char * const template_var; -} pp_variable_list[] = { - { PP_MACADDRESS, "macaddress", "MAC" }, - { PP_USERNAME, "username", "USERNAME" }, - { PP_FULLNAME, "fullname", "DISPLAY_NAME" }, - { PP_SECRET, "secret", "SECRET" }, - { PP_LABEL, "label", "LABEL" }, - { PP_CALLERID, "cid_number", "CALLERID" }, - { PP_TIMEZONE, "timezone", "TIMEZONE" }, - { PP_LINENUMBER, "linenumber", "LINE" }, - { PP_LINEKEYS, "linekeys", "LINEKEYS" }, -}; +struct ao2_container *providers; +SIMPLE_HASH_FN(phoneprov_provider_hash_fn, phoneprov_provider, provider_name) +SIMPLE_CMP_FN(phoneprov_provider_cmp_fn, phoneprov_provider, provider_name) /*! \brief structure to hold file data */ struct phoneprov_file { @@ -153,6 +282,16 @@ AST_STRING_FIELD(mime_type);/*!< Mime-type of the file */ ); AST_LIST_ENTRY(phoneprov_file) entry; +}; + +/*! \brief structure to hold extensions */ +struct extension { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(name); + ); + int index; + struct varshead *headp; /*!< List of variables to substitute into templates */ + AST_LIST_ENTRY(extension) entry; }; /*! \brief structure to hold phone profiles read from phoneprov.conf */ @@ -166,24 +305,22 @@ AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files; /*!< List of static files */ AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files; /*!< List of dynamic files */ }; - -struct extension { - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(name); - ); - int index; - struct varshead *headp; /*!< List of variables to substitute into templates */ - AST_LIST_ENTRY(extension) entry; -}; +struct ao2_container *profiles; +SIMPLE_HASH_FN(phone_profile_hash_fn, phone_profile, name) +SIMPLE_CMP_FN(phone_profile_cmp_fn, phone_profile, name) /*! \brief structure to hold users read from users.conf */ struct user { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(macaddress); /*!< Mac address of user's phone */ + AST_STRING_FIELD(provider_name); /*!< Name of the provider who registered this mac */ ); struct phone_profile *profile; /*!< Profile the phone belongs to */ AST_LIST_HEAD_NOLOCK(, extension) extensions; }; +struct ao2_container *users; +SIMPLE_HASH_FN(user_hash_fn, user, macaddress) +SIMPLE_CMP_FN(user_cmp_fn, user, macaddress) /*! \brief structure to hold http routes (valid URIs, and the files they link to) */ struct http_route { @@ -193,19 +330,13 @@ struct phoneprov_file *file; /*!< The file that links to the URI */ struct user *user; /*!< The user that has variables to substitute into the file * NULL in the case of a static route */ + struct phone_profile *profile; }; - -static struct ao2_container *profiles; -static struct ao2_container *http_routes; -static struct ao2_container *users; - -static char global_server[80] = ""; /*!< Server to substitute into templates */ -static char global_serverport[6] = ""; /*!< Server port to substitute into templates */ -static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */ - -/*! \brief List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH */ -static struct varshead global_variables; -static ast_mutex_t globals_lock; +struct ao2_container *http_routes; +SIMPLE_HASH_FN(http_route_hash_fn, http_route, uri) +SIMPLE_CMP_FN(http_route_cmp_fn, http_route, uri) + +#define SIPUSERS_PROVIDER_NAME "sipusers" /* iface is the interface (e.g. eth0); address is the return value */ static int lookup_iface(const char *iface, struct in_addr *address) @@ -238,88 +369,31 @@ } } -static struct phone_profile *unref_profile(struct phone_profile *prof) -{ - ao2_ref(prof, -1); - - return NULL; -} - -/*! \brief Return a phone profile looked up by name */ -static struct phone_profile *find_profile(const char *name) -{ - struct phone_profile tmp = { - .name = name, - }; - - return ao2_find(profiles, &tmp, OBJ_POINTER); -} - -static int profile_hash_fn(const void *obj, const int flags) -{ - const struct phone_profile *profile = obj; - - return ast_str_case_hash(profile->name); -} - -static int profile_cmp_fn(void *obj, void *arg, int flags) -{ - const struct phone_profile *profile1 = obj, *profile2 = arg; - - return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH | CMP_STOP : 0; +static struct phoneprov_provider *find_provider(char *name) +{ + return ao2_find(providers, name, OBJ_SEARCH_KEY); +} + +/*! \brief Delete all providers */ +static void delete_providers(void) +{ + if (!providers) { + return; + } + + ao2_callback(providers, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); +} + +static void provider_destructor(void *obj) +{ + struct phoneprov_provider *provider = obj; + ast_string_field_free_memory(provider); } static void delete_file(struct phoneprov_file *file) { ast_string_field_free_memory(file); free(file); -} - -static void profile_destructor(void *obj) -{ - struct phone_profile *profile = obj; - struct phoneprov_file *file; - struct ast_var_t *var; - - while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry))) - delete_file(file); - - while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry))) - delete_file(file); - - while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries))) - ast_var_delete(var); - - ast_free(profile->headp); - ast_string_field_free_memory(profile); -} - -static struct http_route *unref_route(struct http_route *route) -{ - ao2_ref(route, -1); - - return NULL; -} - -static int routes_hash_fn(const void *obj, const int flags) -{ - const struct http_route *route = obj; - - return ast_str_case_hash(route->uri); -} - -static int routes_cmp_fn(void *obj, void *arg, int flags) -{ - const struct http_route *route1 = obj, *route2 = arg; - - return !strcasecmp(route1->uri, route2->uri) ? CMP_MATCH | CMP_STOP : 0; -} - -static void route_destructor(void *obj) -{ - struct http_route *route = obj; - - ast_string_field_free_memory(route); } /*! \brief Read a TEXT file into a string and return the length */ @@ -365,189 +439,66 @@ struct ast_tm tm_info; int tzoffset; char buffer[21]; - struct ast_var_t *var; struct timeval when; time(&utc_time); ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone); snprintf(buffer, sizeof(buffer), "%d", tzoffset); - var = ast_var_assign("TZOFFSET", buffer); - if (var) - AST_LIST_INSERT_TAIL(headp, var, entries); - - if (!dstenable) + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("TZOFFSET", buffer)); + + if (!dstenable) { return; - - if ((var = ast_var_assign("DST_ENABLE", "1"))) - AST_LIST_INSERT_TAIL(headp, var, entries); + } + + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_ENABLE", "1")); when.tv_sec = dststart; ast_localtime(&when, &tm_info, zone); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1); - if ((var = ast_var_assign("DST_START_MONTH", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_MONTH", buffer)); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday); - if ((var = ast_var_assign("DST_START_MDAY", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_MDAY", buffer)); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour); - if ((var = ast_var_assign("DST_START_HOUR", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_HOUR", buffer)); when.tv_sec = dstend; ast_localtime(&when, &tm_info, zone); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1); - if ((var = ast_var_assign("DST_END_MONTH", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_MONTH", buffer)); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday); - if ((var = ast_var_assign("DST_END_MDAY", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_MDAY", buffer)); snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour); - if ((var = ast_var_assign("DST_END_HOUR", buffer))) - AST_LIST_INSERT_TAIL(headp, var, entries); -} - -/*! \brief Callback that is executed everytime an http request is received by this module */ -static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers) -{ - struct http_route *route; - struct http_route search_route = { - .uri = uri, - }; - struct ast_str *result; - char path[PATH_MAX]; - char *file = NULL; - int len; - int fd; - struct ast_str *http_header; - - if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) { - ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); - return 0; - } - - if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) { - goto out404; - } - - snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template); - - if (!route->user) { /* Static file */ - - fd = open(path, O_RDONLY); - if (fd < 0) { - goto out500; - } - - len = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - if (len < 0) { - ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len); - close(fd); - goto out500; - } - - http_header = ast_str_create(80); - ast_str_set(&http_header, 0, "Content-type: %s\r\n", - route->file->mime_type); - - ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0); - - close(fd); - route = unref_route(route); - return 0; - } else { /* Dynamic file */ - struct ast_str *tmp; - - len = load_file(path, &file); - if (len < 0) { - ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len); - if (file) { - ast_free(file); - } - - goto out500; - } - - if (!file) { - goto out500; - } - - if (!(tmp = ast_str_create(len))) { - if (file) { - ast_free(file); - } - - goto out500; - } - - /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to - * the IP address we are listening on that the phone contacted for this config file */ - if (ast_strlen_zero(global_server)) { - union { - struct sockaddr sa; - struct sockaddr_in sa_in; - } name; - socklen_t namelen = sizeof(name.sa); - int res; - - if ((res = getsockname(ser->fd, &name.sa, &namelen))) { - ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n"); - } else { - struct ast_var_t *var; - struct extension *exten_iter; - - if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr)))) { - AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) { - AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries); - } - } - } - } - - ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file); - - if (file) { - ast_free(file); - } - - http_header = ast_str_create(80); - ast_str_set(&http_header, 0, "Content-type: %s\r\n", - route->file->mime_type); - - if (!(result = ast_str_create(512))) { - ast_log(LOG_ERROR, "Could not create result string!\n"); - if (tmp) { - ast_free(tmp); - } - ast_free(http_header); - goto out500; - } - ast_str_append(&result, 0, "%s", ast_str_buffer(tmp)); - - ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0); - if (tmp) { - ast_free(tmp); - } - - route = unref_route(route); - - return 0; - } - -out404: - ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along."); - return 0; - -out500: - route = unref_route(route); - ast_http_error(ser, 500, "Internal Error", "An internal error has occured."); - return 0; + AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_HOUR", buffer)); +} + +static struct http_route *unref_route(struct http_route *route) +{ + ao2_cleanup(route); + + return NULL; +} + +static void route_destructor(void *obj) +{ + struct http_route *route = obj; + + ast_string_field_free_memory(route); +} + +/*! \brief Delete all http routes, freeing their memory */ +static void delete_routes(void) +{ + if (!http_routes) { + return; + } + + ao2_callback(http_routes, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); } /*! \brief Build a route structure and add it to the list of available http routes @@ -555,7 +506,7 @@ \param user User to link to the route (NULL means static route) \param uri URI of the route */ -static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri) +static void build_route(struct phoneprov_file *pp_file, struct phone_profile *profile, struct user *user, char *uri) { struct http_route *route; @@ -572,10 +523,56 @@ ast_string_field_set(route, uri, S_OR(uri, pp_file->format)); route->user = user; route->file = pp_file; + route->profile = profile; ao2_link(http_routes, route); route = unref_route(route); +} + +static struct phone_profile *unref_profile(struct phone_profile *prof) +{ + ao2_cleanup(prof); + + return NULL; +} + +/*! \brief Return a phone profile looked up by name */ +static struct phone_profile *find_profile(const char *name) +{ + return ao2_find(profiles, name, OBJ_SEARCH_KEY); +} + +static void profile_destructor(void *obj) +{ + struct phone_profile *profile = obj; + struct phoneprov_file *file; + struct ast_var_t *var; + + while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry))) { + delete_file(file); + } + + while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry))) { + delete_file(file); + } + + while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries))) { + ast_var_delete(var); + } + + ast_free(profile->headp); + ast_string_field_free_memory(profile); +} + +/*! \brief Delete all phone profiles, freeing their memory */ +static void delete_profiles(void) +{ + if (!profiles) { + return; + } + + ao2_callback(profiles, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); } /*! \brief Build a phone profile and add it to the list of phone profiles @@ -585,7 +582,6 @@ static void build_profile(const char *name, struct ast_variable *v) { struct phone_profile *profile; - struct ast_var_t *var; if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) { return; @@ -596,7 +592,7 @@ return; } - if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) { + if (!(profile->headp = ast_var_list_create())) { profile = unref_profile(profile); return; } @@ -609,7 +605,6 @@ if (!strcasecmp(v->name, "mime_type")) { ast_string_field_set(profile, default_mime_type, v->value); } else if (!strcasecmp(v->name, "setvar")) { - struct ast_var_t *variable; char *value_copy = ast_strdupa(v->value); AST_DECLARE_APP_ARGS(args, @@ -625,8 +620,7 @@ args.varval = ast_strip(args.varval); if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval)) break; - if ((variable = ast_var_assign(args.varname, args.varval))) - AST_LIST_INSERT_TAIL(profile->headp, variable, entries); + AST_VAR_LIST_INSERT_TAIL(profile->headp, ast_var_assign(args.varname, args.varval)); } while (0); } else if (!strcasecmp(v->name, "staticdir")) { ast_string_field_set(profile, staticdir, v->value); @@ -664,7 +658,7 @@ ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename); AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry); /* Add a route for the static files, as their filenames won't change per-user */ - build_route(pp_file, NULL, NULL); + build_route(pp_file, profile, NULL, NULL); } else { ast_string_field_set(pp_file, format, v->name); ast_string_field_set(pp_file, template, args.filename); @@ -673,18 +667,6 @@ } } - /* Append the global variables to the variables list for this profile. - * This is for convenience later, when we need to provide a single - * variable list for use in substitution. */ - ast_mutex_lock(&globals_lock); - AST_LIST_TRAVERSE(&global_variables, var, entries) { - struct ast_var_t *new_var; - if ((new_var = ast_var_assign(var->name, var->value))) { - AST_LIST_INSERT_TAIL(profile->headp, new_var, entries); - } - } - ast_mutex_unlock(&globals_lock); - ao2_link(profiles, profile); profile = unref_profile(profile); @@ -692,24 +674,17 @@ static struct extension *delete_extension(struct extension *exten) { - struct ast_var_t *var; - while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) { - ast_var_delete(var); - } - ast_free(exten->headp); + ast_var_list_destroy(exten->headp); ast_string_field_free_memory(exten); - ast_free(exten); return NULL; } -static struct extension *build_extension(struct ast_config *cfg, const char *name) +static struct extension *build_extension(const char *name, struct varshead *vars) { struct extension *exten; - struct ast_var_t *var; const char *tmp; - int i; if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) { return NULL; @@ -717,56 +692,36 @@ ast_string_field_set(exten, name, name); - if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) { - ast_free(exten); - exten = NULL; + exten->headp = ast_var_list_clone(vars); + if (!exten->headp) { + ast_log(LOG_ERROR, "Unable to clone variables for extension '%s'\n", name); + delete_extension(exten); return NULL; } - for (i = 0; i < PP_VAR_LIST_LENGTH; i++) { - tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var); - - /* If we didn't get a USERNAME variable, set it to the user->name */ - if (i == PP_USERNAME && !tmp) { - if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) { - AST_LIST_INSERT_TAIL(exten->headp, var, entries); - } - continue; - } else if (i == PP_TIMEZONE) { - /* perfectly ok if tmp is NULL, will set variables based on server's time zone */ - set_timezone_variables(exten->headp, tmp); - } else if (i == PP_LINENUMBER) { - if (!tmp) { - tmp = "1"; - } - exten->index = atoi(tmp); - } else if (i == PP_LINEKEYS) { - if (!tmp) { - tmp = "1"; - } - } - - if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) { - AST_LIST_INSERT_TAIL(exten->headp, var, entries); - } - } - - if (!ast_strlen_zero(global_server)) { - if ((var = ast_var_assign("SERVER", global_server))) - AST_LIST_INSERT_TAIL(exten->headp, var, entries); - } - - if (!ast_strlen_zero(global_serverport)) { - if ((var = ast_var_assign("SERVER_PORT", global_serverport))) - AST_LIST_INSERT_TAIL(exten->headp, var, entries); - } + tmp = ast_var_find(exten->headp, ast_phoneprov_std_variable_lookup[AST_PHONEPROV_STD_LINENUMBER]); + if (!tmp) { + AST_VAR_LIST_INSERT_TAIL(exten->headp, + ast_var_assign(ast_phoneprov_std_variable_lookup[AST_PHONEPROV_STD_LINENUMBER], "1")); + exten->index = 1; + } else { + sscanf(tmp, "%d", &exten->index); + } + + if (!ast_var_find(exten->headp, ast_phoneprov_std_variable_lookup[AST_PHONEPROV_STD_LINEKEYS])) { + AST_VAR_LIST_INSERT_TAIL(exten->headp, + ast_var_assign(ast_phoneprov_std_variable_lookup[AST_PHONEPROV_STD_LINEKEYS], "1")); + } + + set_timezone_variables(exten->headp, + ast_var_find(vars, ast_phoneprov_std_variable_lookup[AST_PHONEPROV_STD_TIMEZONE])); return exten; } static struct user *unref_user(struct user *user) { - ao2_ref(user, -1); + ao2_cleanup(user); return NULL; } @@ -774,25 +729,19 @@ /*! \brief Return a user looked up by name */ static struct user *find_user(const char *macaddress) { - struct user tmp = { - .macaddress = macaddress, - }; - - return ao2_find(users, &tmp, OBJ_POINTER); -} - -static int users_hash_fn(const void *obj, const int flags) -{ - const struct user *user = obj; - - return ast_str_case_hash(user->macaddress); -} - -static int users_cmp_fn(void *obj, void *arg, int flags) -{ - const struct user *user1 = obj, *user2 = arg; - - return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0; + return ao2_find(users, macaddress, OBJ_SEARCH_KEY); +} + +static int routes_delete_cb(void *obj, void *arg, int flags) +{ + struct http_route *route = obj; + struct user *user = route->user; + char *macaddress = arg; + + if (user && !strcmp(user->macaddress, macaddress)) { + return CMP_MATCH; + } + return 0; } /*! \brief Free all memory associated with a user */ @@ -809,41 +758,39 @@ user->profile = unref_profile(user->profile); } + ao2_callback(http_routes, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, routes_delete_cb, (void *)user->macaddress); + ast_string_field_free_memory(user); } /*! \brief Delete all users */ static void delete_users(void) { - struct ao2_iterator i; + if (!users) { + return; + } + + ao2_callback(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); +} + +/*! \brief Build and return a user structure based on gathered config data */ +static struct user *build_user(const char *mac, struct phone_profile *profile, char *provider_name) +{ struct user *user; - i = ao2_iterator_init(users, 0); - while ((user = ao2_iterator_next(&i))) { - ao2_unlink(users, user); - user = unref_user(user); - } - ao2_iterator_destroy(&i); -} - -/*! \brief Build and return a user structure based on gathered config data */ -static struct user *build_user(const char *mac, struct phone_profile *profile) -{ - struct user *user; - if (!(user = ao2_alloc(sizeof(*user), user_destructor))) { - profile = unref_profile(profile); return NULL; } - if (ast_string_field_init(user, 32)) { - profile = unref_profile(profile); + if (ast_string_field_init(user, 64)) { user = unref_user(user); return NULL; } ast_string_field_set(user, macaddress, mac); - user->profile = profile; /* already ref counted by find_profile */ + ast_string_field_set(user, provider_name, provider_name); + user->profile = profile; + ao2_ref(profile, 1); return user; } @@ -851,7 +798,7 @@ /*! \brief Add an extension to a user ordered by index/linenumber */ static int add_user_extension(struct user *user, struct extension *exten) { - struct ast_var_t *var; + struct ast_var_t *pvar, *var2; struct ast_str *str = ast_str_create(16); if (!str) { @@ -860,15 +807,16 @@ /* Append profile variables here, and substitute variables on profile * setvars, so that we can use user specific variables in them */ - AST_LIST_TRAVERSE(user->profile->headp, var, entries) { - struct ast_var_t *var2; - - ast_str_substitute_variables_varshead(&str, 0, exten->headp, var->value); - if ((var2 = ast_var_assign(var->name, ast_str_buffer(str)))) { - AST_LIST_INSERT_TAIL(exten->headp, var2, entries); - } - } - + AST_VAR_LIST_TRAVERSE(user->profile->headp, pvar) { + if (ast_var_find(exten->headp, pvar->name)) { + continue; + } + + ast_str_substitute_variables_varshead(&str, 0, exten->headp, pvar->value); + if ((var2 = ast_var_assign(pvar->name, ast_str_buffer(str)))) { + AST_VAR_LIST_INSERT_TAIL(exten->headp, var2); + } + } ast_free(str); if (AST_LIST_EMPTY(&user->extensions)) { @@ -904,187 +852,146 @@ AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) { ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format); - build_route(pp_file, user, ast_str_buffer(str)); + build_route(pp_file, user->profile, user, ast_str_buffer(str)); } ast_free(str); return 0; } -/* \brief Parse config files and create appropriate structures */ -static int set_config(void) -{ - struct ast_config *cfg, *phoneprov_cfg; - char *cat; - struct ast_variable *v; - struct ast_flags config_flags = { 0 }; - struct ast_var_t *var; - - /* Try to grab the port from sip.conf. If we don't get it here, we'll set it - * to whatever is set in phoneprov.conf or default to 5060 */ - if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) { - ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport)); - ast_config_destroy(cfg); - } - - if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_WARNING, "Unable to load users.conf\n"); +/*! \brief Callback that is executed everytime an http request is received by this module */ +static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers) +{ + struct http_route *route; + struct ast_str *result; + char path[PATH_MAX]; + char *file = NULL; + char *server; + int len; + int fd; + struct ast_str *http_header; + + if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) { + ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); return 0; } - /* Go ahead and load global variables from users.conf so we can append to profiles */ - for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { - if (!strcasecmp(v->name, "vmexten")) { [... 861 lines stripped ...] -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits
