home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume9 / pc-mail-nfs / pc-mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-11-26  |  10.6 KB  |  417 lines

  1. /*++
  2. /* NAME
  3. /*    pc-mail 8
  4. /* SUMMARY
  5. /*    deliver mail to nfs-based pc-mail users
  6. /* PROJECT
  7. /*    pc-mail
  8. /* PACKAGE
  9. /*    nfs
  10. /* SYNOPSIS
  11. /*    pc-mail user
  12. /* DESCRIPTION
  13. /*    This program is to be run on the nfs server that exports mail
  14. /*    directories to MS-DOS pc-mail users. The program replaces the
  15. /*    UNIX -> MS-DOS file transfer function of the MS-DOS \fIcico\fR
  16. /*    program.
  17. /*
  18. /*    Normally, the pc-mail delivery program is invoked by sendmail(8).
  19. /*    Its purpose is to deliver new mail in the mail directory of the
  20. /*    specified \fIuser\fR (default /var/spool/pc-mail/\fIuser\fR).
  21. /*    Any error conditions detected by the pc-mail delivery program
  22. /*    are reported back in a sendmail-compatible manner.
  23. /*
  24. /*    This program must be run with root privileges. It will assume
  25. /*    the (uid, gid) of the specified user before delivering mail.
  26. /*
  27. /*    The program attempts to create any missing directories, and to
  28. /*    correct ownerships or protections where needed.
  29. /* FILES
  30. /*    /usr/spool/pc-mail/\fIuser\fR/nNNNNN, mail message.
  31. /*    /usr/spool/pc-mail/\fIuser\fR/hNNNNN, sender of message.
  32. /*    (NNNNN is the pc-mail "message id").
  33. /* SEE ALSO
  34. /*    pc-maild(1)
  35. /* DIAGNOSTICS
  36. /*    All conceivable error conditions cause the program to terminate
  37. /*    with a non-zero exit status, after printing an error message on
  38. /*    the standard error stream, and appending an entry to the system log.
  39. /*    See <sysexits.h> for details.
  40. /* BUGS
  41. /*    There is no way to notify a pc-mail user of the arrival of new mail.
  42. /* AUTHOR(S)
  43. /*    W.Z. Venema
  44. /*    Eindhoven University of Technology
  45. /*    Department of Mathematics and Computer Science
  46. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  47. /* CREATION DATE
  48. /*    Sun Oct 22 18:00:53 MED 1989
  49. /* LAST MODIFICATION
  50. /*    11/19/89 13:46:04
  51. /* VERSION/RELEASE
  52. /*    1.2
  53. /*--*/
  54.  
  55. #ifndef lint
  56. static char sccsid[] = "@(#) pc-mail.c 1.2 11/19/89 13:46:04";
  57.  
  58. #endif
  59.  
  60. #include <stdio.h>
  61. #include <sys/types.h>
  62. #include <sys/stat.h>
  63. #include <pwd.h>
  64. #include <varargs.h>
  65.  
  66. #ifdef SYSLOG
  67. #include <syslog.h>
  68. #else
  69. #include "syslog.h"
  70. #endif
  71.  
  72. #ifdef SYSV
  73. #include <ndir.h>
  74. #else
  75. #include <sys/dir.h>
  76. #endif
  77.  
  78. #ifdef    SYSEXITS
  79. #include <sysexits.h>
  80. #else
  81. #include "sysexits.h"
  82. #endif
  83.  
  84. #include "dosunix.h"
  85. #include "percentm.h"
  86.  
  87. /* Stuff related to failed system calls */
  88.  
  89. extern int errno;
  90.  
  91. /* External functions */
  92.  
  93. extern struct passwd *getpwnam();
  94. extern long time();
  95. extern char *mktemp();
  96. extern void exit();
  97. extern unsigned sleep();
  98.  
  99. /* Local declarations */
  100.  
  101. #ifndef    MAILDIR
  102. #define    MAILDIR    "/usr/spool/pc-mail"    /* pc-mail directory tree */
  103. #endif
  104.  
  105. #define    LOCK    "pc-mail.lck"        /* the lock file */
  106. #define    STALE    1800            /* max age of lock file */
  107. #define    MAXTRY    60            /* max retry count for lock creation */
  108. #define    MSGFIL_FMT    "n%05d"        /* message file name format */
  109. #define    SNDFIL_FMT    "h%05d"        /* sender file name format */
  110.  
  111. char    template[] = "pc.XXXXXX";    /* template lock file */
  112.  
  113. /* local functions */
  114.  
  115. void    sender();
  116. void    message();
  117. void    error();
  118.  
  119. char   *progname;
  120.  
  121. main(argc, argv)
  122. int     argc;
  123. char  **argv;
  124. {
  125.     struct passwd *pwd;
  126.     static char userdir[BUFSIZ];
  127.     int     seqno;
  128.  
  129.     progname = argv[0];
  130.  
  131.     /* Garbage in, garbage out */
  132.  
  133.     if (argc != 2)
  134.     error(EX_USAGE, "usage: %s user", *argv);
  135.  
  136. #ifndef DEBUG
  137.     if (geteuid() != 0)
  138.     error(EX_USAGE, "must run with root privileges");
  139.  
  140.     /* need this for SYSVR2 or mkdir(1) fails */
  141. #ifdef SYSV
  142.     if (setuid(0) != 0)
  143.     error(EX_OSERR, "cannot setuid(0)");
  144. #endif
  145. #endif
  146.  
  147.     if ((pwd = getpwnam(argv[1])) == 0)
  148.     error(EX_NOUSER, "unknown user: %s", argv[1]);
  149.  
  150.     /* Setup a somewhat safe environment */
  151.  
  152.     if (putenv("PATH=/bin:/usr/bin:/usr/ucb")
  153.     || putenv("IFS= \t\n"))
  154.     error(EX_TEMPFAIL, "putenv() failed");
  155.  
  156.     /* Check the necessary directories exist */
  157.  
  158.     (void) sprintf(userdir, "%s/%s", MAILDIR, argv[1]);
  159.     checkdir(userdir, pwd->pw_uid, pwd->pw_gid, 0700);
  160.  
  161.     /* Now with that out of the way, try to deliver the message */
  162.  
  163.     if (setgid(pwd->pw_gid))
  164.     error(EX_USAGE, "setgid(%s) failed: %m", argv[1]);
  165.     if (setuid(pwd->pw_uid))
  166.     error(EX_USAGE, "setuid(%s) failed: %m", argv[1]);
  167.  
  168.     /* make sure the user mail directory is accessible */
  169.  
  170.     if (chdir(userdir))
  171.     error(EX_TEMPFAIL, "can't access mail directory %s", userdir);
  172.  
  173.     /* deliver mail */
  174.  
  175.     seqno = newseqno(userdir);            /* Allocate sequence number */
  176.     message(pwd, seqno);            /* Create message file */
  177.     sender(seqno);                /* Create metafile (sender) */
  178.     exit(EX_OK);                /* Done. */
  179.     /* NOTREACHED */
  180. }
  181.  
  182. /* message - write message file */
  183.  
  184. void    message(pwd, seqno)
  185. struct passwd *pwd;
  186. int     seqno;
  187. {
  188.     static char buf[BUFSIZ];
  189.     register FILE *fp;
  190.  
  191.     /* Create the message file */
  192.  
  193.     (void) sprintf(buf, MSGFIL_FMT, seqno);
  194.     if ((fp = fopen(buf, "w")) == 0)
  195.     error(EX_CANTCREAT, "create error for file %s/%s: %m",
  196.           pwd->pw_name, buf);
  197.     if (unix2dos(stdin, fp)) {
  198.     (void) unlink(buf);
  199.     error(EX_CANTCREAT, "write error for file %s/%s: %m",
  200.           pwd->pw_name, buf);
  201.     }
  202.     (void) fclose(fp);
  203.     (void) chmod(buf, 0400);            /* Avoid tampering */
  204. }
  205.  
  206. /* sender - extract sender from message */
  207.  
  208. void    sender(seqno)
  209. int     seqno;
  210. {
  211.     register FILE *ifp;
  212.     register FILE *ofp;
  213.     static char fname[BUFSIZ];        /* file names */
  214.     static char line[BUFSIZ];        /* read buffer */
  215.     static char from[BUFSIZ] = "unknown";    /* sender */
  216.  
  217.     /*
  218.      * Try to open the message file; if that fails, let the pc software scan
  219.      * for the sender at a later time.
  220.      * 
  221.      * We recognize the following From line formats:
  222.      *
  223.      * From name
  224.      * 
  225.      * >From name
  226.      * 
  227.      * From: address (full name)
  228.      * 
  229.      * From: full name <address>
  230.      * 
  231.      * From: full name
  232.      */
  233.  
  234.     (void) sprintf(fname, MSGFIL_FMT, seqno);
  235.     if ((ifp = fopen(fname, "r")) == 0)
  236.     return;
  237.  
  238.     /* Extract sender from message */
  239.  
  240.     while (dosgets(line, sizeof(line), ifp) && *line) {
  241.     if (sscanf(line, "From: %*s ( %[^)] )", from) == 1)
  242.         break;
  243.     if (sscanf(line, "From: %[^<]", from) == 1)
  244.         break;
  245.     sscanf(line, "%*[>] From %s", from) || sscanf(line, "From %s", from);
  246.     }
  247.     (void) fclose(ifp);
  248.  
  249.     /*
  250.      * Try to create the meta file; if that fails, let the pc software try
  251.      * again at a later time.
  252.      */
  253.  
  254.     (void) sprintf(fname, SNDFIL_FMT, seqno);
  255.     if (ofp = fopen(fname, "w")) {
  256.     (void) fprintf(ofp, "%s\r\n", from);
  257.     if (fflush(ofp) || ferror(ofp) || feof(ofp) || fclose(ofp)) {
  258.         (void) unlink(fname);
  259.     } else {
  260.         (void) chmod(fname, 0400);        /* avoid tampering */
  261.     }
  262.     }
  263. }
  264.  
  265. /* newseqno - allocate new message sequence number */
  266.  
  267. int     newseqno(userdir)
  268. char   *userdir;
  269. {
  270.     register DIR *dd;
  271.     register struct direct *p;
  272.     struct stat st;
  273.     register int seqno = 0;
  274.     int     tmp = 0;
  275.     int     i;
  276.     char    junk;
  277.  
  278.     /*
  279.      * When the pc adds a file to the "mail data base", the file name is
  280.      * composed of a single letter and a unique sequence number. The pc
  281.      * chooses a new sequence number by adding one to the highest existing
  282.      * sequence number.
  283.      * 
  284.      * Now that the pc mounts its mail directory from the nfs server we must
  285.      * avoid possible concurrency conflicts when both pc and file server try
  286.      * to update the "mail data base".
  287.      * 
  288.      * Since the pc does not know about concurrent access from the nfs server,
  289.      * the server has to add 2 to the highest existing message sequence
  290.      * number, in order to avoid conflicts. Fortunately, only one pc at a
  291.      * time will be accessing a mail directory of a particular user.
  292.      * 
  293.      * Further concurrency conflicts are be avoided on the server side by using
  294.      * lock files.
  295.      * 
  296.      * If we cannot create a lock file right now, we back off and let sendmail
  297.      * try again later.
  298.      */
  299.  
  300.     /* Get rid of stale lock files */
  301.  
  302.     if (stat(LOCK, &st) == 0 && st.st_mtime < time((long *) 0) - STALE)
  303.     (void) unlink(LOCK);
  304.  
  305.     /* Wait until we can create the lock file */
  306.  
  307.     if (creat(mktemp(template), 0400) < 0)
  308.     error(EX_TEMPFAIL, "cannot set lock in directory %s: check ownership",
  309.           userdir);
  310.     for (i = 0; link(template, LOCK) && i < MAXTRY; i++)
  311.     (void) sleep(1);
  312.     (void) unlink(template);
  313.     if (i >= MAXTRY)
  314.     error(EX_TEMPFAIL, "locked: %s", userdir);
  315.  
  316.     /* Scan the user mail directory for the highest existing message number */
  317.  
  318.     if ((dd = opendir(userdir)) == 0) {
  319.     (void) unlink(LOCK);
  320.     error(EX_TEMPFAIL, "opendir(\"%s\") failed: %m", userdir);
  321.     }
  322.     while (p = readdir(dd)) {
  323.     if (sscanf(p->d_name + 1, "%d%c", &tmp, &junk) == 1 && tmp > seqno)
  324.         seqno = tmp;
  325.     }
  326.  
  327.     /* clean up and terminate */
  328.  
  329.     closedir(dd);
  330.     (void) unlink(LOCK);
  331.     return (seqno + 2);
  332. }
  333.  
  334. /* checkdir - check/update presence/ownership/protection of directory */
  335.  
  336. checkdir(path, uid, gid, mode)
  337. char   *path;
  338. int     uid;
  339. int     gid;
  340. int     mode;
  341. {
  342.     struct stat st;
  343.  
  344.     /*
  345.      * If a user mail directory does not exist, try to create it. Otherwise,
  346.      * make sure it has sane permissions
  347.      */
  348.  
  349.     if (stat(path, &st) == -1) {        /* no directory */
  350.     if (mkdir(path, mode))            /* try to create it */
  351.         error(EX_TEMPFAIL, "cannot create directory %s: %m", path);
  352.     if (chown(path, uid, gid))        /* set owner, group */
  353.         error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  354.     } else {                    /* directory exists */
  355.     if ((st.st_mode & S_IFMT) != S_IFDIR)    /* must be directory! */
  356.         error(EX_TEMPFAIL, "%s should be a directory", path);
  357.     if ((st.st_uid != uid || st.st_gid != gid)    /* check owner/group */
  358.         &&chown(path, uid, gid))        /* correct owner, group */
  359.         error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  360.     if ((st.st_mode & 0777) != mode        /* check permissions */
  361.         && chmod(path, mode))        /* correct permissions */
  362.         error(EX_TEMPFAIL, "cannot chmod %o directory %s: %m", mode, path);
  363.     }
  364. }
  365.  
  366. /* error - print diagnostic and terminate */
  367.  
  368. /* VARARGS */
  369.  
  370. void    error(va_alist) va_dcl
  371. {
  372.     va_list ap;
  373.     register int exstat;
  374.     register char *fmt;
  375.     char    buf[BUFSIZ];
  376.     int     err = errno;
  377.  
  378.     /* Format the error message */
  379.  
  380.     va_start(ap);
  381.     exstat = va_arg(ap, int);            /* exit status */
  382.     fmt = va_arg(ap, char *);            /* format string */
  383.     (void) vsprintf(buf, percentm(fmt, err), ap);
  384.     va_end(ap);
  385.  
  386.     /* Write message to standard error stream */
  387.  
  388.     (void) fprintf(stderr, "%s: %s\n", progname, buf);
  389.  
  390.     /* Append the same message to system log */
  391.  
  392.     (void) openlog("pc-mail", LOG_PID, LOG_MAIL);
  393.     (void) syslog(LOG_WARNING, "%s", buf);
  394.     (void) closelog();
  395.  
  396.     /* Notify sendmail of the nature of the problem */
  397.  
  398.     exit(exstat);
  399. }
  400.  
  401. #ifdef SYSV
  402.  
  403. /* mkdir - create directory */
  404.  
  405. int     mkdir(dir, mode)
  406. char   *dir;
  407. int     mode;
  408. {
  409.     char    cmd[BUFSIZ];
  410.  
  411.     sprintf(cmd, "mkdir %s 2>&1 >/dev/null 2>&1 && chmod %o %s",
  412.         dir, mode, dir);
  413.     return (system(cmd));            /* does not set errno */
  414. }
  415.  
  416. #endif
  417.