/* File: cid11.c
 * Desc: A simple caller-ID log util
 * Auth: Woody
 * Note: GPL
 */

#define USE_SAY 1
//#define DO_HTML 1
#define	DO_XDIALOG 1

#include <termios.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
//#include <sys/signal.h>
#include <sys/types.h>

#define BAUDRATE B115200
//#define MODEMDEVICE "/dev/ttyS1"
#define LOGFILE "/var/log/phone.log"
#define LOCKFILE "/var/lock/cid..lck"
#define _POSIX_SOURCE 1			/* POSIX compliant source */
#define MAX(a,b) ((a>b)?a:b)

#define deb(x) x
#define BUFFSIZE 256

extern int errno;
char *mytime ();
static int modem_fd = 0;
static int log_fd = 0;

/*********************************************************************/
void cleanup(void)
{
int n;

//printf("wms: cleanup().\n");
    n = open (LOCKFILE, O_RDONLY);
    if (n > 0)
    {
	close(n);
	unlink(LOCKFILE);
    }

    if (modem_fd)
    {
	close(modem_fd);
	modem_fd = 0;
    }
    
    if (log_fd)
    {
	close(log_fd);
	log_fd = 0;
    }
}
/*********************************************************************/
void sighandler(int n)
{
//printf("wms: sig handler, n=%d..\n",n);
    cleanup();
    exit (0);
}
/*********************************************************************/
int 
main (argc, argv)
char **argv;
{
	int i;
	struct termios oldtio, newtio;
	char buff[BUFFSIZE];
	char modemdevice[16];
	char modeminit[64];
	int buff_wait;

	/* DATE=0704  TIME=1556  NAME=O  NMBR=O  MESG=06014C:= */
	char cid_date[8];
	char cid_time[8];
	char cid_name[16];
	char cid_num[16];

	int xterm_flag = 0;
	int quiet_flag = 0;
	int say_flag = 0;
	
	strcpy(modeminit,"AT#CID=1\r");
	strcpy(modemdevice,"/dev/ttyS1");
	
	if (argc)
	{
	    for (i=1; i<argc; i++)
	    {
		if (!strncmp(argv[i],"-device=",8))
		{
		    strcpy(modemdevice,"/dev/");
		    strcat(modemdevice,argv[i]+8);
		}
		if (!strncmp(argv[i],"-init=",6))
		{
		    strcpy(modeminit,argv[i]+6);
		    strcat(modeminit,"\r");
		}
		else if (!strncmp(argv[i],"-quiet",6))
		{
		    quiet_flag++;
		}
		else if (!strncmp(argv[i],"-say",4))
		{
		    say_flag++;
		}
		else if (!strncmp(argv[i],"-help",5))
		{
		    fprintf(stderr,"Use: %s [-quiet] [-say] [-device=xxxxx] [-help]\n",argv[0]);
		    fprintf(stderr,"where: quiet  - do not printf anything\n");
		    fprintf(stderr,"       say    - use 'say' program to read out the name\n");
		    fprintf(stderr,"       device - modem device: ttyS0... (default=ttyS1)\n");
		    fprintf(stderr,"       init   - modem CID init string (default=AT#CID=1)\n");
		    fprintf(stderr,"       help   - this message.\n");
		    return (0);
		}
		else
		{
		    fprintf(stderr,"Unknown command line option %s...\n",argv[i]);
		    return (1);
		}
	    }
	}
	
	if (!quiet_flag)
	{
	    fprintf(stderr,"CallerID V.0.12 (c)1999 W.Suwalski woody@suwalski.net\n");
	    fprintf(stderr,"Receiving data from %s\n",modemdevice);
	    fprintf(stderr,"Use: %s [-quiet] [-say] [-device=xxxxx] [-help]\n",argv[0]);
	}

	signal(SIGTERM, sighandler);
	signal(SIGABRT, sighandler);
	signal(SIGQUIT, sighandler);
	signal(SIGKILL, sighandler);
	signal(SIGSEGV, sighandler);

	i = open (LOCKFILE, O_WRONLY | O_CREAT | O_EXCL);
	/* if old lock file - get the PID, kill old PID, del lock, reopen */
	if (i < 0)
	{
	int oldpid, rc;
	char* pidstr;
	
//		fprintf(stderr, "Lock file %s: another instance running?\n",LOCKFILE);
		i = open (LOCKFILE, O_RDONLY);
	        if (i > 0)
		{
		    read(i, buff, BUFFSIZE);
		    pidstr = strchr(buff,'.');
		    if (pidstr > 0)
			*pidstr = 0;
//printf("wms: buff=%s.\n",buff);
		    pidstr = strchr(buff,'=');
//printf("wms: pidstr=%s.\n",pidstr+1);
		    rc = sscanf (pidstr+1, "%d", &oldpid);
//printf("wms: rc=%d, oldpid=%d.\n",rc, oldpid);
		    fprintf(stderr, "CID taking over from: %s\n", buff);

		    if (rc == 1)
		    {
			kill (oldpid, SIGTERM);
		    }
		    close(i);
		    unlink(LOCKFILE);
		}

		i = open (LOCKFILE, O_WRONLY | O_CREAT | O_EXCL);
	}
	sprintf (buff, "%s: pid = %d, device = %s.\n", 
	    argv[0], getpid(), modemdevice);
	write (i, buff, strlen(buff));
	close (i);

	/* open the device to be non-blocking (read will return immediatly) */
	modem_fd = open (modemdevice, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (modem_fd < 0)
	{
		fprintf(stderr,"Error openinng serial connection on %s.",modemdevice);
		cleanup();
		exit (1);
	}

	/* print text of incoming call msg if in interactive mode
	 * and inside an xterm. set xterm name as well...
	*/
	if (!quiet_flag && isatty(0))	/* if in interactive mode == foreground... */
	{
	const char *term_type;
    	    term_type = getenv("TERM");
	    if (!strcmp(term_type,"xterm"))
	    {
		fprintf(stderr, "\033]0;CallerID\007");
		xterm_flag = 1;	/* print text in the xterm window */
	    }
	}
	                                                                       
	log_fd = open (LOGFILE, O_WRONLY | O_CREAT);
	if (log_fd < 0)
	{
		perror ("Open log file");
		cleanup();
		exit (1);
	}
	i = lseek(log_fd,0,SEEK_END);	/* seek to the end == append */

	tcgetattr (modem_fd, &oldtio);	/* save current port settings */
	/* set new port settings for non-canonical input processing */
	memset (&newtio, 0, sizeof (newtio));

	newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
	newtio.c_iflag = IGNBRK | INPCK | ICRNL;
	newtio.c_oflag = 0;
	newtio.c_lflag = 0;
	newtio.c_cc[VMIN] = 1;		// wait for up to 32 chars
	newtio.c_cc[VTIME] = 1;		// * 100 ms

	tcflush (modem_fd, TCIOFLUSH);
	tcsetattr (modem_fd, TCSANOW, &newtio);
	buff_wait = 0;

//	write(modem_fd, "AT#CID=1\r", 9);
	write(modem_fd, modeminit, strlen(modeminit));

	while (1)
	{
		struct timeval to;
		int I_rc;
		int	maxfd;
		fd_set readfds;

		to.tv_sec = 0;
		to.tv_usec = 400000;
		FD_ZERO (&readfds);
		FD_SET (modem_fd, &readfds);
		maxfd = modem_fd + 1;

		if ((I_rc = select (maxfd, &readfds, 0, 0, &to)) < 0)
		{
		    if (errno == EINTR)
			continue; /* We were interrupted by a signal... continue james... */
		    perror ("select");
		    cleanup();
		    exit (1);
		}
		else if (!I_rc) //no data within to.tv_sec sec
		{
dump_data_now:
		    if(buff_wait)
		    {
		    char* p;
		    char* q;

//printf("cid11:buff_wait=%d.\n",buff_wait);			
			buff[buff_wait]=0;
			cid_date[0] = cid_time[0] = cid_name[0] = cid_num[0] = 0;

			/* search for string 'DATE=', 'NAME=' and 'MESG=' */
			for (i=0; i<buff_wait; i++)
			{
			    if (buff[i]=='D' && buff[i+1]=='A' &&
				buff[i+2]=='T' && buff[i+3]=='E' &&
				buff[i+4]=='=')
			    {
				p = &buff[i+5];
				q = &cid_date[0];
				while (*p >= ' ')
				    *q++=*p++;
				*q = 0;
			    }
			    else if (buff[i]=='T' && buff[i+1]=='I' &&
				buff[i+2]=='M' && buff[i+3]=='E' &&
				buff[i+4]=='=')
			    {
				p = &buff[i+5];
				q = &cid_time[0];
				while (*p >= ' ')
				    *q++=*p++;
				*q = 0;
			    }
			    else if (buff[i]=='N' && buff[i+1]=='A' &&
				buff[i+2]=='M' && buff[i+3]=='E' &&
				buff[i+4]=='=')
			    {
				p = &buff[i+5];
				q = &cid_name[0];
				while (*p >= ' ')
				    *q++=*p++;
				*q = 0;
			    }
			    else if (buff[i]=='N' && buff[i+1]=='M' &&
				buff[i+2]=='B' && buff[i+3]=='R' &&
				buff[i+4]=='=')
			    {
				p = &buff[i+5];
				q = &cid_num[0];
				while (*p >= ' ')
				    *q++=*p++;
				*q = 0;
			    }

			    /* handle the long distance calls... */
			    /* DATE=0704  TIME=1556  NAME=O  NMBR=O  MESG=06014C:= */
			    else if (buff[i]=='M' && buff[i+1]=='E' &&
				buff[i+2]=='S' && buff[i+3]=='G' &&
				buff[i+4]=='=')
			    {
				if (cid_name[0] == 'O')
				    strcpy(cid_name,"Out of Area");

				if (cid_num[0] == 'O')
				    strcpy(cid_num,"???");
			    }
			}


			if (cid_date[0] && cid_time[0])
			{
			    sprintf(buff,"DATE=%s  TIME=%s  NAME=%s  NMBR=%s\n",
				cid_date, cid_time, cid_name, cid_num);

			    if (xterm_flag)
				printf("%s",buff);

			    write(log_fd, buff, strlen(buff));
			    fsync(log_fd);	/* flush buff */

#ifdef DO_HTML
			    system("/etc/ppp/phoned.sh");
#endif
#ifdef DO_XDIALOG
			    if (cid_name[0])
			    {
				/* prepare the Xdialog app string... */
				sprintf(buff,
				"/usr/bin/Xdialog --title 'Caller ID' --no-button --infobox 'Name=%s #%s' 5 38 15000",
				cid_name, cid_num );
				//printf("xdialog: %s.\n", buff);
				system(buff);
			    }
#endif
#ifdef USE_SAY
			    if (cid_name[0] && say_flag)
			    {
				/* convert to lower case for the say app */
				p = &cid_name[0];
				while (*p)
				{
				    *p = tolower(*p);
				    p++;
				}

		    		/* prepare the say app string... */
				sprintf(buff, 
				    "/usr/local/bin/say '%s' > /dev/null",
				    cid_name);
				//printf("cid9: %s.\n", buff);
				system(buff);
			    }
#endif
			}
			buff_wait = 0;
		    }
		    continue;
		}
		else if (FD_ISSET (modem_fd, &readfds))
		{
			/* get COM data */
			//deb(printf("read(fd=%X,buff=%p,cnt=%d.\n",modem_fd,buff+buff_wait,BUFFSIZE-buff_wait));
			i = read (modem_fd, buff+buff_wait, BUFFSIZE-buff_wait);
			if (i < 0)
			{
				perror ("bad com data link");
				cleanup();
				exit (1);
			}
			//deb(printf("got(%d)=%s",i,buff));
			buff_wait += i;	//so much data waiting...
			if (buff_wait == BUFFSIZE)
			    goto dump_data_now;
			continue;
		}
		else
		{
			printf("select() assert! failure");
			cleanup();
			exit (1);
		}
	}
}

