Merged revisions 152535 via svnmerge from
authorSteve Murphy <murf@digium.com>
Wed, 29 Oct 2008 05:01:00 +0000 (05:01 +0000)
committerSteve Murphy <murf@digium.com>
Wed, 29 Oct 2008 05:01:00 +0000 (05:01 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r152535 | murf | 2008-10-28 22:36:32 -0600 (Tue, 28 Oct 2008) | 46 lines

The magic trick to avoid this crash is not to
try to find the channel by name in the list,
which is slow and resource consuming, but rather
to pay attention to the result codes from the
ast_bridge_call, to which I added the
AST_PBX_NO_HANGUP_PEER_PARKED value, which
now are returned when a channel is parked.
Why? because CDR's aren't generated via parking,
so nothing is needed, but if a transfer occurred,
there are critical things I need.

If you get AST_PBX_KEEPALIVE,
then don't touch the channel pointer.

If you get AST_PBX_NO_HANGUP_PEER, or
AST_PBX_NO_HANGUP_PEER_PARKED, then don't
touch the peer pointer.

Updated the several places where the results
from a bridge were not being properly obeyed,
and fixed some code I had introduced so that
the results of the bridge were not overridden
(in trunk).

All the places that previously tested for
AST_PBX_NO_HANGUP_PEER now have to check for
both AST_PBX_NO_HANGUP_PEER and AST_PBX_NO_HANGUP_PEER_PARKED.

I tested this against the 4 common parking
scenarios:

1. A calls B; B answers; A parks B; B hangs up while A is getting the parking
slot announcement, immediately after being put on hold.

2. A calls B; B answers; A parks B; B hangs up after A has been hung up, but
before the park times out.

3. A calls B; B answers; B parks A; A hangs up while B is getting the parking slot announcement, immediately after being put on hold.

4. A calls B; B answers; B parks A; A hangs up after B has been hung up, but before the park times out.

No crash.

I also ran the scenarios above against valgrind, and accesses looked good.

........

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@152536 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c
apps/app_queue.c
include/asterisk/features.h
include/asterisk/pbx.h
main/features.c

index a3dba3c..deaa824 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2008, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
  *
  * Mark Spencer <markster@digium.com>
  *
@@ -111,7 +111,8 @@ static char *descrip =
 "           DTMF string is sent to the called party, and the 'calling' DTMF\n"
 "           string is sent to the calling party. Both parameters can be used\n"
 "           alone.\n"
 "           DTMF string is sent to the called party, and the 'calling' DTMF\n"
 "           string is sent to the calling party. Both parameters can be used\n"
 "           alone.\n"
-"    e    - execute the 'h' extension for peer after the call ends\n"
+"    e    - execute the 'h' extension for peer after the call ends. This\n"
+"           operation will not be performed if the peer was parked\n"
 "    f    - Force the callerid of the *calling* channel to be set as the\n"
 "           extension associated with the channel using a dialplan 'hint'.\n"
 "           For example, some PSTNs do not allow CallerID to be set to anything\n"
 "    f    - Force the callerid of the *calling* channel to be set as the\n"
 "           extension associated with the channel using a dialplan 'hint'.\n"
 "           For example, some PSTNs do not allow CallerID to be set to anything\n"
@@ -1793,16 +1794,17 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        struct ast_app *theapp;
                        const char *gosub_result;
                        char *gosub_args, *gosub_argstart;
                        struct ast_app *theapp;
                        const char *gosub_result;
                        char *gosub_args, *gosub_argstart;
+                       int res9 = -1;
 
 
-                       res = ast_autoservice_start(chan);
-                       if (res) {
+                       res9 = ast_autoservice_start(chan);
+                       if (res9) {
                                ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
                                ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
-                               res = -1;
+                               res9 = -1;
                        }
 
                        theapp = pbx_findapp("Gosub");
 
                        }
 
                        theapp = pbx_findapp("Gosub");
 
-                       if (theapp && !res) {
+                       if (theapp && !res9) {
                                replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
 
                                /* Set where we came from */
                                replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
 
                                /* Set where we came from */
@@ -1820,50 +1822,50 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                                }
 
                                if (gosub_args) {
                                }
 
                                if (gosub_args) {
-                                       res = pbx_exec(peer, theapp, gosub_args);
+                                       res9 = pbx_exec(peer, theapp, gosub_args);
                                        ast_pbx_run(peer);
                                        ast_free(gosub_args);
                                        if (option_debug)
                                        ast_pbx_run(peer);
                                        ast_free(gosub_args);
                                        if (option_debug)
-                                               ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+                                               ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res9);
                                } else
                                        ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
 
                                } else
                                        ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
 
