2009-03-05 18:18:27 +00:00
/*
* Asterisk - - An open source telephony toolkit .
*
* Copyright ( C ) 2009 , Digium , Inc .
*
* Joshua Colp < jcolp @ digium . com >
*
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
* This program is free software , distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
/*! \file
*
* \ brief Built in bridging features
*
* \ author Joshua Colp < jcolp @ digium . com >
*
* \ ingroup bridges
*/
2011-07-14 20:28:54 +00:00
/*** MODULEINFO
< support_level > core < / support_level >
* * */
2009-03-05 18:18:27 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include "asterisk/module.h"
# include "asterisk/channel.h"
# include "asterisk/bridging.h"
# include "asterisk/bridging_technology.h"
# include "asterisk/frame.h"
# include "asterisk/file.h"
# include "asterisk/app.h"
# include "asterisk/astobj2.h"
2013-05-21 18:00:22 +00:00
# include "asterisk/pbx.h"
# include "asterisk/parking.h"
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*!
* \ brief Helper function that presents dialtone and grabs extension
*
* \ retval 0 on success
* \ retval - 1 on failure
*/
2009-03-05 18:18:27 +00:00
static int grab_transfer ( struct ast_channel * chan , char * exten , size_t exten_len , const char * context )
{
int res ;
/* Play the simple "transfer" prompt out and wait */
res = ast_stream_and_wait ( chan , " pbx-transfer " , AST_DIGIT_ANY ) ;
ast_stopstream ( chan ) ;
2013-05-21 18:00:22 +00:00
if ( res < 0 ) {
/* Hangup or error */
return - 1 ;
}
2009-03-05 18:18:27 +00:00
if ( res ) {
2013-05-21 18:00:22 +00:00
/* Store the DTMF digit that interrupted playback of the file. */
exten [ 0 ] = res ;
2009-03-05 18:18:27 +00:00
}
/* Drop to dialtone so they can enter the extension they want to transfer to */
2013-05-21 18:00:22 +00:00
/* BUGBUG the timeout needs to be configurable from features.conf. */
res = ast_app_dtget ( chan , context , exten , exten_len , exten_len - 1 , 3000 ) ;
if ( res < 0 ) {
/* Hangup or error */
res = - 1 ;
} else if ( ! res ) {
/* 0 for invalid extension dialed. */
if ( ast_strlen_zero ( exten ) ) {
ast_debug ( 1 , " %s dialed no digits. \n " , ast_channel_name ( chan ) ) ;
} else {
ast_debug ( 1 , " %s dialed '%s@%s' does not exist. \n " ,
ast_channel_name ( chan ) , exten , context ) ;
}
ast_stream_and_wait ( chan , " pbx-invalid " , AST_DIGIT_NONE ) ;
res = - 1 ;
} else {
/* Dialed extension is valid. */
res = 0 ;
}
2009-03-05 18:18:27 +00:00
return res ;
}
/*! \brief Helper function that creates an outgoing channel and returns it immediately */
2011-12-13 23:10:42 +00:00
static struct ast_channel * dial_transfer ( struct ast_channel * caller , const char * exten , const char * context )
2009-03-05 18:18:27 +00:00
{
2011-12-13 23:10:42 +00:00
char destination [ AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1 ] ;
struct ast_channel * chan ;
2009-03-05 18:18:27 +00:00
int cause ;
/* Fill the variable with the extension and context we want to call */
snprintf ( destination , sizeof ( destination ) , " %s@%s " , exten , context ) ;
2013-05-21 18:00:22 +00:00
/* Now we request a local channel to prepare to call the destination */
chan = ast_request ( " Local " , ast_channel_nativeformats ( caller ) , caller , destination ,
& cause ) ;
if ( ! chan ) {
2009-03-05 18:18:27 +00:00
return NULL ;
}
2011-12-13 23:10:42 +00:00
/* Before we actually dial out let's inherit appropriate information. */
ast_channel_lock_both ( caller , chan ) ;
2012-02-29 16:52:47 +00:00
ast_connected_line_copy_from_caller ( ast_channel_connected ( chan ) , ast_channel_caller ( caller ) ) ;
2009-03-05 18:18:27 +00:00
ast_channel_inherit_variables ( caller , chan ) ;
2011-12-13 23:10:42 +00:00
ast_channel_datastore_inherit ( caller , chan ) ;
ast_channel_unlock ( chan ) ;
ast_channel_unlock ( caller ) ;
2009-03-05 18:18:27 +00:00
/* Since the above worked fine now we actually call it and return the channel */
if ( ast_call ( chan , destination , 0 ) ) {
ast_hangup ( chan ) ;
return NULL ;
}
return chan ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Determine the transfer context to use .
* \ since 12.0 .0
*
* \ param transferer Channel initiating the transfer .
* \ param context User supplied context if available . May be NULL .
*
* \ return The context to use for the transfer .
*/
static const char * get_transfer_context ( struct ast_channel * transferer , const char * context )
{
if ( ! ast_strlen_zero ( context ) ) {
return context ;
}
context = pbx_builtin_getvar_helper ( transferer , " TRANSFER_CONTEXT " ) ;
if ( ! ast_strlen_zero ( context ) ) {
return context ;
}
context = ast_channel_macrocontext ( transferer ) ;
if ( ! ast_strlen_zero ( context ) ) {
return context ;
}
context = ast_channel_context ( transferer ) ;
if ( ! ast_strlen_zero ( context ) ) {
return context ;
}
return " default " ;
}
2009-03-05 18:18:27 +00:00
/*! \brief Internal built in feature for blind transfers */
static int feature_blind_transfer ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
{
char exten [ AST_MAX_EXTENSION ] = " " ;
struct ast_channel * chan = NULL ;
struct ast_bridge_features_blind_transfer * blind_transfer = hook_pvt ;
2013-05-21 18:00:22 +00:00
const char * context ;
struct ast_exten * park_exten ;
/* BUGBUG the peer needs to be put on hold for the transfer. */
ast_channel_lock ( bridge_channel - > chan ) ;
context = ast_strdupa ( get_transfer_context ( bridge_channel - > chan ,
blind_transfer ? blind_transfer - > context : NULL ) ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
2009-03-05 18:18:27 +00:00
/* Grab the extension to transfer to */
2013-05-21 18:00:22 +00:00
if ( grab_transfer ( bridge_channel - > chan , exten , sizeof ( exten ) , context ) ) {
return 0 ;
}
/* Parking blind transfer override - phase this out for something more general purpose in the future. */
park_exten = ast_get_parking_exten ( exten , bridge_channel - > chan , context ) ;
if ( park_exten ) {
/* We are transfering the transferee to a parking lot. */
if ( ast_park_blind_xfer ( bridge , bridge_channel , park_exten ) ) {
ast_log ( LOG_ERROR , " %s attempted to transfer to park application and failed. \n " , ast_channel_name ( bridge_channel - > chan ) ) ;
} ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/* BUGBUG just need to ast_async_goto the peer so this bridge will go away and not accumulate local channels and bridges if the destination is to an application. */
/* ast_async_goto actually is a blind transfer. */
/* BUGBUG Use the bridge count to determine if can do DTMF transfer features. If count is not 2 then don't allow it. */
2009-03-05 18:18:27 +00:00
/* Get a channel that is the destination we wish to call */
2013-05-21 18:00:22 +00:00
chan = dial_transfer ( bridge_channel - > chan , exten , context ) ;
if ( ! chan ) {
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/* Impart the new channel onto the bridge, and have it take our place. */
if ( ast_bridge_impart ( bridge_channel - > bridge , chan , bridge_channel - > chan , NULL , 1 ) ) {
ast_hangup ( chan ) ;
return 0 ;
}
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/*! Attended transfer code */
enum atxfer_code {
/*! Party C hungup or other reason to abandon the transfer. */
ATXFER_INCOMPLETE ,
/*! Transfer party C to party A. */
ATXFER_COMPLETE ,
/*! Turn the transfer into a threeway call. */
ATXFER_THREEWAY ,
/*! Hangup party C and return party B to the bridge. */
ATXFER_ABORT ,
} ;
/*! \brief Attended transfer feature to complete transfer */
static int attended_transfer_complete ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
enum atxfer_code * transfer_code = hook_pvt ;
* transfer_code = ATXFER_COMPLETE ;
ast_bridge_change_state ( bridge_channel , AST_BRIDGE_CHANNEL_STATE_HANGUP ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/*! \brief Attended transfer feature to turn it into a threeway call */
static int attended_transfer_threeway ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
enum atxfer_code * transfer_code = hook_pvt ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
* transfer_code = ATXFER_THREEWAY ;
ast_bridge_change_state ( bridge_channel , AST_BRIDGE_CHANNEL_STATE_HANGUP ) ;
return 0 ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*! \brief Attended transfer feature to abort transfer */
static int attended_transfer_abort ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
{
enum atxfer_code * transfer_code = hook_pvt ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
* transfer_code = ATXFER_ABORT ;
ast_bridge_change_state ( bridge_channel , AST_BRIDGE_CHANNEL_STATE_HANGUP ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
/*! \brief Internal built in feature for attended transfers */
static int feature_attended_transfer ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
{
char exten [ AST_MAX_EXTENSION ] = " " ;
2013-05-21 18:00:22 +00:00
struct ast_channel * peer ;
struct ast_bridge * attended_bridge ;
struct ast_bridge_features caller_features ;
int xfer_failed ;
2009-03-05 18:18:27 +00:00
struct ast_bridge_features_attended_transfer * attended_transfer = hook_pvt ;
2013-05-21 18:00:22 +00:00
const char * context ;
enum atxfer_code transfer_code = ATXFER_INCOMPLETE ;
bridge = ast_bridge_channel_merge_inhibit ( bridge_channel , + 1 ) ;
/* BUGBUG the peer needs to be put on hold for the transfer. */
ast_channel_lock ( bridge_channel - > chan ) ;
context = ast_strdupa ( get_transfer_context ( bridge_channel - > chan ,
attended_transfer ? attended_transfer - > context : NULL ) ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
2009-03-05 18:18:27 +00:00
/* Grab the extension to transfer to */
2013-05-21 18:00:22 +00:00
if ( grab_transfer ( bridge_channel - > chan , exten , sizeof ( exten ) , context ) ) {
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
/* Get a channel that is the destination we wish to call */
2013-05-21 18:00:22 +00:00
peer = dial_transfer ( bridge_channel - > chan , exten , context ) ;
if ( ! peer ) {
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait ( bridge_channel - > chan , " beeperr " , AST_DIGIT_NONE ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/* BUGBUG bridging API features does not support features.conf featuremap */
/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
/* Setup a DTMF menu to control the transfer. */
if ( ast_bridge_features_init ( & caller_features )
| | ast_bridge_hangup_hook ( & caller_features ,
attended_transfer_complete , & transfer_code , NULL , 0 )
| | ast_bridge_dtmf_hook ( & caller_features ,
attended_transfer & & ! ast_strlen_zero ( attended_transfer - > abort )
? attended_transfer - > abort : " *1 " ,
attended_transfer_abort , & transfer_code , NULL , 0 )
| | ast_bridge_dtmf_hook ( & caller_features ,
attended_transfer & & ! ast_strlen_zero ( attended_transfer - > complete )
? attended_transfer - > complete : " *2 " ,
attended_transfer_complete , & transfer_code , NULL , 0 )
| | ast_bridge_dtmf_hook ( & caller_features ,
attended_transfer & & ! ast_strlen_zero ( attended_transfer - > threeway )
? attended_transfer - > threeway : " *3 " ,
attended_transfer_threeway , & transfer_code , NULL , 0 ) ) {
ast_bridge_features_cleanup ( & caller_features ) ;
ast_hangup ( peer ) ;
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait ( bridge_channel - > chan , " beeperr " , AST_DIGIT_NONE ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
/* Create a bridge to use to talk to the person we are calling */
attended_bridge = ast_bridge_base_new ( AST_BRIDGE_CAPABILITY_1TO1MIX ,
AST_BRIDGE_FLAG_DISSOLVE_HANGUP ) ;
if ( ! attended_bridge ) {
ast_bridge_features_cleanup ( & caller_features ) ;
ast_hangup ( peer ) ;
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait ( bridge_channel - > chan , " beeperr " , AST_DIGIT_NONE ) ;
return 0 ;
}
ast_bridge_merge_inhibit ( attended_bridge , + 1 ) ;
2009-03-05 18:18:27 +00:00
/* This is how this is going down, we are imparting the channel we called above into this bridge first */
2013-05-21 18:00:22 +00:00
/* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
if ( ast_bridge_impart ( attended_bridge , peer , NULL , NULL , 0 ) ) {
ast_bridge_destroy ( attended_bridge ) ;
ast_bridge_features_cleanup ( & caller_features ) ;
ast_hangup ( peer ) ;
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait ( bridge_channel - > chan , " beeperr " , AST_DIGIT_NONE ) ;
return 0 ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*
* For the caller we want to join the bridge in a blocking
* fashion so we don ' t spin around in this function doing
* nothing while waiting .
*/
ast_bridge_join ( attended_bridge , bridge_channel - > chan , NULL , & caller_features , NULL , 0 ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*
* BUGBUG there is a small window where the channel does not point to the bridge_channel .
*
* This window is expected to go away when atxfer is redesigned
* to fully support existing functionality . There will be one
* and only one ast_bridge_channel structure per channel .
*/
/* Point the channel back to the original bridge and bridge_channel. */
ast_bridge_channel_lock ( bridge_channel ) ;
ast_channel_lock ( bridge_channel - > chan ) ;
ast_channel_internal_bridge_channel_set ( bridge_channel - > chan , bridge_channel ) ;
ast_channel_internal_bridge_set ( bridge_channel - > chan , bridge_channel - > bridge ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
ast_bridge_channel_unlock ( bridge_channel ) ;
/* Wait for peer thread to exit bridge and die. */
if ( ! ast_autoservice_start ( bridge_channel - > chan ) ) {
ast_bridge_depart ( peer ) ;
ast_autoservice_stop ( bridge_channel - > chan ) ;
} else {
ast_bridge_depart ( peer ) ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Now that all channels are out of it we can destroy the bridge and the feature structures */
ast_bridge_destroy ( attended_bridge ) ;
2009-03-05 18:18:27 +00:00
ast_bridge_features_cleanup ( & caller_features ) ;
2013-05-21 18:00:22 +00:00
xfer_failed = - 1 ;
switch ( transfer_code ) {
case ATXFER_INCOMPLETE :
/* Peer hungup */
break ;
case ATXFER_COMPLETE :
/* The peer takes our place in the bridge. */
ast_bridge_change_state ( bridge_channel , AST_BRIDGE_CHANNEL_STATE_HANGUP ) ;
xfer_failed = ast_bridge_impart ( bridge_channel - > bridge , peer , bridge_channel - > chan , NULL , 1 ) ;
break ;
case ATXFER_THREEWAY :
/*
* Transferer wants to convert to a threeway call .
*
* Just impart the peer onto the bridge and have us return to it
* as normal .
*/
xfer_failed = ast_bridge_impart ( bridge_channel - > bridge , peer , NULL , NULL , 1 ) ;
break ;
case ATXFER_ABORT :
/* Transferer decided not to transfer the call after all. */
break ;
}
ast_bridge_merge_inhibit ( bridge , - 1 ) ;
ao2_ref ( bridge , - 1 ) ;
if ( xfer_failed ) {
ast_hangup ( peer ) ;
if ( ! ast_check_hangup_locked ( bridge_channel - > chan ) ) {
ast_stream_and_wait ( bridge_channel - > chan , " beeperr " , AST_DIGIT_NONE ) ;
2009-03-05 18:18:27 +00:00
}
}
return 0 ;
}
/*! \brief Internal built in feature for hangup */
static int feature_hangup ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
{
2013-01-21 17:55:48 +00:00
/*
* This is very simple , we simply change the state on the
* bridge_channel to force the channel out of the bridge and the
* core takes care of the rest .
*/
2009-03-05 18:18:27 +00:00
ast_bridge_change_state ( bridge_channel , AST_BRIDGE_CHANNEL_STATE_END ) ;
return 0 ;
}
static int unload_module ( void )
{
return 0 ;
}
static int load_module ( void )
{
ast_bridge_features_register ( AST_BRIDGE_BUILTIN_BLINDTRANSFER , feature_blind_transfer , NULL ) ;
ast_bridge_features_register ( AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER , feature_attended_transfer , NULL ) ;
ast_bridge_features_register ( AST_BRIDGE_BUILTIN_HANGUP , feature_hangup , NULL ) ;
/* Bump up our reference count so we can't be unloaded */
ast_module_ref ( ast_module_info - > self ) ;
return AST_MODULE_LOAD_SUCCESS ;
}
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Built in bridging features " ) ;