Call Parking and Pickup API Includes code and algorithms from the Zapata library. More...


Go to the source code of this file.
Data Structures | |
| struct | ast_call_feature |
Defines | |
| #define | AST_FEATURE_RETURN_HANGUP -1 |
| #define | AST_FEATURE_RETURN_KEEPTRYING 24 |
| #define | AST_FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER |
| #define | AST_FEATURE_RETURN_PARKFAILED 25 |
| #define | AST_FEATURE_RETURN_PASSDIGITS 21 |
| #define | AST_FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE |
| #define | AST_FEATURE_RETURN_STOREDIGITS 22 |
| #define | AST_FEATURE_RETURN_SUCCESS 23 |
| #define | AST_FEATURE_RETURN_SUCCESSBREAK 0 |
| #define | DEFAULT_PARKINGLOT "default" |
| #define | FEATURE_APP_ARGS_LEN 256 |
| #define | FEATURE_APP_LEN 64 |
| #define | FEATURE_EXTEN_LEN 32 |
| #define | FEATURE_MAX_LEN 11 |
| #define | FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */ |
| #define | FEATURE_SENSE_CHAN (1 << 0) |
| #define | FEATURE_SENSE_PEER (1 << 1) |
| #define | FEATURE_SNAME_LEN 32 |
Typedefs | |
| typedef int(* | ast_feature_operation )(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data) |
Enumerations | |
| enum | { AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), AST_FEATURE_FLAG_ONPEER = (1 << 1), AST_FEATURE_FLAG_ONSELF = (1 << 2), AST_FEATURE_FLAG_BYCALLEE = (1 << 3), AST_FEATURE_FLAG_BYCALLER = (1 << 4), AST_FEATURE_FLAG_BYBOTH = (3 << 3) } |
| main call feature structure More... | |
Functions | |
| int | ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) |
| Bridge a call, optionally allowing redirection. | |
| void | ast_bridge_end_dtmf (struct ast_channel *chan, char digit, struct timeval start, const char *why) |
| Simulate a DTMF end on a broken bridge channel. | |
| int | ast_bridge_timelimit (struct ast_channel *chan, struct ast_bridge_config *config, char *parse, struct timeval *calldurationlimit) |
| parse L option and read associated channel variables to set warning, warning frequency, and timelimit | |
| int | ast_can_pickup (struct ast_channel *chan) |
| Test if a channel can be picked up. | |
| int | ast_do_pickup (struct ast_channel *chan, struct ast_channel *target) |
| Pickup a call target. | |
| int | ast_feature_detect (struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature) |
| detect a feature before bridging | |
| int | ast_features_reload (void) |
| Reload call features from features.conf. | |
| struct ast_call_feature * | ast_find_call_feature (const char *name) |
| look for a call feature entry by its sname | |
| int | ast_masq_park_call (struct ast_channel *park_me, struct ast_channel *parker, int timeout, int *extout) |
| Park a call via a masqueraded channel. | |
| int | ast_masq_park_call_exten (struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout) |
| Park a call via a masqueraded channel. | |
| int | ast_park_call (struct ast_channel *park_me, struct ast_channel *parker, int timeout, const char *park_exten, int *extout) |
| Park a call and read back parked location. | |
| int | ast_park_call_exten (struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout) |
| Park a call and read back parked location. | |
| int | ast_parking_ext_valid (const char *exten_str, struct ast_channel *chan, const char *context) |
| Determine if parking extension exists in a given context. | |
| int | ast_pickup_call (struct ast_channel *chan) |
| Pickup a call. | |
| const char * | ast_pickup_ext (void) |
| Determine system call pickup extension. | |
| struct ast_channel * | ast_pickup_find_by_group (struct ast_channel *chan) |
| Find a pickup channel target by group. | |
| void | ast_rdlock_call_features (void) |
| void | ast_register_feature (struct ast_call_feature *feature) |
| register new feature into feature_set | |
| void | ast_unlock_call_features (void) |
| void | ast_unregister_feature (struct ast_call_feature *feature) |
| unregister feature from feature_set | |
Call Parking and Pickup API Includes code and algorithms from the Zapata library.
Definition in file features.h.
| #define AST_FEATURE_RETURN_HANGUP -1 |
Definition at line 39 of file features.h.
Referenced by builtin_disconnect().
| #define AST_FEATURE_RETURN_KEEPTRYING 24 |
Definition at line 46 of file features.h.
Referenced by feature_exec_app(), and feature_interpret_helper().
| #define AST_FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER |
Definition at line 42 of file features.h.
| #define AST_FEATURE_RETURN_PARKFAILED 25 |
Definition at line 47 of file features.h.
| #define AST_FEATURE_RETURN_PASSDIGITS 21 |
Definition at line 43 of file features.h.
Referenced by ast_bridge_call(), and feature_interpret_helper().
| #define AST_FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE |
Definition at line 41 of file features.h.
| #define AST_FEATURE_RETURN_STOREDIGITS 22 |
Definition at line 44 of file features.h.
Referenced by detect_disconnect(), and feature_interpret_helper().
| #define AST_FEATURE_RETURN_SUCCESS 23 |
Definition at line 45 of file features.h.
Referenced by ast_bridge_call(), builtin_atxfer(), builtin_automixmonitor(), builtin_automonitor(), builtin_blindtransfer(), builtin_parkcall(), feature_exec_app(), feature_interpret_helper(), and xfer_park_call_helper().
| #define AST_FEATURE_RETURN_SUCCESSBREAK 0 |
Definition at line 40 of file features.h.
Referenced by builtin_blindtransfer(), and feature_exec_app().
| #define DEFAULT_PARKINGLOT "default" |
Default parking lot
Definition at line 37 of file features.h.
Referenced by build_parkinglot(), gtalk_load_config(), load_config(), process_config(), and reload_config().
| #define FEATURE_APP_ARGS_LEN 256 |
Definition at line 32 of file features.h.
Referenced by process_applicationmap_line().
| #define FEATURE_APP_LEN 64 |
Definition at line 31 of file features.h.
Referenced by process_applicationmap_line().
| #define FEATURE_EXTEN_LEN 32 |
Definition at line 34 of file features.h.
Referenced by process_applicationmap_line().
| #define FEATURE_MAX_LEN 11 |
Definition at line 30 of file features.h.
Referenced by ast_bridge_call(), feature_interpret_helper(), and wait_for_answer().
| #define FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */ |
Definition at line 35 of file features.h.
Referenced by process_applicationmap_line().
| #define FEATURE_SENSE_CHAN (1 << 0) |
Definition at line 49 of file features.h.
Referenced by ast_bridge_call(), feature_exec_app(), and feature_interpret().
| #define FEATURE_SENSE_PEER (1 << 1) |
Definition at line 50 of file features.h.
Referenced by ast_bridge_call(), and set_peers().
| #define FEATURE_SNAME_LEN 32 |
Definition at line 33 of file features.h.
Referenced by process_applicationmap_line().
| typedef int(* ast_feature_operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data) |
Definition at line 52 of file features.h.
| anonymous enum |
main call feature structure
| AST_FEATURE_FLAG_NEEDSDTMF | |
| AST_FEATURE_FLAG_ONPEER | |
| AST_FEATURE_FLAG_ONSELF | |
| AST_FEATURE_FLAG_BYCALLEE | |
| AST_FEATURE_FLAG_BYCALLER | |
| AST_FEATURE_FLAG_BYBOTH |
Definition at line 56 of file features.h.
{
AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
AST_FEATURE_FLAG_ONPEER = (1 << 1),
AST_FEATURE_FLAG_ONSELF = (1 << 2),
AST_FEATURE_FLAG_BYCALLEE = (1 << 3),
AST_FEATURE_FLAG_BYCALLER = (1 << 4),
AST_FEATURE_FLAG_BYBOTH = (3 << 3),
};
| int ast_bridge_call | ( | struct ast_channel * | chan, |
| struct ast_channel * | peer, | ||
| struct ast_bridge_config * | config | ||
| ) |
Bridge a call, optionally allowing redirection.
Bridge a call, optionally allowing redirection.
| chan | The bridge considers this channel the caller. |
| peer | The bridge considers this channel the callee. |
| config | Configuration for this bridge. |
Set start time, check for two channels,check if monitor on check for feature activation, create new CDR
| res | on success. |
| -1 | on failure to bridge. |
TRUE if h-exten or hangup handlers run.
append the event to featurecode. we rely on the string being zero-filled, and not overflowing it.
Definition at line 4292 of file features.c.
References ast_cdr::accountcode, add_features_datastores(), ast_cdr::amaflags, ast_cdr::answer, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_end_dtmf(), AST_BRIDGE_RETRY, ast_bridged_channel(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_appenduserfield(), ast_cdr_copy_vars(), ast_cdr_detach(), ast_cdr_discard(), ast_cdr_dup_unique_swap(), ast_cdr_end(), AST_CDR_FLAG_BRIDGED, AST_CDR_FLAG_DIALED, AST_CDR_FLAG_MAIN, AST_CDR_FLAG_POST_DISABLED, AST_CDR_NULL, ast_cdr_setaccount(), ast_cdr_setanswer(), ast_cdr_setcid(), ast_cdr_setdisposition(), ast_cdr_setuserfield(), ast_cdr_specialized_reset(), ast_cdr_start(), ast_cdr_update(), AST_CEL_BRIDGE_END, AST_CEL_BRIDGE_START, ast_cel_report_event(), ast_channel_accountcode(), ast_channel_amaflags(), ast_channel_appl(), ast_channel_bridge(), ast_channel_caller(), ast_channel_cdr(), ast_channel_cdr_set(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_context(), ast_channel_context_set(), ast_channel_data(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_lock_both, ast_channel_log(), ast_channel_macrocontext(), AST_CHANNEL_NAME, ast_channel_name(), ast_channel_priority(), ast_channel_priority_set(), ast_channel_redirecting_macro(), ast_channel_redirecting_sub(), ast_channel_sending_dtmf_digit(), ast_channel_sending_dtmf_tv(), ast_channel_set_linkgroup(), ast_channel_setoption(), ast_channel_softhangup_internal_flag(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_channel_visible_indication(), ast_check_hangup(), ast_clear_flag, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_MCID, AST_CONTROL_OPTION, AST_CONTROL_PVT_CAUSE_CODE, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_string(), ast_debug, ast_default_amaflags, ast_dtmf_stream(), ast_exists_extension(), AST_FEATURE_NO_H_EXTEN, AST_FEATURE_RETURN_PASSDIGITS, AST_FEATURE_RETURN_SUCCESS, AST_FEATURE_WARNING_ACTIVE, AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT, AST_FLAG_BRIDGE_HANGUP_DONT, AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, ast_frfree, ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_opt_transmit_silence, AST_OPTION_AUDIO_MODE, AST_OPTION_DIGIT_DETECT, AST_OPTION_FAX_DETECT, AST_OPTION_FLAG_REQUEST, AST_OPTION_RELAXDTMF, AST_OPTION_TDD, AST_OPTION_TONE_VERIFY, ast_pbx_h_exten_run(), ast_pbx_hangup_handler_run(), ast_raw_answer(), ast_set_flag, AST_SOFTHANGUP_ASYNCGOTO, AST_SOFTHANGUP_UNBRIDGE, AST_STATE_RINGING, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, ast_tvcmp(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), ast_write(), ast_cdr::channel, clear_dialed_interfaces(), ast_frame::data, ast_option_header::data, ast_frame::datalen, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, f, feature_check(), feature_interpret(), FEATURE_MAX_LEN, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_bridge_config::feature_start_time, ast_bridge_config::feature_timer, featuredigittimeout, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_frame::frametype, ast_frame_subclass::integer, ast_cdr::lastapp, ast_cdr::lastdata, ast_frame::len, LOG_DEBUG, LOG_WARNING, monitor_app, monitor_ok, ast_cdr::next, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pick_unlocked_cdr(), ast_frame::ptr, S_COR, S_OR, set_bridge_features_on_config(), set_config_flags(), ast_cdr::start, ast_frame::subclass, ast_bridge_config::timelimit, ast_cdr::uniqueid, ast_channel::userfield, and ast_cdr::userfield.
Referenced by app_exec(), bridge_call_thread(), bridge_exec(), builtin_atxfer(), dial_exec_full(), parked_call_exec(), and try_calling().
{
/* Copy voice back and forth between the two channels. Give the peer
the ability to transfer calls with '#<extension' syntax. */
struct ast_frame *f;
struct ast_channel *who;
char chan_featurecode[FEATURE_MAX_LEN + 1]="";
char peer_featurecode[FEATURE_MAX_LEN + 1]="";
char orig_channame[AST_CHANNEL_NAME];
char orig_peername[AST_CHANNEL_NAME];
int res;
int diff;
int hasfeatures=0;
int hadfeatures=0;
int sendingdtmfdigit = 0;
int we_disabled_peer_cdr = 0;
struct ast_option_header *aoh;
struct ast_cdr *bridge_cdr = NULL;
struct ast_cdr *chan_cdr = ast_channel_cdr(chan); /* the proper chan cdr, if there are forked cdrs */
struct ast_cdr *peer_cdr = ast_channel_cdr(peer); /* the proper chan cdr, if there are forked cdrs */
struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
struct ast_silence_generator *silgen = NULL;
/*! TRUE if h-exten or hangup handlers run. */
int hangup_run = 0;
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer));
pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan));
/* Clear any BLINDTRANSFER since the transfer has completed. */
pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", NULL);
set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES"));
add_features_datastores(chan, peer, config);
/* This is an interesting case. One example is if a ringing channel gets redirected to
* an extension that picks up a parked call. This will make sure that the call taken
* out of parking gets told that the channel it just got bridged to is still ringing. */
if (ast_channel_state(chan) == AST_STATE_RINGING && ast_channel_visible_indication(peer) != AST_CONTROL_RINGING) {
ast_indicate(peer, AST_CONTROL_RINGING);
}
if (monitor_ok) {
const char *monitor_exec;
struct ast_channel *src = NULL;
if (!monitor_app) {
if (!(monitor_app = pbx_findapp("Monitor")))
monitor_ok=0;
}
if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR")))
src = chan;
else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
src = peer;
if (monitor_app && src) {
char *tmp = ast_strdupa(monitor_exec);
pbx_exec(src, monitor_app, tmp);
}
}
set_config_flags(chan, config);
/* Answer if need be */
if (ast_channel_state(chan) != AST_STATE_UP) {
if (ast_raw_answer(chan, 1)) {
return -1;
}
}
#ifdef FOR_DEBUG
/* show the two channels and cdrs involved in the bridge for debug & devel purposes */
ast_channel_log("Pre-bridge CHAN Channel info", chan);
ast_channel_log("Pre-bridge PEER Channel info", peer);
#endif
/* two channels are being marked as linked here */
ast_channel_set_linkgroup(chan,peer);
/* copy the userfield from the B-leg to A-leg if applicable */
if (ast_channel_cdr(chan) && ast_channel_cdr(peer) && !ast_strlen_zero(ast_channel_cdr(peer)->userfield)) {
char tmp[256];
ast_channel_lock(chan);
if (!ast_strlen_zero(ast_channel_cdr(chan)->userfield)) {
snprintf(tmp, sizeof(tmp), "%s;%s", ast_channel_cdr(chan)->userfield, ast_channel_cdr(peer)->userfield);
ast_cdr_appenduserfield(chan, tmp);
} else {
ast_cdr_setuserfield(chan, ast_channel_cdr(peer)->userfield);
}
ast_channel_unlock(chan);
/* Don't delete the CDR; just disable it. */
ast_set_flag(ast_channel_cdr(peer), AST_CDR_FLAG_POST_DISABLED);
we_disabled_peer_cdr = 1;
}
ast_copy_string(orig_channame,ast_channel_name(chan),sizeof(orig_channame));
ast_copy_string(orig_peername,ast_channel_name(peer),sizeof(orig_peername));
if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
ast_channel_lock_both(chan, peer);
if (chan_cdr) {
ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
ast_cdr_update(chan);
bridge_cdr = ast_cdr_dup_unique_swap(chan_cdr);
/* rip any forked CDR's off of the chan_cdr and attach
* them to the bridge_cdr instead */
bridge_cdr->next = chan_cdr->next;
chan_cdr->next = NULL;
ast_copy_string(bridge_cdr->lastapp, S_OR(ast_channel_appl(chan), ""), sizeof(bridge_cdr->lastapp));
ast_copy_string(bridge_cdr->lastdata, S_OR(ast_channel_data(chan), ""), sizeof(bridge_cdr->lastdata));
if (peer_cdr && !ast_strlen_zero(peer_cdr->userfield)) {
ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
}
ast_cdr_setaccount(peer, ast_channel_accountcode(chan));
} else {
/* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
ast_copy_string(bridge_cdr->channel, ast_channel_name(chan), sizeof(bridge_cdr->channel));
ast_copy_string(bridge_cdr->dstchannel, ast_channel_name(peer), sizeof(bridge_cdr->dstchannel));
ast_copy_string(bridge_cdr->uniqueid, ast_channel_uniqueid(chan), sizeof(bridge_cdr->uniqueid));
ast_copy_string(bridge_cdr->lastapp, S_OR(ast_channel_appl(chan), ""), sizeof(bridge_cdr->lastapp));
ast_copy_string(bridge_cdr->lastdata, S_OR(ast_channel_data(chan), ""), sizeof(bridge_cdr->lastdata));
ast_cdr_setcid(bridge_cdr, chan);
bridge_cdr->disposition = (ast_channel_state(chan) == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
bridge_cdr->amaflags = ast_channel_amaflags(chan) ? ast_channel_amaflags(chan) : ast_default_amaflags;
ast_copy_string(bridge_cdr->accountcode, ast_channel_accountcode(chan), sizeof(bridge_cdr->accountcode));
/* Destination information */
ast_copy_string(bridge_cdr->dst, ast_channel_exten(chan), sizeof(bridge_cdr->dst));
ast_copy_string(bridge_cdr->dcontext, ast_channel_context(chan), sizeof(bridge_cdr->dcontext));
if (peer_cdr) {
bridge_cdr->start = peer_cdr->start;
ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
} else {
ast_cdr_start(bridge_cdr);
}
}
ast_channel_unlock(chan);
ast_channel_unlock(peer);
ast_debug(4, "bridge answer set, chan answer set\n");
/* peer_cdr->answer will be set when a macro runs on the peer;
in that case, the bridge answer will be delayed while the
macro plays on the peer channel. The peer answered the call
before the macro started playing. To the phone system,
this is billable time for the call, even tho the caller
hears nothing but ringing while the macro does its thing. */
/* Another case where the peer cdr's time will be set, is when
A self-parks by pickup up phone and dialing 700, then B
picks up A by dialing its parking slot; there may be more
practical paths that get the same result, tho... in which
case you get the previous answer time from the Park... which
is before the bridge's start time, so I added in the
tvcmp check to the if below */
if (peer_cdr && !ast_tvzero(peer_cdr->answer) && ast_tvcmp(peer_cdr->answer, bridge_cdr->start) >= 0) {
ast_cdr_setanswer(bridge_cdr, peer_cdr->answer);
ast_cdr_setdisposition(bridge_cdr, peer_cdr->disposition);
if (chan_cdr) {
ast_cdr_setanswer(chan_cdr, peer_cdr->answer);
ast_cdr_setdisposition(chan_cdr, peer_cdr->disposition);
}
} else {
ast_cdr_answer(bridge_cdr);
if (chan_cdr) {
ast_cdr_answer(chan_cdr); /* for the sake of cli status checks */
}
}
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT) && (chan_cdr || peer_cdr)) {
if (chan_cdr) {
ast_set_flag(chan_cdr, AST_CDR_FLAG_BRIDGED);
}
if (peer_cdr) {
ast_set_flag(peer_cdr, AST_CDR_FLAG_BRIDGED);
}
}
/* the DIALED flag may be set if a dialed channel is transferred
* and then bridged to another channel. In order for the
* bridge CDR to be written, the DIALED flag must not be
* present. */
ast_clear_flag(bridge_cdr, AST_CDR_FLAG_DIALED);
}
ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, peer);
/* If we are bridging a call, stop worrying about forwarding loops. We presume that if
* a call is being bridged, that the humans in charge know what they're doing. If they
* don't, well, what can we do about that? */
clear_dialed_interfaces(chan);
clear_dialed_interfaces(peer);
for (;;) {
struct ast_channel *other; /* used later */
res = ast_channel_bridge(chan, peer, config, &f, &who);
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
|| ast_test_flag(ast_channel_flags(peer), AST_FLAG_ZOMBIE)) {
/* Zombies are present time to leave! */
res = -1;
if (f) {
ast_frfree(f);
}
goto before_you_go;
}
/* When frame is not set, we are probably involved in a situation
where we've timed out.
When frame is set, we'll come this code twice; once for DTMF_BEGIN
and also for DTMF_END. If we flow into the following 'if' for both, then
our wait times are cut in half, as both will subtract from the
feature_timer. Not good!
*/
if (config->feature_timer && (!f || f->frametype == AST_FRAME_DTMF_END)) {
/* Update feature timer for next pass */
diff = ast_tvdiff_ms(ast_tvnow(), config->feature_start_time);
if (res == AST_BRIDGE_RETRY) {
/* The feature fully timed out but has not been updated. Skip
* the potential round error from the diff calculation and
* explicitly set to expired. */
config->feature_timer = -1;
} else {
config->feature_timer -= diff;
}
if (hasfeatures) {
if (config->feature_timer <= 0) {
/* Not *really* out of time, just out of time for
digits to come in for features. */
ast_debug(1, "Timed out for feature!\n");
if (!ast_strlen_zero(peer_featurecode)) {
ast_dtmf_stream(chan, peer, peer_featurecode, 0, f ? f->len : 0);
memset(peer_featurecode, 0, sizeof(peer_featurecode));
}
if (!ast_strlen_zero(chan_featurecode)) {
ast_dtmf_stream(peer, chan, chan_featurecode, 0, f ? f->len : 0);
memset(chan_featurecode, 0, sizeof(chan_featurecode));
}
if (f)
ast_frfree(f);
hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
if (!hasfeatures) {
/* No more digits expected - reset the timer */
config->feature_timer = 0;
}
hadfeatures = hasfeatures;
/* Continue as we were */
continue;
} else if (!f) {
/* The bridge returned without a frame and there is a feature in progress.
* However, we don't think the feature has quite yet timed out, so just
* go back into the bridge. */
continue;
}
} else {
if (config->feature_timer <=0) {
/* We ran out of time */
config->feature_timer = 0;
who = chan;
if (f)
ast_frfree(f);
f = NULL;
res = 0;
}
}
}
if (res < 0) {
if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) && !ast_test_flag(ast_channel_flags(peer), AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) {
ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", ast_channel_name(chan), ast_channel_name(peer));
}
goto before_you_go;
}
if (!f || (f->frametype == AST_FRAME_CONTROL &&
(f->subclass.integer == AST_CONTROL_HANGUP || f->subclass.integer == AST_CONTROL_BUSY ||
f->subclass.integer == AST_CONTROL_CONGESTION))) {
res = -1;
break;
}
/* many things should be sent to the 'other' channel */
other = (who == chan) ? peer : chan;
if (f->frametype == AST_FRAME_CONTROL) {
switch (f->subclass.integer) {
case AST_CONTROL_RINGING:
case AST_CONTROL_FLASH:
case AST_CONTROL_MCID:
case -1:
ast_indicate(other, f->subclass.integer);
break;
case AST_CONTROL_CONNECTED_LINE:
if (ast_channel_connected_line_sub(who, other, f, 1) &&
ast_channel_connected_line_macro(who, other, f, who != chan, 1)) {
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
case AST_CONTROL_REDIRECTING:
if (ast_channel_redirecting_sub(who, other, f, 1) &&
ast_channel_redirecting_macro(who, other, f, who != chan, 1)) {
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
}
break;
case AST_CONTROL_PVT_CAUSE_CODE:
case AST_CONTROL_AOC:
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
break;
case AST_CONTROL_OPTION:
aoh = f->data.ptr;
/* Forward option Requests, but only ones we know are safe
* These are ONLY sent by chan_iax2 and I'm not convinced that
* they are useful. I haven't deleted them entirely because I
* just am not sure of the ramifications of removing them. */
if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
switch (ntohs(aoh->option)) {
case AST_OPTION_TONE_VERIFY:
case AST_OPTION_TDD:
case AST_OPTION_RELAXDTMF:
case AST_OPTION_AUDIO_MODE:
case AST_OPTION_DIGIT_DETECT:
case AST_OPTION_FAX_DETECT:
ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
f->datalen - sizeof(struct ast_option_header), 0);
}
}
break;
}
} else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
struct ast_flags *cfg;
char dtmfcode[2] = { f->subclass.integer, };
size_t featurelen;
if (who == chan) {
featurelen = strlen(chan_featurecode);
cfg = &(config->features_caller);
} else {
featurelen = strlen(peer_featurecode);
cfg = &(config->features_callee);
}
/* Take a peek if this (possibly) matches a feature. If not, just pass this
* DTMF along untouched. If this is not the first digit of a multi-digit code
* then we need to fall through and stream the characters if it matches */
if (featurelen == 0
&& feature_check(chan, cfg, &dtmfcode[0]) == AST_FEATURE_RETURN_PASSDIGITS) {
if (option_debug > 3) {
ast_log(LOG_DEBUG, "Passing DTMF through, since it is not a feature code\n");
}
ast_write(other, f);
sendingdtmfdigit = 1;
} else {
/* If ast_opt_transmit_silence is set, then we need to make sure we are
* transmitting something while we hold on to the DTMF waiting for a
* feature. */
if (!silgen && ast_opt_transmit_silence) {
silgen = ast_channel_start_silence_generator(other);
}
if (option_debug > 3) {
ast_log(LOG_DEBUG, "Not passing DTMF through, since it may be a feature code\n");
}
}
} else if (f->frametype == AST_FRAME_DTMF_END) {
char *featurecode;
int sense;
unsigned int dtmfduration = f->len;
hadfeatures = hasfeatures;
/* This cannot overrun because the longest feature is one shorter than our buffer */
if (who == chan) {
sense = FEATURE_SENSE_CHAN;
featurecode = chan_featurecode;
} else {
sense = FEATURE_SENSE_PEER;
featurecode = peer_featurecode;
}
if (sendingdtmfdigit == 1) {
/* We let the BEGIN go through happily, so let's not bother with the END,
* since we already know it's not something we bother with */
ast_write(other, f);
sendingdtmfdigit = 0;
} else {
/*! append the event to featurecode. we rely on the string being zero-filled, and
* not overflowing it.
* \todo XXX how do we guarantee the latter ?
*/
featurecode[strlen(featurecode)] = f->subclass.integer;
/* Get rid of the frame before we start doing "stuff" with the channels */
ast_frfree(f);
f = NULL;
if (silgen) {
ast_channel_stop_silence_generator(other, silgen);
silgen = NULL;
}
config->feature_timer = 0;
res = feature_interpret(chan, peer, config, featurecode, sense);
switch(res) {
case AST_FEATURE_RETURN_PASSDIGITS:
ast_dtmf_stream(other, who, featurecode, 0, dtmfduration);
/* Fall through */
case AST_FEATURE_RETURN_SUCCESS:
memset(featurecode, 0, sizeof(chan_featurecode));
break;
}
if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
res = 0;
} else {
break;
}
hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
if (hadfeatures && !hasfeatures) {
/* Feature completed or timed out */
config->feature_timer = 0;
} else if (hasfeatures) {
if (config->timelimit) {
/* No warning next time - we are waiting for feature code */
ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
}
config->feature_start_time = ast_tvnow();
config->feature_timer = featuredigittimeout;
ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
}
}
}
if (f)
ast_frfree(f);
}
ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, peer);
before_you_go:
if (ast_channel_sending_dtmf_digit(chan)) {
ast_bridge_end_dtmf(chan, ast_channel_sending_dtmf_digit(chan),
ast_channel_sending_dtmf_tv(chan), "bridge end");
}
if (ast_channel_sending_dtmf_digit(peer)) {
ast_bridge_end_dtmf(peer, ast_channel_sending_dtmf_digit(peer),
ast_channel_sending_dtmf_tv(peer), "bridge end");
}
/* Just in case something weird happened and we didn't clean up the silence generator... */
if (silgen) {
ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);
silgen = NULL;
}
/* Wait for any dual redirect to complete. */
while (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
sched_yield();
}
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT)) {
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */
if (bridge_cdr) {
ast_cdr_discard(bridge_cdr);
/* QUESTION: should we copy bridge_cdr fields to the peer before we throw it away? */
}
return res; /* if we shouldn't do the h-exten, we shouldn't do the bridge cdr, either! */
}
if (config->end_bridge_callback) {
config->end_bridge_callback(config->end_bridge_callback_data);
}
/* run the hangup exten on the chan object IFF it was NOT involved in a parking situation
* if it were, then chan belongs to a different thread now, and might have been hung up long
* ago.
*/
if (!(ast_channel_softhangup_internal_flag(chan) & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))
&& !ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
struct ast_cdr *swapper = NULL;
char savelastapp[AST_MAX_EXTENSION];
char savelastdata[AST_MAX_EXTENSION];
char save_context[AST_MAX_CONTEXT];
char save_exten[AST_MAX_EXTENSION];
int save_prio;
ast_channel_lock(chan);
if (bridge_cdr) {
/*
* Swap the bridge_cdr and the chan cdr for a moment, and let
* the hangup dialplan code operate on it.
*/
swapper = ast_channel_cdr(chan);
ast_channel_cdr_set(chan, bridge_cdr);
/* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
}
ast_copy_string(save_context, ast_channel_context(chan), sizeof(save_context));
ast_copy_string(save_exten, ast_channel_exten(chan), sizeof(save_exten));
save_prio = ast_channel_priority(chan);
ast_channel_unlock(chan);
ast_autoservice_start(peer);
if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1,
S_COR(ast_channel_caller(chan)->id.number.valid,
ast_channel_caller(chan)->id.number.str, NULL))) {
ast_pbx_h_exten_run(chan, ast_channel_context(chan));
hangup_run = 1;
} else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
&& ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1,
S_COR(ast_channel_caller(chan)->id.number.valid,
ast_channel_caller(chan)->id.number.str, NULL))) {
ast_pbx_h_exten_run(chan, ast_channel_macrocontext(chan));
hangup_run = 1;
}
if (ast_pbx_hangup_handler_run(chan)) {
/* Indicate hangup handlers were run. */
hangup_run = 1;
}
ast_autoservice_stop(peer);
ast_channel_lock(chan);
/* swap it back */
ast_channel_context_set(chan, save_context);
ast_channel_exten_set(chan, save_exten);
ast_channel_priority_set(chan, save_prio);
if (bridge_cdr) {
if (ast_channel_cdr(chan) == bridge_cdr) {
ast_channel_cdr_set(chan, swapper);
/* Restore the lastapp/lastdata */
ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
} else {
bridge_cdr = NULL;
}
}
ast_channel_unlock(chan);
}
/* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
new_chan_cdr = pick_unlocked_cdr(ast_channel_cdr(chan)); /* the proper chan cdr, if there are forked cdrs */
/*
* If the channel CDR has been modified during the call, record
* the changes in the bridge cdr, BUT, if hangup_run, the CDR
* got swapped so don't overwrite what was done in the
* h-extension or hangup handlers. What a mess. This is why
* you never touch CDR code.
*/
if (new_chan_cdr && bridge_cdr && !hangup_run) {
ast_cdr_copy_vars(bridge_cdr, new_chan_cdr);
ast_copy_string(bridge_cdr->userfield, new_chan_cdr->userfield, sizeof(bridge_cdr->userfield));
bridge_cdr->amaflags = new_chan_cdr->amaflags;
ast_copy_string(bridge_cdr->accountcode, new_chan_cdr->accountcode, sizeof(bridge_cdr->accountcode));
if (ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED)) {
ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
}
}
/* we can post the bridge CDR at this point */
if (bridge_cdr) {
ast_cdr_end(bridge_cdr);
ast_cdr_detach(bridge_cdr);
}
/* do a specialized reset on the beginning channel
CDR's, if they still exist, so as not to mess up
issues in future bridges;
Here are the rules of the game:
1. The chan and peer channel pointers will not change
during the life of the bridge.
2. But, in transfers, the channel names will change.
between the time the bridge is started, and the
time the channel ends.
Usually, when a channel changes names, it will
also change CDR pointers.
3. Usually, only one of the two channels (chan or peer)
will change names.
4. Usually, if a channel changes names during a bridge,
it is because of a transfer. Usually, in these situations,
it is normal to see 2 bridges running simultaneously, and
it is not unusual to see the two channels that change
swapped between bridges.
5. After a bridge occurs, we have 2 or 3 channels' CDRs
to attend to; if the chan or peer changed names,
we have the before and after attached CDR's.
*/
if (new_chan_cdr) {
struct ast_channel *chan_ptr = NULL;
if (strcasecmp(orig_channame, ast_channel_name(chan)) != 0) {
/* old channel */
if ((chan_ptr = ast_channel_get_by_name(orig_channame))) {
ast_channel_lock(chan_ptr);
if (!ast_bridged_channel(chan_ptr)) {
struct ast_cdr *cur;
for (cur = ast_channel_cdr(chan_ptr); cur; cur = cur->next) {
if (cur == chan_cdr) {
break;
}
}
if (cur) {
ast_cdr_specialized_reset(chan_cdr, 0);
}
}
ast_channel_unlock(chan_ptr);
chan_ptr = ast_channel_unref(chan_ptr);
}
/* new channel */
ast_cdr_specialized_reset(new_chan_cdr, 0);
} else {
ast_cdr_specialized_reset(ast_channel_cdr(chan), 0); /* nothing changed, reset the chan cdr */
}
}
{
struct ast_channel *chan_ptr = NULL;
new_peer_cdr = pick_unlocked_cdr(ast_channel_cdr(peer)); /* the proper chan cdr, if there are forked cdrs */
if (new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED) && new_peer_cdr && !ast_test_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED))
ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
if (strcasecmp(orig_peername, ast_channel_name(peer)) != 0) {
/* old channel */
if ((chan_ptr = ast_channel_get_by_name(orig_peername))) {
ast_channel_lock(chan_ptr);
if (!ast_bridged_channel(chan_ptr)) {
struct ast_cdr *cur;
for (cur = ast_channel_cdr(chan_ptr); cur; cur = cur->next) {
if (cur == peer_cdr) {
break;
}
}
if (cur) {
ast_cdr_specialized_reset(peer_cdr, 0);
}
}
ast_channel_unlock(chan_ptr);
chan_ptr = ast_channel_unref(chan_ptr);
}
/* new channel */
if (new_peer_cdr) {
ast_cdr_specialized_reset(new_peer_cdr, 0);
}
} else {
if (we_disabled_peer_cdr) {
ast_clear_flag(ast_channel_cdr(peer), AST_CDR_FLAG_POST_DISABLED);
}
ast_cdr_specialized_reset(ast_channel_cdr(peer), 0); /* nothing changed, reset the peer cdr */
}
}
return res;
}
| void ast_bridge_end_dtmf | ( | struct ast_channel * | chan, |
| char | digit, | ||
| struct timeval | start, | ||
| const char * | why | ||
| ) |
Simulate a DTMF end on a broken bridge channel.
| chan | Channel sending DTMF that has not ended. |
| digit | DTMF digit to stop. |
| start | DTMF digit start time. |
| why | Reason bridge broken. |
Definition at line 4259 of file features.c.
References ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_softhangup_internal_flag(), ast_channel_unlock, AST_FLAG_ZOMBIE, ast_log(), ast_senddigit_end(), AST_SOFTHANGUP_ASYNCGOTO, AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, ast_tvdiff_ms(), ast_tvnow(), and LOG_DTMF.
Referenced by ast_bridge_call(), and ast_do_masquerade().
{
int dead;
long duration;
ast_channel_lock(chan);
dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
|| (ast_channel_softhangup_internal_flag(chan)
& ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
ast_channel_unlock(chan);
if (dead) {
/* Channel is a zombie or a real hangup. */
return;
}
duration = ast_tvdiff_ms(ast_tvnow(), start);
ast_senddigit_end(chan, digit, duration);
ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
digit, ast_channel_name(chan), why, duration);
}
| int ast_bridge_timelimit | ( | struct ast_channel * | chan, |
| struct ast_bridge_config * | config, | ||
| char * | parse, | ||
| struct timeval * | calldurationlimit | ||
| ) |
parse L option and read associated channel variables to set warning, warning frequency, and timelimit
Definition at line 8044 of file features.c.
References ast_channel_lock, ast_channel_unlock, AST_FEATURE_PLAY_WARNING, ast_log(), ast_set_flag, ast_strdup, ast_strlen_zero(), ast_true(), ast_verb, ast_bridge_config::end_sound, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, pbx_builtin_getvar_helper(), ast_bridge_config::play_warning, S_OR, ast_bridge_config::start_sound, ast_bridge_config::timelimit, var, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.
Referenced by bridge_exec(), and dial_exec_full().
{
char *stringp = ast_strdupa(parse);
char *limit_str, *warning_str, *warnfreq_str;
const char *var;
int play_to_caller = 0, play_to_callee = 0;
int delta;
limit_str = strsep(&stringp, ":");
warning_str = strsep(&stringp, ":");
warnfreq_str = strsep(&stringp, ":");
config->timelimit = atol(limit_str);
if (warning_str)
config->play_warning = atol(warning_str);
if (warnfreq_str)
config->warning_freq = atol(warnfreq_str);
if (!config->timelimit) {
ast_log(LOG_WARNING, "Bridge does not accept L(%s), hanging up.\n", limit_str);
config->timelimit = config->play_warning = config->warning_freq = 0;
config->warning_sound = NULL;
return -1; /* error */
} else if ( (delta = config->play_warning - config->timelimit) > 0) {
int w = config->warning_freq;
/*
* If the first warning is requested _after_ the entire call
* would end, and no warning frequency is requested, then turn
* off the warning. If a warning frequency is requested, reduce
* the 'first warning' time by that frequency until it falls
* within the call's total time limit.
*
* Graphically:
* timelim->| delta |<-playwarning
* 0__________________|_________________|
* | w | | | |
*
* so the number of intervals to cut is 1+(delta-1)/w
*/
if (w == 0) {
config->play_warning = 0;
} else {
config->play_warning -= w * ( 1 + (delta-1)/w );
if (config->play_warning < 1)
config->play_warning = config->warning_freq = 0;
}
}
ast_channel_lock(chan);
var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
play_to_caller = var ? ast_true(var) : 1;
var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
play_to_callee = var ? ast_true(var) : 0;
if (!play_to_caller && !play_to_callee)
play_to_caller = 1;
var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");
/* The code looking at config wants a NULL, not just "", to decide
* that the message should not be played, so we replace "" with NULL.
* Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
* not found.
*/
var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
ast_channel_unlock(chan);
/* undo effect of S(x) in case they are both used */
calldurationlimit->tv_sec = 0;
calldurationlimit->tv_usec = 0;
/* more efficient to do it like S(x) does since no advanced opts */
if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
calldurationlimit->tv_sec = config->timelimit / 1000;
calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
config->timelimit = play_to_caller = play_to_callee =
config->play_warning = config->warning_freq = 0;
} else {
ast_verb(4, "Limit Data for this call:\n");
ast_verb(4, "timelimit = %ld ms (%.3lf s)\n", config->timelimit, config->timelimit / 1000.0);
ast_verb(4, "play_warning = %ld ms (%.3lf s)\n", config->play_warning, config->play_warning / 1000.0);
ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
ast_verb(4, "warning_freq = %ld ms (%.3lf s)\n", config->warning_freq, config->warning_freq / 1000.0);
ast_verb(4, "start_sound = %s\n", S_OR(config->start_sound, ""));
ast_verb(4, "warning_sound = %s\n", config->warning_sound);
ast_verb(4, "end_sound = %s\n", S_OR(config->end_sound, ""));
}
if (play_to_caller)
ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
if (play_to_callee)
ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
return 0;
}
| int ast_can_pickup | ( | struct ast_channel * | chan | ) |
Test if a channel can be picked up.
| chan | Channel to test if can be picked up. |
Definition at line 7752 of file features.c.
References ast_channel_datastore_find(), ast_channel_flags(), ast_channel_masq(), ast_channel_pbx(), AST_FLAG_ZOMBIE, AST_STATE_DOWN, AST_STATE_RING, AST_STATE_RINGING, and ast_test_flag.
Referenced by ast_pickup_find_by_group(), find_by_mark(), find_by_part(), find_channel_by_group(), pickup_by_exten(), and pickup_by_name_cb().
{
if (!ast_channel_pbx(chan) && !ast_channel_masq(chan) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
&& (ast_channel_state(chan) == AST_STATE_RINGING
|| ast_channel_state(chan) == AST_STATE_RING
/*
* Check the down state as well because some SIP devices do not
* give 180 ringing when they can just give 183 session progress
* instead. Issue 14005. (Some ISDN switches as well for that
* matter.)
*/
|| ast_channel_state(chan) == AST_STATE_DOWN)
&& !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
return 1;
}
return 0;
}
| int ast_do_pickup | ( | struct ast_channel * | chan, |
| struct ast_channel * | target | ||
| ) |
Pickup a call target.
| chan | channel that initiated pickup. |
| target | channel to be picked up. |
| 0 | on success. |
| -1 | on failure. |
< A masquerade changes channel names.
< A masquerade changes channel names.
Definition at line 7910 of file features.c.
References ast_answer(), AST_CAUSE_ANSWERED_ELSEWHERE, AST_CEL_PICKUP, ast_cel_report_event(), ast_channel_caller(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_datastore_add(), ast_channel_datastore_remove(), ast_channel_hangupcause_set(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_queue_connected_line_update(), ast_channel_unlock, ast_channel_update_connected_line(), ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, ast_datastore_alloc(), ast_datastore_free(), ast_debug, ast_do_masquerade(), ast_log(), ast_manager_event_multichan, ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_id_reset(), ast_queue_control(), EVENT_FLAG_CALL, LOG_WARNING, ast_party_connected_line::priv, and ast_party_connected_line::source.
Referenced by ast_pickup_call(), pickup_by_channel(), pickup_by_exten(), pickup_by_group(), pickup_by_mark(), and pickup_by_part().
{
struct ast_party_connected_line connected_caller;
struct ast_channel *chans[2] = { chan, target };
struct ast_datastore *ds_pickup;
const char *chan_name;/*!< A masquerade changes channel names. */
const char *target_name;/*!< A masquerade changes channel names. */
int res = -1;
target_name = ast_strdupa(ast_channel_name(target));
ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan));
/* Mark the target to block any call pickup race. */
ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
if (!ds_pickup) {
ast_log(LOG_WARNING,
"Unable to create channel datastore on '%s' for call pickup\n", target_name);
return -1;
}
ast_channel_datastore_add(target, ds_pickup);
ast_party_connected_line_init(&connected_caller);
ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target));
ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
/* Reset any earlier private connected id representation */
ast_party_id_reset(&connected_caller.priv);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) &&
ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
ast_channel_update_connected_line(chan, &connected_caller, NULL);
}
ast_party_connected_line_free(&connected_caller);
ast_channel_lock(chan);
chan_name = ast_strdupa(ast_channel_name(chan));
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan));
ast_channel_unlock(chan);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
if (ast_answer(chan)) {
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
goto pickup_failed;
}
if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
goto pickup_failed;
}
ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
/* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */
ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);
if (ast_channel_masquerade(target, chan)) {
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
target_name);
goto pickup_failed;
}
/* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a call pickup occurs.</synopsis>
<syntax>
<parameter name="Channel"><para>The name of the channel that initiated the pickup.</para></parameter>
<parameter name="TargetChannel"><para>The name of the channel that is being picked up.</para></parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
"Channel: %s\r\n"
"TargetChannel: %s\r\n",
chan_name, target_name);
/* Do the masquerade manually to make sure that it is completed. */
ast_do_masquerade(target);
res = 0;
pickup_failed:
ast_channel_lock(target);
if (!ast_channel_datastore_remove(target, ds_pickup)) {
ast_datastore_free(ds_pickup);
}
ast_party_connected_line_free(&connected_caller);
return res;
}
| int ast_feature_detect | ( | struct ast_channel * | chan, |
| struct ast_flags * | features, | ||
| const char * | code, | ||
| struct ast_call_feature * | feature | ||
| ) |
detect a feature before bridging
| chan | |
| features | an ast_flags ptr |
| code | ptr of input code |
| feature |
| ast_call_feature | ptr to be set if found |
Definition at line 3746 of file features.c.
References FEATURE_INTERPRET_DETECT, and feature_interpret_helper().
Referenced by detect_disconnect().
{
return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
}
| int ast_features_reload | ( | void | ) |
Reload call features from features.conf.
Definition at line 7240 of file features.c.
References ast_context_destroy(), ast_context_find(), ast_mutex_lock, ast_mutex_unlock, features_reload_lock, load_config(), parking_con_dial, and registrar.
Referenced by handle_features_reload().
{
struct ast_context *con;
int res;
ast_mutex_lock(&features_reload_lock);/* Searialize reloading features.conf */
/*
* Always destroy the parking_con_dial context to remove buildup
* of recalled extensions in the context. At worst, the parked
* call gets hungup attempting to run an invalid extension when
* we are trying to callback the parker or the preset return
* extension. This is a small window of opportunity on an
* execution chain that is not expected to happen very often.
*/
con = ast_context_find(parking_con_dial);
if (con) {
ast_context_destroy(con, registrar);
}
res = load_config(1);
ast_mutex_unlock(&features_reload_lock);
return res;
}
| struct ast_call_feature* ast_find_call_feature | ( | const char * | name | ) | [read] |
look for a call feature entry by its sname
| name | a string ptr, should match "automon", "blindxfer", "atxfer", etc. |
Definition at line 3322 of file features.c.
References FEATURES_COUNT, find_dynamic_feature(), and ast_call_feature::sname.
Referenced by action_atxfer(), builtin_feature_get_exten(), featuremap_write(), handle_request_info(), and process_config().
{
int x;
for (x = 0; x < FEATURES_COUNT; x++) {
if (!strcasecmp(name, builtin_features[x].sname))
return &builtin_features[x];
}
return find_dynamic_feature(name);
}
| int ast_masq_park_call | ( | struct ast_channel * | park_me, |
| struct ast_channel * | parker, | ||
| int | timeout, | ||
| int * | extout | ||
| ) |
Park a call via a masqueraded channel.
| park_me | Channel to be parked. |
| parker | Channel parking the call. |
| timeout | is a timeout in milliseconds |
| extout | is a parameter to an int that will hold the parked location, or NULL if you want. |
Masquerade the park_me channel into a new, empty channel which is then parked.
| 0 | on success. |
| -1 | on failure. |
Definition at line 2028 of file features.c.
References ast_channel_name(), ast_park_call_args::extout, masq_park_call(), ast_park_call_args::orig_chan_name, and ast_park_call_args::timeout.
Referenced by handle_soft_key_event_message(), handle_stimulus_message(), and parkandannounce_exec().
{
struct ast_park_call_args args = {
.timeout = timeout,
.extout = extout,
};
if (peer) {
args.orig_chan_name = ast_strdupa(ast_channel_name(peer));
}
return masq_park_call(rchan, peer, &args);
}
| int ast_masq_park_call_exten | ( | struct ast_channel * | park_me, |
| struct ast_channel * | parker, | ||
| const char * | park_exten, | ||
| const char * | park_context, | ||
| int | timeout, | ||
| int * | extout | ||
| ) |
Park a call via a masqueraded channel.
| park_me | Channel to be parked. |
| parker | Channel parking the call. |
| park_exten | Parking lot access extension |
| park_context | Parking lot context |
| timeout | is a timeout in milliseconds |
| extout | is a parameter to an int that will hold the parked location, or NULL if you want. |
Masquerade the park_me channel into a new, empty channel which is then parked.
| 0 | on success. |
| -1 | on failure. |
Definition at line 1974 of file features.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_channel_name(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), create_dynamic_parkinglot(), feature_group_exten::exten, ast_park_call_args::extout, find_parkinglot(), get_parking_exten(), masq_park_call(), ast_park_call_args::orig_chan_name, parkeddynamic, ast_park_call_args::parkinglot, parkinglot_unref(), parse(), park_app_args::pl_name, and ast_park_call_args::timeout.
Referenced by __analog_ss_thread(), analog_ss_thread(), and mgcp_ss().
{
int res;
char *parse;
const char *app_data;
struct ast_exten *exten;
struct park_app_args app_args;
struct ast_park_call_args args = {
.timeout = timeout,
.extout = extout,
};
if (parker) {
args.orig_chan_name = ast_strdupa(ast_channel_name(parker));
}
if (!park_exten || !park_context) {
return masq_park_call(park_me, parker, &args);
}
/*
* Determiine if the specified park extension has an exclusive
* parking lot to use.
*/
if (parker && parker != park_me) {
ast_autoservice_start(park_me);
}
exten = get_parking_exten(park_exten, parker, park_context);
if (exten) {
app_data = ast_get_extension_app_data(exten);
if (!app_data) {
app_data = "";
}
parse = ast_strdupa(app_data);
AST_STANDARD_APP_ARGS(app_args, parse);
if (!ast_strlen_zero(app_args.pl_name)) {
/* Find the specified exclusive parking lot */
args.parkinglot = find_parkinglot(app_args.pl_name);
if (!args.parkinglot && parkeddynamic) {
args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
}
}
}
if (parker && parker != park_me) {
ast_autoservice_stop(park_me);
}
res = masq_park_call(park_me, parker, &args);
if (args.parkinglot) {
parkinglot_unref(args.parkinglot);
}
return res;
}
| int ast_park_call | ( | struct ast_channel * | park_me, |
| struct ast_channel * | parker, | ||
| int | timeout, | ||
| const char * | park_exten, | ||
| int * | extout | ||
| ) |
Park a call and read back parked location.
| park_me | Channel to be parked. |
| parker | Channel parking the call. |
| timeout | is a timeout in milliseconds |
| park_exten | Parking lot access extension (Not used) |
| extout | is a parameter to an int that will hold the parked location, or NULL if you want. |
Park the park_me channel, and read back the parked location to the parker channel. If the call is not picked up within a specified period of time, then the call will return to the last step that it was in (in terms of exten, priority and context).
| 0 | on success. |
| -1 | on failure. |
Definition at line 1877 of file features.c.
References ast_park_call_args::extout, park_call_full(), and ast_park_call_args::timeout.
{
struct ast_park_call_args args = {
.timeout = timeout,
.extout = extout,
};
return park_call_full(park_me, parker, &args);
}
| int ast_park_call_exten | ( | struct ast_channel * | park_me, |
| struct ast_channel * | parker, | ||
| const char * | park_exten, | ||
| const char * | park_context, | ||
| int | timeout, | ||
| int * | extout | ||
| ) |
Park a call and read back parked location.
| park_me | Channel to be parked. |
| parker | Channel parking the call. |
| park_exten | Parking lot access extension |
| park_context | Parking lot context |
| timeout | is a timeout in milliseconds |
| extout | is a parameter to an int that will hold the parked location, or NULL if you want. |
Park the park_me channel, and read back the parked location to the parker channel. If the call is not picked up within a specified period of time, then the call will return to the last step that it was in (in terms of exten, priority and context).
| 0 | on success. |
| -1 | on failure. |
Definition at line 1826 of file features.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), create_dynamic_parkinglot(), feature_group_exten::exten, ast_park_call_args::extout, find_parkinglot(), get_parking_exten(), park_call_full(), parkeddynamic, ast_park_call_args::parkinglot, parkinglot_unref(), parse(), park_app_args::pl_name, and ast_park_call_args::timeout.
Referenced by iax_park_thread(), and sip_park_thread().
{
int res;
char *parse;
const char *app_data;
struct ast_exten *exten;
struct park_app_args app_args;
struct ast_park_call_args args = {
.timeout = timeout,
.extout = extout,
};
if (!park_exten || !park_context) {
return park_call_full(park_me, parker, &args);
}
/*
* Determiine if the specified park extension has an exclusive
* parking lot to use.
*/
if (parker && parker != park_me) {
ast_autoservice_start(park_me);
}
exten = get_parking_exten(park_exten, parker, park_context);
if (exten) {
app_data = ast_get_extension_app_data(exten);
if (!app_data) {
app_data = "";
}
parse = ast_strdupa(app_data);
AST_STANDARD_APP_ARGS(app_args, parse);
if (!ast_strlen_zero(app_args.pl_name)) {
/* Find the specified exclusive parking lot */
args.parkinglot = find_parkinglot(app_args.pl_name);
if (!args.parkinglot && parkeddynamic) {
args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
}
}
}
if (parker && parker != park_me) {
ast_autoservice_stop(park_me);
}
res = park_call_full(park_me, parker, &args);
if (args.parkinglot) {
parkinglot_unref(args.parkinglot);
}
return res;
}
| int ast_parking_ext_valid | ( | const char * | exten_str, |
| struct ast_channel * | chan, | ||
| const char * | context | ||
| ) |
Determine if parking extension exists in a given context.
| 0 | if extension does not exist |
| 1 | if extension does exist |
Definition at line 981 of file features.c.
References get_parking_exten().
Referenced by __analog_ss_thread(), analog_ss_thread(), dp_lookup(), handle_request_refer(), mgcp_ss(), and socket_process_helper().
{
return get_parking_exten(exten_str, chan, context) ? 1 : 0;
}
| int ast_pickup_call | ( | struct ast_channel * | chan | ) |
Pickup a call.
| chan | channel that initiated pickup. |
Walk list of channels, checking it is not itself, channel is pbx one, check that the callgroup for both channels are the same and the channel is ringing. Answer calling channel, flag channel as answered on queue, masq channels together.
< Potential pickup target
Definition at line 7875 of file features.c.
References ast_answer(), ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_debug, ast_do_pickup(), ast_log(), ast_pickup_find_by_group(), ast_stream_and_wait(), ast_strlen_zero(), LOG_NOTICE, LOG_WARNING, pbx_builtin_setvar_helper(), pickupfailsound, and pickupsound.
Referenced by __analog_ss_thread(), analog_ss_thread(), cb_events(), handle_call_outgoing(), mgcp_ss(), and sip_pickup_thread().
{
struct ast_channel *target;/*!< Potential pickup target */
int res = -1;
ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));
/* The found channel is already locked. */
target = ast_pickup_find_by_group(chan);
if (target) {
ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
res = ast_do_pickup(chan, target);
ast_channel_unlock(target);
if (!res) {
if (!ast_strlen_zero(pickupsound)) {
pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickupsound);
}
} else {
ast_log(LOG_WARNING, "pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
}
target = ast_channel_unref(target);
}
if (res < 0) {
ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
if (!ast_strlen_zero(pickupfailsound)) {
ast_answer(chan);
ast_stream_and_wait(chan, pickupfailsound, "");
}
}
return res;
}
| const char* ast_pickup_ext | ( | void | ) |
Determine system call pickup extension.
Definition at line 986 of file features.c.
References pickup_ext.
Referenced by __analog_ss_thread(), analog_canmatch_featurecode(), analog_ss_thread(), canmatch_featurecode(), cb_events(), get_destination(), handle_call_outgoing(), handle_feature_show(), handle_request_invite(), key_main_page(), and mgcp_ss().
{
return pickup_ext;
}
| struct ast_channel* ast_pickup_find_by_group | ( | struct ast_channel * | chan | ) | [read] |
Find a pickup channel target by group.
| chan | channel that initiated pickup. |
| target | on success. The returned channel is locked and reffed. |
| NULL | on error. |
< Candidate channels found to pickup.
< Potential pickup target
< Potential new older target
Definition at line 7808 of file features.c.
References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_container_alloc_options, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, ast_can_pickup(), ast_channel_callback(), ast_channel_creationtime(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_tvcmp(), and find_channel_by_group().
Referenced by ast_pickup_call(), and pickup_by_group().
{
struct ao2_container *candidates;/*!< Candidate channels found to pickup. */
struct ast_channel *target;/*!< Potential pickup target */
candidates = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
if (!candidates) {
return NULL;
}
/* Find all candidate targets by group. */
ast_channel_callback(find_channel_by_group, chan, candidates, 0);
/* Find the oldest pickup target candidate */
target = NULL;
for (;;) {
struct ast_channel *candidate;/*!< Potential new older target */
struct ao2_iterator iter;
iter = ao2_iterator_init(candidates, 0);
while ((candidate = ao2_iterator_next(&iter))) {
if (!target) {
/* First target. */
target = candidate;
continue;
}
if (ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
/* We have a new target. */
ast_channel_unref(target);
target = candidate;
continue;
}
ast_channel_unref(candidate);
}
ao2_iterator_destroy(&iter);
if (!target) {
/* No candidates found. */
break;
}
/* The found channel must be locked and ref'd. */
ast_channel_lock(target);
/* Recheck pickup ability */
if (ast_can_pickup(target)) {
/* This is the channel to pickup. */
break;
}
/* Someone else picked it up or the call went away. */
ast_channel_unlock(target);
ao2_unlink(candidates, target);
target = ast_channel_unref(target);
}
ao2_ref(candidates, -1);
return target;
}
| void ast_rdlock_call_features | ( | void | ) |
Definition at line 3308 of file features.c.
References ast_rwlock_rdlock, and features_lock.
Referenced by builtin_feature_get_exten(), featuremap_read(), and handle_request_info().
{
ast_rwlock_rdlock(&features_lock);
}
| void ast_register_feature | ( | struct ast_call_feature * | feature | ) |
register new feature into feature_set
| feature | an ast_call_feature object which contains a keysequence and a callback function which is called when this keysequence is pressed during a call. |
Definition at line 3152 of file features.c.
References ast_log(), AST_RWLIST_INSERT_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_verb, ast_call_feature::feature_entry, LOG_NOTICE, and ast_call_feature::sname.
Referenced by process_applicationmap_line().
{
if (!feature) {
ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
return;
}
AST_RWLIST_WRLOCK(&feature_list);
AST_RWLIST_INSERT_HEAD(&feature_list,feature,feature_entry);
AST_RWLIST_UNLOCK(&feature_list);
ast_verb(2, "Registered Feature '%s'\n",feature->sname);
}
| void ast_unlock_call_features | ( | void | ) |
Definition at line 3313 of file features.c.
References ast_rwlock_unlock, and features_lock.
Referenced by builtin_feature_get_exten(), featuremap_read(), and handle_request_info().
{
ast_rwlock_unlock(&features_lock);
}
| void ast_unregister_feature | ( | struct ast_call_feature * | feature | ) |
unregister feature from feature_set
| feature | the ast_call_feature object which was registered before |
Definition at line 3232 of file features.c.
References ast_free, AST_RWLIST_REMOVE, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.
{
if (!feature) {
return;
}
AST_RWLIST_WRLOCK(&feature_list);
AST_RWLIST_REMOVE(&feature_list, feature, feature_entry);
AST_RWLIST_UNLOCK(&feature_list);
ast_free(feature);
}