ftp.nice.ch/pub/next/unix/communication/am.1.16.NIHS.bs.tar.gz#/am.1.16.NIHS.bs/src/am.c

This is am.c in view mode; [Download] [Up]

/*	TEXT WAS MADE WITH 4*SPACE = 1*TAB

	program	:	am.c
	version	:	see the version.h file	
	date	:	Tue May  3 12:11:51 MET DST 1994
	purpose	:	simulate an answering machine with fax- and datacall capabilities 
	author	:	by jolly ( who else )
	
	ADPCM3 support included by kiwi@belly, April '94

*/


int	modem; 			// fildes of modem 
int	timerstatus;	// sigalarm timerstatus for recording and playing
int	realtime;		// sigalarm timer for recording and playing
int connected;
int amstatus;

#import <libc.h>
#import <mach/cthreads.h>
#import "precomp.h"

//extern int init_amserver();

int main(void)
{	
	int	tty;
	
	signal(SIGALRM	,(void *)sig_alrm);
	signal(SIGINT	,(void *)sig_fatal);
	signal(SIGTERM	,(void *)sig_fatal);
	
	if(chdir(PATH)==-1)
		fatal("Can't change working directory to %s\n",PATH);
		
	/* forking server thread for status inquiries, kiwi@belly */
	//cthread_detach(cthread_fork((cthread_fn_t)init_amserver, (any_t)NULL));
		
	//tty=open("/dev/tty",O_RDWR);
	//ioctl(tty, TIOCNOTTY, 0);

	close(STD_IN);
	close(STD_OUT);
	close(STD_ERR);
#if NORMALMODE
	tty=open("/dev/tty",O_RDWR);
	ioctl(tty, TIOCNOTTY, 0);
#endif
#if DEBUG
	log("*** Answering Machine started ***");
#endif
	amstatus = 1;
#if NORMALMODE
	while(1) wait_for_call();
#else
	wait_for_call();
#endif
}


void wait_for_call(void)
{
	int		nfds=1;
	fd_set fdset;
	

	do
	{
		do
		{	
#ifndef NORMALMODE
			close(STD_IN);
#endif
			open_line(DIAL_OUT);
			reset_modem();
			unlock_line(DIAL_OUT);
			/* set EDR to recognize CNG (FAX) and DTMF 0 (DATACALLS) */
			/*ask("ATS40=%d",OK,8+16+32+64);*/
			wait_for("XXX",200);
			FD_ZERO(&fdset);
			FD_SET(modem,&fdset);
#if NORMALMODE
			select(nfds,&fdset,NULL,NULL,NULL);
#endif
		}
		while( tst_lock() && close_line(DIAL_OUT));

#if NORMALMODE
		if(wait_for("NO CARRIER",100))
#else
		if(1)
#endif
		{
#if DEBUG > 2
			log("answering call");
#endif
			ask("AT+FCLASS=8","OK");
			say("ATA");
		}
		else
		{
			ask("AT+FCLASS=8","OK");
			ask("ATS0=%d","OK",RING);
		}
	}
#if NORMALMODE
	/* Jedes Klingeln dauert 6 Sekunden? kiwi@belly,
		oh Mann, diese 20000 hat mir den letzten Nerv geraubt! */
	while( !wait_for("VCON",RING*6000) && close_line(DIAL_OUT) );
#else
	while(0);
#endif

	main_loop();
	
	reset_modem();
	close_line(DIAL_OUT);
}





/*************************************************************************************nifty fncts */

/* bug fixes by kiwi@belly, Fri May  6 15:26:27 MET DST 1994 */

