root/usr/src/cmd/bnu/uucleanup.c
/*
 * 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   */

/*
 *      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);

}