convert to db(3) interface, don't need dbm library
[unix-history] / usr / src / usr.bin / vacation / vacation.c
CommitLineData
de4d1f2e
KB
1/*
2 * Copyright (c) 1983, 1987 Regents of the University of California.
3 * All rights reserved.
4 *
32ce521f 5 * %sccs.include.redist.c%
de4d1f2e
KB
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
66ff479e 15static char sccsid[] = "@(#)vacation.c 5.19 (Berkeley) %G%";
de4d1f2e
KB
16#endif /* not lint */
17
d9e2d747
EA
18/*
19** Vacation
20** Copyright (c) 1983 Eric P. Allman
21** Berkeley, California
d9e2d747
EA
22*/
23
87338d3e 24#include <sys/param.h>
66ff479e
KB
25#include <sys/stat.h>
26#include <fcntl.h>
87338d3e 27#include <pwd.h>
66ff479e
KB
28#include <db.h>
29#include <time.h>
72d85c41
KB
30#include <syslog.h>
31#include <tzfile.h>
66ff479e
KB
32#include <errno.h>
33#include <unistd.h>
87338d3e
KB
34#include <stdio.h>
35#include <ctype.h>
66ff479e
KB
36#include <stdlib.h>
37#include <string.h>
d3fb15e8 38#include <paths.h>
d34d58b1 39
d34d58b1 40/*
72d85c41
KB
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 */
d34d58b1 48
66ff479e 49#define MAXLINE 1024 /* max line from mail header */
abb23bd6 50#define VDB ".vacation.db" /* dbm's database */
66ff479e 51#define VMSG ".vacation.msg" /* vacation message */
87338d3e 52
de4d1f2e
KB
53typedef struct alias {
54 struct alias *next;
72d85c41 55 char *name;
de4d1f2e 56} ALIAS;
72d85c41 57ALIAS *names;
351c396d 58
66ff479e 59DB *db;
72d85c41 60
66ff479e 61char from[MAXLINE];
351c396d 62
d34d58b1 63main(argc, argv)
de4d1f2e
KB
64 int argc;
65 char **argv;
d34d58b1 66{
72d85c41 67 extern int optind, opterr;
de4d1f2e
KB
68 extern char *optarg;
69 struct passwd *pw;
70 ALIAS *cur;
72d85c41 71 time_t interval;
de4d1f2e 72 int ch, iflag;
de4d1f2e 73
72d85c41
KB
74 opterr = iflag = 0;
75 interval = -1;
76 while ((ch = getopt(argc, argv, "a:Iir:")) != EOF)
87338d3e 77 switch((char)ch) {
de4d1f2e
KB
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;
ca42b8e8 85 case 'I': /* backward compatible */
72d85c41 86 case 'i': /* init the database */
de4d1f2e 87 iflag = 1;
ca42b8e8 88 break;
72d85c41
KB
89 case 'r':
90 if (isdigit(*optarg)) {
ae8fc90e 91 interval = atol(optarg) * SECSPERDAY;
72d85c41 92 if (interval < 0)
66ff479e 93 usage();
72d85c41
KB
94 }
95 else
96 interval = LONG_MAX;
97 break;
87338d3e
KB
98 case '?':
99 default:
66ff479e 100 usage();
d34d58b1 101 }
87338d3e 102 argc -= optind;
de4d1f2e 103 argv += optind;
d34d58b1 104
ca42b8e8 105 if (argc != 1) {
66ff479e
KB
106 if (!iflag)
107 usage();
de4d1f2e 108 if (!(pw = getpwuid(getuid()))) {
66ff479e
KB
109 syslog(LOG_ERR,
110 "vacation: no such user uid %u.\n", getuid());
111 exit(1);
ca42b8e8 112 }
87338d3e 113 }
de4d1f2e 114 else if (!(pw = getpwnam(*argv))) {
ca42b8e8 115 syslog(LOG_ERR, "vacation: no such user %s.\n", *argv);
66ff479e 116 exit(1);
87338d3e 117 }
de4d1f2e 118 if (chdir(pw->pw_dir)) {
abb23bd6
KB
119 syslog(LOG_NOTICE,
120 "vacation: no such directory %s.\n", pw->pw_dir);
66ff479e 121 exit(1);
de4d1f2e 122 }
1f5b97ad 123
66ff479e
KB
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);
ca42b8e8
KB
129 }
130
72d85c41
KB
131 if (interval != -1)
132 setinterval(interval);
72d85c41 133
66ff479e
KB
134 if (iflag) {
135 (void)(db->close)(db);
136 exit(0);
137 }
138
139 if (!(cur = malloc((u_int)sizeof(ALIAS))))
140 exit(1);
de4d1f2e
KB
141 cur->name = pw->pw_name;
142 cur->next = names;
143 names = cur;
ca42b8e8 144
de4d1f2e 145 readheaders();
de4d1f2e
KB
146 if (!recent()) {
147 setreply();
66ff479e 148 (void)(db->close)(db);
de4d1f2e 149 sendmessage(pw->pw_name);
d34d58b1 150 }
66ff479e
KB
151 (void)(db->close)(db);
152 exit(0);
72d85c41 153 /* NOTREACHED */
d34d58b1 154}
d34d58b1 155
87338d3e 156/*
de4d1f2e
KB
157 * readheaders --
158 * read mail headers
87338d3e 159 */
c974903d
EA
160getfrom(shortp)
161char **shortp;
d34d58b1 162{
de4d1f2e
KB
163 register ALIAS *cur;
164 register char *p;
165 int tome, cont;
66ff479e 166 char buf[MAXLINE];
de4d1f2e
KB
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);
72d85c41
KB
177 if (p = index(from, '\n'))
178 *p = '\0';
de4d1f2e 179 if (junkmail())
66ff479e 180 exit(0);
de4d1f2e
KB
181 }
182 break;
183 case 'P': /* "Precedence:" */
184 cont = 0;
66ff479e
KB
185 if (strncasecmp(buf, "Precedence", 10) ||
186 buf[10] != ':' && buf[10] != ' ' && buf[10] != '\t')
de4d1f2e
KB
187 break;
188 if (!(p = index(buf, ':')))
189 break;
190 while (*++p && isspace(*p));
191 if (!*p)
192 break;
66ff479e
KB
193 if (!strncasecmp(p, "junk", 4) ||
194 !strncasecmp(p, "bulk", 4))
195 exit(0);
de4d1f2e
KB
196 break;
197 case 'C': /* "Cc:" */
3f225bc3 198 if (strncmp(buf, "Cc:", 3))
de4d1f2e
KB
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)
66ff479e 216 exit(0);
de4d1f2e 217 if (!*from) {
72d85c41 218 syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n");
66ff479e 219 exit(1);
87338d3e 220 }
de4d1f2e 221}
c974903d 222
c974903d 223 return start;
d34d58b1 224}
351c396d 225
87338d3e
KB
226/*
227 * junkmail --
228 * read the header and return if automagic/junk/bulk mail
229 */
de4d1f2e 230junkmail()
351c396d 231{
87338d3e
KB
232 static struct ignore {
233 char *name;
234 int len;
235 } ignore[] = {
34a2d933
KB
236 "-request", 8, "postmaster", 10, "uucp", 4,
237 "mailer-daemon", 13, "mailer", 6, "-relay", 6,
238 NULL, NULL,
87338d3e 239 };
de4d1f2e
KB
240 register struct ignore *cur;
241 register int len;
242 register char *p;
87338d3e
KB
243
244 /*
de4d1f2e
KB
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:
87338d3e 248 *
de4d1f2e 249 * From site!site!SENDER%site.domain%site.domain@site.domain
87338d3e
KB
250 */
251 if (!(p = index(from, '%')))
ca42b8e8
KB
252 if (!(p = index(from, '@'))) {
253 if (p = rindex(from, '!'))
254 ++p;
de4d1f2e
KB
255 else
256 p = from;
257 for (; *p; ++p);
ca42b8e8
KB
258 }
259 len = p - from;
de4d1f2e 260 for (cur = ignore; cur->name; ++cur)
66ff479e
KB
261 if (len >= cur->len &&
262 !strncasecmp(cur->name, p - cur->len, cur->len))
de4d1f2e
KB
263 return(1);
264 return(0);
265}
351c396d 266
66ff479e
KB
267#define VIT "__VACATION__INTERVAL__TIMER__"
268
87338d3e 269/*
de4d1f2e 270 * recent --
87338d3e 271 * find out if user has gotten a vacation message recently.
72d85c41 272 * use bcopy for machines with alignment restrictions
87338d3e 273 */
c974903d 274char *user;
d34d58b1 275{
66ff479e
KB
276 DBT key, data;
277 time_t then, next;
d34d58b1 278
0fdb1a20 279 if (d.dptr == NULL)
c974903d
EA
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++;
d34d58b1 292}
c974903d 293#endif
d34d58b1 294
72d85c41
KB
295/*
296 * setinterval --
297 * store the reply interval
298 */
299setinterval(interval)
300 time_t interval;
301{
66ff479e 302 DBT key, data;
72d85c41 303
66ff479e
KB
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);
72d85c41
KB
309}
310
87338d3e 311/*
de4d1f2e 312 * setreply --
87338d3e
KB
313 * store that this user knows about the vacation.
314 */
de4d1f2e 315setreply()
d34d58b1 316{
66ff479e
KB
317 DBT key, data;
318 time_t now;
72d85c41 319
66ff479e
KB
320 key.data = from;
321 key.size = strlen(from);
72d85c41 322 (void)time(&now);
66ff479e
KB
323 data.data = &now;
324 data.size = sizeof(now);
325 (void)(db->put)(db, &key, &data, R_PUT);
d34d58b1 326}
d34d58b1 327
87338d3e
KB
328/*
329 * sendmessage --
de4d1f2e 330 * exec sendmail to send the vacation file to sender
87338d3e 331 */
de4d1f2e
KB
332sendmessage(myname)
333 char *myname;
d34d58b1 334{
ca42b8e8 335 if (!freopen(VMSG, "r", stdin)) {
72d85c41 336 syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG);
66ff479e 337 exit(1);
ca42b8e8 338 }
435e8dff
KB
339 execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL);
340 syslog(LOG_ERR, "vacation: can't exec %s.\n", _PATH_SENDMAIL);
66ff479e 341 exit(1);
d34d58b1 342}
1f5b97ad 343
66ff479e 344usage()
72d85c41 345{
66ff479e
KB
346 syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n",
347 getuid());
348 exit(1);
351c396d 349}