home *** CD-ROM | disk | FTP | other *** search
- /*++
- /* NAME
- /* pc-mail 8
- /* SUMMARY
- /* deliver mail to nfs-based pc-mail users
- /* PROJECT
- /* pc-mail
- /* PACKAGE
- /* nfs
- /* SYNOPSIS
- /* pc-mail user
- /* DESCRIPTION
- /* This program is to be run on the nfs server that exports mail
- /* directories to MS-DOS pc-mail users. The program replaces the
- /* UNIX -> MS-DOS file transfer function of the MS-DOS \fIcico\fR
- /* program.
- /*
- /* Normally, the pc-mail delivery program is invoked by sendmail(8).
- /* Its purpose is to deliver new mail in the mail directory of the
- /* specified \fIuser\fR (default /usr/spool/pc-mail/\fIuser\fR).
- /* Any error conditions detected by the pc-mail delivery program
- /* are reported back in a sendmail-compatible manner.
- /*
- /* This program must be run with root privileges. It will assume
- /* the (uid, gid) of the specified user before delivering mail.
- /*
- /* The program attempts to create any missing directories, and to
- /* correct ownerships or protections where needed.
- /* FILES
- /* /usr/spool/pc-mail/\fIuser\fR/nNNNNN, mail message.
- /* /usr/spool/pc-mail/\fIuser\fR/hNNNNN, sender of message and subject.
- /* (NNNNN is the pc-mail "message id").
- /* SEE ALSO
- /* pc-maild(1)
- /* DIAGNOSTICS
- /* All conceivable error conditions cause the program to terminate
- /* with a non-zero exit status, after printing an error message on
- /* the standard error stream, and appending an entry to the system log.
- /* See <sysexits.h> for details.
- /* BUGS
- /* There is no way to notify a pc-mail user of the arrival of new mail.
- /* AUTHOR(S)
- /* W.Z. Venema
- /* Eindhoven University of Technology
- /* Department of Mathematics and Computer Science
- /* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
- /* CREATION DATE
- /* Sun Oct 22 18:00:53 MED 1989
- /* LAST MODIFICATION
- /* 1/6/90 19:08:13
- /* VERSION/RELEASE
- /* 1.10
- /*--*/
-
- #ifndef lint
- static char sccsid[] = "@(#) pc-mail.c 1.10 1/6/90 19:08:13";
-
- #endif
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <pwd.h>
- #include <varargs.h>
-
- #ifdef SYSLOG
- #include <syslog.h>
- #else
- #include "syslog.h"
- #endif
-
- #ifdef SYSV
- #include <ndir.h>
- #else
- #include <sys/dir.h>
- #endif
-
- #ifdef SYSEXITS
- #include <sysexits.h>
- #else
- #include "sysexits.h"
- #endif
-
- #include "dosunix.h"
- #include "percentm.h"
- #include "ms_parse.h"
-
- /* Stuff related to failed system calls */
-
- extern int errno;
-
- /* External functions */
-
- extern struct passwd *getpwnam();
- extern long time();
- extern char *mktemp();
- extern void exit();
- extern unsigned sleep();
-
- /* Local declarations */
-
- #ifndef MAILDIR
- #define MAILDIR "/usr/spool/pc-mail" /* pc-mail directory tree */
- #endif
-
- #define LOCK "pc-mail.lck" /* the lock file */
- #define STALE 1800 /* max age of lock file */
- #define MAXTRY 60 /* max retry count for lock creation */
- #define MSGFIL_FMT "n%05d" /* message file name format */
- #define SNDFIL_FMT "h%05d" /* sender file name format */
- #define MAXLINE 1024 /* max length of recipient line */
-
- char template[] = "pc.XXXXXX"; /* template lock file */
-
- /* local functions */
-
- void sender();
- void message();
- void error();
-
- char *progname;
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- struct passwd *pwd;
- static char userdir[BUFSIZ];
- int seqno;
-
- progname = argv[0];
-
- /* Garbage in, garbage out */
-
- if (argc != 2)
- error(EX_USAGE, "usage: %s user", *argv);
-
- #ifndef DEBUG
- if (geteuid() != 0)
- error(EX_USAGE, "must run with root privileges");
-
- /* need this for SYSVR2 or mkdir(1) fails */
- #ifdef SYSV
- if (setuid(0) != 0)
- error(EX_OSERR, "cannot setuid(0)");
- #endif
- #endif
-
- if ((pwd = getpwnam(argv[1])) == 0)
- error(EX_NOUSER, "unknown user: %s", argv[1]);
-
- /* Setup a somewhat safe environment */
-
- if (putenv("PATH=/bin:/usr/bin:/usr/ucb")
- || putenv("IFS= \t\n"))
- error(EX_TEMPFAIL, "putenv() failed");
-
- /* Check the necessary directories exist */
-
- (void) sprintf(userdir, "%s/%s", MAILDIR, argv[1]);
- checkdir(userdir, pwd->pw_uid, pwd->pw_gid, 0700);
-
- /* Now with that out of the way, try to deliver the message */
-
- if (setgid(pwd->pw_gid))
- error(EX_USAGE, "setgid(%s) failed: %m", argv[1]);
- if (setuid(pwd->pw_uid))
- error(EX_USAGE, "setuid(%s) failed: %m", argv[1]);
-
- /* make sure the user mail directory is accessible */
-
- if (chdir(userdir))
- error(EX_TEMPFAIL, "can't access mail directory %s", userdir);
-
- /* deliver mail */
-
- seqno = newseqno(userdir); /* Allocate sequence number */
- message(pwd, seqno); /* Create message file */
- sender(seqno); /* Create metafile (sender) */
- exit(EX_OK); /* Done. */
- /* NOTREACHED */
- }
-
- /* message - write message file */
-
- void message(pwd, seqno)
- struct passwd *pwd;
- int seqno;
- {
- static char buf[BUFSIZ];
- register FILE *fp;
-
- /* Create the message file */
-
- (void) sprintf(buf, MSGFIL_FMT, seqno);
- if ((fp = fopen(buf, "w")) == 0)
- error(EX_CANTCREAT, "create error for file %s/%s: %m",
- pwd->pw_name, buf);
- if (unix2dos(stdin, fp)) {
- (void) unlink(buf);
- error(EX_CANTCREAT, "write error for file %s/%s: %m",
- pwd->pw_name, buf);
- }
- (void) fclose(fp);
- (void) chmod(buf, 0400); /* Avoid tampering */
- }
-
- /* sender - extract sender from message */
-
- void sender(seqno)
- int seqno;
- {
- register FILE *ifp;
- register FILE *ofp;
- static char fname[BUFSIZ]; /* file names */
- static char line[MAXLINE]; /* read buffer */
- static char from[MAXLINE] = "Unknown"; /* sender */
- static char subject[MAXLINE] = ""; /* subject */
- register int context = MS_UUCP;
-
- /*
- * Try to open the message file; if that fails, let the pc software scan
- * for the sender at a later time.
- *
- * We recognize the following From line formats:
- *
- * From name stuff use name
- *
- * >From name stuff use name
- *
- * From: address (full_name) use full_name
- *
- * From: full_name <address> use full_name
- *
- * From: full_name use full_name
- */
-
- (void) sprintf(fname, MSGFIL_FMT, seqno);
- if ((ifp = fopen(fname, "r")) == 0)
- return;
-
- /* Extract sender and subject from message */
-
- while (dosgets(line, sizeof(line), ifp) != 0
- && (context = ms_parse(context, line)) != MS_BODY) {
- switch (context) {
- case MS_UUCP:
- if (sscanf(line, "%*[>] From %s", from) != 1)
- (void) sscanf(line, "From %s", from);
- break;
- case MS_HEADER:
- if (hscanf(line, "Subject:", " %[^\n]", subject) == 0
- && hscanf(line, "From:", " %*s ( %[^)] )", from) == 0)
- (void) hscanf(line, "From:", " %[^<]", from);
- break;
- }
- }
- (void) fclose(ifp);
-
- /*
- * Try to create the meta file; if that fails, let the pc software try
- * again at a later time.
- */
-
- (void) sprintf(fname, SNDFIL_FMT, seqno);
- if (ofp = fopen(fname, "w")) {
- (void) fprintf(ofp, "%s\r\n%s\r\n", from, subject);
- if (fflush(ofp) || ferror(ofp) || feof(ofp) || fclose(ofp)) {
- (void) unlink(fname);
- } else {
- (void) chmod(fname, 0400); /* avoid tampering */
- }
- }
- }
-
- /* newseqno - allocate new message sequence number */
-
- int newseqno(userdir)
- char *userdir;
- {
- register DIR *dd;
- register struct direct *p;
- struct stat st;
- register int seqno = 0;
- int tmp = 0;
- int i;
- char junk;
-
- /*
- * When the pc adds a file to the "mail data base", the file name is
- * composed of a single letter and a unique sequence number. The pc
- * chooses a new sequence number by adding one to the highest existing
- * sequence number.
- *
- * Now that the pc mounts its mail directory from the nfs server we must
- * avoid possible concurrency conflicts when both pc and file server try
- * to update the "mail data base".
- *
- * Since the pc does not know about concurrent access from the nfs server,
- * the server has to add 2 to the highest existing message sequence
- * number, in order to avoid conflicts. Fortunately, only one pc at a
- * time will be accessing a mail directory of a particular user.
- *
- * Further concurrency conflicts are be avoided on the server side by using
- * lock files.
- *
- * If we cannot create a lock file right now, we back off and let sendmail
- * try again later.
- */
-
- /* Get rid of stale lock files */
-
- if (stat(LOCK, &st) == 0 && st.st_mtime < time((long *) 0) - STALE)
- (void) unlink(LOCK);
-
- /* Wait until we can create the lock file */
-
- if (creat(mktemp(template), 0400) < 0)
- error(EX_TEMPFAIL, "cannot set lock in directory %s: check ownership",
- userdir);
- for (i = 0; link(template, LOCK) && i < MAXTRY; i++)
- (void) sleep(1);
- (void) unlink(template);
- if (i >= MAXTRY)
- error(EX_TEMPFAIL, "locked: %s", userdir);
-
- /* Scan the user mail directory for the highest existing message number */
-
- if ((dd = opendir(userdir)) == 0) {
- (void) unlink(LOCK);
- error(EX_TEMPFAIL, "opendir(\"%s\") failed: %m", userdir);
- }
- while (p = readdir(dd)) {
- if (strlen(p->d_name) == 6
- && sscanf(p->d_name + 1, "%d%c", &tmp, &junk) == 1 && tmp > seqno)
- seqno = tmp;
- }
-
- /* clean up and terminate */
-
- closedir(dd);
- (void) unlink(LOCK);
- return (seqno + 2);
- }
-
- /* checkdir - check/update presence/ownership/protection of directory */
-
- checkdir(path, uid, gid, mode)
- char *path;
- int uid;
- int gid;
- int mode;
- {
- struct stat st;
-
- /*
- * If a user mail directory does not exist, try to create it. Otherwise,
- * make sure it has sane permissions
- */
-
- if (stat(path, &st) == -1) { /* no directory */
- if (mkdir(path, mode)) /* try to create it */
- error(EX_TEMPFAIL, "cannot create directory %s: %m", path);
- if (chown(path, uid, gid)) /* set owner, group */
- error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
- } else { /* directory exists */
- if ((st.st_mode & S_IFMT) != S_IFDIR) /* must be directory! */
- error(EX_TEMPFAIL, "%s should be a directory", path);
- if ((st.st_uid != uid || st.st_gid != gid) /* check owner/group */
- &&chown(path, uid, gid)) /* correct owner, group */
- error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
- if ((st.st_mode & 0777) != mode /* check permissions */
- && chmod(path, mode)) /* correct permissions */
- error(EX_TEMPFAIL, "cannot chmod %o directory %s: %m", mode, path);
- }
- }
-
- /* error - print diagnostic and terminate */
-
- /* VARARGS */
-
- void error(va_alist) va_dcl
- {
- va_list ap;
- register int exstat;
- register char *fmt;
- char buf[BUFSIZ];
- int err = errno;
-
- /* Format the error message */
-
- va_start(ap);
- exstat = va_arg(ap, int); /* exit status */
- fmt = va_arg(ap, char *); /* format string */
- (void) vsprintf(buf, percentm(fmt, err), ap);
- va_end(ap);
-
- /* Write message to standard error stream */
-
- (void) fprintf(stderr, "%s: %s\n", progname, buf);
-
- /* Append the same message to system log */
-
- (void) openlog("pc-mail", LOG_PID, LOG_MAIL);
- (void) syslog(LOG_WARNING, "%s", buf);
- (void) closelog();
-
- /* Notify sendmail of the nature of the problem */
-
- exit(exstat);
- }
-
- #ifdef SYSV
-
- /* mkdir - create directory */
-
- int mkdir(dir, mode)
- char *dir;
- int mode;
- {
- char cmd[BUFSIZ];
-
- (void) sprintf(cmd, "mkdir %s 2>&1 >/dev/null 2>&1 && chmod %o %s",
- dir, mode, dir);
- return (system(cmd)); /* does not set errno */
- }
-
- #endif
-