OpenSolaris_b135/cmd/bnu/uucleanup.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *	uucleanup - This is a program based on the heuristics
 *	for cleaning up and doing something
 *	useful with old files left in the uucp queues.
 *	It also will send warning messags to users where requests are not
 *	going out due to failure to contact the remote system.
 *
 *	This program knows a lot about the construction and
 *	contents of the C., D. and X. files.  In addition, it
 *	thinks it knows what mail and netnews data files look like.
 *
 *	At present, this is what is done:
 *	For WARNING messages:
 *	C. files of age given by -W option are read, looking for
 *		either user files to be sent or received, or
 *		mail to be sent.  (Other remote execution that
 *		does not involve sending user files is not checked
 *		for now.)  In either of the cases, the user is
 *		informed by mail that the request is not being
 *		processed due to lack of communications with the remote
 *		system, and the request will be deleted in the future
 *		if it the condition remains for several more days.
 *
 *	For DELETIONS:
 *	C. files - if they reference only D. files, the C. is
 *		merely deleted, because the D. files are usually
 *		mail or news, and the later D. processing will
 *		take care of them.
 *		- if they reference files from the file system,
 *		a message is constructed that will contain a
 *		lines like
 *		   We can't contact the remote.
 *
 *		   local!file -> remote!otherfile
 *
 *		   can't be executed.
 *	X. files - merely deleted at present - D.s will be taken
 *		care of later. Besides, some of the D.s are
 *		missing or else the X. wouldn't be left around.
 *	D. files - mail type data is sent to a local person if that
 *		is where it originated.  If not, it is returned to the
 *		sender -- assumed to be from the first From line.  If
 *		a sender can't be determing, the file is merely deleted.
 *		- rnews: if locally generated, just delete.  If remote,
 *		the X. got lost, so execute rnews.
 *	other files - just delete them.
 *	.Workspace files over a day old
 *
 *	Deletions and executions are logged in
 *	(CLEANUPLOG)--/var/uucp/.Admin/uucleanup
*/

#include	"uucp.h"

#ifdef	V7
#define O_RDONLY	0
#endif

#define USAGE	"[-oDAYS] [-mSTRING] [-Cdays] [-Ddays] [-Wdays] [-Xdays] [-xLEVEL] [-sSYSTEM]"

extern int _age();		/* find the age of a file */
extern void procdtype(), oprocess(), xprocess(), cprocess();
extern void dXprocess(), dNprocess(), dMprocess(), dDprocess(), wprocess(); 
extern int toWho(), sendMail(), execRnews();
extern void logit();

/* need these dummys to satisy some .o files */
void cleanup(){}
void systat(){}
void logent(){}

static void cleanworkspace(void);

/* types of D. files */
#define D_MAIL	1
#define D_NEWS	2
#define D_XFILE	3
#define D_DATA	4
#define FULLNAME(full,dir,file)	(void) sprintf(full, "%s/%s", dir, file);

int _Ddays = 7;			/* D. limit */
int _Cdays = 7;			/* C. limit */
int _Xdays = 2;			/* X. limit */
int _Odays = 2;			/* O. limit */
int _Wdays = 1;			/* Warning limit for C. files */
char _ShortLocal[6];		/* 5 char or less version of local name */

char *_Undeliverable[] = {
"Subject: Undeliverable Mail\n",
"This mail message is undeliverable.\n",
"(Probably to or from system '%s')\n",
"It was sent to you or by you.\n",
"Sorry for the inconvenience.\n",
"",
};

#define CANT1	2	/* first line to fill in */
#define CANT2	3	/* second line to fill in */
char *_CantContact[] = {
"Subject: Job Killed By uucp\n",
"We can't contact machine '%s'.\n",
" ",	/* uucleanup will fill in variable text here */
" ",	/* fill in jobid of killed job */
"",
};

#define WARN1	2
#define WARN2	5
#define WARN3	6
char *_Warning[] = {
"Subject: Warning From uucp\n",
"We have been unable to contact machine '%s' since you queued your job.\n",
" ",	/*  wprocess FILLS IN THIS LINE OF TEXT */
"Attempts will continue for a few more days.\n",
"",
" ",	/*  wprocess FILLS IN THIS LINE WITH:  uucp job id is JOBid. */
" ",	/* FILL IN THE -m STRING IF SPECIFIED */
"",
};

