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