Index: asterisk1.8/apps/app_meetme.c =================================================================== --- asterisk1.8/apps/app_meetme.c (revision 376948) +++ asterisk1.8/apps/app_meetme.c (working copy) @@ -3219,6 +3219,11 @@ if (c) { char dtmfstr[2] = ""; + /* we must lock channel here because c->fds[0] is updated in the middle of do_masquerade(). If that + * takes place on another thread we must wait for the masquerade process to fully complete + * before proceeding + */ + ast_channel_lock(c); if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) { if (using_pseudo) { /* Kill old pseudo */ @@ -3228,8 +3233,69 @@ ast_debug(1, "Ooh, something swapped out under us, starting over\n"); retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0); user->dahdichannel = !retrydahdi; + + /* CDR on the channel might be marked as AST_CDR_NULL which will prevent any CDR logging from + * taking place on this channel. This is bad because it might be a trunk for which billsec is + * required. This can occur if a channel in the conference does a Dial() (e.g. because of SLAStaion()) + * and on connection this causes the original channel to be bridged to the new channel. The function + * ast_bridge_call() performs this, which causes a do_masquerade() to occur. Masquerading just swaps + * the CDR pointers in the channel structure, one side effect of this is that the uniqueID in the CDR + * does not match the uniqueID in the channel. ast_bridge_call() function later does a + * ast_cdr_specialized_reset() on the original and bridged channel CDRs. This zero's out some + * CDR fields, doesn't fix the uniqueID, and sets AST_CDR_NULL. Quite why it does this I don't know + * but it means that we need to fix it up here. + * + * Unfortunately we might have got to here on this thread after the masquerading (which caused fds[0] != origfd) + * but before the ast_cdr_specialized_reset() in ast_bridge_call() on another thread. If that occurs + * then we have missed our opportunity to fix the CDR (disposition will not yet be set to AST_CDR_NULL). + * Ideally we don't want to be here while another thread is inside ast_bridge_call() and should wait until + * it exits... so that any pending calls to ast_cdr_specialized_reset() can take place. I can not figure out + * any way to do that, so... + * Major hack is to sleep here for, say 50ms, giving other threads a chance to do their thing! + */ + if (c->cdr->disposition != AST_CDR_NULL) { + /* This should rarely occur */ + ast_log(LOG_WARNING, "Channel changed in Meetme Conference to %s and CDR disposition %s\n", c->name, ast_cdr_disp2str(c->cdr->disposition)); + ast_channel_unlock(c); + ast_safe_sleep(c,50); + ast_channel_lock(c); + } + + if (c->cdr->disposition == AST_CDR_NULL) { + ast_debug(1, "CDR disposition for %s is %ld reset its CDR record\n", c->name, c->cdr->disposition); + /* Re-initialize the CDR currently on the channel as it is bogus (wrong UniqueID, etc) */ + ast_cdr_init(c->cdr,c); + /* Start timestamps the CDR start field with now, if not already set */ + if (ast_tvzero(c->cdr->start)) + ast_cdr_start(c->cdr); + /* And we want to mark the CDR as answered as it enters the Meetme conference (now!) */ + ast_cdr_answer(c->cdr); + } + + /* If channel type is "Local" then we should do the same to the other side of the local channel */ + if (strcasecmp(c->tech->type, "Local") == 0) { + struct ast_channel *c2; + char *name; + name = ast_strdupa(c->name); + if (name[strlen(name)-1] == '1') name[strlen(name)-1] = '2'; + else name[strlen(name)-1] = '1'; + c2 = ast_channel_get_by_name(name); + if (c2 && (c2->cdr->disposition == AST_CDR_NULL)) { + ast_channel_lock(c2); + ast_debug(1, "CDR disposition for %s is %ld reset its CDR record\n", c2->name, c2->cdr->disposition); + ast_cdr_init(c2->cdr,c2); + if (ast_tvzero(c2->cdr->start)) + ast_cdr_start(c2->cdr); + ast_cdr_answer(c2->cdr); + ast_channel_unlock(c2); + } + } + + ast_channel_unlock(c); /* because we locked it above and are heading into a 'goto' */ goto dahdiretry; } + ast_channel_unlock(c); /* because we locked it above */ + if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) { f = ast_read_noaudio(c); } else { @@ -6135,6 +6201,8 @@ struct ast_flags64 conf_flags = { 0 }; struct sla_trunk_ref *trunk_ref = args->trunk_ref; int caller_is_saved; + struct ast_cdr *orig_cdr = NULL; + struct ast_channel *chan2 = NULL; struct ast_party_caller caller; if (!(dial = ast_dial_create())) { @@ -6161,6 +6229,10 @@ caller = trunk_ref->chan->caller; ast_party_caller_init(&trunk_ref->chan->caller); } + else { + /* Save copy of CDR record that we can use on hangup */ + orig_cdr = ast_cdr_dup(trunk_ref->chan->cdr); + } dial_res = ast_dial_run(dial, trunk_ref->chan, 1); @@ -6227,6 +6299,85 @@ /* If the trunk is going away, it is definitely now IDLE. */ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL); + /* Fixup the Caller ID for the trunk channel if we saved original CDR that initiated SLAStation(). + * During the course of a outbound SLA call, dialplan may have issued a new Dial() to connect to an outside + * party over a SIP trunk. This may be issued direcly in the dialplan or through a Disa() application. + * When this occurs the Meetme conference sees the channel change in the conference because the external SIP + * Channel is bridged into the local channel through channel masquerade. The CDR associated with the new + * outbound channel has lost information like who (which SLAStation channel/extension) initiated the call + * in the first place. This is important if you need to use CDR records for billing purposes. + * The following code fixes the CDR for the outbound trunk channel to better record who initiated + * the call and the number dialed. + */ + + /* if we are in a "Local" channel, find the other side of the channel as well */ + if (strcasecmp(trunk_ref->trunk->chan->tech->type, "Local") == 0) { + char *name; + name = ast_strdupa(trunk_ref->trunk->chan->name); + if (name[strlen(name)-1] == '1') name[strlen(name)-1] = '2'; + else name[strlen(name)-1] = '1'; + ast_debug(1, "Find %s the other side of local channel %s\n", name, trunk_ref->trunk->chan->name); + chan2 = ast_channel_get_by_name(name); + } + + if (orig_cdr) { + struct ast_channel *c = trunk_ref->trunk->chan; + struct ast_cdr *cdr = c->cdr; + char *dst; + + ast_debug(1, "Fixup CDR for channel %s; disposition %ld; Tech type %s\n", c->name, cdr->disposition, c->tech->type); + /* while messing with CDR we should lock the channel */ + ast_channel_lock(c); + /* Set destination channel */ + ast_copy_string(cdr->dstchannel, c->name, sizeof(cdr->dstchannel)); + /* and the source channel should be set to the channel that originated the SLA outbound call */ + ast_copy_string(cdr->channel, orig_cdr->channel, sizeof(cdr->channel)); + /* The destination should be the number that the caller dialed. Rather than use the number dialed + * from the original CDR (which might just have been a special extension to connect to a DISA() + * dialtone) we want the number that was actually sent out to the SIP trunk. If we have been through + * a Dial() then we could use DIALEDPEERNUMBER else we will use original destination. + */ + dst = (char *)S_OR(pbx_builtin_getvar_helper(c, "DIALEDPEERNUMBER"), orig_cdr->dst); + /* destination may have channel name in front of the number dialed, separated by '/', skip over that */ + ast_copy_string(cdr->dst, S_COR(strchr(dst,'/'),&strchr(dst,'/')[1],dst), sizeof(cdr->dst)); + /* The CallerID string, if it exists, is likely the ID of the destination */ + ast_cdr_setvar(cdr, "dnid", cdr->clid, 0); + /* The source and CallerID of the originator should be set to that of the extension that originated the call */ + ast_copy_string(cdr->src, orig_cdr->src, sizeof(cdr->src)); + ast_copy_string(cdr->clid, orig_cdr->clid, sizeof(cdr->clid)); + /* end the CDR */ + ast_cdr_end(cdr); + ast_channel_unlock(c); + + /* Now do the same for other side of a local channel */ + if (chan2) { + cdr = chan2->cdr; + ast_debug(1, "Fixup CDR for channel %s; disposition %ld; Tech type %s\n", chan2->name, cdr->disposition, chan2->tech->type); + ast_channel_lock(chan2); + ast_copy_string(cdr->dstchannel, chan2->name, sizeof(cdr->dstchannel)); + ast_copy_string(cdr->channel, orig_cdr->channel, sizeof(cdr->channel)); + dst = (char *)S_OR(pbx_builtin_getvar_helper(chan2, "DIALEDPEERNUMBER"), orig_cdr->dst); + ast_copy_string(cdr->dst, S_COR(strchr(dst,'/'),&strchr(dst,'/')[1],dst), sizeof(cdr->dst)); + ast_cdr_setvar(cdr, "dnid", cdr->clid, 0); + ast_copy_string(cdr->src, orig_cdr->src, sizeof(cdr->src)); + ast_copy_string(cdr->clid, orig_cdr->clid, sizeof(cdr->clid)); + ast_cdr_end(cdr); + ast_channel_unlock(chan2); + } + + /* Now we are done with the original CDR record we saved */ + ast_cdr_free(orig_cdr); + orig_cdr = NULL; + } + else { + ast_debug(1, "End channel CDR for %s without any fixup\n", trunk_ref->trunk->chan->name); + ast_cdr_end(trunk_ref->trunk->chan->cdr); + if (chan2) { + ast_debug(1, "End channel CDR for %s without any fixup\n", chan2->name); + ast_cdr_end(chan2->cdr); + } + } + trunk_ref->trunk->chan = NULL; trunk_ref->trunk->on_hold = 0;