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