summaryrefslogtreecommitdiff
path: root/main/features.c
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2012-06-29 17:02:32 +0000
committerRichard Mudgett <rmudgett@digium.com>2012-06-29 17:02:32 +0000
commitac35b92b628064a1fcf962895f0befd57aff0442 (patch)
tree0f2a4fb769643d0ae24dfed49468d80e8652aabf /main/features.c
parent35c533156cf94b005bd7c561d69e40dc5e837dd6 (diff)
Hangup handlers - Dialplan subroutines that run when the channel hangs up.
Hangup handlers are an alternative to the h extension. They can be used in addition to the h extension. The idea is to attach a Gosub routine to a channel that will execute when the call hangs up. Whereas which h extension gets executed depends on the location of dialplan execution when the call hangs up, hangup handlers are attached to the call channel. You can attach multiple handlers that will execute in the order of most recently added first. (closes issue ASTERISK-19549) Reported by: Mark Murawski Tested by: rmudgett Review: https://reviewboard.asterisk.org/r/2002/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@369493 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/features.c')
-rw-r--r--main/features.c139
1 files changed, 58 insertions, 81 deletions
diff --git a/main/features.c b/main/features.c
index 79d1036f2..b602dbb98 100644
--- a/main/features.c
+++ b/main/features.c
@@ -1077,10 +1077,10 @@ static void *bridge_call_thread(void *data)
ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", ast_channel_name(tobj->peer));
if (ast_pbx_start(tobj->peer)) {
ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", ast_channel_name(tobj->peer));
- ast_hangup(tobj->peer);
+ ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
}
} else {
- ast_hangup(tobj->peer);
+ ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
}
if (!ast_check_hangup(tobj->chan)) {
ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan));
@@ -2526,7 +2526,7 @@ static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
if (ast_channel_make_compatible(c, newchan) < 0) {
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
ast_channel_name(c), ast_channel_name(newchan));
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(c, newchan);
return -1;
}
return 0;
@@ -2762,7 +2762,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
}
if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferer, newchan);
if (ast_stream_and_wait(transferer, xfersound, "")) {
ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
}
@@ -2882,7 +2882,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
/* newchan is up, we should prepare transferee and bridge them */
if (ast_check_hangup(newchan)) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -2908,7 +2908,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
if (!xferchan) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -2922,7 +2922,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
if (ast_channel_masquerade(xferchan, transferee)) {
ast_hangup(xferchan);
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -4169,7 +4169,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
int diff;
int hasfeatures=0;
int hadfeatures=0;
- int autoloopflag;
int sendingdtmfdigit = 0;
int we_disabled_peer_cdr = 0;
struct ast_option_header *aoh;
@@ -4179,7 +4178,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
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;
- const char *h_context;
+ /*! 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));
@@ -4611,105 +4611,82 @@ before_you_go:
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_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
- h_context = NULL;
- } else 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))) {
- h_context = ast_channel_context(chan);
- } 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))) {
- h_context = ast_channel_macrocontext(chan);
- } else {
- h_context = NULL;
- }
- if (h_context) {
+ if (!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;
- int found = 0; /* set if we find at least one match */
- int spawn_error = 0;
- /*
- * Make sure that the channel is marked as hungup since we are
- * going to run the "h" exten on it.
- */
- ast_softhangup(chan, AST_SOFTHANGUP_APPUNLOAD);
-
- autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- if (bridge_cdr && ast_opt_end_cdr_before_h_exten) {
- ast_cdr_end(bridge_cdr);
- }
-
- /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
- dialplan code operate on it */
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_channel_cdr_set(chan, bridge_cdr);
}
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);
- if (h_context != ast_channel_context(chan)) {
- ast_channel_context_set(chan, h_context);
- }
- ast_channel_exten_set(chan, "h");
- ast_channel_priority_set(chan, 1);
ast_channel_unlock(chan);
- while ((spawn_error = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
- ast_channel_priority(chan),
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
- &found, 1)) == 0) {
- ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
- }
- if (found && spawn_error) {
- /* Something bad happened, or a hangup has been requested. */
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(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);
- /* swap it back */
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;
}
}
- /* An "h" exten has been run, so indicate that one has been run. */
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
ast_channel_unlock(chan);
-
- /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
- if (bridge_cdr) {
- ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
- ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
- }
- ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
}
/* 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 we've gone through the h extension block above, the CDR got swapped so don't overwrite
- * what was done in the h extension. What a mess. This is why you never touch CDR code. */
- if (new_chan_cdr && bridge_cdr && !h_context) {
+
+ /*
+ * 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;
@@ -5540,7 +5517,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
break;
}
if (res) {
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
parkinglot_unref(parkinglot);
return -1;
}
@@ -5549,7 +5526,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
res = ast_channel_make_compatible(chan, peer);
if (res < 0) {
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(peer));
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
parkinglot_unref(parkinglot);
return -1;
}
@@ -5601,7 +5578,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
/* Simulate the PBX hanging up */
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
} else {
if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark",
@@ -8014,7 +7991,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
"Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
/* Maybe we should return this channel to the PBX? */
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
current_dest_chan = ast_channel_unref(current_dest_chan);
@@ -8088,7 +8065,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority + 1);
}
if (goto_opt || ast_pbx_start(final_dest_chan)) {
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
@@ -8097,16 +8074,16 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
if (ast_pbx_start(final_dest_chan)) {
ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan));
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
} else {
ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan));
}
} else {
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
} else {
ast_debug(1, "chan %s was hungup\n", ast_channel_name(final_dest_chan));
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
done:
ast_free((char *) bconfig.warning_sound);