int rec_message(char *name)
{
	static char		*buf=NULL;
	FILE 			*feil;
	static struct	zyxel		header;
	static struct	itimerval	alrm,oalrm;
	int 			pass=0, dle_recv=0;
	char			*i,*bend, *prev;
	char			dle = DLE;

	amstatus = 2;
	
	if(buf==NULL)
	{
		buf=(char *)malloc(sizeof(char)*BSIZE);
		strcpy(header.title,"ZyXEL");
		header.mode=ZYX_VOC;
		switch(SPEECH)
		{
			case ADPCM_3	:header.voice=ZYX_AD3;break;
			case ADPCM_2	:header.voice=ZYX_AD2;break;
			case CELP		:header.voice=ZYX_CLP;break;
			default			:header.voice=ZYX_AD2;break;
		}
	}

	if((feil=fopen(name,"w"))==NULL)
		fatal("Can't open File :%s",name);
	fwrite(&header,sizeof(struct zyxel),1,feil);

	ask("at+vsd=%d,%d","OK",SIL_TRES,SIL_TIME*10);
	/*ask("at+vvt=%d","OK",40);
	ask("at+vgt=%d","OK",100); seems to work only on internal speaker*/
	ask(ATSP,"OK");
	ask("at+vrx","CONNECT\r\n");
#if DEBUG
	log("recording message : %s",name);
#endif

	siginterrupt(SIGALRM,1);
	alrm.it_interval.tv_sec=0;
	alrm.it_interval.tv_usec=1000000/RPS;	
	alrm.it_value.tv_sec=0;
	alrm.it_value.tv_usec=1000000/RPS;
	if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
		fatal("error creating timer\n");

	/* who is going to remove this timer when exit on silence or similar circumstances?
	   kiwi@belly
	*/

	timerstatus	=T_REC;
	realtime	=0;
	do
	{	
		do
		{
			sigpause(SIGALRM);
			if(timerstatus==T_STP) {
										fclose(feil);
										ask("\x10\x03","VCON");
										alrm.it_interval.tv_usec=0;	
										alrm.it_value.tv_usec=0;
										if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
											fatal("error creating timer\n");
										return MAXTIME;
									}
			bend=buf+read(modem,buf,BSIZE);
		}
		while(bend<=buf);
		
		prev = buf;
		for(i=buf;i<bend;i++)
		{
			/* we have to handle a dle shielded code? */
			if(dle_recv)
			{	
				dle_recv = 0; /* we got it */
				
				/* write the buffer without the last DLE */
				if((i-prev-1)>0) fwrite(prev,1,i-prev-1,feil);

				/* previous marker gets set after <DLE>x */
				prev = i+1;

#if DEBUG 
				if(*i!=DLE) log("REC time=%d  %c=%x",realtime,(char)*i,(char)*i);
#endif
				switch(*i)
				{
					case 'e'	:{fclose(feil);	ask("\x10\x03","VCON");return DATACALL;} /* kiwi, ROM6.12 */
					case 's' 	:{fclose(feil);	ask("\x10\x03","VCON");return SILENCE;}
					case 'q' 	:{fclose(feil);	ask("\x10\x03","VCON");return QUIET;}
					case '*' 	:{fclose(feil);	ask("\x10\x03","VCON");return (pass)?pass:STAR;}
					case '#' 	:pass=0;break;
					case '1'	:pass=pass*10+1;break;
					case '2'	:pass=pass*10+2;break;
					case '3'	:pass=pass*10+3;break;
					case '4'	:pass=pass*10+4;break;
					case '5'	:pass=pass*10+5;break;
					case '6'	:pass=pass*10+6;break;
					case '7'	:pass=pass*10+7;break;
					case '8'	:pass=pass*10+8;break;
					case '9'	:pass=pass*10+9;break;
					case '0'	:pass=pass*10;break;
					case 'c'	:{fclose(feil);	ask("\x10\x03","VCON");return FAX;}
					case 'b'	:{fclose(feil);	ask("\x10\x03","VCON");return BUSY;}
					case 'h'	:
					case 'd'	:{fclose(feil);	ask("\x10\x03","VCON");return HANG_UP;}
					case ETX	:{fclose(feil); ask("\x10\x03","VCON");return EOS;}
					case DLE	:fwrite(&dle,1,1,feil);
								 fwrite(&dle,1,1,feil);break;	/* write <DLE><DLE> verbatim */
					
					/*  'h' is not documented?!?!
						'd' is new since 6.11a and means Dialtone detected
					*/
					
					default		:log("unknown <DLE> code, time=%d %c=%x",realtime,(char)*i,(char)*i);
								 break;
					}
			}
			else
			if(*i == DLE) dle_recv = 1;
		} /* closed for loop */
				
		/* there is really something to write?
		 	if there is and no dle is received at the moment - write to end of buffer.
			else don't write the <DLE> which is the last char in buffer	
		 */
		if(bend>prev) fwrite(prev,1,bend-prev - (dle_recv?1:0),feil);
		prev = buf;
	}
	while(1);
	return 0;	// never reached
}





