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 9 char 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 15 static char sccsid[] = "@(#)vacation.c 5.20 (Berkeley) 09/23/91"; 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 exit(0); 195 break; 196 case 'C': /* "Cc:" */ 197 if (strncmp(buf, "Cc:", 3)) 198 break; 199 cont = 1; 200 goto findme; 201 case 'T': /* "To:" */ 202 if (strncmp(buf, "To:", 3)) 203 break; 204 cont = 1; 205 goto findme; 206 default: 207 if (!isspace(*buf) || !cont || tome) { 208 cont = 0; 209 break; 210 } 211 findme: for (cur = names; !tome && cur; cur = cur->next) 212 tome += nsearch(cur->name, buf); 213 } 214 if (!tome) 215 exit(0); 216 if (!*from) { 217 syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n"); 218 exit(1); 219 } 220 } 221 222 /* 223 * nsearch -- 224 * do a nice, slow, search of a string for a substring. 225 */ 226 nsearch(name, str) 227 register char *name, *str; 228 { 229 register int len; 230 231 for (len = strlen(name); *str; ++str) 232 if (*str == *name && !strncasecmp(name, str, len)) 233 return(1); 234 return(0); 235 } 236 237 /* 238 * junkmail -- 239 * read the header and return if automagic/junk/bulk mail 240 */ 241 junkmail() 242 { 243 static struct ignore { 244 char *name; 245 int len; 246 } ignore[] = { 247 "-request", 8, "postmaster", 10, "uucp", 4, 248 "mailer-daemon", 13, "mailer", 6, "-relay", 6, 249 NULL, NULL, 250 }; 251 register struct ignore *cur; 252 register int len; 253 register char *p; 254 255 /* 256 * This is mildly amusing, and I'm not positive it's right; trying 257 * to find the "real" name of the sender, assuming that addresses 258 * will be some variant of: 259 * 260 * From site!site!SENDER%site.domain%site.domain@site.domain 261 */ 262 if (!(p = index(from, '%'))) 263 if (!(p = index(from, '@'))) { 264 if (p = rindex(from, '!')) 265 ++p; 266 else 267 p = from; 268 for (; *p; ++p); 269 } 270 len = p - from; 271 for (cur = ignore; cur->name; ++cur) 272 if (len >= cur->len && 273 !strncasecmp(cur->name, p - cur->len, cur->len)) 274 return(1); 275 return(0); 276 } 277 278 #define VIT "__VACATION__INTERVAL__TIMER__" 279 280 /* 281 * recent -- 282 * find out if user has gotten a vacation message recently. 283 * use bcopy for machines with alignment restrictions 284 */ 285 recent() 286 { 287 DBT key, data; 288 time_t then, next; 289 290 /* get interval time */ 291 key.data = VIT; 292 key.size = sizeof(VIT); 293 if ((db->get)(db, &key, &data, 0)) 294 next = SECSPERDAY * DAYSPERWEEK; 295 else 296 bcopy(data.data, &next, sizeof(next)); 297 298 /* get record for this address */ 299 key.data = from; 300 key.size = strlen(from); 301 if (!(db->get)(db, &key, &data, 0)) { 302 bcopy(data.data, &then, sizeof(then)); 303 if (next == LONG_MAX || then + next > time(NULL)) 304 return(1); 305 } 306 return(0); 307 } 308 309 /* 310 * setinterval -- 311 * store the reply interval 312 */ 313 setinterval(interval) 314 time_t interval; 315 { 316 DBT key, data; 317 318 key.data = VIT; 319 key.size = sizeof(VIT); 320 data.data = &interval; 321 data.size = sizeof(interval); 322 (void)(db->put)(db, &key, &data, 0); 323 } 324 325 /* 326 * setreply -- 327 * store that this user knows about the vacation. 328 */ 329 setreply() 330 { 331 DBT key, data; 332 time_t now; 333 334 key.data = from; 335 key.size = strlen(from); 336 (void)time(&now); 337 data.data = &now; 338 data.size = sizeof(now); 339 (void)(db->put)(db, &key, &data, 0); 340 } 341 342 /* 343 * sendmessage -- 344 * exec sendmail to send the vacation file to sender 345 */ 346 sendmessage(myname) 347 char *myname; 348 { 349 if (!freopen(VMSG, "r", stdin)) { 350 syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG); 351 exit(1); 352 } 353 execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL); 354 syslog(LOG_ERR, "vacation: can't exec %s.\n", _PATH_SENDMAIL); 355 exit(1); 356 } 357 358 usage() 359 { 360 syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n", 361 getuid()); 362 exit(1); 363 } 364