Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 310878) +++ include/asterisk/channel.h (working copy) @@ -193,7 +193,7 @@ * as caller ID on numeric E.164 phone networks (DAHDI or SIP/IAX2 to pstn gateway). * * \todo Implement settings for transliteration between UTF8 caller ID names in - * to Ascii Caller ID's (DAHDI). Östen Åsklund might be transliterated into + * to Ascii Caller ID's (DAHDI). ï¿œsten ï¿œsklund might be transliterated into * Osten Asklund or Oesten Aasklund depending upon language and person... * We need automatic routines for incoming calls and static settings for * our own accounts. @@ -656,6 +656,14 @@ AST_SOFTHANGUP_APPUNLOAD = (1 << 4), AST_SOFTHANGUP_EXPLICIT = (1 << 5), AST_SOFTHANGUP_UNBRIDGE = (1 << 6), + + /*! + * \brief All softhangup flags. + * + * This can be used as an argument to ast_channel_softhangup_clear + * to clear all softhangup flags from a channel. + */ + AST_SOFTHANGUP_ALL = (0xFFFFFFFF) }; @@ -985,6 +993,20 @@ */ int ast_softhangup_nolock(struct ast_channel *chan, int reason); +/*! + * \brief Clear a set of softhangup flags from a channel + * + * Never clear a softhangup flag from a channel directly. Instead, + * use this function. This ensures that all aspects of the softhangup + * process are aborted. + * + * \param chan the channel to clear the flag on + * \param flag the flag or flags to clear + * + * \return Nothing. + */ +void ast_channel_clear_softhangup(struct ast_channel *chan, int flag); + /*! \brief Check to see if a channel is needing hang up * \param chan channel on which to check for hang up * This function determines if the channel is being requested to be hung up. Index: include/asterisk/frame.h =================================================================== --- include/asterisk/frame.h (revision 310878) +++ include/asterisk/frame.h (working copy) @@ -318,6 +318,7 @@ AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */ AST_CONTROL_T38_PARAMETERS = 24, /*!< T38 state change request/notification with parameters */ AST_CONTROL_SRCCHANGE = 25, /*!< Media source has changed and requires a new RTP SSRC */ + AST_CONTROL_END_OF_Q = 29, /*!< Indicate that this position was the end of the channel queue for a softhangup. */ }; enum ast_control_t38 { Index: main/channel.c =================================================================== --- main/channel.c (revision 310878) +++ main/channel.c (working copy) @@ -1013,13 +1013,40 @@ ast_channel_lock(chan); - /* See if the last frame on the queue is a hangup, if so don't queue anything */ - if ((cur = AST_LIST_LAST(&chan->readq)) && - (cur->frametype == AST_FRAME_CONTROL) && - (cur->subclass == AST_CONTROL_HANGUP)) { + /* + * Check the last frame on the queue if we are queuing the new + * frames after it. + */ + cur = AST_LIST_LAST(&chan->readq); + if (cur && cur->frametype == AST_FRAME_CONTROL && !head && (!after || after == cur)) { + switch (cur->subclass) { + case AST_CONTROL_END_OF_Q: + if (fin->frametype == AST_FRAME_CONTROL + && fin->subclass == AST_CONTROL_HANGUP) { + /* + * Destroy the end-of-Q marker frame so we can queue the hangup + * frame in its place. + */ + AST_LIST_REMOVE(&chan->readq, cur, frame_list); + ast_frfree(cur); + + /* + * This has degenerated to a normal queue append anyway. Since + * we just destroyed the last frame in the queue we must make + * sure that "after" is NULL or bad things will happen. + */ + after = NULL; + break; + } + /* Fall through */ + case AST_CONTROL_HANGUP: + /* Don't queue anything. */ ast_channel_unlock(chan); return 0; + default: + break; } + } /* Build copies of all the frames and count them */ AST_LIST_HEAD_INIT_NOLOCK(&frames); @@ -1717,6 +1744,31 @@ return; } +void ast_channel_clear_softhangup(struct ast_channel *chan, int flag) +{ + ast_channel_lock(chan); + + chan->_softhangup &= ~flag; + + if (!chan->_softhangup) { + struct ast_frame *fr; + + /* If we have completely cleared the softhangup flag, + * then we need to fully abort the hangup process. This requires + * pulling the END_OF_Q frame out of the channel frame queue if it + * still happens to be there. */ + + fr = AST_LIST_LAST(&chan->readq); + if (fr && fr->frametype == AST_FRAME_CONTROL && + fr->subclass == AST_CONTROL_END_OF_Q) { + AST_LIST_REMOVE(&chan->readq, fr, frame_list); + ast_frfree(fr); + } + } + + ast_channel_unlock(chan); +} + /*! \brief Softly hangup a channel, don't lock */ int ast_softhangup_nolock(struct ast_channel *chan, int cause) { @@ -2740,8 +2792,8 @@ * it can mark the end of the read queue. If there are frames to be read, * ast_queue_control will be called repeatedly, but will only queue one hangup * frame. */ - if (ast_check_hangup(chan)) { - ast_queue_control(chan, AST_CONTROL_HANGUP); + if (chan->_softhangup) { + ast_queue_control(chan, AST_CONTROL_END_OF_Q); } else { goto done; } @@ -2862,13 +2914,22 @@ } } - /* Interpret hangup and return NULL */ + /* Interpret hangup and end-of-Q frames to return NULL */ /* XXX why not the same for frames from the channel ? */ - if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) { + if (f->frametype == AST_FRAME_CONTROL) { + switch (f->subclass) { + case AST_CONTROL_HANGUP: + chan->_softhangup |= AST_SOFTHANGUP_DEV; cause = f->data.uint32; + /* Fall through */ + case AST_CONTROL_END_OF_Q: ast_frfree(f); f = NULL; + break; + default: + break; } + } } else { chan->blocker = pthread_self(); if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) { @@ -3232,6 +3293,7 @@ case AST_CONTROL_HANGUP: case AST_CONTROL_T38_PARAMETERS: case _XXX_AST_CONTROL_T38: + case AST_CONTROL_END_OF_Q: break; case AST_CONTROL_CONGESTION: @@ -3340,6 +3402,7 @@ case AST_CONTROL_RING: case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: + case AST_CONTROL_END_OF_Q: /* Nothing left to do for these. */ res = 0; break; @@ -5132,9 +5195,9 @@ ast_jb_get_and_deliver(c0, c1); if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) { if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE) - c0->_softhangup = 0; + ast_channel_clear_softhangup(c0, AST_SOFTHANGUP_UNBRIDGE); if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) - c1->_softhangup = 0; + ast_channel_clear_softhangup(c1, AST_SOFTHANGUP_UNBRIDGE); c0->_bridge = c1; c1->_bridge = c0; } @@ -5449,9 +5512,9 @@ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) { if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE) - c0->_softhangup = 0; + ast_channel_clear_softhangup(c0, AST_SOFTHANGUP_UNBRIDGE); if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) - c1->_softhangup = 0; + ast_channel_clear_softhangup(c1, AST_SOFTHANGUP_UNBRIDGE); c0->_bridge = c1; c1->_bridge = c0; ast_debug(1, "Unbridge signal received. Ending native bridge.\n"); Index: main/features.c =================================================================== --- main/features.c (revision 310878) +++ main/features.c (working copy) @@ -390,7 +390,7 @@ ast_parseable_goto(xferchan, goto_on_transfer); xferchan->_state = AST_STATE_UP; ast_clear_flag(xferchan, AST_FLAGS_ALL); - xferchan->_softhangup = 0; + ast_channel_clear_softhangup(xferchan, AST_SOFTHANGUP_ALL); if ((f = ast_read(xferchan))) { ast_frfree(f); f = NULL; Index: main/pbx.c =================================================================== --- main/pbx.c (revision 310878) +++ main/pbx.c (working copy) @@ -4250,7 +4250,7 @@ keep reading digits until we can't possibly get a right answer anymore. */ digit = ast_waitfordigit(c, waittime); if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) { - c->_softhangup = 0; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO); } else { if (!digit) /* No entry */ break; @@ -4321,14 +4321,14 @@ set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */ memset(&c->whentohangup, 0, sizeof(c->whentohangup)); - c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT); } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) { pbx_builtin_raise_exception(c, "ABSOLUTETIMEOUT"); /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */ memset(&c->whentohangup, 0, sizeof(c->whentohangup)); - c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT); } else if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) { - c->_softhangup = 0; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO); continue; } else if (ast_check_hangup(c)) { ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n", @@ -4373,13 +4373,13 @@ } if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) { - c->_softhangup = 0; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO); continue; } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) { set_ext_pri(c, "T", 1); /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */ memset(&c->whentohangup, 0, sizeof(c->whentohangup)); - c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT); continue; } else { if (c->cdr) @@ -4417,7 +4417,7 @@ } } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) { /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */ - c->_softhangup = 0; + ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT); } else { /* keypress received, get more digits for a full extension */ int waittime = 0; if (digit)