Merged revisions 130102 via svnmerge from
[asterisk/asterisk.git] / channels / chan_agent.c
index 277ebda..5ca4425 100644 (file)
@@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/devicestate.h"
 #include "asterisk/monitor.h"
 #include "asterisk/stringfields.h"
+#include "asterisk/event.h"
 
 static const char tdesc[] = "Call Agent Proxy Channel";
 static const char config[] = "agents.conf";
@@ -147,6 +148,7 @@ static char urlprefix[AST_MAX_BUF] = "";
 static char savecallsin[AST_MAX_BUF] = "";
 static int updatecdr = 0;
 static char beep[AST_MAX_BUF] = "beep";
+struct ast_event_sub *agent_devicestate_sub = NULL;
 
 #define GETAGENTBYCALLERID     "AGENTBYCALLERID"
 
@@ -171,6 +173,7 @@ struct agent_pvt {
        char agent[AST_MAX_AGENT];     /*!< Agent ID */
        char password[AST_MAX_AGENT];  /*!< Password for Agent login */
        char name[AST_MAX_AGENT];
+       int inherited_devicestate;     /*!< Does the underlying channel have a devicestate to pass? */
        ast_mutex_t app_lock;          /**< Synchronization between owning applications */
        volatile pthread_t owning_app; /**< Owning application thread id */
        volatile int app_sleep_cond;   /**< Sleep condition for the login app */
@@ -261,6 +264,48 @@ static const struct ast_channel_tech agent_tech = {
        .set_base_channel = agent_set_base_channel,
 };
 
+static void agent_devicestate_cb(const struct ast_event *event, void *unused)
+{
+       int res, i;
+       struct agent_pvt *p;
+       char basename[AST_CHANNEL_NAME], *tmp;
+       const char *device;
+       enum ast_device_state state;
+
+       /* Try to be safe, but don't deadlock */
+       for (i = 0; i < 10; i++) {
+               if ((res = AST_LIST_TRYLOCK(&agents)) == 0) {
+                       break;
+               }
+       }
+       if (res) {
+               return;
+       }
+
+       state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+       device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+
+       if (ast_strlen_zero(device)) {
+               return;
+       }
+
+       AST_LIST_TRAVERSE(&agents, p, list) {
+               ast_mutex_lock(&p->lock);
+               if (p->chan) {
+                       ast_copy_string(basename, p->chan->name, sizeof(basename));
+                       if ((tmp = strrchr(basename, '-'))) {
+                               *tmp = '\0';
+                       }
+                       if (strcasecmp(p->chan->name, device) == 0 || strcasecmp(basename, device) == 0) {
+                               p->inherited_devicestate = state;
+                               ast_device_state_changed("Agent/%s", p->agent);
+                       }
+               }
+               ast_mutex_unlock(&p->lock);
+       }
+       AST_LIST_UNLOCK(&agents);
+}
+
 /*!
  * Adds an agent to the global list of agents.
  *
@@ -323,6 +368,7 @@ static struct agent_pvt *add_agent(const char *agent, int pending)
                p->app_sleep_cond = 1;
                p->group = group;
                p->pending = pending;
+               p->inherited_devicestate = -1;
                AST_LIST_INSERT_TAIL(&agents, p, list);
        }
        
@@ -466,6 +512,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
                                        p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
                        }
                        p->chan = NULL;
+                       p->inherited_devicestate = -1;
                        p->acknowledged = 0;
                }
        } else {
@@ -680,6 +727,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
        } else {
                /* Agent hung-up */
                p->chan = NULL;
+               p->inherited_devicestate = -1;
        }
 
        if (!res) {
@@ -800,6 +848,7 @@ static int agent_hangup(struct ast_channel *ast)
                                /* Recognize the hangup and pass it along immediately */
                                ast_hangup(p->chan);
                                p->chan = NULL;
+                               p->inherited_devicestate = -1;
                        }
                        ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
                        if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
@@ -1523,6 +1572,7 @@ static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long
        set_agentbycallerid(p->logincallerid, NULL);
        p->loginchan[0] ='\0';
        p->logincallerid[0] = '\0';
+       p->inherited_devicestate = -1;
        ast_device_state_changed("Agent/%s", p->agent);
        if (persistent_agents)
                dump_agents();  
@@ -2078,8 +2128,10 @@ static int login_exec(struct ast_channel *chan, void *data)
                                                if (res && p->owner) 
                                                        ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
                                                /* Log us off if appropriate */
-                                               if (p->chan == chan)
+                                               if (p->chan == chan) {
                                                        p->chan = NULL;
+                                                       p->inherited_devicestate = -1;
+                                               }
                                                p->acknowledged = 0;
                                                logintime = time(NULL) - p->loginstart;
                                                p->loginstart = 0;
@@ -2292,6 +2344,8 @@ static int agent_devicestate(void *data)
                        if (p->owner) {
                                if (res != AST_DEVICE_INUSE)
                                        res = AST_DEVICE_BUSY;
+                       } else if (p->inherited_devicestate > -1) {
+                               res = p->inherited_devicestate;
                        } else {
                                if (res == AST_DEVICE_BUSY)
                                        res = AST_DEVICE_INUSE;
@@ -2433,6 +2487,8 @@ static int load_module(void)
        /* Dialplan Functions */
        ast_custom_function_register(&agent_function);
 
+       agent_devicestate_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, agent_devicestate_cb, NULL);
+
        return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -2450,6 +2506,8 @@ static int unload_module(void)
        struct agent_pvt *p;
        /* First, take us out of the channel loop */
        ast_channel_unregister(&agent_tech);
+       /* Delete devicestate subscription */
+       agent_devicestate_sub = ast_event_unsubscribe(agent_devicestate_sub);
        /* Unregister dialplan functions */
        ast_custom_function_unregister(&agent_function);        
        /* Unregister CLI commands */