int
main(argc, argv, envp)
int argc;
char *argv[];
char **envp;
{
	DIR *jcdir, *machdir, *spooldir;
	char fullname[MAXFULLNAME], statfile[MAXFULLNAME], machname[MAXFULLNAME];
	char file1[NAMESIZE+1], file2[NAMESIZE+1], file3[NAMESIZE+1];
	char soptName[MAXFULLNAME], lockname[MAXFULLNAME];	/* name from -s option */
	int i, value;

	soptName[0] = NULLCHAR;
	(void) strcpy(Logfile, CLEANUPLOGFILE);
	uucpname(Myname);
	(void) strncpy(_ShortLocal, Myname, 5);
	_ShortLocal[5] = NULLCHAR;
	(void) strcpy(Progname, "uucleanup");
	while ((i = getopt(argc, argv, "C:D:W:X:m:o:s:x:")) != EOF) {
		switch(i){
		case 's':	/* for debugging - choose system */
			(void) strcpy(soptName, optarg);
			break;
		case 'x':
			Debug = atoi(optarg);
			if (Debug <= 0 || Debug >= 10) {
				fprintf(stderr,
				"WARNING: %s: invalid debug level %s ignored, using level 1\n",
					Progname, optarg);
				Debug = 1;
			}
#ifdef SMALL
			fprintf(stderr,
				"WARNING: uucleanup built with SMALL flag defined -- no debug info available\n");
#endif /* SMALL */
			break;
		case 'm':
			_Warning[WARN3] = optarg;
			break;
		default:
			(void) fprintf(stderr, "\tusage: %s %s\n",
				Progname, USAGE);
			exit(1);

		case 'C':
		case 'D':
		case 'W':
		case 'X':
		case 'o':
			value = atoi(optarg);
			if (value < 1) {
				fprintf(stderr," Options: CDWXo require value > 0\n");
				exit(1);
			}
			switch(i) {
			case 'C':
				_Cdays = value;
				break;
			case 'D':
				_Ddays = value;
				break;
			case 'W':
				_Wdays = value;
				break;
			case 'X':
				_Xdays = value;
				break;
			case 'o':
				_Odays = value;
				break;
			}
			break;
		}
	}

	if (argc != optind) {
		(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
		exit(1);
	}

	DEBUG(5, "Progname (%s): STARTED\n", Progname);
	DEBUG(5, "Myname (%s), ", Myname);
	DEBUG(5, "_ShortLocal (%s)\n", _ShortLocal);
	DEBUG(5, "Days C.(%d), ", _Cdays);
	DEBUG(5, "D.(%d), ", _Ddays);
	DEBUG(5, "W.(%d), ", _Wdays);
	DEBUG(5, "X.(%d), ", _Xdays);
	DEBUG(5, "other (%d)\n", _Odays);

	cleanworkspace();
	if (chdir(SPOOL) != 0) {
		(void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n",
			SPOOL, errno);
		exit(1);
	}
	if ((spooldir = opendir(SPOOL)) == NULL) {
		(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n",
			SPOOL, errno);
		exit(1);
	}

	while (gdirf(spooldir, file1, SPOOL) == TRUE) {

		if (*soptName && !EQUALS(soptName, file1))
			continue;

		(void) strcpy(Rmtname, file1);
		(void) sprintf(machname, "%s/%s", SPOOL, file1);
		if ((machdir = opendir(machname)) == NULL) {
			(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n",
				machname, errno);
			if (*soptName)
				break;
			else
				continue;
		}
		DEBUG(7, "Directory: (%s) is open\n", file1);
		while (gnamef(machdir, file2) == TRUE) {

			(void) sprintf(statfile, "%s/%s", machname, file2);
			if (DIRECTORY(statfile)) {
				(void) sprintf(lockname, "%s.%.*s.%s",
					LOCKPRE, SYSNSIZE, file1, file2);
				if (cklock(lockname))
					continue;
				if ((jcdir = opendir(statfile)) == NULL) {
					(void) fprintf(stderr,
						"CAN'T OPEN (%s): errno (%d)\n",
						statfile, errno);
					continue;
				}

				DEBUG(7, "Directory: (%s) is open\n", file2);
				while (gnamef(jcdir, file3)) {
					DEBUG(9, "file: %s\n", file3);
					FULLNAME(fullname, statfile, file3);
					DEBUG(9,"Fullname is (%s)\n", fullname);
					if (EQUALSN(file3, "C.", 2)) {
						if (_age(fullname) >= _Cdays)
							cprocess(fullname);
						else if(_age(fullname) >= _Wdays)
							wprocess(statfile, file3);
					}
					else if (EQUALSN(file3, "D.", 2)) {
						if (_age(fullname) >= _Ddays)
							procdtype(statfile, file3);
					}
					else if (_age(fullname) >= _Odays)
						oprocess(fullname);
				}
				closedir(jcdir);
				continue;
			}
			DEBUG(9, "file: %s\n", file2);
			DEBUG(9, "Fullname is (%s)\n", statfile);
			if (EQUALSN(file2, "X.", 2)) {
				if (_age(statfile) >= _Xdays)
					xprocess(statfile);
			}
			else if (EQUALSN(file2, "D.", 2)) {
				if (_age(statfile) >= _Ddays)
					procdtype(machname, file2);
			}
			else if (_age(statfile) >= _Odays)
				oprocess(statfile);
		}
		closedir(machdir);
	}
	closedir(spooldir);
	return (0);
}

/* procdtype - select the type of processing that a D. file should receive */

void
procdtype(dir, file)
char *dir, *file;
{

	char fullname[MAXFULLNAME];

	FULLNAME(fullname, dir, file);

	switch(dType(fullname)) {
	case D_DATA:
		dDprocess(fullname);
		break;
	case D_MAIL:
		dMprocess(dir, file);
		break;
	case D_NEWS:
		dNprocess(dir, file);
		break;
	case D_XFILE:
		dXprocess(fullname);
		break;
	default:
		break;
	}
	return;
}

/* xprocess - X. file processing -- just remove the X. for now */

void
xprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "xprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "xprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
	return;
}

