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