xref: /original-bsd/usr.bin/uucp/uuxqt/uuxqt.c (revision a8414ee1)
1 /*-
2  * Copyright (c) 1985, 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1985, 1988 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)uuxqt.c	5.14 (Berkeley) 04/24/91";
16 #endif /* not lint */
17 
18 #include "uucp.h"
19 #include <sys/stat.h>
20 #ifdef	NDIR
21 #include "ndir.h"
22 #else
23 #include <sys/dir.h>
24 #endif
25 #include <signal.h>
26 
27 #define BADCHARS	"&^|(`\\<>;\"{}\n'"
28 #define RECHECKTIME	60*10	/* 10 minutes */
29 
30 #define APPCMD(d) {\
31 char *p;\
32 for (p = d; *p != '\0';) *cmdp++ = *p++; *cmdp++ = ' '; *cmdp = '\0';}
33 
34 extern char Filent[LLEN][NAMESIZE];
35 
36 /*
37  *	uuxqt will execute commands set up by a uux command,
38  *	usually from a remote machine - set by uucp.
39  */
40 
41 #define	NCMDS	50
42 char *Cmds[NCMDS+1];
43 int Notify[NCMDS+1];
44 #define	NT_YES	0	/* if should notify on execution */
45 #define	NT_ERR	1	/* if should notify if non-zero exit status (-z equivalent) */
46 #define	NT_NO	2	/* if should not notify ever (-n equivalent) */
47 
48 extern int Nfiles;
49 
50 int TransferSucceeded = 1;
51 int notiok = 1;
52 int nonzero = 0;
53 
54 struct timeb Now;
55 
56 char PATH[MAXFULLNAME] = "PATH=/bin:/usr/bin";
57 char UU_MACHINE[MAXFULLNAME];
58 char Shell[MAXFULLNAME];
59 char HOME[MAXFULLNAME];
60 
61 extern char **environ;
62 char *nenv[] = {
63 	PATH,
64 	Shell,
65 	HOME,
66 	UU_MACHINE,
67 	0
68 };
69 
70 /*  to remove restrictions from uuxqt
71  *  define ALLOK 1
72  *
73  *  to add allowable commands, add to the file CMDFILE
74  *  A line of form "PATH=..." changes the search path
75  */
76 main(argc, argv)
77 char *argv[];
78 {
79 	char xcmd[BUFSIZ*2];
80 	int argnok;
81 	int notiflg;
82 	char xfile[MAXFULLNAME], user[MAXFULLNAME], buf[BUFSIZ*2];
83 	char lbuf[MAXFULLNAME];
84 	char cfile[NAMESIZE], dfile[MAXFULLNAME];
85 	char file[NAMESIZE];
86 	char fin[MAXFULLNAME], sysout[NAMESIZE], fout[MAXFULLNAME];
87 	register FILE *xfp, *fp;
88 	FILE *dfp;
89 	char path[MAXFULLNAME];
90 	char cmd[BUFSIZ*2];
91 	char *cmdp, prm[1000], *ptr;
92 	char *getprm(), *lastpart();
93 	int uid, ret, badfiles;
94 	register int i;
95 	int stcico = 0;
96 	time_t xstart, xnow;
97 	char retstat[30];
98 	extern char *optarg;
99 	extern int optind;
100 
101 	strcpy(Progname, "uuxqt");
102 	uucpname(Myname);
103 	strcpy(Rmtname, Myname);
104 
105 	umask(WFMASK);
106 	Ofn = 1;
107 	Ifn = 0;
108 	while ((i = getopt(argc, argv, "x:S:")) != EOF)
109 		switch(i) {
110 		case 'x':
111 			chkdebug();
112 			Debug = atoi(optarg);
113 			if (Debug <= 0)
114 				Debug = 1;
115 			break;
116 		case 'S':
117 			Spool = optarg;
118 			DEBUG(1, "Spool set to %s", Spool);
119 			break;
120 		case '?':
121 		default:
122 			fprintf(stderr, "unknown flag %s\n", argv[optind-1]);
123 				break;
124 		}
125 
126 	DEBUG(4, "\n\n** START **\n", CNULL);
127 	if (subchdir(Spool) < 0) {
128 		syslog(LOG_WARNING, "chdir(%s) failed: %m", Spool);
129 		cleanup(1);
130 	}
131 	strcpy(Wrkdir, Spool);
132 	uid = getuid();
133 	if (guinfo(uid, User, path) != SUCCESS) {
134 		syslog(LOG_WARNING, "Can't find username for uid %d", uid);
135 		DEBUG(1, "Using username", "uucp");
136 		strcpy(User, "uucp");
137 	}
138 	setgid(getegid());
139 	setuid(geteuid());
140 
141 	DEBUG(4, "User - %s\n", User);
142 	if (ulockf(X_LOCK, X_LOCKTIME) != 0)
143 		exit(0);
144 
145 	fp = fopen(CMDFILE, "r");
146 	if (fp == NULL) {
147 		logent(CANTOPEN, CMDFILE);
148 		Cmds[0] = "rmail";
149 		Cmds[1] = "rnews";
150 		Cmds[2] = "ruusend";
151 		Cmds[3] = NULL;
152 		goto doprocess;
153 	}
154 	DEBUG(5, "%s opened\n", CMDFILE);
155 	for (i=0; i<NCMDS && cfgets(xcmd, sizeof(xcmd), fp) != NULL; i++) {
156 		int j;
157 		/* strip trailing whitespace */
158 		for (j = strlen(xcmd)-1; j >= 0; --j)
159 			if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
160 				xcmd[j] = '\0';
161 			else
162 				break;
163 		/* look for imbedded whitespace */
164 		for (; j >= 0; --j)
165 			if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
166 				break;
167 		/* skip this entry if it has embedded whitespace */
168 		/* This defends against a bad PATH=, for example */
169 		if (j >= 0) {
170 			logent(xcmd, "BAD WHITESPACE");
171 			continue;
172 		}
173 		if (strncmp(xcmd, "PATH=", 5) == 0) {
174 			strcpy(PATH, xcmd);
175 			i--;	/*kludge */
176 			continue;
177 		}
178 		DEBUG(5, "xcmd = %s\n", xcmd);
179 
180 		if ((ptr = index(xcmd, ',')) != NULL) {
181 			*ptr++ = '\0';
182 			if (strncmp(ptr, "Err", 3) == SAME)
183 				Notify[i] = NT_ERR;
184 			else if (strcmp(ptr, "No") == SAME)
185 				Notify[i] = NT_NO;
186 			else
187 				Notify[i] = NT_YES;
188 		} else
189 			Notify[i] = NT_YES;
190 		if ((Cmds[i] = malloc((unsigned)(strlen(xcmd)+1))) == NULL) {
191 			DEBUG(1, "MALLOC FAILED", CNULL);
192 			break;
193 		}
194 		strcpy(Cmds[i], xcmd);
195 	}
196 	Cmds[i] = CNULL;
197 	fclose(fp);
198 
199 doprocess:
200 
201 	(void) sprintf(HOME, "HOME=%s", Spool);
202 	(void) sprintf(Shell, "SHELL=%s", SHELL);
203 	environ = nenv; /* force use if our environment */
204 
205 	DEBUG(11,"path = %s\n", getenv("PATH"));
206 
207 	DEBUG(4, "process %s\n", CNULL);
208 
209 	time(&xstart);
210 	while (gtxfile(xfile) > 0) {
211 		/* if /etc/nologin exists, exit cleanly */
212 #if defined(BSD4_2) || defined(USG)
213 		if (access(NOLOGIN) == 0) {
214 #else !BSD4_2 && ! USG
215 		ultouch();
216 		if (nologinflag) {
217 #endif !BSD4_2 && !USG
218 			logent(NOLOGIN, "UUXQT SHUTDOWN");
219 			if (Debug)
220 				logent("debugging", "continuing anyway");
221 			else
222 				break;
223 		}
224 		DEBUG(4, "xfile - %s\n", xfile);
225 
226 		xfp = fopen(subfile(xfile), "r");
227 		if (xfp == NULL) {
228 			syslog(LOG_ERR, "fopen(%s) failed: %m", subfile(xfile));
229 			cleanup(1);
230 		}
231 
232 		/*  initialize to default  */
233 		strcpy(user, User);
234 		strcpy(fin, DEVNULL);
235 		strcpy(fout, DEVNULL);
236 		strcpy(sysout, Myname);
237 		badfiles = 0;
238 		while (fgets(buf, BUFSIZ, xfp) != NULL) {
239 			if(buf[0] != '\0' && buf[0] != '#' &&
240 			    buf[1] != ' ' && buf[1] != '\0' && buf[1] != '\n') {
241 				char *bnp, cfilename[BUFSIZ];
242 				DEBUG(4, "uuxqt: buf = %s\n", buf);
243 				bnp = rindex(xfile, '/');
244 				sprintf(cfilename, "%s/%s", CORRUPT,
245 					bnp ? bnp + 1 : xfile);
246 				DEBUG(4, "uuxqt: move %s to ", xfile);
247 				DEBUG(4, "%s\n", cfilename);
248 				xmv(xfile, cfilename);
249 				syslog(LOG_WARNING, "%s: X. FILE CORRUPTED",
250 					xfile);
251 				fclose(xfp);
252 				goto doprocess;
253 			}
254 			switch (buf[0]) {
255 			case X_USER: {
256 				char ORmtname[MAXFULLNAME];
257 				strcpy(ORmtname, Rmtname);
258 				sscanf(&buf[1], "%s %s", user, Rmtname);
259 				sprintf(UU_MACHINE, "UU_MACHINE=%s", Rmtname);
260 				if (strcmp(ORmtname, Rmtname) != 0)
261 					logcls();
262 				break;}
263 			case X_RETURNTO:
264 				sscanf(&buf[1], "%s", user);
265 				break;
266 			case X_STDIN:
267 				sscanf(&buf[1], "%s", fin);
268 				i = expfile(fin);
269 				/* rti!trt: do not check permissions of
270 				 * vanilla spool file */
271 				if (i != 0
272 				 && (chkpth("", "", fin) || anyread(fin) != 0))
273 					badfiles = 1;
274 				break;
275 			case X_STDOUT:
276 				sscanf(&buf[1], "%s%s", fout, sysout);
277 				sysout[MAXBASENAME] = '\0';
278 				/* rti!trt: do not check permissions of
279 				 * vanilla spool file.  DO check permissions
280 				 * of writing on a non-vanilla file */
281 				i = 1;
282 				if (fout[0] != '~' || prefix(sysout, Myname))
283 					i = expfile(fout);
284 				if (i != 0
285 				 && (chkpth("", "", fout)
286 					|| chkperm(fout, (char *)1)))
287 					badfiles = 1;
288 				break;
289 			case X_CMD:
290 				strcpy(cmd, &buf[2]);
291 				if (*(cmd + strlen(cmd) - 1) == '\n')
292 					*(cmd + strlen(cmd) - 1) = '\0';
293 				break;
294 			case X_NONOTI:
295 				notiok = 0;
296 				break;
297 			case X_NONZERO:
298 				nonzero = 1;
299 				break;
300 			default:
301 				break;
302 			}
303 		}
304 
305 		fclose(xfp);
306 		DEBUG(4, "fin - %s, ", fin);
307 		DEBUG(4, "fout - %s, ", fout);
308 		DEBUG(4, "sysout - %s, ", sysout);
309 		DEBUG(4, "user - %s\n", user);
310 		DEBUG(4, "cmd - %s\n", cmd);
311 
312 		/*  command execution  */
313 		if (strcmp(fout, DEVNULL) == SAME)
314 			strcpy(dfile,DEVNULL);
315 		else
316 			gename(DATAPRE, sysout, 'O', dfile);
317 
318 		/* expand file names where necessary */
319 		expfile(dfile);
320 		cmdp = buf;
321 		ptr = cmd;
322 		xcmd[0] = '\0';
323 		argnok = 0;
324 		while ((ptr = getprm(ptr, prm)) != NULL) {
325 			if (prm[0] == ';' || prm[0] == '^'
326 			  || prm[0] == '&'  || prm[0] == '|') {
327 				xcmd[0] = '\0';
328 				APPCMD(prm);
329 				continue;
330 			}
331 
332 			if ((argnok = argok(xcmd, prm)) != SUCCESS)
333 				/*  command not valid  */
334 				break;
335 
336 			if (prm[0] == '~')
337 				expfile(prm);
338 			APPCMD(prm);
339 		}
340 		/*
341 		 * clean up trailing ' ' in command.
342 		 */
343 		if (cmdp > buf && cmdp[0] == '\0' && cmdp[-1] == ' ')
344 			*--cmdp = '\0';
345 		if (argnok || badfiles) {
346 			sprintf(lbuf, "%s XQT DENIED", user);
347 			logent(cmd, lbuf);
348 			DEBUG(4, "bad command %s\n", prm);
349 			notify(user, Rmtname, cmd, "DENIED");
350 			goto rmfiles;
351 		}
352 		sprintf(lbuf, "%s XQT", user);
353 		logent(buf, lbuf);
354 		DEBUG(4, "cmd %s\n", buf);
355 
356 		mvxfiles(xfile);
357 		if (subchdir(XQTDIR) < 0) {
358 			syslog(LOG_ERR, "chdir(%s) failed: %m", XQTDIR);
359 			cleanup(1);
360 		}
361 		ret = shio(buf, fin, dfile);
362 		sprintf(retstat, "signal %d, exit %d", ret & 0377,
363 		  (ret>>8) & 0377);
364 		if (strcmp(xcmd, "rmail") == SAME)
365 			notiok = 0;
366 		if (strcmp(xcmd, "rnews") == SAME)
367 			nonzero = 1;
368 		notiflg = chknotify(xcmd);
369 		if (notiok && notiflg != NT_NO &&
370 		   (ret != 0 || (!nonzero && notiflg == NT_YES)))
371 			notify(user, Rmtname, cmd, retstat);
372 		else if (ret != 0 && strcmp(xcmd, "rmail") == SAME) {
373 			/* mail failed - return letter to sender  */
374 #ifdef	DANGEROUS
375 			/* NOT GUARANTEED SAFE!!! */
376 			if (!nonzero)
377 				retosndr(user, Rmtname, fin);
378 #else
379 			notify(user, Rmtname, cmd, retstat);
380 #endif
381 			sprintf(buf, "%s (%s) from %s!%s", buf, retstat, Rmtname, user);
382 			logent("MAIL FAIL", buf);
383 		}
384 		DEBUG(4, "exit cmd - %d\n", ret);
385 		if (subchdir(Spool) < 0) {
386 			syslog(LOG_ERR, "chdir(%s) failed: %m", Spool);
387 			cleanup(1);
388 		}
389 		rmxfiles(xfile);
390 		if (ret != 0) {
391 			/*  exit status not zero */
392 			dfp = fopen(subfile(dfile), "a");
393 			if (dfp == NULL) {
394 				syslog(LOG_ERR, "fopen(%s) failed: %m",
395 					subfile(dfile));
396 				cleanup(1);
397 			}
398 			fprintf(dfp, "exit status %d", ret);
399 			fclose(dfp);
400 		}
401 		if (strcmp(fout, DEVNULL) != SAME) {
402 			if (prefix(sysout, Myname)) {
403 				xmv(dfile, fout);
404 				chmod(fout, BASEMODE);
405 			} else {
406 				char *cp = rindex(user, '!');
407 				gename(CMDPRE, sysout, 'O', cfile);
408 				fp = fopen(subfile(cfile), "w");
409 				if (fp == NULL) {
410 					syslog(LOG_ERR, "fopen(%s) failed: %m",
411 						subfile(cfile));
412 					cleanup(1);
413 				}
414 				fprintf(fp, "S %s %s %s - %s 0666\n", dfile,
415 					fout, cp ? cp : user, lastpart(dfile));
416 				fclose(fp);
417 			}
418 		}
419 	rmfiles:
420 		xfp = fopen(subfile(xfile), "r");
421 		if (xfp == NULL) {
422 			syslog(LOG_ERR, "fopen(%s) failed: %m",
423 				subfile(xfile));
424 			cleanup(1);
425 		}
426 		while (fgets(buf, BUFSIZ, xfp) != NULL) {
427 			if (buf[0] != X_RQDFILE)
428 				continue;
429 			sscanf(&buf[1], "%s", file);
430 			unlink(subfile(file));
431 		}
432 		unlink(subfile(xfile));
433 		fclose(xfp);
434 
435 		/* rescan X. for new work every RECHECKTIME seconds */
436 		time(&xnow);
437 		if (xnow > (xstart + RECHECKTIME)) {
438 			extern int Nfiles;
439 			Nfiles = 0; 	/*force rescan for new work */
440 		}
441 		xstart = xnow;
442 	}
443 
444 	if (stcico)
445 		xuucico("");
446 	cleanup(0);
447 }
448 
449 
450 cleanup(code)
451 int code;
452 {
453 	logcls();
454 	rmlock(CNULL);
455 #ifdef	VMS
456 	/*
457 	 *	Since we run as a BATCH job we must wait for all processes to
458 	 *	to finish
459 	 */
460 	while(wait(0) != -1)
461 		;
462 #endif VMS
463 	exit(code);
464 }
465 
466 
467 /*
468  *	get a file to execute
469  *
470  *	return codes:  0 - no file  |  1 - file to execute
471  */
472 
473 gtxfile(file)
474 register char *file;
475 {
476 	char pre[3];
477 	register int rechecked, i;
478 	time_t ystrdy;		/* yesterday */
479 	struct stat stbuf;	/* for X file age */
480 
481 	pre[0] = XQTPRE;
482 	pre[1] = '.';
483 	pre[2] = '\0';
484 	rechecked = 0;
485 retry:
486 	if (Nfiles-- <= 0) {
487 		Nfiles = 0;
488 		if (rechecked)
489 			return 0;
490 		rechecked = 1;
491 		DEBUG(4, "iswrk\n", CNULL);
492 		return iswrk(file, "get", Spool, pre);
493 	}
494 	sprintf(file, "%s/%s", Spool, Filent[0]);
495 	for (i=0; i<Nfiles;i++)
496 		strcpy(Filent[i], Filent[i+1]);
497 
498 	DEBUG(4, "file - %s\n", file);
499 	/* skip spurious subdirectories */
500 	if (strcmp(pre, file) == SAME)
501 		goto retry;
502 	if (gotfiles(file))
503 		return 1;
504 	/* check for old X. file with no work files and remove them. */
505 	if (Nfiles > LLEN/2) {
506 	    time(&ystrdy);
507 	    ystrdy -= (4 * 3600L);		/* 4 hours ago */
508 	    DEBUG(4, "gtxfile: Nfiles > LLEN/2\n", CNULL);
509 	    while (Nfiles-- > 0) {
510 		sprintf(file, "%s/%s", Spool, Filent[0]);
511 		for (i=0; i<Nfiles; i++)
512 			strcpy(Filent[i], Filent[i+1]);
513 
514 		if (gotfiles(file))
515 			return 1;
516 		if (stat(subfile(file), &stbuf) == 0)
517 		    if (stbuf.st_mtime <= ystrdy) {
518 			char *bnp, cfilename[NAMESIZE];
519 			DEBUG(4, "gtxfile: move %s to CORRUPT \n", file);
520 			bnp = rindex(file, '/');
521 			sprintf(cfilename, "%s/%s", CORRUPT,
522 				bnp ? bnp + 1 : file);
523 			xmv(file, cfilename);
524 			syslog(LOG_WARNING, "%s: X. FILE MISSING FILES", file);
525 		    }
526 	    }
527  	    Nfiles = 0;
528 	    DEBUG(4, "iswrk\n", CNULL);
529 	    if (!iswrk(file, "get", Spool, pre))
530 		return 0;
531 	}
532 	goto retry;
533 }
534 
535 /*
536  *	check for needed files
537  *
538  *	return codes:  0 - not ready  |  1 - all files ready
539  */
540 
541 gotfiles(file)
542 register char *file;
543 {
544 	struct stat stbuf;
545 	register FILE *fp;
546 	char buf[BUFSIZ], rqfile[MAXFULLNAME];
547 
548 	fp = fopen(subfile(file), "r");
549 	if (fp == NULL)
550 		return 0;
551 
552 	while (fgets(buf, BUFSIZ, fp) != NULL) {
553 		DEBUG(4, "%s\n", buf);
554 		if (buf[0] != X_RQDFILE)
555 			continue;
556 		sscanf(&buf[1], "%s", rqfile);
557 		expfile(rqfile);
558 		if (stat(subfile(rqfile), &stbuf) == -1) {
559 			fclose(fp);
560 			return 0;
561 		}
562 	}
563 
564 	fclose(fp);
565 	return 1;
566 }
567 
568 
569 /*
570  *	remove execute files to x-directory
571  */
572 
573 rmxfiles(xfile)
574 register char *xfile;
575 {
576 	register FILE *fp;
577 	char buf[BUFSIZ], file[NAMESIZE], tfile[NAMESIZE];
578 	char tfull[MAXFULLNAME];
579 
580 	if((fp = fopen(subfile(xfile), "r")) == NULL)
581 		return;
582 
583 	while (fgets(buf, BUFSIZ, fp) != NULL) {
584 		if (buf[0] != X_RQDFILE)
585 			continue;
586 		if (sscanf(&buf[1], "%s%s", file, tfile) < 2)
587 			continue;
588 		sprintf(tfull, "%s/%s", XQTDIR, tfile);
589 		unlink(subfile(tfull));
590 	}
591 	fclose(fp);
592 	return;
593 }
594 
595 
596 /*
597  *	move execute files to x-directory
598  */
599 
600 mvxfiles(xfile)
601 char *xfile;
602 {
603 	register FILE *fp;
604 	char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[NAMESIZE];
605 	char tfull[MAXFULLNAME];
606 
607 	if((fp = fopen(subfile(xfile), "r")) == NULL)
608 		return;
609 
610 	while (fgets(buf, BUFSIZ, fp) != NULL) {
611 		if (buf[0] != X_RQDFILE)
612 			continue;
613 		if (sscanf(&buf[1], "%s%s", ffile, tfile) < 2)
614 			continue;
615 		expfile(ffile);
616 		sprintf(tfull, "%s/%s", XQTDIR, tfile);
617 		unlink(subfile(tfull));
618 		if (xmv(ffile, tfull) != 0) {
619 			syslog(LOG_WARNING, "xmv(%s,%s) failed: %m",
620 				ffile, tfull);
621 			cleanup(1);
622 		}
623 	}
624 	fclose(fp);
625 }
626 
627 /*
628  *	check for valid command/argument
629  *	*NOTE - side effect is to set xc to the	command to be executed.
630  *
631  *	return 0 - ok | 1 nok
632  */
633 
634 argok(xc, cmd)
635 register char *xc, *cmd;
636 {
637 	register char **ptr;
638 
639 #ifndef ALLOK
640 	if (strpbrk(cmd, BADCHARS) != NULL) {
641 		DEBUG(1,"MAGIC CHARACTER FOUND\n", CNULL);
642 		logent(cmd, "NASTY MAGIC CHARACTER FOUND");
643 		return FAIL;
644 	}
645 #endif !ALLOK
646 
647 	if (xc[0] != '\0')
648 		return SUCCESS;
649 
650 #ifndef ALLOK
651 	ptr = Cmds;
652 	DEBUG(9, "Compare %s and\n", cmd);
653 	while(*ptr != NULL) {
654 		DEBUG(9, "\t%s\n", *ptr);
655 		if (strcmp(cmd, *ptr) == SAME)
656 			break;
657 		ptr++;
658 	}
659 	if (*ptr == NULL) {
660 		DEBUG(1,"COMMAND NOT FOUND\n", CNULL);
661 		return FAIL;
662 	}
663 #endif
664 	strcpy(xc, cmd);
665 	DEBUG(9, "MATCHED %s\n", xc);
666 	return SUCCESS;
667 }
668 
669 
670 /*
671  *	if notification should be sent for successful execution of cmd
672  *
673  *	return NT_YES - do notification
674  *	       NT_ERR - do notification if exit status != 0
675  *	       NT_NO  - don't do notification ever
676  */
677 
678 chknotify(cmd)
679 char *cmd;
680 {
681 	register char **ptr;
682 	register int *nptr;
683 
684 	ptr = Cmds;
685 	nptr = Notify;
686 	while (*ptr != NULL) {
687 		if (strcmp(cmd, *ptr) == SAME)
688 			return *nptr;
689 		ptr++;
690 		nptr++;
691 	}
692 	return NT_YES;		/* "shouldn't happen" */
693 }
694 
695 
696 
697 /*
698  *	send mail to user giving execution results
699  */
700 
701 notify(user, rmt, cmd, str)
702 char *user, *rmt, *cmd, *str;
703 {
704 	char text[BUFSIZ*2];
705 	char ruser[MAXFULLNAME];
706 
707 	if (strpbrk(user, BADCHARS) != NULL) {
708 		char lbuf[MAXFULLNAME];
709 		sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
710 		logent(cmd, lbuf);
711 		strcpy(user, "postmaster");
712 	}
713 	sprintf(text, "uuxqt cmd (%s) status (%s)", cmd, str);
714 	if (prefix(rmt, Myname))
715 		strcpy(ruser, user);
716 	else
717 		sprintf(ruser, "%s!%s", rmt, user);
718 	mailst(ruser, text, CNULL);
719 }
720 
721 /*
722  *	return mail to sender
723  *
724  */
725 retosndr(user, rmt, file)
726 char *user, *rmt, *file;
727 {
728 	char ruser[MAXFULLNAME];
729 
730 	if (strpbrk(user, BADCHARS) != NULL) {
731 		char lbuf[MAXFULLNAME];
732 		sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
733 		logent(file, lbuf);
734 		strcpy(user, "postmaster");
735 	}
736 	if (strcmp(rmt, Myname) == SAME)
737 		strcpy(ruser, user);
738 	else
739 		sprintf(ruser, "%s!%s", rmt, user);
740 
741 	if (anyread(file) == 0)
742 		mailst(ruser, "Mail failed.  Letter returned to sender.\n", file);
743 	else
744 		mailst(ruser, "Mail failed.  Letter returned to sender.\n", CNULL);
745 	return;
746 }
747 
748 /*
749  *	execute shell of command with fi and fo as standard input/output
750  */
751 
752 shio(cmd, fi, fo)
753 char *cmd, *fi, *fo;
754 {
755 	int status, f;
756 	int pid, ret;
757 	char *args[256];
758 	extern int errno;
759 
760 	if (fi == NULL)
761 		fi = DEVNULL;
762 	if (fo == NULL)
763 		fo = DEVNULL;
764 
765 	getargs(cmd, args, 256);
766 	DEBUG(3, "shio - %s\n", cmd);
767 #ifdef SIGCHLD
768 	signal(SIGCHLD, SIG_IGN);
769 #endif SIGCHLD
770 	if ((pid = fork()) == 0) {
771 		signal(SIGINT, SIG_IGN);
772 		signal(SIGHUP, SIG_IGN);
773 		signal(SIGQUIT, SIG_IGN);
774 		close(Ifn);
775 		close(Ofn);
776 		close(0);
777 		setuid(getuid());
778 		f = open(subfile(fi), 0);
779 		if (f != 0) {
780 			logent(fi, "CAN'T READ");
781 			exit(-errno);
782 		}
783 		close(1);
784 		f = creat(subfile(fo), 0666);
785 		if (f != 1) {
786 			logent(fo, "CAN'T WRITE");
787 			exit(-errno);
788 		}
789 		execvp(args[0], args);
790 		exit(100+errno);
791 	}
792 	while ((ret = wait(&status)) != pid && ret != -1)
793 		;
794 	DEBUG(3, "status %d\n", status);
795 	return status;
796 }
797