/*
 * cprocess - Process old C. files
 *
 */

#define CMFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n\nCan't be executed."
#define XFMT  "\n\t%s!%s  (Date %2.2d/%2.2d)\n"
#define XMFMT  "\n\tmail %s!%s   (Date %2.2d/%2.2d)\n"
#define WFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n"
void
cprocess(fullname)
char *fullname;
{
	struct stat s;
	struct tm *tp;
	char buf[BUFSIZ], user[9];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
	FILE *fp;
	int ret;

	DEBUG(5, "cprocess(%s)\n", fullname);


	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(5, "Can't open file (%s), ", fullname);
		DEBUG(5, "errno=%d -- skip it!\n", errno);
		return;
	}
	if (fstat(fileno(fp), &s) != 0) {
	    /* can't happen; _age() did stat of this file and file is opened */
	    (void) fclose(fp);
	    return;
	}
	tp = localtime(&s.st_mtime);

	if (s.st_size == 0) { /* dummy C. for polling */
		DEBUG(5, "dummy C. -- unlink(%s)\n", fullname);
		(void) sprintf(text, "dDprocess: dummy C. unlinked(%s)",
			fullname);
		errno = 0;
		(void) unlink(fullname);
		logit(text, errno);
		(void) fclose(fp);
		return;
	}

	/* Read the C. file and process it */
	while (fgets(buf, BUFSIZ, fp) != NULL) {
		buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
		if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
			user, opt, file3) <5) {
			(void) sprintf(text, "cprocess: Bad C. %s, unlink(%s)",
				buf, fullname);
			break;
		}

		*text = NULLCHAR;
		ret = 0;
		/* fill in line 3 of text */
		(void) sprintf(text2, "Job (%s) killed!\n",
		BASENAME(fullname, '/')+2);
		_CantContact[CANT2] = text2;
		if (*type == 'S') {
			if (EQUALSN(file1, "D.", 2))
				/* generated file (mail/news) I think */
				/* D. processing will return it later */
				continue;

			/* some data was requested -- tell user */

			(void) sprintf(text1, CMFMT, Myname, file1, Rmtname, file2,
				tp->tm_mon + 1, tp->tm_mday);
			_CantContact[CANT1] = text1;
			ret = sendMail((char *) NULL, user, "", _CantContact);
		}
		else if (*type == 'R') {
			(void) sprintf(text1, CMFMT, Rmtname, file1, Myname, file2,
				tp->tm_mon + 1, tp->tm_mday);
			_CantContact[CANT1] = text1;
			ret = sendMail((char *) NULL, user, "", _CantContact);
		}
	}

	if (!*text) {
		(void) sprintf(text,
			"cprocess: C. %s, mail returned (%d), unlink(%s)",
			buf, ret, fullname);
	}
	DEBUG(3, "text (%s)\n", text);

	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);

	(void) fclose(fp);
	return;
}

