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.1 (Berkeley) 06/06/93"; 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 (void)(db->close)(db); 152 exit(0); 153 /* NOTREACHED */ 154 } 155 156 /* 157 * readheaders -- 158 * read mail headers 159 */ 160 readheaders() 161 { 162 register ALIAS *cur; 163 register char *p; 164 int tome, cont; 165 char buf[MAXLINE]; 166 167 cont = tome = 0; 168 while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') 169 switch(*buf) { 170 case 'F': /* "From " */ 171 cont = 0; 172 if (!strncmp(buf, "From ", 5)) { 173 for (p = buf + 5; *p && *p != ' '; ++p); 174 *p = '\0'; 175 (void)strcpy(from, buf + 5); 176 if (p = index(from, '\n')) 177 *p = '\0'; 178 if (junkmail()) 179 exit(0); 180 } 181 break; 182 case 'P': /* "Precedence:" */ 183 cont = 0; 184 if (strncasecmp(buf, "Precedence", 10) || 185 buf[10] != ':' && buf[10] != ' ' && buf[10] != '\t') 186 break; 187 if (!(p = index(buf, ':'))) 188 break; 189 while (*++p && isspace(*p)); 190 if (!*p) 191 break; 192 if (!strncasecmp(p, "junk", 4) || 193 !strncasecmp(p, "bulk", 4) || 194 !strncasecmp(p, "list", 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 } 212 findme: 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 /* 224 * nsearch -- 225 * do a nice, slow, search of a string for a substring. 226 */ 227 nsearch(name, str) 228 register char *name, *str; 229 { 230 register int len; 231 232 for (len = strlen(name); *str; ++str) 233 if (*str == *name && !strncasecmp(name, str, len)) 234 return(1); 235 return(0); 236 } 237 238 /* 239 * junkmail -- 240 * read the header and return if automagic/junk/bulk/list mail 241 */ 242 junkmail() 243 { 244 static struct ignore { 245 char *name; 246 int len; 247 } ignore[] = { 248 "-request", 8, "postmaster", 10, "uucp", 4, 249 "mailer-daemon", 13, "mailer", 6, "-relay", 6, 250 NULL, NULL, 251 }; 252 register struct ignore *cur; 253 register int len; 254 register char *p; 255 256 /* 257 * This is mildly amusing, and I'm not positive it's right; trying 258 * to find the "real" name of the sender, assuming that addresses 259 * will be some variant of: 260 * 261 * From site!site!SENDER%site.domain%site.domain@site.domain 262 */ 263 if (!(p = index(from, '%'))) 264 if (!(p = index(from, '@'))) { 265 if (p = rindex(from, '!')) 266 ++p; 267 else 268 p = from; 269 for (; *p; ++p); 270 } 271 len = p - from; 272 for (cur = ignore; cur->name; ++cur) 273 if (len >= cur->len && 274 !strncasecmp(cur->name, p - cur->len, cur->len)) 275 return(1); 276 return(0); 277 } 278 279 #define VIT "__VACATION__INTERVAL__TIMER__" 280 281 /* 282 * recent -- 283 * find out if user has gotten a vacation message recently. 284 * use bcopy for machines with alignment restrictions 285 */ 286 recent() 287 { 288 DBT key, data; 289 time_t then, next; 290 291 /* get interval time */ 292 key.data = VIT; 293 key.size = sizeof(VIT); 294 if ((db->get)(db, &key, &data, 0)) 295 next = SECSPERDAY * DAYSPERWEEK; 296 else 297 bcopy(data.data, &next, sizeof(next)); 298 299 /* get record for this address */ 300 key.data = from; 301 key.size = strlen(from); 302 if (!(db->get)(db, &key, &data, 0)) { 303 bcopy(data.data, &then, sizeof(then)); 304 if (next == LONG_MAX || then + next > time(NULL)) 305 return(1); 306 } 307 return(0); 308 } 309 310 /* 311 * setinterval -- 312 * store the reply interval 313 */ 314 setinterval(interval) 315 time_t interval; 316 { 317 DBT key, data; 318 319 key.data = VIT; 320 key.size = sizeof(VIT); 321 data.data = &interval; 322 data.size = sizeof(interval); 323 (void)(db->put)(db, &key, &data, 0); 324 } 325 326 /* 327 * setreply -- 328 * store that this user knows about the vacation. 329 */ 330 setreply() 331 { 332 DBT key, data; 333 time_t now; 334 335 key.data = from; 336 key.size = strlen(from); 337 (void)time(&now); 338 data.data = &now; 339 data.size = sizeof(now); 340 (void)(db->put)(db, &key, &data, 0); 341 } 342 343 /* 344 * sendmessage -- 345 * exec sendmail to send the vacation file to sender 346 */ 347 sendmessage(myname) 348 char *myname; 349 { 350 if (!freopen(VMSG, "r", stdin)) { 351 syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG); 352 exit(1); 353 } 354 execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL); 355 syslog(LOG_ERR, "vacation: can't exec %s.\n", _PATH_SENDMAIL); 356 exit(1); 357 } 358 359 usage() 360 { 361 syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n", 362 getuid()); 363 exit(1); 364 } 365