-                               res = 0;
-                       } else if (!res) {
+                               res9 = 0;
+                       } else if (!res9) {
                                ast_log(LOG_ERROR, "Could not find application Gosub\n");
                                ast_log(LOG_ERROR, "Could not find application Gosub\n");
-                               res = -1;
+                               res9 = -1;
                        }
 
                        if (ast_autoservice_stop(chan) < 0) {
                                ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
                        }
 
                        if (ast_autoservice_stop(chan) < 0) {
                                ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
-                               res = -1;
+                               res9 = -1;
                        }
                        
                        ast_channel_lock(peer);
 
                        }
                        
                        ast_channel_lock(peer);
 
-                       if (!res && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
+                       if (!res9 && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
                                char *gosub_transfer_dest;
 
                                if (!strcasecmp(gosub_result, "BUSY")) {
                                        ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
                                        ast_set_flag64(peerflags, OPT_GO_ON);
                                char *gosub_transfer_dest;
 
                                if (!strcasecmp(gosub_result, "BUSY")) {
                                        ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
                                        ast_set_flag64(peerflags, OPT_GO_ON);
-                                       res = -1;
+                                       res9 = -1;
                                } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
                                        ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
                                        ast_set_flag64(peerflags, OPT_GO_ON);
                                } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
                                        ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
                                        ast_set_flag64(peerflags, OPT_GO_ON);
-                                       res = -1;
+                                       res9 = -1;
                                } else if (!strcasecmp(gosub_result, "CONTINUE")) {
                                        /* hangup peer and keep chan alive assuming the macro has changed
                                           the context / exten / priority or perhaps
                                           the next priority in the current exten is desired.
                                        */
                                        ast_set_flag64(peerflags, OPT_GO_ON);
                                } else if (!strcasecmp(gosub_result, "CONTINUE")) {
                                        /* hangup peer and keep chan alive assuming the macro has changed
                                           the context / exten / priority or perhaps
                                           the next priority in the current exten is desired.
                                        */
                                        ast_set_flag64(peerflags, OPT_GO_ON);
-                                       res = -1;
+                                       res9 = -1;
                                } else if (!strcasecmp(gosub_result, "ABORT")) {
                                        /* Hangup both ends unless the caller has the g flag */
                                } else if (!strcasecmp(gosub_result, "ABORT")) {
                                        /* Hangup both ends unless the caller has the g flag */
-                                       res = -1;
+                                       res9 = -1;
                                } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
                                } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
-                                       res = -1;
+                                       res9 = -1;
                                        /* perform a transfer to a new extension */
                                        if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
                                                replace_macro_delimiter(gosub_transfer_dest);
                                        /* perform a transfer to a new extension */
                                        if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
                                                replace_macro_delimiter(gosub_transfer_dest);
@@ -1952,32 +1954,34 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
                pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
 
                snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
                pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
 
