CHANGES: Update changes log to include r403414 entry
[asterisk/asterisk.git] / apps / app_agent_pool.c
index 286a937..a44c75d 100644 (file)
@@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/astobj2.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/causes.h"
 
 /*** DOCUMENTATION
        <application name="AgentLogin" language="en_US">
@@ -760,7 +761,7 @@ static void agent_pvt_destructor(void *vdoomed)
 
        ast_party_connected_line_free(&doomed->waiting_colp);
        if (doomed->caller_bridge) {
-               ast_bridge_destroy(doomed->caller_bridge);
+               ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY);
                doomed->caller_bridge = NULL;
        }
        if (doomed->logged) {
@@ -1019,15 +1020,17 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 
        if (!caller_bridge) {
                /* Reset agent. */
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
                return;
        }
        res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
                NULL, 0);
        if (res) {
                /* Reset agent. */
-               ast_bridge_destroy(caller_bridge);
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
                return;
        }
        ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
@@ -1122,13 +1125,15 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel
 
        if (deferred_logoff) {
                ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
        } else if (probation_timedout) {
                ast_debug(1, "Agent %s: Login complete.\n", agent->username);
                agent_devstate_changed(agent->username);
        } else if (ack_timedout) {
                ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
        } else if (wrapup_timedout) {
                ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
                agent_devstate_changed(agent->username);
@@ -1202,7 +1207,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
 
        /* Add heartbeat interval hook. */
        ao2_ref(agent, +1);
-       if (ast_bridge_interval_hook(bridge_channel->features, 1000,
+       if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
                bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
                ao2_ref(agent, -1);
                res = -1;
@@ -1233,7 +1238,8 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
                 * agent will have some slightly different behavior in corner
                 * cases.
                 */
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
                return 0;
        }
 
@@ -1347,7 +1353,7 @@ static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_ch
  */
 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
 {
-       ao2_global_obj_replace_unref(agent_holding, NULL);
+       ao2_global_obj_release(agent_holding);
        ast_bridge_base_v_table.dissolving(self);
 }
 
@@ -1360,7 +1366,8 @@ static struct ast_bridge *bridge_agent_hold_new(void)
        bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
        bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
                AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
-                       | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
+                       | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED,
+               "AgentPool", NULL);
        bridge = bridge_register(bridge);
        return bridge;
 }
@@ -1455,10 +1462,12 @@ static void agent_logout(struct agent_pvt *agent)
        agent_devstate_changed(agent->username);
 
        if (caller_bridge) {
-               ast_bridge_destroy(caller_bridge);
+               ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
        }
 
+       ast_channel_lock(logged);
        send_agent_logoff(logged, agent->username, time_logged_in);
+       ast_channel_unlock(logged);
        ast_verb(2, "Agent '%s' logged out.  Logged in for %ld seconds.\n",
                agent->username, time_logged_in);
        ast_channel_unref(logged);
@@ -1479,6 +1488,7 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
        struct ast_bridge_features features;
 
        if (ast_bridge_features_init(&features)) {
+               ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
                goto agent_run_cleanup;
        }
        for (;;) {
@@ -1488,6 +1498,8 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
                struct ast_bridge *holding;
                struct ast_bridge *caller_bridge;
 
+               ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
+
                holding = ao2_global_obj_ref(agent_holding);
                if (!holding) {
                        ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
@@ -1500,7 +1512,8 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
                 * want to put the agent back into the holding bridge for the
                 * next caller.
                 */
-               ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
+               ast_bridge_join(holding, logged, NULL, &features, NULL,
+                       AST_BRIDGE_JOIN_PASS_REFERENCE);
                if (logged != agent->logged) {
                        /* This channel is no longer the logged in agent. */
                        break;
@@ -1535,7 +1548,7 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
                agent_unlock(agent);
                ao2_ref(cfg_old, -1);
                if (caller_bridge) {
-                       ast_bridge_destroy(caller_bridge);
+                       ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
                }
 
                if (agent->state == AGENT_STATE_LOGGING_OUT
@@ -1661,13 +1674,14 @@ static void caller_abort_agent(struct agent_pvt *agent)
                agent->caller_bridge = NULL;
                agent_unlock(agent);
                if (caller_bridge) {
-                       ast_bridge_destroy(caller_bridge);
+                       ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
                }
                return;
        }
 
        /* Kick the agent out of the holding bridge to reset it. */
-       ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END);
+       ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
+               AST_CAUSE_NORMAL_CLEARING);
        ast_bridge_channel_unlock(logged);
 }
 
