Skip to content
Snippets Groups Projects
channel.c 114 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	clone->_softhangup = AST_SOFTHANGUP_DEV;
    
    	/* And of course, so does our current state.  Note we need not
    	   call ast_setstate since the event manager doesn't really consider
    	   these separate.  We do this early so that the clone has the proper
    	   state of the original channel. */
    	origstate = original->_state;
    	original->_state = clone->_state;
    	clone->_state = origstate;
    
    
    	if (clone->tech->fixup){
    		res = clone->tech->fixup(original, clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res) 
    			ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Start by disconnecting the original's physical side */
    
    	if (clone->tech->hangup)
    		res = clone->tech->hangup(clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res) {
    		ast_log(LOG_WARNING, "Hangup failed!  Strange things may happen!\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Mangle the name of the clone channel */
    
    	ast_string_field_set(clone, name, zombn);
    
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clone->uniqueid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Update the type. */
    
    	t_pvt = original->monitor;
    	original->monitor = clone->monitor;
    	clone->monitor = t_pvt;
    
    	
    	/* Keep the same language.  */
    
    	ast_string_field_set(original, language, clone->language);
    
    	/* Copy the FD's other than the generator fd */
    
    	for (x = 0; x < AST_MAX_FDS; x++) {
    
    		if (x != AST_GENERATOR_FD)
    			original->fds[x] = clone->fds[x];
    
    	clone_variables(original, clone);
    
    	AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Presense of ADSI capable CPE follows clone */
    	original->adsicpe = clone->adsicpe;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Bridge remains the same */
    	/* CDR fields remain the same */
    	/* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
    	/* Application and data remain the same */
    
    	/* Clone exception  becomes real one, as with fdno */
    
    	ast_copy_flags(original, clone, AST_FLAG_EXCEPTION);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	original->fdno = clone->fdno;
    	/* Schedule context remains the same */
    	/* Stream stuff stays the same */
    
    	/* Keep the original state.  The fixup code will need to work with it most likely */
    
    	/* Just swap the whole structures, nevermind the allocations, they'll work themselves
    	   out. */
    	tmpcid = original->cid;
    	original->cid = clone->cid;
    	clone->cid = tmpcid;
    
    	/* Restore original timing file descriptor */
    
    	original->fds[AST_TIMING_FD] = original->timingfd;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Our native formats are different now */
    	original->nativeformats = clone->nativeformats;
    	
    
    	/* Context, extension, priority, app data, jump table,  remain the same */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* pvt switches.  pbx stays the same, as does next */
    	
    	/* Set the write format */
    
    	ast_set_write_format(original, wformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Set the read format */
    
    	ast_set_read_format(original, rformat);
    
    	/* Copy the music class */
    
    	ast_string_field_set(original, musicclass, clone->musicclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_log(LOG_DEBUG, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Okay.  Last thing is to let the channel driver know about all this mess, so he
    	   can fix up everything as best as possible */
    
    	if (original->tech->fixup) {
    		res = original->tech->fixup(clone, original);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res) {
    
    			ast_log(LOG_WARNING, "Channel for type '%s' could not fixup channel %s\n",
    
    				original->tech->type, original->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	} else
    
    		ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)!  Bad things may happen.\n",
    
    			original->tech->type, original->name);
    
    	
    	/* Now, at this point, the "clone" channel is totally F'd up.  We mark it as
    	   a zombie so nothing tries to touch it.  If it's already been marked as a
    	   zombie, then free it now (since it already is considered invalid). */
    
    	if (ast_test_flag(clone, AST_FLAG_ZOMBIE)) {
    
    		ast_log(LOG_DEBUG, "Destroying channel clone '%s'\n", clone->name);
    
    		manager_event(EVENT_FLAG_CALL, "Hangup", 
    			"Channel: %s\r\n"
    			"Uniqueid: %s\r\n"
    			"Cause: %d\r\n"
    			"Cause-txt: %s\r\n",
    			clone->name, 
    			clone->uniqueid, 
    			clone->hangupcause,
    			ast_cause2str(clone->hangupcause)
    			);
    
    	} else {
    		ast_log(LOG_DEBUG, "Released clone lock on '%s'\n", clone->name);
    
    		ast_set_flag(clone, AST_FLAG_ZOMBIE);
    
    		ast_queue_frame(clone, &ast_null_frame);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Signal any blocker */
    
    	if (ast_test_flag(original, AST_FLAG_BLOCKING))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_kill(original->blocker, SIGURG);
    
    	ast_log(LOG_DEBUG, "Done Masquerading %s (%d)\n", original->name, original->_state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    void ast_set_callerid(struct ast_channel *chan, const char *callerid, const char *calleridname, const char *ani)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (callerid) {
    
    		if (chan->cid.cid_num)
    			free(chan->cid.cid_num);
    		if (ast_strlen_zero(callerid))
    			chan->cid.cid_num = NULL;
    		else
    			chan->cid.cid_num = strdup(callerid);
    	}
    	if (calleridname) {
    		if (chan->cid.cid_name)
    			free(chan->cid.cid_name);
    		if (ast_strlen_zero(calleridname))
    			chan->cid.cid_name = NULL;
    		else
    			chan->cid.cid_name = strdup(calleridname);
    	}
    	if (ani) {
    		if (chan->cid.cid_ani)
    			free(chan->cid.cid_ani);
    		if (ast_strlen_zero(ani))
    			chan->cid.cid_ani = NULL;
    		else
    			chan->cid.cid_ani = strdup(ani);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if (chan->cdr)
    		ast_cdr_setcid(chan->cdr, chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Newcallerid", 
    				"Channel: %s\r\n"
    
    				"CallerID: %s\r\n"
    				"CallerIDName: %s\r\n"
    
    				"Uniqueid: %s\r\n"
    				"CID-CallingPres: %d (%s)\r\n",
    
    				chan->name, chan->cid.cid_num ? 
    				chan->cid.cid_num : "<Unknown>",
    				chan->cid.cid_name ? 
    				chan->cid.cid_name : "<Unknown>",
    
    				chan->uniqueid,
    				chan->cid.cid_pres,
    				ast_describe_caller_presentation(chan->cid.cid_pres)
    				);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_setstate(struct ast_channel *chan, int state)
    {
    
    	int oldstate = chan->_state;
    
    	if (oldstate == state)
    		return 0;
    
    	chan->_state = state;
    
    	manager_event(EVENT_FLAG_CALL,
    		      (oldstate == AST_STATE_DOWN) ? "Newchannel" : "Newstate",
    		      "Channel: %s\r\n"
    		      "State: %s\r\n"
    		      "CallerID: %s\r\n"
    		      "CallerIDName: %s\r\n"
    		      "Uniqueid: %s\r\n",
    		      chan->name, ast_state2str(chan->_state), 
    		      chan->cid.cid_num ? chan->cid.cid_num : "<unknown>", 
    		      chan->cid.cid_name ? chan->cid.cid_name : "<unknown>", 
    		      chan->uniqueid);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    /*! \brief Find bridged channel */
    
    struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
    {
    	struct ast_channel *bridged;
    	bridged = chan->_bridge;
    
    	if (bridged && bridged->tech->bridged_channel) 
    		bridged = bridged->tech->bridged_channel(chan, bridged);
    
    static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, const char *sound, int remain) 
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int min = 0, sec = 0, check;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (check) 
    
    	if (remain > 0) {
    		if (remain / 60 > 1) {
    
    			min = remain / 60;
    			sec = remain % 60;
    
    	if (!strcmp(sound,"timeleft")) {	/* Queue support */
    
    Russell Bryant's avatar
    Russell Bryant committed
    		ast_streamfile(chan, "vm-youhave", chan->language);
    		ast_waitstream(chan, "");
    
    Russell Bryant's avatar
    Russell Bryant committed
    			ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, (char *) NULL);
    			ast_streamfile(chan, "queue-minutes", chan->language);
    			ast_waitstream(chan, "");
    
    Russell Bryant's avatar
    Russell Bryant committed
    			ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, (char *) NULL);
    			ast_streamfile(chan, "queue-seconds", chan->language);
    			ast_waitstream(chan, "");
    
    Russell Bryant's avatar
    Russell Bryant committed
    		ast_streamfile(chan, sound, chan->language);
    		ast_waitstream(chan, "");
    
    static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
    
    						 struct ast_bridge_config *config, struct ast_frame **fo,
    						 struct ast_channel **rc, struct timeval bridge_end)
    
    	/* Copy voice back and forth between the two channels. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *cs[3];
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *who = NULL;
    
    	enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
    
    	int o0nativeformats;
    	int o1nativeformats;
    
    	int watch_c0_dtmf;
    	int watch_c1_dtmf;
    
    	
    	cs[0] = c0;
    	cs[1] = c1;
    
    	o0nativeformats = c0->nativeformats;
    	o1nativeformats = c1->nativeformats;
    
    	watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
    	watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
    
    
    	for (;;) {
    
    		if ((c0->tech_pvt != pvt0) || (c1->tech_pvt != pvt1) ||
    
    		    (o0nativeformats != c0->nativeformats) ||
    		    (o1nativeformats != c1->nativeformats)) {
    
    			/* Check for Masquerade, codec changes, etc */
    
    			res = AST_BRIDGE_RETRY;
    
    		if (bridge_end.tv_sec) {
    			to = ast_tvdiff_ms(bridge_end, ast_tvnow());
    			if (to <= 0) {
    				res = AST_BRIDGE_RETRY;
    				break;
    			}
    		} else
    			to = -1;
    
    		who = ast_waitfor_n(cs, 2, &to);
    
    		if (!who) {
    			ast_log(LOG_DEBUG, "Nobody there, continuing...\n"); 
    
    			if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
    				if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    
    					c0->_softhangup = 0;
    				if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    					c1->_softhangup = 0;
    
    			continue;
    		}
    		f = ast_read(who);
    		if (!f) {
    			*fo = NULL;
    			*rc = who;
    
    			res = AST_BRIDGE_COMPLETE;
    
    			ast_log(LOG_DEBUG, "Didn't get a frame from channel: %s\n",who->name);
    			break;
    		}
    
    		if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) ||
    			    (f->subclass == AST_CONTROL_VIDUPDATE)) {
    
    				ast_indicate(who == c0 ? c1 : c0, f->subclass);
    			} else {
    				*fo = f;
    				*rc = who;
    
    				res =  AST_BRIDGE_COMPLETE;
    
    				ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
    				break;
    			}
    		}
    		if ((f->frametype == AST_FRAME_VOICE) ||
    
    		    (f->frametype == AST_FRAME_DTMF) ||
    		    (f->frametype == AST_FRAME_VIDEO) || 
    		    (f->frametype == AST_FRAME_IMAGE) ||
    		    (f->frametype == AST_FRAME_HTML) ||
    
    #if defined(T38_SUPPORT)
    		    (f->frametype == AST_FRAME_MODEM) ||
    #endif
    
    		    (f->frametype == AST_FRAME_TEXT)) {
    			if (f->frametype == AST_FRAME_DTMF) {
    				if (((who == c0) && watch_c0_dtmf) ||
    				    ((who == c1) && watch_c1_dtmf)) {
    					*rc = who;
    					*fo = f;
    					res = AST_BRIDGE_COMPLETE;
    					ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
    					break;
    				} else {
    					goto tackygoto;
    
    				}
    			} else {
    #if 0
    				ast_log(LOG_DEBUG, "Read from %s\n", who->name);
    				if (who == last) 
    					ast_log(LOG_DEBUG, "Servicing channel %s twice in a row?\n", last->name);
    				last = who;
    #endif
    tackygoto:
    
    				ast_write((who == c0) ? c1 : c0, f);
    
    			}
    		}
    		ast_frfree(f);
    
    		/* Swap who gets priority */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    	return res;
    }
    
    
    /*! \brief Bridge two channels together */
    
    enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1,
    					  struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc) 
    
    {
    	struct ast_channel *who = NULL;
    
    	enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int nativefailed=0;
    
    	int firstpass;
    
    	int o0nativeformats;
    	int o1nativeformats;
    
    	long time_left_ms=0;
    	struct timeval nexteventts = { 0, };
    
    	char caller_warning = 0;
    	char callee_warning = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    	/* Stop if we're a zombie or need a soft hangup */
    	if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
    	    ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) 
    		return -1;
    
    	*fo = NULL;
    	firstpass = config->firstpass;
    	config->firstpass = 0;
    
    	if (ast_tvzero(config->start_time))
    		config->start_time = ast_tvnow();
    	time_left_ms = config->timelimit;
    
    	caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
    	callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
    
    	if (config->start_sound && firstpass) {
    		if (caller_warning)
    			bridge_playfile(c0, c1, config->start_sound, time_left_ms / 1000);
    		if (callee_warning)
    			bridge_playfile(c1, c0, config->start_sound, time_left_ms / 1000);
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Keep track of bridge */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	manager_event(EVENT_FLAG_CALL, "Link", 
    
    		      "Channel1: %s\r\n"
    		      "Channel2: %s\r\n"
    		      "Uniqueid1: %s\r\n"
    		      "Uniqueid2: %s\r\n"
    		      "CallerID1: %s\r\n"
    		      "CallerID2: %s\r\n",
    		      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    
    	o0nativeformats = c0->nativeformats;
    
    	o1nativeformats = c1->nativeformats;
    
    
    	if (config->timelimit) {
    		nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
    		if (caller_warning || callee_warning)
    			nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (/* ever */;;) {
    
    		if (config->timelimit) {
    
    			struct timeval now;
    			now = ast_tvnow();
    			to = ast_tvdiff_ms(nexteventts, now);
    			if (to < 0)
    				to = 0;
    			time_left_ms = config->timelimit - ast_tvdiff_ms(now, config->start_time);
    			if (time_left_ms < to)
    				to = time_left_ms;
    
    
    			if (time_left_ms <= 0) {
    
    				if (caller_warning && config->end_sound)
    					bridge_playfile(c0, c1, config->end_sound, 0);
    				if (callee_warning && config->end_sound)
    					bridge_playfile(c1, c0, config->end_sound, 0);
    
    Anthony Minessale II's avatar
    Anthony Minessale II committed
    				*fo = NULL;
    
    Anthony Minessale II's avatar
    Anthony Minessale II committed
    				res = 0;
    
    			
    			if (!to) {
    				if (time_left_ms >= 5000) {
    
    					/* force the time left to round up if appropriate */
    
    					if (caller_warning && config->warning_sound && config->play_warning)
    
    						bridge_playfile(c0, c1, config->warning_sound,
    								(time_left_ms + 500) / 1000);
    
    					if (callee_warning && config->warning_sound && config->play_warning)
    
    						bridge_playfile(c1, c0, config->warning_sound,
    								(time_left_ms + 500) / 1000);
    
    				}
    				if (config->warning_freq) {
    					nexteventts = ast_tvadd(nexteventts, ast_samp2tv(config->warning_freq, 1000));
    				} else
    					nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
    
    
    		if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
    			if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    				c0->_softhangup = 0;
    			if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    				c1->_softhangup = 0;
    			c0->_bridge = c1;
    			c1->_bridge = c0;
    
    			ast_log(LOG_DEBUG, "Unbridge signal received. Ending native bridge.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Stop if we're a zombie or need a soft hangup */
    
    		if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
    		    ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*fo = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = 0;
    
    			ast_log(LOG_DEBUG, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",
    				c0->name, c1->name,
    				ast_test_flag(c0, AST_FLAG_ZOMBIE) ? "Yes" : "No",
    				ast_check_hangup(c0) ? "Yes" : "No",
    				ast_test_flag(c1, AST_FLAG_ZOMBIE) ? "Yes" : "No",
    				ast_check_hangup(c1) ? "Yes" : "No");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    		}
    
    
    		if (c0->tech->bridge &&
    		    (config->timelimit == 0) &&
    		    (c0->tech->bridge == c1->tech->bridge) &&
    
    		    !nativefailed && !c0->monitor && !c1->monitor &&
    		    !c0->spies && !c1->spies) {
    			/* Looks like they share a bridge method and nothing else is in the way */
    
    			ast_set_flag(c0, AST_FLAG_NBRIDGE);
    			ast_set_flag(c1, AST_FLAG_NBRIDGE);
    
    			if ((res = c0->tech->bridge(c0, c1, config->flags, fo, rc, to)) == AST_BRIDGE_COMPLETE) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				manager_event(EVENT_FLAG_CALL, "Unlink", 
    
    					      "Channel1: %s\r\n"
    					      "Channel2: %s\r\n"
    					      "Uniqueid1: %s\r\n"
    					      "Uniqueid2: %s\r\n"
    					      "CallerID1: %s\r\n"
    					      "CallerID2: %s\r\n",
    					      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    				ast_log(LOG_DEBUG, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name);
    
    
    				ast_clear_flag(c0, AST_FLAG_NBRIDGE);
    				ast_clear_flag(c1, AST_FLAG_NBRIDGE);
    
    
    				if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    
    					continue;
    
    
    				c0->_bridge = NULL;
    				c1->_bridge = NULL;
    
    				return res;
    
    			} else {
    				ast_clear_flag(c0, AST_FLAG_NBRIDGE);
    				ast_clear_flag(c1, AST_FLAG_NBRIDGE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			switch (res) {
    			case AST_BRIDGE_RETRY:
    
    				continue;
    
    			default:
    
    					ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s ended\n",
    
    				/* fallthrough */
    			case AST_BRIDGE_FAILED_NOWARN:
    
    				nativefailed++;
    
    		if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) ||
    		    (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
    		    !(c0->generator || c1->generator)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ast_channel_make_compatible(c0, c1)) {
    				ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
    
                                    manager_event(EVENT_FLAG_CALL, "Unlink",
    
    					      "Channel1: %s\r\n"
    					      "Channel2: %s\r\n"
    					      "Uniqueid1: %s\r\n"
    					      "Uniqueid2: %s\r\n"
    					      "CallerID1: %s\r\n"
    					      "CallerID2: %s\r\n",
    					      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    				return AST_BRIDGE_FAILED;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			o0nativeformats = c0->nativeformats;
    			o1nativeformats = c1->nativeformats;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		res = ast_generic_bridge(c0, c1, config, fo, rc, nexteventts);
    
    		if (res != AST_BRIDGE_RETRY)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	manager_event(EVENT_FLAG_CALL, "Unlink",
    
    		      "Channel1: %s\r\n"
    		      "Channel2: %s\r\n"
    		      "Uniqueid1: %s\r\n"
    		      "Uniqueid2: %s\r\n"
    		      "CallerID1: %s\r\n"
    		      "CallerID2: %s\r\n",
    		      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    	ast_log(LOG_DEBUG, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Sets an option on a channel */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
    {
    	int res;
    
    	if (chan->tech->setoption) {
    		res = chan->tech->setoption(chan, option, data, datalen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res < 0)
    			return res;
    	} else {
    		errno = ENOSYS;
    		return -1;
    	}
    	if (block) {
    		/* XXX Implement blocking -- just wait for our option frame reply, discarding
    		   intermediate packets. XXX */
    		ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct tonepair_def {
    	int freq1;
    	int freq2;
    	int duration;
    	int vol;
    };
    
    struct tonepair_state {
    	float freq1;
    	float freq2;
    	float vol;
    	int duration;
    	int pos;
    	int origwfmt;
    	struct ast_frame f;
    	unsigned char offset[AST_FRIENDLY_OFFSET];
    	short data[4000];
    };
    
    static void tonepair_release(struct ast_channel *chan, void *params)
    {
    	struct tonepair_state *ts = params;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan) {
    
    		ast_set_write_format(chan, ts->origwfmt);
    
    static void *tonepair_alloc(struct ast_channel *chan, void *params)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct tonepair_state *ts;
    	struct tonepair_def *td = params;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	ts = calloc(1, sizeof(struct tonepair_state));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!ts)
    		return NULL;
    	ts->origwfmt = chan->writeformat;
    
    	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
    		tonepair_release(NULL, ts);
    		ts = NULL;
    	} else {
    		ts->freq1 = td->freq1;
    		ts->freq2 = td->freq2;
    		ts->duration = td->duration;
    		ts->vol = td->vol;
    	}
    	/* Let interrupts interrupt :) */
    
    	ast_set_flag(chan, AST_FLAG_WRITE_INT);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ts;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int tonepair_generator(struct ast_channel *chan, void *data, int len, int samples)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct tonepair_state *ts = data;
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* we need to prepare a frame with 16 * timelen samples as we're 
    	 * generating SLIN audio
    	 */
    	len = samples * 2;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (len > sizeof(ts->data) / 2 - 1) {
    		ast_log(LOG_WARNING, "Can't generate that much data!\n");
    		return -1;
    	}
    	memset(&ts->f, 0, sizeof(ts->f));
    
    	for (x = 0; x < (len / 2); x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ts->data[x] = ts->vol * (
    				sin((ts->freq1 * 2.0 * M_PI / 8000.0) * (ts->pos + x)) +
    				sin((ts->freq2 * 2.0 * M_PI / 8000.0) * (ts->pos + x))
    			);
    	}
    	ts->f.frametype = AST_FRAME_VOICE;
    	ts->f.subclass = AST_FORMAT_SLINEAR;
    	ts->f.datalen = len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ts->f.samples = samples;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ts->f.offset = AST_FRIENDLY_OFFSET;
    	ts->f.data = ts->data;
    	ast_write(chan, &ts->f);
    	ts->pos += x;
    	if (ts->duration > 0) {
    		if (ts->pos >= ts->duration * 8)
    			return -1;
    	}
    	return 0;
    }
    
    static struct ast_generator tonepair = {
    	alloc: tonepair_alloc,
    	release: tonepair_release,
    	generate: tonepair_generator,
    };
    
    int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
    {
    	struct tonepair_def d = { 0, };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	d.freq1 = freq1;
    	d.freq2 = freq2;
    	d.duration = duration;
    	if (vol < 1)
    		d.vol = 8192;
    	else
    		d.vol = vol;
    	if (ast_activate_generator(chan, &tonepair, &d))
    		return -1;
    	return 0;
    }
    
    void ast_tonepair_stop(struct ast_channel *chan)
    {
    	ast_deactivate_generator(chan);
    }
    
    int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
    {
    	struct ast_frame *f;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
    		return res;
    
    	/* Give us some wiggle room */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while (chan->generatordata && (ast_waitfor(chan, 100) >= 0)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		f = ast_read(chan);
    		if (f)
    			ast_frfree(f);
    		else
    			return -1;
    	}
    	return 0;
    }
    
    ast_group_t ast_get_group(char *s)
    
    {
    	char *copy;
    	char *piece;
    	char *c=NULL;
    
    	int start=0, finish=0, x;
    
    	ast_group_t group = 0;
    
    	c = copy = ast_strdupa(s);
    	if (!copy)
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ((piece = strsep(&c, ","))) {
    
    		if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
    			/* Range */
    		} else if (sscanf(piece, "%d", &start)) {
    			/* Just one */
    			finish = start;
    		} else {
    
    			ast_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", s, piece);
    
    		for (x = start; x <= finish; x++) {
    
    			if ((x > 63) || (x < 0)) {
    				ast_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
    
    				group |= ((ast_group_t) 1 << x);
    
    static int (*ast_moh_start_ptr)(struct ast_channel *, const char *) = NULL;
    
    static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
    
    static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
    
    void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, const char *),
    				 void (*stop_ptr)(struct ast_channel *),
    				 void (*cleanup_ptr)(struct ast_channel *))
    
    {
    	ast_moh_start_ptr = start_ptr;
    	ast_moh_stop_ptr = stop_ptr;
    
    }
    
    void ast_uninstall_music_functions(void) 
    {
    	ast_moh_start_ptr = NULL;
    	ast_moh_stop_ptr = NULL;
    
    /*! \brief Turn on music on hold on a given channel */
    
    int ast_moh_start(struct ast_channel *chan, const char *mclass) 
    
    	if (ast_moh_start_ptr)
    
    		return ast_moh_start_ptr(chan, mclass);
    
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : "default");
    	
    	return 0;
    }
    
    
    /*! \brief Turn off music on hold on a given channel */
    
    void ast_moh_stop(struct ast_channel *chan) 
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (ast_moh_stop_ptr)
    
    		ast_moh_stop_ptr(chan);
    }
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (ast_moh_cleanup_ptr)
    		ast_moh_cleanup_ptr(chan);
    
    void ast_channels_init(void)
    {
    	ast_cli_register(&cli_show_channeltypes);
    
    	ast_cli_register(&cli_show_channeltype);
    
    /*! \brief Print call group and pickup group ---*/
    
    char *ast_print_group(char *buf, int buflen, ast_group_t group) 
    {
    	unsigned int i;
    	int first=1;
    	char num[3];
    
    	buf[0] = '\0';
    	
    	if (!group)	/* Return empty string if no group */
    
    Russell Bryant's avatar
    Russell Bryant committed
    		return buf;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (i = 0; i <= 63; i++) {	/* Max group is 63 */
    
    		if (group & ((ast_group_t) 1 << i)) {
    
    	   		if (!first) {
    				strncat(buf, ", ", buflen);
    			} else {
    				first=0;
    	  		}
    			snprintf(num, sizeof(num), "%u", i);
    			strncat(buf, num, buflen);
    		}
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return buf;
    
    
    void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
    {
    	struct ast_variable *cur;
    
    	for (cur = vars; cur; cur = cur->next)
    		pbx_builtin_setvar_helper(chan, cur->name, cur->value);	
    }
    
    
    static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf, unsigned int samples)
    {
    	struct ast_frame *f;
    	int tocopy;
    	int bytestocopy;
    
    	while (samples) {
    		f = queue->head;
    
    		if (!f) {
    			ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n");
    			break;
    		}
    
    		tocopy = (f->samples > samples) ? samples : f->samples;
    
    		bytestocopy = ast_codec_get_len(queue->format, tocopy);
    
    		memcpy(buf, f->data, bytestocopy);
    		samples -= tocopy;
    		buf += tocopy;
    		f->samples -= tocopy;
    		f->data += bytestocopy;
    		f->datalen -= bytestocopy;
    		f->offset += bytestocopy;
    		queue->samples -= tocopy;
    		if (!f->samples) {
    			queue->head = f->next;
    			ast_frfree(f);
    		}
    	}
    }
    
    struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsigned int samples)
    {
    	struct ast_frame *result;
    	/* buffers are allocated to hold SLINEAR, which is the largest format */
            short read_buf[samples];
            short write_buf[samples];
    	struct ast_frame *read_frame;
    	struct ast_frame *write_frame;
    	int need_dup;
    	struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
    					      .subclass = spy->read_queue.format,
    					      .data = read_buf,
    					      .samples = samples,
    					      .datalen = ast_codec_get_len(spy->read_queue.format, samples),
    	};
    	struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
    					       .subclass = spy->write_queue.format,
    					       .data = write_buf,
    					       .samples = samples,
    					       .datalen = ast_codec_get_len(spy->write_queue.format, samples),
    	};
    
    	/* if a flush has been requested, dump everything in whichever queue is larger */
    	if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
    		if (spy->read_queue.samples > spy->write_queue.samples) {
    			if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
    				for (result = spy->read_queue.head; result; result = result->next)
    					ast_frame_adjust_volume(result, spy->read_vol_adjustment);
    			}
    			result = spy->read_queue.head;
    			spy->read_queue.head = NULL;
    			spy->read_queue.samples = 0;
    			ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
    			return result;
    		} else {
    			if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
    				for (result = spy->write_queue.head; result; result = result->next)
    					ast_frame_adjust_volume(result, spy->write_vol_adjustment);
    			}
    			result = spy->write_queue.head;
    			spy->write_queue.head = NULL;
    			spy->write_queue.samples = 0;
    			ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
    			return result;
    		}
    	}
    
    	if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
    		return NULL;
    
    	/* short-circuit if both head frames have exactly what we want */
    	if ((spy->read_queue.head->samples == samples) &&
    	    (spy->write_queue.head->samples == samples)) {
    		read_frame = spy->read_queue.head;
    		spy->read_queue.head = read_frame->next;
    		read_frame->next = NULL;
    
    		write_frame = spy->write_queue.head;
    		spy->write_queue.head = write_frame->next;
    		write_frame->next = NULL;
    
    		spy->read_queue.samples -= samples;
    		spy->write_queue.samples -= samples;
    
    		need_dup = 0;
    	} else {
    		copy_data_from_queue(&spy->read_queue, read_buf, samples);
    		copy_data_from_queue(&spy->write_queue, write_buf, samples);
    
    		read_frame = &stack_read_frame;
    		write_frame = &stack_write_frame;
    		need_dup = 1;
    	}
    	
    	if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
    		ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
    
    	if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
    		ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
    
    	if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
    		ast_frame_slinear_sum(read_frame, write_frame);
    
    		if (need_dup)
    			result = ast_frdup(read_frame);
    
    	} else {
    		if (need_dup) {
    			result = ast_frdup(read_frame);
    			result->next = ast_frdup(write_frame);
    		} else {
    			result = read_frame;
    			result->next = write_frame;
    		}
    	}
    
    	return result;
    }
    
    
    static void *silence_generator_alloc(struct ast_channel *chan, void *data)
    {
    	/* just store the data pointer in the channel structure */
    	return data;
    }