From: George Joseph Date: Tue, 10 Feb 2015 23:17:17 +0000 (+0000) Subject: res_pjsip_config_wizard: Add ability to auto-create hints. X-Git-Tag: 14.0.0-beta1~1261 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=49161d8df80925ff674204d288ba491876530715;p=thirdparty%2Fasterisk.git res_pjsip_config_wizard: Add ability to auto-create hints. Looking at the Super Awesome Company sample reminded me that creating hints is just plain gruntwork. So you can now have the pjsip conifg wizard auto-create them for you. Specifying 'hint_exten' in the wizard will create 'exten => ,hint/PJSIP/' in whatever is specified for 'hint_context'. Specifying 'hint_application' in the wizard will create 'exten => ,1,' in whatever is specified for 'hint_context'. The default for 'hint_context' is the endpoint's context. There's no default for 'hint_application'. If not specified, no app is added. There's no default for 'hint_exten'. If not specified, neither the hint itself nor the application will be created. Some may think this is the slippery slope to users.conf but hints are a basic necessity for phones unlike voicemail, manager, etc that users.conf creates. Tested-by: George Joseph Review: https://reviewboard.asterisk.org/r/4383/ ........ Merged revisions 431643 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431644 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/configs/samples/pjsip_wizard.conf.sample b/configs/samples/pjsip_wizard.conf.sample index b27e146beb..0f46083af5 100644 --- a/configs/samples/pjsip_wizard.conf.sample +++ b/configs/samples/pjsip_wizard.conf.sample @@ -24,7 +24,7 @@ ;============EXAMPLE WIZARD CONFIGURATION FOR A PHONE======================= ; This config would create an endpoint, aor with dynamic contact, inbound -; auth and a phoneprov object. +; auth, a phoneprov object and a dialplan hint for extension 1000. ;[myphone] ;type = wizard @@ -32,6 +32,8 @@ ;accepts_registrations = yes ;has_phoneprov = yes ;transport = ipv4 +;has_hint = yes +;hint_exten = 1000 ;inbound_auth/username = testname ;inbound_auth/password = test password ;endpoint/allow = ulaw @@ -116,6 +118,24 @@ ; If yes, phoneprov/MAC must be specified. ; (default: "no") +;has_hint= ; Create hint and optionally a default application. + ; (default: "no") + +;hint_context ; Any hints created for this wizard will be placed in this + ; context. + ; (default: endpoint/context) + +;hint_exten ; If specified, a PJSIP/ hint will be created + ; for this extension in 'hint_context'. + ; context. + ; (default: none) + +;hint_application ; If specified, an extension will be placed in 'hint_context' + ; at priority 1 that calls this application. Could be any + ; valid dialplan expression like + ; "Gosub(stdexten,${EXTEN},1(${HINT}))" + ; (default: "Dial(${HINT})") + ;endpoint/ ; Any parameters to be passed directly to and validated ;aor/ ; by their respective objects. ;inbound_auth/ diff --git a/res/res_pjsip_config_wizard.c b/res/res_pjsip_config_wizard.c index d88d84e54a..2d7e15c33b 100644 --- a/res/res_pjsip_config_wizard.c +++ b/res/res_pjsip_config_wizard.c @@ -64,7 +64,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") For example, the following configuration snippet would create the endpoint, aor, contact, auth and phoneprov objects necessary for a phone to - get phone provisioning information, register, and make and receive calls. + get phone provisioning information, register, and make and receive calls. + A hint is also created in the default context for extension 1000. [myphone] @@ -75,6 +76,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") accepts_registrations = yes has_phoneprov = yes transport = ipv4 + has_hint = yes + hint_exten = 1000 inbound_auth/username = testname inbound_auth/password = test password endpoint/allow = ulaw @@ -83,7 +86,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") phoneprov/PROFILE = profile1 - The first 7 items are specific to the wizard. The rest of the items + The first 8 items are specific to the wizard. The rest of the items are passed verbatim to the underlying objects. @@ -108,11 +111,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") Of course, any of the items in either example could be placed into templates and shared among wizard objects. + + + For more information, visit: + https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard Provides config wizard. + + For more information, visit: + https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard + Must be 'wizard'. @@ -171,6 +182,52 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") The literal ${REMOTE_HOST} will be substituted with the appropriate remote_host for each contact. + + Create hint and optionally a default application. + Create hint and optionally a default application. + + + The context in which to place hints. + + Ignored if hint_exten is not specified otherwise specifies the + context into which the dialplan hints will be placed. If not specified, + defaults to the endpoint's context or default if that isn't + found. + + + + Extension to map a PJSIP hint to. + + Will create the following entry in hint_context: + exten => <hint_exten>,hint,PJSIP/<wizard_id> + + Normal dialplan precedence rules apply so if there's already a hint for + this extension in hint_context, this one will be ignored. + For more information, visit: + https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard + + + + Application to call when 'hint_exten' is dialed. + + Ignored if hint_exten isn't specified otherwise + will create the following priority 1 extension in hint_context: + exten => <hint_exten>,1,<hint_application> + + You can specify any valid extensions.conf application expression. + Examples: + Dial(${HINT}) + Gosub(stdexten,${EXTEN},1(${HINT})) + + Any extensions.conf style variables specified are passed directly to the + dialplan. + + Normal dialplan precedence rules apply so if there's already a priority 1 + application for this specific extension in hint_context, + this one will be ignored. For more information, visit: + https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard + + Variables to be passed directly to the endpoint. @@ -206,6 +263,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*! \brief Defines the maximum number of characters that can be added to a wizard id. */ #define MAX_ID_SUFFIX 20 +#define BASE_REGISTRAR "res_pjsip_config_wizard" + /*! \brief A generic char * vector definition. */ AST_VECTOR(string_vector, char *); @@ -329,6 +388,134 @@ static struct ast_variable *get_object_variables(struct ast_variable *vars, char return return_vars; } +/* Don't call while holding context locks. */ +static int delete_extens(const char *context, const char *exten) +{ + struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ + + if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) { + ast_context_remove_extension(context, exten, PRIORITY_HINT, BASE_REGISTRAR); + } + + if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) { + ast_context_remove_extension(context, exten, 1, BASE_REGISTRAR); + } + + return 0; +} + +static int add_extension(struct ast_context *context, const char *exten, + int priority, const char *application) +{ + struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ + struct ast_exten *existing_exten; + char *data = NULL; + char *app = NULL; + void *free_ptr = NULL; + char *paren; + const char *context_name; + + if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) { + return -1; + } + + /* The incoming application has to be split into the app name and the + * arguments (data). The app name can be any storage type as add_extension + * copies it into its own buffer. Data however, needs to be dynamically + * allocated and a free function provided. + */ + + paren = strchr(application, '('); + if (!paren) { + app = (char *)application; + } else { + app = ast_strdupa(application); + app[paren - application] = '\0'; + data = ast_strdup(paren + 1); + if (!data) { + return -1; + } + data[strlen(data) - 1] = '\0'; + free_ptr = ast_free_ptr; + if (ast_strlen_zero(app) || ast_strlen_zero(data)) { + ast_free(data); + return -1; + } + } + + /* Don't disturb existing, exact-match, entries. */ + context_name = ast_get_context_name(context); + if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten, + priority, NULL, NULL, E_MATCH))) { + const char *existing_app = ast_get_extension_app(existing_exten); + const char *existing_data = ast_get_extension_app_data(existing_exten); + if (!strcmp(existing_app, app) + && !strcmp(existing_data ? existing_data : "", data ? data : "")) { + ast_free(data); + return 0; + } + + ast_context_remove_extension2(context, exten, priority, BASE_REGISTRAR, 1); + } + + if (ast_add_extension2_nolock(context, 0, exten, priority, NULL, NULL, + app, data, free_ptr, BASE_REGISTRAR)) { + ast_free(data); + return -1; + } + + return 0; +} + +static int add_hints(const char *context, const char *exten, const char *application, const char *id) +{ + struct ast_context *hint_context; + char *hint_device; + + hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1); + sprintf(hint_device, "PJSIP/%s", id); + + /* We need the contexts list locked to safely be able to both read and lock the specific context within */ + if (ast_wrlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock the contexts list.\n"); + return -1; + } + + if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) { + ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context); + if (ast_unlock_contexts()) { + ast_assert(0); + } + return -1; + } + + /* Transfer the all-contexts lock to the specific context */ + if (ast_wrlock_context(hint_context)) { + ast_unlock_contexts(); + ast_log(LOG_ERROR, "failed to obtain write lock on context\n"); + return -1; + } + ast_unlock_contexts(); + + if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) { + ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n", + exten, context); + } + + if (!ast_strlen_zero(application)) { + if (add_extension(hint_context, exten, 1, application)) { + ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n", + exten, context); + } + } else { + ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1); + } + + ast_unlock_context(hint_context); + + return 0; +} + static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, char *direction) { @@ -459,12 +646,29 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type struct ast_sorcery_object *obj = NULL; const char *id = ast_category_get_name(wiz); const char *transport = ast_variable_find_in_list(wizvars, "transport"); + const char *hint_context = hint_context = ast_variable_find_in_list(wizvars, "hint_context"); + const char *hint_exten = ast_variable_find_in_list(wizvars, "hint_exten"); + const char *hint_application= ast_variable_find_in_list(wizvars, "hint_application"); char new_id[strlen(id) + MAX_ID_SUFFIX]; RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy); variable_list_append_return(&vars, "@pjsip_wizard", id); variable_list_append_return(&vars, "aors", id); + if (ast_strlen_zero(hint_context)) { + hint_context = ast_variable_find_in_list(vars, "context"); + } + + if (ast_strlen_zero(hint_context)) { + hint_context = "default"; + } + + if (!ast_strlen_zero(hint_exten)) { + /* These are added so we can find and delete the hints when the endpoint gets deleted */ + variable_list_append_return(&vars, "@hint_context", hint_context); + variable_list_append_return(&vars, "@hint_exten", hint_exten); + } + if (!ast_strlen_zero(transport)) { variable_list_append_return(&vars, "transport", transport); } @@ -489,6 +693,14 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type } ao2_ref(obj, -1); + if (!ast_strlen_zero(hint_exten)) { + if (is_variable_true(wizvars, "has_hint")) { + add_hints(hint_context, hint_exten, hint_application, id); + } else { + delete_extens(hint_context, hint_exten); + } + } + return 0; } @@ -601,6 +813,14 @@ static int delete_existing_cb(void *obj, void *arg, int flags) { struct object_type_wizard *otw = arg; + if (!strcmp(otw->object_type, "endpoint")) { + const char *context = ast_sorcery_object_get_extended(obj, "hint_context"); + const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten"); + if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) { + delete_extens(context, exten); + } + } + otw->wizard->delete(otw->sorcery, otw->wizard_data, obj); return CMP_MATCH;