home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / vacation / vacation.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  8.7 KB  |  390 lines

  1. /*
  2.  * Copyright (c) 1983, 1987 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. char copyright[] =
  36. "@(#) Copyright (c) 1983, 1987 Regents of the University of California.\n\
  37.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)vacation.c    5.19 (Berkeley) 3/23/91";
  42. #endif /* not lint */
  43.  
  44. /*
  45. **  Vacation
  46. **  Copyright (c) 1983  Eric P. Allman
  47. **  Berkeley, California
  48. */
  49.  
  50. #include <sys/param.h>
  51. #include <sys/stat.h>
  52. #include <fcntl.h>
  53. #include <pwd.h>
  54. #include <db.h>
  55. #include <time.h>
  56. #include <syslog.h>
  57. #include <tzfile.h>
  58. #include <errno.h>
  59. #include <unistd.h>
  60. #include <stdio.h>
  61. #include <ctype.h>
  62. #include <stdlib.h>
  63. #include <string.h>
  64. #include <paths.h>
  65.  
  66. /*
  67.  *  VACATION -- return a message to the sender when on vacation.
  68.  *
  69.  *    This program is invoked as a message receiver.  It returns a
  70.  *    message specified by the user to whomever sent the mail, taking
  71.  *    care not to return a message too often to prevent "I am on
  72.  *    vacation" loops.
  73.  */
  74.  
  75. #define    MAXLINE    1024            /* max line from mail header */
  76. #define    VDB    ".vacation.db"        /* dbm's database */
  77. #define    VMSG    ".vacation.msg"        /* vacation message */
  78.  
  79. typedef struct alias {
  80.     struct alias *next;
  81.     char *name;
  82. } ALIAS;
  83. ALIAS *names;
  84.  
  85. DB *db;
  86.  
  87. char from[MAXLINE];
  88.  
  89. main(argc, argv)
  90.     int argc;
  91.     char **argv;
  92. {
  93.     extern int optind, opterr;
  94.     extern char *optarg;
  95.     struct passwd *pw;
  96.     ALIAS *cur;
  97.     time_t interval;
  98.     int ch, iflag;
  99.  
  100.     opterr = iflag = 0;
  101.     interval = -1;
  102.     while ((ch = getopt(argc, argv, "a:Iir:")) != EOF)
  103.         switch((char)ch) {
  104.         case 'a':            /* alias */
  105.             if (!(cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))))
  106.                 break;
  107.             cur->name = optarg;
  108.             cur->next = names;
  109.             names = cur;
  110.             break;
  111.         case 'I':            /* backward compatible */
  112.         case 'i':            /* init the database */
  113.             iflag = 1;
  114.             break;
  115.         case 'r':
  116.             if (isdigit(*optarg)) {
  117.                 interval = atol(optarg) * SECSPERDAY;
  118.                 if (interval < 0)
  119.                     usage();
  120.             }
  121.             else
  122.                 interval = LONG_MAX;
  123.             break;
  124.         case '?':
  125.         default:
  126.             usage();
  127.         }
  128.     argc -= optind;
  129.     argv += optind;
  130.  
  131.     if (argc != 1) {
  132.         if (!iflag)
  133.             usage();
  134.         if (!(pw = getpwuid(getuid()))) {
  135.             syslog(LOG_ERR,
  136.                 "vacation: no such user uid %u.\n", getuid());
  137.             exit(1);
  138.         }
  139.     }
  140.     else if (!(pw = getpwnam(*argv))) {
  141.         syslog(LOG_ERR, "vacation: no such user %s.\n", *argv);
  142.         exit(1);
  143.     }
  144.     if (chdir(pw->pw_dir)) {
  145.         syslog(LOG_NOTICE,
  146.             "vacation: no such directory %s.\n", pw->pw_dir);
  147.         exit(1);
  148.     }
  149.  
  150.     db = hash_open(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
  151.         S_IRUSR|S_IWUSR, (HASHINFO *)NULL);
  152.     if (!db) {
  153.         syslog(LOG_NOTICE, "vacation: %s: %s\n", VDB, strerror(errno));
  154.         exit(1);
  155.     }
  156.  
  157.     if (interval != -1)
  158.         setinterval(interval);
  159.  
  160.     if (iflag) {
  161.         (void)(db->close)(db);
  162.         exit(0);
  163.     }
  164.  
  165.     if (!(cur = malloc((u_int)sizeof(ALIAS))))
  166.         exit(1);
  167.     cur->name = pw->pw_name;
  168.     cur->next = names;
  169.     names = cur;
  170.  
  171.     readheaders();
  172.     if (!recent()) {
  173.         setreply();
  174.         (void)(db->close)(db);
  175.         sendmessage(pw->pw_name);
  176.     }
  177.     (void)(db->close)(db);
  178.     exit(0);
  179.     /* NOTREACHED */
  180. }
  181.  
  182. /*
  183.  * readheaders --
  184.  *    read mail headers
  185.  */
  186. readheaders()
  187. {
  188.     register ALIAS *cur;
  189.     register char *p;
  190.     int tome, cont;
  191.     char buf[MAXLINE];
  192.  
  193.     cont = tome = 0;
  194.     while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
  195.         switch(*buf) {
  196.         case 'F':        /* "From " */
  197.             cont = 0;
  198.             if (!strncmp(buf, "From ", 5)) {
  199.                 for (p = buf + 5; *p && *p != ' '; ++p);
  200.                 *p = '\0';
  201.                 (void)strcpy(from, buf + 5);
  202.                 if (p = index(from, '\n'))
  203.                     *p = '\0';
  204.                 if (junkmail())
  205.                     exit(0);
  206.             }
  207.             break;
  208.         case 'P':        /* "Precedence:" */
  209.             cont = 0;
  210.             if (strncasecmp(buf, "Precedence", 10) ||
  211.                 buf[10] != ':' && buf[10] != ' ' && buf[10] != '\t')
  212.                 break;
  213.             if (!(p = index(buf, ':')))
  214.                 break;
  215.             while (*++p && isspace(*p));
  216.             if (!*p)
  217.                 break;
  218.             if (!strncasecmp(p, "junk", 4) ||
  219.                 !strncasecmp(p, "bulk", 4))
  220.                 exit(0);
  221.             break;
  222.         case 'C':        /* "Cc:" */
  223.             if (strncmp(buf, "Cc:", 3))
  224.                 break;
  225.             cont = 1;
  226.             goto findme;
  227.         case 'T':        /* "To:" */
  228.             if (strncmp(buf, "To:", 3))
  229.                 break;
  230.             cont = 1;
  231.             goto findme;
  232.         default:
  233.             if (!isspace(*buf) || !cont || tome) {
  234.                 cont = 0;
  235.                 break;
  236.             }
  237. findme:            for (cur = names; !tome && cur; cur = cur->next)
  238.                 tome += nsearch(cur->name, buf);
  239.         }
  240.     if (!tome)
  241.         exit(0);
  242.     if (!*from) {
  243.         syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n");
  244.         exit(1);
  245.     }
  246. }
  247.  
  248. /*
  249.  * nsearch --
  250.  *    do a nice, slow, search of a string for a substring.
  251.  */
  252. nsearch(name, str)
  253.     register char *name, *str;
  254. {
  255.     register int len;
  256.  
  257.     for (len = strlen(name); *str; ++str)
  258.         if (*str == *name && !strncasecmp(name, str, len))
  259.             return(1);
  260.     return(0);
  261. }
  262.  
  263. /*
  264.  * junkmail --
  265.  *    read the header and return if automagic/junk/bulk mail
  266.  */
  267. junkmail()
  268. {
  269.     static struct ignore {
  270.         char    *name;
  271.         int    len;
  272.     } ignore[] = {
  273.         "-request", 8,        "postmaster", 10,    "uucp", 4,
  274.         "mailer-daemon", 13,    "mailer", 6,        "-relay", 6,
  275.         NULL, NULL,
  276.     };
  277.     register struct ignore *cur;
  278.     register int len;
  279.     register char *p;
  280.  
  281.     /*
  282.      * This is mildly amusing, and I'm not positive it's right; trying
  283.      * to find the "real" name of the sender, assuming that addresses
  284.      * will be some variant of:
  285.      *
  286.      * From site!site!SENDER%site.domain%site.domain@site.domain
  287.      */
  288.     if (!(p = index(from, '%')))
  289.         if (!(p = index(from, '@'))) {
  290.             if (p = rindex(from, '!'))
  291.                 ++p;
  292.             else
  293.                 p = from;
  294.             for (; *p; ++p);
  295.         }
  296.     len = p - from;
  297.     for (cur = ignore; cur->name; ++cur)
  298.         if (len >= cur->len &&
  299.             !strncasecmp(cur->name, p - cur->len, cur->len))
  300.             return(1);
  301.     return(0);
  302. }
  303.  
  304. #define    VIT    "__VACATION__INTERVAL__TIMER__"
  305.  
  306. /*
  307.  * recent --
  308.  *    find out if user has gotten a vacation message recently.
  309.  *    use bcopy for machines with alignment restrictions
  310.  */
  311. recent()
  312. {
  313.     DBT key, data;
  314.     time_t then, next;
  315.  
  316.     /* get interval time */
  317.     key.data = VIT;
  318.     key.size = sizeof(VIT);
  319.     if ((db->get)(db, &key, &data, 0))
  320.         next = SECSPERDAY * DAYSPERWEEK;
  321.     else
  322.         bcopy(data.data, &next, sizeof(next));
  323.  
  324.     /* get record for this address */
  325.     key.data = from;
  326.     key.size = strlen(from);
  327.     if (!(db->get)(db, &key, &data, 0)) {
  328.         bcopy(data.data, &then, sizeof(then));
  329.         if (next == LONG_MAX || then + next > time(NULL))
  330.             return(1);
  331.     }
  332.     return(0);
  333. }
  334.  
  335. /*
  336.  * setinterval --
  337.  *    store the reply interval
  338.  */
  339. setinterval(interval)
  340.     time_t interval;
  341. {
  342.     DBT key, data;
  343.  
  344.     key.data = VIT;
  345.     key.size = sizeof(VIT);
  346.     data.data = &interval;
  347.     data.size = sizeof(interval);
  348.     (void)(db->put)(db, &key, &data, R_PUT);
  349. }
  350.  
  351. /*
  352.  * setreply --
  353.  *    store that this user knows about the vacation.
  354.  */
  355. setreply()
  356. {
  357.     DBT key, data;
  358.     time_t now;
  359.  
  360.     key.data = from;
  361.     key.size = strlen(from);
  362.     (void)time(&now);
  363.     data.data = &now;
  364.     data.size = sizeof(now);
  365.     (void)(db->put)(db, &key, &data, R_PUT);
  366. }
  367.  
  368. /*
  369.  * sendmessage --
  370.  *    exec sendmail to send the vacation file to sender
  371.  */
  372. sendmessage(myname)
  373.     char *myname;
  374. {
  375.     if (!freopen(VMSG, "r", stdin)) {
  376.         syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG);
  377.         exit(1);
  378.     }
  379.     execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL);
  380.     syslog(LOG_ERR, "vacation: can't exec %s.\n", _PATH_SENDMAIL);
  381.     exit(1);
  382. }
  383.  
  384. usage()
  385. {
  386.     syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n",
  387.         getuid());
  388.     exit(1);
  389. }
  390.