Skip to content
Snippets Groups Projects
channel.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	/* XXX This is a seriously wacked out operation.  We're essentially putting the guts of
    
    	   the clone channel into the original channel.  Start by killing off the original
    
    	   channel's backend.   I'm not sure we're going to keep this function, because
    
    Mark Spencer's avatar
    Mark Spencer committed
    	   while the features are nice, the cost is very high in terms of pure nastiness. XXX */
    
    
    	ast_channel_lock(clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    	if (option_debug > 1)
    		ast_log(LOG_DEBUG, "Got clone lock for masquerade on '%s' at %p\n", clone->name, &clone->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Having remembered the original read/write formats, we turn off any translation on either
    	   one */
    	free_translation(clone);
    	free_translation(original);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Unlink the masquerade */
    	original->masq = NULL;
    	clone->masqr = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* Save the original name */
    
    	ast_copy_string(orig, original->name, sizeof(orig));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Save the new name */
    
    	ast_copy_string(newn, clone->name, sizeof(newn));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Create the masq name */
    	snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		
    	/* Copy the name from the clone channel */
    
    	ast_string_field_set(original, name, newn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Mangle the name of the clone channel */
    
    	ast_string_field_set(clone, name, masqn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* Notify any managers of the change, first the masq then the other */
    
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid);
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
    
    	t = original->tech;
    	original->tech = clone->tech;
    	clone->tech = t;
    
    	t_pvt = original->tech_pvt;
    	original->tech_pvt = clone->tech_pvt;
    	clone->tech_pvt = t_pvt;
    
    	/* Swap the readq's */
    	cur = original->readq;
    	original->readq = clone->readq;
    	clone->readq = cur;
    
    	/* Swap the alertpipes */
    	for (i = 0; i < 2; i++) {
    		x = original->alertpipe[i];
    		original->alertpipe[i] = clone->alertpipe[i];
    		clone->alertpipe[i] = x;
    	}
    
    	/* Swap the raw formats */
    	x = original->rawreadformat;
    	original->rawreadformat = clone->rawreadformat;
    	clone->rawreadformat = x;
    	x = original->rawwriteformat;
    	original->rawwriteformat = clone->rawwriteformat;
    	clone->rawwriteformat = x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Save any pending frames on both sides.  Start by counting
    	 * how many we're going to need... */
    	prev = NULL;
    	x = 0;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (cur = clone->readq; cur; cur = cur->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		x++;
    		prev = cur;
    	}
    
    	/* If we had any, prepend them to the ones already in the queue, and
    
    Mark Spencer's avatar
    Mark Spencer committed
    	 * load up the alertpipe */
    	if (prev) {
    
    		prev->next = original->readq;
    		original->readq = clone->readq;
    		clone->readq = NULL;
    		if (original->alertpipe[1] > -1) {
    
    			for (i = 0; i < x; i++)
    
    				write(original->alertpipe[1], &x, sizeof(x));
    
    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
    			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");
    
    		ast_channel_unlock(clone);
    
    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];
    
    	/* Move data stores over */
    	if (AST_LIST_FIRST(&clone->datastores))
    
                    AST_LIST_INSERT_TAIL(&original->datastores, AST_LIST_FIRST(&clone->datastores), entry);
    
    	AST_LIST_HEAD_INIT_NOLOCK(&clone->datastores);
    
    
    	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);
    
    	if (option_debug)
    		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);
    
    			ast_channel_unlock(clone);
    
    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)) {
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Destroying channel clone '%s'\n", clone->name);
    		ast_channel_unlock(clone);
    
    		manager_event(EVENT_FLAG_CALL, "Hangup",
    
    			"Channel: %s\r\n"
    			"Uniqueid: %s\r\n"
    			"Cause: %d\r\n"
    			"Cause-txt: %s\r\n",
    
    			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);
    
    		ast_channel_unlock(clone);
    
    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);
    
    	if (option_debug)
    		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);
    
    		chan->cid.cid_num = ast_strdup(callerid);
    
    	}
    	if (calleridname) {
    		if (chan->cid.cid_name)
    			free(chan->cid.cid_name);
    
    		chan->cid.cid_name = ast_strdup(calleridname);
    
    	}
    	if (ani) {
    		if (chan->cid.cid_ani)
    			free(chan->cid.cid_ani);
    
    		chan->cid.cid_ani = ast_strdup(ani);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if (chan->cdr)
    		ast_cdr_setcid(chan->cdr, chan);
    
    	manager_event(EVENT_FLAG_CALL, "Newcallerid",
    
    Mark Spencer's avatar
    Mark Spencer committed
    				"Channel: %s\r\n"
    
    				"CallerID: %s\r\n"
    				"CallerIDName: %s\r\n"
    
    				"Uniqueid: %s\r\n"
    				"CID-CallingPres: %d (%s)\r\n",
    
    				chan->name,
    				S_OR(chan->cid.cid_num, "<Unknown>"),
    				S_OR(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),
    
    		      S_OR(chan->cid.cid_num, "<unknown>"),
    		      S_OR(chan->cid.cid_name, "<unknown>"),
    
    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;
    
    	if (remain > 0) {
    		if (remain / 60 > 1) {
    
    			min = remain / 60;
    			sec = remain % 60;
    
    	if (!strcmp(sound,"timeleft")) {	/* Queue support */
    
    		ast_stream_and_wait(chan, "vm-youhave", chan->language, "");
    
    			ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, NULL);
    
    			ast_stream_and_wait(chan, "queue-minutes", chan->language, "");
    
    			ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, NULL);
    
    			ast_stream_and_wait(chan, "queue-seconds", chan->language, "");
    
    		ast_stream_and_wait(chan, sound, chan->language, "");
    
    	ast_autoservice_stop(peer);
    
    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;
    
    	enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
    
    	int o0nativeformats;
    	int o1nativeformats;
    
    	int watch_c0_dtmf;
    	int watch_c1_dtmf;
    
    	/* Indicates whether a frame was queued into a jitterbuffer */
    	int frame_put_in_jb = 0;
    	int jb_in_use;
    
    	
    	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;
    
    	/* Check the need of a jitterbuffer for each channel */
    	jb_in_use = ast_jb_do_usecheck(c0, c1);
    
    
    	for (;;) {
    
    		struct ast_channel *who, *other;
    
    
    		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;
    
    		/* Calculate the appropriate max sleep interval - in general, this is the time,
    		   left to the closest jb delivery moment */
    		if (jb_in_use)
    			to = ast_jb_get_when_to_wakeup(c0, c1, to);
    
    		who = ast_waitfor_n(cs, 2, &to);
    
    		if (!who) {
    
    			/* No frame received within the specified timeout - check if we have to deliver now */
    			if (jb_in_use)
    				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;
    				if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    					c1->_softhangup = 0;
    
    			continue;
    		}
    		f = ast_read(who);
    		if (!f) {
    			*fo = NULL;
    			*rc = who;
    			ast_log(LOG_DEBUG, "Didn't get a frame from channel: %s\n",who->name);
    			break;
    		}
    
    
    		other = (who == c0) ? c1 : c0; /* the 'other' channel */
    
    		/* Try add the frame info the who's bridged channel jitterbuff */
    		if (jb_in_use)
    			frame_put_in_jb = !ast_jb_put(other, f);
    
    		if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
    
    			switch (f->subclass) {
    			case AST_CONTROL_HOLD:
    			case AST_CONTROL_UNHOLD:
    			case AST_CONTROL_VIDUPDATE:
    				ast_indicate_data(other, f->subclass, f->data, f->datalen);
    				break;
    			default:
    
    				*fo = f;
    				*rc = who;
    
    				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) ||
    
    		    (f->frametype == AST_FRAME_MODEM) ||
    
    		    (f->frametype == AST_FRAME_TEXT)) {
    
    			/* monitored dtmf causes exit from bridge */
    			int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
    
    			if (f->frametype == AST_FRAME_DTMF && monitored_source) {
    				*fo = f;
    				*rc = who;
    				ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
    				break;
    
    			/* Write immediately frames, not passed through jb */
    			if (!frame_put_in_jb)
    				ast_write(other, f);
    				
    			/* Check if we have to deliver now */
    			if (jb_in_use)
    				ast_jb_get_and_deliver(c0, c1);
    
    		/* XXX do we want to pass on also frames not matched above ? */
    
    		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;
    
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    		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 */
    
    
    	/* \todo  XXX here should check that cid_num is not NULL */
    
    	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 (time_left_ms >= 5000 && config->warning_sound && config->play_warning) {
    					int t = (time_left_ms + 500) / 1000; /* round to nearest second */
    					if (caller_warning)
    						bridge_playfile(c0, c1, config->warning_sound, t);
    					if (callee_warning)
    						bridge_playfile(c1, c0, config->warning_sound, t);
    
    				}
    				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) {
    
    				/* \todo  XXX here should check that cid_num is not NULL */
    
    				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);
    
    				/* \todo  XXX here should check that cid_num is not NULL */
    
                                    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
    	}
    
    	/* \todo  XXX here should check that cid_num is not NULL */
    
    	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;
    
    		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;
    
    	if (!(ts = ast_calloc(1, sizeof(*ts))))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    
    	/* we need to prepare a frame with 16 * timelen samples as we're
    
    Mark Spencer's avatar
    Mark Spencer committed
    	 * 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;
    
    	d.vol = (vol < 1) ? 8192 : vol; /* force invalid to 8192 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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)
    {
    	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 */
    
    	while (chan->generatordata && ast_waitfor(chan, 100) >= 0) {
    		struct ast_frame *f = ast_read(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (f)
    			ast_frfree(f);
    		else
    			return -1;
    	}
    	return 0;
    }
    
    ast_group_t ast_get_group(char *s)
    
    	int start=0, finish=0, x;
    
    	ast_group_t group = 0;
    
    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);
    }
    
    void ast_moh_cleanup(struct ast_channel *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,