No empty .Rs/.Re
[netbsd-mini2440.git] / usr.bin / vacation / vacation.c
blobb601ae680e1d13148a4b496223e4881d7adaa0e5
1 /* $NetBSD: vacation.c,v 1.35 2007/12/15 19:44:54 perry Exp $ */
3 /*
4 * Copyright (c) 1983, 1987, 1993
5 * The Regents of the University of California. All rights reserved.
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 #ifndef lint
35 __COPYRIGHT("@(#) Copyright (c) 1983, 1987, 1993\
36 The Regents of the University of California. All rights reserved.");
37 #endif /* not lint */
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94";
42 #endif
43 __RCSID("$NetBSD: vacation.c,v 1.35 2007/12/15 19:44:54 perry Exp $");
44 #endif /* not lint */
47 ** Vacation
48 ** Copyright (c) 1983 Eric P. Allman
49 ** Berkeley, California
52 #include <sys/param.h>
53 #include <sys/stat.h>
55 #include <ctype.h>
56 #include <db.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <paths.h>
61 #include <pwd.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <syslog.h>
66 #include <time.h>
67 #include <tzfile.h>
68 #include <unistd.h>
71 * VACATION -- return a message to the sender when on vacation.
73 * This program is invoked as a message receiver. It returns a
74 * message specified by the user to whomever sent the mail, taking
75 * care not to return a message too often to prevent "I am on
76 * vacation" loops.
79 #define MAXLINE 1024 /* max line from mail header */
81 static const char *dbprefix = ".vacation"; /* dbm's database sans .db */
82 static const char *msgfile = ".vacation.msg"; /* vacation message */
84 typedef struct alias {
85 struct alias *next;
86 const char *name;
87 } alias_t;
88 static alias_t *names;
90 static DB *db;
91 static char from[MAXLINE];
92 static char subject[MAXLINE];
94 static int iflag = 0; /* Initialize the database */
96 static int tflag = 0;
97 #define APPARENTLY_TO 1
98 #define DELIVERED_TO 2
100 static int fflag = 0;
101 #define FROM_FROM 1
102 #define RETURN_PATH_FROM 2
103 #define SENDER_FROM 4
105 static int toanybody = 0; /* Don't check if we appear in the to or cc */
107 static int debug = 0;
109 static void opendb(void);
110 static int junkmail(const char *);
111 static int nsearch(const char *, const char *);
112 static int readheaders(void);
113 static int recent(void);
114 static void getfrom(char *);
115 static void sendmessage(const char *);
116 static void setinterval(time_t);
117 static void setreply(void);
118 static void usage(void) __dead;
121 main(int argc, char **argv)
123 struct passwd *pw;
124 alias_t *cur;
125 long interval;
126 int ch, rv;
127 char *p;
129 setprogname(argv[0]);
130 opterr = 0;
131 interval = -1;
132 openlog(getprogname(), 0, LOG_USER);
133 while ((ch = getopt(argc, argv, "a:df:F:Iijm:r:s:t:T:")) != -1)
134 switch((char)ch) {
135 case 'a': /* alias */
136 if (!(cur = (alias_t *)malloc((size_t)sizeof(alias_t))))
137 break;
138 cur->name = optarg;
139 cur->next = names;
140 names = cur;
141 break;
142 case 'd':
143 debug++;
144 break;
145 case 'F':
146 for (p = optarg; *p; p++)
147 switch (*p) {
148 case 'F':
149 fflag |= FROM_FROM;
150 break;
151 case 'R':
152 fflag |= RETURN_PATH_FROM;
153 break;
154 case 'S':
155 fflag |= SENDER_FROM;
156 break;
157 default:
158 errx(1, "Unknown -f option `%c'", *p);
160 break;
161 case 'f':
162 dbprefix = optarg;
163 break;
164 case 'I': /* backward compatible */
165 case 'i': /* init the database */
166 iflag = 1;
167 break;
168 case 'j':
169 toanybody = 1;
170 break;
171 case 'm':
172 msgfile = optarg;
173 break;
174 case 'r':
175 case 't': /* Solaris compatibility */
176 if (!isdigit((unsigned char)*optarg)) {
177 interval = LONG_MAX;
178 break;
180 if (*optarg == '\0')
181 goto bad;
182 interval = strtol(optarg, &p, 0);
183 if (errno == ERANGE &&
184 (interval == LONG_MAX || interval == LONG_MIN))
185 err(1, "Bad interval `%s'", optarg);
186 switch (*p) {
187 case 's':
188 break;
189 case 'm':
190 interval *= SECSPERMIN;
191 break;
192 case 'h':
193 interval *= SECSPERHOUR;
194 break;
195 case 'd':
196 case '\0':
197 interval *= SECSPERDAY;
198 break;
199 case 'w':
200 interval *= DAYSPERWEEK * SECSPERDAY;
201 break;
202 default:
203 bad:
204 errx(1, "Invalid interval `%s'", optarg);
206 if (interval < 0 || (*p && p[1]))
207 goto bad;
208 break;
209 case 's':
210 (void)strlcpy(from, optarg, sizeof(from));
211 break;
212 case 'T':
213 for (p = optarg; *p; p++)
214 switch (*p) {
215 case 'A':
216 tflag |= APPARENTLY_TO;
217 break;
218 case 'D':
219 tflag |= DELIVERED_TO;
220 break;
221 default:
222 errx(1, "Unknown -t option `%c'", *p);
224 break;
225 case '?':
226 default:
227 usage();
229 argc -= optind;
230 argv += optind;
232 if (argc != 1) {
233 if (!iflag)
234 usage();
235 if (!(pw = getpwuid(getuid()))) {
236 syslog(LOG_ERR, "%s: no such user uid %u.",
237 getprogname(), getuid());
238 exit(1);
241 else if (!(pw = getpwnam(*argv))) {
242 syslog(LOG_ERR, "%s: no such user %s.",
243 getprogname(), *argv);
244 exit(1);
246 if (chdir(pw->pw_dir) == -1 &&
247 (dbprefix[0] != '/' || msgfile[0] != '/')) {
248 syslog(LOG_ERR, "%s: no such directory %s.",
249 getprogname(), pw->pw_dir);
250 exit(1);
253 opendb();
255 if (interval != -1)
256 setinterval((time_t)interval);
258 if (iflag) {
259 (void)(db->close)(db);
260 exit(0);
263 if (!(cur = malloc((size_t)sizeof(alias_t)))) {
264 syslog(LOG_ERR, "%s: %m", getprogname());
265 (void)(db->close)(db);
266 exit(1);
268 cur->name = pw->pw_name;
269 cur->next = names;
270 names = cur;
272 if ((rv = readheaders()) != -1) {
273 (void)(db->close)(db);
274 exit(rv);
277 if (!recent()) {
278 setreply();
279 (void)(db->close)(db);
280 sendmessage(pw->pw_name);
282 else
283 (void)(db->close)(db);
284 exit(0);
285 /* NOTREACHED */
288 static void
289 opendb(void)
291 char path[MAXPATHLEN];
293 (void)snprintf(path, sizeof(path), "%s.db", dbprefix);
294 db = dbopen(path, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
295 S_IRUSR|S_IWUSR, DB_HASH, NULL);
297 if (!db) {
298 syslog(LOG_ERR, "%s: %s: %m", getprogname(), path);
299 exit(1);
304 * readheaders --
305 * read mail headers
307 static int
308 readheaders(void)
310 alias_t *cur;
311 char *p;
312 int tome, cont;
313 char buf[MAXLINE];
315 cont = tome = 0;
316 #define COMPARE(a, b) strncmp(a, b, sizeof(b) - 1)
317 #define CASECOMPARE(a, b) strncasecmp(a, b, sizeof(b) - 1)
318 while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
319 switch(*buf) {
320 case 'F': /* "From " or "From:" */
321 cont = 0;
322 if (COMPARE(buf, "From ") == 0)
323 getfrom(buf + sizeof("From ") - 1);
324 if ((fflag & FROM_FROM) != 0 &&
325 COMPARE(buf, "From:") == 0)
326 getfrom(buf + sizeof("From:") - 1);
327 break;
328 case 'P': /* "Precedence:" */
329 cont = 0;
330 if (CASECOMPARE(buf, "Precedence") != 0 ||
331 (buf[10] != ':' && buf[10] != ' ' &&
332 buf[10] != '\t'))
333 break;
334 if ((p = strchr(buf, ':')) == NULL)
335 break;
336 while (*++p && isspace((unsigned char)*p))
337 continue;
338 if (!*p)
339 break;
340 if (CASECOMPARE(p, "junk") == 0 ||
341 CASECOMPARE(p, "bulk") == 0||
342 CASECOMPARE(p, "list") == 0)
343 exit(0);
344 break;
345 case 'C': /* "Cc:" */
346 if (COMPARE(buf, "Cc:"))
347 break;
348 cont = 1;
349 goto findme;
350 case 'T': /* "To:" */
351 if (COMPARE(buf, "To:"))
352 break;
353 cont = 1;
354 goto findme;
355 case 'A': /* "Apparently-To:" */
356 if ((tflag & APPARENTLY_TO) == 0 ||
357 COMPARE(buf, "Apparently-To:") != 0)
358 break;
359 cont = 1;
360 goto findme;
361 case 'D': /* "Delivered-To:" */
362 if ((tflag & DELIVERED_TO) == 0 ||
363 COMPARE(buf, "Delivered-To:") != 0)
364 break;
365 cont = 1;
366 goto findme;
367 case 'R': /* "Return-Path:" */
368 cont = 0;
369 if ((fflag & RETURN_PATH_FROM) != 0 &&
370 COMPARE(buf, "Return-Path:") == 0)
371 getfrom(buf + sizeof("Return-Path:") - 1);
372 break;
373 case 'S': /* "Sender:" */
374 cont = 0;
375 if (COMPARE(buf, "Subject:") == 0) {
376 /* trim leading blanks */
377 char *s = NULL;
378 for (p = buf + sizeof("Subject:") - 1; *p; p++)
379 if (s == NULL &&
380 !isspace((unsigned char)*p))
381 s = p;
382 /* trim trailing blanks */
383 if (s) {
384 for (--p; p != s; p--)
385 if (!isspace((unsigned char)*p))
386 break;
387 *++p = '\0';
389 if (s) {
390 (void)strlcpy(subject, s, sizeof(subject));
391 } else {
392 subject[0] = '\0';
395 if ((fflag & SENDER_FROM) != 0 &&
396 COMPARE(buf, "Sender:") == 0)
397 getfrom(buf + sizeof("Sender:") - 1);
398 break;
399 default:
400 if (!isspace((unsigned char)*buf) || !cont || tome) {
401 cont = 0;
402 break;
404 findme: for (cur = names; !tome && cur; cur = cur->next)
405 tome += nsearch(cur->name, buf);
407 if (!toanybody && !tome)
408 return 0;
409 if (!*from) {
410 syslog(LOG_ERR, "%s: no initial \"From\" line.",
411 getprogname());
412 return 1;
414 return -1;
418 * nsearch --
419 * do a nice, slow, search of a string for a substring.
421 static int
422 nsearch(const char *name, const char *str)
424 size_t len;
426 for (len = strlen(name); *str; ++str)
427 if (!strncasecmp(name, str, len))
428 return(1);
429 return(0);
433 * getfrom --
434 * return the first string in the buffer, stripping leading and trailing
435 * blanks and <>.
437 void
438 getfrom(char *buf)
440 char *s, *p;
442 if ((s = strchr(buf, '<')) != NULL)
443 s++;
444 else
445 s = buf;
447 for (; *s && isspace((unsigned char)*s); s++)
448 continue;
449 for (p = s; *p && !isspace((unsigned char)*p); p++)
450 continue;
452 if (*--p == '>')
453 *p = '\0';
454 else
455 *++p = '\0';
457 if (junkmail(s))
458 exit(0);
460 if (!*from)
461 (void)strlcpy(from, s, sizeof(from));
465 * junkmail --
466 * read the header and return if automagic/junk/bulk/list mail
468 static int
469 junkmail(const char *addr)
471 static struct ignore {
472 const char *name;
473 size_t len;
474 } ignore[] = {
475 #define INIT(a) { a, sizeof(a) - 1 }
476 INIT("-request"),
477 INIT("postmaster"),
478 INIT("uucp"),
479 INIT("mailer-daemon"),
480 INIT("mailer"),
481 INIT("-relay"),
482 {NULL, 0 }
484 struct ignore *cur;
485 size_t len;
486 const char *p;
489 * This is mildly amusing, and I'm not positive it's right; trying
490 * to find the "real" name of the sender, assuming that addresses
491 * will be some variant of:
493 * From site!site!SENDER%site.domain%site.domain@site.domain
495 if (!(p = strchr(addr, '%')))
496 if (!(p = strchr(addr, '@'))) {
497 if ((p = strrchr(addr, '!')) != NULL)
498 ++p;
499 else
500 p = addr;
501 for (; *p; ++p)
502 continue;
504 len = p - addr;
505 for (cur = ignore; cur->name; ++cur)
506 if (len >= cur->len &&
507 !strncasecmp(cur->name, p - cur->len, cur->len))
508 return(1);
509 return(0);
512 #define VIT "__VACATION__INTERVAL__TIMER__"
515 * recent --
516 * find out if user has gotten a vacation message recently.
517 * use memmove for machines with alignment restrictions
519 static int
520 recent(void)
522 DBT key, data;
523 time_t then, next;
525 /* get interval time */
526 key.data = (void *)(intptr_t)VIT;
527 key.size = sizeof(VIT);
528 if ((db->get)(db, &key, &data, 0))
529 next = SECSPERDAY * DAYSPERWEEK;
530 else
531 (void)memmove(&next, data.data, sizeof(next));
533 /* get record for this address */
534 key.data = from;
535 key.size = strlen(from);
536 if (!(db->get)(db, &key, &data, 0)) {
537 (void)memmove(&then, data.data, sizeof(then));
538 if (next == (time_t)LONG_MAX || /* XXX */
539 then + next > time(NULL))
540 return(1);
542 return(0);
546 * setinterval --
547 * store the reply interval
549 static void
550 setinterval(time_t interval)
552 DBT key, data;
554 key.data = (void *)(intptr_t)VIT;
555 key.size = sizeof(VIT);
556 data.data = &interval;
557 data.size = sizeof(interval);
558 (void)(db->put)(db, &key, &data, 0);
562 * setreply --
563 * store that this user knows about the vacation.
565 static void
566 setreply(void)
568 DBT key, data;
569 time_t now;
571 key.data = from;
572 key.size = strlen(from);
573 (void)time(&now);
574 data.data = &now;
575 data.size = sizeof(now);
576 (void)(db->put)(db, &key, &data, 0);
580 * sendmessage --
581 * exec sendmail to send the vacation file to sender
583 static void
584 sendmessage(const char *myname)
586 FILE *mfp, *sfp;
587 int i;
588 int pvect[2];
589 char buf[MAXLINE];
591 mfp = fopen(msgfile, "r");
592 if (mfp == NULL) {
593 syslog(LOG_ERR, "%s: no `%s' file for `%s'.", getprogname(),
594 myname, msgfile);
595 exit(1);
598 if (debug) {
599 sfp = stdout;
600 } else {
601 if (pipe(pvect) < 0) {
602 syslog(LOG_ERR, "%s: pipe: %m", getprogname());
603 exit(1);
605 i = vfork();
606 if (i < 0) {
607 syslog(LOG_ERR, "%s: fork: %m", getprogname());
608 exit(1);
610 if (i == 0) {
611 (void)dup2(pvect[0], 0);
612 (void)close(pvect[0]);
613 (void)close(pvect[1]);
614 (void)close(fileno(mfp));
615 (void)execl(_PATH_SENDMAIL, "sendmail", "-f", myname,
616 "--", from, NULL);
617 syslog(LOG_ERR, "%s: can't exec %s: %m",
618 getprogname(), _PATH_SENDMAIL);
619 _exit(1);
621 (void)close(pvect[0]);
622 sfp = fdopen(pvect[1], "w");
623 if (sfp == NULL) {
624 syslog(LOG_ERR, "%s: can't fdopen %d: %m",
625 getprogname(), pvect[1]);
626 _exit(1);
629 (void)fprintf(sfp, "To: %s\n", from);
630 (void)fputs("Auto-Submitted: auto-replied\n", sfp);
631 while (fgets(buf, sizeof buf, mfp) != NULL) {
632 char *p;
633 if ((p = strstr(buf, "$SUBJECT")) != NULL) {
634 *p = '\0';
635 (void)fputs(buf, sfp);
636 (void)fputs(subject, sfp);
637 p += sizeof("$SUBJECT") - 1;
638 (void)fputs(p, sfp);
639 } else
640 (void)fputs(buf, sfp);
642 (void)fclose(mfp);
643 if (sfp != stdout)
644 (void)fclose(sfp);
647 static void
648 usage(void)
651 syslog(LOG_ERR, "uid %u: Usage: %s [-dIij] [-a alias] [-f database_file] [-F F|R|S] [-m message_file] [-s sender] [-t interval] [-T A|D]"
652 " login", getuid(), getprogname());
653 exit(1);