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