chat.c revision 24541
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
3424541Sjmgstatic char rcsid[] = "$Id: chat.c,v 1.6 1997/02/22 19:54:23 peter 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
894374Slarschar *program_name;
904374Slars
914374Slars#define	MAX_ABORTS		50
9211990Speter#define	MAX_REPORTS		50
934374Slars#define	DEFAULT_CHAT_TIMEOUT	45
944374Slars
9511990Speterint verbose       = 0;
9611990Speterint quiet         = 0;
9711990Speterint report        = 0;
9811990Speterint exit_code     = 0;
9911990SpeterFILE* report_fp   = (FILE *) 0;
10011990Speterchar *report_file = (char *) 0;
10111990Speterchar *chat_file   = (char *) 0;
10211990Speterint timeout       = DEFAULT_CHAT_TIMEOUT;
1034374Slars
1044374Slarsint have_tty_parameters = 0;
10511990Speter
1064374Slars#ifdef TERMIO
10711990Speter#define term_parms struct termio
10811990Speter#define get_term_param(param) ioctl(0, TCGETA, param)
10911990Speter#define set_term_param(param) ioctl(0, TCSETA, param)
1104374Slarsstruct termio saved_tty_parameters;
1114374Slars#endif
11211990Speter
1134374Slars#ifdef TERMIOS
11411990Speter#define term_parms struct termios
11511990Speter#define get_term_param(param) tcgetattr(0, param)
11611990Speter#define set_term_param(param) tcsetattr(0, TCSANOW, param)
1174374Slarsstruct termios saved_tty_parameters;
1184374Slars#endif
1194374Slars
1204374Slarschar *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1214374Slars	fail_buffer[50];
1224374Slarsint n_aborts = 0, abort_next = 0, timeout_next = 0;
1234374Slars
12411990Speterchar *report_string[MAX_REPORTS] ;
12511990Speterchar  report_buffer[50] ;
12611990Speterint n_reports = 0, report_next = 0, report_gathering = 0 ;
12711990Speter
1284374Slarsvoid *dup_mem __P((void *b, size_t c));
1294374Slarsvoid *copy_of __P((char *s));
1304374Slarsvoid usage __P((void));
1314374Slarsvoid logf __P((const char *str));
1324374Slarsvoid logflush __P((void));
1334374Slarsvoid fatal __P((const char *msg));
1344374Slarsvoid sysfatal __P((const char *msg));
1354374SlarsSIGTYPE sigalrm __P((int signo));
1364374SlarsSIGTYPE sigint __P((int signo));
1374374SlarsSIGTYPE sigterm __P((int signo));
1384374SlarsSIGTYPE sighup __P((int signo));
1394374Slarsvoid unalarm __P((void));
1404374Slarsvoid init __P((void));
1414374Slarsvoid set_tty_parameters __P((void));
1424374Slarsvoid break_sequence __P((void));
1434374Slarsvoid terminate __P((int status));
1444374Slarsvoid do_file __P((char *chat_file));
1454374Slarsint  get_string __P((register char *string));
1464374Slarsint  put_string __P((register char *s));
1474374Slarsint  write_char __P((int c));
14811990Speterint  put_char __P((int c));
1494374Slarsint  get_char __P((void));
1504374Slarsvoid chat_send __P((register char *s));
15111990Speterchar *character __P((int c));
1524374Slarsvoid chat_expect __P((register char *s));
1534374Slarschar *clean __P((register char *s, int sending));
1544374Slarsvoid break_sequence __P((void));
1554374Slarsvoid terminate __P((int status));
1564374Slarsvoid die __P((void));
1574374Slars
1584374Slarsvoid *dup_mem(b, c)
1594374Slarsvoid *b;
1604374Slarssize_t c;
1614374Slars    {
1624374Slars    void *ans = malloc (c);
1634374Slars    if (!ans)
16411990Speter        {
1654374Slars	fatal ("memory error!\n");
16611990Speter        }
1674374Slars    memcpy (ans, b, c);
1684374Slars    return ans;
1694374Slars    }
1704374Slars
1714374Slarsvoid *copy_of (s)
1724374Slarschar *s;
1734374Slars    {
1744374Slars    return dup_mem (s, strlen (s) + 1);
1754374Slars    }
1764374Slars
1774374Slars/*
17811990Speter *	chat [ -v ] [ -t timeout ] [ -f chat-file ] [ -r report-file ] \
1794374Slars *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
1804374Slars *
1814374Slars *	Perform a UUCP-dialer-like chat script on stdin and stdout.
1824374Slars */
1834374Slarsint
1844374Slarsmain(argc, argv)
1854374Slarsint argc;
1864374Slarschar **argv;
1874374Slars    {
1884374Slars    int option;
1894374Slars    char *arg;
1904374Slars
1914374Slars    program_name = *argv;
19211990Speter    tzset();
1934374Slars
1944374Slars    while (option = OPTION(argc, argv))
19511990Speter        {
1964374Slars	switch (option)
1974374Slars	    {
1984374Slars	    case 'v':
1994374Slars		++verbose;
2004374Slars		break;
2014374Slars
2024374Slars	    case 'f':
2034374Slars		if (arg = OPTARG(argc, argv))
20411990Speter		    {
2054374Slars		    chat_file = copy_of(arg);
20611990Speter		    }
2074374Slars		else
20811990Speter		    {
2094374Slars		    usage();
21011990Speter		    }
2114374Slars		break;
2124374Slars
2134374Slars	    case 't':
2144374Slars		if (arg = OPTARG(argc, argv))
21511990Speter		    {
2164374Slars		    timeout = atoi(arg);
21711990Speter		    }
2184374Slars		else
21911990Speter		    {
2204374Slars		    usage();
22111990Speter		    }
22211990Speter		break;
2234374Slars
22411990Speter	    case 'r':
22511990Speter		arg = OPTARG (argc, argv);
22611990Speter		if (arg)
22711990Speter		    {
22811990Speter		    if (report_fp != NULL)
22911990Speter		        {
23011990Speter			fclose (report_fp);
23111990Speter		        }
23211990Speter		    report_file = copy_of (arg);
23311990Speter		    report_fp   = fopen (report_file, "a");
23411990Speter		    if (report_fp != NULL)
23511990Speter		        {
23611990Speter			if (verbose)
23711990Speter			    {
23811990Speter			    fprintf (report_fp, "Opening \"%s\"...\n",
23911990Speter				     report_file);
24011990Speter			    }
24111990Speter			report = 1;
24211990Speter		        }
24311990Speter		    }
2444374Slars		break;
2454374Slars
2464374Slars	    default:
2474374Slars		usage();
24811990Speter		break;
2494374Slars	    }
25011990Speter      }
25111990Speter/*
25211990Speter * Default the report file to the stderr location
25311990Speter */
25411990Speter    if (report_fp == NULL)
25511990Speter        {
25611990Speter	report_fp = stderr;
25711990Speter        }
2584374Slars
2594374Slars#ifdef ultrix
2604374Slars    openlog("chat", LOG_PID);
2614374Slars#else
2624374Slars    openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
2634374Slars
26411990Speter    if (verbose)
26511990Speter        {
2664374Slars	setlogmask(LOG_UPTO(LOG_INFO));
26711990Speter        }
26811990Speter    else
26911990Speter        {
2704374Slars	setlogmask(LOG_UPTO(LOG_WARNING));
27111990Speter        }
2724374Slars#endif
2734374Slars
2744374Slars    init();
27511990Speter
2764374Slars    if (chat_file != NULL)
2774374Slars	{
2784374Slars	arg = ARG(argc, argv);
2794374Slars	if (arg != NULL)
28011990Speter	    {
2814374Slars	    usage();
28211990Speter	    }
2834374Slars	else
28411990Speter	    {
2854374Slars	    do_file (chat_file);
28611990Speter	    }
2874374Slars	}
2884374Slars    else
2894374Slars	{
2904374Slars	while (arg = ARG(argc, argv))
2914374Slars	    {
2924374Slars	    chat_expect(arg);
2934374Slars
2944374Slars	    if (arg = ARG(argc, argv))
29511990Speter	        {
2964374Slars		chat_send(arg);
29711990Speter	        }
2984374Slars	    }
2994374Slars	}
3004374Slars
3014374Slars    terminate(0);
3024374Slars    }
3034374Slars
3044374Slars/*
3054374Slars *  Process a chat script when read from a file.
3064374Slars */
3074374Slars
3084374Slarsvoid do_file (chat_file)
3094374Slarschar *chat_file;
3104374Slars    {
3114374Slars    int linect, len, sendflg;
3124374Slars    char *sp, *arg, quote;
3134374Slars    char buf [STR_LEN];
3144374Slars    FILE *cfp;
3154374Slars
31611990Speter    cfp = fopen (chat_file, "r");
31711990Speter    if (cfp == NULL)
3184374Slars	{
3194374Slars	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
3204374Slars	terminate (1);
3214374Slars	}
3224374Slars
3234374Slars    linect = 0;
3244374Slars    sendflg = 0;
3254374Slars
3264374Slars    while (fgets(buf, STR_LEN, cfp) != NULL)
3274374Slars	{
3284374Slars	sp = strchr (buf, '\n');
3294374Slars	if (sp)
33011990Speter	    {
3314374Slars	    *sp = '\0';
33211990Speter	    }
3334374Slars
3344374Slars	linect++;
3354374Slars	sp = buf;
3364374Slars	while (*sp != '\0')
3374374Slars	    {
3384374Slars	    if (*sp == ' ' || *sp == '\t')
3394374Slars		{
3404374Slars		++sp;
3414374Slars		continue;
3424374Slars		}
3434374Slars
3444374Slars	    if (*sp == '"' || *sp == '\'')
3454374Slars		{
3464374Slars		quote = *sp++;
3474374Slars		arg = sp;
3484374Slars		while (*sp != quote)
3494374Slars		    {
3504374Slars		    if (*sp == '\0')
3514374Slars			{
3524374Slars			syslog (LOG_ERR, "unterminated quote (line %d)",
3534374Slars				linect);
3544374Slars			terminate (1);
3554374Slars			}
35611990Speter
3574374Slars		    if (*sp++ == '\\')
35811990Speter		        {
3594374Slars			if (*sp != '\0')
36011990Speter			    {
3614374Slars			    ++sp;
36211990Speter			    }
36311990Speter		        }
3644374Slars		    }
3654374Slars		}
3664374Slars	    else
3674374Slars		{
3684374Slars		arg = sp;
3694374Slars		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
37011990Speter		    {
3714374Slars		    ++sp;
37211990Speter		    }
3734374Slars		}
3744374Slars
3754374Slars	    if (*sp != '\0')
37611990Speter	        {
3774374Slars		*sp++ = '\0';
37811990Speter	        }
3794374Slars
3804374Slars	    if (sendflg)
3814374Slars		{
3824374Slars		chat_send (arg);
3834374Slars		}
3844374Slars	    else
3854374Slars		{
3864374Slars		chat_expect (arg);
3874374Slars		}
3884374Slars	    sendflg = !sendflg;
3894374Slars	    }
3904374Slars	}
3914374Slars    fclose (cfp);
3924374Slars    }
3934374Slars
3944374Slars/*
3954374Slars *	We got an error parsing the command line.
3964374Slars */
3974374Slarsvoid usage()
3984374Slars    {
3994374Slars    fprintf(stderr, "\
40011990SpeterUsage: %s [-v] [-t timeout] [-r report-file] {-f chat-file | chat-script}\n",
4014374Slars	    program_name);
4024374Slars    exit(1);
4034374Slars    }
4044374Slars
4054374Slarschar line[256];
4064374Slarschar *p;
4074374Slars
4084374Slarsvoid logf (str)
4094374Slarsconst char *str;
4104374Slars    {
4114374Slars    p = line + strlen(line);
4124374Slars    strcat (p, str);
4134374Slars
4144374Slars    if (str[strlen(str)-1] == '\n')
4154374Slars	{
4164374Slars	syslog (LOG_INFO, "%s", line);
4174374Slars	line[0] = 0;
4184374Slars	}
4194374Slars    }
4204374Slars
4214374Slarsvoid logflush()
4224374Slars    {
4234374Slars    if (line[0] != 0)
4244374Slars	{
4254374Slars	syslog(LOG_INFO, "%s", line);
4264374Slars	line[0] = 0;
4274374Slars        }
4284374Slars    }
4294374Slars
4304374Slars/*
43111990Speter *	Terminate with an error.
4324374Slars */
4334374Slarsvoid die()
4344374Slars    {
4354374Slars    terminate(1);
4364374Slars    }
4374374Slars
4384374Slars/*
4394374Slars *	Print an error message and terminate.
4404374Slars */
4414374Slars
4424374Slarsvoid fatal (msg)
4434374Slarsconst char *msg;
4444374Slars    {
4454374Slars    syslog(LOG_ERR, "%s", msg);
44611990Speter    terminate(2);
4474374Slars    }
4484374Slars
4494374Slars/*
4504374Slars *	Print an error message along with the system error message and
4514374Slars *	terminate.
4524374Slars */
4534374Slars
4544374Slarsvoid sysfatal (msg)
4554374Slarsconst char *msg;
4564374Slars    {
4574374Slars    syslog(LOG_ERR, "%s: %m", msg);
45811990Speter    terminate(2);
4594374Slars    }
4604374Slars
4614374Slarsint alarmed = 0;
4624374Slars
4634374SlarsSIGTYPE sigalrm(signo)
4644374Slarsint signo;
4654374Slars    {
4664374Slars    int flags;
4674374Slars
4684374Slars    alarm(1);
4694374Slars    alarmed = 1;		/* Reset alarm to avoid race window */
4704374Slars    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
4714374Slars
4724374Slars    logflush();
4734374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
47411990Speter        {
4754374Slars	sysfatal("Can't get file mode flags on stdin");
47611990Speter        }
4774374Slars    else
47811990Speter        {
47911990Speter	if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
48011990Speter	    {
4814374Slars	    sysfatal("Can't set file mode flags on stdin");
48211990Speter	    }
48311990Speter        }
4844374Slars
4854374Slars    if (verbose)
4864374Slars	{
4874374Slars	syslog(LOG_INFO, "alarm");
4884374Slars	}
4894374Slars    }
4904374Slars
4914374Slarsvoid unalarm()
4924374Slars    {
4934374Slars    int flags;
4944374Slars
4954374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
49611990Speter        {
4974374Slars	sysfatal("Can't get file mode flags on stdin");
49811990Speter        }
4994374Slars    else
50011990Speter        {
50111990Speter	if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
50211990Speter	    {
5034374Slars	    sysfatal("Can't set file mode flags on stdin");
50411990Speter	    }
50511990Speter        }
5064374Slars    }
5074374Slars
5084374SlarsSIGTYPE sigint(signo)
5094374Slarsint signo;
5104374Slars    {
5114374Slars    fatal("SIGINT");
5124374Slars    }
5134374Slars
5144374SlarsSIGTYPE sigterm(signo)
5154374Slarsint signo;
5164374Slars    {
5174374Slars    fatal("SIGTERM");
5184374Slars    }
5194374Slars
5204374SlarsSIGTYPE sighup(signo)
5214374Slarsint signo;
5224374Slars    {
5234374Slars    fatal("SIGHUP");
5244374Slars    }
5254374Slars
5264374Slarsvoid init()
5274374Slars    {
5284374Slars    signal(SIGINT, sigint);
5294374Slars    signal(SIGTERM, sigterm);
5304374Slars    signal(SIGHUP, sighup);
5314374Slars
5324374Slars    set_tty_parameters();
5334374Slars    signal(SIGALRM, sigalrm);
5344374Slars    alarm(0);
5354374Slars    alarmed = 0;
5364374Slars    }
5374374Slars
5384374Slarsvoid set_tty_parameters()
5394374Slars    {
54011990Speter#if defined(get_term_param)
54111990Speter    term_parms t;
5424374Slars
54311990Speter    if (get_term_param (&t) < 0)
54411990Speter        {
54524541Sjmg	have_tty_parameters = 0;
54624541Sjmg	return;
54711990Speter        }
5484374Slars
5494374Slars    saved_tty_parameters = t;
55011990Speter    have_tty_parameters  = 1;
5514374Slars
55211990Speter    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
55311990Speter    t.c_oflag      = 0;
55411990Speter    t.c_lflag      = 0;
55511990Speter    t.c_cc[VERASE] =
55611990Speter    t.c_cc[VKILL]  = 0;
55711990Speter    t.c_cc[VMIN]   = 1;
55811990Speter    t.c_cc[VTIME]  = 0;
5594374Slars
56011990Speter    if (set_term_param (&t) < 0)
56111990Speter        {
5624374Slars	sysfatal("Can't set terminal parameters");
56311990Speter        }
5644374Slars#endif
5654374Slars    }
5664374Slars
5674374Slarsvoid break_sequence()
5684374Slars    {
5694374Slars#ifdef TERMIOS
5704374Slars    tcsendbreak (0, 0);
5714374Slars#endif
5724374Slars    }
5734374Slars
5744374Slarsvoid terminate(status)
5754374Slarsint status;
5764374Slars    {
57711990Speter    if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
57811990Speter        {
57911990Speter	if (verbose)
58011990Speter	    {
58111990Speter	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
58211990Speter	    }
58311990Speter	fclose (report_fp);
58411990Speter	report_fp = (FILE*) NULL;
5854374Slars        }
5864374Slars
58711990Speter#if defined(get_term_param)
58811990Speter    if (have_tty_parameters)
58911990Speter        {
59011990Speter	if (set_term_param (&saved_tty_parameters) < 0)
59111990Speter	    {
59211990Speter	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
59311990Speter	    exit(1);
59411990Speter	    }
59511990Speter        }
59611990Speter#endif
5974374Slars
59811990Speter    exit(status);
5994374Slars    }
6004374Slars
6014374Slars/*
6024374Slars *	'Clean up' this string.
6034374Slars */
6044374Slarschar *clean(s, sending)
6054374Slarsregister char *s;
6064374Slarsint sending;
6074374Slars    {
6084374Slars    char temp[STR_LEN], cur_chr;
6094374Slars    register char *s1;
6104374Slars    int add_return = sending;
6114374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
6124374Slars
6134374Slars    s1 = temp;
6144374Slars    while (*s)
6154374Slars	{
6164374Slars	cur_chr = *s++;
6174374Slars	if (cur_chr == '^')
6184374Slars	    {
6194374Slars	    cur_chr = *s++;
6204374Slars	    if (cur_chr == '\0')
6214374Slars		{
6224374Slars		*s1++ = '^';
6234374Slars		break;
6244374Slars		}
6254374Slars	    cur_chr &= 0x1F;
6264374Slars	    if (cur_chr != 0)
62711990Speter	        {
6284374Slars		*s1++ = cur_chr;
62911990Speter	        }
6304374Slars	    continue;
6314374Slars	    }
6324374Slars
6334374Slars	if (cur_chr != '\\')
6344374Slars	    {
6354374Slars	    *s1++ = cur_chr;
6364374Slars	    continue;
6374374Slars	    }
6384374Slars
6394374Slars	cur_chr = *s++;
6404374Slars	if (cur_chr == '\0')
6414374Slars	    {
6424374Slars	    if (sending)
6434374Slars		{
6444374Slars		*s1++ = '\\';
6454374Slars		*s1++ = '\\';
6464374Slars		}
6474374Slars	    break;
6484374Slars	    }
6494374Slars
6504374Slars	switch (cur_chr)
6514374Slars	    {
6524374Slars	case 'b':
6534374Slars	    *s1++ = '\b';
6544374Slars	    break;
6554374Slars
6564374Slars	case 'c':
6574374Slars	    if (sending && *s == '\0')
65811990Speter	        {
6594374Slars		add_return = 0;
66011990Speter	        }
6614374Slars	    else
66211990Speter	        {
6634374Slars		*s1++ = cur_chr;
66411990Speter	        }
6654374Slars	    break;
6664374Slars
6674374Slars	case '\\':
6684374Slars	case 'K':
6694374Slars	case 'p':
6704374Slars	case 'd':
6714374Slars	    if (sending)
67211990Speter	        {
6734374Slars		*s1++ = '\\';
67411990Speter	        }
6754374Slars
6764374Slars	    *s1++ = cur_chr;
6774374Slars	    break;
6784374Slars
6794374Slars	case 'q':
6804374Slars	    quiet = ! quiet;
6814374Slars	    break;
6824374Slars
6834374Slars	case 'r':
6844374Slars	    *s1++ = '\r';
6854374Slars	    break;
6864374Slars
6874374Slars	case 'n':
6884374Slars	    *s1++ = '\n';
6894374Slars	    break;
6904374Slars
6914374Slars	case 's':
6924374Slars	    *s1++ = ' ';
6934374Slars	    break;
6944374Slars
6954374Slars	case 't':
6964374Slars	    *s1++ = '\t';
6974374Slars	    break;
6984374Slars
6994374Slars	case 'N':
7004374Slars	    if (sending)
7014374Slars		{
7024374Slars		*s1++ = '\\';
7034374Slars		*s1++ = '\0';
7044374Slars		}
7054374Slars	    else
70611990Speter	        {
7074374Slars		*s1++ = 'N';
70811990Speter	        }
7094374Slars	    break;
71011990Speter
7114374Slars	default:
7124374Slars	    if (isoctal (cur_chr))
7134374Slars		{
7144374Slars		cur_chr &= 0x07;
7154374Slars		if (isoctal (*s))
7164374Slars		    {
7174374Slars		    cur_chr <<= 3;
7184374Slars		    cur_chr |= *s++ - '0';
7194374Slars		    if (isoctal (*s))
7204374Slars			{
7214374Slars			cur_chr <<= 3;
7224374Slars			cur_chr |= *s++ - '0';
7234374Slars			}
7244374Slars		    }
7254374Slars
7264374Slars		if (cur_chr != 0 || sending)
7274374Slars		    {
7284374Slars		    if (sending && (cur_chr == '\\' || cur_chr == 0))
72911990Speter		        {
7304374Slars			*s1++ = '\\';
73111990Speter		        }
7324374Slars		    *s1++ = cur_chr;
7334374Slars		    }
7344374Slars		break;
7354374Slars		}
7364374Slars
7374374Slars	    if (sending)
73811990Speter	        {
7394374Slars		*s1++ = '\\';
74011990Speter	        }
7414374Slars	    *s1++ = cur_chr;
7424374Slars	    break;
7434374Slars	    }
7444374Slars	}
7454374Slars
7464374Slars    if (add_return)
74711990Speter        {
7484374Slars	*s1++ = '\r';
74911990Speter        }
7504374Slars
7514374Slars    *s1++ = '\0'; /* guarantee closure */
7524374Slars    *s1++ = '\0'; /* terminate the string */
7534374Slars    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7544374Slars    }
7554374Slars
7564374Slars/*
7574374Slars * Process the expect string
7584374Slars */
7594374Slarsvoid chat_expect(s)
7604374Slarsregister char *s;
7614374Slars    {
7624374Slars    if (strcmp(s, "ABORT") == 0)
7634374Slars	{
7644374Slars	++abort_next;
7654374Slars	return;
7664374Slars	}
7674374Slars
76811990Speter    if (strcmp(s, "REPORT") == 0)
76911990Speter	{
77011990Speter	++report_next;
77111990Speter	return;
77211990Speter	}
77311990Speter
7744374Slars    if (strcmp(s, "TIMEOUT") == 0)
7754374Slars	{
7764374Slars	++timeout_next;
7774374Slars	return;
7784374Slars	}
7794374Slars
7804374Slars    while (*s)
7814374Slars	{
7824374Slars	register char *hyphen;
7834374Slars
7844374Slars	for (hyphen = s; *hyphen; ++hyphen)
78511990Speter	    {
7864374Slars	    if (*hyphen == '-')
78711990Speter	        {
7884374Slars		if (hyphen == s || hyphen[-1] != '\\')
78911990Speter		    {
7904374Slars		    break;
79111990Speter		    }
79211990Speter	        }
79311990Speter	    }
79411990Speter
7954374Slars	if (*hyphen == '-')
7964374Slars	    {
7974374Slars	    *hyphen = '\0';
7984374Slars
7994374Slars	    if (get_string(s))
80011990Speter	        {
8014374Slars		return;
80211990Speter	        }
8034374Slars	    else
8044374Slars		{
8054374Slars		s = hyphen + 1;
8064374Slars
8074374Slars		for (hyphen = s; *hyphen; ++hyphen)
80811990Speter		    {
8094374Slars		    if (*hyphen == '-')
81011990Speter		        {
8114374Slars			if (hyphen == s || hyphen[-1] != '\\')
81211990Speter			    {
8134374Slars			    break;
81411990Speter			    }
81511990Speter		        }
81611990Speter		    }
8174374Slars
8184374Slars		if (*hyphen == '-')
8194374Slars		    {
8204374Slars		    *hyphen = '\0';
8214374Slars
8224374Slars		    chat_send(s);
8234374Slars		    s = hyphen + 1;
8244374Slars		    }
8254374Slars		else
8264374Slars		    {
8274374Slars		    chat_send(s);
8284374Slars		    return;
8294374Slars		    }
8304374Slars		}
8314374Slars	    }
8324374Slars	else
83311990Speter	    {
8344374Slars	    if (get_string(s))
83511990Speter	        {
8364374Slars		return;
83711990Speter	        }
8384374Slars	    else
8394374Slars		{
8404374Slars		if (fail_reason)
84111990Speter		    {
8424374Slars		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
84311990Speter		    }
8444374Slars		else
84511990Speter		    {
8464374Slars		    syslog(LOG_INFO, "Failed");
84711990Speter		    }
8484374Slars
84911990Speter		terminate(exit_code);
8504374Slars		}
85111990Speter	    }
8524374Slars	}
8534374Slars    }
8544374Slars
8554374Slarschar *character(c)
85611990Speterint c;
8574374Slars    {
8584374Slars    static char string[10];
8594374Slars    char *meta;
8604374Slars
8614374Slars    meta = (c & 0x80) ? "M-" : "";
8624374Slars    c &= 0x7F;
8634374Slars
8644374Slars    if (c < 32)
86511990Speter        {
8664374Slars	sprintf(string, "%s^%c", meta, (int)c + '@');
86711990Speter        }
8684374Slars    else
86911990Speter        {
8704374Slars	if (c == 127)
87111990Speter	    {
8724374Slars	    sprintf(string, "%s^?", meta);
87311990Speter	    }
8744374Slars	else
87511990Speter	    {
8764374Slars	    sprintf(string, "%s%c", meta, c);
87711990Speter	    }
87811990Speter        }
8794374Slars
8804374Slars    return (string);
8814374Slars    }
8824374Slars
8834374Slars/*
8844374Slars *  process the reply string
8854374Slars */
8864374Slarsvoid chat_send (s)
8874374Slarsregister char *s;
8884374Slars    {
8894374Slars    if (abort_next)
89011990Speter        {
8914374Slars	char *s1;
89211990Speter
8934374Slars	abort_next = 0;
89411990Speter
8954374Slars	if (n_aborts >= MAX_ABORTS)
89611990Speter	    {
8974374Slars	    fatal("Too many ABORT strings");
89811990Speter	    }
89911990Speter
9004374Slars	s1 = clean(s, 0);
90111990Speter
90211990Speter	if (strlen(s1) > strlen(s)
90311990Speter	    || strlen(s1) + 1 > sizeof(fail_buffer))
9044374Slars	    {
9054374Slars	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
9064374Slars	    die();
9074374Slars	    }
9084374Slars
9094374Slars	abort_string[n_aborts++] = s1;
9104374Slars
9114374Slars	if (verbose)
9124374Slars	    {
9134374Slars	    logf("abort on (");
9144374Slars
9154374Slars	    for (s1 = s; *s1; ++s1)
91611990Speter	        {
9174374Slars		logf(character(*s1));
91811990Speter	        }
9194374Slars
9204374Slars	    logf(")\n");
9214374Slars	    }
92211990Speter	return;
9234374Slars	}
92411990Speter
92511990Speter    if (report_next)
92611990Speter        {
92711990Speter	char *s1;
92811990Speter
92911990Speter	report_next = 0;
93011990Speter	if (n_reports >= MAX_REPORTS)
9314374Slars	    {
93211990Speter	    fatal("Too many REPORT strings");
93311990Speter	    }
93411990Speter
93511990Speter	s1 = clean(s, 0);
93611990Speter
93711990Speter	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
93811990Speter	    {
93911990Speter	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
94011990Speter	    die();
94111990Speter	    }
94211990Speter
94311990Speter	report_string[n_reports++] = s1;
94411990Speter
94511990Speter	if (verbose)
94611990Speter	    {
94711990Speter	    logf("report (");
94811990Speter	    s1 = s;
94911990Speter	    while (*s1)
95011990Speter	        {
95111990Speter		logf(character(*s1));
95211990Speter		++s1;
95311990Speter	        }
95411990Speter	    logf(")\n");
95511990Speter	    }
95611990Speter	return;
95711990Speter        }
9584374Slars
95911990Speter    if (timeout_next)
96011990Speter        {
96111990Speter	timeout_next = 0;
96211990Speter	timeout = atoi(s);
96311990Speter
96411990Speter	if (timeout <= 0)
96511990Speter	    {
96611990Speter	    timeout = DEFAULT_CHAT_TIMEOUT;
96711990Speter	    }
9684374Slars
96911990Speter	if (verbose)
97011990Speter	    {
97111990Speter	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
9724374Slars	    }
97311990Speter	return;
97411990Speter        }
97511990Speter
97611990Speter    if (strcmp(s, "EOT") == 0)
97711990Speter        {
97811990Speter	s = "^D\\c";
97911990Speter        }
98011990Speter    else
98111990Speter        {
98211990Speter	if (strcmp(s, "BREAK") == 0)
9834374Slars	    {
98411990Speter	    s = "\\K\\c";
9854374Slars	    }
98611990Speter        }
98711990Speter
98811990Speter    if (!put_string(s))
98911990Speter        {
99011990Speter	syslog(LOG_INFO, "Failed");
99111990Speter	terminate(1);
99211990Speter        }
9934374Slars    }
9944374Slars
9954374Slarsint get_char()
9964374Slars    {
9974374Slars    int status;
9984374Slars    char c;
9994374Slars
10004374Slars    status = read(0, &c, 1);
10014374Slars
10024374Slars    switch (status)
100311990Speter        {
100411990Speter    case 1:
100511990Speter	return ((int)c & 0x7F);
10064374Slars
100711990Speter    default:
100811990Speter	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
100911990Speter	       status);
10104374Slars
101111990Speter    case -1:
101211990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
101311990Speter	    {
101411990Speter	    sysfatal("Can't get file mode flags on stdin");
101511990Speter	    }
101611990Speter	else
101711990Speter	    {
101811990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
101911990Speter	        {
102011990Speter		sysfatal("Can't set file mode flags on stdin");
102111990Speter	        }
102211990Speter	    }
102311990Speter
102411990Speter	return (-1);
102511990Speter        }
10264374Slars    }
10274374Slars
10284374Slarsint put_char(c)
102911990Speterint c;
10304374Slars    {
10314374Slars    int status;
103211990Speter    char ch = c;
10334374Slars
103411990Speter    usleep(10000);		/* inter-character typing delay (?) */
10354374Slars
103611990Speter    status = write(1, &ch, 1);
10374374Slars
10384374Slars    switch (status)
103911990Speter        {
104011990Speter    case 1:
104111990Speter	return (0);
104211990Speter
104311990Speter    default:
104411990Speter	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
104511990Speter	       status);
104611990Speter
104711990Speter    case -1:
104811990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
104911990Speter	    {
105011990Speter	    sysfatal("Can't get file mode flags on stdin");
105111990Speter	    }
105211990Speter	else
105311990Speter	    {
105411990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
105511990Speter	        {
105611990Speter		sysfatal("Can't set file mode flags on stdin");
105711990Speter	        }
105811990Speter	    }
105911990Speter
106011990Speter	return (-1);
106111990Speter        }
10624374Slars    }
10634374Slars
10644374Slarsint write_char (c)
10654374Slarsint c;
10664374Slars    {
10674374Slars    if (alarmed || put_char(c) < 0)
10684374Slars	{
10694374Slars	extern int errno;
10704374Slars
107111990Speter	alarm(0);
107211990Speter	alarmed = 0;
10734374Slars
10744374Slars	if (verbose)
10754374Slars	    {
10764374Slars	    if (errno == EINTR || errno == EWOULDBLOCK)
107711990Speter	        {
10784374Slars		syslog(LOG_INFO, " -- write timed out");
107911990Speter	        }
10804374Slars	    else
108111990Speter	        {
10824374Slars		syslog(LOG_INFO, " -- write failed: %m");
108311990Speter	        }
10844374Slars	    }
10854374Slars	return (0);
10864374Slars	}
10874374Slars    return (1);
10884374Slars    }
10894374Slars
10904374Slarsint put_string (s)
10914374Slarsregister char *s;
10924374Slars    {
10934374Slars    s = clean(s, 1);
10944374Slars
10954374Slars    if (verbose)
10964374Slars	{
10974374Slars	logf("send (");
10984374Slars
10994374Slars	if (quiet)
110011990Speter	    {
11014374Slars	    logf("??????");
110211990Speter	    }
11034374Slars	else
11044374Slars	    {
11054374Slars	    register char *s1 = s;
11064374Slars
11074374Slars	    for (s1 = s; *s1; ++s1)
110811990Speter	        {
11094374Slars		logf(character(*s1));
111011990Speter	        }
11114374Slars	    }
11124374Slars
11134374Slars	logf(")\n");
11144374Slars	}
11154374Slars
11164374Slars    alarm(timeout); alarmed = 0;
11174374Slars
11184374Slars    while (*s)
11194374Slars	{
11204374Slars	register char c = *s++;
11214374Slars
11224374Slars	if (c != '\\')
11234374Slars	    {
11244374Slars	    if (!write_char (c))
112511990Speter	        {
11264374Slars		return 0;
112711990Speter	        }
11284374Slars	    continue;
11294374Slars	    }
11304374Slars
11314374Slars	c = *s++;
11324374Slars	switch (c)
11334374Slars	    {
11344374Slars	case 'd':
11354374Slars	    sleep(1);
11364374Slars	    break;
11374374Slars
11384374Slars	case 'K':
11394374Slars	    break_sequence();
11404374Slars	    break;
11414374Slars
11424374Slars	case 'p':
114311990Speter	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
11444374Slars	    break;
11454374Slars
11464374Slars	default:
11474374Slars	    if (!write_char (c))
11484374Slars		return 0;
11494374Slars	    break;
11504374Slars	    }
11514374Slars	}
11524374Slars
11534374Slars    alarm(0);
11544374Slars    alarmed = 0;
11554374Slars    return (1);
11564374Slars    }
11574374Slars
11584374Slars/*
11594374Slars *	'Wait for' this string to appear on this file descriptor.
11604374Slars */
11614374Slarsint get_string(string)
11624374Slarsregister char *string;
11634374Slars    {
11644374Slars    char temp[STR_LEN];
11654374Slars    int c, printed = 0, len, minlen;
11664374Slars    register char *s = temp, *end = s + STR_LEN;
11674374Slars
11684374Slars    fail_reason = (char *)0;
11694374Slars    string = clean(string, 0);
11704374Slars    len = strlen(string);
11714374Slars    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
11724374Slars
11734374Slars    if (verbose)
11744374Slars	{
11754374Slars	register char *s1;
11764374Slars
11774374Slars	logf("expect (");
11784374Slars
11794374Slars	for (s1 = string; *s1; ++s1)
118011990Speter	    {
11814374Slars	    logf(character(*s1));
118211990Speter	    }
11834374Slars
11844374Slars	logf(")\n");
11854374Slars	}
11864374Slars
11874374Slars    if (len > STR_LEN)
11884374Slars	{
11894374Slars	syslog(LOG_INFO, "expect string is too long");
119011990Speter	exit_code = 1;
11914374Slars	return 0;
11924374Slars	}
11934374Slars
11944374Slars    if (len == 0)
11954374Slars	{
11964374Slars	if (verbose)
11974374Slars	    {
11984374Slars	    syslog(LOG_INFO, "got it");
11994374Slars	    }
12004374Slars
12014374Slars	return (1);
12024374Slars	}
12034374Slars
120411990Speter    alarm(timeout);
120511990Speter    alarmed = 0;
12064374Slars
12074374Slars    while ( ! alarmed && (c = get_char()) >= 0)
12084374Slars	{
120911990Speter	int n, abort_len, report_len;
12104374Slars
12114374Slars	if (verbose)
12124374Slars	    {
12134374Slars	    if (c == '\n')
121411990Speter	        {
12154374Slars		logf("\n");
121611990Speter	        }
12174374Slars	    else
121811990Speter	        {
12194374Slars		logf(character(c));
122011990Speter	        }
12214374Slars	    }
12224374Slars
12234374Slars	*s++ = c;
12244374Slars
12254374Slars	if (s - temp >= len &&
12264374Slars	    c == string[len - 1] &&
12274374Slars	    strncmp(s - len, string, len) == 0)
12284374Slars	    {
12294374Slars	    if (verbose)
12304374Slars		{
12314374Slars		logf(" -- got it\n");
12324374Slars		}
12334374Slars
123411990Speter	    alarm(0);
123511990Speter	    alarmed = 0;
12364374Slars	    return (1);
12374374Slars	    }
12384374Slars
12394374Slars	for (n = 0; n < n_aborts; ++n)
124011990Speter	    {
12414374Slars	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
12424374Slars		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
124311990Speter	        {
12444374Slars		if (verbose)
12454374Slars		    {
12464374Slars		    logf(" -- failed\n");
12474374Slars		    }
124811990Speter
124911990Speter		alarm(0);
125011990Speter		alarmed = 0;
125111990Speter		exit_code = n + 4;
12524374Slars		strcpy(fail_reason = fail_buffer, abort_string[n]);
12534374Slars		return (0);
125411990Speter	        }
125511990Speter	    }
12564374Slars
125711990Speter	if (!report_gathering)
125811990Speter	    {
125911990Speter	    for (n = 0; n < n_reports; ++n)
126011990Speter	        {
126111990Speter		if ((report_string[n] != (char*) NULL) &&
126211990Speter		    s - temp >= (report_len = strlen(report_string[n])) &&
126311990Speter		    strncmp(s - report_len, report_string[n], report_len) == 0)
126411990Speter		    {
126511990Speter		    time_t time_now   = time ((time_t*) NULL);
126611990Speter		    struct tm* tm_now = localtime (&time_now);
126711990Speter
126811990Speter		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
126911990Speter		    strcat (report_buffer, report_string[n]);
127011990Speter
127111990Speter		    report_string[n] = (char *) NULL;
127211990Speter		    report_gathering = 1;
127311990Speter		    break;
127411990Speter		    }
127511990Speter	        }
127611990Speter	    }
127711990Speter	else
127811990Speter	    {
127911990Speter	    if (!iscntrl (c))
128011990Speter	        {
128111990Speter		int rep_len = strlen (report_buffer);
128211990Speter		report_buffer[rep_len]     = c;
128311990Speter		report_buffer[rep_len + 1] = '\0';
128411990Speter	        }
128511990Speter	    else
128611990Speter	        {
128711990Speter		report_gathering = 0;
128811990Speter		fprintf (report_fp, "chat:  %s\n", report_buffer);
128911990Speter	        }
129011990Speter	    }
129111990Speter
12924374Slars	if (s >= end)
12934374Slars	    {
129411990Speter	    strncpy (temp, s - minlen, minlen);
12954374Slars	    s = temp + minlen;
12964374Slars	    }
12974374Slars
12984374Slars	if (alarmed && verbose)
129911990Speter	    {
13004374Slars	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
130111990Speter	    }
13024374Slars	}
13034374Slars
13044374Slars    alarm(0);
130511990Speter
13064374Slars    if (verbose && printed)
13074374Slars	{
13084374Slars	if (alarmed)
130911990Speter	    {
13104374Slars	    logf(" -- read timed out\n");
131111990Speter	    }
13124374Slars	else
13134374Slars	    {
13144374Slars	    logflush();
13154374Slars	    syslog(LOG_INFO, " -- read failed: %m");
13164374Slars	    }
13174374Slars	}
13184374Slars
131911990Speter    exit_code = 3;
132011990Speter    alarmed   = 0;
13214374Slars    return (0);
13224374Slars    }
13234374Slars
132411990Speter#ifdef NO_USLEEP
13254374Slars#include <sys/types.h>
13264374Slars#include <sys/time.h>
13274374Slars
13284374Slars/*
13294374Slars  usleep -- support routine for 4.2BSD system call emulations
13304374Slars  last edit:  29-Oct-1984     D A Gwyn
13314374Slars  */
13324374Slars
13334374Slarsextern int	  select();
13344374Slars
13354374Slarsint
13364374Slarsusleep( usec )				  /* returns 0 if ok, else -1 */
13374374Slars    long		usec;		/* delay in microseconds */
13384374Slars{
13394374Slars    static struct			/* `timeval' */
134011990Speter        {
134111990Speter	long	tv_sec;		/* seconds */
134211990Speter	long	tv_usec;	/* microsecs */
134311990Speter        } delay;	    /* _select() timeout */
13444374Slars
134511990Speter    delay.tv_sec  = usec / 1000000L;
13464374Slars    delay.tv_usec = usec % 1000000L;
13474374Slars
13484374Slars    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
13494374Slars}
13504374Slars#endif
1351