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