Skip to content
Snippets Groups Projects
channel.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				}
    			}
    			break;
    		case AST_FRAME_DTMF:
    			ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
    			if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
    				if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
    					chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
    				else
    					ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
    
    			}
    			break;
    		case AST_FRAME_DTMF_BEGIN:
    			ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
    			break;
    		case AST_FRAME_DTMF_END:
    			ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
    			break;
    		case AST_FRAME_VOICE:
    			if (dropaudio) {
    				ast_frfree(f);
    
    			} else if (!(f->subclass & chan->nativeformats)) {
    				/* This frame can't be from the current native formats -- drop it on the
    				   floor */
    				ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
    					chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
    				ast_frfree(f);
    
    			} else {
    				if (chan->spies)
    					queue_frame_to_spies(chan, f, SPY_READ);
    				
    				if (chan->monitor && chan->monitor->read_stream ) {
    
    					/* XXX what does this do ? */
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    
    					int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
    					if (jump >= 0) {
    						if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    						chan->insmpl += jump + 4 * f->samples;
    					} else
    						chan->insmpl+= f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #else
    
    					int jump = chan->outsmpl - chan->insmpl;
    					if (jump - MONITOR_DELAY >= 0) {
    						if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    						chan->insmpl += jump;
    					} else
    						chan->insmpl += f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #endif
    
    					if (chan->monitor->state == AST_MONITOR_RUNNING) {
    						if (ast_writestream(chan->monitor->read_stream, f) < 0)
    							ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
    					}
    				}
    
    
    				if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL)
    					f = &ast_null_frame;
    
    				/* Run generator sitting on the line if timing device not available
    				* and synchronous generation of outgoing frames is necessary       */
    				if (chan->generatordata &&  !ast_internal_timing_enabled(chan)) {
    
    					void *tmp = chan->generatordata;
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
    
    					chan->generatordata = NULL;	/* reset, to let writes go through */
    					res = chan->generator->generate(chan, tmp, f->datalen, f->samples);
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
    
    				} else if (f->frametype == AST_FRAME_CNG) {
    					if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Generator got CNG, switching to timed mode\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	} else {
    		/* Make sure we always return NULL in the future */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    		if (chan->generator)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* End the CDR if appropriate */
    		if (chan->cdr)
    			ast_cdr_end(chan->cdr);
    
    	/* High bit prints debugging */
    
    	if (chan->fin & DEBUGCHAN_FLAG)
    
    		ast_frame_dump(chan->name, f, "<<");
    
    	chan->fin = FRAMECOUNT_INC(chan->fin);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return f;
    }
    
    
    int ast_internal_timing_enabled(struct ast_channel *chan)
    {
    
    	int ret = ast_opt_internal_timing && chan->timingfd > -1;
    
    		ast_log(LOG_DEBUG, "Internal timing is %s (option_internal_timing=%d chan->timingfd=%d)\n", ret? "enabled": "disabled", ast_opt_internal_timing, chan->timingfd);
    
    struct ast_frame *ast_read(struct ast_channel *chan)
    {
    	return __ast_read(chan, 0);
    }
    
    struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
    {
    	return __ast_read(chan, 1);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_indicate(struct ast_channel *chan, int condition)
    
    {
    	return ast_indicate_data(chan, condition, NULL, 0);
    }
    
    int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = -1;
    
    	ast_channel_lock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
    
    		ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    		res = chan->tech->indicate(chan, condition, data, datalen);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/*
    		 * Device does not support (that) indication, lets fake
    		 * it by doing our own tone generation. (PM2002)
    		 */
    
    		if (condition < 0)
    			ast_playtones_stop(chan);
    		else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			const struct tone_zone_sound *ts = NULL;
    			switch (condition) {
    
    			case AST_CONTROL_RINGING:
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ts = ast_get_indication_tone(chan->zone, "ring");
    				break;
    
    			case AST_CONTROL_BUSY:
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ts = ast_get_indication_tone(chan->zone, "busy");
    				break;
    
    			case AST_CONTROL_CONGESTION:
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ts = ast_get_indication_tone(chan->zone, "congestion");
    				break;
    			}
    			if (ts && ts->data[0]) {
    				ast_log(LOG_DEBUG, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
    
    				ast_playtones_start(chan,0,ts->data, 1);
    
    			} else if (condition == AST_CONTROL_PROGRESS) {
    				/* ast_playtones_stop(chan); */
    
    			} else if (condition == AST_CONTROL_PROCEEDING) {
    				/* Do nothing, really */
    
    			} else if (condition == AST_CONTROL_HOLD) {
    				/* Do nothing.... */
    			} else if (condition == AST_CONTROL_UNHOLD) {
    				/* Do nothing.... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else if (condition == AST_CONTROL_VIDUPDATE) {
    				/* Do nothing.... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* not handled */
    				ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
    
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_recvchar(struct ast_channel *chan, int timeout)
    {
    
    	int c;
    	char *buf = ast_recvtext(chan, timeout);
    	if (buf == NULL)
    		return -1;	/* error or timeout */
    	c = *(unsigned char *)buf;
    	free(buf);
    	return c;
    
    char *ast_recvtext(struct ast_channel *chan, int timeout)
    {
    
    	int res, done = 0;
    	char *buf = NULL;
    
    	while (!done) {
    		struct ast_frame *f;
    		if (ast_check_hangup(chan))
    			break;
    		res = ast_waitfor(chan, timeout);
    		if (res <= 0) /* timeout or error */
    			break;
    		timeout = res;	/* update timeout */
    
    		f = ast_read(chan);
    
    		if (f == NULL)
    			break; /* no frame */
    		if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)
    			done = 1;	/* force a break */
    
    		else if (f->frametype == AST_FRAME_TEXT) {		/* what we want */
    
    			buf = ast_strndup((char *) f->data, f->datalen);	/* dup and break */
    
    int ast_sendtext(struct ast_channel *chan, const char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    
    	if (chan->tech->send_text)
    		res = chan->tech->send_text(chan, text);
    
    	ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int do_senddigit(struct ast_channel *chan, char digit)
    {
    	int res = -1;
    
    
    	if (chan->tech->send_digit)
    		res = chan->tech->send_digit(chan, digit);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/*
    		 * Device does not support DTMF tones, lets fake
    		 * it by doing our own generation. (PM2002)
    		 */
    		static const char* dtmf_tones[] = {
    
    			"!941+1336/100,!0/100",	/* 0 */
    			"!697+1209/100,!0/100",	/* 1 */
    			"!697+1336/100,!0/100",	/* 2 */
    			"!697+1477/100,!0/100",	/* 3 */
    			"!770+1209/100,!0/100",	/* 4 */
    			"!770+1336/100,!0/100",	/* 5 */
    			"!770+1477/100,!0/100",	/* 6 */
    			"!852+1209/100,!0/100",	/* 7 */
    			"!852+1336/100,!0/100",	/* 8 */
    			"!852+1477/100,!0/100",	/* 9 */
    			"!697+1633/100,!0/100",	/* A */
    			"!770+1633/100,!0/100",	/* B */
    			"!852+1633/100,!0/100",	/* C */
    			"!941+1633/100,!0/100",	/* D */
    			"!941+1209/100,!0/100",	/* * */
    
    			"!941+1477/100,!0/100" };	/* # */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (digit >= '0' && digit <='9')
    
    			ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit >= 'A' && digit <= 'D')
    
    			ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '*')
    
    			ast_playtones_start(chan, 0, dtmf_tones[14], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '#')
    
    			ast_playtones_start(chan, 0, dtmf_tones[15], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else {
    			/* not handled */
    
    			ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
    
    int ast_senddigit(struct ast_channel *chan, char digit)
    {
    
    	return do_senddigit(chan, digit);
    
    int ast_prod(struct ast_channel *chan)
    {
    	struct ast_frame a = { AST_FRAME_VOICE };
    	char nothing[128];
    
    	/* Send an empty audio frame to get things moving */
    	if (chan->_state != AST_STATE_UP) {
    
    		ast_log(LOG_DEBUG, "Prodding channel '%s'\n", chan->name);
    
    		a.data = nothing + AST_FRIENDLY_OFFSET;
    
    		a.src = "ast_prod";
    
    		if (ast_write(chan, &a))
    			ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
    
    int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
    {
    	int res;
    
    		return 0;
    	res = ast_write(chan, fr);
    	if (!res)
    		res = 1;
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_write(struct ast_channel *chan, struct ast_frame *fr)
    {
    	int res = -1;
    
    	struct ast_frame *f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	ast_channel_lock(chan);
    
    	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
    		goto done;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Handle any pending masquerades */
    
    	if (chan->masq && ast_do_masquerade(chan)) {
    		ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (chan->masqr) {
    
    		res = 0;	/* XXX explain, why 0 ? */
    		goto done;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (ast_test_flag(chan, AST_FLAG_WRITE_INT))
    
    			ast_deactivate_generator(chan);
    
    			res = 0;	/* XXX explain, why 0 ? */
    			goto done;
    
    	/* High bit prints debugging */
    
    	if (chan->fout & DEBUGCHAN_FLAG)
    
    		ast_frame_dump(chan->name, fr, ">>");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    	switch(fr->frametype) {
    	case AST_FRAME_CONTROL:
    		/* XXX Interpret control frames XXX */
    		ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
    		break;
    
    		res = (chan->tech->send_digit_begin == NULL) ? 0 :
    			chan->tech->send_digit_begin(chan, fr->subclass);
    
    		res = (chan->tech->send_digit_end == NULL) ? 0 :
    			chan->tech->send_digit_end(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FRAME_DTMF:
    
    		ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    		ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = do_senddigit(chan,fr->subclass);
    
    		ast_channel_lock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CHECK_BLOCKING(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FRAME_TEXT:
    
    		res = (chan->tech->send_text == NULL) ? 0 :
    			chan->tech->send_text(chan, (char *) fr->data);
    
    		break;
    	case AST_FRAME_HTML:
    
    		res = (chan->tech->send_html == NULL) ? 0 :
    			chan->tech->send_html(chan, fr->subclass, (char *) fr->data, fr->datalen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    	case AST_FRAME_VIDEO:
    		/* XXX Handle translation of video codecs one day XXX */
    
    		res = (chan->tech->write_video == NULL) ? 0 :
    			chan->tech->write_video(chan, fr);
    
    	case AST_FRAME_MODEM:
    		res = (chan->tech->write == NULL) ? 0 :
    			chan->tech->write(chan, fr);
    		break;
    
    		if (chan->tech->write == NULL)
    
    			break;	/*! \todo XXX should return 0 maybe ? */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* Bypass translator if we're writing format in the raw write format.  This
    		   allows mixing of native / non-native formats */
    		if (fr->subclass == chan->rawwriteformat)
    			f = fr;
    		else
    			f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
    		if (f == NULL) {
    			res = 0;
    		} else {
    			if (chan->spies)
    				queue_frame_to_spies(chan, f, SPY_WRITE);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			if (chan->monitor && chan->monitor->write_stream) {
    
    				/* XXX must explain this code */
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
    				if (jump >= 0) {
    					if (ast_seekstream(chan->monitor->write_stream, jump + f->samples, SEEK_FORCECUR) == -1)
    						ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
    					chan->outsmpl += jump + 4 * f->samples;
    				} else
    					chan->outsmpl += f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #else
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				int jump = chan->insmpl - chan->outsmpl;
    				if (jump - MONITOR_DELAY >= 0) {
    					if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
    						ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
    					chan->outsmpl += jump;
    				} else
    					chan->outsmpl += f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #endif
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				if (chan->monitor->state == AST_MONITOR_RUNNING) {
    					if (ast_writestream(chan->monitor->write_stream, f) < 0)
    						ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    			res = chan->tech->write(chan, f);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (f && f != fr)
    
    		ast_frfree(f);
    
    	ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Consider a write failure to force a soft hangup */
    	if (res < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    		chan->fout = FRAMECOUNT_INC(chan->fout);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format,
    		      struct ast_trans_pvt **trans, const int direction)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int native;
    	int res;
    	
    
    	/* Make sure we only consider audio */
    	fmt &= AST_FORMAT_AUDIO_MASK;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	native = chan->nativeformats;
    
    	/* Find a translation path from the native format to one of the desired formats */
    	if (!direction)
    		/* reading */
    		res = ast_translator_best_choice(&fmt, &native);
    	else
    		/* writing */
    		res = ast_translator_best_choice(&native, &fmt);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res < 0) {
    
    		ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
    
    			ast_getformatname(native), ast_getformatname(fmt));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    	/* Now we have a good choice for both. */
    
    	ast_channel_lock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* User perspective is fmt */
    
    	*format = fmt;
    	/* Free any read translation we have right now */
    	if (*trans)
    		ast_translator_free_path(*trans);
    	/* Build a translation path from the raw format to the desired format */
    	if (!direction)
    		/* reading */
    		*trans = ast_translator_build_path(*format, *rawformat);
    	else
    		/* writing */
    		*trans = ast_translator_build_path(*rawformat, *format);
    
    	ast_channel_unlock(chan);
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Set channel %s to %s format %s\n", chan->name,
    			direction ? "write" : "read", ast_getformatname(fmt));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    int ast_set_read_format(struct ast_channel *chan, int fmt)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return set_format(chan, fmt, &chan->rawreadformat, &chan->readformat,
    			  &chan->readtrans, 0);
    }
    
    int ast_set_write_format(struct ast_channel *chan, int fmt)
    {
    	return set_format(chan, fmt, &chan->rawwriteformat, &chan->writeformat,
    			  &chan->writetrans, 1);
    
    struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *chan;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	int res = 0;
    
    	if (outstate)
    		*outstate = 0;
    	else
    		outstate = &dummy_outstate;	/* make outstate always a valid pointer */
    
    
    	chan = ast_request(type, format, data, &cause);
    
    	if (!chan) {
    		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    		/* compute error and return */
    		if (cause == AST_CAUSE_BUSY)
    			*outstate = AST_CONTROL_BUSY;
    		else if (cause == AST_CAUSE_CONGESTION)
    			*outstate = AST_CONTROL_CONGESTION;
    		return NULL;
    	}
    
    	if (oh) {
    		if (oh->vars)	
    			ast_set_variables(chan, oh->vars);
    		/* XXX why is this necessary, for the parent_channel perhaps ? */
    		if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name))
    			ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
    		if (oh->parent_channel)
    			ast_channel_inherit_variables(oh->parent_channel, chan);
    		if (oh->account)
    			ast_cdr_setaccount(chan, oh->account);	
    	}
    	ast_set_callerid(chan, cid_num, cid_name, cid_num);
    
    	if (ast_call(chan, data, 0)) {	/* ast_call failed... */
    		ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
    	} else {
    		res = 1;	/* mark success in case chan->_state is already AST_STATE_UP */
    		while (timeout && chan->_state != AST_STATE_UP) {
    			struct ast_frame *f;
    			res = ast_waitfor(chan, timeout);
    			if (res <= 0) /* error, timeout, or done */
    				break;
    			if (timeout > -1)
    				timeout = res;
    			f = ast_read(chan);
    			if (!f) {
    				*outstate = AST_CONTROL_HANGUP;
    				res = 0;
    				break;
    			}
    			if (f->frametype == AST_FRAME_CONTROL) {
    				switch (f->subclass) {
    				case AST_CONTROL_RINGING:	/* record but keep going */
    					*outstate = f->subclass;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    
    
    				case AST_CONTROL_BUSY:
    				case AST_CONTROL_CONGESTION:
    				case AST_CONTROL_ANSWER:
    					*outstate = f->subclass;
    					timeout = 0;		/* trick to force exit from the while() */
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    
    				/* Ignore these */
    				case AST_CONTROL_PROGRESS:
    				case AST_CONTROL_PROCEEDING:
    				case AST_CONTROL_HOLD:
    				case AST_CONTROL_UNHOLD:
    				case AST_CONTROL_VIDUPDATE:
    
    				case -1:			/* Ignore -- just stopping indications */
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    
    
    				default:
    					ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
    
    
    	/* Final fixups */
    	if (oh) {
    		if (!ast_strlen_zero(oh->context))
    			ast_copy_string(chan->context, oh->context, sizeof(chan->context));
    		if (!ast_strlen_zero(oh->exten))
    			ast_copy_string(chan->exten, oh->exten, sizeof(chan->exten));
    		if (oh->priority)	
    			chan->priority = oh->priority;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (chan->_state == AST_STATE_UP)
    		*outstate = AST_CONTROL_ANSWER;
    
    	if (res <= 0) {
    		if (!chan->cdr && (chan->cdr = ast_cdr_alloc()))
    
    			ast_cdr_init(chan->cdr, chan);
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    		if (chan->cdr) {
    			char tmp[256];
    
    			snprintf(tmp, sizeof(tmp), "%s/%s", type, (char *)data);
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			ast_cdr_setapp(chan->cdr,"Dial",tmp);
    			ast_cdr_update(chan);
    			ast_cdr_start(chan->cdr);
    			ast_cdr_end(chan->cdr);
    			/* If the cause wasn't handled properly */
    			if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
    				ast_cdr_failed(chan->cdr);
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    		ast_hangup(chan);
    		chan = NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return chan;
    }
    
    
    struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
    
    	return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
    
    struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct chanlist *chan;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	struct ast_channel *c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int capabilities;
    	int fmt;
    	int res;
    
    	int videoformat = format & AST_FORMAT_VIDEO_MASK;
    
    	if (!cause)
    		cause = &foo;
    	*cause = AST_CAUSE_NOTDEFINED;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    	if (AST_LIST_LOCK(&channels)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return NULL;
    	}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		if (strcasecmp(type, chan->tech->type))
    			continue;
    
    		capabilities = chan->tech->capabilities;
    
    		fmt = format & AST_FORMAT_AUDIO_MASK;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		res = ast_translator_best_choice(&fmt, &capabilities);
    		if (res < 0) {
    			ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->tech->capabilities, format);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		if (!chan->tech->requester)
    			return NULL;
    		
    
    		if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause)))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			return NULL;
    
    		if (c->_state == AST_STATE_DOWN) {
    			manager_event(EVENT_FLAG_CALL, "Newchannel",
    				      "Channel: %s\r\n"
    				      "State: %s\r\n"
    				      "CallerID: %s\r\n"
    				      "CallerIDName: %s\r\n"
    				      "Uniqueid: %s\r\n",
    				      c->name, ast_state2str(c->_state),
    
    				      S_OR(c->cid.cid_num, "<unknown>"),
    				      S_OR(c->cid.cid_name, "<unknown>"),
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				      c->uniqueid);
    		}
    		return c;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
    	*cause = AST_CAUSE_NOSUCHDRIVER;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	return NULL;
    
    int ast_call(struct ast_channel *chan, char *addr, int timeout)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
    	   If the remote end does not answer within the timeout, then do NOT hang up, but
    
    Mark Spencer's avatar
    Mark Spencer committed
    	   return anyway.  */
    	int res = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	ast_channel_lock(chan);
    
    	if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
    
    		if (chan->tech->call)
    			res = chan->tech->call(chan, addr, timeout);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
      \brief Transfer a call to dest, if the channel supports transfer
    
    
        \arg app_transfer
        \arg the manager interface
    */
    
    int ast_transfer(struct ast_channel *chan, char *dest)
    
    	/* Stop if we're a zombie or need a soft hangup */
    
    	ast_channel_lock(chan);
    
    	if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
    
    		if (chan->tech->transfer) {
    			res = chan->tech->transfer(chan, dest);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
    {
    
    	return ast_readstring_full(c, s, len, timeout, ftimeout, enders, -1, -1);
    
    }
    
    int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
    {
    
    	int pos = 0;	/* index in the buffer where we accumulate digits */
    
    	int to = ftimeout;
    
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
    
    		return -1;
    	if (!len)
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->stream) {
    
    			d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
    			ast_stopstream(c);
    			usleep(1000);
    			if (!d)
    				d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		} else {
    			d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		}
    		if (d < 0)
    			return -1;
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    		if (d == 1) {
    			s[pos]='\0';
    			return 2;
    		}
    		if (!strchr(enders, d))
    			s[pos++] = d;
    		if (strchr(enders, d) || (pos >= len)) {
    			s[pos]='\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		to = timeout;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Never reached */
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_supports_html(struct ast_channel *chan)
    {
    
    	return (chan->tech->send_html) ? 1 : 0;
    
    int ast_channel_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (chan->tech->send_html)
    		return chan->tech->send_html(chan, subclass, data, datalen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    int ast_channel_sendurl(struct ast_channel *chan, const char *url)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return ast_channel_sendhtml(chan, AST_HTML_URL, url, strlen(url) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
    {
    
    
    	/* Set up translation from the chan to the peer */
    
    	src = chan->nativeformats;
    	dst = peer->nativeformats;
    	if (ast_translator_best_choice(&dst, &src) < 0) {
    		ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, src, peer->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	/* if the best path is not 'pass through', then
    	   transcoding is needed; if desired, force transcode path
    
    	   to use SLINEAR between channels, but only if there is
    	   no direct conversion available */
    
    	if ((src != dst) && ast_opt_transcode_via_slin &&
    	    (ast_translate_path_steps(dst, src) != 1))
    
    		dst = AST_FORMAT_SLINEAR;
    	if (ast_set_read_format(chan, dst) < 0) {
    		ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	if (ast_set_write_format(peer, dst) < 0) {
    		ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* Set up translation from the peer to the chan */
    	src = peer->nativeformats;
    	dst = chan->nativeformats;
    	if (ast_translator_best_choice(&dst, &src) < 0) {
    		ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, src, chan->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	/* if the best path is not 'pass through', then
    	   transcoding is needed; if desired, force transcode path
    
    	   to use SLINEAR between channels, but only if there is
    	   no direct conversion available */
    
    	if ((src != dst) && ast_opt_transcode_via_slin &&
    	    (ast_translate_path_steps(dst, src) != 1))
    
    		dst = AST_FORMAT_SLINEAR;
    	if (ast_set_read_format(peer, dst) < 0) {
    		ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	if (ast_set_write_format(chan, dst) < 0) {
    		ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, dst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
    {
    
    	/* each of these channels may be sitting behind a channel proxy (i.e. chan_agent)
    	   and if so, we don't really want to masquerade it, but its proxy */
    	if (original->_bridge && (original->_bridge != ast_bridged_channel(original)))
    		original = original->_bridge;
    
    	if (clone->_bridge && (clone->_bridge != ast_bridged_channel(clone)))
    		clone = clone->_bridge;
    
    
    	if (original == clone) {
    		ast_log(LOG_WARNING, "Can't masquerade channel '%s' into itself!\n", original->name);
    		return -1;
    	}
    
    	ast_channel_lock(original);
    	while(ast_channel_trylock(clone)) {
    		ast_channel_unlock(original);
    
    		ast_channel_lock(original);
    
    	ast_log(LOG_DEBUG, "Planning to masquerade channel %s into the structure of %s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    		clone->name, original->name);
    	if (original->masq) {
    
    		ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    			original->masq->name, original->name);
    
    		ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    			clone->name, clone->masqr->name);
    
    	} else {
    		original->masq = clone;
    		clone->masqr = original;
    
    		ast_queue_frame(original, &ast_null_frame);
    		ast_queue_frame(clone, &ast_null_frame);
    
    		ast_log(LOG_DEBUG, "Done planning to masquerade channel %s into the structure of %s\n", clone->name, original->name);
    
    	ast_channel_unlock(clone);
    	ast_channel_unlock(original);
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_change_name(struct ast_channel *chan, char *newname)
    {
    
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", chan->name, newname, chan->uniqueid);
    	ast_string_field_set(chan, name, newname);
    
    void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
    {
    	struct ast_var_t *current, *newvar;
    
    
    	AST_LIST_TRAVERSE(&parent->varshead, current, entries) {
    		int vartype = 0;
    
    		varname = ast_var_full_name(current);
    		if (!varname)
    			continue;
    
    		if (varname[0] == '_') {
    			vartype = 1;
    			if (varname[1] == '_')
    				vartype = 2;
    		}
    
    		switch (vartype) {
    		case 1:
    			newvar = ast_var_assign(&varname[1], ast_var_value(current));
    			if (newvar) {
    
    				AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
    
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Copying soft-transferable variable %s.\n", ast_var_name(newvar));
    			}
    			break;
    		case 2:
    			newvar = ast_var_assign(ast_var_full_name(current), ast_var_value(current));
    			if (newvar) {
    
    				AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
    
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Copying hard-transferable variable %s.\n", ast_var_name(newvar));
    			}
    			break;
    		default:
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Not copying variable %s.\n", ast_var_name(current));
    			break;
    		}
    	}
    }
    
    
    /*!
      \brief Clone channel variables from 'clone' channel into 'original' channel
    
      All variables except those related to app_groupcount are cloned.
      Variables are actually _removed_ from 'clone' channel, presumably
      because it will subsequently be destroyed.
    
      \note Assumes locks will be in place on both channels when called.
    */
    
    static void clone_variables(struct ast_channel *original, struct ast_channel *clone)
    {
    	struct ast_var_t *varptr;
    
    	/* we need to remove all app_groupcount related variables from the original
    	   channel before merging in the clone's variables; any groups assigned to the
    	   original channel should be released, only those assigned to the clone
    	   should remain
    	*/
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&original->varshead, varptr, entries) {
    		if (!strncmp(ast_var_name(varptr), GROUP_CATEGORY_PREFIX, strlen(GROUP_CATEGORY_PREFIX))) {
    			AST_LIST_REMOVE(&original->varshead, varptr, entries);
    			ast_var_delete(varptr);
    		}
    	}
    	AST_LIST_TRAVERSE_SAFE_END;
    
    	/* Append variables from clone channel into original channel */
    	/* XXX Is this always correct?  We have to in order to keep MACROS working XXX */
    
    	if (AST_LIST_FIRST(&clone->varshead))
    		AST_LIST_INSERT_TAIL(&original->varshead, AST_LIST_FIRST(&clone->varshead), entries);
    
    /*!
      \brief Masquerade a channel
    
    
      \note Assumes channel will be locked when called
    
    int ast_do_masquerade(struct ast_channel *original)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int x,i;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_frame *cur, *prev;
    
    	const struct ast_channel_tech *t;
    	void *t_pvt;
    
    	struct ast_callerid tmpcid;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *clone = original->masq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int rformat = original->readformat;
    	int wformat = original->writeformat;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char newn[100];
    	char orig[100];
    	char masqn[100];
    	char zombn[100];
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
    			clone->name, clone->_state, original->name, original->_state);