Newer
Older
/* When a call is forwarded, we don't want to track new interfaces
* dialed for CC purposes. Setting the done flag will ensure that
* any Dial operations that happen later won't record CC interfaces.
*/
ast_ignore_cc(o->chan);
ast_log(LOG_NOTICE, "Not accepting call completion offers from call-forward recipient %s\n", ast_channel_name(o->chan));
ast_log(LOG_NOTICE,
"Forwarding failed to create channel to dial '%s/%s' (cause = %d)\n",
tech, stuff, cause);
ast_channel_publish_dial(in, original, stuff, "BUSY");
ast_hangup(original);
ast_channel_lock_both(c, original);
ast_party_redirecting_copy(ast_channel_redirecting(c),
ast_channel_redirecting(original));
ast_channel_unlock(c);
ast_channel_unlock(original);
ast_channel_lock_both(c, in);
if (single && !caller_entertained && CAN_EARLY_BRIDGE(peerflags, c, in)) {
ast_rtp_instance_early_bridge_make_compatible(c, in);
}
Mark Michelson
committed
if (!ast_channel_redirecting(c)->from.number.valid
|| ast_strlen_zero(ast_channel_redirecting(c)->from.number.str)) {
Richard Mudgett
committed
/*
* The call was not previously redirected so it is
* now redirected from this number.
*/
ast_party_number_free(&ast_channel_redirecting(c)->from.number);
ast_party_number_init(&ast_channel_redirecting(c)->from.number);
ast_channel_redirecting(c)->from.number.valid = 1;
ast_channel_redirecting(c)->from.number.str =
ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
Richard Mudgett
committed
}
Mark Michelson
committed
ast_channel_dialed(c)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
Mark Michelson
committed
/* Determine CallerID to store in outgoing channel. */
ast_party_caller_set_init(&caller, ast_channel_caller(c));
if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
caller.id = *stored_clid;
ast_channel_set_caller_event(c, &caller, NULL);
ast_set_flag64(o, DIAL_CALLERID_ABSENT);
} else if (ast_strlen_zero(S_COR(ast_channel_caller(c)->id.number.valid,
ast_channel_caller(c)->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;
ast_channel_set_caller_event(c, &caller, NULL);
ast_set_flag64(o, DIAL_CALLERID_ABSENT);
} else {
ast_clear_flag64(o, DIAL_CALLERID_ABSENT);
}
/* Determine CallerID for outgoing channel to send. */
if (ast_test_flag64(o, OPT_FORCECLID)) {
struct ast_party_connected_line connected;
ast_party_connected_line_init(&connected);
connected.id = *forced_clid;
ast_party_connected_line_copy(ast_channel_connected(c), &connected);
ast_connected_line_copy_from_caller(ast_channel_connected(c), ast_channel_caller(in));
ast_channel_req_accountcodes(c, in, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
ast_channel_appl_set(c, "AppDial");
ast_channel_data_set(c, "(Outgoing Line)");
ast_channel_publish_snapshot(c);
ast_channel_unlock(in);
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
if (single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
struct ast_party_redirecting redirecting;
/*
* Redirecting updates to the caller make sense only on single
* calls.
*
* We must unlock c before calling
* ast_channel_redirecting_macro, because we put c into
* autoservice there. That is pretty much a guaranteed
* deadlock. This is why the handling of c's lock may seem a
* bit unusual here.
*/
ast_party_redirecting_init(&redirecting);
ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(c));
ast_channel_unlock(c);
if (ast_channel_redirecting_sub(c, in, &redirecting, 0) &&
ast_channel_redirecting_macro(c, in, &redirecting, 1, 0)) {
ast_channel_update_redirecting(in, &redirecting, NULL);
}
ast_party_redirecting_free(&redirecting);
} else {
ast_channel_unlock(c);
}
Mark Michelson
committed
if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) {
*to = -1;
}
if (ast_call(c, stuff, 0)) {
ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
tech, stuff);
ast_channel_publish_dial(in, original, stuff, "CONGESTION");
Joshua Colp
committed
ast_hangup(original);
ast_hangup(c);
c = o->chan = NULL;
num->nochan++;
} else {
ast_channel_publish_dial_forward(in, original, c, NULL, "CANCEL",
Richard Mudgett
committed
ast_channel_call_forward(original));
ast_channel_publish_dial(in, c, stuff, NULL);
/* Hangup the original channel now, in case we needed it */
Joshua Colp
committed
ast_hangup(original);
if (single && !caller_entertained) {
/* argument used for some functions. */
struct privacy_args {
int sentringing;
int privdb_val;
char privcid[256];
char privintro[1024];
char status[256];
static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out_chans, struct ast_channel *exception, const char *status)
{
struct chanlist *outgoing;
AST_LIST_TRAVERSE(out_chans, outgoing, node) {
if (!outgoing->chan || outgoing->chan == exception) {
continue;
}
ast_channel_publish_dial(in, outgoing->chan, NULL, status);
}
}
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
/*!
* \internal
* \brief Update connected line on chan from peer.
* \since 13.6.0
*
* \param chan Channel to get connected line updated.
* \param peer Channel providing connected line information.
* \param is_caller Non-zero if chan is the calling channel.
*
* \return Nothing
*/
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
{
struct ast_party_connected_line connected_caller;
ast_party_connected_line_init(&connected_caller);
ast_channel_lock(peer);
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
ast_channel_unlock(peer);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
ast_channel_update_connected_line(chan, &connected_caller, NULL);
}
ast_party_connected_line_free(&connected_caller);
}
/*!
* \internal
* \pre chan is locked
*/
static void set_duration_var(struct ast_channel *chan, const char *var_base, int64_t duration)
{
char buf[32];
char full_var_name[128];
snprintf(buf, sizeof(buf), "%" PRId64, duration / 1000);
pbx_builtin_setvar_helper(chan, var_base, buf);
snprintf(full_var_name, sizeof(full_var_name), "%s_MS", var_base);
snprintf(buf, sizeof(buf), "%" PRId64, duration);
pbx_builtin_setvar_helper(chan, full_var_name, buf);
}
static struct ast_channel *wait_for_answer(struct ast_channel *in,
Richard Mudgett
committed
struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
Alec L Davis
committed
char *opt_args[],
struct privacy_args *pa,
const struct cause_args *num_in, int *result, char *dtmf_progress,
const int ignore_cc,
struct ast_party_id *forced_clid, struct ast_party_id *stored_clid)
struct cause_args num = *num_in;
int prestart = num.busy + num.congestion + num.nochan;
int orig = *to;
struct ast_channel *peer = NULL;
Richard Mudgett
committed
struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
/* single is set if only one destination is enabled */
int single = outgoing && !AST_LIST_NEXT(outgoing, node);
int caller_entertained = outgoing
&& ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
struct ast_str *featurecode = ast_str_alloca(AST_FEATURE_MAX_LEN + 1);
int cc_recall_core_id;
int is_cc_recall;
int cc_frame_received = 0;
int num_ringing = 0;
int sent_ring = 0;
int sent_progress = 0;
/* Turn off hold music, etc */
Richard Mudgett
committed
if (!caller_entertained) {
Mark Michelson
committed
ast_deactivate_generator(in);
Alec L Davis
committed
/* If we are calling a single channel, and not providing ringback or music, */
/* then, make them compatible for in-band tone purpose */
if (ast_channel_make_compatible(in, outgoing->chan) < 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_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
Alec L Davis
committed
}
Mark Michelson
committed
if (!ast_test_flag64(outgoing, OPT_IGNORE_CONNECTEDLINE)
&& !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) {
update_connected_line_from_peer(in, outgoing->chan, 1);
Mark Michelson
committed
}
Joshua Colp
committed
is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
while ((*to = ast_remaining_ms(start, orig)) && !peer) {
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;
Richard Mudgett
committed
AST_LIST_TRAVERSE(out_chans, o, node) {
if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
if (pos == 1) { /* only the input channel is available */
if (numlines == (num.busy + num.congestion + num.nochan)) {
ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
strcpy(pa->status, "CONGESTION");
strcpy(pa->status, "CHANUNAVAIL");
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
if (is_cc_recall) {
ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
}
Richard Mudgett
committed
AST_LIST_TRAVERSE(out_chans, o, node) {
struct ast_frame *f;
struct ast_channel *c = o->chan;
if (c == NULL)
continue;
if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
if (o->orig_chan_name
&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
/*
* The channel name changed so we must generate COLP update.
* Likely because a call pickup channel masqueraded in.
*/
update_connected_line_from_peer(in, c, 1);
} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
update_connected_line_from_peer(in, c, 1);
Mark Michelson
committed
}
}
if (o->aoc_s_rate_list) {
size_t encoded_size;
struct ast_aoc_encoded *encoded;
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
ast_aoc_destroy_encoded(encoded);
}
}
peer = c;
publish_dial_end_event(in, out_chans, peer, "CANCEL");
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);
continue;
}
if (c != winner)
continue;
/* here, o->chan == c == winner */
if (!ast_strlen_zero(ast_channel_call_forward(c))) {
if (!ignore_cc && (f = ast_read(c))) {
if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_CC) {
/* This channel is forwarding the call, and is capable of CC, so
* be sure to add the new device interface to the list
*/
ast_handle_cc_control_frame(in, c, f->data.ptr);
}
ast_frfree(f);
}
if (o->pending_connected_update) {
/*
* Re-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.
*/
o->pending_connected_update = 0;
ast_channel_lock(in);
ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
ast_channel_unlock(in);
}
do_forward(o, &num, peerflags, single, caller_entertained, &orig,
Richard Mudgett
committed
forced_clid, stored_clid);
if (o->chan) {
ast_free(o->orig_chan_name);
o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
if (single
&& !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
&& !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
update_connected_line_from_peer(in, o->chan, 1);
}
}
f = ast_read(winner);
if (!f) {
ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
ast_channel_publish_dial(in, c, NULL, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
ast_hangup(c);
c = o->chan = NULL;
ast_clear_flag64(o, DIAL_STILLGOING);
handle_cause(ast_channel_hangupcause(in), &num);
continue;
}
Richard Mudgett
committed
switch (f->frametype) {
case AST_FRAME_CONTROL:
switch (f->subclass.integer) {
case AST_CONTROL_ANSWER:
/* This is our guy if someone answered. */
if (!peer) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
if (o->orig_chan_name
&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
/*
* The channel name changed so we must generate COLP update.
* Likely because a call pickup channel masqueraded in.
*/
update_connected_line_from_peer(in, c, 1);
} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
update_connected_line_from_peer(in, c, 1);
Mark Michelson
committed
}
}
if (o->aoc_s_rate_list) {
size_t encoded_size;
struct ast_aoc_encoded *encoded;
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
ast_aoc_destroy_encoded(encoded);
}
}
Matthew Jordan
committed
/* Inform everyone else that they've been canceled.
* The dial end event for the peer will be sent out after
* other Dial options have been handled.
*/
publish_dial_end_event(in, out_chans, peer, "CANCEL");
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);
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 */
ast_channel_hangupcause_set(in, AST_CAUSE_NORMAL_CLEARING);
ast_channel_hangupcause_set(c, AST_CAUSE_NORMAL_CLEARING);
break;
case AST_CONTROL_BUSY:
ast_verb(3, "%s is busy\n", ast_channel_name(c));
ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
ast_channel_publish_dial(in, c, NULL, "BUSY");
ast_hangup(c);
c = o->chan = NULL;
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", ast_channel_name(c));
ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
ast_channel_publish_dial(in, c, NULL, "CONGESTION");
ast_hangup(c);
c = o->chan = NULL;
ast_clear_flag64(o, DIAL_STILLGOING);
handle_cause(AST_CAUSE_CONGESTION, &num);
break;
case AST_CONTROL_RINGING:
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
/* This is a tricky area to get right when using a native
* CC agent. The reason is that we do the best we can to send only a
* single ringing notification to the caller.
*
* Call completion complicates the logic used here. CCNR is typically
* offered during a ringing message. Let's say that party A calls
* parties B, C, and D. B and C do not support CC requests, but D
* does. If we were to receive a ringing notification from B before
* the others, then we would end up sending a ringing message to
* A with no CCNR offer present.
*
* The approach that we have taken is that if we receive a ringing
* response from a party and no CCNR offer is present, we need to
* wait. Specifically, we need to wait until either a) a called party
* offers CCNR in its ringing response or b) all called parties have
* responded in some way to our call and none offers CCNR.
*
* The drawback to this is that if one of the parties has a delayed
* response or, god forbid, one just plain doesn't respond to our
* outgoing call, then this will result in a significant delay between
* when the caller places the call and hears ringback.
*
* Note also that if CC is disabled for this call, then it is perfectly
* fine for ringing frames to get sent through.
*/
++num_ringing;
if (ignore_cc || cc_frame_received || num_ringing == numlines) {
ast_verb(3, "%s is ringing\n", ast_channel_name(c));
/* Setup early media if appropriate */
Richard Mudgett
committed
if (single && !caller_entertained
&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
ast_channel_early_bridge(in, c);
Richard Mudgett
committed
}
if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
ast_indicate(in, AST_CONTROL_RINGING);
pa->sentringing++;
}
if (!sent_ring) {
struct timeval now, then;
int64_t diff;
now = ast_tvnow();
ast_channel_lock(in);
ast_channel_stage_snapshot(in);
then = ast_channel_creationtime(c);
diff = ast_tvzero(then) ? 0 : ast_tvdiff_ms(now, then);
set_duration_var(in, "RINGTIME", diff);
ast_channel_stage_snapshot_done(in);
ast_channel_unlock(in);
sent_ring = 1;
}
ast_channel_publish_dial(in, c, NULL, "RINGING");
break;
case AST_CONTROL_PROGRESS:
ast_verb(3, "%s is making progress passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
/* Setup early media if appropriate */
Richard Mudgett
committed
if (single && !caller_entertained
&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
Joshua Colp
committed
ast_channel_early_bridge(in, c);
Richard Mudgett
committed
}
if (!ast_test_flag64(outgoing, OPT_RINGBACK)) {
if (single || (!single && !pa->sentringing)) {
ast_indicate(in, AST_CONTROL_PROGRESS);
}
if (!sent_progress) {
struct timeval now, then;
int64_t diff;
now = ast_tvnow();
ast_channel_lock(in);
ast_channel_stage_snapshot(in);
then = ast_channel_creationtime(c);
diff = ast_tvzero(then) ? 0 : ast_tvdiff_ms(now, then);
set_duration_var(in, "PROGRESSTIME", diff);
ast_channel_stage_snapshot_done(in);
ast_channel_unlock(in);
sent_progress = 1;
}
if (!ast_strlen_zero(dtmf_progress)) {
ast_verb(3,
"Sending DTMF '%s' to the called party as result of receiving a PROGRESS message.\n",
dtmf_progress);
ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
}
ast_channel_publish_dial(in, c, NULL, "PROGRESS");
break;
case AST_CONTROL_VIDUPDATE:
Richard Mudgett
committed
case AST_CONTROL_SRCCHANGE:
if (!single || caller_entertained) {
break;
}
ast_verb(3, "%s requested media update control %d, passing it to %s\n",
ast_channel_name(c), f->subclass.integer, ast_channel_name(in));
ast_indicate(in, f->subclass.integer);
Mark Michelson
committed
case AST_CONTROL_CONNECTED_LINE:
if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(in));
break;
}
if (!single) {
Mark Michelson
committed
struct ast_party_connected_line connected;
ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n",
ast_channel_name(c), ast_channel_name(in));
Mark Michelson
committed
ast_party_connected_line_set_init(&connected, &o->connected);
ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
ast_party_connected_line_set(&o->connected, &connected, NULL);
Mark Michelson
committed
ast_party_connected_line_free(&connected);
o->pending_connected_update = 1;
break;
}
if (ast_channel_connected_line_sub(c, in, f, 1) &&
ast_channel_connected_line_macro(c, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
Mark Michelson
committed
}
break;
case AST_CONTROL_AOC:
{
struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
ast_aoc_destroy_decoded(o->aoc_s_rate_list);
o->aoc_s_rate_list = decoded;
} else {
ast_aoc_destroy_decoded(decoded);
}
}
break;
Mark Michelson
committed
case AST_CONTROL_REDIRECTING:
if (!single) {
/*
* Redirecting updates to the caller make sense only on single
* calls.
*/
break;
}
if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(in));
break;
Mark Michelson
committed
}
ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
ast_channel_name(c), ast_channel_name(in));
if (ast_channel_redirecting_sub(c, in, f, 1) &&
ast_channel_redirecting_macro(c, in, f, 1, 1)) {
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
}
pa->sentringing = 0;
Mark Michelson
committed
break;
case AST_CONTROL_PROCEEDING:
ast_verb(3, "%s is proceeding passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
Richard Mudgett
committed
if (single && !caller_entertained
&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
Joshua Colp
committed
ast_channel_early_bridge(in, c);
Richard Mudgett
committed
}
if (!ast_test_flag64(outgoing, OPT_RINGBACK))
ast_indicate(in, AST_CONTROL_PROCEEDING);
ast_channel_publish_dial(in, c, NULL, "PROCEEDING");
break;
case AST_CONTROL_HOLD:
Richard Mudgett
committed
/* XXX this should be saved like AST_CONTROL_CONNECTED_LINE for !single || caller_entertained */
ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(c));
Richard Mudgett
committed
ast_indicate_data(in, AST_CONTROL_HOLD, f->data.ptr, f->datalen);
break;
case AST_CONTROL_UNHOLD:
Richard Mudgett
committed
/* XXX this should be saved like AST_CONTROL_CONNECTED_LINE for !single || caller_entertained */
ast_verb(3, "Call on %s left from hold\n", ast_channel_name(c));
ast_indicate(in, AST_CONTROL_UNHOLD);
break;
case AST_CONTROL_OFFHOOK:
case AST_CONTROL_FLASH:
/* Ignore going off hook and flash */
break;
case AST_CONTROL_CC:
if (!ignore_cc) {
ast_handle_cc_control_frame(in, c, f->data.ptr);
cc_frame_received = 1;
}
break;
case AST_CONTROL_PVT_CAUSE_CODE:
ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
break;
Richard Mudgett
committed
if (single && !caller_entertained) {
ast_verb(3, "%s stopped sounds\n", ast_channel_name(c));
ast_indicate(in, -1);
pa->sentringing = 0;
}
break;
default:
ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
Richard Mudgett
committed
}
break;
Richard Mudgett
committed
case AST_FRAME_VOICE:
case AST_FRAME_IMAGE:
if (caller_entertained) {
Richard Mudgett
committed
/* Fall through */
case AST_FRAME_TEXT:
if (single && ast_write(in, f)) {
ast_log(LOG_WARNING, "Unable to write frametype: %u\n",
Richard Mudgett
committed
f->frametype);
}
break;
case AST_FRAME_HTML:
if (single && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML)
&& ast_channel_sendhtml(in, f->subclass.integer, f->data.ptr, f->datalen) == -1) {
ast_log(LOG_WARNING, "Unable to send URL\n");
}
break;
default:
break;
ast_frfree(f);
} /* end for */
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);
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
strcpy(pa->status, "CANCEL");
publish_dial_end_event(in, out_chans, NULL, pa->status);
if (f) {
Michiel van Baak
committed
if (f->data.uint32) {
ast_channel_hangupcause_set(in, f->data.uint32);
Michiel van Baak
committed
}
if (is_cc_recall) {
ast_cc_completed(in, "CC completed, although the caller hung up (cancelled)");
}
/* 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.integer, 1)) {
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
Tilghman Lesher
committed
*to = 0;
*result = f->subclass.integer;
strcpy(pa->status, "CANCEL");
publish_dial_end_event(in, out_chans, NULL, pa->status);
ast_channel_unlock(in);
if (is_cc_recall) {
ast_cc_completed(in, "CC completed, but the caller used DTMF to exit");
}
ast_channel_unlock(in);
if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
detect_disconnect(in, f->subclass.integer, &featurecode)) {
ast_verb(3, "User requested call disconnect.\n");
Tilghman Lesher
committed
*to = 0;
strcpy(pa->status, "CANCEL");
publish_dial_end_event(in, out_chans, NULL, pa->status);
if (is_cc_recall) {
ast_cc_completed(in, "CC completed, but the caller hung up with DTMF");
}
/* Send the frame from the in channel to all outgoing channels. */
Richard Mudgett
committed
AST_LIST_TRAVERSE(out_chans, o, node) {
if (!o->chan || !ast_test_flag64(o, DIAL_STILLGOING)) {
/* This outgoing channel has died so don't send the frame to it. */
continue;
}
switch (f->frametype) {
case AST_FRAME_HTML:
/* Forward HTML stuff */
if (!ast_test_flag64(o, DIAL_NOFORWARDHTML)
&& ast_channel_sendhtml(o->chan, f->subclass.integer, f->data.ptr, f->datalen) == -1) {
ast_log(LOG_WARNING, "Unable to send URL\n");
case AST_FRAME_VOICE:
case AST_FRAME_IMAGE:
Richard Mudgett
committed
if (!single || caller_entertained) {
/*
* We are calling multiple parties or caller is being
* entertained and has thus not been made compatible.
* No need to check any other called parties.
*/
goto skip_frame;
}
/* Fall through */
case AST_FRAME_TEXT:
case AST_FRAME_DTMF_BEGIN:
case AST_FRAME_DTMF_END:
if (ast_write(o->chan, f)) {
ast_log(LOG_WARNING, "Unable to forward frametype: %u\n",
f->frametype);
}
break;
case AST_FRAME_CONTROL:
switch (f->subclass.integer) {
case AST_CONTROL_HOLD:
Richard Mudgett
committed
ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(o->chan));
ast_indicate_data(o->chan, AST_CONTROL_HOLD, f->data.ptr, f->datalen);
break;
case AST_CONTROL_UNHOLD:
Richard Mudgett
committed
ast_verb(3, "Call on %s left from hold\n", ast_channel_name(o->chan));
ast_indicate(o->chan, AST_CONTROL_UNHOLD);
break;
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
Richard Mudgett
committed
case AST_CONTROL_SRCCHANGE:
if (!single || caller_entertained) {
/*
* We are calling multiple parties or caller is being
* entertained and has thus not been made compatible.
* No need to check any other called parties.
*/
goto skip_frame;
}
ast_verb(3, "%s requested media update control %d, passing it to %s\n",
ast_channel_name(in), f->subclass.integer, ast_channel_name(o->chan));
Richard Mudgett
committed
ast_indicate(o->chan, f->subclass.integer);
break;
case AST_CONTROL_CONNECTED_LINE:
Alexei Gradinari
committed
if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
break;
}
if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
case AST_CONTROL_REDIRECTING:
Alexei Gradinari
committed
if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
break;
}
if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
default:
Richard Mudgett
committed
/* We are not going to do anything with this frame. */
goto skip_frame;
break;
default:
Richard Mudgett
committed
/* We are not going to do anything with this frame. */
goto skip_frame;
Richard Mudgett
committed
skip_frame:;
if (!*to || ast_check_hangup(in)) {
ast_verb(3, "Nobody picked up in %d ms\n", orig);
publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
if (is_cc_recall) {
ast_cc_completed(in, "Recall completed!");
}
}
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode)
char disconnect_code[AST_FEATURE_MAX_LEN];
ast_str_append(featurecode, 1, "%c", code);
res = ast_get_builtin_feature(chan, "disconnect", disconnect_code, sizeof(disconnect_code));
if (res) {
ast_str_reset(*featurecode);
if (strlen(disconnect_code) > ast_str_strlen(*featurecode)) {
/* Could be a partial match, anyway */
if (strncmp(disconnect_code, ast_str_buffer(*featurecode), ast_str_strlen(*featurecode))) {
ast_str_reset(*featurecode);
}
return 0;
if (strcmp(disconnect_code, ast_str_buffer(*featurecode))) {
ast_str_reset(*featurecode);
return 0;
}
return 1;
/* returns true if there is a valid privacy reply */
static int valid_priv_reply(struct ast_flags64 *opts, int res)
{
if (res < '1')
return 0;
if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5')
if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4')
return 1;
return 0;
}
static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
{
int res2;
int loopcount = 0;
/* 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 */
/* all ring indications and moh for the caller has been halted as soon as the
target extension was picked up. We are going to have to kill some
time and make the caller believe the peer hasn't picked up yet */
if (ast_test_flag64(opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
char *original_moh = ast_strdupa(ast_channel_musicclass(chan));
ast_indicate(chan, -1);
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);
Joshua Colp
committed
} else if (ast_test_flag64(opts, OPT_RINGBACK) || ast_test_flag64(opts, OPT_RING_WITH_EARLY_MEDIA)) {
ast_indicate(chan, AST_CONTROL_RINGING);
pa->sentringing++;
}
/* Start autoservice on the other chan ?? */
res2 = ast_autoservice_start(chan);
/* Now Stream the File */
for (loopcount = 0; loopcount < 3; loopcount++) {
if (res2 && loopcount == 0) /* error in ast_autoservice_start() */
if (!res2) /* on timeout, play the message again */
Tilghman Lesher
committed
res2 = ast_play_and_wait(peer, "priv-callpending");
if (!valid_priv_reply(opts, res2))
res2 = 0;
"I have a caller waiting, who introduces themselves as:"
*/
if (!res2)
res2 = ast_play_and_wait(peer, pa->privintro);
if (!valid_priv_reply(opts, res2))
res2 = 0;
/* now get input from the called party, as to their choice */
Tilghman Lesher
committed
if (!res2) {
/* XXX can we have both, or they are mutually exclusive ? */
Tilghman Lesher
committed
if (ast_test_flag64(opts, OPT_PRIVACY))
res2 = ast_play_and_wait(peer, "priv-callee-options");
if (ast_test_flag64(opts, OPT_SCREENING))
res2 = ast_play_and_wait(peer, "screen-callee-options");
/*! \page DialPrivacy Dial Privacy scripts
* \li Dial 1 if you wish this caller to reach you directly in the future,
* and immediately connect to their incoming call.
* \li Dial 2 if you wish to send this caller to voicemail now and forevermore.
* \li Dial 3 to send this caller to the torture menus, now and forevermore.
* \li Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
* \li Dial 5 to allow this caller to come straight thru to you in the future,
* but right now, just this once, send them to voicemail.
*
* \par screen-callee-options script:
* \li Dial 1 if you wish to immediately connect to the incoming call
* \li Dial 2 if you wish to send this caller to voicemail.
* \li Dial 3 to send this caller to the torture menus.
* \li Dial 4 to send this caller to a simple "go away" menu.
*/
if (valid_priv_reply(opts, res2))
break;
/* invalid option */
res2 = ast_play_and_wait(peer, "vm-sorry");
}
if (ast_test_flag64(opts, OPT_MUSICBACK)) {
ast_moh_stop(chan);
Joshua Colp
committed
} else if (ast_test_flag64(opts, OPT_RINGBACK) || ast_test_flag64(opts, OPT_RING_WITH_EARLY_MEDIA)) {
ast_indicate(chan, -1);
Tilghman Lesher
committed
pa->sentringing = 0;
}
ast_autoservice_stop(chan);
Tilghman Lesher
committed
if (ast_test_flag64(opts, OPT_PRIVACY) && (res2 >= '1' && res2 <= '5')) {
/* map keypresses to various things, the index is res2 - '1' */
static const char * const _val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" };
static const int _flag[] = { AST_PRIVACY_ALLOW, AST_PRIVACY_DENY, AST_PRIVACY_TORTURE, AST_PRIVACY_KILL, AST_PRIVACY_ALLOW};
int i = res2 - '1';
ast_verb(3, "--Set privacy database entry %s/%s to %s\n",
opt_args[OPT_ARG_PRIVACY], pa->privcid, _val[i]);
ast_privacy_set(opt_args[OPT_ARG_PRIVACY], pa->privcid, _flag[i]);
}
switch (res2) {
case '1':
break;
case '2':
ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
break;
case '3':
ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
break;
case '4':
ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
break;
case '5':
if (ast_test_flag64(opts, OPT_PRIVACY)) {
ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
/* if not privacy, then 5 is the same as "default" case */