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
*
* \ author Joshua Colp < jcolp @ digium . com >
*/
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/time.h>
# include <signal.h>
# include <errno.h>
# include <unistd.h>
# include "asterisk/logger.h"
# include "asterisk/channel.h"
# include "asterisk/options.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"
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 */
enum ast_dial_result state ; /*!< Status of dial */
void * options [ AST_DIAL_OPTION_MAX ] ; /*!< Global options */
ast_dial_state_callback state_callback ; /*!< Status callback */
AST_LIST_HEAD_NOLOCK ( , ast_dial_channel ) channels ; /*!< Channels being dialed */
pthread_t thread ; /*!< Thread (if running in async) */
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 {
2007-04-28 21:01:44 +00:00
int num ; /*!< Unique number for dialed channel */
const char * tech ; /*!< Technology being dialed */
const char * device ; /*!< Device being dialed */
void * options [ AST_DIAL_OPTION_MAX ] ; /*!< Channel specific options */
int cause ; /*!< Cause code in case of failure */
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 ;
/* Parse out application and arguments */
if ( ( args = strchr ( app , ' | ' ) ) ) {
* 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-01-24 18:23:07 +00:00
static void answer_exec_run ( struct ast_channel * chan , char * app , char * args )
{
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 ) ;
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 */
{ AST_DIAL_OPTION_MAX , NULL , NULL } , /*!< Terminator of list */
2007-01-24 18:23:07 +00:00
} ;
2007-05-16 07:08:48 +00:00
/*! \brief free the buffer if allocated, and set the pointer to the second arg */
2007-01-24 18:23:07 +00:00
# define S_REPLACE(s, new_val) \
do { \
if ( s ) \
free ( s ) ; \
s = ( new_val ) ; \
} while ( 0 )
/*! \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 */
AST_LIST_HEAD_INIT_NOLOCK ( & dial - > channels ) ;
/* Initialize thread to NULL */
dial - > thread = AST_PTHREADT_NULL ;
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 */
channel - > tech = tech ;
channel - > device = device ;
/* Grab reference number from dial structure */
channel - > num = ast_atomic_fetchadd_int ( & dial - > num , + 1 ) ;
/* Insert into channels list */
AST_LIST_INSERT_TAIL ( & dial - > channels , channel , list ) ;
return channel - > num ;
}
/*! \brief Helper function that does the beginning dialing */
static int begin_dial ( struct ast_dial * dial , struct ast_channel * chan )
{
struct ast_dial_channel * channel = NULL ;
int success = 0 , res = 0 ;
/* Iterate through channel list, requesting and calling each one */
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
char numsubst [ AST_MAX_EXTENSION ] ;
/* Copy device string over */
ast_copy_string ( numsubst , channel - > device , sizeof ( numsubst ) ) ;
/* Request that the channel be created */
2007-02-10 00:40:57 +00:00
if ( ! ( channel - > owner = ast_request ( channel - > tech ,
chan ? chan - > nativeformats : AST_FORMAT_AUDIO_MASK , numsubst , & channel - > cause ) ) ) {
2007-01-24 18:23:07 +00:00
continue ;
2007-02-10 00:40:57 +00:00
}
2007-01-24 18:23:07 +00:00
channel - > owner - > appl = " AppDial2 " ;
channel - > owner - > data = " (Outgoing Line) " ;
channel - > owner - > whentohangup = 0 ;
/* Inherit everything from he who spawned this Dial */
2007-02-10 00:40:57 +00:00
if ( chan ) {
ast_channel_inherit_variables ( chan , channel - > owner ) ;
/* Copy over callerid information */
S_REPLACE ( channel - > owner - > cid . cid_num , ast_strdup ( chan - > cid . cid_num ) ) ;
S_REPLACE ( channel - > owner - > cid . cid_name , ast_strdup ( chan - > cid . cid_name ) ) ;
S_REPLACE ( channel - > owner - > cid . cid_ani , ast_strdup ( chan - > cid . cid_ani ) ) ;
S_REPLACE ( channel - > owner - > cid . cid_rdnis , ast_strdup ( chan - > cid . cid_rdnis ) ) ;
ast_string_field_set ( channel - > owner , language , chan - > language ) ;
ast_string_field_set ( channel - > owner , accountcode , chan - > accountcode ) ;
channel - > owner - > cdrflags = chan - > cdrflags ;
if ( ast_strlen_zero ( channel - > owner - > musicclass ) )
ast_string_field_set ( channel - > owner , musicclass , chan - > musicclass ) ;
channel - > owner - > cid . cid_pres = chan - > cid . cid_pres ;
channel - > owner - > cid . cid_ton = chan - > cid . cid_ton ;
channel - > owner - > cid . cid_tns = chan - > cid . cid_tns ;
channel - > owner - > adsicpe = chan - > adsicpe ;
channel - > owner - > transfercapability = chan - > transfercapability ;
}
2007-01-24 18:23:07 +00:00
/* Actually call the device */
if ( ( res = ast_call ( channel - > owner , numsubst , 0 ) ) ) {
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
} else {
success + + ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Called %s \n " , numsubst ) ;
2007-01-24 18:23:07 +00:00
}
}
/* If number of failures matches the number of channels, then this truly failed */
return success ;
}
/*! \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 ;
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( channel - > owner = = owner )
break ;
}
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 ) {
switch ( fr - > subclass ) {
case AST_CONTROL_ANSWER :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s answered %s \n " , channel - > owner - > name , chan - > name ) ;
2007-01-24 18:23:07 +00:00
AST_LIST_REMOVE ( & dial - > channels , channel , list ) ;
AST_LIST_INSERT_HEAD ( & dial - > channels , channel , list ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is busy \n " , channel - > owner - > name ) ;
2007-01-24 18:23:07 +00:00
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
break ;
case AST_CONTROL_CONGESTION :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is circuit-busy \n " , channel - > owner - > name ) ;
2007-01-24 18:23:07 +00:00
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
break ;
case AST_CONTROL_RINGING :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is ringing \n " , channel - > owner - > name ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is making progress, passing it to %s \n " , channel - > owner - > name , chan - > name ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s requested a video update, passing it to %s \n " , channel - > owner - > name , chan - > name ) ;
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_VIDUPDATE ) ;
break ;
case AST_CONTROL_PROCEEDING :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is proceeding, passing it to %s \n " , channel - > owner - > name , chan - > name ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Call on %s placed on hold \n " , chan - > name ) ;
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_HOLD ) ;
break ;
case AST_CONTROL_UNHOLD :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Call on %s left from hold \n " , chan - > name ) ;
2007-01-24 18:23:07 +00:00
ast_indicate ( chan , AST_CONTROL_UNHOLD ) ;
break ;
case AST_CONTROL_OFFHOOK :
case AST_CONTROL_FLASH :
break ;
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 ;
switch ( fr - > subclass ) {
case AST_CONTROL_ANSWER :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s answered \n " , channel - > owner - > name ) ;
2007-01-24 18:23:07 +00:00
AST_LIST_REMOVE ( & dial - > channels , channel , list ) ;
AST_LIST_INSERT_HEAD ( & dial - > channels , channel , list ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is busy \n " , channel - > owner - > name ) ;
2007-01-24 18:23:07 +00:00
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
break ;
case AST_CONTROL_CONGESTION :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is circuit-busy \n " , channel - > owner - > name ) ;
2007-01-24 18:23:07 +00:00
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
break ;
case AST_CONTROL_RINGING :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is ringing \n " , channel - > owner - > name ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is making progress \n " , channel - > owner - > name ) ;
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 :
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " %s is proceeding \n " , channel - > owner - > name ) ;
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 ;
}
/*! \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 )
{
int timeout = - 1 , count = 0 ;
struct ast_channel * cs [ AST_MAX_WATCHERS ] , * who = NULL ;
struct ast_dial_channel * channel = NULL ;
struct answer_exec_struct * answer_exec = NULL ;
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 ) ;
2007-04-10 19:16:24 +00:00
} else if ( chan & & dial - > options [ AST_DIAL_OPTION_MUSIC ] & &
! ast_strlen_zero ( dial - > options [ AST_DIAL_OPTION_MUSIC ] ) ) {
char * original_moh = ast_strdupa ( chan - > musicclass ) ;
ast_indicate ( chan , - 1 ) ;
ast_string_field_set ( chan , musicclass , dial - > options [ AST_DIAL_OPTION_MUSIC ] ) ;
ast_moh_start ( chan , dial - > options [ AST_DIAL_OPTION_MUSIC ] , NULL ) ;
ast_string_field_set ( chan , musicclass , original_moh ) ;
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-01-24 18:23:07 +00:00
int pos = 0 ;
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 */
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( channel - > owner ) {
cs [ pos + + ] = channel - > owner ;
count + + ;
}
}
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 ;
/* If we are not being cancelled and we have no channel, then timeout was tripped */
if ( ! who )
continue ;
/* Find relative dial channel */
if ( ! chan | | ! IS_CALLER ( chan , who ) )
channel = find_relative_dial_channel ( dial , who ) ;
/* 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 ;
}
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 */
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( ! channel - > owner | | channel - > owner = = who )
continue ;
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
}
/* If ANSWER_EXEC is enabled as an option, execute application on answered channel */
if ( ( channel = find_relative_dial_channel ( dial , who ) ) & & ( answer_exec = FIND_RELATIVE_OPTION ( dial , channel , AST_DIAL_OPTION_ANSWER_EXEC ) ) )
answer_exec_run ( who , answer_exec - > app , answer_exec - > args ) ;
2007-04-10 19:16:24 +00:00
if ( chan & & dial - > options [ AST_DIAL_OPTION_MUSIC ] & &
! 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 */
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( ! channel - > owner )
continue ;
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
}
}
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 ;
/* 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 ) {
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
}
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 ;
/* Stop the thread */
dial - > thread = AST_PTHREADT_STOP ;
/* Now we signal it with SIGURG so it will break out of it's waitfor */
pthread_kill ( thread , SIGURG ) ;
/* 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 ;
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( channel - > owner ) {
ast_hangup ( channel - > owner ) ;
channel - > owner = NULL ;
}
}
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 ;
/* Hangup and deallocate all the dialed channels */
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
/* 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-06-06 21:20:11 +00:00
ast_free ( channel ) ;
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 ;
}
/* 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 ;
}
/*! \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 ;
/* Look for channel, we can sort of cheat and predict things - the last channel in the list will probably be what they want */
if ( AST_LIST_LAST ( & dial - > channels ) - > num ! = num ) {
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( channel - > num = = num )
break ;
}
} else {
channel = AST_LIST_LAST ( & dial - > channels ) ;
}
/* If none found, return failure */
if ( ! channel )
return - 1 ;
/* If the option is already enabled, return failure */
if ( channel - > options [ option ] )
return - 1 ;
/* Execute enable callback if it exists, if not simply make sure the value is set */
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 )
{
/* If the option is not enabled, return failure */
if ( ! dial - > options [ option ] )
return - 1 ;
/* 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 ;
return 0 ;
}
/*! \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 ;
/* Look for channel, we can sort of cheat and predict things - the last channel in the list will probably be what they want */
if ( AST_LIST_LAST ( & dial - > channels ) - > num ! = num ) {
AST_LIST_TRAVERSE ( & dial - > channels , channel , list ) {
if ( channel - > num = = num )
break ;
}
} else {
channel = AST_LIST_LAST ( & dial - > channels ) ;
}
/* If none found, return failure */
if ( ! channel )
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 ;
}