-               if (ast_test_flag64(&opts, OPT_PEER_H)) {
+               if (res != AST_PBX_NO_HANGUP_PEER_PARKED && ast_test_flag64(&opts, OPT_PEER_H)) {
                        ast_log(LOG_NOTICE, "PEER context: %s; PEER exten: %s;  PEER priority: %d\n",
                                peer->context, peer->exten, peer->priority);
                }
                        ast_log(LOG_NOTICE, "PEER context: %s; PEER exten: %s;  PEER priority: %d\n",
                                peer->context, peer->exten, peer->priority);
                }
+               if (res != AST_PBX_NO_HANGUP_PEER_PARKED)
+                       strcpy(peer->context, chan->context);
 
 
-               strcpy(peer->context, chan->context);
-
-               if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) {
+               if (res != AST_PBX_NO_HANGUP_PEER_PARKED && ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) {
                        int autoloopflag;
                        int found;
                        int autoloopflag;
                        int found;
+                       int res9;
+                       
                        strcpy(peer->exten, "h");
                        peer->priority = 1;
                        autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
                        ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP);
 
                        strcpy(peer->exten, "h");
                        peer->priority = 1;
                        autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
                        ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP);
 
-                       while ((res = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1)) == 0)
+                       while ((res9 = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1)) == 0)
                                peer->priority++;
 
                                peer->priority++;
 
-                       if (found && res) {
+                       if (found && res9) {
                                /* Something bad happened, or a hangup has been requested. */
                                ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
                                ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
                        }
                        ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP);  /* set it back the way it was */
                }
                                /* Something bad happened, or a hangup has been requested. */
                                ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
                                ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
                        }
                        ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP);  /* set it back the way it was */
                }
-               if (res != AST_PBX_NO_HANGUP_PEER) {
+               if (res != AST_PBX_NO_HANGUP_PEER && res != AST_PBX_NO_HANGUP_PEER_PARKED) {
                        if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {          
                                replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
                                ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
                        if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {          
                                replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
                                ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
@@ -1990,23 +1994,28 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                }
        }
 out:
                }
        }
 out:
-       if (moh) {
-               moh = 0;
-               ast_moh_stop(chan);
-       } else if (sentringing) {
-               sentringing = 0;
-               ast_indicate(chan, -1);
-       }
-       ast_channel_early_bridge(chan, NULL);
-       hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
-       pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
-       senddialendevent(chan, pa.status);
-       ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
-
-       if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE) && (res != AST_PBX_INCOMPLETE)) {
-               if (calldurationlimit)
-                       memset(&chan->whentohangup, 0, sizeof(chan->whentohangup));
-               res = 0;
+       /* cleaning up chan is not a good idea here if AST_PBX_KEEPALIVE
+          is returned; chan will get the love it needs from another
+          thread */
+       if (res != AST_PBX_KEEPALIVE) {
+               if (moh) {
+                       moh = 0;
+                       ast_moh_stop(chan);
+               } else if (sentringing) {
+                       sentringing = 0;
+                       ast_indicate(chan, -1);
+               }
+               ast_channel_early_bridge(chan, NULL);
+               hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
+               pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
+               senddialendevent(chan, pa.status);
+               ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
+
+               if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE) && (res != AST_PBX_INCOMPLETE)) {
+                       if (calldurationlimit)
+                               memset(&chan->whentohangup, 0, sizeof(chan->whentohangup));
+                       res = 0;
+               }
        }
 
 done:
        }
 
 done:
index 030dab0..d9df46f 100644 (file)
@@ -3757,21 +3757,24 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
                 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
                 */
                /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
                 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
                 */
-               if (!attended_transfer_occurred(qe->chan)) {
+               if (bridge != AST_PBX_KEEPALIVE && !attended_transfer_occurred(qe->chan)) {
                        struct ast_datastore *transfer_ds;
                        if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
                                        qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
                                        (long) (time(NULL) - callstart), qe->opos);
                        struct ast_datastore *transfer_ds;
                        if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
                                        qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
                                        (long) (time(NULL) - callstart), qe->opos);
