Newer
Older
time(&end);
ast_channel_lock(chan);
if (ast_channel_cdr(chan)->answer.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->answer.tv_sec);
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
}
if (ast_channel_cdr(chan)->start.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->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) {
bconfig->end_bridge_callback_data = originator;
}
Alec L Davis
committed
static int dial_handle_playtones(struct ast_channel *chan, const char *data)
{
struct ast_tone_zone_sound *ts = NULL;
int res;
const char *str = data;
if (ast_strlen_zero(str)) {
ast_debug(1,"Nothing to play\n");
return -1;
}
ts = ast_get_indication_tone(ast_channel_zone(chan), str);
Alec L Davis
committed
if (ts && ts->data[0]) {
res = ast_playtones_start(chan, 0, ts->data, 0);
} else {
res = -1;
}
if (ts) {
ts = ast_tone_zone_sound_unref(ts);
}
if (res) {
ast_log(LOG_WARNING, "Unable to start playtone \'%s\'\n", str);
}
return res;
}
static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec)
int res = -1; /* default: error */
char *rest, *cur; /* scan the list of destinations */
Richard Mudgett
committed
struct dial_head out_chans = AST_LIST_HEAD_NOLOCK_INIT_VALUE; /* list of destinations */
struct chanlist *outgoing;
struct chanlist *tmp;
struct cause_args num = { chan, 0, 0, 0 };
Mark Spencer
committed
int cause;
Tilghman Lesher
committed
struct ast_bridge_config config = { { 0, } };
struct timeval calldurationlimit = { 0, };
char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL;
struct privacy_args pa = {
.sentringing = 0,
.privdb_val = 0,
int sentringing = 0, moh = 0;
const char *outbound_group = NULL;
char *parse;
int opermode = 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];
Jason Parker
committed
struct ast_datastore *datastore = NULL;
int fulldial = 0, num_dialed = 0;
int ignore_cc = 0;
char device_name[AST_CHANNEL_NAME];
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
char forced_clid_name[AST_MAX_EXTENSION];
char stored_clid_name[AST_MAX_EXTENSION];
int force_forwards_only; /*!< TRUE if force CallerID on call forward only. Legacy behaviour.*/
/*!
* \brief Forced CallerID party information to send.
* \note This will not have any malloced strings so do not free it.
*/
struct ast_party_id forced_clid;
/*!
* \brief Stored CallerID information if needed.
*
* \note If OPT_ORIGINAL_CLID set then this is the o option
* CallerID. Otherwise it is the dialplan extension and hint
* name.
*
* \note This will not have any malloced strings so do not free it.
*/
struct ast_party_id stored_clid;
/*!
* \brief CallerID party information to store.
* \note This will not have any malloced strings so do not free it.
*/
struct ast_party_caller caller;
/* 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)) {
Richard Mudgett
committed
ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
Russell Bryant
committed
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)) {
Richard Mudgett
committed
ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
goto done;
if (ast_cc_call_init(chan, &ignore_cc)) {
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;
}
}
Alec L Davis
committed
if (!ast_test_flag64(&opts, OPT_RINGBACK)) {
opt_args[OPT_ARG_RINGBACK] = NULL;
}
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 seconds.\n", calldurationlimit.tv_sec + calldurationlimit.tv_usec / 1000000.0);
if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
dtmf_progress = opt_args[OPT_ARG_SENDDTMF];
dtmfcalled = strsep(&dtmf_progress, ":");
dtmfcalling = strsep(&dtmf_progress, ":");
if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
if (ast_bridge_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
goto done;
/* Setup the forced CallerID information to send if used. */
ast_party_id_init(&forced_clid);
force_forwards_only = 0;
if (ast_test_flag64(&opts, OPT_FORCECLID)) {
if (ast_strlen_zero(opt_args[OPT_ARG_FORCECLID])) {
ast_channel_lock(chan);
forced_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
ast_channel_unlock(chan);
forced_clid_name[0] = '\0';
forced_clid.name.str = (char *) get_cid_name(forced_clid_name,
sizeof(forced_clid_name), chan);
force_forwards_only = 1;
} else {
/* Note: The opt_args[OPT_ARG_FORCECLID] string value is altered here. */
ast_callerid_parse(opt_args[OPT_ARG_FORCECLID], &forced_clid.name.str,
&forced_clid.number.str);
}
if (!ast_strlen_zero(forced_clid.name.str)) {
forced_clid.name.valid = 1;
}
if (!ast_strlen_zero(forced_clid.number.str)) {
forced_clid.number.valid = 1;
}
}
if (ast_test_flag64(&opts, OPT_FORCE_CID_TAG)
&& !ast_strlen_zero(opt_args[OPT_ARG_FORCE_CID_TAG])) {
forced_clid.tag = opt_args[OPT_ARG_FORCE_CID_TAG];
}
forced_clid.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
if (ast_test_flag64(&opts, OPT_FORCE_CID_PRES)
&& !ast_strlen_zero(opt_args[OPT_ARG_FORCE_CID_PRES])) {
int pres;
pres = ast_parse_caller_presentation(opt_args[OPT_ARG_FORCE_CID_PRES]);
if (0 <= pres) {
forced_clid.number.presentation = pres;
}
}
/* Setup the stored CallerID information if needed. */
ast_party_id_init(&stored_clid);
if (ast_test_flag64(&opts, OPT_ORIGINAL_CLID)) {
if (ast_strlen_zero(opt_args[OPT_ARG_ORIGINAL_CLID])) {
ast_channel_lock(chan);
ast_party_id_set_init(&stored_clid, &ast_channel_caller(chan)->id);
if (!ast_strlen_zero(ast_channel_caller(chan)->id.name.str)) {
stored_clid.name.str = ast_strdupa(ast_channel_caller(chan)->id.name.str);
if (!ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
stored_clid.number.str = ast_strdupa(ast_channel_caller(chan)->id.number.str);
if (!ast_strlen_zero(ast_channel_caller(chan)->id.subaddress.str)) {
stored_clid.subaddress.str = ast_strdupa(ast_channel_caller(chan)->id.subaddress.str);
if (!ast_strlen_zero(ast_channel_caller(chan)->id.tag)) {
stored_clid.tag = ast_strdupa(ast_channel_caller(chan)->id.tag);
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
}
ast_channel_unlock(chan);
} else {
/* Note: The opt_args[OPT_ARG_ORIGINAL_CLID] string value is altered here. */
ast_callerid_parse(opt_args[OPT_ARG_ORIGINAL_CLID], &stored_clid.name.str,
&stored_clid.number.str);
if (!ast_strlen_zero(stored_clid.name.str)) {
stored_clid.name.valid = 1;
}
if (!ast_strlen_zero(stored_clid.number.str)) {
stored_clid.number.valid = 1;
}
}
} else {
/*
* In case the new channel has no preset CallerID number by the
* channel driver, setup the dialplan extension and hint name.
*/
stored_clid_name[0] = '\0';
stored_clid.name.str = (char *) get_cid_name(stored_clid_name,
sizeof(stored_clid_name), chan);
if (ast_strlen_zero(stored_clid.name.str)) {
stored_clid.name.str = NULL;
} else {
stored_clid.name.valid = 1;
}
ast_channel_lock(chan);
stored_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
stored_clid.number.valid = 1;
ast_channel_unlock(chan);
}
if (ast_test_flag64(&opts, OPT_RESETCDR) && ast_channel_cdr(chan))
ast_cdr_reset(ast_channel_cdr(chan), NULL);
if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
opt_args[OPT_ARG_PRIVACY] = ast_strdupa(ast_channel_exten(chan));
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;
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"))) {
Richard Mudgett
committed
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);
Richard Mudgett
committed
ast_channel_unlock(chan);
/* Set per dial instance flags. These flags are also passed back to RetryDial. */
ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID
| OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CANCEL_TIMEOUT
| OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID);
Terry Wilson
committed
Richard Mudgett
committed
/* PREDIAL: Run gosub on the caller's channel */
if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER)
&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
Richard Mudgett
committed
}
/* loop through the list of dial destinations */
rest = args.peers;
while ((cur = strsep(&rest, "&")) ) {
struct ast_channel *tc; /* channel for this destination */
Richard Mudgett
committed
/* Get a technology/resource pair */
char *number = cur;
char *tech = strsep(&number, "/");
Richard Mudgett
committed
size_t tech_len;
size_t number_len;
/* find if we already dialed this interface */
struct ast_dialed_interface *di;
AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
Richard Mudgett
committed
if (ast_strlen_zero(number)) {
Richard Mudgett
committed
ast_log(LOG_WARNING, "Dial argument takes format (technology/resource)\n");
Richard Mudgett
committed
tech_len = strlen(tech) + 1;
number_len = strlen(number) + 1;
tmp = ast_calloc(1, sizeof(*tmp) + (2 * tech_len) + number_len);
if (!tmp) {
Richard Mudgett
committed
}
/* Save tech, number, and interface. */
cur = tmp->stuff;
strcpy(cur, tech);
tmp->tech = cur;
cur += tech_len;
strcpy(cur, tech);
cur[tech_len - 1] = '/';
tmp->interface = cur;
cur += tech_len;
strcpy(cur, number);
tmp->number = cur;
if (opts.flags) {
/* Set per outgoing call leg options. */
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 | OPT_IGNORE_CONNECTEDLINE);
ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
Richard Mudgett
committed
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
/*
* Seed the chanlist's connected line information with previously
* acquired connected line info from the incoming channel. The
* previously acquired connected line info could have been set
* through the CONNECTED_LINE dialplan function.
Mark Michelson
committed
*/
ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
ast_channel_unlock(chan);
if (datastore)
dialed_interfaces = datastore->data;
else {
Kevin P. Fleming
committed
if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
datastore->inheritance = DATASTORE_INHERIT_FOREVER;
if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
ast_datastore_free(datastore);
chanlist_free(tmp);
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) {
Richard Mudgett
committed
if (!strcasecmp(di->interface, tmp->interface)) {
ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
AST_LIST_UNLOCK(dialed_interfaces);
if (di) {
fulldial++;
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. */
Richard Mudgett
committed
if (strcasecmp(tmp->tech, "Local")) {
if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) {
Richard Mudgett
committed
strcpy(di->interface, tmp->interface);
AST_LIST_LOCK(dialed_interfaces);
AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
AST_LIST_UNLOCK(dialed_interfaces);
Richard Mudgett
committed
tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), chan, tmp->number, &cause);
if (!tc) {
ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
Richard Mudgett
committed
tmp->tech, cause, ast_cause2str(cause));
Richard Mudgett
committed
if (!rest) {
/* we are on the last destination */
ast_channel_hangupcause_set(chan, cause);
Richard Mudgett
committed
}
if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) {
Richard Mudgett
committed
if (!ast_cc_callback(chan, tmp->tech, tmp->number, ast_cc_busy_interface)) {
ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, "");
Richard Mudgett
committed
chanlist_free(tmp);
ast_channel_get_device_name(tc, device_name, sizeof(device_name));
if (!ignore_cc) {
Richard Mudgett
committed
ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, device_name);
Richard Mudgett
committed
pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", tmp->number);
ast_channel_lock_both(tc, chan);
/* Setup outgoing SDP to match incoming one */
Richard Mudgett
committed
if (!AST_LIST_FIRST(&out_chans) && !rest && CAN_EARLY_BRIDGE(peerflags, chan, tc)) {
/* We are on the only destination. */
ast_rtp_instance_early_bridge_make_compatible(tc, chan);
}
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables(chan, tc);
ast_channel_datastore_inherit(chan, tc);
ast_channel_appl_set(tc, "AppDial");
ast_channel_data_set(tc, "(Outgoing Line)");
memset(ast_channel_whentohangup(tc), 0, sizeof(*ast_channel_whentohangup(tc)));
/* Determine CallerID to store in outgoing channel. */
ast_party_caller_set_init(&caller, ast_channel_caller(tc));
if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
caller.id = stored_clid;
ast_channel_set_caller_event(tc, &caller, NULL);
ast_set_flag64(tmp, DIAL_CALLERID_ABSENT);
} else if (ast_strlen_zero(S_COR(ast_channel_caller(tc)->id.number.valid,
ast_channel_caller(tc)->id.number.str, NULL))) {
/*
* The new channel has no preset CallerID number by the channel
* driver. Use the dialplan extension and hint name.
*/
caller.id = stored_clid;
if (!caller.id.name.valid
&& !ast_strlen_zero(S_COR(ast_channel_connected(chan)->id.name.valid,
ast_channel_connected(chan)->id.name.str, NULL))) {
/*
* No hint name available. We have a connected name supplied by
* the dialplan we can use instead.
*/
caller.id.name = ast_channel_connected(chan)->id.name;
Mark Michelson
committed
}
ast_channel_set_caller_event(tc, &caller, NULL);
ast_set_flag64(tmp, DIAL_CALLERID_ABSENT);
} else if (ast_strlen_zero(S_COR(ast_channel_caller(tc)->id.name.valid, ast_channel_caller(tc)->id.name.str,
NULL))) {
/* The new channel has no preset CallerID name by the channel driver. */
if (!ast_strlen_zero(S_COR(ast_channel_connected(chan)->id.name.valid,
ast_channel_connected(chan)->id.name.str, NULL))) {
/*
* We have a connected name supplied by the dialplan we can
* use instead.
*/
caller.id.name = ast_channel_connected(chan)->id.name;
ast_channel_set_caller_event(tc, &caller, NULL);
}
Mark Michelson
committed
}
/* Determine CallerID for outgoing channel to send. */
if (ast_test_flag64(peerflags, OPT_FORCECLID) && !force_forwards_only) {
struct ast_party_connected_line connected;
ast_party_connected_line_set_init(&connected, ast_channel_connected(tc));
connected.id = forced_clid;
ast_channel_set_connected_line(tc, &connected, NULL);
ast_connected_line_copy_from_caller(ast_channel_connected(tc), ast_channel_caller(chan));
Mark Michelson
committed
ast_party_redirecting_copy(ast_channel_redirecting(tc), ast_channel_redirecting(chan));
Mark Michelson
committed
ast_channel_dialed(tc)->transit_network_select = ast_channel_dialed(chan)->transit_network_select;
Mark Michelson
committed
if (!ast_strlen_zero(ast_channel_accountcode(chan))) {
ast_channel_accountcode_set(tc, ast_channel_accountcode(chan));
if (ast_strlen_zero(ast_channel_musicclass(tc))) {
ast_channel_musicclass_set(tc, ast_channel_musicclass(chan));
Mark Michelson
committed
/* Pass ADSI CPE and transfer capability */
ast_channel_adsicpe_set(tc, ast_channel_adsicpe(chan));
ast_channel_transfercapability_set(tc, ast_channel_transfercapability(chan));
/* If we have an outbound group, set this peer channel to it */
if (outbound_group)
ast_app_group_set_channel(tc, outbound_group);
Olle Johansson
committed
/* If the calling channel has the ANSWERED_ELSEWHERE flag set, inherit it. This is to support local channels */
if (ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE)
ast_channel_hangupcause_set(tc, AST_CAUSE_ANSWERED_ELSEWHERE);
Olle Johansson
committed
/* Check if we're forced by configuration */
if (ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE))
ast_channel_hangupcause_set(tc, AST_CAUSE_ANSWERED_ELSEWHERE);
Olle Johansson
committed
/* Inherit context and extension */
ast_channel_dialcontext_set(tc, ast_strlen_zero(ast_channel_macrocontext(chan)) ? ast_channel_context(chan) : ast_channel_macrocontext(chan));
if (!ast_strlen_zero(ast_channel_macroexten(chan)))
ast_channel_exten_set(tc, ast_channel_macroexten(chan));
ast_channel_exten_set(tc, ast_channel_exten(chan));
ast_channel_unlock(tc);
Richard Mudgett
committed
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
/* Put channel in the list of outgoing thingies. */
tmp->chan = tc;
AST_LIST_INSERT_TAIL(&out_chans, tmp, node);
}
/*
* PREDIAL: Run gosub on all of the callee channels
*
* We run the callee predial before ast_call() in case the user
* wishes to do something on the newly created channels before
* the channel does anything important.
*
* Inside the target gosub we will be able to do something with
* the newly created channel name ie: now the calling channel
* can know what channel will be used to call the destination
* ex: now we will know that SIP/abc-123 is calling SIP/def-124
*/
if (ast_test_flag64(&opts, OPT_PREDIAL_CALLEE)
&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])
&& !AST_LIST_EMPTY(&out_chans)) {
const char *predial_callee;
Richard Mudgett
committed
ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
predial_callee = ast_app_expand_sub_args(chan, opt_args[OPT_ARG_PREDIAL_CALLEE]);
if (predial_callee) {
ast_autoservice_start(chan);
AST_LIST_TRAVERSE(&out_chans, tmp, node) {
ast_pre_call(tmp->chan, predial_callee);
}
ast_autoservice_stop(chan);
ast_free((char *) predial_callee);
Richard Mudgett
committed
}
}
/* Start all outgoing calls */
AST_LIST_TRAVERSE_SAFE_BEGIN(&out_chans, tmp, node) {
res = ast_call(tmp->chan, tmp->number, 0); /* Place the call, but don't wait on the answer */
Martin Pycko
committed
/* Save the info in cdr's that we called them */
if (ast_channel_cdr(chan))
Richard Mudgett
committed
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(tmp->chan));
Martin Pycko
committed
if (res) {
/* Again, keep going even if there's an error */
ast_debug(1, "ast call on peer returned %d\n", res);
Richard Mudgett
committed
ast_verb(3, "Couldn't call %s\n", tmp->interface);
if (ast_channel_hangupcause(tmp->chan)) {
ast_channel_hangupcause_set(chan, ast_channel_hangupcause(tmp->chan));
Mark Michelson
committed
ast_channel_unlock(chan);
Richard Mudgett
committed
ast_cc_call_failed(chan, tmp->chan, tmp->interface);
ast_hangup(tmp->chan);
tmp->chan = NULL;
AST_LIST_REMOVE_CURRENT(node);
Mark Spencer
committed
}
Richard Mudgett
committed
senddialevent(chan, tmp->chan, tmp->number);
ast_channel_unlock(chan);
ast_verb(3, "Called %s\n", tmp->interface);
Richard Mudgett
committed
Richard Mudgett
committed
if (ast_channel_state(tmp->chan) == AST_STATE_UP) {
Richard Mudgett
committed
}
Richard Mudgett
committed
AST_LIST_TRAVERSE_SAFE_END;
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;
}
Richard Mudgett
committed
outgoing = AST_LIST_FIRST(&out_chans);
if (!outgoing) {
strcpy(pa.status, "CHANUNAVAIL");
Tilghman Lesher
committed
if (fulldial == num_dialed) {
res = -1;
goto out;
}
} else {
Mark Spencer
committed
/* Our status will at least be NOANSWER */
strcpy(pa.status, "NOANSWER");
if (ast_test_flag64(outgoing, OPT_MUSICBACK)) {
Kevin P. Fleming
committed
moh = 1;
if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
char *original_moh = ast_strdupa(ast_channel_musicclass(chan));
ast_channel_musicclass_set(chan, opt_args[OPT_ARG_MUSICBACK]);
ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
ast_channel_musicclass_set(chan, original_moh);
} else {
ast_moh_start(chan, NULL, NULL);
}
ast_indicate(chan, AST_CONTROL_PROGRESS);
} else if (ast_test_flag64(outgoing, OPT_RINGBACK)) {
Alec L Davis
committed
if (!ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
if (dial_handle_playtones(chan, opt_args[OPT_ARG_RINGBACK])){
ast_indicate(chan, AST_CONTROL_RINGING);
sentringing++;
} else {
ast_indicate(chan, AST_CONTROL_PROGRESS);
}
} else {
ast_indicate(chan, AST_CONTROL_RINGING);
sentringing++;
}
Richard Mudgett
committed
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
dtmf_progress, ignore_cc, &forced_clid, &stored_clid);
/* 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
*/
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* make sure we weren't cleaned up already */
if (datastore && !ast_channel_datastore_remove(chan, datastore)) {
ast_datastore_free(datastore);
}
ast_channel_unlock(chan);
if (result) {
res = result;
} else if (to) { /* Musta gotten hung up */
} else { /* Nobody answered, next please? */
res = 0;
}
} else {
Matthew Nicholson
committed
if (ast_test_flag64(&opts, OPT_CALLER_ANSWER))
ast_answer(chan);
strcpy(pa.status, "ANSWER");
Terry Wilson
committed
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
Richard Mudgett
committed
hanguptree(&out_chans, peer, 1);
Matthew Nicholson
committed
/* If appropriate, log that we have a destination channel and set the answer time */
if (ast_channel_cdr(chan)) {
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
ast_cdr_setanswer(ast_channel_cdr(chan), ast_channel_cdr(peer)->answer);
Matthew Nicholson
committed
}
if (ast_channel_name(peer))
pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
ast_channel_lock(peer);
Richard Mudgett
committed
number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
if (ast_strlen_zero(number)) {
number = NULL;
} else {
number = ast_strdupa(number);
}
ast_channel_unlock(peer);
Richard Mudgett
committed
pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
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)) {
if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
res = 0;
} else {
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], ast_channel_language(peer));
if (res) {
res = 0;
ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]);
ast_set_flag(ast_channel_flags(peer), AST_FLAG_END_DTMF_ONLY);
while (ast_channel_stream(peer)) {
ms = ast_sched_wait(ast_channel_sched(peer));
if (ms < 0 && !ast_channel_timingfunc(peer)) {
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) {
Richard Mudgett
committed
ast_autoservice_chan_hangup_peer(chan, peer);
res = -1;
goto done;
}
switch(fr->frametype) {
case AST_FRAME_DTMF_END:
digit = fr->subclass.integer;
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.integer) {
case AST_CONTROL_HANGUP:
ast_frfree(fr);
Richard Mudgett
committed
ast_autoservice_chan_hangup_peer(chan, peer);
res = -1;
goto done;
default:
break;
}
break;
default:
/* Ignore all others */
break;
}
ast_frfree(fr);
}
ast_sched_runq(ast_channel_sched(peer));
ast_clear_flag(ast_channel_flags(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(ast_channel_cdr(chan), AST_CDR_FLAG_DIALED);
ast_clear_flag(ast_channel_cdr(peer), AST_CDR_FLAG_DIALED);
ast_replace_subargument_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_channel_context_set(peer, ast_channel_context(chan));
ast_channel_exten_set(peer, ast_channel_exten(chan));
ast_channel_priority_set(peer, ast_channel_priority(chan) + 2);
Richard Mudgett
committed
if (ast_pbx_start(peer)) {
Richard Mudgett
committed
ast_autoservice_chan_hangup_peer(chan, peer);
Richard Mudgett
committed
}
Richard Mudgett
committed
hanguptree(&out_chans, 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])) {
const char *macro_result_peer;
/* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */
ast_channel_lock_both(chan, peer);
ast_channel_context_set(peer, ast_channel_context(chan));
ast_channel_exten_set(peer, ast_channel_exten(chan));
ast_channel_unlock(peer);
ast_channel_unlock(chan);
ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
ast_channel_lock(peer);
if (!res && (macro_result_peer = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
char *macro_result = ast_strdupa(macro_result_peer);
char *macro_transfer_dest;
ast_channel_unlock(peer);
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));
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.
*/
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 = macro_result + 5;
res = -1;
/* perform a transfer to a new extension */
if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
ast_replace_subargument_delimiter(macro_transfer_dest);
if (!ast_parseable_goto(chan, macro_transfer_dest))
ast_set_flag64(peerflags, OPT_GO_ON);
} else {
ast_channel_unlock(peer);
if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
const char *gosub_result_peer;
char *gosub_argstart;
char *gosub_args = NULL;
Steve Murphy
committed
ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], ',');
if (gosub_argstart) {
const char *what_is_s = "s";
*gosub_argstart = 0;
if (!ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
what_is_s = "~~s~~";
if (asprintf(&gosub_args, "%s,%s,1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s, gosub_argstart + 1) < 0) {
ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
gosub_args = NULL;
}
*gosub_argstart = ',';
} else {
const char *what_is_s = "s";
if (!ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
what_is_s = "~~s~~";
}
if (asprintf(&gosub_args, "%s,%s,1", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s) < 0) {
ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
gosub_args = NULL;
Mark Michelson
committed
}
Steve Murphy
committed
}
if (gosub_args) {
res9 = ast_app_exec_sub(chan, peer, gosub_args, 0);
ast_free(gosub_args);
} else {
ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
Steve Murphy
committed
}
ast_channel_lock_both(chan, peer);
if (!res9 && (gosub_result_peer = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
Steve Murphy
committed
char *gosub_transfer_dest;
char *gosub_result = ast_strdupa(gosub_result_peer);
const char *gosub_retval = pbx_builtin_getvar_helper(peer, "GOSUB_RETVAL");
/* Inherit return value from the peer, so it can be used in the master */
if (gosub_retval) {
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", gosub_retval);
}
Steve Murphy
committed
ast_channel_unlock(peer);
ast_channel_unlock(chan);
Steve Murphy
committed
if (!strcasecmp(gosub_result, "BUSY")) {
ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
ast_set_flag64(peerflags, OPT_GO_ON);
res = -1;
Steve Murphy
committed
} else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
res = -1;
Steve Murphy
committed
} else if (!strcasecmp(gosub_result, "CONTINUE")) {
/* Hangup peer and continue with the next extension priority. */
res = -1;
Steve Murphy
committed
} 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 = gosub_result + 5;
res = -1;
Steve Murphy
committed
/* perform a transfer to a new extension */
if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
ast_replace_subargument_delimiter(gosub_transfer_dest);
Steve Murphy
committed
if (!ast_parseable_goto(chan, gosub_transfer_dest))
ast_set_flag64(peerflags, OPT_GO_ON);
Steve Murphy
committed
}
}
} else {
ast_channel_unlock(peer);
ast_channel_unlock(chan);
Steve Murphy
committed
}
}
if (!ast_tvzero(calldurationlimit)) {
struct timeval whentohangup = ast_tvadd(ast_tvnow(), calldurationlimit);
ast_channel_whentohangup_set(peer, &whentohangup);
ast_verb(3, "Sending DTMF '%s' to the called party.\n", dtmfcalled);
Tilghman Lesher
committed
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);
Tilghman Lesher
committed
res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0);
Terry Wilson
committed
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))