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@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@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@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@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@xmission.com> 60 * 61 * Added -s "use stderr" and -S "don't use syslog" switches. 62 * June 18, 1997 63 * Karl O. Pinc <kop@meme.com> 64 * 65 * 66 * Added -e "echo" switch & ECHO keyword 67 * Dick Streefland <dicks@tasking.nl> 68 * 69 * 70 * Considerable updates and modifications by 71 * Al Longyear <longyear@pobox.com> 72 * Paul Mackerras <paulus@cs.anu.edu.au> 73 * 74 * 75 * The original author is: 76 * 77 * Karl Fox <karl@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