-                               send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
+                               if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED)
+                                       send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
                        } else if (ast_check_hangup(qe->chan)) {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
                                        (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
                        } else if (ast_check_hangup(qe->chan)) {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
                                        (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
-                               send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
+                               if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED)
+                                       send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
                        } else {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
                                        (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
                        } else {
                                ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
                                        (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
-                               send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
+                               if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED)
+                                       send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
                        }
                        ast_channel_lock(qe->chan);
                        transfer_ds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL);
                        }
                        ast_channel_lock(qe->chan);
                        transfer_ds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL);
@@ -3783,7 +3786,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                        update_queue(qe->parent, member, callcompletedinsl);
                }
 
                        update_queue(qe->parent, member, callcompletedinsl);
                }
 
-               if (bridge != AST_PBX_NO_HANGUP_PEER)
+               if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED)
                        ast_hangup(peer);
                res = bridge ? bridge : 1;
                ao2_ref(member, -1);
                        ast_hangup(peer);
                res = bridge ? bridge : 1;
                ao2_ref(member, -1);
index 723373f..20b26c3 100644 (file)
@@ -61,14 +61,15 @@ struct ast_call_feature {
 };
 
 
 };
 
 
-#define AST_FEATURE_RETURN_HANGUP            -1
-#define AST_FEATURE_RETURN_SUCCESSBREAK      0
-#define AST_FEATURE_RETURN_PBX_KEEPALIVE     AST_PBX_KEEPALIVE
-#define AST_FEATURE_RETURN_NO_HANGUP_PEER    AST_PBX_NO_HANGUP_PEER
-#define AST_FEATURE_RETURN_PASSDIGITS        21
-#define AST_FEATURE_RETURN_STOREDIGITS       22
-#define AST_FEATURE_RETURN_SUCCESS           23
-#define AST_FEATURE_RETURN_KEEPTRYING        24
+#define AST_FEATURE_RETURN_HANGUP                   -1
+#define AST_FEATURE_RETURN_SUCCESSBREAK             0
+#define AST_FEATURE_RETURN_PBX_KEEPALIVE            AST_PBX_KEEPALIVE
+#define AST_FEATURE_RETURN_NO_HANGUP_PEER           AST_PBX_NO_HANGUP_PEER
+#define AST_FEATURE_RETURN_NO_HANGUP_PEER_PARKED    AST_PBX_NO_HANGUP_PEER_PARKED
+#define AST_FEATURE_RETURN_PASSDIGITS               21
+#define AST_FEATURE_RETURN_STOREDIGITS              22
+#define AST_FEATURE_RETURN_SUCCESS                  23
+#define AST_FEATURE_RETURN_KEEPTRYING               24
 
 
 /*!
 
 
 /*!
index b4bec23..d2d95ac 100644 (file)
@@ -37,12 +37,13 @@ extern "C" {
 #define AST_PBX_REPLACE 1
 
 /*! \brief Special return values from applications to the PBX { */
 #define AST_PBX_REPLACE 1
 
 /*! \brief Special return values from applications to the PBX { */
-#define AST_PBX_HANGUP         -1      /*!< Jump to the 'h' exten */
-#define AST_PBX_OK             0       /*!< No errors */
-#define AST_PBX_ERROR          1       /*!< Jump to the 'e' exten */
-#define AST_PBX_KEEPALIVE      10      /*!< Destroy the thread, but don't hang up the channel */
-#define AST_PBX_NO_HANGUP_PEER 11
-#define AST_PBX_INCOMPLETE     12      /*!< Return to PBX matching, allowing more digits for the extension */
+#define AST_PBX_HANGUP                -1    /*!< Jump to the 'h' exten */
+#define AST_PBX_OK                     0    /*!< No errors */
+#define AST_PBX_ERROR                  1    /*!< Jump to the 'e' exten */
+#define AST_PBX_KEEPALIVE              10   /*!< Destroy the thread, but don't hang up the channel */
+#define AST_PBX_NO_HANGUP_PEER         11   /*!< The peer has been involved in a transfer */
+#define AST_PBX_INCOMPLETE             12   /*!< Return to PBX matching, allowing more digits for the extension */
+#define AST_PBX_NO_HANGUP_PEER_PARKED  13   /*!< Don't touch the peer channel - it was sent to the parking lot and might be gone by now */
 /*! } */
 
 #define PRIORITY_HINT  -1      /*!< Special Priority for a hint */
 /*! } */
 
 #define PRIORITY_HINT  -1      /*!< Special Priority for a hint */
