chat.c revision 11990
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
3411990Speterstatic char rcsid[] = "$Id: chat.c,v 1.1.1.1 1994/11/12 05:25:32 lars 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        {
5454374Slars	sysfatal("Can't get terminal parameters");
54611990Speter        }
5474374Slars
5484374Slars    saved_tty_parameters = t;
54911990Speter    have_tty_parameters  = 1;
5504374Slars
55111990Speter    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
55211990Speter    t.c_oflag      = 0;
55311990Speter    t.c_lflag      = 0;
55411990Speter    t.c_cc[VERASE] =
55511990Speter    t.c_cc[VKILL]  = 0;
55611990Speter    t.c_cc[VMIN]   = 1;
55711990Speter    t.c_cc[VTIME]  = 0;
5584374Slars
55911990Speter    if (set_term_param (&t) < 0)
56011990Speter        {
5614374Slars	sysfatal("Can't set terminal parameters");
56211990Speter        }
5634374Slars#endif
5644374Slars    }
5654374Slars
5664374Slarsvoid break_sequence()
5674374Slars    {
5684374Slars#ifdef TERMIOS
5694374Slars    tcsendbreak (0, 0);
5704374Slars#endif
5714374Slars    }
5724374Slars
5734374Slarsvoid terminate(status)
5744374Slarsint status;
5754374Slars    {
57611990Speter    if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
57711990Speter        {
57811990Speter	if (verbose)
57911990Speter	    {
58011990Speter	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
58111990Speter	    }
58211990Speter	fclose (report_fp);
58311990Speter	report_fp = (FILE*) NULL;
5844374Slars        }
5854374Slars
58611990Speter#if defined(get_term_param)
58711990Speter    if (have_tty_parameters)
58811990Speter        {
58911990Speter	if (set_term_param (&saved_tty_parameters) < 0)
59011990Speter	    {
59111990Speter	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
59211990Speter	    exit(1);
59311990Speter	    }
59411990Speter        }
59511990Speter#endif
5964374Slars
59711990Speter    exit(status);
5984374Slars    }
5994374Slars
6004374Slars/*
6014374Slars *	'Clean up' this string.
6024374Slars */
6034374Slarschar *clean(s, sending)
6044374Slarsregister char *s;
6054374Slarsint sending;
6064374Slars    {
6074374Slars    char temp[STR_LEN], cur_chr;
6084374Slars    register char *s1;
6094374Slars    int add_return = sending;
6104374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
6114374Slars
6124374Slars    s1 = temp;
6134374Slars    while (*s)
6144374Slars	{
6154374Slars	cur_chr = *s++;
6164374Slars	if (cur_chr == '^')
6174374Slars	    {
6184374Slars	    cur_chr = *s++;
6194374Slars	    if (cur_chr == '\0')
6204374Slars		{
6214374Slars		*s1++ = '^';
6224374Slars		break;
6234374Slars		}
6244374Slars	    cur_chr &= 0x1F;
6254374Slars	    if (cur_chr != 0)
62611990Speter	        {
6274374Slars		*s1++ = cur_chr;
62811990Speter	        }
6294374Slars	    continue;
6304374Slars	    }
6314374Slars
6324374Slars	if (cur_chr != '\\')
6334374Slars	    {
6344374Slars	    *s1++ = cur_chr;
6354374Slars	    continue;
6364374Slars	    }
6374374Slars
6384374Slars	cur_chr = *s++;
6394374Slars	if (cur_chr == '\0')
6404374Slars	    {
6414374Slars	    if (sending)
6424374Slars		{
6434374Slars		*s1++ = '\\';
6444374Slars		*s1++ = '\\';
6454374Slars		}
6464374Slars	    break;
6474374Slars	    }
6484374Slars
6494374Slars	switch (cur_chr)
6504374Slars	    {
6514374Slars	case 'b':
6524374Slars	    *s1++ = '\b';
6534374Slars	    break;
6544374Slars
6554374Slars	case 'c':
6564374Slars	    if (sending && *s == '\0')
65711990Speter	        {
6584374Slars		add_return = 0;
65911990Speter	        }
6604374Slars	    else
66111990Speter	        {
6624374Slars		*s1++ = cur_chr;
66311990Speter	        }
6644374Slars	    break;
6654374Slars
6664374Slars	case '\\':
6674374Slars	case 'K':
6684374Slars	case 'p':
6694374Slars	case 'd':
6704374Slars	    if (sending)
67111990Speter	        {
6724374Slars		*s1++ = '\\';
67311990Speter	        }
6744374Slars
6754374Slars	    *s1++ = cur_chr;
6764374Slars	    break;
6774374Slars
6784374Slars	case 'q':
6794374Slars	    quiet = ! quiet;
6804374Slars	    break;
6814374Slars
6824374Slars	case 'r':
6834374Slars	    *s1++ = '\r';
6844374Slars	    break;
6854374Slars
6864374Slars	case 'n':
6874374Slars	    *s1++ = '\n';
6884374Slars	    break;
6894374Slars
6904374Slars	case 's':
6914374Slars	    *s1++ = ' ';
6924374Slars	    break;
6934374Slars
6944374Slars	case 't':
6954374Slars	    *s1++ = '\t';
6964374Slars	    break;
6974374Slars
6984374Slars	case 'N':
6994374Slars	    if (sending)
7004374Slars		{
7014374Slars		*s1++ = '\\';
7024374Slars		*s1++ = '\0';
7034374Slars		}
7044374Slars	    else
70511990Speter	        {
7064374Slars		*s1++ = 'N';
70711990Speter	        }
7084374Slars	    break;
70911990Speter
7104374Slars	default:
7114374Slars	    if (isoctal (cur_chr))
7124374Slars		{
7134374Slars		cur_chr &= 0x07;
7144374Slars		if (isoctal (*s))
7154374Slars		    {
7164374Slars		    cur_chr <<= 3;
7174374Slars		    cur_chr |= *s++ - '0';
7184374Slars		    if (isoctal (*s))
7194374Slars			{
7204374Slars			cur_chr <<= 3;
7214374Slars			cur_chr |= *s++ - '0';
7224374Slars			}
7234374Slars		    }
7244374Slars
7254374Slars		if (cur_chr != 0 || sending)
7264374Slars		    {
7274374Slars		    if (sending && (cur_chr == '\\' || cur_chr == 0))
72811990Speter		        {
7294374Slars			*s1++ = '\\';
73011990Speter		        }
7314374Slars		    *s1++ = cur_chr;
7324374Slars		    }
7334374Slars		break;
7344374Slars		}
7354374Slars
7364374Slars	    if (sending)
73711990Speter	        {
7384374Slars		*s1++ = '\\';
73911990Speter	        }
7404374Slars	    *s1++ = cur_chr;
7414374Slars	    break;
7424374Slars	    }
7434374Slars	}
7444374Slars
7454374Slars    if (add_return)
74611990Speter        {
7474374Slars	*s1++ = '\r';
74811990Speter        }
7494374Slars
7504374Slars    *s1++ = '\0'; /* guarantee closure */
7514374Slars    *s1++ = '\0'; /* terminate the string */
7524374Slars    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7534374Slars    }
7544374Slars
7554374Slars/*
7564374Slars * Process the expect string
7574374Slars */
7584374Slarsvoid chat_expect(s)
7594374Slarsregister char *s;
7604374Slars    {
7614374Slars    if (strcmp(s, "ABORT") == 0)
7624374Slars	{
7634374Slars	++abort_next;
7644374Slars	return;
7654374Slars	}
7664374Slars
76711990Speter    if (strcmp(s, "REPORT") == 0)
76811990Speter	{
76911990Speter	++report_next;
77011990Speter	return;
77111990Speter	}
77211990Speter
7734374Slars    if (strcmp(s, "TIMEOUT") == 0)
7744374Slars	{
7754374Slars	++timeout_next;
7764374Slars	return;
7774374Slars	}
7784374Slars
7794374Slars    while (*s)
7804374Slars	{
7814374Slars	register char *hyphen;
7824374Slars
7834374Slars	for (hyphen = s; *hyphen; ++hyphen)
78411990Speter	    {
7854374Slars	    if (*hyphen == '-')
78611990Speter	        {
7874374Slars		if (hyphen == s || hyphen[-1] != '\\')
78811990Speter		    {
7894374Slars		    break;
79011990Speter		    }
79111990Speter	        }
79211990Speter	    }
79311990Speter
7944374Slars	if (*hyphen == '-')
7954374Slars	    {
7964374Slars	    *hyphen = '\0';
7974374Slars
7984374Slars	    if (get_string(s))
79911990Speter	        {
8004374Slars		return;
80111990Speter	        }
8024374Slars	    else
8034374Slars		{
8044374Slars		s = hyphen + 1;
8054374Slars
8064374Slars		for (hyphen = s; *hyphen; ++hyphen)
80711990Speter		    {
8084374Slars		    if (*hyphen == '-')
80911990Speter		        {
8104374Slars			if (hyphen == s || hyphen[-1] != '\\')
81111990Speter			    {
8124374Slars			    break;
81311990Speter			    }
81411990Speter		        }
81511990Speter		    }
8164374Slars
8174374Slars		if (*hyphen == '-')
8184374Slars		    {
8194374Slars		    *hyphen = '\0';
8204374Slars
8214374Slars		    chat_send(s);
8224374Slars		    s = hyphen + 1;
8234374Slars		    }
8244374Slars		else
8254374Slars		    {
8264374Slars		    chat_send(s);
8274374Slars		    return;
8284374Slars		    }
8294374Slars		}
8304374Slars	    }
8314374Slars	else
83211990Speter	    {
8334374Slars	    if (get_string(s))
83411990Speter	        {
8354374Slars		return;
83611990Speter	        }
8374374Slars	    else
8384374Slars		{
8394374Slars		if (fail_reason)
84011990Speter		    {
8414374Slars		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
84211990Speter		    }
8434374Slars		else
84411990Speter		    {
8454374Slars		    syslog(LOG_INFO, "Failed");
84611990Speter		    }
8474374Slars
84811990Speter		terminate(exit_code);
8494374Slars		}
85011990Speter	    }
8514374Slars	}
8524374Slars    }
8534374Slars
8544374Slarschar *character(c)
85511990Speterint c;
8564374Slars    {
8574374Slars    static char string[10];
8584374Slars    char *meta;
8594374Slars
8604374Slars    meta = (c & 0x80) ? "M-" : "";
8614374Slars    c &= 0x7F;
8624374Slars
8634374Slars    if (c < 32)
86411990Speter        {
8654374Slars	sprintf(string, "%s^%c", meta, (int)c + '@');
86611990Speter        }
8674374Slars    else
86811990Speter        {
8694374Slars	if (c == 127)
87011990Speter	    {
8714374Slars	    sprintf(string, "%s^?", meta);
87211990Speter	    }
8734374Slars	else
87411990Speter	    {
8754374Slars	    sprintf(string, "%s%c", meta, c);
87611990Speter	    }
87711990Speter        }
8784374Slars
8794374Slars    return (string);
8804374Slars    }
8814374Slars
8824374Slars/*
8834374Slars *  process the reply string
8844374Slars */
8854374Slarsvoid chat_send (s)
8864374Slarsregister char *s;
8874374Slars    {
8884374Slars    if (abort_next)
88911990Speter        {
8904374Slars	char *s1;
89111990Speter
8924374Slars	abort_next = 0;
89311990Speter
8944374Slars	if (n_aborts >= MAX_ABORTS)
89511990Speter	    {
8964374Slars	    fatal("Too many ABORT strings");
89711990Speter	    }
89811990Speter
8994374Slars	s1 = clean(s, 0);
90011990Speter
90111990Speter	if (strlen(s1) > strlen(s)
90211990Speter	    || strlen(s1) + 1 > sizeof(fail_buffer))
9034374Slars	    {
9044374Slars	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
9054374Slars	    die();
9064374Slars	    }
9074374Slars
9084374Slars	abort_string[n_aborts++] = s1;
9094374Slars
9104374Slars	if (verbose)
9114374Slars	    {
9124374Slars	    logf("abort on (");
9134374Slars
9144374Slars	    for (s1 = s; *s1; ++s1)
91511990Speter	        {
9164374Slars		logf(character(*s1));
91711990Speter	        }
9184374Slars
9194374Slars	    logf(")\n");
9204374Slars	    }
92111990Speter	return;
9224374Slars	}
92311990Speter
92411990Speter    if (report_next)
92511990Speter        {
92611990Speter	char *s1;
92711990Speter
92811990Speter	report_next = 0;
92911990Speter	if (n_reports >= MAX_REPORTS)
9304374Slars	    {
93111990Speter	    fatal("Too many REPORT strings");
93211990Speter	    }
93311990Speter
93411990Speter	s1 = clean(s, 0);
93511990Speter
93611990Speter	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
93711990Speter	    {
93811990Speter	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
93911990Speter	    die();
94011990Speter	    }
94111990Speter
94211990Speter	report_string[n_reports++] = s1;
94311990Speter
94411990Speter	if (verbose)
94511990Speter	    {
94611990Speter	    logf("report (");
94711990Speter	    s1 = s;
94811990Speter	    while (*s1)
94911990Speter	        {
95011990Speter		logf(character(*s1));
95111990Speter		++s1;
95211990Speter	        }
95311990Speter	    logf(")\n");
95411990Speter	    }
95511990Speter	return;
95611990Speter        }
9574374Slars
95811990Speter    if (timeout_next)
95911990Speter        {
96011990Speter	timeout_next = 0;
96111990Speter	timeout = atoi(s);
96211990Speter
96311990Speter	if (timeout <= 0)
96411990Speter	    {
96511990Speter	    timeout = DEFAULT_CHAT_TIMEOUT;
96611990Speter	    }
9674374Slars
96811990Speter	if (verbose)
96911990Speter	    {
97011990Speter	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
9714374Slars	    }
97211990Speter	return;
97311990Speter        }
97411990Speter
97511990Speter    if (strcmp(s, "EOT") == 0)
97611990Speter        {
97711990Speter	s = "^D\\c";
97811990Speter        }
97911990Speter    else
98011990Speter        {
98111990Speter	if (strcmp(s, "BREAK") == 0)
9824374Slars	    {
98311990Speter	    s = "\\K\\c";
9844374Slars	    }
98511990Speter        }
98611990Speter
98711990Speter    if (!put_string(s))
98811990Speter        {
98911990Speter	syslog(LOG_INFO, "Failed");
99011990Speter	terminate(1);
99111990Speter        }
9924374Slars    }
9934374Slars
9944374Slarsint get_char()
9954374Slars    {
9964374Slars    int status;
9974374Slars    char c;
9984374Slars
9994374Slars    status = read(0, &c, 1);
10004374Slars
10014374Slars    switch (status)
100211990Speter        {
100311990Speter    case 1:
100411990Speter	return ((int)c & 0x7F);
10054374Slars
100611990Speter    default:
100711990Speter	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
100811990Speter	       status);
10094374Slars
101011990Speter    case -1:
101111990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
101211990Speter	    {
101311990Speter	    sysfatal("Can't get file mode flags on stdin");
101411990Speter	    }
101511990Speter	else
101611990Speter	    {
101711990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
101811990Speter	        {
101911990Speter		sysfatal("Can't set file mode flags on stdin");
102011990Speter	        }
102111990Speter	    }
102211990Speter
102311990Speter	return (-1);
102411990Speter        }
10254374Slars    }
10264374Slars
10274374Slarsint put_char(c)
102811990Speterint c;
10294374Slars    {
10304374Slars    int status;
103111990Speter    char ch = c;
10324374Slars
103311990Speter    usleep(10000);		/* inter-character typing delay (?) */
10344374Slars
103511990Speter    status = write(1, &ch, 1);
10364374Slars
10374374Slars    switch (status)
103811990Speter        {
103911990Speter    case 1:
104011990Speter	return (0);
104111990Speter
104211990Speter    default:
104311990Speter	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
104411990Speter	       status);
104511990Speter
104611990Speter    case -1:
104711990Speter	if ((status = fcntl(0, F_GETFL, 0)) == -1)
104811990Speter	    {
104911990Speter	    sysfatal("Can't get file mode flags on stdin");
105011990Speter	    }
105111990Speter	else
105211990Speter	    {
105311990Speter	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
105411990Speter	        {
105511990Speter		sysfatal("Can't set file mode flags on stdin");
105611990Speter	        }
105711990Speter	    }
105811990Speter
105911990Speter	return (-1);
106011990Speter        }
10614374Slars    }
10624374Slars
10634374Slarsint write_char (c)
10644374Slarsint c;
10654374Slars    {
10664374Slars    if (alarmed || put_char(c) < 0)
10674374Slars	{
10684374Slars	extern int errno;
10694374Slars
107011990Speter	alarm(0);
107111990Speter	alarmed = 0;
10724374Slars
10734374Slars	if (verbose)
10744374Slars	    {
10754374Slars	    if (errno == EINTR || errno == EWOULDBLOCK)
107611990Speter	        {
10774374Slars		syslog(LOG_INFO, " -- write timed out");
107811990Speter	        }
10794374Slars	    else
108011990Speter	        {
10814374Slars		syslog(LOG_INFO, " -- write failed: %m");
108211990Speter	        }
10834374Slars	    }
10844374Slars	return (0);
10854374Slars	}
10864374Slars    return (1);
10874374Slars    }
10884374Slars
10894374Slarsint put_string (s)
10904374Slarsregister char *s;
10914374Slars    {
10924374Slars    s = clean(s, 1);
10934374Slars
10944374Slars    if (verbose)
10954374Slars	{
10964374Slars	logf("send (");
10974374Slars
10984374Slars	if (quiet)
109911990Speter	    {
11004374Slars	    logf("??????");
110111990Speter	    }
11024374Slars	else
11034374Slars	    {
11044374Slars	    register char *s1 = s;
11054374Slars
11064374Slars	    for (s1 = s; *s1; ++s1)
110711990Speter	        {
11084374Slars		logf(character(*s1));
110911990Speter	        }
11104374Slars	    }
11114374Slars
11124374Slars	logf(")\n");
11134374Slars	}
11144374Slars
11154374Slars    alarm(timeout); alarmed = 0;
11164374Slars
11174374Slars    while (*s)
11184374Slars	{
11194374Slars	register char c = *s++;
11204374Slars
11214374Slars	if (c != '\\')
11224374Slars	    {
11234374Slars	    if (!write_char (c))
112411990Speter	        {
11254374Slars		return 0;
112611990Speter	        }
11274374Slars	    continue;
11284374Slars	    }
11294374Slars
11304374Slars	c = *s++;
11314374Slars	switch (c)
11324374Slars	    {
11334374Slars	case 'd':
11344374Slars	    sleep(1);
11354374Slars	    break;
11364374Slars
11374374Slars	case 'K':
11384374Slars	    break_sequence();
11394374Slars	    break;
11404374Slars
11414374Slars	case 'p':
114211990Speter	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
11434374Slars	    break;
11444374Slars
11454374Slars	default:
11464374Slars	    if (!write_char (c))
11474374Slars		return 0;
11484374Slars	    break;
11494374Slars	    }
11504374Slars	}
11514374Slars
11524374Slars    alarm(0);
11534374Slars    alarmed = 0;
11544374Slars    return (1);
11554374Slars    }
11564374Slars
11574374Slars/*
11584374Slars *	'Wait for' this string to appear on this file descriptor.
11594374Slars */
11604374Slarsint get_string(string)
11614374Slarsregister char *string;
11624374Slars    {
11634374Slars    char temp[STR_LEN];
11644374Slars    int c, printed = 0, len, minlen;
11654374Slars    register char *s = temp, *end = s + STR_LEN;
11664374Slars
11674374Slars    fail_reason = (char *)0;
11684374Slars    string = clean(string, 0);
11694374Slars    len = strlen(string);
11704374Slars    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
11714374Slars
11724374Slars    if (verbose)
11734374Slars	{
11744374Slars	register char *s1;
11754374Slars
11764374Slars	logf("expect (");
11774374Slars
11784374Slars	for (s1 = string; *s1; ++s1)
117911990Speter	    {
11804374Slars	    logf(character(*s1));
118111990Speter	    }
11824374Slars
11834374Slars	logf(")\n");
11844374Slars	}
11854374Slars
11864374Slars    if (len > STR_LEN)
11874374Slars	{
11884374Slars	syslog(LOG_INFO, "expect string is too long");
118911990Speter	exit_code = 1;
11904374Slars	return 0;
11914374Slars	}
11924374Slars
11934374Slars    if (len == 0)
11944374Slars	{
11954374Slars	if (verbose)
11964374Slars	    {
11974374Slars	    syslog(LOG_INFO, "got it");
11984374Slars	    }
11994374Slars
12004374Slars	return (1);
12014374Slars	}
12024374Slars
120311990Speter    alarm(timeout);
120411990Speter    alarmed = 0;
12054374Slars
12064374Slars    while ( ! alarmed && (c = get_char()) >= 0)
12074374Slars	{
120811990Speter	int n, abort_len, report_len;
12094374Slars
12104374Slars	if (verbose)
12114374Slars	    {
12124374Slars	    if (c == '\n')
121311990Speter	        {
12144374Slars		logf("\n");
121511990Speter	        }
12164374Slars	    else
121711990Speter	        {
12184374Slars		logf(character(c));
121911990Speter	        }
12204374Slars	    }
12214374Slars
12224374Slars	*s++ = c;
12234374Slars
12244374Slars	if (s - temp >= len &&
12254374Slars	    c == string[len - 1] &&
12264374Slars	    strncmp(s - len, string, len) == 0)
12274374Slars	    {
12284374Slars	    if (verbose)
12294374Slars		{
12304374Slars		logf(" -- got it\n");
12314374Slars		}
12324374Slars
123311990Speter	    alarm(0);
123411990Speter	    alarmed = 0;
12354374Slars	    return (1);
12364374Slars	    }
12374374Slars
12384374Slars	for (n = 0; n < n_aborts; ++n)
123911990Speter	    {
12404374Slars	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
12414374Slars		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
124211990Speter	        {
12434374Slars		if (verbose)
12444374Slars		    {
12454374Slars		    logf(" -- failed\n");
12464374Slars		    }
124711990Speter
124811990Speter		alarm(0);
124911990Speter		alarmed = 0;
125011990Speter		exit_code = n + 4;
12514374Slars		strcpy(fail_reason = fail_buffer, abort_string[n]);
12524374Slars		return (0);
125311990Speter	        }
125411990Speter	    }
12554374Slars
125611990Speter	if (!report_gathering)
125711990Speter	    {
125811990Speter	    for (n = 0; n < n_reports; ++n)
125911990Speter	        {
126011990Speter		if ((report_string[n] != (char*) NULL) &&
126111990Speter		    s - temp >= (report_len = strlen(report_string[n])) &&
126211990Speter		    strncmp(s - report_len, report_string[n], report_len) == 0)
126311990Speter		    {
126411990Speter		    time_t time_now   = time ((time_t*) NULL);
126511990Speter		    struct tm* tm_now = localtime (&time_now);
126611990Speter
126711990Speter		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
126811990Speter		    strcat (report_buffer, report_string[n]);
126911990Speter
127011990Speter		    report_string[n] = (char *) NULL;
127111990Speter		    report_gathering = 1;
127211990Speter		    break;
127311990Speter		    }
127411990Speter	        }
127511990Speter	    }
127611990Speter	else
127711990Speter	    {
127811990Speter	    if (!iscntrl (c))
127911990Speter	        {
128011990Speter		int rep_len = strlen (report_buffer);
128111990Speter		report_buffer[rep_len]     = c;
128211990Speter		report_buffer[rep_len + 1] = '\0';
128311990Speter	        }
128411990Speter	    else
128511990Speter	        {
128611990Speter		report_gathering = 0;
128711990Speter		fprintf (report_fp, "chat:  %s\n", report_buffer);
128811990Speter	        }
128911990Speter	    }
129011990Speter
12914374Slars	if (s >= end)
12924374Slars	    {
129311990Speter	    strncpy (temp, s - minlen, minlen);
12944374Slars	    s = temp + minlen;
12954374Slars	    }
12964374Slars
12974374Slars	if (alarmed && verbose)
129811990Speter	    {
12994374Slars	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
130011990Speter	    }
13014374Slars	}
13024374Slars
13034374Slars    alarm(0);
130411990Speter
13054374Slars    if (verbose && printed)
13064374Slars	{
13074374Slars	if (alarmed)
130811990Speter	    {
13094374Slars	    logf(" -- read timed out\n");
131011990Speter	    }
13114374Slars	else
13124374Slars	    {
13134374Slars	    logflush();
13144374Slars	    syslog(LOG_INFO, " -- read failed: %m");
13154374Slars	    }
13164374Slars	}
13174374Slars
131811990Speter    exit_code = 3;
131911990Speter    alarmed   = 0;
13204374Slars    return (0);
13214374Slars    }
13224374Slars
132311990Speter#ifdef NO_USLEEP
13244374Slars#include <sys/types.h>
13254374Slars#include <sys/time.h>
13264374Slars
13274374Slars/*
13284374Slars  usleep -- support routine for 4.2BSD system call emulations
13294374Slars  last edit:  29-Oct-1984     D A Gwyn
13304374Slars  */
13314374Slars
13324374Slarsextern int	  select();
13334374Slars
13344374Slarsint
13354374Slarsusleep( usec )				  /* returns 0 if ok, else -1 */
13364374Slars    long		usec;		/* delay in microseconds */
13374374Slars{
13384374Slars    static struct			/* `timeval' */
133911990Speter        {
134011990Speter	long	tv_sec;		/* seconds */
134111990Speter	long	tv_usec;	/* microsecs */
134211990Speter        } delay;	    /* _select() timeout */
13434374Slars
134411990Speter    delay.tv_sec  = usec / 1000000L;
13454374Slars    delay.tv_usec = usec % 1000000L;
13464374Slars
13474374Slars    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
13484374Slars}
13494374Slars#endif
1350