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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 389 usage() 390 { 391 syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n", 392 getuid()); 393 exit(1); 394 } 395