chat.c revision 4374
14374Slars/*
24374Slars *	Chat -- a program for automatic session establishment (i.e. dial
34374Slars *		the phone and log in).
44374Slars *
54374Slars *	This software is in the public domain.
64374Slars *
74374Slars *	Please send all bug reports, requests for information, etc. to:
84374Slars *
94374Slars *		Al Longyear (longyear@netcom.com)
104374Slars *		(I was the last person to change this code.)
114374Slars *
124374Slars *	The original author is:
134374Slars *
144374Slars *		Karl Fox <karl@MorningStar.Com>
154374Slars *		Morning Star Technologies, Inc.
164374Slars *		1760 Zollinger Road
174374Slars *		Columbus, OH  43221
184374Slars *		(614)451-1883
194374Slars */
204374Slars
214374Slarsstatic char rcsid[] = "$Id: chat.c,v 1.4 1994/05/30 00:30:37 paulus Exp $";
224374Slars
234374Slars#include <stdio.h>
244374Slars#include <fcntl.h>
254374Slars#include <signal.h>
264374Slars#include <errno.h>
274374Slars#include <string.h>
284374Slars#include <stdlib.h>
294374Slars#include <sys/types.h>
304374Slars#include <sys/stat.h>
314374Slars#include <syslog.h>
324374Slars
334374Slars#ifndef TERMIO
344374Slars#undef	TERMIOS
354374Slars#define TERMIOS
364374Slars#endif
374374Slars
384374Slars#ifdef sun
394374Slars# if defined(SUNOS) && SUNOS >= 41
404374Slars# ifndef HDB
414374Slars#  define	HDB
424374Slars# endif
434374Slars# endif
444374Slars#endif
454374Slars
464374Slars#ifdef TERMIO
474374Slars#include <termio.h>
484374Slars#endif
494374Slars#ifdef TERMIOS
504374Slars#include <termios.h>
514374Slars#endif
524374Slars
534374Slars#define	STR_LEN	1024
544374Slars
554374Slars#ifndef SIGTYPE
564374Slars#define SIGTYPE void
574374Slars#endif
584374Slars
594374Slars#ifdef __STDC__
604374Slars#undef __P
614374Slars#define __P(x)	x
624374Slars#else
634374Slars#define __P(x)	()
644374Slars#define const
654374Slars#endif
664374Slars
674374Slars/*************** Micro getopt() *********************************************/
684374Slars#define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
694374Slars				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
704374Slars				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
714374Slars#define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
724374Slars				(_O=4,(char*)0):(char*)0)
734374Slars#define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
744374Slars#define	ARG(c,v)	(c?(--c,*v++):(char*)0)
754374Slars
764374Slarsstatic int _O = 0;		/* Internal state */
774374Slars/*************** Micro getopt() *********************************************/
784374Slars
794374Slarschar *program_name;
804374Slars
814374Slars#ifndef LOCK_DIR
824374Slars# ifdef __NetBSD__
834374Slars# define	PIDSTRING
844374Slars# define	LOCK_DIR	"/var/spool/lock"
854374Slars# else
864374Slars#  ifdef HDB
874374Slars#   define	PIDSTRING
884374Slars#   define	LOCK_DIR	"/usr/spool/locks"
894374Slars#  else /* HDB */
904374Slars#   define	LOCK_DIR	"/usr/spool/uucp"
914374Slars#  endif /* HDB */
924374Slars# endif
934374Slars#endif /* LOCK_DIR */
944374Slars
954374Slars#define	MAX_ABORTS		50
964374Slars#define	DEFAULT_CHAT_TIMEOUT	45
974374Slars
984374Slarsint verbose = 0;
994374Slarsint quiet = 0;
1004374Slarschar *lock_file = (char *)0;
1014374Slarschar *chat_file = (char *)0;
1024374Slarsint timeout = DEFAULT_CHAT_TIMEOUT;
1034374Slars
1044374Slarsint have_tty_parameters = 0;
1054374Slars#ifdef TERMIO
1064374Slarsstruct termio saved_tty_parameters;
1074374Slars#endif
1084374Slars#ifdef TERMIOS
1094374Slarsstruct termios saved_tty_parameters;
1104374Slars#endif
1114374Slars
1124374Slarschar *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1134374Slars	fail_buffer[50];
1144374Slarsint n_aborts = 0, abort_next = 0, timeout_next = 0;
1154374Slars
1164374Slarsvoid *dup_mem __P((void *b, size_t c));
1174374Slarsvoid *copy_of __P((char *s));
1184374Slarsvoid usage __P((void));
1194374Slarsvoid logf __P((const char *str));
1204374Slarsvoid logflush __P((void));
1214374Slarsvoid fatal __P((const char *msg));
1224374Slarsvoid sysfatal __P((const char *msg));
1234374SlarsSIGTYPE sigalrm __P((int signo));
1244374SlarsSIGTYPE sigint __P((int signo));
1254374SlarsSIGTYPE sigterm __P((int signo));
1264374SlarsSIGTYPE sighup __P((int signo));
1274374Slarsvoid unalarm __P((void));
1284374Slarsvoid init __P((void));
1294374Slarsvoid set_tty_parameters __P((void));
1304374Slarsvoid break_sequence __P((void));
1314374Slarsvoid terminate __P((int status));
1324374Slarsvoid do_file __P((char *chat_file));
1334374Slarsvoid lock __P((void));
1344374Slarsvoid delay __P((void));
1354374Slarsint  get_string __P((register char *string));
1364374Slarsint  put_string __P((register char *s));
1374374Slarsint  write_char __P((int c));
1384374Slarsint  put_char __P((char c));
1394374Slarsint  get_char __P((void));
1404374Slarsvoid chat_send __P((register char *s));
1414374Slarschar *character __P((char c));
1424374Slarsvoid chat_expect __P((register char *s));
1434374Slarschar *clean __P((register char *s, int sending));
1444374Slarsvoid unlock __P((void));
1454374Slarsvoid lock __P((void));
1464374Slarsvoid break_sequence __P((void));
1474374Slarsvoid terminate __P((int status));
1484374Slarsvoid die __P((void));
1494374Slars
1504374Slarsvoid *dup_mem(b, c)
1514374Slarsvoid *b;
1524374Slarssize_t c;
1534374Slars    {
1544374Slars    void *ans = malloc (c);
1554374Slars    if (!ans)
1564374Slars	fatal ("memory error!\n");
1574374Slars    memcpy (ans, b, c);
1584374Slars    return ans;
1594374Slars    }
1604374Slars
1614374Slarsvoid *copy_of (s)
1624374Slarschar *s;
1634374Slars    {
1644374Slars    return dup_mem (s, strlen (s) + 1);
1654374Slars    }
1664374Slars
1674374Slars/*
1684374Slars *	chat [ -v ] [ -t timeout ] [ -l lock-file ] [ -f chat-file ] \
1694374Slars *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
1704374Slars *
1714374Slars *	Perform a UUCP-dialer-like chat script on stdin and stdout.
1724374Slars */
1734374Slarsint
1744374Slarsmain(argc, argv)
1754374Slarsint argc;
1764374Slarschar **argv;
1774374Slars    {
1784374Slars    int option;
1794374Slars    char *arg;
1804374Slars
1814374Slars    program_name = *argv;
1824374Slars
1834374Slars    while (option = OPTION(argc, argv))
1844374Slars	switch (option)
1854374Slars	    {
1864374Slars	    case 'v':
1874374Slars		++verbose;
1884374Slars		break;
1894374Slars
1904374Slars	    case 'f':
1914374Slars		if (arg = OPTARG(argc, argv))
1924374Slars		    chat_file = copy_of(arg);
1934374Slars		else
1944374Slars		    usage();
1954374Slars
1964374Slars		break;
1974374Slars
1984374Slars	    case 'l':
1994374Slars		if (arg = OPTARG(argc, argv))
2004374Slars		    lock_file = copy_of(arg);
2014374Slars		else
2024374Slars		    usage();
2034374Slars
2044374Slars		break;
2054374Slars
2064374Slars	    case 't':
2074374Slars		if (arg = OPTARG(argc, argv))
2084374Slars		    timeout = atoi(arg);
2094374Slars		else
2104374Slars		    usage();
2114374Slars
2124374Slars		break;
2134374Slars
2144374Slars	    default:
2154374Slars		usage();
2164374Slars	    }
2174374Slars
2184374Slars#ifdef ultrix
2194374Slars    openlog("chat", LOG_PID);
2204374Slars#else
2214374Slars    openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
2224374Slars
2234374Slars    if (verbose) {
2244374Slars	setlogmask(LOG_UPTO(LOG_INFO));
2254374Slars    } else {
2264374Slars	setlogmask(LOG_UPTO(LOG_WARNING));
2274374Slars    }
2284374Slars#endif
2294374Slars
2304374Slars    init();
2314374Slars
2324374Slars    if (chat_file != NULL)
2334374Slars	{
2344374Slars	arg = ARG(argc, argv);
2354374Slars	if (arg != NULL)
2364374Slars	    usage();
2374374Slars	else
2384374Slars	    do_file (chat_file);
2394374Slars	}
2404374Slars    else
2414374Slars	{
2424374Slars	while (arg = ARG(argc, argv))
2434374Slars	    {
2444374Slars	    chat_expect(arg);
2454374Slars
2464374Slars	    if (arg = ARG(argc, argv))
2474374Slars		chat_send(arg);
2484374Slars	    }
2494374Slars	}
2504374Slars
2514374Slars    terminate(0);
2524374Slars    }
2534374Slars
2544374Slars/*
2554374Slars *  Process a chat script when read from a file.
2564374Slars */
2574374Slars
2584374Slarsvoid do_file (chat_file)
2594374Slarschar *chat_file;
2604374Slars    {
2614374Slars    int linect, len, sendflg;
2624374Slars    char *sp, *arg, quote;
2634374Slars    char buf [STR_LEN];
2644374Slars    FILE *cfp;
2654374Slars
2664374Slars    if ((cfp = fopen (chat_file, "r")) == NULL)
2674374Slars	{
2684374Slars	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
2694374Slars	terminate (1);
2704374Slars	}
2714374Slars
2724374Slars    linect = 0;
2734374Slars    sendflg = 0;
2744374Slars
2754374Slars    while (fgets(buf, STR_LEN, cfp) != NULL)
2764374Slars	{
2774374Slars	sp = strchr (buf, '\n');
2784374Slars	if (sp)
2794374Slars	    *sp = '\0';
2804374Slars
2814374Slars	linect++;
2824374Slars	sp = buf;
2834374Slars	while (*sp != '\0')
2844374Slars	    {
2854374Slars	    if (*sp == ' ' || *sp == '\t')
2864374Slars		{
2874374Slars		++sp;
2884374Slars		continue;
2894374Slars		}
2904374Slars
2914374Slars	    if (*sp == '"' || *sp == '\'')
2924374Slars		{
2934374Slars		quote = *sp++;
2944374Slars		arg = sp;
2954374Slars		while (*sp != quote)
2964374Slars		    {
2974374Slars		    if (*sp == '\0')
2984374Slars			{
2994374Slars			syslog (LOG_ERR, "unterminated quote (line %d)",
3004374Slars				linect);
3014374Slars			terminate (1);
3024374Slars			}
3034374Slars
3044374Slars		    if (*sp++ == '\\')
3054374Slars			if (*sp != '\0')
3064374Slars			    ++sp;
3074374Slars		    }
3084374Slars		}
3094374Slars	    else
3104374Slars		{
3114374Slars		arg = sp;
3124374Slars		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
3134374Slars		    ++sp;
3144374Slars		}
3154374Slars
3164374Slars	    if (*sp != '\0')
3174374Slars		*sp++ = '\0';
3184374Slars
3194374Slars	    if (sendflg)
3204374Slars		{
3214374Slars		chat_send (arg);
3224374Slars		}
3234374Slars	    else
3244374Slars		{
3254374Slars		chat_expect (arg);
3264374Slars		}
3274374Slars	    sendflg = !sendflg;
3284374Slars	    }
3294374Slars	}
3304374Slars    fclose (cfp);
3314374Slars    }
3324374Slars
3334374Slars/*
3344374Slars *	We got an error parsing the command line.
3354374Slars */
3364374Slarsvoid usage()
3374374Slars    {
3384374Slars    fprintf(stderr, "\
3394374SlarsUsage: %s [-v] [-l lock-file] [-t timeout] {-f chat-file || chat-script}\n",
3404374Slars	    program_name);
3414374Slars    exit(1);
3424374Slars    }
3434374Slars
3444374Slarschar line[256];
3454374Slarschar *p;
3464374Slars
3474374Slarsvoid logf (str)
3484374Slarsconst char *str;
3494374Slars    {
3504374Slars    p = line + strlen(line);
3514374Slars    strcat (p, str);
3524374Slars
3534374Slars    if (str[strlen(str)-1] == '\n')
3544374Slars	{
3554374Slars	syslog (LOG_INFO, "%s", line);
3564374Slars	line[0] = 0;
3574374Slars	}
3584374Slars    }
3594374Slars
3604374Slarsvoid logflush()
3614374Slars    {
3624374Slars    if (line[0] != 0)
3634374Slars	{
3644374Slars	syslog(LOG_INFO, "%s", line);
3654374Slars	line[0] = 0;
3664374Slars        }
3674374Slars    }
3684374Slars
3694374Slars/*
3704374Slars *	Unlock and terminate with an error.
3714374Slars */
3724374Slarsvoid die()
3734374Slars    {
3744374Slars    unlock();
3754374Slars    terminate(1);
3764374Slars    }
3774374Slars
3784374Slars/*
3794374Slars *	Print an error message and terminate.
3804374Slars */
3814374Slars
3824374Slarsvoid fatal (msg)
3834374Slarsconst char *msg;
3844374Slars    {
3854374Slars    syslog(LOG_ERR, "%s", msg);
3864374Slars    unlock();
3874374Slars    terminate(1);
3884374Slars    }
3894374Slars
3904374Slars/*
3914374Slars *	Print an error message along with the system error message and
3924374Slars *	terminate.
3934374Slars */
3944374Slars
3954374Slarsvoid sysfatal (msg)
3964374Slarsconst char *msg;
3974374Slars    {
3984374Slars    syslog(LOG_ERR, "%s: %m", msg);
3994374Slars    unlock();
4004374Slars    terminate(1);
4014374Slars    }
4024374Slars
4034374Slarsint alarmed = 0;
4044374Slars
4054374SlarsSIGTYPE sigalrm(signo)
4064374Slarsint signo;
4074374Slars    {
4084374Slars    int flags;
4094374Slars
4104374Slars    alarm(1);
4114374Slars    alarmed = 1;		/* Reset alarm to avoid race window */
4124374Slars    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
4134374Slars
4144374Slars    logflush();
4154374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
4164374Slars	sysfatal("Can't get file mode flags on stdin");
4174374Slars    else
4184374Slars	if (fcntl(0, F_SETFL, flags | FNDELAY) == -1)
4194374Slars	    sysfatal("Can't set file mode flags on stdin");
4204374Slars
4214374Slars    if (verbose)
4224374Slars	{
4234374Slars	syslog(LOG_INFO, "alarm");
4244374Slars	}
4254374Slars    }
4264374Slars
4274374Slarsvoid unalarm()
4284374Slars    {
4294374Slars    int flags;
4304374Slars
4314374Slars    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
4324374Slars	sysfatal("Can't get file mode flags on stdin");
4334374Slars    else
4344374Slars	if (fcntl(0, F_SETFL, flags & ~FNDELAY) == -1)
4354374Slars	    sysfatal("Can't set file mode flags on stdin");
4364374Slars    }
4374374Slars
4384374SlarsSIGTYPE sigint(signo)
4394374Slarsint signo;
4404374Slars    {
4414374Slars    fatal("SIGINT");
4424374Slars    }
4434374Slars
4444374SlarsSIGTYPE sigterm(signo)
4454374Slarsint signo;
4464374Slars    {
4474374Slars    fatal("SIGTERM");
4484374Slars    }
4494374Slars
4504374SlarsSIGTYPE sighup(signo)
4514374Slarsint signo;
4524374Slars    {
4534374Slars    fatal("SIGHUP");
4544374Slars    }
4554374Slars
4564374Slarsvoid init()
4574374Slars    {
4584374Slars    signal(SIGINT, sigint);
4594374Slars    signal(SIGTERM, sigterm);
4604374Slars    signal(SIGHUP, sighup);
4614374Slars
4624374Slars    if (lock_file)
4634374Slars	lock();
4644374Slars
4654374Slars    set_tty_parameters();
4664374Slars    signal(SIGALRM, sigalrm);
4674374Slars    alarm(0);
4684374Slars    alarmed = 0;
4694374Slars    }
4704374Slars
4714374Slarsvoid set_tty_parameters()
4724374Slars    {
4734374Slars#ifdef TERMIO
4744374Slars    struct termio t;
4754374Slars
4764374Slars    if (ioctl(0, TCGETA, &t) < 0)
4774374Slars	sysfatal("Can't get terminal parameters");
4784374Slars#endif
4794374Slars#ifdef TERMIOS
4804374Slars    struct termios t;
4814374Slars
4824374Slars    if (tcgetattr(0, &t) < 0)
4834374Slars	sysfatal("Can't get terminal parameters");
4844374Slars#endif
4854374Slars
4864374Slars    saved_tty_parameters = t;
4874374Slars    have_tty_parameters = 1;
4884374Slars
4894374Slars    t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
4904374Slars    t.c_oflag  = 0;
4914374Slars    t.c_lflag  = 0;
4924374Slars    t.c_cc[VERASE] = t.c_cc[VKILL] = 0;
4934374Slars    t.c_cc[VMIN] = 1;
4944374Slars    t.c_cc[VTIME] = 0;
4954374Slars
4964374Slars#ifdef TERMIO
4974374Slars    if (ioctl(0, TCSETA, &t) < 0)
4984374Slars	sysfatal("Can't set terminal parameters");
4994374Slars#endif
5004374Slars#ifdef TERMIOS
5014374Slars    if (tcsetattr(0, TCSANOW, &t) < 0)
5024374Slars	sysfatal("Can't set terminal parameters");
5034374Slars#endif
5044374Slars    }
5054374Slars
5064374Slarsvoid break_sequence()
5074374Slars    {
5084374Slars#ifdef TERMIOS
5094374Slars    tcsendbreak (0, 0);
5104374Slars#endif
5114374Slars    }
5124374Slars
5134374Slarsvoid terminate(status)
5144374Slarsint status;
5154374Slars    {
5164374Slars    if (have_tty_parameters &&
5174374Slars#ifdef TERMIO
5184374Slars        ioctl(0, TCSETA, &saved_tty_parameters) < 0
5194374Slars#endif
5204374Slars#ifdef TERMIOS
5214374Slars	tcsetattr(0, TCSANOW, &saved_tty_parameters) < 0
5224374Slars#endif
5234374Slars	) {
5244374Slars	syslog(LOG_ERR, "Can't restore terminal parameters: %m");
5254374Slars	unlock();
5264374Slars	exit(1);
5274374Slars        }
5284374Slars    exit(status);
5294374Slars    }
5304374Slars
5314374Slars/*
5324374Slars *	Create a lock file for the named lock device
5334374Slars */
5344374Slarsvoid lock()
5354374Slars    {
5364374Slars    int fd, pid;
5374374Slars# ifdef PIDSTRING
5384374Slars    char hdb_lock_buffer[12];
5394374Slars# endif
5404374Slars
5414374Slars    lock_file = strcat(strcat(strcpy(malloc(strlen(LOCK_DIR)
5424374Slars				       + 1 + strlen(lock_file) + 1),
5434374Slars				LOCK_DIR), "/"), lock_file);
5444374Slars
5454374Slars    if ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0)
5464374Slars	{
5474374Slars	char *s = lock_file;
5484374Slars	lock_file = (char *)0;	/* Don't remove someone else's lock file! */
5494374Slars	syslog(LOG_ERR, "Can't get lock file '%s': %m", s);
5504374Slars	die();
5514374Slars	}
5524374Slars
5534374Slars# ifdef PIDSTRING
5544374Slars    sprintf(hdb_lock_buffer, "%10d\n", getpid());
5554374Slars    write(fd, hdb_lock_buffer, 11);
5564374Slars# else
5574374Slars    pid = getpid();
5584374Slars    write(fd, &pid, sizeof pid);
5594374Slars# endif
5604374Slars
5614374Slars    close(fd);
5624374Slars    }
5634374Slars
5644374Slars/*
5654374Slars *	Remove our lockfile
5664374Slars */
5674374Slarsvoid unlock()
5684374Slars    {
5694374Slars    if (lock_file)
5704374Slars	{
5714374Slars	unlink(lock_file);
5724374Slars	lock_file = (char *)0;
5734374Slars	}
5744374Slars    }
5754374Slars
5764374Slars/*
5774374Slars *	'Clean up' this string.
5784374Slars */
5794374Slarschar *clean(s, sending)
5804374Slarsregister char *s;
5814374Slarsint sending;
5824374Slars    {
5834374Slars    char temp[STR_LEN], cur_chr;
5844374Slars    register char *s1;
5854374Slars    int add_return = sending;
5864374Slars#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
5874374Slars
5884374Slars    s1 = temp;
5894374Slars    while (*s)
5904374Slars	{
5914374Slars	cur_chr = *s++;
5924374Slars	if (cur_chr == '^')
5934374Slars	    {
5944374Slars	    cur_chr = *s++;
5954374Slars	    if (cur_chr == '\0')
5964374Slars		{
5974374Slars		*s1++ = '^';
5984374Slars		break;
5994374Slars		}
6004374Slars	    cur_chr &= 0x1F;
6014374Slars	    if (cur_chr != 0)
6024374Slars		*s1++ = cur_chr;
6034374Slars	    continue;
6044374Slars	    }
6054374Slars
6064374Slars	if (cur_chr != '\\')
6074374Slars	    {
6084374Slars	    *s1++ = cur_chr;
6094374Slars	    continue;
6104374Slars	    }
6114374Slars
6124374Slars	cur_chr = *s++;
6134374Slars	if (cur_chr == '\0')
6144374Slars	    {
6154374Slars	    if (sending)
6164374Slars		{
6174374Slars		*s1++ = '\\';
6184374Slars		*s1++ = '\\';
6194374Slars		}
6204374Slars	    break;
6214374Slars	    }
6224374Slars
6234374Slars	switch (cur_chr)
6244374Slars	    {
6254374Slars	case 'b':
6264374Slars	    *s1++ = '\b';
6274374Slars	    break;
6284374Slars
6294374Slars	case 'c':
6304374Slars	    if (sending && *s == '\0')
6314374Slars		add_return = 0;
6324374Slars	    else
6334374Slars		*s1++ = cur_chr;
6344374Slars	    break;
6354374Slars
6364374Slars	case '\\':
6374374Slars	case 'K':
6384374Slars	case 'p':
6394374Slars	case 'd':
6404374Slars	    if (sending)
6414374Slars		*s1++ = '\\';
6424374Slars
6434374Slars	    *s1++ = cur_chr;
6444374Slars	    break;
6454374Slars
6464374Slars	case 'q':
6474374Slars	    quiet = ! quiet;
6484374Slars	    break;
6494374Slars
6504374Slars	case 'r':
6514374Slars	    *s1++ = '\r';
6524374Slars	    break;
6534374Slars
6544374Slars	case 'n':
6554374Slars	    *s1++ = '\n';
6564374Slars	    break;
6574374Slars
6584374Slars	case 's':
6594374Slars	    *s1++ = ' ';
6604374Slars	    break;
6614374Slars
6624374Slars	case 't':
6634374Slars	    *s1++ = '\t';
6644374Slars	    break;
6654374Slars
6664374Slars	case 'N':
6674374Slars	    if (sending)
6684374Slars		{
6694374Slars		*s1++ = '\\';
6704374Slars		*s1++ = '\0';
6714374Slars		}
6724374Slars	    else
6734374Slars		*s1++ = 'N';
6744374Slars	    break;
6754374Slars
6764374Slars	default:
6774374Slars	    if (isoctal (cur_chr))
6784374Slars		{
6794374Slars		cur_chr &= 0x07;
6804374Slars		if (isoctal (*s))
6814374Slars		    {
6824374Slars		    cur_chr <<= 3;
6834374Slars		    cur_chr |= *s++ - '0';
6844374Slars		    if (isoctal (*s))
6854374Slars			{
6864374Slars			cur_chr <<= 3;
6874374Slars			cur_chr |= *s++ - '0';
6884374Slars			}
6894374Slars		    }
6904374Slars
6914374Slars		if (cur_chr != 0 || sending)
6924374Slars		    {
6934374Slars		    if (sending && (cur_chr == '\\' || cur_chr == 0))
6944374Slars			*s1++ = '\\';
6954374Slars		    *s1++ = cur_chr;
6964374Slars		    }
6974374Slars		break;
6984374Slars		}
6994374Slars
7004374Slars	    if (sending)
7014374Slars		*s1++ = '\\';
7024374Slars	    *s1++ = cur_chr;
7034374Slars	    break;
7044374Slars	    }
7054374Slars	}
7064374Slars
7074374Slars    if (add_return)
7084374Slars	*s1++ = '\r';
7094374Slars
7104374Slars    *s1++ = '\0'; /* guarantee closure */
7114374Slars    *s1++ = '\0'; /* terminate the string */
7124374Slars    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7134374Slars    }
7144374Slars
7154374Slars/*
7164374Slars * Process the expect string
7174374Slars */
7184374Slarsvoid chat_expect(s)
7194374Slarsregister char *s;
7204374Slars    {
7214374Slars    if (strcmp(s, "ABORT") == 0)
7224374Slars	{
7234374Slars	++abort_next;
7244374Slars	return;
7254374Slars	}
7264374Slars
7274374Slars    if (strcmp(s, "TIMEOUT") == 0)
7284374Slars	{
7294374Slars	++timeout_next;
7304374Slars	return;
7314374Slars	}
7324374Slars
7334374Slars    while (*s)
7344374Slars	{
7354374Slars	register char *hyphen;
7364374Slars
7374374Slars	for (hyphen = s; *hyphen; ++hyphen)
7384374Slars	    if (*hyphen == '-')
7394374Slars		if (hyphen == s || hyphen[-1] != '\\')
7404374Slars		    break;
7414374Slars
7424374Slars	if (*hyphen == '-')
7434374Slars	    {
7444374Slars	    *hyphen = '\0';
7454374Slars
7464374Slars	    if (get_string(s))
7474374Slars		return;
7484374Slars	    else
7494374Slars		{
7504374Slars		s = hyphen + 1;
7514374Slars
7524374Slars		for (hyphen = s; *hyphen; ++hyphen)
7534374Slars		    if (*hyphen == '-')
7544374Slars			if (hyphen == s || hyphen[-1] != '\\')
7554374Slars			    break;
7564374Slars
7574374Slars		if (*hyphen == '-')
7584374Slars		    {
7594374Slars		    *hyphen = '\0';
7604374Slars
7614374Slars		    chat_send(s);
7624374Slars		    s = hyphen + 1;
7634374Slars		    }
7644374Slars		else
7654374Slars		    {
7664374Slars		    chat_send(s);
7674374Slars		    return;
7684374Slars		    }
7694374Slars		}
7704374Slars	    }
7714374Slars	else
7724374Slars	    if (get_string(s))
7734374Slars		return;
7744374Slars	    else
7754374Slars		{
7764374Slars		if (fail_reason)
7774374Slars		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
7784374Slars		else
7794374Slars		    syslog(LOG_INFO, "Failed");
7804374Slars
7814374Slars		unlock();
7824374Slars		terminate(1);
7834374Slars		}
7844374Slars	}
7854374Slars    }
7864374Slars
7874374Slarschar *character(c)
7884374Slarschar c;
7894374Slars    {
7904374Slars    static char string[10];
7914374Slars    char *meta;
7924374Slars
7934374Slars    meta = (c & 0x80) ? "M-" : "";
7944374Slars    c &= 0x7F;
7954374Slars
7964374Slars    if (c < 32)
7974374Slars	sprintf(string, "%s^%c", meta, (int)c + '@');
7984374Slars    else
7994374Slars	if (c == 127)
8004374Slars	    sprintf(string, "%s^?", meta);
8014374Slars	else
8024374Slars	    sprintf(string, "%s%c", meta, c);
8034374Slars
8044374Slars    return (string);
8054374Slars    }
8064374Slars
8074374Slars/*
8084374Slars *  process the reply string
8094374Slars */
8104374Slarsvoid chat_send (s)
8114374Slarsregister char *s;
8124374Slars    {
8134374Slars    if (abort_next)
8144374Slars	{
8154374Slars	char *s1;
8164374Slars
8174374Slars	abort_next = 0;
8184374Slars
8194374Slars	if (n_aborts >= MAX_ABORTS)
8204374Slars	    fatal("Too many ABORT strings");
8214374Slars
8224374Slars	s1 = clean(s, 0);
8234374Slars
8244374Slars	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
8254374Slars	    {
8264374Slars	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
8274374Slars	    die();
8284374Slars	    }
8294374Slars
8304374Slars	abort_string[n_aborts++] = s1;
8314374Slars
8324374Slars	if (verbose)
8334374Slars	    {
8344374Slars	    logf("abort on (");
8354374Slars
8364374Slars	    for (s1 = s; *s1; ++s1)
8374374Slars		logf(character(*s1));
8384374Slars
8394374Slars	    logf(")\n");
8404374Slars	    }
8414374Slars	}
8424374Slars    else
8434374Slars	if (timeout_next)
8444374Slars	    {
8454374Slars	    timeout_next = 0;
8464374Slars	    timeout = atoi(s);
8474374Slars
8484374Slars	    if (timeout <= 0)
8494374Slars		timeout = DEFAULT_CHAT_TIMEOUT;
8504374Slars
8514374Slars	    if (verbose)
8524374Slars		{
8534374Slars		syslog(LOG_INFO, "timeout set to %d seconds", timeout);
8544374Slars		}
8554374Slars	    }
8564374Slars	else
8574374Slars	    {
8584374Slars	    if (strcmp(s, "EOT") == 0)
8594374Slars		s = "^D\\c";
8604374Slars	    else
8614374Slars		if (strcmp(s, "BREAK") == 0)
8624374Slars		    s = "\\K\\c";
8634374Slars	    if ( ! put_string(s))
8644374Slars		{
8654374Slars		syslog(LOG_INFO, "Failed");
8664374Slars		unlock();
8674374Slars		terminate(1);
8684374Slars		}
8694374Slars	    }
8704374Slars    }
8714374Slars
8724374Slarsint get_char()
8734374Slars    {
8744374Slars    int status;
8754374Slars    char c;
8764374Slars
8774374Slars    status = read(0, &c, 1);
8784374Slars
8794374Slars    switch (status)
8804374Slars	{
8814374Slars	case 1:
8824374Slars	    return ((int)c & 0x7F);
8834374Slars
8844374Slars	default:
8854374Slars	    syslog(LOG_WARNING, "warning: read() on stdin returned %d",
8864374Slars		   status);
8874374Slars
8884374Slars	case -1:
8894374Slars	    if ((status = fcntl(0, F_GETFL, 0)) == -1)
8904374Slars		sysfatal("Can't get file mode flags on stdin");
8914374Slars	    else
8924374Slars		if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
8934374Slars		    sysfatal("Can't set file mode flags on stdin");
8944374Slars
8954374Slars	    return (-1);
8964374Slars	}
8974374Slars    }
8984374Slars
8994374Slarsint put_char(c)
9004374Slarschar c;
9014374Slars    {
9024374Slars    int status;
9034374Slars
9044374Slars    delay();
9054374Slars
9064374Slars    status = write(1, &c, 1);
9074374Slars
9084374Slars    switch (status)
9094374Slars	{
9104374Slars	case 1:
9114374Slars	    return (0);
9124374Slars
9134374Slars	default:
9144374Slars	    syslog(LOG_WARNING, "warning: write() on stdout returned %d",
9154374Slars		   status);
9164374Slars
9174374Slars	case -1:
9184374Slars	    if ((status = fcntl(0, F_GETFL, 0)) == -1)
9194374Slars		sysfatal("Can't get file mode flags on stdin");
9204374Slars	    else
9214374Slars		if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
9224374Slars		    sysfatal("Can't set file mode flags on stdin");
9234374Slars
9244374Slars	    return (-1);
9254374Slars	}
9264374Slars    }
9274374Slars
9284374Slarsint write_char (c)
9294374Slarsint c;
9304374Slars    {
9314374Slars    if (alarmed || put_char(c) < 0)
9324374Slars	{
9334374Slars	extern int errno;
9344374Slars
9354374Slars	alarm(0); alarmed = 0;
9364374Slars
9374374Slars	if (verbose)
9384374Slars	    {
9394374Slars	    if (errno == EINTR || errno == EWOULDBLOCK)
9404374Slars		syslog(LOG_INFO, " -- write timed out");
9414374Slars	    else
9424374Slars		syslog(LOG_INFO, " -- write failed: %m");
9434374Slars	    }
9444374Slars	return (0);
9454374Slars	}
9464374Slars    return (1);
9474374Slars    }
9484374Slars
9494374Slarsint put_string (s)
9504374Slarsregister char *s;
9514374Slars    {
9524374Slars    s = clean(s, 1);
9534374Slars
9544374Slars    if (verbose)
9554374Slars	{
9564374Slars	logf("send (");
9574374Slars
9584374Slars	if (quiet)
9594374Slars	    logf("??????");
9604374Slars	else
9614374Slars	    {
9624374Slars	    register char *s1 = s;
9634374Slars
9644374Slars	    for (s1 = s; *s1; ++s1)
9654374Slars		logf(character(*s1));
9664374Slars	    }
9674374Slars
9684374Slars	logf(")\n");
9694374Slars	}
9704374Slars
9714374Slars    alarm(timeout); alarmed = 0;
9724374Slars
9734374Slars    while (*s)
9744374Slars	{
9754374Slars	register char c = *s++;
9764374Slars
9774374Slars	if (c != '\\')
9784374Slars	    {
9794374Slars	    if (!write_char (c))
9804374Slars		return 0;
9814374Slars	    continue;
9824374Slars	    }
9834374Slars
9844374Slars	c = *s++;
9854374Slars	switch (c)
9864374Slars	    {
9874374Slars	case 'd':
9884374Slars	    sleep(1);
9894374Slars	    break;
9904374Slars
9914374Slars	case 'K':
9924374Slars	    break_sequence();
9934374Slars	    break;
9944374Slars
9954374Slars	case 'p':
9964374Slars	    usleep(10000); /* 1/100th of a second. */
9974374Slars	    break;
9984374Slars
9994374Slars	default:
10004374Slars	    if (!write_char (c))
10014374Slars		return 0;
10024374Slars	    break;
10034374Slars	    }
10044374Slars	}
10054374Slars
10064374Slars    alarm(0);
10074374Slars    alarmed = 0;
10084374Slars    return (1);
10094374Slars    }
10104374Slars
10114374Slars/*
10124374Slars *	'Wait for' this string to appear on this file descriptor.
10134374Slars */
10144374Slarsint get_string(string)
10154374Slarsregister char *string;
10164374Slars    {
10174374Slars    char temp[STR_LEN];
10184374Slars    int c, printed = 0, len, minlen;
10194374Slars    register char *s = temp, *end = s + STR_LEN;
10204374Slars
10214374Slars    fail_reason = (char *)0;
10224374Slars    string = clean(string, 0);
10234374Slars    len = strlen(string);
10244374Slars    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
10254374Slars
10264374Slars    if (verbose)
10274374Slars	{
10284374Slars	register char *s1;
10294374Slars
10304374Slars	logf("expect (");
10314374Slars
10324374Slars	for (s1 = string; *s1; ++s1)
10334374Slars	    logf(character(*s1));
10344374Slars
10354374Slars	logf(")\n");
10364374Slars	}
10374374Slars
10384374Slars    if (len > STR_LEN)
10394374Slars	{
10404374Slars	syslog(LOG_INFO, "expect string is too long");
10414374Slars	return 0;
10424374Slars	}
10434374Slars
10444374Slars    if (len == 0)
10454374Slars	{
10464374Slars	if (verbose)
10474374Slars	    {
10484374Slars	    syslog(LOG_INFO, "got it");
10494374Slars	    }
10504374Slars
10514374Slars	return (1);
10524374Slars	}
10534374Slars
10544374Slars    alarm(timeout); alarmed = 0;
10554374Slars
10564374Slars    while ( ! alarmed && (c = get_char()) >= 0)
10574374Slars	{
10584374Slars	int n, abort_len;
10594374Slars
10604374Slars	if (verbose)
10614374Slars	    {
10624374Slars	    if (c == '\n')
10634374Slars		logf("\n");
10644374Slars	    else
10654374Slars		logf(character(c));
10664374Slars	    }
10674374Slars
10684374Slars	*s++ = c;
10694374Slars
10704374Slars	if (s - temp >= len &&
10714374Slars	    c == string[len - 1] &&
10724374Slars	    strncmp(s - len, string, len) == 0)
10734374Slars	    {
10744374Slars	    if (verbose)
10754374Slars		{
10764374Slars		logf(" -- got it\n");
10774374Slars		}
10784374Slars
10794374Slars	    alarm(0); alarmed = 0;
10804374Slars	    return (1);
10814374Slars	    }
10824374Slars
10834374Slars	for (n = 0; n < n_aborts; ++n)
10844374Slars	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
10854374Slars		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
10864374Slars		{
10874374Slars		if (verbose)
10884374Slars		    {
10894374Slars		    logf(" -- failed\n");
10904374Slars		    }
10914374Slars
10924374Slars		alarm(0); alarmed = 0;
10934374Slars		strcpy(fail_reason = fail_buffer, abort_string[n]);
10944374Slars		return (0);
10954374Slars		}
10964374Slars
10974374Slars	if (s >= end)
10984374Slars	    {
10994374Slars	    strncpy(temp, s - minlen, minlen);
11004374Slars	    s = temp + minlen;
11014374Slars	    }
11024374Slars
11034374Slars	if (alarmed && verbose)
11044374Slars	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
11054374Slars	}
11064374Slars
11074374Slars    alarm(0);
11084374Slars
11094374Slars    if (verbose && printed)
11104374Slars	{
11114374Slars	if (alarmed)
11124374Slars	    logf(" -- read timed out\n");
11134374Slars	else
11144374Slars	    {
11154374Slars	    logflush();
11164374Slars	    syslog(LOG_INFO, " -- read failed: %m");
11174374Slars	    }
11184374Slars	}
11194374Slars
11204374Slars    alarmed = 0;
11214374Slars    return (0);
11224374Slars    }
11234374Slars
11244374Slars#ifdef ultrix
11254374Slars#undef NO_USLEEP
11264374Slars#include <sys/types.h>
11274374Slars#include <sys/time.h>
11284374Slars
11294374Slars/*
11304374Slars  usleep -- support routine for 4.2BSD system call emulations
11314374Slars  last edit:  29-Oct-1984     D A Gwyn
11324374Slars  */
11334374Slars
11344374Slarsextern int	  select();
11354374Slars
11364374Slarsint
11374374Slarsusleep( usec )				  /* returns 0 if ok, else -1 */
11384374Slars    long		usec;		/* delay in microseconds */
11394374Slars{
11404374Slars    static struct			/* `timeval' */
11414374Slars	{
11424374Slars	    long	tv_sec;		/* seconds */
11434374Slars	    long	tv_usec;	/* microsecs */
11444374Slars	}   delay;	    /* _select() timeout */
11454374Slars
11464374Slars    delay.tv_sec = usec / 1000000L;
11474374Slars    delay.tv_usec = usec % 1000000L;
11484374Slars
11494374Slars    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
11504374Slars}
11514374Slars#endif
11524374Slars
11534374Slars/*
11544374Slars *	Delay an amount appropriate for between typed characters.
11554374Slars */
11564374Slarsvoid delay()
11574374Slars    {
11584374Slars# ifdef NO_USLEEP
11594374Slars    register int i;
11604374Slars
11614374Slars    for (i = 0; i < 30000; ++i)		/* ... did we just say appropriate? */
11624374Slars	;
11634374Slars# else /* NO_USLEEP */
11644374Slars    usleep(100);
11654374Slars# endif /* NO_USLEEP */
11664374Slars    }
1167