index c5e6d92..ef07b23 100644 (file)
@@ -777,7 +777,7 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
                        res = ast_park_call(parkee, parker, 0, NULL);
                        if (!res) {
                                if (sense == FEATURE_SENSE_CHAN) {
                        res = ast_park_call(parkee, parker, 0, NULL);
                        if (!res) {
                                if (sense == FEATURE_SENSE_CHAN) {
-                                       res = AST_PBX_NO_HANGUP_PEER;
+                                       res = AST_PBX_NO_HANGUP_PEER_PARKED;
                                } else {
                                        res = AST_PBX_KEEPALIVE;
                                }
                                } else {
                                        res = AST_PBX_KEEPALIVE;
                                }
@@ -1127,7 +1127,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
                           the thread dies -- We have to be careful now though.  We are responsible for 
                           hanging up the channel, else it will never be hung up! */
 
                           the thread dies -- We have to be careful now though.  We are responsible for 
                           hanging up the channel, else it will never be hung up! */
 
-                       return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
+                       return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER_PARKED;
                } else {
                        ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
                }
                } else {
                        ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
                }
@@ -1730,6 +1730,8 @@ static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer,
        }
        else if (res == AST_PBX_NO_HANGUP_PEER)
                return AST_FEATURE_RETURN_NO_HANGUP_PEER;
        }
        else if (res == AST_PBX_NO_HANGUP_PEER)
                return AST_FEATURE_RETURN_NO_HANGUP_PEER;
+       else if (res == AST_PBX_NO_HANGUP_PEER_PARKED)
+               return AST_FEATURE_RETURN_NO_HANGUP_PEER_PARKED;
        else if (res)
                return AST_FEATURE_RETURN_SUCCESSBREAK;
        
        else if (res)
                return AST_FEATURE_RETURN_SUCCESSBREAK;
        
@@ -2377,10 +2379,12 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
 
        }
    before_you_go:
 
        }
    before_you_go:
