2007-01-24 18:23:07 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2007, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Dialing API
*
2008-01-24 03:25:52 +00:00
* \author Joshua Colp <jcolp@digium.com>
2007-01-24 18:23:07 +00:00
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2007-01-24 18:23:07 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
#include <sys/time.h>
#include <signal.h>
#include "asterisk/channel.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/dial.h"
#include "asterisk/pbx.h"
2007-04-10 19:16:24 +00:00
#include "asterisk/musiconhold.h"
2009-06-01 20:57:31 +00:00
#include "asterisk/app.h"
2007-01-24 18:23:07 +00:00
/*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
struct ast_dial {
2007-04-28 21:01:44 +00:00
int num ; /*!< Current number to give to next dialed channel */
2007-07-30 20:42:28 +00:00
int timeout ; /*!< Maximum time allowed for dial attempts */
int actual_timeout ; /*!< Actual timeout based on all factors (ie: channels) */
2007-04-28 21:01:44 +00:00
enum ast_dial_result state ; /*!< Status of dial */
void * options [ AST_DIAL_OPTION_MAX ]; /*!< Global options */
ast_dial_state_callback state_callback ; /*!< Status callback */
2012-03-10 20:06:46 +00:00
void * user_data ; /*!< Attached user data */
2008-02-28 20:14:04 +00:00
AST_LIST_HEAD (, ast_dial_channel ) channels ; /*!< Channels being dialed */
2007-04-28 21:01:44 +00:00
pthread_t thread ; /*!< Thread (if running in async) */
2012-03-29 20:01:20 +00:00
struct ast_callid * callid ; /*!< callid pointer (if running in async) */
2008-01-16 15:09:37 +00:00
ast_mutex_t lock ; /*! Lock to protect the thread information above */
2007-01-24 18:23:07 +00:00
};
/*! \brief Dialing channel structure. Contains per-channel dialing options, asterisk channel, and more! */
struct ast_dial_channel {
2008-10-30 16:49:02 +00:00
int num ; /*!< Unique number for dialed channel */
int timeout ; /*!< Maximum time allowed for attempt */
char * tech ; /*!< Technology being dialed */
char * device ; /*!< Device being dialed */
void * options [ AST_DIAL_OPTION_MAX ]; /*!< Channel specific options */
int cause ; /*!< Cause code in case of failure */
unsigned int is_running_app : 1 ; /*!< Is this running an application? */
struct ast_channel * owner ; /*!< Asterisk channel */
AST_LIST_ENTRY ( ast_dial_channel ) list ; /*!< Linked list information */
2007-01-24 18:23:07 +00:00
};
/*! \brief Typedef for dial option enable */
typedef void * ( * ast_dial_option_cb_enable )( void * data );
/*! \brief Typedef for dial option disable */
typedef int ( * ast_dial_option_cb_disable )( void * data );
2007-05-16 07:08:48 +00:00
/*! \brief Structure for 'ANSWER_EXEC' option */
2007-01-24 18:23:07 +00:00
struct answer_exec_struct {
2007-05-16 07:08:48 +00:00
char app [ AST_MAX_APP ]; /*!< Application name */
char * args ; /*!< Application arguments */
2007-01-24 18:23:07 +00:00
};
2007-05-16 07:08:48 +00:00
/*! \brief Enable function for 'ANSWER_EXEC' option */
2007-01-24 18:23:07 +00:00
static void * answer_exec_enable ( void * data )
{
struct answer_exec_struct * answer_exec = NULL ;
char * app = ast_strdupa (( char * ) data ), * args = NULL ;
/* Not giving any data to this option is bad, mmmk? */
if ( ast_strlen_zero ( app ))
return NULL ;
/* Create new data structure */
if ( ! ( answer_exec = ast_calloc ( 1 , sizeof ( * answer_exec ))))
return NULL ;
2012-03-22 19:51:16 +00:00
2007-01-24 18:23:07 +00:00
/* Parse out application and arguments */
2008-06-13 14:15:07 +00:00
if (( args = strchr ( app , ',' ))) {
2007-01-24 18:23:07 +00:00
* args ++ = '\0' ;
answer_exec -> args = ast_strdup ( args );
}
/* Copy application name */
ast_copy_string ( answer_exec -> app , app , sizeof ( answer_exec -> app ));
return answer_exec ;
}
2007-05-16 07:08:48 +00:00
/*! \brief Disable function for 'ANSWER_EXEC' option */
2007-01-24 18:23:07 +00:00
static int answer_exec_disable ( void * data )
{
struct answer_exec_struct * answer_exec = data ;
/* Make sure we have a value */
if ( ! answer_exec )
return - 1 ;
/* If arguments are present, free them too */
if ( answer_exec -> args )
2007-06-06 21:20:11 +00:00
ast_free ( answer_exec -> args );
2007-01-24 18:23:07 +00:00
/* This is simple - just free the structure */
2007-06-06 21:20:11 +00:00
ast_free ( answer_exec );
2007-01-24 18:23:07 +00:00
return 0 ;
}
2007-04-10 19:16:24 +00:00
static void * music_enable ( void * data )
{
return ast_strdup ( data );
}
static int music_disable ( void * data )
{
if ( ! data )
return - 1 ;
2007-06-06 21:20:11 +00:00
ast_free ( data );
2007-04-10 19:16:24 +00:00
return 0 ;
}
2007-05-16 07:08:48 +00:00
/*! \brief Application execution function for 'ANSWER_EXEC' option */
2007-11-26 21:14:07 +00:00
static void answer_exec_run ( struct ast_dial * dial , struct ast_dial_channel * dial_channel , char * app , char * args )
2007-01-24 18:23:07 +00:00
{
2007-11-26 21:14:07 +00:00
struct ast_channel * chan = dial_channel -> owner ;
2007-01-24 18:23:07 +00:00
struct ast_app * ast_app = pbx_findapp ( app );
/* If the application was not found, return immediately */
if ( ! ast_app )
return ;
/* All is well... execute the application */
pbx_exec ( chan , ast_app , args );
2007-11-26 21:14:07 +00:00
/* If another thread is not taking over hang up the channel */
2008-01-16 15:09:37 +00:00
ast_mutex_lock ( & dial -> lock );
2007-11-26 21:14:07 +00:00
if ( dial -> thread != AST_PTHREADT_STOP ) {
ast_hangup ( chan );
dial_channel -> owner = NULL ;
}
2008-01-16 15:09:37 +00:00
ast_mutex_unlock ( & dial -> lock );
2007-11-26 21:14:07 +00:00
2007-01-24 18:23:07 +00:00
return ;
}
/*! \brief Options structure - maps options to respective handlers (enable/disable). This list MUST be perfectly kept in order, or else madness will happen. */
static const struct ast_option_types {
enum ast_dial_option option ;
ast_dial_option_cb_enable enable ;
ast_dial_option_cb_disable disable ;
} option_types [] = {
2007-04-10 19:16:24 +00:00
{ AST_DIAL_OPTION_RINGING , NULL , NULL }, /*!< Always indicate ringing to caller */
{ AST_DIAL_OPTION_ANSWER_EXEC , answer_exec_enable , answer_exec_disable }, /*!< Execute application upon answer in async mode */
{ AST_DIAL_OPTION_MUSIC , music_enable , music_disable }, /*!< Play music to the caller instead of ringing */
2007-07-30 20:42:28 +00:00
{ AST_DIAL_OPTION_DISABLE_CALL_FORWARDING , NULL , NULL }, /*!< Disable call forwarding on channels */
2007-04-10 19:16:24 +00:00
{ AST_DIAL_OPTION_MAX , NULL , NULL }, /*!< Terminator of list */
2007-01-24 18:23:07 +00:00
};
/*! \brief Maximum number of channels we can watch at a time */
#define AST_MAX_WATCHERS 256
/*! \brief Macro for finding the option structure to use on a dialed channel */
#define FIND_RELATIVE_OPTION(dial, dial_channel, ast_dial_option) (dial_channel->options[ast_dial_option] ? dial_channel->options[ast_dial_option] : dial->options[ast_dial_option])
/*! \brief Macro that determines whether a channel is the caller or not */
#define IS_CALLER(chan, owner) (chan == owner ? 1 : 0)
/*! \brief New dialing structure
* \note Create a dialing structure
* \return Returns a calloc'd ast_dial structure, NULL on failure
*/
struct ast_dial * ast_dial_create ( void )
{
struct ast_dial * dial = NULL ;
/* Allocate new memory for structure */
if ( ! ( dial = ast_calloc ( 1 , sizeof ( * dial ))))
return NULL ;
/* Initialize list of channels */
2008-02-28 20:14:04 +00:00
AST_LIST_HEAD_INIT ( & dial -> channels );
2007-01-24 18:23:07 +00:00
/* Initialize thread to NULL */
dial -> thread = AST_PTHREADT_NULL ;
2007-07-30 20:42:28 +00:00
/* No timeout exists... yet */
dial -> timeout = - 1 ;
dial -> actual_timeout = - 1 ;
2008-01-16 15:09:37 +00:00
/* Can't forget about the lock */
ast_mutex_init ( & dial -> lock );
2007-01-24 18:23:07 +00:00
return dial ;
}
/*! \brief Append a channel
* \note Appends a channel to a dialing structure
* \return Returns channel reference number on success, -1 on failure
*/
int ast_dial_append ( struct ast_dial * dial , const char * tech , const char * device )
{
struct ast_dial_channel * channel = NULL ;
/* Make sure we have required arguments */
if ( ! dial || ! tech || ! device )
return - 1 ;
/* Allocate new memory for dialed channel structure */
if ( ! ( channel = ast_calloc ( 1 , sizeof ( * channel ))))
return - 1 ;
/* Record technology and device for when we actually dial */
2007-07-30 20:42:28 +00:00
channel -> tech = ast_strdup ( tech );
channel -> device = ast_strdup ( device );
2007-01-24 18:23:07 +00:00
/* Grab reference number from dial structure */
channel -> num = ast_atomic_fetchadd_int ( & dial -> num , + 1 );
2007-07-30 20:42:28 +00:00
/* No timeout exists... yet */
channel -> timeout = - 1 ;
2007-01-24 18:23:07 +00:00
/* Insert into channels list */
AST_LIST_INSERT_TAIL ( & dial -> channels , channel , list );
return channel -> num ;
}
2007-07-30 20:42:28 +00:00
/*! \brief Helper function that does the beginning dialing per-appended channel */
static int begin_dial_channel ( struct ast_dial_channel * channel , struct ast_channel * chan )
{
char numsubst [ AST_MAX_EXTENSION ];
int res = 1 ;
2011-02-03 16:22:10 +00:00
struct ast_format_cap * cap_all_audio = NULL ;
struct ast_format_cap * cap_request ;
2007-07-30 20:42:28 +00:00
/* Copy device string over */
ast_copy_string ( numsubst , channel -> device , sizeof ( numsubst ));
2011-02-03 16:22:10 +00:00
if ( chan ) {
2012-02-20 23:43:27 +00:00
cap_request = ast_channel_nativeformats ( chan );
2011-02-03 16:22:10 +00:00
} else {
cap_all_audio = ast_format_cap_alloc_nolock ();
ast_format_cap_add_all_by_type ( cap_all_audio , AST_FORMAT_TYPE_AUDIO );
cap_request = cap_all_audio ;
}
2007-07-30 20:42:28 +00:00
/* If we fail to create our owner channel bail out */
2011-02-03 16:22:10 +00:00
if ( ! ( channel -> owner = ast_request ( channel -> tech , cap_request , chan , numsubst , & channel -> cause ))) {
cap_all_audio = ast_format_cap_destroy ( cap_all_audio );
2007-07-30 20:42:28 +00:00
return - 1 ;
2011-02-03 16:22:10 +00:00
}
cap_request = NULL ;
cap_all_audio = ast_format_cap_destroy ( cap_all_audio );
2007-07-30 20:42:28 +00:00
2012-02-13 17:27:06 +00:00
ast_channel_appl_set ( channel -> owner , "AppDial2" );
ast_channel_data_set ( channel -> owner , "(Outgoing Line)" );
2012-02-29 16:52:47 +00:00
memset ( ast_channel_whentohangup ( channel -> owner ), 0 , sizeof ( * ast_channel_whentohangup ( channel -> owner )));
2007-07-30 20:42:28 +00:00
/* Inherit everything from he who spawned this dial */
if ( chan ) {
ast_channel_inherit_variables ( chan , channel -> owner );
2008-10-03 17:35:37 +00:00
ast_channel_datastore_inherit ( chan , channel -> owner );
2007-07-30 20:42:28 +00:00
/* Copy over callerid information */
2012-02-29 16:52:47 +00:00
ast_party_redirecting_copy ( ast_channel_redirecting ( channel -> owner ), ast_channel_redirecting ( chan ));
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_channel_dialed ( channel -> owner ) -> transit_network_select = ast_channel_dialed ( chan ) -> transit_network_select ;
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_connected_line_copy_from_caller ( ast_channel_connected ( channel -> owner ), ast_channel_caller ( chan ));
2007-07-30 20:42:28 +00:00
2012-01-24 20:12:09 +00:00
ast_channel_language_set ( channel -> owner , ast_channel_language ( chan ));
ast_channel_accountcode_set ( channel -> owner , ast_channel_accountcode ( chan ));
if ( ast_strlen_zero ( ast_channel_musicclass ( channel -> owner )))
ast_channel_musicclass_set ( channel -> owner , ast_channel_musicclass ( chan ));
2007-07-30 20:42:28 +00:00
2012-02-20 23:43:27 +00:00
ast_channel_adsicpe_set ( channel -> owner , ast_channel_adsicpe ( chan ));
ast_channel_transfercapability_set ( channel -> owner , ast_channel_transfercapability ( chan ));
2007-07-30 20:42:28 +00:00
}
/* Attempt to actually call this device */
if (( res = ast_call ( channel -> owner , numsubst , 0 ))) {
res = 0 ;
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
} else {
2007-08-10 18:37:32 +00:00
if ( chan )
ast_poll_channel_add ( chan , channel -> owner );
2007-07-30 20:42:28 +00:00
res = 1 ;
ast_verb ( 3 , "Called %s \n " , numsubst );
}
return res ;
}
/*! \brief Helper function that does the beginning dialing per dial structure */
2007-01-24 18:23:07 +00:00
static int begin_dial ( struct ast_dial * dial , struct ast_channel * chan )
{
struct ast_dial_channel * channel = NULL ;
2007-07-30 20:42:28 +00:00
int success = 0 ;
2007-01-24 18:23:07 +00:00
/* Iterate through channel list, requesting and calling each one */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
2007-07-30 20:42:28 +00:00
success += begin_dial_channel ( channel , chan );
}
2008-03-20 18:01:36 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-07-30 20:42:28 +00:00
/* If number of failures matches the number of channels, then this truly failed */
return success ;
}
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/*! \brief Helper function to handle channels that have been call forwarded */
static int handle_call_forward ( struct ast_dial * dial , struct ast_dial_channel * channel , struct ast_channel * chan )
{
struct ast_channel * original = channel -> owner ;
2012-01-24 20:12:09 +00:00
char * tmp = ast_strdupa ( ast_channel_call_forward ( channel -> owner ));
2007-07-30 20:42:28 +00:00
char * tech = "Local" , * device = tmp , * stuff ;
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
if ( FIND_RELATIVE_OPTION ( dial , channel , AST_DIAL_OPTION_DISABLE_CALL_FORWARDING )) {
ast_hangup ( original );
channel -> owner = NULL ;
return 0 ;
}
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/* Figure out the new destination */
if (( stuff = strchr ( tmp , '/' ))) {
* stuff ++ = '\0' ;
tech = tmp ;
device = stuff ;
}
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/* Drop old destination information */
ast_free ( channel -> tech );
ast_free ( channel -> device );
2007-02-10 00:40:57 +00:00
2007-07-30 20:42:28 +00:00
/* Update the dial channel with the new destination information */
channel -> tech = ast_strdup ( tech );
channel -> device = ast_strdup ( device );
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/* Finally give it a go... send it out into the world */
begin_dial_channel ( channel , chan );
2007-01-24 18:23:07 +00:00
2007-07-30 20:42:28 +00:00
/* Drop the original channel */
ast_hangup ( original );
return 0 ;
2007-01-24 18:23:07 +00:00
}
/*! \brief Helper function that finds the dialed channel based on owner */
static struct ast_dial_channel * find_relative_dial_channel ( struct ast_dial * dial , struct ast_channel * owner )
{
struct ast_dial_channel * channel = NULL ;
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( channel -> owner == owner )
break ;
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
return channel ;
}
2007-02-12 18:01:15 +00:00
static void set_state ( struct ast_dial * dial , enum ast_dial_result state )
{
dial -> state = state ;
if ( dial -> state_callback )
dial -> state_callback ( dial );
}
2007-01-24 18:23:07 +00:00
/*! \brief Helper function that handles control frames WITH owner */
static void handle_frame ( struct ast_dial * dial , struct ast_dial_channel * channel , struct ast_frame * fr , struct ast_channel * chan )
{
if ( fr -> frametype == AST_FRAME_CONTROL ) {
2009-11-04 14:05:12 +00:00
switch ( fr -> subclass . integer ) {
2007-01-24 18:23:07 +00:00
case AST_CONTROL_ANSWER :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s answered %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_REMOVE ( & dial -> channels , channel , list );
AST_LIST_INSERT_HEAD ( & dial -> channels , channel , list );
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_ANSWERED );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_BUSY :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is busy \n " , ast_channel_name ( channel -> owner ));
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
break ;
case AST_CONTROL_CONGESTION :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is circuit-busy \n " , ast_channel_name ( channel -> owner ));
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
break ;
2011-09-09 16:28:23 +00:00
case AST_CONTROL_INCOMPLETE :
2012-02-13 17:27:06 +00:00
ast_verb ( 3 , "%s dialed Incomplete extension %s \n " , ast_channel_name ( channel -> owner ), ast_channel_exten ( channel -> owner ));
2011-09-09 16:28:23 +00:00
ast_indicate ( chan , AST_CONTROL_INCOMPLETE );
break ;
2007-01-24 18:23:07 +00:00
case AST_CONTROL_RINGING :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is ringing \n " , ast_channel_name ( channel -> owner ));
2007-04-10 19:16:24 +00:00
if ( ! dial -> options [ AST_DIAL_OPTION_MUSIC ])
ast_indicate ( chan , AST_CONTROL_RINGING );
2007-04-24 16:17:36 +00:00
set_state ( dial , AST_DIAL_RESULT_RINGING );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_PROGRESS :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is making progress, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_PROGRESS );
2007-04-24 16:17:36 +00:00
set_state ( dial , AST_DIAL_RESULT_PROGRESS );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_VIDUPDATE :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s requested a video update, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_VIDUPDATE );
break ;
2008-03-05 22:43:22 +00:00
case AST_CONTROL_SRCUPDATE :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s requested a source update, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2008-03-05 22:43:22 +00:00
ast_indicate ( chan , AST_CONTROL_SRCUPDATE );
2008-03-16 21:50:58 +00:00
break ;
2009-04-03 22:41:46 +00:00
case AST_CONTROL_CONNECTED_LINE :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s connected line has changed, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2012-02-27 16:50:19 +00:00
if ( ast_channel_connected_line_sub ( channel -> owner , chan , fr , 1 ) &&
ast_channel_connected_line_macro ( channel -> owner , chan , fr , 1 , 1 )) {
2009-06-01 20:57:31 +00:00
ast_indicate_data ( chan , AST_CONTROL_CONNECTED_LINE , fr -> data . ptr , fr -> datalen );
}
2009-04-03 22:41:46 +00:00
break ;
case AST_CONTROL_REDIRECTING :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s redirecting info has changed, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2012-02-27 16:50:19 +00:00
if ( ast_channel_redirecting_sub ( channel -> owner , chan , fr , 1 ) &&
ast_channel_redirecting_macro ( channel -> owner , chan , fr , 1 , 1 )) {
2010-05-17 15:36:31 +00:00
ast_indicate_data ( chan , AST_CONTROL_REDIRECTING , fr -> data . ptr , fr -> datalen );
}
2009-04-03 22:41:46 +00:00
break ;
2007-01-24 18:23:07 +00:00
case AST_CONTROL_PROCEEDING :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is proceeding, passing it to %s \n " , ast_channel_name ( channel -> owner ), ast_channel_name ( chan ));
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_PROCEEDING );
2007-04-24 16:17:36 +00:00
set_state ( dial , AST_DIAL_RESULT_PROCEEDING );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_HOLD :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "Call on %s placed on hold \n " , ast_channel_name ( chan ));
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_HOLD );
break ;
case AST_CONTROL_UNHOLD :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "Call on %s left from hold \n " , ast_channel_name ( chan ));
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_UNHOLD );
break ;
case AST_CONTROL_OFFHOOK :
case AST_CONTROL_FLASH :
break ;
2012-05-14 19:44:27 +00:00
case AST_CONTROL_PVT_CAUSE_CODE :
ast_indicate_data ( chan , AST_CONTROL_PVT_CAUSE_CODE , fr -> data . ptr , fr -> datalen );
break ;
2007-01-24 18:23:07 +00:00
case - 1 :
/* Prod the channel */
ast_indicate ( chan , - 1 );
break ;
default :
break ;
}
}
return ;
}
/*! \brief Helper function that handles control frames WITHOUT owner */
static void handle_frame_ownerless ( struct ast_dial * dial , struct ast_dial_channel * channel , struct ast_frame * fr )
{
2007-02-12 18:01:15 +00:00
/* If we have no owner we can only update the state of the dial structure, so only look at control frames */
2007-01-24 18:23:07 +00:00
if ( fr -> frametype != AST_FRAME_CONTROL )
return ;
2009-11-04 14:05:12 +00:00
switch ( fr -> subclass . integer ) {
2007-01-24 18:23:07 +00:00
case AST_CONTROL_ANSWER :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s answered \n " , ast_channel_name ( channel -> owner ));
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_REMOVE ( & dial -> channels , channel , list );
AST_LIST_INSERT_HEAD ( & dial -> channels , channel , list );
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_ANSWERED );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_BUSY :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is busy \n " , ast_channel_name ( channel -> owner ));
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
break ;
case AST_CONTROL_CONGESTION :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is circuit-busy \n " , ast_channel_name ( channel -> owner ));
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
break ;
case AST_CONTROL_RINGING :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is ringing \n " , ast_channel_name ( channel -> owner ));
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_RINGING );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_PROGRESS :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is making progress \n " , ast_channel_name ( channel -> owner ));
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_PROGRESS );
2007-01-24 18:23:07 +00:00
break ;
case AST_CONTROL_PROCEEDING :
2012-01-09 22:15:50 +00:00
ast_verb ( 3 , "%s is proceeding \n " , ast_channel_name ( channel -> owner ));
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_PROCEEDING );
2007-01-24 18:23:07 +00:00
break ;
default :
break ;
}
return ;
}
2007-07-30 20:42:28 +00:00
/*! \brief Helper function to handle when a timeout occurs on dialing attempt */
static int handle_timeout_trip ( struct ast_dial * dial , struct timeval start )
{
struct ast_dial_channel * channel = NULL ;
int diff = ast_tvdiff_ms ( ast_tvnow (), start ), lowest_timeout = - 1 , new_timeout = - 1 ;
/* If the global dial timeout tripped switch the state to timeout so our channel loop will drop every channel */
if ( diff >= dial -> timeout ) {
set_state ( dial , AST_DIAL_RESULT_TIMEOUT );
new_timeout = 0 ;
}
/* Go through dropping out channels that have met their timeout */
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( dial -> state == AST_DIAL_RESULT_TIMEOUT || diff >= channel -> timeout ) {
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
} else if (( lowest_timeout == - 1 ) || ( lowest_timeout > channel -> timeout )) {
lowest_timeout = channel -> timeout ;
}
}
/* Calculate the new timeout using the lowest timeout found */
if ( lowest_timeout >= 0 )
new_timeout = lowest_timeout - diff ;
return new_timeout ;
}
2007-01-24 18:23:07 +00:00
/*! \brief Helper function that basically keeps tabs on dialing attempts */
static enum ast_dial_result monitor_dial ( struct ast_dial * dial , struct ast_channel * chan )
{
2007-07-30 20:42:28 +00:00
int timeout = - 1 ;
2007-01-24 18:23:07 +00:00
struct ast_channel * cs [ AST_MAX_WATCHERS ], * who = NULL ;
struct ast_dial_channel * channel = NULL ;
struct answer_exec_struct * answer_exec = NULL ;
2007-07-30 20:42:28 +00:00
struct timeval start ;
2007-01-24 18:23:07 +00:00
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_TRYING );
2007-01-24 18:23:07 +00:00
2007-02-12 18:01:15 +00:00
/* If the "always indicate ringing" option is set, change state to ringing and indicate to the owner if present */
2007-01-24 18:23:07 +00:00
if ( dial -> options [ AST_DIAL_OPTION_RINGING ]) {
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_RINGING );
2007-01-24 18:23:07 +00:00
if ( chan )
ast_indicate ( chan , AST_CONTROL_RINGING );
2012-03-22 19:51:16 +00:00
} else if ( chan && dial -> options [ AST_DIAL_OPTION_MUSIC ] &&
2007-04-10 19:16:24 +00:00
! ast_strlen_zero ( dial -> options [ AST_DIAL_OPTION_MUSIC ])) {
2012-01-24 20:12:09 +00:00
char * original_moh = ast_strdupa ( ast_channel_musicclass ( chan ));
2007-04-10 19:16:24 +00:00
ast_indicate ( chan , - 1 );
2012-01-24 20:12:09 +00:00
ast_channel_musicclass_set ( chan , dial -> options [ AST_DIAL_OPTION_MUSIC ]);
2007-04-10 19:16:24 +00:00
ast_moh_start ( chan , dial -> options [ AST_DIAL_OPTION_MUSIC ], NULL );
2012-01-24 20:12:09 +00:00
ast_channel_musicclass_set ( chan , original_moh );
2007-01-24 18:23:07 +00:00
}
2007-07-30 20:42:28 +00:00
/* Record start time for timeout purposes */
start = ast_tvnow ();
/* We actually figured out the maximum timeout we can do as they were added, so we can directly access the info */
timeout = dial -> actual_timeout ;
2007-01-24 18:23:07 +00:00
/* Go into an infinite loop while we are trying */
2007-02-12 18:01:15 +00:00
while (( dial -> state != AST_DIAL_RESULT_UNANSWERED ) && ( dial -> state != AST_DIAL_RESULT_ANSWERED ) && ( dial -> state != AST_DIAL_RESULT_HANGUP ) && ( dial -> state != AST_DIAL_RESULT_TIMEOUT )) {
2007-07-30 20:42:28 +00:00
int pos = 0 , count = 0 ;
2007-01-24 18:23:07 +00:00
struct ast_frame * fr = NULL ;
/* Set up channel structure array */
pos = count = 0 ;
if ( chan )
cs [ pos ++ ] = chan ;
/* Add channels we are attempting to dial */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( channel -> owner ) {
cs [ pos ++ ] = channel -> owner ;
count ++ ;
}
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
2007-02-12 18:01:15 +00:00
/* If we have no outbound channels in progress, switch state to unanswered and stop */
2007-01-24 18:23:07 +00:00
if ( ! count ) {
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_UNANSWERED );
2007-01-24 18:23:07 +00:00
break ;
}
/* Just to be safe... */
if ( dial -> thread == AST_PTHREADT_STOP )
break ;
/* Wait for frames from channels */
who = ast_waitfor_n ( cs , pos , & timeout );
/* Check to see if our thread is being cancelled */
if ( dial -> thread == AST_PTHREADT_STOP )
break ;
2007-07-30 20:42:28 +00:00
/* If the timeout no longer exists OR if we got no channel it basically means the timeout was tripped, so handle it */
if ( ! timeout || ! who ) {
timeout = handle_timeout_trip ( dial , start );
2007-01-24 18:23:07 +00:00
continue ;
2007-07-30 20:42:28 +00:00
}
2007-01-24 18:23:07 +00:00
/* Find relative dial channel */
if ( ! chan || ! IS_CALLER ( chan , who ))
channel = find_relative_dial_channel ( dial , who );
2007-07-30 20:42:28 +00:00
/* See if this channel has been forwarded elsewhere */
2012-01-24 20:12:09 +00:00
if ( ! ast_strlen_zero ( ast_channel_call_forward ( who ))) {
2007-07-30 20:42:28 +00:00
handle_call_forward ( dial , channel , chan );
continue ;
}
2007-01-24 18:23:07 +00:00
/* Attempt to read in a frame */
if ( ! ( fr = ast_read ( who ))) {
2007-02-12 18:01:15 +00:00
/* If this is the caller then we switch state to hangup and stop */
2007-01-24 18:23:07 +00:00
if ( chan && IS_CALLER ( chan , who )) {
2007-02-12 18:01:15 +00:00
set_state ( dial , AST_DIAL_RESULT_HANGUP );
2007-01-24 18:23:07 +00:00
break ;
}
2007-08-10 18:37:32 +00:00
if ( chan )
ast_poll_channel_del ( chan , channel -> owner );
2007-01-24 18:23:07 +00:00
ast_hangup ( who );
channel -> owner = NULL ;
continue ;
}
/* Process the frame */
if ( chan )
handle_frame ( dial , channel , fr , chan );
else
handle_frame_ownerless ( dial , channel , fr );
/* Free the received frame and start all over */
ast_frfree ( fr );
}
/* Do post-processing from loop */
2007-02-12 18:01:15 +00:00
if ( dial -> state == AST_DIAL_RESULT_ANSWERED ) {
2007-01-24 18:23:07 +00:00
/* Hangup everything except that which answered */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( ! channel -> owner || channel -> owner == who )
continue ;
2007-08-10 18:37:32 +00:00
if ( chan )
ast_poll_channel_del ( chan , channel -> owner );
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
/* If ANSWER_EXEC is enabled as an option, execute application on answered channel */
2007-11-26 21:14:07 +00:00
if (( channel = find_relative_dial_channel ( dial , who )) && ( answer_exec = FIND_RELATIVE_OPTION ( dial , channel , AST_DIAL_OPTION_ANSWER_EXEC ))) {
channel -> is_running_app = 1 ;
answer_exec_run ( dial , channel , answer_exec -> app , answer_exec -> args );
channel -> is_running_app = 0 ;
}
2007-04-10 19:16:24 +00:00
2012-03-22 19:51:16 +00:00
if ( chan && dial -> options [ AST_DIAL_OPTION_MUSIC ] &&
2007-04-10 19:16:24 +00:00
! ast_strlen_zero ( dial -> options [ AST_DIAL_OPTION_MUSIC ])) {
ast_moh_stop ( chan );
}
2007-02-12 18:01:15 +00:00
} else if ( dial -> state == AST_DIAL_RESULT_HANGUP ) {
2007-01-24 18:23:07 +00:00
/* Hangup everything */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( ! channel -> owner )
continue ;
2007-08-10 18:37:32 +00:00
if ( chan )
ast_poll_channel_del ( chan , channel -> owner );
2007-01-24 18:23:07 +00:00
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
}
2007-02-12 18:01:15 +00:00
return dial -> state ;
2007-01-24 18:23:07 +00:00
}
/*! \brief Dial async thread function */
static void * async_dial ( void * data )
{
struct ast_dial * dial = data ;
2012-03-29 20:01:20 +00:00
if ( dial -> callid ) {
ast_callid_threadassoc_add ( dial -> callid );
}
2007-01-24 18:23:07 +00:00
/* This is really really simple... we basically pass monitor_dial a NULL owner and it changes it's behavior */
monitor_dial ( dial , NULL );
return NULL ;
}
/*! \brief Execute dialing synchronously or asynchronously
* \note Dials channels in a dial structure.
* \return Returns dial result code. (TRYING/INVALID/FAILED/ANSWERED/TIMEOUT/UNANSWERED).
*/
enum ast_dial_result ast_dial_run ( struct ast_dial * dial , struct ast_channel * chan , int async )
{
enum ast_dial_result res = AST_DIAL_RESULT_TRYING ;
/* Ensure required arguments are passed */
2007-02-10 00:40:57 +00:00
if ( ! dial || ( ! chan && ! async )) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "invalid #1 \n " );
2007-01-24 18:23:07 +00:00
return AST_DIAL_RESULT_INVALID ;
2007-02-10 00:40:57 +00:00
}
2007-01-24 18:23:07 +00:00
/* If there are no channels to dial we can't very well try to dial them */
2007-02-10 00:40:57 +00:00
if ( AST_LIST_EMPTY ( & dial -> channels )) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "invalid #2 \n " );
2007-01-24 18:23:07 +00:00
return AST_DIAL_RESULT_INVALID ;
2007-02-10 00:40:57 +00:00
}
2007-01-24 18:23:07 +00:00
/* Dial each requested channel */
if ( ! begin_dial ( dial , chan ))
return AST_DIAL_RESULT_FAILED ;
/* If we are running async spawn a thread and send it away... otherwise block here */
if ( async ) {
2012-03-29 20:01:20 +00:00
/* reference be released at dial destruction if it isn't NULL */
dial -> callid = ast_read_threadstorage_callid ();
2007-02-22 23:12:26 +00:00
dial -> state = AST_DIAL_RESULT_TRYING ;
2007-01-24 18:23:07 +00:00
/* Try to create a thread */
if ( ast_pthread_create ( & dial -> thread , NULL , async_dial , dial )) {
/* Failed to create the thread - hangup all dialed channels and return failed */
ast_dial_hangup ( dial );
res = AST_DIAL_RESULT_FAILED ;
}
} else {
res = monitor_dial ( dial , chan );
}
return res ;
}
/*! \brief Return channel that answered
* \note Returns the Asterisk channel that answered
* \param dial Dialing structure
*/
struct ast_channel * ast_dial_answered ( struct ast_dial * dial )
{
if ( ! dial )
return NULL ;
2007-02-12 18:01:15 +00:00
return (( dial -> state == AST_DIAL_RESULT_ANSWERED ) ? AST_LIST_FIRST ( & dial -> channels ) -> owner : NULL );
2007-01-24 18:23:07 +00:00
}
2008-01-25 02:52:10 +00:00
/*! \brief Steal the channel that answered
* \note Returns the Asterisk channel that answered and removes it from the dialing structure
* \param dial Dialing structure
*/
struct ast_channel * ast_dial_answered_steal ( struct ast_dial * dial )
{
struct ast_channel * chan = NULL ;
if ( ! dial )
return NULL ;
if ( dial -> state == AST_DIAL_RESULT_ANSWERED ) {
chan = AST_LIST_FIRST ( & dial -> channels ) -> owner ;
AST_LIST_FIRST ( & dial -> channels ) -> owner = NULL ;
}
return chan ;
}
2007-02-12 18:01:15 +00:00
/*! \brief Return state of dial
* \note Returns the state of the dial attempt
2007-01-24 18:23:07 +00:00
* \param dial Dialing structure
*/
2007-02-12 18:01:15 +00:00
enum ast_dial_result ast_dial_state ( struct ast_dial * dial )
2007-01-24 18:23:07 +00:00
{
2007-02-12 18:01:15 +00:00
return dial -> state ;
2007-01-24 18:23:07 +00:00
}
/*! \brief Cancel async thread
* \note Cancel a running async thread
* \param dial Dialing structure
*/
enum ast_dial_result ast_dial_join ( struct ast_dial * dial )
{
pthread_t thread ;
/* If the dial structure is not running in async, return failed */
if ( dial -> thread == AST_PTHREADT_NULL )
return AST_DIAL_RESULT_FAILED ;
/* Record thread */
thread = dial -> thread ;
2008-01-16 15:09:37 +00:00
/* Boom, commence locking */
ast_mutex_lock ( & dial -> lock );
2007-01-24 18:23:07 +00:00
/* Stop the thread */
dial -> thread = AST_PTHREADT_STOP ;
2007-11-26 21:14:07 +00:00
/* If the answered channel is running an application we have to soft hangup it, can't just poke the thread */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-11-26 21:14:07 +00:00
if ( AST_LIST_FIRST ( & dial -> channels ) -> is_running_app ) {
struct ast_channel * chan = AST_LIST_FIRST ( & dial -> channels ) -> owner ;
2008-02-28 20:14:04 +00:00
if ( chan ) {
ast_channel_lock ( chan );
ast_softhangup ( chan , AST_SOFTHANGUP_EXPLICIT );
ast_channel_unlock ( chan );
}
2007-11-26 21:14:07 +00:00
} else {
/* Now we signal it with SIGURG so it will break out of it's waitfor */
pthread_kill ( thread , SIGURG );
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
2008-01-16 15:09:37 +00:00
/* Yay done with it */
ast_mutex_unlock ( & dial -> lock );
2007-01-24 18:23:07 +00:00
/* Finally wait for the thread to exit */
pthread_join ( thread , NULL );
/* Yay thread is all gone */
dial -> thread = AST_PTHREADT_NULL ;
2007-02-12 18:01:15 +00:00
return dial -> state ;
2007-01-24 18:23:07 +00:00
}
/*! \brief Hangup channels
* \note Hangup all active channels
* \param dial Dialing structure
*/
void ast_dial_hangup ( struct ast_dial * dial )
{
struct ast_dial_channel * channel = NULL ;
if ( ! dial )
return ;
2012-03-22 19:51:16 +00:00
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( channel -> owner ) {
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
}
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2007-01-24 18:23:07 +00:00
return ;
}
/*! \brief Destroys a dialing structure
* \note Destroys (free's) the given ast_dial structure
* \param dial Dialing structure to free
* \return Returns 0 on success, -1 on failure
*/
int ast_dial_destroy ( struct ast_dial * dial )
{
int i = 0 ;
struct ast_dial_channel * channel = NULL ;
if ( ! dial )
return - 1 ;
2012-03-22 19:51:16 +00:00
2007-01-24 18:23:07 +00:00
/* Hangup and deallocate all the dialed channels */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-12-21 16:52:04 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & dial -> channels , channel , list ) {
2007-01-24 18:23:07 +00:00
/* Disable any enabled options */
for ( i = 0 ; i < AST_DIAL_OPTION_MAX ; i ++ ) {
if ( ! channel -> options [ i ])
continue ;
if ( option_types [ i ]. disable )
option_types [ i ]. disable ( channel -> options [ i ]);
channel -> options [ i ] = NULL ;
}
/* Hang up channel if need be */
if ( channel -> owner ) {
ast_hangup ( channel -> owner );
channel -> owner = NULL ;
}
/* Free structure */
2007-07-30 20:42:28 +00:00
ast_free ( channel -> tech );
ast_free ( channel -> device );
2007-12-21 17:40:44 +00:00
AST_LIST_REMOVE_CURRENT ( list );
2007-06-06 21:20:11 +00:00
ast_free ( channel );
2007-01-24 18:23:07 +00:00
}
2007-12-21 16:52:04 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2012-03-22 19:51:16 +00:00
2007-01-24 18:23:07 +00:00
/* Disable any enabled options globally */
for ( i = 0 ; i < AST_DIAL_OPTION_MAX ; i ++ ) {
if ( ! dial -> options [ i ])
continue ;
if ( option_types [ i ]. disable )
option_types [ i ]. disable ( dial -> options [ i ]);
dial -> options [ i ] = NULL ;
}
2008-01-16 15:09:37 +00:00
/* Lock be gone! */
ast_mutex_destroy ( & dial -> lock );
2012-03-29 20:01:20 +00:00
/* Get rid of the reference to the ast_callid */
if ( dial -> callid ) {
ast_callid_unref ( dial -> callid );
}
2007-01-24 18:23:07 +00:00
/* Free structure */
2007-06-06 21:20:11 +00:00
ast_free ( dial );
2007-01-24 18:23:07 +00:00
return 0 ;
}
/*! \brief Enables an option globally
* \param dial Dial structure to enable option on
* \param option Option to enable
* \param data Data to pass to this option (not always needed)
* \return Returns 0 on success, -1 on failure
*/
int ast_dial_option_global_enable ( struct ast_dial * dial , enum ast_dial_option option , void * data )
{
/* If the option is already enabled, return failure */
if ( dial -> options [ option ])
return - 1 ;
/* Execute enable callback if it exists, if not simply make sure the value is set */
if ( option_types [ option ]. enable )
dial -> options [ option ] = option_types [ option ]. enable ( data );
else
dial -> options [ option ] = ( void * ) 1 ;
return 0 ;
}
2007-07-30 20:42:28 +00:00
/*! \brief Helper function for finding a channel in a dial structure based on number
*/
static struct ast_dial_channel * find_dial_channel ( struct ast_dial * dial , int num )
{
struct ast_dial_channel * channel = AST_LIST_LAST ( & dial -> channels );
/* We can try to predict programmer behavior, the last channel they added is probably the one they wanted to modify */
if ( channel -> num == num )
return channel ;
/* Hrm not at the end... looking through the list it is! */
2008-02-28 20:14:04 +00:00
AST_LIST_LOCK ( & dial -> channels );
2007-07-30 20:42:28 +00:00
AST_LIST_TRAVERSE ( & dial -> channels , channel , list ) {
if ( channel -> num == num )
break ;
}
2008-02-28 20:14:04 +00:00
AST_LIST_UNLOCK ( & dial -> channels );
2012-03-22 19:51:16 +00:00
2007-07-30 20:42:28 +00:00
return channel ;
}
2007-01-24 18:23:07 +00:00
/*! \brief Enables an option per channel
* \param dial Dial structure
* \param num Channel number to enable option on
* \param option Option to enable
* \param data Data to pass to this option (not always needed)
* \return Returns 0 on success, -1 on failure
*/
int ast_dial_option_enable ( struct ast_dial * dial , int num , enum ast_dial_option option , void * data )
{
struct ast_dial_channel * channel = NULL ;
/* Ensure we have required arguments */
if ( ! dial || AST_LIST_EMPTY ( & dial -> channels ))
return - 1 ;
2007-07-30 20:42:28 +00:00
if ( ! ( channel = find_dial_channel ( dial , num )))
2007-01-24 18:23:07 +00:00
return - 1 ;
/* If the option is already enabled, return failure */
if ( channel -> options [ option ])
return - 1 ;
2008-03-04 23:04:29 +00:00
/* Execute enable callback if it exists, if not simply make sure the value is set */
2007-01-24 18:23:07 +00:00
if ( option_types [ option ]. enable )
channel -> options [ option ] = option_types [ option ]. enable ( data );
else
channel -> options [ option ] = ( void * ) 1 ;
return 0 ;
}
/*! \brief Disables an option globally
* \param dial Dial structure to disable option on
* \param option Option to disable
* \return Returns 0 on success, -1 on failure
*/
int ast_dial_option_global_disable ( struct ast_dial * dial , enum ast_dial_option option )
{
2008-03-04 23:04:29 +00:00
/* If the option is not enabled, return failure */
if ( ! dial -> options [ option ]) {
return - 1 ;
}
2007-01-24 18:23:07 +00:00
/* Execute callback of option to disable if it exists */
if ( option_types [ option ]. disable )
option_types [ option ]. disable ( dial -> options [ option ]);
/* Finally disable option on the structure */
dial -> options [ option ] = NULL ;
2008-03-04 23:04:29 +00:00
return 0 ;
2007-01-24 18:23:07 +00:00
}
/*! \brief Disables an option per channel
* \param dial Dial structure
* \param num Channel number to disable option on
* \param option Option to disable
* \return Returns 0 on success, -1 on failure
*/
int ast_dial_option_disable ( struct ast_dial * dial , int num , enum ast_dial_option option )
{
struct ast_dial_channel * channel = NULL ;
/* Ensure we have required arguments */
if ( ! dial || AST_LIST_EMPTY ( & dial -> channels ))
return - 1 ;
2007-07-30 20:42:28 +00:00
if ( ! ( channel = find_dial_channel ( dial , num )))
2007-01-24 18:23:07 +00:00
return - 1 ;
/* If the option is not enabled, return failure */
if ( ! channel -> options [ option ])
return - 1 ;
/* Execute callback of option to disable it if it exists */
if ( option_types [ option ]. disable )
option_types [ option ]. disable ( channel -> options [ option ]);
/* Finally disable the option on the structure */
channel -> options [ option ] = NULL ;
return 0 ;
}
2007-02-12 18:01:15 +00:00
2007-02-12 19:18:33 +00:00
void ast_dial_set_state_callback ( struct ast_dial * dial , ast_dial_state_callback callback )
2007-02-12 18:01:15 +00:00
{
dial -> state_callback = callback ;
}
2007-07-30 20:42:28 +00:00
2012-03-10 20:06:46 +00:00
void ast_dial_set_user_data ( struct ast_dial * dial , void * user_data )
{
dial -> user_data = user_data ;
}
void * ast_dial_get_user_data ( struct ast_dial * dial )
{
return dial -> user_data ;
}
2007-07-30 20:42:28 +00:00
/*! \brief Set the maximum time (globally) allowed for trying to ring phones
* \param dial The dial structure to apply the time limit to
* \param timeout Maximum time allowed
* \return nothing
*/
void ast_dial_set_global_timeout ( struct ast_dial * dial , int timeout )
{
dial -> timeout = timeout ;
2008-10-31 20:05:46 +00:00
if ( dial -> timeout > 0 && ( dial -> actual_timeout > dial -> timeout || dial -> actual_timeout == - 1 ))
2007-07-30 20:42:28 +00:00
dial -> actual_timeout = dial -> timeout ;
return ;
}
/*! \brief Set the maximum time (per channel) allowed for trying to ring the phone
* \param dial The dial structure the channel belongs to
* \param num Channel number to set timeout on
* \param timeout Maximum time allowed
* \return nothing
*/
void ast_dial_set_timeout ( struct ast_dial * dial , int num , int timeout )
{
struct ast_dial_channel * channel = NULL ;
if ( ! ( channel = find_dial_channel ( dial , num )))
return ;
channel -> timeout = timeout ;
2008-10-31 20:05:46 +00:00
if ( channel -> timeout > 0 && ( dial -> actual_timeout > channel -> timeout || dial -> actual_timeout == - 1 ))
2007-07-30 20:42:28 +00:00
dial -> actual_timeout = channel -> timeout ;
return ;
}