Skip to content
Snippets Groups Projects
app_dial.c 91.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2008, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * 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.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 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.
     */
    
    
     * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
    
     *
     * \author Mark Spencer <markster@digium.com>
    
    Russell Bryant's avatar
    Russell Bryant committed
     * \ingroup applications
    
    /*** MODULEINFO
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	<depend>chan_local</depend>
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    #include <sys/time.h>
    #include <sys/signal.h>
    
    #include <sys/stat.h>
    
    #include <netinet/in.h>
    
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    #include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
    
    #include "asterisk/lock.h"
    #include "asterisk/file.h"
    #include "asterisk/channel.h"
    #include "asterisk/pbx.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"
    
    #include "asterisk/rtp_engine.h"
    
    #include "asterisk/cdr.h"
    
    #include "asterisk/privacy.h"
    
    #include "asterisk/stringfields.h"
    
    #include "asterisk/global_datastores.h"
    
    /*** DOCUMENTATION
    	<application name="Dial" language="en_US">
    		<synopsis>
    			Attempt to connect to another device or endpoint and bridge the call.
    		</synopsis>
    		<syntax>
    			<parameter name="Technology/Resource" required="true" argsep="&amp;">
    				<argument name="Technology/Resource" required="true">
    					<para>Specification of the device(s) to dial.  These must be in the format of
    					<literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
    					represents a particular channel driver, and <replaceable>Resource</replaceable>
    					represents a resource available to that particular channel driver.</para>
    				</argument>
    				<argument name="Technology2/Resource2" required="false" multiple="true">
    					<para>Optional extra devices to dial in parallel</para>
    					<para>If you need more then one enter them as
    					Technology2/Resource2&amp;Technology3/Resourse3&amp;.....</para>
    				</argument>
    			</parameter>
    			<parameter name="timeout" required="false">
    				<para>Specifies the number of seconds we attempt to dial the specified devices</para>
    				<para>If not specified, this defaults to 136 years.</para>
    			</parameter>
    			<parameter name="options" required="false">
    			   <optionlist>
    				<option name="A">
    					<argument name="x" required="true">
    						<para>The file to play to the called party</para>
    					</argument>
    					<para>Play an announcement to the called party, where <replaceable>x</replaceable> is the prompt to be played</para>
    				</option>
    				<option name="C">
    					<para>Reset the call detail record (CDR) for this call.</para>
    				</option>
    				<option name="c">
    					<para>If the Dial() application cancels this call, always set the flag to tell the channel
    					driver that the call is answered elsewhere.</para>
    				</option>
    				<option name="d">
    					<para>Allow the calling user to dial a 1 digit extension while waiting for
    					a call to be answered. Exit to that extension if it exists in the
    					current context, or the context defined in the <variable>EXITCONTEXT</variable> variable,
    					if it exists.</para>
    				</option>
    				<option name="D" argsep=":">
    					<argument name="called" />
    					<argument name="calling" />
    
    					<argument name="progress" />
    
    					<para>Send the specified DTMF strings <emphasis>after</emphasis> the called
    					party has answered, but before the call gets bridged. The 
    					<replaceable>called</replaceable> DTMF string is sent to the called party, and the 
    					<replaceable>calling</replaceable> DTMF string is sent to the calling party. Both arguments 
    
    					can be used alone.  If <replaceable>progress</replaceable> is specified, its DTMF is sent
    					immediately after receiving a PROGRESS message.</para>
    
    				</option>
    				<option name="e">
    					<para>Execute the <literal>h</literal> extension for peer after the call ends</para>
    				</option>
    				<option name="f">
    					<para>Force the callerid of the <emphasis>calling</emphasis> channel to be set as the
    					extension associated with the channel using a dialplan <literal>hint</literal>.
    					For example, some PSTNs do not allow CallerID to be set to anything
    					other than the number assigned to the caller.</para>
    				</option>
    				<option name="F" argsep="^">
    					<argument name="context" required="false" />
    					<argument name="exten" required="false" />
    					<argument name="priority" required="true" />
    					<para>When the caller hangs up, transfer the called party
    					to the specified destination and continue execution at that location.</para>
    				</option>
    
    				<option name="F">
    					<para>Proceed with dialplan execution at the next priority in the current extension if the
    					source channel hangs up.</para>
    				</option>
    
    				<option name="g">
    					<para>Proceed with dialplan execution at the next priority in the current extension if the
    					destination channel hangs up.</para>
    				</option>
    				<option name="G" argsep="^">
    					<argument name="context" required="false" />
    					<argument name="exten" required="false" />
    					<argument name="priority" required="true" />
    					<para>If the call is answered, transfer the calling party to
    					the specified <replaceable>priority</replaceable> and the called party to the specified 
    					<replaceable>priority</replaceable> plus one.</para>
    					<note>
    						<para>You cannot use any additional action post answer options in conjunction with this option.</para>
    					</note>
    				</option>
    				<option name="h">
    					<para>Allow the called party to hang up by sending the <literal>*</literal> DTMF digit.</para>
    				</option>
    				<option name="H">
    					<para>Allow the calling party to hang up by hitting the <literal>*</literal> DTMF digit.</para>
    				</option>
    				<option name="i">
    					<para>Asterisk will ignore any forwarding requests it may receive on this dial attempt.</para>
    				</option>
    
    				<option name="I">
    					<para>Asterisk will ignore any connected line update requests or redirecting party update
    					requests it may receiveon this dial attempt.</para>
    				</option>
    
    				<option name="k">
    					<para>Allow the called party to enable parking of the call by sending
    					the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="K">
    					<para>Allow the calling party to enable parking of the call by sending
    					the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="L" argsep=":">
    					<argument name="x" required="true">
    						<para>Maximum call time, in milliseconds</para>
    					</argument>
    					<argument name="y">
    						<para>Warning time, in milliseconds</para>
    					</argument>
    					<argument name="z">
    						<para>Repeat time, in milliseconds</para>
    					</argument>
    					<para>Limit the call to <replaceable>x</replaceable> milliseconds. Play a warning when <replaceable>y</replaceable> milliseconds are
    					left. Repeat the warning every <replaceable>z</replaceable> milliseconds until time expires.</para>
    					<para>This option is affected by the following variables:</para>
    					<variablelist>
    						<variable name="LIMIT_PLAYAUDIO_CALLER">
    							<value name="yes" default="true" />
    							<value name="no" />
    							<para>If set, this variable causes Asterisk to play the prompts to the caller.</para>
    						</variable>
    						<variable name="LIMIT_PLAYAUDIO_CALLEE">
    							<value name="yes" />
    							<value name="no" default="true"/>
    							<para>If set, this variable causes Asterisk to play the prompts to the callee.</para>
    						</variable>
    						<variable name="LIMIT_TIMEOUT_FILE">
    							<value name="filename"/>
    							<para>If specified, <replaceable>filename</replaceable> specifies the sound prompt to play when the timeout is reached.
    							If not set, the time remaining will be announced.</para>
    						</variable>
    						<variable name="LIMIT_CONNECT_FILE">
    							<value name="filename"/>
    							<para>If specified, <replaceable>filename</replaceable> specifies the sound prompt to play when the call begins.
    							If not set, the time remaining will be announced.</para>
    						</variable>
    						<variable name="LIMIT_WARNING_FILE">
    							<value name="filename"/>
    							<para>If specified, <replaceable>filename</replaceable> specifies the sound prompt to play as
    							a warning when time <replaceable>x</replaceable> is reached. If not set, the time remaining will be announced.</para>
    						</variable>
    					</variablelist>
    				</option>
    				<option name="m">
    					<argument name="class" required="false"/>
    					<para>Provide hold music to the calling party until a requested
    					channel answers. A specific music on hold <replaceable>class</replaceable>
    					(as defined in <filename>musiconhold.conf</filename>) can be specified.</para>
    				</option>
    				<option name="M" argsep="^">
    					<argument name="macro" required="true">
    						<para>Name of the macro that should be executed.</para>
    					</argument>
    					<argument name="arg" multiple="true">
    						<para>Macro arguments</para>
    					</argument>
    					<para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel 
    					before connecting to the calling channel. Arguments can be specified to the Macro
    					using <literal>^</literal> as a delimiter. The macro can set the variable
    					<variable>MACRO_RESULT</variable> to specify the following actions after the macro is
    					finished executing:</para>
    					<variablelist>
    						<variable name="MACRO_RESULT">
    							<para>If set, this action will be taken after the macro finished executing.</para>
    							<value name="ABORT">
    								Hangup both legs of the call
    							</value>
    							<value name="CONGESTION">
    								Behave as if line congestion was encountered
    							</value>
    							<value name="BUSY">
    								Behave as if a busy signal was encountered
    							</value>
    							<value name="CONTINUE">
    								Hangup the called party and allow the calling party to continue dialplan execution at the next priority
    							</value>
    							<!-- TODO: Fix this syntax up, once we've figured out how to specify the GOTO syntax -->
    							<value name="GOTO:&lt;context&gt;^&lt;exten&gt;^&lt;priority&gt;">
    								Transfer the call to the specified destination.
    							</value>
    						</variable>
    					</variablelist>
    					<note>
    						<para>You cannot use any additional action post answer options in conjunction
    						with this option. Also, pbx services are not run on the peer (called) channel,
    						so you will not be able to set timeouts via the TIMEOUT() function in this macro.</para>
    					</note>
    				</option>
    				<option name="n">
    					<para>This option is a modifier for the call screening/privacy mode. (See the 
    					<literal>p</literal> and <literal>P</literal> options.) It specifies
    					that no introductions are to be saved in the <directory>priv-callerintros</directory>
    					directory.</para>
    				</option>
    				<option name="N">
    					<para>This option is a modifier for the call screening/privacy mode. It specifies
    					that if Caller*ID is present, do not screen the call.</para>
    				</option>
    				<option name="o">
    					<para>Specify that the Caller*ID that was present on the <emphasis>calling</emphasis> channel
    					be set as the Caller*ID on the <emphasis>called</emphasis> channel. This was the
    					behavior of Asterisk 1.0 and earlier.</para>
    				</option>
    				<option name="O">
    					<argument name="mode">
    						<para>With <replaceable>mode</replaceable> either not specified or set to <literal>1</literal>,
    						the originator hanging up will cause the phone to ring back immediately.</para>
    						<para>With <replaceable>mode</replaceable> set to <literal>2</literal>, when the operator 
    						flashes the trunk, it will ring their phone back.</para>
    					</argument>
    					<para>Enables <emphasis>operator services</emphasis> mode.  This option only
    					works when bridging a DAHDI channel to another DAHDI channel
    					only. if specified on non-DAHDI interfaces, it will be ignored.
    					When the destination answers (presumably an operator services
    					station), the originator no longer has control of their line.
    					They may hang up, but the switch will not release their line
    					until the destination party (the operator) hangs up.</para>
    				</option>
    				<option name="p">
    					<para>This option enables screening mode. This is basically Privacy mode
    					without memory.</para>
    				</option>
    				<option name="P">
    					<argument name="x" />
    					<para>Enable privacy mode. Use <replaceable>x</replaceable> as the family/key in the AstDB database if
    					it is provided. The current extension is used if a database family/key is not specified.</para>
    				</option>
    				<option name="r">
    					<para>Indicate ringing to the calling party, even if the called party isn't actually ringing. Pass no audio to the calling
    					party until the called channel has answered.</para>
    				</option>
    				<option name="S">
    					<argument name="x" required="true" />
    					<para>Hang up the call <replaceable>x</replaceable> seconds <emphasis>after</emphasis> the called party has
    					answered the call.</para>
    				</option>
    				<option name="t">
    					<para>Allow the called party to transfer the calling party by sending the
    					DTMF sequence defined in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="T">
    					<para>Allow the calling party to transfer the called party by sending the
    					DTMF sequence defined in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="U" argsep="^">
    					<argument name="x" required="true">
    						<para>Name of the subroutine to execute via Gosub</para>
    					</argument>
    					<argument name="arg" multiple="true" required="false">
    						<para>Arguments for the Gosub routine</para>
    					</argument>
    					<para>Execute via Gosub the routine <replaceable>x</replaceable> for the <emphasis>called</emphasis> channel before connecting
    					to the calling channel. Arguments can be specified to the Gosub
    					using <literal>^</literal> as a delimiter. The Gosub routine can set the variable
    					<variable>GOSUB_RESULT</variable> to specify the following actions after the Gosub returns.</para>
    					<variablelist>
    						<variable name="GOSUB_RESULT">
    							<value name="ABORT">
    								Hangup both legs of the call.
    							</value>
    							<value name="CONGESTION">
    								Behave as if line congestion was encountered.
    							</value>
    							<value name="BUSY">
    								Behave as if a busy signal was encountered.
    							</value>
    							<value name="CONTINUE">
    								Hangup the called party and allow the calling party
    								to continue dialplan execution at the next priority.
    							</value>
    							<!-- TODO: Fix this syntax up, once we've figured out how to specify the GOTO syntax -->
    							<value name="GOTO:&lt;context&gt;^&lt;exten&gt;^&lt;priority&gt;">
    								Transfer the call to the specified priority. Optionally, an extension, or
    								extension and priority can be specified.
    							</value>
    						</variable>
    					</variablelist>
    					<note>
    						<para>You cannot use any additional action post answer options in conjunction
    						with this option. Also, pbx services are not run on the peer (called) channel,
    						so you will not be able to set timeouts via the TIMEOUT() function in this routine.</para>
    					</note>
    				</option>
    				<option name="w">
    					<para>Allow the called party to enable recording of the call by sending
    					the DTMF sequence defined for one-touch recording in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="W">
    					<para>Allow the calling party to enable recording of the call by sending
    					the DTMF sequence defined for one-touch recording in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="x">
    					<para>Allow the called party to enable recording of the call by sending
    					the DTMF sequence defined for one-touch automixmonitor in <filename>features.conf</filename>.</para>
    				</option>
    				<option name="X">
    					<para>Allow the calling party to enable recording of the call by sending
    					the DTMF sequence defined for one-touch automixmonitor in <filename>features.conf</filename>.</para>
    				</option>
    				</optionlist>
    			</parameter>
    			<parameter name="URL">
    				<para>The optional URL will be sent to the called party if the channel driver supports it.</para>
    			</parameter>
    		</syntax>
    		<description>
    			<para>This application will place calls to one or more specified channels. As soon
    			as one of the requested channels answers, the originating channel will be
    			answered, if it has not already been answered. These two channels will then
    			be active in a bridged call. All other channels that were requested will then
    			be hung up.</para>
    
    			<para>Unless there is a timeout specified, the Dial application will wait
    			indefinitely until one of the called channels answers, the user hangs up, or
    			if all of the called channels are busy or unavailable. Dialplan executing will
    			continue if no requested channels can be called, or if the timeout expires.
    			This application will report normal termination if the originating channel
    			hangs up, or if the call is bridged and either of the parties in the bridge
    			ends the call.</para>
    			<para>If the <variable>OUTBOUND_GROUP</variable> variable is set, all peer channels created by this
    			application will be put into that group (as in Set(GROUP()=...).
    			If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this
    			application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,
    			however, the variable will be unset after use.</para>
    
    			<para>This application sets the following channel variables:</para>
    			<variablelist>
    				<variable name="DIALEDTIME">
    					<para>This is the time from dialing a channel until when it is disconnected.</para>
    				</variable>
    				<variable name="ANSWEREDTIME">
    					<para>This is the amount of time for actual call.</para>
    				</variable>
    				<variable name="DIALSTATUS">
    					<para>This is the status of the call</para>
    					<value name="CHANUNAVAIL" />
    					<value name="CONGESTION" />
    					<value name="NOANSWER" />
    					<value name="BUSY" />
    					<value name="ANSWER" />
    					<value name="CANCEL" />
    					<value name="DONTCALL">
    						For the Privacy and Screening Modes.
    						Will be set if the called party chooses to send the calling party to the 'Go Away' script.
    					</value>
    					<value name="TORTURE">
    						For the Privacy and Screening Modes.
    						Will be set if the called party chooses to send the calling party to the 'torture' script.
    					</value>
    					<value name="INVALIDARGS" />
    				</variable>
    			</variablelist>
    		</description>
    	</application>
    	<application name="RetryDial" language="en_US">
    		<synopsis>
    			Place a call, retrying on failure allowing an optional exit extension.
    		</synopsis>
    		<syntax>
    			<parameter name="announce" required="true">
    				<para>Filename of sound that will be played when no channel can be reached</para>
    			</parameter>
    			<parameter name="sleep" required="true">
    
    				<para>Number of seconds to wait after a dial attempt failed before a new attempt is made</para>
    
    			</parameter>
    			<parameter name="retries" required="true">
    				<para>Number of retries</para>
    				<para>When this is reached flow will continue at the next priority in the dialplan</para>
    			</parameter>
    			<parameter name="dialargs" required="true">
    				<para>Same format as arguments provided to the Dial application</para>
    			</parameter>
    		</syntax>
    		<description>
    			<para>This application will attempt to place a call using the normal Dial application.
    			If no channel can be reached, the <replaceable>announce</replaceable> file will be played.
    			Then, it will wait <replaceable>sleep</replaceable> number of seconds before retrying the call.
    			After <replaceable>retries</replaceable> number of attempts, the calling channel will continue at the next priority in the dialplan.
    			If the <replaceable>retries</replaceable> setting is set to 0, this application will retry endlessly.
    			While waiting to retry a call, a 1 digit extension may be dialed. If that
    			extension exists in either the context defined in <variable>EXITCONTEXT</variable> or the current
    			one, The call will jump to that extension immediately.
    			The <replaceable>dialargs</replaceable> are specified in the same format that arguments are provided
    			to the Dial application.</para>
    		</description>
    	</application>
     ***/
    
    static char *app = "Dial";
    
    static char *rapp = "RetryDial";
    
    
    enum {
    
    	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_DURATION_LIMIT =    (1 << 9),
    	OPT_MUSICBACK =         (1 << 10),
    	OPT_CALLEE_MACRO =      (1 << 11),
    	OPT_SCREEN_NOINTRO =    (1 << 12),
    
    	OPT_SCREEN_NOCALLERID = (1 << 13),
    	OPT_IGNORE_CONNECTEDLINE = (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),
    	OPT_OPERMODE =          (1 << 24),
    	OPT_CALLEE_PARK =       (1 << 25),
    	OPT_CALLER_PARK =       (1 << 26),
    
    	OPT_CALLEE_MIXMONITOR = (1 << 29),
    	OPT_CALLER_MIXMONITOR = (1 << 30),
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    #define DIAL_STILLGOING      (1 << 31)
    #define DIAL_NOFORWARDHTML   ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */
    
    #define DIAL_NOCONNECTEDLINE ((uint64_t)1 << 33)
    #define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 34)
    #define OPT_PEER_H           ((uint64_t)1 << 35)
    #define OPT_CALLEE_GO_ON     ((uint64_t)1 << 36)
    
    
    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,
    
    	/* note: this entry _MUST_ be the last one in the enum */
    	OPT_ARG_ARRAY_SIZE,
    
    AST_APP_OPTIONS(dial_exec_options, BEGIN_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_ARG('F', OPT_CALLEE_GO_ON, OPT_ARG_CALLEE_GO_ON),
    
    	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),
    
    	AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
    
    	AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
    
    	AST_APP_OPTION('K', OPT_CALLER_PARK),
    
    	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_NOCALLERID),
    
    	AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
    
    	AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE),
    
    	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_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB),
    
    	AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
    	AST_APP_OPTION('W', OPT_CALLER_MONITOR),
    
    	AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR),
    	AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR),
    
    #define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \
    
    	OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
    
    	OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK) && \
    	!chan->audiohooks && !peer->audiohooks)
    
    /*
     * The list of active channels
     */
    struct chanlist {
    	struct chanlist *next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *chan;
    
    	struct ast_party_connected_line connected;
    
    static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode);
    
    static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Hang up a tree of stuff */
    
    	struct chanlist *oo;
    
    	while (outgoing) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Hangup any existing lines we have open */
    
    		if (outgoing->chan && (outgoing->chan != exception)) {
    
    			if (answered_elsewhere) {
    				/* The flag is used for local channel inheritance and stuff */
    
    				ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
    
    				/* This is for the channel drivers */
    				outgoing->chan->hangupcause = AST_CAUSE_ANSWERED_ELSEWHERE;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(outgoing->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		oo = outgoing;
    
    /*
     * 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_NO_ROUTE_DESTINATION:
    
    	case AST_CAUSE_UNREGISTERED:
    		if (cdr)
    			ast_cdr_failed(cdr);
    		num->nochan++;
    		break;
    
    	case AST_CAUSE_NORMAL_CLEARING:
    		break;
    
    	default:
    		num->nochan++;
    		break;
    	}
    }
    
    /* free the buffer if allocated, and set the pointer to the second arg */
    #define S_REPLACE(s, new_val)		\
    	do {				\
    		if (s)			\
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri)
    
    		if (!ast_goto_if_exists(chan, context, rexten, pri))
    
    		if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
    
    		else if (!ast_strlen_zero(chan->macrocontext)) {
    
    			if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
    
    /* do not call with chan lock held */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
    
    	const char *context;
    	const char *exten;
    
    	ast_channel_lock(chan);
    	context = ast_strdupa(S_OR(chan->macrocontext, chan->context));
    	exten = ast_strdupa(S_OR(chan->macroexten, chan->exten));
    	ast_channel_unlock(chan);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
    
    Olle Johansson's avatar
    Olle Johansson committed
    static void senddialevent(struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	manager_event(EVENT_FLAG_CALL, "Dial",
    		"SubEvent: Begin\r\n"
    		"Channel: %s\r\n"
    		"Destination: %s\r\n"
    		"CallerIDNum: %s\r\n"
    		"CallerIDName: %s\r\n"
    		"UniqueID: %s\r\n"
    		"DestUniqueID: %s\r\n"
    		"Dialstring: %s\r\n",
    		src->name, dst->name, S_OR(src->cid.cid_num, "<unknown>"),
    		S_OR(src->cid.cid_name, "<unknown>"), src->uniqueid,
    		dst->uniqueid, dialstring ? dialstring : "");
    
    static void senddialendevent(const struct ast_channel *src, const char *dialstatus)
    {
    	manager_event(EVENT_FLAG_CALL, "Dial",
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		"SubEvent: End\r\n"
    		"Channel: %s\r\n"
    		"UniqueID: %s\r\n"
    		"DialStatus: %s\r\n",
    		src->name, src->uniqueid, dialstatus);
    }
    
    /*!
     * helper function for wait_for_answer()
     *
     * XXX this code is highly suspicious, as it essentially overwrites
     * the outgoing channel without properly deleting it.
     */
    
    static void do_forward(struct chanlist *o,
    
    	struct cause_args *num, struct ast_flags64 *peerflags, int single)
    
    {
    	char tmpchan[256];
    
    	struct ast_channel *original = o->chan;
    
    	struct ast_channel *c = o->chan; /* the winner */
    	struct ast_channel *in = num->chan; /* the input channel */
    
    	struct ast_party_redirecting *apr = &o->chan->redirecting;
    	struct ast_party_connected_line *apc = &o->chan->connected;
    
    	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;
    		ast_channel_lock(c);
    		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 */
    
    	ast_verb(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_flag64(peerflags, OPT_IGNORE_FORWARDING)) {
    		ast_verb(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) {
    			if (single)
    				ast_channel_make_compatible(o->chan, in);
    			ast_channel_inherit_variables(in, o->chan);
    			ast_channel_datastore_inherit(in, o->chan);
    		} else
    			ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		ast_clear_flag64(o, DIAL_STILLGOING);
    
    		handle_cause(cause, num);
    
    		if (single) {
    			ast_rtp_instance_early_bridge_make_compatible(c, in);
    		}
    
    
    		c->cdrflags = in->cdrflags;
    
    		ast_channel_set_redirecting(c, apr);
    		ast_channel_lock(c);
    		while (ast_channel_trylock(in)) {
    			CHANNEL_DEADLOCK_AVOIDANCE(c);
    		}
    		S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(original->cid.cid_rdnis, S_OR(in->macroexten, in->exten))));
    
    		c->cid.cid_tns = in->cid.cid_tns;
    
    
    		if (ast_test_flag64(o, OPT_FORCECLID)) {
    
    			S_REPLACE(c->cid.cid_num, ast_strdupa(S_OR(in->macroexten, in->exten)));
    			S_REPLACE(c->cid.cid_name, NULL);
    			ast_string_field_set(c, accountcode, c->accountcode);
    
    			ast_party_caller_copy(&c->cid, &in->cid);
    			ast_string_field_set(c, accountcode, in->accountcode);
    
    		ast_party_connected_line_copy(&c->connected, apc);
    
    		S_REPLACE(in->cid.cid_rdnis, ast_strdup(c->cid.cid_rdnis));
    		ast_channel_update_redirecting(in, apr);
    
    		ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE);
    
    		if (ast_call(c, tmpchan, 0)) {
    			ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			ast_clear_flag64(o, DIAL_STILLGOING);
    
    			c = o->chan = NULL;
    			num->nochan++;
    		} else {
    
    			ast_channel_lock(c);
    			while (ast_channel_trylock(in)) {
    				CHANNEL_DEADLOCK_AVOIDANCE(c);
    			}
    
    Olle Johansson's avatar
    Olle Johansson committed
    			senddialevent(in, c, stuff);
    
    			if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
    
    				char cidname[AST_MAX_EXTENSION] = "";
    
    				const char *tmpexten;
    				tmpexten = ast_strdupa(S_OR(in->macroexten, in->exten));
    				ast_channel_unlock(in);
    				ast_channel_unlock(c);
    				ast_set_callerid(c, tmpexten, get_cid_name(cidname, sizeof(cidname), in), NULL);
    			} else {
    				ast_channel_unlock(in);
    				ast_channel_unlock(c);
    
    			/* Hangup the original channel now, in case we needed it */
    
    		if (single) {
    			ast_indicate(in, -1);
    		}
    
    /* argument used for some functions. */
    struct privacy_args {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	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,
    
    	struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
    
    	const struct cause_args *num_in, int *result, char *dtmf_progress)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct cause_args num = *num_in;
    	int prestart = num.busy + num.congestion + num.nochan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int orig = *to;
    	struct ast_channel *peer = NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* single is set if only one destination is enabled */
    
    	int single = outgoing && !outgoing->next;
    
    #ifdef HAVE_EPOLL
    	struct chanlist *epollo;
    #endif
    
    	struct ast_party_connected_line connected_caller;
    
    	struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (single) {
    
    		/* Turn off hold music, etc */
    
    		if (!ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK))
    			ast_deactivate_generator(in);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If we are calling a single channel, make them compatible for in-band tone purpose */
    		ast_channel_make_compatible(outgoing->chan, in);
    
    
    		if (!ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE) && !ast_test_flag64(outgoing, DIAL_NOCONNECTEDLINE)) {
    			ast_channel_lock(outgoing->chan);
    			ast_connected_line_copy_from_caller(&connected_caller, &outgoing->chan->cid);
    			ast_channel_unlock(outgoing->chan);
    			connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
    			ast_channel_update_connected_line(in, &connected_caller);
    			ast_party_connected_line_free(&connected_caller);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    #ifdef HAVE_EPOLL
    	for (epollo = outgoing; epollo; epollo = epollo->next)
    		ast_poll_channel_add(in, epollo->chan);
    
    	while (*to && !peer) {
    
    		struct chanlist *o;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		int pos = 0; /* how many channels do we handle */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		struct ast_channel *winner;
    		struct ast_channel *watchers[AST_MAX_WATCHERS];
    
    
    		watchers[pos++] = in;
    		for (o = outgoing; o; o = o->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Keep track of important channels */
    
    			if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				watchers[pos++] = o->chan;
    			numlines++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (pos == 1) { /* only the input channel is available */
    
    			if (numlines == (num.busy + num.congestion + num.nochan)) {
    
    				ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
    
    				if (num.busy)
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					strcpy(pa->status, "BUSY");
    
    				else if (num.congestion)
    
    					strcpy(pa->status, "CONGESTION");
    
    				else if (num.nochan)
    
    					strcpy(pa->status, "CHANUNAVAIL");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    
    				ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*to = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		winner = ast_waitfor_n(watchers, pos, to);
    
    		for (o = outgoing; o; o = o->next) {
    			struct ast_frame *f;
    			struct ast_channel *c = o->chan;
    
    			if (c == NULL)
    				continue;
    
    			if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!peer) {
    
    					ast_verb(3, "%s answered %s\n", c->name, in->name);
    
    					if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) {
    						if (o->connected.id.number) {
    							ast_channel_update_connected_line(in, &o->connected);
    						} else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) {
    							ast_channel_lock(c);
    							ast_connected_line_copy_from_caller(&connected_caller, &c->cid);
    							ast_channel_unlock(c);
    							connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
    							ast_channel_update_connected_line(in, &connected_caller);
    							ast_party_connected_line_free(&connected_caller);
    						}
    					}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    						OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    						OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    						OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    						OPT_CALLEE_PARK | OPT_CALLER_PARK |
    						OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
    						DIAL_NOFORWARDHTML);
    
    					ast_string_field_set(c, dialcontext, "");
    
    					ast_copy_string(c->exten, "", sizeof(c->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			/* here, o->chan == c == winner */
    
    			if (!ast_strlen_zero(c->call_forward)) {
    
    				do_forward(o, &num, peerflags, single);
    
    				in->hangupcause = c->hangupcause;
    
    #ifdef HAVE_EPOLL
    				ast_poll_channel_del(in, c);
    #endif
    
    				ast_hangup(c);
    				c = o->chan = NULL;
    
    				ast_clear_flag64(o, DIAL_STILLGOING);
    
    				handle_cause(in->hangupcause, &num);
    
    				continue;
    			}
    			if (f->frametype == AST_FRAME_CONTROL) {
    				switch(f->subclass) {
    				case AST_CONTROL_ANSWER:
    					/* This is our guy if someone answered. */
    					if (!peer) {
    
    						ast_verb(3, "%s answered %s\n", c->name, in->name);
    
    						if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) {
    							if (o->connected.id.number) {
    								ast_channel_update_connected_line(in, &o->connected);
    							} else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) {
    								ast_channel_lock(c);
    								ast_connected_line_copy_from_caller(&connected_caller, &c->cid);
    								ast_channel_unlock(c);
    								connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
    								ast_channel_update_connected_line(in, &connected_caller);
    								ast_party_connected_line_free(&connected_caller);
    							}
    						}
    
    						if (peer->cdr) {
    							peer->cdr->answer = ast_tvnow();
    							peer->cdr->disposition = AST_CDR_ANSWERED;
    						}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    							OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    							OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    							OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    							OPT_CALLEE_PARK | OPT_CALLER_PARK |
    							OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
    							DIAL_NOFORWARDHTML);
    
    						ast_string_field_set(c, dialcontext, "");
    
    						ast_copy_string(c->exten, "", sizeof(c->exten));
    
    						if (CAN_EARLY_BRIDGE(peerflags, in, peer))
    
    							/* Setup early bridge if appropriate */
    							ast_channel_early_bridge(in, peer);
    
    					}
    					/* 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: