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