/*
 * wprocess - send warning messages for C. == Wdays
 */

void
wprocess(dir, file)
char *dir, *file;
{
	struct stat s;
	struct tm *tp;
	char fullname[BUFSIZ], xfile[BUFSIZ], xF_file[BUFSIZ];
	char buf[BUFSIZ], user[BUFSIZ];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
	char *realuser, uline_m[NAMESIZE], uline_u[BUFSIZ], retaddr[BUFSIZ];
	FILE *fp, *xfp;
	int ret;

	FULLNAME(fullname, dir, file);
	DEBUG(5, "wprocess(%s)\n", fullname);

	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(4, "Can't open file (%s), ", fullname);
		DEBUG(4, "errno=%d -- skip it!\n", errno);
		return;
	}

	if (fstat(fileno(fp), &s) != 0) {

	/* can't happen; _age() did stat of this file and file is opened */

		(void) fclose(fp);
		return;
	}

	tp = localtime(&s.st_mtime);

	if (s.st_size == 0) { /* dummy C. for polling */
		DEBUG(5, "dummy C. -- skip(%s)\n", fullname);
		(void) fclose(fp);
		return;
	}

	/* read C. and process it */
	while (fgets(buf, BUFSIZ, fp) != NULL) {
		buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
		if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
			user, opt, file3) <5) {
			DEBUG(5, "short line (%s): ", buf);
			DEBUG(5, "bad D. -- skip(%s)\n", fullname);
			(void) fclose(fp);
			return;
		}

		/* set up the 6th text line of the mail message */

		(void) sprintf(text2,
			"\nuucp job id is %s.\n", BASENAME(fullname, '/')+2);

		_Warning[WARN2] = text2;

		/* if Send type then do C. file processing */

		if (*type == 'S') {

		/* if this is a uux job - tell user about it */

			if (EQUALSN(file2, "X.", 2)) {
				FULLNAME(xfile, dir, file1);

				/* if X.file can't be read then skip it */

				if ((xfp = fopen(xfile, "r")) == NULL) {
					DEBUG(3, "Can't read %s\n", xfile);
					break;
				}
				*retaddr = *uline_u = *uline_m = *text = NULLCHAR;
				while (fgets(buf, BUFSIZ, xfp) != NULL) {

				/* remove \n from end of buffer */

					buf[strlen(buf)-1] = NULLCHAR;
					switch(*buf) {

					/* save the file name */

					case 'F':
						FULLNAME(xF_file, dir, &buf[2]);
						break;

					/* save return address */

					case 'R':
						sscanf(buf+2, "%s", retaddr);
						DEBUG(7, "retaddr (%s)\n", retaddr);
						break;

					/* save machine, user */

					case 'U':
						sscanf(buf+2, "%s%s",
						    uline_u, uline_m);
						break;
					}

					if (buf[0] != 'C')
						continue;
					realuser = uline_u;
					if (*retaddr != NULLCHAR)
						realuser = retaddr;
					if (*realuser == NULLCHAR)
						strcpy(realuser, user);
					if (!EQUALS(uline_m, Myname))
						sprintf(user, "%s!%s",
						    uline_m, realuser);
					else
						strcpy(user, realuser);

					/* give mail special handling */
					if (EQUALSN(buf+2, "rmail ", 6))
						(void) sprintf(text1, XMFMT,
						    Rmtname, buf+8,
						    tp->tm_mon+1, tp->tm_mday);
					else
						(void) sprintf(text1, XFMT,
						    Rmtname, buf+2,
						    tp->tm_mon+1, tp->tm_mday);

					_Warning[WARN1] = text1;
					if (EQUALSN(&buf[2], "rmail", 5))
						/*
						 * this is mail; append
						 * user mail (xF_file).
						 */
						ret = sendMail((char *) NULL,
						    user, xF_file, _Warning);
					else
						ret = sendMail((char *) NULL,
						    user, "", _Warning);
					break;
				}
				(void) fclose(xfp);
				break;
			}

			/* if file1 is a D. file the it might be (mail/news) */
			/* if so then D. processing will take of it later */

			if (EQUALSN(file1, "D.", 2))
				continue;

			/* some data was requested -- tell user */
			/* set up the 2nd text line of the mail message */

			(void) sprintf(text1, WFMT, Myname, file1, Rmtname, file2,
				tp->tm_mon + 1, tp->tm_mday);
			_Warning[WARN1] = text1;
			ret = sendMail((char *) NULL, user, "", _Warning);
		}

		/* Receive C. file processing */

		else if (*type == 'R') {
			if (EQUALSN(file1, "D.", 2) && EQUALSN(file2, "D.", 2))
				continue;

			(void) sprintf(text1, WFMT, Rmtname, file1, Myname, file2,
				tp->tm_mon + 1, tp->tm_mday);
			_Warning[WARN1] = text1;
			ret = sendMail((char *) NULL, user, "", _Warning);
		}
	}	/* end while - read C. lines loop */

	(void) sprintf(text,
		"wprocess: %s: %s, warning message sent to %s, returned (%d)",
		fullname, buf, user, ret);

	DEBUG(3, "text (%s)\n", text);

	logit(text, errno);

	(void) fclose(fp);
	return;
}
/*
 * oprocess - some unknown file just remove the file
 */