int ply_message(char *name)
{
	char *snd;
	FILE *feil;
	int mfl;
	int pwd;
	
	static char a_name[200];
	static struct zyxel header;
		
	strcpy(a_name,name);
	strcat(a_name,Z_EXT);

	amstatus = 3;
	if((feil=fopen(a_name,"r"))==NULL)
	{
		log("Can't play Message :%s",a_name); 
		return Z_ERROR;
	}
#if DEBUG
	log("Playing Message :%s",a_name);
#endif
	fseek(feil,0L,SEEK_END);
	mfl=ftell(feil);
	rewind(feil);

	snd=(char *)malloc(sizeof(char)*mfl-sizeof(struct zyxel));
	fread(&header,sizeof(struct zyxel),1,feil);
	fread(snd,1,mfl,feil);
	fclose(feil);

	switch(isZyXEL(&header))
	{
		case ZYX_AD3		:ask("at+vsm=3","OK");break;
		case ZYX_AD2		:ask("at+vsm=2","OK");break;
		case ZYX_CLP		:ask("at+vsm=1","OK");break;
		case ZYX_NOSNDHEADER:return log("File:%s is not a voice file",name);
		default				:return log("File:%s voice format not supported",name);
	}		

	/* set modem to send voice data */
	ask("AT+VTX","CONNECT\r\n");
	pwd=block_write(snd,mfl);
	if(pwd==FAX)
		{
			free(snd);
			ask("\x10\x03","VCON");
#if DEBUG
			log("Detected Fax while playing a message");
#endif
			try_fax_connection(NULL);
			return FAX;
		}
	ask("\x10\x03","VCON");
	free(snd);
	return pwd;
}



int try_data_connection(char *rec_name)
{		
	amstatus = 4;

	ask("at+fclass=0","OK");
	ask("at&n0&d0&c0s2=32","OK");
	close_line(DIAL_OUT);
	open_line(DIAL_CONNECT);
	ask("AT&D3","OK");
	say("ATA");

	if( (realtime<(SIL_TIME+5)*RPS) && wait_for("CONNECT",MAX_CD*1000)) 
	{	
		char c=32;
#if DEBUG
		log("Got Data Connection");
#endif
		remove(rec_name);
		write(modem,&c,1);usleep(500);
		write(modem,&c,1);usleep(500);
		write(modem,&c,1);usleep(500);
		wait_for("OK",2000);
		ask("at&c1","OK");
		ask("ats2=255","OK");
		say("ato");
		execle("/usr/etc/getty","/usr/etc/getty",GETY,NULL,NULL);
		exit(1);
	}
	else
	{
#if DEBUG
		log("Got no Data Connection");
#endif
		close_line(DIAL_CONNECT);
		open_line(DIAL_OUT);

		return shorten_file(rec_name);
	}
	return EOC;
}



/* shortens the recorded message file by the given silence interval value.
   I would like very much to add something to cut off the hangup noise at
   the end of a recording but I fear that a fair amount of signal processing
   has to be done to achieve this.
   This procedure was moved from inside try_data_connection, because I needed
   it for correct handling of <DLE>q which works since I upgraded to ROM 6.12
   (it didn't work in 5.01 so _every_ recording was trying a data connection
   in the end and the recording got truncated appropriately. Since <DLE>q
   works, we have to truncate also on normal termination.)
   kiwi@belly */

