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

  1. /*++
  2. /* NAME
  3. /*    pc-maild 8
  4. /* SUMMARY
  5. /*    deliver unsent mail
  6. /* PROJECT
  7. /*    pc-mail
  8. /* PACKAGE
  9. /*    nfs
  10. /* SYNOPSIS
  11. /*    pc-maild [delay]
  12. /* DESCRIPTION
  13. /*    This program should be run on the nfs file server that exports
  14. /*    mail directories to MS-DOS pc-mail users. It replaces the
  15. /*    (MS-DOS -> UNIX) transmission function of the MS-DOS \fIcico\fR
  16. /*    program.
  17. /*
  18. /*    The per-user mail directories (default: /var/spool/pc-mail/\fIuser\fR)
  19. /*    are scanned for outgoing mail every \fIdelay\fR seconds (default: 300).
  20. /*    When outgoing mail is found, it is sent through the UNIX rmail program
  21. /*    (uucp mail interface) and the corresponding files are removed from the
  22. /*    user\'s mail directory.
  23. /*
  24. /*    The program should run with root privileges. It will assume
  25. /*    the (uid, gid) of the sending user before accessing mail files.
  26. /* COMMANDS
  27. /*    rmail(1), uucp mail interface program
  28. /* FILES
  29. /*    /usr/spool/pc-mail/\fIuser\fR/dNNNNN, mail message
  30. /*    /usr/spool/pc-mail/\fIuser\fR/xNNNNN, recipients
  31. /*    (NNNNN is the pc-mail "message id").
  32. /* SEE ALSO
  33. /*    pc-mail(1)
  34. /* DIAGNOSTICS
  35. /*    Errors found during initialization cause the program to
  36. /*    terminate with a diagnostic on the standard error stream.
  37. /*    All other errors are considered transient, i.e. if something
  38. /*    fails, it is tried again at a later time.  Where possible,
  39. /*    diagnostics are logged through the syslog facility.
  40. /* BUGS
  41. /*    Scanning mail directories is an inefficient way to detect
  42. /*    unsent mail.
  43. /* AUTHOR(S)
  44. /*    W.Z. Venema
  45. /*    Eindhoven University of Technology
  46. /*    Department of Mathematics and Computer Science
  47. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  48. /* CREATION DATE
  49. /*    Sun Oct 22 22:12:15 MED 1989
  50. /* LAST MODIFICATION
  51. /*    10/31/89 15:44:54
  52. /* VERSION/RELEASE
  53. /*    1.3
  54. /*--*/
  55.  
  56. #ifndef lint
  57. static char sccsid[] = "@(#) pc-maild.c 1.3 10/31/89 15:44:54";
  58.  
  59. #endif
  60.  
  61.  /*
  62.   * General return-value conventions:
  63.   * 
  64.   * int func():        0 means OK
  65.   * 
  66.   * stuff *func():    0 means error
  67.   * 
  68.   */
  69.  
  70. #include <stdio.h>
  71. #include <pwd.h>
  72. #include <time.h>
  73. #include <signal.h>
  74. #include <sys/types.h>
  75. #include <sys/stat.h>
  76.  
  77. #ifdef SYSLOG
  78. #include <syslog.h>
  79. #else
  80. #include "syslog.h"
  81. #endif
  82.  
  83. #ifdef SYSV
  84. #include <sys/utsname.h>
  85. #include <ndir.h>
  86. #else
  87. #include <sys/types.h>
  88. #include <sys/dir.h>
  89. #include <sgtty.h>
  90. #endif
  91.  
  92. #include "dosunix.h"
  93. #include "util.h"
  94. #include "mtime.h"
  95.  
  96. /* Library functions */
  97.  
  98. extern char *strtok();
  99. extern char *strncpy();
  100. extern struct passwd *getpwnam();
  101. extern unsigned sleep();
  102. extern void exit();
  103. extern void _exit();
  104. extern struct tm *localtime();
  105. extern char *asctime();
  106. extern long time();
  107.  
  108. /* Local defines, declarations */
  109.  
  110. #ifndef    DELAY
  111. #define    DELAY    300            /* default: scan every 5 minutes */
  112. #endif
  113.  
  114. #ifndef    MAILDIR
  115. #define    MAILDIR    "/usr/spool/pc-mail"    /* default pc-mail directory tree */
  116. #endif
  117.  
  118.  
  119. void    catchsig();
  120. void    finduser();
  121. char   *getrcpt();
  122. char   *fullname();
  123.  
  124. int     delay = DELAY;            /* the default delay */
  125. char   *progname;            /* my process name */
  126.  
  127.  
  128. int     main(argc, argv)
  129. int     argc;
  130. char  **argv;
  131. {
  132.     progname = *argv;
  133.  
  134.     /* Sanity checks */
  135.  
  136. #ifndef DEBUG
  137.  
  138.     if (geteuid() != 0) {
  139.     fprintf(stderr, "%s: must be run as root\n", progname);
  140.     exit(1);
  141.     }
  142. #endif
  143.  
  144.     /* Check for command-line delay argument */
  145.  
  146.     if (argc > 1 && (delay = atoi(argv[1])) < 1)
  147.     delay = DELAY;
  148.  
  149.     /* Become a daemon process */
  150.  
  151. #ifndef DEBUG
  152.     disconnect();
  153. #endif
  154.  
  155.     /* Set up signal handling */
  156.  
  157.     catchsig();
  158.  
  159.     /* Enable syslogging */
  160.  
  161.     (void) openlog(progname, LOG_PID, LOG_MAIL);
  162.     syslog(LOG_WARNING, "daemon restarted");
  163.  
  164.     /* Setup a decent environment */
  165.  
  166.     if (putenv("PATH=/bin:/usr/bin:/usr/ucb") || putenv("IFS= \t\n")) {
  167.     syslog(LOG_WARNING, "initialization failed (insufficient resources)");
  168.     exit(1);
  169.     }
  170.     /* Enter the main loop */
  171.  
  172.     finduser();
  173.     /* NOTREACHED */
  174. }
  175.  
  176. /* finduser - repeatedly iterate over all pc-mail user directories */
  177.  
  178. void    finduser()
  179. {
  180.     register DIR *maildp;
  181.     register struct direct *dp;
  182.     register struct passwd *uinfo;
  183.     MTIME  *dirtime;
  184.     char    userdir[BUFSIZ];
  185.     struct stat st;
  186.  
  187.     /*
  188.      * Ignore names that start with a period.
  189.      *
  190.      * Ignore names that do not correspond to a directory.
  191.      * 
  192.      * Ignore directories that did not change since the last time they were
  193.      * known to hold no unsent mail.
  194.      * 
  195.      * Ignore directories that do not have a name equal to the login name of a
  196.      * user.
  197.      */
  198.  
  199.     for (;;) {
  200.     if ((e_chdir(MAILDIR) == 0) && (maildp = e_opendir(MAILDIR))) {
  201.         while (dp = readdir(maildp)) {
  202.         if ((dp->d_name[0] != '.')
  203.             && (stat(dp->d_name, &st) == 0)
  204.             && ((st.st_mode & S_IFMT) == S_IFDIR)
  205.             && (st.st_mtime > (dirtime = mtime(dp->d_name))->time)
  206.             && ((uinfo = getpwnam(dp->d_name)) != 0)) {
  207.             (void) sprintf(userdir, "%s/%s", MAILDIR, dp->d_name);
  208.             if (findmail(uinfo, userdir) == 0)
  209.             dirtime->time = st.st_mtime;    /* say it was empty */
  210.         }
  211.         }
  212.         closedir(maildp);            /* done with this user */
  213.         (void) chdir("/");            /* be friendly */
  214.     }
  215.     (void) sleep(delay);            /* try again later */
  216.     }
  217. }
  218. /* findmail - enter a user\'s mail directory and scan for unsent mail */
  219.  
  220. int     findmail(uinfo, userdir)
  221. struct passwd *uinfo;
  222. char   *userdir;
  223. {
  224.     register DIR *dd;
  225.     register struct direct *p;
  226.     int     seqno;
  227.     static char xfile[BUFSIZ];        /* file with recipients */
  228.     static char dfile[BUFSIZ];        /* file with mail message */
  229.     int     found = 0;            /* no mail found yet */
  230.  
  231.     /*
  232.      * Use the fact that 'x' files (recipient addresses) are created later
  233.      * than 'd' files (message body) when a pc user generates a message.
  234.      * Extract the pc-mail message id from the file name and try to pipe the
  235.      * message through the UNIX rmail command. All knowledge about pc-mail
  236.      * file names resides in this function. Return zero if no unsent mail was
  237.      * found.
  238.      */
  239.  
  240.     if ((e_chdir(userdir) == 0) && (dd = e_opendir(userdir))) {
  241.     while (p = readdir(dd)) {
  242.         if (*p->d_name == 'x' && sscanf(p->d_name + 1, "%d", &seqno) == 1) {
  243.         (void) sprintf(xfile, "x%05d", seqno);    /* recipients */
  244.         if (strcmp(p->d_name, xfile) == 0) {    /* ignore junk */
  245.             (void) sprintf(dfile, "d%05d", seqno);
  246.             pickup(uinfo, xfile, dfile);
  247.             found = 1;            /* found unsent mail */
  248.         }
  249.         }
  250.     }
  251.     closedir(dd);
  252.     (void) chdir(MAILDIR);
  253.     }
  254.     return (found);
  255. }
  256.  
  257. /* pickup - pick up one message from a user\'s mail directory */
  258.  
  259. pickup(uinfo, xfile, dfile)
  260. struct passwd *uinfo;
  261. char   *xfile;
  262. char   *dfile;
  263.  
  264. {
  265.  
  266.     /*
  267.      * Actual delivery must be done with the (uid, gid) of the sender, or the
  268.      * From: lines will not be correct. Therefore, we do delivery in a child
  269.      * process. This also avoid all kinds of nasty security problems. All
  270.      * errors are considered non-fatal.
  271.      */
  272.  
  273. #ifdef DEBUG
  274.     sendasuser(uinfo, xfile, dfile);        /* don't fork */
  275. #else
  276.     switch (e_fork()) {
  277.     case -1:                    /* failure */
  278.     break;
  279.     case 0:                    /* child */
  280.     sendasuser(uinfo, xfile, dfile);
  281.     _exit(0);
  282.     /* NOTREACHED */
  283.     default:                    /* parent */
  284.     (void) wait((int *) 0);
  285.     break;
  286.     }
  287. #endif
  288. }
  289.  
  290. /* sendasuser - send mail through rmail(1), having (uid, gid) of sender */
  291.  
  292. sendasuser(uinfo, xfile, dfile)
  293. struct passwd *uinfo;
  294. char   *xfile;
  295. char   *dfile;
  296. {
  297.     char   *dest;            /* recipient address(es) */
  298.  
  299.     /*
  300.      * User-specific mail files must be opened AFTER we have assumed the
  301.      * (uid, gid) of the pc-mail user; this in order to avoid nasty security
  302.      * holes.
  303.      */
  304.  
  305.     if ((setugid(uinfo) == 0)            /* assume proper (uid, gid) */
  306.     &&(dest = getrcpt(uinfo, xfile))    /* extract recipients */
  307.     &&(rmail(uinfo, dfile, dest) == 0)) {    /* pipe message through rmail */
  308.     (void) u_unlink(uinfo, xfile);        /* recipients file */
  309.     (void) u_unlink(uinfo, dfile);        /* message body file */
  310.     }
  311. }
  312.  
  313. /* setugid - assume (uid, gid) of user */
  314.  
  315. int     setugid(uinfo)
  316. struct passwd *uinfo;
  317. {
  318.     if (setgid(uinfo->pw_gid)) {
  319.     syslog(LOG_WARNING, "setgid(%s) failed: %m", uinfo->pw_name);
  320.     return (1);
  321.     }
  322.     if (setuid(uinfo->pw_uid)) {
  323.     syslog(LOG_WARNING, "setuid(%s) failed: %m", uinfo->pw_name);
  324.     return (1);
  325.     } else {
  326.     return (0);
  327.     }
  328. }
  329.  
  330. /* getrcpt - extract recipient from user mail file */
  331.  
  332. char   *getrcpt(uinfo, xfile)
  333. struct passwd *uinfo;
  334. char   *xfile;
  335. {
  336.     FILE   *xfp;            /* recipient file pointer */
  337.     static char dest[2 * BUFSIZ];    /* recipient names */
  338.     register char *ret;
  339.  
  340.     if ((xfp = u_fopen(uinfo, xfile, "r")) == 0) {
  341.     return (0);
  342.     } else {
  343.     pc_wait(fileno(xfp));        /* let pc finish writing */
  344.     ret = dosgets(dest, sizeof(dest), xfp);
  345.     (void) fclose(xfp);
  346.     if (ret == 0)
  347.         syslog(LOG_WARNING, "no recipients specified in %s/%s",
  348.            uinfo->pw_name, xfile);
  349.     return (ret);
  350.     }
  351. }
  352.  
  353. /* rmail - pipe a pc mail message through the UNIX rmail program */
  354.  
  355. int     rmail(uinfo, dfile, dest)
  356. struct passwd *uinfo;            /* originator */
  357. char   *dfile;                /* message file */
  358. char   *dest;                /* recipients */
  359. {
  360.     static char cmd[BUFSIZ * 2];    /* command + arguments */
  361.     FILE   *dfp;            /* source stream */
  362.     FILE   *pfp;            /* output stream */
  363.     int     ret;            /* return value, 0 if OK */
  364.     long    secs;            /* absolute UNIX time */
  365.     static char hostname[BUFSIZ];    /* our host name */
  366.  
  367.     /*
  368.      * The UNIX rmail command needs a UUCP-style From_ line.
  369.      * 
  370.      * The To: and From: lines can be added for esthetical porposes.
  371.      * 
  372.      * Report communication failures with the rmail command. Error returns from
  373.      * rmail are ignored; they should be handled in sendmail.
  374.      */
  375.  
  376.     if (dfp = u_fopen(uinfo, dfile, "r")) {    /* open message file */
  377.     sprintf(cmd, "rmail %s", dest);
  378.     if ((pfp = popen(cmd, "w")) == 0) {    /* invoke rmail... */
  379.         syslog(LOG_WARNING, "cannot invoke %.20s...: %m", rmail);
  380.         ret = 1;
  381.     } else {
  382.         secs = time((long *) 0);
  383.         (void) gethostname(hostname, sizeof(hostname));
  384.         fprintf(pfp, "From %s %.24s remote from %s\n", uinfo->pw_name,
  385.             asctime(localtime(&secs)),
  386.             hostname);            /* add UUCP From_ line */
  387. #ifdef    RFC822
  388.         rfc822hdr(uinfo, dest, pfp);    /* do RFC822 stuff */
  389. #endif
  390.         if (ret = dos2unix(dfp, pfp))    /* append message body */
  391.         syslog(LOG_WARNING, "write to rmail failed: %m");
  392.         (void) pclose(pfp);
  393.     }
  394.     (void) fclose(dfp);
  395.     }
  396.     return (ret);
  397. }
  398.  
  399. /* rfc822hdr - generate subset of RFC822 header lines */
  400.  
  401. rfc822hdr(uinfo, dest, pfp)
  402. register struct passwd *uinfo;
  403. char   *dest;
  404. register FILE *pfp;
  405. {
  406.     char   *sep = " ,\t\r\n";
  407.     char   *name;
  408.  
  409.     /*
  410.      * There are a few problems with this function. First of all, it destroys
  411.      * the dest argument. In the second place, putting each recipient on a
  412.      * separate To: header line is ugly.
  413.      */
  414.  
  415.     fprintf(pfp, "From: %s (%s)\n", uinfo->pw_name,
  416.         fullname(uinfo));            /* add From: header line */
  417.     for (name = strtok(dest, sep); name; name = strtok((char *) 0, sep))
  418.     fprintf(pfp, "To: %s\n", name);        /* add To: header line */
  419. }
  420.  
  421. /* fullname - extract full name from gecos field */
  422.  
  423. char   *fullname(uinfo)
  424. struct passwd *uinfo;
  425. {
  426.     static char name[BUFSIZ];
  427.  
  428.     /* This code assumes BSD-style gecos fields (name,stuff,stuff...) */
  429.  
  430.     if (sscanf(uinfo->pw_gecos, "%[^,]", name) == 0)
  431.     name[0] = '\0';
  432.     return (name);
  433. }
  434.  
  435. /* gotsig - caught a signal; terminate with diagnostic */
  436.  
  437. void    gotsig(sig)
  438. int     sig;
  439. {
  440.     syslog(LOG_WARNING, "going down on signal %d", sig);
  441.     closelog();
  442.     exit(sig);
  443. }
  444.  
  445. /* catchsig - catch some signals */
  446.  
  447. void    catchsig()
  448. {
  449. #ifdef    DEBUG
  450.     (void) signal(SIGHUP, gotsig);
  451.     (void) signal(SIGINT, gotsig);
  452.     (void) signal(SIGQUIT, gotsig);
  453. #else
  454.     (void) signal(SIGHUP, SIG_IGN);
  455.     (void) signal(SIGINT, SIG_IGN);
  456.     (void) signal(SIGQUIT, SIG_IGN);
  457. #endif
  458.     (void) signal(SIGBUS, gotsig);
  459.     (void) signal(SIGSEGV, gotsig);
  460.     (void) signal(SIGTERM, gotsig);
  461. }
  462.  
  463. /* disconnect - become a daemon process */
  464.  
  465. disconnect()
  466. {
  467. #ifndef    SYSV
  468.     int     fd;
  469.  
  470. #endif
  471.  
  472.     /* Get rid of the parent process */
  473.  
  474.     switch (e_fork()) {
  475.     case -1:                    /* failure */
  476.     exit(1);
  477.     /* NOTREACHED */
  478.     default:
  479.     _exit(0);                /* parent */
  480.     /* NOTREACHED */
  481.     case 0:                    /* child */
  482.     break;
  483.     }
  484.  
  485.     /* Get rid of the controlling terminal */
  486.  
  487.     (void) close(0);
  488.     (void) close(1);
  489.     (void) close(2);
  490. #ifdef SYSV
  491.     (void) setpgrp();
  492. #else
  493.     if ((fd = open("/dev/tty", 0)) >= 0) {
  494.     (void) ioctl(fd, TIOCNOTTY, 0);
  495.     (void) close(fd);
  496.     }
  497. #endif
  498. }
  499.  
  500. /* pc_wait - wait till the pc has finished writing a file */
  501.  
  502. pc_wait(fd)
  503. int     fd;
  504. {
  505.     struct stat st;
  506.     long    oldsize = 0;
  507.  
  508.     /*
  509.      * Repeatedly sleep one second until the file size does not change
  510.      * anymore.
  511.      * 
  512.      * At first sight, this does not seem to be a very robust algorithm. It is,
  513.      * however, sufficient. The pc sofware will first create a message file,
  514.      * then the file with recipient addresses. The pc-maild program, on the
  515.      * other hand, will read the recipient-address file first. If that file
  516.      * turns out to be empty, it will try again at a later time. So, the only
  517.      * time we may produce an incorrect result is under the following
  518.      * conditions:
  519.      * 
  520.      * (1) the file with recipient names is longer than the PC/NFS packet size
  521.      * or the pc\'s stdio buffer size.
  522.      * 
  523.      * (2) the network connection goes down for > 1 second after part of the
  524.      * data has arrived in the file with recipient addresses.
  525.      */
  526.  
  527.     while (fstat(fd, &st) == 0 && oldsize != st.st_size) {
  528.     oldsize = st.st_size;
  529.     sleep(1);
  530.     }
  531. }
  532.  
  533. #ifdef SYSV
  534.  
  535. /* gethostname - BSD compatibility routine */
  536.  
  537. gethostname(name, len)
  538. char   *name;
  539. int     len;
  540. {
  541.     struct utsname ut;
  542.  
  543.     uname(&ut);
  544.     (void) strncpy(name, ut.nodename, len);
  545.     return (0);
  546. }
  547.  
  548. #endif
  549.