1 /* $NetBSD: vacation.c,v 1.35 2007/12/15 19:44:54 perry Exp $ */
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
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
32 #include <sys/cdefs.h>
35 __COPYRIGHT("@(#) Copyright (c) 1983, 1987, 1993\
36 The Regents of the University of California. All rights reserved.");
41 static char sccsid
[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94";
43 __RCSID("$NetBSD: vacation.c,v 1.35 2007/12/15 19:44:54 perry Exp $");
48 ** Copyright (c) 1983 Eric P. Allman
49 ** Berkeley, California
52 #include <sys/param.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
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
{
88 static alias_t
*names
;
91 static char from
[MAXLINE
];
92 static char subject
[MAXLINE
];
94 static int iflag
= 0; /* Initialize the database */
97 #define APPARENTLY_TO 1
98 #define DELIVERED_TO 2
100 static int fflag
= 0;
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
)
129 setprogname(argv
[0]);
132 openlog(getprogname(), 0, LOG_USER
);
133 while ((ch
= getopt(argc
, argv
, "a:df:F:Iijm:r:s:t:T:")) != -1)
135 case 'a': /* alias */
136 if (!(cur
= (alias_t
*)malloc((size_t)sizeof(alias_t
))))
146 for (p
= optarg
; *p
; p
++)
152 fflag
|= RETURN_PATH_FROM
;
155 fflag
|= SENDER_FROM
;
158 errx(1, "Unknown -f option `%c'", *p
);
164 case 'I': /* backward compatible */
165 case 'i': /* init the database */
175 case 't': /* Solaris compatibility */
176 if (!isdigit((unsigned char)*optarg
)) {
182 interval
= strtol(optarg
, &p
, 0);
183 if (errno
== ERANGE
&&
184 (interval
== LONG_MAX
|| interval
== LONG_MIN
))
185 err(1, "Bad interval `%s'", optarg
);
190 interval
*= SECSPERMIN
;
193 interval
*= SECSPERHOUR
;
197 interval
*= SECSPERDAY
;
200 interval
*= DAYSPERWEEK
* SECSPERDAY
;
204 errx(1, "Invalid interval `%s'", optarg
);
206 if (interval
< 0 || (*p
&& p
[1]))
210 (void)strlcpy(from
, optarg
, sizeof(from
));
213 for (p
= optarg
; *p
; p
++)
216 tflag
|= APPARENTLY_TO
;
219 tflag
|= DELIVERED_TO
;
222 errx(1, "Unknown -t option `%c'", *p
);
235 if (!(pw
= getpwuid(getuid()))) {
236 syslog(LOG_ERR
, "%s: no such user uid %u.",
237 getprogname(), getuid());
241 else if (!(pw
= getpwnam(*argv
))) {
242 syslog(LOG_ERR
, "%s: no such user %s.",
243 getprogname(), *argv
);
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
);
256 setinterval((time_t)interval
);
259 (void)(db
->close
)(db
);
263 if (!(cur
= malloc((size_t)sizeof(alias_t
)))) {
264 syslog(LOG_ERR
, "%s: %m", getprogname());
265 (void)(db
->close
)(db
);
268 cur
->name
= pw
->pw_name
;
272 if ((rv
= readheaders()) != -1) {
273 (void)(db
->close
)(db
);
279 (void)(db
->close
)(db
);
280 sendmessage(pw
->pw_name
);
283 (void)(db
->close
)(db
);
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
);
298 syslog(LOG_ERR
, "%s: %s: %m", getprogname(), path
);
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')
320 case 'F': /* "From " or "From:" */
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);
328 case 'P': /* "Precedence:" */
330 if (CASECOMPARE(buf
, "Precedence") != 0 ||
331 (buf
[10] != ':' && buf
[10] != ' ' &&
334 if ((p
= strchr(buf
, ':')) == NULL
)
336 while (*++p
&& isspace((unsigned char)*p
))
340 if (CASECOMPARE(p
, "junk") == 0 ||
341 CASECOMPARE(p
, "bulk") == 0||
342 CASECOMPARE(p
, "list") == 0)
345 case 'C': /* "Cc:" */
346 if (COMPARE(buf
, "Cc:"))
350 case 'T': /* "To:" */
351 if (COMPARE(buf
, "To:"))
355 case 'A': /* "Apparently-To:" */
356 if ((tflag
& APPARENTLY_TO
) == 0 ||
357 COMPARE(buf
, "Apparently-To:") != 0)
361 case 'D': /* "Delivered-To:" */
362 if ((tflag
& DELIVERED_TO
) == 0 ||
363 COMPARE(buf
, "Delivered-To:") != 0)
367 case 'R': /* "Return-Path:" */
369 if ((fflag
& RETURN_PATH_FROM
) != 0 &&
370 COMPARE(buf
, "Return-Path:") == 0)
371 getfrom(buf
+ sizeof("Return-Path:") - 1);
373 case 'S': /* "Sender:" */
375 if (COMPARE(buf
, "Subject:") == 0) {
376 /* trim leading blanks */
378 for (p
= buf
+ sizeof("Subject:") - 1; *p
; p
++)
380 !isspace((unsigned char)*p
))
382 /* trim trailing blanks */
384 for (--p
; p
!= s
; p
--)
385 if (!isspace((unsigned char)*p
))
390 (void)strlcpy(subject
, s
, sizeof(subject
));
395 if ((fflag
& SENDER_FROM
) != 0 &&
396 COMPARE(buf
, "Sender:") == 0)
397 getfrom(buf
+ sizeof("Sender:") - 1);
400 if (!isspace((unsigned char)*buf
) || !cont
|| tome
) {
404 findme
: for (cur
= names
; !tome
&& cur
; cur
= cur
->next
)
405 tome
+= nsearch(cur
->name
, buf
);
407 if (!toanybody
&& !tome
)
410 syslog(LOG_ERR
, "%s: no initial \"From\" line.",
419 * do a nice, slow, search of a string for a substring.
422 nsearch(const char *name
, const char *str
)
426 for (len
= strlen(name
); *str
; ++str
)
427 if (!strncasecmp(name
, str
, len
))
434 * return the first string in the buffer, stripping leading and trailing
442 if ((s
= strchr(buf
, '<')) != NULL
)
447 for (; *s
&& isspace((unsigned char)*s
); s
++)
449 for (p
= s
; *p
&& !isspace((unsigned char)*p
); p
++)
461 (void)strlcpy(from
, s
, sizeof(from
));
466 * read the header and return if automagic/junk/bulk/list mail
469 junkmail(const char *addr
)
471 static struct ignore
{
475 #define INIT(a) { a, sizeof(a) - 1 }
479 INIT("mailer-daemon"),
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
)
505 for (cur
= ignore
; cur
->name
; ++cur
)
506 if (len
>= cur
->len
&&
507 !strncasecmp(cur
->name
, p
- cur
->len
, cur
->len
))
512 #define VIT "__VACATION__INTERVAL__TIMER__"
516 * find out if user has gotten a vacation message recently.
517 * use memmove for machines with alignment restrictions
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
;
531 (void)memmove(&next
, data
.data
, sizeof(next
));
533 /* get record for this address */
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
))
547 * store the reply interval
550 setinterval(time_t interval
)
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);
563 * store that this user knows about the vacation.
572 key
.size
= strlen(from
);
575 data
.size
= sizeof(now
);
576 (void)(db
->put
)(db
, &key
, &data
, 0);
581 * exec sendmail to send the vacation file to sender
584 sendmessage(const char *myname
)
591 mfp
= fopen(msgfile
, "r");
593 syslog(LOG_ERR
, "%s: no `%s' file for `%s'.", getprogname(),
601 if (pipe(pvect
) < 0) {
602 syslog(LOG_ERR
, "%s: pipe: %m", getprogname());
607 syslog(LOG_ERR
, "%s: fork: %m", getprogname());
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
,
617 syslog(LOG_ERR
, "%s: can't exec %s: %m",
618 getprogname(), _PATH_SENDMAIL
);
621 (void)close(pvect
[0]);
622 sfp
= fdopen(pvect
[1], "w");
624 syslog(LOG_ERR
, "%s: can't fdopen %d: %m",
625 getprogname(), pvect
[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
) {
633 if ((p
= strstr(buf
, "$SUBJECT")) != NULL
) {
635 (void)fputs(buf
, sfp
);
636 (void)fputs(subject
, sfp
);
637 p
+= sizeof("$SUBJECT") - 1;
640 (void)fputs(buf
, sfp
);
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());