home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / communic / pcmail / daemon / pc-mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  11.5 KB  |  429 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 /usr/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 and subject.
  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. /*    1/6/90 19:08:13
  51. /* VERSION/RELEASE
  52. /*    1.10
  53. /*--*/
  54.  
  55. #ifndef lint
  56. static char sccsid[] = "@(#) pc-mail.c 1.10 1/6/90 19:08:13";
  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. #include "ms_parse.h"
  87.  
  88. /* Stuff related to failed system calls */
  89.  
  90. extern int errno;
  91.  
  92. /* External functions */
  93.  
  94. extern struct passwd *getpwnam();
  95. extern long time();
  96. extern char *mktemp();
  97. extern void exit();
  98. extern unsigned sleep();
  99.  
  100. /* Local declarations */
  101.  
  102. #ifndef    MAILDIR
  103. #define    MAILDIR    "/usr/spool/pc-mail"    /* pc-mail directory tree */
  104. #endif
  105.  
  106. #define    LOCK    "pc-mail.lck"        /* the lock file */
  107. #define    STALE    1800            /* max age of lock file */
  108. #define    MAXTRY    60            /* max retry count for lock creation */
  109. #define    MSGFIL_FMT    "n%05d"        /* message file name format */
  110. #define    SNDFIL_FMT    "h%05d"        /* sender file name format */
  111. #define    MAXLINE    1024            /* max length of recipient line */
  112.  
  113. char    template[] = "pc.XXXXXX";    /* template lock file */
  114.  
  115. /* local functions */
  116.  
  117. void    sender();
  118. void    message();
  119. void    error();
  120.  
  121. char   *progname;
  122.  
  123. main(argc, argv)
  124. int     argc;
  125. char  **argv;
  126. {
  127.     struct passwd *pwd;
  128.     static char userdir[BUFSIZ];
  129.     int     seqno;
  130.  
  131.     progname = argv[0];
  132.  
  133.     /* Garbage in, garbage out */
  134.  
  135.     if (argc != 2)
  136.     error(EX_USAGE, "usage: %s user", *argv);
  137.  
  138. #ifndef DEBUG
  139.     if (geteuid() != 0)
  140.     error(EX_USAGE, "must run with root privileges");
  141.  
  142.     /* need this for SYSVR2 or mkdir(1) fails */
  143. #ifdef SYSV
  144.     if (setuid(0) != 0)
  145.     error(EX_OSERR, "cannot setuid(0)");
  146. #endif
  147. #endif
  148.  
  149.     if ((pwd = getpwnam(argv[1])) == 0)
  150.     error(EX_NOUSER, "unknown user: %s", argv[1]);
  151.  
  152.     /* Setup a somewhat safe environment */
  153.  
  154.     if (putenv("PATH=/bin:/usr/bin:/usr/ucb")
  155.     || putenv("IFS= \t\n"))
  156.     error(EX_TEMPFAIL, "putenv() failed");
  157.  
  158.     /* Check the necessary directories exist */
  159.  
  160.     (void) sprintf(userdir, "%s/%s", MAILDIR, argv[1]);
  161.     checkdir(userdir, pwd->pw_uid, pwd->pw_gid, 0700);
  162.  
  163.     /* Now with that out of the way, try to deliver the message */
  164.  
  165.     if (setgid(pwd->pw_gid))
  166.     error(EX_USAGE, "setgid(%s) failed: %m", argv[1]);
  167.     if (setuid(pwd->pw_uid))
  168.     error(EX_USAGE, "setuid(%s) failed: %m", argv[1]);
  169.  
  170.     /* make sure the user mail directory is accessible */
  171.  
  172.     if (chdir(userdir))
  173.     error(EX_TEMPFAIL, "can't access mail directory %s", userdir);
  174.  
  175.     /* deliver mail */
  176.  
  177.     seqno = newseqno(userdir);            /* Allocate sequence number */
  178.     message(pwd, seqno);            /* Create message file */
  179.     sender(seqno);                /* Create metafile (sender) */
  180.     exit(EX_OK);                /* Done. */
  181.     /* NOTREACHED */
  182. }
  183.  
  184. /* message - write message file */
  185.  
  186. void    message(pwd, seqno)
  187. struct passwd *pwd;
  188. int     seqno;
  189. {
  190.     static char buf[BUFSIZ];
  191.     register FILE *fp;
  192.  
  193.     /* Create the message file */
  194.  
  195.     (void) sprintf(buf, MSGFIL_FMT, seqno);
  196.     if ((fp = fopen(buf, "w")) == 0)
  197.     error(EX_CANTCREAT, "create error for file %s/%s: %m",
  198.           pwd->pw_name, buf);
  199.     if (unix2dos(stdin, fp)) {
  200.     (void) unlink(buf);
  201.     error(EX_CANTCREAT, "write error for file %s/%s: %m",
  202.           pwd->pw_name, buf);
  203.     }
  204.     (void) fclose(fp);
  205.     (void) chmod(buf, 0400);            /* Avoid tampering */
  206. }
  207.  
  208. /* sender - extract sender from message */
  209.  
  210. void    sender(seqno)
  211. int     seqno;
  212. {
  213.     register FILE *ifp;
  214.     register FILE *ofp;
  215.     static char fname[BUFSIZ];        /* file names */
  216.     static char line[MAXLINE];        /* read buffer */
  217.     static char from[MAXLINE] = "Unknown";    /* sender */
  218.     static char subject[MAXLINE] = "";    /* subject */
  219.     register int context = MS_UUCP;
  220.  
  221.     /*
  222.      * Try to open the message file; if that fails, let the pc software scan
  223.      * for the sender at a later time.
  224.      * 
  225.      * We recognize the following From line formats:
  226.      * 
  227.      * From name stuff            use name
  228.      * 
  229.      * >From name stuff            use name
  230.      * 
  231.      * From: address (full_name)    use full_name
  232.      * 
  233.      * From: full_name <address>    use full_name
  234.      * 
  235.      * From: full_name            use full_name
  236.      */
  237.  
  238.     (void) sprintf(fname, MSGFIL_FMT, seqno);
  239.     if ((ifp = fopen(fname, "r")) == 0)
  240.     return;
  241.  
  242.     /* Extract sender and subject from message */
  243.  
  244.     while (dosgets(line, sizeof(line), ifp) != 0
  245.     && (context = ms_parse(context, line)) != MS_BODY) {
  246.     switch (context) {
  247.     case MS_UUCP:
  248.         if (sscanf(line, "%*[>] From %s", from) != 1)
  249.         (void) sscanf(line, "From %s", from);
  250.         break;
  251.     case MS_HEADER:
  252.         if (hscanf(line, "Subject:", " %[^\n]", subject) == 0
  253.         && hscanf(line, "From:", " %*s ( %[^)] )", from) == 0)
  254.         (void) hscanf(line, "From:", " %[^<]", from);
  255.         break;
  256.     }
  257.     }
  258.     (void) fclose(ifp);
  259.  
  260.     /*
  261.      * Try to create the meta file; if that fails, let the pc software try
  262.      * again at a later time.
  263.      */
  264.  
  265.     (void) sprintf(fname, SNDFIL_FMT, seqno);
  266.     if (ofp = fopen(fname, "w")) {
  267.     (void) fprintf(ofp, "%s\r\n%s\r\n", from, subject);
  268.     if (fflush(ofp) || ferror(ofp) || feof(ofp) || fclose(ofp)) {
  269.         (void) unlink(fname);
  270.     } else {
  271.         (void) chmod(fname, 0400);        /* avoid tampering */
  272.     }
  273.     }
  274. }
  275.  
  276. /* newseqno - allocate new message sequence number */
  277.  
  278. int     newseqno(userdir)
  279. char   *userdir;
  280. {
  281.     register DIR *dd;
  282.     register struct direct *p;
  283.     struct stat st;
  284.     register int seqno = 0;
  285.     int     tmp = 0;
  286.     int     i;
  287.     char    junk;
  288.  
  289.     /*
  290.      * When the pc adds a file to the "mail data base", the file name is
  291.      * composed of a single letter and a unique sequence number. The pc
  292.      * chooses a new sequence number by adding one to the highest existing
  293.      * sequence number.
  294.      * 
  295.      * Now that the pc mounts its mail directory from the nfs server we must
  296.      * avoid possible concurrency conflicts when both pc and file server try
  297.      * to update the "mail data base".
  298.      * 
  299.      * Since the pc does not know about concurrent access from the nfs server,
  300.      * the server has to add 2 to the highest existing message sequence
  301.      * number, in order to avoid conflicts. Fortunately, only one pc at a
  302.      * time will be accessing a mail directory of a particular user.
  303.      * 
  304.      * Further concurrency conflicts are be avoided on the server side by using
  305.      * lock files.
  306.      * 
  307.      * If we cannot create a lock file right now, we back off and let sendmail
  308.      * try again later.
  309.      */
  310.  
  311.     /* Get rid of stale lock files */
  312.  
  313.     if (stat(LOCK, &st) == 0 && st.st_mtime < time((long *) 0) - STALE)
  314.     (void) unlink(LOCK);
  315.  
  316.     /* Wait until we can create the lock file */
  317.  
  318.     if (creat(mktemp(template), 0400) < 0)
  319.     error(EX_TEMPFAIL, "cannot set lock in directory %s: check ownership",
  320.           userdir);
  321.     for (i = 0; link(template, LOCK) && i < MAXTRY; i++)
  322.     (void) sleep(1);
  323.     (void) unlink(template);
  324.     if (i >= MAXTRY)
  325.     error(EX_TEMPFAIL, "locked: %s", userdir);
  326.  
  327.     /* Scan the user mail directory for the highest existing message number */
  328.  
  329.     if ((dd = opendir(userdir)) == 0) {
  330.     (void) unlink(LOCK);
  331.     error(EX_TEMPFAIL, "opendir(\"%s\") failed: %m", userdir);
  332.     }
  333.     while (p = readdir(dd)) {
  334.     if (strlen(p->d_name) == 6
  335.     && sscanf(p->d_name + 1, "%d%c", &tmp, &junk) == 1 && tmp > seqno)
  336.         seqno = tmp;
  337.     }
  338.  
  339.     /* clean up and terminate */
  340.  
  341.     closedir(dd);
  342.     (void) unlink(LOCK);
  343.     return (seqno + 2);
  344. }
  345.  
  346. /* checkdir - check/update presence/ownership/protection of directory */
  347.  
  348. checkdir(path, uid, gid, mode)
  349. char   *path;
  350. int     uid;
  351. int     gid;
  352. int     mode;
  353. {
  354.     struct stat st;
  355.  
  356.     /*
  357.      * If a user mail directory does not exist, try to create it. Otherwise,
  358.      * make sure it has sane permissions
  359.      */
  360.  
  361.     if (stat(path, &st) == -1) {        /* no directory */
  362.     if (mkdir(path, mode))            /* try to create it */
  363.         error(EX_TEMPFAIL, "cannot create directory %s: %m", path);
  364.     if (chown(path, uid, gid))        /* set owner, group */
  365.         error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  366.     } else {                    /* directory exists */
  367.     if ((st.st_mode & S_IFMT) != S_IFDIR)    /* must be directory! */
  368.         error(EX_TEMPFAIL, "%s should be a directory", path);
  369.     if ((st.st_uid != uid || st.st_gid != gid)    /* check owner/group */
  370.         &&chown(path, uid, gid))        /* correct owner, group */
  371.         error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  372.     if ((st.st_mode & 0777) != mode        /* check permissions */
  373.         && chmod(path, mode))        /* correct permissions */
  374.         error(EX_TEMPFAIL, "cannot chmod %o directory %s: %m", mode, path);
  375.     }
  376. }
  377.  
  378. /* error - print diagnostic and terminate */
  379.  
  380. /* VARARGS */
  381.  
  382. void    error(va_alist) va_dcl
  383. {
  384.     va_list ap;
  385.     register int exstat;
  386.     register char *fmt;
  387.     char    buf[BUFSIZ];
  388.     int     err = errno;
  389.  
  390.     /* Format the error message */
  391.  
  392.     va_start(ap);
  393.     exstat = va_arg(ap, int);            /* exit status */
  394.     fmt = va_arg(ap, char *);            /* format string */
  395.     (void) vsprintf(buf, percentm(fmt, err), ap);
  396.     va_end(ap);
  397.  
  398.     /* Write message to standard error stream */
  399.  
  400.     (void) fprintf(stderr, "%s: %s\n", progname, buf);
  401.  
  402.     /* Append the same message to system log */
  403.  
  404.     (void) openlog("pc-mail", LOG_PID, LOG_MAIL);
  405.     (void) syslog(LOG_WARNING, "%s", buf);
  406.     (void) closelog();
  407.  
  408.     /* Notify sendmail of the nature of the problem */
  409.  
  410.     exit(exstat);
  411. }
  412.  
  413. #ifdef SYSV
  414.  
  415. /* mkdir - create directory */
  416.  
  417. int     mkdir(dir, mode)
  418. char   *dir;
  419. int     mode;
  420. {
  421.     char    cmd[BUFSIZ];
  422.  
  423.     (void) sprintf(cmd, "mkdir %s 2>&1 >/dev/null 2>&1 && chmod %o %s",
  424.         dir, mode, dir);
  425.     return (system(cmd));            /* does not set errno */
  426. }
  427.  
  428. #endif
  429.