Skip to content
Snippets Groups Projects
channel.c 65.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	if (prev) {
    		prev->next = original->pvt->readq;
    		original->pvt->readq = clone->pvt->readq;
    		clone->pvt->readq = NULL;
    		if (original->pvt->alertpipe[1] > -1) {
    			for (i=0;i<x;i++)
    				write(original->pvt->alertpipe[1], &x, sizeof(x));
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	clone->_softhangup = AST_SOFTHANGUP_DEV;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    	if (clone->pvt->fixup){
    		res = clone->pvt->fixup(original, clone);
    		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->pvt->hangup)
    		res = clone->pvt->hangup(clone);
    	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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	strncpy(clone->name, zombn, sizeof(clone->name) - 1);
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", masqn, zombn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Keep the same language.  */
    	/* Update the type. */
    	original->type = clone->type;
    	/* Copy the FD's */
    
    	for (x=0;x<AST_MAX_FDS;x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		original->fds[x] = clone->fds[x];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Move the variables */
    	tmpv = original->varshead.first;
    	original->varshead.first = clone->varshead.first;
    	clone->varshead.first = tmpv;
    
    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 */
    	original->exception = clone->exception;
    	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 */
    
    	/* dnid and callerid change to become the new, HOWEVER, we also link the original's
    	   fields back into the defunct 'clone' so that they will be freed when
    	   ast_frfree is eventually called */
    	tmp = original->dnid;
    	original->dnid = clone->dnid;
    	clone->dnid = tmp;
    	
    	tmp = original->callerid;
    	original->callerid = clone->callerid;
    	clone->callerid = tmp;
    	
    
    	/* Restore original timing file descriptor */
    	original->fds[AST_MAX_FDS - 2] = original->timingfd;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Our native formats are different now */
    	original->nativeformats = clone->nativeformats;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* 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 */
    	original->_state = clone->_state;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Context, extension, priority, app data, jump table,  remain the same */
    	/* pvt switches.  pbx stays the same, as does next */
    	
    	/* 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 (clone->zombie) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_DEBUG, "Destroying clone '%s'\n", clone->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_channel_free(clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		manager_event(EVENT_FLAG_CALL, "Hangup", "Channel: %s\r\n", zombn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_DEBUG, "Released clone lock on '%s'\n", clone->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		clone->zombie=1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	/* Set the write format */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_set_write_format(original, wformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Set the read format */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_set_read_format(original, rformat);
    
    	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->pvt->fixup) {
    		res = original->pvt->fixup(clone, original);
    		if (res) {
    			ast_log(LOG_WARNING, "Driver for '%s' could not fixup channel %s\n",
    				original->type, original->name);
    			return -1;
    		}
    	} else
    		ast_log(LOG_WARNING, "Driver '%s' does not have a fixup routine (for %s)!  Bad things may happen.\n",
    			original->type, original->name);
    	/* Signal any blocker */
    	if (original->blocking)
    		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;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_set_callerid(struct ast_channel *chan, char *callerid, int anitoo)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	if (chan->callerid)
    		free(chan->callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (anitoo && chan->ani)
    		free(chan->ani);
    	if (callerid) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->callerid = strdup(callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (anitoo)
    			chan->ani = strdup(callerid);
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->callerid = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (anitoo)
    			chan->ani = NULL;
    	}
    	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"
    
    Mark Spencer's avatar
    Mark Spencer committed
    				"Callerid: %s\r\n"
    				"Uniqueid: %s\r\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    				chan->name, chan->callerid ? 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				chan->callerid : "<Unknown>",
    				chan->uniqueid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_setstate(struct ast_channel *chan, int state)
    {
    	if (chan->_state != state) {
    		int oldstate = chan->_state;
    		chan->_state = state;
    		if (oldstate == AST_STATE_DOWN) {
    
    			ast_device_state_changed(chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			manager_event(EVENT_FLAG_CALL, "Newchannel",
    			"Channel: %s\r\n"
    			"State: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Callerid: %s\r\n"
    			"Uniqueid: %s\r\n",
    			chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>", chan->uniqueid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    			manager_event(EVENT_FLAG_CALL, "Newstate", 
    				"Channel: %s\r\n"
    				"State: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    				"Callerid: %s\r\n"
    				"Uniqueid: %s\r\n",
    				chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>", chan->uniqueid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
    {
    	/* Copy voice back and forth between the two channels.  Give the peer
    	   the ability to transfer calls with '#<extension' syntax. */
    	struct ast_channel *cs[3];
    	int to = -1;
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *who = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int nativefailed=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (c0->zombie || ast_check_hangup_locked(c0) || c1->zombie || ast_check_hangup_locked(c1)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	if (c0->bridge) {
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    			c0->name, c0->bridge->name);
    		return -1;
    	}
    	if (c1->bridge) {
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    			c1->name, c1->bridge->name);
    		return -1;
    	}
    	
    	/* Keep track of bridge */
    	c0->bridge = c1;
    	c1->bridge = c0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cs[0] = c0;
    	cs[1] = c1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	manager_event(EVENT_FLAG_CALL, "Link", 
    			"Channel1: %s\r\n"
    			"Channel2: %s\r\n",
    			c0->name, c1->name);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (/* ever */;;) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Stop if we're a zombie or need a soft hangup */
    
    		if (c0->zombie || ast_check_hangup_locked(c0) || c1->zombie || ast_check_hangup_locked(c1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*fo = NULL;
    			if (who) *rc = who;
    			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,c0->zombie?"Yes":"No",ast_check_hangup(c0)?"Yes":"No",c1->zombie?"Yes":"No",ast_check_hangup(c1)?"Yes":"No");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    		}
    		if (c0->pvt->bridge && 
    
    			(c0->pvt->bridge == c1->pvt->bridge) && !nativefailed && !c0->monitor && !c1->monitor) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Looks like they share a bridge code */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 2) 
    				ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!(res = c0->pvt->bridge(c0, c1, flags, fo, rc))) {
    				c0->bridge = NULL;
    				c1->bridge = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				manager_event(EVENT_FLAG_CALL, "Unlink", 
    					"Channel1: %s\r\n"
    					"Channel2: %s\r\n",
    					c0->name, c1->name);
    
    				ast_log(LOG_DEBUG, "Returning from native bridge, channels: %s, %s\n",c0->name ,c1->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return 0;
    			}
    			/* If they return non-zero then continue on normally.  Let "-2" mean don't worry about
    			   my not wanting to bridge */
    			if ((res != -2) && (res != -3))
    				ast_log(LOG_WARNING, "Private bridge between %s and %s failed\n", c0->name, c1->name);
    			if (res != -3) nativefailed++;
    		}
    	
    			
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat)) &&
    			!(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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				manager_event(EVENT_FLAG_CALL, "Unlink", 
    					"Channel1: %s\r\n"
    					"Channel2: %s\r\n",
    					c0->name, c1->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1;
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		who = ast_waitfor_n(cs, 2, &to);
    		if (!who) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_DEBUG, "Nobody there, continuing...\n"); 
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		}
    		f = ast_read(who);
    		if (!f) {
    			*fo = NULL;
    			*rc = who;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = 0;
    
    			ast_log(LOG_DEBUG, "Didn't get a frame from channel: %s\n",who->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
    			*fo = f;
    			*rc = who;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res =  0;
    
    			ast_log(LOG_DEBUG, "Got a FRAME_CONTROL frame on channel %s\n",who->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if ((f->frametype == AST_FRAME_VOICE) ||
    			(f->frametype == AST_FRAME_TEXT) ||
    			(f->frametype == AST_FRAME_VIDEO) || 
    			(f->frametype == AST_FRAME_IMAGE) ||
    			(f->frametype == AST_FRAME_DTMF)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((f->frametype == AST_FRAME_DTMF) && 
    				(flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
    				if ((who == c0)) {
    					if  ((flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
    						*rc = c0;
    						*fo = f;
    						/* Take out of conference mode */
    						res = 0;
    
    Martin Pycko's avatar
    Martin Pycko committed
    						ast_log(LOG_DEBUG, "Got AST_BRIDGE_DTMF_CHANNEL_0 on c0 (%s)\n",c0->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    					} else 
    						goto tackygoto;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if ((who == c1)) {
    					if (flags & AST_BRIDGE_DTMF_CHANNEL_1) {
    						*rc = c1;
    						*fo = f;
    						res =  0;
    
    Martin Pycko's avatar
    Martin Pycko committed
    						ast_log(LOG_DEBUG, "Got AST_BRIDGE_DTMF_CHANNEL_1 on c1 (%s)\n",c1->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    					} else
    						goto tackygoto;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    			} 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
    
    Mark Spencer's avatar
    Mark Spencer committed
    tackygoto:
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Don't copy packets if there is a generator on either one, since they're
    				   not supposed to be listening anyway */
    
    				if (who == c0) 
    					ast_write(c1, f);
    				else 
    					ast_write(c0, f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    			ast_frfree(f);
    		} else
    			ast_frfree(f);
    		/* Swap who gets priority */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c0->bridge = NULL;
    	c1->bridge = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Unlink", 
    					"Channel1: %s\r\n"
    					"Channel2: %s\r\n",
    					c0->name, c1->name);
    
    	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;
    }
    
    int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
    {
    	int res;
    	if (chan->pvt->setoption) {
    		res = chan->pvt->setoption(chan, option, data, datalen);
    		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;
    	if (chan) {
    		ast_set_write_format(chan, ts->origwfmt);
    	}
    	free(ts);
    }
    
    static void * tonepair_alloc(struct ast_channel *chan, void *params)
    {
    	struct tonepair_state *ts;
    	struct tonepair_def *td = params;
    	ts = malloc(sizeof(struct tonepair_state));
    	if (!ts)
    		return NULL;
    	memset(ts, 0, sizeof(struct tonepair_state));
    	ts->origwfmt = chan->writeformat;
    	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
    		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 :) */
    	chan->writeinterrupt = 1;
    	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++) {
    		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, };
    	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;
    	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)) {
    		f = ast_read(chan);
    		if (f)
    			ast_frfree(f);
    		else
    			return -1;
    	}
    	return 0;
    }