Index: channels/chan_skinny.c =================================================================== --- channels/chan_skinny.c (revision 69257) +++ channels/chan_skinny.c (working copy) @@ -100,6 +100,7 @@ static int keep_alive = 120; static char vmexten[AST_MAX_EXTENSION]; /* Voicemail pilot number */ +static char regcontext[AST_MAX_CONTEXT]; /* Context for auto-extension */ static char date_format[6] = "D-M-Y"; static char version_id[16] = "P002F202"; @@ -798,6 +799,7 @@ /* static int busycount = 3;*/ static char accountcode[AST_MAX_ACCOUNT_CODE] = ""; static char mailbox[AST_MAX_EXTENSION]; +static char regexten[AST_MAX_EXTENSION]; static int amaflags = 0; static int callnums = 1; @@ -957,6 +959,8 @@ char call_forward[AST_MAX_EXTENSION]; char mailbox[AST_MAX_EXTENSION]; char vmexten[AST_MAX_EXTENSION]; + char regexten[AST_MAX_EXTENSION]; /* Extension for auto-extensions */ + char regcontext[AST_MAX_CONTEXT]; /* Context for auto-extensions */ char mohinterpret[MAX_MUSICCLASS]; char mohsuggest[MAX_MUSICCLASS]; char lastnumberdialed[AST_MAX_EXTENSION]; /* Last number that was dialed - used for redial */ @@ -1383,6 +1387,78 @@ } } +static void cleanup_stale_contexts(char *new, char *old) +{ + char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT]; + + while ((oldcontext = strsep(&old, "&"))) { + stalecontext = '\0'; + ast_copy_string(newlist, new, sizeof(newlist)); + stringp = newlist; + while ((newcontext = strsep(&stringp, "&"))) { + if (strcmp(newcontext, oldcontext) == 0) { + /* This is not the context you're looking for */ + stalecontext = '\0'; + break; + } else if (strcmp(newcontext, oldcontext)) { + stalecontext = oldcontext; + } + + } + if (stalecontext) + ast_context_destroy(ast_context_find(stalecontext), "Skinny"); + } +} + +static void register_exten(struct skinny_line *l) +{ + char multi[256]; + char *stringp, *ext, *context; + + if (ast_strlen_zero(regcontext)) + return; + + ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi)); + stringp = multi; + while ((ext = strsep(&stringp, "&"))) { + if ((context = strchr(ext, '@'))) { + *context++ = '\0'; /* split ext@context */ + if (!ast_context_find(context)) { + ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context); + continue; + } + } else { + context = regcontext; + } + ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop", + ast_strdup(l->name), ast_free, "Skinny"); + } +} + +static void unregister_exten(struct skinny_line *l) +{ + char multi[256]; + char *stringp, *ext, *context; + + if (ast_strlen_zero(regcontext)) + return; + + ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi)); + stringp = multi; + while ((ext = strsep(&stringp, "&"))) { + if ((context = strchr(ext, '@'))) { + *context++ = '\0'; /* split ext@context */ + if (!ast_context_find(context)) { + ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context); + continue; + } + } else { + context = regcontext; + } + ast_context_remove_extension(context, ext, 1, NULL); + } +} + static int skinny_register(struct skinny_req *req, struct skinnysession *s) { struct skinny_device *d; @@ -1414,6 +1490,7 @@ sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd); } for (l = d->lines; l; l = l->next) { + register_exten(l); ast_device_state_changed("Skinny/%s@%s", l->name, d->name); } break; @@ -1443,6 +1520,7 @@ ast_extension_state_del(sd->stateid, NULL); } for (l = d->lines; l; l = l->next) { + unregister_exten(l); ast_device_state_changed("Skinny/%s@%s", l->name, d->name); } } @@ -2167,6 +2245,8 @@ ast_copy_string(device_vmexten, v->value, sizeof(device_vmexten)); } else if (!strcasecmp(v->name, "context")) { ast_copy_string(context, v->value, sizeof(context)); + } else if (!strcasecmp(v->name, "regexten")) { + ast_copy_string(regexten, v->value, sizeof(regexten)); } else if (!strcasecmp(v->name, "allow")) { ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { @@ -2273,8 +2353,8 @@ ast_copy_string(l->language, language, sizeof(l->language)); ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret)); ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest)); + ast_copy_string(l->regexten, regexten, sizeof(l->regexten)); ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox)); - ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox)); if (!ast_strlen_zero(mailbox)) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name); @@ -4761,6 +4841,8 @@ char *cat; struct skinny_device *d; int oldport = ntohs(bindaddr.sin_port); + char *stringp, *context, *oldregcontext; + char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT]; if (gethostname(ourhost, sizeof(ourhost))) { ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n"); @@ -4776,6 +4858,10 @@ memset(&bindaddr, 0, sizeof(bindaddr)); memset(&default_prefs, 0, sizeof(default_prefs)); + /* Initialize copy of current global_regcontext for later use in removing stale contexts */ + ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts)); + oldregcontext = oldcontexts; + /* Copy the default jb config over global_jbconf */ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); @@ -4799,6 +4885,17 @@ keep_alive = atoi(v->value); } else if (!strcasecmp(v->name, "vmexten")) { ast_copy_string(vmexten, v->value, sizeof(vmexten)); + } else if (!strcasecmp(v->name, "regcontext")) { + ast_copy_string(newcontexts, v->value, sizeof(newcontexts)); + stringp = newcontexts; + /* Let's remove any contexts that are no longer defined in regcontext */ + cleanup_stale_contexts(stringp, oldregcontext); + /* Create contexts if they don't exist already */ + while ((context = strsep(&stringp, "&"))) { + if (!ast_context_find(context)) + ast_context_create(NULL, context, "Skinny"); + } + ast_copy_string(regcontext, v->value, sizeof(regcontext)); } else if (!strcasecmp(v->name, "dateformat")) { ast_copy_string(date_format, v->value, sizeof(date_format)); } else if (!strcasecmp(v->name, "allow")) { Index: configs/skinny.conf.sample =================================================================== --- configs/skinny.conf.sample (revision 69257) +++ configs/skinny.conf.sample (working copy) @@ -11,6 +11,18 @@ ; It must be in the same context as the calling ; device/line +; If regcontext is specified, Asterisk will dynamically create and destroy a +; NoOp priority 1 extension for a given line which registers or unregisters with +; us and have a "regexten=" configuration item. +; Multiple contexts may be specified by separating them with '&'. The +; actual extension is the 'regexten' parameter of the registering line or its +; name if 'regexten' is not provided. If more than one context is provided, +; the context must be specified within regexten by appending the desired +; context after '@'. More than one regexten may be supplied if they are +; separated by '&'. Patterns may be used in regexten. +; +;regcontext=skinnyregistrations + ;allow=all ; see doc/rtp-packetization for framing options ;disallow= @@ -79,12 +91,14 @@ ;callerid="Customer Support" <810-234-1212> ;mailbox=100 ;vmexten=8500 ; Device level voicemailmain pilot number +;regexten=100 ;context=inbound ;linelabel="Support Line" ; Displays next to the line ; button on 7940's and 7960s ;line => 100 ;callerid="John Chambers" <408-526-4000> ;context=did +;regexten=110 ;linelabel="John" ;mailbox=110 ;line => 110