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