int shorten_file(char *rec_name)
{
		FILE	*feil;
		char	*tmp;
		long	len;


		feil=fopen(rec_name,"r");
		if(feil==NULL)
		{
			log("failed fopen can't open messagefile %s for reading",rec_name);
			return Z_ERROR;
		}
		fseek(feil,0L,SEEK_END);
		len=ftell(feil)-( (SIL_TIME)*BPS+(BPS/2) );
		if(len<0)
		{
			fclose(feil);
			remove(rec_name); // never again - this fuckin people that hangup after the welcome message !
		}
		else
		{
			rewind(feil);
			tmp=(char *)malloc(sizeof(char)*len+1);
			if(!tmp)
			{
				log("Cant alloc enough memory");
				return Z_ERROR;
			}
			fread(tmp,1,len,feil);
			fclose(feil);
			remove(rec_name);
			feil=fopen(rec_name,"w");
			if(feil==NULL)
			{
				log("failed fopen can't open messagefile %s for writing",rec_name);
				return Z_ERROR;
			}
			fwrite(tmp,1,len,feil);
			fclose(feil);
			free(tmp);
		}
		return EOC;
}




/*************************************************************************************low level fncts */
/* changed to log any ERROR message even ifndef DEBUG, kiwi@belly */

int wait_for(char *s,long t)
{
	static char tst[100];
	static char err[] = "ERROR";
	static struct itimerval alrm,oalrm;
	int b=0,a=0,ok,flg=0,len,errok;

	siginterrupt(SIGALRM,1);
	alrm.it_interval.tv_sec=t/1000;				/* changed interval timer from 0, because it might happen */
	alrm.it_interval.tv_usec=(t%1000)*1000;		/* (in rare circumstances) that the timer expires during  */
	alrm.it_value.tv_sec=t/1000;				/* the handling of the input (not during the read) which  */
	alrm.it_value.tv_usec=(t%1000)*1000; 		/* would cause this function to hang forever. kiwi@belly  */
	if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
		fatal("error creating timer");

	flg=0;
	len=strlen(s);
	tst[len]=0;
	ok=0;
	errok=0;
	do
	{
		a=read(modem,tst+((++b)%len),1);
		if(a<1)								/* changed from a==-1 because a==0 means EOF (DTR dropped?).
												There might be more reasons why the read failed than
												SIGALRM, kiwi@belly. It really got stuck here!!! */
		{
			flg=1;
		}
		else
		{
			/*log("wait for recv: %c=%x",tst[b%len],tst[b%len]);*/
			if(tst[b%len]==s[ok]) ok++;
			else ok=0;
			if(tst[b%len]==err[errok]) errok++;
			else errok=0;
			if(errok == 5)
			{
				log("Waited for (%s), got ERROR time=%d",s,oalrm.it_value.tv_usec); 
				flg = 1;
			}
		}
	}
	while(ok!=len && !flg);
	alrm.it_interval.tv_sec=0;
	alrm.it_interval.tv_usec=0;
	alrm.it_value.tv_sec=0;
	alrm.it_value.tv_usec=0;
	if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
		fatal("error removing timer");

#if DEBUG > 1
	log("Waited for (%s) got (%s) == %s time=%d",s,tst,ok?"YES":"NO",oalrm.it_value.tv_usec);
#endif
	return ok;
}


/* some variables we need for the communication between the sigalrm routine and block_write */

int modrecv_start = 0;
int modrecv_end = 0;
unsigned char modems_opinion[8];    /* ringbuffer for XON/XOFF requests from modem */


/* almost completely rewritten by kiwi@belly.
	added XON/XOFF support.
	
	Why don't we get XON/XOFF support by the tty driver when using RAW|TANDEM?
*/

