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