CHANGES: Update changes log to include r403414 entry
[asterisk/asterisk.git] / apps / app_agent_pool.c
index 92209e1..a44c75d 100644 (file)
@@ -40,13 +40,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
-#include "asterisk/bridging.h"
-#include "asterisk/bridging_basic.h"
+#include "asterisk/bridge.h"
+#include "asterisk/bridge_internal.h"
+#include "asterisk/bridge_basic.h"
+#include "asterisk/bridge_after.h"
 #include "asterisk/config_options.h"
 #include "asterisk/features_config.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/causes.h"
 
 /*** DOCUMENTATION
        <application name="AgentLogin" language="en_US">
@@ -200,49 +203,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <para>Epoche time when the agent logged in.</para>
                                        <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
                                </parameter>
-                               <parameter name="Channel">
-                                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Channel']/para)" />
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="ChannelState">
-                                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelState']/para)" />
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="ChannelStateDesc">
-                                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/para)" />
-                                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/enumlist)" />
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="CallerIDNum">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="CallerIDName">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="ConnectedLineNum">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="ConnectedLineName">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="AccountCode">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="Context">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="Exten">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="Priority">
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
-                               <parameter name="Uniqueid">
-                                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Uniqueid']/para)" />
-                                       <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
-                               </parameter>
+                               <channel_snapshot/>
                                <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
                        </syntax>
+                       <description>
+                               <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
+                       </description>
                        <see-also>
                                <ref type="manager">Agents</ref>
                        </see-also>
@@ -795,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) {
@@ -1054,15 +1020,17 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 
        if (!caller_bridge) {
                /* Reset agent. */
-               ast_bridge_change_state(bridge_channel, AST_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_change_state(bridge_channel, AST_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);
@@ -1076,12 +1044,11 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
                 * The agent is in the new bridge so we can invoke the
                 * mixmonitor hook to only start recording.
                 */
-               ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, caller_bridge,
-                       bridge_channel, &options);
+               ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
        }
 }
 
-static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
        struct agent_pvt *agent = hook_pvt;
 
@@ -1099,7 +1066,7 @@ static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_ch
        return 0;
 }
 
-static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
        struct agent_pvt *agent = hook_pvt;
        int probation_timedout = 0;
@@ -1158,13 +1125,15 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri
 
        if (deferred_logoff) {
                ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
-               ast_bridge_change_state(bridge_channel, AST_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_change_state(bridge_channel, AST_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);
@@ -1174,7 +1143,7 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri
 }
 
 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
-static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data);
+static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
 
 /*!
  * \internal
@@ -1238,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;
@@ -1251,7 +1220,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
        }
 
        if (swap) {
-               res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb,
+               res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
                        agent_after_bridge_cb_failed, chan);
                if (res) {
                        ast_channel_remove_bridge_role(chan, "holding_participant");
@@ -1269,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_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_NORMAL_CLEARING);
                return 0;
        }
 
@@ -1383,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);
 }
 
@@ -1393,15 +1363,16 @@ static struct ast_bridge *bridge_agent_hold_new(void)
 {
        struct ast_bridge *bridge;
 
-       bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
-       bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
+       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);
-       bridge = ast_bridge_register(bridge);
+                       | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED,
+               "AgentPool", NULL);
+       bridge = bridge_register(bridge);
        return bridge;
 }
 
-static void bridging_init_agent_hold(void)
+static void bridge_init_agent_hold(void)
 {
        /* Setup bridge agent_hold subclass v_table. */
        bridge_agent_hold_v_table = ast_bridge_base_v_table;
@@ -1491,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);
@@ -1515,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 (;;) {
@@ -1524,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",
@@ -1536,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;
@@ -1571,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
@@ -1619,7 +1596,7 @@ static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
        ao2_ref(agent, -1);
 }
 
-static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data)
+static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
 {
        struct ast_channel *chan = data;
        struct agent_pvt *agent;
@@ -1630,7 +1607,7 @@ static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason,
        }
        ast_log(LOG_WARNING, "Agent %s: Forced logout.  Lost control of %s because: %s\n",
                agent->username, ast_channel_name(chan),
-               ast_after_bridge_cb_reason_string(reason));
+               ast_bridge_after_cb_reason_string(reason));
        agent_lock(agent);
        agent_logout(agent);
        ao2_ref(agent, -1);
@@ -1697,23 +1674,25 @@ 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_change_state_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END);
+       ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
+               AST_CAUSE_NORMAL_CLEARING);
        ast_bridge_channel_unlock(logged);
 }
 
-static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
        struct agent_pvt *agent = hook_pvt;
 
        if (agent->state == AGENT_STATE_CALL_PRESENT) {
                ast_verb(3, "Agent '%s' did not respond.  Safety timeout.\n", agent->username);
-               ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+                       AST_CAUSE_USER_BUSY);
                caller_abort_agent(agent);
        }
 
@@ -1732,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);
@@ -1760,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)
@@ -1833,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);
@@ -1858,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");
@@ -1872,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");
@@ -1884,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");
@@ -1899,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");
@@ -1908,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;
@@ -2048,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);
@@ -2060,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;
@@ -2528,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();
@@ -2554,7 +2544,7 @@ static int load_module(void)
        }
 
        /* Init agent holding bridge v_table. */
-       bridging_init_agent_hold();
+       bridge_init_agent_hold();
 
        /* Setup to provide Agent:agent-id device state. */
        res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);