void
oprocess(fullname)
char *fullname;
{

	char *p, text[BUFSIZ];

	p = BASENAME(fullname, '/');
	if (EQUALSN(p, "P.", 2) == 0)
		if (_age(fullname) <= _Cdays)
			return;
	DEBUG(5, "oprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "oprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
	return;
}

/*
 * dDprocess - random D. file (not mail or rnews)
 *--just delete it for now
 */

void
dDprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "dDprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "dDprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
	return;
}

/*
 * dXprocess - process D. files that are destined for X. on remote
 * --for now just delete it
 */

void
dXprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "dXprocess(%s), ", fullname);
	DEBUG(5, "  unlink(%s)\n", fullname);
	(void) sprintf(text, "dXprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
	return;
}

/*
 * dMprocess - process ophan D. mail files
 * There are two types: ones generated locally and
 * others that are from remotes.  They can be identified
 * by the system name following the D.
 * Local ones have the local name.
 */

void
dMprocess(dir, file)
char *dir, *file;
{
	int ret;
	char fullname[MAXFULLNAME];
	char *toUser, *toSystem;
	char text[BUFSIZ];

	(void) sprintf(fullname, "%s/%s", dir, file);
	DEBUG(5, "dMprocess(%s)\n", fullname);


	if (PREFIX(_ShortLocal, &file[2])) {
		DEBUG(5, "  Local file %s: ", file);
	}
	else {
		DEBUG(5, "  Remote file %s: ", file);
	}
	if (toWho(fullname, &toUser, &toSystem)) {
		DEBUG(5, "toUser %s, ", toUser);
		DEBUG(5, "toSystem %s  ", toSystem);
		ret = sendMail(toSystem, toUser, fullname, _Undeliverable);
		DEBUG(5, "Mail sent, unlink(%s)\n", fullname);
		(void) sprintf(text,
			"dMprocess: mail %s to %s!%s, returned (%d),  unlink(%s)",
			fullname, toSystem, toUser, ret, fullname);
		errno = 0;
		(void) unlink(fullname);
		logit(text, errno);
	}
	return;
}

/*
 * dNprocess - process ophan D. netnews files
 * There are two types: ones generated locally and
 * others that are from remotes.  They can be identified
 * by the system name following the D.
 * Local ones have the local name.
 */

void
dNprocess(dir, file)
char *dir, *file;
{
	char fullname[MAXFULLNAME];
	char text[BUFSIZ];
	int ret;

	(void) sprintf(fullname, "%s/%s", dir, file);
	DEBUG(5, "dNprocess(%s)\n", fullname);


	if (PREFIX(_ShortLocal, &file[2])) {
	/* just delete it, the C. is gone */
		DEBUG(5, "  Local file %s, ", file);
		DEBUG(5, "unlink(%s)\n", fullname);
		(void) unlink(fullname);
		(void) sprintf(text, "dNprocess: Local news item unlink(%s)",
			fullname);
		errno = 0;
		(void) unlink(fullname);
		logit(text, errno);
	}
	else {
	/* execute rnews with this file - the X. is missing */
		DEBUG(5, "  Remote file %s, ", file);
		DEBUG(5, "exec rnews(%s), ", fullname);
		ret = execRnews(fullname);
		DEBUG(5, "unlink(%s)\n", fullname);
		(void) sprintf(text,
			"dNprocess: Remote - exec rnews %s: returned (%d), unlink(%s)",
			fullname, ret, fullname);
		errno = 0;
		(void) unlink(fullname);
		logit(text, errno);
	}
	return;
}



