xref: /original-bsd/usr.bin/uucp/uucico/conn.c (revision fbb2a877)
1 #ifndef lint
2 static char sccsid[] = "@(#)conn.c	5.15	(Berkeley) 05/04/88";
3 #endif
4 
5 #include <signal.h>
6 #include "uucp.h"
7 #include <setjmp.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #ifdef	USG
11 #include <termio.h>
12 #include <fcntl.h>
13 #endif
14 #ifndef	USG
15 #include <sgtty.h>
16 #endif
17 #ifdef BSD4_2
18 #include <sys/time.h>
19 #else
20 #include <time.h>
21 #endif
22 
23 #define MAXC 1000
24 
25 extern jmp_buf Sjbuf;
26 jmp_buf Cjbuf;
27 extern int errno, onesys;
28 extern char *sys_errlist[];
29 extern char MaxGrade, DefMaxGrade;
30 
31 /* Parity control during login procedure */
32 #define	P_ZERO	0
33 #define	P_ONE	1
34 #define	P_EVEN	2
35 #define	P_ODD	3
36 
37 #define ABORT -2
38 
39 char 	*AbortOn = NULL;
40 char	par_tab[128];	/* must be power of two */
41 int	linebaudrate;	/* used for the sleep test in pk1.c */
42 int next_fd = -1;	/* predicted fd to close interrupted opens */
43 
44 char *PCP = "PCP";	/* PC Pursuit device type */
45 /*
46  *	catch alarm routine for "expect".
47  */
48 alarmtr()
49 {
50 	signal(SIGALRM, alarmtr);
51 	if (next_fd >= 0) {
52 		if (close(next_fd))
53 			logent("FAIL", "ACU LINE CLOSE");
54 		next_fd = -1;
55 	}
56 	longjmp(Sjbuf, 1);
57 }
58 
59 /* This template is for seismo to call ihnp4
60  * the 3 lines marked ---> will be overwritten for the appropriate city
61  */
62 #define PCP_BAUD	3
63 #define PCP_PHONE	4
64 #define PCP_CITY	14
65 #define PCP_PASSWORD	16
66 #define PCP_RPHONE	20
67 #define NPCFIELDS	23
68 
69 static char *PCFlds[] = {
70 	"PC-PURSUIT",
71 	"Any",
72 	"ACU",
73 	"1200",
74 	CNULL,
75 	CNULL,
76 	"P_ZERO",	/* Telenet insists on zero parity */
77 	"ABORT",
78 	"BUSY",		/* Abort on Busy Signal */
79 	CNULL,
80 	"\\d\\d\\r\\d\\r",	/* Get telenet's attention */
81 	"TERMINAL=~3-\r-TERM~3-\r-TERM~5", 	/* Terminal type ? */
82 	"\\r",
83 	"@",		/* telenet's prompt */
84 	"D/DCWAS/21,telenetloginstring", /* overwritten later */
85 	"PASSWORD",
86 	CNULL,		/* telenet password */
87 	"CONNECTED",	/* We're now talking to a Hayes in the remote city */
88 	"ATZ",		/* Reset it */
89 	"OK",
90 	"ATDT6907171", /* overwritten */
91 	"CONNECT",
92 	"\\d\\r",		/* We're in !*/
93 	CNULL,
94 };
95 
96 static char PCP_brand[25];
97 int Dcf = -1;
98 char *Flds[MAXC/10];
99 char LineType[10];
100 extern int LocalOnly;
101 
102 /*
103  *	place a telephone call to system and login, etc.
104  *
105  *	return codes:
106  *		CF_SYSTEM: don't know system
107  *		CF_TIME: wrong time to call
108  *		CF_DIAL: call failed
109  *		CF_NODEV: no devices available to place call
110  *		CF_LOGIN: login/password dialog failed
111  *
112  *		>0  - file no.  -  connect ok
113  */
114 conn(system)
115 char *system;
116 {
117 	int nf;
118 	char info[MAXC], wkpre[NAMESIZE], file[NAMESIZE];
119 	register FILE *fsys;
120 	int fcode = 0;
121 
122 	nf = 0;
123 
124 	fsys = fopen(SYSFILE, "r");
125 	if (fsys == NULL) {
126 		syslog(LOG_ERR, "fopen(%s) failed: %m", SYSFILE);
127 		cleanup(FAIL);
128 	}
129 
130 	DEBUG(4, "finds (%s) called\n", system);
131 keeplooking:
132 	while((nf = finds(fsys, system, info, Flds)) > 0) {
133 		strncpy(LineType, Flds[F_LINE], 10);
134 		if (LocalOnly) {
135 			if (strcmp("TCP", LineType)
136 				&& strcmp("DIR", LineType)
137 				&& strcmp("LOCAL", LineType) ) {
138 					fcode = CF_TIME;
139 					continue;
140 			}
141 		}
142 		sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname);
143 		if (!onesys && MaxGrade != DefMaxGrade &&
144 			!iswrk(file, "chk", Spool, wkpre))  {
145 				fcode = CF_TIME;
146 				continue;
147 		}
148 		/* For GTE's PC Pursuit */
149 		if (snccmp(LineType, PCP) == SAME) {
150 			FILE *dfp;
151 			int status;
152 			static struct Devices dev;
153 
154 			dfp = fopen(DEVFILE, "r");
155 			if (dfp == NULL) {
156 				syslog(LOG_ERR, "fopen(%s) failed: %m",
157 					DEVFILE);
158 				cleanup(FAIL);
159 			}
160 			while ((status=rddev(dfp, &dev)) != FAIL
161 				&& strcmp(PCP, dev.D_type) != SAME)
162 					;
163 			fclose(dfp);
164 			if (status == FAIL)
165 				continue;
166 			if (mlock(PCP) == FAIL) {
167 				fcode = CF_NODEV;
168 				logent("DEVICE", "NO");
169 				continue;
170 			}
171 			PCFlds[PCP_BAUD] = dev.D_class;
172 			PCFlds[PCP_PHONE] = dev.D_calldev;
173 			sprintf(PCFlds[PCP_CITY], "c d/%s%s,%s",
174 				Flds[F_CLASS],
175 				index(Flds[F_CLASS], '/') == NULL ? "/12" : "",
176 				dev.D_arg[D_CHAT]);
177 			PCFlds[PCP_PASSWORD] = dev.D_line;
178 			strncpy(&PCFlds[PCP_RPHONE][4], Flds[F_PHONE], 7);
179 			strncpy(PCP_brand, dev.D_brand, sizeof(PCP_brand));
180 			if ((fcode = getto(PCFlds)) < 0) {
181 				rmlock(PCP);
182 				continue;
183 			}
184 			Dcf = fcode;
185 			fcode = login(NPCFIELDS, PCFlds, Dcf);
186 			if (fcode == SUCCESS)
187 				break;
188 			fcode = CF_DIAL;
189 			rmlock(PCP);
190 			/* end PC Pursuit */
191 		} else if ((fcode = getto(Flds)) > 0)  {
192 			Dcf = fcode;
193 			break;
194 		}
195 	}
196 
197 	if (nf <= 0) {
198 		fclose(fsys);
199 		return fcode ? fcode : nf;
200 	}
201 
202 
203 	if (fcode >= 0) {
204 		DEBUG(4, "login %s\n", "called");
205 		setproctitle("login");
206 		fcode = login(nf, Flds, Dcf); }
207 	if (fcode < 0) {
208 		clsacu();
209 		if (fcode == ABORT) {
210 			fcode = CF_DIAL;
211 			goto  keeplooking;
212 		} else {
213 			fclose(fsys);
214 			return CF_LOGIN;
215 		}
216 	}
217 	fclose(fsys);
218 	fioclex(Dcf);
219 	return Dcf;
220 }
221 
222 int nulldev();
223 int (*CU_end)() = nulldev;
224 
225 /*
226  *	connect to remote machine
227  *
228  *	return codes:
229  *		>0  -  file number - ok
230  *		FAIL  -  failed
231  */
232 getto(flds)
233 register char *flds[];
234 {
235 	register struct condev *cd;
236 	int diropn();
237 	char *line;
238 
239 	DEBUG(4, "getto: call no. %s ", flds[F_PHONE]);
240 	DEBUG(4, "for sys %s\n", flds[F_NAME]);
241 
242 	if (snccmp(flds[F_LINE], "LOCAL") == SAME)
243 		line = "ACU";
244 	else
245 		line = flds[F_LINE];
246 #ifdef DIALINOUT
247 	if (snccmp(line, "ACU") != SAME)
248 		reenable();
249 #endif DIALINOUT
250 	CU_end = nulldev;
251 	if (snccmp(line, PCP) == SAME) {
252 		for(cd = condevs; cd->CU_meth != NULL; cd++) {
253 			if (snccmp(PCP_brand, cd->CU_brand) == SAME) {
254 				CU_end = cd->CU_clos;
255 				return diropn(flds);
256 			}
257 		}
258 		logent(PCP_brand, "UNSUPPORTED ACU TYPE");
259 	} else {
260 		for (cd = condevs; cd->CU_meth != NULL; cd++) {
261 			if (snccmp(cd->CU_meth, line) == SAME) {
262 				DEBUG(4, "Using %s to call\n", cd->CU_meth);
263 				return (*(cd->CU_gen))(flds);
264 			}
265 		}
266 		DEBUG(1, "Can't find %s, assuming DIR\n", flds[F_LINE]);
267 	}
268 	return diropn(flds);	/* search failed, so use direct */
269 }
270 
271 /*
272  *	close call unit
273  *
274  *	return codes:  none
275  */
276 clsacu()
277 {
278 	/* make *sure* Dcf is no longer exclusive.
279 	 * Otherwise dual call-in/call-out modems could get stuck.
280 	 * Unfortunately, doing this here is not ideal, but it is the
281 	 * easiest place to put the call.
282 	 * Hopefully everyone honors the LCK protocol, of course
283 	 */
284 #ifdef	TIOCNXCL
285 	if (!IsTcpIp && Dcf >= 0 && ioctl(Dcf, TIOCNXCL, STBNULL) < 0)
286 		DEBUG(5, "clsacu ioctl %s\n", sys_errlist[errno]);
287 #endif
288 	if  (setjmp(Sjbuf))
289 		logent(Rmtname, "CLOSE TIMEOUT");
290 	else {
291 		signal(SIGALRM, alarmtr);
292 		alarm(20);
293 		(*(CU_end))(Dcf);
294 		alarm(0);
295 	}
296 	if (close(Dcf) == 0) {
297 		DEBUG(4, "fd %d NOT CLOSED by CU_clos\n", Dcf);
298 		logent("clsacu", "NOT CLOSED by CU_clos");
299 	}
300 	Dcf = -1;
301 	CU_end = nulldev;
302 }
303 
304 /*
305  *	expand phone number for given prefix and number
306  */
307 exphone(in, out)
308 register char *in, *out;
309 {
310 	FILE *fn;
311 	char pre[MAXPH], npart[MAXPH], tpre[MAXPH], p[MAXPH];
312 	char buf[BUFSIZ];
313 	register char *s1;
314 
315 	if (!isascii(*in) || !isalpha(*in)) {
316 		strcpy(out, in);
317 		return;
318 	}
319 
320 	s1=pre;
321 	while (isascii(*in) && isalpha(*in))
322 		*s1++ = *in++;
323 	*s1 = '\0';
324 	s1 = npart;
325 	while (*in != '\0')
326 		*s1++ = *in++;
327 	*s1 = '\0';
328 
329 	tpre[0] = '\0';
330 	if ((fn = fopen(DIALFILE, "r")) == NULL)
331 		DEBUG(2, "CAN'T OPEN %s\n", DIALFILE);
332 	else {
333 		while (cfgets(buf, BUFSIZ, fn)) {
334 			if (sscanf(buf, "%s%s", p, tpre) != 2)
335 				continue;
336 			if (strcmp(p, pre) == SAME)
337 				goto found;
338 			tpre[0] = '\0';
339 		}
340 		DEBUG(2, "CAN'T FIND dialcodes prefix '%s'\n", pre);
341 	found:;
342 		fclose(fn);
343 	}
344 
345 	strcpy(out, tpre);
346 	strcat(out, npart);
347 }
348 
349 /*
350  *	read and decode a line from device file
351  *
352  *	return code - FAIL at end-of file; 0 otherwise
353  */
354 rddev(fp, dev)
355 register struct Devices *dev;
356 FILE *fp;
357 {
358 	register int na;
359 
360 	if (!cfgets(dev->D_argbfr, sizeof(dev->D_argbfr), fp))
361 		return FAIL;
362 	na = getargs(dev->D_argbfr, dev->D_arg, 20);
363 	if (na < 4) {
364 		syslog(LOG_ERR, "%s: invalid device entry", dev->D_argbfr);
365 		cleanup(FAIL);
366 	}
367 	if (na == 4) {
368 		dev->D_brand = "";
369 		na++;
370 	}
371 	dev->D_speed = atoi(fdig(dev->D_class));
372 	dev->D_numargs = na;
373 	return 0;
374 }
375 
376 /*
377  *	set system attribute vector
378  *
379  *	return codes:
380  *		>0  -  number of arguments in vector - succeeded
381  *		CF_SYSTEM  -  system name not found
382  *		CF_TIME  -  wrong time to call
383  */
384 finds(fsys, sysnam, info, flds)
385 char *sysnam, info[], *flds[];
386 FILE *fsys;
387 {
388 	int na;
389 	int fcode = 0;
390 
391 	/* format of fields
392 	 *	0 name;
393 	 *	1 time
394 	 *	2 acu/hardwired
395 	 *	3 speed
396 	 *	etc
397 	 */
398 	while (cfgets(info, MAXC, fsys) != NULL) {
399 		na = getargs(info, flds, MAXC/10);
400 		if (strncmp(sysnam, flds[F_NAME], MAXBASENAME) != SAME)
401 			continue;
402 		if (ifdate(flds[F_TIME]) != FAIL)
403 			/*  found a good entry  */
404 			return na;
405 		DEBUG(2, "Wrong time ('%s') to call\n", flds[F_TIME]);
406 		fcode = CF_TIME;
407 	}
408 	return fcode ? fcode : CF_SYSTEM;
409 }
410 
411 /*
412  *	do login conversation
413  *
414  *	return codes:  SUCCESS  |  FAIL
415  */
416 login(nf, flds, fn)
417 register char *flds[];
418 int nf, fn;
419 {
420 	register char *want, *altern;
421 	int k, ok;
422 
423 	if (nf < 4) {
424 		syslog(LOG_ERR, "Too few log fields: %d", nf);
425 		cleanup(FAIL);
426 	}
427 	if (setjmp(Cjbuf))
428 		return FAIL;
429 	AbortOn = NULL;
430 	for (k = F_LOGIN; k < nf; k += 2) {
431 		want = flds[k];
432 		if (want == NULL)
433 			want = "";
434 		ok = FAIL;
435 		while (ok != SUCCESS) {
436 			altern = index(want, '-');
437 			if (altern != NULL)
438 				*altern++ = '\0';
439 			if (strcmp(want, "ABORT") == 0) {
440 				AbortOn = flds[k+1];
441 				DEBUG(4, "ABORT ON: %s\n", AbortOn);
442 				goto nextfield;
443 			}
444 			DEBUG(4, "wanted \"%s\"\n", want);
445 			ok = expect(want, fn);
446 			DEBUG(4, "got: %s\n", ok ? "?" : "that");
447 			if (ok == FAIL) {
448 				if (altern == NULL) {
449 					logent("LOGIN", _FAILED);
450 					return FAIL;
451 				}
452 				want = index(altern, '-');
453 				if (want != NULL)
454 					*want++ = '\0';
455 				sendthem(altern, fn);
456 			} else
457 				if (ok == ABORT) {
458 					char sbuf[MAXFULLNAME];
459 					sprintf(sbuf, "LOGIN ABORTED on \"%s\"",						AbortOn);
460 					logent(sbuf, _FAILED);
461 					return ABORT;
462 				}
463 		}
464 		sleep(1);
465 		if (k+1 < nf)
466 			sendthem(flds[k+1], fn);
467 nextfield: ;
468 	}
469 	return SUCCESS;
470 }
471 
472 
473 /* conditional table generation to support odd speeds */
474 struct sg_spds {int sp_val, sp_name;} spds[] = {
475 #ifdef B50
476 	{  50,	 B50},
477 #endif
478 #ifdef B75
479 	{  75,	 B75},
480 #endif
481 #ifdef B110
482 	{ 110,	B110},
483 #endif
484 #ifdef B150
485 	{ 150,	B150},
486 #endif
487 #ifdef B200
488 	{ 200,	B200},
489 #endif
490 #ifdef B300
491 	{ 300,  B300},
492 #endif
493 #ifdef B600
494 	{600,	B600},
495 #endif
496 #ifdef B1200
497 	{1200, B1200},
498 #endif
499 #ifdef B1800
500 	{1800, B1800},
501 #endif
502 #ifdef B2000
503 	{2000, B2000},
504 #endif
505 #ifdef B2400
506 	{2400, B2400},
507 #endif
508 #ifdef B3600
509 	{3600, B3600},
510 #endif
511 #ifdef B4800
512 	{4800, B4800},
513 #endif
514 #ifdef B7200
515 	{7200, B7200},
516 #endif
517 #ifdef B9600
518 	{9600, B9600},
519 #endif
520 #ifdef B19200
521 	{19200, B19200},
522 #endif
523 #ifdef EXTA
524 	{19200, EXTA},
525 #endif
526 	{0, 0}
527 };
528 
529 /*
530  *	set speed/echo/mode...
531  *
532  *	return codes:  none
533  */
534 fixline(tty, spwant)
535 int tty, spwant;
536 {
537 #ifdef	USG
538 	struct termio ttbuf;
539 #else	!USG
540 	struct sgttyb ttbuf;
541 #endif !USG
542 	register struct sg_spds *ps;
543 	int speed = -1;
544 
545 	for (ps = spds; ps->sp_val; ps++)
546 		if (ps->sp_val == spwant)
547 			speed = ps->sp_name;
548 	if (speed < 0) {
549 		syslog(LOG_ERR, "unrecognized speed: %d", speed);
550 		cleanup(FAIL);
551 	}
552 #ifdef	USG
553 	if (ioctl(tty, TCGETA, &ttbuf) < 0)
554 		return FAIL;
555 	/* ttbuf.sg_flags = (ANYP|RAW);
556 	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed; */
557 	ttbuf.c_iflag = (ushort)0;
558 	ttbuf.c_oflag = (ushort)0;
559 	ttbuf.c_cflag = (speed|CS8|HUPCL|CREAD);
560 	ttbuf.c_lflag = (ushort)0;
561 	ttbuf.c_cc[VMIN] = 6;
562 	ttbuf.c_cc[VTIME] = 1;
563 	if (ioctl(tty, TCSETA, &ttbuf) < 0)
564 		return FAIL;
565 #else	!USG
566 	if (ioctl(tty, TIOCGETP, &ttbuf) < 0)
567 		return FAIL;
568 	ttbuf.sg_flags = (ANYP|RAW);
569 	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed;
570 	if (ioctl(tty, TIOCSETP, &ttbuf) < 0)
571 		return FAIL;
572 #endif
573 #ifndef	USG
574 	if (ioctl(tty, TIOCHPCL, STBNULL) < 0)
575 		return FAIL;
576 	if (ioctl(tty, TIOCEXCL, STBNULL) < 0)
577 		return FAIL;
578 #endif
579 	linebaudrate = spwant;
580 	return SUCCESS;
581 }
582 
583 #define MR 100
584 
585 /*
586  *	look for expected string
587  *
588  *	return codes:
589  *		0  -  found
590  *		FAIL  -  lost line or too many characters read
591  *		some character  -  timed out
592  */
593 expect(str, fn)
594 register char *str;
595 int fn;
596 {
597 	char rdvec[MR];
598 	register char *rp = rdvec, *strptr;
599 	int kr, cnt_char;
600 	char nextch;
601 	int timo = MAXMSGTIME;
602 
603 	if (*str == '\0' || strcmp(str, "\"\"") == SAME)
604 		return SUCCESS;
605 	/* Cleanup str, convert \0xx strings to one char  */
606 	for (strptr = str; *strptr; strptr++) {
607 		if (*strptr == '\\')
608 			switch(*++strptr) {
609 			case 's':
610 				DEBUG(5, "BLANK\n", CNULL);
611 				strptr--;
612 				*strptr = ' ';
613 				strcpy(&strptr[1], &strptr[4]);
614 				break;
615 			default:
616 				strptr--;  /* back up to backslash */
617 				sscanf(strptr + 1,"%o", &cnt_char);
618 				DEBUG(6, "BACKSLASHED %02xH\n", cnt_char);
619 				*strptr = (char) (cnt_char);
620 				strcpy(&strptr[1], &strptr[4]);
621 			}
622 	}
623 
624 	strptr = index(str, '~');
625 	if (strptr != NULL) {
626 		*strptr++ = '\0';
627 		timo = atoi(strptr);
628 		if (timo <= 0)
629 			timo = MAXMSGTIME;
630 	}
631 
632 	if (setjmp(Sjbuf))
633 		return FAIL;
634 	signal(SIGALRM, alarmtr);
635 	alarm(timo);
636 	*rp = 0;
637 	while (notin(str, rdvec)) {
638 		int c;
639 		if(AbortOn != NULL && !notin(AbortOn, rdvec)) {
640 			DEBUG(1, "Call aborted on '%s'\n", AbortOn);
641 			alarm(0);
642 			return ABORT;
643 		}
644 		kr = read(fn, &nextch, 1);
645 		if (kr <= 0) {
646 			alarm(0);
647 			DEBUG(4, "lost line kr - %d\n, ", kr);
648 			logent("LOGIN", "LOST LINE");
649 			return FAIL;
650 		}
651 		c = nextch & 0177;
652 		if (c == '\0')
653 			continue;
654 		DEBUG(4, (isprint(c) || isspace(c)) ? "%c" : "\\%03o", c);
655 		*rp++ = c;
656 		if (rp >= rdvec + MR) {
657 			register char *p;
658 			for (p = rdvec+MR/2; p < rp; p++)
659 				*(p-MR/2) = *p;
660 			rp -= MR/2;
661 		}
662 		*rp = '\0';
663 	}
664 	alarm(0);
665 	return SUCCESS;
666 }
667 
668 
669 /*
670  * Determine next file descriptor that would be allocated.
671  * This permits later closing of a file whose open was interrupted.
672  * It is a UNIX kernel problem, but it has to be handled.
673  * unc!smb (Steve Bellovin) probably first discovered it.
674  */
675 getnextfd()
676 {
677 	close(next_fd = open("/", 0));
678 }
679 
680 /*
681  *	send line of login sequence
682  *
683  *	return codes:  none
684  */
685 sendthem(str, fn)
686 register char *str;
687 int fn;
688 {
689 	register char *strptr;
690 	int i, n, cr = 1;
691 	register char c;
692 	static int p_init = 0;
693 
694 	DEBUG(5, "send \"%s\"\n", str);
695 
696 	if (!p_init) {
697 		p_init++;
698 		bld_partab(P_ZERO);
699 	}
700 
701 	if (prefix("BREAK", str)) {
702 		sscanf(&str[5], "%1d", &i);
703 		if (i <= 0 || i > 10)
704 			i = 3;
705 		/* send break */
706 		genbrk(fn, i);
707 		return;
708 	}
709 
710 	if (prefix("PAUSE", str)) {
711 		sscanf(&str[5], "%1d", &i);
712 		if (i <= 0 || i > 10)
713 			i = 3;
714 		/* pause for a while */
715 		sleep((unsigned)i);
716 		return;
717 	}
718 
719 	if (strcmp(str, "EOT") == SAME) {
720 		p_chwrite(fn, '\04');
721 		return;
722 	}
723 
724 	/* Send a '\n' */
725 	if (strcmp(str, "LF") == SAME) {
726 		p_chwrite(fn, '\n');
727 		return;
728 	}
729 
730 	/* Send a '\r' */
731 	if (strcmp(str, "CR") == SAME) {
732 		p_chwrite(fn, '\r');
733 		return;
734 	}
735 
736 	/* Set parity as needed */
737 	if (strcmp(str, "P_ZERO") == SAME) {
738 		bld_partab(P_ZERO);
739 		return;
740 	}
741 	if (strcmp(str, "P_ONE") == SAME) {
742 		bld_partab(P_ONE);
743 		return;
744 	}
745 	if (strcmp(str, "P_EVEN") == SAME) {
746 		bld_partab(P_EVEN);
747 		return;
748 	}
749 	if (strcmp(str, "P_ODD") == SAME) {
750 		bld_partab(P_ODD);
751 		return;
752 	}
753 
754 	/* If "", just send '\r' */
755 	if (strcmp(str, "\"\"") == SAME) {
756 		p_chwrite(fn, '\r');
757 		return;
758 	}
759 
760 	strptr = str;
761 	while ((c = *strptr++) != '\0') {
762 		if (c == '\\') {
763 			switch(*strptr++) {
764 			case '\0':
765 				DEBUG(5, "TRAILING BACKSLASH IGNORED\n", CNULL);
766 				--strptr;
767 				continue;
768 			case 's':
769 				DEBUG(5, "BLANK\n", CNULL);
770 				c = ' ';
771 				break;
772 			case 'd':
773 				DEBUG(5, "DELAY\n", CNULL);
774 				sleep(1);
775 				continue;
776 			case 'n':
777 				DEBUG(5, "NEW LINE\n", CNULL);
778 				c = '\n';
779 				break;
780 			case 'r':
781 				DEBUG(5, "RETURN\n", CNULL);
782 				c = '\r';
783 				break;
784 			case 'b':
785 				if (isdigit(*strptr)) {
786 					i = (*strptr++ - '0');
787 					if (i <= 0 || i > 10)
788 						i = 3;
789 				} else
790 					i = 3;
791 				/* send break */
792 				genbrk(fn, i);
793 				if (*strptr == '\0')
794 					cr = 0;
795 				continue;
796 			case 'c':
797 				if (*strptr == '\0') {
798 					DEBUG(5, "NO CR\n", CNULL);
799 					cr = 0;
800 				} else
801 					DEBUG(5, "NO CR - IGNORED NOT EOL\n", CNULL);
802 				continue;
803 #define isoctal(x)	((x >= '0') && (x <= '7'))
804 			default:
805 				if (isoctal(strptr[-1])) {
806 					i = 0;
807 					n = 0;
808 					--strptr;
809 					while (isoctal(*strptr) && ++n <= 3)
810 						i = i * 8 + (*strptr++ - '0');
811 					DEBUG(5, "\\%o\n", i);
812 					p_chwrite(fn, (char)i);
813 					continue;
814 				}
815 			}
816 		}
817 		p_chwrite(fn, c);
818 	}
819 
820 	if (cr)
821 		p_chwrite(fn, '\r');
822 	return;
823 }
824 
825 p_chwrite(fd, c)
826 int fd;
827 char c;
828 {
829 	c = par_tab[c&0177];
830 	if (write(fd, &c, 1) != 1) {
831 		logent(sys_errlist[errno], "BAD WRITE");
832 		longjmp(Cjbuf, 2);
833 	}
834 }
835 
836 /*
837  * generate parity table for use by p_chwrite.
838  */
839 bld_partab(type)
840 int type;
841 {
842 	register int i, j, n;
843 
844 	for (i = 0; i < sizeof(par_tab); i++) {
845 		n = 0;
846 		for (j = i&(sizeof(par_tab)-1); j; j = (j-1)&j)
847 			n++;
848 		par_tab[i] = i;
849 		if (type == P_ONE
850 		 || (type == P_EVEN && (n&01) != 0)
851 		 || (type == P_ODD && (n&01) == 0))
852 			par_tab[i] |= sizeof(par_tab);
853 	}
854 }
855 
856 /*
857  *	check for occurrence of substring "sh"
858  *
859  *	return codes:
860  *		0  -  found the string
861  *		1  -  not in the string
862  */
863 notin(sh, lg)
864 register char *sh, *lg;
865 {
866 	while (*lg != '\0') {
867 		if (wprefix(sh, lg))
868 			return 0;
869 		else
870 			lg++;
871 	}
872 	return 1;
873 }
874 
875 /*
876  *	Allow multiple date specifications separated by ','.
877  */
878 ifdate(p)
879 register char *p;
880 {
881 	register char *np;
882 	register int ret, g;
883 	int rtime, i;
884 
885 	/*  pick up retry time for failures  */
886 	/*  global variable Retrytime is set here  */
887 	if ((np = index(p, ';')) == NULL) {
888 		Retrytime = RETRYTIME;
889 	} else {
890 		i = sscanf(np+1, "%d", &rtime);
891 		if (i < 1 || rtime < 0)
892 			rtime = 5;
893 		Retrytime  = rtime * 60;
894 	}
895 
896 	ret = FAIL;
897 	MaxGrade = '\0';
898 	do {
899 		np = strpbrk(p, ",|");	/* prefer , but allow | for compat */
900 		if (np)
901 			*np = '\0';
902 		g = ifadate(p);
903 		DEBUG(11,"ifadate returns %o\n", g);
904 		if (g != FAIL) {
905 			ret = SUCCESS;
906 			if (g > MaxGrade)
907 				MaxGrade = g;
908 		}
909 		if (np)
910 			*np = ',';
911 		p = np + 1;
912 	} while (np);
913 	if (MaxGrade == '\0')
914 		MaxGrade = DefMaxGrade;
915 	return ret;
916 }
917 
918 /*
919  *	this routine will check a string (string)
920  *	like "MoTu0800-1730" to see if the present
921  *	time is within the given limits.
922  *	SIDE EFFECT - Retrytime is set
923  *
924  *	return codes:
925  *		0  -  not within limits
926  *		1  -  within limits
927  */
928 
929 ifadate(string)
930 char *string;
931 {
932 	static char *days[]={
933 		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
934 	};
935 	time_t clock;
936 	register char *s = string;
937 	int i, tl, th, tn, dayok=0;
938 	struct tm *localtime();
939 	struct tm *tp;
940 	char *p, MGrade;
941 
942 	if ((p = index(s, '/')) == NULL)
943 		MGrade = DefMaxGrade;
944 	else
945 		MGrade = p[1];
946 
947 	time(&clock);
948 	tp = localtime(&clock);
949 	while (isascii(*s) && isalpha(*s)) {
950 		for (i = 0; days[i]; i++) {
951 			if (prefix(days[i], s))
952 				if (tp->tm_wday == i)
953 					dayok = 1;
954 		}
955 
956 		if (prefix("Wk", s))
957 			if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
958 				dayok = 1;
959 		if (prefix("Any", s))
960 			dayok = 1;
961 		if (prefix("Evening", s)) {
962 			/* Sat or Sun */
963 			if (tp->tm_wday == 6 || tp->tm_wday == 0
964 				|| tp->tm_hour >= 17 || tp->tm_hour < 8)
965 					dayok = 1;
966 		}
967 		if (prefix("Night", s)) {
968 			if (tp->tm_wday == 6  /* Sat */
969 				|| tp->tm_hour >= 23 || tp->tm_hour < 8
970 					/* Sunday before 5pm */
971 				|| (tp->tm_wday == 0 && tp->tm_hour < 17))
972 					dayok = 1;
973 		}
974 		if (prefix("NonPeak", s)) { /* For Tymnet and PC Pursuit */
975 			/* Sat or Sun */
976 			if (tp->tm_wday == 6 || tp->tm_wday == 0
977 				|| tp->tm_hour >= 18 || tp->tm_hour < 7)
978 					dayok = 1;
979 		}
980 		s++;
981 	}
982 
983 	if (dayok == 0 && s != string)
984 		return FAIL;
985 	i = sscanf(s, "%d-%d", &tl, &th);
986   	if (i < 2)
987   		return MGrade;
988 	tn = tp->tm_hour * 100 + tp->tm_min;
989   	if (th < tl) { 		/* crosses midnight */
990   		if (tl <= tn || tn < th)
991   			return MGrade;
992   	} else {
993 		if (tl <= tn && tn < th)
994 			return MGrade;
995 	}
996 	return FAIL;
997 }
998 
999 /*
1000  *	find first digit in string
1001  *
1002  *	return - pointer to first digit in string or end of string
1003  */
1004 char *
1005 fdig(cp)
1006 register char *cp;
1007 {
1008 	register char *c;
1009 
1010 	for (c = cp; *c; c++)
1011 		if (*c >= '0' && *c <= '9')
1012 			break;
1013 	return c;
1014 }
1015 
1016 /*
1017  * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
1018  * Strings are compared as if they contain all capital letters.
1019  */
1020 snccmp(s1, s2)
1021 register char *s1, *s2;
1022 {
1023 	char c1, c2;
1024 
1025 	if (islower(*s1))
1026 		c1 = toupper(*s1);
1027 	else
1028 		c1 = *s1;
1029 	if (islower(*s2))
1030 		c2 = toupper(*s2);
1031 	else
1032 		c2 = *s2;
1033 
1034 	while (c1 == c2) {
1035 		if (*s1++ == '\0')
1036 			return 0;
1037 		s2++;
1038 		if (islower(*s1))
1039 			c1 = toupper(*s1);
1040 		else
1041 			c1 = *s1;
1042 		if (islower(*s2))
1043 			c2 = toupper(*s2);
1044 		else
1045 			c2 = *s2;
1046 	}
1047 	return c1 - c2;
1048 }
1049 
1050 /*
1051  * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
1052  * Strings are compared as if they contain all capital letters.
1053  */
1054 sncncmp(s1, s2, n)
1055 register char *s1, *s2;
1056 register int n;
1057 {
1058 	char c1, c2;
1059 
1060 	if (islower(*s1))
1061 		c1 = toupper(*s1);
1062 	else
1063 		c1 = *s1;
1064 	if (islower(*s2))
1065 		c2 = toupper(*s2);
1066 	else
1067 		c2 = *s2;
1068 
1069 	while ( --n >= 0 && c1 == c2) {
1070 		if (*s1++ == '\0')
1071 			return 0;
1072 		s2++;
1073 		if (islower(*s1))
1074 			c1 = toupper(*s1);
1075 		else
1076 			c1 = *s1;
1077 		if (islower(*s2))
1078 			c2 = toupper(*s2);
1079 		else
1080 			c2 = *s2;
1081 	}
1082 	return n<0 ? 0 : (c1 - c2);
1083 }
1084 /*
1085  * do chat script
1086  * occurs after local port is opened,
1087  * before 'dialing' the other machine.
1088  */
1089 dochat(dev, flds, fd)
1090 register struct Devices *dev;
1091 char *flds[];
1092 int fd;
1093 {
1094 	register int i;
1095 	register char *p;
1096 	char bfr[sizeof(dev->D_argbfr)];
1097 
1098 	if (dev->D_numargs <= 5)
1099 		return(0);
1100 	DEBUG(4, "dochat called %d\n", dev->D_numargs);
1101 	for (i = 0; i < dev->D_numargs-5; i++) {
1102 		sprintf(bfr, dev->D_arg[D_CHAT+i], flds[F_PHONE]);
1103 		if (strcmp(bfr, dev->D_arg[D_CHAT+i])) {
1104 			p = malloc((unsigned)strlen(bfr)+1);
1105 			if (p != NULL) {
1106 				strcpy(p, bfr);
1107 				dev->D_arg[D_CHAT+i] = p;
1108 			}
1109 		}
1110 	}
1111 	/* following is a kludge because login() arglist is a kludge */
1112 	i = login(dev->D_numargs, &dev->D_arg[D_CHAT-5], fd);
1113 	/*
1114 	 * If login() last did a sendthem(), must pause so things can settle.
1115 	 * But don't bother if chat failed.
1116 	 */
1117 	if (i == 0 && (dev->D_numargs&01))
1118 		sleep(2);
1119 	return(i);
1120 }
1121 
1122 /*
1123  *	fix kill/echo/raw on line
1124  *
1125  *	return codes:  none
1126  */
1127 fixmode(tty)
1128 register int tty;
1129 {
1130 #ifdef	USG
1131 	struct termio ttbuf;
1132 #else	!USG
1133 	struct sgttyb ttbuf;
1134 #endif	!USG
1135 	register struct sg_spds *ps;
1136 	int speed;
1137 
1138 	if (IsTcpIp)
1139 		return;
1140 #ifdef	USG
1141 	ioctl(tty, TCGETA, &ttbuf);
1142 	ttbuf.c_iflag = ttbuf.c_oflag = ttbuf.c_lflag = (ushort)0;
1143 	speed = ttbuf.c_cflag &= (CBAUD);
1144 	ttbuf.c_cflag |= (CS8|CREAD);
1145 	ttbuf.c_cc[VMIN] = 6;
1146 	ttbuf.c_cc[VTIME] = 1;
1147 	ioctl(tty, TCSETA, &ttbuf);
1148 #else	!USG
1149 	ioctl(tty, TIOCGETP, &ttbuf);
1150 	ttbuf.sg_flags = (ANYP | RAW);
1151 	ioctl(tty, TIOCSETP, &ttbuf);
1152 	speed = ttbuf.sg_ispeed;
1153 	ioctl(tty, TIOCEXCL, STBNULL);
1154 #endif	!USG
1155 
1156 	for (ps = spds; ps->sp_val; ps++)
1157 		if (ps->sp_name == speed) {
1158 			linebaudrate = ps->sp_val;
1159 			DEBUG(9,"Incoming baudrate is %d\n", linebaudrate);
1160 			return;
1161 		}
1162 	if (linebaudrate < 0) {
1163 		syslog(LOG_ERR, "unrecognized speed: %d", linebaudrate);
1164 		cleanup(FAIL);
1165 	}
1166 }
1167