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