chat.c revision 32069
1/*
2 *	Chat -- a program for automatic session establishment (i.e. dial
3 *		the phone and log in).
4 *
5 * Standard termination codes:
6 *  0 - successful completion of the script
7 *  1 - invalid argument, expect string too large, etc.
8 *  2 - error on an I/O operation or fatal error condtion.
9 *  3 - timeout waiting for a simple string.
10 *  4 - the first string declared as "ABORT"
11 *  5 - the second string declared as "ABORT"
12 *  6 - ... and so on for successive ABORT strings.
13 *
14 *	This software is in the public domain.
15 *
16 * -----------------
17 *
18 *	Added SAY keyword to send output to stderr.
19 *      This allows to turn ECHO OFF and to output specific, user selected,
20 *      text to give progress messages. This best works when stderr
21 *      exists (i.e.: pppd in nodetach mode).
22 *
23 * 	Added HANGUP directives to allow for us to be called
24 *      back. When HANGUP is set to NO, chat will not hangup at HUP signal.
25 *      We rely on timeouts in that case.
26 *
27 *      Added CLR_ABORT to clear previously set ABORT string. This has been
28 *      dictated by the HANGUP above as "NO CARRIER" (for example) must be
29 *      an ABORT condition until we know the other host is going to close
30 *      the connection for call back. As soon as we have completed the
31 *      first stage of the call back sequence, "NO CARRIER" is a valid, non
32 *      fatal string. As soon as we got called back (probably get "CONNECT"),
33 *      we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
34 *      Note that CLR_ABORT packs the abort_strings[] array so that we do not
35 *      have unused entries not being reclaimed.
36 *
37 *      In the same vein as above, added CLR_REPORT keyword.
38 *
39 *      Allow for comments. Line starting with '#' are comments and are
40 *      ignored. If a '#' is to be expected as the first character, the
41 *      expect string must be quoted.
42 *
43 *
44 *		Francis Demierre <Francis@SwissMail.Com>
45 * 		Thu May 15 17:15:40 MET DST 1997
46 *
47 *
48 *      Added -r "report file" switch & REPORT keyword.
49 *              Robert Geer <bgeer@xmission.com>
50 *
51 *
52 *	Added -e "echo" switch & ECHO keyword
53 *		Dick Streefland <dicks@tasking.nl>
54 *
55 *
56 *	Considerable updates and modifications by
57 *		Al Longyear <longyear@pobox.com>
58 *		Paul Mackerras <paulus@cs.anu.edu.au>
59 *
60 *
61 *	The original author is:
62 *
63 *		Karl Fox <karl@MorningStar.Com>
64 *		Morning Star Technologies, Inc.
65 *		1760 Zollinger Road
66 *		Columbus, OH  43221
67 *		(614)451-1883
68 *
69 *
70 */
71
72#ifndef lint
73static char rcsid[] = "$Id: chat.c,v 1.9 1997/08/22 15:24:36 peter Exp $";
74#endif
75
76#include <stdio.h>
77#include <ctype.h>
78#include <time.h>
79#include <fcntl.h>
80#include <signal.h>
81#include <errno.h>
82#include <string.h>
83#include <stdlib.h>
84#include <unistd.h>
85#include <sys/types.h>
86#include <sys/stat.h>
87#include <syslog.h>
88
89#ifndef TERMIO
90#undef	TERMIOS
91#define TERMIOS
92#endif
93
94#ifdef TERMIO
95#include <termio.h>
96#endif
97#ifdef TERMIOS
98#include <termios.h>
99#endif
100
101#define	STR_LEN	1024
102
103#ifndef SIGTYPE
104#define SIGTYPE void
105#endif
106
107#ifdef __STDC__
108#undef __P
109#define __P(x)	x
110#else
111#define __P(x)	()
112#define const
113#endif
114
115#ifndef O_NONBLOCK
116#define O_NONBLOCK	O_NDELAY
117#endif
118
119/*************** Micro getopt() *********************************************/
120#define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
121				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
122				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
123#define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
124				(_O=4,(char*)0):(char*)0)
125#define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
126#define	ARG(c,v)	(c?(--c,*v++):(char*)0)
127
128static int _O = 0;		/* Internal state */
129/*************** Micro getopt() *********************************************/
130
131#define	MAX_ABORTS		50
132#define	MAX_REPORTS		50
133#define	DEFAULT_CHAT_TIMEOUT	45
134
135int echo          = 0;
136int verbose       = 0;
137int Verbose       = 0;
138int quiet         = 0;
139int report        = 0;
140int exit_code     = 0;
141FILE* report_fp   = (FILE *) 0;
142char *report_file = (char *) 0;
143char *chat_file   = (char *) 0;
144int timeout       = DEFAULT_CHAT_TIMEOUT;
145
146int have_tty_parameters = 0;
147
148#ifdef TERMIO
149#define term_parms struct termio
150#define get_term_param(param) ioctl(0, TCGETA, param)
151#define set_term_param(param) ioctl(0, TCSETA, param)
152struct termio saved_tty_parameters;
153#endif
154
155#ifdef TERMIOS
156#define term_parms struct termios
157#define get_term_param(param) tcgetattr(0, param)
158#define set_term_param(param) tcsetattr(0, TCSANOW, param)
159struct termios saved_tty_parameters;
160#endif
161
162char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
163	fail_buffer[50];
164int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
165int clear_abort_next = 0;
166
167char *report_string[MAX_REPORTS] ;
168char  report_buffer[50] ;
169int n_reports = 0, report_next = 0, report_gathering = 0 ;
170int clear_report_next = 0;
171
172int say_next = 0, hup_next = 0;
173
174void *dup_mem __P((void *b, size_t c));
175void *copy_of __P((char *s));
176static void usage __P((void));
177void logf __P((const char *str));
178void logflush __P((void));
179void fatal __P((const char *msg));
180void sysfatal __P((const char *msg));
181SIGTYPE sigalrm __P((int signo));
182SIGTYPE sigint __P((int signo));
183SIGTYPE sigterm __P((int signo));
184SIGTYPE sighup __P((int signo));
185void unalarm __P((void));
186void init __P((void));
187void set_tty_parameters __P((void));
188void echo_stderr __P((int));
189void break_sequence __P((void));
190void terminate __P((int status));
191void do_file __P((char *chat_file));
192int  get_string __P((register char *string));
193int  put_string __P((register char *s));
194int  write_char __P((int c));
195int  put_char __P((int c));
196int  get_char __P((void));
197void chat_send __P((register char *s));
198char *character __P((int c));
199void chat_expect __P((register char *s));
200char *clean __P((register char *s, int sending));
201void break_sequence __P((void));
202void terminate __P((int status));
203void die __P((void));
204void pack_array __P((char **array, int end));
205char *expect_strtok __P((char *, char *));
206
207int main __P((int, char *[]));
208
209void *dup_mem(b, c)
210void *b;
211size_t c;
212    {
213    void *ans = malloc (c);
214    if (!ans)
215        {
216	fatal ("memory error!\n");
217        }
218    memcpy (ans, b, c);
219    return ans;
220    }
221
222void *copy_of (s)
223char *s;
224    {
225    return dup_mem (s, strlen (s) + 1);
226    }
227
228/*
229 *	chat [ -v ] [ -t timeout ] [ -f chat-file ] [ -r report-file ] \
230 *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
231 *
232 *	Perform a UUCP-dialer-like chat script on stdin and stdout.
233 */
234int
235main(argc, argv)
236int argc;
237char **argv;
238    {
239    int option;
240    char *arg;
241
242    tzset();
243
244    while ((option = OPTION(argc, argv)) != 0)
245        {
246	switch (option)
247	    {
248	    case 'e':
249		++echo;
250		break;
251
252	    case 'v':
253		++verbose;
254		break;
255
256	    case 'V':
257	        ++Verbose;
258		break;
259
260	    case 'f':
261		if ((arg = OPTARG(argc, argv)) != NULL)
262		    {
263		    chat_file = copy_of(arg);
264		    }
265		else
266		    {
267		    usage();
268		    }
269		break;
270
271	    case 't':
272		if ((arg = OPTARG(argc, argv)) != NULL)
273		    {
274		    timeout = atoi(arg);
275		    }
276		else
277		    {
278		    usage();
279		    }
280		break;
281
282	    case 'r':
283		arg = OPTARG (argc, argv);
284		if (arg)
285		    {
286		    if (report_fp != NULL)
287		        {
288			fclose (report_fp);
289		        }
290		    report_file = copy_of (arg);
291		    report_fp   = fopen (report_file, "a");
292		    if (report_fp != NULL)
293		        {
294			if (verbose)
295			    {
296			    fprintf (report_fp, "Opening \"%s\"...\n",
297				     report_file);
298			    }
299			report = 1;
300		        }
301		    }
302		break;
303
304	    default:
305		usage();
306		break;
307	    }
308      }
309/*
310 * Default the report file to the stderr location
311 */
312    if (report_fp == NULL)
313        {
314	report_fp = stderr;
315        }
316
317#ifdef ultrix
318    openlog("chat", LOG_PID);
319#else
320    openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
321
322    if (verbose)
323        {
324	setlogmask(LOG_UPTO(LOG_INFO));
325        }
326    else
327        {
328	setlogmask(LOG_UPTO(LOG_WARNING));
329        }
330#endif
331
332    init();
333
334    if (chat_file != NULL)
335	{
336	arg = ARG(argc, argv);
337	if (arg != NULL)
338	    {
339	    usage();
340	    }
341	else
342	    {
343	    do_file (chat_file);
344	    }
345	}
346    else
347	{
348	while ((arg = ARG(argc, argv)) != NULL)
349	    {
350	    chat_expect(arg);
351
352	    if ((arg = ARG(argc, argv)) != NULL)
353	        {
354		chat_send(arg);
355	        }
356	    }
357	}
358
359    terminate(0);
360    return 0;
361    }
362
363/*
364 *  Process a chat script when read from a file.
365 */
366
367void do_file (chat_file)
368char *chat_file;
369    {
370    int linect, sendflg;
371    char *sp, *arg, quote;
372    char buf [STR_LEN];
373    FILE *cfp;
374
375    cfp = fopen (chat_file, "r");
376    if (cfp == NULL)
377	{
378	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
379	terminate (1);
380	}
381
382    linect = 0;
383    sendflg = 0;
384
385    while (fgets(buf, STR_LEN, cfp) != NULL)
386	{
387	sp = strchr (buf, '\n');
388	if (sp)
389	    {
390	    *sp = '\0';
391	    }
392
393	linect++;
394	sp = buf;
395
396        /* lines starting with '#' are comments. If a real '#'
397           is to be expected, it should be quoted .... */
398        if ( *sp == '#' ) continue;
399
400	while (*sp != '\0')
401	    {
402	    if (*sp == ' ' || *sp == '\t')
403		{
404		++sp;
405		continue;
406		}
407
408	    if (*sp == '"' || *sp == '\'')
409		{
410		quote = *sp++;
411		arg = sp;
412		while (*sp != quote)
413		    {
414		    if (*sp == '\0')
415			{
416			syslog (LOG_ERR, "unterminated quote (line %d)",
417				linect);
418			terminate (1);
419			}
420
421		    if (*sp++ == '\\')
422		        {
423			if (*sp != '\0')
424			    {
425			    ++sp;
426			    }
427		        }
428		    }
429		}
430	    else
431		{
432		arg = sp;
433		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
434		    {
435		    ++sp;
436		    }
437		}
438
439	    if (*sp != '\0')
440	        {
441		*sp++ = '\0';
442	        }
443
444	    if (sendflg)
445		{
446		chat_send (arg);
447		}
448	    else
449		{
450		chat_expect (arg);
451		}
452	    sendflg = !sendflg;
453	    }
454	}
455    fclose (cfp);
456    }
457
458/*
459 *	We got an error parsing the command line.
460 */
461static void
462usage()
463    {
464    fprintf(stderr, "%s %s\n",
465		"usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file]",
466		"{-f chat-file | chat-script}");
467    exit(1);
468    }
469
470char line[256];
471char *p;
472
473void logf (str)
474const char *str;
475{
476    int l = strlen(line);
477
478    if (l + strlen(str) >= sizeof(line)) {
479	syslog(LOG_INFO, "%s", line);
480	l = 0;
481    }
482    strcpy(line + l, str);
483
484    if (str[strlen(str)-1] == '\n') {
485	syslog (LOG_INFO, "%s", line);
486	line[0] = 0;
487    }
488}
489
490void logflush()
491    {
492    if (line[0] != 0)
493	{
494	syslog(LOG_INFO, "%s", line);
495	line[0] = 0;
496        }
497    }
498
499/*
500 *	Terminate with an error.
501 */
502void die()
503    {
504    terminate(1);
505    }
506
507/*
508 *	Print an error message and terminate.
509 */
510
511void fatal (msg)
512const char *msg;
513    {
514    syslog(LOG_ERR, "%s", msg);
515    terminate(2);
516    }
517
518/*
519 *	Print an error message along with the system error message and
520 *	terminate.
521 */
522
523void sysfatal (msg)
524const char *msg;
525    {
526    syslog(LOG_ERR, "%s: %m", msg);
527    terminate(2);
528    }
529
530int alarmed = 0;
531
532SIGTYPE sigalrm(signo)
533int signo;
534    {
535    int flags;
536
537    alarm(1);
538    alarmed = 1;		/* Reset alarm to avoid race window */
539    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
540
541    logflush();
542    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
543        {
544	sysfatal("Can't get file mode flags on stdin");
545        }
546    else
547        {
548	if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
549	    {
550	    sysfatal("Can't set file mode flags on stdin");
551	    }
552        }
553
554    if (verbose)
555	{
556	syslog(LOG_INFO, "alarm");
557	}
558    }
559
560void unalarm()
561    {
562    int flags;
563
564    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
565        {
566	sysfatal("Can't get file mode flags on stdin");
567        }
568    else
569        {
570	if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
571	    {
572	    sysfatal("Can't set file mode flags on stdin");
573	    }
574        }
575    }
576
577SIGTYPE sigint(signo)
578int signo;
579    {
580    fatal("SIGINT");
581    }
582
583SIGTYPE sigterm(signo)
584int signo;
585    {
586    fatal("SIGTERM");
587    }
588
589SIGTYPE sighup(signo)
590int signo;
591    {
592    fatal("SIGHUP");
593    }
594
595void init()
596    {
597    signal(SIGINT, sigint);
598    signal(SIGTERM, sigterm);
599    signal(SIGHUP, sighup);
600
601    set_tty_parameters();
602    signal(SIGALRM, sigalrm);
603    alarm(0);
604    alarmed = 0;
605    }
606
607void set_tty_parameters()
608    {
609#if defined(get_term_param)
610    term_parms t;
611
612    if (get_term_param (&t) < 0)
613        {
614	have_tty_parameters = 0;
615	return;
616        }
617
618    saved_tty_parameters = t;
619    have_tty_parameters  = 1;
620
621    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
622    t.c_oflag      = 0;
623    t.c_lflag      = 0;
624    t.c_cc[VERASE] =
625    t.c_cc[VKILL]  = 0;
626    t.c_cc[VMIN]   = 1;
627    t.c_cc[VTIME]  = 0;
628
629    if (set_term_param (&t) < 0)
630        {
631	sysfatal("Can't set terminal parameters");
632        }
633#endif
634    }
635
636void break_sequence()
637    {
638#ifdef TERMIOS
639    tcsendbreak (0, 0);
640#endif
641    }
642
643void terminate(status)
644int status;
645    {
646    echo_stderr(-1);
647    if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
648	{
649/*
650 * Allow the last of the report string to be gathered before we terminate.
651 */
652	if (report_gathering) {
653	    int c, rep_len;
654
655	    rep_len = strlen(report_buffer);
656	    while (rep_len + 1 <= sizeof(report_buffer)) {
657		alarm(1);
658		c = get_char();
659		alarm(0);
660		if (c < 0 || iscntrl(c))
661		    break;
662		report_buffer[rep_len] = c;
663		++rep_len;
664	    }
665	    report_buffer[rep_len] = 0;
666	    fprintf (report_fp, "chat:  %s\n", report_buffer);
667	}
668	if (verbose)
669	    {
670	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
671	    }
672	fclose (report_fp);
673	report_fp = (FILE *) NULL;
674        }
675
676#if defined(get_term_param)
677    if (have_tty_parameters)
678        {
679	if (set_term_param (&saved_tty_parameters) < 0)
680	    {
681	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
682	    exit(1);
683	    }
684        }
685#endif
686
687    exit(status);
688    }
689
690/*
691 *	'Clean up' this string.
692 */
693char *clean(s, sending)
694register char *s;
695int sending;
696    {
697    char temp[STR_LEN], cur_chr;
698    register char *s1;
699    int add_return = sending;
700#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
701
702    s1 = temp;
703    while (*s)
704	{
705	cur_chr = *s++;
706	if (cur_chr == '^')
707	    {
708	    cur_chr = *s++;
709	    if (cur_chr == '\0')
710		{
711		*s1++ = '^';
712		break;
713		}
714	    cur_chr &= 0x1F;
715	    if (cur_chr != 0)
716	        {
717		*s1++ = cur_chr;
718	        }
719	    continue;
720	    }
721
722	if (cur_chr != '\\')
723	    {
724	    *s1++ = cur_chr;
725	    continue;
726	    }
727
728	cur_chr = *s++;
729	if (cur_chr == '\0')
730	    {
731	    if (sending)
732		{
733		*s1++ = '\\';
734		*s1++ = '\\';
735		}
736	    break;
737	    }
738
739	switch (cur_chr)
740	    {
741	case 'b':
742	    *s1++ = '\b';
743	    break;
744
745	case 'c':
746	    if (sending && *s == '\0')
747	        {
748		add_return = 0;
749	        }
750	    else
751	        {
752		*s1++ = cur_chr;
753	        }
754	    break;
755
756	case '\\':
757	case 'K':
758	case 'p':
759	case 'd':
760	    if (sending)
761	        {
762		*s1++ = '\\';
763	        }
764
765	    *s1++ = cur_chr;
766	    break;
767
768	case 'q':
769	    quiet = 1;
770	    break;
771
772	case 'r':
773	    *s1++ = '\r';
774	    break;
775
776	case 'n':
777	    *s1++ = '\n';
778	    break;
779
780	case 's':
781	    *s1++ = ' ';
782	    break;
783
784	case 't':
785	    *s1++ = '\t';
786	    break;
787
788	case 'N':
789	    if (sending)
790		{
791		*s1++ = '\\';
792		*s1++ = '\0';
793		}
794	    else
795	        {
796		*s1++ = 'N';
797	        }
798	    break;
799
800	default:
801	    if (isoctal (cur_chr))
802		{
803		cur_chr &= 0x07;
804		if (isoctal (*s))
805		    {
806		    cur_chr <<= 3;
807		    cur_chr |= *s++ - '0';
808		    if (isoctal (*s))
809			{
810			cur_chr <<= 3;
811			cur_chr |= *s++ - '0';
812			}
813		    }
814
815		if (cur_chr != 0 || sending)
816		    {
817		    if (sending && (cur_chr == '\\' || cur_chr == 0))
818		        {
819			*s1++ = '\\';
820		        }
821		    *s1++ = cur_chr;
822		    }
823		break;
824		}
825
826	    if (sending)
827	        {
828		*s1++ = '\\';
829	        }
830	    *s1++ = cur_chr;
831	    break;
832	    }
833	}
834
835    if (add_return)
836        {
837	*s1++ = '\r';
838        }
839
840    *s1++ = '\0'; /* guarantee closure */
841    *s1++ = '\0'; /* terminate the string */
842    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
843    }
844
845/*
846 * A modified version of 'strtok'. This version skips \ sequences.
847 */
848
849char *expect_strtok (s, term)
850char *s, *term;
851    {
852    static  char *str   = "";
853    int	    escape_flag = 0;
854    char   *result;
855/*
856 * If a string was specified then do initial processing.
857 */
858    if (s)
859	{
860	str = s;
861	}
862/*
863 * If this is the escape flag then reset it and ignore the character.
864 */
865    if (*str)
866        {
867	result = str;
868        }
869    else
870        {
871	result = (char *) 0;
872        }
873
874    while (*str)
875	{
876	if (escape_flag)
877	    {
878	    escape_flag = 0;
879	    ++str;
880	    continue;
881	    }
882
883	if (*str == '\\')
884	    {
885	    ++str;
886	    escape_flag = 1;
887	    continue;
888	    }
889/*
890 * If this is not in the termination string, continue.
891 */
892	if (strchr (term, *str) == (char *) 0)
893	    {
894	    ++str;
895	    continue;
896	    }
897/*
898 * This is the terminator. Mark the end of the string and stop.
899 */
900	*str++ = '\0';
901	break;
902	}
903    return (result);
904    }
905
906/*
907 * Process the expect string
908 */
909
910void chat_expect (s)
911char *s;
912    {
913    char *expect;
914    char *reply;
915
916    if (strcmp(s, "HANGUP") == 0)
917        {
918	++hup_next;
919        return;
920        }
921
922    if (strcmp(s, "ABORT") == 0)
923	{
924	++abort_next;
925	return;
926	}
927
928    if (strcmp(s, "CLR_ABORT") == 0)
929	{
930	++clear_abort_next;
931	return;
932	}
933
934    if (strcmp(s, "REPORT") == 0)
935	{
936	++report_next;
937	return;
938	}
939
940    if (strcmp(s, "CLR_REPORT") == 0)
941	{
942	++clear_report_next;
943	return;
944	}
945
946    if (strcmp(s, "TIMEOUT") == 0)
947	{
948	++timeout_next;
949	return;
950	}
951
952    if (strcmp(s, "ECHO") == 0)
953	{
954	++echo_next;
955	return;
956	}
957    if (strcmp(s, "SAY") == 0)
958	{
959	++say_next;
960	return;
961	}
962/*
963 * Fetch the expect and reply string.
964 */
965    for (;;)
966	{
967	expect = expect_strtok (s, "-");
968	s      = (char *) 0;
969
970	if (expect == (char *) 0)
971	    {
972	    return;
973	    }
974
975	reply = expect_strtok (s, "-");
976/*
977 * Handle the expect string. If successful then exit.
978 */
979	if (get_string (expect))
980	    {
981	    return;
982	    }
983/*
984 * If there is a sub-reply string then send it. Otherwise any condition
985 * is terminal.
986 */
987	if (reply == (char *) 0 || exit_code != 3)
988	    {
989	    break;
990	    }
991
992	chat_send (reply);
993	}
994/*
995 * The expectation did not occur. This is terminal.
996 */
997    if (fail_reason)
998	{
999	syslog(LOG_INFO, "Failed (%s)", fail_reason);
1000	}
1001    else
1002	{
1003	syslog(LOG_INFO, "Failed");
1004	}
1005    terminate(exit_code);
1006    }
1007
1008/*
1009 * Translate the input character to the appropriate string for printing
1010 * the data.
1011 */
1012
1013char *character(c)
1014int c;
1015    {
1016    static char string[10];
1017    char *meta;
1018
1019    meta = (c & 0x80) ? "M-" : "";
1020    c &= 0x7F;
1021
1022    if (c < 32)
1023        {
1024	sprintf(string, "%s^%c", meta, (int)c + '@');
1025        }
1026    else
1027        {
1028	if (c == 127)
1029	    {
1030	    sprintf(string, "%s^?", meta);
1031	    }
1032	else
1033	    {
1034	    sprintf(string, "%s%c", meta, c);
1035	    }
1036        }
1037
1038    return (string);
1039    }
1040
1041/*
1042 *  process the reply string
1043 */
1044void chat_send (s)
1045register char *s;
1046    {
1047    if (say_next)
1048	{
1049	say_next = 0;
1050	s = clean(s,0);
1051	write(2, s, strlen(s));
1052        free(s);
1053	return;
1054	}
1055    if (hup_next)
1056        {
1057        hup_next = 0;
1058	if (strcmp(s, "OFF") == 0)
1059           signal(SIGHUP, SIG_IGN);
1060        else
1061           signal(SIGHUP, sighup);
1062        return;
1063        }
1064    if (echo_next)
1065	{
1066	echo_next = 0;
1067	echo = (strcmp(s, "ON") == 0);
1068	return;
1069	}
1070    if (abort_next)
1071        {
1072	char *s1;
1073
1074	abort_next = 0;
1075
1076	if (n_aborts >= MAX_ABORTS)
1077	    {
1078	    fatal("Too many ABORT strings");
1079	    }
1080
1081	s1 = clean(s, 0);
1082
1083	if (strlen(s1) > strlen(s)
1084	    || strlen(s1) + 1 > sizeof(fail_buffer))
1085	    {
1086	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
1087	    die();
1088	    }
1089
1090	abort_string[n_aborts++] = s1;
1091
1092	if (verbose)
1093	    {
1094	    logf("abort on (");
1095
1096	    for (s1 = s; *s1; ++s1)
1097	        {
1098		logf(character(*s1));
1099	        }
1100
1101	    logf(")\n");
1102	    }
1103	return;
1104	}
1105
1106    if (clear_abort_next)
1107        {
1108	char *s1;
1109	char *s2 = s;
1110	int   i;
1111        int   old_max;
1112	int   pack = 0;
1113
1114	clear_abort_next = 0;
1115
1116	s1 = clean(s, 0);
1117
1118	if (strlen(s1) > strlen(s)
1119	    || strlen(s1) + 1 > sizeof(fail_buffer))
1120	    {
1121	    syslog(LOG_WARNING, "Illegal or too-long CLR_ABORT string ('%s')", s);
1122	    die();
1123	    }
1124
1125        old_max = n_aborts;
1126	for (i=0; i < n_aborts; i++)
1127	    {
1128		if ( strcmp(s1,abort_string[i]) == 0 )
1129		    {
1130                        free(abort_string[i]);
1131			abort_string[i] = NULL;
1132			pack++;
1133                        n_aborts--;
1134			if (verbose)
1135	    		{
1136	    		logf("clear abort on (");
1137
1138	    		for (s2 = s; *s2; ++s2)
1139	        		{
1140				logf(character(*s2));
1141	        		}
1142
1143	    		logf(")\n");
1144	    		}
1145	    	    }
1146	    }
1147        free(s1);
1148	if (pack) pack_array(abort_string,old_max);
1149	return;
1150	}
1151
1152    if (report_next)
1153        {
1154	char *s1;
1155
1156	report_next = 0;
1157	if (n_reports >= MAX_REPORTS)
1158	    {
1159	    fatal("Too many REPORT strings");
1160	    }
1161
1162	s1 = clean(s, 0);
1163
1164	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
1165	    {
1166	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
1167	    die();
1168	    }
1169
1170	report_string[n_reports++] = s1;
1171
1172	if (verbose)
1173	    {
1174	    logf("report (");
1175	    s1 = s;
1176	    while (*s1)
1177	        {
1178		logf(character(*s1));
1179		++s1;
1180	        }
1181	    logf(")\n");
1182	    }
1183	return;
1184        }
1185
1186    if (clear_report_next)
1187        {
1188	char *s1;
1189        char *s2 = s;
1190	int   i;
1191	int   old_max;
1192	int   pack = 0;
1193
1194	clear_report_next = 0;
1195
1196	s1 = clean(s, 0);
1197
1198	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
1199	    {
1200	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
1201	    die();
1202	    }
1203
1204	old_max = n_reports;
1205	for (i=0; i < n_reports; i++)
1206	    {
1207		if ( strcmp(s1,report_string[i]) == 0 )
1208		    {
1209			free(report_string[i]);
1210			report_string[i] = NULL;
1211			pack++;
1212			n_reports--;
1213			if (verbose)
1214	    		{
1215	    		logf("clear report (");
1216
1217	    		for (s2 = s; *s2; ++s2)
1218	        		{
1219				logf(character(*s2));
1220	        		}
1221
1222	    		logf(")\n");
1223	    		}
1224	    	    }
1225	    }
1226        free(s1);
1227        if (pack) pack_array(report_string,old_max);
1228
1229	return;
1230        }
1231
1232    if (timeout_next)
1233        {
1234	timeout_next = 0;
1235	timeout = atoi(s);
1236
1237	if (timeout <= 0)
1238	    {
1239	    timeout = DEFAULT_CHAT_TIMEOUT;
1240	    }
1241
1242	if (verbose)
1243	    {
1244	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
1245	    }
1246	return;
1247        }
1248
1249    if (strcmp(s, "EOT") == 0)
1250        {
1251	s = "^D\\c";
1252        }
1253    else
1254        {
1255	if (strcmp(s, "BREAK") == 0)
1256	    {
1257	    s = "\\K\\c";
1258	    }
1259        }
1260
1261    if (!put_string(s))
1262        {
1263	syslog(LOG_INFO, "Failed");
1264	terminate(1);
1265        }
1266    }
1267
1268int get_char()
1269    {
1270    int status;
1271    char c;
1272
1273    status = read(0, &c, 1);
1274
1275    switch (status)
1276        {
1277    case 1:
1278	return ((int)c & 0x7F);
1279
1280    default:
1281	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
1282	       status);
1283
1284    case -1:
1285	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1286	    {
1287	    sysfatal("Can't get file mode flags on stdin");
1288	    }
1289	else
1290	    {
1291	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1292	        {
1293		sysfatal("Can't set file mode flags on stdin");
1294	        }
1295	    }
1296
1297	return (-1);
1298        }
1299    }
1300
1301int put_char(c)
1302int c;
1303    {
1304    int status;
1305    char ch = c;
1306
1307    usleep(10000);		/* inter-character typing delay (?) */
1308
1309    status = write(1, &ch, 1);
1310
1311    switch (status)
1312        {
1313    case 1:
1314	return (0);
1315
1316    default:
1317	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
1318	       status);
1319
1320    case -1:
1321	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1322	    {
1323	    sysfatal("Can't get file mode flags on stdin");
1324	    }
1325	else
1326	    {
1327	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1328	        {
1329		sysfatal("Can't set file mode flags on stdin");
1330	        }
1331	    }
1332
1333	return (-1);
1334        }
1335    }
1336
1337int write_char (c)
1338int c;
1339    {
1340    if (alarmed || put_char(c) < 0)
1341	{
1342	extern int errno;
1343
1344	alarm(0);
1345	alarmed = 0;
1346
1347	if (verbose)
1348	    {
1349	    if (errno == EINTR || errno == EWOULDBLOCK)
1350	        {
1351		syslog(LOG_INFO, " -- write timed out");
1352	        }
1353	    else
1354	        {
1355		syslog(LOG_INFO, " -- write failed: %m");
1356	        }
1357	    }
1358	return (0);
1359	}
1360    return (1);
1361    }
1362
1363int put_string (s)
1364register char *s;
1365    {
1366    quiet = 0;
1367    s = clean(s, 1);
1368
1369    if (verbose)
1370	{
1371	logf("send (");
1372
1373	if (quiet)
1374	    {
1375	    logf("??????");
1376	    }
1377	else
1378	    {
1379	    register char *s1 = s;
1380
1381	    for (s1 = s; *s1; ++s1)
1382	        {
1383		logf(character(*s1));
1384	        }
1385	    }
1386
1387	logf(")\n");
1388	}
1389
1390    alarm(timeout); alarmed = 0;
1391
1392    while (*s)
1393	{
1394	register char c = *s++;
1395
1396	if (c != '\\')
1397	    {
1398	    if (!write_char (c))
1399	        {
1400		return 0;
1401	        }
1402	    continue;
1403	    }
1404
1405	c = *s++;
1406	switch (c)
1407	    {
1408	case 'd':
1409	    sleep(1);
1410	    break;
1411
1412	case 'K':
1413	    break_sequence();
1414	    break;
1415
1416	case 'p':
1417	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
1418	    break;
1419
1420	default:
1421	    if (!write_char (c))
1422		return 0;
1423	    break;
1424	    }
1425	}
1426
1427    alarm(0);
1428    alarmed = 0;
1429    return (1);
1430    }
1431
1432/*
1433 *	Echo a character to stderr.
1434 *	When called with -1, a '\n' character is generated when
1435 *	the cursor is not at the beginning of a line.
1436 */
1437void echo_stderr(n)
1438int n;
1439    {
1440	static int need_lf;
1441	char *s;
1442
1443	switch (n)
1444	    {
1445	case '\r':		/* ignore '\r' */
1446	    break;
1447	case -1:
1448	    if (need_lf == 0)
1449		break;
1450	    /* fall through */
1451	case '\n':
1452	    write(2, "\n", 1);
1453	    need_lf = 0;
1454	    break;
1455	default:
1456	    s = character(n);
1457	    write(2, s, strlen(s));
1458	    need_lf = 1;
1459	    break;
1460	    }
1461    }
1462
1463/*
1464 *	'Wait for' this string to appear on this file descriptor.
1465 */
1466int get_string(string)
1467register char *string;
1468    {
1469    char temp[STR_LEN];
1470    int c, printed = 0, len, minlen;
1471    register char *s = temp, *end = s + STR_LEN;
1472
1473    fail_reason = (char *)0;
1474    string = clean(string, 0);
1475    len = strlen(string);
1476    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1477
1478    if (verbose)
1479	{
1480	register char *s1;
1481
1482	logf("expect (");
1483
1484	for (s1 = string; *s1; ++s1)
1485	    {
1486	    logf(character(*s1));
1487	    }
1488
1489	logf(")\n");
1490	}
1491
1492    if (len > STR_LEN)
1493	{
1494	syslog(LOG_INFO, "expect string is too long");
1495	exit_code = 1;
1496	return 0;
1497	}
1498
1499    if (len == 0)
1500	{
1501	if (verbose)
1502	    {
1503	    syslog(LOG_INFO, "got it");
1504	    }
1505
1506	return (1);
1507	}
1508
1509    alarm(timeout);
1510    alarmed = 0;
1511
1512    while ( ! alarmed && (c = get_char()) >= 0)
1513	{
1514	int n, abort_len, report_len;
1515
1516	if (echo)
1517	    {
1518	    echo_stderr(c);
1519	    }
1520	if (verbose)
1521	    {
1522	    if (c == '\n')
1523	        {
1524		logf("\n");
1525	        }
1526	    else
1527	        {
1528		logf(character(c));
1529	        }
1530	    }
1531
1532	if (Verbose) {
1533	   if (c == '\n')      fputc( '\n', stderr );
1534	   else if (c != '\r') fprintf( stderr, "%s", character(c) );
1535	}
1536
1537	*s++ = c;
1538
1539	if (!report_gathering)
1540	    {
1541	    for (n = 0; n < n_reports; ++n)
1542	        {
1543		if ((report_string[n] != (char*) NULL) &&
1544		    s - temp >= (report_len = strlen(report_string[n])) &&
1545		    strncmp(s - report_len, report_string[n], report_len) == 0)
1546		    {
1547		    time_t time_now   = time ((time_t*) NULL);
1548		    struct tm* tm_now = localtime (&time_now);
1549
1550		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1551		    strcat (report_buffer, report_string[n]);
1552
1553		    report_string[n] = (char *) NULL;
1554		    report_gathering = 1;
1555		    break;
1556		    }
1557	        }
1558	    }
1559	else
1560	    {
1561	    if (!iscntrl (c))
1562	        {
1563		int rep_len = strlen (report_buffer);
1564		report_buffer[rep_len]     = c;
1565		report_buffer[rep_len + 1] = '\0';
1566	        }
1567	    else
1568	        {
1569		report_gathering = 0;
1570		fprintf (report_fp, "chat:  %s\n", report_buffer);
1571	        }
1572	    }
1573
1574	if (s - temp >= len &&
1575	    c == string[len - 1] &&
1576	    strncmp(s - len, string, len) == 0)
1577	    {
1578	    if (verbose)
1579		{
1580		logf(" -- got it\n");
1581		}
1582
1583	    alarm(0);
1584	    alarmed = 0;
1585	    return (1);
1586	    }
1587
1588	for (n = 0; n < n_aborts; ++n)
1589	    {
1590	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1591		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
1592	        {
1593		if (verbose)
1594		    {
1595		    logf(" -- failed\n");
1596		    }
1597
1598		alarm(0);
1599		alarmed = 0;
1600		exit_code = n + 4;
1601		strcpy(fail_reason = fail_buffer, abort_string[n]);
1602		return (0);
1603	        }
1604	    }
1605
1606	if (s >= end)
1607	    {
1608	    strncpy (temp, s - minlen, minlen);
1609	    s = temp + minlen;
1610	    }
1611
1612	if (alarmed && verbose)
1613	    {
1614	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
1615	    }
1616	}
1617
1618    alarm(0);
1619
1620    if (verbose && printed)
1621	{
1622	if (alarmed)
1623	    {
1624	    logf(" -- read timed out\n");
1625	    }
1626	else
1627	    {
1628	    logflush();
1629	    syslog(LOG_INFO, " -- read failed: %m");
1630	    }
1631	}
1632
1633    exit_code = 3;
1634    alarmed   = 0;
1635    return (0);
1636    }
1637
1638#ifdef NO_USLEEP
1639#include <sys/types.h>
1640#include <sys/time.h>
1641
1642/*
1643  usleep -- support routine for 4.2BSD system call emulations
1644  last edit:  29-Oct-1984     D A Gwyn
1645  */
1646
1647extern int	  select();
1648
1649int
1650usleep( usec )				  /* returns 0 if ok, else -1 */
1651    long		usec;		/* delay in microseconds */
1652{
1653    static struct			/* `timeval' */
1654        {
1655	long	tv_sec;		/* seconds */
1656	long	tv_usec;	/* microsecs */
1657        } delay;	    /* _select() timeout */
1658
1659    delay.tv_sec  = usec / 1000000L;
1660    delay.tv_usec = usec % 1000000L;
1661
1662    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
1663}
1664#endif
1665
1666void
1667pack_array (array, end)
1668    char **array; /* The address of the array of string pointers */
1669    int    end;   /* The index of the next free entry before CLR_ */
1670{
1671    int i, j;
1672
1673    for (i = 0; i < end; i++) {
1674	if (array[i] == NULL) {
1675	    for (j = i+1; j < end; ++j)
1676		if (array[j] != NULL)
1677		    array[i++] = array[j];
1678	    for (; i < end; ++i)
1679		array[i] = NULL;
1680	    break;
1681	}
1682    }
1683}
1684