xref: /original-bsd/usr.bin/uucp/uucico/cntrl.c (revision a9c19d04)
1 #ifndef lint
2 static char sccsid[] = "@(#)cntrl.c	5.8 (Berkeley) 01/24/86";
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) {
197 			char *bnp;
198 			bnp = rindex(Wfile, '/');
199 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
200 			xmv(Wfile, rqstr);
201 			logent(Wfile, "CMD FILE CORRUPTED");
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 			if (msg[1] != '4')
396 				unlinkdf(W_DFILE);
397 			goto top;
398 		}
399 
400 		if (msg[1] == 'Y') {
401 			/* send file */
402 			ASSERT(role == MASTER, "WRONG ROLE - SY", CNULL, role);
403 			ret = fstat(fileno(fp), &stbuf);
404 			ASSERT(ret != -1, "STAT FAILED", filename, 0);
405 			i = 1 + (int)(stbuf.st_size / XFRRATE);
406 			if (send_or_receive != SNDFILE) {
407 				send_or_receive = SNDFILE;
408 				systat(Rmtname, SS_INPROGRESS, "SENDING");
409 			}
410 			ret = (*Wrdata)(fp, Ofn);
411 			fclose(fp);
412 			fp = NULL;
413 			if (ret != SUCCESS) {
414 				(*Turnoff)();
415 				USRF(USR_CFAIL);
416 				return FAIL;
417 			}
418 			RMESG(RQSTCMPT, msg, i);
419 			unlinkdf(W_DFILE);
420 			goto process;
421 		}
422 
423 		/*  SLAVE section of SNDFILE  */
424 		ASSERT(role == SLAVE, "WRONG ROLE - SLAVE", CNULL, role);
425 
426 		/* request to receive file */
427 		/* check permissions */
428 		i = getargs(msg, wrkvec, 20);
429 		if (i < 5) {
430 			char *bnp;
431 			bnp = rindex(Wfile, '/');
432 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
433 			xmv(Wfile, rqstr);
434 			logent(Wfile, "CMD FILE CORRUPTED");
435 			Wfile[0] = '\0';
436 			goto top;
437 		}
438 		sprintf(rqstr, "%s %s %s %s", W_TYPE, W_FILE1, W_FILE2, W_USER);
439 		logent(rqstr, "REQUESTED");
440 		DEBUG(4, "msg - %s\n", msg);
441 		strcpy(filename, W_FILE2);
442 		/* Run uuxqt occasionally */
443 		if (filename[0] == XQTPRE) {
444 			if (++nXfiles > 10) {
445 				nXfiles = 0;
446 				/*
447 				 * want to create an orphan uuxqt,
448 				 * so a double-fork is needed.
449 				 */
450 				if (fork() == 0) {
451 					xuuxqt();
452 					_exit(0);
453 				}
454 				wait((int *)0);
455 			}
456 		}
457 		/* expand filename, i is set to 0 if this is
458 		 * is a vanilla spool file, so no stat(II)s are needed */
459 		i = expfile(filename);
460 		DEBUG(4, "expfile type - %d\n", i);
461 		if (i != 0) {
462 			if (chkpth("", Rmtname, filename)
463 			 || chkperm(filename, index(W_OPTNS, 'd'))) {
464 				WMESG(SNDFILE, EM_RMTACC);
465 				logent("DENIED", "PERMISSION");
466 				goto top;
467 			}
468 			if (isdir(filename)) {
469 				strcat(filename, "/");
470 				strcat(filename, lastpart(W_FILE1));
471 			}
472 		}
473 		sprintf(User, "%.9s", W_USER);
474 
475 		DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname);
476 		/* speed things up by OKing file before
477 		 * creating TM file.  If the TM file cannot be created,
478 		 * then the conversation bombs, but that seems reasonable,
479 		 * as there are probably serious problems then.
480 		 */
481 		WMESG(SNDFILE, YES);
482 		sprintf(Dfile, "%s/TM.%05d.%03d", Spool, pnum, tmpnum++);
483 		if((fp = fopen(subfile(Dfile), "w")) == NULL) {
484 /*			WMESG(SNDFILE, EM_NOTMP);*/
485 			logent("CAN'T OPEN", "TM FILE");
486 			unlinkdf(Dfile);
487 			(*Turnoff)();
488 			return FAIL;
489 		}
490 
491 		if (send_or_receive != RCVFILE) {
492 			send_or_receive = RCVFILE;
493 			systat(Rmtname, SS_INPROGRESS, "RECEIVING");
494 		}
495 		ret = (*Rddata)(Ifn, fp);
496 		fflush(fp);
497 		if (ferror(fp) || fclose(fp))
498 			ret = FAIL;
499 
500 		if (ret != SUCCESS) {
501 			(void) unlinkdf(Dfile);
502 			(*Turnoff)();
503 			return FAIL;
504 		}
505 		/* copy to user directory */
506 		ntfyopt = index(W_OPTNS, 'n') != NULL;
507 		status = xmv(Dfile, filename);
508 
509 		if (willturn && Now.time > (LastTurned.time+turntime)
510 			&& iswrk(Wfile, "chk", Spool, wkpre)) {
511 				WMESG(RQSTCMPT, status ? EM_RMTCP : "YM");
512 				willturn = -1;
513 		} else
514 			WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
515 		if (i == 0)
516 			;	/* vanilla file, nothing to do */
517 		else if (status == 0) {
518 			if (W_MODE == 0 || sscanf(W_MODE, "%o", &filemode) != 1)
519 				filemode = BASEMODE;
520 			chmod(subfile(filename), (filemode|BASEMODE)&0777);
521 			arrived(ntfyopt, filename, W_NUSER, Rmtname, User);
522 		} else {
523 			logent(_FAILED, "COPY");
524 			status = putinpub(filename, Dfile, W_USER);
525 			DEBUG(4, "->PUBDIR %d\n", status);
526 			if (status == 0)
527 				arrived(ntfyopt, filename, W_NUSER, Rmtname, User);
528 		}
529 
530 		goto top;
531 
532 	case RCVFILE:
533 		/*  MASTER section of RCVFILE  */
534 
535 		DEBUG(4, "%s\n", "RCVFILE:");
536 		if (msg[1] == 'N') {
537 			i = atoi(&msg[2]);
538 			if (i < 0 || i > EM_MAX)
539 				i = 0;
540 			logent(Em_msg[i], "REQUEST FAILED");
541 			USRF( 1 << i );
542 			fclose(fp);
543 			fp = NULL;
544 			notify(mailopt, W_USER, W_FILE1, Rmtname, &msg[1]);
545 			ASSERT(role == MASTER, "WRONG ROLE - RN", CNULL, role);
546 			unlinkdf(Dfile);
547 			goto top;
548 		}
549 
550 		if (msg[1] == 'Y') {
551 			/* receive file */
552 			ASSERT(role == MASTER, "WRONG ROLE - RY", CNULL, role);
553 			if (send_or_receive != RCVFILE) {
554 				send_or_receive = RCVFILE;
555 				systat(Rmtname, SS_INPROGRESS, "RECEIVING");
556 			}
557 			ret = (*Rddata)(Ifn, fp);
558 			fflush(fp);
559 			if (ferror(fp) || fclose(fp))
560 				ret = FAIL;
561 			if (ret != SUCCESS) {
562 				unlinkdf(Dfile);
563 				(*Turnoff)();
564 				USRF(USR_CFAIL);
565 				return FAIL;
566 			}
567 			/* copy to user directory */
568 			if (isdir(filename)) {
569 				strcat(filename, "/");
570 				strcat(filename, lastpart(W_FILE1));
571 			}
572 			status = xmv(Dfile, filename);
573 			if (willturn && Now.time > (LastTurned.time+turntime)
574 				&& iswrk(Wfile, "chk", Spool, wkpre)) {
575 					WMESG(RQSTCMPT, status ? EM_RMTCP : "YM");
576 					willturn = -1;
577 			} else
578 				WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
579 			notify(mailopt, W_USER, filename, Rmtname,
580 				status ? EM_LOCCP : YES);
581 			if (status == 0) {
582 				sscanf(&msg[2], "%o", &filemode);
583 				if (filemode <= 0)
584 					filemode = BASEMODE;
585 				chmod(subfile(filename), (filemode|BASEMODE)&0777);
586 				USRF(USR_COK);
587 			} else {
588 				logent(_FAILED, "COPY");
589 				putinpub(filename, Dfile, W_USER);
590 				USRF(USR_LOCCP);
591 			}
592 			goto top;
593 		}
594 
595 		/*  SLAVE section of RCVFILE  */
596 		ASSERT(role == SLAVE, "WRONG ROLE - SLAVE RCV", CNULL, role);
597 
598 		/* request to send file */
599 		strcpy(rqstr, msg);
600 		logent(rqstr, "REQUESTED");
601 
602 		/* check permissions */
603 		i = getargs(msg, wrkvec, 20);
604 		if (i < 4) {
605 			char *bnp;
606 			bnp = rindex(Wfile, '/');
607 			sprintf(rqstr, "%s/%s", CORRUPT, bnp ? bnp + 1 : Wfile);
608 			xmv(Wfile, rqstr);
609 			logent(Wfile, "CMD FILE CORRUPTED");
610 			Wfile[0] = '\0';
611 			goto top;
612 		}
613 		DEBUG(4, "msg - %s\n", msg);
614 		DEBUG(4, "W_FILE1 - %s\n", W_FILE1);
615 		strcpy(filename, W_FILE1);
616 		expfile(filename);
617 		if (isdir(filename)) {
618 			strcat(filename, "/");
619 			strcat(filename, lastpart(W_FILE2));
620 		}
621 		sprintf(User, "%.9s", W_USER);
622 		if (chkpth("", Rmtname, filename) || anyread(filename)) {
623 			WMESG(RCVFILE, EM_RMTACC);
624 			logent("DENIED", "PERMISSION");
625 			goto top;
626 		}
627 		DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname);
628 
629 		if ((fp = fopen(subfile(filename), "r")) == NULL) {
630 			WMESG(RCVFILE, EM_RMTACC);
631 			logent("CAN'T OPEN", "DENIED");
632 			goto top;
633 		}
634 
635 		/*  ok to send file */
636 		ret = fstat(fileno(fp), &stbuf);
637 		ASSERT(ret != -1, "STAT FAILED", filename, 0);
638 		i = 1 + (int)(stbuf.st_size / XFRRATE);
639 		sprintf(msg, "%s %o", YES, (int)stbuf.st_mode & 0777);
640 		WMESG(RCVFILE, msg);
641 		if (send_or_receive != SNDFILE) {
642 			send_or_receive = SNDFILE;
643 			systat(Rmtname, SS_INPROGRESS, "SENDING");
644 		}
645 		ret = (*Wrdata)(fp, Ofn);
646 		fclose(fp);
647 		if (ret != SUCCESS) {
648 			(*Turnoff)();
649 			return FAIL;
650 		}
651 		RMESG(RQSTCMPT, msg, i);
652 		goto process;
653 	}
654 	(*Turnoff)();
655 	return FAIL;
656 }
657 
658 
659 /*
660  *	read message 'c'. try 'n' times
661  *
662  *	return code:  SUCCESS  |  FAIL
663  */
664 rmesg(c, msg, n)
665 register char *msg, c;
666 register int n;
667 {
668 	char str[MAXFULLNAME];
669 
670 	DEBUG(4, "rmesg - '%c' ", c);
671 	while ((*Rdmsg)(msg, Ifn) != SUCCESS) {
672 		if (--n > 0) {
673 			sprintf(str, "%d", n);
674 			logent(str, "PATIENCE");
675 			continue;
676 		}
677 		DEBUG(4, "got FAIL\n", CNULL);
678 		if (c != '\0')
679 			sprintf(str, "expected '%c' got FAIL (%d)", c, errno);
680 		else
681 			sprintf(str, "expected ANY got FAIL (%d)", errno);
682 		logent(str, "BAD READ");
683 		return FAIL;
684 	}
685 	if (c != '\0' && msg[0] != c) {
686 		DEBUG(4, "got %s\n", msg);
687 		sprintf(str, "expected '%c' got %s", c, msg);
688 		logent(str, "BAD READ");
689 		return FAIL;
690 	}
691 	DEBUG(4, "got %s\n", msg);
692 	return SUCCESS;
693 }
694 
695 
696 /*
697  *	write a message (type m)
698  *
699  *	return codes: SUCCESS - ok | FAIL - ng
700  */
701 wmesg(m, s)
702 register char *s, m;
703 {
704 	DEBUG(4, "wmesg '%c' ", m);
705 	DEBUG(4, "%s\n", s);
706 	return (*Wrmsg)(m, s, Ofn);
707 }
708 
709 /*
710  *	mail results of command
711  *
712  *	return codes:  none
713  */
714 notify(mailopt, user, file, sys, msgcode)
715 char *user, *file, *sys, *msgcode;
716 {
717 	char str[BUFSIZ];
718 	int i;
719 	char *msg;
720 
721 	if (!mailopt && *msgcode == 'Y')
722 		return;
723 	if (*msgcode == 'Y')
724 		msg = "copy succeeded";
725 	else {
726 		i = atoi(msgcode + 1);
727 		if (i < 1 || i > EM_MAX)
728 			i = 0;
729 		msg = Em_msg[i];
730 	}
731 	sprintf(str, "file %s!%s -- %s\n",
732 		sys,file, msg);
733 	mailst(user, str, CNULL);
734 	return;
735 }
736 
737 /*
738  *	local notify
739  *
740  *	return code - none
741  */
742 lnotify(user, file, mesg)
743 char *user, *file, *mesg;
744 {
745 	char mbuf[200];
746 	sprintf(mbuf, "file %s!%s -- %s\n", Myname, file, mesg);
747 	mailst(user, mbuf, CNULL);
748 	return;
749 }
750 
751 /*
752  *	converse with the remote machine, agree upon a protocol (if possible)
753  *	and start the protocol.
754  *
755  *	return codes:
756  *		SUCCESS - successful protocol selection
757  *		FAIL - can't find common or open failed
758  */
759 startup(role)
760 int role;
761 {
762 	extern (*Rdmsg)(), (*Wrmsg)();
763 	extern char *blptcl(), fptcl();
764 	char msg[BUFSIZ], str[MAXFULLNAME];
765 
766 	Rdmsg = Imsg;
767 	Wrmsg = Omsg;
768 	if (role == MASTER) {
769 		RMESG(SLTPTCL, msg, 1);
770 		if ((str[0] = fptcl(&msg[1])) == NULL) {
771 			/* no protocol match */
772 			WMESG(USEPTCL, NO);
773 			return FAIL;
774 		}
775 		str[1] = '\0';
776 		WMESG(USEPTCL, str);
777 		if (stptcl(str) != 0)
778 			return FAIL;
779 		DEBUG(4, "protocol %s\n", str);
780 		return SUCCESS;
781 	}
782 	else {
783 		WMESG(SLTPTCL, blptcl(str));
784 		RMESG(USEPTCL, msg, 1);
785 		if (msg[1] == 'N') {
786 			return FAIL;
787 		}
788 
789 		if (stptcl(&msg[1]) != 0)
790 			return FAIL;
791 		DEBUG(4, "Protocol %s\n", msg);
792 		return SUCCESS;
793 	}
794 }
795 
796 /*
797  *	choose a protocol from the input string (str) and return the it
798  *
799  *	return codes:
800  *		'\0'  -  no acceptable protocol
801  *		any character  -  the chosen protocol
802  */
803 char
804 fptcl(str)
805 register char *str;
806 {
807 	register struct Proto *p;
808 	extern char LineType[];
809 
810 	for (p = Ptbl; p->P_id != '\0'; p++) {
811 #ifdef TCPIP
812 		/* Only use 't' on TCP/IP */
813 		if (p->P_id == 't' && strcmp("TCP", LineType))
814 			continue;
815 #endif TCPIP
816 #ifdef PAD
817 		/* only use 'f' protocol on PAD */
818 		if (p->P_id == 'f' && strcmp("PAD", LineType))
819 			continue;
820 #endif PAD
821 		if (index(str, p->P_id) != NULL) {
822 			return p->P_id;
823 		}
824 	}
825 
826 	return '\0';
827 }
828 
829 /*
830  *	build a string of the letters of the available protocols
831  */
832 char *
833 blptcl(str)
834 register char *str;
835 {
836 	register struct Proto *p;
837 	register char *s;
838 
839 	for (p = Ptbl, s = str; (*s++ = p->P_id) != '\0'; p++)
840 		;
841 	*s = '\0';
842 	return str;
843 }
844 
845 /*
846  *	this routine will set up the six routines
847  *	(Rdmsg, Wrmsg, Rddata, Wrdata, Turnon, Turnoff) for the
848  *	desired protocol.
849  *
850  *	return codes:
851  *		SUCCESS - ok
852  *		FAIL - no find or failed to open
853  *
854  */
855 stptcl(c)
856 register char *c;
857 {
858 	register struct Proto *p;
859 
860 	for (p = Ptbl; p->P_id != '\0'; p++) {
861 		if (*c == p->P_id) {
862 			/* found protocol - set routines */
863 			Rdmsg = p->P_rdmsg;
864 			Wrmsg = p->P_wrmsg;
865 			Rddata = p->P_rddata;
866 			Wrdata = p->P_wrdata;
867 			Turnon = p->P_turnon;
868 			Turnoff = p->P_turnoff;
869 			if ((*Turnon)() != SUCCESS)
870 				return FAIL;
871 			DEBUG(4, "Proto started %c\n", *c);
872 			return SUCCESS;
873 		}
874 	}
875 	DEBUG(4, "Proto start-fail %c\n", *c);
876 	return FAIL;
877 }
878 
879 /*
880  *	put file in public place. if successful, filename is modified
881  *
882  *	return code  SUCCESS | FAIL
883  */
884 
885 putinpub(file, tmp, user)
886 register char *file, *tmp, *user;
887 {
888 	char fullname[MAXFULLNAME];
889 	char *lastpart();
890 	int status;
891 
892 	sprintf(fullname, "%s/%s/", PUBDIR, user);
893 	if (mkdirs(fullname) != 0) {
894 		/* can not make directories */
895 		DEBUG(1, "Cannot mkdirs(%s)\n", fullname);
896 		return FAIL;
897 	}
898 	strcat(fullname, lastpart(file));
899 	status = xmv(tmp, fullname);
900 	if (status == 0) {
901 		strcpy(file, fullname);
902 		chmod(subfile(fullname), BASEMODE);
903 	}
904 	return status;
905 }
906 
907 /*
908  *	unlink D. file
909  *
910  *	return code - none
911  */
912 
913 unlinkdf(file)
914 register char *file;
915 {
916 	if (strlen(file) > 6)
917 		unlink(subfile(file));
918 	return;
919 }
920 
921 /*
922  *	notify receiver of arrived file
923  *
924  *	return code - none
925  */
926 arrived(opt, file, nuser, rmtsys, rmtuser)
927 char *file, *nuser, *rmtsys, *rmtuser;
928 {
929 	char mbuf[200];
930 
931 	if (!opt)
932 		return;
933 	sprintf(mbuf, "%s from %s!%s arrived\n", file, rmtsys, rmtuser);
934 	mailst(nuser, mbuf, CNULL);
935 	return;
936 }
937 
938 nullf()
939 {
940 	return SUCCESS;
941 }
942