From aa58c91fbed6cbf18fc959a70c56cc3fb6752629 Mon Sep 17 00:00:00 2001 From: Steve Murphy Date: Thu, 11 Sep 2008 22:55:49 +0000 Subject: [PATCH] (closes issue #13364) Reported by: mdu113 Well, fundamentally, the problems revealed in 13364 are because of the ForkCDR call that is done before the dial. When the bridge is in place, it's dealing with the first (and wrong) cdr in the list. So, I wrote a little func to zip down to the first non-locked cdr in the chain, and thru-out the ast_bridge_call, these results are used instead of raw chan->cdr and peer->cdr pointers. This shouldn't affect anyone who isn't forking cdrs before a dial, and should correct the cdr's of those that do. So, this change ends up correcting the dstchannel and userfield; the disposition was fixed by a previous patch, it was OK coming into this problem. git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@142575 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- res/res_features.c | 64 +++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/res/res_features.c b/res/res_features.c index c8975158f0..fa1dd39644 100644 --- a/res/res_features.c +++ b/res/res_features.c @@ -1394,6 +1394,18 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call return chan; } +static struct ast_cdr *pick_unlocked_cdr(struct ast_cdr *cdr) +{ + struct ast_cdr *cdr_orig = cdr; + while (cdr) { + if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED)) + return cdr; + cdr = cdr->next; + } + return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */ +} + + int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config) { /* Copy voice back and forth between the two channels. Give the peer @@ -1413,6 +1425,8 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast struct ast_bridge_config backup_config; struct ast_cdr *bridge_cdr = NULL; struct ast_cdr *orig_peer_cdr = NULL; + struct ast_cdr *chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */ + struct ast_cdr *peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */ memset(&backup_config, 0, sizeof(backup_config)); @@ -1450,14 +1464,14 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast ast_copy_string(orig_channame,chan->name,sizeof(orig_channame)); ast_copy_string(orig_peername,peer->name,sizeof(orig_peername)); - orig_peer_cdr = peer->cdr; + orig_peer_cdr = peer_cdr; - if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) { + if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) { - if (chan->cdr) { - ast_set_flag(chan->cdr, AST_CDR_FLAG_MAIN); + if (chan_cdr) { + ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN); ast_cdr_update(chan); - bridge_cdr = ast_cdr_dup(chan->cdr); + bridge_cdr = ast_cdr_dup(chan_cdr); ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp)); ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata)); } else { @@ -1475,31 +1489,31 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast /* Destination information */ ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst)); ast_copy_string(bridge_cdr->dcontext, chan->context, 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)); + 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); } } - /* peer->cdr->answer will be set when a macro runs on the peer; + /* 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. */ - if (peer->cdr && !ast_tvzero(peer->cdr->answer)) { - bridge_cdr->answer = peer->cdr->answer; - chan->cdr->answer = peer->cdr->answer; - bridge_cdr->disposition = peer->cdr->disposition; - chan->cdr->disposition = peer->cdr->disposition; + if (peer_cdr && !ast_tvzero(peer_cdr->answer)) { + bridge_cdr->answer = peer_cdr->answer; + chan_cdr->answer = peer_cdr->answer; + bridge_cdr->disposition = peer_cdr->disposition; + chan_cdr->disposition = peer_cdr->disposition; } else { ast_cdr_answer(bridge_cdr); - ast_cdr_answer(chan->cdr); /* for the sake of cli status checks */ + ast_cdr_answer(chan_cdr); /* for the sake of cli status checks */ } - ast_set_flag(chan->cdr, AST_CDR_FLAG_BRIDGED); - if (peer->cdr) { - ast_set_flag(peer->cdr, AST_CDR_FLAG_BRIDGED); + ast_set_flag(chan_cdr, AST_CDR_FLAG_BRIDGED); + if (peer_cdr) { + ast_set_flag(peer_cdr, AST_CDR_FLAG_BRIDGED); } } @@ -1669,21 +1683,23 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast } before_you_go: /* obey the NoCDR() wishes. */ - if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) { + if (chan_cdr && ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED) && peer_cdr && !ast_test_flag(peer_cdr, AST_CDR_FLAG_POST_DISABLED)) + ast_set_flag(peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */ + if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) { ast_cdr_end(bridge_cdr); ast_cdr_detach(bridge_cdr); /* just in case, these channels get bridged again before hangup */ - if (chan->cdr) { - ast_cdr_specialized_reset(chan->cdr,0); + if (chan_cdr) { + ast_cdr_specialized_reset(chan_cdr,0); } - if (peer->cdr) { + if (peer_cdr) { struct ast_cdr *cur; ast_channel_lock(peer); - for (cur = peer->cdr; cur; cur = cur->next) { + for (cur = peer_cdr; cur; cur = cur->next) { if (cur == orig_peer_cdr) { break; } @@ -1700,7 +1716,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast backend, just in case the cdr.conf file is calling for unanswered CDR's. */ - /* When peer->cdr isn't the same addr as orig_peer_cdr, + /* When peer_cdr isn't the same addr as orig_peer_cdr, this can only happen if there was a transfer, methinks; at any rate, only pay attention to the original*/ if (ast_cdr_isset_unanswered()) {