Annotation of src/usr.bin/vacation/vacation.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: vacation.c,v 1.7 1995/04/29 05:58:27 cgd 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.7 1995/04/29 05:58:27 cgd 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: char from[MAXLINE];
92:
93: int junkmail __P((void));
94: int nsearch __P((char *, char *));
95: void readheaders __P((void));
96: int recent __P((void));
97: void sendmessage __P((char *));
98: void setinterval __P((time_t));
99: void setreply __P((void));
100: void usage __P((void));
101:
102: int
103: main(argc, argv)
104: int argc;
105: char **argv;
106: {
107: extern int optind, opterr;
108: extern char *optarg;
109: struct passwd *pw;
110: ALIAS *cur;
111: time_t interval;
112: int ch, iflag;
113:
114: opterr = iflag = 0;
115: interval = -1;
116: while ((ch = getopt(argc, argv, "a:Iir:")) != EOF)
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,
150: "vacation: no such user uid %u.\n", getuid());
151: exit(1);
152: }
153: }
154: else if (!(pw = getpwnam(*argv))) {
155: syslog(LOG_ERR, "vacation: no such user %s.\n", *argv);
156: exit(1);
157: }
158: if (chdir(pw->pw_dir)) {
159: syslog(LOG_NOTICE,
160: "vacation: no such directory %s.\n", pw->pw_dir);
161: exit(1);
162: }
163:
164: db = dbopen(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
165: S_IRUSR|S_IWUSR, DB_HASH, NULL);
166: if (!db) {
167: syslog(LOG_NOTICE, "vacation: %s: %s\n", VDB, strerror(errno));
168: exit(1);
169: }
170:
171: if (interval != -1)
172: setinterval(interval);
173:
174: if (iflag) {
175: (void)(db->close)(db);
176: exit(0);
177: }
178:
179: if (!(cur = malloc((u_int)sizeof(ALIAS))))
180: exit(1);
181: cur->name = pw->pw_name;
182: cur->next = names;
183: names = cur;
184:
185: readheaders();
186: if (!recent()) {
187: setreply();
188: (void)(db->close)(db);
189: sendmessage(pw->pw_name);
190: }
191: else
192: (void)(db->close)(db);
193: exit(0);
194: /* NOTREACHED */
195: }
196:
197: /*
198: * readheaders --
199: * read mail headers
200: */
201: void
202: readheaders()
203: {
204: register ALIAS *cur;
205: register char *p;
206: int tome, cont;
207: char buf[MAXLINE];
208:
209: cont = tome = 0;
210: while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
211: switch(*buf) {
212: case 'F': /* "From " */
213: cont = 0;
214: if (!strncmp(buf, "From ", 5)) {
215: for (p = buf + 5; *p && *p != ' '; ++p);
216: *p = '\0';
217: (void)strcpy(from, buf + 5);
218: if (p = index(from, '\n'))
219: *p = '\0';
220: if (junkmail())
221: exit(0);
222: }
223: break;
224: case 'P': /* "Precedence:" */
225: cont = 0;
226: if (strncasecmp(buf, "Precedence", 10) ||
227: buf[10] != ':' && buf[10] != ' ' && buf[10] != '\t')
228: break;
229: if (!(p = index(buf, ':')))
230: break;
231: while (*++p && isspace(*p));
232: if (!*p)
233: break;
234: if (!strncasecmp(p, "junk", 4) ||
235: !strncasecmp(p, "bulk", 4) ||
236: !strncasecmp(p, "list", 4))
237: exit(0);
238: break;
239: case 'C': /* "Cc:" */
240: if (strncmp(buf, "Cc:", 3))
241: break;
242: cont = 1;
243: goto findme;
244: case 'T': /* "To:" */
245: if (strncmp(buf, "To:", 3))
246: break;
247: cont = 1;
248: goto findme;
249: default:
250: if (!isspace(*buf) || !cont || tome) {
251: cont = 0;
252: break;
253: }
254: findme: for (cur = names; !tome && cur; cur = cur->next)
255: tome += nsearch(cur->name, buf);
256: }
257: if (!tome)
258: exit(0);
259: if (!*from) {
260: syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n");
261: exit(1);
262: }
263: }
264:
265: /*
266: * nsearch --
267: * do a nice, slow, search of a string for a substring.
268: */
269: int
270: nsearch(name, str)
271: register char *name, *str;
272: {
273: register int len;
274:
275: for (len = strlen(name); *str; ++str)
276: if (*str == *name && !strncasecmp(name, str, len))
277: return(1);
278: return(0);
279: }
280:
281: /*
282: * junkmail --
283: * read the header and return if automagic/junk/bulk/list mail
284: */
285: int
286: junkmail()
287: {
288: static struct ignore {
289: char *name;
290: int len;
291: } ignore[] = {
292: "-request", 8, "postmaster", 10, "uucp", 4,
293: "mailer-daemon", 13, "mailer", 6, "-relay", 6,
294: NULL, NULL,
295: };
296: register struct ignore *cur;
297: register int len;
298: register char *p;
299:
300: /*
301: * This is mildly amusing, and I'm not positive it's right; trying
302: * to find the "real" name of the sender, assuming that addresses
303: * will be some variant of:
304: *
305: * From site!site!SENDER%site.domain%site.domain@site.domain
306: */
307: if (!(p = index(from, '%')))
308: if (!(p = index(from, '@'))) {
309: if (p = rindex(from, '!'))
310: ++p;
311: else
312: p = from;
313: for (; *p; ++p);
314: }
315: len = p - from;
316: for (cur = ignore; cur->name; ++cur)
317: if (len >= cur->len &&
318: !strncasecmp(cur->name, p - cur->len, cur->len))
319: return(1);
320: return(0);
321: }
322:
323: #define VIT "__VACATION__INTERVAL__TIMER__"
324:
325: /*
326: * recent --
327: * find out if user has gotten a vacation message recently.
328: * use bcopy for machines with alignment restrictions
329: */
330: int
331: recent()
332: {
333: DBT key, data;
334: time_t then, next;
335:
336: /* get interval time */
337: key.data = VIT;
338: key.size = sizeof(VIT);
339: if ((db->get)(db, &key, &data, 0))
340: next = SECSPERDAY * DAYSPERWEEK;
341: else
342: bcopy(data.data, &next, sizeof(next));
343:
344: /* get record for this address */
345: key.data = from;
346: key.size = strlen(from);
347: if (!(db->get)(db, &key, &data, 0)) {
348: bcopy(data.data, &then, sizeof(then));
349: if (next == (time_t)LONG_MAX || /* XXX */
350: then + next > time(NULL))
351: return(1);
352: }
353: return(0);
354: }
355:
356: /*
357: * setinterval --
358: * store the reply interval
359: */
360: void
361: setinterval(interval)
362: time_t interval;
363: {
364: DBT key, data;
365:
366: key.data = VIT;
367: key.size = sizeof(VIT);
368: data.data = &interval;
369: data.size = sizeof(interval);
370: (void)(db->put)(db, &key, &data, 0);
371: }
372:
373: /*
374: * setreply --
375: * store that this user knows about the vacation.
376: */
377: void
378: setreply()
379: {
380: DBT key, data;
381: time_t now;
382:
383: key.data = from;
384: key.size = strlen(from);
385: (void)time(&now);
386: data.data = &now;
387: data.size = sizeof(now);
388: (void)(db->put)(db, &key, &data, 0);
389: }
390:
391: /*
392: * sendmessage --
393: * exec sendmail to send the vacation file to sender
394: */
395: void
396: sendmessage(myname)
397: char *myname;
398: {
399: FILE *mfp, *sfp;
400: int i;
401: int pvect[2];
402: char buf[MAXLINE];
403:
404: mfp = fopen(VMSG, "r");
405: if (mfp == NULL) {
406: syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG);
407: exit(1);
408: }
409: if (pipe(pvect) < 0) {
410: syslog(LOG_ERR, "vacation: pipe: %s", strerror(errno));
411: exit(1);
412: }
413: i = vfork();
414: if (i < 0) {
415: syslog(LOG_ERR, "vacation: fork: %s", strerror(errno));
416: exit(1);
417: }
418: if (i == 0) {
419: dup2(pvect[0], 0);
420: close(pvect[0]);
421: close(pvect[1]);
422: fclose(mfp);
423: execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL);
424: syslog(LOG_ERR, "vacation: can't exec %s: %s",
425: _PATH_SENDMAIL, strerror(errno));
426: exit(1);
427: }
428: close(pvect[0]);
429: sfp = fdopen(pvect[1], "w");
430: fprintf(sfp, "To: %s\n", from);
431: while (fgets(buf, sizeof buf, mfp))
432: fputs(buf, sfp);
433: fclose(mfp);
434: fclose(sfp);
435: }
436:
437: void
438: usage()
439: {
440: syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n",
441: getuid());
442: exit(1);
443: }