From e6918cb42edd1edb1f4ead796042d95b6cd98f08 Mon Sep 17 00:00:00 2001 From: Mark Spencer <markster@digium.com> Date: Sun, 30 Mar 2003 22:55:42 +0000 Subject: [PATCH] Merge hint patch, add new variables, and misc. PBX cleanups git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@722 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- README.variables | 1 + cdr.c | 1 + channel.c | 78 ++++++++ channels/chan_modem_bestdata.c | 1 + channels/chan_sip.c | 11 +- codecs/codec_speex.c | 8 +- doc/README.variables | 1 + formats/format_pcm_alaw.c | 2 +- include/asterisk/channel.h | 39 ++++ include/asterisk/pbx.h | 66 +++++++ pbx.c | 350 +++++++++++++++++++++++++++++++-- pbx/pbx_config.c | 45 +++-- 12 files changed, 569 insertions(+), 34 deletions(-) diff --git a/README.variables b/README.variables index 957ab633cb..bb44ff1fec 100755 --- a/README.variables +++ b/README.variables @@ -35,6 +35,7 @@ ${CALLERIDNUM} Caller ID Number only ${EXTEN} Current extension ${CONTEXT} Current context ${PRIORITY} Current priority +${CHANNEL} Current channel name There are two reference modes - reference by value and reference by name. To refer to a variable with its name (as an argument to a function that diff --git a/cdr.c b/cdr.c index 32999fd0ee..a06c88f99b 100755 --- a/cdr.c +++ b/cdr.c @@ -21,6 +21,7 @@ #include <asterisk/callerid.h> #include <unistd.h> #include <stdlib.h> +#include <string.h> #include <pthread.h> int ast_default_amaflags = AST_CDR_DOCUMENTATION; diff --git a/channel.c b/channel.c index f1ea7f780d..ed9bd25113 100755 --- a/channel.c +++ b/channel.c @@ -64,6 +64,7 @@ struct chanlist { char description[80]; int capabilities; struct ast_channel * (*requester)(char *type, int format, void *data); + int (*devicestate)(void *data); struct chanlist *next; } *backends = NULL; struct ast_channel *channels = NULL; @@ -143,6 +144,13 @@ time_t myt; int ast_channel_register(char *type, char *description, int capabilities, struct ast_channel *(*requester)(char *type, int format, void *data)) +{ + return ast_channel_register_ex(type, description, capabilities, requester, NULL); +} + +int ast_channel_register_ex(char *type, char *description, int capabilities, + struct ast_channel *(*requester)(char *type, int format, void *data), + int (*devicestate)(void *data)) { struct chanlist *chan, *last=NULL; if (PTHREAD_MUTEX_LOCK(&chlock)) { @@ -169,6 +177,7 @@ int ast_channel_register(char *type, char *description, int capabilities, strncpy(chan->description, description, sizeof(chan->description)-1); chan->capabilities = capabilities; chan->requester = requester; + chan->devicestate = devicestate; chan->next = NULL; if (last) last->next = chan; @@ -469,6 +478,7 @@ void ast_channel_free(struct ast_channel *chan) struct ast_var_t *vardata; struct ast_frame *f, *fp; struct varshead *headp; + char name[AST_CHANNEL_NAME]; headp=&chan->varshead; @@ -489,10 +499,14 @@ void ast_channel_free(struct ast_channel *chan) ast_log(LOG_WARNING, "Unable to find channel in list\n"); if (chan->pvt->pvt) ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name); + + strncpy(name, chan->name, sizeof(name)-1); + /* Stop monitoring */ if (chan->monitor) { chan->monitor->stop( chan, 0 ); } + /* Free translatosr */ if (chan->pvt->readtrans) ast_translator_free_path(chan->pvt->readtrans); @@ -537,6 +551,8 @@ void ast_channel_free(struct ast_channel *chan) chan->pvt = NULL; free(chan); PTHREAD_MUTEX_UNLOCK(&chlock); + + ast_device_state_changed(name); } int ast_softhangup_nolock(struct ast_channel *chan, int cause) @@ -1409,6 +1425,7 @@ struct ast_channel *ast_request(char *type, int format, void *data) if (chan->requester) c = chan->requester(type, capabilities, data); if (c) { +// ast_device_state_changed(c->name); manager_event(EVENT_FLAG_CALL, "Newchannel", "Channel: %s\r\n" "State: %s\r\n" @@ -1425,6 +1442,66 @@ struct ast_channel *ast_request(char *type, int format, void *data) return c; } +int ast_parse_device_state(char *device) +{ + char name[AST_CHANNEL_NAME] = ""; + char *cut; + struct ast_channel *chan; + + chan = ast_channel_walk(NULL); + while (chan) { + strncpy(name, chan->name, sizeof(name)-1); + cut = strchr(name,'-'); + if (cut) + *cut = 0; + if (!strcmp(name, device)) + return AST_DEVICE_INUSE; + chan = ast_channel_walk(chan); + } + return AST_DEVICE_UNKNOWN; +} + +int ast_device_state(char *device) +{ + char tech[AST_MAX_EXTENSION] = ""; + char *number; + struct chanlist *chanls; + int res = 0; + + strncpy(tech, device, sizeof(tech)-1); + number = strchr(tech, '/'); + if (!number) { + return AST_DEVICE_INVALID; + } + *number = 0; + number++; + + if (PTHREAD_MUTEX_LOCK(&chlock)) { + ast_log(LOG_WARNING, "Unable to lock channel list\n"); + return -1; + } + chanls = backends; + while(chanls) { + if (!strcasecmp(tech, chanls->type)) { + PTHREAD_MUTEX_UNLOCK(&chlock); + if (!chanls->devicestate) + return ast_parse_device_state(device); + else { + res = chanls->devicestate(number); + if (res == AST_DEVICE_UNKNOWN) + return ast_parse_device_state(device); + else + return res; + } + } + chanls = chanls->next; + } + if (!chanls) + ast_log(LOG_WARNING, "No channel type registered for '%s'\n", tech); + PTHREAD_MUTEX_UNLOCK(&chlock); + return AST_DEVICE_INVALID; +} + int ast_call(struct ast_channel *chan, char *addr, int timeout) { /* Place an outgoing call, but don't wait any longer than timeout ms before returning. @@ -1813,6 +1890,7 @@ int ast_setstate(struct ast_channel *chan, int state) int oldstate = chan->_state; chan->_state = state; if (oldstate == AST_STATE_DOWN) { + ast_device_state_changed(chan->name); manager_event(EVENT_FLAG_CALL, "Newchannel", "Channel: %s\r\n" "State: %s\r\n" diff --git a/channels/chan_modem_bestdata.c b/channels/chan_modem_bestdata.c index 027cba0746..3faad53e8e 100755 --- a/channels/chan_modem_bestdata.c +++ b/channels/chan_modem_bestdata.c @@ -17,6 +17,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#include <unistd.h> #include <asterisk/lock.h> #include <asterisk/vmodem.h> #include <asterisk/module.h> diff --git a/channels/chan_sip.c b/channels/chan_sip.c index f86f38279e..2d6ddcd98b 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1740,10 +1740,10 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru char contact[256]; char *c; if ((c=getsipuri(ot))) { - snprintf(contact, sizeof(contact), "<%s@%s>", c, inet_ntoa(p->ourip)); + snprintf(contact, sizeof(contact), "<%s@%s:%d>", c, inet_ntoa(p->ourip), ourport); free(c); } else { - snprintf(contact, sizeof(contact), "<%s>", inet_ntoa(p->ourip)); + snprintf(contact, sizeof(contact), "<%s:%d>", inet_ntoa(p->ourip), ourport); } snprintf(tmp, sizeof(tmp), "%d", p->expirey); add_header(resp, "Expires", tmp); @@ -1754,10 +1754,10 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru very stupidly *sigh* XXX */ char *c; if ((c=getsipuri(ot))) { - snprintf(contact, sizeof(contact), "<%s@%s>", c, inet_ntoa(p->ourip)); + snprintf(contact, sizeof(contact), "<%s@%s:%d>", c, inet_ntoa(p->ourip), ourport); free(c); } else { - snprintf(contact, sizeof(contact), "<%s>", inet_ntoa(p->ourip)); + snprintf(contact, sizeof(contact), "<%s:%d>", inet_ntoa(p->ourip), ourport); } add_header(resp, "Contact", contact); } @@ -2048,7 +2048,8 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, c } if (!n) n = l; - snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%08x", n, l, inet_ntoa(p->ourip), p->tag); + snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%08x", n, l, inet_ntoa(p->ourip), ourport, p->tag); + if (strlen(p->username)) { if (ntohs(p->sa.sin_port) != DEFAULT_SIP_PORT) { snprintf(invite, sizeof(invite), "sip:%s@%s:%d",p->username, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c index 98a2aaab29..3c2e4f5199 100755 --- a/codecs/codec_speex.c +++ b/codecs/codec_speex.c @@ -58,7 +58,7 @@ struct ast_translator_pvt { #define speex_coder_pvt ast_translator_pvt -static struct ast_translator_pvt *lintospeex_new() +static struct ast_translator_pvt *lintospeex_new(void) { struct speex_coder_pvt *tmp; tmp = malloc(sizeof(struct speex_coder_pvt)); @@ -77,7 +77,7 @@ static struct ast_translator_pvt *lintospeex_new() return tmp; } -static struct ast_translator_pvt *speextolin_new() +static struct ast_translator_pvt *speextolin_new(void) { struct speex_coder_pvt *tmp; tmp = malloc(sizeof(struct speex_coder_pvt)); @@ -95,7 +95,7 @@ static struct ast_translator_pvt *speextolin_new() return tmp; } -static struct ast_frame *lintospeex_sample() +static struct ast_frame *lintospeex_sample(void) { static struct ast_frame f; f.frametype = AST_FRAME_VOICE; @@ -110,7 +110,7 @@ static struct ast_frame *lintospeex_sample() return &f; } -static struct ast_frame *speextolin_sample() +static struct ast_frame *speextolin_sample(void) { static struct ast_frame f; f.frametype = AST_FRAME_VOICE; diff --git a/doc/README.variables b/doc/README.variables index 957ab633cb..bb44ff1fec 100755 --- a/doc/README.variables +++ b/doc/README.variables @@ -35,6 +35,7 @@ ${CALLERIDNUM} Caller ID Number only ${EXTEN} Current extension ${CONTEXT} Current context ${PRIORITY} Current priority +${CHANNEL} Current channel name There are two reference modes - reference by value and reference by name. To refer to a variable with its name (as an argument to a function that diff --git a/formats/format_pcm_alaw.c b/formats/format_pcm_alaw.c index c7e1ef7496..129e292b3e 100755 --- a/formats/format_pcm_alaw.c +++ b/formats/format_pcm_alaw.c @@ -64,7 +64,7 @@ static char *exts = "alaw|al"; /* Returns time in msec since system boot. */ -static unsigned long get_time() +static unsigned long get_time(void) { struct tms buf; clock_t cur; diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 6979515da5..09cd2a6cfb 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -233,6 +233,19 @@ struct chanmon; /*! Do not transmit voice data */ #define AST_STATE_MUTE (1 << 16) +/*! Device is valid but channel didn't know state */ +#define AST_DEVICE_UNKNOWN 0 +/*! Device is not used */ +#define AST_DEVICE_NOT_INUSE 1 +/*! Device is in use */ +#define AST_DEVICE_INUSE 2 +/*! Device is busy */ +#define AST_DEVICE_BUSY 3 +/*! Device is invalid */ +#define AST_DEVICE_INVALID 4 +/*! Device is unavailable */ +#define AST_DEVICE_UNAVAILABLE 5 + //! Requests a channel /*! * \param type type of channel to request @@ -244,6 +257,27 @@ struct chanmon; */ struct ast_channel *ast_request(char *type, int format, void *data); +//! Search the Channels by Name +/*! + * \param device like a dialstring + * Search the Device in active channels by compare the channelname against + * the devicename. Compared are only the first chars to the first '-' char. + * Returns an AST_DEVICE_UNKNOWN if no channel found or + * AST_DEVICE_INUSE if a channel is found + */ +int ast_parse_device_state(char *device); + +//! Asks a channel for device state +/*! + * \param device like a dialstring + * Asks a channel for device state, data is normaly a number from dialstring + * used by the low level module + * Trys the channel devicestate callback if not supported search in the + * active channels list for the device. + * Returns an AST_DEVICE_??? state -1 on failure + */ +int ast_device_state(char *device); + /*! * \param type type of channel to request * \param format requested channel format @@ -271,6 +305,11 @@ struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int int ast_channel_register(char *type, char *description, int capabilities, struct ast_channel* (*requester)(char *type, int format, void *data)); +/* Same like the upper function but with support for devicestate */ +int ast_channel_register_ex(char *type, char *description, int capabilities, + struct ast_channel *(*requester)(char *type, int format, void *data), + int (*devicestate)(void *data)); + //! Unregister a channel class /* * \param type the character string that corresponds to the channel you wish to unregister diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index f6034b61e6..a616414e33 100755 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -29,12 +29,27 @@ extern "C" { //! Special return values from applications to the PBX #define AST_PBX_KEEPALIVE 10 /* Destroy the thread, but don't hang up the channel */ +//! Special Priority for an hint +#define PRIORITY_HINT -1 + +//! Extension states +//! No device INUSE or BUSY +#define AST_EXTENSION_NOT_INUSE 0 +//! One or more devices INUSE +#define AST_EXTENSION_INUSE 1 +//! All devices BUSY +#define AST_EXTENSION_BUSY 2 +//! All devices UNAVAILABLE/UNREGISTERED +#define AST_EXTENSION_UNAVAILABLE 3 + struct ast_context; struct ast_exten; struct ast_include; struct ast_ignorepat; struct ast_sw; +typedef int (*ast_notify_cb_type)(char *context, char* id, int state, void *data); + //! Data structure associated with an asterisk switch struct ast_switch { /*! NULL */ @@ -189,6 +204,57 @@ int ast_register_application(char *app, int (*execute)(struct ast_channel *, voi */ int ast_unregister_application(char *app); +//! Uses hint and devicestate callback to get the state of an extension +/*! + * \param c this is not important + * \param context which context to look in + * \param exten which extension to get state + * Returns extension state !! = AST_EXTENSION_??? + */ +int ast_extension_state(struct ast_channel *c, char *context, char *exten); + +//! Tells Asterisk the State for Device is changed +/*! + * \param device devicename like a dialstring + * Asterisk polls the new extensionstates and calls the registered + * callbacks for the changed extensions + * Returns 0 on success, -1 on failure + */ +int ast_device_state_changed(char *device); + +//! Registers a state change callback +/*! + * \param context which context to look in + * \param exten which extension to get state + * \param callback callback to call if state changed + * \param data to pass to callback + * The callback is called if the state for extension is changed + * Return -1 on failure, ID on success + */ +int ast_extension_state_add(char *context, char *exten, + ast_notify_cb_type callback, void *data); + +//! Deletes a registered state change callback by ID +/*! + * \param id of the callback to delete + * Removes the callback from list of callbacks + * Return 0 on success, -1 on failure + */ +int ast_extension_state_del(int id); + +//! If an extension exists, return non-zero +/*! + * \param hint buffer for hint + * \param maxlen size of hint buffer + * \param c this is not important + * \param context which context to look in + * \param exten which extension to search for + * If an extension within the given context with the priority PRIORITY_HINT + * is found a non zero value will be returned. + * Otherwise, 0 is returned. + */ +int ast_get_hint(char *hint, int maxlen, struct ast_channel *c, char *context, char *exten); + //! If an extension exists, return non-zero // work /*! diff --git a/pbx.c b/pbx.c index 0c01820d58..655f9bd7d4 100755 --- a/pbx.c +++ b/pbx.c @@ -132,6 +132,22 @@ struct ast_app { struct ast_app *next; }; +/* An extension state notify */ +struct ast_notify_cb { + int id; + void *data; + ast_notify_cb_type callback; + struct ast_notify_cb *next; +}; + +struct ast_notify { + struct ast_exten *exten; + int laststate; + struct ast_notify_cb *callbacks; + struct ast_notify *next; +}; + + static int pbx_builtin_prefix(struct ast_channel *, void *); static int pbx_builtin_stripmsd(struct ast_channel *, void *); static int pbx_builtin_answer(struct ast_channel *, void *); @@ -294,6 +310,11 @@ static struct ast_app *apps = NULL; static pthread_mutex_t switchlock = AST_MUTEX_INITIALIZER; struct ast_switch *switches = NULL; +/* Lock for extension state notifys */ +static pthread_mutex_t notifylock = AST_MUTEX_INITIALIZER; +static int notifycnt = 0; +struct ast_notify *notifys = NULL; + int pbx_exec(struct ast_channel *c, /* Channel */ struct ast_app *app, void *data, /* Data for execution */ @@ -676,20 +697,19 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan, char *cont return NULL; } -static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char **cp4) +static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char **cp4, char *workspace, int workspacelen) { char *first,*second; int offset,offset2; struct ast_var_t *variables; char *name, *num; /* for callerid name + num variables */ struct varshead *headp; - char pri[80]; headp=&c->varshead; *cp4=NULL; /* Now we have the variable name on cp3 */ if ((first=strchr(cp3,':'))) { *first='\0'; - pbx_substitute_variables_temp(c,cp3,cp4); + pbx_substitute_variables_temp(c,cp3,cp4,workspace,workspacelen); if (!(*cp4)) return; offset=atoi(first+1); if ((second=strchr(first+1,':'))) { @@ -711,20 +731,18 @@ static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char * *cp4+=strlen(*cp4)+offset; (*cp4)[offset2] = '\0'; } else if (!strcmp(cp3, "CALLERIDNUM")) { - char cid[256] = ""; if (c->callerid) - strncpy(cid, c->callerid, sizeof(cid) - 1); - ast_callerid_parse(cid, &name, &num); + strncpy(workspace, c->callerid, workspacelen - 1); + ast_callerid_parse(workspace, &name, &num); if (num) { ast_shrink_phone_number(num); *cp4 = num; } else *cp4 = ""; } else if (!strcmp(cp3, "CALLERIDNAME")) { - char cid[256] = ""; if (c->callerid) - strncpy(cid, c->callerid, sizeof(cid) - 1); - ast_callerid_parse(cid, &name, &num); + strncpy(workspace, c->callerid, workspacelen - 1); + ast_callerid_parse(workspace, &name, &num); if (name) *cp4 = name; else @@ -733,6 +751,11 @@ static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char * *cp4 = c->callerid; if (!(*cp4)) *cp4 = ""; + } else if (!strcmp(cp3, "HINT")) { + if (!ast_get_hint(workspace, workspacelen - 1, c, c->context, c->exten)) + *cp4 = ""; + else + *cp4 = workspace; } else if (!strcmp(cp3, "EXTEN")) { *cp4 = c->exten; } else if (!strncmp(cp3, "EXTEN-", strlen("EXTEN-")) && @@ -760,8 +783,10 @@ static void pbx_substitute_variables_temp(struct ast_channel *c,char *cp3,char * } else if (!strcmp(cp3, "CONTEXT")) { *cp4 = c->context; } else if (!strcmp(cp3, "PRIORITY")) { - snprintf(pri, sizeof(pri), "%d", c->priority); - *cp4 = pri; + snprintf(workspace, workspacelen, "%d", c->priority); + *cp4 = workspace; + } else if (!strcmp(cp3, "CHANNEL")) { + *cp4 = c->name; } else { AST_LIST_TRAVERSE(headp,variables,entries) { #if 0 @@ -796,6 +821,7 @@ static void pbx_substitute_variables_helper(struct ast_channel *c,char *cp1,char char *cp4,*cp2; char *tmp,*wherearewe,*finish=NULL,*ltmp,*lval,*nextvar; int length,variables=0; + char workspace[256]; wherearewe=tmp=cp1; cp2=*ecp2; @@ -854,7 +880,7 @@ static void pbx_substitute_variables_helper(struct ast_channel *c,char *cp1,char cp1=cp2; } if (count) { - pbx_substitute_variables_temp(c,cp1,&cp4); + pbx_substitute_variables_temp(c,cp1,&cp4, workspace, sizeof(workspace)); if (cp4) { /* reset output variable so we could store the result */ *cp2='\0'; @@ -1080,6 +1106,300 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte } +static struct ast_exten *ast_hint_extension(struct ast_channel *c, char *context, char *exten) +{ + struct ast_exten *e; + struct ast_switch *sw; + char *data; + int status = 0; + char *incstack[AST_PBX_MAX_STACK]; + int stacklen = 0; + + if (ast_pthread_mutex_lock(&conlock)) { + ast_log(LOG_WARNING, "Unable to obtain lock\n"); + return NULL; + } + e = pbx_find_extension(c, context, exten, PRIORITY_HINT, "", HELPER_EXISTS, incstack, &stacklen, &status, &sw, &data); + ast_pthread_mutex_unlock(&conlock); + return e; +} + +static int ast_extension_state2(struct ast_exten *e) +{ + char hint[AST_MAX_EXTENSION] = ""; + char *cur, *rest; + int res = -1; + int allunavailable = 1, allbusy = 1, allfree = 1; + int busy = 0; + + strncpy(hint, ast_get_extension_app(e), sizeof(hint)-1); + + cur = hint; + do { + rest = strchr(cur, '&'); + if (rest) { + *rest = 0; + rest++; + } + + res = ast_device_state(cur); + switch (res) { + case AST_DEVICE_NOT_INUSE: + allunavailable = 0; + allbusy = 0; + break; + case AST_DEVICE_INUSE: + return AST_EXTENSION_INUSE; + case AST_DEVICE_BUSY: + allunavailable = 0; + allfree = 0; + busy = 1; + break; + case AST_DEVICE_UNAVAILABLE: + case AST_DEVICE_INVALID: + allbusy = 0; + allfree = 0; + break; + default: + allunavailable = 0; + allbusy = 0; + allfree = 0; + } + cur = rest; + } while (cur); + + if (allfree) + return AST_EXTENSION_NOT_INUSE; + if (allbusy) + return AST_EXTENSION_BUSY; + if (allunavailable) + return AST_EXTENSION_UNAVAILABLE; + if (busy) + return AST_EXTENSION_INUSE; + + return AST_EXTENSION_NOT_INUSE; +} + + +int ast_extension_state(struct ast_channel *c, char *context, char *exten) +{ + struct ast_exten *e; + + e = ast_hint_extension(c, context, exten); + if (!e) + return -1; + + return ast_extension_state2(e); +} + +int ast_device_state_changed(char *device) +{ + struct ast_notify *list; + struct ast_notify_cb *cblist; + char hint[AST_MAX_EXTENSION]; + char *cur, *rest; + int state; + + pthread_mutex_lock(¬ifylock); + + list = notifys; + + while (list) { + + strcpy(hint, ast_get_extension_app(list->exten)); + cur = hint; + do { + rest = strchr(cur, '&'); + if (rest) { + *rest = 0; + rest++; + } + + if (!strncmp(cur, device, strlen(cur))) { + // Found extension execute callbacks + state = ast_extension_state2(list->exten); + if ((state != -1) && (state != list->laststate)) { + cblist = list->callbacks; + while (cblist) { + cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data); + cblist = cblist->next; + } + list->laststate = state; + } + break; + } + cur = rest; + } while (cur); + + list = list->next; + } + + pthread_mutex_unlock(¬ifylock); + return 1; +} + +int ast_extension_state_add(char *context, char *exten, + ast_notify_cb_type callback, void *data) +{ + struct ast_notify *list; + struct ast_notify_cb *cblist; + struct ast_exten *e; + + pthread_mutex_lock(¬ifylock); + list = notifys; + + while (list) { + if (!strcmp(list->exten->parent->name, context) && + !strcmp(list->exten->exten, exten)) + break; + list = list->next; + } + + if (!list) { + e = ast_hint_extension(NULL, context, exten); + if (!e) { + pthread_mutex_unlock(¬ifylock); + return -1; + } + list = malloc(sizeof(struct ast_notify)); + if (!list) { + pthread_mutex_unlock(¬ifylock); + return -1; + } + /* Initialize and insert new item */ + memset(list, 0, sizeof(struct ast_notify)); + list->exten = e; + list->laststate = -1; + list->next = notifys; + notifys = list; + } + + /* Now inserts the callback */ + cblist = malloc(sizeof(struct ast_notify_cb)); + if (!cblist) { + pthread_mutex_unlock(¬ifylock); + return -1; + } + memset(cblist, 0, sizeof(struct ast_notify_cb)); + cblist->id = notifycnt++; + cblist->callback = callback; + cblist->data = data; + + cblist->next = list->callbacks; + list->callbacks = cblist; + + pthread_mutex_unlock(¬ifylock); + return cblist->id; +} + +static int ast_extension_state_clean(struct ast_exten *e) +{ + /* Cleanup the Notifys if hint is removed */ + struct ast_notify *list, *prev = NULL; + struct ast_notify_cb *cblist, *cbprev; + + pthread_mutex_lock(¬ifylock); + + list = notifys; + while(list) { + if (list->exten==e) { + cbprev = NULL; + cblist = list->callbacks; + while (cblist) { + cbprev = cblist; + cblist = cblist->next; + cblist->callback(list->exten->parent->name, list->exten->exten, -1, cblist->data); + free(cbprev); + } + list->callbacks = NULL; + + if (!prev) { + notifys = list->next; + free(list); + list = notifys; + } else { + prev->next = list->next; + free(list); + list = prev->next; + } + } else { + prev = list; + list = list->next; + } + } + + pthread_mutex_unlock(¬ifylock); + return 1; +} + +int ast_extension_state_del(int id) +{ + struct ast_notify *list, *prev = NULL; + struct ast_notify_cb *cblist, *cbprev; + + pthread_mutex_lock(¬ifylock); + + list = notifys; + while (list) { + cblist = list->callbacks; + cbprev = NULL; + while (cblist) { + if (cblist->id==id) { + if (!cbprev) { + list->callbacks = cblist->next; + free(cblist); + cblist = list->callbacks; + } else { + cbprev->next = cblist->next; + free(cblist); + cblist = cbprev->next; + } + + if (!list->callbacks) { + if (!prev) { + notifys = list->next; + free(list); + list = notifys; + } else { + prev->next = list->next; + free(list); + list = prev->next; + } + } + break; + } else { + cbprev = cblist; + cblist = cblist->next; + } + } + + // we can have only one item + if (cblist) + break; + + prev = list; + list = list->next; + } + + pthread_mutex_unlock(¬ifylock); + if (list) + return 0; + else + return -1; + +} + +int ast_get_hint(char *hint, int maxlen, struct ast_channel *c, char *context, char *exten) +{ + struct ast_exten *e; + e = ast_hint_extension(c, context, exten); + if (e) { + strncpy(hint, ast_get_extension_app(e), maxlen); + return -1; + } + return 0; +} + int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int priority, char *callerid) { return pbx_extension_helper(c, context, exten, priority, callerid, HELPER_EXISTS); @@ -1544,6 +1864,9 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int peer = exten; while (peer) { exten = peer->peer; + + if (!peer->priority==PRIORITY_HINT) + ast_extension_state_clean(peer); peer->datad(peer->data); free(peer); @@ -1589,6 +1912,8 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int } /* now, free whole priority extension */ + if (peer->priority==PRIORITY_HINT) + ast_extension_state_clean(peer); peer->datad(peer->data); free(peer); @@ -2956,6 +3281,7 @@ int ast_add_extension2(struct ast_context *con, tmp->matchcid = 0; } strncpy(tmp->app, application, sizeof(tmp->app)-1); + tmp->parent = con; tmp->data = data; tmp->datad = datad; tmp->registrar = registrar; diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c index f7b143dafb..b957f29e42 100755 --- a/pbx/pbx_config.c +++ b/pbx/pbx_config.c @@ -344,14 +344,16 @@ static int handle_context_remove_extension(int fd, int argc, char *argv[]) * why? because atoi (strtol) returns 0 if any characters in * string and whole extension will be removed, it's not good */ - while (*c != '\0') { + if (strcmp("hint", c)) { + while (*c != '\0') { if (!isdigit(*c++)) { ast_cli(fd, "Invalid priority '%s'\n", argv[3]); return RESULT_FAILURE; } - } - - removing_priority = atoi(argv[3]); + } + removing_priority = atoi(argv[3]); + } else + removing_priority = PRIORITY_HINT; if (removing_priority == 0) { ast_cli(fd, "If you want to remove whole extension, please " \ @@ -983,11 +985,17 @@ static int handle_save_dialplan(int fd, int argc, char *argv[]) context_header_written = 1; } - fprintf(output, "exten => %s,%d,%s,%s\n", - ast_get_extension_name(p), - ast_get_extension_priority(p), - ast_get_extension_app(p), - (char *)ast_get_extension_app_data(p)); + if (!ast_get_extension_priority(p)==PRIORITY_HINT) + fprintf(output, "exten => %s,%d,%s,%s\n", + ast_get_extension_name(p), + ast_get_extension_priority(p), + ast_get_extension_app(p), + (char *)ast_get_extension_app_data(p)); + else + fprintf(output, "exten => %s,hint,%s\n", + ast_get_extension_name(p), + ast_get_extension_app(p)); + } p = ast_walk_extension_priorities(e, p); } @@ -1058,6 +1066,7 @@ static int handle_context_add_extension(int fd, int argc, char *argv[]) { char *whole_exten; char *exten, *prior; + int iprior = -2; char *cidmatch, *app, *app_data; char *start, *end; @@ -1075,6 +1084,11 @@ static int handle_context_add_extension(int fd, int argc, char *argv[]) cidmatch = NULL; } prior = strsep(&whole_exten,","); + if (!strcmp(prior, "hint")) { + iprior = PRIORITY_HINT; + } else { + iprior = atoi(prior); + } app = strsep(&whole_exten,","); if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) { *start = *end = '\0'; @@ -1085,9 +1099,11 @@ static int handle_context_add_extension(int fd, int argc, char *argv[]) } else app_data = whole_exten; - if (!exten || !prior || !app || !app_data) return RESULT_SHOWUSAGE; + if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE; - if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, atoi(prior), cidmatch, app, + if (!app_data) + app_data=""; + if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, cidmatch, app, (void *)strdup(app_data), free, registrar)) { switch (errno) { case ENOMEM: @@ -1500,6 +1516,7 @@ static int pbx_load_module(void) while(v) { if (!strcasecmp(v->name, "exten")) { char *stringp=NULL; + int ipri = -2; tc = strdup(v->value); if(tc!=NULL){ stringp=tc; @@ -1509,6 +1526,10 @@ static int pbx_load_module(void) pri = strsep(&stringp, ","); if (!pri) pri=""; + if (!strcmp(pri,"hint")) + ipri=PRIORITY_HINT; + else + ipri=atoi(pri); appl = stringp; if (!appl) appl=""; @@ -1544,7 +1565,7 @@ static int pbx_load_module(void) if (!data) data=""; - if (ast_add_extension2(con, 0, ext, atoi(pri), cidmatch, appl, strdup(data), free, registrar)) { + if (ast_add_extension2(con, 0, ext, ipri, cidmatch, appl, strdup(data), free, registrar)) { ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); } free(tc); -- GitLab