static long _sec_per_day = 86400L;

/*
 * _age - find the age of "file" in days
 * return:
 *	age of file
 *	0 - if stat fails
 */

int
_age(fullname)
char *fullname;
{
	static time_t ptime = 0;
	time_t time();
	struct stat stbuf;
	int e;

	if (!ptime)
		(void) time(&ptime);
	if (stat(fullname, &stbuf) != -1) {
		return ((int)((ptime - stbuf.st_mtime)/_sec_per_day));
	}
	e = errno;
	DEBUG(9, "_age: stat (%s) failed", fullname);
	DEBUG(9, ", errno %d\n", e);
	return(0);
}

/*
 * dType - return the type of D. file
 * return:
 *	FAIL - can't read D. file
 *	D_MAIL - mail message D. file
 *	D_NEWS - netnews D. file
 *	D_DATA - other kind of D. file
 *	D_XFILE - destined for X. on destination machine
 */

/* NLINES - number of lines of D. file to read to determine type */
#define NLINES	10

int
dType(fullname)
char *fullname;
{
	char buf[BUFSIZ];
	FILE *fp;
	int i, type;

	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(4, "Can't open file (%s), ", fullname);
		DEBUG(4, "errno=%d -- skip it!\n", errno);
		return(FAIL);
	}
	type = D_DATA;

	/* read first NLINES lines to determine file type */

	for (i=0; i<NLINES; i++) {
		if (fgets(buf, BUFSIZ, fp) == NULL)
			break;	/* no more lines */
		DEBUG(9, "buf: %s\n", buf);
		if (EQUALSN(buf, "From ", 5)) {
			type = D_MAIL;
			break;
		}
		if (EQUALSN(buf, "U ", 2)) {
			type = D_XFILE;
			break;
		}
		if (EQUALSN(buf, "Newsgroups: ", 12)) {
			type = D_NEWS;
			break;
		}
	}

	(void) fclose(fp);
	return(type);
}

/*
 * sendMail - send mail file and message to user (local or remote)
 * return:
 *	the return from the pclose - mail exit status
 */
int
sendMail(system, user, file, mtext)
char *system, *user, *file;
char *mtext[];
{
	FILE *fp, *fi;
	char cmd[BUFSIZ];
	char *p;

	DEBUG(5, "Mail %s to ", file);
	DEBUG(5, "%s\n", user);

	/* get rid of some stuff that could be dangerous */
	if (system != NULL && (p = strpbrk(system, Shchar)) != NULL) {
		*p = NULLCHAR;
	}
	if (user != NULL && (p = strpbrk(user, Shchar)) != NULL) {
		*p = NULLCHAR;
	}
	if (system != NULL && *system != '\0')
		(void) sprintf(cmd, "%s %s '%s!%s'", PATH, MAIL, system, user);
	else
		(void) sprintf(cmd, "%s %s '%s'", PATH, MAIL, user);

	DEBUG(7, "sendMail: %s\n", cmd);
	if ((fp = popen(cmd, "w")) == NULL)
		return(-errno);
	while (*mtext[0] )
		(void) fprintf(fp, *mtext++, Rmtname);

	(void) fprintf(fp, "\n\tSincerely,\n\t%s!uucp\n", Myname);
	(void) fprintf(fp,
		"\n#############################################\n");

	if (*file) {
	/*next statement should never happen;I read once */
		if ((fi= fopen(file, "r")) == NULL)
			return(pclose(fp));
		(void) fprintf(fp,
			"##### Data File: ############################\n");
		xfappend(fi, fp);
		(void) fclose(fi);
	}
	return(pclose(fp));
}

/*
 * execRnews - execute rnews command with stdin file
 * return:
 *	the return from the pclose - rnews exit status
 */
