xref: /original-bsd/usr.bin/uucp/uux/uux.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1988, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)uux.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 #include "uucp.h"
19 #include <sys/stat.h>
20 #include <sysexits.h>
21 
22 #define NOSYSPART 0
23 #define HASSYSPART 1
24 
25 #define LQUOTE	'('
26 #define RQUOTE ')'
27 
28 #define APPCMD(d) {\
29 register char *p; for (p = d; *p != '\0';)\
30 	{*cmdp++ = *p++;\
31 		if(cmdp>(sizeof(cmd)+&cmd[0])){\
32 			fprintf(stderr,"argument list too long\n");\
33 			cleanup(EX_SOFTWARE);\
34 		}\
35 	}\
36 	*cmdp++ = ' '; *cmdp = '\0';}
37 
38 #define GENSEND(f, a, b, c, d, e) {\
39 	fprintf(f, "S %s %s %s -%s %s 0666\n", a, b, c, d, e); }
40 #define GENRCV(f, a, b, c) {fprintf(f, "R %s %s %s - \n", a, b, c);}
41 
42 struct timeb Now;
43 
44 main(argc, argv)
45 int argc;
46 char **argv;
47 {
48 	char cfile[NAMESIZE];	/* send commands for files from here */
49 	char dfile[NAMESIZE];	/* used for all data files from here */
50 	char rxfile[NAMESIZE];	/* to be sent to xqt file (X. ...) */
51 	char tfile[NAMESIZE];	/* temporary file name */
52 	char tcfile[NAMESIZE];	/* temporary file name */
53 	char t2file[NAMESIZE];	/* temporary file name */
54 	int cflag = 0;		/*  commands in C. file flag  */
55 	int rflag = 0;		/*  C. files for receiving flag  */
56 #ifdef DONTCOPY
57 	int Copy = 0;		/* Don't Copy spool files */
58 #else !DONTCOPY
59 	int Copy = 1;		/* Copy spool files */
60 #endif !DONTCOPY
61 	int Linkit = 0;		/* Try link before copy */
62 	char buf[2*BUFSIZ];
63 	char inargs[2*BUFSIZ];
64 	int pipein = 0;
65 	int startjob = 1;
66 	char Grade = 'A';
67 	long Gradedelta = 100000000L;	/* "huge number" */
68 	long size = 0L;
69 	char path[MAXFULLNAME];
70 	char cmd[2*BUFSIZ];
71 	char *ap, *cmdp;
72 	char prm[2*BUFSIZ];
73 	char syspart[MAXBASENAME+1], rest[MAXFULLNAME];
74 	char Xsys[MAXBASENAME+1], local[MAXBASENAME+1];
75 	char *xsys = Xsys;
76 	FILE *fprx, *fpc, *fpd, *fp;
77 	extern char *getprm(), *lastpart();
78 	extern FILE *ufopen();
79 	int uid, ret, c;
80 	char redir = '\0';
81 	int nonoti = 0;
82 	int nonzero = 0;
83 	int link_failed;
84 	char *ReturnTo = NULL;
85 	extern int LocalOnly;
86 	extern char *optarg;
87 	extern int optind;
88 
89 	strcpy(Progname, "uux");
90 	uucpname(Myname);
91 	umask(WFMASK);
92 	Ofn = 1;
93 	Ifn = 0;
94 #ifdef	VMS
95 	arg_fix(argc, argv);
96 #endif
97 	while (((c = getopt(argc, argv, "-prclCg:x:nzLa:")) != EOF) ||
98 	    (optind < argc && (c = *argv[optind]) == '-' && ++optind))
99 		switch (c) {
100 		case '-':
101 			/* FALLTHROUGH */
102 		case 'p':
103 			pipein = 1;
104 			break;
105 		case 'r':
106 			startjob = 0;
107 			break;
108 		case 'c':
109 			Copy = 0;
110 			Linkit = 0;
111 			break;
112 		case 'l':
113 			Copy = 0;
114 			Linkit = 1;
115 			break;
116 		case 'C':
117 			Copy = 1;
118 			Linkit = 0;
119 			break;
120 		case 'g':
121 			Grade = *optarg;
122 			Gradedelta = atol(optarg+1);
123 			break;
124 		case 'x':
125 			chkdebug();
126 			Debug = atoi(optarg);
127 			if (Debug <= 0)
128 				Debug = 1;
129 			break;
130 		case 'n':
131 			nonoti = 1;
132 			break;
133 		case 'z':
134 			nonzero = 1;
135 			break;
136 		case 'L':
137 			LocalOnly++;
138 			break;
139 		case 'a':
140 			ReturnTo = optarg;
141 			if (prefix(Myname, ReturnTo) && ReturnTo[strlen(Myname)]				== '!')
142 				ReturnTo = index(ReturnTo, '!') + 1;
143 			break;
144 		case '?':
145 		default:
146 			break;
147 		}
148 
149 	ap = getwd(Wrkdir);
150 	if (ap == 0) {
151 		fprintf(stderr, "can't get working directory; will try to continue\n");
152 		strcpy(Wrkdir, "/UNKNOWN");
153 	}
154 
155 	DEBUG(4, "\n\n** %s **\n", "START");
156 
157 	inargs[0] = '\0';
158 	while (optind < argc) {
159 		DEBUG(4, "arg - %s:", argv[optind]);
160 		strcat(inargs, " ");
161 		strcat(inargs, argv[optind++]);
162 	}
163 	DEBUG(4, "arg - %s\n", inargs);
164 	if (subchdir(Spool) < 0) {
165 		syslog(LOG_WARNING, "chdir(%s) failed: %m", Spool);
166 		cleanup(1);
167 	}
168 	uid = getuid();
169 	if (guinfo(uid, User, path) != SUCCESS) {
170 		syslog(LOG_WARNING, "Can't find username for uid %d", uid);
171 		DEBUG(1, "Using username", "uucp");
172 		strcpy(User, "uucp");
173 	}
174 
175 	strncpy(local, Myname, MAXBASENAME);
176 	cmdp = cmd;
177 	*cmdp = '\0';
178 	gename(DATAPRE, local, 'X', rxfile);
179 	fprx = ufopen(rxfile, "w");
180 	if (fprx == NULL) {
181 		syslog(LOG_WARNING, "fopen(%s) failed: %m", rxfile);
182 		cleanup(1);
183 	}
184 	gename(DATAPRE, local, 'T', tcfile);
185 	fpc = ufopen(tcfile, "w");
186 	if (fpc == NULL) {
187 		syslog(LOG_WARNING, "fopen(%s) failed: %m", tcfile);
188 		cleanup(1);
189 	}
190 	fprintf(fprx, "%c %s %s\n", X_USER, User, local);
191 	if (nonoti)
192 		fprintf(fprx, "%c\n", X_NONOTI);
193 	if (nonzero)
194 		fprintf(fprx, "%c\n", X_NONZERO);
195 	if (ReturnTo == NULL || *ReturnTo == '\0')
196 		ReturnTo = User;
197 	fprintf(fprx, "%c %s\n", X_RETURNTO, ReturnTo);
198 
199 	/* find remote system name */
200 	ap = inargs;
201 	xsys[0] = '\0';
202 	while ((ap = getprm(ap, prm)) != NULL) {
203 		if (prm[0] == '>' || prm[0] == '<') {
204 			ap = getprm(ap, prm);
205 			continue;
206 		}
207 
208 		split(prm, xsys, rest);
209 		break;
210 	}
211 	if (xsys[0] == '\0')
212 		strcpy(xsys, local);
213 	if (versys(&xsys) != 0) {
214 		/*  bad system name  */
215 		fprintf(stderr, "bad system name: %s\n", xsys);
216 		fclose(fprx);
217 		fclose(fpc);
218 		cleanup(EX_NOHOST);
219 	}
220 
221 	strncpy(Rmtname, xsys, MAXBASENAME);
222 	DEBUG(4, "xsys %s\n", xsys);
223 
224 	if (pipein) {
225 		gename(DATAPRE, local, 'B', dfile);
226 		fpd = ufopen(dfile, "w");
227 		if (fpd == NULL) {
228 			syslog(LOG_WARNING, "fopen(%s) failed: %m", dfile);
229 			cleanup(1);
230 		}
231 		while (!feof(stdin)) {
232 			ret = fread(buf, 1, BUFSIZ, stdin);
233 			fwrite(buf, 1, ret, fpd);
234 			if (ferror(stdin)) {
235 				perror("stdin");
236 				cleanup(EX_IOERR);
237 			}
238 			if (ferror(fpd)) {
239 				perror(dfile);
240 				cleanup(EX_IOERR);
241 			}
242 			size += ret;
243 		}
244 		fclose(fpd);
245 		strcpy(tfile, dfile);
246 		if (strcmp(local, xsys) != SAME) {
247 			register int Len = strlen(local);
248 			if (Len > SYSNSIZE)
249 				Len = SYSNSIZE;
250 			tfile[Len + 2] = 'S';
251 			GENSEND(fpc, dfile, tfile, User, "", dfile);
252 			cflag++;
253 		}
254 		fprintf(fprx, "%c %s\n", X_RQDFILE, tfile);
255 		fprintf(fprx, "%c %s\n", X_STDIN, tfile);
256 	}
257 	/* parse command */
258 	ap = inargs;
259 	while ((ap = getprm(ap, prm)) != NULL) {
260 		DEBUG(4, "prm - %s\n", prm);
261 		if (prm[0] == '>' || prm[0] == '<') {
262 			redir = prm[0];
263 			continue;
264 		}
265 
266 		if (prm[0] == ';') {
267 			APPCMD(prm);
268 			continue;
269 		}
270 
271 		if (prm[0] == '|' || prm[0] == '^') {
272 			if (cmdp != cmd)
273 				APPCMD(prm);
274 			continue;
275 		}
276 
277 		/* process command or file or option */
278 		ret = split(prm, syspart, rest);
279 		DEBUG(4, "s - %s, ", syspart);
280 		DEBUG(4, "r - %s, ", rest);
281 		DEBUG(4, "ret - %d\n", ret);
282 		if (syspart[0] == '\0')
283 			strcpy(syspart, local);
284 
285 		if (cmdp == cmd && redir == '\0') {
286 			/* command */
287 			APPCMD(rest);
288 			continue;
289 		}
290 
291 		/* process file or option */
292 		DEBUG(4, "file s- %s, ", syspart);
293 		DEBUG(4, "local - %s\n", local);
294 		/* process file */
295 		if (redir == '>') {
296 			if (rest[0] != '~')
297 				if (ckexpf(rest))
298 					cleanup(EX_CANTCREAT);
299 			fprintf(fprx, "%c %s %s\n", X_STDOUT, rest,
300 			 syspart);
301 			redir = '\0';
302 			continue;
303 		}
304 
305 		if (ret == NOSYSPART && redir == '\0') {
306 			/* option */
307 			APPCMD(rest);
308 			continue;
309 		}
310 
311 		if (rest[0] != '\0') {
312 			struct stat stbuf;
313 			if (stat(rest, &stbuf) < 0)
314 				DEBUG(4, "Can't stat %s\n", rest);
315 			else
316 				size += stbuf.st_size;
317 			DEBUG(4, "size = %ld\n", size);
318 		}
319 
320 		if (strcmp(xsys, local) == SAME
321 		 && strcmp(xsys, syspart) == SAME) {
322 			if (ckexpf(rest))
323 				cleanup(EX_CANTCREAT);
324 			if (redir == '<')
325 				fprintf(fprx, "%c %s\n", X_STDIN, rest);
326 			else
327 				APPCMD(rest);
328 			redir = '\0';
329 			continue;
330 		}
331 
332 		if (strcmp(syspart, local) == SAME) {
333 			/*  generate send file */
334 			if (ckexpf(rest))
335 				cleanup(EX_CANTCREAT);
336 			gename(DATAPRE, local, 'A', dfile);
337 			DEBUG(4, "rest %s\n", rest);
338 			if ((chkpth(User, "", rest) || anyread(rest)) != 0) {
339 				fprintf(stderr, "permission denied %s\n", rest);
340 				cleanup(EX_NOINPUT);
341 			}
342 			link_failed = 0;
343 			if (Linkit) {
344 				if (link(subfile(rest), subfile(dfile)) != 0)
345 					link_failed++;
346 				else
347 					GENSEND(fpc, rest, dfile, User, "", dfile);
348 			}
349 			if (Copy || link_failed) {
350 				if (xcp(rest, dfile) != 0) {
351 					fprintf(stderr, "can't copy %s to %s\n", rest, dfile);
352 					cleanup(EX_NOINPUT);
353 				}
354 				GENSEND(fpc, rest, dfile, User, "", dfile);
355 			}
356 			if (!Copy && !Linkit) {
357 				GENSEND(fpc, rest, dfile, User, "c", "D.0");
358 			}
359 			cflag++;
360 			if (redir == '<') {
361 				fprintf(fprx, "%c %s\n", X_STDIN, dfile);
362 				fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
363 			} else {
364 				APPCMD(lastpart(rest));
365 				fprintf(fprx, "%c %s %s\n", X_RQDFILE,
366 				 dfile, lastpart(rest));
367 			}
368 			redir = '\0';
369 			continue;
370 		}
371 
372 		if (strcmp(local, xsys) == SAME) {
373 			/*  generate local receive  */
374 			gename(CMDPRE, syspart, 'R', tfile);
375 			strcpy(dfile, tfile);
376 			dfile[0] = DATAPRE;
377 			fp = ufopen(tfile, "w");
378 			if (fp == NULL) {
379 				syslog(LOG_WARNING, "fopen(%s) failed: %m",
380 					tfile);
381 				cleanup(1);
382 			}
383 			if (ckexpf(rest))
384 				cleanup(EX_CANTCREAT);
385 			GENRCV(fp, rest, dfile, User);
386 			fclose(fp);
387 			rflag++;
388 			if (rest[0] != '~')
389 				if (ckexpf(rest))
390 					cleanup(EX_CANTCREAT);
391 			if (redir == '<') {
392 				fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
393 				fprintf(fprx, "%c %s\n", X_STDIN, dfile);
394 			} else {
395 				fprintf(fprx, "%c %s %s\n", X_RQDFILE, dfile,
396 				  lastpart(rest));
397 				APPCMD(lastpart(rest));
398 			}
399 
400 			redir = '\0';
401 			continue;
402 		}
403 
404 		if (strcmp(syspart, xsys) != SAME) {
405 			/* generate remote receives */
406 			gename(DATAPRE, syspart, 'R', dfile);
407 			strcpy(tfile, dfile);
408 			tfile[0] = CMDPRE;
409 			fpd = ufopen(dfile, "w");
410 			if (fpd == NULL) {
411 				syslog(LOG_WARNING, "fopen(%s) failed: %m",
412 					dfile);
413 				cleanup(1);
414 			}
415 			gename(DATAPRE, local, 'T', t2file);
416 			GENRCV(fpd, rest, t2file, User);
417 			fclose(fpd);
418 			GENSEND(fpc, dfile, tfile, User, "", dfile);
419 			cflag++;
420 			if (redir == '<') {
421 				fprintf(fprx, "%c %s\n", X_RQDFILE, t2file);
422 				fprintf(fprx, "%c %s\n", X_STDIN, t2file);
423 			} else {
424 				fprintf(fprx, "%c %s %s\n", X_RQDFILE, t2file,
425 				  lastpart(rest));
426 				APPCMD(lastpart(rest));
427 			}
428 			redir = '\0';
429 			continue;
430 		}
431 
432 		/* file on remote system */
433 		if (rest[0] != '~')
434 			if (ckexpf(rest))
435 				cleanup(EX_CANTCREAT);
436 		if (redir == '<')
437 			fprintf(fprx, "%c %s\n", X_STDIN, rest);
438 		else
439 			APPCMD(rest);
440 		redir = '\0';
441 		continue;
442 
443 	}
444 	/*
445 	 * clean up trailing ' ' in command.
446 	 */
447 	if (cmdp > cmd && cmdp[0] == '\0' && cmdp[-1] == ' ')
448 		*--cmdp = '\0';
449 	/* block multi-hop uux, which doesn't work */
450 	for (ap = cmd; *ap && *ap != ' '; ap++)
451 		if (*ap == '!') {
452 			fprintf(stderr, "uux handles only adjacent sites.\n");
453 			fprintf(stderr, "Try uusend for multi-hop delivery.\n");
454 			cleanup(EX_USAGE);
455 		}
456 
457 	fprintf(fprx, "%c %s\n", X_CMD, cmd);
458 	if (ferror(fprx)) {
459 		logent(cmd, "COULD NOT QUEUE XQT");
460 		cleanup(EX_IOERR);
461 	} else
462 		logent(cmd, "XQT QUE'D");
463 	fclose(fprx);
464 
465 	if (size > 0 && Gradedelta > 0) {
466 		DEBUG (4, "Grade changed from %c ", Grade);
467 		Grade += size/Gradedelta;
468 		if (Grade > 'z')
469 			Grade = 'z';
470 		DEBUG(4, "to %c\n", Grade);
471 	}
472 	gename(XQTPRE, local, Grade, tfile);
473 	if (strcmp(xsys, local) == SAME) {
474 		/* rti!trt: xmv() works across filesystems, link(II) doesnt */
475 		xmv(rxfile, tfile);
476 		if (startjob)
477 			if (rflag)
478 				xuucico(xsys);
479 			else
480 				xuuxqt();
481 	}
482 	else {
483 		GENSEND(fpc, rxfile, tfile, User, "", rxfile);
484 		cflag++;
485 	}
486 
487 	if (ferror(fpc))
488 		cleanup(EX_IOERR);
489 	fclose(fpc);
490 	if (cflag) {
491 		gename(CMDPRE, xsys, Grade, cfile);
492 		/* rti!trt: use xmv() rather than link(II) */
493 		xmv(tcfile, cfile);
494 		if (startjob)
495 			xuucico(xsys);
496 		cleanup(0);
497 	}
498 	else
499 		unlink(subfile(tcfile));
500 	exit(0);
501 }
502 
503 #define FTABSIZE 30
504 char Fname[FTABSIZE][NAMESIZE];
505 int Fnamect = 0;
506 
507 /*
508  *	cleanup and unlink if error
509  *
510  *	return - none - do exit()
511  */
512 
513 cleanup(code)
514 int code;
515 {
516 	int i;
517 
518 	logcls();
519 	rmlock(CNULL);
520 	if (code) {
521 		for (i = 0; i < Fnamect; i++)
522 			unlink(subfile(Fname[i]));
523 		fprintf(stderr, "uux failed. code %d\n", code);
524 	}
525 	DEBUG(1, "exit code %d\n", code);
526 	exit(code);
527 }
528 
529 /*
530  *	open file and record name
531  *
532  *	return file pointer.
533  */
534 
535 FILE *ufopen(file, mode)
536 char *file, *mode;
537 {
538 	if (Fnamect < FTABSIZE)
539 		strcpy(Fname[Fnamect++], file);
540 	else
541 		logent("Fname", "TABLE OVERFLOW");
542 	return fopen(subfile(file), mode);
543 }
544 #ifdef	VMS
545 /*
546  * EUNICE bug:
547  *	quotes are not stripped from DCL.  Do it here.
548  *	Note if we are running under Unix shell we don't
549  *	do the right thing.
550  */
551 arg_fix(argc, argv)
552 char **argv;
553 {
554 	register char *cp, *tp;
555 
556 	for (; argc > 0; --argc, argv++) {
557 		cp = *argv;
558 		if (cp == (char *)0 || *cp++ != '"')
559 			continue;
560 		tp = cp;
561 		while (*tp++) ;
562 		tp -= 2;
563 		if (*tp == '"') {
564 			*tp = '\0';
565 			*argv = cp;
566 		}
567 	}
568 }
569 #endif VMS
570 
571 /*
572  *	split into system and file part
573  *
574  *	return codes:
575  *		NOSYSPART
576  *		HASSYSPART
577  */
578 
579 split(name, sys, rest)
580 register char *name, *rest;
581 char *sys;
582 {
583 	register char *c;
584 
585 	if (*name == LQUOTE) {
586 		if ((c = index(name + 1, RQUOTE)) != NULL) {
587 		/* strip off quotes */
588 			name++;
589 			while (c != name)
590 				*rest++ = *name++;
591 			*rest = '\0';
592 			*sys = '\0';
593 			return NOSYSPART;
594 		}
595 	}
596 
597 	if ((c = index(name, '!')) == NULL) {
598 		strcpy(rest, name);
599 		*sys = '\0';
600 		return NOSYSPART;
601 	}
602 
603 	*c++ = '\0';
604 	strncpy(sys, name, MAXBASENAME);
605 	sys[MAXBASENAME] = '\0';
606 
607 	strcpy(rest, c);
608 	return HASSYSPART;
609 }
610