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