int
execRnews(file)
char *file;
{
	FILE *fp, *fi;
	char cmd[BUFSIZ];

	DEBUG(5, "Rnews %s\n", file);

	(void) sprintf(cmd, "%s rnews ", PATH);
	if ((fp = popen(cmd, "w")) == NULL)
		return(-errno);

	if ( (fi = fopen(file, "r")) == NULL) /* never happen - I read once */
		return(pclose(fp));
	xfappend(fi, fp);
	(void) fclose(fi);

	return(pclose(fp));
}

/*
 * toWho - figure out who to send this dead mail to
 *	It is a guess;
 *	If there is a local address, send it there.
 *	If not, send it back where it came from.
 * return:
 *	0 - could not find system and user information
 *	1 - found it
 */

int
toWho(file, user, system)
char *file;	/* the D. mail message file */
char **system;	/* pointer to the system name */
char **user;	/* pointer to the user name */
{
	char buf[BUFSIZ];
	FILE *fp;
	int i;
	static char fuser[BUFSIZ], fsystem[MAXBASENAME+1];  /* from first From */
	static char luser[BUFSIZ], lsystem[MAXBASENAME+1];  /* from other From */

	*fuser = NULLCHAR;
	DEBUG(5, "toWho(%s)\n", file);
	fp = fopen(file, "r");
	for (i=0; i<NLINES; i++) {
		if (fgets(buf, BUFSIZ, fp) == NULL)
			break;	/* no more lines */
		DEBUG(9, "buf: %s\n", buf);
		if (!analFrom(buf, luser, lsystem))
			continue;
		if ( !*fuser) {
			(void) strcpy(fuser, luser);
			(void) strcpy(fsystem, lsystem);
		}
		if (EQUALS(Myname, lsystem)) {
			*user = luser;
			*system = lsystem;
			(void) fclose(fp);
			return(1);
		}
	}

	/* could not find local user - use first line */
	(void) fclose(fp);
	if (!*fuser)	/* didn't find all information */
		return(0);
	*user = fuser;
	*system = fsystem;
	return(1);
}

/* analFrom - analyze From line
 * return:
 *	0 - didn't find both from and remote from info
 *	1 - found info.
 */

int
analFrom(line, user, system)
char *line, *user, *system;
{
	char *s;
	int i;

	if (!PREFIX("From ", line) && !PREFIX(">From ", line))
		return(0);
	
	s = strchr(line, ' ') + 1;
	for (i = 0;  *s && *s != ' ' && *s != '\n'; i++)
		user[i] = *s++;
	user[i] = NULLCHAR;

	/* look for "remote from" */
	while (*s && ((s = strchr(s, ' ')) != NULL)) {
		s++;
		if (PREFIX("remote from ", s)) {	/* found it */
			s = s + strlen("remote from ");
			for (i = 0; (i<MAXBASENAME) && *s && *s != ' ' && *s != '\n'; i++)
				system[i] = *s++;
			system[i] = NULLCHAR;
			return(1);
		}
	}
	return(0);
}



static FILE	*_Lf = NULL;

/*
 * Make log entry
 *	text	-> ptr to text string
 *	status	errno number
 * Returns:
 *	none
 */

void
logit(text, status)
char	*text;
int status;
{

	if (Nstat.t_pid == 0)
		Nstat.t_pid = getpid();

	if (_Lf == NULL) {
		_Lf = fopen(Logfile, "a");
		(void) chmod(Logfile, LOGFILEMODE);
		if (_Lf == NULL)
			return;
		setbuf(_Lf, CNULL);
	}
	(void) fseek(_Lf, 0L, 2);
	(void) fprintf(_Lf, "%s ", Rmtname);
	(void) fprintf(_Lf, "(%s,%ld,%d) ", timeStamp(), (long) Nstat.t_pid, Seqn);
	(void) fprintf(_Lf, "%s (%d)\n", text, status);
	return;
}

static void
cleanworkspace(void)
{
	DIR	*spooldir;
	char f[MAXFULLNAME];

	if (chdir(WORKSPACE) != 0) {
		(void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n", WORKSPACE, errno);
		return;
	}
	if ((spooldir = opendir(WORKSPACE)) == NULL) {
		(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", WORKSPACE, errno);
		return;
	}

	while (gnamef(spooldir, f) == TRUE)
		if (_age(f) >= 1)
			if (unlink(f) != 0)
				(void) fprintf(stderr, "CAN'T UNLINK (%s): errno (%d)\n", f, errno);
				
}