int block_write(char * snd,int size)
{
	int 	i=0,a,pass=0;
	static 	struct itimerval alrm,oalrm;
	char	c;
	int 	en_tx = 1, dle_recv = 0;
	
	modrecv_start = modrecv_end = 0;
	

	siginterrupt(SIGALRM,1);
	alrm.it_interval.tv_sec=0;
	alrm.it_interval.tv_usec=1000000/(PPS);
	alrm.it_value.tv_sec=0;
	alrm.it_value.tv_usec=1000000/(PPS);
	if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
		fatal("error creating timer");
	
	realtime	=PPS;
	timerstatus	=T_PLY;
	do
	{
		/* if transmission enabled */
		if(en_tx)
		{
			if((size-i)>=BPP)
				a=write(modem,snd+i,BPP);
			else
				a=write(modem,snd+i,size-i);			
			if(a>0)
				i+=a;
#if DEBUG > 4
			else log("block write probably interrupted by sigalrm - subject to retry");
#endif
		}

		/* got something from modem */
		if(modrecv_start != modrecv_end)
		{
			
			c = modems_opinion[modrecv_start];
			modrecv_start = (++modrecv_start) & 7;

#if DEBUG > 2
			log("checking modem input");
			log("received from modem : %c = %x - realtime(s)=%f",c,c,1.0*realtime/PPS);
#endif			
			if(c=='\x13')
			{
				en_tx = 0;	/* disable transmission */
#if DEBUG > 2
				log("XOFF from modem - stopped");
#endif			
			}
			else
			if(c=='\x11')
			{
				en_tx = 1;	/* enable transmission */
#if DEBUG > 2
				log("XON from modem - started");
#endif			
			}
			else
			/* got something important? */
			if(dle_recv) 
			{
#if DEBUG 
				log("PLY time=%d  %c=%x",realtime,c,c);
#endif
				dle_recv = 0;
				switch(c)
				{
					case '*' 	:{return (pass)?pass:STAR;}
					case '#' 	:pass=0;break;
					case '1'	:pass=pass*10+1;break;
					case '2'	:pass=pass*10+2;break;
					case '3'	:pass=pass*10+3;break;
					case '4'	:pass=pass*10+4;break;
					case '5'	:pass=pass*10+5;break;
					case '6'	:pass=pass*10+6;break;
					case '7'	:pass=pass*10+7;break;
					case '8'	:pass=pass*10+8;break;
					case '9'	:pass=pass*10+9;break;
					case '0'	:pass=pass*10;break;
					case 'c'	:return FAX;
					case 'b'	:return BUSY;
					case 'h'	:
					case 'd'	:return HANG_UP; 		/* 'd' = Dialtone (6.11a) */
					case 'e'	:return DATACALL;		/* 'e' = Data Calling Tone (6.11a) */					
					case ETX	:return EOS;
				}
			}
			else
			if(c==DLE) dle_recv=1;
		}
	}
	while(i<size);

	/*wenn einkommentiert bleibt am gelegentlich an dieser Stelle haengen (startrek.zyxel)
    while(realtime*BPP<i)
		sigpause(SIGALRM);*/

	alrm.it_interval.tv_sec=0;
	alrm.it_interval.tv_usec=0;
	alrm.it_value.tv_sec=0;
	alrm.it_value.tv_usec=0;
	if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
		fatal("error removing timer");
	return EOC;
}




/*************************************************************************************signals */

int sig_alrm()
{
	int val;
	
	switch(timerstatus)
	{
		case T_REC	:if(realtime++>MAXRECTIME) timerstatus=T_STP;break;
		
		case T_PLY	:  /* In case of playing a message, the feedback from the modem gets
					   regularily checked if modem wants a pause (XOFF). The fcntl calls
					   make input non blocking for a moment (non blocking writes are
					   not what we want usually)
					   If we get something, modrecv_end gets incremented to reflect the number
					   of characters received. modrecv_start gets incremented after the
					   processing in block_write.
					   kiwi@belly
					   */
					   
					 realtime++;
					 val = fcntl(modem, F_GETFL, 0);
					 fcntl(modem, F_SETFL, val|O_NDELAY);

					 while (read(modem,&modems_opinion[modrecv_end],1)==1)
					 	modrecv_end = (++modrecv_end) & 7;

					 fcntl(modem, F_SETFL, val);
					 break;
		case T_FAX	:realtime--;break;
	}
	return 0;
}

/* modified to reset modem even if signal occured during voice transmission
	this makes up a better testing environment where we can simply kill the job without hanging the modem 
	kiwi@belly
*/

int sig_fatal()
{
	log("sig_fatal");
	ask("\x10\x03", "VCON");
	ask("AT+VLS=0", "OK");
	ask("AT+FCLASS=0", "OK");
	fatal("Signal occured");
	return 0;
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.