-       new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
-       new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
 
 
-       if (!ast_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN) && ast_exists_extension(chan, chan->context, "h", 1, chan->cid.cid_num)) {
+       /* 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 (res != AST_PBX_KEEPALIVE && !ast_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN) && ast_exists_extension(chan, chan->context, "h", 1, chan->cid.cid_num)) {
                struct ast_cdr *swapper;
                char savelastapp[AST_MAX_EXTENSION];
                char savelastdata[AST_MAX_EXTENSION];
                struct ast_cdr *swapper;
                char savelastapp[AST_MAX_EXTENSION];
                char savelastdata[AST_MAX_EXTENSION];
@@ -2424,39 +2428,57 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
                ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
        }
 
                ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
        }
 
-       /* obey the NoCDR() wishes. */
-       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 (!new_chan_cdr || (new_chan_cdr && !ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
-               struct ast_channel *chan_ptr = NULL;
-               
-               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.
-               */
+       /* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
+       if (res != AST_PBX_KEEPALIVE) {
+               new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
+               if (new_chan_cdr && 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 */
+       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.
+          6. Parking has to be accounted for in the code:
+             a. Parking will cause ast_bridge_call to return
+                either AST_PBX_NO_HANGUP_PEER or AST_PBX_NO_HANGUP_PEER_PARKED;
+                        in the latter case, peer is (most likely) a bad
+                        pointer, you can no longer deref it. If it does still
+                        exist, it is under another's thread control, and
+                        could be destroyed at any time.
+          b. The same applies to AST_PBX_KEEPALIVE, in which
+                    case, the chan ptr cannot be used, as another thread
+                        owns it and may have destroyed the channel.
+             c. In the former case, you need to check peer to see if it 
+                still exists before you deref it, and obtain a lock.
+             d. In neither case should you do an ast_hangup(peer).
+                 e. Do not overwrite the result code from ast_bridge_call.
+       */
+       
+       if (res != AST_PBX_KEEPALIVE && new_chan_cdr) {
+               struct ast_channel *chan_ptr = NULL;
  
                if (strcasecmp(orig_channame, chan->name) != 0) { 
                        /* old channel */
  
                if (strcasecmp(orig_channame, chan->name) != 0) { 
                        /* old channel */
@@ -2479,28 +2501,35 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
                } else {
                        ast_cdr_specialized_reset(chan_cdr,0); /* nothing changed, reset the chan_cdr  */
                }
                } else {
                        ast_cdr_specialized_reset(chan_cdr,0); /* nothing changed, reset the chan_cdr  */
                }
-               if (strcasecmp(orig_peername, peer->name) != 0) { 
-                       /* old channel */
-                       chan_ptr = ast_get_channel_by_name_locked(orig_peername);
-                       if (chan_ptr) {
-                               if (!ast_bridged_channel(chan_ptr)) {
-                                       struct ast_cdr *cur;
-                                       for (cur = chan_ptr->cdr; cur; cur = cur->next) {
+       }
+       
+       if (res != AST_PBX_NO_HANGUP_PEER_PARKED) { /* if the peer was involved in a park, don't even touch it; it's probably gone */
+               struct ast_channel *chan_ptr = NULL;
+               new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* 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, peer->name) != 0) { 
+                       /* old channel */
+                       chan_ptr = ast_get_channel_by_name_locked(orig_peername);
+                       if (chan_ptr) {
+                               if (!ast_bridged_channel(chan_ptr)) {
+                                       struct ast_cdr *cur;
+                                       for (cur = chan_ptr->cdr; cur; cur = cur->next) {
                                                if (cur == peer_cdr) {
                                                if (cur == peer_cdr) {
-                                                       break;
-                                               }
-                                       }
-                                       if (cur)
-                                               ast_cdr_specialized_reset(peer_cdr,0);
-                               }
-                               ast_channel_unlock(chan_ptr);
-                       }
-                       /* new channel */
-                       ast_cdr_specialized_reset(new_peer_cdr,0);
-               } else {
-                       ast_cdr_specialized_reset(peer_cdr,0); /* nothing changed, reset the peer_cdr  */
-               }
-       }
+                                                       break;
+                                               }
+                                       }
+                                       if (cur)
+                                               ast_cdr_specialized_reset(peer_cdr,0);
+                               }
+                               ast_channel_unlock(chan_ptr);
+                       }
+                       /* new channel */
+                       ast_cdr_specialized_reset(new_peer_cdr,0);
+               } else {
+                       ast_cdr_specialized_reset(peer_cdr,0); /* nothing changed, reset the peer_cdr  */
+               }
+       }
        return res;
 }
 
        return res;
 }
 
@@ -2955,7 +2984,7 @@ static int park_exec_full(struct ast_channel *chan, void *data, struct ast_parki
                ast_cdr_setdestchan(chan->cdr, peer->name);
 
                /* Simulate the PBX hanging up */
                ast_cdr_setdestchan(chan->cdr, peer->name);
 
                /* Simulate the PBX hanging up */
-               if (res != AST_PBX_NO_HANGUP_PEER)
+               if (res != AST_PBX_NO_HANGUP_PEER && res != AST_PBX_NO_HANGUP_PEER_PARKED)
                        ast_hangup(peer);
                return res;
        } else {
                        ast_hangup(peer);
                return res;
        } else {