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