GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/pppd/chat/chat.c Lines: 0 700 0.0 %
Date: 2017-11-07 Branches: 0 592 0.0 %

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