1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33/*
34 *	uucleanup - This is a program based on the heuristics
35 *	for cleaning up and doing something
36 *	useful with old files left in the uucp queues.
37 *	It also will send warning messags to users where requests are not
38 *	going out due to failure to contact the remote system.
39 *
40 *	This program knows a lot about the construction and
41 *	contents of the C., D. and X. files.  In addition, it
42 *	thinks it knows what mail and netnews data files look like.
43 *
44 *	At present, this is what is done:
45 *	For WARNING messages:
46 *	C. files of age given by -W option are read, looking for
47 *		either user files to be sent or received, or
48 *		mail to be sent.  (Other remote execution that
49 *		does not involve sending user files is not checked
50 *		for now.)  In either of the cases, the user is
51 *		informed by mail that the request is not being
52 *		processed due to lack of communications with the remote
53 *		system, and the request will be deleted in the future
54 *		if it the condition remains for several more days.
55 *
56 *	For DELETIONS:
57 *	C. files - if they reference only D. files, the C. is
58 *		merely deleted, because the D. files are usually
59 *		mail or news, and the later D. processing will
60 *		take care of them.
61 *		- if they reference files from the file system,
62 *		a message is constructed that will contain a
63 *		lines like
64 *		   We can't contact the remote.
65 *
66 *		   local!file -> remote!otherfile
67 *
68 *		   can't be executed.
69 *	X. files - merely deleted at present - D.s will be taken
70 *		care of later. Besides, some of the D.s are
71 *		missing or else the X. wouldn't be left around.
72 *	D. files - mail type data is sent to a local person if that
73 *		is where it originated.  If not, it is returned to the
74 *		sender -- assumed to be from the first From line.  If
75 *		a sender can't be determing, the file is merely deleted.
76 *		- rnews: if locally generated, just delete.  If remote,
77 *		the X. got lost, so execute rnews.
78 *	other files - just delete them.
79 *	.Workspace files over a day old
80 *
81 *	Deletions and executions are logged in
82 *	(CLEANUPLOG)--/var/uucp/.Admin/uucleanup
83*/
84
85#include	"uucp.h"
86
87#ifdef	V7
88#define O_RDONLY	0
89#endif
90
91#define USAGE	"[-oDAYS] [-mSTRING] [-Cdays] [-Ddays] [-Wdays] [-Xdays] [-xLEVEL] [-sSYSTEM]"
92
93extern int _age();		/* find the age of a file */
94extern void procdtype(), oprocess(), xprocess(), cprocess();
95extern void dXprocess(), dNprocess(), dMprocess(), dDprocess(), wprocess();
96extern int toWho(), sendMail(), execRnews();
97extern void logit();
98
99/* need these dummys to satisy some .o files */
100void cleanup(){}
101void systat(){}
102void logent(){}
103
104static void cleanworkspace(void);
105
106/* types of D. files */
107#define D_MAIL	1
108#define D_NEWS	2
109#define D_XFILE	3
110#define D_DATA	4
111#define FULLNAME(full,dir,file)	(void) sprintf(full, "%s/%s", dir, file);
112
113int _Ddays = 7;			/* D. limit */
114int _Cdays = 7;			/* C. limit */
115int _Xdays = 2;			/* X. limit */
116int _Odays = 2;			/* O. limit */
117int _Wdays = 1;			/* Warning limit for C. files */
118char _ShortLocal[6];		/* 5 char or less version of local name */
119
120char *_Undeliverable[] = {
121"Subject: Undeliverable Mail\n",
122"This mail message is undeliverable.\n",
123"(Probably to or from system '%s')\n",
124"It was sent to you or by you.\n",
125"Sorry for the inconvenience.\n",
126"",
127};
128
129#define CANT1	2	/* first line to fill in */
130#define CANT2	3	/* second line to fill in */
131char *_CantContact[] = {
132"Subject: Job Killed By uucp\n",
133"We can't contact machine '%s'.\n",
134" ",	/* uucleanup will fill in variable text here */
135" ",	/* fill in jobid of killed job */
136"",
137};
138
139#define WARN1	2
140#define WARN2	5
141#define WARN3	6
142char *_Warning[] = {
143"Subject: Warning From uucp\n",
144"We have been unable to contact machine '%s' since you queued your job.\n",
145" ",	/*  wprocess FILLS IN THIS LINE OF TEXT */
146"Attempts will continue for a few more days.\n",
147"",
148" ",	/*  wprocess FILLS IN THIS LINE WITH:  uucp job id is JOBid. */
149" ",	/* FILL IN THE -m STRING IF SPECIFIED */
150"",
151};
152
153int
154main(argc, argv, envp)
155int argc;
156char *argv[];
157char **envp;
158{
159	DIR *jcdir, *machdir, *spooldir;
160	char fullname[MAXFULLNAME], statfile[MAXFULLNAME], machname[MAXFULLNAME];
161	char file1[NAMESIZE+1], file2[NAMESIZE+1], file3[NAMESIZE+1];
162	char soptName[MAXFULLNAME], lockname[MAXFULLNAME];	/* name from -s option */
163	int i, value;
164
165	soptName[0] = NULLCHAR;
166	(void) strcpy(Logfile, CLEANUPLOGFILE);
167	uucpname(Myname);
168	(void) strncpy(_ShortLocal, Myname, 5);
169	_ShortLocal[5] = NULLCHAR;
170	(void) strcpy(Progname, "uucleanup");
171	while ((i = getopt(argc, argv, "C:D:W:X:m:o:s:x:")) != EOF) {
172		switch(i){
173		case 's':	/* for debugging - choose system */
174			(void) strcpy(soptName, optarg);
175			break;
176		case 'x':
177			Debug = atoi(optarg);
178			if (Debug <= 0 || Debug >= 10) {
179				fprintf(stderr,
180				"WARNING: %s: invalid debug level %s ignored, using level 1\n",
181					Progname, optarg);
182				Debug = 1;
183			}
184#ifdef SMALL
185			fprintf(stderr,
186				"WARNING: uucleanup built with SMALL flag defined -- no debug info available\n");
187#endif /* SMALL */
188			break;
189		case 'm':
190			_Warning[WARN3] = optarg;
191			break;
192		default:
193			(void) fprintf(stderr, "\tusage: %s %s\n",
194				Progname, USAGE);
195			exit(1);
196
197		case 'C':
198		case 'D':
199		case 'W':
200		case 'X':
201		case 'o':
202			value = atoi(optarg);
203			if (value < 1) {
204				fprintf(stderr," Options: CDWXo require value > 0\n");
205				exit(1);
206			}
207			switch(i) {
208			case 'C':
209				_Cdays = value;
210				break;
211			case 'D':
212				_Ddays = value;
213				break;
214			case 'W':
215				_Wdays = value;
216				break;
217			case 'X':
218				_Xdays = value;
219				break;
220			case 'o':
221				_Odays = value;
222				break;
223			}
224			break;
225		}
226	}
227
228	if (argc != optind) {
229		(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
230		exit(1);
231	}
232
233	DEBUG(5, "Progname (%s): STARTED\n", Progname);
234	DEBUG(5, "Myname (%s), ", Myname);
235	DEBUG(5, "_ShortLocal (%s)\n", _ShortLocal);
236	DEBUG(5, "Days C.(%d), ", _Cdays);
237	DEBUG(5, "D.(%d), ", _Ddays);
238	DEBUG(5, "W.(%d), ", _Wdays);
239	DEBUG(5, "X.(%d), ", _Xdays);
240	DEBUG(5, "other (%d)\n", _Odays);
241
242	cleanworkspace();
243	if (chdir(SPOOL) != 0) {
244		(void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n",
245			SPOOL, errno);
246		exit(1);
247	}
248	if ((spooldir = opendir(SPOOL)) == NULL) {
249		(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n",
250			SPOOL, errno);
251		exit(1);
252	}
253
254	while (gdirf(spooldir, file1, SPOOL) == TRUE) {
255
256		if (*soptName && !EQUALS(soptName, file1))
257			continue;
258
259		(void) strcpy(Rmtname, file1);
260		(void) sprintf(machname, "%s/%s", SPOOL, file1);
261		if ((machdir = opendir(machname)) == NULL) {
262			(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n",
263				machname, errno);
264			if (*soptName)
265				break;
266			else
267				continue;
268		}
269		DEBUG(7, "Directory: (%s) is open\n", file1);
270		while (gnamef(machdir, file2) == TRUE) {
271
272			(void) sprintf(statfile, "%s/%s", machname, file2);
273			if (DIRECTORY(statfile)) {
274				(void) sprintf(lockname, "%s.%.*s.%s",
275					LOCKPRE, SYSNSIZE, file1, file2);
276				if (cklock(lockname))
277					continue;
278				if ((jcdir = opendir(statfile)) == NULL) {
279					(void) fprintf(stderr,
280						"CAN'T OPEN (%s): errno (%d)\n",
281						statfile, errno);
282					continue;
283				}
284
285				DEBUG(7, "Directory: (%s) is open\n", file2);
286				while (gnamef(jcdir, file3)) {
287					DEBUG(9, "file: %s\n", file3);
288					FULLNAME(fullname, statfile, file3);
289					DEBUG(9,"Fullname is (%s)\n", fullname);
290					if (EQUALSN(file3, "C.", 2)) {
291						if (_age(fullname) >= _Cdays)
292							cprocess(fullname);
293						else if(_age(fullname) >= _Wdays)
294							wprocess(statfile, file3);
295					}
296					else if (EQUALSN(file3, "D.", 2)) {
297						if (_age(fullname) >= _Ddays)
298							procdtype(statfile, file3);
299					}
300					else if (_age(fullname) >= _Odays)
301						oprocess(fullname);
302				}
303				closedir(jcdir);
304				continue;
305			}
306			DEBUG(9, "file: %s\n", file2);
307			DEBUG(9, "Fullname is (%s)\n", statfile);
308			if (EQUALSN(file2, "X.", 2)) {
309				if (_age(statfile) >= _Xdays)
310					xprocess(statfile);
311			}
312			else if (EQUALSN(file2, "D.", 2)) {
313				if (_age(statfile) >= _Ddays)
314					procdtype(machname, file2);
315			}
316			else if (_age(statfile) >= _Odays)
317				oprocess(statfile);
318		}
319		closedir(machdir);
320	}
321	closedir(spooldir);
322	return (0);
323}
324
325/* procdtype - select the type of processing that a D. file should receive */
326
327void
328procdtype(dir, file)
329char *dir, *file;
330{
331
332	char fullname[MAXFULLNAME];
333
334	FULLNAME(fullname, dir, file);
335
336	switch(dType(fullname)) {
337	case D_DATA:
338		dDprocess(fullname);
339		break;
340	case D_MAIL:
341		dMprocess(dir, file);
342		break;
343	case D_NEWS:
344		dNprocess(dir, file);
345		break;
346	case D_XFILE:
347		dXprocess(fullname);
348		break;
349	default:
350		break;
351	}
352	return;
353}
354
355/* xprocess - X. file processing -- just remove the X. for now */
356
357void
358xprocess(fullname)
359char *fullname;
360{
361	char text[BUFSIZ];
362
363	DEBUG(5, "xprocess(%s), ", fullname);
364	DEBUG(5, "unlink(%s)\n", fullname);
365	(void) sprintf(text, "xprocess: unlink(%s)", fullname);
366	errno = 0;
367	(void) unlink(fullname);
368	logit(text, errno);
369	return;
370}
371
372/*
373 * cprocess - Process old C. files
374 *
375 */
376
377#define CMFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n\nCan't be executed."
378#define XFMT  "\n\t%s!%s  (Date %2.2d/%2.2d)\n"
379#define XMFMT  "\n\tmail %s!%s   (Date %2.2d/%2.2d)\n"
380#define WFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n"
381void
382cprocess(fullname)
383char *fullname;
384{
385	struct stat s;
386	struct tm *tp;
387	char buf[BUFSIZ], user[9];
388	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
389	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
390	FILE *fp;
391	int ret;
392
393	DEBUG(5, "cprocess(%s)\n", fullname);
394
395
396	fp = fopen(fullname, "r");
397	if (fp == NULL) {
398		DEBUG(5, "Can't open file (%s), ", fullname);
399		DEBUG(5, "errno=%d -- skip it!\n", errno);
400		return;
401	}
402	if (fstat(fileno(fp), &s) != 0) {
403	    /* can't happen; _age() did stat of this file and file is opened */
404	    (void) fclose(fp);
405	    return;
406	}
407	tp = localtime(&s.st_mtime);
408
409	if (s.st_size == 0) { /* dummy C. for polling */
410		DEBUG(5, "dummy C. -- unlink(%s)\n", fullname);
411		(void) sprintf(text, "dDprocess: dummy C. unlinked(%s)",
412			fullname);
413		errno = 0;
414		(void) unlink(fullname);
415		logit(text, errno);
416		(void) fclose(fp);
417		return;
418	}
419
420	/* Read the C. file and process it */
421	while (fgets(buf, BUFSIZ, fp) != NULL) {
422		buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
423		if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
424			user, opt, file3) <5) {
425			(void) sprintf(text, "cprocess: Bad C. %s, unlink(%s)",
426				buf, fullname);
427			break;
428		}
429
430		*text = NULLCHAR;
431		ret = 0;
432		/* fill in line 3 of text */
433		(void) sprintf(text2, "Job (%s) killed!\n",
434		BASENAME(fullname, '/')+2);
435		_CantContact[CANT2] = text2;
436		if (*type == 'S') {
437			if (EQUALSN(file1, "D.", 2))
438				/* generated file (mail/news) I think */
439				/* D. processing will return it later */
440				continue;
441
442			/* some data was requested -- tell user */
443
444			(void) sprintf(text1, CMFMT, Myname, file1, Rmtname, file2,
445				tp->tm_mon + 1, tp->tm_mday);
446			_CantContact[CANT1] = text1;
447			ret = sendMail((char *) NULL, user, "", _CantContact);
448		}
449		else if (*type == 'R') {
450			(void) sprintf(text1, CMFMT, Rmtname, file1, Myname, file2,
451				tp->tm_mon + 1, tp->tm_mday);
452			_CantContact[CANT1] = text1;
453			ret = sendMail((char *) NULL, user, "", _CantContact);
454		}
455	}
456
457	if (!*text) {
458		(void) sprintf(text,
459			"cprocess: C. %s, mail returned (%d), unlink(%s)",
460			buf, ret, fullname);
461	}
462	DEBUG(3, "text (%s)\n", text);
463
464	errno = 0;
465	(void) unlink(fullname);
466	logit(text, errno);
467
468	(void) fclose(fp);
469	return;
470}
471
472/*
473 * wprocess - send warning messages for C. == Wdays
474 */
475
476void
477wprocess(dir, file)
478char *dir, *file;
479{
480	struct stat s;
481	struct tm *tp;
482	char fullname[BUFSIZ], xfile[BUFSIZ], xF_file[BUFSIZ];
483	char buf[BUFSIZ], user[BUFSIZ];
484	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
485	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
486	char *realuser, uline_m[NAMESIZE], uline_u[BUFSIZ], retaddr[BUFSIZ];
487	FILE *fp, *xfp;
488	int ret;
489
490	FULLNAME(fullname, dir, file);
491	DEBUG(5, "wprocess(%s)\n", fullname);
492
493	fp = fopen(fullname, "r");
494	if (fp == NULL) {
495		DEBUG(4, "Can't open file (%s), ", fullname);
496		DEBUG(4, "errno=%d -- skip it!\n", errno);
497		return;
498	}
499
500	if (fstat(fileno(fp), &s) != 0) {
501
502	/* can't happen; _age() did stat of this file and file is opened */
503
504		(void) fclose(fp);
505		return;
506	}
507
508	tp = localtime(&s.st_mtime);
509
510	if (s.st_size == 0) { /* dummy C. for polling */
511		DEBUG(5, "dummy C. -- skip(%s)\n", fullname);
512		(void) fclose(fp);
513		return;
514	}
515
516	/* read C. and process it */
517	while (fgets(buf, BUFSIZ, fp) != NULL) {
518		buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
519		if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
520			user, opt, file3) <5) {
521			DEBUG(5, "short line (%s): ", buf);
522			DEBUG(5, "bad D. -- skip(%s)\n", fullname);
523			(void) fclose(fp);
524			return;
525		}
526
527		/* set up the 6th text line of the mail message */
528
529		(void) sprintf(text2,
530			"\nuucp job id is %s.\n", BASENAME(fullname, '/')+2);
531
532		_Warning[WARN2] = text2;
533
534		/* if Send type then do C. file processing */
535
536		if (*type == 'S') {
537
538		/* if this is a uux job - tell user about it */
539
540			if (EQUALSN(file2, "X.", 2)) {
541				FULLNAME(xfile, dir, file1);
542
543				/* if X.file can't be read then skip it */
544
545				if ((xfp = fopen(xfile, "r")) == NULL) {
546					DEBUG(3, "Can't read %s\n", xfile);
547					break;
548				}
549				*retaddr = *uline_u = *uline_m = *text = NULLCHAR;
550				while (fgets(buf, BUFSIZ, xfp) != NULL) {
551
552				/* remove \n from end of buffer */
553
554					buf[strlen(buf)-1] = NULLCHAR;
555					switch(*buf) {
556
557					/* save the file name */
558
559					case 'F':
560						FULLNAME(xF_file, dir, &buf[2]);
561						break;
562
563					/* save return address */
564
565					case 'R':
566						sscanf(buf+2, "%s", retaddr);
567						DEBUG(7, "retaddr (%s)\n", retaddr);
568						break;
569
570					/* save machine, user */
571
572					case 'U':
573						sscanf(buf+2, "%s%s",
574						    uline_u, uline_m);
575						break;
576					}
577
578					if (buf[0] != 'C')
579						continue;
580					realuser = uline_u;
581					if (*retaddr != NULLCHAR)
582						realuser = retaddr;
583					if (*realuser == NULLCHAR)
584						strcpy(realuser, user);
585					if (!EQUALS(uline_m, Myname))
586						sprintf(user, "%s!%s",
587						    uline_m, realuser);
588					else
589						strcpy(user, realuser);
590
591					/* give mail special handling */
592					if (EQUALSN(buf+2, "rmail ", 6))
593						(void) sprintf(text1, XMFMT,
594						    Rmtname, buf+8,
595						    tp->tm_mon+1, tp->tm_mday);
596					else
597						(void) sprintf(text1, XFMT,
598						    Rmtname, buf+2,
599						    tp->tm_mon+1, tp->tm_mday);
600
601					_Warning[WARN1] = text1;
602					if (EQUALSN(&buf[2], "rmail", 5))
603						/*
604						 * this is mail; append
605						 * user mail (xF_file).
606						 */
607						ret = sendMail((char *) NULL,
608						    user, xF_file, _Warning);
609					else
610						ret = sendMail((char *) NULL,
611						    user, "", _Warning);
612					break;
613				}
614				(void) fclose(xfp);
615				break;
616			}
617
618			/* if file1 is a D. file the it might be (mail/news) */
619			/* if so then D. processing will take of it later */
620
621			if (EQUALSN(file1, "D.", 2))
622				continue;
623
624			/* some data was requested -- tell user */
625			/* set up the 2nd text line of the mail message */
626
627			(void) sprintf(text1, WFMT, Myname, file1, Rmtname, file2,
628				tp->tm_mon + 1, tp->tm_mday);
629			_Warning[WARN1] = text1;
630			ret = sendMail((char *) NULL, user, "", _Warning);
631		}
632
633		/* Receive C. file processing */
634
635		else if (*type == 'R') {
636			if (EQUALSN(file1, "D.", 2) && EQUALSN(file2, "D.", 2))
637				continue;
638
639			(void) sprintf(text1, WFMT, Rmtname, file1, Myname, file2,
640				tp->tm_mon + 1, tp->tm_mday);
641			_Warning[WARN1] = text1;
642			ret = sendMail((char *) NULL, user, "", _Warning);
643		}
644	}	/* end while - read C. lines loop */
645
646	(void) sprintf(text,
647		"wprocess: %s: %s, warning message sent to %s, returned (%d)",
648		fullname, buf, user, ret);
649
650	DEBUG(3, "text (%s)\n", text);
651
652	logit(text, errno);
653
654	(void) fclose(fp);
655	return;
656}
657/*
658 * oprocess - some unknown file just remove the file
659 */
660
661void
662oprocess(fullname)
663char *fullname;
664{
665
666	char *p, text[BUFSIZ];
667
668	p = BASENAME(fullname, '/');
669	if (EQUALSN(p, "P.", 2) == 0)
670		if (_age(fullname) <= _Cdays)
671			return;
672	DEBUG(5, "oprocess(%s), ", fullname);
673	DEBUG(5, "unlink(%s)\n", fullname);
674	(void) sprintf(text, "oprocess: unlink(%s)", fullname);
675	errno = 0;
676	(void) unlink(fullname);
677	logit(text, errno);
678	return;
679}
680
681/*
682 * dDprocess - random D. file (not mail or rnews)
683 *--just delete it for now
684 */
685
686void
687dDprocess(fullname)
688char *fullname;
689{
690	char text[BUFSIZ];
691
692	DEBUG(5, "dDprocess(%s), ", fullname);
693	DEBUG(5, "unlink(%s)\n", fullname);
694	(void) sprintf(text, "dDprocess: unlink(%s)", fullname);
695	errno = 0;
696	(void) unlink(fullname);
697	logit(text, errno);
698	return;
699}
700
701/*
702 * dXprocess - process D. files that are destined for X. on remote
703 * --for now just delete it
704 */
705
706void
707dXprocess(fullname)
708char *fullname;
709{
710	char text[BUFSIZ];
711
712	DEBUG(5, "dXprocess(%s), ", fullname);
713	DEBUG(5, "  unlink(%s)\n", fullname);
714	(void) sprintf(text, "dXprocess: unlink(%s)", fullname);
715	errno = 0;
716	(void) unlink(fullname);
717	logit(text, errno);
718	return;
719}
720
721/*
722 * dMprocess - process ophan D. mail files
723 * There are two types: ones generated locally and
724 * others that are from remotes.  They can be identified
725 * by the system name following the D.
726 * Local ones have the local name.
727 */
728
729void
730dMprocess(dir, file)
731char *dir, *file;
732{
733	int ret;
734	char fullname[MAXFULLNAME];
735	char *toUser, *toSystem;
736	char text[BUFSIZ];
737
738	(void) sprintf(fullname, "%s/%s", dir, file);
739	DEBUG(5, "dMprocess(%s)\n", fullname);
740
741
742	if (PREFIX(_ShortLocal, &file[2])) {
743		DEBUG(5, "  Local file %s: ", file);
744	}
745	else {
746		DEBUG(5, "  Remote file %s: ", file);
747	}
748	if (toWho(fullname, &toUser, &toSystem)) {
749		DEBUG(5, "toUser %s, ", toUser);
750		DEBUG(5, "toSystem %s  ", toSystem);
751		ret = sendMail(toSystem, toUser, fullname, _Undeliverable);
752		DEBUG(5, "Mail sent, unlink(%s)\n", fullname);
753		(void) sprintf(text,
754			"dMprocess: mail %s to %s!%s, returned (%d),  unlink(%s)",
755			fullname, toSystem, toUser, ret, fullname);
756		errno = 0;
757		(void) unlink(fullname);
758		logit(text, errno);
759	}
760	return;
761}
762
763/*
764 * dNprocess - process ophan D. netnews files
765 * There are two types: ones generated locally and
766 * others that are from remotes.  They can be identified
767 * by the system name following the D.
768 * Local ones have the local name.
769 */
770
771void
772dNprocess(dir, file)
773char *dir, *file;
774{
775	char fullname[MAXFULLNAME];
776	char text[BUFSIZ];
777	int ret;
778
779	(void) sprintf(fullname, "%s/%s", dir, file);
780	DEBUG(5, "dNprocess(%s)\n", fullname);
781
782
783	if (PREFIX(_ShortLocal, &file[2])) {
784	/* just delete it, the C. is gone */
785		DEBUG(5, "  Local file %s, ", file);
786		DEBUG(5, "unlink(%s)\n", fullname);
787		(void) unlink(fullname);
788		(void) sprintf(text, "dNprocess: Local news item unlink(%s)",
789			fullname);
790		errno = 0;
791		(void) unlink(fullname);
792		logit(text, errno);
793	}
794	else {
795	/* execute rnews with this file - the X. is missing */
796		DEBUG(5, "  Remote file %s, ", file);
797		DEBUG(5, "exec rnews(%s), ", fullname);
798		ret = execRnews(fullname);
799		DEBUG(5, "unlink(%s)\n", fullname);
800		(void) sprintf(text,
801			"dNprocess: Remote - exec rnews %s: returned (%d), unlink(%s)",
802			fullname, ret, fullname);
803		errno = 0;
804		(void) unlink(fullname);
805		logit(text, errno);
806	}
807	return;
808}
809
810
811
812static long _sec_per_day = 86400L;
813
814/*
815 * _age - find the age of "file" in days
816 * return:
817 *	age of file
818 *	0 - if stat fails
819 */
820
821int
822_age(fullname)
823char *fullname;
824{
825	static time_t ptime = 0;
826	time_t time();
827	struct stat stbuf;
828	int e;
829
830	if (!ptime)
831		(void) time(&ptime);
832	if (stat(fullname, &stbuf) != -1) {
833		return ((int)((ptime - stbuf.st_mtime)/_sec_per_day));
834	}
835	e = errno;
836	DEBUG(9, "_age: stat (%s) failed", fullname);
837	DEBUG(9, ", errno %d\n", e);
838	return(0);
839}
840
841/*
842 * dType - return the type of D. file
843 * return:
844 *	FAIL - can't read D. file
845 *	D_MAIL - mail message D. file
846 *	D_NEWS - netnews D. file
847 *	D_DATA - other kind of D. file
848 *	D_XFILE - destined for X. on destination machine
849 */
850
851/* NLINES - number of lines of D. file to read to determine type */
852#define NLINES	10
853
854int
855dType(fullname)
856char *fullname;
857{
858	char buf[BUFSIZ];
859	FILE *fp;
860	int i, type;
861
862	fp = fopen(fullname, "r");
863	if (fp == NULL) {
864		DEBUG(4, "Can't open file (%s), ", fullname);
865		DEBUG(4, "errno=%d -- skip it!\n", errno);
866		return(FAIL);
867	}
868	type = D_DATA;
869
870	/* read first NLINES lines to determine file type */
871
872	for (i=0; i<NLINES; i++) {
873		if (fgets(buf, BUFSIZ, fp) == NULL)
874			break;	/* no more lines */
875		DEBUG(9, "buf: %s\n", buf);
876		if (EQUALSN(buf, "From ", 5)) {
877			type = D_MAIL;
878			break;
879		}
880		if (EQUALSN(buf, "U ", 2)) {
881			type = D_XFILE;
882			break;
883		}
884		if (EQUALSN(buf, "Newsgroups: ", 12)) {
885			type = D_NEWS;
886			break;
887		}
888	}
889
890	(void) fclose(fp);
891	return(type);
892}
893
894/*
895 * sendMail - send mail file and message to user (local or remote)
896 * return:
897 *	the return from the pclose - mail exit status
898 */
899int
900sendMail(system, user, file, mtext)
901char *system, *user, *file;
902char *mtext[];
903{
904	FILE *fp, *fi;
905	char cmd[BUFSIZ];
906	char *p;
907
908	DEBUG(5, "Mail %s to ", file);
909	DEBUG(5, "%s\n", user);
910
911	/* get rid of some stuff that could be dangerous */
912	if (system != NULL && (p = strpbrk(system, Shchar)) != NULL) {
913		*p = NULLCHAR;
914	}
915	if (user != NULL && (p = strpbrk(user, Shchar)) != NULL) {
916		*p = NULLCHAR;
917	}
918	if (system != NULL && *system != '\0')
919		(void) sprintf(cmd, "%s %s '%s!%s'", PATH, MAIL, system, user);
920	else
921		(void) sprintf(cmd, "%s %s '%s'", PATH, MAIL, user);
922
923	DEBUG(7, "sendMail: %s\n", cmd);
924	if ((fp = popen(cmd, "w")) == NULL)
925		return(-errno);
926	while (*mtext[0] )
927		(void) fprintf(fp, *mtext++, Rmtname);
928
929	(void) fprintf(fp, "\n\tSincerely,\n\t%s!uucp\n", Myname);
930	(void) fprintf(fp,
931		"\n#############################################\n");
932
933	if (*file) {
934	/*next statement should never happen;I read once */
935		if ((fi= fopen(file, "r")) == NULL)
936			return(pclose(fp));
937		(void) fprintf(fp,
938			"##### Data File: ############################\n");
939		xfappend(fi, fp);
940		(void) fclose(fi);
941	}
942	return(pclose(fp));
943}
944
945/*
946 * execRnews - execute rnews command with stdin file
947 * return:
948 *	the return from the pclose - rnews exit status
949 */
950int
951execRnews(file)
952char *file;
953{
954	FILE *fp, *fi;
955	char cmd[BUFSIZ];
956
957	DEBUG(5, "Rnews %s\n", file);
958
959	(void) sprintf(cmd, "%s rnews ", PATH);
960	if ((fp = popen(cmd, "w")) == NULL)
961		return(-errno);
962
963	if ( (fi = fopen(file, "r")) == NULL) /* never happen - I read once */
964		return(pclose(fp));
965	xfappend(fi, fp);
966	(void) fclose(fi);
967
968	return(pclose(fp));
969}
970
971/*
972 * toWho - figure out who to send this dead mail to
973 *	It is a guess;
974 *	If there is a local address, send it there.
975 *	If not, send it back where it came from.
976 * return:
977 *	0 - could not find system and user information
978 *	1 - found it
979 */
980
981int
982toWho(file, user, system)
983char *file;	/* the D. mail message file */
984char **system;	/* pointer to the system name */
985char **user;	/* pointer to the user name */
986{
987	char buf[BUFSIZ];
988	FILE *fp;
989	int i;
990	static char fuser[BUFSIZ], fsystem[MAXBASENAME+1];  /* from first From */
991	static char luser[BUFSIZ], lsystem[MAXBASENAME+1];  /* from other From */
992
993	*fuser = NULLCHAR;
994	DEBUG(5, "toWho(%s)\n", file);
995	fp = fopen(file, "r");
996	for (i=0; i<NLINES; i++) {
997		if (fgets(buf, BUFSIZ, fp) == NULL)
998			break;	/* no more lines */
999		DEBUG(9, "buf: %s\n", buf);
1000		if (!analFrom(buf, luser, lsystem))
1001			continue;
1002		if ( !*fuser) {
1003			(void) strcpy(fuser, luser);
1004			(void) strcpy(fsystem, lsystem);
1005		}
1006		if (EQUALS(Myname, lsystem)) {
1007			*user = luser;
1008			*system = lsystem;
1009			(void) fclose(fp);
1010			return(1);
1011		}
1012	}
1013
1014	/* could not find local user - use first line */
1015	(void) fclose(fp);
1016	if (!*fuser)	/* didn't find all information */
1017		return(0);
1018	*user = fuser;
1019	*system = fsystem;
1020	return(1);
1021}
1022
1023/* analFrom - analyze From line
1024 * return:
1025 *	0 - didn't find both from and remote from info
1026 *	1 - found info.
1027 */
1028
1029int
1030analFrom(line, user, system)
1031char *line, *user, *system;
1032{
1033	char *s;
1034	int i;
1035
1036	if (!PREFIX("From ", line) && !PREFIX(">From ", line))
1037		return(0);
1038
1039	s = strchr(line, ' ') + 1;
1040	for (i = 0;  *s && *s != ' ' && *s != '\n'; i++)
1041		user[i] = *s++;
1042	user[i] = NULLCHAR;
1043
1044	/* look for "remote from" */
1045	while (*s && ((s = strchr(s, ' ')) != NULL)) {
1046		s++;
1047		if (PREFIX("remote from ", s)) {	/* found it */
1048			s = s + strlen("remote from ");
1049			for (i = 0; (i<MAXBASENAME) && *s && *s != ' ' && *s != '\n'; i++)
1050				system[i] = *s++;
1051			system[i] = NULLCHAR;
1052			return(1);
1053		}
1054	}
1055	return(0);
1056}
1057
1058
1059
1060static FILE	*_Lf = NULL;
1061
1062/*
1063 * Make log entry
1064 *	text	-> ptr to text string
1065 *	status	errno number
1066 * Returns:
1067 *	none
1068 */
1069
1070void
1071logit(text, status)
1072char	*text;
1073int status;
1074{
1075
1076	if (Nstat.t_pid == 0)
1077		Nstat.t_pid = getpid();
1078
1079	if (_Lf == NULL) {
1080		_Lf = fopen(Logfile, "a");
1081		(void) chmod(Logfile, LOGFILEMODE);
1082		if (_Lf == NULL)
1083			return;
1084		setbuf(_Lf, CNULL);
1085	}
1086	(void) fseek(_Lf, 0L, 2);
1087	(void) fprintf(_Lf, "%s ", Rmtname);
1088	(void) fprintf(_Lf, "(%s,%ld,%d) ", timeStamp(), (long) Nstat.t_pid, Seqn);
1089	(void) fprintf(_Lf, "%s (%d)\n", text, status);
1090	return;
1091}
1092
1093static void
1094cleanworkspace(void)
1095{
1096	DIR	*spooldir;
1097	char f[MAXFULLNAME];
1098
1099	if (chdir(WORKSPACE) != 0) {
1100		(void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n", WORKSPACE, errno);
1101		return;
1102	}
1103	if ((spooldir = opendir(WORKSPACE)) == NULL) {
1104		(void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", WORKSPACE, errno);
1105		return;
1106	}
1107
1108	while (gnamef(spooldir, f) == TRUE)
1109		if (_age(f) >= 1)
1110			if (unlink(f) != 0)
1111				(void) fprintf(stderr, "CAN'T UNLINK (%s): errno (%d)\n", f, errno);
1112
1113}
1114