xref: /csrg-svn/usr.bin/uucp/uucico/cntrl.c (revision 17832)
1 #ifndef lint
2 static char sccsid[] = "@(#)cntrl.c	5.4 (Berkeley) 01/22/85";
3 #endif
4 
5 #include "uucp.h"
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include "uust.h"
9 
10 extern int errno;
11 
12 struct Proto {
13 	char P_id;
14 	int (*P_turnon)();
15 	int (*P_rdmsg)();
16 	int (*P_wrmsg)();
17 	int (*P_rddata)();
18 	int (*P_wrdata)();
19 	int (*P_turnoff)();
20 };
21 
22 extern int gturnon(), gturnoff();
23 extern int grdmsg(), grddata();
24 extern int gwrmsg(), gwrdata();
25 extern int imsg(), omsg();
26 #ifdef BSDTCP
27 extern int tnullf();
28 extern int twrmsg(), trdmsg();
29 extern int twrdata(), trddata();
30 #endif BSDTCP
31 #ifdef PAD
32 extern int fturnon(), fturnoff();
33 extern int frdmsg(), frddata();
34 extern int fwrmsg(), fwrdata();
35 #endif PAD
36 
37 struct Proto Ptbl[]={
38 #ifdef BSDTCP
39 	't', tnullf, trdmsg, twrmsg, trddata, twrdata, tnullf,
40 #endif BSDTCP
41 #ifdef PAD
42 	'f', fturnon, frdmsg, fwrmsg, frddata, fwrdata, fturnoff,
43 #endif PAD
44 	'g', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff,
45 	'\0'
46 };
47 
48 int (*Imsg)() = imsg, (*Omsg)() = omsg;
49 
50 int (*Rdmsg)()=imsg, (*Rddata)();
51 int (*Wrmsg)()=omsg, (*Wrdata)();
52 int (*Turnon)(), (*Turnoff)();
53 
54 
55 static char *YES = "Y";
56 static char *NO = "N";
57 
58 /*  failure messages  */
59 #define EM_MAX		6
60 #define EM_LOCACC	"N1"	/* local access to file denied */
61 #define EM_RMTACC	"N2"	/* remote access to file/path denied */
62 #define EM_BADUUCP	"N3"	/* a bad uucp command was generated */
63 #define EM_NOTMP	"N4"	/* remote error - can't create temp */
64 #define EM_RMTCP	"N5"	/* can't copy to remote directory - file in public */
65 #define EM_LOCCP	"N6"	/* can't copy on local system */
66 
67 char *Em_msg[] = {
68 	"COPY FAILED (reason not given by remote)",
69 	"local access to file denied",
70 	"remote access to path/file denied",
71 	"system error - bad uucp command generated",
72 	"remote system can't create temp file",
73 	"can't copy to file/directory - file left in PUBDIR/user/file",
74 	"can't copy to file/directory on local system  - file left in PUBDIR/user/file"
75 };
76 
77 
78 #define XUUCP 'X'	/* execute uucp (string) */
79 #define SLTPTCL 'P'	/* select protocol  (string)  */
80 #define USEPTCL 'U'	/* use protocol (character) */
81 #define RCVFILE 'R'	/* receive file (string) */
82 #define SNDFILE 'S'	/* send file (string) */
83 #define RQSTCMPT 'C'	/* request complete (string - yes | no) */
84 #define HUP     'H'	/* ready to hangup (string - yes | no) */
85 #define RESET	'X'	/* reset line modes */
86 
87 #define W_TYPE		wrkvec[0]
88 #define W_FILE1		wrkvec[1]
89 #define W_FILE2		wrkvec[2]
90 #define W_USER		wrkvec[3]
91 #define W_OPTNS		wrkvec[4]
92 #define W_DFILE		wrkvec[5]
93 #define W_MODE		wrkvec[6]
94 #define W_NUSER		wrkvec[7]
95 
96 #define	XFRRATE	35000L
97 #define RMESG(m, s, n) if (rmesg(m, s, n) != 0) {(*Turnoff)(); return FAIL;} else
98 #define RAMESG(s, n) if (rmesg('\0', s, n) != 0) {(*Turnoff)(); return FAIL;} else
99 #define WMESG(m, s) if(wmesg(m, s) != 0) {(*Turnoff)(); return FAIL;} else
100 
101 char Wfile[MAXFULLNAME] = {'\0'};
102 char Dfile[MAXFULLNAME];
103 
104 /*
105  * To avoid a huge backlog of X. files, start uuxqt every so often.
106  */
107 static int nXfiles = 0;	/* number of X files since last uuxqt start */
108 static int nXQTs = 0;	/* number of uuxqts started */
109 static char send_or_receive;
110 struct stat stbuf;
111 
112 /*
113  *	cntrl  -  this routine will execute the conversation
114  *	between the two machines after both programs are
115  *	running.
116  *
117  *	return codes
118  *		SUCCESS - ok
119  *		FAIL - failed
120  */
121 
122 cntrl(role, wkpre)
123 int role;
124 char *wkpre;
125 {
126 	char msg[BUFSIZ], rqstr[BUFSIZ];
127 	register FILE *fp;
128 	int filemode;
129 	char filename[MAXFULLNAME], wrktype, *wrkvec[20];
130 	extern (*Rdmsg)(), (*Wrmsg)();
131 	extern char *index(), *lastpart();
132 	int status = 1;
133 	register int i, narg;
134 	int mailopt, ntfyopt;
135 	int ret;
136 	static int pnum, tmpnum = 0;
137 	extern int ReverseRole;
138 
139 	pnum = getpid();
140 	Wfile[0] = '\0';
141 top:
142 	for (i = 0; i < sizeof wrkvec / sizeof wrkvec[0]; i++)
143 		wrkvec[i] = 0;
144 	DEBUG(4, "*** TOP ***  -  role=%s\n", role ? "MASTER" : "SLAVE");
145 	setline(RESET);
146 	send_or_receive = RESET;
147 	if (role == MASTER) {
148 		/* get work */
149 		if (ReverseRole || (narg = gtwvec(Wfile, Spool, wkpre, wrkvec)) == 0) {
150 			ReverseRole = 0;
151 			WMESG(HUP, "");
152 			RMESG(HUP, msg, 1);
153 			goto process;
154 		}
155 		wrktype = W_TYPE[0];
156 
157 		msg[0] = '\0';
158 		for (i = 1; i < narg; i++) {
159 			strcat(msg, " ");
160 			strcat(msg, wrkvec[i]);
161 		}
162 
163 		if (wrktype == XUUCP) {
164 			sprintf(rqstr, "X %s", msg);
165 			logent(rqstr, "REQUEST");
166 			goto sendmsg;
167 		}
168 		mailopt = index(W_OPTNS, 'm') != NULL;
169 		ntfyopt = index(W_OPTNS, 'n') != NULL;
170 
171 		if (narg < 5) {
172 			char *bnp;
173 			bnp = rindex(Wfile, '/');
174 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
175 			xmv(Wfile, rqstr);
176 			logent(Wfile, "CMD FILE CORRUPTED");
177 			Wfile[0] = '\0';
178 			goto top;
179 		}
180 		sprintf(User, "%.9s", W_USER);
181 		sprintf(rqstr, "%s %s %s %s", W_TYPE, W_FILE1,
182 		  W_FILE2, W_USER);
183 		logent(rqstr, "REQUEST");
184 		if (wrktype == SNDFILE ) {
185 			strcpy(filename, W_FILE1);
186 			i = expfile(filename);
187 			DEBUG(4, "expfile type - %d, ", i);
188 			if (i != 0 && chkpth(User, "", filename))
189 				goto e_access;
190 			strcpy(Dfile, W_DFILE);
191 			fp = NULL;
192 			if (index(W_OPTNS, 'c') == NULL) {
193 				fp = fopen(subfile(Dfile), "r");
194 				if (fp != NULL)
195 					i = 0;
196 			}
197 			if (fp == NULL &&
198 			   (fp = fopen(subfile(filename), "r")) == NULL) {
199 				/*  can not read data file  */
200 				logent("CAN'T READ DATA", _FAILED);
201 				USRF(USR_LOCACC);
202 				unlinkdf(Dfile);
203 				lnotify(User, filename, "can't access");
204 				goto top;
205 			}
206 			/* if file exists but is not generally readable... */
207 			if (i != 0 && fstat(fileno(fp), &stbuf) == 0
208 			&&  (stbuf.st_mode & ANYREAD) == 0) {
209 		e_access:;
210 				/*  access denied  */
211 				fclose(fp);
212 				fp = NULL;
213 				logent("DENIED", "ACCESS");
214 				USRF(USR_LOCACC);
215 				unlinkdf(W_DFILE);
216 				lnotify(User, filename, "access denied");
217 				goto top;
218 			}
219 
220 			setline(SNDFILE);
221 		}
222 
223 		if (wrktype == RCVFILE) {
224 			strcpy(filename, W_FILE2);
225 			expfile(filename);
226 			if (chkpth(User, "", filename)
227 			 || chkperm(filename, index(W_OPTNS, 'd'))) {
228 				/*  access denied  */
229 				logent("DENIED", "ACCESS");
230 				USRF(USR_LOCACC);
231 				lnotify(User, filename, "access denied");
232 				goto top;
233 			}
234 			sprintf(Dfile, "%s/TM.%05d.%03d", Spool, pnum, tmpnum++);
235 			if ((fp = fopen(subfile(Dfile), "w")) == NULL) {
236 				/*  can not create temp  */
237 				logent("CAN'T CREATE TM", _FAILED);
238 				USRF(USR_LNOTMP);
239 				unlinkdf(Dfile);
240 				goto top;
241 			}
242 			setline(RCVFILE);
243 		}
244 sendmsg:
245 		DEBUG(4, "wrktype - %c\n", wrktype);
246 		WMESG(wrktype, msg);
247 		RMESG(wrktype, msg, 1);
248 		goto process;
249 	}
250 
251 	/* role is slave */
252 	RAMESG(msg, 1);
253 	goto process;
254 
255 process:
256 	DEBUG(4, "PROCESS: msg - %s\n", msg);
257 	switch (msg[0]) {
258 
259 	case RQSTCMPT:
260 		DEBUG(4, "RQSTCMPT:\n", CNULL);
261 		if (msg[1] == 'N') {
262 			i = atoi(&msg[2]);
263 			if (i<0 || i>EM_MAX) i=0;
264 			USRF( 1 << i );
265 				i = 0;
266 			logent(Em_msg[i], "REQUEST FAILED");
267 			if (strcmp(&msg[1], EM_NOTMP) == 0) {
268 				/* dont send him files he can't save */
269 				WMESG(HUP, "");
270 				RMESG(HUP, msg, 1);
271 				goto process;
272 			}
273 		}
274 		if (msg[1] == 'Y')
275 			USRF(USR_COK);
276 		if (role == MASTER) {
277 			notify(mailopt, W_USER, W_FILE1, Rmtname, &msg[1]);
278 		}
279 		goto top;
280 
281 	case HUP:
282 		DEBUG(4, "HUP:\n", CNULL);
283 		if (msg[1] == 'Y') {
284 			if (role == MASTER)
285 				WMESG(HUP, YES);
286 			(*Turnoff)();
287 			Rdmsg = Imsg;
288 			Wrmsg = Omsg;
289 			return SUCCESS;
290 		}
291 
292 		if (msg[1] == 'N') {
293 			ASSERT(role == MASTER, "WRONG ROLE - HUP", CNULL, role);
294 			role = SLAVE;
295 			goto top;
296 		}
297 
298 		/* get work */
299 		if (!iswrk(Wfile, "chk", Spool, wkpre)) {
300 			WMESG(HUP, YES);
301 			RMESG(HUP, msg, 1);
302 			goto process;
303 		}
304 
305 		WMESG(HUP, NO);
306 		role = MASTER;
307 		goto top;
308 
309 	case XUUCP:
310 		if (role == MASTER) {
311 			goto top;
312 		}
313 
314 		/*  slave part  */
315 		i = getargs(msg, wrkvec, 20);
316 		strcpy(filename, W_FILE1);
317 		if (index(filename, ';') != NULL || index(W_FILE2, ';') != NULL
318 		    || i < 3) {
319 			WMESG(XUUCP, NO);
320 			goto top;
321 		}
322 		expfile(filename);
323 		if (chkpth("", Rmtname, filename)) {
324 			WMESG(XUUCP, NO);
325 			logent("XUUCP DENIED", filename);
326 				USRF(USR_XUUCP);
327 			goto top;
328 		}
329 		sprintf(rqstr, "%s %s", filename, W_FILE2);
330 		xuucp(rqstr);
331 		WMESG(XUUCP, YES);
332 		goto top;
333 
334 	case SNDFILE:
335 		/*  MASTER section of SNDFILE  */
336 
337 		DEBUG(4, "%s\n", "SNDFILE:");
338 		if (msg[1] == 'N') {
339 			i = atoi(&msg[2]);
340 			if (i < 0 || i > EM_MAX)
341 				i = 0;
342 			logent(Em_msg[i], "REQUEST FAILED");
343 			USRF( 1 << i );
344 			fclose(fp);
345 			fp = NULL;
346 			if (strcmp(&msg[1], EM_NOTMP) == 0) {
347 				/* dont send him files he can't save */
348 				WMESG(HUP, "");
349 				RMESG(HUP, msg, 1);
350 				goto process;
351 			}
352 			notify(mailopt, W_USER, W_FILE1, Rmtname, &msg[1]);
353 			ASSERT(role == MASTER, "WRONG ROLE - SN", CNULL, role);
354 			if (msg[1] != '4')
355 				unlinkdf(W_DFILE);
356 			goto top;
357 		}
358 
359 		if (msg[1] == 'Y') {
360 			/* send file */
361 			ASSERT(role == MASTER, "WRONG ROLE - SY", CNULL, role);
362 			ret = fstat(fileno(fp), &stbuf);
363 			ASSERT(ret != -1, "STAT FAILED", filename, 0);
364 			i = 1 + (int)(stbuf.st_size / XFRRATE);
365 			if (send_or_receive != SNDFILE) {
366 				send_or_receive = SNDFILE;
367 				systat(Rmtname, SS_INPROGRESS, "SENDING");
368 			}
369 			ret = (*Wrdata)(fp, Ofn);
370 			fclose(fp);
371 			fp = NULL;
372 			if (ret != SUCCESS) {
373 				(*Turnoff)();
374 				USRF(USR_CFAIL);
375 				return FAIL;
376 			}
377 			RMESG(RQSTCMPT, msg, i);
378 			unlinkdf(W_DFILE);
379 			goto process;
380 		}
381 
382 		/*  SLAVE section of SNDFILE  */
383 		ASSERT(role == SLAVE, "WRONG ROLE - SLAVE", CNULL, role);
384 
385 		/* request to receive file */
386 		/* check permissions */
387 		i = getargs(msg, wrkvec, 20);
388 		if (i < 5) {
389 			char *bnp;
390 			bnp = rindex(Wfile, '/');
391 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
392 			xmv(Wfile, rqstr);
393 			logent(Wfile, "CMD FILE CORRUPTED");
394 			Wfile[0] = '\0';
395 			goto top;
396 		}
397 		sprintf(rqstr, "%s %s %s %s", W_TYPE, W_FILE1, W_FILE2, W_USER);
398 		logent(rqstr, "REQUESTED");
399 		DEBUG(4, "msg - %s\n", msg);
400 		strcpy(filename, W_FILE2);
401 		/* Run uuxqt occasionally */
402 		if (filename[0] == XQTPRE) {
403 			if (++nXfiles > 10) {
404 				nXfiles = 0;
405 				/*
406 				 * want to create an orphan uuxqt,
407 				 * so a double-fork is needed.
408 				 */
409 				if (fork() == 0) {
410 					xuuxqt();
411 					_exit(0);
412 				}
413 				wait((int *)0);
414 			}
415 		}
416 		/* expand filename, i is set to 0 if this is
417 		 * is a vanilla spool file, so no stat(II)s are needed */
418 		i = expfile(filename);
419 		DEBUG(4, "expfile type - %d\n", i);
420 		if (i != 0) {
421 			if (chkpth("", Rmtname, filename)
422 			 || chkperm(filename, index(W_OPTNS, 'd'))) {
423 				WMESG(SNDFILE, EM_RMTACC);
424 				logent("DENIED", "PERMISSION");
425 				goto top;
426 			}
427 			if (isdir(filename)) {
428 				strcat(filename, "/");
429 				strcat(filename, lastpart(W_FILE1));
430 			}
431 		}
432 		sprintf(User, "%.9s", W_USER);
433 
434 		DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname);
435 		/* speed things up by OKing file before
436 		 * creating TM file.  If the TM file cannot be created,
437 		 * then the conversation bombs, but that seems reasonable,
438 		 * as there are probably serious problems then.
439 		 */
440 		WMESG(SNDFILE, YES);
441 		sprintf(Dfile, "%s/TM.%05d.%03d", Spool, pnum, tmpnum++);
442 		if((fp = fopen(subfile(Dfile), "w")) == NULL) {
443 /*			WMESG(SNDFILE, EM_NOTMP);*/
444 			logent("CAN'T OPEN", "TM FILE");
445 			unlinkdf(Dfile);
446 			(*Turnoff)();
447 			return FAIL;
448 		}
449 
450 		if (send_or_receive != RCVFILE) {
451 			send_or_receive = RCVFILE;
452 			systat(Rmtname, SS_INPROGRESS, "RECEIVING");
453 		}
454 		ret = (*Rddata)(Ifn, fp);
455 		fflush(fp);
456 		if (ferror(fp) || fclose(fp))
457 			ret = FAIL;
458 		if (ret != SUCCESS) {
459 			(void) unlinkdf(Dfile);
460 			(*Turnoff)();
461 			return FAIL;
462 		}
463 		/* copy to user directory */
464 		ntfyopt = index(W_OPTNS, 'n') != NULL;
465 		status = xmv(Dfile, filename);
466 		WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
467 		if (i == 0)
468 			;	/* vanilla file, nothing to do */
469 		else if (status == 0) {
470 			if (W_MODE == 0 || sscanf(W_MODE, "%o", &filemode) != 1)
471 				filemode = BASEMODE;
472 			chmod(subfile(filename), (filemode|BASEMODE)&0777);
473 			arrived(ntfyopt, filename, W_NUSER, Rmtname, User);
474 		} else {
475 			logent(_FAILED, "COPY");
476 			status = putinpub(filename, Dfile, W_USER);
477 			DEBUG(4, "->PUBDIR %d\n", status);
478 			if (status == 0)
479 				arrived(ntfyopt, filename, W_NUSER, Rmtname, User);
480 		}
481 
482 		goto top;
483 
484 	case RCVFILE:
485 		/*  MASTER section of RCVFILE  */
486 
487 		DEBUG(4, "%s\n", "RCVFILE:");
488 		if (msg[1] == 'N') {
489 			i = atoi(&msg[2]);
490 			if (i < 0 || i > EM_MAX)
491 				i = 0;
492 			logent(Em_msg[i], "REQUEST FAILED");
493 			USRF( 1 << i );
494 			fclose(fp);
495 			fp = NULL;
496 			if (strcmp(&msg[1], EM_NOTMP) == 0) {
497 				/* dont send him files he can't save */
498 				WMESG(HUP, "");
499 				RMESG(HUP, msg, 1);
500 				goto process;
501 			}
502 			notify(mailopt, W_USER, W_FILE1, Rmtname, &msg[1]);
503 			ASSERT(role == MASTER, "WRONG ROLE - RN", CNULL, role);
504 			unlinkdf(Dfile);
505 			goto top;
506 		}
507 
508 		if (msg[1] == 'Y') {
509 			/* receive file */
510 			ASSERT(role == MASTER, "WRONG ROLE - RY", CNULL, role);
511 			if (send_or_receive != RCVFILE) {
512 				send_or_receive = RCVFILE;
513 				systat(Rmtname, SS_INPROGRESS, "RECEIVING");
514 			}
515 			ret = (*Rddata)(Ifn, fp);
516 			fflush(fp);
517 			if (ferror(fp) || fclose(fp))
518 				ret = FAIL;
519 			if (ret != SUCCESS) {
520 				unlinkdf(Dfile);
521 				(*Turnoff)();
522 				USRF(USR_CFAIL);
523 				return FAIL;
524 			}
525 			/* copy to user directory */
526 			if (isdir(filename)) {
527 				strcat(filename, "/");
528 				strcat(filename, lastpart(W_FILE1));
529 			}
530 			status = xmv(Dfile, filename);
531 			WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
532 			notify(mailopt, W_USER, filename, Rmtname,
533 			  status ? EM_LOCCP : YES);
534 			if (status == 0) {
535 				sscanf(&msg[2], "%o", &filemode);
536 				if (filemode <= 0)
537 					filemode = BASEMODE;
538 				chmod(subfile(filename), (filemode|BASEMODE)&0777);
539 				USRF(USR_COK);
540 			}
541 			else {
542 				logent(_FAILED, "COPY");
543 				putinpub(filename, Dfile, W_USER);
544 				USRF(USR_LOCCP);
545 			}
546 			goto top;
547 		}
548 
549 		/*  SLAVE section of RCVFILE  */
550 		ASSERT(role == SLAVE, "WRONG ROLE - SLAVE RCV", CNULL, role);
551 
552 		/* request to send file */
553 		strcpy(rqstr, msg);
554 		logent(rqstr, "REQUESTED");
555 
556 		/* check permissions */
557 		i = getargs(msg, wrkvec, 20);
558 		if (i < 4) {
559 			char *bnp;
560 			bnp = rindex(Wfile, '/');
561 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
562 			xmv(Wfile, rqstr);
563 			logent(Wfile, "CMD FILE CORRUPTED");
564 			Wfile[0] = '\0';
565 			goto top;
566 		}
567 		DEBUG(4, "msg - %s\n", msg);
568 		DEBUG(4, "W_FILE1 - %s\n", W_FILE1);
569 		strcpy(filename, W_FILE1);
570 		expfile(filename);
571 		if (isdir(filename)) {
572 			strcat(filename, "/");
573 			strcat(filename, lastpart(W_FILE2));
574 		}
575 		sprintf(User, "%.9s", W_USER);
576 		if (chkpth("", Rmtname, filename) || anyread(filename)) {
577 			WMESG(RCVFILE, EM_RMTACC);
578 			logent("DENIED", "PERMISSION");
579 			goto top;
580 		}
581 		DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname);
582 
583 		if ((fp = fopen(subfile(filename), "r")) == NULL) {
584 			WMESG(RCVFILE, EM_RMTACC);
585 			logent("CAN'T OPEN", "DENIED");
586 			goto top;
587 		}
588 
589 		/*  ok to send file */
590 		ret = fstat(fileno(fp), &stbuf);
591 		ASSERT(ret != -1, "STAT FAILED", filename, 0);
592 		i = 1 + (int)(stbuf.st_size / XFRRATE);
593 		sprintf(msg, "%s %o", YES, stbuf.st_mode & 0777);
594 		WMESG(RCVFILE, msg);
595 		if (send_or_receive != SNDFILE) {
596 			send_or_receive = SNDFILE;
597 			systat(Rmtname, SS_INPROGRESS, "SENDING");
598 		}
599 		ret = (*Wrdata)(fp, Ofn);
600 		fclose(fp);
601 		if (ret != SUCCESS) {
602 			(*Turnoff)();
603 			return FAIL;
604 		}
605 		RMESG(RQSTCMPT, msg, i);
606 		goto process;
607 	}
608 	(*Turnoff)();
609 	return FAIL;
610 }
611 
612 
613 /***
614  *	rmesg(c, msg, n)	read message 'c'
615  *				try 'n' times
616  *	char *msg, c;
617  *
618  *	return code:  0  |  FAIL
619  */
620 
621 rmesg(c, msg, n)
622 register char *msg, c;
623 register int n;
624 {
625 	char str[128];
626 
627 	DEBUG(4, "rmesg - '%c' ", c);
628 	while ((*Rdmsg)(msg, Ifn) != SUCCESS) {
629 		if (--n > 0) {
630 			sprintf(str, "%d", n);
631 			logent(str, "PATIENCE");
632 			continue;
633 		}
634 		DEBUG(4, "got FAIL\n", CNULL);
635 		if (c != '\0')
636 			sprintf(str, "expected '%c' got FAIL (%d)", c, errno);
637 		else
638 			sprintf(str, "expected ANY got FAIL (%d)", errno);
639 		logent(str, "BAD READ");
640 		return FAIL;
641 	}
642 	if (c != '\0' && msg[0] != c) {
643 		DEBUG(4, "got %s\n", msg);
644 		sprintf(str, "expected '%c' got %s", c, msg);
645 		logent(str, "BAD READ");
646 		return FAIL;
647 	}
648 	DEBUG(4, "got %s\n", msg);
649 	return SUCCESS;
650 }
651 
652 
653 /***
654  *	wmesg(m, s)	write a message (type m)
655  *	char *s, m;
656  *
657  *	return codes: 0 - ok | FAIL - ng
658  */
659 
660 wmesg(m, s)
661 register char *s, m;
662 {
663 	DEBUG(4, "wmesg '%c' ", m);
664 	DEBUG(4, "%s\n", s);
665 	return (*Wrmsg)(m, s, Ofn);
666 }
667 
668 
669 /***
670  *	notify		mail results of command
671  *
672  *	return codes:  none
673  */
674 
675 notify(mailopt, user, file, sys, msgcode)
676 char *user, *file, *sys, *msgcode;
677 {
678 	char str[200];
679 	int i;
680 	char *msg;
681 
682 	if (!mailopt && *msgcode == 'Y')
683 		return;
684 	if (*msgcode == 'Y')
685 		msg = "copy succeeded";
686 	else {
687 		i = atoi(msgcode + 1);
688 		if (i < 1 || i > EM_MAX)
689 			i = 0;
690 		msg = Em_msg[i];
691 	}
692 	sprintf(str, "file %s!%s -- %s\n",
693 		sys,file, msg);
694 	mailst(user, str, CNULL);
695 	return;
696 }
697 
698 /***
699  *	lnotify(user, file, mesg)	- local notify
700  *
701  *	return code - none
702  */
703 
704 lnotify(user, file, mesg)
705 char *user, *file, *mesg;
706 {
707 	char mbuf[200];
708 	sprintf(mbuf, "file %s!%s -- %s\n", Myname, file, mesg);
709 	mailst(user, mbuf, CNULL);
710 	return;
711 }
712 
713 
714 /***
715  *	startup(role)
716  *	int role;
717  *
718  *	startup  -  this routine will converse with the remote
719  *	machine, agree upon a protocol (if possible) and start the
720  *	protocol.
721  *
722  *	return codes:
723  *		SUCCESS - successful protocol selection
724  *		FAIL - can't find common or open failed
725  */
726 
727 startup(role)
728 int role;
729 {
730 	extern (*Rdmsg)(), (*Wrmsg)();
731 	extern char *blptcl(), fptcl();
732 	char msg[BUFSIZ], str[MAXFULLNAME];
733 
734 	Rdmsg = Imsg;
735 	Wrmsg = Omsg;
736 	if (role == MASTER) {
737 		RMESG(SLTPTCL, msg, 1);
738 		if ((str[0] = fptcl(&msg[1])) == NULL) {
739 			/* no protocol match */
740 			WMESG(USEPTCL, NO);
741 			return FAIL;
742 		}
743 		str[1] = '\0';
744 		WMESG(USEPTCL, str);
745 		if (stptcl(str) != 0)
746 			return FAIL;
747 		DEBUG(4, "protocol %s\n", str);
748 		return SUCCESS;
749 	}
750 	else {
751 		WMESG(SLTPTCL, blptcl(str));
752 		RMESG(USEPTCL, msg, 1);
753 		if (msg[1] == 'N') {
754 			return FAIL;
755 		}
756 
757 		if (stptcl(&msg[1]) != 0)
758 			return FAIL;
759 		DEBUG(4, "Protocol %s\n", msg);
760 		return SUCCESS;
761 	}
762 }
763 
764 
765 /*******
766  *	char
767  *	fptcl(str)
768  *	char *str;
769  *
770  *	fptcl  -  this routine will choose a protocol from
771  *	the input string (str) and return the found letter.
772  *
773  *	return codes:
774  *		'\0'  -  no acceptable protocol
775  *		any character  -  the chosen protocol
776  */
777 
778 char
779 fptcl(str)
780 register char *str;
781 {
782 	register struct Proto *p;
783 	extern char *Flds[];
784 
785 	for (p = Ptbl; p->P_id != '\0'; p++) {
786 #ifdef BSDTCP
787 		if (!IsTcpIp && p->P_id == 't')	/* Only use 't' on TCP/IP */
788 			continue;
789 #endif BSDTCP
790 		/* only use 'f' protocol on PAD */
791 		if (strcmp("PAD", Flds[F_LINE]) && p->P_id == 'f')
792 			continue;
793 		if (index(str, p->P_id) != NULL) {
794 			return p->P_id;
795 		}
796 	}
797 
798 	return '\0';
799 }
800 
801 
802 /***
803  *	char *
804  *	blptcl(str)
805  *	char *str;
806  *
807  *	blptcl  -  this will build a string of the
808  *	letters of the available protocols and return
809  *	the string (str).
810  *
811  *	return:
812  *		a pointer to string (str)
813  */
814 
815 char *
816 blptcl(str)
817 register char *str;
818 {
819 	register struct Proto *p;
820 	register char *s;
821 
822 	for (p = Ptbl, s = str; (*s++ = p->P_id) != '\0'; p++)
823 		;
824 	*s = '\0';
825 	return str;
826 }
827 
828 /***
829  *	stptcl(c)
830  *	char *c;
831  *
832  *	stptcl  -  this routine will set up the six routines
833  *	(Rdmsg, Wrmsg, Rddata, Wrdata, Turnon, Turnoff) for the
834  *	desired protocol.
835  *
836  *	return codes:
837  *		SUCCESS - ok
838  *		FAIL - no find or failed to open
839  *
840  */
841 
842 stptcl(c)
843 register char *c;
844 {
845 	register struct Proto *p;
846 
847 	for (p = Ptbl; p->P_id != '\0'; p++) {
848 		if (*c == p->P_id) {
849 			/* found protocol - set routines */
850 			Rdmsg = p->P_rdmsg;
851 			Wrmsg = p->P_wrmsg;
852 			Rddata = p->P_rddata;
853 			Wrdata = p->P_wrdata;
854 			Turnon = p->P_turnon;
855 			Turnoff = p->P_turnoff;
856 			if ((*Turnon)() != SUCCESS)
857 				return FAIL;
858 			DEBUG(4, "Proto started %c\n", *c);
859 			return SUCCESS;
860 		}
861 	}
862 	DEBUG(4, "Proto start-fail %c\n", *c);
863 	return FAIL;
864 }
865 
866 /***
867  *	putinpub	put file in public place
868  *			if successful, filename is modified
869  *
870  *	return code  0 | FAIL
871  */
872 
873 putinpub(file, tmp, user)
874 register char *file, *user, *tmp;
875 {
876 	char fullname[MAXFULLNAME];
877 	char *lastpart();
878 	int status;
879 
880 	sprintf(fullname, "%s/%s/", PUBDIR, user);
881 	if (mkdirs(fullname) != 0) {
882 		/* can not make directories */
883 		return FAIL;
884 	}
885 	strcat(fullname, lastpart(file));
886 	status = xmv(tmp, fullname);
887 	if (status == 0) {
888 		strcpy(file, fullname);
889 		chmod(subfile(fullname), BASEMODE);
890 	}
891 	return status;
892 }
893 
894 /***
895  *	unlinkdf(file)	- unlink D. file
896  *
897  *	return code - none
898  */
899 
900 unlinkdf(file)
901 register char *file;
902 {
903 	if (strlen(file) > 6)
904 		unlink(subfile(file));
905 	return;
906 }
907 
908 /***
909  *	arrived - notify receiver of arrived file
910  *
911  *	return code - none
912  */
913 
914 arrived(opt, file, nuser, rmtsys, rmtuser)
915 char *file, *nuser, *rmtsys, *rmtuser;
916 {
917 	char mbuf[200];
918 
919 	if (!opt)
920 		return;
921 	sprintf(mbuf, "%s from %s!%s arrived\n", file, rmtsys, rmtuser);
922 	mailst(nuser, mbuf, CNULL);
923 	return;
924 }
925