convert to db(3) interface, don't need dbm library
[unix-history] / usr / src / usr.bin / vacation / vacation.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1983, 1987 Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1983, 1987 Regents of the University of California.\n\
11 All rights reserved.\n";
12#endif /* not lint */
13
14#ifndef lint
15static char sccsid[] = "@(#)vacation.c 5.19 (Berkeley) %G%";
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
53typedef struct alias {
54 struct alias *next;
55 char *name;
56} ALIAS;
57ALIAS *names;
58
59DB *db;
60
61char from[MAXLINE];
62
63main(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 = hash_open(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
125 S_IRUSR|S_IWUSR, (HASHINFO *)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 (void)(db->close)(db);
152 exit(0);
153 /* NOTREACHED */
154}
155
156/*
157 * readheaders --
158 * read mail headers
159 */
160getfrom(shortp)
161char **shortp;
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 exit(0);
196 break;
197 case 'C': /* "Cc:" */
198 if (strncmp(buf, "Cc:", 3))
199 break;
200 cont = 1;
201 goto findme;
202 case 'T': /* "To:" */
203 if (strncmp(buf, "To:", 3))
204 break;
205 cont = 1;
206 goto findme;
207 default:
208 if (!isspace(*buf) || !cont || tome) {
209 cont = 0;
210 break;
211 }
212findme: for (cur = names; !tome && cur; cur = cur->next)
213 tome += nsearch(cur->name, buf);
214 }
215 if (!tome)
216 exit(0);
217 if (!*from) {
218 syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n");
219 exit(1);
220 }
221}
222
223 return start;
224}
225
226/*
227 * junkmail --
228 * read the header and return if automagic/junk/bulk mail
229 */
230junkmail()
231{
232 static struct ignore {
233 char *name;
234 int len;
235 } ignore[] = {
236 "-request", 8, "postmaster", 10, "uucp", 4,
237 "mailer-daemon", 13, "mailer", 6, "-relay", 6,
238 NULL, NULL,
239 };
240 register struct ignore *cur;
241 register int len;
242 register char *p;
243
244 /*
245 * This is mildly amusing, and I'm not positive it's right; trying
246 * to find the "real" name of the sender, assuming that addresses
247 * will be some variant of:
248 *
249 * From site!site!SENDER%site.domain%site.domain@site.domain
250 */
251 if (!(p = index(from, '%')))
252 if (!(p = index(from, '@'))) {
253 if (p = rindex(from, '!'))
254 ++p;
255 else
256 p = from;
257 for (; *p; ++p);
258 }
259 len = p - from;
260 for (cur = ignore; cur->name; ++cur)
261 if (len >= cur->len &&
262 !strncasecmp(cur->name, p - cur->len, cur->len))
263 return(1);
264 return(0);
265}
266
267#define VIT "__VACATION__INTERVAL__TIMER__"
268
269/*
270 * recent --
271 * find out if user has gotten a vacation message recently.
272 * use bcopy for machines with alignment restrictions
273 */
274char *user;
275{
276 DBT key, data;
277 time_t then, next;
278
279 if (d.dptr == NULL)
280 return FALSE;
281 bcopy(d.dptr, (char *)&ldbrec, sizeof ldbrec); /* realign data */
282 return ldbrec.sentdate + Timeout >= time((time_t *)0);
283}
284
285#ifndef VMUNIX
286bcopy(from, to, size)
287register char *to, *from;
288register unsigned size;
289{
290 while (size-- > 0)
291 *to++ = *from++;
292}
293#endif
294
295/*
296 * setinterval --
297 * store the reply interval
298 */
299setinterval(interval)
300 time_t interval;
301{
302 DBT key, data;
303
304 key.data = VIT;
305 key.size = sizeof(VIT);
306 data.data = &interval;
307 data.size = sizeof(interval);
308 (void)(db->put)(db, &key, &data, R_PUT);
309}
310
311/*
312 * setreply --
313 * store that this user knows about the vacation.
314 */
315setreply()
316{
317 DBT key, data;
318 time_t now;
319
320 key.data = from;
321 key.size = strlen(from);
322 (void)time(&now);
323 data.data = &now;
324 data.size = sizeof(now);
325 (void)(db->put)(db, &key, &data, R_PUT);
326}
327
328/*
329 * sendmessage --
330 * exec sendmail to send the vacation file to sender
331 */
332sendmessage(myname)
333 char *myname;
334{
335 if (!freopen(VMSG, "r", stdin)) {
336 syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG);
337 exit(1);
338 }
339 execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL);
340 syslog(LOG_ERR, "vacation: can't exec %s.\n", _PATH_SENDMAIL);
341 exit(1);
342}
343
344usage()
345{
346 syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n",
347 getuid());
348 exit(1);
349}