Add BUGBUG note for ASTERISK-22009
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393631 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
c841c34aae
commit
b4e9a3fc2f
28
CHANGES
28
CHANGES
|
@ -14,10 +14,19 @@
|
||||||
Applications
|
Applications
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
AgentLogin
|
||||||
|
------------------
|
||||||
|
* The application no longer does agent authentication. The dialplan needs to
|
||||||
|
perform this function before running AgentLogin. If the agent is already
|
||||||
|
logged in, dialplan will continue with the AGENT_STATUS channel variable
|
||||||
|
set to ALREADY_LOGGED_IN.
|
||||||
|
|
||||||
AgentMonitorOutgoing
|
AgentMonitorOutgoing
|
||||||
------------------
|
------------------
|
||||||
* The 'c' option has been removed. It is not possible to modify the name of a
|
* The 'c' option has been removed. It is not possible to modify the name of a
|
||||||
channel involved in a CDR.
|
channel involved in a CDR.
|
||||||
|
* Application removed. It was a holdover from when AgentCallbackLogin was
|
||||||
|
removed.
|
||||||
|
|
||||||
ForkCDR
|
ForkCDR
|
||||||
------------------
|
------------------
|
||||||
|
@ -244,8 +253,8 @@ AMI (Asterisk Manager Interface)
|
||||||
of "CallerID" and "ConnectedID" to avoid confusion with similarly named
|
of "CallerID" and "ConnectedID" to avoid confusion with similarly named
|
||||||
parameters in the channel snapshot.
|
parameters in the channel snapshot.
|
||||||
|
|
||||||
* The "Agentlogin" and "Agentlogoff" events have been renamed "AgentLogin" and
|
* The AMI events "Agentlogin" and "Agentlogoff" have been renamed
|
||||||
"AgentLogoff" respectively.
|
"AgentLogin" and "AgentLogoff" respectively.
|
||||||
|
|
||||||
* The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been
|
* The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been
|
||||||
renamed "DAHDIChannel" since it does not convey an Asterisk channel name.
|
renamed "DAHDIChannel" since it does not convey an Asterisk channel name.
|
||||||
|
@ -423,6 +432,21 @@ chan_agent
|
||||||
and pretending otherwise helps no one.
|
and pretending otherwise helps no one.
|
||||||
* The AGENTUPDATECDR channel variable has also been removed, for the same
|
* The AGENTUPDATECDR channel variable has also been removed, for the same
|
||||||
reason as the updatecdr option.
|
reason as the updatecdr option.
|
||||||
|
* The driver is no longer a Data retrieval API data provider for the
|
||||||
|
AMI DataGet action.
|
||||||
|
* The endcall and enddtmf configuration options are removed. Use the
|
||||||
|
dialplan function CHANNEL(dtmf-features) to set DTMF features on the agent
|
||||||
|
channel before calling AgentLogin.
|
||||||
|
* chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
|
||||||
|
applications. Agents are connected with callers using the new AgentRequest
|
||||||
|
dialplan application. The Agents:<agent-id> device state is available to
|
||||||
|
monitor the status of an agent. See agents.conf.sample for valid
|
||||||
|
configuration options.
|
||||||
|
|
||||||
|
chan_bridge
|
||||||
|
------------------
|
||||||
|
* chan_bridge is removed and its functionality is incorporated into ConfBridge
|
||||||
|
itself.
|
||||||
|
|
||||||
chan_local
|
chan_local
|
||||||
------------------
|
------------------
|
||||||
|
|
11
UPGRADE.txt
11
UPGRADE.txt
|
@ -24,6 +24,8 @@
|
||||||
AgentMonitorOutgoing
|
AgentMonitorOutgoing
|
||||||
- The 'c' option has been removed. It is not possible to modify the name of a
|
- The 'c' option has been removed. It is not possible to modify the name of a
|
||||||
channel involved in a CDR.
|
channel involved in a CDR.
|
||||||
|
- Application removed. It was a holdover from when AgentCallbackLogin was
|
||||||
|
removed.
|
||||||
|
|
||||||
NoCDR:
|
NoCDR:
|
||||||
- This application is deprecated. Please use the CDR_PROP function instead.
|
- This application is deprecated. Please use the CDR_PROP function instead.
|
||||||
|
@ -124,6 +126,15 @@ chan_agent:
|
||||||
and pretending otherwise helps no one.
|
and pretending otherwise helps no one.
|
||||||
- The AGENTUPDATECDR channel variable has also been removed, for the same
|
- The AGENTUPDATECDR channel variable has also been removed, for the same
|
||||||
reason as the updatecdr option.
|
reason as the updatecdr option.
|
||||||
|
- chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
|
||||||
|
applications. Agents are connected with callers using the new AgentRequest
|
||||||
|
dialplan application. The Agents:<agent-id> device state is available to
|
||||||
|
monitor the status of an agent. See agents.conf.sample for valid
|
||||||
|
configuration options.
|
||||||
|
|
||||||
|
chan_bridge
|
||||||
|
- chan_bridge is removed and its functionality is incorporated into ConfBridge
|
||||||
|
itself.
|
||||||
|
|
||||||
chan_dahdi:
|
chan_dahdi:
|
||||||
- Analog port dialing and deferred DTMF dialing for PRI now distinguishes
|
- Analog port dialing and deferred DTMF dialing for PRI now distinguishes
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +1,68 @@
|
||||||
;
|
;
|
||||||
; Agent configuration
|
; Agent pool configuration
|
||||||
;
|
;
|
||||||
|
|
||||||
[general]
|
[general]
|
||||||
|
; The general section of this config is not currently used, but reserved
|
||||||
|
; for future use.
|
||||||
|
|
||||||
[agents]
|
;[agent-id]
|
||||||
|
; Define ackcall to require the agent to give a DTMF acknowledgement
|
||||||
|
; when the agent receives a call.
|
||||||
|
; The channel variable AGENTACKCALL overrides on agent login.
|
||||||
|
; Default is "no".
|
||||||
|
;ackcall=no
|
||||||
;
|
;
|
||||||
; Define maxlogintries to allow agent to try max logins before
|
; Set what DTMF key sequence the agent should use to acknowledge a call.
|
||||||
; failed.
|
; The channel variable AGENTACCEPTDTMF overrides on agent login.
|
||||||
; default to 3
|
; Default is "#".
|
||||||
;
|
;acceptdtmf=##
|
||||||
;maxlogintries=5
|
|
||||||
;
|
|
||||||
;
|
|
||||||
; Define autologoff times if appropriate. This is how long
|
|
||||||
; the phone has to ring with no answer before the agent is
|
|
||||||
; automatically logged off (in seconds)
|
|
||||||
;
|
;
|
||||||
|
; Set how many seconds a call for the agent has to wait for the agent to
|
||||||
|
; acknowledge the call before the agent is automatically logged off. If
|
||||||
|
; set to zero then the call will wait forever for the agent to acknowledge.
|
||||||
|
; The channel variable AGENTAUTOLOGOFF overrides on agent login.
|
||||||
|
; Default is 0.
|
||||||
;autologoff=15
|
;autologoff=15
|
||||||
;
|
;
|
||||||
; Define autologoffunavail to have agents automatically logged
|
; Set the minimum amount of time after disconnecting a call before
|
||||||
; out when the extension that they are at returns a CHANUNAVAIL
|
; the agent can receive a new call in milliseconds.
|
||||||
; status when a call is attempted to be sent there.
|
; The channel variable AGENTWRAPUPTIME overrides on agent login.
|
||||||
; Default is "no".
|
; Default is 0.
|
||||||
;
|
|
||||||
;autologoffunavail=yes
|
|
||||||
;
|
|
||||||
; Define ackcall to require a DTMF acknowledgement when
|
|
||||||
; a logged-in agent receives a call. Default is "no".
|
|
||||||
; Use the acceptdtmf option to configure what DTMF key
|
|
||||||
; press should be used to acknowledge the call. The
|
|
||||||
; default is '#'.
|
|
||||||
;
|
|
||||||
;ackcall=no
|
|
||||||
;acceptdtmf=#
|
|
||||||
;
|
|
||||||
; Define endcall to allow an agent to hangup a call with a
|
|
||||||
; DTMF keypress. Default is "yes". Use the enddtmf option to
|
|
||||||
; configure which DTMF key will end a call. The default is
|
|
||||||
; '*'.
|
|
||||||
;
|
|
||||||
;endcall=yes
|
|
||||||
;enddtmf=*
|
|
||||||
;
|
|
||||||
; Define wrapuptime. This is the minimum amount of time when
|
|
||||||
; after disconnecting before the caller can receive a new call
|
|
||||||
; note this is in milliseconds.
|
|
||||||
;
|
|
||||||
;wrapuptime=5000
|
;wrapuptime=5000
|
||||||
;
|
;
|
||||||
; Define the default musiconhold for agents
|
; Set the musiconhold class for the agent.
|
||||||
; musiconhold => music_class
|
; Default is "default".
|
||||||
|
;musiconhold=default
|
||||||
;
|
;
|
||||||
;musiconhold => default
|
; Enable recording calls the agent takes automatically by invoking the
|
||||||
;
|
; DTMF automixmon feature when the agent connects to a caller.
|
||||||
; Define the default good bye sound file for agents
|
; See features.conf.sample for information about the automixmon feature.
|
||||||
; default to vm-goodbye
|
; Default is "no".
|
||||||
;
|
|
||||||
;goodbye => goodbye_file
|
|
||||||
;
|
|
||||||
; Define updatecdr. This is whether or not to change the source
|
|
||||||
; channel in the CDR record for this call to agent/agent_id so
|
|
||||||
; that we know which agent generates the call
|
|
||||||
;
|
|
||||||
;updatecdr=no
|
|
||||||
;
|
|
||||||
; Group memberships for agents (may change in mid-file)
|
|
||||||
;
|
|
||||||
;group=3
|
|
||||||
;group=1,2
|
|
||||||
;group=
|
|
||||||
;
|
|
||||||
; --------------------------------------------------
|
|
||||||
; This section is devoted to recording agent's calls
|
|
||||||
; The keywords are global to the chan_agent channel driver
|
|
||||||
;
|
|
||||||
; Enable recording calls addressed to agents. It's turned off by default.
|
|
||||||
;recordagentcalls=yes
|
;recordagentcalls=yes
|
||||||
;
|
;
|
||||||
; The format to be used to record the calls: wav, gsm, wav49.
|
; The sound file played to alert the agent when a call is present.
|
||||||
; By default its "wav".
|
; Default is "beep".
|
||||||
;recordformat=gsm
|
|
||||||
;
|
|
||||||
; The text to be added to the name of the recording. Allows forming a url link.
|
|
||||||
;urlprefix=http://localhost/calls/
|
|
||||||
;
|
|
||||||
; The optional directory to save the conversations in. The default is
|
|
||||||
; /var/spool/asterisk/monitor
|
|
||||||
;savecallsin=/var/calls
|
|
||||||
;
|
|
||||||
; An optional custom beep sound file to play to always-connected agents.
|
|
||||||
;custom_beep=beep
|
;custom_beep=beep
|
||||||
;
|
;
|
||||||
|
; A friendly name for the agent used in log messages.
|
||||||
|
; Default is "".
|
||||||
|
;fullname=Mark Spencer
|
||||||
|
;
|
||||||
; --------------------------------------------------
|
; --------------------------------------------------
|
||||||
;
|
;
|
||||||
; This section contains the agent definitions, in the form:
|
; This section contains example agent definitions:
|
||||||
;
|
;
|
||||||
; agent => agentid,agentpassword,name
|
; Define a template called my-agents:
|
||||||
|
;[my-agents](!)
|
||||||
|
;autologoff=15
|
||||||
|
;ackcall=yes
|
||||||
|
;acceptdtmf=##
|
||||||
;
|
;
|
||||||
;agent => 1001,4321,Mark Spencer
|
; Define agent 1001 using the my-agents template:
|
||||||
;agent => 1002,4321,Will Meadows
|
;[1001](my-agents)
|
||||||
|
;fullname=Mark Spencer
|
||||||
|
;
|
||||||
|
; Define agent 1002 using the my-agents template:
|
||||||
|
;[1002](my-agents)
|
||||||
|
;fullname=Will Meadows
|
||||||
|
|
|
@ -543,18 +543,7 @@ monitor-type = MixMonitor
|
||||||
;member => DAHDI/1
|
;member => DAHDI/1
|
||||||
;member => DAHDI/2,10
|
;member => DAHDI/2,10
|
||||||
;member => DAHDI/3,10,Bob Johnson
|
;member => DAHDI/3,10,Bob Johnson
|
||||||
;member => Agent/1001
|
;member => Local/1001@agents,0,May Flowers,Agent:1001
|
||||||
;member => Agent/1002
|
;member => Local/1002@agents,0,John Doe,Agent:1002
|
||||||
;member => Local/1000@default,0,John Smith,SIP/1000
|
;member => Local/1000@default,0,John Smith,SIP/1000
|
||||||
;member => Local/2000@default,0,Lorem Ipsum,SIP/2000,no
|
;member => Local/2000@default,0,Lorem Ipsum,SIP/2000,no
|
||||||
|
|
||||||
;
|
|
||||||
; Note that using agent groups is probably not what you want. Strategies do
|
|
||||||
; not propagate down to the Agent system so if you want round robin, least
|
|
||||||
; recent, etc, you should list all the agents in this file individually and not
|
|
||||||
; use agent groups.
|
|
||||||
;
|
|
||||||
;member => Agent/@1 ; Any agent in group 1
|
|
||||||
;member => Agent/:1,1 ; Any agent in group 1, wait for first
|
|
||||||
; available, but consider with penalty
|
|
||||||
|
|
||||||
|
|
|
@ -1647,7 +1647,7 @@ void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t b
|
||||||
|
|
||||||
/*! Reason the the after bridge callback will not be called. */
|
/*! Reason the the after bridge callback will not be called. */
|
||||||
enum ast_after_bridge_cb_reason {
|
enum ast_after_bridge_cb_reason {
|
||||||
/*! The datastore is being destroyed. Likely due to hangup. */
|
/*! The datastore is being destroyed. Likely due to hangup. (Enum value must be zero.) */
|
||||||
AST_AFTER_BRIDGE_CB_REASON_DESTROY,
|
AST_AFTER_BRIDGE_CB_REASON_DESTROY,
|
||||||
/*! Something else replaced the callback with another. */
|
/*! Something else replaced the callback with another. */
|
||||||
AST_AFTER_BRIDGE_CB_REASON_REPLACED,
|
AST_AFTER_BRIDGE_CB_REASON_REPLACED,
|
||||||
|
@ -1666,6 +1666,9 @@ enum ast_after_bridge_cb_reason {
|
||||||
* \param reason Reason callback is failing.
|
* \param reason Reason callback is failing.
|
||||||
* \param data Extra data what setup the callback wanted to pass.
|
* \param data Extra data what setup the callback wanted to pass.
|
||||||
*
|
*
|
||||||
|
* \note Called when the channel leaves the bridging system or
|
||||||
|
* is destroyed.
|
||||||
|
*
|
||||||
* \return Nothing
|
* \return Nothing
|
||||||
*/
|
*/
|
||||||
typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
|
typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
|
||||||
|
@ -1705,6 +1708,9 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
|
||||||
*
|
*
|
||||||
* \note chan is locked by this function.
|
* \note chan is locked by this function.
|
||||||
*
|
*
|
||||||
|
* \note failed is called when the channel leaves the bridging
|
||||||
|
* system or is destroyed.
|
||||||
|
*
|
||||||
* \retval 0 on success.
|
* \retval 0 on success.
|
||||||
* \retval -1 on error.
|
* \retval -1 on error.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -461,7 +461,7 @@ enum aco_process_status {
|
||||||
/*! \brief Process a config info via the options registered with an aco_info
|
/*! \brief Process a config info via the options registered with an aco_info
|
||||||
*
|
*
|
||||||
* \param info The config_options_info to be used for handling the config
|
* \param info The config_options_info to be used for handling the config
|
||||||
* \param reload Whether or not this is a reload
|
* \param reload Non-zero if this is for a reload.
|
||||||
*
|
*
|
||||||
* \retval ACO_PROCESS_OK Success
|
* \retval ACO_PROCESS_OK Success
|
||||||
* \retval ACO_PROCESS_ERROR Failure
|
* \retval ACO_PROCESS_ERROR Failure
|
||||||
|
|
|
@ -421,6 +421,22 @@ struct stasis_message_type *ast_channel_monitor_start_type(void);
|
||||||
*/
|
*/
|
||||||
struct stasis_message_type *ast_channel_monitor_stop_type(void);
|
struct stasis_message_type *ast_channel_monitor_stop_type(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
|
* \brief Message type for agent login on a channel
|
||||||
|
*
|
||||||
|
* \retval A stasis message type
|
||||||
|
*/
|
||||||
|
struct stasis_message_type *ast_channel_agent_login_type(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
|
* \brief Message type for agent logoff on a channel
|
||||||
|
*
|
||||||
|
* \retval A stasis message type
|
||||||
|
*/
|
||||||
|
struct stasis_message_type *ast_channel_agent_logoff_type(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12
|
||||||
* \brief Message type for starting music on hold on a channel
|
* \brief Message type for starting music on hold on a channel
|
||||||
|
|
261
main/bridging.c
261
main/bridging.c
|
@ -3145,15 +3145,70 @@ static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge
|
||||||
return bridge_channel;
|
return bridge_channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct after_bridge_cb_ds {
|
struct after_bridge_cb_node {
|
||||||
|
/*! Next list node. */
|
||||||
|
AST_LIST_ENTRY(after_bridge_cb_node) list;
|
||||||
/*! Desired callback function. */
|
/*! Desired callback function. */
|
||||||
ast_after_bridge_cb callback;
|
ast_after_bridge_cb callback;
|
||||||
/*! After bridge callback will not be called and destroy any resources data may contain. */
|
/*! After bridge callback will not be called and destroy any resources data may contain. */
|
||||||
ast_after_bridge_cb_failed failed;
|
ast_after_bridge_cb_failed failed;
|
||||||
/*! Extra data to pass to the callback. */
|
/*! Extra data to pass to the callback. */
|
||||||
void *data;
|
void *data;
|
||||||
|
/*! Reason the after bridge callback failed. */
|
||||||
|
enum ast_after_bridge_cb_reason reason;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct after_bridge_cb_ds {
|
||||||
|
/*! After bridge callbacks container. */
|
||||||
|
AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Indicate after bridge callback failed.
|
||||||
|
* \since 12.0.0
|
||||||
|
*
|
||||||
|
* \param node After bridge callback node.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
|
||||||
|
{
|
||||||
|
if (node->failed) {
|
||||||
|
node->failed(node->reason, node->data);
|
||||||
|
node->failed = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Run discarding any after bridge callbacks.
|
||||||
|
* \since 12.0.0
|
||||||
|
*
|
||||||
|
* \param after_bridge After bridge callback container process.
|
||||||
|
* \param reason Why are we doing this.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
|
||||||
|
{
|
||||||
|
struct after_bridge_cb_node *node;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
AST_LIST_LOCK(&after_bridge->callbacks);
|
||||||
|
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
|
||||||
|
AST_LIST_UNLOCK(&after_bridge->callbacks);
|
||||||
|
if (!node) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!node->reason) {
|
||||||
|
node->reason = reason;
|
||||||
|
}
|
||||||
|
after_bridge_cb_failed(node);
|
||||||
|
ast_free(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Destroy the after bridge callback datastore.
|
* \brief Destroy the after bridge callback datastore.
|
||||||
|
@ -3167,10 +3222,9 @@ static void after_bridge_cb_destroy(void *data)
|
||||||
{
|
{
|
||||||
struct after_bridge_cb_ds *after_bridge = data;
|
struct after_bridge_cb_ds *after_bridge = data;
|
||||||
|
|
||||||
if (after_bridge->failed) {
|
after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
|
||||||
after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data);
|
|
||||||
after_bridge->failed = NULL;
|
AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
|
||||||
}
|
|
||||||
ast_free(after_bridge);
|
ast_free(after_bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3187,7 +3241,6 @@ static void after_bridge_cb_destroy(void *data)
|
||||||
*/
|
*/
|
||||||
static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
||||||
{
|
{
|
||||||
/* There can be only one. Discard any already on the new channel. */
|
|
||||||
ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
|
ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3199,47 +3252,67 @@ static const struct ast_datastore_info after_bridge_cb_info = {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Remove channel after the bridge callback and return it.
|
* \brief Setup/create an after bridge callback datastore container.
|
||||||
* \since 12.0.0
|
* \since 12.0.0
|
||||||
*
|
*
|
||||||
* \param chan Channel to remove after bridge callback.
|
* \param chan Channel to setup/create the after bridge callback container on.
|
||||||
*
|
*
|
||||||
* \retval datastore on success.
|
* \retval after_bridge datastore container on success.
|
||||||
* \retval NULL on error or not found.
|
* \retval NULL on error.
|
||||||
*/
|
*/
|
||||||
static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan)
|
static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
struct ast_datastore *datastore;
|
struct ast_datastore *datastore;
|
||||||
|
struct after_bridge_cb_ds *after_bridge;
|
||||||
|
SCOPED_CHANNELLOCK(lock, chan);
|
||||||
|
|
||||||
ast_channel_lock(chan);
|
|
||||||
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
|
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
|
||||||
if (datastore && ast_channel_datastore_remove(chan, datastore)) {
|
|
||||||
datastore = NULL;
|
|
||||||
}
|
|
||||||
ast_channel_unlock(chan);
|
|
||||||
|
|
||||||
return datastore;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
|
|
||||||
{
|
|
||||||
struct ast_datastore *datastore;
|
|
||||||
|
|
||||||
datastore = after_bridge_cb_remove(chan);
|
|
||||||
if (datastore) {
|
if (datastore) {
|
||||||
struct after_bridge_cb_ds *after_bridge = datastore->data;
|
return datastore->data;
|
||||||
|
|
||||||
if (after_bridge && after_bridge->failed) {
|
|
||||||
after_bridge->failed(reason, after_bridge->data);
|
|
||||||
after_bridge->failed = NULL;
|
|
||||||
}
|
|
||||||
ast_datastore_free(datastore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create a new datastore. */
|
||||||
|
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
|
||||||
|
if (!datastore) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
after_bridge = ast_calloc(1, sizeof(*after_bridge));
|
||||||
|
if (!after_bridge) {
|
||||||
|
ast_datastore_free(datastore);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
AST_LIST_HEAD_INIT(&after_bridge->callbacks);
|
||||||
|
datastore->data = after_bridge;
|
||||||
|
ast_channel_datastore_add(chan, datastore);
|
||||||
|
|
||||||
|
return datastore->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Run any after bridge callback if possible.
|
* \brief Find an after bridge callback datastore container.
|
||||||
|
* \since 12.0.0
|
||||||
|
*
|
||||||
|
* \param chan Channel to find the after bridge callback container on.
|
||||||
|
*
|
||||||
|
* \retval after_bridge datastore container on success.
|
||||||
|
* \retval NULL on error.
|
||||||
|
*/
|
||||||
|
static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
|
||||||
|
{
|
||||||
|
struct ast_datastore *datastore;
|
||||||
|
SCOPED_CHANNELLOCK(lock, chan);
|
||||||
|
|
||||||
|
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
|
||||||
|
if (!datastore) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return datastore->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Run any after bridge callback.
|
||||||
* \since 12.0.0
|
* \since 12.0.0
|
||||||
*
|
*
|
||||||
* \param chan Channel to run after bridge callback.
|
* \param chan Channel to run after bridge callback.
|
||||||
|
@ -3248,33 +3321,75 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
|
||||||
*/
|
*/
|
||||||
static void after_bridge_callback_run(struct ast_channel *chan)
|
static void after_bridge_callback_run(struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
struct ast_datastore *datastore;
|
struct after_bridge_cb_ds *after_bridge;
|
||||||
|
struct after_bridge_cb_node *node;
|
||||||
|
|
||||||
|
after_bridge = after_bridge_cb_find(chan);
|
||||||
|
if (!after_bridge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
AST_LIST_LOCK(&after_bridge->callbacks);
|
||||||
|
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
|
||||||
|
AST_LIST_UNLOCK(&after_bridge->callbacks);
|
||||||
|
if (!node) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (node->reason) {
|
||||||
|
after_bridge_cb_failed(node);
|
||||||
|
} else {
|
||||||
|
node->failed = NULL;
|
||||||
|
node->callback(chan, node->data);
|
||||||
|
}
|
||||||
|
ast_free(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Run discarding any after bridge callbacks.
|
||||||
|
* \since 12.0.0
|
||||||
|
*
|
||||||
|
* \param chan Channel to run after bridge callback.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
|
||||||
|
{
|
||||||
struct after_bridge_cb_ds *after_bridge;
|
struct after_bridge_cb_ds *after_bridge;
|
||||||
|
|
||||||
if (ast_check_hangup(chan)) {
|
after_bridge = after_bridge_cb_find(chan);
|
||||||
|
if (!after_bridge) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get after bridge goto datastore. */
|
after_bridge_cb_run_discard(after_bridge, reason);
|
||||||
datastore = after_bridge_cb_remove(chan);
|
}
|
||||||
if (!datastore) {
|
|
||||||
|
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
|
||||||
|
{
|
||||||
|
struct after_bridge_cb_ds *after_bridge;
|
||||||
|
struct after_bridge_cb_node *node;
|
||||||
|
|
||||||
|
after_bridge = after_bridge_cb_find(chan);
|
||||||
|
if (!after_bridge) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
after_bridge = datastore->data;
|
AST_LIST_LOCK(&after_bridge->callbacks);
|
||||||
if (after_bridge) {
|
node = AST_LIST_LAST(&after_bridge->callbacks);
|
||||||
after_bridge->failed = NULL;
|
if (node && !node->reason) {
|
||||||
after_bridge->callback(chan, after_bridge->data);
|
node->reason = reason;
|
||||||
}
|
}
|
||||||
|
AST_LIST_UNLOCK(&after_bridge->callbacks);
|
||||||
/* Discard after bridge callback datastore. */
|
|
||||||
ast_datastore_free(datastore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
|
int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
|
||||||
{
|
{
|
||||||
struct ast_datastore *datastore;
|
|
||||||
struct after_bridge_cb_ds *after_bridge;
|
struct after_bridge_cb_ds *after_bridge;
|
||||||
|
struct after_bridge_cb_node *new_node;
|
||||||
|
struct after_bridge_cb_node *last_node;
|
||||||
|
|
||||||
/* Sanity checks. */
|
/* Sanity checks. */
|
||||||
ast_assert(chan != NULL);
|
ast_assert(chan != NULL);
|
||||||
|
@ -3282,29 +3397,28 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new datastore. */
|
after_bridge = after_bridge_cb_setup(chan);
|
||||||
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
|
|
||||||
if (!datastore) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
after_bridge = ast_calloc(1, sizeof(*after_bridge));
|
|
||||||
if (!after_bridge) {
|
if (!after_bridge) {
|
||||||
ast_datastore_free(datastore);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize it. */
|
/* Create a new callback node. */
|
||||||
after_bridge->callback = callback;
|
new_node = ast_calloc(1, sizeof(*new_node));
|
||||||
after_bridge->failed = failed;
|
if (!new_node) {
|
||||||
after_bridge->data = data;
|
return -1;
|
||||||
datastore->data = after_bridge;
|
}
|
||||||
|
new_node->callback = callback;
|
||||||
/* Put it on the channel replacing any existing one. */
|
new_node->failed = failed;
|
||||||
ast_channel_lock(chan);
|
new_node->data = data;
|
||||||
ast_after_bridge_callback_discard(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED);
|
|
||||||
ast_channel_datastore_add(chan, datastore);
|
|
||||||
ast_channel_unlock(chan);
|
|
||||||
|
|
||||||
|
/* Put it in the container disabling any previously active one. */
|
||||||
|
AST_LIST_LOCK(&after_bridge->callbacks);
|
||||||
|
last_node = AST_LIST_LAST(&after_bridge->callbacks);
|
||||||
|
if (last_node && !last_node->reason) {
|
||||||
|
last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
|
||||||
|
}
|
||||||
|
AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
|
||||||
|
AST_LIST_UNLOCK(&after_bridge->callbacks);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3769,7 +3883,7 @@ static void *bridge_channel_depart_thread(void *data)
|
||||||
ast_bridge_features_destroy(bridge_channel->features);
|
ast_bridge_features_destroy(bridge_channel->features);
|
||||||
bridge_channel->features = NULL;
|
bridge_channel->features = NULL;
|
||||||
|
|
||||||
ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
|
after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
|
||||||
ast_after_bridge_goto_discard(bridge_channel->chan);
|
ast_after_bridge_goto_discard(bridge_channel->chan);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -4993,6 +5107,23 @@ int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ast_bridge_features_do(enum ast_bridge_builtin_feature feature, struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||||
|
{
|
||||||
|
ast_bridge_hook_callback callback;
|
||||||
|
|
||||||
|
if (ARRAY_LEN(builtin_features_handlers) <= feature) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback = builtin_features_handlers[feature];
|
||||||
|
if (!callback) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
callback(bridge, bridge_channel, hook_pvt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback)
|
int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback)
|
||||||
{
|
{
|
||||||
if (ARRAY_LEN(builtin_interval_handlers) <= interval
|
if (ARRAY_LEN(builtin_interval_handlers) <= interval
|
||||||
|
|
|
@ -598,6 +598,12 @@ enum aco_process_status aco_process_config(struct aco_info *info, int reload)
|
||||||
return ACO_PROCESS_ERROR;
|
return ACO_PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BUGBUG must fix config framework loading of multiple files.
|
||||||
|
*
|
||||||
|
* A reload with multiple files must reload all files if any
|
||||||
|
* file has been touched.
|
||||||
|
*/
|
||||||
while (res != ACO_PROCESS_ERROR && (file = info->files[x++])) {
|
while (res != ACO_PROCESS_ERROR && (file = info->files[x++])) {
|
||||||
const char *filename = file->filename;
|
const char *filename = file->filename;
|
||||||
try_alias:
|
try_alias:
|
||||||
|
|
|
@ -53,6 +53,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||||
</syntax>
|
</syntax>
|
||||||
</managerEventInstance>
|
</managerEventInstance>
|
||||||
</managerEvent>
|
</managerEvent>
|
||||||
|
<managerEvent language="en_US" name="AgentLogin">
|
||||||
|
<managerEventInstance class="EVENT_FLAG_AGENT">
|
||||||
|
<synopsis>Raised when an Agent has logged in.</synopsis>
|
||||||
|
<syntax>
|
||||||
|
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
|
||||||
|
<parameter name="Agent">
|
||||||
|
<para>The name of the agent.</para>
|
||||||
|
</parameter>
|
||||||
|
</syntax>
|
||||||
|
<see-also>
|
||||||
|
<ref type="application">AgentLogin</ref>
|
||||||
|
<ref type="managerEvent">Agentlogoff</ref>
|
||||||
|
</see-also>
|
||||||
|
</managerEventInstance>
|
||||||
|
</managerEvent>
|
||||||
|
<managerEvent language="en_US" name="AgentLogoff">
|
||||||
|
<managerEventInstance class="EVENT_FLAG_AGENT">
|
||||||
|
<synopsis>Raised when an Agent has logged off.</synopsis>
|
||||||
|
<syntax>
|
||||||
|
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentLogin']/managerEventInstance/syntax/parameter)" />
|
||||||
|
<parameter name="Logintime">
|
||||||
|
<para>The number of seconds the agent was logged in.</para>
|
||||||
|
</parameter>
|
||||||
|
</syntax>
|
||||||
|
<see-also>
|
||||||
|
<ref type="managerEvent">AgentLogin</ref>
|
||||||
|
</see-also>
|
||||||
|
</managerEventInstance>
|
||||||
|
</managerEvent>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7
|
#define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7
|
||||||
|
@ -590,6 +619,44 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
|
||||||
ast_str_buffer(channel_event_string), variable, value);
|
ast_str_buffer(channel_event_string), variable, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ast_manager_event_blob *agent_login_to_ami(struct stasis_message *msg)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
|
||||||
|
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
|
||||||
|
struct ast_channel_blob *obj = stasis_message_data(msg);
|
||||||
|
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
|
||||||
|
|
||||||
|
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
|
||||||
|
if (!channel_string) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogin",
|
||||||
|
"%s"
|
||||||
|
"Agent: %s\r\n",
|
||||||
|
ast_str_buffer(channel_string), agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ast_manager_event_blob *agent_logoff_to_ami(struct stasis_message *msg)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
|
||||||
|
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
|
||||||
|
struct ast_channel_blob *obj = stasis_message_data(msg);
|
||||||
|
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
|
||||||
|
long logintime = ast_json_integer_get(ast_json_object_get(obj->blob, "logintime"));
|
||||||
|
|
||||||
|
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
|
||||||
|
if (!channel_string) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogoff",
|
||||||
|
"%s"
|
||||||
|
"Agent: %s\r\n"
|
||||||
|
"Logintime: %ld\r\n",
|
||||||
|
ast_str_buffer(channel_string), agent, logintime);
|
||||||
|
}
|
||||||
|
|
||||||
void ast_publish_channel_state(struct ast_channel *chan)
|
void ast_publish_channel_state(struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
|
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
|
||||||
|
@ -790,6 +857,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type);
|
||||||
STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
|
STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
|
||||||
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
|
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
|
||||||
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
|
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
|
||||||
|
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_login_type,
|
||||||
|
.to_ami = agent_login_to_ami,
|
||||||
|
);
|
||||||
|
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_logoff_type,
|
||||||
|
.to_ami = agent_logoff_to_ami,
|
||||||
|
);
|
||||||
|
|
||||||
/*! @} */
|
/*! @} */
|
||||||
|
|
||||||
|
@ -816,6 +889,8 @@ static void stasis_channels_cleanup(void)
|
||||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
|
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
|
||||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
|
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
|
||||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
|
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
|
||||||
|
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_login_type);
|
||||||
|
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ast_stasis_channels_init(void)
|
void ast_stasis_channels_init(void)
|
||||||
|
@ -839,6 +914,8 @@ void ast_stasis_channels_init(void)
|
||||||
STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
|
STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
|
||||||
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
|
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
|
||||||
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
|
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
|
||||||
|
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);
|
||||||
|
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);
|
||||||
|
|
||||||
channel_topic_all = stasis_topic_create("ast_channel_topic_all");
|
channel_topic_all = stasis_topic_create("ast_channel_topic_all");
|
||||||
channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);
|
channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);
|
||||||
|
|
Loading…
Reference in New Issue