Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2008, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
*
* \author Mark Spencer <markster@digium.com>
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/time.h>
#include <sys/signal.h>
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
Kevin P. Fleming
committed
#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"
Kevin P. Fleming
committed
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*** 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="&">
<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&Technology3/Resourse3&.....</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>
Mark Michelson
committed
<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>
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
<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:<context>^<exten>^<priority>">
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:<context>^<exten>^<priority>">
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>
<option name="z">
<para>On a call forward, cancel any dial timeout which has been set for this call.</para>
</option>
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
</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 *rapp = "RetryDial";
Tilghman Lesher
committed
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),
Mark Michelson
committed
OPT_ORIGINAL_CLID = (1 << 8),
Tilghman Lesher
committed
OPT_DURATION_LIMIT = (1 << 9),
OPT_MUSICBACK = (1 << 10),
OPT_CALLEE_MACRO = (1 << 11),
OPT_SCREEN_NOINTRO = (1 << 12),
Mark Michelson
committed
OPT_SCREEN_NOCALLERID = (1 << 13),
OPT_IGNORE_CONNECTEDLINE = (1 << 14),
Tilghman Lesher
committed
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_IGNORE_FORWARDING = (1 << 27),
Tilghman Lesher
committed
OPT_CALLEE_GOSUB = (1 << 28),
OPT_CALLEE_MIXMONITOR = (1 << 29),
OPT_CALLER_MIXMONITOR = (1 << 30),
#define DIAL_STILLGOING (1 << 31)
#define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */
Mark Michelson
committed
#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)
#define OPT_CANCEL_TIMEOUT ((uint64_t)1 << 37)
enum {
OPT_ARG_ANNOUNCE = 0,
OPT_ARG_SENDDTMF,
OPT_ARG_GOTO,
OPT_ARG_DURATION_LIMIT,
OPT_ARG_MUSICBACK,
OPT_ARG_CALLEE_MACRO,
Steve Murphy
committed
OPT_ARG_CALLEE_GOSUB,
OPT_ARG_CALLEE_GO_ON,
OPT_ARG_PRIVACY,
OPT_ARG_DURATION_STOP,
OPT_ARG_OPERMODE,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE,
Russell Bryant
committed
AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
AST_APP_OPTION('C', OPT_RESETCDR),
Olle Johansson
committed
AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
AST_APP_OPTION('d', OPT_DTMF_EXIT),
AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
Steve Murphy
committed
AST_APP_OPTION('e', OPT_PEER_H),
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),
Mark Michelson
committed
AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
Steve Murphy
committed
AST_APP_OPTION('k', OPT_CALLEE_PARK),
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),
Mark Michelson
committed
AST_APP_OPTION('N', OPT_SCREEN_NOCALLERID),
Tilghman Lesher
committed
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),
Steve Murphy
committed
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),
AST_APP_OPTION('z', OPT_CANCEL_TIMEOUT),
Russell Bryant
committed
END_OPTIONS );
#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;
Steve Murphy
committed
uint64_t flags;
Mark Michelson
committed
struct ast_party_connected_line connected;
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode);
Olle Johansson
committed
static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
Olle Johansson
committed
if (outgoing->chan && (outgoing->chan != exception)) {
Olle Johansson
committed
if (answered_elsewhere) {
/* The flag is used for local channel inheritance and stuff */
Olle Johansson
committed
ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
Olle Johansson
committed
/* This is for the channel drivers */
outgoing->chan->hangupcause = AST_CAUSE_ANSWERED_ELSEWHERE;
}
Olle Johansson
committed
}
Tilghman Lesher
committed
outgoing = outgoing->next;
Tilghman Lesher
committed
ast_free(oo);
Mark Spencer
committed
#define AST_MAX_WATCHERS 256
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
/*
* 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) \
Tilghman Lesher
committed
ast_free(s); \
s = (new_val); \
} while (0)
static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri)
Kevin P. Fleming
committed
char rexten[2] = { exten, '\0' };
if (!ast_goto_if_exists(chan, context, rexten, pri))
if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
Kevin P. Fleming
committed
else if (!ast_strlen_zero(chan->macrocontext)) {
if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
return 1;
}
}
return 0;
}
Mark Spencer
committed
/* do not call with chan lock held */
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);
return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
static void senddialevent(struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
{
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",
"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, int *to)
Joshua Colp
committed
struct ast_channel *original = o->chan;
struct ast_channel *c = o->chan; /* the winner */
struct ast_channel *in = num->chan; /* the input channel */
Mark Michelson
committed
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);
ast_channel_unlock(c);
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);
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);
ast_hangup(original);
if (single) {
ast_rtp_instance_early_bridge_make_compatible(c, in);
}
Mark Michelson
committed
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)) {
Mark Michelson
committed
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);
Mark Michelson
committed
ast_party_caller_copy(&c->cid, &in->cid);
ast_string_field_set(c, accountcode, in->accountcode);
Mark Michelson
committed
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_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) {
*to = -1;
}
David Vossel
committed
ast_channel_unlock(in);
ast_channel_unlock(c);
if (ast_call(c, tmpchan, 0)) {
ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
Joshua Colp
committed
ast_hangup(original);
ast_hangup(c);
c = o->chan = NULL;
num->nochan++;
} else {
David Vossel
committed
ast_channel_lock(c);
while (ast_channel_trylock(in)) {
CHANNEL_DEADLOCK_AVOIDANCE(c);
}
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 */
Joshua Colp
committed
ast_hangup(original);
if (single) {
ast_indicate(in, -1);
}
/* 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,
struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
struct privacy_args *pa,
const struct cause_args *num_in, int *result, char *dtmf_progress)
struct cause_args num = *num_in;
int prestart = num.busy + num.congestion + num.nochan;
int orig = *to;
struct ast_channel *peer = NULL;
/* single is set if only one destination is enabled */
Mark Michelson
committed
int single = outgoing && !outgoing->next;
Joshua Colp
committed
#ifdef HAVE_EPOLL
struct chanlist *epollo;
#endif
Mark Michelson
committed
struct ast_party_connected_line connected_caller;
struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
/* Turn off hold music, etc */
Mark Michelson
committed
if (!ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK))
ast_deactivate_generator(in);
/* If we are calling a single channel, make them compatible for in-band tone purpose */
ast_channel_make_compatible(outgoing->chan, in);
Mark Michelson
committed
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);
}
Joshua Colp
committed
#ifdef HAVE_EPOLL
for (epollo = outgoing; epollo; epollo = epollo->next)
ast_poll_channel_add(in, epollo->chan);
int pos = 0; /* how many channels do we handle */
int numlines = prestart;
struct ast_channel *winner;
struct ast_channel *watchers[AST_MAX_WATCHERS];
watchers[pos++] = in;
for (o = outgoing; o; o = o->next) {
if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
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);
strcpy(pa->status, "CONGESTION");
strcpy(pa->status, "CHANUNAVAIL");
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
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) {
ast_verb(3, "%s answered %s\n", c->name, in->name);
Mark Michelson
committed
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);
}
}
peer = c;
ast_copy_flags64(peerflags, o,
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);
Kevin P. Fleming
committed
ast_string_field_set(c, dialcontext, "");
ast_copy_string(c->exten, "", sizeof(c->exten));
continue;
}
if (c != winner)
continue;
/* here, o->chan == c == winner */
if (!ast_strlen_zero(c->call_forward)) {
do_forward(o, &num, peerflags, single, to);
}
f = ast_read(winner);
if (!f) {
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);
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);
Mark Michelson
committed
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;
}
ast_copy_flags64(peerflags, o,
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);
Kevin P. Fleming
committed
ast_string_field_set(c, dialcontext, "");
ast_copy_string(c->exten, "", sizeof(c->exten));