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