2001-12-27 11:07:33 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
2001-12-27 11:07:33 +00:00
*
2006-01-05 09:12:33 +00:00
* Copyright (C) 1999 - 2006, Digium, Inc.
2001-12-27 11:07:33 +00:00
*
2004-08-31 13:32:11 +00:00
* Mark Spencer <markster@digium.com>
2001-12-27 11:07:33 +00:00
*
2005-09-14 20:46:50 +00:00
* 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.
*
2001-12-27 11:07:33 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2006-01-19 22:09:18 +00:00
* \brief Routines implementing call features as call pickup, parking and transfer
2005-12-30 21:18:06 +00:00
*
* \author Mark Spencer <markster@digium.com>
2001-12-27 11:07:33 +00:00
*/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-06-06 22:12:19 +00:00
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
2005-04-21 06:02:45 +00:00
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
2005-06-23 22:12:01 +00:00
#include "asterisk/causes.h"
2005-04-21 06:02:45 +00:00
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/utils.h"
#include "asterisk/adsi.h"
2006-06-26 16:43:21 +00:00
#include "asterisk/devicestate.h"
2005-11-08 04:02:35 +00:00
#include "asterisk/monitor.h"
2001-12-27 11:07:33 +00:00
2003-02-02 19:37:23 +00:00
#define DEFAULT_PARK_TIME 45000
2004-08-01 01:38:15 +00:00
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
2005-01-04 04:01:40 +00:00
#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
2006-05-23 18:23:05 +00:00
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
2007-05-01 22:24:51 +00:00
#define DEFAULT_ATXFER_DROP_CALL 0
#define DEFAULT_ATXFER_LOOP_DELAY 10000
#define DEFAULT_ATXFER_CALLBACK_RETRIES 2
2003-02-02 19:37:23 +00:00
2005-06-23 22:12:01 +00:00
#define AST_MAX_WATCHERS 256
2006-08-07 04:15:52 +00:00
enum {
AST_FEATURE_FLAG_NEEDSDTMF = ( 1 << 0 ),
AST_FEATURE_FLAG_ONPEER = ( 1 << 1 ),
AST_FEATURE_FLAG_ONSELF = ( 1 << 2 ),
AST_FEATURE_FLAG_BYCALLEE = ( 1 << 3 ),
AST_FEATURE_FLAG_BYCALLER = ( 1 << 4 ),
AST_FEATURE_FLAG_BYBOTH = ( 3 << 3 ),
};
2007-05-31 18:21:47 +00:00
struct feature_group_exten {
AST_LIST_ENTRY ( feature_group_exten ) entry ;
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( exten );
);
struct ast_call_feature * feature ;
};
struct feature_group {
AST_LIST_ENTRY ( feature_group ) entry ;
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( gname );
);
AST_LIST_HEAD_NOLOCK (, feature_group_exten ) features ;
};
static AST_RWLIST_HEAD_STATIC ( feature_groups , feature_group );
2001-12-27 11:07:33 +00:00
static char * parkedcall = "ParkedCall" ;
2006-06-26 16:43:21 +00:00
static int parkaddhints = 0 ; /*!< Add parking hints automatically */
2007-01-16 17:50:25 +00:00
static int parkedcalltransfers = 0 ; /*!< Enable DTMF based transfers on bridge when picking up parked calls */
2007-02-16 17:41:27 +00:00
static int parkedcallreparking = 0 ; /*!< Enable DTMF based parking on bridge when picking up parked calls */
2006-06-21 07:49:29 +00:00
static int parkingtime = DEFAULT_PARK_TIME ; /*!< No more than 45 seconds parked before you do something with them */
static char parking_con [ AST_MAX_EXTENSION ]; /*!< Context for which parking is made accessible */
static char parking_con_dial [ AST_MAX_EXTENSION ]; /*!< Context for dialback for parking (KLUDGE) */
static char parking_ext [ AST_MAX_EXTENSION ]; /*!< Extension you type to park the call */
static char pickup_ext [ AST_MAX_EXTENSION ]; /*!< Call pickup extension */
2006-07-19 20:44:39 +00:00
static char parkmohclass [ MAX_MUSICCLASS ]; /*!< Music class used for parking */
2006-06-21 07:49:29 +00:00
static int parking_start ; /*!< First available extension for parking */
static int parking_stop ; /*!< Last available extension for parking */
static char courtesytone [ 256 ]; /*!< Courtesy tone */
static int parkedplay = 0 ; /*!< Who to play the courtesy tone to */
static char xfersound [ 256 ]; /*!< Call transfer sound */
static char xferfailsound [ 256 ]; /*!< Call transfer failure sound */
2001-12-27 11:07:33 +00:00
2005-10-13 23:58:33 +00:00
static int parking_offset ;
static int parkfindnext ;
2005-04-27 03:58:40 +00:00
2005-10-13 23:58:33 +00:00
static int adsipark ;
2004-09-15 19:49:33 +00:00
2005-10-13 23:58:33 +00:00
static int transferdigittimeout ;
static int featuredigittimeout ;
2006-11-01 00:07:37 +00:00
static int comebacktoorigin = 1 ;
2004-08-01 01:38:15 +00:00
2006-05-23 18:23:05 +00:00
static int atxfernoanswertimeout ;
2007-05-01 22:24:51 +00:00
static unsigned int atxferdropcall ;
static unsigned int atxferloopdelay ;
static unsigned int atxfercallbackretries ;
2006-05-23 18:23:05 +00:00
2006-06-21 07:49:29 +00:00
static char * registrar = "res_features" ; /*!< Registrar for operations */
2001-12-27 11:07:33 +00:00
2006-01-09 09:07:58 +00:00
/* module and CLI command definitions */
2001-12-27 11:07:33 +00:00
static char * synopsis = "Answer a parked call" ;
static char * descrip = "ParkedCall(exten):"
2004-12-28 23:49:46 +00:00
"Used to connect to a parked call. This application is always \n "
2001-12-27 11:07:33 +00:00
"registered internally and does not need to be explicitly added \n "
"into the dialplan, although you should include the 'parkedcalls' \n "
"context. \n " ;
2004-08-03 06:31:20 +00:00
static char * parkcall = "Park" ;
static char * synopsis2 = "Park yourself" ;
2006-04-10 17:32:29 +00:00
static char * descrip2 = "Park():"
2004-08-03 06:31:20 +00:00
"Used to park yourself (typically in combination with a supervised \n "
2004-12-28 23:49:46 +00:00
"transfer to know the parking space). This application is always \n "
2004-08-03 06:31:20 +00:00
"registered internally and does not need to be explicitly added \n "
"into the dialplan, although you should include the 'parkedcalls' \n "
2006-06-26 16:43:21 +00:00
"context (or the context specified in features.conf). \n\n "
"If you set the PARKINGEXTEN variable to an extension in your \n "
"parking context, park() will park the call on that extension, unless \n "
"it already exists. In that case, execution will continue at next \n "
"priority. \n " ;
2004-08-03 06:31:20 +00:00
2006-01-09 09:07:58 +00:00
static struct ast_app * monitor_app = NULL ;
static int monitor_ok = 1 ;
2004-09-17 03:49:57 +00:00
2001-12-27 11:07:33 +00:00
struct parkeduser {
2006-06-21 07:49:29 +00:00
struct ast_channel * chan ; /*!< Parking channel */
struct timeval start ; /*!< Time the parking started */
int parkingnum ; /*!< Parking lot */
2006-06-26 16:43:21 +00:00
char parkingexten [ AST_MAX_EXTENSION ]; /*!< If set beforehand, parking extension used for this call */
2006-06-21 07:49:29 +00:00
char context [ AST_MAX_CONTEXT ]; /*!< Where to go if our parking time expires */
2001-12-27 11:07:33 +00:00
char exten [ AST_MAX_EXTENSION ];
int priority ;
2006-06-21 07:49:29 +00:00
int parkingtime ; /*!< Maximum length in parking lot before return */
2004-08-03 06:31:20 +00:00
int notquiteyet ;
2004-12-09 22:39:14 +00:00
char peername [ 1024 ];
2005-03-17 21:52:57 +00:00
unsigned char moh_trys ;
2007-07-09 02:29:00 +00:00
AST_LIST_ENTRY ( parkeduser ) list ;
2001-12-27 11:07:33 +00:00
};
2007-07-09 02:29:00 +00:00
static AST_LIST_HEAD_STATIC ( parkinglot , parkeduser );
2001-12-27 11:07:33 +00:00
static pthread_t parking_thread ;
2007-06-06 21:08:07 +00:00
const char * ast_parking_ext ( void )
2001-12-27 11:07:33 +00:00
{
return parking_ext ;
}
2007-06-06 21:08:07 +00:00
const char * ast_pickup_ext ( void )
2003-04-09 04:00:43 +00:00
{
return pickup_ext ;
}
2005-01-05 19:56:47 +00:00
struct ast_bridge_thread_obj
{
struct ast_bridge_config bconfig ;
struct ast_channel * chan ;
struct ast_channel * peer ;
2007-04-10 20:44:44 +00:00
unsigned int return_to_pbx : 1 ;
2005-01-05 19:56:47 +00:00
};
2007-03-30 14:37:21 +00:00
2007-08-07 23:04:01 +00:00
/*!
* \brief store context, extension and priority
* \param chan, context, ext, pri
*/
2006-04-17 04:31:21 +00:00
static void set_c_e_p ( struct ast_channel * chan , const char * context , const char * ext , int pri )
2006-04-16 18:49:46 +00:00
{
2006-04-17 04:31:21 +00:00
ast_copy_string ( chan -> context , context , sizeof ( chan -> context ));
2006-04-16 18:49:46 +00:00
ast_copy_string ( chan -> exten , ext , sizeof ( chan -> exten ));
chan -> priority = pri ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Check goto on transfer
* \param chan
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Check if channel has 'GOTO_ON_BLINDXFR' set, if not exit.
* When found make sure the types are compatible. Check if channel is valid
* if so start the new channel else hangup the call.
*/
2005-04-22 02:55:14 +00:00
static void check_goto_on_transfer ( struct ast_channel * chan )
{
struct ast_channel * xferchan ;
2005-12-03 19:25:33 +00:00
const char * val = pbx_builtin_getvar_helper ( chan , "GOTO_ON_BLINDXFR" );
char * x , * goto_on_transfer ;
struct ast_frame * f ;
2005-04-22 02:55:14 +00:00
2006-05-10 13:22:15 +00:00
if ( ast_strlen_zero ( val ))
return ;
goto_on_transfer = ast_strdupa ( val );
2007-04-10 05:41:34 +00:00
if ( ! ( xferchan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , "" , "" , "" , 0 , chan -> name )))
2006-05-10 13:22:15 +00:00
return ;
for ( x = goto_on_transfer ; x && * x ; x ++ ) {
if ( * x == '^' )
* x = '|' ;
}
/* Make formats okay */
xferchan -> readformat = chan -> readformat ;
xferchan -> writeformat = chan -> writeformat ;
ast_channel_masquerade ( xferchan , chan );
ast_parseable_goto ( xferchan , goto_on_transfer );
xferchan -> _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL );
xferchan -> _softhangup = 0 ;
if (( f = ast_read ( xferchan ))) {
ast_frfree ( f );
f = NULL ;
ast_pbx_start ( xferchan );
} else {
ast_hangup ( xferchan );
2005-04-22 02:55:14 +00:00
}
}
2007-05-01 22:24:51 +00:00
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , struct ast_channel * transferee , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_name , int igncallerstate );
2005-06-23 22:12:01 +00:00
2007-08-07 23:04:01 +00:00
/*!
* \brief bridge the call
2007-09-05 16:31:39 +00:00
* \param data thread bridge.
*
2007-08-07 23:04:01 +00:00
* Set Last Data for respective channels, reset cdr for channels
* bridge call, check if we're going back to dialplan
* if not hangup both legs of the call
*/
2005-01-05 19:56:47 +00:00
static void * ast_bridge_call_thread ( void * data )
{
struct ast_bridge_thread_obj * tobj = data ;
2007-04-10 20:44:44 +00:00
int res ;
2005-11-06 21:00:35 +00:00
2007-04-10 20:44:44 +00:00
tobj -> chan -> appl = ! tobj -> return_to_pbx ? "Transferred Call" : "ManagerBridge" ;
2006-04-14 23:20:29 +00:00
tobj -> chan -> data = tobj -> peer -> name ;
2007-04-10 20:44:44 +00:00
tobj -> peer -> appl = ! tobj -> return_to_pbx ? "Transferred Call" : "ManagerBridge" ;
2006-04-14 23:20:29 +00:00
tobj -> peer -> data = tobj -> chan -> name ;
2007-04-10 20:44:44 +00:00
2005-01-05 19:56:47 +00:00
if ( tobj -> chan -> cdr ) {
2005-11-06 21:00:35 +00:00
ast_cdr_reset ( tobj -> chan -> cdr , NULL );
2005-01-05 19:56:47 +00:00
ast_cdr_setdestchan ( tobj -> chan -> cdr , tobj -> peer -> name );
}
if ( tobj -> peer -> cdr ) {
2005-11-06 21:00:35 +00:00
ast_cdr_reset ( tobj -> peer -> cdr , NULL );
2005-01-05 19:56:47 +00:00
ast_cdr_setdestchan ( tobj -> peer -> cdr , tobj -> chan -> name );
}
ast_bridge_call ( tobj -> peer , tobj -> chan , & tobj -> bconfig );
2007-04-10 20:44:44 +00:00
if ( tobj -> return_to_pbx ) {
if ( ! ast_check_hangup ( tobj -> peer )) {
ast_log ( LOG_VERBOSE , "putting peer %s into PBX again \n " , tobj -> peer -> name );
res = ast_pbx_start ( tobj -> peer );
if ( res != AST_PBX_SUCCESS )
ast_log ( LOG_WARNING , "FAILED continuing PBX on peer %s \n " , tobj -> peer -> name );
} else
ast_hangup ( tobj -> peer );
if ( ! ast_check_hangup ( tobj -> chan )) {
ast_log ( LOG_VERBOSE , "putting chan %s into PBX again \n " , tobj -> chan -> name );
res = ast_pbx_start ( tobj -> chan );
if ( res != AST_PBX_SUCCESS )
ast_log ( LOG_WARNING , "FAILED continuing PBX on chan %s \n " , tobj -> chan -> name );
} else
ast_hangup ( tobj -> chan );
} else {
ast_hangup ( tobj -> chan );
ast_hangup ( tobj -> peer );
}
2007-06-06 21:20:11 +00:00
ast_free ( tobj );
2007-04-10 20:44:44 +00:00
2005-01-05 19:56:47 +00:00
return NULL ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief create thread for the parked call
* \param data
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Create thread and attributes, call ast_bridge_call_thread
*/
2005-01-05 19:56:47 +00:00
static void ast_bridge_call_thread_launch ( void * data )
{
pthread_t thread ;
pthread_attr_t attr ;
2005-11-09 00:16:08 +00:00
struct sched_param sched ;
2005-01-05 19:56:47 +00:00
2005-11-09 00:16:08 +00:00
pthread_attr_init ( & attr );
2005-01-05 19:56:47 +00:00
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED );
2005-11-09 00:16:08 +00:00
ast_pthread_create ( & thread , & attr , ast_bridge_call_thread , data );
pthread_attr_destroy ( & attr );
memset ( & sched , 0 , sizeof ( sched ));
pthread_setschedparam ( thread , SCHED_RR , & sched );
2005-01-05 19:56:47 +00:00
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Announce call parking by ADSI
2007-09-05 16:31:39 +00:00
* \param chan .
* \param parkingexten .
2007-08-07 23:04:01 +00:00
* Create message to show for ADSI, display message.
* \retval 0 on success.
* \retval -1 on failure.
*/
2006-06-26 16:43:21 +00:00
static int adsi_announce_park ( struct ast_channel * chan , char * parkingexten )
2004-09-15 19:49:33 +00:00
{
int res ;
int justify [ 5 ] = { ADSI_JUST_CENT , ADSI_JUST_CENT , ADSI_JUST_CENT , ADSI_JUST_CENT };
2005-09-07 21:01:31 +00:00
char tmp [ 256 ];
2005-09-07 18:55:03 +00:00
char * message [ 5 ] = { NULL , NULL , NULL , NULL , NULL };
2004-09-15 19:49:33 +00:00
2006-06-26 16:43:21 +00:00
snprintf ( tmp , sizeof ( tmp ), "Parked on %s" , parkingexten );
2005-09-07 18:55:03 +00:00
message [ 0 ] = tmp ;
2006-09-20 04:34:51 +00:00
res = ast_adsi_load_session ( chan , NULL , 0 , 1 );
2006-04-14 23:20:29 +00:00
if ( res == - 1 )
2004-09-15 19:49:33 +00:00
return res ;
2006-09-20 04:34:51 +00:00
return ast_adsi_print ( chan , message , justify , 1 );
2004-09-15 19:49:33 +00:00
}
2006-06-26 16:43:21 +00:00
/*! \brief Notify metermaids that we've changed an extension */
2007-08-10 16:24:11 +00:00
static void notify_metermaids ( const char * exten , char * context , enum ast_device_state state )
2006-06-26 16:43:21 +00:00
{
2007-08-10 16:24:11 +00:00
ast_debug ( 4 , "Notification of state change to metermaids %s@%s \n to state '%s'" ,
exten , context , devstate2str ( state ));
2006-06-26 16:43:21 +00:00
2007-08-10 16:24:11 +00:00
ast_devstate_changed ( state , "park:%s@%s" , exten , context );
2006-06-26 16:43:21 +00:00
}
/*! \brief metermaids callback from devicestate.c */
2007-02-13 22:02:20 +00:00
static enum ast_device_state metermaidstate ( const char * data )
2006-06-26 16:43:21 +00:00
{
2007-07-08 13:22:30 +00:00
char * context ;
2006-06-26 16:43:21 +00:00
char * exten ;
2007-07-08 13:22:30 +00:00
context = ast_strdupa ( data );
2006-06-26 16:43:21 +00:00
exten = strsep ( & context , "@" );
if ( ! context )
2007-07-08 13:22:30 +00:00
return AST_DEVICE_INVALID ;
2006-06-26 16:43:21 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 4 , "Checking state of exten %s in context %s \n " , exten , context );
2006-06-26 16:43:21 +00:00
2007-07-08 13:22:30 +00:00
if ( ! ast_exists_extension ( NULL , context , exten , 1 , NULL ))
2006-06-26 16:43:21 +00:00
return AST_DEVICE_NOT_INUSE ;
2007-07-08 13:22:30 +00:00
return AST_DEVICE_INUSE ;
2006-06-26 16:43:21 +00:00
}
2007-08-07 23:04:01 +00:00
/* Park a call */
2003-02-02 19:37:23 +00:00
int ast_park_call ( struct ast_channel * chan , struct ast_channel * peer , int timeout , int * extout )
2001-12-27 11:07:33 +00:00
{
struct parkeduser * pu , * cur ;
2006-06-26 16:43:21 +00:00
int i , x = - 1 , parking_range ;
2004-08-03 06:31:20 +00:00
struct ast_context * con ;
2006-06-26 16:43:21 +00:00
const char * parkingexten ;
2006-01-21 22:09:06 +00:00
2006-06-26 16:43:21 +00:00
/* Allocate memory for parking data */
2006-06-21 07:49:29 +00:00
if ( ! ( pu = ast_calloc ( 1 , sizeof ( * pu ))))
2005-07-25 17:31:53 +00:00
return - 1 ;
2006-06-21 07:49:29 +00:00
2006-06-26 16:43:21 +00:00
/* Lock parking lot */
2007-07-09 02:29:00 +00:00
AST_LIST_LOCK ( & parkinglot );
2006-06-26 16:43:21 +00:00
/* Check for channel variable PARKINGEXTEN */
parkingexten = pbx_builtin_getvar_helper ( chan , "PARKINGEXTEN" );
if ( ! ast_strlen_zero ( parkingexten )) {
if ( ast_exists_extension ( NULL , parking_con , parkingexten , 1 , NULL )) {
2007-07-09 02:29:00 +00:00
AST_LIST_UNLOCK ( & parkinglot );
2007-06-06 21:20:11 +00:00
ast_free ( pu );
2006-06-26 16:43:21 +00:00
ast_log ( LOG_WARNING , "Requested parking extension already exists: %s@%s \n " , parkingexten , parking_con );
2007-06-04 17:43:21 +00:00
return - 1 ; /* We failed to park this call, plain and simple so we need to error out */
2006-06-26 16:43:21 +00:00
}
ast_copy_string ( pu -> parkingexten , parkingexten , sizeof ( pu -> parkingexten ));
2007-05-11 15:48:38 +00:00
x = atoi ( parkingexten );
2006-06-26 16:43:21 +00:00
} else {
/* Select parking space within range */
parking_range = parking_stop - parking_start + 1 ;
for ( i = 0 ; i < parking_range ; i ++ ) {
x = ( i + parking_offset ) % parking_range + parking_start ;
2007-07-09 02:29:00 +00:00
AST_LIST_TRAVERSE ( & parkinglot , cur , list ) {
if ( cur -> parkingnum == x )
2006-06-26 16:43:21 +00:00
break ;
}
if ( ! cur )
2001-12-27 11:07:33 +00:00
break ;
}
2005-04-27 03:58:40 +00:00
2006-06-26 16:43:21 +00:00
if ( ! ( i < parking_range )) {
ast_log ( LOG_WARNING , "No more parking spaces \n " );
2007-06-06 21:20:11 +00:00
ast_free ( pu );
2007-07-09 02:29:00 +00:00
AST_LIST_UNLOCK ( & parkinglot );
2006-06-26 16:43:21 +00:00
return - 1 ;
}
/* Set pointer for next parking */
if ( parkfindnext )
parking_offset = x - parking_start + 1 ;
2005-07-25 17:31:53 +00:00
}
2006-06-26 16:43:21 +00:00
2005-07-25 17:31:53 +00:00
chan -> appl = "Parked Call" ;
chan -> data = NULL ;
pu -> chan = chan ;
2006-06-26 16:43:21 +00:00
2006-07-19 20:44:39 +00:00
/* Put the parked channel on hold if we have two different channels */
2005-07-25 17:31:53 +00:00
if ( chan != peer ) {
2006-07-19 20:44:39 +00:00
ast_indicate_data ( pu -> chan , AST_CONTROL_HOLD ,
S_OR ( parkmohclass , NULL ),
! ast_strlen_zero ( parkmohclass ) ? strlen ( parkmohclass ) + 1 : 0 );
2005-07-25 17:31:53 +00:00
}
2006-07-19 20:44:39 +00:00
2005-07-25 17:31:53 +00:00
pu -> start = ast_tvnow ();
pu -> parkingnum = x ;
2006-04-16 18:37:01 +00:00
pu -> parkingtime = ( timeout > 0 ) ? timeout : parkingtime ;
2005-07-25 17:31:53 +00:00
if ( extout )
* extout = x ;
2006-06-26 16:43:21 +00:00
2005-07-25 17:31:53 +00:00
if ( peer )
ast_copy_string ( pu -> peername , peer -> name , sizeof ( pu -> peername ));
/* Remember what had been dialed, so that if the parking
expires, we try to come back to the same place */
2006-04-14 23:20:29 +00:00
ast_copy_string ( pu -> context , S_OR ( chan -> macrocontext , chan -> context ), sizeof ( pu -> context ));
ast_copy_string ( pu -> exten , S_OR ( chan -> macroexten , chan -> exten ), sizeof ( pu -> exten ));
pu -> priority = chan -> macropriority ? chan -> macropriority : chan -> priority ;
2007-07-09 02:29:00 +00:00
AST_LIST_INSERT_TAIL ( & parkinglot , pu , list );
2006-06-26 16:43:21 +00:00
2005-07-25 17:31:53 +00:00
/* If parking a channel directly, don't quiet yet get parking running on it */
if ( peer == chan )
pu -> notquiteyet = 1 ;
2007-07-09 02:29:00 +00:00
AST_LIST_UNLOCK ( & parkinglot );
2005-07-25 17:31:53 +00:00
/* Wake up the (presumably select()ing) thread */
pthread_kill ( parking_thread , SIGURG );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds \n " , pu -> chan -> name , pu -> parkingnum , parking_con , pu -> context , pu -> exten , pu -> priority , ( pu -> parkingtime / 1000 ));
2005-07-25 17:31:53 +00:00
2006-06-26 16:43:21 +00:00
if ( pu -> parkingnum != - 1 )
snprintf ( pu -> parkingexten , sizeof ( pu -> parkingexten ), "%d" , x );
2005-07-25 17:31:53 +00:00
manager_event ( EVENT_FLAG_CALL , "ParkedCall" ,
2006-06-26 16:43:21 +00:00
"Exten: %s \r\n "
2005-07-25 17:31:53 +00:00
"Channel: %s \r\n "
"From: %s \r\n "
"Timeout: %ld \r\n "
2006-10-02 20:35:16 +00:00
"CallerIDNum: %s \r\n "
2006-04-21 10:47:07 +00:00
"CallerIDName: %s \r\n " ,
2006-06-26 16:43:21 +00:00
pu -> parkingexten , pu -> chan -> name , peer ? peer -> name : "" ,
2006-04-21 10:47:07 +00:00
( long ) pu -> start . tv_sec + ( long )( pu -> parkingtime / 1000 ) - ( long ) time ( NULL ),
S_OR ( pu -> chan -> cid . cid_num , "<unknown>" ),
S_OR ( pu -> chan -> cid . cid_name , "<unknown>" )
2005-07-25 17:31:53 +00:00
);
2003-07-02 14:06:12 +00:00
2006-09-20 04:34:51 +00:00
if ( peer && adsipark && ast_adsi_available ( peer )) {
2006-06-26 16:43:21 +00:00
adsi_announce_park ( peer , pu -> parkingexten ); /* Only supports parking numbers */
2006-09-20 04:34:51 +00:00
ast_adsi_unload_session ( peer );
2005-07-25 17:31:53 +00:00
}
2006-06-21 07:49:29 +00:00
2005-07-25 17:31:53 +00:00
con = ast_context_find ( parking_con );
2006-06-21 07:49:29 +00:00
if ( ! con )
2005-07-25 17:31:53 +00:00
con = ast_context_create ( NULL , parking_con , registrar );
2006-06-21 07:49:29 +00:00
if ( ! con ) /* Still no context? Bad */
ast_log ( LOG_ERROR , "Parking context '%s' does not exist and unable to create \n " , parking_con );
/* Tell the peer channel the number of the parking space */
2007-09-05 20:58:19 +00:00
if ( peer && pu -> parkingnum != - 1 ) { /* Only say number if it's a number */
/* Make sure we don't start saying digits to the channel being parked */
ast_set_flag ( peer , AST_FLAG_MASQ_NOSTREAM );
2005-07-25 17:31:53 +00:00
ast_say_digits ( peer , pu -> parkingnum , "" , peer -> language );
2007-09-05 20:58:19 +00:00
ast_clear_flag ( peer , AST_FLAG_MASQ_NOSTREAM );
}
2007-06-19 13:04:24 +00:00
if ( con ) {
2007-09-17 18:57:56 +00:00
if ( ! ast_add_extension2 ( con , 1 , pu -> parkingexten , 1 , NULL , NULL , parkedcall , ast_strdup ( pu -> parkingexten ), ast_free_ptr , registrar ))
2007-08-10 16:24:11 +00:00
notify_metermaids ( pu -> parkingexten , parking_con , AST_DEVICE_INUSE );
2007-06-19 13:04:24 +00:00
}
2005-07-25 17:31:53 +00:00
if ( pu -> notquiteyet ) {
/* Wake up parking thread if we're really done */
2006-07-19 20:44:39 +00:00
ast_indicate_data ( pu -> chan , AST_CONTROL_HOLD ,
S_OR ( parkmohclass , NULL ),
! ast_strlen_zero ( parkmohclass ) ? strlen ( parkmohclass ) + 1 : 0 );
2005-07-25 17:31:53 +00:00
pu -> notquiteyet = 0 ;
pthread_kill ( parking_thread , SIGURG );
2001-12-27 11:07:33 +00:00
}
return 0 ;
}
2007-08-07 23:04:01 +00:00
/* Park call via masquraded channel */
2003-02-02 19:37:23 +00:00
int ast_masq_park_call ( struct ast_channel * rchan , struct ast_channel * peer , int timeout , int * extout )
2001-12-27 11:07:33 +00:00
{
struct ast_channel * chan ;
struct ast_frame * f ;
2005-07-25 17:31:53 +00:00
2001-12-27 11:07:33 +00:00
/* Make a new, fake channel that we'll use to masquerade in the real one */
2007-04-10 05:41:34 +00:00
if ( ! ( chan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , rchan -> accountcode , rchan -> exten , rchan -> context , rchan -> amaflags , "Parked/%s" , rchan -> name ))) {
2001-12-27 11:07:33 +00:00
ast_log ( LOG_WARNING , "Unable to create parked channel \n " );
return - 1 ;
}
2006-06-21 07:49:29 +00:00
/* Make formats okay */
chan -> readformat = rchan -> readformat ;
chan -> writeformat = rchan -> writeformat ;
ast_channel_masquerade ( chan , rchan );
/* Setup the extensions and such */
set_c_e_p ( chan , rchan -> context , rchan -> exten , rchan -> priority );
/* Make the masq execute */
f = ast_read ( chan );
if ( f )
ast_frfree ( f );
ast_park_call ( chan , peer , timeout , extout );
2001-12-27 11:07:33 +00:00
return 0 ;
}
2005-01-04 04:01:40 +00:00
#define FEATURE_RETURN_HANGUP -1
#define FEATURE_RETURN_SUCCESSBREAK 0
#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
#define FEATURE_RETURN_PASSDIGITS 21
#define FEATURE_RETURN_STOREDIGITS 22
#define FEATURE_RETURN_SUCCESS 23
2007-09-17 16:58:23 +00:00
#define FEATURE_RETURN_KEEPTRYING 24
2005-01-04 04:01:40 +00:00
#define FEATURE_SENSE_CHAN (1 << 0)
#define FEATURE_SENSE_PEER (1 << 1)
2005-08-23 02:22:33 +00:00
2007-08-07 23:04:01 +00:00
/*!
* \brief set caller and callee according to the direction
* \param caller, callee, peer, chan, sense
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Detect who triggered feature and set callee/caller variables accordingly
*/
2006-04-16 19:26:57 +00:00
static void set_peers ( struct ast_channel ** caller , struct ast_channel ** callee ,
struct ast_channel * peer , struct ast_channel * chan , int sense )
{
if ( sense == FEATURE_SENSE_PEER ) {
* caller = peer ;
* callee = chan ;
} else {
* callee = peer ;
* caller = chan ;
}
}
2005-01-04 04:01:40 +00:00
2007-08-07 23:04:01 +00:00
/*!
* \brief support routing for one touch call parking
* \param chan channel parking call
* \param peer channel to be parked
* \param config unsed
* \param code unused
2007-09-05 16:31:39 +00:00
* \param sense feature options
*
2007-08-23 20:20:17 +00:00
* \param data
2007-08-07 23:04:01 +00:00
* Setup channel, set return exten,priority to 's,1'
* answer chan, sleep chan, park call
*/
2007-08-23 20:20:17 +00:00
static int builtin_parkcall ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2006-05-22 16:43:43 +00:00
{
struct ast_channel * parker ;
struct ast_channel * parkee ;
2006-08-21 02:11:39 +00:00
int res = 0 ;
struct ast_module_user * u ;
2006-05-22 16:43:43 +00:00
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-05-22 16:43:43 +00:00
set_peers ( & parker , & parkee , peer , chan , sense );
/* Setup the exten/priority to be s/1 since we don't know
where this call should return */
strcpy ( chan -> exten , "s" );
chan -> priority = 1 ;
if ( chan -> _state != AST_STATE_UP )
res = ast_answer ( chan );
if ( ! res )
res = ast_safe_sleep ( chan , 1000 );
if ( ! res )
res = ast_park_call ( parkee , parker , 0 , NULL );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-05-22 16:43:43 +00:00
if ( ! res ) {
if ( sense == FEATURE_SENSE_CHAN )
res = AST_PBX_NO_HANGUP_PEER ;
else
res = AST_PBX_KEEPALIVE ;
}
return res ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Monitor a channel by DTMF
* \param chan channel requesting monitor
* \param peer channel to be monitored
* \param config
* \param code
2007-09-05 16:31:39 +00:00
* \param sense feature options
*
2007-08-23 20:20:17 +00:00
* \param data
2007-08-07 23:04:01 +00:00
* Check monitor app enabled, setup channels, both caller/callee chans not null
* get TOUCH_MONITOR variable for filename if exists, exec monitor app.
* \retval FEATURE_RETURN_SUCCESS on success.
* \retval -1 on error.
*/
2007-08-23 20:20:17 +00:00
static int builtin_automonitor ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2005-01-05 19:56:47 +00:00
{
2005-12-08 18:18:59 +00:00
char * caller_chan_id = NULL , * callee_chan_id = NULL , * args = NULL , * touch_filename = NULL ;
2005-01-10 04:03:30 +00:00
int x = 0 ;
size_t len ;
2006-04-18 13:05:48 +00:00
struct ast_channel * caller_chan , * callee_chan ;
2005-01-11 18:24:27 +00:00
2005-01-10 04:03:30 +00:00
if ( ! monitor_ok ) {
ast_log ( LOG_ERROR , "Cannot record the call. The monitor application is disabled. \n " );
return - 1 ;
}
2005-01-05 19:56:47 +00:00
2006-04-16 18:37:01 +00:00
if ( ! monitor_app && ! ( monitor_app = pbx_findapp ( "Monitor" ))) {
2006-06-26 16:43:21 +00:00
monitor_ok = 0 ;
2006-04-16 18:37:01 +00:00
ast_log ( LOG_ERROR , "Cannot record the call. The monitor application is disabled. \n " );
return - 1 ;
2005-01-10 04:03:30 +00:00
}
2006-04-16 19:26:57 +00:00
set_peers ( & caller_chan , & callee_chan , peer , chan , sense );
2005-01-10 04:03:30 +00:00
if ( ! ast_strlen_zero ( courtesytone )) {
2005-01-11 18:24:27 +00:00
if ( ast_autoservice_start ( callee_chan ))
2005-01-10 04:03:30 +00:00
return - 1 ;
2006-11-17 23:18:51 +00:00
if ( ast_stream_and_wait ( caller_chan , courtesytone , "" )) {
2006-04-16 22:46:00 +00:00
ast_log ( LOG_WARNING , "Failed to play courtesy tone! \n " );
ast_autoservice_stop ( callee_chan );
return - 1 ;
2005-01-05 22:38:07 +00:00
}
2005-01-11 18:24:27 +00:00
if ( ast_autoservice_stop ( callee_chan ))
2005-01-10 04:03:30 +00:00
return - 1 ;
}
2005-01-11 18:24:27 +00:00
if ( callee_chan -> monitor ) {
2007-07-26 15:49:18 +00:00
ast_verb ( 4 , "User hit '%s' to stop recording call. \n " , code );
2005-01-11 18:24:27 +00:00
ast_monitor_stop ( callee_chan , 1 );
2005-01-05 19:56:47 +00:00
return FEATURE_RETURN_SUCCESS ;
}
2005-01-11 18:24:27 +00:00
if ( caller_chan && callee_chan ) {
2005-12-03 19:25:33 +00:00
const char * touch_format = pbx_builtin_getvar_helper ( caller_chan , "TOUCH_MONITOR_FORMAT" );
const char * touch_monitor = pbx_builtin_getvar_helper ( caller_chan , "TOUCH_MONITOR" );
2007-10-15 20:08:04 +00:00
const char * touch_monitor_prefix = pbx_builtin_getvar_helper ( caller_chan , "TOUCH_MONITOR_PREFIX" );
2005-12-03 19:25:33 +00:00
2005-05-16 00:43:16 +00:00
if ( ! touch_format )
touch_format = pbx_builtin_getvar_helper ( callee_chan , "TOUCH_MONITOR_FORMAT" );
2005-01-10 04:03:30 +00:00
if ( ! touch_monitor )
2005-01-11 18:24:27 +00:00
touch_monitor = pbx_builtin_getvar_helper ( callee_chan , "TOUCH_MONITOR" );
2005-12-08 18:18:59 +00:00
2007-10-15 20:08:04 +00:00
if ( ! touch_monitor_prefix )
touch_monitor_prefix = pbx_builtin_getvar_helper ( callee_chan , "TOUCH_MONITOR_PREFIX" );
2005-01-10 04:03:30 +00:00
if ( touch_monitor ) {
len = strlen ( touch_monitor ) + 50 ;
args = alloca ( len );
2005-12-08 18:18:59 +00:00
touch_filename = alloca ( len );
2007-10-15 20:08:04 +00:00
snprintf ( touch_filename , len , "%s-%ld-%s" , S_OR ( touch_monitor_prefix , "auto" ), ( long ) time ( NULL ), touch_monitor );
snprintf ( args , len , "%s|%s|m" , S_OR ( touch_format , "wav" ), touch_filename );
2005-01-10 04:03:30 +00:00
} else {
2006-04-21 10:47:07 +00:00
caller_chan_id = ast_strdupa ( S_OR ( caller_chan -> cid . cid_num , caller_chan -> name ));
callee_chan_id = ast_strdupa ( S_OR ( callee_chan -> cid . cid_num , callee_chan -> name ));
2005-01-11 18:24:27 +00:00
len = strlen ( caller_chan_id ) + strlen ( callee_chan_id ) + 50 ;
2005-01-10 04:03:30 +00:00
args = alloca ( len );
2005-12-08 18:18:59 +00:00
touch_filename = alloca ( len );
2007-10-15 20:08:04 +00:00
snprintf ( touch_filename , len , "%s-%ld-%s-%s" , S_OR ( touch_monitor_prefix , "auto" ), ( long ) time ( NULL ), caller_chan_id , callee_chan_id );
2006-04-21 10:47:07 +00:00
snprintf ( args , len , "%s|%s|m" , S_OR ( touch_format , "wav" ), touch_filename );
2005-01-10 04:03:30 +00:00
}
2007-04-10 20:44:44 +00:00
for ( x = 0 ; x < strlen ( args ); x ++ ) {
2005-01-10 04:03:30 +00:00
if ( args [ x ] == '/' )
args [ x ] = '-' ;
2006-04-14 23:20:29 +00:00
}
2005-01-10 04:03:30 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 4 , "User hit '%s' to record call. filename: %s \n " , code , args );
2005-01-10 04:03:30 +00:00
2006-03-30 21:29:39 +00:00
pbx_exec ( callee_chan , monitor_app , args );
2005-12-08 18:18:59 +00:00
pbx_builtin_setvar_helper ( callee_chan , "TOUCH_MONITOR_OUTPUT" , touch_filename );
pbx_builtin_setvar_helper ( caller_chan , "TOUCH_MONITOR_OUTPUT" , touch_filename );
2005-01-10 04:03:30 +00:00
return FEATURE_RETURN_SUCCESS ;
}
ast_log ( LOG_NOTICE , "Cannot record the call. One or both channels have gone away. \n " );
2005-01-05 19:56:47 +00:00
return - 1 ;
}
2007-08-23 20:20:17 +00:00
static int builtin_disconnect ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2005-01-04 04:01:40 +00:00
{
2007-07-26 15:49:18 +00:00
ast_verb ( 4 , "User hit '%s' to disconnect call. \n " , code );
2005-01-04 04:01:40 +00:00
return FEATURE_RETURN_HANGUP ;
}
2006-04-16 19:26:57 +00:00
static int finishup ( struct ast_channel * chan )
{
ast_indicate ( chan , AST_CONTROL_UNHOLD );
2006-07-19 20:44:39 +00:00
return ast_autoservice_stop ( chan );
2006-04-16 19:26:57 +00:00
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Find the context for the transfer
* \param transferer
* \param transferee
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Grab the TRANSFER_CONTEXT, if fails try grabbing macrocontext.
* \return a context string
*/
2006-04-16 19:26:57 +00:00
static const char * real_ctx ( struct ast_channel * transferer , struct ast_channel * transferee )
{
2006-05-26 17:59:29 +00:00
const char * s = pbx_builtin_getvar_helper ( transferer , "TRANSFER_CONTEXT" );
2006-04-16 19:26:57 +00:00
if ( ast_strlen_zero ( s ))
2006-05-26 17:59:29 +00:00
s = pbx_builtin_getvar_helper ( transferee , "TRANSFER_CONTEXT" );
2006-04-16 19:26:57 +00:00
if ( ast_strlen_zero ( s )) /* Use the non-macro context to transfer the call XXX ? */
s = transferer -> macrocontext ;
if ( ast_strlen_zero ( s ))
s = transferer -> context ;
return s ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Blind transfer user to another extension
2007-09-05 16:31:39 +00:00
* \param chan channel to be transfered
* \param peer channel initiated blind transfer
2007-08-07 23:04:01 +00:00
* \param config
* \param code
2007-08-23 20:20:17 +00:00
* \param data
2007-09-05 16:31:39 +00:00
* \param sense feature options
*
* Place chan on hold, check if transferred to parkinglot extension,
2007-08-07 23:04:01 +00:00
* otherwise check extension exists and transfer caller.
* \retval FEATURE_RETURN_SUCCESS.
* \retval -1 on failure.
*/
2007-08-23 20:20:17 +00:00
static int builtin_blindtransfer ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2005-01-04 04:01:40 +00:00
{
struct ast_channel * transferer ;
struct ast_channel * transferee ;
2005-12-03 19:25:33 +00:00
const char * transferer_real_context ;
2006-04-16 19:56:08 +00:00
char xferto [ 256 ];
2005-01-04 04:01:40 +00:00
int res ;
2006-04-16 19:26:57 +00:00
set_peers ( & transferer , & transferee , peer , chan , sense );
transferer_real_context = real_ctx ( transferer , transferee );
2006-04-16 19:41:12 +00:00
/* Start autoservice on chan while we talk to the originator */
2005-01-04 04:01:40 +00:00
ast_autoservice_start ( transferee );
2006-07-19 20:44:39 +00:00
ast_indicate ( transferee , AST_CONTROL_HOLD );
2005-01-04 04:01:40 +00:00
2006-04-16 19:56:08 +00:00
memset ( xferto , 0 , sizeof ( xferto ));
2005-02-17 20:04:10 +00:00
2005-01-04 04:01:40 +00:00
/* Transfer */
2006-11-17 23:18:51 +00:00
res = ast_stream_and_wait ( transferer , "pbx-transfer" , AST_DIGIT_ANY );
2006-04-16 21:41:06 +00:00
if ( res < 0 ) {
finishup ( transferee );
return - 1 ; /* error ? */
2005-01-04 04:01:40 +00:00
}
2006-04-18 13:05:48 +00:00
if ( res > 0 ) /* If they've typed a digit already, handle it */
xferto [ 0 ] = ( char ) res ;
2005-01-04 04:01:40 +00:00
2005-02-17 20:04:10 +00:00
ast_stopstream ( transferer );
2006-04-16 19:56:08 +00:00
res = ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ), 100 , transferdigittimeout );
2006-04-16 21:41:06 +00:00
if ( res < 0 ) { /* hangup, would be 0 for invalid and 1 for valid */
finishup ( transferee );
2005-01-04 04:01:40 +00:00
return res ;
}
2006-04-16 19:56:08 +00:00
if ( ! strcmp ( xferto , ast_parking_ext ())) {
2006-04-16 21:41:06 +00:00
res = finishup ( transferee );
2005-01-17 12:37:55 +00:00
if ( res )
2005-01-04 04:01:40 +00:00
res = - 1 ;
2006-04-16 21:41:06 +00:00
else if ( ! ast_park_call ( transferee , transferer , 0 , NULL )) { /* success */
2005-01-04 04:01:40 +00:00
/* We return non-zero, but tell the PBX not to hang the channel when
the thread dies -- We have to be careful now though. We are responsible for
hanging up the channel, else it will never be hung up! */
2006-04-14 23:20:29 +00:00
return ( transferer == peer ) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER ;
2005-01-04 04:01:40 +00:00
} else {
ast_log ( LOG_WARNING , "Unable to park call %s \n " , transferee -> name );
}
2006-04-17 04:31:21 +00:00
/*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
2006-04-16 19:56:08 +00:00
} else if ( ast_exists_extension ( transferee , transferer_real_context , xferto , 1 , transferer -> cid . cid_num )) {
2007-03-30 14:37:21 +00:00
pbx_builtin_setvar_helper ( peer , "BLINDTRANSFER" , transferee -> name );
2005-01-04 04:01:40 +00:00
pbx_builtin_setvar_helper ( chan , "BLINDTRANSFER" , peer -> name );
2006-04-16 21:41:06 +00:00
res = finishup ( transferee );
2007-03-30 14:37:21 +00:00
if ( ! transferer -> cdr ) {
transferer -> cdr = ast_cdr_alloc ();
2007-03-30 17:57:47 +00:00
if ( transferer ) {
ast_cdr_init ( transferer -> cdr , transferer ); /* initilize our channel's cdr */
ast_cdr_start ( transferer -> cdr );
}
}
if ( transferer -> cdr ) {
ast_cdr_setdestchan ( transferer -> cdr , transferee -> name );
ast_cdr_setapp ( transferer -> cdr , "BLINDTRANSFER" , "" );
2007-03-30 14:37:21 +00:00
}
2007-08-14 15:30:03 +00:00
if ( ! transferee -> pbx ) {
/* Doh! Use our handy async_goto functions */
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1 \n "
, transferee -> name , xferto , transferer_real_context );
if ( ast_async_goto ( transferee , transferer_real_context , xferto , 1 ))
ast_log ( LOG_WARNING , "Async goto failed :-( \n " );
} else {
/* Set the channel's new extension, since it exists, using transferer context */
set_c_e_p ( transferee , transferer_real_context , xferto , 0 );
}
2005-04-22 02:55:14 +00:00
check_goto_on_transfer ( transferer );
2005-01-04 04:01:40 +00:00
return res ;
} else {
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , "Unable to find extension '%s' in context '%s' \n " , xferto , transferer_real_context );
2005-01-04 04:01:40 +00:00
}
2007-04-10 20:44:44 +00:00
if ( ast_stream_and_wait ( transferer , xferfailsound , AST_DIGIT_ANY ) < 0 ) {
2006-04-16 21:41:06 +00:00
finishup ( transferee );
return - 1 ;
2005-01-04 04:01:40 +00:00
}
ast_stopstream ( transferer );
2006-04-16 21:41:06 +00:00
res = finishup ( transferee );
2005-01-04 04:01:40 +00:00
if ( res ) {
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Hungup during autoservice stop on '%s' \n " , transferee -> name );
2005-01-04 04:01:40 +00:00
return res ;
}
return FEATURE_RETURN_SUCCESS ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief make channels compatible
* \param c
* \param newchan
* \retval 0 on success.
* \retval -1 on failure.
*/
2006-04-18 13:05:48 +00:00
static int check_compat ( struct ast_channel * c , struct ast_channel * newchan )
{
if ( ast_channel_make_compatible ( c , newchan ) < 0 ) {
ast_log ( LOG_WARNING , "Had to drop call because I couldn't make %s compatible with %s \n " ,
c -> name , newchan -> name );
ast_hangup ( newchan );
return - 1 ;
}
return 0 ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Attended transfer
2007-09-05 16:31:39 +00:00
* \param chan transfered user
* \param peer person transfering call
2007-08-07 23:04:01 +00:00
* \param config
* \param code
2007-09-05 16:31:39 +00:00
* \param sense feature options
*
2007-08-23 20:20:17 +00:00
* \param data
2007-08-07 23:04:01 +00:00
* Get extension to transfer to, if you cannot generate channel (or find extension)
* return to host channel. After called channel answered wait for hangup of transferer,
* bridge call between transfer peer (taking them off hold) to attended transfer channel.
2007-09-05 16:31:39 +00:00
*
* \return -1 on failure
2007-08-07 23:04:01 +00:00
*/
2007-08-23 20:20:17 +00:00
static int builtin_atxfer ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2005-01-05 19:56:47 +00:00
{
struct ast_channel * transferer ;
struct ast_channel * transferee ;
2005-12-03 19:25:33 +00:00
const char * transferer_real_context ;
2006-07-19 20:44:39 +00:00
char xferto [ 256 ] = "" ;
2007-05-01 22:24:51 +00:00
char callbackto [ 256 ] = "" ;
2005-01-05 19:56:47 +00:00
int res ;
2006-04-18 13:05:48 +00:00
int outstate = 0 ;
struct ast_channel * newchan ;
struct ast_channel * xferchan ;
2005-01-05 19:56:47 +00:00
struct ast_bridge_thread_obj * tobj ;
2006-04-18 13:05:48 +00:00
struct ast_bridge_config bconfig ;
struct ast_frame * f ;
int l ;
2005-01-05 19:56:47 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Executing Attended Transfer %s, %s (sense=%d) \n " , chan -> name , peer -> name , sense );
2006-04-16 19:26:57 +00:00
set_peers ( & transferer , & transferee , peer , chan , sense );
transferer_real_context = real_ctx ( transferer , transferee );
2006-04-16 19:41:12 +00:00
/* Start autoservice on chan while we talk to the originator */
2005-01-05 19:56:47 +00:00
ast_autoservice_start ( transferee );
2006-07-19 20:44:39 +00:00
ast_indicate ( transferee , AST_CONTROL_HOLD );
2005-01-05 19:56:47 +00:00
/* Transfer */
2006-11-17 23:18:51 +00:00
res = ast_stream_and_wait ( transferer , "pbx-transfer" , AST_DIGIT_ANY );
2006-04-16 22:22:53 +00:00
if ( res < 0 ) {
finishup ( transferee );
return res ;
2006-04-18 13:05:48 +00:00
}
if ( res > 0 ) /* If they've typed a digit already, handle it */
2006-04-16 22:22:53 +00:00
xferto [ 0 ] = ( char ) res ;
2006-04-16 19:26:57 +00:00
2006-04-16 22:22:53 +00:00
/* this is specific of atxfer */
res = ast_app_dtget ( transferer , transferer_real_context , xferto , sizeof ( xferto ), 100 , transferdigittimeout );
2006-04-16 23:05:42 +00:00
if ( res < 0 ) { /* hangup, would be 0 for invalid and 1 for valid */
finishup ( transferee );
return res ;
}
if ( res == 0 ) {
2006-04-16 22:22:53 +00:00
ast_log ( LOG_WARNING , "Did not read data. \n " );
2006-04-16 23:05:42 +00:00
finishup ( transferee );
2006-11-17 23:18:51 +00:00
if ( ast_stream_and_wait ( transferer , "beeperr" , "" ))
2006-04-16 22:22:53 +00:00
return - 1 ;
2006-04-16 23:05:42 +00:00
return FEATURE_RETURN_SUCCESS ;
}
2006-04-18 13:05:48 +00:00
2006-04-16 23:05:42 +00:00
/* valid extension, res == 1 */
2006-04-18 13:05:48 +00:00
if ( ! ast_exists_extension ( transferer , transferer_real_context , xferto , 1 , transferer -> cid . cid_num )) {
ast_log ( LOG_WARNING , "Extension %s does not exist in context %s \n " , xferto , transferer_real_context );
finishup ( transferee );
2006-11-17 23:18:51 +00:00
if ( ast_stream_and_wait ( transferer , "beeperr" , "" ))
2006-04-18 13:05:48 +00:00
return - 1 ;
return FEATURE_RETURN_SUCCESS ;
}
l = strlen ( xferto );
snprintf ( xferto + l , sizeof ( xferto ) - l , "@%s/n" , transferer_real_context ); /* append context */
2007-05-01 22:24:51 +00:00
newchan = ast_feature_request_and_dial ( transferer , transferee , "Local" , ast_best_codec ( transferer -> nativeformats ),
xferto , atxfernoanswertimeout , & outstate , transferer -> cid . cid_num , transferer -> cid . cid_name , 1 );
if ( ! ast_check_hangup ( transferer )) {
/* Transferer is up - old behaviour */
ast_indicate ( transferer , - 1 );
if ( ! newchan ) {
finishup ( transferee );
/* any reason besides user requested cancel and busy triggers the failed sound */
if ( outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
2006-11-17 23:18:51 +00:00
ast_stream_and_wait ( transferer , xferfailsound , "" ))
2007-05-01 22:24:51 +00:00
return - 1 ;
if ( ast_stream_and_wait ( transferer , xfersound , "" ))
ast_log ( LOG_WARNING , "Failed to play transfer sound! \n " );
return FEATURE_RETURN_SUCCESS ;
}
2007-10-02 18:59:39 +00:00
if ( check_compat ( transferer , newchan )) {
/* we do mean transferee here, NOT transferer */
finishup ( transferee );
2006-04-18 13:05:48 +00:00
return - 1 ;
2007-10-02 18:59:39 +00:00
}
2007-05-01 22:24:51 +00:00
memset ( & bconfig , 0 , sizeof ( struct ast_bridge_config ));
ast_set_flag ( & ( bconfig . features_caller ), AST_FEATURE_DISCONNECT );
ast_set_flag ( & ( bconfig . features_callee ), AST_FEATURE_DISCONNECT );
res = ast_bridge_call ( transferer , newchan , & bconfig );
2007-08-01 15:39:54 +00:00
if ( ast_check_hangup ( newchan ) || ! ast_check_hangup ( transferer )) {
2007-05-01 22:24:51 +00:00
ast_hangup ( newchan );
if ( ast_stream_and_wait ( transferer , xfersound , "" ))
ast_log ( LOG_WARNING , "Failed to play transfer sound! \n " );
finishup ( transferee );
transferer -> _softhangup = 0 ;
return FEATURE_RETURN_SUCCESS ;
}
2007-10-02 18:59:39 +00:00
if ( check_compat ( transferee , newchan )) {
finishup ( transferee );
2007-05-01 22:24:51 +00:00
return - 1 ;
2007-10-02 18:59:39 +00:00
}
2007-05-01 22:24:51 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD );
if (( ast_autoservice_stop ( transferee ) < 0 )
|| ( ast_waitfordigit ( transferee , 100 ) < 0 )
|| ( ast_waitfordigit ( newchan , 100 ) < 0 )
|| ast_check_hangup ( transferee )
|| ast_check_hangup ( newchan )) {
ast_hangup ( newchan );
return - 1 ;
}
xferchan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , "" , "" , "" , 0 , "Transfered/%s" , transferee -> name );
if ( ! xferchan ) {
ast_hangup ( newchan );
return - 1 ;
}
/* Make formats okay */
xferchan -> readformat = transferee -> readformat ;
xferchan -> writeformat = transferee -> writeformat ;
ast_channel_masquerade ( xferchan , transferee );
ast_explicit_goto ( xferchan , transferee -> context , transferee -> exten , transferee -> priority );
xferchan -> _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL );
xferchan -> _softhangup = 0 ;
if (( f = ast_read ( xferchan )))
ast_frfree ( f );
newchan -> _state = AST_STATE_UP ;
ast_clear_flag ( newchan , AST_FLAGS_ALL );
newchan -> _softhangup = 0 ;
if ( ! ( tobj = ast_calloc ( 1 , sizeof ( * tobj )))) {
ast_hangup ( xferchan );
ast_hangup ( newchan );
return - 1 ;
}
tobj -> chan = xferchan ;
tobj -> peer = newchan ;
tobj -> bconfig = * config ;
2006-04-18 13:05:48 +00:00
2007-05-01 22:24:51 +00:00
if ( ast_stream_and_wait ( newchan , xfersound , "" ))
2006-05-08 11:22:00 +00:00
ast_log ( LOG_WARNING , "Failed to play transfer sound! \n " );
2007-05-01 22:24:51 +00:00
ast_bridge_call_thread_launch ( tobj );
return - 1 ; /* XXX meaning the channel is bridged ? */
} else if ( ! ast_check_hangup ( transferee )) {
/* act as blind transfer */
if ( ast_autoservice_stop ( transferee ) < 0 ) {
ast_hangup ( newchan );
return - 1 ;
}
2006-04-18 13:05:48 +00:00
2007-05-01 22:24:51 +00:00
if ( ! newchan ) {
unsigned int tries = 0 ;
2005-01-05 19:56:47 +00:00
2007-05-01 22:24:51 +00:00
/* newchan wasn't created - we should callback to transferer */
if ( ! ast_exists_extension ( transferer , transferer_real_context , transferer -> cid . cid_num , 1 , transferee -> cid . cid_num )) {
ast_log ( LOG_WARNING , "Extension %s does not exist in context %s - callback failed \n " , transferer -> cid . cid_num , transferer_real_context );
if ( ast_stream_and_wait ( transferee , "beeperr" , "" ))
return - 1 ;
return FEATURE_RETURN_SUCCESS ;
}
snprintf ( callbackto , sizeof ( callbackto ), "%s@%s/n" , transferer -> cid . cid_num , transferer_real_context ); /* append context */
newchan = ast_feature_request_and_dial ( transferee , NULL , "Local" , ast_best_codec ( transferee -> nativeformats ),
callbackto , atxfernoanswertimeout , & outstate , transferee -> cid . cid_num , transferee -> cid . cid_name , 0 );
while ( ! newchan && ! atxferdropcall && tries < atxfercallbackretries ) {
/* Trying to transfer again */
ast_autoservice_start ( transferee );
ast_indicate ( transferee , AST_CONTROL_HOLD );
newchan = ast_feature_request_and_dial ( transferer , transferee , "Local" , ast_best_codec ( transferer -> nativeformats ),
xferto , atxfernoanswertimeout , & outstate , transferer -> cid . cid_num , transferer -> cid . cid_name , 1 );
if ( ast_autoservice_stop ( transferee ) < 0 ) {
2007-09-17 02:20:40 +00:00
if ( newchan )
ast_hangup ( newchan );
2007-05-01 22:24:51 +00:00
return - 1 ;
}
if ( ! newchan ) {
/* Transfer failed, sleeping */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Sleeping for %d ms before callback. \n " , atxferloopdelay );
2007-05-01 22:24:51 +00:00
ast_safe_sleep ( transferee , atxferloopdelay );
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Trying to callback... \n " );
2007-05-01 22:24:51 +00:00
newchan = ast_feature_request_and_dial ( transferee , NULL , "Local" , ast_best_codec ( transferee -> nativeformats ),
callbackto , atxfernoanswertimeout , & outstate , transferee -> cid . cid_num , transferee -> cid . cid_name , 0 );
}
tries ++ ;
}
}
if ( ! newchan )
return - 1 ;
2006-04-18 13:05:48 +00:00
2007-05-01 22:24:51 +00:00
/* newchan is up, we should prepare transferee and bridge them */
2007-10-02 18:59:39 +00:00
if ( check_compat ( transferee , newchan )) {
finishup ( transferee );
2007-05-01 22:24:51 +00:00
return - 1 ;
2007-10-02 18:59:39 +00:00
}
2007-05-01 22:24:51 +00:00
ast_indicate ( transferee , AST_CONTROL_UNHOLD );
2006-04-18 13:05:48 +00:00
2007-05-01 22:24:51 +00:00
if (( ast_waitfordigit ( transferee , 100 ) < 0 )
|| ( ast_waitfordigit ( newchan , 100 ) < 0 )
|| ast_check_hangup ( transferee )
|| ast_check_hangup ( newchan )) {
ast_hangup ( newchan );
return - 1 ;
}
2006-04-18 13:05:48 +00:00
2007-05-01 22:24:51 +00:00
xferchan = ast_channel_alloc ( 0 , AST_STATE_DOWN , 0 , 0 , "" , "" , "" , 0 , "Transfered/%s" , transferee -> name );
if ( ! xferchan ) {
ast_hangup ( newchan );
return - 1 ;
}
/* Make formats okay */
xferchan -> readformat = transferee -> readformat ;
xferchan -> writeformat = transferee -> writeformat ;
ast_channel_masquerade ( xferchan , transferee );
ast_explicit_goto ( xferchan , transferee -> context , transferee -> exten , transferee -> priority );
xferchan -> _state = AST_STATE_UP ;
ast_clear_flag ( xferchan , AST_FLAGS_ALL );
xferchan -> _softhangup = 0 ;
if (( f = ast_read ( xferchan )))
ast_frfree ( f );
newchan -> _state = AST_STATE_UP ;
ast_clear_flag ( newchan , AST_FLAGS_ALL );
newchan -> _softhangup = 0 ;
if ( ! ( tobj = ast_calloc ( 1 , sizeof ( * tobj )))) {
ast_hangup ( xferchan );
ast_hangup ( newchan );
return - 1 ;
}
tobj -> chan = xferchan ;
tobj -> peer = newchan ;
tobj -> bconfig = * config ;
if ( ast_stream_and_wait ( newchan , xfersound , "" ))
ast_log ( LOG_WARNING , "Failed to play transfer sound! \n " );
ast_bridge_call_thread_launch ( tobj );
return - 1 ; /* XXX meaning the channel is bridged ? */
} else {
/* Transferee hung up */
finishup ( transferee );
2006-04-18 13:05:48 +00:00
return - 1 ;
}
2005-01-05 19:56:47 +00:00
}
/* add atxfer and automon as undefined so you can only use em if you configure them */
2007-05-01 22:24:51 +00:00
#define FEATURES_COUNT ARRAY_LEN(builtin_features)
2006-01-19 22:09:18 +00:00
2007-05-08 16:31:16 +00:00
AST_RWLOCK_DEFINE_STATIC ( features_lock );
static struct ast_call_feature builtin_features [] =
2007-05-31 18:21:47 +00:00
{
2006-08-29 21:20:43 +00:00
{ AST_FEATURE_REDIRECT , "Blind Transfer" , "blindxfer" , "#" , "#" , builtin_blindtransfer , AST_FEATURE_FLAG_NEEDSDTMF , "" },
{ AST_FEATURE_REDIRECT , "Attended Transfer" , "atxfer" , "" , "" , builtin_atxfer , AST_FEATURE_FLAG_NEEDSDTMF , "" },
{ AST_FEATURE_AUTOMON , "One Touch Monitor" , "automon" , "" , "" , builtin_automonitor , AST_FEATURE_FLAG_NEEDSDTMF , "" },
{ AST_FEATURE_DISCONNECT , "Disconnect Call" , "disconnect" , "*" , "*" , builtin_disconnect , AST_FEATURE_FLAG_NEEDSDTMF , "" },
{ AST_FEATURE_PARKCALL , "Park Call" , "parkcall" , "" , "" , builtin_parkcall , AST_FEATURE_FLAG_NEEDSDTMF , "" },
2005-01-04 04:01:40 +00:00
};
2005-08-23 02:22:33 +00:00
2006-05-11 20:07:44 +00:00
static AST_LIST_HEAD_STATIC ( feature_list , ast_call_feature );
2005-08-23 02:22:33 +00:00
2006-01-19 22:09:18 +00:00
/*! \brief register new feature into feature_list*/
2005-08-23 02:22:33 +00:00
void ast_register_feature ( struct ast_call_feature * feature )
{
if ( ! feature ) {
ast_log ( LOG_NOTICE , "You didn't pass a feature! \n " );
2007-06-06 18:43:12 +00:00
return ;
2005-08-23 02:22:33 +00:00
}
AST_LIST_LOCK ( & feature_list );
AST_LIST_INSERT_HEAD ( & feature_list , feature , feature_entry );
AST_LIST_UNLOCK ( & feature_list );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Registered Feature '%s' \n " , feature -> sname );
2005-08-23 02:22:33 +00:00
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Add new feature group
2007-09-05 16:31:39 +00:00
* \param fgname feature group name.
*
2007-08-07 23:04:01 +00:00
* Add new feature group to the feature group list insert at head of list.
* \note This function MUST be called while feature_groups is locked.
*/
2007-05-31 18:21:47 +00:00
static struct feature_group * register_group ( const char * fgname )
{
struct feature_group * fg ;
if ( ! fgname ) {
ast_log ( LOG_NOTICE , "You didn't pass a new group name! \n " );
return NULL ;
}
if ( ! ( fg = ast_calloc ( 1 , sizeof ( * fg ))))
return NULL ;
if ( ast_string_field_init ( fg , 128 )) {
2007-06-06 21:20:11 +00:00
ast_free ( fg );
2007-05-31 18:21:47 +00:00
return NULL ;
}
ast_string_field_set ( fg , gname , fgname );
AST_LIST_INSERT_HEAD ( & feature_groups , fg , entry );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Registered group '%s' \n " , fg -> gname );
2007-05-31 18:21:47 +00:00
return fg ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Add feature to group
* \param fg feature group
* \param exten
2007-09-05 16:31:39 +00:00
* \param feature feature to add.
*
2007-08-07 23:04:01 +00:00
* Check fg and feature specified, add feature to list
* \note This function MUST be called while feature_groups is locked.
*/
2007-05-31 18:21:47 +00:00
static void register_group_feature ( struct feature_group * fg , const char * exten , struct ast_call_feature * feature )
{
struct feature_group_exten * fge ;
if ( ! ( fge = ast_calloc ( 1 , sizeof ( * fge ))))
return ;
if ( ast_string_field_init ( fge , 128 )) {
2007-06-06 21:20:11 +00:00
ast_free ( fge );
2007-05-31 18:21:47 +00:00
return ;
}
if ( ! fg ) {
ast_log ( LOG_NOTICE , "You didn't pass a group! \n " );
return ;
}
if ( ! feature ) {
ast_log ( LOG_NOTICE , "You didn't pass a feature! \n " );
return ;
}
ast_string_field_set ( fge , exten , ( ast_strlen_zero ( exten ) ? feature -> exten : exten ));
fge -> feature = feature ;
AST_LIST_INSERT_HEAD ( & fg -> features , fge , entry );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Registered feature '%s' for group '%s' at exten '%s' \n " ,
2007-05-31 18:21:47 +00:00
feature -> sname , fg -> gname , exten );
}
2005-08-23 02:22:33 +00:00
void ast_unregister_feature ( struct ast_call_feature * feature )
{
2006-04-16 18:37:01 +00:00
if ( ! feature )
return ;
2005-08-23 02:22:33 +00:00
AST_LIST_LOCK ( & feature_list );
AST_LIST_REMOVE ( & feature_list , feature , feature_entry );
AST_LIST_UNLOCK ( & feature_list );
2007-06-06 21:20:11 +00:00
ast_free ( feature );
2005-08-23 02:22:33 +00:00
}
2006-06-26 16:43:21 +00:00
/*! \brief Remove all features in the list */
2005-08-23 14:40:03 +00:00
static void ast_unregister_features ( void )
{
struct ast_call_feature * feature ;
AST_LIST_LOCK ( & feature_list );
while (( feature = AST_LIST_REMOVE_HEAD ( & feature_list , feature_entry )))
2007-06-06 21:20:11 +00:00
ast_free ( feature );
2005-08-23 14:40:03 +00:00
AST_LIST_UNLOCK ( & feature_list );
}
2005-08-23 02:22:33 +00:00
2007-05-04 17:49:20 +00:00
/*! \brief find a call feature by name */
2007-05-08 16:54:02 +00:00
static struct ast_call_feature * find_dynamic_feature ( const char * name )
2005-08-23 02:22:33 +00:00
{
struct ast_call_feature * tmp ;
2005-09-07 21:36:30 +00:00
AST_LIST_TRAVERSE ( & feature_list , tmp , feature_entry ) {
if ( ! strcasecmp ( tmp -> sname , name ))
break ;
2005-08-23 02:22:33 +00:00
}
return tmp ;
}
2007-08-07 23:04:01 +00:00
/*! \brief Remove all feature groups in the list */
2007-05-31 18:21:47 +00:00
static void ast_unregister_groups ( void )
{
struct feature_group * fg ;
struct feature_group_exten * fge ;
AST_RWLIST_WRLOCK ( & feature_groups );
while (( fg = AST_LIST_REMOVE_HEAD ( & feature_groups , entry ))) {
while (( fge = AST_LIST_REMOVE_HEAD ( & fg -> features , entry ))) {
ast_string_field_free_all ( fge );
2007-06-06 21:20:11 +00:00
ast_free ( fge );
2007-05-31 18:21:47 +00:00
}
ast_string_field_free_all ( fg );
2007-06-06 21:20:11 +00:00
ast_free ( fg );
2007-05-31 18:21:47 +00:00
}
AST_RWLIST_UNLOCK ( & feature_groups );
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Find a group by name
* \param name feature name
* \retval feature group on success.
* \retval NULL on failure.
*/
2007-05-31 18:21:47 +00:00
static struct feature_group * find_group ( const char * name ) {
struct feature_group * fg = NULL ;
AST_LIST_TRAVERSE ( & feature_groups , fg , entry ) {
if ( ! strcasecmp ( fg -> gname , name ))
break ;
}
return fg ;
}
2007-05-08 16:41:35 +00:00
void ast_rdlock_call_features ( void )
{
ast_rwlock_rdlock ( & features_lock );
}
void ast_unlock_call_features ( void )
{
ast_rwlock_unlock ( & features_lock );
}
struct ast_call_feature * ast_find_call_feature ( const char * name )
2007-05-04 18:47:19 +00:00
{
int x ;
for ( x = 0 ; x < FEATURES_COUNT ; x ++ ) {
2007-05-08 16:41:35 +00:00
if ( ! strcasecmp ( name , builtin_features [ x ]. sname ))
2007-05-04 18:47:19 +00:00
return & builtin_features [ x ];
}
return NULL ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief exec an app by feature
2007-09-05 16:31:39 +00:00
* \param chan,peer,config,code,sense,data
*
2007-08-07 23:04:01 +00:00
* Find a feature, determine which channel activated
* \retval FEATURE_RETURN_PBX_KEEPALIVE,FEATURE_RETURN_NO_HANGUP_PEER
* \retval -1 error.
* \retval -2 when an application cannot be found.
*/
2007-08-23 20:20:17 +00:00
static int feature_exec_app ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense , void * data )
2005-08-23 02:22:33 +00:00
{
struct ast_app * app ;
2007-08-23 20:20:17 +00:00
struct ast_call_feature * feature = data ;
2006-08-29 21:20:43 +00:00
struct ast_channel * work , * idle ;
2005-08-23 02:22:33 +00:00
int res ;
if ( ! feature ) { /* shouldn't ever happen! */
ast_log ( LOG_NOTICE , "Found feature before, but at execing we've lost it?? \n " );
return - 1 ;
}
2006-08-07 04:15:52 +00:00
if ( sense == FEATURE_SENSE_CHAN ) {
if ( ! ast_test_flag ( feature , AST_FEATURE_FLAG_BYCALLER ))
2007-09-17 16:58:23 +00:00
return FEATURE_RETURN_KEEPTRYING ;
2006-08-29 21:20:43 +00:00
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_ONSELF )) {
work = chan ;
idle = peer ;
} else {
work = peer ;
idle = chan ;
}
2005-08-23 02:22:33 +00:00
} else {
2006-08-07 04:15:52 +00:00
if ( ! ast_test_flag ( feature , AST_FEATURE_FLAG_BYCALLEE ))
2007-09-17 16:58:23 +00:00
return FEATURE_RETURN_KEEPTRYING ;
2006-08-29 21:20:43 +00:00
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_ONSELF )) {
work = peer ;
idle = chan ;
} else {
work = chan ;
idle = peer ;
}
2006-08-07 04:15:52 +00:00
}
if ( ! ( app = pbx_findapp ( feature -> app ))) {
2005-08-23 02:22:33 +00:00
ast_log ( LOG_WARNING , "Could not find application (%s) \n " , feature -> app );
2006-01-05 23:08:55 +00:00
return - 2 ;
2005-08-23 02:22:33 +00:00
}
2006-08-07 04:15:52 +00:00
2006-08-29 21:20:43 +00:00
ast_autoservice_start ( idle );
if ( ! ast_strlen_zero ( feature -> moh_class ))
ast_moh_start ( idle , feature -> moh_class , NULL );
2006-08-07 04:15:52 +00:00
res = pbx_exec ( work , app , feature -> app_args );
2006-08-29 21:20:43 +00:00
if ( ! ast_strlen_zero ( feature -> moh_class ))
ast_moh_stop ( idle );
ast_autoservice_stop ( idle );
2006-08-07 04:15:52 +00:00
if ( res == AST_PBX_KEEPALIVE )
return FEATURE_RETURN_PBX_KEEPALIVE ;
else if ( res == AST_PBX_NO_HANGUP_PEER )
return FEATURE_RETURN_NO_HANGUP_PEER ;
else if ( res )
return FEATURE_RETURN_SUCCESSBREAK ;
2005-08-23 02:22:33 +00:00
2006-04-17 04:31:21 +00:00
return FEATURE_RETURN_SUCCESS ; /*! \todo XXX should probably return res */
2005-08-23 02:22:33 +00:00
}
2005-01-04 04:01:40 +00:00
static void unmap_features ( void )
{
int x ;
2007-05-08 16:31:16 +00:00
ast_rwlock_wrlock ( & features_lock );
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x ++ )
2005-01-04 04:01:40 +00:00
strcpy ( builtin_features [ x ]. exten , builtin_features [ x ]. default_exten );
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2005-01-04 04:01:40 +00:00
}
static int remap_feature ( const char * name , const char * value )
{
2007-05-08 16:54:02 +00:00
int x , res = - 1 ;
2007-05-08 16:31:16 +00:00
ast_rwlock_wrlock ( & features_lock );
2007-05-08 16:54:02 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x ++ ) {
if ( strcasecmp ( builtin_features [ x ]. sname , name ))
continue ;
ast_copy_string ( builtin_features [ x ]. exten , value , sizeof ( builtin_features [ x ]. exten ));
2007-05-08 16:31:16 +00:00
res = 0 ;
2007-05-08 16:54:02 +00:00
break ;
2005-01-04 04:01:40 +00:00
}
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2005-01-04 04:01:40 +00:00
return res ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Check the dynamic features
* \param chan,peer,config,code,sense
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Lock features list, browse for code, unlock list
* \retval res on success.
* \retval -1 on failure.
*/
2005-01-04 04:01:40 +00:00
static int ast_feature_interpret ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config , char * code , int sense )
{
int x ;
2005-01-10 14:46:59 +00:00
struct ast_flags features ;
2005-01-04 04:01:40 +00:00
int res = FEATURE_RETURN_PASSDIGITS ;
2005-08-23 02:22:33 +00:00
struct ast_call_feature * feature ;
2007-05-31 18:21:47 +00:00
struct feature_group * fg = NULL ;
struct feature_group_exten * fge ;
2005-12-03 19:25:33 +00:00
const char * dynamic_features = pbx_builtin_getvar_helper ( chan , "DYNAMIC_FEATURES" );
2007-05-08 16:31:16 +00:00
char * tmp , * tok ;
2005-01-04 04:01:40 +00:00
if ( sense == FEATURE_SENSE_CHAN )
2007-05-31 18:21:47 +00:00
ast_copy_flags ( & features , & ( config -> features_caller ), AST_FLAGS_ALL );
2005-01-04 04:01:40 +00:00
else
2007-05-31 18:21:47 +00:00
ast_copy_flags ( & features , & ( config -> features_callee ), AST_FLAGS_ALL );
After some study, thought, comparing, etc. I've backed out the previous universal mod to make ast_flags a 64 bit thing. Instead, I added a 64-bit version of ast_flags (ast_flags64), and 64-bit versions of the test-flag, set-flag, etc. macros, and an app_parse_options64 routine, and I use these in app_dial alone, to eliminate the 30-option limit it had grown to meet. There is room now for 32 more options and flags. I was heavily tempted to implement some of the other ideas that were presented, but this solution does not intro any new versions of dial, doesn't have a different API, has a minimal/zero impact on code outside of dial, and doesn't seriously (I hope) affect the code structure of dial. It's the best I can think of right now. My goal was NOT to rewrite dial. I leave that to a future, coordinated effort.
2007-07-19 23:24:27 +00:00
ast_debug ( 3 , "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d \n " , chan -> name , peer -> name , sense , features . flags );
2005-09-07 21:36:30 +00:00
2007-05-08 16:31:16 +00:00
ast_rwlock_rdlock ( & features_lock );
for ( x = 0 ; x < FEATURES_COUNT ; x ++ ) {
2005-01-10 14:46:59 +00:00
if (( ast_test_flag ( & features , builtin_features [ x ]. feature_mask )) &&
2005-01-04 04:01:40 +00:00
! ast_strlen_zero ( builtin_features [ x ]. exten )) {
/* Feature is up for consideration */
if ( ! strcmp ( builtin_features [ x ]. exten , code )) {
2007-08-23 20:20:17 +00:00
res = builtin_features [ x ]. operation ( chan , peer , config , code , sense , NULL );
2005-01-04 04:01:40 +00:00
break ;
} else if ( ! strncmp ( builtin_features [ x ]. exten , code , strlen ( code ))) {
if ( res == FEATURE_RETURN_PASSDIGITS )
2005-09-07 21:36:30 +00:00
res = FEATURE_RETURN_STOREDIGITS ;
2005-08-23 02:22:33 +00:00
}
}
}
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2005-08-23 02:22:33 +00:00
2007-05-08 16:31:16 +00:00
if ( ast_strlen_zero ( dynamic_features ))
return res ;
2005-08-23 02:22:33 +00:00
2007-05-08 16:31:16 +00:00
tmp = ast_strdupa ( dynamic_features );
2005-09-07 21:36:30 +00:00
2007-05-08 16:31:16 +00:00
while (( tok = strsep ( & tmp , "#" ))) {
2007-05-31 18:21:47 +00:00
AST_RWLIST_RDLOCK ( & feature_groups );
fg = find_group ( tok );
2007-09-17 16:58:23 +00:00
if ( fg ) {
AST_LIST_TRAVERSE ( & fg -> features , fge , entry ) {
if ( strcasecmp ( fge -> exten , code ))
continue ;
res = fge -> feature -> operation ( chan , peer , config , code , sense , fge -> feature );
if ( res != FEATURE_RETURN_KEEPTRYING ) {
AST_RWLIST_UNLOCK ( & feature_groups );
break ;
}
res = FEATURE_RETURN_PASSDIGITS ;
}
if ( fge )
break ;
2007-05-31 18:21:47 +00:00
}
AST_RWLIST_UNLOCK ( & feature_groups );
AST_LIST_LOCK ( & feature_list );
if ( ! ( feature = find_dynamic_feature ( tok ))) {
AST_LIST_UNLOCK ( & feature_list );
continue ;
}
2007-05-08 16:31:16 +00:00
/* Feature is up for consideration */
if ( ! strcmp ( feature -> exten , code )) {
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Feature Found: %s exten: %s \n " , feature -> sname , tok );
2007-08-23 20:20:17 +00:00
res = feature -> operation ( chan , peer , config , code , sense , feature );
2007-09-17 16:58:23 +00:00
if ( res != FEATURE_RETURN_KEEPTRYING ) {
AST_LIST_UNLOCK ( & feature_list );
break ;
}
res = FEATURE_RETURN_PASSDIGITS ;
2007-05-08 16:31:16 +00:00
} else if ( ! strncmp ( feature -> exten , code , strlen ( code )))
res = FEATURE_RETURN_STOREDIGITS ;
2007-05-08 16:54:02 +00:00
AST_LIST_UNLOCK ( & feature_list );
2005-01-04 04:01:40 +00:00
}
2005-08-23 02:22:33 +00:00
2005-01-04 04:01:40 +00:00
return res ;
}
2005-09-07 21:36:30 +00:00
static void set_config_flags ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config )
2005-01-04 04:01:40 +00:00
{
int x ;
2005-09-07 21:36:30 +00:00
2007-05-08 16:31:16 +00:00
ast_clear_flag ( config , AST_FLAGS_ALL );
ast_rwlock_rdlock ( & features_lock );
2005-08-23 15:33:27 +00:00
for ( x = 0 ; x < FEATURES_COUNT ; x ++ ) {
2007-05-08 16:31:16 +00:00
if ( ! ast_test_flag ( builtin_features + x , AST_FEATURE_FLAG_NEEDSDTMF ))
continue ;
2005-09-07 21:36:30 +00:00
2007-05-08 16:31:16 +00:00
if ( ast_test_flag ( & ( config -> features_caller ), builtin_features [ x ]. feature_mask ))
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 );
if ( ast_test_flag ( & ( config -> features_callee ), builtin_features [ x ]. feature_mask ))
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 );
2005-01-04 04:01:40 +00:00
}
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2005-09-07 21:36:30 +00:00
if ( chan && peer && ! ( ast_test_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 ) && ast_test_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 ))) {
2005-12-03 19:25:33 +00:00
const char * dynamic_features = pbx_builtin_getvar_helper ( chan , "DYNAMIC_FEATURES" );
2005-09-07 21:36:30 +00:00
if ( dynamic_features ) {
char * tmp = ast_strdupa ( dynamic_features );
char * tok ;
struct ast_call_feature * feature ;
/* while we have a feature */
2006-05-10 13:22:15 +00:00
while (( tok = strsep ( & tmp , "#" ))) {
2007-05-08 16:54:02 +00:00
AST_LIST_LOCK ( & feature_list );
if (( feature = find_dynamic_feature ( tok )) && ast_test_flag ( feature , AST_FEATURE_FLAG_NEEDSDTMF )) {
2006-08-07 04:15:52 +00:00
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_BYCALLER ))
2006-04-16 18:37:01 +00:00
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_0 );
2006-08-07 04:15:52 +00:00
if ( ast_test_flag ( feature , AST_FEATURE_FLAG_BYCALLEE ))
2006-04-16 18:37:01 +00:00
ast_set_flag ( config , AST_BRIDGE_DTMF_CHANNEL_1 );
2005-09-07 21:36:30 +00:00
}
2007-05-08 16:54:02 +00:00
AST_LIST_UNLOCK ( & feature_list );
2005-09-07 21:36:30 +00:00
}
}
}
2005-01-04 04:01:40 +00:00
}
2007-08-07 23:04:01 +00:00
/*!
2007-09-05 16:31:39 +00:00
* \brief Get feature and dial
2007-08-07 23:04:01 +00:00
* \param caller,transferee,type,format,data,timeout,outstate,cid_num,cid_name,igncallerstate
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Request channel, set channel variables, initiate call,check if they want to disconnect
* go into loop, check if timeout has elapsed, check if person to be transfered hung up,
* check for answer break loop, set cdr return channel.
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* \todo XXX Check - this is very similar to the code in channel.c
* \return always a channel
*/
2007-05-01 22:24:51 +00:00
static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel * caller , struct ast_channel * transferee , const char * type , int format , void * data , int timeout , int * outstate , const char * cid_num , const char * cid_name , int igncallerstate )
2005-06-23 22:12:01 +00:00
{
int state = 0 ;
int cause = 0 ;
int to ;
struct ast_channel * chan ;
struct ast_channel * monitor_chans [ 2 ];
struct ast_channel * active_channel ;
int res = 0 , ready = 0 ;
if (( chan = ast_request ( type , format , data , & cause ))) {
ast_set_callerid ( chan , cid_num , cid_name , cid_num );
2005-11-01 20:29:36 +00:00
ast_channel_inherit_variables ( caller , chan );
2006-05-26 17:59:29 +00:00
pbx_builtin_setvar_helper ( chan , "TRANSFERERNAME" , caller -> name );
2007-03-30 14:37:21 +00:00
if ( ! chan -> cdr ) {
chan -> cdr = ast_cdr_alloc ();
2007-03-30 17:57:47 +00:00
if ( chan -> cdr ) {
ast_cdr_init ( chan -> cdr , chan ); /* initilize our channel's cdr */
ast_cdr_start ( chan -> cdr );
}
2007-03-30 14:37:21 +00:00
}
2005-06-23 22:12:01 +00:00
if ( ! ast_call ( chan , data , timeout )) {
2005-07-15 23:00:47 +00:00
struct timeval started ;
2005-06-23 22:12:01 +00:00
int x , len = 0 ;
char * disconnect_code = NULL , * dialed_code = NULL ;
ast_indicate ( caller , AST_CONTROL_RINGING );
/* support dialing of the featuremap disconnect code while performing an attended tranfer */
2007-05-08 16:31:16 +00:00
ast_rwlock_rdlock ( & features_lock );
for ( x = 0 ; x < FEATURES_COUNT ; x ++ ) {
2005-06-23 22:12:01 +00:00
if ( strcasecmp ( builtin_features [ x ]. sname , "disconnect" ))
continue ;
disconnect_code = builtin_features [ x ]. exten ;
len = strlen ( disconnect_code ) + 1 ;
dialed_code = alloca ( len );
memset ( dialed_code , 0 , len );
break ;
}
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2005-06-23 22:12:01 +00:00
x = 0 ;
2005-07-15 23:00:47 +00:00
started = ast_tvnow ();
2005-06-23 22:12:01 +00:00
to = timeout ;
2007-08-28 14:37:09 +00:00
ast_poll_channel_add ( caller , chan );
2007-08-01 15:39:54 +00:00
while ( ! (( transferee && ast_check_hangup ( transferee )) && ( ! igncallerstate && ast_check_hangup ( caller ))) && timeout && ( chan -> _state != AST_STATE_UP )) {
2006-04-16 20:09:01 +00:00
struct ast_frame * f = NULL ;
2005-06-23 22:12:01 +00:00
monitor_chans [ 0 ] = caller ;
monitor_chans [ 1 ] = chan ;
active_channel = ast_waitfor_n ( monitor_chans , 2 , & to );
/* see if the timeout has been violated */
2005-07-15 23:00:47 +00:00
if ( ast_tvdiff_ms ( ast_tvnow (), started ) > timeout ) {
2005-06-23 22:12:01 +00:00
state = AST_CONTROL_UNHOLD ;
ast_log ( LOG_NOTICE , "We exceeded our AT-timeout \n " );
break ; /*doh! timeout*/
}
2006-04-16 20:09:01 +00:00
if ( ! active_channel )
2005-06-23 22:12:01 +00:00
continue ;
if ( chan && ( chan == active_channel )){
f = ast_read ( chan );
if ( f == NULL ) { /*doh! where'd he go?*/
state = AST_CONTROL_HANGUP ;
res = 0 ;
break ;
}
if ( f -> frametype == AST_FRAME_CONTROL || f -> frametype == AST_FRAME_DTMF || f -> frametype == AST_FRAME_TEXT ) {
if ( f -> subclass == AST_CONTROL_RINGING ) {
state = f -> subclass ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , "%s is ringing \n " , chan -> name );
2005-06-23 22:12:01 +00:00
ast_indicate ( caller , AST_CONTROL_RINGING );
} else if (( f -> subclass == AST_CONTROL_BUSY ) || ( f -> subclass == AST_CONTROL_CONGESTION )) {
state = f -> subclass ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , "%s is busy \n " , chan -> name );
2006-03-23 22:00:11 +00:00
ast_indicate ( caller , AST_CONTROL_BUSY );
2005-06-23 22:12:01 +00:00
ast_frfree ( f );
f = NULL ;
break ;
} else if ( f -> subclass == AST_CONTROL_ANSWER ) {
/* This is what we are hoping for */
state = f -> subclass ;
ast_frfree ( f );
f = NULL ;
ready = 1 ;
break ;
2007-08-31 14:40:21 +00:00
} else if ( f -> subclass != - 1 ) {
2005-06-23 22:12:01 +00:00
ast_log ( LOG_NOTICE , "Don't know what to do about control frame: %d \n " , f -> subclass );
}
/* else who cares */
}
} else if ( caller && ( active_channel == caller )) {
f = ast_read ( caller );
if ( f == NULL ) { /*doh! where'd he go?*/
2007-05-01 22:24:51 +00:00
if ( ! igncallerstate ) {
2007-08-01 15:39:54 +00:00
if ( ast_check_hangup ( caller ) && ! ast_check_hangup ( chan )) {
2007-05-01 22:24:51 +00:00
/* make this a blind transfer */
ready = 1 ;
break ;
}
state = AST_CONTROL_HANGUP ;
res = 0 ;
2005-06-23 22:12:01 +00:00
break ;
}
2007-05-01 22:24:51 +00:00
} else {
2005-06-23 22:12:01 +00:00
2007-05-01 22:24:51 +00:00
if ( f -> frametype == AST_FRAME_DTMF ) {
dialed_code [ x ++ ] = f -> subclass ;
2005-06-23 22:12:01 +00:00
dialed_code [ x ] = '\0' ;
2007-05-01 22:24:51 +00:00
if ( strlen ( dialed_code ) == len ) {
x = 0 ;
} else if ( x && strncmp ( dialed_code , disconnect_code , x )) {
x = 0 ;
dialed_code [ x ] = '\0' ;
}
if ( * dialed_code && ! strcmp ( dialed_code , disconnect_code )) {
/* Caller Canceled the call */
state = AST_CONTROL_UNHOLD ;
ast_frfree ( f );
f = NULL ;
break ;
}
2005-06-23 22:12:01 +00:00
}
}
}
2006-04-16 20:09:01 +00:00
if ( f )
2005-06-23 22:12:01 +00:00
ast_frfree ( f );
2006-04-16 20:09:01 +00:00
} /* end while */
2007-08-28 14:37:09 +00:00
ast_poll_channel_del ( caller , chan );
2005-06-23 22:12:01 +00:00
} else
ast_log ( LOG_NOTICE , "Unable to call channel %s/%s \n " , type , ( char * ) data );
} else {
ast_log ( LOG_NOTICE , "Unable to request channel %s/%s \n " , type , ( char * ) data );
switch ( cause ) {
case AST_CAUSE_BUSY :
state = AST_CONTROL_BUSY ;
break ;
case AST_CAUSE_CONGESTION :
state = AST_CONTROL_CONGESTION ;
break ;
}
}
ast_indicate ( caller , - 1 );
if ( chan && ready ) {
if ( chan -> _state == AST_STATE_UP )
state = AST_CONTROL_ANSWER ;
res = 0 ;
} else if ( chan ) {
res = - 1 ;
ast_hangup ( chan );
chan = NULL ;
} else {
res = - 1 ;
}
if ( outstate )
* outstate = state ;
if ( chan && res <= 0 ) {
2006-01-21 22:09:06 +00:00
if ( chan -> cdr || ( chan -> cdr = ast_cdr_alloc ())) {
2005-06-23 22:12:01 +00:00
char tmp [ 256 ];
ast_cdr_init ( chan -> cdr , chan );
snprintf ( tmp , 256 , "%s/%s" , type , ( char * ) data );
ast_cdr_setapp ( chan -> cdr , "Dial" , tmp );
ast_cdr_update ( chan );
ast_cdr_start ( chan -> cdr );
ast_cdr_end ( chan -> cdr );
/* If the cause wasn't handled properly */
if ( ast_cdr_disposition ( chan -> cdr , chan -> hangupcause ))
ast_cdr_failed ( chan -> cdr );
} else {
ast_log ( LOG_WARNING , "Unable to create Call Detail Record \n " );
}
}
return chan ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief bridge the call and set CDR
* \param chan,peer,config
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Set start time, check for two channels,check if monitor on
* check for feature activation, create new CDR
* \retval res on success.
* \retval -1 on failure to bridge.
*/
2004-04-26 23:22:34 +00:00
int ast_bridge_call ( struct ast_channel * chan , struct ast_channel * peer , struct ast_bridge_config * config )
2001-12-27 11:07:33 +00:00
{
/* Copy voice back and forth between the two channels. Give the peer
the ability to transfer calls with '#<extension' syntax. */
struct ast_frame * f ;
struct ast_channel * who ;
2005-01-04 04:01:40 +00:00
char chan_featurecode [ FEATURE_MAX_LEN + 1 ] = "" ;
char peer_featurecode [ FEATURE_MAX_LEN + 1 ] = "" ;
2001-12-27 11:07:33 +00:00
int res ;
2004-08-06 13:54:07 +00:00
int diff ;
2005-01-04 04:01:40 +00:00
int hasfeatures = 0 ;
int hadfeatures = 0 ;
2001-12-27 11:07:33 +00:00
struct ast_option_header * aoh ;
2005-01-04 04:01:40 +00:00
struct ast_bridge_config backup_config ;
2007-03-30 17:57:47 +00:00
struct ast_cdr * bridge_cdr ;
2004-09-17 03:49:57 +00:00
2005-01-04 04:01:40 +00:00
memset ( & backup_config , 0 , sizeof ( backup_config ));
2005-08-03 04:42:59 +00:00
config -> start_time = ast_tvnow ();
2004-10-27 22:01:33 +00:00
if ( chan && peer ) {
2004-10-28 15:53:36 +00:00
pbx_builtin_setvar_helper ( chan , "BRIDGEPEER" , peer -> name );
pbx_builtin_setvar_helper ( peer , "BRIDGEPEER" , chan -> name );
} else if ( chan )
2004-10-27 22:01:33 +00:00
pbx_builtin_setvar_helper ( chan , "BLINDTRANSFER" , NULL );
2004-09-17 14:15:11 +00:00
if ( monitor_ok ) {
2005-12-03 19:25:33 +00:00
const char * monitor_exec ;
struct ast_channel * src = NULL ;
2004-09-17 14:15:11 +00:00
if ( ! monitor_app ) {
if ( ! ( monitor_app = pbx_findapp ( "Monitor" )))
2004-09-17 03:49:57 +00:00
monitor_ok = 0 ;
2004-09-17 14:15:11 +00:00
}
if (( monitor_exec = pbx_builtin_getvar_helper ( chan , "AUTO_MONITOR" )))
2005-12-03 19:25:33 +00:00
src = chan ;
2004-09-17 14:15:11 +00:00
else if (( monitor_exec = pbx_builtin_getvar_helper ( peer , "AUTO_MONITOR" )))
2005-12-03 19:25:33 +00:00
src = peer ;
2006-01-17 18:20:33 +00:00
if ( monitor_app && src ) {
2005-12-03 19:25:33 +00:00
char * tmp = ast_strdupa ( monitor_exec );
2006-05-10 13:22:15 +00:00
pbx_exec ( src , monitor_app , tmp );
2005-12-03 19:25:33 +00:00
}
2004-09-17 03:49:57 +00:00
}
2005-09-07 21:36:30 +00:00
set_config_flags ( chan , peer , config );
2004-08-06 14:43:25 +00:00
config -> firstpass = 1 ;
2003-07-02 14:06:12 +00:00
2001-12-27 11:07:33 +00:00
/* Answer if need be */
2003-02-02 19:37:23 +00:00
if ( ast_answer ( chan ))
return - 1 ;
2001-12-27 11:07:33 +00:00
peer -> appl = "Bridged Call" ;
2006-04-16 20:32:14 +00:00
peer -> data = chan -> name ;
2005-07-25 17:31:53 +00:00
2004-02-03 16:57:00 +00:00
/* copy the userfield from the B-leg to A-leg if applicable */
2004-09-17 14:15:11 +00:00
if ( chan -> cdr && peer -> cdr && ! ast_strlen_zero ( peer -> cdr -> userfield )) {
2004-02-03 16:57:00 +00:00
char tmp [ 256 ];
2004-09-17 14:15:11 +00:00
if ( ! ast_strlen_zero ( chan -> cdr -> userfield )) {
2005-07-25 17:31:53 +00:00
snprintf ( tmp , sizeof ( tmp ), "%s;%s" , chan -> cdr -> userfield , peer -> cdr -> userfield );
2004-02-03 16:57:00 +00:00
ast_cdr_appenduserfield ( chan , tmp );
} else
ast_cdr_setuserfield ( chan , peer -> cdr -> userfield );
/* free the peer's cdr without ast_cdr_free complaining */
2007-06-06 21:20:11 +00:00
ast_free ( peer -> cdr );
2004-02-03 16:57:00 +00:00
peer -> cdr = NULL ;
}
2007-03-30 14:37:21 +00:00
2001-12-27 11:07:33 +00:00
for (;;) {
2006-04-14 23:20:29 +00:00
struct ast_channel * other ; /* used later */
2005-08-03 04:42:59 +00:00
2005-07-25 17:31:53 +00:00
res = ast_channel_bridge ( chan , peer , config , & f , & who );
2005-08-03 04:42:59 +00:00
if ( config -> feature_timer ) {
2004-08-06 13:54:07 +00:00
/* Update time limit for next pass */
2006-09-27 16:57:44 +00:00
diff = ast_tvdiff_ms ( ast_tvnow (), config -> start_time );
2005-08-03 04:42:59 +00:00
config -> feature_timer -= diff ;
2005-01-04 04:01:40 +00:00
if ( hasfeatures ) {
/* Running on backup config, meaning a feature might be being
activated, but that's no excuse to keep things going
indefinitely! */
2005-08-03 04:42:59 +00:00
if ( backup_config . feature_timer && (( backup_config . feature_timer -= diff ) <= 0 )) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Timed out, realtime this time! \n " );
2005-08-03 04:42:59 +00:00
config -> feature_timer = 0 ;
2005-01-04 04:01:40 +00:00
who = chan ;
if ( f )
ast_frfree ( f );
f = NULL ;
res = 0 ;
2005-08-03 04:42:59 +00:00
} else if ( config -> feature_timer <= 0 ) {
2005-01-04 04:01:40 +00:00
/* Not *really* out of time, just out of time for
digits to come in for features. */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Timed out for feature! \n " );
2005-01-04 04:01:40 +00:00
if ( ! ast_strlen_zero ( peer_featurecode )) {
2007-08-06 21:52:30 +00:00
ast_dtmf_stream ( chan , peer , peer_featurecode , 0 , 0 );
2005-01-04 04:01:40 +00:00
memset ( peer_featurecode , 0 , sizeof ( peer_featurecode ));
}
if ( ! ast_strlen_zero ( chan_featurecode )) {
2007-08-06 21:52:30 +00:00
ast_dtmf_stream ( peer , chan , chan_featurecode , 0 , 0 );
2005-01-04 04:01:40 +00:00
memset ( chan_featurecode , 0 , sizeof ( chan_featurecode ));
}
if ( f )
ast_frfree ( f );
hasfeatures = ! ast_strlen_zero ( chan_featurecode ) || ! ast_strlen_zero ( peer_featurecode );
if ( ! hasfeatures ) {
/* Restore original (possibly time modified) bridge config */
memcpy ( config , & backup_config , sizeof ( struct ast_bridge_config ));
memset ( & backup_config , 0 , sizeof ( backup_config ));
}
hadfeatures = hasfeatures ;
/* Continue as we were */
continue ;
2006-09-27 16:57:44 +00:00
} else if ( ! f ) {
/* The bridge returned without a frame and there is a feature in progress.
* However, we don't think the feature has quite yet timed out, so just
* go back into the bridge. */
continue ;
}
2005-01-04 04:01:40 +00:00
} else {
2005-08-03 04:42:59 +00:00
if ( config -> feature_timer <= 0 ) {
2005-01-04 04:01:40 +00:00
/* We ran out of time */
2005-08-03 04:42:59 +00:00
config -> feature_timer = 0 ;
2005-01-04 04:01:40 +00:00
who = chan ;
if ( f )
ast_frfree ( f );
f = NULL ;
res = 0 ;
}
2004-08-06 13:54:07 +00:00
}
}
2001-12-27 11:07:33 +00:00
if ( res < 0 ) {
2007-08-30 23:55:39 +00:00
if ( ! ast_test_flag ( chan , AST_FLAG_ZOMBIE ) && ! ast_test_flag ( peer , AST_FLAG_ZOMBIE ))
ast_log ( LOG_WARNING , "Bridge failed on channels %s and %s, res = %d \n " , chan -> name , peer -> name , res );
2001-12-27 11:07:33 +00:00
return - 1 ;
}
2006-04-16 18:37:01 +00:00
if ( ! f || ( f -> frametype == AST_FRAME_CONTROL &&
( f -> subclass == AST_CONTROL_HANGUP || f -> subclass == AST_CONTROL_BUSY ||
2007-04-10 20:44:44 +00:00
f -> subclass == AST_CONTROL_CONGESTION ))) {
2006-04-16 18:37:01 +00:00
res = - 1 ;
break ;
2001-12-27 11:07:33 +00:00
}
2006-04-14 23:20:29 +00:00
/* many things should be sent to the 'other' channel */
other = ( who == chan ) ? peer : chan ;
if ( f -> frametype == AST_FRAME_CONTROL ) {
2007-07-05 20:51:08 +00:00
switch ( f -> subclass ) {
case AST_CONTROL_RINGING :
case AST_CONTROL_FLASH :
case - 1 :
ast_indicate ( other , f -> subclass );
break ;
case AST_CONTROL_HOLD :
case AST_CONTROL_UNHOLD :
ast_indicate_data ( other , f -> subclass , f -> data , f -> datalen );
break ;
case AST_CONTROL_OPTION :
2006-04-14 23:20:29 +00:00
aoh = f -> data ;
/* Forward option Requests */
2007-07-05 20:51:08 +00:00
if ( aoh && aoh -> flag == AST_OPTION_FLAG_REQUEST ) {
ast_channel_setoption ( other , ntohs ( aoh -> option ), aoh -> data ,
f -> datalen - sizeof ( struct ast_option_header ), 0 );
}
break ;
2001-12-27 11:07:33 +00:00
}
2006-08-31 01:59:02 +00:00
} else if ( f -> frametype == AST_FRAME_DTMF_BEGIN ) {
/* eat it */
} else if ( f -> frametype == AST_FRAME_DTMF ) {
2005-01-04 04:01:40 +00:00
char * featurecode ;
int sense ;
2005-07-25 17:31:53 +00:00
2005-01-04 04:01:40 +00:00
hadfeatures = hasfeatures ;
/* This cannot overrun because the longest feature is one shorter than our buffer */
if ( who == chan ) {
sense = FEATURE_SENSE_CHAN ;
featurecode = chan_featurecode ;
} else {
sense = FEATURE_SENSE_PEER ;
featurecode = peer_featurecode ;
}
2006-04-17 04:31:21 +00:00
/*! append the event to featurecode. we rely on the string being zero-filled, and
* not overflowing it.
* \todo XXX how do we guarantee the latter ?
2006-04-14 23:20:29 +00:00
*/
2005-01-04 04:01:40 +00:00
featurecode [ strlen ( featurecode )] = f -> subclass ;
2006-05-23 16:37:40 +00:00
/* Get rid of the frame before we start doing "stuff" with the channels */
ast_frfree ( f );
f = NULL ;
2005-08-03 04:42:59 +00:00
config -> feature_timer = backup_config . feature_timer ;
2005-01-04 04:01:40 +00:00
res = ast_feature_interpret ( chan , peer , config , featurecode , sense );
switch ( res ) {
case FEATURE_RETURN_PASSDIGITS :
2007-08-06 21:52:30 +00:00
ast_dtmf_stream ( other , who , featurecode , 0 , 0 );
2005-01-04 04:01:40 +00:00
/* Fall through */
case FEATURE_RETURN_SUCCESS :
memset ( featurecode , 0 , sizeof ( chan_featurecode ));
break ;
}
if ( res >= FEATURE_RETURN_PASSDIGITS ) {
2001-12-27 11:07:33 +00:00
res = 0 ;
2006-05-23 16:37:40 +00:00
} else
2005-01-04 04:01:40 +00:00
break ;
hasfeatures = ! ast_strlen_zero ( chan_featurecode ) || ! ast_strlen_zero ( peer_featurecode );
if ( hadfeatures && ! hasfeatures ) {
/* Restore backup */
memcpy ( config , & backup_config , sizeof ( struct ast_bridge_config ));
memset ( & backup_config , 0 , sizeof ( struct ast_bridge_config ));
} else if ( hasfeatures ) {
if ( ! hadfeatures ) {
/* Backup configuration */
memcpy ( & backup_config , config , sizeof ( struct ast_bridge_config ));
/* Setup temporary config options */
config -> play_warning = 0 ;
2005-01-10 14:46:59 +00:00
ast_clear_flag ( & ( config -> features_caller ), AST_FEATURE_PLAY_WARNING );
2005-07-25 17:31:53 +00:00
ast_clear_flag ( & ( config -> features_callee ), AST_FEATURE_PLAY_WARNING );
2005-01-04 04:01:40 +00:00
config -> warning_freq = 0 ;
config -> warning_sound = NULL ;
config -> end_sound = NULL ;
config -> start_sound = NULL ;
config -> firstpass = 0 ;
}
2006-08-01 23:09:28 +00:00
config -> start_time = ast_tvnow ();
2005-08-03 04:42:59 +00:00
config -> feature_timer = featuredigittimeout ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Set time limit to %ld \n " , config -> feature_timer );
2001-12-27 11:07:33 +00:00
}
2005-01-04 04:01:40 +00:00
}
if ( f )
ast_frfree ( f );
2007-04-10 05:41:34 +00:00
2001-12-27 11:07:33 +00:00
}
2007-04-10 05:41:34 +00:00
/* arrange the cdrs */
bridge_cdr = ast_cdr_alloc ();
2007-03-30 17:57:47 +00:00
if ( bridge_cdr ) {
2007-04-10 05:41:34 +00:00
if ( chan -> cdr && peer -> cdr ) { /* both of them? merge */
ast_cdr_init ( bridge_cdr , chan ); /* seems more logicaller to use the destination as a base, but, really, it's random */
ast_cdr_start ( bridge_cdr ); /* now is the time to start */
/* absorb the channel cdr */
ast_cdr_merge ( bridge_cdr , chan -> cdr );
2007-06-23 03:32:59 +00:00
if ( ! ast_test_flag ( chan -> cdr , AST_CDR_FLAG_LOCKED ))
ast_cdr_discard ( chan -> cdr ); /* if locked cdrs are in chan, they are taken over in the merge */
2007-04-10 05:41:34 +00:00
/* absorb the peer cdr */
ast_cdr_merge ( bridge_cdr , peer -> cdr );
2007-09-06 15:21:45 +00:00
if ( ! ast_test_flag ( peer -> cdr , AST_CDR_FLAG_LOCKED ))
2007-06-23 03:32:59 +00:00
ast_cdr_discard ( peer -> cdr ); /* if locked cdrs are in peer, they are taken over in the merge */
2007-04-10 05:41:34 +00:00
peer -> cdr = NULL ;
chan -> cdr = bridge_cdr ; /* make this available to the rest of the world via the chan while the call is in progress */
} else if ( chan -> cdr ) {
/* take the cdr from the channel - literally */
ast_cdr_init ( bridge_cdr , chan );
/* absorb this data */
ast_cdr_merge ( bridge_cdr , chan -> cdr );
2007-06-23 03:32:59 +00:00
if ( ! ast_test_flag ( chan -> cdr , AST_CDR_FLAG_LOCKED ))
ast_cdr_discard ( chan -> cdr ); /* if locked cdrs are in chan, they are taken over in the merge */
2007-04-10 05:41:34 +00:00
chan -> cdr = bridge_cdr ; /* make this available to the rest of the world via the chan while the call is in progress */
} else if ( peer -> cdr ) {
/* take the cdr from the peer - literally */
ast_cdr_init ( bridge_cdr , peer );
/* absorb this data */
ast_cdr_merge ( bridge_cdr , peer -> cdr );
2007-06-24 17:51:55 +00:00
if ( ! ast_test_flag ( peer -> cdr , AST_CDR_FLAG_LOCKED ))
ast_cdr_discard ( peer -> cdr ); /* if locked cdrs are in chan, they are taken over in the merge */
2007-04-10 05:41:34 +00:00
peer -> cdr = NULL ;
peer -> cdr = bridge_cdr ; /* make this available to the rest of the world via the chan while the call is in progress */
} else {
/* make up a new cdr */
ast_cdr_init ( bridge_cdr , chan ); /* eh, just pick one of them */
chan -> cdr = bridge_cdr ; /* */
}
if ( ast_strlen_zero ( bridge_cdr -> dstchannel )) {
if ( strcmp ( bridge_cdr -> channel , peer -> name ) != 0 )
ast_cdr_setdestchan ( bridge_cdr , peer -> name );
else
ast_cdr_setdestchan ( bridge_cdr , chan -> name );
}
2007-03-30 17:57:47 +00:00
}
2001-12-27 11:07:33 +00:00
return res ;
}
2007-02-18 15:01:07 +00:00
/*! \brief Output parking event to manager */
static void post_manager_event ( const char * s , struct parkeduser * pu )
2006-04-21 16:04:25 +00:00
{
manager_event ( EVENT_FLAG_CALL , s ,
2006-06-26 16:43:21 +00:00
"Exten: %s \r\n "
2006-04-21 16:04:25 +00:00
"Channel: %s \r\n "
2006-10-02 20:35:16 +00:00
"CallerIDNum: %s \r\n "
2006-04-21 16:04:25 +00:00
"CallerIDName: %s \r\n\r\n " ,
2007-02-18 15:01:07 +00:00
pu -> parkingexten ,
pu -> chan -> name ,
S_OR ( pu -> chan -> cid . cid_num , "<unknown>" ),
S_OR ( pu -> chan -> cid . cid_name , "<unknown>" )
2006-04-21 16:04:25 +00:00
);
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Take care of parked calls and unpark them if needed
2007-09-05 16:31:39 +00:00
* \param ignore unused var.
*
2007-08-07 23:04:01 +00:00
* Start inf loop, lock parking lot, check if any parked channels have gone above timeout
* if so, remove channel from parking lot and return it to the extension that parked it.
* Check if parked channel decided to hangup, wait until next FD via select().
*/
2001-12-27 11:07:33 +00:00
static void * do_parking_thread ( void * ignore )
{
2006-11-01 17:09:47 +00:00
char parkingslot [ AST_MAX_EXTENSION ];
2006-04-16 20:32:14 +00:00
fd_set rfds , efds ; /* results from previous select, to be preserved across loops. */
2006-11-01 17:09:47 +00:00
2001-12-27 11:07:33 +00:00
FD_ZERO ( & rfds );
FD_ZERO ( & efds );
2004-12-09 22:39:14 +00:00
2001-12-27 11:07:33 +00:00
for (;;) {
2007-07-09 02:29:00 +00:00
struct parkeduser * pu ;
2006-04-16 19:05:19 +00:00
int ms = - 1 ; /* select timeout, uninitialized */
int max = - 1 ; /* max fd, none there yet */
2006-04-16 20:32:14 +00:00
fd_set nrfds , nefds ; /* args for the next select */
2006-04-16 19:05:19 +00:00
FD_ZERO ( & nrfds );
FD_ZERO ( & nefds );
2007-07-09 02:29:00 +00:00
AST_LIST_LOCK ( & parkinglot );
AST_LIST_TRAVERSE_SAFE_BEGIN ( & parkinglot , pu , list ) {
2006-04-16 19:41:12 +00:00
struct ast_channel * chan = pu -> chan ; /* shorthand */
2006-04-16 19:05:19 +00:00
int tms ; /* timeout for this item */
int x ; /* fd index in channel */
struct ast_context * con ;
2007-07-09 02:29:00 +00:00
if ( pu -> notquiteyet ) /* Pretend this one isn't here yet */
2004-08-03 06:31:20 +00:00
continue ;
2005-07-15 23:00:47 +00:00
tms = ast_tvdiff_ms ( ast_tvnow (), pu -> start );
2003-02-02 19:37:23 +00:00
if ( tms > pu -> parkingtime ) {
2006-04-16 19:41:12 +00:00
ast_indicate ( chan , AST_CONTROL_UNHOLD );
2004-12-09 22:39:14 +00:00
/* Get chan, exten from derived kludge */
2004-12-28 23:49:46 +00:00
if ( pu -> peername [ 0 ]) {
2006-04-16 20:32:14 +00:00
char * peername = ast_strdupa ( pu -> peername );
char * cp = strrchr ( peername , '-' );
2004-12-28 23:49:46 +00:00
if ( cp )
* cp = 0 ;
2004-12-09 22:39:14 +00:00
con = ast_context_find ( parking_con_dial );
if ( ! con ) {
2004-12-28 23:49:46 +00:00
con = ast_context_create ( NULL , parking_con_dial , registrar );
2006-06-26 16:43:21 +00:00
if ( ! con )
2004-12-09 22:39:14 +00:00
ast_log ( LOG_ERROR , "Parking dial context '%s' does not exist and unable to create \n " , parking_con_dial );
}
if ( con ) {
2006-04-16 19:05:19 +00:00
char returnexten [ AST_MAX_EXTENSION ];
2007-08-17 16:04:20 +00:00
snprintf ( returnexten , sizeof ( returnexten ), "%s,,t" , peername );
2007-09-17 18:57:56 +00:00
ast_add_extension2 ( con , 1 , peername , 1 , NULL , NULL , "Dial" , ast_strdup ( returnexten ), ast_free_ptr , registrar );
2004-12-09 22:39:14 +00:00
}
2006-11-01 00:07:37 +00:00
if ( comebacktoorigin ) {
2007-02-01 00:38:43 +00:00
set_c_e_p ( chan , parking_con_dial , peername , 1 );
2006-11-01 00:07:37 +00:00
} else {
2007-02-01 00:38:43 +00:00
ast_log ( LOG_WARNING , "now going to parkedcallstimeout,s,1 | ps is %d \n " , pu -> parkingnum );
snprintf ( parkingslot , sizeof ( parkingslot ), "%d" , pu -> parkingnum );
pbx_builtin_setvar_helper ( pu -> chan , "PARKINGSLOT" , parkingslot );
set_c_e_p ( chan , "parkedcallstimeout" , peername , 1 );
}
2004-12-09 22:39:14 +00:00
} else {
/* They've been waiting too long, send them back to where they came. Theoretically they
should have their original extensions and such, but we copy to be on the safe side */
2006-04-16 19:41:12 +00:00
set_c_e_p ( chan , pu -> context , pu -> exten , pu -> priority );
2004-12-09 22:39:14 +00:00
}
2005-02-26 07:54:28 +00:00
2007-02-18 15:01:07 +00:00
post_manager_event ( "ParkedCallTimeOut" , pu );
2005-02-26 07:54:28 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Timeout for %s parked on %d. Returning to %s,%s,%d \n " , chan -> name , pu -> parkingnum , chan -> context , chan -> exten , chan -> priority );
2001-12-27 11:07:33 +00:00
/* Start up the PBX, or hang them up */
2006-04-16 19:41:12 +00:00
if ( ast_pbx_start ( chan )) {
ast_log ( LOG_WARNING , "Unable to restart the PBX for user on '%s', hanging them up... \n " , chan -> name );
2006-04-16 21:41:06 +00:00
ast_hangup ( chan );
2001-12-27 11:07:33 +00:00
}
/* And take them out of the parking lot */
2007-07-09 02:29:00 +00:00
AST_LIST_REMOVE_CURRENT ( & parkinglot , list );
2004-08-03 06:31:20 +00:00
con = ast_context_find ( parking_con );
if ( con ) {
2007-07-09 02:29:00 +00:00
if ( ast_context_remove_extension2 ( con , pu -> parkingexten , 1 , NULL ))
2004-08-03 06:31:20 +00:00
ast_log ( LOG_WARNING , "Whoa, failed to remove the extension! \n " );
2006-06-26 16:43:21 +00:00
else
2007-08-10 16:24:11 +00:00
notify_metermaids ( pu -> parkingexten , parking_con , AST_DEVICE_NOT_INUSE );
2004-08-03 06:31:20 +00:00
} else
ast_log ( LOG_WARNING , "Whoa, no parking context? \n " );
2007-07-09 02:29:00 +00:00
ast_free ( pu );
2006-04-16 20:32:14 +00:00
} else { /* still within parking time, process descriptors */
2005-07-25 17:31:53 +00:00
for ( x = 0 ; x < AST_MAX_FDS ; x ++ ) {
2006-04-16 19:05:19 +00:00
struct ast_frame * f ;
2006-04-16 20:32:14 +00:00
if ( chan -> fds [ x ] == - 1 || ( ! FD_ISSET ( chan -> fds [ x ], & rfds ) && ! FD_ISSET ( chan -> fds [ x ], & efds )))
continue ; /* nothing on this descriptor */
2006-04-16 19:05:19 +00:00
2006-04-16 19:41:12 +00:00
if ( FD_ISSET ( chan -> fds [ x ], & efds ))
ast_set_flag ( chan , AST_FLAG_EXCEPTION );
2006-04-16 19:12:51 +00:00
else
2006-04-16 19:41:12 +00:00
ast_clear_flag ( chan , AST_FLAG_EXCEPTION );
chan -> fdno = x ;
2006-06-26 16:43:21 +00:00
2006-04-16 19:12:51 +00:00
/* See if they need servicing */
2006-04-16 19:41:12 +00:00
f = ast_read ( chan );
2006-04-16 20:32:14 +00:00
if ( ! f || ( f -> frametype == AST_FRAME_CONTROL && f -> subclass == AST_CONTROL_HANGUP )) {
2006-04-16 19:12:51 +00:00
if ( f )
2001-12-27 11:07:33 +00:00
ast_frfree ( f );
2007-02-18 15:01:07 +00:00
post_manager_event ( "ParkedCallGiveUp" , pu );
2006-04-16 19:12:51 +00:00
/* There's a problem, hang them up*/
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "%s got tired of being parked \n " , chan -> name );
2006-04-16 19:41:12 +00:00
ast_hangup ( chan );
2006-04-16 19:12:51 +00:00
/* And take them out of the parking lot */
2007-07-09 02:29:00 +00:00
AST_LIST_REMOVE_CURRENT ( & parkinglot , list );
2006-04-16 19:12:51 +00:00
con = ast_context_find ( parking_con );
if ( con ) {
2007-07-09 02:29:00 +00:00
if ( ast_context_remove_extension2 ( con , pu -> parkingexten , 1 , NULL ))
2006-04-16 19:12:51 +00:00
ast_log ( LOG_WARNING , "Whoa, failed to remove the extension! \n " );
2007-02-01 00:38:43 +00:00
else
2007-08-10 16:24:11 +00:00
notify_metermaids ( pu -> parkingexten , parking_con , AST_DEVICE_NOT_INUSE );
2006-04-16 19:12:51 +00:00
} else
ast_log ( LOG_WARNING , "Whoa, no parking context? \n " );
2007-07-09 02:29:00 +00:00
ast_free ( pu );
2006-04-16 19:12:51 +00:00
break ;
} else {
2006-04-17 04:31:21 +00:00
/*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
2006-04-16 19:12:51 +00:00
ast_frfree ( f );
2006-04-16 19:41:12 +00:00
if ( pu -> moh_trys < 3 && ! chan -> generatordata ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "MOH on parked call stopped by outside source. Restarting. \n " );
2007-02-18 15:11:52 +00:00
ast_indicate_data ( chan , AST_CONTROL_HOLD ,
2006-07-19 20:44:39 +00:00
S_OR ( parkmohclass , NULL ),
! ast_strlen_zero ( parkmohclass ) ? strlen ( parkmohclass ) + 1 : 0 );
2006-04-16 19:12:51 +00:00
pu -> moh_trys ++ ;
2001-12-27 11:07:33 +00:00
}
2006-04-17 04:31:21 +00:00
goto std ; /*! \todo XXX Ick: jumping into an else statement??? XXX */
2006-04-16 19:12:51 +00:00
}
2006-04-16 19:05:19 +00:00
2006-04-16 18:37:01 +00:00
} /* end for */
2001-12-27 11:07:33 +00:00
if ( x >= AST_MAX_FDS ) {
2006-04-16 20:32:14 +00:00
std : for ( x = 0 ; x < AST_MAX_FDS ; x ++ ) { /* mark fds for next round */
2006-04-16 19:41:12 +00:00
if ( chan -> fds [ x ] > - 1 ) {
FD_SET ( chan -> fds [ x ], & nrfds );
FD_SET ( chan -> fds [ x ], & nefds );
if ( chan -> fds [ x ] > max )
max = chan -> fds [ x ];
2001-12-27 11:07:33 +00:00
}
}
2006-04-16 20:32:14 +00:00
/* Keep track of our shortest wait */
if ( tms < ms || ms < 0 )
2001-12-27 11:07:33 +00:00
ms = tms ;
}
}
2006-04-16 18:37:01 +00:00
} /* end while */
2007-07-09 02:29:00 +00:00
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK ( & parkinglot );
2001-12-27 11:07:33 +00:00
rfds = nrfds ;
efds = nefds ;
2006-04-16 20:32:14 +00:00
{
struct timeval tv = ast_samp2tv ( ms , 1000 );
/* Wait for something to happen */
ast_select ( max + 1 , & rfds , NULL , & efds , ( ms > - 1 ) ? & tv : NULL );
}
2001-12-27 11:07:33 +00:00
pthread_testcancel ();
}
return NULL ; /* Never reached */
}
2006-06-26 16:43:21 +00:00
/*! \brief Park a call */
2004-08-03 06:31:20 +00:00
static int park_call_exec ( struct ast_channel * chan , void * data )
{
/* Data is unused at the moment but could contain a parking
lot context eventually */
2006-08-21 02:11:39 +00:00
int res = 0 ;
struct ast_module_user * u ;
u = ast_module_user_add ( chan );
2004-08-03 06:31:20 +00:00
/* Setup the exten/priority to be s/1 since we don't know
where this call should return */
strcpy ( chan -> exten , "s" );
chan -> priority = 1 ;
2006-06-26 16:43:21 +00:00
/* Answer if call is not up */
2004-08-03 06:31:20 +00:00
if ( chan -> _state != AST_STATE_UP )
res = ast_answer ( chan );
2006-06-26 16:43:21 +00:00
/* Sleep to allow VoIP streams to settle down */
2004-08-03 06:31:20 +00:00
if ( ! res )
res = ast_safe_sleep ( chan , 1000 );
2006-06-26 16:43:21 +00:00
/* Park the call */
2004-08-03 06:31:20 +00:00
if ( ! res )
res = ast_park_call ( chan , chan , 0 , NULL );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
return ! res ? AST_PBX_KEEPALIVE : res ;
2004-08-03 06:31:20 +00:00
}
2006-06-26 16:43:21 +00:00
/*! \brief Pickup parked call */
2001-12-27 11:07:33 +00:00
static int park_exec ( struct ast_channel * chan , void * data )
{
2006-08-21 02:11:39 +00:00
int res = 0 ;
struct ast_module_user * u ;
2001-12-27 11:07:33 +00:00
struct ast_channel * peer = NULL ;
2007-07-09 02:29:00 +00:00
struct parkeduser * pu ;
2004-08-03 06:31:20 +00:00
struct ast_context * con ;
2007-03-30 14:37:21 +00:00
2001-12-27 11:07:33 +00:00
int park ;
2004-04-26 23:22:34 +00:00
struct ast_bridge_config config ;
2001-12-27 11:07:33 +00:00
if ( ! data ) {
2006-06-26 16:43:21 +00:00
ast_log ( LOG_WARNING , "Parkedcall requires an argument (extension number) \n " );
2001-12-27 11:07:33 +00:00
return - 1 ;
}
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2001-12-27 11:07:33 +00:00
park = atoi (( char * ) data );
2007-07-09 02:29:00 +00:00
AST_LIST_LOCK ( & parkinglot );
AST_LIST_TRAVERSE_SAFE_BEGIN ( & parkinglot , pu , list ) {
2001-12-27 11:07:33 +00:00
if ( pu -> parkingnum == park ) {
2007-07-09 02:29:00 +00:00
AST_LIST_REMOVE_CURRENT ( & parkinglot , list );
2001-12-27 11:07:33 +00:00
break ;
}
}
2007-07-09 02:29:00 +00:00
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK ( & parkinglot );
2001-12-27 11:07:33 +00:00
if ( pu ) {
peer = pu -> chan ;
2004-08-03 06:31:20 +00:00
con = ast_context_find ( parking_con );
if ( con ) {
2006-06-26 16:43:21 +00:00
if ( ast_context_remove_extension2 ( con , pu -> parkingexten , 1 , NULL ))
2004-08-03 06:31:20 +00:00
ast_log ( LOG_WARNING , "Whoa, failed to remove the extension! \n " );
2006-06-26 16:43:21 +00:00
else
2007-08-10 16:24:11 +00:00
notify_metermaids ( pu -> parkingexten , parking_con , AST_DEVICE_NOT_INUSE );
2004-08-03 06:31:20 +00:00
} else
ast_log ( LOG_WARNING , "Whoa, no parking context? \n " );
2005-02-26 07:54:28 +00:00
manager_event ( EVENT_FLAG_CALL , "UnParkedCall" ,
2006-06-26 16:43:21 +00:00
"Exten: %s \r\n "
2005-02-26 07:54:28 +00:00
"Channel: %s \r\n "
"From: %s \r\n "
2006-10-02 20:35:16 +00:00
"CallerIDNum: %s \r\n "
2006-04-21 10:47:07 +00:00
"CallerIDName: %s \r\n " ,
2006-06-26 16:43:21 +00:00
pu -> parkingexten , pu -> chan -> name , chan -> name ,
2006-04-21 10:47:07 +00:00
S_OR ( pu -> chan -> cid . cid_num , "<unknown>" ),
S_OR ( pu -> chan -> cid . cid_name , "<unknown>" )
2005-02-26 07:54:28 +00:00
);
2007-06-06 21:20:11 +00:00
ast_free ( pu );
2001-12-27 11:07:33 +00:00
}
2003-02-02 19:37:23 +00:00
/* JK02: it helps to answer the channel if not already up */
2006-04-16 20:32:14 +00:00
if ( chan -> _state != AST_STATE_UP )
2003-02-02 19:37:23 +00:00
ast_answer ( chan );
2001-12-27 11:07:33 +00:00
if ( peer ) {
2006-01-08 00:25:31 +00:00
/* Play a courtesy to the source(s) configured to prefix the bridge connecting */
2004-08-31 13:32:11 +00:00
if ( ! ast_strlen_zero ( courtesytone )) {
2006-04-16 20:32:14 +00:00
int error = 0 ;
ast_indicate ( peer , AST_CONTROL_UNHOLD );
2006-01-08 00:25:31 +00:00
if ( parkedplay == 0 ) {
2006-11-17 23:18:51 +00:00
error = ast_stream_and_wait ( chan , courtesytone , "" );
2006-04-16 21:41:06 +00:00
} else if ( parkedplay == 1 ) {
2006-11-17 23:18:51 +00:00
error = ast_stream_and_wait ( peer , courtesytone , "" );
2006-04-16 20:32:14 +00:00
} else if ( parkedplay == 2 ) {
if ( ! ast_streamfile ( chan , courtesytone , chan -> language ) &&
! ast_streamfile ( peer , courtesytone , chan -> language )) {
2006-04-17 04:31:21 +00:00
/*! \todo XXX we would like to wait on both! */
2006-04-16 20:32:14 +00:00
res = ast_waitstream ( chan , "" );
if ( res >= 0 )
res = ast_waitstream ( peer , "" );
if ( res < 0 )
error = 1 ;
2004-08-31 13:32:11 +00:00
}
2006-04-16 20:32:14 +00:00
}
if ( error ) {
ast_log ( LOG_WARNING , "Failed to play courtesy tone! \n " );
ast_hangup ( peer );
return - 1 ;
2004-08-31 13:32:11 +00:00
}
2006-07-19 20:44:39 +00:00
} else
2006-05-03 21:39:24 +00:00
ast_indicate ( peer , AST_CONTROL_UNHOLD );
2001-12-27 11:07:33 +00:00
res = ast_channel_make_compatible ( chan , peer );
if ( res < 0 ) {
ast_log ( LOG_WARNING , "Could not make channels %s and %s compatible for bridge \n " , chan -> name , peer -> name );
ast_hangup ( peer );
return - 1 ;
}
/* This runs sorta backwards, since we give the incoming channel control, as if it
were the person called. */
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , "Channel %s connected to parked call %d \n " , chan -> name , park );
2004-04-26 23:22:34 +00:00
2007-03-30 14:37:21 +00:00
pbx_builtin_setvar_helper ( chan , "PARKEDCHANNEL" , peer -> name );
ast_cdr_setdestchan ( chan -> cdr , peer -> name );
2005-07-25 17:31:53 +00:00
memset ( & config , 0 , sizeof ( struct ast_bridge_config ));
2007-02-16 18:08:34 +00:00
if (( parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE ) || ( parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH ))
2007-01-16 17:50:25 +00:00
ast_set_flag ( & ( config . features_callee ), AST_FEATURE_REDIRECT );
2007-02-16 18:08:34 +00:00
if (( parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER ) || ( parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH ))
2007-01-16 17:50:25 +00:00
ast_set_flag ( & ( config . features_caller ), AST_FEATURE_REDIRECT );
2007-02-16 18:08:34 +00:00
if (( parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE ) || ( parkedcallreparking == AST_FEATURE_FLAG_BYBOTH ))
2007-02-16 17:41:27 +00:00
ast_set_flag ( & ( config . features_callee ), AST_FEATURE_PARKCALL );
2007-02-16 18:08:34 +00:00
if (( parkedcallreparking == AST_FEATURE_FLAG_BYCALLER ) || ( parkedcallreparking == AST_FEATURE_FLAG_BYBOTH ))
2007-02-16 17:41:27 +00:00
ast_set_flag ( & ( config . features_caller ), AST_FEATURE_PARKCALL );
2005-07-25 17:31:53 +00:00
res = ast_bridge_call ( chan , peer , & config );
2004-04-26 23:22:34 +00:00
2007-01-23 20:36:08 +00:00
pbx_builtin_setvar_helper ( chan , "PARKEDCHANNEL" , peer -> name );
ast_cdr_setdestchan ( chan -> cdr , peer -> name );
2001-12-27 11:07:33 +00:00
/* Simulate the PBX hanging up */
2004-04-07 04:11:00 +00:00
if ( res != AST_PBX_NO_HANGUP_PEER )
2001-12-27 11:07:33 +00:00
ast_hangup ( peer );
2004-04-07 04:11:00 +00:00
return res ;
2001-12-27 11:07:33 +00:00
} else {
2006-04-17 04:31:21 +00:00
/*! \todo XXX Play a message XXX */
2006-11-17 23:18:51 +00:00
if ( ast_stream_and_wait ( chan , "pbx-invalidpark" , "" ))
2004-12-28 23:49:46 +00:00
ast_log ( LOG_WARNING , "ast_streamfile of %s failed on %s \n " , "pbx-invalidpark" , chan -> name );
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , "Channel %s tried to talk to nonexistent parked call %d \n " , chan -> name , park );
2001-12-27 11:07:33 +00:00
res = - 1 ;
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2001-12-27 11:07:33 +00:00
return res ;
}
2007-09-18 22:43:45 +00:00
/*!
* \brief CLI command to list configured features
* \param e
* \param cmd
* \param a
*
* \retval CLI_SUCCESS on success.
* \retval NULL when tab completion is used.
*/
static char * handle_feature_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a ) {
2005-02-06 23:36:35 +00:00
int i ;
2005-08-23 02:22:33 +00:00
struct ast_call_feature * feature ;
2005-02-06 23:36:35 +00:00
char format [] = "%-25s %-7s %-7s \n " ;
2007-09-18 22:43:45 +00:00
switch ( cmd ) {
case CLI_INIT :
e -> command = "features show" ;
e -> usage =
"Usage: features show \n "
" Lists configured features \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
ast_cli ( a -> fd , format , "Builtin Feature" , "Default" , "Current" );
ast_cli ( a -> fd , format , "---------------" , "-------" , "-------" );
2005-02-06 23:36:35 +00:00
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , format , "Pickup" , "*8" , ast_pickup_ext ()); /* default hardcoded above, so we'll hardcode it here */
2005-02-06 23:36:35 +00:00
2007-05-08 16:31:16 +00:00
ast_rwlock_rdlock ( & features_lock );
for ( i = 0 ; i < FEATURES_COUNT ; i ++ )
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , format , builtin_features [ i ]. fname , builtin_features [ i ]. default_exten , builtin_features [ i ]. exten );
2007-05-08 16:31:16 +00:00
ast_rwlock_unlock ( & features_lock );
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , " \n " );
ast_cli ( a -> fd , format , "Dynamic Feature" , "Default" , "Current" );
ast_cli ( a -> fd , format , "---------------" , "-------" , "-------" );
2007-05-08 16:31:16 +00:00
if ( AST_LIST_EMPTY ( & feature_list ))
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , "(none) \n " );
2005-08-23 14:40:03 +00:00
else {
AST_LIST_LOCK ( & feature_list );
2007-05-08 16:31:16 +00:00
AST_LIST_TRAVERSE ( & feature_list , feature , feature_entry )
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , format , feature -> sname , "no def" , feature -> exten );
2005-08-23 14:40:03 +00:00
AST_LIST_UNLOCK ( & feature_list );
2005-08-23 02:22:33 +00:00
}
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , " \n Call parking \n " );
ast_cli ( a -> fd , "------------ \n " );
ast_cli ( a -> fd , "%-20s: %s \n " , "Parking extension" , parking_ext );
ast_cli ( a -> fd , "%-20s: %s \n " , "Parking context" , parking_con );
ast_cli ( a -> fd , "%-20s: %d-%d \n " , "Parked call extensions" , parking_start , parking_stop );
ast_cli ( a -> fd , " \n " );
return CLI_SUCCESS ;
2005-02-06 23:36:35 +00:00
}
2007-04-10 20:44:44 +00:00
static char mandescr_bridge [] =
"Description: Bridge together two channels already in the PBX \n "
"Variables: ( Headers marked with * are required ) \n "
" *Channel1: Channel to Bridge to Channel2 \n "
" *Channel2: Channel to Bridge to Channel1 \n "
" Tone: (Yes|No) Play courtesy tone to Channel 2 \n "
" \n " ;
2007-08-07 23:04:01 +00:00
/*!
* \brief Actual bridge
* \param chan
* \param tmpchan
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Stop hold music, lock both channels, masq channels,
* after bridge return channel to next priority.
*/
2007-04-10 20:44:44 +00:00
static void do_bridge_masquerade ( struct ast_channel * chan , struct ast_channel * tmpchan )
{
ast_moh_stop ( chan );
ast_mutex_lock ( & chan -> lock );
ast_setstate ( tmpchan , chan -> _state );
tmpchan -> readformat = chan -> readformat ;
tmpchan -> writeformat = chan -> writeformat ;
ast_channel_masquerade ( tmpchan , chan );
ast_mutex_lock ( & tmpchan -> lock );
ast_do_masquerade ( tmpchan );
/* when returning from bridge, the channel will continue at the next priority */
ast_explicit_goto ( tmpchan , chan -> context , chan -> exten , chan -> priority + 1 );
ast_mutex_unlock ( & tmpchan -> lock );
ast_mutex_unlock ( & chan -> lock );
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Bridge channels together
* \param s
* \param m
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Make sure valid channels were specified,
* send errors if any of the channels could not be found/locked, answer channels if needed,
* create the placeholder channels and grab the other channels
* make the channels compatible, send error if we fail doing so
2007-09-05 16:31:39 +00:00
* setup the bridge thread object and start the bridge.
*
2007-08-07 23:04:01 +00:00
* \retval 0 on success or on incorrect use.
* \retval 1 on failure to bridge channels.
*/
2007-04-10 20:44:44 +00:00
static int action_bridge ( struct mansession * s , const struct message * m )
{
const char * channela = astman_get_header ( m , "Channel1" );
const char * channelb = astman_get_header ( m , "Channel2" );
const char * playtone = astman_get_header ( m , "Tone" );
struct ast_channel * chana = NULL , * chanb = NULL ;
struct ast_channel * tmpchana = NULL , * tmpchanb = NULL ;
struct ast_bridge_thread_obj * tobj = NULL ;
/* make sure valid channels were specified */
if ( ! ast_strlen_zero ( channela ) && ! ast_strlen_zero ( channelb )) {
chana = ast_get_channel_by_name_prefix_locked ( channela , strlen ( channela ));
chanb = ast_get_channel_by_name_prefix_locked ( channelb , strlen ( channelb ));
if ( chana )
ast_mutex_unlock ( & chana -> lock );
if ( chanb )
ast_mutex_unlock ( & chanb -> lock );
/* send errors if any of the channels could not be found/locked */
if ( ! chana ) {
char buf [ 256 ];
snprintf ( buf , sizeof ( buf ), "Channel1 does not exists: %s" , channela );
astman_send_error ( s , m , buf );
return 0 ;
}
if ( ! chanb ) {
char buf [ 256 ];
snprintf ( buf , sizeof ( buf ), "Channel2 does not exists: %s" , channelb );
astman_send_error ( s , m , buf );
return 0 ;
}
} else {
astman_send_error ( s , m , "Missing channel parameter in request" );
return 0 ;
}
/* Answer the channels if needed */
if ( chana -> _state != AST_STATE_UP )
ast_answer ( chana );
if ( chanb -> _state != AST_STATE_UP )
ast_answer ( chanb );
/* create the placeholder channels and grab the other channels */
if ( ! ( tmpchana = ast_channel_alloc ( 0 , AST_STATE_DOWN , NULL , NULL , NULL ,
NULL , NULL , 0 , "Bridge/%s" , chana -> name ))) {
astman_send_error ( s , m , "Unable to create temporary channel!" );
return 1 ;
}
2007-05-22 12:58:38 +00:00
if ( ! ( tmpchanb = ast_channel_alloc ( 0 , AST_STATE_DOWN , NULL , NULL , NULL ,
2007-04-10 20:44:44 +00:00
NULL , NULL , 0 , "Bridge/%s" , chanb -> name ))) {
astman_send_error ( s , m , "Unable to create temporary channels!" );
ast_channel_free ( tmpchana );
return 1 ;
}
do_bridge_masquerade ( chana , tmpchana );
do_bridge_masquerade ( chanb , tmpchanb );
/* make the channels compatible, send error if we fail doing so */
if ( ast_channel_make_compatible ( tmpchana , tmpchanb )) {
ast_log ( LOG_WARNING , "Could not make channels %s and %s compatible for manager bridge \n " , tmpchana -> name , tmpchanb -> name );
astman_send_error ( s , m , "Could not make channels compatible for manager bridge" );
ast_hangup ( tmpchana );
ast_hangup ( tmpchanb );
return 1 ;
}
/* setup the bridge thread object and start the bridge */
if ( ! ( tobj = ast_calloc ( 1 , sizeof ( * tobj )))) {
ast_log ( LOG_WARNING , "Unable to spawn a new bridge thread on %s and %s: %s \n " , tmpchana -> name , tmpchanb -> name , strerror ( errno ));
astman_send_error ( s , m , "Unable to spawn a new bridge thread" );
ast_hangup ( tmpchana );
ast_hangup ( tmpchanb );
return 1 ;
}
tobj -> chan = tmpchana ;
tobj -> peer = tmpchanb ;
tobj -> return_to_pbx = 1 ;
if ( ast_true ( playtone )) {
if ( ! ast_strlen_zero ( xfersound ) && ! ast_streamfile ( tmpchanb , xfersound , tmpchanb -> language )) {
if ( ast_waitstream ( tmpchanb , "" ) < 0 )
ast_log ( LOG_WARNING , "Failed to play a courtesy tone on chan %s \n " , tmpchanb -> name );
}
}
ast_bridge_call_thread_launch ( tobj );
astman_send_ack ( s , m , "Launched bridge thread with success" );
return 0 ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief CLI command to list parked calls
* \param e
* \param cmd
* \param a
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Check right usage, lock parking lot, display parked calls, unlock parking lot list.
* \retval CLI_SUCCESS on success.
* \retval CLI_SHOWUSAGE on incorrect number of arguements.
* \retval NULL when tab completion is used.
*/
2007-06-06 14:45:29 +00:00
static char * handle_parkedcalls ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2003-07-02 14:06:12 +00:00
{
struct parkeduser * cur ;
2005-04-20 16:27:44 +00:00
int numparked = 0 ;
2003-07-02 14:06:12 +00:00
2007-06-06 14:45:29 +00:00
switch ( cmd ) {
case CLI_INIT :
e -> command = "parkedcalls show" ;
e -> usage =
"Usage: parkedcalls show \n "
" List currently parked calls \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
if ( a -> argc > e -> args )
return CLI_SHOWUSAGE ;
ast_cli ( a -> fd , "%4s %25s (%-15s %-12s %-4s) %-6s \n " , "Num" , "Channel"
2003-07-02 14:06:12 +00:00
, "Context" , "Extension" , "Pri" , "Timeout" );
2007-07-09 02:29:00 +00:00
AST_LIST_LOCK ( & parkinglot );
AST_LIST_TRAVERSE ( & parkinglot , cur , list ) {
2007-06-06 14:45:29 +00:00
ast_cli ( a -> fd , "%-10.10s %25s (%-15s %-12s %-4d) %6lds \n "
2006-06-26 16:43:21 +00:00
, cur -> parkingexten , cur -> chan -> name , cur -> context , cur -> exten
2003-07-02 14:06:12 +00:00
, cur -> priority , cur -> start . tv_sec + ( cur -> parkingtime / 1000 ) - time ( NULL ));
2005-04-20 16:27:44 +00:00
numparked ++ ;
2003-07-02 14:06:12 +00:00
}
2007-07-09 02:29:00 +00:00
AST_LIST_UNLOCK ( & parkinglot );
2007-06-06 14:45:29 +00:00
ast_cli ( a -> fd , "%d parked call%s. \n " , numparked , ESS ( numparked ));
2003-07-02 14:06:12 +00:00
2007-06-06 14:45:29 +00:00
return CLI_SUCCESS ;
}
static char * handle_parkedcalls_deprecated ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
char * res = handle_parkedcalls ( e , cmd , a );
if ( cmd == CLI_INIT )
e -> command = "show parkedcalls" ;
return res ;
2003-07-02 14:06:12 +00:00
}
2007-10-19 18:29:40 +00:00
static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI ( handle_parkedcalls_deprecated , "List currently parked calls." );
2003-07-02 14:06:12 +00:00
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_features [] = {
2007-10-19 18:29:40 +00:00
AST_CLI ( handle_feature_show , "Lists configured features" ),
AST_CLI ( handle_parkedcalls , "List currently parked calls" , . deprecate_cmd = & cli_show_parkedcalls_deprecated ),
2006-09-18 19:54:18 +00:00
};
2005-07-25 17:31:53 +00:00
2007-08-07 23:04:01 +00:00
/*!
* \brief Dump parking lot status
* \param s
* \param m
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Lock parking lot, iterate list and append parked calls status, unlock parking lot.
* \return Always RESULT_SUCCESS
*/
2007-04-10 20:44:44 +00:00
static int manager_parking_status ( struct mansession * s , const struct message * m )
2004-01-30 05:49:44 +00:00
{
struct parkeduser * cur ;
2007-02-16 17:44:39 +00:00
const char * id = astman_get_header ( m , "ActionID" );
2004-07-14 07:53:57 +00:00
char idText [ 256 ] = "" ;
2005-11-08 01:55:31 +00:00
if ( ! ast_strlen_zero ( id ))
2007-02-16 17:44:39 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2004-01-30 05:49:44 +00:00
2004-01-30 06:31:25 +00:00
astman_send_ack ( s , m , "Parked calls will follow" );
2004-01-30 05:49:44 +00:00
2007-07-09 02:29:00 +00:00
AST_LIST_LOCK ( & parkinglot );
2004-01-30 05:49:44 +00:00
2007-07-09 02:29:00 +00:00
AST_LIST_TRAVERSE ( & parkinglot , cur , list ) {
2006-04-16 21:41:06 +00:00
astman_append ( s , "Event: ParkedCall \r\n "
2004-01-30 05:49:44 +00:00
"Exten: %d \r\n "
"Channel: %s \r\n "
2005-12-21 08:51:44 +00:00
"From: %s \r\n "
2004-01-30 06:31:25 +00:00
"Timeout: %ld \r\n "
2006-10-02 20:35:16 +00:00
"CallerIDNum: %s \r\n "
2004-10-02 00:58:31 +00:00
"CallerIDName: %s \r\n "
2004-07-14 07:53:57 +00:00
"%s"
2006-04-21 10:47:07 +00:00
" \r\n " ,
2007-02-16 17:44:39 +00:00
cur -> parkingnum , cur -> chan -> name , cur -> peername ,
( long ) cur -> start . tv_sec + ( long ) ( cur -> parkingtime / 1000 ) - ( long ) time ( NULL ),
2006-04-21 10:47:07 +00:00
S_OR ( cur -> chan -> cid . cid_num , "" ), /* XXX in other places it is <unknown> */
S_OR ( cur -> chan -> cid . cid_name , "" ),
idText );
2007-02-16 17:44:39 +00:00
}
2004-01-30 05:49:44 +00:00
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2006-04-16 21:41:06 +00:00
"Event: ParkedCallsComplete \r\n "
"%s"
" \r\n " , idText );
2004-07-14 07:53:57 +00:00
2007-07-09 02:29:00 +00:00
AST_LIST_UNLOCK ( & parkinglot );
2004-01-30 05:49:44 +00:00
2007-02-16 17:44:39 +00:00
return RESULT_SUCCESS ;
2004-01-30 05:49:44 +00:00
}
2006-03-06 23:12:48 +00:00
static char mandescr_park [] =
"Description: Park a channel. \n "
"Variables: (Names marked with * are required) \n "
" *Channel: Channel name to park \n "
" *Channel2: Channel to announce park info to (and return to if timeout) \n "
" Timeout: Number of milliseconds to wait before callback. \n " ;
2007-08-07 23:04:01 +00:00
/*!
* \brief Create manager event for parked calls
* \param s
* \param m
2007-09-05 16:31:39 +00:00
*
2007-08-07 23:04:01 +00:00
* Get channels involved in park, create event.
* \return Always 0
*/
2007-01-05 22:43:18 +00:00
static int manager_park ( struct mansession * s , const struct message * m )
2006-03-06 23:12:48 +00:00
{
2007-01-05 22:43:18 +00:00
const char * channel = astman_get_header ( m , "Channel" );
const char * channel2 = astman_get_header ( m , "Channel2" );
const char * timeout = astman_get_header ( m , "Timeout" );
2006-03-06 23:12:48 +00:00
char buf [ BUFSIZ ];
int to = 0 ;
int res = 0 ;
int parkExt = 0 ;
struct ast_channel * ch1 , * ch2 ;
if ( ast_strlen_zero ( channel )) {
astman_send_error ( s , m , "Channel not specified" );
return 0 ;
}
if ( ast_strlen_zero ( channel2 )) {
astman_send_error ( s , m , "Channel2 not specified" );
return 0 ;
}
ch1 = ast_get_channel_by_name_locked ( channel );
if ( ! ch1 ) {
snprintf ( buf , sizeof ( buf ), "Channel does not exist: %s" , channel );
astman_send_error ( s , m , buf );
return 0 ;
}
ch2 = ast_get_channel_by_name_locked ( channel2 );
if ( ! ch2 ) {
snprintf ( buf , sizeof ( buf ), "Channel does not exist: %s" , channel2 );
astman_send_error ( s , m , buf );
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( ch1 );
2006-03-06 23:12:48 +00:00
return 0 ;
}
if ( ! ast_strlen_zero ( timeout )) {
sscanf ( timeout , "%d" , & to );
}
res = ast_masq_park_call ( ch1 , ch2 , to , & parkExt );
if ( ! res ) {
ast_softhangup ( ch2 , AST_SOFTHANGUP_EXPLICIT );
astman_send_ack ( s , m , "Park successful" );
} else {
astman_send_error ( s , m , "Park failure" );
}
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( ch1 );
ast_channel_unlock ( ch2 );
2006-03-06 23:12:48 +00:00
return 0 ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Pickup a call
2007-09-05 16:31:39 +00:00
* \param chan channel that initiated pickup.
*
2007-08-07 23:04:01 +00:00
* Walk list of channels, checking it is not itself, channel is pbx one,
* check that the callgroup for both channels are the same and the channel is ringing.
* Answer calling channel, flag channel as answered on queue, masq channels together.
*/
2005-07-25 17:31:53 +00:00
int ast_pickup_call ( struct ast_channel * chan )
{
struct ast_channel * cur = NULL ;
int res = - 1 ;
2007-04-10 20:44:44 +00:00
while (( cur = ast_channel_walk_locked ( cur )) != NULL ) {
2005-07-25 17:31:53 +00:00
if ( ! cur -> pbx &&
( cur != chan ) &&
( chan -> pickupgroup & cur -> callgroup ) &&
(( cur -> _state == AST_STATE_RINGING ) ||
( cur -> _state == AST_STATE_RING ))) {
break ;
}
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( cur );
2005-07-25 17:31:53 +00:00
}
if ( cur ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Call pickup on chan '%s' by '%s' \n " , cur -> name , chan -> name );
2005-07-25 17:31:53 +00:00
res = ast_answer ( chan );
if ( res )
ast_log ( LOG_WARNING , "Unable to answer '%s' \n " , chan -> name );
res = ast_queue_control ( chan , AST_CONTROL_ANSWER );
if ( res )
ast_log ( LOG_WARNING , "Unable to queue answer on '%s' \n " , chan -> name );
res = ast_channel_masquerade ( cur , chan );
if ( res )
ast_log ( LOG_WARNING , "Unable to masquerade '%s' into '%s' \n " , chan -> name , cur -> name ); /* Done */
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( cur );
2005-07-25 17:31:53 +00:00
} else {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "No call pickup possible... \n " );
2005-07-25 17:31:53 +00:00
}
return res ;
}
2007-08-07 23:04:01 +00:00
/*!
* \brief Add parking hints for all defined parking lots
* \param context
* \param start starting parkinglot number
* \param stop ending parkinglot number
*/
2006-06-26 16:43:21 +00:00
static void park_add_hints ( char * context , int start , int stop )
{
int numext ;
char device [ AST_MAX_EXTENSION ];
char exten [ 10 ];
for ( numext = start ; numext <= stop ; numext ++ ) {
snprintf ( exten , sizeof ( exten ), "%d" , numext );
snprintf ( device , sizeof ( device ), "park:%s@%s" , exten , context );
ast_add_extension ( context , 1 , exten , PRIORITY_HINT , NULL , NULL , device , NULL , NULL , registrar );
}
}
2005-01-10 04:03:30 +00:00
static int load_config ( void )
2001-12-27 11:07:33 +00:00
{
2005-01-10 04:03:30 +00:00
int start = 0 , end = 0 ;
2006-06-26 16:43:21 +00:00
int res ;
2007-05-31 18:21:47 +00:00
int i ;
2005-01-10 04:03:30 +00:00
struct ast_context * con = NULL ;
struct ast_config * cfg = NULL ;
struct ast_variable * var = NULL ;
2007-05-31 18:21:47 +00:00
struct feature_group * fg = NULL ;
2007-08-16 21:09:46 +00:00
struct ast_flags config_flags = { 0 };
2005-10-13 23:58:33 +00:00
char old_parking_ext [ AST_MAX_EXTENSION ];
2005-11-10 23:26:40 +00:00
char old_parking_con [ AST_MAX_EXTENSION ] = "" ;
2007-05-31 18:21:47 +00:00
char * ctg ;
static const char * categories [] = {
/* Categories in features.conf that are not
* to be parsed as group categories
*/
"general" ,
"featuremap" ,
"applicationmap"
};
2005-10-13 23:58:33 +00:00
if ( ! ast_strlen_zero ( parking_con )) {
strcpy ( old_parking_ext , parking_ext );
strcpy ( old_parking_con , parking_con );
}
/* Reset to defaults */
strcpy ( parking_con , "parkedcalls" );
strcpy ( parking_con_dial , "park-dial" );
strcpy ( parking_ext , "700" );
strcpy ( pickup_ext , "*8" );
2006-07-19 20:44:39 +00:00
strcpy ( parkmohclass , "default" );
2005-10-13 23:58:33 +00:00
courtesytone [ 0 ] = '\0' ;
strcpy ( xfersound , "beep" );
strcpy ( xferfailsound , "pbx-invalid" );
parking_start = 701 ;
parking_stop = 750 ;
parkfindnext = 0 ;
2006-03-30 01:35:56 +00:00
adsipark = 0 ;
2006-11-01 00:07:37 +00:00
comebacktoorigin = 1 ;
2006-06-26 16:43:21 +00:00
parkaddhints = 0 ;
2007-01-16 17:50:25 +00:00
parkedcalltransfers = 0 ;
2007-02-16 17:41:27 +00:00
parkedcallreparking = 0 ;
2005-10-13 23:58:33 +00:00
2005-01-04 04:01:40 +00:00
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2006-05-23 18:23:05 +00:00
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
2007-05-01 22:24:51 +00:00
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY ;
atxferdropcall = DEFAULT_ATXFER_DROP_CALL ;
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES ;
2005-01-04 04:01:40 +00:00
2007-08-16 21:09:46 +00:00
cfg = ast_config_load ( "features.conf" , config_flags );
2006-08-31 21:00:20 +00:00
if ( ! cfg ) {
ast_log ( LOG_WARNING , "Could not load features.conf \n " );
return AST_MODULE_LOAD_DECLINE ;
}
for ( var = ast_variable_browse ( cfg , "general" ); var ; var = var -> next ) {
if ( ! strcasecmp ( var -> name , "parkext" )) {
ast_copy_string ( parking_ext , var -> value , sizeof ( parking_ext ));
} else if ( ! strcasecmp ( var -> name , "context" )) {
ast_copy_string ( parking_con , var -> value , sizeof ( parking_con ));
} else if ( ! strcasecmp ( var -> name , "parkingtime" )) {
if (( sscanf ( var -> value , "%d" , & parkingtime ) != 1 ) || ( parkingtime < 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid parkingtime \n " , var -> value );
parkingtime = DEFAULT_PARK_TIME ;
} else
parkingtime = parkingtime * 1000 ;
} else if ( ! strcasecmp ( var -> name , "parkpos" )) {
if ( sscanf ( var -> value , "%d-%d" , & start , & end ) != 2 ) {
ast_log ( LOG_WARNING , "Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf \n " , var -> lineno );
} else {
parking_start = start ;
parking_stop = end ;
}
} else if ( ! strcasecmp ( var -> name , "findslot" )) {
parkfindnext = ( ! strcasecmp ( var -> value , "next" ));
} else if ( ! strcasecmp ( var -> name , "parkinghints" )) {
parkaddhints = ast_true ( var -> value );
2007-01-16 17:50:25 +00:00
} else if ( ! strcasecmp ( var -> name , "parkedcalltransfers" )) {
2007-02-16 18:08:34 +00:00
if ( ! strcasecmp ( var -> value , "both" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH ;
else if ( ! strcasecmp ( var -> value , "caller" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER ;
else if ( ! strcasecmp ( var -> value , "callee" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE ;
2007-02-16 17:41:27 +00:00
} else if ( ! strcasecmp ( var -> name , "parkedcallreparking" )) {
2007-02-16 18:08:34 +00:00
if ( ! strcasecmp ( var -> value , "both" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH ;
else if ( ! strcasecmp ( var -> value , "caller" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER ;
else if ( ! strcasecmp ( var -> value , "callee" ))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE ;
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( var -> name , "adsipark" )) {
adsipark = ast_true ( var -> value );
} else if ( ! strcasecmp ( var -> name , "transferdigittimeout" )) {
if (( sscanf ( var -> value , "%d" , & transferdigittimeout ) != 1 ) || ( transferdigittimeout < 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid transferdigittimeout \n " , var -> value );
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT ;
} else
transferdigittimeout = transferdigittimeout * 1000 ;
} else if ( ! strcasecmp ( var -> name , "featuredigittimeout" )) {
if (( sscanf ( var -> value , "%d" , & featuredigittimeout ) != 1 ) || ( featuredigittimeout < 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid featuredigittimeout \n " , var -> value );
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT ;
2002-05-30 01:34:15 +00:00
}
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( var -> name , "atxfernoanswertimeout" )) {
if (( sscanf ( var -> value , "%d" , & atxfernoanswertimeout ) != 1 ) || ( atxfernoanswertimeout < 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid atxfernoanswertimeout \n " , var -> value );
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER ;
} else
atxfernoanswertimeout = atxfernoanswertimeout * 1000 ;
2007-05-01 22:24:51 +00:00
} else if ( ! strcasecmp ( var -> name , "atxferloopdelay" )) {
if (( sscanf ( var -> value , "%u" , & atxferloopdelay ) != 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid atxferloopdelay \n " , var -> value );
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY ;
} else
atxferloopdelay *= 1000 ;
} else if ( ! strcasecmp ( var -> name , "atxferdropcall" )) {
atxferdropcall = ast_true ( var -> value );
} else if ( ! strcasecmp ( var -> name , "atxfercallbackretries" )) {
if (( sscanf ( var -> value , "%u" , & atxferloopdelay ) != 1 )) {
ast_log ( LOG_WARNING , "%s is not a valid atxfercallbackretries \n " , var -> value );
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES ;
}
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( var -> name , "courtesytone" )) {
ast_copy_string ( courtesytone , var -> value , sizeof ( courtesytone ));
} else if ( ! strcasecmp ( var -> name , "parkedplay" )) {
if ( ! strcasecmp ( var -> value , "both" ))
parkedplay = 2 ;
else if ( ! strcasecmp ( var -> value , "parked" ))
parkedplay = 1 ;
else
parkedplay = 0 ;
} else if ( ! strcasecmp ( var -> name , "xfersound" )) {
ast_copy_string ( xfersound , var -> value , sizeof ( xfersound ));
} else if ( ! strcasecmp ( var -> name , "xferfailsound" )) {
ast_copy_string ( xferfailsound , var -> value , sizeof ( xferfailsound ));
} else if ( ! strcasecmp ( var -> name , "pickupexten" )) {
ast_copy_string ( pickup_ext , var -> value , sizeof ( pickup_ext ));
2006-11-01 00:07:37 +00:00
} else if ( ! strcasecmp ( var -> name , "comebacktoorigin" )) {
comebacktoorigin = ast_true ( var -> value );
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( var -> name , "parkedmusicclass" )) {
ast_copy_string ( parkmohclass , var -> value , sizeof ( parkmohclass ));
2002-05-30 01:34:15 +00:00
}
2006-08-31 21:00:20 +00:00
}
2005-08-23 02:22:33 +00:00
2006-08-31 21:00:20 +00:00
unmap_features ();
for ( var = ast_variable_browse ( cfg , "featuremap" ); var ; var = var -> next ) {
if ( remap_feature ( var -> name , var -> value ))
ast_log ( LOG_NOTICE , "Unknown feature '%s' \n " , var -> name );
}
/* Map a key combination to an application*/
ast_unregister_features ();
for ( var = ast_variable_browse ( cfg , "applicationmap" ); var ; var = var -> next ) {
char * tmp_val = ast_strdupa ( var -> value );
char * exten , * activateon , * activatedby , * app , * app_args , * moh_class ;
struct ast_call_feature * feature ;
/* strsep() sets the argument to NULL if match not found, and it
* is safe to use it with a NULL argument, so we don't check
* between calls.
*/
exten = strsep ( & tmp_val , "," );
activatedby = strsep ( & tmp_val , "," );
app = strsep ( & tmp_val , "," );
app_args = strsep ( & tmp_val , "," );
moh_class = strsep ( & tmp_val , "," );
activateon = strsep ( & activatedby , "/" );
/*! \todo XXX var_name or app_args ? */
if ( ast_strlen_zero ( app ) || ast_strlen_zero ( exten ) || ast_strlen_zero ( activateon ) || ast_strlen_zero ( var -> name )) {
ast_log ( LOG_NOTICE , "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s \n " ,
app , exten , activateon , var -> name );
continue ;
2005-01-04 04:01:40 +00:00
}
2005-08-23 02:22:33 +00:00
2007-05-08 16:54:02 +00:00
AST_LIST_LOCK ( & feature_list );
if (( feature = find_dynamic_feature ( var -> name ))) {
AST_LIST_UNLOCK ( & feature_list );
2006-08-31 21:00:20 +00:00
ast_log ( LOG_WARNING , "Dynamic Feature '%s' specified more than once! \n " , var -> name );
continue ;
}
2007-05-08 16:54:02 +00:00
AST_LIST_UNLOCK ( & feature_list );
2006-08-31 21:00:20 +00:00
if ( ! ( feature = ast_calloc ( 1 , sizeof ( * feature ))))
continue ;
2005-08-23 02:22:33 +00:00
2006-08-31 21:00:20 +00:00
ast_copy_string ( feature -> sname , var -> name , FEATURE_SNAME_LEN );
ast_copy_string ( feature -> app , app , FEATURE_APP_LEN );
ast_copy_string ( feature -> exten , exten , FEATURE_EXTEN_LEN );
if ( app_args )
ast_copy_string ( feature -> app_args , app_args , FEATURE_APP_ARGS_LEN );
2005-08-23 02:22:33 +00:00
2006-08-31 21:00:20 +00:00
if ( moh_class )
ast_copy_string ( feature -> moh_class , moh_class , FEATURE_MOH_LEN );
2006-08-07 04:15:52 +00:00
2006-08-31 21:00:20 +00:00
ast_copy_string ( feature -> exten , exten , sizeof ( feature -> exten ));
feature -> operation = feature_exec_app ;
ast_set_flag ( feature , AST_FEATURE_FLAG_NEEDSDTMF );
/* Allow caller and calle to be specified for backwards compatability */
if ( ! strcasecmp ( activateon , "self" ) || ! strcasecmp ( activateon , "caller" ))
ast_set_flag ( feature , AST_FEATURE_FLAG_ONSELF );
else if ( ! strcasecmp ( activateon , "peer" ) || ! strcasecmp ( activateon , "callee" ))
ast_set_flag ( feature , AST_FEATURE_FLAG_ONPEER );
else {
ast_log ( LOG_NOTICE , "Invalid 'ActivateOn' specification for feature '%s',"
" must be 'self', or 'peer' \n " , var -> name );
continue ;
}
2005-08-23 02:22:33 +00:00
2006-08-31 21:00:20 +00:00
if ( ast_strlen_zero ( activatedby ))
ast_set_flag ( feature , AST_FEATURE_FLAG_BYBOTH );
else if ( ! strcasecmp ( activatedby , "caller" ))
ast_set_flag ( feature , AST_FEATURE_FLAG_BYCALLER );
else if ( ! strcasecmp ( activatedby , "callee" ))
ast_set_flag ( feature , AST_FEATURE_FLAG_BYCALLEE );
else if ( ! strcasecmp ( activatedby , "both" ))
ast_set_flag ( feature , AST_FEATURE_FLAG_BYBOTH );
else {
ast_log ( LOG_NOTICE , "Invalid 'ActivatedBy' specification for feature '%s',"
" must be 'caller', or 'callee', or 'both' \n " , var -> name );
continue ;
}
2006-08-07 04:15:52 +00:00
2006-08-31 21:00:20 +00:00
ast_register_feature ( feature );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Mapping Feature '%s' to app '%s(%s)' with code '%s' \n " , var -> name , app , app_args , exten );
2007-05-31 18:21:47 +00:00
}
ast_unregister_groups ();
AST_RWLIST_WRLOCK ( & feature_groups );
ctg = NULL ;
while (( ctg = ast_category_browse ( cfg , ctg ))) {
for ( i = 0 ; i < ARRAY_LEN ( categories ); i ++ ) {
if ( ! strcasecmp ( categories [ i ], ctg ))
break ;
}
if ( i < ARRAY_LEN ( categories ))
continue ;
if ( ! ( fg = register_group ( ctg )))
continue ;
for ( var = ast_variable_browse ( cfg , ctg ); var ; var = var -> next ) {
2007-06-28 19:02:31 +00:00
struct ast_call_feature * feature ;
2007-05-31 18:21:47 +00:00
AST_LIST_LOCK ( & feature_list );
if ( ! ( feature = find_dynamic_feature ( var -> name )) &&
! ( feature = ast_find_call_feature ( var -> name ))) {
AST_LIST_UNLOCK ( & feature_list );
ast_log ( LOG_WARNING , "Feature '%s' was not found. \n " , var -> name );
continue ;
}
AST_LIST_UNLOCK ( & feature_list );
register_group_feature ( fg , var -> value , feature );
}
}
AST_RWLIST_UNLOCK ( & feature_groups );
2005-08-23 02:22:33 +00:00
ast_config_destroy ( cfg );
2005-10-13 23:58:33 +00:00
/* Remove the old parking extension */
if ( ! ast_strlen_zero ( old_parking_con ) && ( con = ast_context_find ( old_parking_con ))) {
2006-06-26 16:43:21 +00:00
if ( ast_context_remove_extension2 ( con , old_parking_ext , 1 , registrar ))
2007-08-10 16:24:11 +00:00
notify_metermaids ( old_parking_ext , old_parking_con , AST_DEVICE_NOT_INUSE );
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Removed old parking extension %s@%s \n " , old_parking_ext , old_parking_con );
2005-10-13 23:58:33 +00:00
}
2005-01-10 04:03:30 +00:00
2006-04-15 00:05:08 +00:00
if ( ! ( con = ast_context_find ( parking_con )) && ! ( con = ast_context_create ( NULL , parking_con , registrar ))) {
ast_log ( LOG_ERROR , "Parking context '%s' does not exist and unable to create \n " , parking_con );
return - 1 ;
2001-12-27 11:07:33 +00:00
}
2007-01-06 00:28:16 +00:00
res = ast_add_extension2 ( con , 1 , ast_parking_ext (), 1 , NULL , NULL , parkcall , NULL , NULL , registrar );
2006-06-26 16:43:21 +00:00
if ( parkaddhints )
park_add_hints ( parking_con , parking_start , parking_stop );
if ( ! res )
2007-08-10 16:24:11 +00:00
notify_metermaids ( ast_parking_ext (), parking_con , AST_DEVICE_INUSE );
2006-06-26 16:43:21 +00:00
return res ;
2005-01-10 04:03:30 +00:00
}
2007-04-10 20:44:44 +00:00
static char * app_bridge = "Bridge" ;
static char * bridge_synopsis = "Bridge two channels" ;
static char * bridge_descrip =
"Usage: Bridge(channel[|options]) \n "
" Allows the ability to bridge two channels via the dialplan. \n "
"The current channel is bridged to the specified 'channel'. \n "
"The following options are supported: \n "
" p - Play a courtesy tone to 'channel'. \n "
"BRIDGERESULT dial plan variable will contain SUCCESS, FAILURE, LOOP, NONEXISTENT or INCOMPATIBLE. \n " ;
enum {
BRIDGE_OPT_PLAYTONE = ( 1 << 0 ),
};
AST_APP_OPTIONS ( bridge_exec_options , BEGIN_OPTIONS
AST_APP_OPTION ( 'p' , BRIDGE_OPT_PLAYTONE )
END_OPTIONS );
2007-08-07 23:04:01 +00:00
/*!
* \brief Bridge channels
* \param chan
2007-09-05 16:31:39 +00:00
* \param data channel to bridge with.
*
2007-08-07 23:04:01 +00:00
* Split data, check we aren't bridging with ourself, check valid channel,
* answer call if not already, check compatible channels, setup bridge config
* now bridge call, if transfered party hangs up return to PBX extension.
*/
2007-04-10 20:44:44 +00:00
static int bridge_exec ( struct ast_channel * chan , void * data )
{
struct ast_module_user * u ;
struct ast_channel * current_dest_chan , * final_dest_chan ;
char * tmp_data = NULL ;
struct ast_flags opts = { 0 , };
struct ast_bridge_config bconfig = { { 0 , }, };
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( dest_chan );
AST_APP_ARG ( options );
);
if ( ast_strlen_zero ( data )) {
ast_log ( LOG_WARNING , "Bridge require at least 1 argument specifying the other end of the bridge \n " );
return - 1 ;
}
u = ast_module_user_add ( chan );
tmp_data = ast_strdupa ( data );
AST_STANDARD_APP_ARGS ( args , tmp_data );
if ( ! ast_strlen_zero ( args . options ))
ast_app_parse_options ( bridge_exec_options , & opts , NULL , args . options );
/* avoid bridge with ourselves */
if ( ! strncmp ( chan -> name , args . dest_chan ,
strlen ( chan -> name ) < strlen ( args . dest_chan ) ?
strlen ( chan -> name ) : strlen ( args . dest_chan ))) {
ast_log ( LOG_WARNING , "Unable to bridge channel %s with itself \n " , chan -> name );
manager_event ( EVENT_FLAG_CALL , "BridgeExec" ,
"Response: Failed \r\n "
"Reason: Unable to bridge channel to itself \r\n "
"Channel1: %s \r\n "
"Channel2: %s \r\n " ,
chan -> name , args . dest_chan );
pbx_builtin_setvar_helper ( chan , "BRIDGERESULT" , "LOOP" );
ast_module_user_remove ( u );
return 0 ;
}
/* make sure we have a valid end point */
if ( ! ( current_dest_chan = ast_get_channel_by_name_prefix_locked ( args . dest_chan ,
strlen ( args . dest_chan )))) {
ast_log ( LOG_WARNING , "Bridge failed because channel %s does not exists or we "
"cannot get its lock \n " , args . dest_chan );
manager_event ( EVENT_FLAG_CALL , "BridgeExec" ,
"Response: Failed \r\n "
"Reason: Cannot grab end point \r\n "
"Channel1: %s \r\n "
"Channel2: %s \r\n " , chan -> name , args . dest_chan );
pbx_builtin_setvar_helper ( chan , "BRIDGERESULT" , "NONEXISTENT" );
ast_module_user_remove ( u );
return 0 ;
}
ast_mutex_unlock ( & current_dest_chan -> lock );
/* answer the channel if needed */
if ( current_dest_chan -> _state != AST_STATE_UP )
ast_answer ( current_dest_chan );
/* try to allocate a place holder where current_dest_chan will be placed */
if ( ! ( final_dest_chan = ast_channel_alloc ( 0 , AST_STATE_DOWN , NULL , NULL , NULL ,
NULL , NULL , 0 , "Bridge/%s" , current_dest_chan -> name ))) {
ast_log ( LOG_WARNING , "Cannot create placeholder channel for chan %s \n " , args . dest_chan );
manager_event ( EVENT_FLAG_CALL , "BridgeExec" ,
"Response: Failed \r\n "
"Reason: cannot create placeholder \r\n "
"Channel1: %s \r\n "
"Channel2: %s \r\n " , chan -> name , args . dest_chan );
}
do_bridge_masquerade ( current_dest_chan , final_dest_chan );
/* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
/* try to make compatible, send error if we fail */
if ( ast_channel_make_compatible ( chan , final_dest_chan ) < 0 ) {
ast_log ( LOG_WARNING , "Could not make channels %s and %s compatible for bridge \n " , chan -> name , final_dest_chan -> name );
manager_event ( EVENT_FLAG_CALL , "BridgeExec" ,
"Response: Failed \r\n "
"Reason: Could not make channels compatible for bridge \r\n "
"Channel1: %s \r\n "
"Channel2: %s \r\n " , chan -> name , final_dest_chan -> name );
ast_hangup ( final_dest_chan ); /* may be we should return this channel to the PBX? */
pbx_builtin_setvar_helper ( chan , "BRIDGERESULT" , "INCOMPATIBLE" );
ast_module_user_remove ( u );
return 0 ;
}
/* Report that the bridge will be successfull */
manager_event ( EVENT_FLAG_CALL , "BridgeExec" ,
"Response: Success \r\n "
"Channel1: %s \r\n "
"Channel2: %s \r\n " , chan -> name , final_dest_chan -> name );
/* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */
if ( ast_test_flag ( & opts , BRIDGE_OPT_PLAYTONE ) && ! ast_strlen_zero ( xfersound )) {
if ( ! ast_streamfile ( final_dest_chan , xfersound , final_dest_chan -> language )) {
if ( ast_waitstream ( final_dest_chan , "" ) < 0 )
ast_log ( LOG_WARNING , "Failed to play courtesy tone on %s \n " , final_dest_chan -> name );
}
}
/* do the bridge */
ast_bridge_call ( chan , final_dest_chan , & bconfig );
/* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */
pbx_builtin_setvar_helper ( chan , "BRIDGERESULT" , "SUCCESS" );
if ( ! ast_check_hangup ( final_dest_chan )) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "starting new PBX in %s,%s,%d for chan %s \n " ,
2007-04-10 20:44:44 +00:00
final_dest_chan -> context , final_dest_chan -> exten ,
final_dest_chan -> priority , final_dest_chan -> name );
if ( ast_pbx_start ( final_dest_chan ) != AST_PBX_SUCCESS ) {
ast_log ( LOG_WARNING , "FAILED continuing PBX on dest chan %s \n " , final_dest_chan -> name );
ast_hangup ( final_dest_chan );
2007-06-14 19:39:12 +00:00
} else
ast_debug ( 1 , "SUCCESS continuing PBX on chan %s \n " , final_dest_chan -> name );
2007-04-10 20:44:44 +00:00
} else {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "hangup chan %s since the other endpoint has hung up \n " , final_dest_chan -> name );
2007-04-10 20:44:44 +00:00
ast_hangup ( final_dest_chan );
}
ast_module_user_remove ( u );
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2006-04-14 14:08:19 +00:00
{
2005-01-10 04:03:30 +00:00
return load_config ();
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2005-01-10 04:03:30 +00:00
{
int res ;
2007-04-10 20:44:44 +00:00
ast_register_application ( app_bridge , bridge_exec , bridge_synopsis , bridge_descrip );
2005-10-13 23:58:33 +00:00
memset ( parking_ext , 0 , sizeof ( parking_ext ));
memset ( parking_con , 0 , sizeof ( parking_con ));
2005-08-23 02:22:33 +00:00
2005-01-10 04:03:30 +00:00
if (( res = load_config ()))
return res ;
2006-09-18 19:54:18 +00:00
ast_cli_register_multiple ( cli_features , sizeof ( cli_features ) / sizeof ( struct ast_cli_entry ));
2004-08-08 17:15:02 +00:00
ast_pthread_create ( & parking_thread , NULL , do_parking_thread , NULL );
2004-01-30 06:31:25 +00:00
res = ast_register_application ( parkedcall , park_exec , synopsis , descrip );
2004-08-03 06:31:20 +00:00
if ( ! res )
res = ast_register_application ( parkcall , park_call_exec , synopsis2 , descrip2 );
2004-01-30 05:49:44 +00:00
if ( ! res ) {
2007-04-10 20:44:44 +00:00
ast_manager_register ( "ParkedCalls" , 0 , manager_parking_status , "List parked calls" );
2006-03-06 23:12:48 +00:00
ast_manager_register2 ( "Park" , EVENT_FLAG_CALL , manager_park ,
"Park a channel" , mandescr_park );
2007-04-10 20:44:44 +00:00
ast_manager_register2 ( "Bridge" , EVENT_FLAG_COMMAND , action_bridge , "Bridge two channels already in the PBX" , mandescr_bridge );
2004-01-30 05:49:44 +00:00
}
2006-06-26 16:43:21 +00:00
res |= ast_devstate_prov_add ( "Park" , metermaidstate );
2001-12-27 11:07:33 +00:00
return res ;
}
2003-04-09 04:00:43 +00:00
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2001-12-27 11:07:33 +00:00
{
2005-07-25 17:31:53 +00:00
ast_manager_unregister ( "ParkedCalls" );
2007-04-10 20:44:44 +00:00
ast_manager_unregister ( "Bridge" );
2006-03-06 23:12:48 +00:00
ast_manager_unregister ( "Park" );
2006-09-18 19:54:18 +00:00
ast_cli_unregister_multiple ( cli_features , sizeof ( cli_features ) / sizeof ( struct ast_cli_entry ));
2004-08-03 06:31:20 +00:00
ast_unregister_application ( parkcall );
2007-04-10 20:44:44 +00:00
ast_unregister_application ( app_bridge );
2006-06-26 16:43:21 +00:00
ast_devstate_prov_del ( "Park" );
2001-12-27 11:07:33 +00:00
return ast_unregister_application ( parkedcall );
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_GLOBAL_SYMBOLS , "Call Features Resource" ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
2007-04-10 20:44:44 +00:00
);