Skip to content
Snippets Groups Projects
app_rpt.c 103 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /** @file app_rpt.c 
     *
     * Asterisk -- A telephony toolkit for Linux.
     *
    
     * Radio Repeater / Remote Base program 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 
    
     * See http://www.zapatatelephony.org/app_rpt.html
     *
    
     * Copyright (C) 2002-2004, Jim Dixon, WB6NIL
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Jim Dixon, WB6NIL <jim@lambdatel.com>
    
     * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     *
    
     * Repeater / Remote Functions:
     * "Simple" Mode:  * - autopatch access, # - autopatch hangup
     * Normal mode:
    
     * See the function list in rpt.conf
    
     *
     *  To send an asterisk (*) while dialing or talking on phone,
     *  use the autopatch acess code.
    
     * status cmds:
     *
     *  1 - Force ID
     *  2 - Give Time of Day
     *  3 - Give software Version
     *
     * cop (control operator) cmds:
     *
     *  1 - System warm boot
     *  2 - System enable
     *  3 - System disable
     *
     * ilink cmds:
     *
     *  1 - Disconnect specified link
     *  2 - Connect specified link -- monitor only
     *  3 - Connect specified link -- tranceive
     *  4 - Enter command mode on specified link
     *  5 - System status
     *  6 - Disconnect all links
     *
     * remote cmds:
     *
     *  0 - Recall Memory MM  (*000-*099) (Gets memory from rpt.conf)
     *  1 - Set VFO MMMMM*KKK*O   (Mhz digits, Khz digits, Offset)
     *  2 - Set Rx PL Tone HHH*D*
     *  3 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
     *  5 - Link Status
     *  100 - RX PL off (Default)
     *  101 - RX PL On
     *  102 - TX PL Off (Default)
     *  103 - TX PL On
     *  104 - Low Power
     *  105 - Med Power
     *  106 - Hi Power
    
    
    /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
    
    
    #define	FUNCTIONS "functions"
    #define TELEMETRY "telemetry"
    #define MORSE "morse"
    #define	FUNCCHAR '*'
    #define	ENDCHAR '#'
    
    
    #define	MAXCONNECTTIME 5000
    
    #define MAXNODESTR 300
    
    
    enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
    
    	CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
    	STATS_VERSION, IDTALKOVER, ARB_ALPHA};
    
    enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
    
    enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR};
    
    
    enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE};
    enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT};
    
    
    enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
    
    
    #include <asterisk/utils.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/lock.h>
    #include <asterisk/file.h>
    #include <asterisk/logger.h>
    #include <asterisk/channel.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/pbx.h>
    #include <asterisk/module.h>
    #include <asterisk/translate.h>
    #include <asterisk/options.h>
    #include <asterisk/config.h>
    
    #include <asterisk/localtime.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <dirent.h>
    #include <ctype.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <sys/file.h>
    #include <sys/ioctl.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <math.h>
    #include <tonezone.h>
    #include <linux/zaptel.h>
    
    
    static  char *tdesc = "Radio Repeater / Remote Base  version 0.18  11/16/2004";
    
    static char *app = "Rpt";
    
    static char *synopsis = "Radio Repeater/Remote Base Control System";
    
    static char *descrip = 
    "  Rpt(sysname):  Radio Remote Link or Remote Base Link Endpoint Process.\n";
    
    
    static int debug = 0;  /* Set this >0 for extra debug output */
    
    Mark Spencer's avatar
    Mark Spencer committed
    STANDARD_LOCAL_USER;
    LOCAL_USER_DECL;
    
    #define	MSWAIT 200
    #define	HANGTIME 5000
    #define	TOTIME 180000
    #define	IDTIME 300000
    #define	MAXRPTS 20
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static  pthread_t rpt_master_thread;
    
    
    struct rpt;
    
    struct rpt_link
    {
    	struct rpt_link *next;
    	struct rpt_link *prev;
    	char	mode;			/* 1 if in tx mode */
    	char	isremote;
    	char	name[MAXNODESTR];	/* identifier (routing) string */
    	char	lasttx;
    	char	lastrx;
    	char	connected;
    	char	outbound;
    	long elaptime;
    	struct ast_channel *chan;	
    	struct ast_channel *pchan;	
    } ;
    
    struct rpt_tele
    {
    	struct rpt_tele *next;
    	struct rpt_tele *prev;
    	struct rpt *rpt;
    
    struct function_table_tag
    {
    	char action[ACTIONSIZE];
    	int (*function)(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    } ;
    
    /* Used to store the morse code patterns */
    
    struct morse_bits
    {		  
    	int len;
    	int ddcomb;
    } ;
    
    struct telem_defaults
    {
    	char name[20];
    	char value[80];
    } ;
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct rpt
    {
    	char *name;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *rxchanname;
    	char *txchanname;
    	char *ourcontext;
    	char *ourcallerid;
    	char *acctcode;
    
    	char *functions;
    	char *link_functions;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int hangtime;
    	int totime;
    	int idtime;
    
    	char tounkeyed;
    	char tonotify;
    	char enable;
    
    	char dtmfbuf[MAXDTMF];
    	char rem_dtmfbuf[MAXDTMF];
    	char cmdnode[50];
    	struct ast_channel *rxchannel,*txchannel;
    
    	struct ast_channel *pchannel,*txpchannel, *remchannel;
    
    	struct rpt_tele tele;
    	pthread_t rpt_call_thread,rpt_thread;
    
    	time_t rem_dtmf_time,dtmf_time_rem;
    
    	int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char exten[AST_MAX_EXTENSION];
    
    	char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
    	char offset;
    	char powerlevel;
    	char txplon;
    	char rxplon;
    
    	char funcchar;
    	char endchar;
    	int link_longestfunc;
    	int longestfunc;
    	int longestnode;	
    } rpt_vars[MAXRPTS];	
    
    static struct telem_defaults tele_defs[] = {
    	{"ct1","|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
    	{"ct2","|t(660,880,150,3072)"},
    	{"ct3","|t(440,0,150,3072)"},
    	{"ct4","|t(550,0,150,3072)"},
    	{"ct5","|t(660,0,150,3072)"},
    	{"ct6","|t(880,0,150,3072)"},
    	{"ct7","|t(660,440,150,3072)"},
    	{"ct8","|t(700,1100,150,3072)"},
    	{"remotemon","|t(1600,0,75,2048)"},
    	{"remotetx","|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
    	{"cmdmode","|t(900,904,200,2048)"},
    	{"functcomplete","|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
    } ;
    
    /*
    * Forward decl's - these suppress compiler warnings when funcs coded further down the file than thier invokation
    */
    
    static int setrbi(struct rpt *myrpt);
    
    
    
    /*
    * Define function protos for function table here
    */
    
    static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    /*
    * Function table
    */
    
    static struct function_table_tag function_table[] = {
    	{"cop", function_cop},
    	{"autopatchup", function_autopatchup},
    	{"autopatchdn", function_autopatchdn},
    	{"ilink", function_ilink},
    	{"status", function_status},
    	{"remote", function_remote}
    } ;
    	
    static int myatoi(char *str)
    {
    int	ret;
    
    	if (str == NULL) return -1;
    	if (sscanf(str,"%i",&ret) != 1) return -1;
    	return ret;
    }
    
    static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
    {
    
    	int flags = ZT_IOMUX_WRITEEMPTY;
    	int res;
    
            if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
                    return res;
                                                                                                                                                
            while(chan->generatordata) {
    
    		if (ast_safe_sleep(chan,1)) return -1;
    	}
    
    
    	/*
    	* Wait for the zaptel driver to physically write the tone blocks to the hardware
    	*/
    
    	res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
    	if (res < 0)
    		return -1;
            return 0;
    
    }
    
    static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
    {
    	return play_tone_pair(chan, freq, 0, duration, amplitude);
    }
    
    static int play_silence(struct ast_channel *chan, int duration)
    {
    	return play_tone_pair(chan, 0, 0, duration, 0);
    }
    
    
    static int send_morse(struct ast_channel *chan, char *string, int speed, int freq, int amplitude)
    {
    
    static struct morse_bits mbits[] = {
    		{0, 0}, /* SPACE */
    		{0, 0}, 
    		{6, 18},/* " */
    		{0, 0},
    		{7, 72},/* $ */
    		{0, 0},
    		{0, 0},
    		{6, 30},/* ' */
    		{5, 13},/* ( */
    		{6, 29},/* ) */
    		{0, 0},
    		{5, 10},/* + */
    		{6, 51},/* , */
    		{6, 33},/* - */
    		{6, 42},/* . */
    		{5, 9}, /* / */
    		{5, 31},/* 0 */
    		{5, 30},/* 1 */
    		{5, 28},/* 2 */
    		{5, 24},/* 3 */
    		{5, 16},/* 4 */
    		{5, 0}, /* 5 */
    		{5, 1}, /* 6 */
    		{5, 3}, /* 7 */
    		{5, 7}, /* 8 */
    		{5, 15},/* 9 */
    		{6, 7}, /* : */
    		{6, 21},/* ; */
    		{0, 0},
    		{5, 33},/* = */
    		{0, 0},
    		{6, 12},/* ? */
    		{0, 0},
            	{2, 2}, /* A */
     		{4, 1}, /* B */
    		{4, 5}, /* C */
    		{3, 1}, /* D */
    		{1, 0}, /* E */
    		{4, 4}, /* F */
    		{3, 3}, /* G */
    		{4, 0}, /* H */
    		{2, 0}, /* I */
    		{4, 14},/* J */
    		{3, 5}, /* K */
    		{4, 2}, /* L */
    		{2, 3}, /* M */
    		{2, 1}, /* N */
    		{3, 7}, /* O */
    		{4, 6}, /* P */
    		{4, 11},/* Q */
    		{3, 2}, /* R */
    		{3, 0}, /* S */
    		{1, 1}, /* T */
    		{3, 4}, /* U */
    		{4, 8}, /* V */
    		{3, 6}, /* W */
    		{4, 9}, /* X */
    		{4, 13},/* Y */
    		{4, 3}  /* Z */
    	};
    
    
    	int dottime;
    	int dashtime;
    	int intralettertime;
    	int interlettertime;
    	int interwordtime;
    	int len, ddcomb;
    	int res;
    	int c;
    		
    	
    	res = 0;
    	
    	/* Approximate the dot time from the speed arg. */
    	
    	dottime = 900/speed;
    	
    	/* Establish timing releationships */
    	
    	dashtime = 3 * dottime;
    	intralettertime = dottime;
    	interlettertime = dottime * 4 ;
    	interwordtime = dottime * 7;
    	
    	for(;(*string) && (!res); string++){
    	
    		c = *string;
    		
    		/* Convert lower case to upper case */
    		
    		if((c >= 'a') && (c <= 'z'))
    			c -= 0x20;
    		
    		/* Can't deal with any char code greater than Z, skip it */
    		
    		if(c  > 'Z')
    			continue;
    		
    		/* If space char, wait the inter word time */
    					
    		if(c == ' '){
    			if(!res)
    				res = play_silence(chan, interwordtime);
    			continue;
    		}
    		
    		/* Subtract out control char offset to match our table */
    		
    		c -= 0x20;
    		
    		/* Get the character data */
    		
    		len = mbits[c].len;
    		ddcomb = mbits[c].ddcomb;
    		
    		/* Send the character */
    		
    		for(; len ; len--){
    			if(!res)
    				res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
    			if(!res)
    				res = play_silence(chan, intralettertime);
    			ddcomb >>= 1;
    		}
    		
    		/* Wait the interletter time */
    		
    		if(!res)
    			res = play_silence(chan, interlettertime - intralettertime);
    	}
    	
    	/* Wait for all the frames to be sent */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    		
    	return res;
    }
    
    static int send_tone_telemetry(struct ast_channel *chan, char *tonestring)
    {
    	char *stringp;
    	char *tonesubset;
    	int f1,f2;
    	int duration;
    	int amplitude;
    	int res;
    	
    	res = 0;
    	
    	stringp = ast_strdupa(tonestring);
    
    	for(;tonestring;){
    		tonesubset = strsep(&stringp,")");
    		if(!tonesubset)
    			break;
    		if(sscanf(tonesubset,"(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
    			break;
    		res = play_tone_pair(chan, f1, f2, duration, amplitude);
    		if(res)
    			break;
    	}
    	if(!res)
    		res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    			
    	return res;
    		
    }
    	
    
    static int sayfile(struct ast_channel *mychannel,char *fname)
    {
    int	res;
    
    	res = ast_streamfile(mychannel, fname, mychannel->language);
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    static int saycharstr(struct ast_channel *mychannel,char *str)
    {
    int	res;
    
    	res = ast_say_character_str(mychannel,str,NULL,mychannel->language);
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    
    /* Retrieve an int from a config file */
                                                                                    
    static int retrieve_astcfgint(char *category, char *name, int min, int max, int defl)
    {
            char *var;
            int ret;
                                                                                    
            var = ast_variable_retrieve(cfg, category, name);
            if(var){
                    ret = myatoi(var);
                    if(ret < min)
                            ret = min;
                    if(ret > max)
                            ret = max;
            }
            else
                    ret = defl;
            return ret;
    }
    
    static int telem_any(struct ast_channel *chan, char *entry)
    {
    	int res;
    	char c;
    	
    	static int morsespeed;
    	static int morsefreq;
    	static int morseampl;
    	static int morseidfreq = 0;
    	static int morseidampl;
    	static char mcat[] = MORSE;
    	
    	res = 0;
    	
    	if(!morseidfreq){ /* Get the morse parameters if not already loaded */
    		morsespeed = retrieve_astcfgint( mcat, "speed", 5, 20, 20);
            	morsefreq = retrieve_astcfgint( mcat, "frequency", 300, 3000, 800);
            	morseampl = retrieve_astcfgint( mcat, "amplitude", 200, 8192, 4096);
    		morseidampl = retrieve_astcfgint( mcat, "idamplitude", 200, 8192, 2048);
    		morseidfreq = retrieve_astcfgint( mcat, "idfrequency", 300, 3000, 330);	
    	}
    	
    	/* Is it a file, or a tone sequence? */
    			
    	if(entry[0] == '|'){
    		c = entry[1];
    		if((c >= 'a')&&(c <= 'z'))
    			c -= 0x20;
    	
    		switch(c){
    			case 'I': /* Morse ID */
    				res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
    				break;
    			
    			case 'M': /* Morse Message */
    				res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
    				break;
    			
    			case 'T': /* Tone sequence */
    				res = send_tone_telemetry(chan, entry + 2);
    				break;
    			default:
    				res = -1;
    		}
    	}
    	else
    		res = sayfile(chan, entry); /* File */
    	return res;
    }
    
    /*
    * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
    *
    * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
    */
    
    
    static int telem_lookup(struct ast_channel *chan, char *node, char *name)
    
    	char *telemetry;
    	char *telemetry_save;
    
    	/* Retrieve the section name for telemetry from the node section */
    	
    	telemetry = ast_variable_retrieve(cfg, node, TELEMETRY);
    	if(telemetry){
    		telemetry_save = ast_strdupa(telemetry);
    		if(!telemetry_save){
    			ast_log(LOG_WARNING,"ast_strdupa() failed in telem_lookup()\n");
    			return res;
    		}
    		entry = ast_variable_retrieve(cfg, telemetry_save, name);
    	}
    	
    
    	/* Try to look up the telemetry name */
    	
    	if(!entry){
    		/* Telemetry name wasn't found in the config file, use the default */
    		for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
    			if(!strcasecmp(tele_defs[i].name, name))
    				entry = tele_defs[i].value;
    		}
    	}
    	if(entry)	
    		telem_any(chan, entry);
    	else{
    		ast_log(LOG_WARNING, "Telemetry name not found: %s\n", name);
    		res = -1;
    	}
    	return res;
    }
    
    
    /*
    * Wait a configurable interval of time 
    */
    
    
    static void wait_interval(struct rpt *myrpt, int type)
    {
    	int interval;
    	char *wait_times;
    	char *wait_times_save;
    	
    	wait_times_save = NULL;
    	wait_times = ast_variable_retrieve(cfg, myrpt->name, "wait_times");
    	
    	if(wait_times){
    		wait_times_save = ast_strdupa(wait_times);
    		if(!wait_times_save){
    			ast_log(LOG_WARNING, "Out of memory in wait_interval()\n");
    			wait_times = NULL;
    		}
    	}
    	
    	switch(type){
    		case DLY_TELEM:
    			if(wait_times)
    				interval = retrieve_astcfgint(wait_times_save, "telemwait", 500, 5000, 1000);
    			else
    				interval = 1000;
    			break;
    		
    		case DLY_ID:
    			if(wait_times)
    				interval = retrieve_astcfgint(wait_times_save, "idwait",250,5000,500);
    			else
    				interval = 500;
    			break;
    			
    		case DLY_UNKEY:
    			if(wait_times)
    				interval = retrieve_astcfgint(wait_times_save, "unkeywait",500,5000,1000);
    			else
    				interval = 1000;
    			break;
    			
    		case DLY_CALLTERM:
    			if(wait_times)
    				interval = retrieve_astcfgint(wait_times_save, "calltermwait",500,5000,1500);
    			else
    				interval = 1500;
    			break;
    			
    		default:
    			return;
    	}
    	
    	usleep(1000 * interval);
    	return;
    }
    
    	
    
    
    static void *rpt_tele_thread(void *this)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    ZT_CONFINFO ci;  /* conference info */
    
    int	res = 0,hastx,imdone = 0, unkeys_queued;
    
    struct	rpt_tele *mytele = (struct rpt_tele *)this;
    
    struct	rpt *myrpt;
    struct	rpt_link *l,*m,linkbase;
    struct	ast_channel *mychannel;
    
    int vmajor, vminor;
    
    char *p,*ct,*ct_copy,*ident, *nodename;
    
    time_t t;
    struct tm localtm;
    
    
    	/* get a pointer to myrpt */
    	myrpt = mytele->rpt;
    
    
    	/* Snag copies of a few key myrpt variables */
    	ast_mutex_lock(&myrpt->lock);
    	nodename = ast_strdupa(myrpt->name);
    	ident = ast_strdupa(myrpt->ident);
    	ast_mutex_unlock(&myrpt->lock);
    	
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* allocate a pseudo-channel thru asterisk */
    
    	mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!mychannel)
    	{
    		fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
    
    		remque((struct qelem *)mytele);
    		ast_mutex_unlock(&myrpt->lock);
    		free(mytele);		
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    
    	ast_mutex_lock(&myrpt->lock);
    	mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
    	ast_mutex_unlock(&myrpt->lock);
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* make a conference for the tx */
    	ci.chan = 0;
    
    	/* If there's an ID queued, only connect the ID audio to the local tx conference so 
    		linked systems can't hear it */
    
    	ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY)) ?
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ci.confmode = ZT_CONF_CONFANN;
    	/* first put the channel on the conference in announce mode */
    	if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
    	{
    		ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
    
    		ast_mutex_lock(&myrpt->lock);
    		remque((struct qelem *)mytele);
    		ast_mutex_unlock(&myrpt->lock);
    		free(mytele);		
    		ast_hangup(mychannel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    	ast_stopstream(mychannel);
    
    		wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM);
    
    		res = telem_any(mychannel, ident); 
    		imdone=1;
    	
    
    		
    		
    	    case IDTALKOVER:
    	    	p = ast_variable_retrieve(cfg, nodename, "idtalkover");
    	    	if(p)
    			res = telem_any(mychannel, p); 
    		imdone=1;	
    	    	break;
    	    		
    
    	    case PROC:
    		/* wait a little bit longer */
    
    		res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
    		break;
    	    case TERM:
    		/* wait a little bit longer */
    
    		wait_interval(myrpt, DLY_CALLTERM);
    
    		res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
    		break;
    	    case COMPLETE:
    		/* wait a little bit */
    
    		wait_interval(myrpt, DLY_TELEM);
    		res = telem_lookup(mychannel, myrpt->name, "functcomplete");
    
    
    		/*
    		* if there's one already queued, don't do another
    		*/
    
    		tlist = myrpt->tele.next;
    		unkeys_queued = 0;
                    if (tlist != &myrpt->tele)
                    {
                            ast_mutex_lock(&myrpt->lock);
                            while(tlist != &myrpt->tele){
                                    if (tlist->mode == UNKEY) unkeys_queued++;
                                    tlist = tlist->next;
                            }
                            ast_mutex_unlock(&myrpt->lock);
    		}
    		if( unkeys_queued > 1){
    			imdone = 1;
    			break;
    		}
    
    
    		l = myrpt->links.next;
    		if (l != &myrpt->links)
    		{
    			ast_mutex_lock(&myrpt->lock);
    			while(l != &myrpt->links)
    			{
    				if (l->mode) hastx++;
    				l = l->next;
    			}
    			ast_mutex_unlock(&myrpt->lock);
    
    			res = telem_lookup(mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
    
    			if(res)
    				ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
    			
    		
    
    		/* if in remote cmd mode, indicate it */
    
    			if (myrpt->cmdnode[0])
    			{
    				ast_safe_sleep(mychannel,200);
    
    				res = telem_lookup(mychannel, myrpt->name, "cmdmode");
    
    				if(res)
    				 	ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
    				ast_stopstream(mychannel);
    			}
    
    		else if((ct = ast_variable_retrieve(cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */
    			ct_copy = ast_strdupa(ct);
    
    			res = telem_lookup(mychannel, myrpt->name, ct_copy);
    
    			if(res)
    			 	ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);		
    		}	
    			
    
    		imdone = 1;
    		break;
    	    case REMDISC:
    		/* wait a little bit */
    
    		res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
    		if (!res) 
    			res = ast_waitstream(mychannel, "");
    		else
    			 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    		ast_stopstream(mychannel);
    		ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
    		res = ast_streamfile(mychannel, ((mytele->mylink.connected) ? 
    			"rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
    		break;
    	    case REMALREADY:
    		/* wait a little bit */
    
    		res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
    		break;
    	    case REMNOTFOUND:
    		/* wait a little bit */
    
    		res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
    		break;
    	    case REMGO:
    		/* wait a little bit */
    
    		res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
    		break;
    	    case CONNECTED:
    		/* wait a little bit */
    
    		res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
    		if (!res) 
    			res = ast_waitstream(mychannel, "");
    		else
    			 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    		ast_stopstream(mychannel);
    		ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
    		res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
    		break;
    	    case CONNFAIL:
    		res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
    		if (!res) 
    			res = ast_waitstream(mychannel, "");
    		else
    			 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    		ast_stopstream(mychannel);
    		ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language);
    		res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
    		break;
    	    case STATUS:
    		/* wait a little bit */
    
    		hastx = 0;
    		linkbase.next = &linkbase;
    		linkbase.prev = &linkbase;
    		ast_mutex_lock(&myrpt->lock);
    		/* make our own list of links */
    		l = myrpt->links.next;
    		while(l != &myrpt->links)
    		{
    			m = malloc(sizeof(struct rpt_link));
    			if (!m)
    			{
    				ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
    				pthread_exit(NULL);
    			}
    			memcpy(m,l,sizeof(struct rpt_link));
    			m->next = m->prev = NULL;
    			insque((struct qelem *)m,(struct qelem *)linkbase.next);
    			l = l->next;
    		}
    		ast_mutex_unlock(&myrpt->lock);
    		res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
    		if (!res) 
    			res = ast_waitstream(mychannel, "");
    		else
    			 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    		ast_stopstream(mychannel);
    		ast_say_character_str(mychannel,myrpt->name,NULL,mychannel->language);
    		if (!res) 
    			res = ast_waitstream(mychannel, "");
    		else
    			 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    		ast_stopstream(mychannel);
    		if (myrpt->callmode)
    		{
    			hastx = 1;
    			res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
    			if (!res) 
    				res = ast_waitstream(mychannel, "");
    			else
    				 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    			ast_stopstream(mychannel);
    		}
    		l = linkbase.next;
    		while(l != &linkbase)
    		{
    			hastx = 1;