chat.c revision 26880
14374Slars/*
24374Slars *	Chat -- a program for automatic session establishment (i.e. dial
34374Slars *		the phone and log in).
44374Slars *
511990Speter * Standard termination codes:
611990Speter *  0 - successful completion of the script
711990Speter *  1 - invalid argument, expect string too large, etc.
811990Speter *  2 - error on an I/O operation or fatal error condtion.
911990Speter *  3 - timeout waiting for a simple string.
1011990Speter *  4 - the first string declared as "ABORT"
1111990Speter *  5 - the second string declared as "ABORT"
1211990Speter *  6 - ... and so on for successive ABORT strings.
1311990Speter *
144374Slars *	This software is in the public domain.
154374Slars *
164374Slars *	Please send all bug reports, requests for information, etc. to:
174374Slars *
184374Slars *		Al Longyear (longyear@netcom.com)
194374Slars *		(I was the last person to change this code.)
204374Slars *
2111990Speter *      Added -r "report file" switch & REPORT keyword.
2211990Speter *              Robert Geer <bgeer@xmission.com>
2311990Speter *
244374Slars *	The original author is:
254374Slars *
264374Slars *		Karl Fox <karl@MorningStar.Com>
274374Slars *		Morning Star Technologies, Inc.
284374Slars *		1760 Zollinger Road
294374Slars *		Columbus, OH  43221
304374Slars *		(614)451-1883
3111990Speter *
324374Slars */
334374Slars
3426880Scharnierstatic char rcsid[] = "$Id: chat.c,v 1.7 1997/04/02 09:55:26 jmg Exp $";
354374Slars
364374Slars#include <stdio.h>
3711990Speter#include <time.h>
384374Slars#include <fcntl.h>
394374Slars#include <signal.h>
404374Slars#include <errno.h>
414374Slars#include <string.h>
424374Slars#include <stdlib.h>
434374Slars#include <sys/types.h>
444374Slars#include <sys/stat.h>
454374Slars#include <syslog.h>
464374Slars
474374Slars#ifndef TERMIO
484374Slars#undef	TERMIOS
494374Slars#define TERMIOS
504374Slars#endif
514374Slars
524374Slars#ifdef TERMIO
534374Slars#include <termio.h>
544374Slars#endif
554374Slars#ifdef TERMIOS
564374Slars#include <termios.h>
574374Slars#endif
584374Slars
594374Slars#define	STR_LEN	1024
604374Slars
614374Slars#ifndef SIGTYPE
624374Slars#define SIGTYPE void
634374Slars#endif
644374Slars
654374Slars#ifdef __STDC__
664374Slars#undef __P
674374Slars#define __P(x)	x
684374Slars#else
694374Slars#define __P(x)	()
704374Slars#define const
714374Slars#endif
724374Slars
7311990Speter#ifndef O_NONBLOCK
7411990Speter#define O_NONBLOCK	O_NDELAY
7511990Speter#endif
7611990Speter
774374Slars/*************** Micro getopt() *********************************************/
784374Slars#define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
794374Slars				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
804374Slars				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
814374Slars#define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
824374Slars				(_O=4,(char*)0):(char*)0)
834374Slars#define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
844374Slars#define	ARG(c,v)	(c?(--c,*v++):(char*)0)
854374Slars
864374Slarsstatic int _O = 0;		/* Internal state */
874374Slars/*************** Micro getopt() *********************************************/
884374Slars
894374Slars#define	MAX_ABORTS		50
9011990Speter#define	MAX_REPORTS		50
914374Slars#define	DEFAULT_CHAT_TIMEOUT	45
924374Slars
9311990Speterint verbose       = 0;
9411990Speterint quiet         = 0;
9511990Speterint report        = 0;
9611990Speterint exit_code     = 0;
9711990SpeterFILE* report_fp   = (FILE *) 0;
9811990Speterchar *report_file = (char *) 0;
9911990Speterchar *chat_file   = (char *) 0;
10011990Speterint timeout       = DEFAULT_CHAT_TIMEOUT;
1014374Slars
1024374Slarsint have_tty_parameters = 0;
10311990Speter
1044374Slars#ifdef TERMIO
10511990Speter#define term_parms struct termio
10611990Speter#define get_term_param(param) ioctl(0, TCGETA, param)
10711990Speter#define set_term_param(param) ioctl(0, TCSETA, param)
1084374Slarsstruct termio saved_tty_parameters;
1094374Slars#endif
11011990Speter
1114374Slars#ifdef TERMIOS
11211990Speter#define term_parms struct termios
11311990Speter#define get_term_param(param) tcgetattr(0, param)
11411990Speter#define set_term_param(param) tcsetattr(0, TCSANOW, param)
1154374Slarsstruct termios saved_tty_parameters;
1164374Slars#endif
1174374Slars
1184374Slarschar *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1194374Slars	fail_buffer[50];
1204374Slarsint n_aborts = 0, abort_next = 0, timeout_next = 0;
1214374Slars
12211990Speterchar *report_string[MAX_REPORTS] ;
12311990Speterchar  report_buffer[50] ;
12411990Speterint n_reports = 0, report_next = 0, report_gathering = 0 ;
12511990Speter
1264374Slarsvoid *dup_mem __P((void *b, size_t c));
1274374Slarsvoid *copy_of __P((char *s));
12826880Scharnierstatic void usage __P((void));
1294374Slarsvoid logf __P((const char *str));
1304374Slarsvoid logflush __P((void));
1314374Slarsvoid fatal __P((const char *msg));
1324374Slarsvoid sysfatal __P((const char *msg));
1334374SlarsSIGTYPE sigalrm __P((int signo));
1344374SlarsSIGTYPE sigint __P((int signo));
1354374SlarsSIGTYPE sigterm __P((int signo));
1364374SlarsSIGTYPE sighup __P((int signo));
1374374Slarsvoid unalarm __P((void));
1384374Slarsvoid init __P((void));
1394374Slarsvoid set_tty_parameters __P((void));
1404374Slarsvoid break_sequence __P((void));
1414374Slarsvoid terminate __P((int status));
1424374Slarsvoid do_file __P((char *chat_file));
1434374Slarsint  get_string __P((register char *string));
1444374Slarsint  put_string __P((register char *s));
1454374Slarsint  write_char __P((int c));
14611990Speterint  put_char __P((int c));
1474374Slarsint  get_char __P((void));
1484374Slarsvoid chat_send __P((register char *s));
14911990Speterchar *character __P((int c));
1504374Slarsvoid chat_expect __P((register char *s));
1514374Slarschar *clean __P((register char *s, int sending));
1524374Slarsvoid break_sequence __P((void));
1534374Slarsvoid terminate __P((int status));
1544374Slarsvoid die __P((void));
1554374Slars
1564374Slarsvoid *dup_mem(b, c)
1574374Slarsvoid *b;
1584374Slarssize_t c;
1594374Slars    {
1604374Slars    void *ans = malloc (c);
1614374Slars    if (!ans)
16211990Speter        {
1634374Slars	fatal ("memory error!\n");
16411990Speter        }
1654374Slars    memcpy (ans, b, c);
1664374Slars    return ans;
1674374Slars    }
1684374Slars
1694374Slarsvoid *copy_of (s)
1704374Slarschar *s;
1714374Slars    {
1724374Slars    return dup_mem (s, strlen (s) + 1);
1734374Slars    }
1744374Slars
1754374Slars/*
17611990Speter *	chat [ -v ] [ -t timeout ] [ -f chat-file ] [ -r report-file ] \
1774374Slars *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
1784374Slars *
1794374Slars *	Perform a UUCP-dialer-like chat script on stdin and stdout.
1804374Slars */
1814374Slarsint
1824374Slarsmain(argc, argv)
1834374Slarsint argc;
1844374Slarschar **argv;
1854374Slars    {
1864374Slars    int option;
1874374Slars    char *arg;
1884374Slars
18911990Speter    tzset();
1904374Slars
1914374Slars    while (option = OPTION(argc, argv))
19211990Speter        {
1934374Slars	switch (option)
1944374Slars	    {
1954374Slars	    case 'v':
1964374Slars		++verbose;
1974374Slars		break;
1984374Slars
1994374Slars	    case 'f':
2004374Slars		if (arg = OPTARG(argc, argv))
20111990Speter		    {
2024374Slars		    chat_file = copy_of(arg);
20311990Speter		    }
2044374Slars		else
20511990Speter		    {
2064374Slars		    usage();
20711990Speter		    }
2084374Slars		break;
2094374Slars
2104374Slars	    case 't':
2114374Slars		if (arg = OPTARG(argc, argv))
21211990Speter		    {
2134374Slars		    timeout = atoi(arg);
21411990Speter		    }
2154374Slars		else
21611990Speter		    {
2174374Slars		    usage();
21811990Speter		    }
21911990Speter		break;
2204374Slars
22111990Speter	    case 'r':
22211990Speter		arg = OPTARG (argc, argv);
22311990Speter		if (arg)
22411990Speter		    {
22511990Speter		    if (report_fp != NULL)
22611990Speter		        {
22711990Speter			fclose (report_fp);
22811990Speter		        }
22911990Speter		    report_file = copy_of (arg);
23011990Speter		    report_fp   = fopen (report_file, "a");
23111990Speter		    if (report_fp != NULL)
23211990Speter		        {
23311990Speter			if (verbose)
23411990Speter			    {
23511990Speter			    fprintf (report_fp, "Opening \"%s\"...\n",
23611990Speter				     report_file);
23711990Speter			    }
23811990Speter			report = 1;
23911990Speter		        }
24011990Speter		    }
2414374Slars		break;
2424374Slars
2434374Slars	    default:
2444374Slars		usage();
24511990Speter		break;
2464374Slars	    }
24711990Speter      }
24811990Speter/*
24911990Speter * Default the report file to the stderr location
25011990Speter */
25111990Speter    if (report_fp == NULL)
25211990Speter        {
25311990Speter	report_fp = stderr;
25411990Speter        }
2554374Slars
2564374Slars#ifdef ultrix
2574374Slars    openlog("chat", LOG_PID);
2584374Slars#else
2594374Slars    openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
2604374Slars
26111990Speter    if (verbose)
26211990Speter        {
2634374Slars	setlogmask(LOG_UPTO(LOG_INFO));
26411990Speter        }
26511990Speter    else
26611990Speter        {
2674374Slars	setlogmask(LOG_UPTO(LOG_WARNING));
26811990Speter        }
2694374Slars#endif
2704374Slars
2714374Slars    init();
27211990Speter
2734374Slars    if (chat_file != NULL)
2744374Slars	{
2754374Slars	arg = ARG(argc, argv);
2764374Slars	if (arg != NULL)
27711990Speter	    {
2784374Slars	    usage();
27911990Speter	    }
2804374Slars	else
28111990Speter	    {
2824374Slars	    do_file (chat_file);
28311990Speter	    }
2844374Slars	}
2854374Slars    else
2864374Slars	{
2874374Slars	while (arg = ARG(argc, argv))
2884374Slars	    {
2894374Slars	    chat_expect(arg);
2904374Slars
2914374Slars	    if (arg = ARG(argc, argv))
29211990Speter	        {
2934374Slars		chat_send(arg);
29411990Speter	        }
2954374Slars	    }
2964374Slars	}
2974374Slars
2984374Slars    terminate(0);
2994374Slars    }
3004374Slars
3014374Slars/*
3024374Slars *  Process a chat script when read from a file.
3034374Slars */
3044374Slars
3054374Slarsvoid do_file (chat_file)
3064374Slarschar *chat_file;
3074374Slars    {
3084374Slars    int linect, len, sendflg;
3094374Slars    char *sp, *arg, quote;
3104374Slars    char buf [STR_LEN];
3114374Slars    FILE *cfp;
3124374Slars
31311990Speter    cfp = fopen (chat_file, "r");
31411990Speter    if (cfp == NULL)
3154374Slars	{
3164374Slars	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
3174374Slars	terminate (1);
3184374Slars	}
3194374Slars
3204374Slars    linect = 0;
3214374Slars    sendflg = 0;
3224374Slars
3234374Slars    while (fgets(buf, STR_LEN, cfp) != NULL)
3244374Slars	{
3254374Slars	sp = strchr (buf, '\n');
3264374Slars	if (sp)
32711990Speter	    {
3284374Slars	    *sp = '\0';
32911990Speter	    }
3304374Slars
3314374Slars	linect++;
3324374Slars	sp = buf;
3334374Slars	while (*sp != '\0')
3344374Slars	    {
3354374Slars	    if (*sp == ' ' || *sp == '\t')
3364374Slars		{
3374374Slars		++sp;
3384374Slars		continue;
3394374Slars		}
3404374Slars
3414374Slars	    if (*sp == '"' || *sp == '\'')
3424374Slars		{
3434374Slars		quote = *sp++;
3444374Slars		arg = sp;
3454374Slars		while (*sp != quote)
3464374Slars		    {
3474374Slars		    if (*sp == '\0')
3484374Slars			{
3494374Slars			syslog (LOG_ERR, "unterminated quote (line %d)",
3504374Slars				linect);
3514374Slars			terminate (1);
3524374Slars			}
35311990Speter
3544374Slars		    if (*sp++ == '\\')
35511990Speter		        {
3564374Slars			if (*sp != '\0')
35711990Speter			    {
3584374Slars			    ++sp;
35911990Speter			    }
36011990Speter		        }
3614374Slars		    }
3624374Slars		}
3634374Slars	    else
3644374Slars		{
3654374Slars		arg = sp;
3664374Slars		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
36711990Speter		    {
3684374Slars		    ++sp;
36911990Speter		    }
3704374Slars		}
3714374Slars
3724374Slars	    if (*sp != '\0')
37311990Speter	        {
3744374Slars		*sp++ = '\0';
37511990Speter	        }
3764374Slars
3774374Slars	    if (sendflg)
3784374Slars		{
3794374Slars		chat_send (arg);
3804374Slars		}
3814374Slars	    else
3824374Slars		{
3834374Slars		chat_expect (arg);
3844374Slars		}
3854374Slars	    sendflg = !sendflg;
3864374Slars	    }
3874374Slars	}
3884374Slars    fclose (cfp);
3894374Slars    }
3904374Slars
3914374Slars/*
3924374Slars *	We got an error parsing the command line.
3934374Slars */
39426880Scharnierstatic void
39526880Scharnierusage()
3964374Slars    {
39726880Scharnier    fprintf(stderr, "%s %s\n",
39826880Scharnier		"usage: chat [-v] [-t timeout] [-r report-file]",
39926880Scharnier		"{-f chat-file | chat-script}");
4004374Slars    exit(1);
4014374Slars    }
4024374Slars
4034374Slarschar line[256];
4044374Slarschar *p;
4054374Slars
4064374Slarsvoid logf (str)
4074374Slarsconst char *str;
4084374Slars    {
4094374Slars    p = line + strlen(line);
4104374Slars    strcat (p, str);
4114374Slars
4124374Slars    if (str[strlen(str)-1] == '\n')
4134374Slars	{
4144374Slars	syslog (LOG_INFO, "%s", line);
4154374Slars	line[0] = 0;
4164374Slars	}
4174374Slars    }
4184374Slars
4194374Slarsvoid logflush()
4204374Slars    {
4214374Slars    if (line[0] != 0)
4224374Slars	{
4234374Slars	syslog(LOG_INFO, "%s", line);
4244374Slars	line[0] = 0;
4254374Slars        }
4264374Slars    }
4274374Slars
4284374Slars/*
42911990Speter *	Terminate with an error.
4304374Slars */
4314374Slarsvoid die()
4324374Slars    {
4334374Slars    terminate(1);
4344374Slars    }
4354374Slars
4364374Slars/*
4374374Slars *	Print an error message and terminate.
4384374Slars */
4394374Slars
4404374Slarsvoid fatal (msg)
4414374Slarsconst char *msg;
4424374Slars    {
4434374Slars    syslog(LOG_ERR, "%s", msg);
44411990Speter    terminate(2);
4454374Slars    }
4464374Slars
4474374Slars/*
4484374Slars *	Print an error message along with the system error message and
4494374Slars *	terminate.
4504374Slars */
4514374Slars
4524374Slarsvoid sysfatal (msg)
4534374Slarsconst char *msg;
4544374Slars    {
4554374Slars    syslog(LOG_ERR, "%s: %m", msg);
45611990Speter    terminate(2);
4574374Slars    }
4584374Slars
4594374Slarsint alarmed = 0;
4604374Slars
4614374SlarsSIGTYPE sigalrm(signo)
4624374Slarsint signo;
4634374Slars    {
4644374Slars    int flags;
4654374Slars
4664374Slars    alarm(1);
4674374Slars    alarmed = 1;		/* Reset alarm to avoid race window */
4684374Slars    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
4694374Slars
4704374Slars    logflush();
4714374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
47211990Speter        {
4734374Slars	sysfatal("Can't get file mode flags on stdin");
47411990Speter        }
4754374Slars    else
47611990Speter        {
47711990Speter	if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
47811990Speter	    {
4794374Slars	    sysfatal("Can't set file mode flags on stdin");
48011990Speter	    }
48111990Speter        }
4824374Slars
4834374Slars    if (verbose)
4844374Slars	{
4854374Slars	syslog(LOG_INFO, "alarm");
4864374Slars	}
4874374Slars    }
4884374Slars
4894374Slarsvoid unalarm()
4904374Slars    {
4914374Slars    int flags;
4924374Slars
4934374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
49411990Speter        {
4954374Slars	sysfatal("Can't get file mode flags on stdin");
49611990Speter        }
4974374Slars    else
49811990Speter        {
49911990Speter	if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
50011990Speter	    {
5014374Slars	    sysfatal("Can't set file mode flags on stdin");
50211990Speter	    }
50311990Speter        }
5044374Slars    }
5054374Slars
5064374SlarsSIGTYPE sigint(signo)
5074374Slarsint signo;
5084374Slars    {
5094374Slars    fatal("SIGINT");
5104374Slars    }
5114374Slars
5124374SlarsSIGTYPE sigterm(signo)
5134374Slarsint signo;
5144374Slars    {
5154374Slars    fatal("SIGTERM");
5164374Slars    }
5174374Slars
5184374SlarsSIGTYPE sighup(signo)
5194374Slarsint signo;
5204374Slars    {
5214374Slars    fatal("SIGHUP");
5224374Slars    }
5234374Slars
5244374Slarsvoid init()
5254374Slars    {
5264374Slars    signal(SIGINT, sigint);
5274374Slars    signal(SIGTERM, sigterm);
5284374Slars    signal(SIGHUP, sighup);
5294374Slars
5304374Slars    set_tty_parameters();
5314374Slars    signal(SIGALRM, sigalrm);
5324374Slars    alarm(0);
5334374Slars    alarmed = 0;
5344374Slars    }
5354374Slars
5364374Slarsvoid set_tty_parameters()
5374374Slars    {
53811990Speter#if defined(get_term_param)
53911990Speter    term_parms t;
5404374Slars
54111990Speter    if (get_term_param (&t) < 0)
54211990Speter        {
54324541Sjmg	have_tty_parameters = 0;
54424541Sjmg	return;
54511990Speter        }
5464374Slars
5474374Slars    saved_tty_parameters = t;
54811990Speter    have_tty_parameters  = 1;
5494374Slars
55011990Speter    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
55111990Speter    t.c_oflag      = 0;
55211990Speter    t.c_lflag      = 0;
55311990Speter    t.c_cc[VERASE] =
55411990Speter    t.c_cc[VKILL]  = 0;
55511990Speter    t.c_cc[VMIN]   = 1;
55611990Speter    t.c_cc[VTIME]  = 0;
5574374Slars
55811990Speter    if (set_term_param (&t) < 0)
55911990Speter        {
5604374Slars	sysfatal("Can't set terminal parameters");
56111990Speter        }
5624374Slars#endif
5634374Slars    }
5644374Slars
5654374Slarsvoid break_sequence()
5664374Slars    {
5674374Slars#ifdef TERMIOS
5684374Slars    tcsendbreak (0, 0);
5694374Slars#endif
5704374Slars    }
5714374Slars
5724374Slarsvoid terminate(status)
5734374Slarsint status;
5744374Slars    {
57511990Speter    if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
57611990Speter        {
57711990Speter	if (verbose)
57811990Speter	    {
57911990Speter	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
58011990Speter	    }
58111990Speter	fclose (report_fp);
58211990Speter	report_fp = (FILE*) NULL;
5834374Slars        }
5844374Slars
58511990Speter#if defined(get_term_param)
58611990Speter    if (have_tty_parameters)
58711990Speter        {
58811990Speter	if (set_term_param (&saved_tty_parameters) < 0)
58911990Speter	    {
59011990Speter	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
59111990Speter	    exit(1);
59211990Speter	    }
59311990Speter        }
59411990Speter#endif
5954374Slars
59611990Speter    exit(status);
5974374Slars    }
5984374Slars
5994374Slars/*
6004374Slars *	'Clean up' this string.
6014374Slars */
6024374Slarschar *clean(s, sending)
6034374Slarsregister char *s;
6044374Slarsint sending;
6054374Slars    {
6064374Slars    char temp[STR_LEN], cur_chr;
6074374Slars    register char *s1;
6084374Slars    int add_return = sending;
6094374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
6104374Slars
6114374Slars    s1 = temp;
6124374Slars    while (*s)
6134374Slars	{
6144374Slars	cur_chr = *s++;
6154374Slars	if (cur_chr == '^')
6164374Slars	    {
6174374Slars	    cur_chr = *s++;
6184374Slars	    if (cur_chr == '\0')
6194374Slars		{
6204374Slars		*s1++ = '^';
6214374Slars		break;
6224374Slars		}
6234374Slars	    cur_chr &= 0x1F;
6244374Slars	    if (cur_chr != 0)
62511990Speter	        {
6264374Slars		*s1++ = cur_chr;
62711990Speter	        }
6284374Slars	    continue;
6294374Slars	    }
6304374Slars
6314374Slars	if (cur_chr != '\\')
6324374Slars	    {
6334374Slars	    *s1++ = cur_chr;
6344374Slars	    continue;
6354374Slars	    }
6364374Slars
6374374Slars	cur_chr = *s++;
6384374Slars	if (cur_chr == '\0')
6394374Slars	    {
6404374Slars	    if (sending)
6414374Slars		{
6424374Slars		*s1++ = '\\';
6434374Slars		*s1++ = '\\';
6444374Slars		}
6454374Slars	    break;
6464374Slars	    }
6474374Slars
6484374Slars	switch (cur_chr)
6494374Slars	    {
6504374Slars	case 'b':
6514374Slars	    *s1++ = '\b';
6524374Slars	    break;
6534374Slars
6544374Slars	case 'c':
6554374Slars	    if (sending && *s == '\0')
65611990Speter	        {
6574374Slars		add_return = 0;
65811990Speter	        }
6594374Slars	    else
66011990Speter	        {
6614374Slars		*s1++ = cur_chr;
66211990Speter	        }
6634374Slars	    break;
6644374Slars
6654374Slars	case '\\':
6664374Slars	case 'K':
6674374Slars	case 'p':
6684374Slars	case 'd':
6694374Slars	    if (sending)
67011990Speter	        {
6714374Slars		*s1++ = '\\';
67211990Speter	        }
6734374Slars
6744374Slars	    *s1++ = cur_chr;
6754374Slars	    break;
6764374Slars
6774374Slars	case 'q':
6784374Slars	    quiet = ! quiet;
6794374Slars	    break;
6804374Slars
6814374Slars	case 'r':
6824374Slars	    *s1++ = '\r';
6834374Slars	    break;
6844374Slars
6854374Slars	case 'n':
6864374Slars	    *s1++ = '\n';
6874374Slars	    break;
6884374Slars
6894374Slars	case 's':
6904374Slars	    *s1++ = ' ';
6914374Slars	    break;
6924374Slars
6934374Slars	case 't':
6944374Slars	    *s1++ = '\t';
6954374Slars	    break;
6964374Slars
6974374Slars	case 'N':
6984374Slars	    if (sending)
6994374Slars		{
7004374Slars		*s1++ = '\\';
7014374Slars		*s1++ = '\0';
7024374Slars		}
7034374Slars	    else
70411990Speter	        {
7054374Slars		*s1++ = 'N';
70611990Speter	        }
7074374Slars	    break;
70811990Speter
7094374Slars	default:
7104374Slars	    if (isoctal (cur_chr))
7114374Slars		{
7124374Slars		cur_chr &= 0x07;
7134374Slars		if (isoctal (*s))
7144374Slars		    {
7154374Slars		    cur_chr <<= 3;
7164374Slars		    cur_chr |= *s++ - '0';
7174374Slars		    if (isoctal (*s))
7184374Slars			{
7194374Slars			cur_chr <<= 3;
7204374Slars			cur_chr |= *s++ - '0';
7214374Slars			}
7224374Slars		    }
7234374Slars
7244374Slars		if (cur_chr != 0 || sending)
7254374Slars		    {
7264374Slars		    if (sending && (cur_chr == '\\' || cur_chr == 0))
72711990Speter		        {
7284374Slars			*s1++ = '\\';
72911990Speter		        }
7304374Slars		    *s1++ = cur_chr;
7314374Slars		    }
7324374Slars		break;
7334374Slars		}
7344374Slars
7354374Slars	    if (sending)
73611990Speter	        {
7374374Slars		*s1++ = '\\';
73811990Speter	        }
7394374Slars	    *s1++ = cur_chr;
7404374Slars	    break;
7414374Slars	    }
7424374Slars	}
7434374Slars
7444374Slars    if (add_return)
74511990Speter        {
7464374Slars	*s1++ = '\r';
74711990Speter        }
7484374Slars
7494374Slars    *s1++ = '\0'; /* guarantee closure */
7504374Slars    *s1++ = '\0'; /* terminate the string */
7514374Slars    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7524374Slars    }
7534374Slars
7544374Slars/*
7554374Slars * Process the expect string
7564374Slars */
7574374Slarsvoid chat_expect(s)
7584374Slarsregister char *s;
7594374Slars    {
7604374Slars    if (strcmp(s, "ABORT") == 0)
7614374Slars	{
7624374Slars	++abort_next;
7634374Slars	return;
7644374Slars	}
7654374Slars
76611990Speter    if (strcmp(s, "REPORT") == 0)
76711990Speter	{
76811990Speter	++report_next;
76911990Speter	return;
77011990Speter	}
77111990Speter
7724374Slars    if (strcmp(s, "TIMEOUT") == 0)
7734374Slars	{
7744374Slars	++timeout_next;
7754374Slars	return;
7764374Slars	}
7774374Slars
7784374Slars    while (*s)
7794374Slars	{
7804374Slars	register char *hyphen;
7814374Slars
7824374Slars	for (hyphen = s; *hyphen; ++hyphen)
78311990Speter	    {
7844374Slars	    if (*hyphen == '-')
78511990Speter	        {
7864374Slars		if (hyphen == s || hyphen[-1] != '\\')
78711990Speter		    {
7884374Slars		    break;
78911990Speter		    }
79011990Speter	        }
79111990Speter	    }
79211990Speter
7934374Slars	if (*hyphen == '-')
7944374Slars	    {
7954374Slars	    *hyphen = '\0';
7964374Slars
7974374Slars	    if (get_string(s))
79811990Speter	        {
7994374Slars		return;
80011990Speter	        }
8014374Slars	    else
8024374Slars		{
8034374Slars		s = hyphen + 1;
8044374Slars
8054374Slars		for (hyphen = s; *hyphen; ++hyphen)
80611990Speter		    {
8074374Slars		    if (*hyphen == '-')
80811990Speter		        {
8094374Slars			if (hyphen == s || hyphen[-1] != '\\')
81011990Speter			    {
8114374Slars			    break;
81211990Speter			    }
81311990Speter		        }
81411990Speter		    }
8154374Slars
8164374Slars		if (*hyphen == '-')
8174374Slars		    {
8184374Slars		    *hyphen = '\0';
8194374Slars
8204374Slars		    chat_send(s);
8214374Slars		    s = hyphen + 1;
8224374Slars		    }
8234374Slars		else
8244374Slars		    {
8254374Slars		    chat_send(s);
8264374Slars		    return;
8274374Slars		    }
8284374Slars		}
8294374Slars	    }
8304374Slars	else
83111990Speter	    {
8324374Slars	    if (get_string(s))
83311990Speter	        {
8344374Slars		return;
83511990Speter	        }
8364374Slars	    else
8374374Slars		{
8384374Slars		if (fail_reason)
83911990Speter		    {
8404374Slars		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
84111990Speter		    }
8424374Slars		else
84311990Speter		    {
8444374Slars		    syslog(LOG_INFO, "Failed");
84511990Speter		    }
8464374Slars
84711990Speter		terminate(exit_code);
8484374Slars		}
84911990Speter	    }
8504374Slars	}
8514374Slars    }
8524374Slars
8534374Slarschar *character(c)
85411990Speterint c;
8554374Slars    {
8564374Slars    static char string[10];
8574374Slars    char *meta;
8584374Slars
8594374Slars    meta = (c & 0x80) ? "M-" : "";
8604374Slars    c &= 0x7F;
8614374Slars
8624374Slars    if (c < 32)
86311990Speter        {
8644374Slars	sprintf(string, "%s^%c", meta, (int)c + '@');
86511990Speter        }
8664374Slars    else
86711990Speter        {
8684374Slars	if (c == 127)
86911990Speter	    {
8704374Slars	    sprintf(string, "%s^?", meta);
87111990Speter	    }
8724374Slars	else
87311990Speter	    {
8744374Slars	    sprintf(string, "%s%c", meta, c);
87511990Speter	    }
87611990Speter        }
8774374Slars
8784374Slars    return (string);
8794374Slars    }
8804374Slars
8814374Slars/*
8824374Slars *  process the reply string
8834374Slars */
8844374Slarsvoid chat_send (s)
8854374Slarsregister char *s;
8864374Slars    {
8874374Slars    if (abort_next)
88811990Speter        {
8894374Slars	char *s1;
89011990Speter
8914374Slars	abort_next = 0;
89211990Speter
8934374Slars	if (n_aborts >= MAX_ABORTS)
89411990Speter	    {
8954374Slars	    fatal("Too many ABORT strings");
89611990Speter	    }
89711990Speter
8984374Slars	s1 = clean(s, 0);
89911990Speter
90011990Speter	if (strlen(s1) > strlen(s)
90111990Speter	    || strlen(s1) + 1 > sizeof(fail_buffer))
9024374Slars	    {
9034374Slars	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
9044374Slars	    die();
9054374Slars	    }
9064374Slars
9074374Slars	abort_string[n_aborts++] = s1;
9084374Slars
9094374Slars	if (verbose)
9104374Slars	    {
9114374Slars	    logf("abort on (");
9124374Slars
9134374Slars	    for (s1 = s; *s1; ++s1)
91411990Speter	        {
9154374Slars		logf(character(*s1));
91611990Speter	        }
9174374Slars
9184374Slars	    logf(")\n");
9194374Slars	    }
92011990Speter	return;
9214374Slars	}
92211990Speter
92311990Speter    if (report_next)
92411990Speter        {
92511990Speter	char *s1;
92611990Speter
92711990Speter	report_next = 0;
92811990Speter	if (n_reports >= MAX_REPORTS)
9294374Slars	    {
93011990Speter	    fatal("Too many REPORT strings");
93111990Speter	    }
93211990Speter
93311990Speter	s1 = clean(s, 0);
93411990Speter
93511990Speter	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
93611990Speter	    {
93711990Speter	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
93811990Speter	    die();
93911990Speter	    }
94011990Speter
94111990Speter	report_string[n_reports++] = s1;
94211990Speter
94311990Speter	if (verbose)
94411990Speter	    {
94511990Speter	    logf("report (");
94611990Speter	    s1 = s;
94711990Speter	    while (*s1)
94811990Speter	        {
94911990Speter		logf(character(*s1));
95011990Speter		++s1;
95111990Speter	        }
95211990Speter	    logf(")\n");
95311990Speter	    }
95411990Speter	return;
95511990Speter        }
9564374Slars
95711990Speter    if (timeout_next)
95811990Speter        {
95911990Speter	timeout_next = 0;
96011990Speter	timeout = atoi(s);
96111990Speter
96211990Speter	if (timeout <= 0)
96311990Speter	    {
96411990Speter	    timeout = DEFAULT_CHAT_TIMEOUT;
96511990Speter	    }
9664374Slars
96711990Speter	if (verbose)
96811990Speter	    {
96911990Speter	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
9704374Slars	    }
97111990Speter	return;
97211990Speter        }
97311990Speter
97411990Speter    if (strcmp(s, "EOT") == 0)
97511990Speter        {
97611990Speter	s = "^D\\c";
97711990Speter        }
97811990Speter    else
97911990Speter        {
98011990Speter	if (strcmp(s, "BREAK") == 0)
9814374Slars	    {
98211990Speter	    s = "\\K\\c";
9834374Slars	    }
98411990Speter        }
98511990Speter
98611990Speter    if (!put_string(s))
98711990Speter        {
98811990Speter	syslog(LOG_INFO, "Failed");
98911990Speter	terminate(1);
99011990Speter        }
9914374Slars    }
9924374Slars
9934374Slarsint get_char()
9944374Slars    {
9954374Slars    int status;
9964374Slars    char c;
9974374Slars
9984374Slars    status = read(0, &c, 1);
9994374Slars
10004374Slars    switch (status)
100111990Speter        {
100211990Speter    case 1:
100311990Speter	return ((int)c & 0x7F);
10044374Slars
100511990Speter    default:
100611990Speter	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
100711990Speter	       status);
10084374Slars
100911990Speter    case -1:
101011990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
101111990Speter	    {
101211990Speter	    sysfatal("Can't get file mode flags on stdin");
101311990Speter	    }
101411990Speter	else
101511990Speter	    {
101611990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
101711990Speter	        {
101811990Speter		sysfatal("Can't set file mode flags on stdin");
101911990Speter	        }
102011990Speter	    }
102111990Speter
102211990Speter	return (-1);
102311990Speter        }
10244374Slars    }
10254374Slars
10264374Slarsint put_char(c)
102711990Speterint c;
10284374Slars    {
10294374Slars    int status;
103011990Speter    char ch = c;
10314374Slars
103211990Speter    usleep(10000);		/* inter-character typing delay (?) */
10334374Slars
103411990Speter    status = write(1, &ch, 1);
10354374Slars
10364374Slars    switch (status)
103711990Speter        {
103811990Speter    case 1:
103911990Speter	return (0);
104011990Speter
104111990Speter    default:
104211990Speter	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
104311990Speter	       status);
104411990Speter
104511990Speter    case -1:
104611990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
104711990Speter	    {
104811990Speter	    sysfatal("Can't get file mode flags on stdin");
104911990Speter	    }
105011990Speter	else
105111990Speter	    {
105211990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
105311990Speter	        {
105411990Speter		sysfatal("Can't set file mode flags on stdin");
105511990Speter	        }
105611990Speter	    }
105711990Speter
105811990Speter	return (-1);
105911990Speter        }
10604374Slars    }
10614374Slars
10624374Slarsint write_char (c)
10634374Slarsint c;
10644374Slars    {
10654374Slars    if (alarmed || put_char(c) < 0)
10664374Slars	{
10674374Slars	extern int errno;
10684374Slars
106911990Speter	alarm(0);
107011990Speter	alarmed = 0;
10714374Slars
10724374Slars	if (verbose)
10734374Slars	    {
10744374Slars	    if (errno == EINTR || errno == EWOULDBLOCK)
107511990Speter	        {
10764374Slars		syslog(LOG_INFO, " -- write timed out");
107711990Speter	        }
10784374Slars	    else
107911990Speter	        {
10804374Slars		syslog(LOG_INFO, " -- write failed: %m");
108111990Speter	        }
10824374Slars	    }
10834374Slars	return (0);
10844374Slars	}
10854374Slars    return (1);
10864374Slars    }
10874374Slars
10884374Slarsint put_string (s)
10894374Slarsregister char *s;
10904374Slars    {
10914374Slars    s = clean(s, 1);
10924374Slars
10934374Slars    if (verbose)
10944374Slars	{
10954374Slars	logf("send (");
10964374Slars
10974374Slars	if (quiet)
109811990Speter	    {
10994374Slars	    logf("??????");
110011990Speter	    }
11014374Slars	else
11024374Slars	    {
11034374Slars	    register char *s1 = s;
11044374Slars
11054374Slars	    for (s1 = s; *s1; ++s1)
110611990Speter	        {
11074374Slars		logf(character(*s1));
110811990Speter	        }
11094374Slars	    }
11104374Slars
11114374Slars	logf(")\n");
11124374Slars	}
11134374Slars
11144374Slars    alarm(timeout); alarmed = 0;
11154374Slars
11164374Slars    while (*s)
11174374Slars	{
11184374Slars	register char c = *s++;
11194374Slars
11204374Slars	if (c != '\\')
11214374Slars	    {
11224374Slars	    if (!write_char (c))
112311990Speter	        {
11244374Slars		return 0;
112511990Speter	        }
11264374Slars	    continue;
11274374Slars	    }
11284374Slars
11294374Slars	c = *s++;
11304374Slars	switch (c)
11314374Slars	    {
11324374Slars	case 'd':
11334374Slars	    sleep(1);
11344374Slars	    break;
11354374Slars
11364374Slars	case 'K':
11374374Slars	    break_sequence();
11384374Slars	    break;
11394374Slars
11404374Slars	case 'p':
114111990Speter	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
11424374Slars	    break;
11434374Slars
11444374Slars	default:
11454374Slars	    if (!write_char (c))
11464374Slars		return 0;
11474374Slars	    break;
11484374Slars	    }
11494374Slars	}
11504374Slars
11514374Slars    alarm(0);
11524374Slars    alarmed = 0;
11534374Slars    return (1);
11544374Slars    }
11554374Slars
11564374Slars/*
11574374Slars *	'Wait for' this string to appear on this file descriptor.
11584374Slars */
11594374Slarsint get_string(string)
11604374Slarsregister char *string;
11614374Slars    {
11624374Slars    char temp[STR_LEN];
11634374Slars    int c, printed = 0, len, minlen;
11644374Slars    register char *s = temp, *end = s + STR_LEN;
11654374Slars
11664374Slars    fail_reason = (char *)0;
11674374Slars    string = clean(string, 0);
11684374Slars    len = strlen(string);
11694374Slars    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
11704374Slars
11714374Slars    if (verbose)
11724374Slars	{
11734374Slars	register char *s1;
11744374Slars
11754374Slars	logf("expect (");
11764374Slars
11774374Slars	for (s1 = string; *s1; ++s1)
117811990Speter	    {
11794374Slars	    logf(character(*s1));
118011990Speter	    }
11814374Slars
11824374Slars	logf(")\n");
11834374Slars	}
11844374Slars
11854374Slars    if (len > STR_LEN)
11864374Slars	{
11874374Slars	syslog(LOG_INFO, "expect string is too long");
118811990Speter	exit_code = 1;
11894374Slars	return 0;
11904374Slars	}
11914374Slars
11924374Slars    if (len == 0)
11934374Slars	{
11944374Slars	if (verbose)
11954374Slars	    {
11964374Slars	    syslog(LOG_INFO, "got it");
11974374Slars	    }
11984374Slars
11994374Slars	return (1);
12004374Slars	}
12014374Slars
120211990Speter    alarm(timeout);
120311990Speter    alarmed = 0;
12044374Slars
12054374Slars    while ( ! alarmed && (c = get_char()) >= 0)
12064374Slars	{
120711990Speter	int n, abort_len, report_len;
12084374Slars
12094374Slars	if (verbose)
12104374Slars	    {
12114374Slars	    if (c == '\n')
121211990Speter	        {
12134374Slars		logf("\n");
121411990Speter	        }
12154374Slars	    else
121611990Speter	        {
12174374Slars		logf(character(c));
121811990Speter	        }
12194374Slars	    }
12204374Slars
12214374Slars	*s++ = c;
12224374Slars
12234374Slars	if (s - temp >= len &&
12244374Slars	    c == string[len - 1] &&
12254374Slars	    strncmp(s - len, string, len) == 0)
12264374Slars	    {
12274374Slars	    if (verbose)
12284374Slars		{
12294374Slars		logf(" -- got it\n");
12304374Slars		}
12314374Slars
123211990Speter	    alarm(0);
123311990Speter	    alarmed = 0;
12344374Slars	    return (1);
12354374Slars	    }
12364374Slars
12374374Slars	for (n = 0; n < n_aborts; ++n)
123811990Speter	    {
12394374Slars	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
12404374Slars		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
124111990Speter	        {
12424374Slars		if (verbose)
12434374Slars		    {
12444374Slars		    logf(" -- failed\n");
12454374Slars		    }
124611990Speter
124711990Speter		alarm(0);
124811990Speter		alarmed = 0;
124911990Speter		exit_code = n + 4;
12504374Slars		strcpy(fail_reason = fail_buffer, abort_string[n]);
12514374Slars		return (0);
125211990Speter	        }
125311990Speter	    }
12544374Slars
125511990Speter	if (!report_gathering)
125611990Speter	    {
125711990Speter	    for (n = 0; n < n_reports; ++n)
125811990Speter	        {
125911990Speter		if ((report_string[n] != (char*) NULL) &&
126011990Speter		    s - temp >= (report_len = strlen(report_string[n])) &&
126111990Speter		    strncmp(s - report_len, report_string[n], report_len) == 0)
126211990Speter		    {
126311990Speter		    time_t time_now   = time ((time_t*) NULL);
126411990Speter		    struct tm* tm_now = localtime (&time_now);
126511990Speter
126611990Speter		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
126711990Speter		    strcat (report_buffer, report_string[n]);
126811990Speter
126911990Speter		    report_string[n] = (char *) NULL;
127011990Speter		    report_gathering = 1;
127111990Speter		    break;
127211990Speter		    }
127311990Speter	        }
127411990Speter	    }
127511990Speter	else
127611990Speter	    {
127711990Speter	    if (!iscntrl (c))
127811990Speter	        {
127911990Speter		int rep_len = strlen (report_buffer);
128011990Speter		report_buffer[rep_len]     = c;
128111990Speter		report_buffer[rep_len + 1] = '\0';
128211990Speter	        }
128311990Speter	    else
128411990Speter	        {
128511990Speter		report_gathering = 0;
128611990Speter		fprintf (report_fp, "chat:  %s\n", report_buffer);
128711990Speter	        }
128811990Speter	    }
128911990Speter
12904374Slars	if (s >= end)
12914374Slars	    {
129211990Speter	    strncpy (temp, s - minlen, minlen);
12934374Slars	    s = temp + minlen;
12944374Slars	    }
12954374Slars
12964374Slars	if (alarmed && verbose)
129711990Speter	    {
12984374Slars	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
129911990Speter	    }
13004374Slars	}
13014374Slars
13024374Slars    alarm(0);
130311990Speter
13044374Slars    if (verbose && printed)
13054374Slars	{
13064374Slars	if (alarmed)
130711990Speter	    {
13084374Slars	    logf(" -- read timed out\n");
130911990Speter	    }
13104374Slars	else
13114374Slars	    {
13124374Slars	    logflush();
13134374Slars	    syslog(LOG_INFO, " -- read failed: %m");
13144374Slars	    }
13154374Slars	}
13164374Slars
131711990Speter    exit_code = 3;
131811990Speter    alarmed   = 0;
13194374Slars    return (0);
13204374Slars    }
13214374Slars
132211990Speter#ifdef NO_USLEEP
13234374Slars#include <sys/types.h>
13244374Slars#include <sys/time.h>
13254374Slars
13264374Slars/*
13274374Slars  usleep -- support routine for 4.2BSD system call emulations
13284374Slars  last edit:  29-Oct-1984     D A Gwyn
13294374Slars  */
13304374Slars
13314374Slarsextern int	  select();
13324374Slars
13334374Slarsint
13344374Slarsusleep( usec )				  /* returns 0 if ok, else -1 */
13354374Slars    long		usec;		/* delay in microseconds */
13364374Slars{
13374374Slars    static struct			/* `timeval' */
133811990Speter        {
133911990Speter	long	tv_sec;		/* seconds */
134011990Speter	long	tv_usec;	/* microsecs */
134111990Speter        } delay;	    /* _select() timeout */
13424374Slars
134311990Speter    delay.tv_sec  = usec / 1000000L;
13444374Slars    delay.tv_usec = usec % 1000000L;
13454374Slars
13464374Slars    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
13474374Slars}
13484374Slars#endif
1349