1999-12-04 21:35:07 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
1999-12-04 21:35:07 +00:00
*
2006-04-10 23:01:22 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
1999-12-04 21:35:07 +00:00
*
2004-09-22 05:19:06 +00:00
* Mark Spencer < markster @ digium . com >
1999-12-04 21:35:07 +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 .
*
1999-12-04 21:35:07 +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
*
2005-10-24 20:12:06 +00:00
* \ brief dial ( ) & retrydial ( ) - Trivial application to dial a channel and send an URL on answer
2005-12-30 21:18:06 +00:00
*
* \ author Mark Spencer < markster @ digium . com >
2005-09-14 20:46:50 +00:00
*
2005-11-06 15:09:47 +00:00
* \ ingroup applications
1999-12-04 21:35:07 +00:00
*/
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2005-06-06 22:39:32 +00:00
# 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>
2006-08-05 06:39:43 +00:00
# include <sys/stat.h>
2005-06-06 22:39:32 +00:00
# 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"
# include "asterisk/module.h"
# include "asterisk/translate.h"
# include "asterisk/say.h"
# include "asterisk/config.h"
# include "asterisk/features.h"
# include "asterisk/musiconhold.h"
# include "asterisk/callerid.h"
# include "asterisk/utils.h"
# include "asterisk/app.h"
# include "asterisk/causes.h"
2005-12-20 17:52:31 +00:00
# include "asterisk/rtp.h"
2005-04-21 06:02:45 +00:00
# include "asterisk/manager.h"
2005-07-12 03:23:31 +00:00
# include "asterisk/privacy.h"
2006-02-01 23:05:28 +00:00
# include "asterisk/stringfields.h"
1999-12-04 21:35:07 +00:00
static char * app = " Dial " ;
2003-11-06 04:08:40 +00:00
static char * synopsis = " Place a call and connect to the current channel " ;
2001-05-07 03:15:48 +00:00
static char * descrip =
2005-11-11 13:34:38 +00:00
" Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]): \n "
2006-11-16 23:20:52 +00:00
" This application will place calls to one or more specified channels. As soon \n "
2005-11-11 13:34:38 +00:00
" as one of the requested channels answers, the originating channel will be \n "
" answered, if it has not already been answered. These two channels will then \n "
" be active in a bridged call. All other channels that were requested will then \n "
" be hung up. \n "
" Unless there is a timeout specified, the Dial application will wait \n "
" indefinitely until one of the called channels answers, the user hangs up, or \n "
" if all of the called channels are busy or unavailable. Dialplan executing will \n "
2005-11-11 15:52:55 +00:00
" continue if no requested channels can be called, or if the timeout expires. \n \n "
2005-11-11 13:34:38 +00:00
" This application sets the following channel variables upon completion: \n "
" DIALEDTIME - This is the time from dialing a channel until when it \n "
2005-11-25 19:59:46 +00:00
" is disconnected. \n "
2005-11-11 13:34:38 +00:00
" ANSWEREDTIME - This is the amount of time for actual call. \n "
" DIALSTATUS - This is the status of the call: \n "
" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL \n "
2007-02-03 20:46:36 +00:00
" DONTCALL | TORTURE | INVALIDARGS \n "
2005-11-11 13:34:38 +00:00
" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to \n "
" DONTCALL if the called party chooses to send the calling party to the 'Go Away' \n "
" script. The DIALSTATUS variable will be set to TORTURE if the called party \n "
" wants to send the caller to the 'torture' script. \n "
" This application will report normal termination if the originating channel \n "
" hangs up, or if the call is bridged and either of the parties in the bridge \n "
" ends the call. \n "
2004-07-19 19:47:21 +00:00
" The optional URL will be sent to the called party if the channel supports it. \n "
2004-11-07 21:49:43 +00:00
" If the OUTBOUND_GROUP variable is set, all peer channels created by this \n "
2005-11-11 15:52:55 +00:00
" application will be put into that group (as in Set(GROUP()=...). \n \n "
2005-11-11 13:34:38 +00:00
" Options: \n "
" A(x) - Play an announcement to the called party, using 'x' as the file. \n "
" C - Reset the CDR for this call. \n "
" d - Allow the calling user to dial a 1 digit extension while waiting for \n "
" a call to be answered. Exit to that extension if it exists in the \n "
" current context, or the context defined in the EXITCONTEXT variable, \n "
" if it exists. \n "
" D([called][:calling]) - Send the specified DTMF strings *after* the called \n "
" party has answered, but before the call gets bridged. The 'called' \n "
" DTMF string is sent to the called party, and the 'calling' DTMF \n "
" string is sent to the calling party. Both parameters can be used \n "
" alone. \n "
" f - Force the callerid of the *calling* channel to be set as the \n "
" extension associated with the channel using a dialplan 'hint'. \n "
" For example, some PSTNs do not allow CallerID to be set to anything \n "
" other than the number assigned to the caller. \n "
" g - Proceed with dialplan execution at the current extension if the \n "
" destination channel hangs up. \n "
2006-03-19 10:11:29 +00:00
" G(context^exten^pri) - If the call is answered, transfer the calling party to \n "
" the specified priority and the called party to the specified priority+1. \n "
" Optionally, an extension, or extension and context may be specified. \n "
2006-05-03 21:11:23 +00:00
" Otherwise, the current extension is used. You cannot use any additional \n "
" action post answer options in conjunction with this option. \n "
2005-11-11 13:34:38 +00:00
" h - Allow the called party to hang up by sending the '*' DTMF digit. \n "
" H - Allow the calling party to hang up by hitting the '*' DTMF digit. \n "
2006-05-31 15:52:32 +00:00
" i - Asterisk will ignore any forwarding requests it may receive on this \n "
" dial attempt. \n "
2005-11-11 13:34:38 +00:00
" j - Jump to priority n+101 if all of the requested channels were busy. \n "
" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are \n "
" left. Repeat the warning every 'z' ms. The following special \n "
" variables can be used with this option: \n "
" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes) \n "
" Play sounds to the caller. \n "
" * LIMIT_PLAYAUDIO_CALLEE yes|no \n "
" Play sounds to the callee. \n "
" * LIMIT_TIMEOUT_FILE File to play when time is up. \n "
" * LIMIT_CONNECT_FILE File to play when call begins. \n "
" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined. \n "
" The default is to say the time remaining. \n "
" m([class]) - Provide hold music to the calling party until a requested \n "
" channel answers. A specific MusicOnHold class can be \n "
" specified. \n "
" M(x[^arg]) - Execute the Macro for the *called* channel before connecting \n "
" to the calling channel. Arguments can be specified to the Macro \n "
" using '^' as a delimeter. The Macro can set the variable \n "
" MACRO_RESULT to specify the following actions after the Macro is \n "
" finished executing. \n "
" * ABORT Hangup both legs of the call. \n "
" * CONGESTION Behave as if line congestion was encountered. \n "
" * BUSY Behave as if a busy signal was encountered. This will also \n "
" have the application jump to priority n+101 if the \n "
" 'j' option is set. \n "
" * CONTINUE Hangup the called party and allow the calling party \n "
" to continue dialplan execution at the next priority. \n "
" * GOTO:<context>^<exten>^<priority> - Transfer the call to the \n "
" specified priority. Optionally, an extension, or \n "
" extension and priority can be specified. \n "
2006-05-03 21:11:23 +00:00
" You cannot use any additional action post answer options in conjunction \n "
2006-09-06 17:14:50 +00:00
" with this option. Also, pbx services are not run on the peer (called) channel, \n "
" so you will not be able to set timeouts via the TIMEOUT() function in this macro. \n "
2005-11-11 13:34:38 +00:00
" n - This option is a modifier for the screen/privacy mode. It specifies \n "
" that no introductions are to be saved in the priv-callerintros \n "
" directory. \n "
" N - This option is a modifier for the screen/privacy mode. It specifies \n "
" that if callerID is present, do not screen the call. \n "
" o - Specify that the CallerID that was present on the *calling* channel \n "
" be set as the CallerID on the *called* channel. This was the \n "
" behavior of Asterisk 1.0 and earlier. \n "
2006-04-22 11:30:06 +00:00
" O([x]) - \" Operator Services \" mode (Zaptel channel to Zaptel channel \n "
" only, if specified on non-Zaptel interface, it will be ignored). \n "
" When the destination answers (presumably an operator services \n "
" station), the originator no longer has control of their line. \n "
" They may hang up, but the switch will not release their line \n "
" until the destination party hangs up (the operator). Specified \n "
" without an arg, or with 1 as an arg, the originator hanging up \n "
" will cause the phone to ring back immediately. With a 2 specified, \n "
" when the \" operator \" flashes the trunk, it will ring their phone \n "
" back. \n "
2005-11-11 13:34:38 +00:00
" p - This option enables screening mode. This is basically Privacy mode \n "
" without memory. \n "
" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if \n "
" it is provided. The current extension is used if a database \n "
" family/key is not specified. \n "
" r - Indicate ringing to the calling party. Pass no audio to the calling \n "
" party until the called channel has answered. \n "
" S(x) - Hang up the call after 'x' seconds *after* the called party has \n "
" answered the call. \n "
" t - Allow the called party to transfer the calling party by sending the \n "
2006-01-11 03:13:45 +00:00
" DTMF sequence defined in features.conf. \n "
2005-11-11 13:34:38 +00:00
" T - Allow the calling party to transfer the called party by sending the \n "
" DTMF sequence defined in features.conf. \n "
" w - Allow the called party to enable recording of the call by sending \n "
" the DTMF sequence defined for one-touch recording in features.conf. \n "
" W - Allow the calling party to enable recording of the call by sending \n "
2006-05-22 16:43:43 +00:00
" the DTMF sequence defined for one-touch recording in features.conf. \n "
" k - Allow the called party to enable parking of the call by sending \n "
" the DTMF sequence defined for call parking in features.conf. \n "
" K - Allow the calling party to enable parking of the call by sending \n "
" the DTMF sequence defined for call parking in features.conf. \n " ;
2001-05-07 03:15:48 +00:00
2005-01-18 03:12:53 +00:00
/* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
static char * rapp = " RetryDial " ;
2005-04-27 01:50:53 +00:00
static char * rsynopsis = " Place a call, retrying on failure allowing optional exit extension. " ;
2005-01-18 03:12:53 +00:00
static char * rdescrip =
2005-11-11 13:34:38 +00:00
" RetryDial(announce|sleep|retries|dialargs): This application will attempt to \n "
" place a call using the normal Dial application. If no channel can be reached, \n "
2005-11-14 10:24:59 +00:00
" the 'announce' file will be played. Then, it will wait 'sleep' number of \n "
2005-11-11 13:34:38 +00:00
" seconds before retying the call. After 'retires' number of attempts, the \n "
" calling channel will continue at the next priority in the dialplan. If the \n "
" 'retries' setting is set to 0, this application will retry endlessly. \n "
" While waiting to retry a call, a 1 digit extension may be dialed. If that \n "
" extension exists in either the context defined in ${EXITCONTEXT} or the current \n "
" one, The call will jump to that extension immediately. \n "
" The 'dialargs' are specified in the same format that arguments are provided \n "
" to the Dial application. \n " ;
2005-01-18 03:12:53 +00:00
2005-11-03 21:40:36 +00:00
enum {
2006-01-17 18:54:56 +00:00
OPT_ANNOUNCE = ( 1 < < 0 ) ,
OPT_RESETCDR = ( 1 < < 1 ) ,
OPT_DTMF_EXIT = ( 1 < < 2 ) ,
OPT_SENDDTMF = ( 1 < < 3 ) ,
OPT_FORCECLID = ( 1 < < 4 ) ,
OPT_GO_ON = ( 1 < < 5 ) ,
OPT_CALLEE_HANGUP = ( 1 < < 6 ) ,
OPT_CALLER_HANGUP = ( 1 < < 7 ) ,
OPT_PRIORITY_JUMP = ( 1 < < 8 ) ,
OPT_DURATION_LIMIT = ( 1 < < 9 ) ,
OPT_MUSICBACK = ( 1 < < 10 ) ,
OPT_CALLEE_MACRO = ( 1 < < 11 ) ,
OPT_SCREEN_NOINTRO = ( 1 < < 12 ) ,
OPT_SCREEN_NOCLID = ( 1 < < 13 ) ,
OPT_ORIGINAL_CLID = ( 1 < < 14 ) ,
OPT_SCREENING = ( 1 < < 15 ) ,
OPT_PRIVACY = ( 1 < < 16 ) ,
OPT_RINGBACK = ( 1 < < 17 ) ,
OPT_DURATION_STOP = ( 1 < < 18 ) ,
OPT_CALLEE_TRANSFER = ( 1 < < 19 ) ,
OPT_CALLER_TRANSFER = ( 1 < < 20 ) ,
OPT_CALLEE_MONITOR = ( 1 < < 21 ) ,
OPT_CALLER_MONITOR = ( 1 < < 22 ) ,
OPT_GOTO = ( 1 < < 23 ) ,
2006-04-22 11:30:06 +00:00
OPT_OPERMODE = ( 1 < < 24 ) ,
2006-05-22 16:43:43 +00:00
OPT_CALLEE_PARK = ( 1 < < 25 ) ,
OPT_CALLER_PARK = ( 1 < < 26 ) ,
2006-05-31 15:52:32 +00:00
OPT_IGNORE_FORWARDING = ( 1 < < 27 ) ,
2005-11-03 21:40:36 +00:00
} dial_exec_option_flags ;
# define DIAL_STILLGOING (1 << 30)
# define DIAL_NOFORWARDHTML (1 << 31)
enum {
OPT_ARG_ANNOUNCE = 0 ,
OPT_ARG_SENDDTMF ,
OPT_ARG_GOTO ,
OPT_ARG_DURATION_LIMIT ,
OPT_ARG_MUSICBACK ,
OPT_ARG_CALLEE_MACRO ,
OPT_ARG_PRIVACY ,
OPT_ARG_DURATION_STOP ,
2006-04-22 11:30:06 +00:00
OPT_ARG_OPERMODE ,
2005-11-03 21:40:36 +00:00
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE ,
} dial_exec_option_args ;
AST_APP_OPTIONS ( dial_exec_options , {
AST_APP_OPTION_ARG ( ' A ' , OPT_ANNOUNCE , OPT_ARG_ANNOUNCE ) ,
AST_APP_OPTION ( ' C ' , OPT_RESETCDR ) ,
AST_APP_OPTION ( ' d ' , OPT_DTMF_EXIT ) ,
AST_APP_OPTION_ARG ( ' D ' , OPT_SENDDTMF , OPT_ARG_SENDDTMF ) ,
AST_APP_OPTION ( ' f ' , OPT_FORCECLID ) ,
AST_APP_OPTION ( ' g ' , OPT_GO_ON ) ,
AST_APP_OPTION_ARG ( ' G ' , OPT_GOTO , OPT_ARG_GOTO ) ,
AST_APP_OPTION ( ' h ' , OPT_CALLEE_HANGUP ) ,
AST_APP_OPTION ( ' H ' , OPT_CALLER_HANGUP ) ,
2006-05-31 15:52:32 +00:00
AST_APP_OPTION ( ' i ' , OPT_IGNORE_FORWARDING ) ,
2005-11-03 21:40:36 +00:00
AST_APP_OPTION ( ' j ' , OPT_PRIORITY_JUMP ) ,
AST_APP_OPTION_ARG ( ' L ' , OPT_DURATION_LIMIT , OPT_ARG_DURATION_LIMIT ) ,
AST_APP_OPTION_ARG ( ' m ' , OPT_MUSICBACK , OPT_ARG_MUSICBACK ) ,
AST_APP_OPTION_ARG ( ' M ' , OPT_CALLEE_MACRO , OPT_ARG_CALLEE_MACRO ) ,
AST_APP_OPTION ( ' n ' , OPT_SCREEN_NOINTRO ) ,
AST_APP_OPTION ( ' N ' , OPT_SCREEN_NOCLID ) ,
2006-04-22 11:30:06 +00:00
AST_APP_OPTION_ARG ( ' O ' , OPT_OPERMODE , OPT_ARG_OPERMODE ) ,
2005-11-03 21:40:36 +00:00
AST_APP_OPTION ( ' o ' , OPT_ORIGINAL_CLID ) ,
AST_APP_OPTION ( ' p ' , OPT_SCREENING ) ,
AST_APP_OPTION_ARG ( ' P ' , OPT_PRIVACY , OPT_ARG_PRIVACY ) ,
AST_APP_OPTION ( ' r ' , OPT_RINGBACK ) ,
AST_APP_OPTION_ARG ( ' S ' , OPT_DURATION_STOP , OPT_ARG_DURATION_STOP ) ,
AST_APP_OPTION ( ' t ' , OPT_CALLEE_TRANSFER ) ,
AST_APP_OPTION ( ' T ' , OPT_CALLER_TRANSFER ) ,
AST_APP_OPTION ( ' w ' , OPT_CALLEE_MONITOR ) ,
AST_APP_OPTION ( ' W ' , OPT_CALLER_MONITOR ) ,
2006-05-22 16:43:43 +00:00
AST_APP_OPTION ( ' k ' , OPT_CALLEE_PARK ) ,
AST_APP_OPTION ( ' K ' , OPT_CALLER_PARK ) ,
2005-11-03 21:40:36 +00:00
} ) ;
2006-12-19 16:36:45 +00:00
/*
* The list of active channels
*/
struct chanlist {
struct chanlist * next ;
1999-12-04 21:35:07 +00:00
struct ast_channel * chan ;
2005-01-05 23:05:49 +00:00
unsigned int flags ;
2004-11-28 22:08:44 +00:00
int forwards ;
1999-12-04 21:35:07 +00:00
} ;
2006-12-19 16:36:45 +00:00
static void hanguptree ( struct chanlist * outgoing , struct ast_channel * exception )
1999-12-04 21:35:07 +00:00
{
/* Hang up a tree of stuff */
2006-12-19 16:36:45 +00:00
struct chanlist * oo ;
2005-01-18 03:12:53 +00:00
while ( outgoing ) {
1999-12-04 21:35:07 +00:00
/* Hangup any existing lines we have open */
2003-04-06 06:37:08 +00:00
if ( outgoing - > chan & & ( outgoing - > chan ! = exception ) )
1999-12-04 21:35:07 +00:00
ast_hangup ( outgoing - > chan ) ;
oo = outgoing ;
outgoing = outgoing - > next ;
free ( oo ) ;
}
}
2004-11-28 22:08:44 +00:00
# define AST_MAX_FORWARDS 8
2004-06-22 17:42:14 +00:00
# define AST_MAX_WATCHERS 256
2001-05-07 03:15:48 +00:00
2006-11-03 22:36:17 +00:00
/*
* argument to handle_cause ( ) and other functions .
*/
struct cause_args {
struct ast_channel * chan ;
int busy ;
int congestion ;
int nochan ;
} ;
static void handle_cause ( int cause , struct cause_args * num )
{
struct ast_cdr * cdr = num - > chan - > cdr ;
switch ( cause ) {
case AST_CAUSE_BUSY :
if ( cdr )
ast_cdr_busy ( cdr ) ;
num - > busy + + ;
break ;
case AST_CAUSE_CONGESTION :
if ( cdr )
ast_cdr_failed ( cdr ) ;
num - > congestion + + ;
break ;
case AST_CAUSE_UNREGISTERED :
if ( cdr )
ast_cdr_failed ( cdr ) ;
num - > nochan + + ;
break ;
case AST_CAUSE_NORMAL_CLEARING :
break ;
default :
num - > nochan + + ;
break ;
}
}
2005-01-18 03:12:53 +00:00
2006-11-03 21:51:16 +00:00
/* free the buffer if allocated, and set the pointer to the second arg */
# define S_REPLACE(s, new_val) \
do { \
if ( s ) \
free ( s ) ; \
s = ( new_val ) ; \
} while ( 0 )
2005-01-18 03:12:53 +00:00
2005-12-20 17:52:31 +00:00
static int onedigit_goto ( struct ast_channel * chan , const char * context , char exten , int pri )
2005-01-18 03:12:53 +00:00
{
2005-04-29 15:04:26 +00:00
char rexten [ 2 ] = { exten , ' \0 ' } ;
2005-01-18 03:12:53 +00:00
if ( context ) {
2005-06-01 18:02:46 +00:00
if ( ! ast_goto_if_exists ( chan , context , rexten , pri ) )
2005-01-18 03:12:53 +00:00
return 1 ;
} else {
2005-06-01 18:02:46 +00:00
if ( ! ast_goto_if_exists ( chan , chan - > context , rexten , pri ) )
2005-01-18 03:12:53 +00:00
return 1 ;
2005-04-29 15:04:26 +00:00
else if ( ! ast_strlen_zero ( chan - > macrocontext ) ) {
2005-06-01 18:02:46 +00:00
if ( ! ast_goto_if_exists ( chan , chan - > macrocontext , rexten , pri ) )
2005-01-18 03:12:53 +00:00
return 1 ;
}
}
return 0 ;
}
2004-10-26 22:25:43 +00:00
2006-04-19 14:14:40 +00:00
static const char * get_cid_name ( char * name , int namelen , struct ast_channel * chan )
2005-02-01 01:53:25 +00:00
{
2006-04-19 14:14:40 +00:00
const char * context = S_OR ( chan - > macrocontext , chan - > context ) ;
const char * exten = S_OR ( chan - > macroexten , chan - > exten ) ;
2005-02-01 01:53:25 +00:00
2006-04-19 14:14:40 +00:00
return ast_get_hint ( NULL , 0 , name , namelen , chan , context , exten ) ? name : " " ;
2005-02-01 01:53:25 +00:00
}
2005-02-07 15:19:34 +00:00
static void senddialevent ( struct ast_channel * src , struct ast_channel * dst )
{
manager_event ( EVENT_FLAG_CALL , " Dial " ,
2006-10-30 23:11:55 +00:00
" SubEvent: Begin \r \n "
2005-04-13 16:43:26 +00:00
" Source: %s \r \n "
" Destination: %s \r \n "
2006-10-02 20:35:16 +00:00
" CallerIDNum: %s \r \n "
2005-04-13 16:43:26 +00:00
" CallerIDName: %s \r \n "
" SrcUniqueID: %s \r \n "
" DestUniqueID: %s \r \n " ,
2006-04-21 10:41:13 +00:00
src - > name , dst - > name , S_OR ( src - > cid . cid_num , " <unknown> " ) ,
S_OR ( src - > cid . cid_name , " <unknown> " ) , src - > uniqueid ,
2005-04-13 16:43:26 +00:00
dst - > uniqueid ) ;
2005-02-07 15:19:34 +00:00
}
2006-10-30 23:11:55 +00:00
static void senddialendevent ( const struct ast_channel * src , const char * dialstatus )
{
manager_event ( EVENT_FLAG_CALL , " Dial " ,
" SubEvent: End \r \n "
" Channel: %s \r \n "
" DialStatus: %s \r \n " ,
src - > name , dialstatus ) ;
}
2006-11-21 11:53:06 +00:00
/*!
* helper function for wait_for_answer ( )
*
* XXX this code is highly suspicious , as it essentially overwrites
* the outgoing channel without properly deleting it .
*/
2006-12-19 16:36:45 +00:00
static void do_forward ( struct chanlist * o ,
2006-11-04 00:01:40 +00:00
struct cause_args * num , struct ast_flags * peerflags , int single )
{
char tmpchan [ 256 ] ;
struct ast_channel * c = o - > chan ; /* the winner */
struct ast_channel * in = num - > chan ; /* the input channel */
char * stuff ;
char * tech ;
int cause ;
ast_copy_string ( tmpchan , c - > call_forward , sizeof ( tmpchan ) ) ;
if ( ( stuff = strchr ( tmpchan , ' / ' ) ) ) {
* stuff + + = ' \0 ' ;
tech = tmpchan ;
} else {
const char * forward_context = pbx_builtin_getvar_helper ( c , " FORWARD_CONTEXT " ) ;
snprintf ( tmpchan , sizeof ( tmpchan ) , " %s@%s " , c - > call_forward , forward_context ? forward_context : c - > context ) ;
stuff = tmpchan ;
tech = " Local " ;
}
/* Before processing channel, go ahead and check for forwarding */
o - > forwards + + ;
if ( o - > forwards < AST_MAX_FORWARDS ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Now forwarding %s to '%s/%s' (thanks to %s) \n " , in - > name , tech , stuff , c - > name ) ;
/* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
if ( ast_test_flag ( peerflags , OPT_IGNORE_FORWARDING ) ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Forwarding %s to '%s/%s' prevented. \n " , in - > name , tech , stuff ) ;
c = o - > chan = NULL ;
cause = AST_CAUSE_BUSY ;
} else {
/* Setup parameters */
c = o - > chan = ast_request ( tech , in - > nativeformats , stuff , & cause ) ;
if ( ! c )
ast_log ( LOG_NOTICE , " Unable to create local channel for call forward to '%s/%s' (cause = %d) \n " , tech , stuff , cause ) ;
else
ast_channel_inherit_variables ( in , o - > chan ) ;
}
} else {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Too many forwards from %s \n " , c - > name ) ;
cause = AST_CAUSE_CONGESTION ;
c = o - > chan = NULL ;
}
if ( ! c ) {
ast_clear_flag ( o , DIAL_STILLGOING ) ;
handle_cause ( cause , num ) ;
} else {
char * new_cid_num , * new_cid_name ;
struct ast_channel * src ;
ast_rtp_make_compatible ( c , in , single ) ;
if ( ast_test_flag ( o , OPT_FORCECLID ) ) {
new_cid_num = ast_strdup ( S_OR ( in - > macroexten , in - > exten ) ) ;
new_cid_name = NULL ; /* XXX no name ? */
src = c ; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */
} else {
new_cid_num = ast_strdup ( in - > cid . cid_num ) ;
new_cid_name = ast_strdup ( in - > cid . cid_name ) ;
src = in ;
}
ast_string_field_set ( c , accountcode , src - > accountcode ) ;
c - > cdrflags = src - > cdrflags ;
S_REPLACE ( c - > cid . cid_num , new_cid_num ) ;
S_REPLACE ( c - > cid . cid_name , new_cid_name ) ;
if ( in - > cid . cid_ani ) { /* XXX or maybe unconditional ? */
S_REPLACE ( c - > cid . cid_ani , ast_strdup ( in - > cid . cid_ani ) ) ;
}
S_REPLACE ( c - > cid . cid_rdnis , ast_strdup ( S_OR ( in - > macroexten , in - > exten ) ) ) ;
if ( ast_call ( c , tmpchan , 0 ) ) {
ast_log ( LOG_NOTICE , " Failed to dial on local channel for call forward to '%s' \n " , tmpchan ) ;
ast_clear_flag ( o , DIAL_STILLGOING ) ;
ast_hangup ( c ) ;
c = o - > chan = NULL ;
num - > nochan + + ;
} else {
senddialevent ( in , c ) ;
/* After calling, set callerid to extension */
if ( ! ast_test_flag ( peerflags , OPT_ORIGINAL_CLID ) ) {
char cidname [ AST_MAX_EXTENSION ] ;
ast_set_callerid ( c , S_OR ( in - > macroexten , in - > exten ) , get_cid_name ( cidname , sizeof ( cidname ) , in ) , NULL ) ;
}
2006-11-21 11:53:06 +00:00
/* Hangup the original channel now, in case we needed it */
ast_hangup ( c ) ;
2006-11-04 00:01:40 +00:00
}
}
}
2006-11-04 11:00:49 +00:00
/* argument used for some functions. */
struct privacy_args {
int sentringing ;
int privdb_val ;
char privcid [ 256 ] ;
char privintro [ 1024 ] ;
char status [ 256 ] ;
} ;
static struct ast_channel * wait_for_answer ( struct ast_channel * in ,
2006-12-19 16:36:45 +00:00
struct chanlist * outgoing , int * to , struct ast_flags * peerflags ,
2006-11-04 11:00:49 +00:00
struct privacy_args * pa ,
2006-11-03 22:36:17 +00:00
const struct cause_args * num_in , int priority_jump , int * result )
1999-12-04 21:35:07 +00:00
{
2006-11-03 22:36:17 +00:00
struct cause_args num = * num_in ;
int prestart = num . busy + num . congestion + num . nochan ;
1999-12-04 21:35:07 +00:00
int orig = * to ;
struct ast_channel * peer = NULL ;
2006-04-19 16:10:11 +00:00
/* single is set if only one destination is enabled */
int single = outgoing & & ! outgoing - > next & & ! ast_test_flag ( outgoing , OPT_MUSICBACK | OPT_RINGBACK ) ;
2001-05-07 03:15:48 +00:00
if ( single ) {
2003-11-05 20:53:49 +00:00
/* Turn off hold music, etc */
2004-07-07 16:02:13 +00:00
ast_deactivate_generator ( in ) ;
2001-05-07 03:15:48 +00:00
/* If we are calling a single channel, make them compatible for in-band tone purpose */
ast_channel_make_compatible ( outgoing - > chan , in ) ;
}
2002-05-17 14:33:10 +00:00
2005-01-18 03:12:53 +00:00
while ( * to & & ! peer ) {
2006-12-19 16:36:45 +00:00
struct chanlist * o ;
2006-04-19 14:02:49 +00:00
int pos = 0 ; /* how many channels do we handle */
int numlines = prestart ;
2006-04-19 16:10:11 +00:00
struct ast_channel * winner ;
struct ast_channel * watchers [ AST_MAX_WATCHERS ] ;
2006-04-19 14:02:49 +00:00
watchers [ pos + + ] = in ;
for ( o = outgoing ; o ; o = o - > next ) {
2001-05-07 03:15:48 +00:00
/* Keep track of important channels */
2006-04-19 14:02:49 +00:00
if ( ast_test_flag ( o , DIAL_STILLGOING ) & & o - > chan )
2001-05-07 03:15:48 +00:00
watchers [ pos + + ] = o - > chan ;
numlines + + ;
1999-12-04 21:35:07 +00:00
}
2006-04-19 14:02:49 +00:00
if ( pos = = 1 ) { /* only the input channel is available */
2006-11-03 22:36:17 +00:00
if ( numlines = = ( num . busy + num . congestion + num . nochan ) ) {
1999-12-04 21:35:07 +00:00
if ( option_verbose > 2 )
2006-11-03 22:36:17 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Everyone is busy/congested at this time (%d:%d/%d/%d) \n " , numlines , num . busy , num . congestion , num . nochan ) ;
if ( num . busy )
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " BUSY " ) ;
2006-11-03 22:36:17 +00:00
else if ( num . congestion )
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " CONGESTION " ) ;
2006-11-03 22:36:17 +00:00
else if ( num . nochan )
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " CHANUNAVAIL " ) ;
2005-12-04 20:40:46 +00:00
if ( ast_opt_priority_jumping | | priority_jump )
2005-07-26 16:29:56 +00:00
ast_goto_if_exists ( in , in - > context , in - > exten , in - > priority + 101 ) ;
1999-12-04 21:35:07 +00:00
} else {
if ( option_verbose > 2 )
2006-11-03 22:36:17 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " No one is available to answer at this time (%d:%d/%d/%d) \n " , numlines , num . busy , num . congestion , num . nochan ) ;
1999-12-04 21:35:07 +00:00
}
2001-05-07 03:15:48 +00:00
* to = 0 ;
1999-12-04 21:35:07 +00:00
return NULL ;
}
2001-05-07 03:15:48 +00:00
winner = ast_waitfor_n ( watchers , pos , to ) ;
2006-04-19 14:02:49 +00:00
for ( o = outgoing ; o ; o = o - > next ) {
struct ast_frame * f ;
struct ast_channel * c = o - > chan ;
if ( c = = NULL )
continue ;
if ( ast_test_flag ( o , DIAL_STILLGOING ) & & c - > _state = = AST_STATE_UP ) {
2002-09-02 15:20:28 +00:00
if ( ! peer ) {
if ( option_verbose > 2 )
2006-04-19 14:02:49 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " %s answered %s \n " , c - > name , in - > name ) ;
peer = c ;
2005-11-12 01:33:10 +00:00
ast_copy_flags ( peerflags , o ,
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
2006-05-22 16:43:43 +00:00
OPT_CALLEE_PARK | OPT_CALLER_PARK |
2005-11-12 01:33:10 +00:00
DIAL_NOFORWARDHTML ) ;
2006-11-20 15:55:58 +00:00
ast_copy_string ( c - > dialcontext , " " , sizeof ( c - > dialcontext ) ) ;
2006-10-13 21:20:18 +00:00
ast_copy_string ( c - > exten , " " , sizeof ( c - > exten ) ) ;
2002-09-02 15:20:28 +00:00
}
2006-04-19 14:02:49 +00:00
continue ;
}
if ( c ! = winner )
continue ;
2006-11-04 00:01:40 +00:00
/* here, o->chan == c == winner */
2006-04-19 14:02:49 +00:00
if ( ! ast_strlen_zero ( c - > call_forward ) ) {
2006-11-04 00:01:40 +00:00
do_forward ( o , & num , peerflags , single ) ;
2006-04-19 14:53:18 +00:00
continue ;
2006-04-19 14:02:49 +00:00
}
f = ast_read ( winner ) ;
if ( ! f ) {
2006-04-19 14:53:18 +00:00
in - > hangupcause = c - > hangupcause ;
ast_hangup ( c ) ;
c = o - > chan = NULL ;
ast_clear_flag ( o , DIAL_STILLGOING ) ;
2006-11-03 22:36:17 +00:00
handle_cause ( in - > hangupcause , & num ) ;
2006-04-19 14:53:18 +00:00
continue ;
}
if ( f - > frametype = = AST_FRAME_CONTROL ) {
switch ( f - > subclass ) {
case AST_CONTROL_ANSWER :
/* This is our guy if someone answered. */
if ( ! peer ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s answered %s \n " , c - > name , in - > name ) ;
peer = c ;
ast_copy_flags ( peerflags , o ,
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
2006-05-22 16:43:43 +00:00
OPT_CALLEE_PARK | OPT_CALLER_PARK |
2006-04-19 14:53:18 +00:00
DIAL_NOFORWARDHTML ) ;
2006-11-20 15:55:58 +00:00
ast_copy_string ( c - > dialcontext , " " , sizeof ( c - > dialcontext ) ) ;
2006-10-13 21:20:18 +00:00
ast_copy_string ( c - > exten , " " , sizeof ( c - > exten ) ) ;
2006-09-21 19:27:26 +00:00
/* Setup early bridge if appropriate */
ast_channel_early_bridge ( in , peer ) ;
2006-04-19 14:53:18 +00:00
}
/* If call has been answered, then the eventual hangup is likely to be normal hangup */
in - > hangupcause = AST_CAUSE_NORMAL_CLEARING ;
c - > hangupcause = AST_CAUSE_NORMAL_CLEARING ;
break ;
case AST_CONTROL_BUSY :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is busy \n " , c - > name ) ;
in - > hangupcause = c - > hangupcause ;
ast_hangup ( c ) ;
c = o - > chan = NULL ;
ast_clear_flag ( o , DIAL_STILLGOING ) ;
2006-11-03 22:36:17 +00:00
handle_cause ( AST_CAUSE_BUSY , & num ) ;
2006-04-19 14:53:18 +00:00
break ;
case AST_CONTROL_CONGESTION :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is circuit-busy \n " , c - > name ) ;
2006-04-19 14:50:17 +00:00
in - > hangupcause = c - > hangupcause ;
ast_hangup ( c ) ;
c = o - > chan = NULL ;
2006-04-19 14:02:49 +00:00
ast_clear_flag ( o , DIAL_STILLGOING ) ;
2006-11-03 22:36:17 +00:00
handle_cause ( AST_CAUSE_CONGESTION , & num ) ;
2006-04-19 14:53:18 +00:00
break ;
case AST_CONTROL_RINGING :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is ringing \n " , c - > name ) ;
2006-05-09 11:44:50 +00:00
/* Setup early media if appropriate */
if ( single )
2006-09-21 19:27:26 +00:00
ast_channel_early_bridge ( in , c ) ;
2006-11-04 11:00:49 +00:00
if ( ! ( pa - > sentringing ) & & ! ast_test_flag ( outgoing , OPT_MUSICBACK ) ) {
2006-04-19 14:53:18 +00:00
ast_indicate ( in , AST_CONTROL_RINGING ) ;
2006-11-04 11:00:49 +00:00
pa - > sentringing + + ;
2006-04-19 14:53:18 +00:00
}
break ;
case AST_CONTROL_PROGRESS :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is making progress passing it to %s \n " , c - > name , in - > name ) ;
2006-05-09 11:44:50 +00:00
/* Setup early media if appropriate */
if ( single )
2006-09-21 19:27:26 +00:00
ast_channel_early_bridge ( in , c ) ;
2006-04-19 14:53:18 +00:00
if ( ! ast_test_flag ( outgoing , OPT_RINGBACK ) )
ast_indicate ( in , AST_CONTROL_PROGRESS ) ;
break ;
case AST_CONTROL_VIDUPDATE :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s requested a video update, passing it to %s \n " , c - > name , in - > name ) ;
ast_indicate ( in , AST_CONTROL_VIDUPDATE ) ;
break ;
case AST_CONTROL_PROCEEDING :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is proceeding passing it to %s \n " , c - > name , in - > name ) ;
2006-05-09 11:44:50 +00:00
if ( single )
2006-09-21 19:27:26 +00:00
ast_channel_early_bridge ( in , c ) ;
2006-04-19 14:53:18 +00:00
if ( ! ast_test_flag ( outgoing , OPT_RINGBACK ) )
ast_indicate ( in , AST_CONTROL_PROCEEDING ) ;
break ;
case AST_CONTROL_HOLD :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Call on %s placed on hold \n " , c - > name ) ;
ast_indicate ( in , AST_CONTROL_HOLD ) ;
break ;
case AST_CONTROL_UNHOLD :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Call on %s left from hold \n " , c - > name ) ;
ast_indicate ( in , AST_CONTROL_UNHOLD ) ;
break ;
case AST_CONTROL_OFFHOOK :
case AST_CONTROL_FLASH :
/* Ignore going off hook and flash */
break ;
case - 1 :
if ( ! ast_test_flag ( outgoing , OPT_RINGBACK | OPT_MUSICBACK ) ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s stopped sounds \n " , c - > name ) ;
ast_indicate ( in , - 1 ) ;
2006-11-04 11:00:49 +00:00
pa - > sentringing = 0 ;
2006-04-19 14:53:18 +00:00
}
break ;
default :
if ( option_debug )
ast_log ( LOG_DEBUG , " Dunno what to do with control type %d \n " , f - > subclass ) ;
}
2006-04-19 15:15:03 +00:00
} else if ( single ) {
/* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */
if ( f - > frametype = = AST_FRAME_VOICE & & ! ast_test_flag ( outgoing , OPT_RINGBACK | OPT_MUSICBACK ) ) {
if ( ast_write ( in , f ) )
ast_log ( LOG_WARNING , " Unable to forward voice frame \n " ) ;
} else if ( f - > frametype = = AST_FRAME_IMAGE & & ! ast_test_flag ( outgoing , OPT_RINGBACK | OPT_MUSICBACK ) ) {
if ( ast_write ( in , f ) )
ast_log ( LOG_WARNING , " Unable to forward image \n " ) ;
} else if ( f - > frametype = = AST_FRAME_TEXT & & ! ast_test_flag ( outgoing , OPT_RINGBACK | OPT_MUSICBACK ) ) {
if ( ast_write ( in , f ) )
ast_log ( LOG_WARNING , " Unable to send text \n " ) ;
} else if ( f - > frametype = = AST_FRAME_HTML & & ! ast_test_flag ( outgoing , DIAL_NOFORWARDHTML ) ) {
if ( ast_channel_sendhtml ( in , f - > subclass , f - > data , f - > datalen ) = = - 1 )
ast_log ( LOG_WARNING , " Unable to send URL \n " ) ;
}
1999-12-04 21:35:07 +00:00
}
2006-04-19 14:02:49 +00:00
ast_frfree ( f ) ;
} /* end for */
2001-05-07 03:15:48 +00:00
if ( winner = = in ) {
2006-04-19 14:02:49 +00:00
struct ast_frame * f = ast_read ( in ) ;
1999-12-04 21:35:07 +00:00
#if 0
if ( f & & ( f - > frametype ! = AST_FRAME_VOICE ) )
2005-09-07 19:13:00 +00:00
printf ( " Frame type: %d, %d \n " , f - > frametype , f - > subclass ) ;
2001-10-15 17:39:25 +00:00
else if ( ! f | | ( f - > frametype ! = AST_FRAME_VOICE ) )
printf ( " Hangup received on %s \n " , in - > name ) ;
1999-12-04 21:35:07 +00:00
# endif
2001-10-15 17:39:25 +00:00
if ( ! f | | ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_HANGUP ) ) ) {
1999-12-04 21:35:07 +00:00
/* Got hung up */
2006-01-17 18:54:56 +00:00
* to = - 1 ;
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " CANCEL " ) ;
2004-12-06 17:12:21 +00:00
if ( f )
ast_frfree ( f ) ;
1999-12-04 21:35:07 +00:00
return NULL ;
}
2005-01-18 03:12:53 +00:00
2006-11-03 22:01:34 +00:00
/* now f is guaranteed non-NULL */
if ( f - > frametype = = AST_FRAME_DTMF ) {
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_DTMF_EXIT ) ) {
2006-04-19 16:10:11 +00:00
const char * context = pbx_builtin_getvar_helper ( in , " EXITCONTEXT " ) ;
2005-04-29 15:04:26 +00:00
if ( onedigit_goto ( in , context , ( char ) f - > subclass , 1 ) ) {
2006-01-17 18:54:56 +00:00
if ( option_verbose > 2 )
2005-01-18 03:12:53 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User hit %c to disconnect call. \n " , f - > subclass ) ;
* to = 0 ;
* result = f - > subclass ;
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " CANCEL " ) ;
2005-01-18 03:12:53 +00:00
ast_frfree ( f ) ;
return NULL ;
}
}
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLER_HANGUP ) & &
2006-01-17 18:54:56 +00:00
( f - > subclass = = ' * ' ) ) { /* hmm it it not guaranteed to be '*' anymore. */
if ( option_verbose > 2 )
2005-01-18 03:12:53 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User hit %c to disconnect call. \n " , f - > subclass ) ;
* to = 0 ;
2006-11-04 11:00:49 +00:00
strcpy ( pa - > status , " CANCEL " ) ;
2005-01-18 03:12:53 +00:00
ast_frfree ( f ) ;
return NULL ;
}
2002-09-02 15:20:28 +00:00
}
2005-01-18 03:12:53 +00:00
2005-02-28 06:06:42 +00:00
/* Forward HTML stuff */
2006-11-03 22:01:34 +00:00
if ( single & & ( f - > frametype = = AST_FRAME_HTML ) & & ! ast_test_flag ( outgoing , DIAL_NOFORWARDHTML ) )
2006-01-17 18:54:56 +00:00
if ( ast_channel_sendhtml ( outgoing - > chan , f - > subclass , f - > data , f - > datalen ) = = - 1 )
ast_log ( LOG_WARNING , " Unable to send URL \n " ) ;
2005-02-28 06:06:42 +00:00
2007-02-14 21:10:53 +00:00
if ( single & & ( ( f - > frametype = = AST_FRAME_VOICE ) | | ( f - > frametype = = AST_FRAME_DTMF_BEGIN ) | | ( f - > frametype = = AST_FRAME_DTMF_END ) ) ) {
2002-09-02 15:20:28 +00:00
if ( ast_write ( outgoing - > chan , f ) )
2007-02-14 21:10:53 +00:00
ast_log ( LOG_WARNING , " Unable to forward voice or dtmf \n " ) ;
2002-09-02 15:20:28 +00:00
}
2006-07-08 02:24:07 +00:00
if ( single & & ( f - > frametype = = AST_FRAME_CONTROL ) & &
( ( f - > subclass = = AST_CONTROL_HOLD ) | |
( f - > subclass = = AST_CONTROL_UNHOLD ) | |
( f - > subclass = = AST_CONTROL_VIDUPDATE ) ) ) {
2005-08-30 02:12:09 +00:00
if ( option_verbose > 2 )
2006-07-08 02:24:07 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " %s requested special control %d, passing it to %s \n " , in - > name , f - > subclass , outgoing - > chan - > name ) ;
ast_indicate_data ( outgoing - > chan , f - > subclass , f - > data , f - > datalen ) ;
2005-08-30 02:12:09 +00:00
}
2005-08-03 20:17:53 +00:00
ast_frfree ( f ) ;
1999-12-04 21:35:07 +00:00
}
2001-05-07 03:15:48 +00:00
if ( ! * to & & ( option_verbose > 2 ) )
2006-01-17 18:54:56 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Nobody picked up in %d ms \n " , orig ) ;
1999-12-04 21:35:07 +00:00
}
2002-05-17 14:33:10 +00:00
1999-12-04 21:35:07 +00:00
return peer ;
2006-04-19 14:02:49 +00:00
}
static void replace_macro_delimiter ( char * s )
{
for ( ; * s ; s + + )
if ( * s = = ' ^ ' )
* s = ' | ' ;
1999-12-04 21:35:07 +00:00
}
2006-04-19 17:58:07 +00:00
/* returns true if there is a valid privacy reply */
static int valid_priv_reply ( struct ast_flags * opts , int res )
{
if ( res < ' 1 ' )
return 0 ;
if ( ast_test_flag ( opts , OPT_PRIVACY ) & & res < = ' 5 ' )
return 1 ;
if ( ast_test_flag ( opts , OPT_SCREENING ) & & res < = ' 4 ' )
return 1 ;
return 0 ;
}
2006-11-04 00:50:18 +00:00
static int do_timelimit ( struct ast_channel * chan , struct ast_bridge_config * config ,
char * parse , unsigned int * calldurationlimit )
{
2006-11-14 20:09:10 +00:00
char * stringp = ast_strdupa ( parse ) ;
2006-11-04 00:50:18 +00:00
char * limit_str , * warning_str , * warnfreq_str ;
const char * var ;
int play_to_caller = 0 , play_to_callee = 0 ;
int delta ;
2006-11-14 20:09:10 +00:00
limit_str = strsep ( & stringp , " : " ) ;
warning_str = strsep ( & stringp , " : " ) ;
warnfreq_str = strsep ( & stringp , " : " ) ;
2006-11-04 00:50:18 +00:00
config - > timelimit = atol ( limit_str ) ;
if ( warning_str )
config - > play_warning = atol ( warning_str ) ;
if ( warnfreq_str )
config - > warning_freq = atol ( warnfreq_str ) ;
if ( ! config - > timelimit ) {
ast_log ( LOG_WARNING , " Dial does not accept L(%s), hanging up. \n " , limit_str ) ;
config - > timelimit = config - > play_warning = config - > warning_freq = 0 ;
config - > warning_sound = NULL ;
return - 1 ; /* error */
} else if ( ( delta = config - > play_warning - config - > timelimit ) > 0 ) {
int w = config - > warning_freq ;
/* If the first warning is requested _after_ the entire call would end,
and no warning frequency is requested , then turn off the warning . If
a warning frequency is requested , reduce the ' first warning ' time by
that frequency until it falls within the call ' s total time limit .
Graphically :
timelim - > | delta | < - playwarning
0 __________________ | _________________ |
| w | | | |
so the number of intervals to cut is 1 + ( delta - 1 ) / w
*/
if ( w = = 0 ) {
config - > play_warning = 0 ;
} else {
config - > play_warning - = w * ( 1 + ( delta - 1 ) / w ) ;
if ( config - > play_warning < 1 )
config - > play_warning = config - > warning_freq = 0 ;
}
}
var = pbx_builtin_getvar_helper ( chan , " LIMIT_PLAYAUDIO_CALLER " ) ;
play_to_caller = var ? ast_true ( var ) : 1 ;
var = pbx_builtin_getvar_helper ( chan , " LIMIT_PLAYAUDIO_CALLEE " ) ;
play_to_callee = var ? ast_true ( var ) : 0 ;
if ( ! play_to_caller & & ! play_to_callee )
play_to_caller = 1 ;
var = pbx_builtin_getvar_helper ( chan , " LIMIT_WARNING_FILE " ) ;
config - > warning_sound = S_OR ( var , " timeleft " ) ;
/* The code looking at config wants a NULL, not just "", to decide
* that the message should not be played , so we replace " " with NULL .
* Note , pbx_builtin_getvar_helper _can_ return NULL if the variable is
* not found .
*/
var = pbx_builtin_getvar_helper ( chan , " LIMIT_TIMEOUT_FILE " ) ;
config - > end_sound = S_OR ( var , NULL ) ;
var = pbx_builtin_getvar_helper ( chan , " LIMIT_CONNECT_FILE " ) ;
config - > start_sound = S_OR ( var , NULL ) ;
/* undo effect of S(x) in case they are both used */
* calldurationlimit = 0 ;
/* more efficient to do it like S(x) does since no advanced opts */
if ( ! config - > play_warning & & ! config - > start_sound & & ! config - > end_sound & & config - > timelimit ) {
* calldurationlimit = config - > timelimit / 1000 ;
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Setting call duration limit to %d seconds. \n " ,
* calldurationlimit ) ;
config - > timelimit = play_to_caller = play_to_callee =
config - > play_warning = config - > warning_freq = 0 ;
} else if ( option_verbose > 2 ) {
ast_verbose ( VERBOSE_PREFIX_3 " Limit Data for this call: \n " ) ;
ast_verbose ( VERBOSE_PREFIX_4 " timelimit = %ld \n " , config - > timelimit ) ;
ast_verbose ( VERBOSE_PREFIX_4 " play_warning = %ld \n " , config - > play_warning ) ;
ast_verbose ( VERBOSE_PREFIX_4 " play_to_caller = %s \n " , play_to_caller ? " yes " : " no " ) ;
ast_verbose ( VERBOSE_PREFIX_4 " play_to_callee = %s \n " , play_to_callee ? " yes " : " no " ) ;
ast_verbose ( VERBOSE_PREFIX_4 " warning_freq = %ld \n " , config - > warning_freq ) ;
ast_verbose ( VERBOSE_PREFIX_4 " start_sound = %s \n " , S_OR ( config - > start_sound , " " ) ) ;
ast_verbose ( VERBOSE_PREFIX_4 " warning_sound = %s \n " , config - > warning_sound ) ;
ast_verbose ( VERBOSE_PREFIX_4 " end_sound = %s \n " , S_OR ( config - > end_sound , " " ) ) ;
}
if ( play_to_caller )
ast_set_flag ( & ( config - > features_caller ) , AST_FEATURE_PLAY_WARNING ) ;
if ( play_to_callee )
ast_set_flag ( & ( config - > features_callee ) , AST_FEATURE_PLAY_WARNING ) ;
return 0 ;
}
2006-11-04 11:00:49 +00:00
static int do_privacy ( struct ast_channel * chan , struct ast_channel * peer ,
struct ast_flags * opts , char * * opt_args , struct privacy_args * pa )
{
int res2 ;
int loopcount = 0 ;
/* Get the user's intro, store it in priv-callerintros/$CID,
unless it is already there - - this should be done before the
call is actually dialed */
/* all ring indications and moh for the caller has been halted as soon as the
target extension was picked up . We are going to have to kill some
time and make the caller believe the peer hasn ' t picked up yet */
if ( ast_test_flag ( opts , OPT_MUSICBACK ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_MUSICBACK ] ) ) {
2007-02-09 19:39:26 +00:00
char * original_moh = ast_strdupa ( chan - > musicclass ) ;
2006-11-04 11:00:49 +00:00
ast_indicate ( chan , - 1 ) ;
2007-02-09 19:39:26 +00:00
ast_string_field_set ( chan , musicclass , opt_args [ OPT_ARG_MUSICBACK ] ) ;
2006-11-04 11:00:49 +00:00
ast_moh_start ( chan , opt_args [ OPT_ARG_MUSICBACK ] , NULL ) ;
2007-02-09 19:39:26 +00:00
ast_string_field_set ( chan , musicclass , original_moh ) ;
2006-11-04 11:00:49 +00:00
} else if ( ast_test_flag ( opts , OPT_RINGBACK ) ) {
ast_indicate ( chan , AST_CONTROL_RINGING ) ;
pa - > sentringing + + ;
}
/* Start autoservice on the other chan ?? */
res2 = ast_autoservice_start ( chan ) ;
/* Now Stream the File */
for ( loopcount = 0 ; loopcount < 3 ; loopcount + + ) {
if ( res2 & & loopcount = = 0 ) /* error in ast_autoservice_start() */
break ;
if ( ! res2 ) /* on timeout, play the message again */
res2 = ast_play_and_wait ( peer , " priv-callpending " ) ;
if ( ! valid_priv_reply ( opts , res2 ) )
res2 = 0 ;
/* priv-callpending script:
" I have a caller waiting, who introduces themselves as: "
*/
if ( ! res2 )
res2 = ast_play_and_wait ( peer , pa - > privintro ) ;
if ( ! valid_priv_reply ( opts , res2 ) )
res2 = 0 ;
/* now get input from the called party, as to their choice */
if ( ! res2 ) {
/* XXX can we have both, or they are mutually exclusive ? */
if ( ast_test_flag ( opts , OPT_PRIVACY ) )
res2 = ast_play_and_wait ( peer , " priv-callee-options " ) ;
if ( ast_test_flag ( opts , OPT_SCREENING ) )
res2 = ast_play_and_wait ( peer , " screen-callee-options " ) ;
}
/*! \page DialPrivacy Dial Privacy scripts
\ par priv - callee - options script :
" Dial 1 if you wish this caller to reach you directly in the future,
and immediately connect to their incoming call
Dial 2 if you wish to send this caller to voicemail now and
forevermore .
Dial 3 to send this caller to the torture menus , now and forevermore .
Dial 4 to send this caller to a simple " go away " menu , now and forevermore .
Dial 5 to allow this caller to come straight thru to you in the future ,
but right now , just this once , send them to voicemail . "
\ par screen - callee - options script :
" Dial 1 if you wish to immediately connect to the incoming call
Dial 2 if you wish to send this caller to voicemail .
Dial 3 to send this caller to the torture menus .
Dial 4 to send this caller to a simple " go away " menu .
*/
if ( valid_priv_reply ( opts , res2 ) )
break ;
/* invalid option */
res2 = ast_play_and_wait ( peer , " vm-sorry " ) ;
}
if ( ast_test_flag ( opts , OPT_MUSICBACK ) ) {
ast_moh_stop ( chan ) ;
} else if ( ast_test_flag ( opts , OPT_RINGBACK ) ) {
ast_indicate ( chan , - 1 ) ;
pa - > sentringing = 0 ;
}
ast_autoservice_stop ( chan ) ;
if ( ast_test_flag ( opts , OPT_PRIVACY ) & & ( res2 > = ' 1 ' & & res2 < = ' 5 ' ) ) {
/* map keypresses to various things, the index is res2 - '1' */
static const char * _val [ ] = { " ALLOW " , " DENY " , " TORTURE " , " KILL " , " ALLOW " } ;
static const int _flag [ ] = { AST_PRIVACY_ALLOW , AST_PRIVACY_DENY , AST_PRIVACY_TORTURE , AST_PRIVACY_KILL , AST_PRIVACY_ALLOW } ;
int i = res2 - ' 1 ' ;
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " --Set privacy database entry %s/%s to %s \n " ,
opt_args [ OPT_ARG_PRIVACY ] , pa - > privcid , _val [ i ] ) ;
ast_privacy_set ( opt_args [ OPT_ARG_PRIVACY ] , pa - > privcid , _flag [ i ] ) ;
}
switch ( res2 ) {
case ' 1 ' :
break ;
case ' 2 ' :
ast_copy_string ( pa - > status , " NOANSWER " , sizeof ( pa - > status ) ) ;
break ;
case ' 3 ' :
ast_copy_string ( pa - > status , " TORTURE " , sizeof ( pa - > status ) ) ;
break ;
case ' 4 ' :
ast_copy_string ( pa - > status , " DONTCALL " , sizeof ( pa - > status ) ) ;
break ;
case ' 5 ' :
/* XXX should we set status to DENY ? */
if ( ast_test_flag ( opts , OPT_PRIVACY ) )
break ;
/* if not privacy, then 5 is the same as "default" case */
default : /* bad input or -1 if failure to start autoservice */
/* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
/* well, there seems basically two choices. Just patch the caller thru immediately,
or , . . . put ' em thru to voicemail . */
/* since the callee may have hung up, let's do the voicemail thing, no database decision */
ast_log ( LOG_NOTICE , " privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding \n " ) ;
/* XXX should we set status to DENY ? */
/* XXX what about the privacy flags ? */
break ;
}
if ( res2 = = ' 1 ' ) { /* the only case where we actually connect */
/* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
just clog things up , and it ' s not useful information , not being tied to a CID */
if ( strncmp ( pa - > privcid , " NOCALLERID " , 10 ) = = 0 | | ast_test_flag ( opts , OPT_SCREEN_NOINTRO ) ) {
ast_filedelete ( pa - > privintro , NULL ) ;
if ( ast_fileexists ( pa - > privintro , NULL , NULL ) > 0 )
ast_log ( LOG_NOTICE , " privacy: ast_filedelete didn't do its job on %s \n " , pa - > privintro ) ;
else if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Successfully deleted %s intro file \n " , pa - > privintro ) ;
}
return 0 ; /* the good exit path */
} else {
ast_hangup ( peer ) ; /* hang up on the callee -- he didn't want to talk anyway! */
return - 1 ;
}
}
2006-12-19 09:15:23 +00:00
/*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
static int setup_privacy_args ( struct privacy_args * pa ,
struct ast_flags * opts , char * opt_args [ ] , struct ast_channel * chan )
{
char callerid [ 60 ] ;
int res ;
char * l ;
if ( ! ast_strlen_zero ( chan - > cid . cid_num ) ) {
l = ast_strdupa ( chan - > cid . cid_num ) ;
ast_shrink_phone_number ( l ) ;
if ( ast_test_flag ( opts , OPT_PRIVACY ) ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Privacy DB is '%s', clid is '%s' \n " ,
opt_args [ OPT_ARG_PRIVACY ] , l ) ;
pa - > privdb_val = ast_privacy_check ( opt_args [ OPT_ARG_PRIVACY ] , l ) ;
} else {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Privacy Screening, clid is '%s' \n " , l ) ;
pa - > privdb_val = AST_PRIVACY_UNKNOWN ;
}
} else {
char * tnam , * tn2 ;
tnam = ast_strdupa ( chan - > name ) ;
/* clean the channel name so slashes don't try to end up in disk file name */
for ( tn2 = tnam ; * tn2 ; tn2 + + ) {
if ( * tn2 = = ' / ' ) /* any other chars to be afraid of? */
* tn2 = ' = ' ;
}
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Privacy-- callerid is empty \n " ) ;
snprintf ( callerid , sizeof ( callerid ) , " NOCALLERID_%s%s " , chan - > exten , tnam ) ;
l = callerid ;
pa - > privdb_val = AST_PRIVACY_UNKNOWN ;
}
ast_copy_string ( pa - > privcid , l , sizeof ( pa - > privcid ) ) ;
if ( strncmp ( pa - > privcid , " NOCALLERID " , 10 ) ! = 0 & & ast_test_flag ( opts , OPT_SCREEN_NOCLID ) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " CallerID set (%s); N option set; Screening should be off \n " , pa - > privcid ) ;
pa - > privdb_val = AST_PRIVACY_ALLOW ;
} else if ( ast_test_flag ( opts , OPT_SCREEN_NOCLID ) & & strncmp ( pa - > privcid , " NOCALLERID " , 10 ) = = 0 ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " CallerID blank; N option set; Screening should happen; dbval is %d \n " , pa - > privdb_val ) ;
}
if ( pa - > privdb_val = = AST_PRIVACY_DENY ) {
ast_copy_string ( pa - > status , " NOANSWER " , sizeof ( pa - > status ) ) ;
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable \n " ) ;
return 0 ;
} else if ( pa - > privdb_val = = AST_PRIVACY_KILL ) {
ast_copy_string ( pa - > status , " DONTCALL " , sizeof ( pa - > status ) ) ;
if ( ast_opt_priority_jumping | | ast_test_flag ( opts , OPT_PRIORITY_JUMP ) ) {
ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 201 ) ;
}
return 0 ; /* Is this right? */
} else if ( pa - > privdb_val = = AST_PRIVACY_TORTURE ) {
ast_copy_string ( pa - > status , " TORTURE " , sizeof ( pa - > status ) ) ;
if ( ast_opt_priority_jumping | | ast_test_flag ( opts , OPT_PRIORITY_JUMP ) ) {
ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 301 ) ;
}
return 0 ; /* is this right??? */
} else if ( pa - > privdb_val = = AST_PRIVACY_UNKNOWN ) {
/* Get the user's intro, store it in priv-callerintros/$CID,
unless it is already there - - this should be done before the
call is actually dialed */
/* make sure the priv-callerintros dir actually exists */
snprintf ( pa - > privintro , sizeof ( pa - > privintro ) , " %s/sounds/priv-callerintros " , ast_config_AST_DATA_DIR ) ;
if ( mkdir ( pa - > privintro , 0755 ) & & errno ! = EEXIST ) {
ast_log ( LOG_WARNING , " privacy: can't create directory priv-callerintros: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
snprintf ( pa - > privintro , sizeof ( pa - > privintro ) , " priv-callerintros/%s " , pa - > privcid ) ;
if ( ast_fileexists ( pa - > privintro , NULL , NULL ) > 0 & & strncmp ( pa - > privcid , " NOCALLERID " , 10 ) ! = 0 ) {
/* the DELUX version of this code would allow this caller the
option to hear and retape their previously recorded intro .
*/
} else {
int duration ; /* for feedback from play_and_wait */
/* the file doesn't exist yet. Let the caller submit his
vocal intro for posterity */
/* priv-recordintro script:
" At the tone, please say your name: "
*/
res = ast_play_and_record ( chan , " priv-recordintro " , pa - > privintro , 4 , " gsm " , & duration , 128 , 2000 , 0 ) ; /* NOTE: I've reduced the total time to 4 sec */
/* don't think we'll need a lock removed, we took care of
conflicts by naming the pa . privintro file */
if ( res = = - 1 ) {
/* Delete the file regardless since they hung up during recording */
ast_filedelete ( pa - > privintro , NULL ) ;
if ( ast_fileexists ( pa - > privintro , NULL , NULL ) > 0 )
ast_log ( LOG_NOTICE , " privacy: ast_filedelete didn't do its job on %s \n " , pa - > privintro ) ;
else if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Successfully deleted %s intro file \n " , pa - > privintro ) ;
return - 1 ;
}
if ( ! ast_streamfile ( chan , " vm-dialout " , chan - > language ) )
ast_waitstream ( chan , " " ) ;
}
}
return 1 ; /* success */
}
2005-01-18 03:12:53 +00:00
static int dial_exec_full ( struct ast_channel * chan , void * data , struct ast_flags * peerflags )
1999-12-04 21:35:07 +00:00
{
2006-11-03 22:36:17 +00:00
int res = - 1 ; /* default: error */
2006-08-21 02:11:39 +00:00
struct ast_module_user * u ;
2006-11-03 22:36:17 +00:00
char * rest , * cur ; /* scan the list of destinations */
2006-12-19 16:36:45 +00:00
struct chanlist * outgoing = NULL ; /* list of destinations */
2004-06-21 13:30:58 +00:00
struct ast_channel * peer ;
2006-11-03 22:36:17 +00:00
int to ; /* timeout */
struct cause_args num = { chan , 0 , 0 , 0 } ;
2004-10-26 22:25:43 +00:00
int cause ;
1999-12-18 07:01:48 +00:00
char numsubst [ AST_MAX_EXTENSION ] ;
2005-02-01 01:53:25 +00:00
char cidname [ AST_MAX_EXTENSION ] ;
2006-11-04 00:50:18 +00:00
struct ast_bridge_config config ;
2006-04-11 16:15:11 +00:00
unsigned int calldurationlimit = 0 ;
char * dtmfcalled = NULL , * dtmfcalling = NULL ;
2006-11-04 11:00:49 +00:00
struct privacy_args pa = {
. sentringing = 0 ,
. privdb_val = 0 ,
2007-02-03 20:46:36 +00:00
. status = " INVALIDARGS " ,
2006-11-04 11:00:49 +00:00
} ;
2006-04-11 16:15:11 +00:00
int sentringing = 0 , moh = 0 ;
2005-12-20 17:52:31 +00:00
const char * outbound_group = NULL ;
2006-04-19 16:54:04 +00:00
int result = 0 ;
2006-04-19 16:36:15 +00:00
time_t start_time ;
2005-11-02 21:46:52 +00:00
char * parse ;
2006-04-22 11:30:06 +00:00
int opermode = 0 ;
2005-11-02 21:46:52 +00:00
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( peers ) ;
AST_APP_ARG ( timeout ) ;
AST_APP_ARG ( options ) ;
AST_APP_ARG ( url ) ;
) ;
struct ast_flags opts = { 0 , } ;
char * opt_args [ OPT_ARG_ARRAY_SIZE ] ;
2005-03-17 22:39:04 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2005-11-02 21:46:52 +00:00
ast_log ( LOG_WARNING , " Dial requires an argument (technology/number) \n " ) ;
2007-02-03 20:46:36 +00:00
pbx_builtin_setvar_helper ( chan , " DIALSTATUS " , pa . status ) ;
1999-12-04 21:35:07 +00:00
return - 1 ;
}
2004-09-22 05:19:06 +00:00
2006-11-04 00:50:18 +00:00
u = ast_module_user_add ( chan ) ; /* XXX is this the right place ? */
2005-10-19 18:19:02 +00:00
2006-05-10 13:22:15 +00:00
parse = ast_strdupa ( data ) ;
2007-02-03 20:46:36 +00:00
2005-11-02 21:46:52 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
2006-11-04 00:50:18 +00:00
memset ( & config , 0 , sizeof ( struct ast_bridge_config ) ) ;
2006-04-19 14:02:49 +00:00
if ( ! ast_strlen_zero ( args . options ) & &
2007-02-03 20:46:36 +00:00
ast_app_parse_options ( dial_exec_options , & opts , opt_args , args . options ) ) {
pbx_builtin_setvar_helper ( chan , " DIALSTATUS " , pa . status ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2007-02-03 20:46:36 +00:00
}
2003-01-17 05:10:52 +00:00
2005-11-02 21:46:52 +00:00
if ( ast_strlen_zero ( args . peers ) ) {
ast_log ( LOG_WARNING , " Dial requires an argument (technology/number) \n " ) ;
2007-02-03 20:46:36 +00:00
pbx_builtin_setvar_helper ( chan , " DIALSTATUS " , pa . status ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2005-11-02 21:46:52 +00:00
}
2004-04-26 23:22:34 +00:00
2006-04-22 11:30:06 +00:00
if ( ast_test_flag ( & opts , OPT_OPERMODE ) ) {
2006-11-04 01:16:20 +00:00
opermode = ast_strlen_zero ( opt_args [ OPT_ARG_OPERMODE ] ) ? 1 : atoi ( opt_args [ OPT_ARG_OPERMODE ] ) ;
2006-04-22 11:30:06 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Setting operator services mode to %d. \n " , opermode ) ;
}
2006-05-31 15:52:32 +00:00
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_DURATION_STOP ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_DURATION_STOP ] ) ) {
calldurationlimit = atoi ( opt_args [ OPT_ARG_DURATION_STOP ] ) ;
2006-04-11 16:15:11 +00:00
if ( ! calldurationlimit ) {
ast_log ( LOG_WARNING , " Dial does not accept S(%s), hanging up. \n " , opt_args [ OPT_ARG_DURATION_STOP ] ) ;
2007-02-03 20:46:36 +00:00
pbx_builtin_setvar_helper ( chan , " DIALSTATUS " , pa . status ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2006-04-11 16:15:11 +00:00
}
2005-11-02 21:46:52 +00:00
if ( option_verbose > 2 )
2006-04-11 16:15:11 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Setting call duration limit to %d seconds. \n " , calldurationlimit ) ;
2005-11-02 21:46:52 +00:00
}
2005-03-17 22:39:04 +00:00
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_SENDDTMF ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_SENDDTMF ] ) ) {
2006-04-19 16:54:04 +00:00
dtmfcalling = opt_args [ OPT_ARG_SENDDTMF ] ;
dtmfcalled = strsep ( & dtmfcalling , " : " ) ;
2005-11-02 21:46:52 +00:00
}
2005-03-17 22:39:04 +00:00
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_DURATION_LIMIT ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_DURATION_LIMIT ] ) ) {
2006-11-04 00:50:18 +00:00
if ( do_timelimit ( chan , & config , opt_args [ OPT_ARG_DURATION_LIMIT ] , & calldurationlimit ) )
2006-04-19 14:02:49 +00:00
goto done ;
2003-01-17 05:10:52 +00:00
}
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_RESETCDR ) & & chan - > cdr )
2005-11-06 21:00:35 +00:00
ast_cdr_reset ( chan - > cdr , NULL ) ;
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_PRIVACY ) & & ast_strlen_zero ( opt_args [ OPT_ARG_PRIVACY ] ) )
opt_args [ OPT_ARG_PRIVACY ] = ast_strdupa ( chan - > exten ) ;
2005-07-12 03:23:31 +00:00
2006-12-19 09:15:23 +00:00
if ( ast_test_flag ( & opts , OPT_PRIVACY ) | | ast_test_flag ( & opts , OPT_SCREENING ) ) {
res = setup_privacy_args ( & pa , & opts , opt_args , chan ) ;
if ( res < = 0 )
2005-07-12 03:23:31 +00:00
goto out ;
2006-12-19 09:15:23 +00:00
res = - 1 ; /* reset default */
2003-01-17 05:10:52 +00:00
}
2004-11-07 21:49:43 +00:00
/* If a channel group has been specified, get it for use when we create peer channels */
outbound_group = pbx_builtin_getvar_helper ( chan , " OUTBOUND_GROUP " ) ;
2006-05-31 15:52:32 +00:00
ast_copy_flags ( peerflags , & opts , OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING ) ;
2006-04-19 14:02:49 +00:00
/* loop through the list of dial destinations */
rest = args . peers ;
while ( ( cur = strsep ( & rest , " & " ) ) ) {
2006-12-19 16:36:45 +00:00
struct chanlist * tmp ;
2006-12-19 09:58:40 +00:00
struct ast_channel * tc ; /* channel for this destination */
1999-12-18 07:01:48 +00:00
/* Get a technology/[device:]number pair */
2006-04-19 16:36:15 +00:00
char * number = cur ;
char * tech = strsep ( & number , " / " ) ;
1999-12-04 21:35:07 +00:00
if ( ! number ) {
2005-11-02 21:46:52 +00:00
ast_log ( LOG_WARNING , " Dial argument takes format (technology/[device:]number1) \n " ) ;
1999-12-04 21:35:07 +00:00
goto out ;
}
2006-04-19 14:02:49 +00:00
if ( ! ( tmp = ast_calloc ( 1 , sizeof ( * tmp ) ) ) )
1999-12-04 21:35:07 +00:00
goto out ;
2005-11-02 21:46:52 +00:00
if ( opts . flags ) {
2005-11-03 21:40:36 +00:00
ast_copy_flags ( tmp , & opts ,
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
2006-05-22 16:43:43 +00:00
OPT_CALLEE_PARK | OPT_CALLER_PARK |
2005-11-03 21:40:36 +00:00
OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID ) ;
2005-11-02 21:46:52 +00:00
ast_set2_flag ( tmp , args . url , DIAL_NOFORWARDHTML ) ;
2002-05-17 14:33:10 +00:00
}
2005-07-07 23:32:37 +00:00
ast_copy_string ( numsubst , number , sizeof ( numsubst ) ) ;
1999-12-04 21:35:07 +00:00
/* Request the peer */
2006-12-19 09:58:40 +00:00
tc = ast_request ( tech , chan - > nativeformats , numsubst , & cause ) ;
if ( ! tc ) {
1999-12-04 21:35:07 +00:00
/* If we can't, just go on to the next call */
2006-12-19 09:58:40 +00:00
ast_log ( LOG_WARNING , " Unable to create channel of type '%s' (cause %d - %s) \n " ,
tech , cause , ast_cause2str ( cause ) ) ;
2006-11-03 22:36:17 +00:00
handle_cause ( cause , & num ) ;
2006-04-19 14:02:49 +00:00
if ( ! rest ) /* we are on the last destination */
2005-10-05 21:18:28 +00:00
chan - > hangupcause = cause ;
2006-12-19 09:33:57 +00:00
free ( tmp ) ;
1999-12-04 21:35:07 +00:00
continue ;
}
2006-12-19 09:58:40 +00:00
pbx_builtin_setvar_helper ( tc , " DIALEDPEERNUMBER " , numsubst ) ;
if ( ! ast_strlen_zero ( tc - > call_forward ) ) {
2005-09-07 21:01:31 +00:00
char tmpchan [ 256 ] ;
2004-06-21 06:11:56 +00:00
char * stuff ;
char * tech ;
2006-12-19 09:58:40 +00:00
ast_copy_string ( tmpchan , tc - > call_forward , sizeof ( tmpchan ) ) ;
2004-06-21 06:11:56 +00:00
if ( ( stuff = strchr ( tmpchan , ' / ' ) ) ) {
2006-04-19 14:02:49 +00:00
* stuff + + = ' \0 ' ;
2004-06-21 06:11:56 +00:00
tech = tmpchan ;
} else {
2006-12-19 09:58:40 +00:00
snprintf ( tmpchan , sizeof ( tmpchan ) , " %s@%s " , tc - > call_forward , tc - > context ) ;
2004-06-21 06:11:56 +00:00
stuff = tmpchan ;
tech = " Local " ;
}
2004-11-28 22:08:44 +00:00
tmp - > forwards + + ;
if ( tmp - > forwards < AST_MAX_FORWARDS ) {
if ( option_verbose > 2 )
2006-12-19 09:58:40 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Now forwarding %s to '%s/%s' (thanks to %s) \n " ,
chan - > name , tech , stuff , tc - > name ) ;
ast_hangup ( tc ) ;
/* If we have been told to ignore forwards, just set this channel to null
* and continue processing extensions normally */
2006-05-31 15:52:32 +00:00
if ( ast_test_flag ( & opts , OPT_IGNORE_FORWARDING ) ) {
2006-12-19 09:58:40 +00:00
tc = NULL ;
2006-05-31 16:11:55 +00:00
cause = AST_CAUSE_BUSY ;
2006-05-31 15:52:32 +00:00
if ( option_verbose > 2 )
2006-12-19 09:58:40 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Forwarding %s to '%s/%s' prevented. \n " ,
chan - > name , tech , stuff ) ;
2006-05-31 15:52:32 +00:00
} else {
2006-12-19 09:58:40 +00:00
tc = ast_request ( tech , chan - > nativeformats , stuff , & cause ) ;
2006-05-31 15:52:32 +00:00
}
2006-12-19 09:58:40 +00:00
if ( ! tc )
2004-11-28 22:08:44 +00:00
ast_log ( LOG_NOTICE , " Unable to create local channel for call forward to '%s/%s' (cause = %d) \n " , tech , stuff , cause ) ;
2006-05-16 23:39:29 +00:00
else
2006-12-19 09:58:40 +00:00
ast_channel_inherit_variables ( chan , tc ) ;
2004-11-28 22:08:44 +00:00
} else {
if ( option_verbose > 2 )
2006-12-19 09:58:40 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Too many forwards from %s \n " , tc - > name ) ;
ast_hangup ( tc ) ;
tc = NULL ;
2004-11-28 22:08:44 +00:00
cause = AST_CAUSE_CONGESTION ;
}
2006-12-19 09:58:40 +00:00
if ( ! tc ) {
2006-11-03 22:36:17 +00:00
handle_cause ( cause , & num ) ;
2006-12-19 09:33:57 +00:00
free ( tmp ) ;
2003-04-06 06:37:08 +00:00
continue ;
}
2002-03-05 15:59:01 +00:00
}
2004-11-01 02:23:28 +00:00
2005-12-20 17:52:31 +00:00
/* Setup outgoing SDP to match incoming one */
2006-12-19 09:58:40 +00:00
ast_rtp_make_compatible ( tc , chan , ! outgoing & & ! rest ) ;
2005-12-20 17:52:31 +00:00
2005-01-08 17:23:29 +00:00
/* Inherit specially named variables from parent channel */
2006-12-19 09:58:40 +00:00
ast_channel_inherit_variables ( chan , tc ) ;
2004-11-01 02:23:28 +00:00
2006-12-19 09:58:40 +00:00
tc - > appl = " AppDial " ;
tc - > data = " (Outgoing Line) " ;
tc - > whentohangup = 0 ;
2006-04-19 16:19:52 +00:00
2006-12-19 09:58:40 +00:00
S_REPLACE ( tc - > cid . cid_num , ast_strdup ( chan - > cid . cid_num ) ) ;
S_REPLACE ( tc - > cid . cid_name , ast_strdup ( chan - > cid . cid_name ) ) ;
S_REPLACE ( tc - > cid . cid_ani , ast_strdup ( chan - > cid . cid_ani ) ) ;
S_REPLACE ( tc - > cid . cid_rdnis , ast_strdup ( chan - > cid . cid_rdnis ) ) ;
2004-10-02 00:58:31 +00:00
2003-12-04 22:26:38 +00:00
/* Copy language from incoming to outgoing */
2006-12-19 09:58:40 +00:00
ast_string_field_set ( tc , language , chan - > language ) ;
ast_string_field_set ( tc , accountcode , chan - > accountcode ) ;
tc - > cdrflags = chan - > cdrflags ;
if ( ast_strlen_zero ( tc - > musicclass ) )
ast_string_field_set ( tc , musicclass , chan - > musicclass ) ;
/* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */
tc - > cid . cid_pres = chan - > cid . cid_pres ;
tc - > cid . cid_ton = chan - > cid . cid_ton ;
tc - > cid . cid_tns = chan - > cid . cid_tns ;
tc - > adsicpe = chan - > adsicpe ;
tc - > transfercapability = chan - > transfercapability ;
2004-11-07 21:49:43 +00:00
/* If we have an outbound group, set this peer channel to it */
if ( outbound_group )
2006-12-19 09:58:40 +00:00
ast_app_group_set_channel ( tc , outbound_group ) ;
2004-11-07 21:49:43 +00:00
2006-10-13 21:20:18 +00:00
/* Inherit context and extension */
2006-12-19 09:58:40 +00:00
ast_copy_string ( tc - > dialcontext , chan - > context , sizeof ( tc - > dialcontext ) ) ;
ast_copy_string ( tc - > exten , chan - > exten , sizeof ( tc - > exten ) ) ;
2006-10-13 21:20:18 +00:00
2006-12-19 09:58:40 +00:00
res = ast_call ( tc , numsubst , 0 ) ; /* Place the call, but don't wait on the answer */
2003-08-14 20:48:44 +00:00
/* Save the info in cdr's that we called them */
if ( chan - > cdr )
2006-12-19 09:58:40 +00:00
ast_cdr_setdestchan ( chan - > cdr , tc - > name ) ;
2003-08-14 20:48:44 +00:00
2004-06-20 06:24:25 +00:00
/* check the results of ast_call */
1999-12-04 21:35:07 +00:00
if ( res ) {
/* Again, keep going even if there's an error */
if ( option_debug )
ast_log ( LOG_DEBUG , " ast call on peer returned %d \n " , res ) ;
2006-09-19 16:23:45 +00:00
if ( option_verbose > 2 )
1999-12-18 07:01:48 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Couldn't call %s \n " , numsubst ) ;
2006-12-19 09:58:40 +00:00
ast_hangup ( tc ) ;
tc = NULL ;
2006-12-19 09:33:57 +00:00
free ( tmp ) ;
1999-12-04 21:35:07 +00:00
continue ;
2005-01-16 07:58:51 +00:00
} else {
2006-12-19 09:58:40 +00:00
senddialevent ( chan , tc ) ;
1999-12-04 21:35:07 +00:00
if ( option_verbose > 2 )
1999-12-18 07:01:48 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Called %s \n " , numsubst ) ;
2005-11-03 21:40:36 +00:00
if ( ! ast_test_flag ( peerflags , OPT_ORIGINAL_CLID ) )
2006-12-19 09:58:40 +00:00
ast_set_callerid ( tc , S_OR ( chan - > macroexten , chan - > exten ) , get_cid_name ( cidname , sizeof ( cidname ) , chan ) , NULL ) ;
2005-01-16 07:58:51 +00:00
}
1999-12-04 21:35:07 +00:00
/* Put them in the list of outgoing thingies... We're ready now.
XXX If we ' re forcibly removed , these outgoing calls won ' t get
hung up XXX */
2005-01-05 23:05:49 +00:00
ast_set_flag ( tmp , DIAL_STILLGOING ) ;
2006-12-19 09:58:40 +00:00
tmp - > chan = tc ;
1999-12-04 21:35:07 +00:00
tmp - > next = outgoing ;
outgoing = tmp ;
2002-09-02 15:20:28 +00:00
/* If this line is up, don't try anybody else */
if ( outgoing - > chan - > _state = = AST_STATE_UP )
break ;
2006-04-19 14:02:49 +00:00
}
2001-03-30 18:47:35 +00:00
2006-04-19 14:02:49 +00:00
if ( ast_strlen_zero ( args . timeout ) ) {
to = - 1 ;
} else {
2005-11-02 21:46:52 +00:00
to = atoi ( args . timeout ) ;
2004-04-02 07:47:23 +00:00
if ( to > 0 )
to * = 1000 ;
else
2005-11-02 21:46:52 +00:00
ast_log ( LOG_WARNING , " Invalid timeout specified: '%s' \n " , args . timeout ) ;
2006-04-19 14:02:49 +00:00
}
2004-06-21 18:28:35 +00:00
2006-04-19 14:02:49 +00:00
if ( ! outgoing ) {
2006-11-04 11:00:49 +00:00
strcpy ( pa . status , " CHANUNAVAIL " ) ;
2006-04-19 14:02:49 +00:00
} else {
2004-06-23 03:16:58 +00:00
/* Our status will at least be NOANSWER */
2006-11-04 11:00:49 +00:00
strcpy ( pa . status , " NOANSWER " ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( outgoing , OPT_MUSICBACK ) ) {
2006-07-19 20:44:39 +00:00
moh = 1 ;
2007-02-09 19:39:26 +00:00
if ( ! ast_strlen_zero ( opt_args [ OPT_ARG_MUSICBACK ] ) ) {
char * original_moh = ast_strdupa ( chan - > musicclass ) ;
ast_string_field_set ( chan , musicclass , opt_args [ OPT_ARG_MUSICBACK ] ) ;
ast_moh_start ( chan , opt_args [ OPT_ARG_MUSICBACK ] , NULL ) ;
ast_string_field_set ( chan , musicclass , original_moh ) ;
} else {
ast_moh_start ( chan , NULL , NULL ) ;
}
2006-12-01 23:39:59 +00:00
ast_indicate ( chan , AST_CONTROL_PROGRESS ) ;
2005-11-03 21:40:36 +00:00
} else if ( ast_test_flag ( outgoing , OPT_RINGBACK ) ) {
2004-06-22 13:53:45 +00:00
ast_indicate ( chan , AST_CONTROL_RINGING ) ;
sentringing + + ;
}
2006-04-19 14:02:49 +00:00
}
2004-06-21 18:28:35 +00:00
2004-07-14 01:10:24 +00:00
time ( & start_time ) ;
2006-11-04 11:00:49 +00:00
peer = wait_for_answer ( chan , outgoing , & to , peerflags , & pa , & num , ast_test_flag ( & opts , OPT_PRIORITY_JUMP ) , & result ) ;
2005-01-05 23:05:49 +00:00
1999-12-04 21:35:07 +00:00
if ( ! peer ) {
2005-01-18 03:12:53 +00:00
if ( result ) {
res = result ;
2006-04-19 14:02:49 +00:00
} else if ( to ) { /* Musta gotten hung up */
1999-12-04 21:35:07 +00:00
res = - 1 ;
2006-04-19 14:02:49 +00:00
} else { /* Nobody answered, next please? */
2005-09-07 19:13:00 +00:00
res = 0 ;
2006-04-19 14:02:49 +00:00
}
2006-04-19 16:54:04 +00:00
/* almost done, although the 'else' block is 400 lines */
2006-04-19 14:02:49 +00:00
} else {
2006-04-19 16:36:15 +00:00
const char * number ;
time_t end_time , answer_time = time ( NULL ) ;
2006-11-04 01:16:20 +00:00
char toast [ 80 ] ; /* buffer to set variables */
2006-04-19 16:36:15 +00:00
2006-11-04 11:00:49 +00:00
strcpy ( pa . status , " ANSWER " ) ;
1999-12-04 21:35:07 +00:00
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with - 1 so that it is hung up properly after the
conversation . */
hanguptree ( outgoing , peer ) ;
outgoing = NULL ;
2001-12-20 15:21:47 +00:00
/* If appropriate, log that we have a destination channel */
if ( chan - > cdr )
ast_cdr_setdestchan ( chan - > cdr , peer - > name ) ;
2003-03-12 06:00:18 +00:00
if ( peer - > name )
pbx_builtin_setvar_helper ( chan , " DIALEDPEERNAME " , peer - > name ) ;
2005-01-27 16:33:12 +00:00
2006-04-19 16:36:15 +00:00
number = pbx_builtin_getvar_helper ( peer , " DIALEDPEERNUMBER " ) ;
2005-01-27 16:33:12 +00:00
if ( ! number )
number = numsubst ;
pbx_builtin_setvar_helper ( chan , " DIALEDPEERNUMBER " , number ) ;
2005-11-02 21:46:52 +00:00
if ( ! ast_strlen_zero ( args . url ) & & ast_channel_supports_html ( peer ) ) {
2006-01-17 18:54:56 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " app_dial: sendurl=%s. \n " , args . url ) ;
2005-11-02 21:46:52 +00:00
ast_channel_sendurl ( peer , args . url ) ;
2005-10-26 19:48:14 +00:00
}
2006-11-04 11:00:49 +00:00
if ( ( ast_test_flag ( & opts , OPT_PRIVACY ) | | ast_test_flag ( & opts , OPT_SCREENING ) ) & & pa . privdb_val = = AST_PRIVACY_UNKNOWN ) {
if ( do_privacy ( chan , peer , & opts , opt_args , & pa ) ) {
2006-04-19 18:00:32 +00:00
res = 0 ;
goto out ;
}
2005-07-12 03:23:31 +00:00
}
2006-04-19 14:02:49 +00:00
if ( ! ast_test_flag ( & opts , OPT_ANNOUNCE ) | | ast_strlen_zero ( opt_args [ OPT_ARG_ANNOUNCE ] ) ) {
res = 0 ;
} else {
2006-04-19 16:54:04 +00:00
int digit = 0 ;
2004-12-19 21:13:41 +00:00
/* Start autoservice on the other chan */
2004-05-07 20:39:14 +00:00
res = ast_autoservice_start ( chan ) ;
2004-12-19 21:13:41 +00:00
/* Now Stream the File */
2004-05-07 20:39:14 +00:00
if ( ! res )
2005-11-02 21:46:52 +00:00
res = ast_streamfile ( peer , opt_args [ OPT_ARG_ANNOUNCE ] , peer - > language ) ;
2004-05-20 00:29:09 +00:00
if ( ! res ) {
digit = ast_waitstream ( peer , AST_DIGIT_ANY ) ;
}
2004-12-19 21:13:41 +00:00
/* Ok, done. stop autoservice */
2004-05-07 20:39:14 +00:00
res = ast_autoservice_stop ( chan ) ;
2004-05-20 00:29:09 +00:00
if ( digit > 0 & & ! res )
res = ast_senddigit ( chan , digit ) ;
else
res = digit ;
2006-04-19 14:02:49 +00:00
}
2005-03-17 22:39:04 +00:00
2005-11-02 21:46:52 +00:00
if ( chan & & peer & & ast_test_flag ( & opts , OPT_GOTO ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_GOTO ] ) ) {
2006-04-19 14:02:49 +00:00
replace_macro_delimiter ( opt_args [ OPT_ARG_GOTO ] ) ;
2005-11-02 21:46:52 +00:00
ast_parseable_goto ( chan , opt_args [ OPT_ARG_GOTO ] ) ;
ast_parseable_goto ( peer , opt_args [ OPT_ARG_GOTO ] ) ;
2006-03-19 10:11:29 +00:00
peer - > priority + + ;
2005-03-17 22:39:04 +00:00
ast_pbx_start ( peer ) ;
hanguptree ( outgoing , NULL ) ;
2007-01-10 04:56:48 +00:00
res = 1 ;
2006-04-19 14:02:49 +00:00
goto done ;
2005-03-17 22:39:04 +00:00
}
2003-07-02 14:06:12 +00:00
2005-11-02 21:46:52 +00:00
if ( ast_test_flag ( & opts , OPT_CALLEE_MACRO ) & & ! ast_strlen_zero ( opt_args [ OPT_ARG_CALLEE_MACRO ] ) ) {
2006-04-19 16:54:04 +00:00
struct ast_app * theapp ;
2006-04-19 18:07:19 +00:00
const char * macro_result ;
2006-04-19 16:54:04 +00:00
2004-09-10 02:31:30 +00:00
res = ast_autoservice_start ( chan ) ;
if ( res ) {
ast_log ( LOG_ERROR , " Unable to start autoservice on calling channel \n " ) ;
res = - 1 ;
}
2006-04-19 16:54:04 +00:00
theapp = pbx_findapp ( " Macro " ) ;
2004-09-10 02:31:30 +00:00
2006-04-19 16:54:04 +00:00
if ( theapp & & ! res ) { /* XXX why check res here ? */
2006-04-19 14:02:49 +00:00
replace_macro_delimiter ( opt_args [ OPT_ARG_CALLEE_MACRO ] ) ;
2006-04-19 16:54:04 +00:00
res = pbx_exec ( peer , theapp , opt_args [ OPT_ARG_CALLEE_MACRO ] ) ;
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Macro exited with status %d \n " , res ) ;
2004-09-10 02:31:30 +00:00
res = 0 ;
} else {
ast_log ( LOG_ERROR , " Could not find application Macro \n " ) ;
res = - 1 ;
}
if ( ast_autoservice_stop ( chan ) < 0 ) {
ast_log ( LOG_ERROR , " Could not stop autoservice on calling channel \n " ) ;
res = - 1 ;
}
2004-11-22 22:11:10 +00:00
2006-04-19 18:07:19 +00:00
if ( ! res & & ( macro_result = pbx_builtin_getvar_helper ( peer , " MACRO_RESULT " ) ) ) {
2006-11-04 01:16:20 +00:00
char * macro_transfer_dest ;
2005-01-05 23:05:49 +00:00
2006-11-04 01:16:20 +00:00
if ( ! strcasecmp ( macro_result , " BUSY " ) ) {
2006-11-04 11:00:49 +00:00
ast_copy_string ( pa . status , macro_result , sizeof ( pa . status ) ) ;
2006-11-04 01:16:20 +00:00
if ( ast_opt_priority_jumping | | ast_test_flag ( & opts , OPT_PRIORITY_JUMP ) ) {
if ( ! ast_goto_if_exists ( chan , NULL , NULL , chan - > priority + 101 ) ) {
ast_set_flag ( peerflags , OPT_GO_ON ) ;
2004-11-22 22:11:10 +00:00
}
2006-11-04 01:16:20 +00:00
} else
ast_set_flag ( peerflags , OPT_GO_ON ) ;
res = - 1 ;
} else if ( ! strcasecmp ( macro_result , " CONGESTION " ) | | ! strcasecmp ( macro_result , " CHANUNAVAIL " ) ) {
2006-11-04 11:00:49 +00:00
ast_copy_string ( pa . status , macro_result , sizeof ( pa . status ) ) ;
2006-11-04 01:16:20 +00:00
ast_set_flag ( peerflags , OPT_GO_ON ) ;
res = - 1 ;
} else if ( ! strcasecmp ( macro_result , " CONTINUE " ) ) {
/* hangup peer and keep chan alive assuming the macro has changed
the context / exten / priority or perhaps
the next priority in the current exten is desired .
*/
ast_set_flag ( peerflags , OPT_GO_ON ) ;
res = - 1 ;
} else if ( ! strcasecmp ( macro_result , " ABORT " ) ) {
/* Hangup both ends unless the caller has the g flag */
res = - 1 ;
} else if ( ! strncasecmp ( macro_result , " GOTO: " , 5 ) & & ( macro_transfer_dest = ast_strdupa ( macro_result + 5 ) ) ) {
res = - 1 ;
/* perform a transfer to a new extension */
if ( strchr ( macro_transfer_dest , ' ^ ' ) ) { /* context^exten^priority*/
replace_macro_delimiter ( macro_transfer_dest ) ;
if ( ! ast_parseable_goto ( chan , macro_transfer_dest ) )
ast_set_flag ( peerflags , OPT_GO_ON ) ;
2004-11-22 22:11:10 +00:00
}
2006-11-04 01:16:20 +00:00
}
2004-11-22 22:11:10 +00:00
}
2004-09-10 02:31:30 +00:00
}
2004-05-07 21:14:55 +00:00
if ( ! res ) {
if ( calldurationlimit > 0 ) {
2006-04-19 16:54:04 +00:00
chan - > whentohangup = time ( NULL ) + calldurationlimit ;
2004-05-07 21:14:55 +00:00
}
2005-10-26 19:48:14 +00:00
if ( ! ast_strlen_zero ( dtmfcalled ) ) {
2005-04-11 02:46:25 +00:00
if ( option_verbose > 2 )
2006-01-17 18:54:56 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Sending DTMF '%s' to the called party. \n " , dtmfcalled ) ;
2005-04-11 02:46:25 +00:00
res = ast_dtmf_stream ( peer , chan , dtmfcalled , 250 ) ;
}
2005-10-26 19:48:14 +00:00
if ( ! ast_strlen_zero ( dtmfcalling ) ) {
2005-04-11 02:46:25 +00:00
if ( option_verbose > 2 )
2006-01-17 18:54:56 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Sending DTMF '%s' to the calling party. \n " , dtmfcalling ) ;
2005-04-11 02:46:25 +00:00
res = ast_dtmf_stream ( chan , peer , dtmfcalling , 250 ) ;
}
2004-05-07 20:39:14 +00:00
}
2006-11-04 01:16:20 +00:00
if ( res ) { /* some error */
res = - 1 ;
end_time = time ( NULL ) ;
} else {
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLEE_TRANSFER ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_callee ) , AST_FEATURE_REDIRECT ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLER_TRANSFER ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_caller ) , AST_FEATURE_REDIRECT ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLEE_HANGUP ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_callee ) , AST_FEATURE_DISCONNECT ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLER_HANGUP ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_caller ) , AST_FEATURE_DISCONNECT ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLEE_MONITOR ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_callee ) , AST_FEATURE_AUTOMON ) ;
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLER_MONITOR ) )
2005-01-10 14:46:59 +00:00
ast_set_flag ( & ( config . features_caller ) , AST_FEATURE_AUTOMON ) ;
2006-05-22 16:43:43 +00:00
if ( ast_test_flag ( peerflags , OPT_CALLEE_PARK ) )
ast_set_flag ( & ( config . features_callee ) , AST_FEATURE_PARKCALL ) ;
if ( ast_test_flag ( peerflags , OPT_CALLER_PARK ) )
ast_set_flag ( & ( config . features_caller ) , AST_FEATURE_PARKCALL ) ;
2005-01-18 03:12:53 +00:00
2004-06-21 18:28:35 +00:00
if ( moh ) {
moh = 0 ;
ast_moh_stop ( chan ) ;
} else if ( sentringing ) {
sentringing = 0 ;
ast_indicate ( chan , - 1 ) ;
}
2004-07-07 16:02:13 +00:00
/* Be sure no generators are left on it */
ast_deactivate_generator ( chan ) ;
/* Make sure channels are compatible */
res = ast_channel_make_compatible ( chan , peer ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Had to drop call because I couldn't make %s compatible with %s \n " , chan - > name , peer - > name ) ;
ast_hangup ( peer ) ;
2006-04-19 16:54:04 +00:00
res = - 1 ;
goto done ;
2004-07-07 16:02:13 +00:00
}
2006-11-04 01:16:20 +00:00
if ( opermode & & ! strncmp ( chan - > name , " Zap " , 3 ) & & ! strncmp ( peer - > name , " Zap " , 3 ) ) {
/* XXX what's this special handling for Zap <-> Zap ? */
2006-04-22 11:30:06 +00:00
struct oprmode oprmode ;
oprmode . peer = peer ;
oprmode . mode = opermode ;
ast_channel_setoption ( chan ,
AST_OPTION_OPRMODE , & oprmode , sizeof ( struct oprmode ) , 0 ) ;
}
2004-05-07 21:14:55 +00:00
res = ast_bridge_call ( chan , peer , & config ) ;
2006-11-04 01:16:20 +00:00
end_time = time ( NULL ) ;
snprintf ( toast , sizeof ( toast ) , " %ld " , ( long ) ( end_time - answer_time ) ) ;
pbx_builtin_setvar_helper ( chan , " ANSWEREDTIME " , toast ) ;
2006-04-19 16:19:52 +00:00
}
2006-11-04 01:16:20 +00:00
snprintf ( toast , sizeof ( toast ) , " %ld " , ( long ) ( end_time - start_time ) ) ;
pbx_builtin_setvar_helper ( chan , " DIALEDTIME " , toast ) ;
2004-05-22 23:17:33 +00:00
if ( res ! = AST_PBX_NO_HANGUP_PEER ) {
if ( ! chan - > _softhangup )
chan - > hangupcause = peer - > hangupcause ;
2004-05-07 21:14:55 +00:00
ast_hangup ( peer ) ;
2004-05-22 23:17:33 +00:00
}
1999-12-04 21:35:07 +00:00
}
out :
2004-06-21 18:28:35 +00:00
if ( moh ) {
moh = 0 ;
ast_moh_stop ( chan ) ;
} else if ( sentringing ) {
sentringing = 0 ;
ast_indicate ( chan , - 1 ) ;
}
2006-09-21 19:27:26 +00:00
ast_channel_early_bridge ( chan , NULL ) ;
1999-12-04 21:35:07 +00:00
hanguptree ( outgoing , NULL ) ;
2006-11-04 11:00:49 +00:00
pbx_builtin_setvar_helper ( chan , " DIALSTATUS " , pa . status ) ;
senddialendevent ( chan , pa . status ) ;
2006-01-17 18:54:56 +00:00
if ( option_debug )
2006-11-04 11:00:49 +00:00
ast_log ( LOG_DEBUG , " Exiting with DIALSTATUS=%s. \n " , pa . status ) ;
2004-06-23 03:16:58 +00:00
2005-11-03 21:40:36 +00:00
if ( ( ast_test_flag ( peerflags , OPT_GO_ON ) ) & & ( ! chan - > _softhangup ) & & ( res ! = AST_PBX_KEEPALIVE ) )
2006-01-17 18:54:56 +00:00
res = 0 ;
2006-04-19 14:02:49 +00:00
done :
2006-11-04 00:50:18 +00:00
ast_module_user_remove ( u ) ; /* XXX probably not the right place for this. */
1999-12-04 21:35:07 +00:00
return res ;
}
2005-01-18 03:12:53 +00:00
static int dial_exec ( struct ast_channel * chan , void * data )
{
struct ast_flags peerflags ;
2007-01-10 04:56:48 +00:00
int res = 0 ;
2005-01-18 03:12:53 +00:00
memset ( & peerflags , 0 , sizeof ( peerflags ) ) ;
2007-01-10 04:56:48 +00:00
res = dial_exec_full ( chan , data , & peerflags ) ;
return ( res > = 0 ? 0 : - 1 ) ;
2005-01-18 03:12:53 +00:00
}
static int retrydial_exec ( struct ast_channel * chan , void * data )
{
2005-12-20 17:52:31 +00:00
char * announce = NULL , * dialdata = NULL ;
const char * context = NULL ;
2006-04-19 14:02:49 +00:00
int sleep = 0 , loops = 0 , res = - 1 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u ;
2005-01-18 03:12:53 +00:00
struct ast_flags peerflags ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2005-10-19 18:19:02 +00:00
ast_log ( LOG_WARNING , " RetryDial requires an argument! \n " ) ;
return - 1 ;
}
2005-01-18 03:12:53 +00:00
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan ) ;
2005-10-19 18:19:02 +00:00
2006-05-10 13:22:15 +00:00
announce = ast_strdupa ( data ) ;
2006-04-19 14:02:49 +00:00
2005-10-19 18:19:02 +00:00
memset ( & peerflags , 0 , sizeof ( peerflags ) ) ;
2005-01-18 03:12:53 +00:00
if ( ( dialdata = strchr ( announce , ' | ' ) ) ) {
2006-04-19 14:02:49 +00:00
* dialdata + + = ' \0 ' ;
2005-01-18 03:12:53 +00:00
if ( ( sleep = atoi ( dialdata ) ) ) {
sleep * = 1000 ;
} else {
2005-02-09 22:59:46 +00:00
ast_log ( LOG_ERROR , " %s requires the numerical argument <sleep> \n " , rapp ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2005-01-18 03:12:53 +00:00
}
if ( ( dialdata = strchr ( dialdata , ' | ' ) ) ) {
2006-04-19 14:02:49 +00:00
* dialdata + + = ' \0 ' ;
2005-01-18 03:12:53 +00:00
if ( ! ( loops = atoi ( dialdata ) ) ) {
2005-02-09 22:59:46 +00:00
ast_log ( LOG_ERROR , " %s requires the numerical argument <loops> \n " , rapp ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2005-01-18 03:12:53 +00:00
}
}
}
if ( ( dialdata = strchr ( dialdata , ' | ' ) ) ) {
2006-04-19 14:02:49 +00:00
* dialdata + + = ' \0 ' ;
2005-01-18 03:12:53 +00:00
} else {
2005-02-09 22:59:46 +00:00
ast_log ( LOG_ERROR , " %s requires more arguments \n " , rapp ) ;
2006-04-19 14:02:49 +00:00
goto done ;
2005-01-18 03:12:53 +00:00
}
if ( sleep < 1000 )
sleep = 10000 ;
2006-04-19 14:02:49 +00:00
2005-01-18 03:12:53 +00:00
if ( ! loops )
2006-04-19 14:02:49 +00:00
loops = - 1 ; /* run forever */
2005-01-18 03:12:53 +00:00
context = pbx_builtin_getvar_helper ( chan , " EXITCONTEXT " ) ;
2006-04-19 14:02:49 +00:00
res = 0 ;
2005-01-18 03:12:53 +00:00
while ( loops ) {
chan - > data = " Retrying " ;
if ( ast_test_flag ( chan , AST_FLAG_MOH ) )
ast_moh_stop ( chan ) ;
2007-01-10 04:56:48 +00:00
res = dial_exec_full ( chan , dialdata , & peerflags ) ;
if ( res = = 1 ) {
break ;
} else if ( res = = 0 ) {
2005-11-03 21:40:36 +00:00
if ( ast_test_flag ( & peerflags , OPT_DTMF_EXIT ) ) {
2005-01-18 03:12:53 +00:00
if ( ! ( res = ast_streamfile ( chan , announce , chan - > language ) ) )
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
2005-01-20 22:59:50 +00:00
if ( ! res & & sleep ) {
2005-01-18 03:12:53 +00:00
if ( ! ast_test_flag ( chan , AST_FLAG_MOH ) )
2006-07-19 20:44:39 +00:00
ast_moh_start ( chan , NULL , NULL ) ;
2005-01-18 03:12:53 +00:00
res = ast_waitfordigit ( chan , sleep ) ;
}
} else {
if ( ! ( res = ast_streamfile ( chan , announce , chan - > language ) ) )
res = ast_waitstream ( chan , " " ) ;
2005-01-20 22:59:50 +00:00
if ( sleep ) {
if ( ! ast_test_flag ( chan , AST_FLAG_MOH ) )
2006-07-19 20:44:39 +00:00
ast_moh_start ( chan , NULL , NULL ) ;
2005-01-20 22:59:50 +00:00
if ( ! res )
2005-06-01 18:02:46 +00:00
res = ast_waitfordigit ( chan , sleep ) ;
2005-01-20 22:59:50 +00:00
}
2005-01-18 03:12:53 +00:00
}
}
if ( res < 0 )
break ;
else if ( res > 0 ) { /* Trying to send the call elsewhere (1 digit ext) */
2005-04-29 15:04:26 +00:00
if ( onedigit_goto ( chan , context , ( char ) res , 1 ) ) {
2005-01-18 03:12:53 +00:00
res = 0 ;
break ;
}
}
loops - - ;
}
2006-04-19 14:02:49 +00:00
if ( loops = = 0 )
res = 0 ;
2007-01-10 04:56:48 +00:00
else if ( res = = 1 )
res = 0 ;
2005-01-18 03:12:53 +00:00
if ( ast_test_flag ( chan , AST_FLAG_MOH ) )
ast_moh_stop ( chan ) ;
2007-01-10 04:56:48 +00:00
done :
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u ) ;
2006-04-19 14:02:49 +00:00
return res ;
2005-01-18 03:12:53 +00:00
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
1999-12-04 21:35:07 +00:00
{
2005-10-18 22:52:21 +00:00
int res ;
res = ast_unregister_application ( app ) ;
res | = ast_unregister_application ( rapp ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_hangup_all ( ) ;
2005-10-18 22:52:21 +00:00
return res ;
1999-12-04 21:35:07 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
1999-12-04 21:35:07 +00:00
{
2000-03-26 01:59:06 +00:00
int res ;
2005-10-18 22:52:21 +00:00
res = ast_register_application ( app , dial_exec , synopsis , descrip ) ;
res | = ast_register_application ( rapp , retrydial_exec , rsynopsis , rdescrip ) ;
2000-03-26 01:59:06 +00:00
return res ;
1999-12-04 21:35:07 +00:00
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Dialing Application " ) ;