Merge hint patch, add new variables, and misc. PBX cleanups
authorMark Spencer <markster@digium.com>
Sun, 30 Mar 2003 22:55:42 +0000 (22:55 +0000)
committerMark Spencer <markster@digium.com>
Sun, 30 Mar 2003 22:55:42 +0000 (22:55 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@722 65c4cc65-6c06-0410-ace0-fbb531ad65f3

12 files changed:
README.variables
cdr.c
channel.c
channels/chan_modem_bestdata.c
channels/chan_sip.c
codecs/codec_speex.c
doc/README.variables
formats/format_pcm_alaw.c
include/asterisk/channel.h
include/asterisk/pbx.h
pbx.c
pbx/pbx_config.c

index 957ab63..bb44ff1 100755 (executable)
@@ -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 32999fd..a06c88f 100755 (executable)
--- 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;
index f1ea7f7..ed9bd25 100755 (executable)
--- 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;
@@ -144,6 +145,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)) {
                ast_log(LOG_WARNING, "Unable to lock channel list\n");
@@ -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"
index 027cba0..3faad53 100755 (executable)
@@ -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>
index f86f382..2d6ddcd 100755 (executable)
@@ -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));
index 98a2aaa..3c2e4f5 100755 (executable)
@@ -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;
index 957ab63..bb44ff1 100755 (executable)
@@ -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 
index c7e1ef7..129e292 100755 (executable)
@@ -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;
index 6979515..09cd2a6 100755 (executable)
@@ -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
index f6034b6..a616414 100755 (executable)
@@ -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 0c01820..655f9bd 100755 (executable)
--- 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(&notifylock);
+
+    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(&notifylock);
+    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(&notifylock);
+    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(&notifylock);
+           return -1;
+       }
+       list = malloc(sizeof(struct ast_notify));
+       if (!list) {
+           pthread_mutex_unlock(&notifylock);
+           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(&notifylock);
+       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(&notifylock);
+    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(&notifylock);
+
+    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(&notifylock);
+    return 1;
+}
+
+int ast_extension_state_del(int id)
+{
+    struct ast_notify *list, *prev = NULL;
+    struct ast_notify_cb *cblist, *cbprev;
+            
+    pthread_mutex_lock(&notifylock);
+
+    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(&notifylock);
+    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;
index f7b143d..b957f29 100755 (executable)
@@ -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);