2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013 Digium, Inc.
6 * Richard Mudgett <rmudgett@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Call center agent pool.
23 * \author Richard Mudgett <rmudgett@digium.com>
26 * \arg \ref AstCREDITS
27 * \arg \ref Config_agent
30 <support_level>core</support_level>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/cli.h"
39 #include "asterisk/app.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/bridge.h"
44 #include "asterisk/bridge_internal.h"
45 #include "asterisk/bridge_basic.h"
46 #include "asterisk/bridge_after.h"
47 #include "asterisk/config_options.h"
48 #include "asterisk/features_config.h"
49 #include "asterisk/astobj2.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/stasis_channels.h"
54 <application name="AgentLogin" language="en_US">
59 <parameter name="AgentId" required="true" />
60 <parameter name="options">
63 <para>silent login - do not announce the login ok segment after
64 agent logged on.</para>
70 <para>Login an agent to the system. Any agent authentication is assumed to
71 already be done by dialplan. While logged in, the agent can receive calls
72 and will hear the sound file specified by the config option custom_beep
73 when a new call comes in for the agent. Login failures will continue in
74 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
75 <para>Before logging in, you can setup on the real agent channel the
76 CHANNEL(dtmf-features) an agent will have when talking to a caller
77 and you can setup on the channel running this application the
78 CONNECTEDLINE() information the agent will see while waiting for a
80 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
82 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
83 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
85 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
86 available to monitor the status of the agent.</para></note>
89 <ref type="application">Authenticate</ref>
90 <ref type="application">Queue</ref>
91 <ref type="application">AddQueueMember</ref>
92 <ref type="application">RemoveQueueMember</ref>
93 <ref type="application">PauseQueueMember</ref>
94 <ref type="application">UnpauseQueueMember</ref>
95 <ref type="function">AGENT</ref>
96 <ref type="function">CHANNEL(dtmf-features)</ref>
97 <ref type="function">CONNECTEDLINE()</ref>
98 <ref type="filename">agents.conf</ref>
99 <ref type="filename">queues.conf</ref>
102 <application name="AgentRequest" language="en_US">
104 Request an agent to connect with the channel.
107 <parameter name="AgentId" required="true" />
110 <para>Request an agent to connect with the channel. Failure to find and
111 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
112 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
114 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
115 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
116 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
117 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
121 <ref type="application">AgentLogin</ref>
124 <function name="AGENT" language="en_US">
126 Gets information about an Agent
129 <parameter name="AgentId" required="true" />
130 <parameter name="item">
131 <para>The valid items to retrieve are:</para>
134 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
136 <enum name="password">
137 <para>Deprecated. The dialplan handles any agent authentication.</para>
140 <para>The name of the agent</para>
142 <enum name="mohclass">
143 <para>MusicOnHold class</para>
145 <enum name="channel">
146 <para>The name of the active channel for the Agent (AgentLogin)</para>
148 <enum name="fullchannel">
149 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
154 <description></description>
156 <manager name="Agents" language="en_US">
158 Lists agents and their status.
161 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
164 <para>Will list info about all defined agents.</para>
167 <ref type="managerEvent">Agents</ref>
168 <ref type="managerEvent">AgentsComplete</ref>
171 <managerEvent language="en_US" name="Agents">
172 <managerEventInstance class="EVENT_FLAG_AGENT">
174 Response event in a series to the Agents AMI action containing
175 information about a defined agent.
178 <parameter name="Agent">
179 <para>Agent ID of the agent.</para>
181 <parameter name="Name">
182 <para>User friendly name of the agent.</para>
184 <parameter name="Status">
185 <para>Current status of the agent.</para>
186 <para>The valid values are:</para>
188 <enum name="AGENT_LOGGEDOFF" />
189 <enum name="AGENT_IDLE" />
190 <enum name="AGENT_ONCALL" />
193 <parameter name="TalkingToChan">
194 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
195 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
197 <parameter name="CallStarted">
198 <para>Epoche time when the agent started talking with the caller.</para>
199 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
201 <parameter name="LoggedInTime">
202 <para>Epoche time when the agent logged in.</para>
203 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
206 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
209 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
212 <ref type="manager">Agents</ref>
214 </managerEventInstance>
216 <managerEvent language="en_US" name="AgentsComplete">
217 <managerEventInstance class="EVENT_FLAG_AGENT">
219 Final response event in a series of events to the Agents AMI action.
222 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
225 <ref type="manager">Agents</ref>
227 </managerEventInstance>
229 <manager name="AgentLogoff" language="en_US">
231 Sets an agent as no longer logged in.
234 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
235 <parameter name="Agent" required="true">
236 <para>Agent ID of the agent to log off.</para>
238 <parameter name="Soft">
239 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
243 <para>Sets an agent as no longer logged in.</para>
246 <configInfo name="app_agent_pool" language="en_US">
247 <synopsis>Agent pool applications</synopsis>
249 <note><para>Option changes take effect on agent login or after an agent
250 disconnects from a call.</para></note>
252 <configFile name="agents.conf">
253 <configObject name="global">
254 <synopsis>Unused, but reserved.</synopsis>
256 <configObject name="agent-id">
257 <synopsis>Configure an agent for the pool.</synopsis>
259 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
261 <configOption name="ackcall">
262 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
264 <para>Enable to require the agent to give a DTMF acknowledgement
265 when the agent receives a call.</para>
266 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
267 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
270 <configOption name="acceptdtmf">
271 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
273 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
274 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
275 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
278 <configOption name="autologoff">
279 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
281 <para>Set how many seconds a call for the agent has to wait for the
282 agent to acknowledge the call before the agent is automatically
283 logged off. If set to zero then the call will wait forever for
284 the agent to acknowledge.</para>
285 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
286 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
287 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
290 <configOption name="wrapuptime">
291 <synopsis>Minimum time the agent has between calls.</synopsis>
293 <para>Set the minimum amount of time in milliseconds after
294 disconnecting a call before the agent can receive a new call.</para>
295 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
296 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
299 <configOption name="musiconhold">
300 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
302 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
305 <configOption name="recordagentcalls">
306 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
308 <para>Enable recording calls the agent takes automatically by
309 invoking the automixmon DTMF feature when the agent connects
310 to a caller. See <filename>features.conf.sample</filename> for information about
311 the automixmon feature.</para>
312 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
315 <configOption name="custom_beep">
316 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
318 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
321 <configOption name="fullname">
322 <synopsis>A friendly name for the agent used in log messages.</synopsis>
324 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
332 /* ------------------------------------------------------------------- */
334 #define AST_MAX_BUF 256
336 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
337 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
339 /*! Number of seconds to wait for local channel optimizations to complete. */
340 #define LOGIN_WAIT_TIMEOUT_TIME 5
342 static const char app_agent_login[] = "AgentLogin";
343 static const char app_agent_request[] = "AgentRequest";
345 /*! Agent config parameters. */
347 AST_DECLARE_STRING_FIELDS(
348 /*! Identification of the agent. (agents config container key) */
349 AST_STRING_FIELD(username);
350 /*! Name of agent for logging and querying purposes */
351 AST_STRING_FIELD(full_name);
354 * \brief DTMF string for an agent to accept a call.
356 * \note The channel variable AGENTACCEPTDTMF overrides on login.
358 AST_STRING_FIELD(dtmf_accept);
359 /*! Beep sound file to use. Alert the agent a call is waiting. */
360 AST_STRING_FIELD(beep_sound);
361 /*! MOH class to use while agent waiting for call. */
362 AST_STRING_FIELD(moh);
365 * \brief Number of seconds for agent to ack a call before being logged off.
367 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
368 * \note If zero then timer is disabled.
370 unsigned int auto_logoff;
372 * \brief Time after a call in ms before the agent can get a new call.
374 * \note The channel variable AGENTWRAPUPTIME overrides on login.
376 unsigned int wrapup_time;
378 * \brief TRUE if agent needs to ack a call to accept it.
380 * \note The channel variable AGENTACKCALL overrides on login.
383 /*! TRUE if agent calls are automatically recorded. */
384 int record_agent_calls;
389 * \brief Agent config ao2 container sort function.
392 * \param obj_left pointer to the (user-defined part) of an object.
393 * \param obj_right pointer to the (user-defined part) of an object.
394 * \param flags flags from ao2_callback()
395 * OBJ_POINTER - if set, 'obj_right', is an object.
396 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
397 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
399 * \retval <0 if obj_left < obj_right
400 * \retval =0 if obj_left == obj_right
401 * \retval >0 if obj_left > obj_right
403 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
405 const struct agent_cfg *cfg_left = obj_left;
406 const struct agent_cfg *cfg_right = obj_right;
407 const char *right_key = obj_right;
410 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
413 right_key = cfg_right->username;
416 cmp = strcmp(cfg_left->username, right_key);
418 case OBJ_PARTIAL_KEY:
419 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
425 static void agent_cfg_destructor(void *vdoomed)
427 struct agent_cfg *doomed = vdoomed;
429 ast_string_field_free_memory(doomed);
432 static void *agent_cfg_alloc(const char *name)
434 struct agent_cfg *cfg;
436 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
437 AO2_ALLOC_OPT_LOCK_NOLOCK);
438 if (!cfg || ast_string_field_init(cfg, 64)) {
441 ast_string_field_set(cfg, username, name);
445 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
447 return ao2_find(agents, username, OBJ_KEY);
450 /*! Agents configuration */
452 /*! Master configured agents container. */
453 struct ao2_container *agents;
456 static struct aco_type agent_type = {
459 .category_match = ACO_BLACKLIST,
460 .category = "^(general|agents)$",
461 .item_alloc = agent_cfg_alloc,
462 .item_find = agent_cfg_find,
463 .item_offset = offsetof(struct agents_cfg, agents),
466 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
468 /* The general category is reserved, but unused */
469 static struct aco_type general_type = {
472 .category_match = ACO_WHITELIST,
473 .category = "^general$",
476 static struct aco_file agents_conf = {
477 .filename = "agents.conf",
478 .types = ACO_TYPES(&general_type, &agent_type),
481 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
483 static void agents_cfg_destructor(void *vdoomed)
485 struct agents_cfg *doomed = vdoomed;
487 ao2_cleanup(doomed->agents);
488 doomed->agents = NULL;
493 * \brief Create struct agents_cfg object.
496 * \note A lock is not needed for the object or any secondary
497 * created cfg objects. These objects are immutable after the
498 * config is loaded and applied.
500 * \retval New struct agents_cfg object.
501 * \retval NULL on error.
503 static void *agents_cfg_alloc(void)
505 struct agents_cfg *cfg;
507 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
508 AO2_ALLOC_OPT_LOCK_NOLOCK);
512 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
513 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
521 static void agents_post_apply_config(void);
523 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
524 .files = ACO_FILES(&agents_conf),
525 .post_apply_config = agents_post_apply_config,
528 static void destroy_config(void)
530 ao2_global_obj_release(cfg_handle);
531 aco_info_destroy(&cfg_info);
534 static int load_config(void)
536 if (aco_info_init(&cfg_info)) {
541 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
542 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
543 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
544 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
545 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
546 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
547 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
548 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
550 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
562 /*! The agent is defined but an agent is not present. */
563 AGENT_STATE_LOGGED_OUT,
564 /*! Forced initial login wait to allow any local channel optimizations to happen. */
565 AGENT_STATE_PROBATION_WAIT,
566 /*! The agent is ready for a call. */
567 AGENT_STATE_READY_FOR_CALL,
568 /*! The agent has a call waiting to connect. */
569 AGENT_STATE_CALL_PRESENT,
570 /*! The agent needs to ack the call. */
571 AGENT_STATE_CALL_WAIT_ACK,
572 /*! The agent is connected with a call. */
574 /*! The agent is resting between calls. */
575 AGENT_STATE_CALL_WRAPUP,
576 /*! The agent is being kicked out. */
577 AGENT_STATE_LOGGING_OUT,
580 /*! Agent config option override flags. */
581 enum agent_override_flags {
582 AGENT_FLAG_ACK_CALL = (1 << 0),
583 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
584 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
585 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
588 /*! \brief Structure representing an agent. */
590 AST_DECLARE_STRING_FIELDS(
591 /*! Identification of the agent. (agents container key) */
592 AST_STRING_FIELD(username);
593 /*! Login override DTMF string for an agent to accept a call. */
594 AST_STRING_FIELD(override_dtmf_accept);
596 /*! Connected line information to send when reentering the holding bridge. */
597 struct ast_party_connected_line waiting_colp;
598 /*! Flags show if settings were overridden by channel vars. */
600 /*! Login override number of seconds for agent to ack a call before being logged off. */
601 unsigned int override_auto_logoff;
602 /*! Login override time after a call in ms before the agent can get a new call. */
603 unsigned int override_wrapup_time;
604 /*! Login override if agent needs to ack a call to accept it. */
605 unsigned int override_ack_call:1;
607 /*! TRUE if the agent is requested to logoff when the current call ends. */
608 unsigned int deferred_logoff:1;
610 /*! Mark and sweep config update to determine if an agent is dead. */
611 unsigned int the_mark:1;
613 * \brief TRUE if the agent is no longer configured and is being destroyed.
615 * \note Agents cannot log in if they are dead.
619 /*! Agent control state variable. */
620 enum agent_state state;
621 /*! Custom device state of agent. */
622 enum ast_device_state devstate;
624 /*! When agent first logged in */
626 /*! When agent login probation started. */
627 time_t probation_start;
628 /*! When call started */
630 /*! When ack timer started */
631 struct timeval ack_time;
632 /*! When last disconnected */
633 struct timeval last_disconnect;
635 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
636 struct ast_bridge *caller_bridge;
637 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
638 struct ast_channel *logged;
639 /*! Active config values from config file. (Holds ref) */
640 struct agent_cfg *cfg;
643 /*! Container of defined agents. */
644 static struct ao2_container *agents;
647 * \brief Lock the agent.
649 * \param agent Agent to lock
653 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
654 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
656 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
660 * \brief Unlock the agent.
662 * \param agent Agent to unlock
666 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
667 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
669 __ao2_unlock(agent, file, function, line, var);
674 * \brief Obtain the agent logged in channel lock if it exists.
677 * \param agent Pointer to the LOCKED agent_pvt.
679 * \note Assumes the agent lock is already obtained.
681 * \note Defined locking order is channel lock then agent lock.
685 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
687 struct ast_channel *logged;
690 if (!agent->logged) { /* No owner. Nothing to do. */
694 /* If we don't ref the logged, it could be killed when we unlock the agent. */
695 logged = ast_channel_ref(agent->logged);
697 /* Locking logged requires us to lock channel, then agent. */
699 ast_channel_lock(logged);
702 /* Check if logged changed during agent unlock period */
703 if (logged != agent->logged) {
704 /* Channel changed. Unref and do another pass. */
705 ast_channel_unlock(logged);
706 ast_channel_unref(logged);
708 /* Channel stayed the same. Return it. */
716 * \brief Get the Agent:agent_id device state.
719 * \param agent_id Username of the agent.
722 * Search the agents container for the agent and return the
725 * \return Device state of the agent.
727 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
729 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
732 return agent->devstate;
734 return AST_DEVICE_INVALID;
739 * \brief Request an agent device state be updated.
742 * \param agent_id Which agent needs the device state updated.
746 static void agent_devstate_changed(const char *agent_id)
748 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
751 static void agent_pvt_destructor(void *vdoomed)
753 struct agent_pvt *doomed = vdoomed;
755 /* Make sure device state reflects agent destruction. */
756 if (!ast_strlen_zero(doomed->username)) {
757 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
758 agent_devstate_changed(doomed->username);
761 ast_party_connected_line_free(&doomed->waiting_colp);
762 if (doomed->caller_bridge) {
763 ast_bridge_destroy(doomed->caller_bridge);
764 doomed->caller_bridge = NULL;
766 if (doomed->logged) {
767 doomed->logged = ast_channel_unref(doomed->logged);
769 ao2_cleanup(doomed->cfg);
771 ast_string_field_free_memory(doomed);
774 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
776 struct agent_pvt *agent;
778 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
782 if (ast_string_field_init(agent, 32)) {
786 ast_string_field_set(agent, username, cfg->username);
787 ast_party_connected_line_init(&agent->waiting_colp);
790 agent->devstate = AST_DEVICE_UNAVAILABLE;
796 * \brief Agents ao2 container sort function.
799 * \param obj_left pointer to the (user-defined part) of an object.
800 * \param obj_right pointer to the (user-defined part) of an object.
801 * \param flags flags from ao2_callback()
802 * OBJ_POINTER - if set, 'obj_right', is an object.
803 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
804 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
806 * \retval <0 if obj_left < obj_right
807 * \retval =0 if obj_left == obj_right
808 * \retval >0 if obj_left > obj_right
810 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
812 const struct agent_pvt *agent_left = obj_left;
813 const struct agent_pvt *agent_right = obj_right;
814 const char *right_key = obj_right;
817 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
820 right_key = agent_right->username;
823 cmp = strcmp(agent_left->username, right_key);
825 case OBJ_PARTIAL_KEY:
826 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
834 * \brief ao2_find() callback function.
838 * found = ao2_find(agents, agent, OBJ_POINTER);
839 * found = ao2_find(agents, "agent-id", OBJ_KEY);
840 * found = ao2_find(agents, agent->logged, 0);
842 static int agent_pvt_cmp(void *obj, void *arg, int flags)
844 const struct agent_pvt *agent = obj;
847 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
850 case OBJ_PARTIAL_KEY:
854 if (agent->logged == arg) {
864 static int agent_mark(void *obj, void *arg, int flags)
866 struct agent_pvt *agent = obj;
874 static void agents_mark(void)
876 ao2_callback(agents, 0, agent_mark, NULL);
879 static int agent_sweep(void *obj, void *arg, int flags)
881 struct agent_pvt *agent = obj;
885 if (agent->the_mark) {
888 /* Unlink dead agents immediately. */
895 static void agents_sweep(void)
897 struct ao2_iterator *iter;
898 struct agent_pvt *agent;
899 struct ast_channel *logged;
901 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
905 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
908 logged = ast_channel_ref(agent->logged);
917 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
918 agent->username, ast_channel_name(logged));
919 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
920 ast_channel_unref(logged);
922 ao2_iterator_destroy(iter);
925 static void agents_post_apply_config(void)
927 struct ao2_iterator iter;
928 struct agent_cfg *cfg;
929 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
931 ast_assert(cfgs != NULL);
934 iter = ao2_iterator_init(cfgs->agents, 0);
935 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
936 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
941 if (!agent->logged) {
942 struct agent_cfg *cfg_old;
944 /* Replace the config of agents not logged in. */
945 cfg_old = agent->cfg;
948 ao2_cleanup(cfg_old);
953 agent = agent_pvt_new(cfg);
957 ao2_link(agents, agent);
958 ast_debug(1, "Agent %s: Created.\n", agent->username);
959 agent_devstate_changed(agent->username);
961 ao2_iterator_destroy(&iter);
965 static int agent_logoff_request(const char *agent_id, int soft)
967 struct ast_channel *logged;
968 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
975 logged = agent_lock_logged(agent);
978 agent->deferred_logoff = 1;
980 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
982 ast_channel_unlock(logged);
983 ast_channel_unref(logged);
989 /*! Agent holding bridge instance holder. */
990 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
992 /*! Agent holding bridge deferred creation lock. */
993 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
997 * \brief Connect the agent with the waiting caller.
1000 * \param bridge_channel Agent channel connecting to the caller.
1001 * \param agent Which agent is connecting to the caller.
1003 * \note The agent is locked on entry and not locked on exit.
1007 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1009 struct ast_bridge *caller_bridge;
1010 int record_agent_calls;
1013 record_agent_calls = agent->cfg->record_agent_calls;
1014 caller_bridge = agent->caller_bridge;
1015 agent->caller_bridge = NULL;
1016 agent->state = AGENT_STATE_ON_CALL;
1017 time(&agent->call_start);
1018 agent_unlock(agent);
1020 if (!caller_bridge) {
1022 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1025 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1029 ast_bridge_destroy(caller_bridge);
1030 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1033 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1035 if (record_agent_calls) {
1036 struct ast_bridge_features_automixmonitor options = {
1037 .start_stop = AUTO_MONITOR_START,
1041 * The agent is in the new bridge so we can invoke the
1042 * mixmonitor hook to only start recording.
1044 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1048 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1050 struct agent_pvt *agent = hook_pvt;
1053 switch (agent->state) {
1054 case AGENT_STATE_CALL_WAIT_ACK:
1055 /* Connect to caller now. */
1056 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1057 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1062 agent_unlock(agent);
1066 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1068 struct agent_pvt *agent = hook_pvt;
1069 int probation_timedout = 0;
1070 int ack_timedout = 0;
1071 int wrapup_timedout = 0;
1072 int deferred_logoff;
1073 unsigned int wrapup_time;
1074 unsigned int auto_logoff;
1077 deferred_logoff = agent->deferred_logoff;
1078 if (deferred_logoff) {
1079 agent->state = AGENT_STATE_LOGGING_OUT;
1082 switch (agent->state) {
1083 case AGENT_STATE_PROBATION_WAIT:
1084 probation_timedout =
1085 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1086 if (probation_timedout) {
1087 /* Now ready for a caller. */
1088 agent->state = AGENT_STATE_READY_FOR_CALL;
1089 agent->devstate = AST_DEVICE_NOT_INUSE;
1092 case AGENT_STATE_CALL_WAIT_ACK:
1093 /* Check ack call time. */
1094 auto_logoff = agent->cfg->auto_logoff;
1095 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1096 auto_logoff = agent->override_auto_logoff;
1099 auto_logoff *= 1000;
1100 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1102 agent->state = AGENT_STATE_LOGGING_OUT;
1106 case AGENT_STATE_CALL_WRAPUP:
1107 /* Check wrapup time. */
1108 wrapup_time = agent->cfg->wrapup_time;
1109 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1110 wrapup_time = agent->override_wrapup_time;
1112 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1113 if (wrapup_timedout) {
1114 agent->state = AGENT_STATE_READY_FOR_CALL;
1115 agent->devstate = AST_DEVICE_NOT_INUSE;
1121 agent_unlock(agent);
1123 if (deferred_logoff) {
1124 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1125 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1126 } else if (probation_timedout) {
1127 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1128 agent_devstate_changed(agent->username);
1129 } else if (ack_timedout) {
1130 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1131 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1132 } else if (wrapup_timedout) {
1133 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1134 agent_devstate_changed(agent->username);
1140 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1141 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1145 * \brief ast_bridge agent_hold push method.
1148 * \param self Bridge to operate upon.
1149 * \param bridge_channel Bridge channel to push.
1150 * \param swap Bridge channel to swap places with if not NULL.
1152 * \note On entry, self is already locked.
1154 * \retval 0 on success
1155 * \retval -1 on failure
1157 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1160 unsigned int wrapup_time;
1161 char dtmf[AST_FEATURE_MAX_LEN];
1162 struct ast_channel *chan;
1163 const char *moh_class;
1164 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1166 chan = bridge_channel->chan;
1168 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1170 /* Could not find the agent. */
1174 /* Setup agent entertainment */
1176 moh_class = ast_strdupa(agent->cfg->moh);
1177 agent_unlock(agent);
1178 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1179 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1180 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1182 /* Add DTMF acknowledge hook. */
1185 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1186 ? agent->override_ack_call : agent->cfg->ack_call) {
1187 const char *dtmf_accept;
1189 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1190 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1191 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1193 agent_unlock(agent);
1194 if (!ast_strlen_zero(dtmf)) {
1196 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1197 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1203 /* Add heartbeat interval hook. */
1205 if (ast_bridge_interval_hook(bridge_channel->features, 1000,
1206 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1211 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1213 ast_channel_remove_bridge_role(chan, "holding_participant");
1218 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1219 agent_after_bridge_cb_failed, chan);
1221 ast_channel_remove_bridge_role(chan, "holding_participant");
1226 ast_channel_unref(agent->logged);
1227 agent->logged = ast_channel_ref(chan);
1228 agent_unlock(agent);
1231 * Kick the channel out so it can come back in fully controlled.
1232 * Otherwise, the after bridge callback will linger and the
1233 * agent will have some slightly different behavior in corner
1236 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1241 switch (agent->state) {
1242 case AGENT_STATE_LOGGED_OUT:
1244 * \todo XXX the login probation time should be only if it is needed.
1246 * Need to determine if there are any local channels that can
1247 * optimize and wait until they actually do before leaving the
1248 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1249 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1252 * Start the login probation timer.
1254 * We cannot handle an agent local channel optimization when the
1255 * agent is on a call. The optimization may kick the agent
1256 * channel we know about out of the call without our being able
1257 * to switch to the replacement channel. Get any agent local
1258 * channel optimization out of the way while the agent is in the
1261 time(&agent->probation_start);
1262 agent->state = AGENT_STATE_PROBATION_WAIT;
1263 agent_unlock(agent);
1265 case AGENT_STATE_PROBATION_WAIT:
1266 /* Restart the probation timer. */
1267 time(&agent->probation_start);
1268 agent_unlock(agent);
1270 case AGENT_STATE_READY_FOR_CALL:
1272 * Likely someone manually kicked us out of the holding bridge
1273 * and we came right back in.
1275 agent_unlock(agent);
1278 /* Unexpected agent state. */
1281 case AGENT_STATE_CALL_PRESENT:
1282 case AGENT_STATE_CALL_WAIT_ACK:
1283 agent->state = AGENT_STATE_READY_FOR_CALL;
1284 agent->devstate = AST_DEVICE_NOT_INUSE;
1285 agent_unlock(agent);
1286 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1287 agent_devstate_changed(agent->username);
1289 case AGENT_STATE_ON_CALL:
1290 case AGENT_STATE_CALL_WRAPUP:
1291 wrapup_time = agent->cfg->wrapup_time;
1292 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1293 wrapup_time = agent->override_wrapup_time;
1296 agent->state = AGENT_STATE_CALL_WRAPUP;
1298 agent->state = AGENT_STATE_READY_FOR_CALL;
1299 agent->devstate = AST_DEVICE_NOT_INUSE;
1301 agent_unlock(agent);
1303 /* No wrapup time. */
1304 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1305 agent_devstate_changed(agent->username);
1315 * \brief ast_bridge agent_hold pull method.
1317 * \param self Bridge to operate upon.
1318 * \param bridge_channel Bridge channel to pull.
1321 * Remove any channel hooks controlled by the bridge. Release
1322 * any resources held by bridge_channel->bridge_pvt and release
1323 * bridge_channel->bridge_pvt.
1325 * \note On entry, self is already locked.
1329 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1331 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1332 ast_bridge_base_v_table.pull(self, bridge_channel);
1336 * \brief The bridge is being dissolved.
1338 * \param self Bridge to operate upon.
1341 * The bridge is being dissolved. Remove any external
1342 * references to the bridge so it can be destroyed.
1344 * \note On entry, self must NOT be locked.
1348 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1350 ao2_global_obj_replace_unref(agent_holding, NULL);
1351 ast_bridge_base_v_table.dissolving(self);
1354 static struct ast_bridge_methods bridge_agent_hold_v_table;
1356 static struct ast_bridge *bridge_agent_hold_new(void)
1358 struct ast_bridge *bridge;
1360 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1361 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1362 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1363 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1364 bridge = bridge_register(bridge);
1368 static void bridge_init_agent_hold(void)
1370 /* Setup bridge agent_hold subclass v_table. */
1371 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1372 bridge_agent_hold_v_table.name = "agent_hold";
1373 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1374 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1375 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1378 static int bridge_agent_hold_deferred_create(void)
1380 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1383 ast_mutex_lock(&agent_holding_lock);
1384 holding = ao2_global_obj_ref(agent_holding);
1386 holding = bridge_agent_hold_new();
1387 ao2_global_obj_replace_unref(agent_holding, holding);
1389 ast_mutex_unlock(&agent_holding_lock);
1391 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1398 static void send_agent_login(struct ast_channel *chan, const char *agent)
1400 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1402 ast_assert(agent != NULL);
1404 blob = ast_json_pack("{s: s}",
1410 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1413 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1415 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1417 ast_assert(agent != NULL);
1419 blob = ast_json_pack("{s: s, s: i}",
1421 "logintime", logintime);
1426 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1431 * \brief Logout the agent.
1434 * \param agent Which agent logging out.
1436 * \note On entry agent is already locked. On exit it is no longer locked.
1440 static void agent_logout(struct agent_pvt *agent)
1442 struct ast_channel *logged;
1443 struct ast_bridge *caller_bridge;
1444 long time_logged_in;
1446 time_logged_in = time(NULL) - agent->login_start;
1447 logged = agent->logged;
1448 agent->logged = NULL;
1449 caller_bridge = agent->caller_bridge;
1450 agent->caller_bridge = NULL;
1451 agent->state = AGENT_STATE_LOGGED_OUT;
1452 agent->devstate = AST_DEVICE_UNAVAILABLE;
1453 ast_clear_flag(agent, AST_FLAGS_ALL);
1454 agent_unlock(agent);
1455 agent_devstate_changed(agent->username);
1457 if (caller_bridge) {
1458 ast_bridge_destroy(caller_bridge);
1461 send_agent_logoff(logged, agent->username, time_logged_in);
1462 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1463 agent->username, time_logged_in);
1464 ast_channel_unref(logged);
1469 * \brief Agent driver loop.
1472 * \param agent Which agent.
1473 * \param logged The logged in channel.
1477 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1479 struct ast_bridge_features features;
1481 if (ast_bridge_features_init(&features)) {
1482 goto agent_run_cleanup;
1485 struct agents_cfg *cfgs;
1486 struct agent_cfg *cfg_new;
1487 struct agent_cfg *cfg_old;
1488 struct ast_bridge *holding;
1489 struct ast_bridge *caller_bridge;
1491 holding = ao2_global_obj_ref(agent_holding);
1493 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1499 * When the agent channel leaves the bridging system we usually
1500 * want to put the agent back into the holding bridge for the
1503 ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
1504 if (logged != agent->logged) {
1505 /* This channel is no longer the logged in agent. */
1510 /* The agent is no longer configured. */
1514 /* Update the agent's config before rejoining the holding bridge. */
1515 cfgs = ao2_global_obj_ref(cfg_handle);
1517 /* There is no agent configuration. All agents were destroyed. */
1520 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1523 /* The agent is no longer configured. */
1527 cfg_old = agent->cfg;
1528 agent->cfg = cfg_new;
1530 agent->last_disconnect = ast_tvnow();
1532 /* Clear out any caller bridge before rejoining the holding bridge. */
1533 caller_bridge = agent->caller_bridge;
1534 agent->caller_bridge = NULL;
1535 agent_unlock(agent);
1536 ao2_ref(cfg_old, -1);
1537 if (caller_bridge) {
1538 ast_bridge_destroy(caller_bridge);
1541 if (agent->state == AGENT_STATE_LOGGING_OUT
1542 || agent->deferred_logoff
1543 || ast_check_hangup_locked(logged)) {
1544 /* The agent was requested to logout or hungup. */
1549 * It is safe to access agent->waiting_colp without a lock. It
1550 * is only setup on agent login and not changed.
1552 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1554 ast_bridge_features_cleanup(&features);
1558 if (logged != agent->logged) {
1560 * We are no longer the agent channel because of local channel
1563 agent_unlock(agent);
1564 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1565 agent->username, ast_channel_name(logged));
1568 agent_logout(agent);
1571 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1573 struct agent_pvt *agent;
1575 agent = ao2_find(agents, chan, 0);
1580 ast_debug(1, "Agent %s: New agent channel %s.\n",
1581 agent->username, ast_channel_name(chan));
1582 agent_run(agent, chan);
1586 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1588 struct ast_channel *chan = data;
1589 struct agent_pvt *agent;
1591 agent = ao2_find(agents, chan, 0);
1595 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1596 agent->username, ast_channel_name(chan),
1597 ast_bridge_after_cb_reason_string(reason));
1599 agent_logout(agent);
1605 * \brief Get the lock on the agent bridge_channel and return it.
1608 * \param agent Whose bridge_channel to get.
1610 * \retval bridge_channel on success (Reffed and locked).
1611 * \retval NULL on error.
1613 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1615 struct ast_channel *logged;
1616 struct ast_bridge_channel *bc;
1620 logged = agent->logged;
1622 agent_unlock(agent);
1625 ast_channel_ref(logged);
1626 agent_unlock(agent);
1628 ast_channel_lock(logged);
1629 bc = ast_channel_get_bridge_channel(logged);
1630 ast_channel_unlock(logged);
1631 ast_channel_unref(logged);
1633 if (agent->logged != logged) {
1639 ast_bridge_channel_lock(bc);
1640 if (bc->chan != logged || agent->logged != logged) {
1641 ast_bridge_channel_unlock(bc);
1649 static void caller_abort_agent(struct agent_pvt *agent)
1651 struct ast_bridge_channel *logged;
1653 logged = agent_bridge_channel_get_lock(agent);
1655 struct ast_bridge *caller_bridge;
1657 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1660 caller_bridge = agent->caller_bridge;
1661 agent->caller_bridge = NULL;
1662 agent_unlock(agent);
1663 if (caller_bridge) {
1664 ast_bridge_destroy(caller_bridge);
1669 /* Kick the agent out of the holding bridge to reset it. */
1670 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END);
1671 ast_bridge_channel_unlock(logged);
1674 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1676 struct agent_pvt *agent = hook_pvt;
1678 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1679 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1680 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1681 caller_abort_agent(agent);
1687 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1689 const char *agent_id = payload;
1690 const char *playfile;
1691 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1693 agent = ao2_find(agents, agent_id, OBJ_KEY);
1695 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1699 /* Alert the agent. */
1701 playfile = ast_strdupa(agent->cfg->beep_sound);
1702 agent_unlock(agent);
1703 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1706 switch (agent->state) {
1707 case AGENT_STATE_CALL_PRESENT:
1708 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1709 ? agent->override_ack_call : agent->cfg->ack_call) {
1710 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1711 agent->ack_time = ast_tvnow();
1715 /* Connect to caller now. */
1716 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1717 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1722 agent_unlock(agent);
1725 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1727 return ast_bridge_channel_queue_callback(bridge_channel, agent_alert, agent_id,
1728 strlen(agent_id) + 1);
1731 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1733 struct ast_set_party_connected_line update = {
1738 unsigned char data[1024]; /* This should be large enough */
1741 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1742 if (datalen == (size_t) -1) {
1746 return ast_bridge_channel_queue_control_data(bridge_channel,
1747 AST_CONTROL_CONNECTED_LINE, data, datalen);
1751 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1753 * \param chan Channel wanting to talk with an agent.
1754 * \param data Application parameters
1756 * \retval 0 To continue in dialplan.
1757 * \retval -1 To hangup.
1759 static int agent_request_exec(struct ast_channel *chan, const char *data)
1761 struct ast_bridge *caller_bridge;
1762 struct ast_bridge_channel *logged;
1765 struct ast_bridge_features caller_features;
1766 struct ast_party_connected_line connected;
1767 AST_DECLARE_APP_ARGS(args,
1768 AST_APP_ARG(agent_id);
1769 AST_APP_ARG(other); /* Any remaining unused arguments */
1772 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1774 if (bridge_agent_hold_deferred_create()) {
1778 parse = ast_strdupa(data);
1779 AST_STANDARD_APP_ARGS(args, parse);
1781 if (ast_strlen_zero(args.agent_id)) {
1782 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1786 /* Find the agent. */
1787 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1789 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1790 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1794 if (ast_bridge_features_init(&caller_features)) {
1798 /* Add safety timeout hook. */
1800 if (ast_bridge_interval_hook(&caller_features, CALLER_SAFETY_TIMEOUT_TIME,
1801 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1803 ast_bridge_features_cleanup(&caller_features);
1807 caller_bridge = ast_bridge_basic_new();
1808 if (!caller_bridge) {
1809 ast_bridge_features_cleanup(&caller_features);
1813 /* Get COLP for agent. */
1814 ast_party_connected_line_init(&connected);
1815 ast_channel_lock(chan);
1816 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1817 ast_channel_unlock(chan);
1820 switch (agent->state) {
1821 case AGENT_STATE_LOGGED_OUT:
1822 case AGENT_STATE_LOGGING_OUT:
1823 agent_unlock(agent);
1824 ast_party_connected_line_free(&connected);
1825 ast_bridge_destroy(caller_bridge);
1826 ast_bridge_features_cleanup(&caller_features);
1827 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1828 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1830 case AGENT_STATE_READY_FOR_CALL:
1831 ao2_ref(caller_bridge, +1);
1832 agent->caller_bridge = caller_bridge;
1833 agent->state = AGENT_STATE_CALL_PRESENT;
1834 agent->devstate = AST_DEVICE_INUSE;
1837 agent_unlock(agent);
1838 ast_party_connected_line_free(&connected);
1839 ast_bridge_destroy(caller_bridge);
1840 ast_bridge_features_cleanup(&caller_features);
1841 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1842 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1845 agent_unlock(agent);
1846 agent_devstate_changed(agent->username);
1848 logged = agent_bridge_channel_get_lock(agent);
1850 ast_party_connected_line_free(&connected);
1851 ast_bridge_destroy(caller_bridge);
1852 ast_bridge_features_cleanup(&caller_features);
1853 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1854 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1855 caller_abort_agent(agent);
1859 send_colp_to_agent(logged, &connected);
1860 ast_party_connected_line_free(&connected);
1862 res = send_alert_to_agent(logged, agent->username);
1863 ast_bridge_channel_unlock(logged);
1864 ao2_ref(logged, -1);
1866 ast_bridge_destroy(caller_bridge);
1867 ast_bridge_features_cleanup(&caller_features);
1868 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1869 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1870 caller_abort_agent(agent);
1874 ast_indicate(chan, AST_CONTROL_RINGING);
1875 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1876 ast_bridge_features_cleanup(&caller_features);
1883 * \brief Get agent config values from the login channel.
1886 * \param agent What to setup channel config values on.
1887 * \param chan Channel logging in as an agent.
1891 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1893 struct ast_flags opts = { 0 };
1894 struct ast_party_connected_line connected;
1895 unsigned int override_ack_call = 0;
1896 unsigned int override_auto_logoff = 0;
1897 unsigned int override_wrapup_time = 0;
1898 const char *override_dtmf_accept = NULL;
1901 ast_party_connected_line_init(&connected);
1903 /* Get config values from channel. */
1904 ast_channel_lock(chan);
1905 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1907 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1908 if (!ast_strlen_zero(var)) {
1909 override_ack_call = ast_true(var) ? 1 : 0;
1910 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1913 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1914 if (!ast_strlen_zero(var)) {
1915 override_dtmf_accept = ast_strdupa(var);
1916 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1919 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1920 if (!ast_strlen_zero(var)) {
1921 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1922 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1926 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1927 if (!ast_strlen_zero(var)) {
1928 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1929 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1932 ast_channel_unlock(chan);
1934 /* Set config values on agent. */
1936 ast_party_connected_line_free(&agent->waiting_colp);
1937 agent->waiting_colp = connected;
1939 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1940 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1941 agent->override_auto_logoff = override_auto_logoff;
1942 agent->override_wrapup_time = override_wrapup_time;
1943 agent->override_ack_call = override_ack_call;
1944 agent_unlock(agent);
1947 enum AGENT_LOGIN_OPT_FLAGS {
1948 OPT_SILENT = (1 << 0),
1950 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1951 AST_APP_OPTION('s', OPT_SILENT),
1955 * \brief Dialplan AgentLogin application to log in an agent.
1957 * \param chan Channel attempting to login as an agent.
1958 * \param data Application parameters
1960 * \retval 0 To continue in dialplan.
1961 * \retval -1 To hangup.
1963 static int agent_login_exec(struct ast_channel *chan, const char *data)
1966 struct ast_flags opts;
1967 AST_DECLARE_APP_ARGS(args,
1968 AST_APP_ARG(agent_id);
1969 AST_APP_ARG(options);
1970 AST_APP_ARG(other); /* Any remaining unused arguments */
1973 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1975 if (bridge_agent_hold_deferred_create()) {
1979 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
1983 parse = ast_strdupa(data);
1984 AST_STANDARD_APP_ARGS(args, parse);
1986 if (ast_strlen_zero(args.agent_id)) {
1987 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
1991 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
1992 /* General invalid option syntax. */
1996 /* Find the agent. */
1997 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1999 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2000 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2004 /* Has someone already logged in as this agent already? */
2006 if (agent->logged) {
2007 agent_unlock(agent);
2008 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2009 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2012 agent->logged = ast_channel_ref(chan);
2013 agent->last_disconnect = ast_tvnow();
2014 time(&agent->login_start);
2015 agent_unlock(agent);
2017 agent_login_channel_config(agent, chan);
2019 if (!ast_test_flag(&opts, OPT_SILENT)
2020 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2021 ast_waitstream(chan, "");
2024 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2025 ast_getformatname(ast_channel_readformat(chan)),
2026 ast_getformatname(ast_channel_writeformat(chan)));
2027 send_agent_login(chan, agent->username);
2029 agent_run(agent, chan);
2033 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2036 struct agent_pvt *agent;
2037 struct ast_channel *logged;
2038 AST_DECLARE_APP_ARGS(args,
2039 AST_APP_ARG(agentid);
2045 parse = ast_strdupa(data ?: "");
2046 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2048 if (ast_strlen_zero(args.agentid)) {
2049 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2053 args.item = "status";
2056 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2058 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2063 if (!strcasecmp(args.item, "status")) {
2066 if (agent->logged) {
2067 status = "LOGGEDIN";
2069 status = "LOGGEDOUT";
2071 ast_copy_string(buf, status, len);
2072 } else if (!strcasecmp(args.item, "name")) {
2073 ast_copy_string(buf, agent->cfg->full_name, len);
2074 } else if (!strcasecmp(args.item, "mohclass")) {
2075 ast_copy_string(buf, agent->cfg->moh, len);
2076 } else if (!strcasecmp(args.item, "channel")) {
2077 logged = agent_lock_logged(agent);
2081 ast_copy_string(buf, ast_channel_name(logged), len);
2082 ast_channel_unlock(logged);
2083 ast_channel_unref(logged);
2085 pos = strrchr(buf, '-');
2090 } else if (!strcasecmp(args.item, "fullchannel")) {
2091 logged = agent_lock_logged(agent);
2093 ast_copy_string(buf, ast_channel_name(logged), len);
2094 ast_channel_unlock(logged);
2095 ast_channel_unref(logged);
2098 agent_unlock(agent);
2104 static struct ast_custom_function agent_function = {
2106 .read = agent_function_read,
2109 struct agent_complete {
2110 /*! Nth match to return. */
2112 /*! Which match currently on. */
2116 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2118 struct agent_complete *search = data;
2120 if (++search->which > search->state) {
2126 static char *complete_agent(const char *word, int state)
2129 struct agent_pvt *agent;
2130 struct agent_complete search = {
2134 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2135 complete_agent_search, (char *) word, &search);
2139 ret = ast_strdup(agent->username);
2144 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2146 struct agent_pvt *agent = obj;
2147 struct agent_complete *search = data;
2149 if (!agent->logged) {
2152 if (++search->which > search->state) {
2158 static char *complete_agent_logoff(const char *word, int state)
2161 struct agent_pvt *agent;
2162 struct agent_complete search = {
2166 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2167 complete_agent_logoff_search, (char *) word, &search);
2171 ret = ast_strdup(agent->username);
2176 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2178 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2179 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2181 struct ao2_iterator iter;
2182 struct agent_pvt *agent;
2183 struct ast_str *out = ast_str_alloca(512);
2184 unsigned int agents_total = 0;
2185 unsigned int agents_logged_in = 0;
2186 unsigned int agents_talking = 0;
2188 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2189 iter = ao2_iterator_init(agents, 0);
2190 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2191 struct ast_channel *logged;
2196 logged = agent_lock_logged(agent);
2198 const char *talking_with;
2202 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2203 if (!ast_strlen_zero(talking_with)) {
2208 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2209 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2210 ast_channel_unlock(logged);
2211 ast_channel_unref(logged);
2213 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2214 ast_devstate_str(agent->devstate), "", "");
2216 agent_unlock(agent);
2218 if (!online_only || logged) {
2219 ast_cli(a->fd, "%s", ast_str_buffer(out));
2222 ao2_iterator_destroy(&iter);
2224 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2225 agents_total, agents_logged_in, agents_talking);
2231 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2235 e->command = "agent show online";
2237 "Usage: agent show online\n"
2238 " Provides summary information for logged in agents.\n";
2245 return CLI_SHOWUSAGE;
2248 agent_show_requested(a, 1);
2253 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2257 e->command = "agent show all";
2259 "Usage: agent show all\n"
2260 " Provides summary information for all agents.\n";
2267 return CLI_SHOWUSAGE;
2270 agent_show_requested(a, 0);
2275 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2277 struct agent_pvt *agent;
2278 struct ast_channel *logged;
2279 struct ast_str *out = ast_str_alloca(4096);
2283 e->command = "agent show";
2285 "Usage: agent show <agent-id>\n"
2286 " Show information about the <agent-id> agent\n";
2290 return complete_agent(a->word, a->n);
2296 return CLI_SHOWUSAGE;
2299 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2301 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2306 logged = agent_lock_logged(agent);
2307 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2308 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2309 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2310 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2311 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2312 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2314 const char *talking_with;
2316 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2317 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2318 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2319 if (!ast_strlen_zero(talking_with)) {
2320 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2321 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2323 ast_channel_unlock(logged);
2324 ast_channel_unref(logged);
2326 agent_unlock(agent);
2329 ast_cli(a->fd, "%s", ast_str_buffer(out));
2334 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2338 e->command = "agent logoff";
2340 "Usage: agent logoff <agent-id> [soft]\n"
2341 " Sets an agent as no longer logged in.\n"
2342 " If 'soft' is specified, do not hangup existing calls.\n";
2346 return complete_agent_logoff(a->word, a->n);
2347 } else if (a->pos == 3 && a->n == 0
2348 && (ast_strlen_zero(a->word)
2349 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2350 return ast_strdup("soft");
2355 if (a->argc < 3 || 4 < a->argc) {
2356 return CLI_SHOWUSAGE;
2358 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2359 return CLI_SHOWUSAGE;
2362 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2363 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2369 static struct ast_cli_entry cli_agents[] = {
2370 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2371 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2372 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2373 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2376 static int action_agents(struct mansession *s, const struct message *m)
2378 const char *id = astman_get_header(m, "ActionID");
2379 char id_text[AST_MAX_BUF];
2380 struct ao2_iterator iter;
2381 struct agent_pvt *agent;
2382 struct ast_str *out = ast_str_alloca(4096);
2384 if (!ast_strlen_zero(id)) {
2385 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2389 astman_send_ack(s, m, "Agents will follow");
2391 iter = ao2_iterator_init(agents, 0);
2392 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2393 struct ast_channel *logged;
2396 logged = agent_lock_logged(agent);
2400 * AGENT_LOGGEDOFF - Agent isn't logged in
2401 * AGENT_IDLE - Agent is logged in, and waiting for call
2402 * AGENT_ONCALL - Agent is logged in, and on a call
2403 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2405 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2406 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2409 const char *talking_to_chan;
2410 struct ast_str *logged_headers;
2411 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2413 if (!logged_snapshot
2414 || !(logged_headers =
2415 ast_manager_build_channel_state_string(logged_snapshot))) {
2416 ast_channel_unlock(logged);
2417 ast_channel_unref(logged);
2418 agent_unlock(agent);
2422 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2423 if (!ast_strlen_zero(talking_to_chan)) {
2424 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2425 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2426 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2428 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2430 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2431 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2432 ast_channel_unlock(logged);
2433 ast_channel_unref(logged);
2434 ast_free(logged_headers);
2436 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2439 agent_unlock(agent);
2441 astman_append(s, "Event: Agents\r\n"
2443 ast_str_buffer(out), id_text);
2445 ao2_iterator_destroy(&iter);
2447 astman_append(s, "Event: AgentsComplete\r\n"
2453 static int action_agent_logoff(struct mansession *s, const struct message *m)
2455 const char *agent = astman_get_header(m, "Agent");
2456 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2458 if (ast_strlen_zero(agent)) {
2459 astman_send_error(s, m, "No agent specified");
2463 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2464 astman_send_ack(s, m, "Agent logged out");
2466 astman_send_error(s, m, "No such agent");
2472 static int unload_module(void)
2474 struct ast_bridge *holding;
2476 /* Unregister dialplan applications */
2477 ast_unregister_application(app_agent_login);
2478 ast_unregister_application(app_agent_request);
2480 /* Unregister dialplan functions */
2481 ast_custom_function_unregister(&agent_function);
2483 /* Unregister manager command */
2484 ast_manager_unregister("Agents");
2485 ast_manager_unregister("AgentLogoff");
2487 /* Unregister CLI commands */
2488 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2490 ast_devstate_prov_del("Agent");
2492 /* Destroy agent holding bridge. */
2493 holding = ao2_global_obj_replace(agent_holding, NULL);
2495 ast_bridge_destroy(holding);
2499 ao2_ref(agents, -1);
2504 static int load_module(void)
2508 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2509 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2511 return AST_MODULE_LOAD_FAILURE;
2513 if (load_config()) {
2514 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2515 ao2_ref(agents, -1);
2517 return AST_MODULE_LOAD_DECLINE;
2520 /* Init agent holding bridge v_table. */
2521 bridge_init_agent_hold();
2523 /* Setup to provide Agent:agent-id device state. */
2524 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2527 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2529 /* Manager commands */
2530 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2531 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2533 /* Dialplan Functions */
2534 res |= ast_custom_function_register(&agent_function);
2536 /* Dialplan applications */
2537 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2538 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2542 return AST_MODULE_LOAD_FAILURE;
2544 return AST_MODULE_LOAD_SUCCESS;
2547 static int reload(void)
2549 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2550 /* Just keep the config we already have in place. */
2556 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2557 .load = load_module,
2558 .unload = unload_module,
2560 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,