Newer
Older
default: /* bad input or -1 if failure to start autoservice */
/* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
/* well, there seems basically two choices. Just patch the caller thru immediately,
or,... put 'em thru to voicemail. */
/* since the callee may have hung up, let's do the voicemail thing, no database decision */
ast_log(LOG_NOTICE, "privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
/* XXX should we set status to DENY ? */
/* XXX what about the privacy flags ? */
break;
}
if (res2 == '1') { /* the only case where we actually connect */
/* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
just clog things up, and it's not useful information, not being tied to a CID */
Tilghman Lesher
committed
if (strncmp(pa->privcid, "NOCALLERID", 10) == 0 || ast_test_flag64(opts, OPT_SCREEN_NOINTRO)) {
ast_filedelete(pa->privintro, NULL);
Tilghman Lesher
committed
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);
} else {
return -1;
}
}
/*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
static int setup_privacy_args(struct privacy_args *pa,
struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan)
{
char callerid[60];
int res;
char *l;
if (ast_channel_caller(chan)->id.number.valid
&& !ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
l = ast_strdupa(ast_channel_caller(chan)->id.number.str);
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(ast_channel_name(chan));
/* 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", ast_channel_exten(chan), tnam);
l = callerid;
pa->privdb_val = AST_PRIVACY_UNKNOWN;
}
Tilghman Lesher
committed
ast_copy_string(pa->privcid, l, sizeof(pa->privcid));
Mark Michelson
committed
if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCALLERID)) {
/* if callerid is set and OPT_SCREEN_NOCALLERID 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;
Mark Michelson
committed
} else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
Tilghman Lesher
committed
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;
Tilghman Lesher
committed
} else if (pa->privdb_val == AST_PRIVACY_KILL) {
ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
return 0; /* Is this right? */
Tilghman Lesher
committed
} else if (pa->privdb_val == AST_PRIVACY_TORTURE) {
ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
return 0; /* is this right??? */
Tilghman Lesher
committed
} 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);
Tilghman Lesher
committed
if ((res = ast_mkdir(pa->privintro, 0755))) {
ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
return -1;
}
Tilghman Lesher
committed
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:"
*/
int silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "sln", &duration, NULL, 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);
Tilghman Lesher
committed
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", ast_channel_language(chan)) )
ast_waitstream(chan, "");
}
}
}
static void end_bridge_callback(void *data)
{
struct ast_channel *chan = data;
ast_channel_lock(chan);
ast_channel_stage_snapshot(chan);
set_duration_var(chan, "ANSWEREDTIME", ast_channel_get_up_time_ms(chan));
set_duration_var(chan, "DIALEDTIME", ast_channel_get_duration_ms(chan));
ast_channel_stage_snapshot_done(chan);
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;
}
Richard Mudgett
committed
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
/*!
* \internal
* \brief Setup the after bridge goto location on the peer.
* \since 12.0.0
*
* \param chan Calling channel for bridge.
* \param peer Peer channel for bridge.
* \param opts Dialing option flags.
* \param opt_args Dialing option argument strings.
*
* \return Nothing
*/
static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
{
const char *context;
const char *extension;
int priority;
if (ast_test_flag64(opts, OPT_PEER_H)) {
ast_channel_lock(chan);
context = ast_strdupa(ast_channel_context(chan));
ast_channel_unlock(chan);
ast_bridge_set_after_h(peer, context);
Richard Mudgett
committed
} else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
ast_channel_lock(chan);
context = ast_strdupa(ast_channel_context(chan));
extension = ast_strdupa(ast_channel_exten(chan));
priority = ast_channel_priority(chan);
ast_channel_unlock(chan);
ast_bridge_set_after_go_on(peer, context, extension, priority,
Richard Mudgett
committed
opt_args[OPT_ARG_CALLEE_GO_ON]);
}
}
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];
int fulldial = 0, num_dialed = 0;
int ignore_cc = 0;
char device_name[AST_CHANNEL_NAME];
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
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). */
ast_channel_lock(chan);
ast_channel_stage_snapshot(chan);
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, "ANSWEREDTIME_MS", "");
pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
pbx_builtin_setvar_helper(chan, "DIALEDTIME_MS", "");
pbx_builtin_setvar_helper(chan, "RINGTIME", "");
pbx_builtin_setvar_helper(chan, "RINGTIME_MS", "");
pbx_builtin_setvar_helper(chan, "PROGRESSTIME", "");
pbx_builtin_setvar_helper(chan, "PROGRESSTIME_MS", "");
ast_channel_stage_snapshot_done(chan);
max_forwards = ast_max_forwards_get(chan);
ast_channel_unlock(chan);
if (max_forwards <= 0) {
ast_log(LOG_WARNING, "Cannot place outbound call from channel '%s'. Max forwards exceeded\n",
ast_channel_name(chan));
pbx_builtin_setvar_helper(chan, "DIALSTATUS", "BUSY");
return -1;
}
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);
if (ast_check_hangup_locked(chan)) {
/*
* Caller hung up before we could dial. If dial is executed
* within an AGI then the AGI has likely eaten all queued
* frames before executing the dial in DeadAGI mode. With
* the caller hung up and no pending frames from the caller's
* read queue, dial would not know that the call has hung up
* until a called channel answers. It is rather annoying to
* whoever just answered the non-existent call.
*
* Dial should not continue execution in DeadAGI mode, hangup
* handlers, or the h exten.
*/
ast_verb(3, "Caller hung up before dial.\n");
pbx_builtin_setvar_helper(chan, "DIALSTATUS", "CANCEL");
return -1;
}
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)));
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
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);
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
}
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_cdr_reset(ast_channel_name(chan), 0);
}
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;
struct ast_stream_topology *topology;
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 |
Joshua Colp
committed
OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID | OPT_IGNORE_CONNECTEDLINE |
OPT_RING_WITH_EARLY_MEDIA);
ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
Richard Mudgett
committed
/*
* 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));
topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
ast_channel_unlock(chan);
tc = ast_request_with_stream_topology(tmp->tech, topology, NULL, chan, tmp->number, &cause);
ast_stream_topology_free(topology);
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);
ast_channel_lock_both(tc, chan);
ast_channel_stage_snapshot(tc);
pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", tmp->number);
/* 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_max_forwards_decrement(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
ast_channel_req_accountcodes(tc, chan, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
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_stage_snapshot_done(tc);
/* Save the original channel name to detect call pickup masquerading in. */
tmp->orig_chan_name = ast_strdup(ast_channel_name(tc));
ast_channel_unlock(tc);
Richard Mudgett
committed
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
/* 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
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
ast_channel_publish_dial(chan, tmp->chan, tmp->number, NULL);
Richard Mudgett
committed
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);
Joshua Colp
committed
} else if (ast_test_flag64(outgoing, OPT_RINGBACK) || ast_test_flag64(outgoing, OPT_RING_WITH_EARLY_MEDIA)) {
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);
if (result) {
res = result;
} else if (to) { /* Musta gotten hung up */
} else { /* Nobody answered, next please? */
res = 0;
}
} else {
const char *name;
Matthew Jordan
committed
int dial_end_raised = 0;
int cause = -1;
if (ast_test_flag64(&opts, OPT_CALLER_ANSWER)) {
Matthew Nicholson
committed
ast_answer(chan);
Matthew Nicholson
committed
/* 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
if (ast_test_flag64(&opts, OPT_HANGUPCAUSE)
&& !ast_strlen_zero(opt_args[OPT_ARG_HANGUPCAUSE])) {
cause = ast_str2cause(opt_args[OPT_ARG_HANGUPCAUSE]);
if (cause <= 0) {
if (!strcasecmp(opt_args[OPT_ARG_HANGUPCAUSE], "NONE")) {
cause = 0;
} else if (sscanf(opt_args[OPT_ARG_HANGUPCAUSE], "%30d", &cause) != 1
|| cause < 0) {
ast_log(LOG_WARNING, "Invalid cause given to Dial(...Q(<cause>)): \"%s\"\n",
opt_args[OPT_ARG_HANGUPCAUSE]);
cause = -1;
}
}
}
hanguptree(&out_chans, peer, cause >= 0 ? cause : AST_CAUSE_ANSWERED_ELSEWHERE);
Matthew Nicholson
committed
/* If appropriate, log that we have a destination channel and set the answer time */
ast_channel_lock(peer);
name = ast_strdupa(ast_channel_name(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);
ast_channel_lock(chan);
ast_channel_stage_snapshot(chan);
strcpy(pa.status, "ANSWER");
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", name);
Richard Mudgett
committed
pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
ast_channel_stage_snapshot_done(chan);
ast_channel_unlock(chan);
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)) {
Matthew Jordan
committed
ast_channel_publish_dial(chan, peer, NULL, pa.status);
Matt Jordan
committed
/* hang up on the callee -- he didn't want to talk anyway! */
ast_autoservice_chan_hangup_peer(chan, peer);
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;
Rodrigo Ramírez Norambuena
committed
/* we need to stream the announcement to the called party when the OPT_ARG_ANNOUNCE (-A) is setted */
/* 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_channel_set_flag(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_channel *other_chan;
struct ast_frame *fr = ast_read(active_chan);
Richard Mudgett
committed
ast_autoservice_chan_hangup_peer(chan, peer);
res = -1;
goto done;
}
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
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);
ast_autoservice_chan_hangup_peer(chan, peer);
res = -1;
goto done;
case AST_CONTROL_CONNECTED_LINE:
/* Pass COLP update to the other channel. */
if (active_chan == chan) {
other_chan = peer;
} else {
other_chan = chan;
if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)
&& ast_channel_connected_line_macro(active_chan,
other_chan, fr, other_chan == chan, 1)) {
ast_indicate_data(other_chan, fr->subclass.integer,
fr->data.ptr, fr->datalen);
}
break;
default:
break;
}
break;
default:
/* Ignore all others */
break;
}
ast_frfree(fr);
}
ast_sched_runq(ast_channel_sched(peer));
ast_channel_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; as such neither are considered
* outgoing channels any longer */
ast_channel_clear_flag(chan, AST_FLAG_OUTGOING);
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*/