@@ -1677,7 +1691,8 @@ static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void
 
        if (agent->state == AGENT_STATE_CALL_PRESENT) {
                ast_verb(3, "Agent '%s' did not respond.  Safety timeout.\n", agent->username);
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_USER_BUSY);
                caller_abort_agent(agent);
        }
 
@@ -1696,6 +1711,13 @@ static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *p
                return;
        }
 
+       /* Change holding bridge participant role's idle mode to silence */
+       ast_bridge_channel_lock_bridge(bridge_channel);
+       ast_bridge_channel_clear_roles(bridge_channel);
+       ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
+       ast_bridge_channel_establish_roles(bridge_channel);
+       ast_bridge_unlock(bridge_channel->bridge);
+
        /* Alert the agent. */
        agent_lock(agent);
        playfile = ast_strdupa(agent->cfg->beep_sound);
@@ -1724,8 +1746,8 @@ static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *p
 
 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
 {
-       return ast_bridge_channel_queue_callback(bridge_channel, agent_alert, agent_id,
-               strlen(agent_id) + 1);
+       return ast_bridge_channel_queue_callback(bridge_channel,
+               AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
 }
 
 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
@@ -1797,7 +1819,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
 
        /* Add safety timeout hook. */
        ao2_ref(agent, +1);
-       if (ast_bridge_interval_hook(&caller_features, CALLER_SAFETY_TIMEOUT_TIME,
+       if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
                caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
                ao2_ref(agent, -1);
                ast_bridge_features_cleanup(&caller_features);
@@ -1822,7 +1844,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
        case AGENT_STATE_LOGGING_OUT:
                agent_unlock(agent);
                ast_party_connected_line_free(&connected);
-               ast_bridge_destroy(caller_bridge);
+               ast_bridge_destroy(caller_bridge, 0);
                ast_bridge_features_cleanup(&caller_features);
                ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
                pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
@@ -1836,7 +1858,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
        default:
                agent_unlock(agent);
                ast_party_connected_line_free(&connected);
-               ast_bridge_destroy(caller_bridge);
+               ast_bridge_destroy(caller_bridge, 0);
                ast_bridge_features_cleanup(&caller_features);
                ast_verb(3, "Agent '%s' is busy.\n", agent->username);
                pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
@@ -1848,7 +1870,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
        logged = agent_bridge_channel_get_lock(agent);
        if (!logged) {
                ast_party_connected_line_free(&connected);
-               ast_bridge_destroy(caller_bridge);
+               ast_bridge_destroy(caller_bridge, 0);
                ast_bridge_features_cleanup(&caller_features);
                ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
                pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
@@ -1863,7 +1885,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
        ast_bridge_channel_unlock(logged);
        ao2_ref(logged, -1);
        if (res) {
-               ast_bridge_destroy(caller_bridge);
+               ast_bridge_destroy(caller_bridge, 0);
                ast_bridge_features_cleanup(&caller_features);
                ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
                pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
@@ -1872,7 +1894,8 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
        }
 
        ast_indicate(chan, AST_CONTROL_RINGING);
-       ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
+       ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
+               AST_BRIDGE_JOIN_PASS_REFERENCE);
        ast_bridge_features_cleanup(&caller_features);
 
        return -1;
@@ -2012,6 +2035,7 @@ static int agent_login_exec(struct ast_channel *chan, const char *data)
        agent->logged = ast_channel_ref(chan);
        agent->last_disconnect = ast_tvnow();
        time(&agent->login_start);
+       agent->deferred_logoff = 0;
        agent_unlock(agent);
 
        agent_login_channel_config(agent, chan);
@@ -2024,7 +2048,9 @@ static int agent_login_exec(struct ast_channel *chan, const char *data)
        ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
                ast_getformatname(ast_channel_readformat(chan)),
                ast_getformatname(ast_channel_writeformat(chan)));
+       ast_channel_lock(chan);
        send_agent_login(chan, agent->username);
+       ast_channel_unlock(chan);
 
        agent_run(agent, chan);
        return -1;
@@ -2492,7 +2518,7 @@ static int unload_module(void)
        /* Destroy agent holding bridge. */
        holding = ao2_global_obj_replace(agent_holding, NULL);
        if (holding) {
-               ast_bridge_destroy(holding);
+               ast_bridge_destroy(holding, 0);
        }
 
        destroy_config();