Skip to content
Snippets Groups Projects
channel.c 108 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    		 */
    		if (condition >= 0) {
    			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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    		}
    		else ast_playtones_stop(chan);
    	}
    
    	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 = 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);
    	if (!chan->tech->send_digit || res) {
    
    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 */
    
    	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
    	/* Handle any pending masquerades */
    	if (chan->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	}
    
    	if (chan->masqr) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (ast_test_flag(chan, AST_FLAG_WRITE_INT))
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    
    	/* High bit prints debugging */
    
    	if (chan->fout & 0x80000000)
    		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;
    	case AST_FRAME_DTMF:
    
    		ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = do_senddigit(chan,fr->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_mutex_lock(&chan->lock);
    		CHECK_BLOCKING(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FRAME_TEXT:
    
    		if (chan->tech->send_text)
    			res = chan->tech->send_text(chan, (char *) fr->data);
    
    		else
    			res = 0;
    		break;
    	case AST_FRAME_HTML:
    
    		if (chan->tech->send_html)
    			res = 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 */
    
    		if (chan->tech->write_video)
    			res = chan->tech->write_video(chan, fr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    			f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			if (f) {
    
    				if (f->frametype == AST_FRAME_VOICE && chan->spies)
    					queue_frame_to_spies(chan, f, SPY_WRITE);
    
    				if( chan->monitor && chan->monitor->write_stream &&
    
    						f && ( f->frametype == AST_FRAME_VOICE ) ) {
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    					int jump = chan->insmpl - chan->outsmpl - 2 * 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 + 2 * f->samples;
    					} else
    						chan->outsmpl += f->samples;
    #else
    					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;
    #endif
    
    					if (ast_writestream(chan->monitor->write_stream, f) < 0)
    
    						ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
    				}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	/* It's possible this is a translated frame */
    	if (f && f->frametype == AST_FRAME_DTMF) {
    		ast_log(LOG_DTMF, "%s : %c\n", chan->name, f->subclass);
    	} else if (fr->frametype == AST_FRAME_DTMF) {
    		ast_log(LOG_DTMF, "%s : %c\n", chan->name, fr->subclass);
    	}
    
    
    	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;
    
    	else {
    		if ((chan->fout & 0x7fffffff) == 0x7fffffff)
    			chan->fout &= 0x80000000;
    		else
    			chan->fout++;
    	}
    
    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;
    	
    	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_mutex_lock(&chan->lock);
    	*rawformat = native;
    
    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);
    
    	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
    {
    	int state = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *chan;
    	struct ast_frame *f;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	int res = 0;
    
    	chan = ast_request(type, format, data, &cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan) {
    
    			ast_set_variables(chan, oh->vars);
    
    			ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
    
    		ast_set_callerid(chan, cid_num, cid_name, cid_num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!ast_call(chan, data, 0)) {
    			while(timeout && (chan->_state != AST_STATE_UP)) {
    				res = ast_waitfor(chan, timeout);
    				if (res < 0) {
    					/* Something not cool, or timed out */
    					break;
    				}
    				/* If done, break out */
    				if (!res)
    					break;
    				if (timeout > -1)
    					timeout = res;
    				f = ast_read(chan);
    				if (!f) {
    					state = AST_CONTROL_HANGUP;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    					res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				}
    				if (f->frametype == AST_FRAME_CONTROL) {
    					if (f->subclass == AST_CONTROL_RINGING)
    						state = AST_CONTROL_RINGING;
    					else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
    						state = f->subclass;
    
    						ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    					} else if (f->subclass == AST_CONTROL_ANSWER) {
    						state = f->subclass;
    
    						ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    
    					} else if (f->subclass == AST_CONTROL_PROGRESS) {
    						/* Ignore */
    
    					} else if (f->subclass == -1) {
    						/* Ignore -- just stopping indications */
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    						ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
    					}
    				}
    				ast_frfree(f);
    			}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    		} else
    
    			ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    
    		switch(cause) {
    		case AST_CAUSE_BUSY:
    			state = AST_CONTROL_BUSY;
    			break;
    		case AST_CAUSE_CONGESTION:
    			state = AST_CONTROL_CONGESTION;
    			break;
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan) {
    		/* Final fixups */
    		if (oh) {
    			if (oh->context && *oh->context)
    
    				ast_copy_string(chan->context, oh->context, sizeof(chan->context));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (oh->exten && *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) 
    			state = AST_CONTROL_ANSWER;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (outstate)
    		*outstate = state;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	if (chan && res <= 0) {
    		if (!chan->cdr) {
    			chan->cdr = ast_cdr_alloc();
    			if (chan->cdr)
    				ast_cdr_init(chan->cdr, chan);
    		}
    		if (chan->cdr) {
    			char tmp[256];
    
    			snprintf(tmp, 256, "%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);
    		} else 
    			ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
    		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;
    
    	if (!cause)
    		cause = &foo;
    	*cause = AST_CAUSE_NOTDEFINED;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    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
    
    	for (chan = backends; chan; chan = chan->next) {
    		if (strcasecmp(type, chan->tech->type))
    			continue;
    
    		capabilities = chan->tech->capabilities;
    		fmt = format;
    		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
    		ast_mutex_unlock(&chlock);
    		if (!chan->tech->requester)
    			return NULL;
    		
    		if (!(c = chan->tech->requester(type, capabilities, data, cause)))
    			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),
    				      c->cid.cid_num ? c->cid.cid_num : "<unknown>",
    				      c->cid.cid_name ? c->cid.cid_name : "<unknown>",
    				      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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_call(struct ast_channel *chan, char *addr, int timeout) 
    {
    	/* 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 
    	   return anyway.  */
    	int res = -1;
    
    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)) 
    
    		if (chan->tech->call)
    			res = chan->tech->call(chan, addr, timeout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*--- ast_transfer: Transfer a call to dest, if the channel supports transfer */
    /*	called by app_transfer or the manager interface */
    
    int ast_transfer(struct ast_channel *chan, char *dest) 
    {
    	int res = -1;
    
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
    
    		if (chan->tech->transfer) {
    			res = chan->tech->transfer(chan, dest);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
    {
    	int pos=0;
    	int to = ftimeout;
    
    	/* XXX Merge with full version? XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!len)
    		return -1;
    	do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->stream) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			d = ast_waitstream(c, AST_DIGIT_ANY);
    			ast_stopstream(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			usleep(1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!d)
    				d = ast_waitfordigit(c, to);
    		} else {
    			d = ast_waitfordigit(c, to);
    		}
    		if (d < 0)
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strchr(enders, d))
    			s[pos++] = d;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strchr(enders, d) || (pos >= len)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			s[pos]='\0';
    
    			return 0;
    		}
    		to = timeout;
    	} while(1);
    	/* Never reached */
    	return 0;
    }
    
    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;
    	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;
    	do {
    
    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;
    	} while(1);
    	/* Never reached */
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_supports_html(struct ast_channel *chan)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 1;
    	return 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
    {
    
    	if (chan->tech->send_html)
    		return chan->tech->send_html(chan, AST_HTML_URL, url, strlen(url) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -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 */
    	if ((src != dst) && option_transcode_slin)
    		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 */
    	if ((src != dst) && option_transcode_slin)
    		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)
    {
    
    	struct ast_frame null = { AST_FRAME_NULL, };
    
    	if (original == clone) {
    		ast_log(LOG_WARNING, "Can't masquerade channel '%s' into itself!\n", original->name);
    		return -1;
    	}
    
    	ast_mutex_lock(&original->lock);
    	while(ast_mutex_trylock(&clone->lock)) {
    		ast_mutex_unlock(&original->lock);
    		usleep(1);
    		ast_mutex_lock(&original->lock);
    	}
    
    	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", 
    			original->masq->name, original->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", 
    			clone->name, clone->masqr->name);
    
    	} else {
    		original->masq = clone;
    		clone->masqr = original;
    		ast_queue_frame(original, &null);
    		ast_queue_frame(clone, &null);
    
    		ast_log(LOG_DEBUG, "Done planning to masquerade channel %s into the structure of %s\n", clone->name, original->name);
    
    		res = 0;
    	}
    	ast_mutex_unlock(&clone->lock);
    	ast_mutex_unlock(&original->lock);
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_change_name(struct ast_channel *chan, char *newname)
    {
    	char tmp[256];
    
    	ast_copy_string(tmp, chan->name, sizeof(tmp));
    	ast_copy_string(chan->name, newname, sizeof(chan->name));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", tmp, chan->name, chan->uniqueid);
    
    void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
    {
    	struct ast_var_t *current, *newvar;
    	char *varname;
    
    	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;
    		}
    	}
    }
    
    
    /* 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.
       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);
    
    /*--- ast_do_masquerade: Masquerade a channel */
    
    /* 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);
    
    
    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 */
    
    
    	/* We need the clone's lock, too */
    	ast_mutex_lock(&clone->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    	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_copy_string(original->name, newn, sizeof(original->name));
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Mangle the name of the clone channel */
    
    	ast_copy_string(clone->name, masqn, sizeof(clone->name));
    
    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);
    
    	/* Swap the technlogies */	
    	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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	x = 0;
    	while(cur) {
    		x++;
    		prev = cur;
    		cur = cur->next;
    	}
    	/* If we had any, prepend them to the ones already in the queue, and 
    	 * 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
    		if (res) 
    			ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Start by disconnecting the original's physical side */
    
    	if (clone->tech->hangup)
    		res = clone->tech->hangup(clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res) {
    		ast_log(LOG_WARNING, "Hangup failed!  Strange things may happen!\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Mangle the name of the clone channel */
    
    	ast_copy_string(clone->name, zombn, sizeof(clone->name));
    
    	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. */
    	original->type = clone->type;
    
    	t_pvt = original->monitor;
    	original->monitor = clone->monitor;
    	clone->monitor = t_pvt;
    
    	
    	/* Keep the same language.  */
    
    	ast_copy_string(original->language, clone->language, sizeof(original->language));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* 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];
    
    	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_MAX_FDS - 2] = original->timingfd;