dial() & retrydial() - Trivial application to dial a channel and send an URL on answer More...
#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include "asterisk/paths.h"
#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.h"
#include "asterisk/cdr.h"
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
Go to the source code of this file.
Data Structures | |
struct | cause_args |
struct | chanlist |
List of channel drivers. More... | |
struct | privacy_args |
Defines | |
#define | AST_MAX_WATCHERS 256 |
#define | CAN_EARLY_BRIDGE(flags, chan, peer) |
#define | DIAL_NOFORWARDHTML ((uint64_t)1 << 32) |
#define | DIAL_STILLGOING (1 << 31) |
#define | OPT_CALLEE_GO_ON ((uint64_t)1 << 35) |
#define | OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) |
#define | OPT_PEER_H ((uint64_t)1 << 34) |
#define | S_REPLACE(s, new_val) |
Enumerations | |
enum | { OPT_ARG_ANNOUNCE = 0, OPT_ARG_SENDDTMF, OPT_ARG_GOTO, OPT_ARG_DURATION_LIMIT, OPT_ARG_MUSICBACK, OPT_ARG_CALLEE_MACRO, OPT_ARG_CALLEE_GOSUB, OPT_ARG_CALLEE_GO_ON, OPT_ARG_PRIVACY, OPT_ARG_DURATION_STOP, OPT_ARG_OPERMODE, OPT_ARG_SCREEN_NOINTRO, OPT_ARG_ARRAY_SIZE } |
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_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), OPT_OPERMODE = (1 << 24), OPT_CALLEE_PARK = (1 << 25), OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_CALLEE_GOSUB = (1 << 28), OPT_CALLEE_MIXMONITOR = (1 << 29), OPT_CALLER_MIXMONITOR = (1 << 30) } |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | dial_exec (struct ast_channel *chan, void *data) |
static int | dial_exec_full (struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec) |
static void | do_forward (struct chanlist *o, struct cause_args *num, struct ast_flags64 *peerflags, int single) |
static int | do_timelimit (struct ast_channel *chan, struct ast_bridge_config *config, char *parse, struct timeval *calldurationlimit) |
static void | end_bridge_callback (void *data) |
static void | end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) |
static const char * | get_cid_name (char *name, int namelen, struct ast_channel *chan) |
static void | handle_cause (int cause, struct cause_args *num) |
static void | hanguptree (struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere) |
static int | load_module (void) |
static int | onedigit_goto (struct ast_channel *chan, const char *context, char exten, int pri) |
static void | replace_macro_delimiter (char *s) |
static int | retrydial_exec (struct ast_channel *chan, void *data) |
static void | senddialendevent (const struct ast_channel *src, const char *dialstatus) |
static void | senddialevent (struct ast_channel *src, struct ast_channel *dst, const char *dialstring) |
static int | setup_privacy_args (struct privacy_args *pa, struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan) |
returns 1 if successful, 0 or <0 if the caller should 'goto out' | |
static int | unload_module (void) |
static int | valid_priv_reply (struct ast_flags64 *opts, int res) |
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) |
Variables | |
static struct ast_module_info __MODULE_INFO_SECTION | __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialing Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } |
static char * | app = "Dial" |
static struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_app_option | dial_exec_options [128] = { [ 'A' ] = { .flag = OPT_ANNOUNCE , .arg_index = OPT_ARG_ANNOUNCE + 1 }, [ 'C' ] = { .flag = OPT_RESETCDR }, [ 'c' ] = { .flag = ((uint64_t)1 << 33) }, [ 'd' ] = { .flag = OPT_DTMF_EXIT }, [ 'D' ] = { .flag = OPT_SENDDTMF , .arg_index = OPT_ARG_SENDDTMF + 1 }, [ 'e' ] = { .flag = ((uint64_t)1 << 34) }, [ 'f' ] = { .flag = OPT_FORCECLID }, [ 'F' ] = { .flag = ((uint64_t)1 << 35) , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'g' ] = { .flag = OPT_GO_ON }, [ 'G' ] = { .flag = OPT_GOTO , .arg_index = OPT_ARG_GOTO + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_FORWARDING }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'm' ] = { .flag = OPT_MUSICBACK , .arg_index = OPT_ARG_MUSICBACK + 1 }, [ 'M' ] = { .flag = OPT_CALLEE_MACRO , .arg_index = OPT_ARG_CALLEE_MACRO + 1 }, [ 'n' ] = { .flag = OPT_SCREEN_NOINTRO , .arg_index = OPT_ARG_SCREEN_NOINTRO + 1 }, [ 'N' ] = { .flag = OPT_SCREEN_NOCLID }, [ 'o' ] = { .flag = OPT_ORIGINAL_CLID }, [ 'O' ] = { .flag = OPT_OPERMODE , .arg_index = OPT_ARG_OPERMODE + 1 }, [ 'p' ] = { .flag = OPT_SCREENING }, [ 'P' ] = { .flag = OPT_PRIVACY , .arg_index = OPT_ARG_PRIVACY + 1 }, [ 'r' ] = { .flag = OPT_RINGBACK }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'U' ] = { .flag = OPT_CALLEE_GOSUB , .arg_index = OPT_ARG_CALLEE_GOSUB + 1 }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_MIXMONITOR }, [ 'X' ] = { .flag = OPT_CALLER_MIXMONITOR }, } |
static char * | rapp = "RetryDial" |
dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
Definition in file app_dial.c.
#define AST_MAX_WATCHERS 256 |
Definition at line 597 of file app_dial.c.
Referenced by wait_for_answer().
#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 | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \ !chan->audiohooks && !peer->audiohooks)
Definition at line 560 of file app_dial.c.
Referenced by dial_exec_full(), do_forward(), and wait_for_answer().
#define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) |
Definition at line 502 of file app_dial.c.
Referenced by dial_exec_full(), and wait_for_answer().
#define DIAL_STILLGOING (1 << 31) |
Definition at line 501 of file app_dial.c.
Referenced by dial_exec_full(), do_forward(), and wait_for_answer().
#define OPT_CALLEE_GO_ON ((uint64_t)1 << 35) |
Definition at line 505 of file app_dial.c.
Referenced by dial_exec_full().
#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) |
Definition at line 503 of file app_dial.c.
Referenced by dial_exec_full().
#define OPT_PEER_H ((uint64_t)1 << 34) |
Definition at line 504 of file app_dial.c.
Referenced by dial_exec_full().
#define S_REPLACE | ( | s, | |
new_val | |||
) |
Definition at line 648 of file app_dial.c.
Referenced by dial_exec_full(), and do_forward().
anonymous enum |
Definition at line 507 of file app_dial.c.
{ OPT_ARG_ANNOUNCE = 0, OPT_ARG_SENDDTMF, OPT_ARG_GOTO, OPT_ARG_DURATION_LIMIT, OPT_ARG_MUSICBACK, OPT_ARG_CALLEE_MACRO, OPT_ARG_CALLEE_GOSUB, OPT_ARG_CALLEE_GO_ON, OPT_ARG_PRIVACY, OPT_ARG_DURATION_STOP, OPT_ARG_OPERMODE, OPT_ARG_SCREEN_NOINTRO, /* note: this entry _MUST_ be the last one in the enum */ OPT_ARG_ARRAY_SIZE, };
anonymous enum |
Definition at line 468 of file app_dial.c.
{ 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_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), OPT_OPERMODE = (1 << 24), OPT_CALLEE_PARK = (1 << 25), OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_CALLEE_GOSUB = (1 << 28), OPT_CALLEE_MIXMONITOR = (1 << 29), OPT_CALLER_MIXMONITOR = (1 << 30), };
static void __reg_module | ( | void | ) | [static] |
Definition at line 2506 of file app_dial.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 2506 of file app_dial.c.
static int dial_exec | ( | struct ast_channel * | chan, |
void * | data | ||
) | [static] |
Definition at line 2356 of file app_dial.c.
References dial_exec_full().
Referenced by load_module().
{ struct ast_flags64 peerflags; memset(&peerflags, 0, sizeof(peerflags)); return dial_exec_full(chan, data, &peerflags, NULL); }
static int dial_exec_full | ( | struct ast_channel * | chan, |
void * | data, | ||
struct ast_flags64 * | peerflags, | ||
int * | continue_exec | ||
) | [static] |
Definition at line 1529 of file app_dial.c.
References __ast_answer(), ast_channel::_state, ast_channel::accountcode, accountcode, ast_channel::adsicpe, ast_channel::appl, asprintf, AST_APP_ARG, ast_app_group_set_channel(), ast_app_parse_options64(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_call(), ast_calloc, ast_cause2str(), AST_CAUSE_INVALID_NUMBER_FORMAT, AST_CDR_FLAG_DIALED, ast_cdr_reset(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_inherit(), ast_channel_datastore_remove(), ast_channel_early_bridge(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_HANGUP, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, ast_copy_flags64, ast_copy_string(), ast_datastore_alloc(), ast_datastore_free(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_dtmf_stream(), ast_exists_extension(), AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_filedelete(), ast_fileexists(), AST_FLAG_ANSWERED_ELSEWHERE, AST_FLAG_END_DTMF_ONLY, AST_FLAG_IN_AUTOLOOP, AST_FRAME_CONTROL, AST_FRAME_DTMF_END, ast_free, ast_frfree, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_EXTENSION, ast_moh_start(), ast_moh_stop(), AST_OPTION_OPRMODE, ast_parseable_goto(), AST_PBX_INCOMPLETE, ast_pbx_run_args(), ast_pbx_start(), AST_PRIVACY_UNKNOWN, ast_read(), ast_request(), ast_rtp_make_compatible(), ast_sched_runq(), ast_sched_wait(), ast_senddigit(), ast_set2_flag, ast_set2_flag64, ast_set_callerid(), ast_set_flag, ast_set_flag64, ast_spawn_extension(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdup, ast_strdupa, ast_streamfile(), ast_string_field_set, ast_strlen_zero(), ast_test_flag, ast_test_flag64, ast_tvadd(), ast_tvnow(), ast_tvzero(), ast_verb, ast_waitfor_n(), CAN_EARLY_BRIDGE, cause, ast_channel::cdr, ast_channel::cdrflags, chanlist::chan, chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_ani2, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_pres, ast_callerid::cid_rdnis, ast_callerid::cid_tns, ast_callerid::cid_ton, ast_channel::context, ast_channel::data, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dial_exec_options, DIAL_NOFORWARDHTML, DIAL_STILLGOING, dialcontext, dialed_interface_info, do_timelimit(), end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, ast_bridge_config::end_sound, errno, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_flags64::flags, ast_frame::frametype, get_cid_name(), handle_cause(), ast_channel::hangupcause, hanguptree(), ast_datastore::inheritance, ast_dialed_interface::interface, ast_channel::language, ast_dialed_interface::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, oprmode::mode, moh, musicclass, ast_channel::musicclass, ast_channel::name, ast_channel::nativeformats, chanlist::next, ast_pbx_args::no_hangup_chan, OPT_ANNOUNCE, OPT_ARG_ANNOUNCE, OPT_ARG_ARRAY_SIZE, OPT_ARG_CALLEE_GO_ON, OPT_ARG_CALLEE_GOSUB, OPT_ARG_CALLEE_MACRO, OPT_ARG_DURATION_LIMIT, OPT_ARG_DURATION_STOP, OPT_ARG_GOTO, OPT_ARG_OPERMODE, OPT_ARG_PRIVACY, OPT_ARG_SCREEN_NOINTRO, OPT_ARG_SENDDTMF, OPT_CALLEE_GO_ON, OPT_CALLEE_GOSUB, OPT_CALLEE_HANGUP, OPT_CALLEE_MACRO, OPT_CALLEE_MIXMONITOR, OPT_CALLEE_MONITOR, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_HANGUP, OPT_CALLER_MIXMONITOR, OPT_CALLER_MONITOR, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_CANCEL_ELSEWHERE, OPT_DTMF_EXIT, OPT_DURATION_LIMIT, OPT_DURATION_STOP, OPT_FORCECLID, OPT_GO_ON, OPT_GOTO, OPT_IGNORE_FORWARDING, OPT_MUSICBACK, OPT_OPERMODE, OPT_ORIGINAL_CLID, OPT_PEER_H, OPT_PRIVACY, OPT_RESETCDR, OPT_RINGBACK, OPT_SCREEN_NOINTRO, OPT_SCREENING, OPT_SENDDTMF, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), oprmode::peer, ast_channel::priority, privacy_args::privdb_val, privacy_args::privintro, replace_macro_delimiter(), S_OR, S_REPLACE, ast_channel::sched, senddialendevent(), senddialevent(), privacy_args::sentringing, setup_privacy_args(), ast_bridge_config::start_sound, privacy_args::status, ast_channel::stream, strsep(), ast_frame::subclass, ast_channel::tech, ast_channel::timingfunc, ast_channel::transfercapability, url, ast_channel::visible_indication, wait_for_answer(), ast_bridge_config::warning_sound, and ast_channel::whentohangup.
Referenced by dial_exec(), and retrydial_exec().
{ int res = -1; /* default: error */ char *rest, *cur; /* scan the list of destinations */ struct chanlist *outgoing = NULL; /* list of destinations */ struct ast_channel *peer; int to; /* timeout */ struct cause_args num = { chan, 0, 0, 0 }; int cause; char numsubst[256]; char cidname[AST_MAX_EXTENSION] = ""; struct ast_bridge_config config = { { 0, } }; struct timeval calldurationlimit = { 0, }; char *dtmfcalled = NULL, *dtmfcalling = NULL; struct privacy_args pa = { .sentringing = 0, .privdb_val = 0, .status = "INVALIDARGS", }; int sentringing = 0, moh = 0; const char *outbound_group = NULL; int result = 0; char *parse; int opermode = 0; int delprivintro = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(peers); AST_APP_ARG(timeout); AST_APP_ARG(options); AST_APP_ARG(url); ); struct ast_flags64 opts = { 0, }; char *opt_args[OPT_ARG_ARRAY_SIZE]; struct ast_datastore *datastore = NULL; int fulldial = 0, num_dialed = 0; /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */ pbx_builtin_setvar_helper(chan, "DIALSTATUS", ""); pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", ""); pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ""); pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", ""); pbx_builtin_setvar_helper(chan, "DIALEDTIME", ""); if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); return -1; } parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); if (!ast_strlen_zero(args.options) && ast_app_parse_options64(dial_exec_options, &opts, opt_args, args.options)) { pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); goto done; } if (ast_strlen_zero(args.peers)) { ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); goto done; } if (ast_test_flag64(&opts, OPT_SCREEN_NOINTRO) && !ast_strlen_zero(opt_args[OPT_ARG_SCREEN_NOINTRO])) { delprivintro = atoi(opt_args[OPT_ARG_SCREEN_NOINTRO]); if (delprivintro < 0 || delprivintro > 1) { ast_log(LOG_WARNING, "Unknown argument %d specified to n option, ignoring\n", delprivintro); delprivintro = 0; } } if (ast_test_flag64(&opts, OPT_OPERMODE)) { opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]); ast_verb(3, "Setting operator services mode to %d.\n", opermode); } if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) { calldurationlimit.tv_sec = atoi(opt_args[OPT_ARG_DURATION_STOP]); if (!calldurationlimit.tv_sec) { ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); goto done; } ast_verb(3, "Setting call duration limit to %.3lf milliseconds.\n", calldurationlimit.tv_sec + calldurationlimit.tv_usec / 1000000.0); } if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) { dtmfcalling = opt_args[OPT_ARG_SENDDTMF]; dtmfcalled = strsep(&dtmfcalling, ":"); } if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { if (do_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) goto done; } if (ast_test_flag64(&opts, OPT_RESETCDR) && chan->cdr) ast_cdr_reset(chan->cdr, NULL); if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY])) opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten); if (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) { res = setup_privacy_args(&pa, &opts, opt_args, chan); if (res <= 0) goto out; res = -1; /* reset default */ } if (ast_test_flag64(&opts, OPT_DTMF_EXIT)) { __ast_answer(chan, 0, 0); } if (continue_exec) *continue_exec = 0; /* If a channel group has been specified, get it for use when we create peer channels */ ast_channel_lock(chan); if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) { outbound_group = ast_strdupa(outbound_group); pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL); } else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) { outbound_group = ast_strdupa(outbound_group); } ast_channel_unlock(chan); ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB); /* loop through the list of dial destinations */ rest = args.peers; while ((cur = strsep(&rest, "&")) ) { struct chanlist *tmp; struct ast_channel *tc; /* channel for this destination */ /* Get a technology/[device:]number pair */ char *number = cur; char *interface = ast_strdupa(number); char *tech = strsep(&number, "/"); /* find if we already dialed this interface */ struct ast_dialed_interface *di; AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; num_dialed++; if (ast_strlen_zero(number)) { ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n"); goto out; } if (!(tmp = ast_calloc(1, sizeof(*tmp)))) goto out; if (opts.flags) { ast_copy_flags64(tmp, &opts, OPT_CANCEL_ELSEWHERE | 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 | OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML); } ast_copy_string(numsubst, number, sizeof(numsubst)); /* Request the peer */ ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); ast_channel_unlock(chan); if (datastore) dialed_interfaces = datastore->data; else { if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n"); ast_free(tmp); goto out; } datastore->inheritance = DATASTORE_INHERIT_FOREVER; if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { ast_datastore_free(datastore); ast_free(tmp); goto out; } datastore->data = dialed_interfaces; AST_LIST_HEAD_INIT(dialed_interfaces); ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); } AST_LIST_LOCK(dialed_interfaces); AST_LIST_TRAVERSE(dialed_interfaces, di, list) { if (!strcasecmp(di->interface, interface)) { ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n", di->interface); break; } } AST_LIST_UNLOCK(dialed_interfaces); if (di) { fulldial++; ast_free(tmp); continue; } /* It is always ok to dial a Local interface. We only keep track of * which "real" interfaces have been dialed. The Local channel will * inherit this list so that if it ends up dialing a real interface, * it won't call one that has already been called. */ if (strcasecmp(tech, "Local")) { if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) { AST_LIST_UNLOCK(dialed_interfaces); ast_free(tmp); goto out; } strcpy(di->interface, interface); AST_LIST_LOCK(dialed_interfaces); AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); AST_LIST_UNLOCK(dialed_interfaces); } tc = ast_request(tech, chan->nativeformats, numsubst, &cause); if (!tc) { /* If we can't, just go on to the next call */ ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause)); handle_cause(cause, &num); if (!rest) /* we are on the last destination */ chan->hangupcause = cause; ast_free(tmp); continue; } pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); /* Setup outgoing SDP to match incoming one */ if (CAN_EARLY_BRIDGE(peerflags, chan, tc)) { ast_rtp_make_compatible(tc, chan, !outgoing && !rest); } /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(chan, tc); ast_channel_datastore_inherit(chan, tc); tc->appl = "AppDial"; tc->data = "(Outgoing Line)"; memset(&tc->whentohangup, 0, sizeof(tc->whentohangup)); 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)); 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->cid.cid_ani2 = chan->cid.cid_ani2; tc->adsicpe = chan->adsicpe; tc->transfercapability = chan->transfercapability; /* If we have an outbound group, set this peer channel to it */ if (outbound_group) ast_app_group_set_channel(tc, outbound_group); /* If the calling channel has the ANSWERED_ELSEWHERE flag set, inherit it. This is to support local channels */ if (ast_test_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE)) ast_set_flag(tc, AST_FLAG_ANSWERED_ELSEWHERE); /* Check if we're forced by configuration */ if (ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE)) ast_set_flag(tc, AST_FLAG_ANSWERED_ELSEWHERE); /* Inherit context and extension */ ast_string_field_set(tc, dialcontext, ast_strlen_zero(chan->macrocontext) ? chan->context : chan->macrocontext); if (!ast_strlen_zero(chan->macroexten)) ast_copy_string(tc->exten, chan->macroexten, sizeof(tc->exten)); else ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten)); res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */ /* Save the info in cdr's that we called them */ if (chan->cdr) ast_cdr_setdestchan(chan->cdr, tc->name); /* check the results of ast_call */ if (res) { /* Again, keep going even if there's an error */ ast_debug(1, "ast call on peer returned %d\n", res); ast_verb(3, "Couldn't call %s\n", numsubst); if (tc->hangupcause) { chan->hangupcause = tc->hangupcause; } ast_hangup(tc); tc = NULL; ast_free(tmp); continue; } else { senddialevent(chan, tc, numsubst); ast_verb(3, "Called %s\n", numsubst); if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL); } /* 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 */ ast_set_flag64(tmp, DIAL_STILLGOING); tmp->chan = tc; tmp->next = outgoing; outgoing = tmp; /* If this line is up, don't try anybody else */ if (outgoing->chan->_state == AST_STATE_UP) break; } if (ast_strlen_zero(args.timeout)) { to = -1; } else { to = atoi(args.timeout); if (to > 0) to *= 1000; else { ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout); to = -1; } } if (!outgoing) { strcpy(pa.status, "CHANUNAVAIL"); if (fulldial == num_dialed) { res = -1; goto out; } } else { /* Our status will at least be NOANSWER */ strcpy(pa.status, "NOANSWER"); if (ast_test_flag64(outgoing, OPT_MUSICBACK)) { moh = 1; 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); } ast_indicate(chan, AST_CONTROL_PROGRESS); } else if (ast_test_flag64(outgoing, OPT_RINGBACK)) { ast_indicate(chan, AST_CONTROL_RINGING); sentringing++; } } peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result); /* The ast_channel_datastore_remove() function could fail here if the * datastore was moved to another channel during a masquerade. If this is * the case, don't free the datastore here because later, when the channel * to which the datastore was moved hangs up, it will attempt to free this * datastore again, causing a crash */ if (!ast_channel_datastore_remove(chan, datastore)) ast_datastore_free(datastore); if (!peer) { if (result) { res = result; } else if (to) { /* Musta gotten hung up */ res = -1; } else { /* Nobody answered, next please? */ res = 0; } /* SIP, in particular, sends back this error code to indicate an * overlap dialled number needs more digits. */ if (chan->hangupcause == AST_CAUSE_INVALID_NUMBER_FORMAT) { res = AST_PBX_INCOMPLETE; } /* almost done, although the 'else' block is 400 lines */ } else { const char *number; strcpy(pa.status, "ANSWER"); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); /* 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, 1); outgoing = NULL; /* If appropriate, log that we have a destination channel */ if (chan->cdr) ast_cdr_setdestchan(chan->cdr, peer->name); if (peer->name) pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name); ast_channel_lock(peer); number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER"); if (!number) number = numsubst; pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number); ast_channel_unlock(peer); if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) { ast_debug(1, "app_dial: sendurl=%s.\n", args.url); ast_channel_sendurl( peer, args.url ); } if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) { if (do_privacy(chan, peer, &opts, opt_args, &pa)) { res = 0; goto out; } } if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) { res = 0; } else { int digit = 0; struct ast_channel *chans[2]; struct ast_channel *active_chan; chans[0] = chan; chans[1] = peer; /* we need to stream the announcment while monitoring the caller for a hangup */ /* stream the file */ res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language); if (res) { res = 0; ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]); } ast_set_flag(peer, AST_FLAG_END_DTMF_ONLY); while (peer->stream) { int ms; ms = ast_sched_wait(peer->sched); if (ms < 0 && !peer->timingfunc) { ast_stopstream(peer); break; } if (ms < 0) ms = 1000; active_chan = ast_waitfor_n(chans, 2, &ms); if (active_chan) { struct ast_frame *fr = ast_read(active_chan); if (!fr) { ast_hangup(peer); res = -1; goto done; } switch(fr->frametype) { case AST_FRAME_DTMF_END: digit = fr->subclass; if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) { ast_stopstream(peer); res = ast_senddigit(chan, digit, 0); } break; case AST_FRAME_CONTROL: switch (fr->subclass) { case AST_CONTROL_HANGUP: ast_frfree(fr); ast_hangup(peer); res = -1; goto done; default: break; } break; default: /* Ignore all others */ break; } ast_frfree(fr); } ast_sched_runq(peer->sched); } ast_clear_flag(peer, AST_FLAG_END_DTMF_ONLY); } if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) { /* chan and peer are going into the PBX, they both * should probably get CDR records. */ ast_clear_flag(chan->cdr, AST_CDR_FLAG_DIALED); ast_clear_flag(peer->cdr, AST_CDR_FLAG_DIALED); replace_macro_delimiter(opt_args[OPT_ARG_GOTO]); ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]); /* peer goes to the same context and extension as chan, so just copy info from chan*/ ast_copy_string(peer->context, chan->context, sizeof(peer->context)); ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten)); peer->priority = chan->priority + 2; ast_pbx_start(peer); hanguptree(outgoing, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0); if (continue_exec) *continue_exec = 1; res = 0; goto done; } if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) { struct ast_app *theapp; const char *macro_result; res = ast_autoservice_start(chan); if (res) { ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); res = -1; } theapp = pbx_findapp("Macro"); if (theapp && !res) { /* XXX why check res here ? */ /* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */ ast_copy_string(peer->context, chan->context, sizeof(peer->context)); ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten)); replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]); res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]); ast_debug(1, "Macro exited with status %d\n", res); res = 0; } else { ast_log(LOG_ERROR, "Could not find application Macro\n"); res = -1; } if (ast_autoservice_stop(chan) < 0) { res = -1; } ast_channel_lock(peer); if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) { char *macro_transfer_dest; if (!strcasecmp(macro_result, "BUSY")) { ast_copy_string(pa.status, macro_result, sizeof(pa.status)); ast_set_flag64(peerflags, OPT_GO_ON); res = -1; } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) { ast_copy_string(pa.status, macro_result, sizeof(pa.status)); ast_set_flag64(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_flag64(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_flag64(peerflags, OPT_GO_ON); } } } ast_channel_unlock(peer); } if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) { struct ast_app *theapp; const char *gosub_result; char *gosub_args, *gosub_argstart; int res9 = -1; res9 = ast_autoservice_start(chan); if (res9) { ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); res9 = -1; } theapp = pbx_findapp("Gosub"); if (theapp && !res9) { replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]); /* Set where we came from */ ast_copy_string(peer->context, "app_dial_gosub_virtual_context", sizeof(peer->context)); ast_copy_string(peer->exten, "s", sizeof(peer->exten)); peer->priority = 0; gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], ','); if (gosub_argstart) { *gosub_argstart = 0; if (asprintf(&gosub_args, "%s,s,1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], gosub_argstart + 1) < 0) { ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); gosub_args = NULL; } *gosub_argstart = ','; } else { if (asprintf(&gosub_args, "%s,s,1", opt_args[OPT_ARG_CALLEE_GOSUB]) < 0) { ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); gosub_args = NULL; } } if (gosub_args) { res9 = pbx_exec(peer, theapp, gosub_args); if (!res9) { struct ast_pbx_args args; /* A struct initializer fails to compile for this case ... */ memset(&args, 0, sizeof(args)); args.no_hangup_chan = 1; ast_pbx_run_args(peer, &args); } ast_free(gosub_args); ast_debug(1, "Gosub exited with status %d\n", res9); } else { ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); } } else if (!res9) { ast_log(LOG_ERROR, "Could not find application Gosub\n"); res9 = -1; } if (ast_autoservice_stop(chan) < 0) { ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); res9 = -1; } ast_channel_lock(peer); if (!res9 && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) { char *gosub_transfer_dest; if (!strcasecmp(gosub_result, "BUSY")) { ast_copy_string(pa.status, gosub_result, sizeof(pa.status)); ast_set_flag64(peerflags, OPT_GO_ON); res = -1; } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) { ast_copy_string(pa.status, gosub_result, sizeof(pa.status)); ast_set_flag64(peerflags, OPT_GO_ON); res = -1; } else if (!strcasecmp(gosub_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_flag64(peerflags, OPT_GO_ON); res = -1; } else if (!strcasecmp(gosub_result, "ABORT")) { /* Hangup both ends unless the caller has the g flag */ res = -1; } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) { res = -1; /* perform a transfer to a new extension */ if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/ replace_macro_delimiter(gosub_transfer_dest); if (!ast_parseable_goto(chan, gosub_transfer_dest)) ast_set_flag64(peerflags, OPT_GO_ON); } } } ast_channel_unlock(peer); } if (!res) { if (!ast_tvzero(calldurationlimit)) { struct timeval whentohangup = calldurationlimit; peer->whentohangup = ast_tvadd(ast_tvnow(), whentohangup); } if (!ast_strlen_zero(dtmfcalled)) { ast_verb(3, "Sending DTMF '%s' to the called party.\n", dtmfcalled); res = ast_dtmf_stream(peer, chan, dtmfcalled, 250, 0); } if (!ast_strlen_zero(dtmfcalling)) { ast_verb(3, "Sending DTMF '%s' to the calling party.\n", dtmfcalling); res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0); } } if (res) { /* some error */ res = -1; } else { if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER)) ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); if (ast_test_flag64(peerflags, OPT_CALLER_TRANSFER)) ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); if (ast_test_flag64(peerflags, OPT_CALLEE_HANGUP)) ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP)) ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); if (ast_test_flag64(peerflags, OPT_CALLEE_MONITOR)) ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); if (ast_test_flag64(peerflags, OPT_CALLER_MONITOR)) ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); if (ast_test_flag64(peerflags, OPT_CALLEE_PARK)) ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); if (ast_test_flag64(peerflags, OPT_CALLER_PARK)) ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR)) ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON); if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR)) ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON); if (ast_test_flag64(peerflags, OPT_GO_ON)) ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN); config.end_bridge_callback = end_bridge_callback; config.end_bridge_callback_data = chan; config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; if (moh) { moh = 0; ast_moh_stop(chan); } else if (sentringing) { sentringing = 0; ast_indicate(chan, -1); } /* Be sure no generators are left on it and reset the visible indication */ ast_deactivate_generator(chan); chan->visible_indication = 0; /* 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); res = -1; goto done; } if (opermode) { struct oprmode oprmode; oprmode.peer = peer; oprmode.mode = opermode; ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0); } res = ast_bridge_call(chan, peer, &config); } strcpy(peer->context, chan->context); if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) { int autoloopflag; int found; int res9; strcpy(peer->exten, "h"); peer->priority = 1; autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */ ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP); while ((res9 = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1)) == 0) peer->priority++; if (found && res9) { /* Something bad happened, or a hangup has been requested. */ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name); ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name); } ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP); /* set it back the way it was */ } if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) { replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]); ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]); ast_pbx_start(peer); } else { if (!ast_check_hangup(chan)) chan->hangupcause = peer->hangupcause; ast_hangup(peer); } } out: if (moh) { moh = 0; ast_moh_stop(chan); } else if (sentringing) { sentringing = 0; ast_indicate(chan, -1); } if (delprivintro && ast_fileexists(pa.privintro, NULL, NULL) > 0) { 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 { ast_verb(3, "Successfully deleted %s intro file\n", pa.privintro); } } ast_channel_early_bridge(chan, NULL); hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */ pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); senddialendevent(chan, pa.status); ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status); if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_INCOMPLETE)) { if (!ast_tvzero(calldurationlimit)) memset(&chan->whentohangup, 0, sizeof(chan->whentohangup)); res = 0; } done: if (config.warning_sound) { ast_free((char *)config.warning_sound); } if (config.end_sound) { ast_free((char *)config.end_sound); } if (config.start_sound) { ast_free((char *)config.start_sound); } return res; }
static void do_forward | ( | struct chanlist * | o, |
struct cause_args * | num, | ||
struct ast_flags64 * | peerflags, | ||
int | single | ||
) | [static] |
helper function for wait_for_answer()
XXX this code is highly suspicious, as it essentially overwrites the outgoing channel without properly deleting it.
Definition at line 714 of file app_dial.c.
References ast_channel::accountcode, accountcode, ast_call(), AST_CAUSE_BUSY, ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_unlock, ast_clear_flag64, ast_copy_string(), ast_hangup(), ast_indicate(), ast_log(), AST_MAX_EXTENSION, ast_request(), ast_rtp_make_compatible(), ast_set_callerid(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_test_flag64, ast_verb, ast_channel::call_forward, CAN_EARLY_BRIDGE, cause, ast_channel::cdrflags, cause_args::chan, chanlist::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, DIAL_STILLGOING, ast_channel::exten, get_cid_name(), handle_cause(), LOG_NOTICE, ast_channel::macroexten, ast_channel::name, ast_channel::nativeformats, cause_args::nochan, OPT_FORCECLID, OPT_IGNORE_FORWARDING, OPT_ORIGINAL_CLID, pbx_builtin_getvar_helper(), S_OR, S_REPLACE, senddialevent(), and ast_channel::tech.
Referenced by wait_for_answer().
{ 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 */ 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"); if (ast_strlen_zero(forward_context)) { forward_context = NULL; } 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); 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, "Forwarding failed to create channel to dial '%s/%s' (cause = %d)\n", tech, stuff, cause); } if (!c) { ast_clear_flag64(o, DIAL_STILLGOING); handle_cause(cause, num); ast_hangup(original); } else { char *new_cid_num, *new_cid_name; struct ast_channel *src; if (CAN_EARLY_BRIDGE(peerflags, c, in)) { ast_rtp_make_compatible(c, in, single); } if (ast_test_flag64(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, stuff, 0)) { ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", tech, stuff); ast_clear_flag64(o, DIAL_STILLGOING); ast_hangup(original); ast_hangup(c); c = o->chan = NULL; num->nochan++; } else { senddialevent(in, c, stuff); /* After calling, set callerid to extension */ if (!ast_test_flag64(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); } /* Hangup the original channel now, in case we needed it */ ast_hangup(original); } if (single) { ast_indicate(in, -1); } } }
static int do_timelimit | ( | struct ast_channel * | chan, |
struct ast_bridge_config * | config, | ||
char * | parse, | ||
struct timeval * | calldurationlimit | ||
) | [static] |
Definition at line 1157 of file app_dial.c.
References ast_channel_lock, ast_channel_unlock, AST_FEATURE_PLAY_WARNING, ast_log(), ast_set_flag, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_true(), ast_verb, ast_bridge_config::end_sound, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, pbx_builtin_getvar_helper(), ast_bridge_config::play_warning, S_OR, ast_bridge_config::start_sound, strsep(), ast_bridge_config::timelimit, var, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.
Referenced by dial_exec_full().
{ char *stringp = ast_strdupa(parse); char *limit_str, *warning_str, *warnfreq_str; const char *var; int play_to_caller = 0, play_to_callee = 0; int delta; limit_str = strsep(&stringp, ":"); warning_str = strsep(&stringp, ":"); warnfreq_str = strsep(&stringp, ":"); 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; } } ast_channel_lock(chan); 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 = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("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 = !ast_strlen_zero(var) ? ast_strdup(var) : NULL; var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE"); config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL; ast_channel_unlock(chan); /* undo effect of S(x) in case they are both used */ calldurationlimit->tv_sec = 0; calldurationlimit->tv_usec = 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->tv_sec = config->timelimit / 1000; calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000; ast_verb(3, "Setting call duration limit to %.3lf seconds.\n", calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0); config->timelimit = play_to_caller = play_to_callee = config->play_warning = config->warning_freq = 0; } else { ast_verb(3, "Limit Data for this call:\n"); ast_verb(4, "timelimit = %ld\n", config->timelimit); ast_verb(4, "play_warning = %ld\n", config->play_warning); ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no"); ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no"); ast_verb(4, "warning_freq = %ld\n", config->warning_freq); ast_verb(4, "start_sound = %s\n", S_OR(config->start_sound, "")); ast_verb(4, "warning_sound = %s\n", config->warning_sound); ast_verb(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; }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 1500 of file app_dial.c.
References ast_cdr::answer, ast_channel_lock, ast_channel_unlock, buf, ast_channel::cdr, ast_channel::data, pbx_builtin_setvar_helper(), and ast_cdr::start.
Referenced by dial_exec_full().
{ char buf[80]; time_t end; struct ast_channel *chan = data; if (!chan->cdr) { return; } time(&end); ast_channel_lock(chan); if (chan->cdr->answer.tv_sec) { snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec); pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); } if (chan->cdr->start.tv_sec) { snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec); pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); } ast_channel_unlock(chan); }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, |
struct ast_channel * | originator, | ||
struct ast_channel * | terminator | ||
) | [static] |
Definition at line 1525 of file app_dial.c.
References ast_bridge_config::end_bridge_callback_data.
Referenced by dial_exec_full().
{ bconfig->end_bridge_callback_data = originator; }
static const char* get_cid_name | ( | char * | name, |
int | namelen, | ||
struct ast_channel * | chan | ||
) | [static] |
Definition at line 674 of file app_dial.c.
References ast_get_hint(), ast_channel::context, context, ast_channel::exten, exten, ast_channel::macrocontext, ast_channel::macroexten, and S_OR.
Referenced by dial_exec_full(), and do_forward().
{ const char *context = S_OR(chan->macrocontext, chan->context); const char *exten = S_OR(chan->macroexten, chan->exten); return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : ""; }
static void handle_cause | ( | int | cause, |
struct cause_args * | num | ||
) | [static] |
Definition at line 609 of file app_dial.c.
References AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, AST_CAUSE_NO_ANSWER, AST_CAUSE_NO_ROUTE_DESTINATION, AST_CAUSE_NORMAL_CLEARING, AST_CAUSE_UNREGISTERED, ast_cdr_busy(), ast_cdr_failed(), ast_cdr_noanswer(), cause_args::busy, ast_channel::cdr, cause_args::chan, cause_args::congestion, and cause_args::nochan.
Referenced by dial_exec_full(), do_forward(), and wait_for_answer().
{ 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_NO_ANSWER: if (cdr) { ast_cdr_noanswer(cdr); } break; case AST_CAUSE_NORMAL_CLEARING: break; default: num->nochan++; break; } }
static void hanguptree | ( | struct chanlist * | outgoing, |
struct ast_channel * | exception, | ||
int | answered_elsewhere | ||
) | [static] |
Definition at line 576 of file app_dial.c.
References AST_CAUSE_ANSWERED_ELSEWHERE, AST_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_hangup(), ast_set_flag, chanlist::chan, ast_channel::hangupcause, and chanlist::next.
Referenced by dial_exec_full().
{ /* Hang up a tree of stuff */ struct chanlist *oo; while (outgoing) { /* 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; } ast_hangup(outgoing->chan); } oo = outgoing; outgoing = outgoing->next; ast_free(oo); } }
static int load_module | ( | void | ) | [static] |
Definition at line 2489 of file app_dial.c.
References ast_add_extension2(), ast_context_find_or_create(), ast_free_ptr, ast_log(), ast_register_application_xml, ast_strdup, dial_exec(), LOG_ERROR, and retrydial_exec().
{ int res; struct ast_context *con; con = ast_context_find_or_create(NULL, NULL, "app_dial_gosub_virtual_context", "app_dial"); if (!con) ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n"); else ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_dial"); res = ast_register_application_xml(app, dial_exec); res |= ast_register_application_xml(rapp, retrydial_exec); return res; }
static int onedigit_goto | ( | struct ast_channel * | chan, |
const char * | context, | ||
char | exten, | ||
int | pri | ||
) | [static] |
Definition at line 655 of file app_dial.c.
References ast_goto_if_exists(), ast_strlen_zero(), ast_channel::context, exten, and ast_channel::macrocontext.
Referenced by retrydial_exec(), and wait_for_answer().
{ char rexten[2] = { exten, '\0' }; if (context) { if (!ast_goto_if_exists(chan, context, rexten, pri)) return 1; } else { if (!ast_goto_if_exists(chan, chan->context, rexten, pri)) return 1; else if (!ast_strlen_zero(chan->macrocontext)) { if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri)) return 1; } } return 0; }
static void replace_macro_delimiter | ( | char * | s | ) | [static] |
static int retrydial_exec | ( | struct ast_channel * | chan, |
void * | data | ||
) | [static] |
Definition at line 2365 of file app_dial.c.
References AST_APP_ARG, ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_fileexists(), AST_FLAG_MOH, ast_log(), ast_moh_start(), ast_moh_stop(), AST_PBX_INCOMPLETE, AST_STANDARD_APP_ARGS, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_test_flag64, ast_waitfordigit(), ast_waitstream(), context, ast_channel::data, dial_exec_full(), ast_channel::language, LOG_ERROR, LOG_WARNING, onedigit_goto(), OPT_DTMF_EXIT, parse(), and pbx_builtin_getvar_helper().
Referenced by load_module().
{ char *parse; const char *context = NULL; int sleepms = 0, loops = 0, res = -1; struct ast_flags64 peerflags = { 0, }; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(announce); AST_APP_ARG(sleep); AST_APP_ARG(retries); AST_APP_ARG(dialdata); ); if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "RetryDial requires an argument!\n"); return -1; } parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); if (!ast_strlen_zero(args.sleep) && (sleepms = atoi(args.sleep))) sleepms *= 1000; if (!ast_strlen_zero(args.retries)) { loops = atoi(args.retries); } if (!args.dialdata) { ast_log(LOG_ERROR, "%s requires a 4th argument (dialdata)\n", rapp); goto done; } if (sleepms < 1000) sleepms = 10000; if (!loops) loops = -1; /* run forever */ ast_channel_lock(chan); context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT"); context = !ast_strlen_zero(context) ? ast_strdupa(context) : NULL; ast_channel_unlock(chan); res = 0; while (loops) { int continue_exec; chan->data = "Retrying"; if (ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_stop(chan); res = dial_exec_full(chan, args.dialdata, &peerflags, &continue_exec); if (continue_exec) break; if (res == 0) { if (ast_test_flag64(&peerflags, OPT_DTMF_EXIT)) { if (!ast_strlen_zero(args.announce)) { if (ast_fileexists(args.announce, NULL, chan->language) > 0) { if (!(res = ast_streamfile(chan, args.announce, chan->language))) ast_waitstream(chan, AST_DIGIT_ANY); } else ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce); } if (!res && sleepms) { if (!ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_start(chan, NULL, NULL); res = ast_waitfordigit(chan, sleepms); } } else { if (!ast_strlen_zero(args.announce)) { if (ast_fileexists(args.announce, NULL, chan->language) > 0) { if (!(res = ast_streamfile(chan, args.announce, chan->language))) res = ast_waitstream(chan, ""); } else ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce); } if (sleepms) { if (!ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_start(chan, NULL, NULL); if (!res) res = ast_waitfordigit(chan, sleepms); } } } if (res < 0 || res == AST_PBX_INCOMPLETE) { break; } else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */ if (onedigit_goto(chan, context, (char) res, 1)) { res = 0; break; } } loops--; } if (loops == 0) res = 0; else if (res == 1) res = 0; if (ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_stop(chan); done: return res; }
static void senddialendevent | ( | const struct ast_channel * | src, |
const char * | dialstatus | ||
) | [static] |
Definition at line 698 of file app_dial.c.
References EVENT_FLAG_CALL, manager_event, ast_channel::name, and ast_channel::uniqueid.
Referenced by dial_exec_full().
{ 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); }
static void senddialevent | ( | struct ast_channel * | src, |
struct ast_channel * | dst, | ||
const char * | dialstring | ||
) | [static] |
Definition at line 682 of file app_dial.c.
References ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, EVENT_FLAG_CALL, manager_event, ast_channel::name, S_OR, and ast_channel::uniqueid.
Referenced by dial_exec_full(), and do_forward().
{ 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 int setup_privacy_args | ( | struct privacy_args * | pa, |
struct ast_flags64 * | opts, | ||
char * | opt_args[], | ||
struct ast_channel * | chan | ||
) | [static] |
returns 1 if successful, 0 or <0 if the caller should 'goto out'
Definition at line 1399 of file app_dial.c.
References ast_answer(), ast_config_AST_DATA_DIR, ast_copy_string(), ast_dsp_get_threshold_from_settings(), ast_filedelete(), ast_fileexists(), ast_log(), ast_mkdir(), ast_play_and_record(), AST_PRIVACY_ALLOW, ast_privacy_check(), AST_PRIVACY_DENY, AST_PRIVACY_KILL, AST_PRIVACY_TORTURE, AST_PRIVACY_UNKNOWN, ast_shrink_phone_number(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag64, ast_verb, ast_waitstream(), ast_channel::cid, ast_callerid::cid_num, ast_channel::exten, ast_channel::language, LOG_NOTICE, LOG_WARNING, ast_channel::name, OPT_ARG_PRIVACY, OPT_PRIVACY, OPT_SCREEN_NOCLID, privacy_args::privcid, privacy_args::privdb_val, privacy_args::privintro, silencethreshold, privacy_args::status, and THRESHOLD_SILENCE.
Referenced by dial_exec_full().
{ char callerid[60]; int res; char *l; int silencethreshold; if (!ast_strlen_zero(chan->cid.cid_num)) { l = ast_strdupa(chan->cid.cid_num); ast_shrink_phone_number(l); if (ast_test_flag64(opts, OPT_PRIVACY) ) { ast_verb(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 { ast_verb(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 = '='; } ast_verb(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_flag64(opts, OPT_SCREEN_NOCLID)) { /* if callerid is set and OPT_SCREEN_NOCLID is set also */ ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid); pa->privdb_val = AST_PRIVACY_ALLOW; } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val); } if (pa->privdb_val == AST_PRIVACY_DENY) { ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status)); return 0; } else if (pa->privdb_val == AST_PRIVACY_KILL) { ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status)); return 0; /* Is this right? */ } else if (pa->privdb_val == AST_PRIVACY_TORTURE) { ast_copy_string(pa->status, "TORTURE", sizeof(pa->status)); 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 ((res = ast_mkdir(pa->privintro, 0755))) { ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res)); 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:" */ silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE); ast_answer(chan); res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "sln", &duration, silencethreshold, 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 ast_verb(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 */ }
static int unload_module | ( | void | ) | [static] |
Definition at line 2473 of file app_dial.c.
References ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), and ast_unregister_application().
{ int res; struct ast_context *con; res = ast_unregister_application(app); res |= ast_unregister_application(rapp); if ((con = ast_context_find("app_dial_gosub_virtual_context"))) { ast_context_remove_extension2(con, "s", 1, NULL, 0); ast_context_destroy(con, "app_dial"); /* leave nothing behind */ } return res; }
static int valid_priv_reply | ( | struct ast_flags64 * | opts, |
int | res | ||
) | [static] |
Definition at line 1146 of file app_dial.c.
References ast_test_flag64, OPT_PRIVACY, and OPT_SCREENING.
{ if (res < '1') return 0; if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5') return 1; if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4') return 1; return 0; }
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 | ||
) | [static, read] |
Definition at line 823 of file app_dial.c.
References ast_channel::_state, ast_cdr::answer, AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, AST_CAUSE_NORMAL_CLEARING, AST_CDR_ANSWERED, ast_cdr_failed(), ast_cdr_noanswer(), ast_channel_early_bridge(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendhtml(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag64, AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_copy_flags64, ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, AST_FRAME_HTML, AST_FRAME_IMAGE, AST_FRAME_TEXT, AST_FRAME_VOICE, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), AST_STATE_UP, ast_string_field_set, ast_strlen_zero(), ast_test_flag64, ast_tvnow(), ast_verb, ast_waitfor_n(), ast_write(), cause_args::busy, ast_channel::call_forward, CAN_EARLY_BRIDGE, ast_channel::cdr, chanlist::chan, cause_args::congestion, context, ast_frame::data, ast_frame::datalen, DIAL_NOFORWARDHTML, DIAL_STILLGOING, dialcontext, ast_cdr::disposition, do_forward(), ast_channel::exten, f, ast_frame::frametype, handle_cause(), ast_channel::hangupcause, LOG_WARNING, ast_channel::name, chanlist::next, cause_args::nochan, onedigit_goto(), OPT_CALLEE_HANGUP, OPT_CALLEE_MIXMONITOR, OPT_CALLEE_MONITOR, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_HANGUP, OPT_CALLER_MIXMONITOR, OPT_CALLER_MONITOR, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_DTMF_EXIT, OPT_MUSICBACK, OPT_RINGBACK, pbx_builtin_getvar_helper(), ast_frame::ptr, privacy_args::sentringing, privacy_args::status, ast_frame::subclass, and ast_frame::uint32.
Referenced by dial_exec_full().
{ 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 */ int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK); #ifdef HAVE_EPOLL struct chanlist *epollo; #endif if (single) { /* Turn off hold music, etc */ ast_deactivate_generator(in); /* If we are calling a single channel, make them compatible for in-band tone purpose */ if (ast_channel_make_compatible(outgoing->chan, in) < 0) { /* If these channels can not be made compatible, * there is no point in continuing. The bridge * will just fail if it gets that far. */ *to = -1; strcpy(pa->status, "CONGESTION"); ast_cdr_failed(in->cdr); return NULL; } } #ifdef HAVE_EPOLL for (epollo = outgoing; epollo; epollo = epollo->next) ast_poll_channel_add(in, epollo->chan); #endif while (*to && !peer) { struct chanlist *o; 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) { /* Keep track of important channels */ if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan) watchers[pos++] = o->chan; numlines++; } 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) strcpy(pa->status, "BUSY"); else if (num.congestion) strcpy(pa->status, "CONGESTION"); else if (num.nochan) strcpy(pa->status, "CHANUNAVAIL"); } 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); } *to = 0; return NULL; } 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) { if (!peer) { ast_verb(3, "%s answered %s\n", c->name, in->name); 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); 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); continue; } 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); 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); peer = c; 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); 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: ast_verb(3, "%s is busy\n", c->name); in->hangupcause = c->hangupcause; ast_hangup(c); c = o->chan = NULL; ast_clear_flag64(o, DIAL_STILLGOING); handle_cause(AST_CAUSE_BUSY, &num); break; case AST_CONTROL_CONGESTION: ast_verb(3, "%s is circuit-busy\n", c->name); in->hangupcause = c->hangupcause; ast_hangup(c); c = o->chan = NULL; ast_clear_flag64(o, DIAL_STILLGOING); handle_cause(AST_CAUSE_CONGESTION, &num); break; case AST_CONTROL_RINGING: ast_verb(3, "%s is ringing\n", c->name); /* Setup early media if appropriate */ if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) ast_channel_early_bridge(in, c); if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK)) { ast_indicate(in, AST_CONTROL_RINGING); pa->sentringing++; } break; case AST_CONTROL_PROGRESS: ast_verb(3, "%s is making progress passing it to %s\n", c->name, in->name); /* Setup early media if appropriate */ if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) ast_channel_early_bridge(in, c); if (!ast_test_flag64(outgoing, OPT_RINGBACK)) if (single || (!single && !pa->sentringing)) { ast_indicate(in, AST_CONTROL_PROGRESS); } break; case AST_CONTROL_VIDUPDATE: ast_verb(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_SRCUPDATE: ast_verb(3, "%s requested a source update, passing it to %s\n", c->name, in->name); ast_indicate(in, AST_CONTROL_SRCUPDATE); break; case AST_CONTROL_PROCEEDING: ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name); if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) ast_channel_early_bridge(in, c); if (!ast_test_flag64(outgoing, OPT_RINGBACK)) ast_indicate(in, AST_CONTROL_PROCEEDING); break; case AST_CONTROL_HOLD: ast_verb(3, "Call on %s placed on hold\n", c->name); ast_indicate(in, AST_CONTROL_HOLD); break; case AST_CONTROL_UNHOLD: ast_verb(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_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) { ast_verb(3, "%s stopped sounds\n", c->name); ast_indicate(in, -1); pa->sentringing = 0; } break; default: ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); } } else if (single) { switch (f->frametype) { case AST_FRAME_VOICE: case AST_FRAME_IMAGE: case AST_FRAME_TEXT: if (ast_write(in, f)) { ast_log(LOG_WARNING, "Unable to write frame\n"); } break; case AST_FRAME_HTML: if (!ast_test_flag64(outgoing, DIAL_NOFORWARDHTML) && ast_channel_sendhtml(in, f->subclass, f->data.ptr, f->datalen) == -1) { ast_log(LOG_WARNING, "Unable to send URL\n"); } break; default: break; } } ast_frfree(f); } /* end for */ if (winner == in) { struct ast_frame *f = ast_read(in); #if 0 if (f && (f->frametype != AST_FRAME_VOICE)) printf("Frame type: %d, %d\n", f->frametype, f->subclass); else if (!f || (f->frametype != AST_FRAME_VOICE)) printf("Hangup received on %s\n", in->name); #endif if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { /* Got hung up */ *to = -1; strcpy(pa->status, "CANCEL"); ast_cdr_noanswer(in->cdr); if (f) { if (f->data.uint32) { in->hangupcause = f->data.uint32; } ast_frfree(f); } return NULL; } /* now f is guaranteed non-NULL */ if (f->frametype == AST_FRAME_DTMF) { if (ast_test_flag64(peerflags, OPT_DTMF_EXIT)) { const char *context; ast_channel_lock(in); context = pbx_builtin_getvar_helper(in, "EXITCONTEXT"); if (onedigit_goto(in, context, (char) f->subclass, 1)) { ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); *to = 0; ast_cdr_noanswer(in->cdr); *result = f->subclass; strcpy(pa->status, "CANCEL"); ast_frfree(f); ast_channel_unlock(in); return NULL; } ast_channel_unlock(in); } if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) && (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */ ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); *to = 0; strcpy(pa->status, "CANCEL"); ast_cdr_noanswer(in->cdr); ast_frfree(f); return NULL; } } /* Forward HTML stuff */ if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML)) if (ast_channel_sendhtml(outgoing->chan, f->subclass, f->data.ptr, f->datalen) == -1) ast_log(LOG_WARNING, "Unable to send URL\n"); if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END))) { if (ast_write(outgoing->chan, f)) ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n"); } if (single && (f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) || (f->subclass == AST_CONTROL_VIDUPDATE) || (f->subclass == AST_CONTROL_SRCUPDATE))) { ast_verb(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.ptr, f->datalen); } ast_frfree(f); } if (!*to) ast_verb(3, "Nobody picked up in %d ms\n", orig); if (!*to || ast_check_hangup(in)) ast_cdr_noanswer(in->cdr); } #ifdef HAVE_EPOLL for (epollo = outgoing; epollo; epollo = epollo->next) { if (epollo->chan) ast_poll_channel_del(in, epollo->chan); } #endif return peer; }
struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialing Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static] |
Definition at line 2506 of file app_dial.c.
char* app = "Dial" [static] |
Definition at line 465 of file app_dial.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 2506 of file app_dial.c.
struct ast_app_option dial_exec_options[128] = { [ 'A' ] = { .flag = OPT_ANNOUNCE , .arg_index = OPT_ARG_ANNOUNCE + 1 }, [ 'C' ] = { .flag = OPT_RESETCDR }, [ 'c' ] = { .flag = ((uint64_t)1 << 33) }, [ 'd' ] = { .flag = OPT_DTMF_EXIT }, [ 'D' ] = { .flag = OPT_SENDDTMF , .arg_index = OPT_ARG_SENDDTMF + 1 }, [ 'e' ] = { .flag = ((uint64_t)1 << 34) }, [ 'f' ] = { .flag = OPT_FORCECLID }, [ 'F' ] = { .flag = ((uint64_t)1 << 35) , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'g' ] = { .flag = OPT_GO_ON }, [ 'G' ] = { .flag = OPT_GOTO , .arg_index = OPT_ARG_GOTO + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_FORWARDING }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'm' ] = { .flag = OPT_MUSICBACK , .arg_index = OPT_ARG_MUSICBACK + 1 }, [ 'M' ] = { .flag = OPT_CALLEE_MACRO , .arg_index = OPT_ARG_CALLEE_MACRO + 1 }, [ 'n' ] = { .flag = OPT_SCREEN_NOINTRO , .arg_index = OPT_ARG_SCREEN_NOINTRO + 1 }, [ 'N' ] = { .flag = OPT_SCREEN_NOCLID }, [ 'o' ] = { .flag = OPT_ORIGINAL_CLID }, [ 'O' ] = { .flag = OPT_OPERMODE , .arg_index = OPT_ARG_OPERMODE + 1 }, [ 'p' ] = { .flag = OPT_SCREENING }, [ 'P' ] = { .flag = OPT_PRIVACY , .arg_index = OPT_ARG_PRIVACY + 1 }, [ 'r' ] = { .flag = OPT_RINGBACK }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'U' ] = { .flag = OPT_CALLEE_GOSUB , .arg_index = OPT_ARG_CALLEE_GOSUB + 1 }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_MIXMONITOR }, [ 'X' ] = { .flag = OPT_CALLER_MIXMONITOR }, } [static] |
Definition at line 558 of file app_dial.c.
Referenced by dial_exec_full().
char* rapp = "RetryDial" [static] |
Definition at line 466 of file app_dial.c.