1 1.38 hauke /* $NetBSD: vacation.c,v 1.38 2023/04/11 10:34:52 hauke Exp $ */ 2 1.5 jtc 3 1.1 cgd /* 4 1.5 jtc * Copyright (c) 1983, 1987, 1993 5 1.5 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.24 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.10 mikel #include <sys/cdefs.h> 33 1.10 mikel 34 1.1 cgd #ifndef lint 35 1.36 lukem __COPYRIGHT("@(#) Copyright (c) 1983, 1987, 1993\ 36 1.36 lukem The Regents of the University of California. All rights reserved."); 37 1.1 cgd #endif /* not lint */ 38 1.1 cgd 39 1.1 cgd #ifndef lint 40 1.5 jtc #if 0 41 1.5 jtc static char sccsid[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94"; 42 1.5 jtc #endif 43 1.38 hauke __RCSID("$NetBSD: vacation.c,v 1.38 2023/04/11 10:34:52 hauke Exp $"); 44 1.1 cgd #endif /* not lint */ 45 1.1 cgd 46 1.1 cgd /* 47 1.1 cgd ** Vacation 48 1.1 cgd ** Copyright (c) 1983 Eric P. Allman 49 1.1 cgd ** Berkeley, California 50 1.1 cgd */ 51 1.1 cgd 52 1.1 cgd #include <sys/param.h> 53 1.1 cgd #include <sys/stat.h> 54 1.10 mikel 55 1.10 mikel #include <ctype.h> 56 1.10 mikel #include <db.h> 57 1.22 christos #include <err.h> 58 1.10 mikel #include <errno.h> 59 1.1 cgd #include <fcntl.h> 60 1.10 mikel #include <paths.h> 61 1.1 cgd #include <pwd.h> 62 1.10 mikel #include <stdio.h> 63 1.10 mikel #include <stdlib.h> 64 1.10 mikel #include <string.h> 65 1.10 mikel #include <syslog.h> 66 1.1 cgd #include <time.h> 67 1.1 cgd #include <tzfile.h> 68 1.1 cgd #include <unistd.h> 69 1.1 cgd 70 1.1 cgd /* 71 1.1 cgd * VACATION -- return a message to the sender when on vacation. 72 1.1 cgd * 73 1.1 cgd * This program is invoked as a message receiver. It returns a 74 1.1 cgd * message specified by the user to whomever sent the mail, taking 75 1.1 cgd * care not to return a message too often to prevent "I am on 76 1.1 cgd * vacation" loops. 77 1.1 cgd */ 78 1.1 cgd 79 1.1 cgd #define MAXLINE 1024 /* max line from mail header */ 80 1.30 christos 81 1.30 christos static const char *dbprefix = ".vacation"; /* dbm's database sans .db */ 82 1.30 christos static const char *msgfile = ".vacation.msg"; /* vacation message */ 83 1.1 cgd 84 1.1 cgd typedef struct alias { 85 1.1 cgd struct alias *next; 86 1.15 mycroft const char *name; 87 1.17 mjl } alias_t; 88 1.30 christos static alias_t *names; 89 1.30 christos 90 1.30 christos static DB *db; 91 1.30 christos static char from[MAXLINE]; 92 1.30 christos static char subject[MAXLINE]; 93 1.30 christos 94 1.30 christos static int iflag = 0; /* Initialize the database */ 95 1.1 cgd 96 1.22 christos static int tflag = 0; 97 1.25 christos #define APPARENTLY_TO 1 98 1.25 christos #define DELIVERED_TO 2 99 1.30 christos 100 1.25 christos static int fflag = 0; 101 1.25 christos #define FROM_FROM 1 102 1.25 christos #define RETURN_PATH_FROM 2 103 1.25 christos #define SENDER_FROM 4 104 1.30 christos 105 1.30 christos static int toanybody = 0; /* Don't check if we appear in the to or cc */ 106 1.30 christos 107 1.27 christos static int debug = 0; 108 1.1 cgd 109 1.30 christos static void opendb(void); 110 1.30 christos static int junkmail(const char *); 111 1.30 christos static int nsearch(const char *, const char *); 112 1.30 christos static int readheaders(void); 113 1.30 christos static int recent(void); 114 1.30 christos static void getfrom(char *); 115 1.30 christos static void sendmessage(const char *); 116 1.30 christos static void setinterval(time_t); 117 1.30 christos static void setreply(void); 118 1.35 perry static void usage(void) __dead; 119 1.1 cgd 120 1.6 jtc int 121 1.17 mjl main(int argc, char **argv) 122 1.1 cgd { 123 1.1 cgd struct passwd *pw; 124 1.17 mjl alias_t *cur; 125 1.30 christos long interval; 126 1.30 christos int ch, rv; 127 1.22 christos char *p; 128 1.1 cgd 129 1.28 wiz setprogname(argv[0]); 130 1.30 christos opterr = 0; 131 1.1 cgd interval = -1; 132 1.25 christos openlog(getprogname(), 0, LOG_USER); 133 1.33 christos while ((ch = getopt(argc, argv, "a:df:F:Iijm:r:s:t:T:")) != -1) 134 1.1 cgd switch((char)ch) { 135 1.1 cgd case 'a': /* alias */ 136 1.17 mjl if (!(cur = (alias_t *)malloc((size_t)sizeof(alias_t)))) 137 1.1 cgd break; 138 1.1 cgd cur->name = optarg; 139 1.1 cgd cur->next = names; 140 1.1 cgd names = cur; 141 1.1 cgd break; 142 1.27 christos case 'd': 143 1.27 christos debug++; 144 1.27 christos break; 145 1.30 christos case 'F': 146 1.25 christos for (p = optarg; *p; p++) 147 1.25 christos switch (*p) { 148 1.25 christos case 'F': 149 1.25 christos fflag |= FROM_FROM; 150 1.25 christos break; 151 1.25 christos case 'R': 152 1.25 christos fflag |= RETURN_PATH_FROM; 153 1.25 christos break; 154 1.25 christos case 'S': 155 1.25 christos fflag |= SENDER_FROM; 156 1.25 christos break; 157 1.25 christos default: 158 1.25 christos errx(1, "Unknown -f option `%c'", *p); 159 1.25 christos } 160 1.25 christos break; 161 1.30 christos case 'f': 162 1.30 christos dbprefix = optarg; 163 1.30 christos break; 164 1.1 cgd case 'I': /* backward compatible */ 165 1.1 cgd case 'i': /* init the database */ 166 1.1 cgd iflag = 1; 167 1.1 cgd break; 168 1.30 christos case 'j': 169 1.30 christos toanybody = 1; 170 1.30 christos break; 171 1.30 christos case 'm': 172 1.30 christos msgfile = optarg; 173 1.30 christos break; 174 1.1 cgd case 'r': 175 1.30 christos case 't': /* Solaris compatibility */ 176 1.30 christos if (!isdigit((unsigned char)*optarg)) { 177 1.30 christos interval = LONG_MAX; 178 1.30 christos break; 179 1.1 cgd } 180 1.30 christos if (*optarg == '\0') 181 1.30 christos goto bad; 182 1.30 christos interval = strtol(optarg, &p, 0); 183 1.30 christos if (errno == ERANGE && 184 1.30 christos (interval == LONG_MAX || interval == LONG_MIN)) 185 1.30 christos err(1, "Bad interval `%s'", optarg); 186 1.30 christos switch (*p) { 187 1.30 christos case 's': 188 1.30 christos break; 189 1.30 christos case 'm': 190 1.30 christos interval *= SECSPERMIN; 191 1.30 christos break; 192 1.30 christos case 'h': 193 1.30 christos interval *= SECSPERHOUR; 194 1.30 christos break; 195 1.30 christos case 'd': 196 1.30 christos case '\0': 197 1.30 christos interval *= SECSPERDAY; 198 1.30 christos break; 199 1.30 christos case 'w': 200 1.30 christos interval *= DAYSPERWEEK * SECSPERDAY; 201 1.30 christos break; 202 1.30 christos default: 203 1.30 christos bad: 204 1.30 christos errx(1, "Invalid interval `%s'", optarg); 205 1.30 christos } 206 1.30 christos if (interval < 0 || (*p && p[1])) 207 1.30 christos goto bad; 208 1.30 christos break; 209 1.30 christos case 's': 210 1.30 christos (void)strlcpy(from, optarg, sizeof(from)); 211 1.1 cgd break; 212 1.30 christos case 'T': 213 1.22 christos for (p = optarg; *p; p++) 214 1.22 christos switch (*p) { 215 1.22 christos case 'A': 216 1.22 christos tflag |= APPARENTLY_TO; 217 1.22 christos break; 218 1.22 christos case 'D': 219 1.22 christos tflag |= DELIVERED_TO; 220 1.22 christos break; 221 1.22 christos default: 222 1.37 pgoyette errx(1, "Unknown -T option `%c'", *p); 223 1.22 christos } 224 1.22 christos break; 225 1.1 cgd case '?': 226 1.1 cgd default: 227 1.1 cgd usage(); 228 1.1 cgd } 229 1.1 cgd argc -= optind; 230 1.1 cgd argv += optind; 231 1.1 cgd 232 1.1 cgd if (argc != 1) { 233 1.1 cgd if (!iflag) 234 1.1 cgd usage(); 235 1.1 cgd if (!(pw = getpwuid(getuid()))) { 236 1.27 christos syslog(LOG_ERR, "%s: no such user uid %u.", 237 1.27 christos getprogname(), getuid()); 238 1.1 cgd exit(1); 239 1.1 cgd } 240 1.1 cgd } 241 1.1 cgd else if (!(pw = getpwnam(*argv))) { 242 1.27 christos syslog(LOG_ERR, "%s: no such user %s.", 243 1.27 christos getprogname(), *argv); 244 1.1 cgd exit(1); 245 1.1 cgd } 246 1.34 christos if (chdir(pw->pw_dir) == -1 && 247 1.34 christos (dbprefix[0] != '/' || msgfile[0] != '/')) { 248 1.27 christos syslog(LOG_ERR, "%s: no such directory %s.", 249 1.27 christos getprogname(), pw->pw_dir); 250 1.1 cgd exit(1); 251 1.1 cgd } 252 1.1 cgd 253 1.30 christos opendb(); 254 1.1 cgd 255 1.1 cgd if (interval != -1) 256 1.30 christos setinterval((time_t)interval); 257 1.1 cgd 258 1.1 cgd if (iflag) { 259 1.1 cgd (void)(db->close)(db); 260 1.1 cgd exit(0); 261 1.1 cgd } 262 1.1 cgd 263 1.27 christos if (!(cur = malloc((size_t)sizeof(alias_t)))) { 264 1.27 christos syslog(LOG_ERR, "%s: %m", getprogname()); 265 1.27 christos (void)(db->close)(db); 266 1.1 cgd exit(1); 267 1.27 christos } 268 1.1 cgd cur->name = pw->pw_name; 269 1.1 cgd cur->next = names; 270 1.1 cgd names = cur; 271 1.1 cgd 272 1.27 christos if ((rv = readheaders()) != -1) { 273 1.27 christos (void)(db->close)(db); 274 1.27 christos exit(rv); 275 1.27 christos } 276 1.27 christos 277 1.1 cgd if (!recent()) { 278 1.1 cgd setreply(); 279 1.1 cgd (void)(db->close)(db); 280 1.1 cgd sendmessage(pw->pw_name); 281 1.1 cgd } 282 1.5 jtc else 283 1.5 jtc (void)(db->close)(db); 284 1.1 cgd exit(0); 285 1.1 cgd /* NOTREACHED */ 286 1.1 cgd } 287 1.1 cgd 288 1.30 christos static void 289 1.30 christos opendb(void) 290 1.30 christos { 291 1.30 christos char path[MAXPATHLEN]; 292 1.30 christos 293 1.30 christos (void)snprintf(path, sizeof(path), "%s.db", dbprefix); 294 1.30 christos db = dbopen(path, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), 295 1.30 christos S_IRUSR|S_IWUSR, DB_HASH, NULL); 296 1.30 christos 297 1.30 christos if (!db) { 298 1.30 christos syslog(LOG_ERR, "%s: %s: %m", getprogname(), path); 299 1.30 christos exit(1); 300 1.30 christos } 301 1.30 christos } 302 1.30 christos 303 1.1 cgd /* 304 1.1 cgd * readheaders -- 305 1.1 cgd * read mail headers 306 1.1 cgd */ 307 1.30 christos static int 308 1.20 perry readheaders(void) 309 1.1 cgd { 310 1.17 mjl alias_t *cur; 311 1.10 mikel char *p; 312 1.1 cgd int tome, cont; 313 1.1 cgd char buf[MAXLINE]; 314 1.1 cgd 315 1.1 cgd cont = tome = 0; 316 1.30 christos #define COMPARE(a, b) strncmp(a, b, sizeof(b) - 1) 317 1.30 christos #define CASECOMPARE(a, b) strncasecmp(a, b, sizeof(b) - 1) 318 1.1 cgd while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') 319 1.1 cgd switch(*buf) { 320 1.25 christos case 'F': /* "From " or "From:" */ 321 1.1 cgd cont = 0; 322 1.30 christos if (COMPARE(buf, "From ") == 0) 323 1.30 christos getfrom(buf + sizeof("From ") - 1); 324 1.26 christos if ((fflag & FROM_FROM) != 0 && 325 1.30 christos COMPARE(buf, "From:") == 0) 326 1.30 christos getfrom(buf + sizeof("From:") - 1); 327 1.1 cgd break; 328 1.38 hauke case 'P': /* "Precedence:" rfc 2076 ch 3.9 */ 329 1.1 cgd cont = 0; 330 1.30 christos if (CASECOMPARE(buf, "Precedence") != 0 || 331 1.10 mikel (buf[10] != ':' && buf[10] != ' ' && 332 1.10 mikel buf[10] != '\t')) 333 1.1 cgd break; 334 1.30 christos if ((p = strchr(buf, ':')) == NULL) 335 1.1 cgd break; 336 1.25 christos while (*++p && isspace((unsigned char)*p)) 337 1.25 christos continue; 338 1.1 cgd if (!*p) 339 1.1 cgd break; 340 1.30 christos if (CASECOMPARE(p, "junk") == 0 || 341 1.30 christos CASECOMPARE(p, "bulk") == 0|| 342 1.30 christos CASECOMPARE(p, "list") == 0) 343 1.1 cgd exit(0); 344 1.1 cgd break; 345 1.1 cgd case 'C': /* "Cc:" */ 346 1.30 christos if (COMPARE(buf, "Cc:")) 347 1.1 cgd break; 348 1.1 cgd cont = 1; 349 1.1 cgd goto findme; 350 1.1 cgd case 'T': /* "To:" */ 351 1.30 christos if (COMPARE(buf, "To:")) 352 1.21 christos break; 353 1.21 christos cont = 1; 354 1.21 christos goto findme; 355 1.38 hauke case 'A': 356 1.38 hauke /* "Apparently-To:" */ 357 1.38 hauke if ((tflag & APPARENTLY_TO) != 0 && 358 1.38 hauke COMPARE(buf, "Apparently-To:") == 0) { 359 1.38 hauke cont = 1; 360 1.38 hauke goto findme; 361 1.38 hauke } 362 1.38 hauke /* "Auto-Submitted:" rfc 3834 ch 5 */ 363 1.38 hauke cont = 0; 364 1.38 hauke if (CASECOMPARE(buf, "Auto-Submitted") != 0 || 365 1.38 hauke (buf[14] != ':' && buf[14] != ' ' && 366 1.38 hauke buf[14] != '\t')) 367 1.21 christos break; 368 1.38 hauke if ((p = strchr(buf, ':')) == NULL) 369 1.38 hauke break; 370 1.38 hauke while (*++p && isspace((unsigned char)*p)) 371 1.38 hauke continue; 372 1.38 hauke if (CASECOMPARE(p, "no") != 0 ) 373 1.38 hauke exit(0); 374 1.38 hauke break; 375 1.25 christos case 'D': /* "Delivered-To:" */ 376 1.22 christos if ((tflag & DELIVERED_TO) == 0 || 377 1.30 christos COMPARE(buf, "Delivered-To:") != 0) 378 1.1 cgd break; 379 1.1 cgd cont = 1; 380 1.1 cgd goto findme; 381 1.25 christos case 'R': /* "Return-Path:" */ 382 1.25 christos cont = 0; 383 1.26 christos if ((fflag & RETURN_PATH_FROM) != 0 && 384 1.30 christos COMPARE(buf, "Return-Path:") == 0) 385 1.30 christos getfrom(buf + sizeof("Return-Path:") - 1); 386 1.25 christos break; 387 1.25 christos case 'S': /* "Sender:" */ 388 1.25 christos cont = 0; 389 1.30 christos if (COMPARE(buf, "Subject:") == 0) { 390 1.30 christos /* trim leading blanks */ 391 1.30 christos char *s = NULL; 392 1.30 christos for (p = buf + sizeof("Subject:") - 1; *p; p++) 393 1.30 christos if (s == NULL && 394 1.30 christos !isspace((unsigned char)*p)) 395 1.30 christos s = p; 396 1.30 christos /* trim trailing blanks */ 397 1.30 christos if (s) { 398 1.30 christos for (--p; p != s; p--) 399 1.30 christos if (!isspace((unsigned char)*p)) 400 1.30 christos break; 401 1.30 christos *++p = '\0'; 402 1.30 christos } 403 1.31 christos if (s) { 404 1.31 christos (void)strlcpy(subject, s, sizeof(subject)); 405 1.31 christos } else { 406 1.31 christos subject[0] = '\0'; 407 1.31 christos } 408 1.30 christos } 409 1.26 christos if ((fflag & SENDER_FROM) != 0 && 410 1.30 christos COMPARE(buf, "Sender:") == 0) 411 1.30 christos getfrom(buf + sizeof("Sender:") - 1); 412 1.25 christos break; 413 1.1 cgd default: 414 1.16 christos if (!isspace((unsigned char)*buf) || !cont || tome) { 415 1.1 cgd cont = 0; 416 1.1 cgd break; 417 1.1 cgd } 418 1.1 cgd findme: for (cur = names; !tome && cur; cur = cur->next) 419 1.1 cgd tome += nsearch(cur->name, buf); 420 1.1 cgd } 421 1.30 christos if (!toanybody && !tome) 422 1.27 christos return 0; 423 1.1 cgd if (!*from) { 424 1.27 christos syslog(LOG_ERR, "%s: no initial \"From\" line.", 425 1.27 christos getprogname()); 426 1.27 christos return 1; 427 1.1 cgd } 428 1.27 christos return -1; 429 1.1 cgd } 430 1.1 cgd 431 1.1 cgd /* 432 1.1 cgd * nsearch -- 433 1.1 cgd * do a nice, slow, search of a string for a substring. 434 1.1 cgd */ 435 1.30 christos static int 436 1.17 mjl nsearch(const char *name, const char *str) 437 1.1 cgd { 438 1.10 mikel size_t len; 439 1.1 cgd 440 1.1 cgd for (len = strlen(name); *str; ++str) 441 1.17 mjl if (!strncasecmp(name, str, len)) 442 1.1 cgd return(1); 443 1.1 cgd return(0); 444 1.1 cgd } 445 1.1 cgd 446 1.1 cgd /* 447 1.25 christos * getfrom -- 448 1.25 christos * return the first string in the buffer, stripping leading and trailing 449 1.25 christos * blanks and <>. 450 1.25 christos */ 451 1.25 christos void 452 1.25 christos getfrom(char *buf) 453 1.25 christos { 454 1.25 christos char *s, *p; 455 1.25 christos 456 1.27 christos if ((s = strchr(buf, '<')) != NULL) 457 1.27 christos s++; 458 1.27 christos else 459 1.27 christos s = buf; 460 1.27 christos 461 1.27 christos for (; *s && isspace((unsigned char)*s); s++) 462 1.25 christos continue; 463 1.25 christos for (p = s; *p && !isspace((unsigned char)*p); p++) 464 1.25 christos continue; 465 1.30 christos 466 1.25 christos if (*--p == '>') 467 1.25 christos *p = '\0'; 468 1.25 christos else 469 1.27 christos *++p = '\0'; 470 1.30 christos 471 1.30 christos if (junkmail(s)) 472 1.25 christos exit(0); 473 1.30 christos 474 1.30 christos if (!*from) 475 1.30 christos (void)strlcpy(from, s, sizeof(from)); 476 1.25 christos } 477 1.25 christos 478 1.25 christos /* 479 1.1 cgd * junkmail -- 480 1.5 jtc * read the header and return if automagic/junk/bulk/list mail 481 1.1 cgd */ 482 1.30 christos static int 483 1.30 christos junkmail(const char *addr) 484 1.1 cgd { 485 1.1 cgd static struct ignore { 486 1.30 christos const char *name; 487 1.30 christos size_t len; 488 1.1 cgd } ignore[] = { 489 1.30 christos #define INIT(a) { a, sizeof(a) - 1 } 490 1.30 christos INIT("-request"), 491 1.30 christos INIT("postmaster"), 492 1.30 christos INIT("uucp"), 493 1.30 christos INIT("mailer-daemon"), 494 1.30 christos INIT("mailer"), 495 1.30 christos INIT("-relay"), 496 1.8 pk {NULL, 0 } 497 1.1 cgd }; 498 1.10 mikel struct ignore *cur; 499 1.30 christos size_t len; 500 1.30 christos const char *p; 501 1.1 cgd 502 1.1 cgd /* 503 1.1 cgd * This is mildly amusing, and I'm not positive it's right; trying 504 1.1 cgd * to find the "real" name of the sender, assuming that addresses 505 1.1 cgd * will be some variant of: 506 1.1 cgd * 507 1.1 cgd * From site!site!SENDER%site.domain%site.domain (at) site.domain 508 1.1 cgd */ 509 1.30 christos if (!(p = strchr(addr, '%'))) 510 1.30 christos if (!(p = strchr(addr, '@'))) { 511 1.30 christos if ((p = strrchr(addr, '!')) != NULL) 512 1.1 cgd ++p; 513 1.1 cgd else 514 1.30 christos p = addr; 515 1.29 christos for (; *p; ++p) 516 1.29 christos continue; 517 1.1 cgd } 518 1.30 christos len = p - addr; 519 1.1 cgd for (cur = ignore; cur->name; ++cur) 520 1.1 cgd if (len >= cur->len && 521 1.1 cgd !strncasecmp(cur->name, p - cur->len, cur->len)) 522 1.1 cgd return(1); 523 1.1 cgd return(0); 524 1.1 cgd } 525 1.1 cgd 526 1.1 cgd #define VIT "__VACATION__INTERVAL__TIMER__" 527 1.1 cgd 528 1.1 cgd /* 529 1.1 cgd * recent -- 530 1.1 cgd * find out if user has gotten a vacation message recently. 531 1.11 lukem * use memmove for machines with alignment restrictions 532 1.1 cgd */ 533 1.30 christos static int 534 1.20 perry recent(void) 535 1.1 cgd { 536 1.1 cgd DBT key, data; 537 1.1 cgd time_t then, next; 538 1.1 cgd 539 1.1 cgd /* get interval time */ 540 1.30 christos key.data = (void *)(intptr_t)VIT; 541 1.1 cgd key.size = sizeof(VIT); 542 1.1 cgd if ((db->get)(db, &key, &data, 0)) 543 1.1 cgd next = SECSPERDAY * DAYSPERWEEK; 544 1.1 cgd else 545 1.29 christos (void)memmove(&next, data.data, sizeof(next)); 546 1.1 cgd 547 1.1 cgd /* get record for this address */ 548 1.1 cgd key.data = from; 549 1.1 cgd key.size = strlen(from); 550 1.1 cgd if (!(db->get)(db, &key, &data, 0)) { 551 1.29 christos (void)memmove(&then, data.data, sizeof(then)); 552 1.7 cgd if (next == (time_t)LONG_MAX || /* XXX */ 553 1.7 cgd then + next > time(NULL)) 554 1.1 cgd return(1); 555 1.1 cgd } 556 1.1 cgd return(0); 557 1.1 cgd } 558 1.1 cgd 559 1.1 cgd /* 560 1.1 cgd * setinterval -- 561 1.1 cgd * store the reply interval 562 1.1 cgd */ 563 1.30 christos static void 564 1.17 mjl setinterval(time_t interval) 565 1.1 cgd { 566 1.1 cgd DBT key, data; 567 1.1 cgd 568 1.30 christos key.data = (void *)(intptr_t)VIT; 569 1.1 cgd key.size = sizeof(VIT); 570 1.1 cgd data.data = &interval; 571 1.1 cgd data.size = sizeof(interval); 572 1.3 mycroft (void)(db->put)(db, &key, &data, 0); 573 1.1 cgd } 574 1.1 cgd 575 1.1 cgd /* 576 1.1 cgd * setreply -- 577 1.1 cgd * store that this user knows about the vacation. 578 1.1 cgd */ 579 1.30 christos static void 580 1.20 perry setreply(void) 581 1.1 cgd { 582 1.1 cgd DBT key, data; 583 1.1 cgd time_t now; 584 1.1 cgd 585 1.1 cgd key.data = from; 586 1.1 cgd key.size = strlen(from); 587 1.1 cgd (void)time(&now); 588 1.1 cgd data.data = &now; 589 1.1 cgd data.size = sizeof(now); 590 1.3 mycroft (void)(db->put)(db, &key, &data, 0); 591 1.1 cgd } 592 1.1 cgd 593 1.1 cgd /* 594 1.1 cgd * sendmessage -- 595 1.1 cgd * exec sendmail to send the vacation file to sender 596 1.1 cgd */ 597 1.30 christos static void 598 1.17 mjl sendmessage(const char *myname) 599 1.1 cgd { 600 1.5 jtc FILE *mfp, *sfp; 601 1.5 jtc int i; 602 1.5 jtc int pvect[2]; 603 1.5 jtc char buf[MAXLINE]; 604 1.5 jtc 605 1.30 christos mfp = fopen(msgfile, "r"); 606 1.5 jtc if (mfp == NULL) { 607 1.30 christos syslog(LOG_ERR, "%s: no `%s' file for `%s'.", getprogname(), 608 1.30 christos myname, msgfile); 609 1.1 cgd exit(1); 610 1.1 cgd } 611 1.27 christos 612 1.27 christos if (debug) { 613 1.27 christos sfp = stdout; 614 1.27 christos } else { 615 1.27 christos if (pipe(pvect) < 0) { 616 1.27 christos syslog(LOG_ERR, "%s: pipe: %m", getprogname()); 617 1.27 christos exit(1); 618 1.27 christos } 619 1.27 christos i = vfork(); 620 1.27 christos if (i < 0) { 621 1.27 christos syslog(LOG_ERR, "%s: fork: %m", getprogname()); 622 1.27 christos exit(1); 623 1.27 christos } 624 1.27 christos if (i == 0) { 625 1.29 christos (void)dup2(pvect[0], 0); 626 1.29 christos (void)close(pvect[0]); 627 1.29 christos (void)close(pvect[1]); 628 1.29 christos (void)close(fileno(mfp)); 629 1.29 christos (void)execl(_PATH_SENDMAIL, "sendmail", "-f", myname, 630 1.27 christos "--", from, NULL); 631 1.27 christos syslog(LOG_ERR, "%s: can't exec %s: %m", 632 1.27 christos getprogname(), _PATH_SENDMAIL); 633 1.27 christos _exit(1); 634 1.27 christos } 635 1.27 christos (void)close(pvect[0]); 636 1.27 christos sfp = fdopen(pvect[1], "w"); 637 1.29 christos if (sfp == NULL) { 638 1.29 christos syslog(LOG_ERR, "%s: can't fdopen %d: %m", 639 1.29 christos getprogname(), pvect[1]); 640 1.29 christos _exit(1); 641 1.29 christos } 642 1.27 christos } 643 1.29 christos (void)fprintf(sfp, "To: %s\n", from); 644 1.32 christos (void)fputs("Auto-Submitted: auto-replied\n", sfp); 645 1.38 hauke (void)fputs("Precedence: bulk\n", sfp); 646 1.30 christos while (fgets(buf, sizeof buf, mfp) != NULL) { 647 1.30 christos char *p; 648 1.30 christos if ((p = strstr(buf, "$SUBJECT")) != NULL) { 649 1.30 christos *p = '\0'; 650 1.30 christos (void)fputs(buf, sfp); 651 1.30 christos (void)fputs(subject, sfp); 652 1.30 christos p += sizeof("$SUBJECT") - 1; 653 1.30 christos (void)fputs(p, sfp); 654 1.30 christos } else 655 1.30 christos (void)fputs(buf, sfp); 656 1.30 christos } 657 1.27 christos (void)fclose(mfp); 658 1.27 christos if (sfp != stdout) 659 1.27 christos (void)fclose(sfp); 660 1.1 cgd } 661 1.1 cgd 662 1.30 christos static void 663 1.20 perry usage(void) 664 1.1 cgd { 665 1.10 mikel 666 1.30 christos syslog(LOG_ERR, "uid %u: Usage: %s [-dIij] [-a alias] [-f database_file] [-F F|R|S] [-m message_file] [-s sender] [-t interval] [-T A|D]" 667 1.27 christos " login", getuid(), getprogname()); 668 1.1 cgd exit(1); 669 1.1 cgd } 670