1 /* 2 * Copyright (c) 1999-2002, 2009 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1987, 1993 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1983 Eric P. Allman. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sm/gen.h> 15 16 SM_IDSTR(copyright, 17 "@(#) Copyright (c) 1999-2002, 2009 Sendmail, Inc. and its suppliers.\n\ 18 All rights reserved.\n\ 19 Copyright (c) 1983, 1987, 1993\n\ 20 The Regents of the University of California. All rights reserved.\n\ 21 Copyright (c) 1983 Eric P. Allman. All rights reserved.\n") 22 23 SM_IDSTR(id, "@(#)$Id: vacation.c,v 8.147 2013/03/12 15:24:56 ca Exp $") 24 25 26 #include <ctype.h> 27 #include <stdlib.h> 28 #include <syslog.h> 29 #include <time.h> 30 #include <unistd.h> 31 #ifdef EX_OK 32 # undef EX_OK /* unistd.h may have another use for this */ 33 #endif /* EX_OK */ 34 #include <sm/sysexits.h> 35 36 #include <sm/cf.h> 37 #include <sm/mbdb.h> 38 #include "sendmail/sendmail.h" 39 #include <sendmail/pathnames.h> 40 #include "libsmdb/smdb.h" 41 42 #define ONLY_ONCE ((time_t) 0) /* send at most one reply */ 43 #define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */ 44 45 uid_t RealUid; 46 gid_t RealGid; 47 char *RealUserName; 48 uid_t RunAsUid; 49 gid_t RunAsGid; 50 char *RunAsUserName; 51 int Verbose = 2; 52 bool DontInitGroups = false; 53 uid_t TrustedUid = 0; 54 BITMAP256 DontBlameSendmail; 55 56 static int readheaders __P((bool)); 57 static bool junkmail __P((char *)); 58 static bool nsearch __P((char *, char *)); 59 static void usage __P((void)); 60 static void setinterval __P((time_t)); 61 static bool recent __P((void)); 62 static void setreply __P((char *, time_t)); 63 static void sendmessage __P((char *, char *, char *)); 64 static void xclude __P((SM_FILE_T *)); 65 66 /* 67 ** VACATION -- return a message to the sender when on vacation. 68 ** 69 ** This program is invoked as a message receiver. It returns a 70 ** message specified by the user to whomever sent the mail, taking 71 ** care not to return a message too often to prevent "I am on 72 ** vacation" loops. 73 */ 74 75 #define VDB ".vacation" /* vacation database */ 76 #define VMSG ".vacation.msg" /* vacation message */ 77 #define SECSPERDAY (60 * 60 * 24) 78 #define DAYSPERWEEK 7 79 80 typedef struct alias 81 { 82 char *name; 83 struct alias *next; 84 } ALIAS; 85 86 ALIAS *Names = NULL; 87 88 SMDB_DATABASE *Db; 89 90 char From[MAXLINE]; 91 bool CloseMBDB = false; 92 93 #if defined(__hpux) || defined(__osf__) 94 # ifndef SM_CONF_SYSLOG_INT 95 # define SM_CONF_SYSLOG_INT 1 96 # endif /* SM_CONF_SYSLOG_INT */ 97 #endif /* defined(__hpux) || defined(__osf__) */ 98 99 #if SM_CONF_SYSLOG_INT 100 # define SYSLOG_RET_T int 101 # define SYSLOG_RET return 0 102 #else /* SM_CONF_SYSLOG_INT */ 103 # define SYSLOG_RET_T void 104 # define SYSLOG_RET 105 #endif /* SM_CONF_SYSLOG_INT */ 106 107 typedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...)); 108 SYSLOG_T *msglog = syslog; 109 static SYSLOG_RET_T debuglog __P((int, const char *, ...)); 110 static void eatmsg __P((void)); 111 static void listdb __P((void)); 112 113 /* exit after reading input */ 114 #define EXITIT(excode) \ 115 { \ 116 eatmsg(); \ 117 if (CloseMBDB) \ 118 { \ 119 sm_mbdb_terminate(); \ 120 CloseMBDB = false; \ 121 } \ 122 return excode; \ 123 } 124 125 #define EXITM(excode) \ 126 { \ 127 if (!initdb && !list) \ 128 eatmsg(); \ 129 if (CloseMBDB) \ 130 { \ 131 sm_mbdb_terminate(); \ 132 CloseMBDB = false; \ 133 } \ 134 exit(excode); \ 135 } 136 137 int 138 main(argc, argv) 139 int argc; 140 char **argv; 141 { 142 bool alwaysrespond = false; 143 bool initdb, exclude; 144 bool runasuser = false; 145 bool list = false; 146 int mfail = 0, ufail = 0; 147 int ch; 148 int result; 149 long sff; 150 time_t interval; 151 struct passwd *pw; 152 ALIAS *cur; 153 char *dbfilename = NULL; 154 char *msgfilename = NULL; 155 char *cfpath = NULL; 156 char *name = NULL; 157 char *returnaddr = NULL; 158 SMDB_USER_INFO user_info; 159 static char rnamebuf[MAXNAME]; 160 extern int optind, opterr; 161 extern char *optarg; 162 163 /* Vars needed to link with smutil */ 164 clrbitmap(DontBlameSendmail); 165 RunAsUid = RealUid = getuid(); 166 RunAsGid = RealGid = getgid(); 167 pw = getpwuid(RealUid); 168 if (pw != NULL) 169 { 170 if (strlen(pw->pw_name) > MAXNAME - 1) 171 pw->pw_name[MAXNAME] = '\0'; 172 sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); 173 } 174 else 175 sm_snprintf(rnamebuf, sizeof rnamebuf, 176 "Unknown UID %d", (int) RealUid); 177 RunAsUserName = RealUserName = rnamebuf; 178 179 # ifdef LOG_MAIL 180 openlog("vacation", LOG_PID, LOG_MAIL); 181 # else /* LOG_MAIL */ 182 openlog("vacation", LOG_PID); 183 # endif /* LOG_MAIL */ 184 185 opterr = 0; 186 initdb = false; 187 exclude = false; 188 interval = INTERVAL_UNDEF; 189 *From = '\0'; 190 191 192 #define OPTIONS "a:C:df:Iijlm:R:r:s:t:Uxz" 193 194 while (mfail == 0 && ufail == 0 && 195 (ch = getopt(argc, argv, OPTIONS)) != -1) 196 { 197 switch((char)ch) 198 { 199 case 'a': /* alias */ 200 cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS)); 201 if (cur == NULL) 202 { 203 mfail++; 204 break; 205 } 206 cur->name = optarg; 207 cur->next = Names; 208 Names = cur; 209 break; 210 211 case 'C': 212 cfpath = optarg; 213 break; 214 215 case 'd': /* debug mode */ 216 msglog = debuglog; 217 break; 218 219 case 'f': /* alternate database */ 220 dbfilename = optarg; 221 break; 222 223 case 'I': /* backward compatible */ 224 case 'i': /* init the database */ 225 initdb = true; 226 break; 227 228 case 'j': 229 alwaysrespond = true; 230 break; 231 232 case 'l': 233 list = true; /* list the database */ 234 break; 235 236 case 'm': /* alternate message file */ 237 msgfilename = optarg; 238 break; 239 240 case 'R': 241 returnaddr = optarg; 242 break; 243 244 case 'r': 245 if (isascii(*optarg) && isdigit(*optarg)) 246 { 247 interval = atol(optarg) * SECSPERDAY; 248 if (interval < 0) 249 ufail++; 250 } 251 else 252 interval = ONLY_ONCE; 253 break; 254 255 case 's': /* alternate sender name */ 256 (void) sm_strlcpy(From, optarg, sizeof From); 257 break; 258 259 case 't': /* SunOS: -t1d (default expire) */ 260 break; 261 262 case 'U': /* run as single user mode */ 263 runasuser = true; 264 break; 265 266 case 'x': 267 exclude = true; 268 break; 269 270 case 'z': 271 returnaddr = "<>"; 272 break; 273 274 case '?': 275 default: 276 ufail++; 277 break; 278 } 279 } 280 argc -= optind; 281 argv += optind; 282 283 if (mfail != 0) 284 { 285 msglog(LOG_NOTICE, 286 "vacation: can't allocate memory for alias.\n"); 287 EXITM(EX_TEMPFAIL); 288 } 289 if (ufail != 0) 290 usage(); 291 292 if (argc != 1) 293 { 294 if (!initdb && !list && !exclude) 295 usage(); 296 if ((pw = getpwuid(getuid())) == NULL) 297 { 298 msglog(LOG_ERR, 299 "vacation: no such user uid %u.\n", getuid()); 300 EXITM(EX_NOUSER); 301 } 302 name = strdup(pw->pw_name); 303 user_info.smdbu_id = pw->pw_uid; 304 user_info.smdbu_group_id = pw->pw_gid; 305 (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, 306 SMDB_MAX_USER_NAME_LEN); 307 if (chdir(pw->pw_dir) != 0) 308 { 309 msglog(LOG_NOTICE, 310 "vacation: no such directory %s.\n", 311 pw->pw_dir); 312 EXITM(EX_NOINPUT); 313 } 314 } 315 else if (runasuser) 316 { 317 name = strdup(*argv); 318 if (dbfilename == NULL || msgfilename == NULL) 319 { 320 msglog(LOG_NOTICE, 321 "vacation: -U requires setting both -f and -m\n"); 322 EXITM(EX_NOINPUT); 323 } 324 user_info.smdbu_id = pw->pw_uid; 325 user_info.smdbu_group_id = pw->pw_gid; 326 (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, 327 SMDB_MAX_USER_NAME_LEN); 328 } 329 else 330 { 331 int err; 332 SM_CF_OPT_T mbdbname; 333 SM_MBDB_T user; 334 335 cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath); 336 mbdbname.opt_name = "MailboxDatabase"; 337 mbdbname.opt_val = "pw"; 338 (void) sm_cf_getopt(cfpath, 1, &mbdbname); 339 err = sm_mbdb_initialize(mbdbname.opt_val); 340 if (err != EX_OK) 341 { 342 msglog(LOG_ERR, 343 "vacation: can't open mailbox database: %s.\n", 344 sm_strexit(err)); 345 EXITM(err); 346 } 347 CloseMBDB = true; 348 err = sm_mbdb_lookup(*argv, &user); 349 if (err == EX_NOUSER) 350 { 351 msglog(LOG_ERR, "vacation: no such user %s.\n", *argv); 352 EXITM(EX_NOUSER); 353 } 354 if (err != EX_OK) 355 { 356 msglog(LOG_ERR, 357 "vacation: can't read mailbox database: %s.\n", 358 sm_strexit(err)); 359 EXITM(err); 360 } 361 name = strdup(user.mbdb_name); 362 if (chdir(user.mbdb_homedir) != 0) 363 { 364 msglog(LOG_NOTICE, 365 "vacation: no such directory %s.\n", 366 user.mbdb_homedir); 367 EXITM(EX_NOINPUT); 368 } 369 user_info.smdbu_id = user.mbdb_uid; 370 user_info.smdbu_group_id = user.mbdb_gid; 371 (void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name, 372 SMDB_MAX_USER_NAME_LEN); 373 } 374 if (name == NULL) 375 { 376 msglog(LOG_ERR, 377 "vacation: can't allocate memory for username.\n"); 378 EXITM(EX_OSERR); 379 } 380 381 if (dbfilename == NULL) 382 dbfilename = VDB; 383 if (msgfilename == NULL) 384 msgfilename = VMSG; 385 386 sff = SFF_CREAT; 387 if (getegid() != getgid()) 388 { 389 /* Allow a set-group-ID vacation binary */ 390 RunAsGid = user_info.smdbu_group_id = getegid(); 391 sff |= SFF_OPENASROOT; 392 } 393 if (getuid() == 0) 394 { 395 /* Allow root to initialize user's vacation databases */ 396 sff |= SFF_OPENASROOT|SFF_ROOTOK; 397 398 /* ... safely */ 399 sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY; 400 } 401 402 403 result = smdb_open_database(&Db, dbfilename, 404 O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0), 405 S_IRUSR|S_IWUSR, sff, 406 SMDB_TYPE_DEFAULT, &user_info, NULL); 407 if (result != SMDBE_OK) 408 { 409 msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename, 410 sm_errstring(result)); 411 EXITM(EX_DATAERR); 412 } 413 414 if (list) 415 { 416 listdb(); 417 (void) Db->smdb_close(Db); 418 exit(EX_OK); 419 } 420 421 if (interval != INTERVAL_UNDEF) 422 setinterval(interval); 423 424 if (initdb && !exclude) 425 { 426 (void) Db->smdb_close(Db); 427 exit(EX_OK); 428 } 429 430 if (exclude) 431 { 432 xclude(smioin); 433 (void) Db->smdb_close(Db); 434 EXITM(EX_OK); 435 } 436 437 if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL) 438 { 439 msglog(LOG_NOTICE, 440 "vacation: can't allocate memory for username.\n"); 441 (void) Db->smdb_close(Db); 442 EXITM(EX_OSERR); 443 } 444 cur->name = name; 445 cur->next = Names; 446 Names = cur; 447 448 result = readheaders(alwaysrespond); 449 if (result == EX_OK && !recent()) 450 { 451 time_t now; 452 453 (void) time(&now); 454 setreply(From, now); 455 (void) Db->smdb_close(Db); 456 sendmessage(name, msgfilename, returnaddr); 457 } 458 else 459 (void) Db->smdb_close(Db); 460 if (result == EX_NOUSER) 461 result = EX_OK; 462 exit(result); 463 } 464 465 /* 466 ** EATMSG -- read stdin till EOF 467 ** 468 ** Parameters: 469 ** none. 470 ** 471 ** Returns: 472 ** nothing. 473 ** 474 */ 475 476 static void 477 eatmsg() 478 { 479 /* 480 ** read the rest of the e-mail and ignore it to avoid problems 481 ** with EPIPE in sendmail 482 */ 483 while (getc(stdin) != EOF) 484 continue; 485 } 486 487 /* 488 ** READHEADERS -- read mail headers 489 ** 490 ** Parameters: 491 ** alwaysrespond -- respond regardless of whether msg is to me 492 ** 493 ** Returns: 494 ** a exit code: NOUSER if no reply, OK if reply, * if error 495 ** 496 ** Side Effects: 497 ** may exit(). 498 ** 499 */ 500 501 static int 502 readheaders(alwaysrespond) 503 bool alwaysrespond; 504 { 505 bool tome, cont; 506 register char *p; 507 register ALIAS *cur; 508 char buf[MAXLINE]; 509 510 cont = false; 511 tome = alwaysrespond; 512 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0 && 513 *buf != '\n') 514 { 515 switch(*buf) 516 { 517 case 'F': /* "From " */ 518 cont = false; 519 if (strncmp(buf, "From ", 5) == 0) 520 { 521 bool quoted = false; 522 523 p = buf + 5; 524 while (*p != '\0') 525 { 526 /* escaped character */ 527 if (*p == '\\') 528 { 529 p++; 530 if (*p == '\0') 531 { 532 msglog(LOG_NOTICE, 533 "vacation: badly formatted \"From \" line.\n"); 534 EXITIT(EX_DATAERR); 535 } 536 } 537 else if (*p == '"') 538 quoted = !quoted; 539 else if (*p == '\r' || *p == '\n') 540 break; 541 else if (*p == ' ' && !quoted) 542 break; 543 p++; 544 } 545 if (quoted) 546 { 547 msglog(LOG_NOTICE, 548 "vacation: badly formatted \"From \" line.\n"); 549 EXITIT(EX_DATAERR); 550 } 551 *p = '\0'; 552 553 /* ok since both strings have MAXLINE length */ 554 if (*From == '\0') 555 (void) sm_strlcpy(From, buf + 5, 556 sizeof From); 557 if ((p = strchr(buf + 5, '\n')) != NULL) 558 *p = '\0'; 559 if (junkmail(buf + 5)) 560 EXITIT(EX_NOUSER); 561 } 562 break; 563 564 case 'P': /* "Precedence:" */ 565 case 'p': 566 cont = false; 567 if (strlen(buf) <= 10 || 568 strncasecmp(buf, "Precedence", 10) != 0 || 569 (buf[10] != ':' && buf[10] != ' ' && 570 buf[10] != '\t')) 571 break; 572 if ((p = strchr(buf, ':')) == NULL) 573 break; 574 while (*++p != '\0' && isascii(*p) && isspace(*p)); 575 if (*p == '\0') 576 break; 577 if (strncasecmp(p, "junk", 4) == 0 || 578 strncasecmp(p, "bulk", 4) == 0 || 579 strncasecmp(p, "list", 4) == 0) 580 EXITIT(EX_NOUSER); 581 break; 582 583 case 'C': /* "Cc:" */ 584 case 'c': 585 if (strncasecmp(buf, "Cc:", 3) != 0) 586 break; 587 cont = true; 588 goto findme; 589 590 case 'T': /* "To:" */ 591 case 't': 592 if (strncasecmp(buf, "To:", 3) != 0) 593 break; 594 cont = true; 595 goto findme; 596 597 default: 598 if (!isascii(*buf) || !isspace(*buf) || !cont || tome) 599 { 600 cont = false; 601 break; 602 } 603 findme: 604 for (cur = Names; 605 !tome && cur != NULL; 606 cur = cur->next) 607 tome = nsearch(cur->name, buf); 608 } 609 } 610 if (!tome) 611 EXITIT(EX_NOUSER); 612 if (*From == '\0') 613 { 614 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n"); 615 EXITIT(EX_DATAERR); 616 } 617 EXITIT(EX_OK); 618 } 619 620 /* 621 ** NSEARCH -- 622 ** do a nice, slow, search of a string for a substring. 623 ** 624 ** Parameters: 625 ** name -- name to search. 626 ** str -- string in which to search. 627 ** 628 ** Returns: 629 ** is name a substring of str? 630 ** 631 */ 632 633 static bool 634 nsearch(name, str) 635 register char *name, *str; 636 { 637 register size_t len; 638 register char *s; 639 640 len = strlen(name); 641 642 for (s = str; *s != '\0'; ++s) 643 { 644 /* 645 ** Check to make sure that the string matches and 646 ** the previous character is not an alphanumeric and 647 ** the next character after the match is not an alphanumeric. 648 ** 649 ** This prevents matching "eric" to "derick" while still 650 ** matching "eric" to "<eric+detail>". 651 */ 652 653 if (tolower(*s) == tolower(*name) && 654 strncasecmp(name, s, len) == 0 && 655 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) && 656 (!isascii(*(s + len)) || !isalnum(*(s + len)))) 657 return true; 658 } 659 return false; 660 } 661 662 /* 663 ** JUNKMAIL -- 664 ** read the header and return if automagic/junk/bulk/list mail 665 ** 666 ** Parameters: 667 ** from -- sender address. 668 ** 669 ** Returns: 670 ** is this some automated/junk/bulk/list mail? 671 ** 672 */ 673 674 struct ignore 675 { 676 char *name; 677 size_t len; 678 }; 679 680 typedef struct ignore IGNORE_T; 681 682 #define MAX_USER_LEN 256 /* maximum length of local part (sender) */ 683 684 /* delimiters for the local part of an address */ 685 #define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+') 686 687 static bool 688 junkmail(from) 689 char *from; 690 { 691 bool quot; 692 char *e; 693 size_t len; 694 IGNORE_T *cur; 695 char sender[MAX_USER_LEN]; 696 static IGNORE_T ignore[] = 697 { 698 { "postmaster", 10 }, 699 { "uucp", 4 }, 700 { "mailer-daemon", 13 }, 701 { "mailer", 6 }, 702 { NULL, 0 } 703 }; 704 705 static IGNORE_T ignorepost[] = 706 { 707 { "-request", 8 }, 708 { "-relay", 6 }, 709 { "-owner", 6 }, 710 { NULL, 0 } 711 }; 712 713 static IGNORE_T ignorepre[] = 714 { 715 { "owner-", 6 }, 716 { NULL, 0 } 717 }; 718 719 /* 720 ** This is mildly amusing, and I'm not positive it's right; trying 721 ** to find the "real" name of the sender, assuming that addresses 722 ** will be some variant of: 723 ** 724 ** From site!site!SENDER%site.domain%site.domain@site.domain 725 */ 726 727 quot = false; 728 e = from; 729 len = 0; 730 while (*e != '\0' && (quot || !isdelim(*e))) 731 { 732 if (*e == '"') 733 { 734 quot = !quot; 735 ++e; 736 continue; 737 } 738 if (*e == '\\') 739 { 740 if (*(++e) == '\0') 741 { 742 /* '\\' at end of string? */ 743 break; 744 } 745 if (len < MAX_USER_LEN) 746 sender[len++] = *e; 747 ++e; 748 continue; 749 } 750 if (*e == '!' && !quot) 751 { 752 len = 0; 753 sender[len] = '\0'; 754 } 755 else 756 if (len < MAX_USER_LEN) 757 sender[len++] = *e; 758 ++e; 759 } 760 if (len < MAX_USER_LEN) 761 sender[len] = '\0'; 762 else 763 sender[MAX_USER_LEN - 1] = '\0'; 764 765 if (len <= 0) 766 return false; 767 #if 0 768 if (quot) 769 return false; /* syntax error... */ 770 #endif /* 0 */ 771 772 /* test prefixes */ 773 for (cur = ignorepre; cur->name != NULL; ++cur) 774 { 775 if (len >= cur->len && 776 strncasecmp(cur->name, sender, cur->len) == 0) 777 return true; 778 } 779 780 /* 781 ** If the name is truncated, don't test the rest. 782 ** We could extract the "tail" of the sender address and 783 ** compare it it ignorepost, however, it seems not worth 784 ** the effort. 785 ** The address surely can't match any entry in ignore[] 786 ** (as long as all of them are shorter than MAX_USER_LEN). 787 */ 788 789 if (len > MAX_USER_LEN) 790 return false; 791 792 /* test full local parts */ 793 for (cur = ignore; cur->name != NULL; ++cur) 794 { 795 if (len == cur->len && 796 strncasecmp(cur->name, sender, cur->len) == 0) 797 return true; 798 } 799 800 /* test postfixes */ 801 for (cur = ignorepost; cur->name != NULL; ++cur) 802 { 803 if (len >= cur->len && 804 strncasecmp(cur->name, e - cur->len - 1, 805 cur->len) == 0) 806 return true; 807 } 808 return false; 809 } 810 811 #define VIT "__VACATION__INTERVAL__TIMER__" 812 813 /* 814 ** RECENT -- 815 ** find out if user has gotten a vacation message recently. 816 ** 817 ** Parameters: 818 ** none. 819 ** 820 ** Returns: 821 ** true iff user has gotten a vacation message recently. 822 ** 823 */ 824 825 static bool 826 recent() 827 { 828 SMDB_DBENT key, data; 829 time_t then, next; 830 bool trydomain = false; 831 int st; 832 char *domain; 833 834 memset(&key, '\0', sizeof key); 835 memset(&data, '\0', sizeof data); 836 837 /* get interval time */ 838 key.data = VIT; 839 key.size = sizeof(VIT); 840 841 st = Db->smdb_get(Db, &key, &data, 0); 842 if (st != SMDBE_OK) 843 next = SECSPERDAY * DAYSPERWEEK; 844 else 845 memmove(&next, data.data, sizeof(next)); 846 847 memset(&data, '\0', sizeof data); 848 849 /* get record for this address */ 850 key.data = From; 851 key.size = strlen(From); 852 853 do 854 { 855 st = Db->smdb_get(Db, &key, &data, 0); 856 if (st == SMDBE_OK) 857 { 858 memmove(&then, data.data, sizeof(then)); 859 if (next == ONLY_ONCE || then == ONLY_ONCE || 860 then + next > time(NULL)) 861 return true; 862 } 863 if ((trydomain = !trydomain) && 864 (domain = strchr(From, '@')) != NULL) 865 { 866 key.data = domain; 867 key.size = strlen(domain); 868 } 869 } while (trydomain); 870 return false; 871 } 872 873 /* 874 ** SETINTERVAL -- 875 ** store the reply interval 876 ** 877 ** Parameters: 878 ** interval -- time interval for replies. 879 ** 880 ** Returns: 881 ** nothing. 882 ** 883 ** Side Effects: 884 ** stores the reply interval in database. 885 */ 886 887 static void 888 setinterval(interval) 889 time_t interval; 890 { 891 SMDB_DBENT key, data; 892 893 memset(&key, '\0', sizeof key); 894 memset(&data, '\0', sizeof data); 895 896 key.data = VIT; 897 key.size = sizeof(VIT); 898 data.data = (char*) &interval; 899 data.size = sizeof(interval); 900 (void) (Db->smdb_put)(Db, &key, &data, 0); 901 } 902 903 /* 904 ** SETREPLY -- 905 ** store that this user knows about the vacation. 906 ** 907 ** Parameters: 908 ** from -- sender address. 909 ** when -- last reply time. 910 ** 911 ** Returns: 912 ** nothing. 913 ** 914 ** Side Effects: 915 ** stores user/time in database. 916 */ 917 918 static void 919 setreply(from, when) 920 char *from; 921 time_t when; 922 { 923 SMDB_DBENT key, data; 924 925 memset(&key, '\0', sizeof key); 926 memset(&data, '\0', sizeof data); 927 928 key.data = from; 929 key.size = strlen(from); 930 data.data = (char*) &when; 931 data.size = sizeof(when); 932 (void) (Db->smdb_put)(Db, &key, &data, 0); 933 } 934 935 /* 936 ** XCLUDE -- 937 ** add users to vacation db so they don't get a reply. 938 ** 939 ** Parameters: 940 ** f -- file pointer with list of address to exclude 941 ** 942 ** Returns: 943 ** nothing. 944 ** 945 ** Side Effects: 946 ** stores users in database. 947 */ 948 949 static void 950 xclude(f) 951 SM_FILE_T *f; 952 { 953 char buf[MAXLINE], *p; 954 955 if (f == NULL) 956 return; 957 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) >= 0) 958 { 959 if ((p = strchr(buf, '\n')) != NULL) 960 *p = '\0'; 961 setreply(buf, ONLY_ONCE); 962 } 963 } 964 965 /* 966 ** SENDMESSAGE -- 967 ** exec sendmail to send the vacation file to sender 968 ** 969 ** Parameters: 970 ** myname -- user name. 971 ** msgfn -- name of file with vacation message. 972 ** sender -- use as sender address 973 ** 974 ** Returns: 975 ** nothing. 976 ** 977 ** Side Effects: 978 ** sends vacation reply. 979 */ 980 981 static void 982 sendmessage(myname, msgfn, sender) 983 char *myname; 984 char *msgfn; 985 char *sender; 986 { 987 SM_FILE_T *mfp, *sfp; 988 int i; 989 int pvect[2]; 990 char *pv[8]; 991 char buf[MAXLINE]; 992 993 mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL); 994 if (mfp == NULL) 995 { 996 if (msgfn[0] == '/') 997 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn); 998 else 999 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", 1000 myname, msgfn); 1001 exit(EX_NOINPUT); 1002 } 1003 if (pipe(pvect) < 0) 1004 { 1005 msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno)); 1006 exit(EX_OSERR); 1007 } 1008 pv[0] = "sendmail"; 1009 pv[1] = "-oi"; 1010 pv[2] = "-f"; 1011 if (sender != NULL) 1012 pv[3] = sender; 1013 else 1014 pv[3] = myname; 1015 pv[4] = "--"; 1016 pv[5] = From; 1017 pv[6] = NULL; 1018 i = fork(); 1019 if (i < 0) 1020 { 1021 msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno)); 1022 exit(EX_OSERR); 1023 } 1024 if (i == 0) 1025 { 1026 (void) dup2(pvect[0], 0); 1027 (void) close(pvect[0]); 1028 (void) close(pvect[1]); 1029 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1030 (void) execv(_PATH_SENDMAIL, pv); 1031 msglog(LOG_ERR, "vacation: can't exec %s: %s", 1032 _PATH_SENDMAIL, sm_errstring(errno)); 1033 exit(EX_UNAVAILABLE); 1034 } 1035 /* check return status of the following calls? XXX */ 1036 (void) close(pvect[0]); 1037 if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 1038 (void *) &(pvect[1]), 1039 SM_IO_WRONLY, NULL)) != NULL) 1040 { 1041 #if _FFR_VAC_WAIT4SM 1042 # ifdef WAITUNION 1043 union wait st; 1044 # else /* WAITUNION */ 1045 auto int st; 1046 # endif /* WAITUNION */ 1047 #endif /* _FFR_VAC_WAIT4SM */ 1048 1049 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From); 1050 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, 1051 "Auto-Submitted: auto-replied\n"); 1052 while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf) >= 0) 1053 (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf); 1054 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1055 (void) sm_io_close(sfp, SM_TIME_DEFAULT); 1056 #if _FFR_VAC_WAIT4SM 1057 (void) wait(&st); 1058 #endif /* _FFR_VAC_WAIT4SM */ 1059 } 1060 else 1061 { 1062 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1063 msglog(LOG_ERR, "vacation: can't open pipe to sendmail"); 1064 exit(EX_UNAVAILABLE); 1065 } 1066 } 1067 1068 static void 1069 usage() 1070 { 1071 msglog(LOG_NOTICE, 1072 "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-j] [-l] [-m msg] [-R returnaddr] [-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n", 1073 getuid()); 1074 exit(EX_USAGE); 1075 } 1076 1077 /* 1078 ** LISTDB -- list the contents of the vacation database 1079 ** 1080 ** Parameters: 1081 ** none. 1082 ** 1083 ** Returns: 1084 ** nothing. 1085 */ 1086 1087 static void 1088 listdb() 1089 { 1090 int result; 1091 time_t t; 1092 SMDB_CURSOR *cursor = NULL; 1093 SMDB_DBENT db_key, db_value; 1094 1095 memset(&db_key, '\0', sizeof db_key); 1096 memset(&db_value, '\0', sizeof db_value); 1097 1098 result = Db->smdb_cursor(Db, &cursor, 0); 1099 if (result != SMDBE_OK) 1100 { 1101 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1102 "vacation: set cursor: %s\n", 1103 sm_errstring(result)); 1104 return; 1105 } 1106 1107 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value, 1108 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) 1109 { 1110 char *timestamp; 1111 1112 /* skip magic VIT entry */ 1113 if (db_key.size == strlen(VIT) + 1 && 1114 strncmp((char *)db_key.data, VIT, 1115 (int)db_key.size - 1) == 0) 1116 continue; 1117 1118 /* skip bogus values */ 1119 if (db_value.size != sizeof t) 1120 { 1121 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1122 "vacation: %.*s invalid time stamp\n", 1123 (int) db_key.size, (char *) db_key.data); 1124 continue; 1125 } 1126 1127 memcpy(&t, db_value.data, sizeof t); 1128 1129 if (db_key.size > 40) 1130 db_key.size = 40; 1131 1132 if (t <= 0) 1133 { 1134 /* must be an exclude */ 1135 timestamp = "(exclusion)\n"; 1136 } 1137 else 1138 { 1139 timestamp = ctime(&t); 1140 } 1141 sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s", 1142 (int) db_key.size, (char *) db_key.data, 1143 timestamp); 1144 1145 memset(&db_key, '\0', sizeof db_key); 1146 memset(&db_value, '\0', sizeof db_value); 1147 } 1148 1149 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) 1150 { 1151 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1152 "vacation: get value at cursor: %s\n", 1153 sm_errstring(result)); 1154 if (cursor != NULL) 1155 { 1156 (void) cursor->smdbc_close(cursor); 1157 cursor = NULL; 1158 } 1159 return; 1160 } 1161 (void) cursor->smdbc_close(cursor); 1162 cursor = NULL; 1163 } 1164 1165 /* 1166 ** DEBUGLOG -- write message to standard error 1167 ** 1168 ** Append a message to the standard error for the convenience of 1169 ** end-users debugging without access to the syslog messages. 1170 ** 1171 ** Parameters: 1172 ** i -- syslog log level 1173 ** fmt -- string format 1174 ** 1175 ** Returns: 1176 ** nothing. 1177 */ 1178 1179 /*VARARGS2*/ 1180 static SYSLOG_RET_T 1181 #ifdef __STDC__ 1182 debuglog(int i, const char *fmt, ...) 1183 #else /* __STDC__ */ 1184 debuglog(i, fmt, va_alist) 1185 int i; 1186 const char *fmt; 1187 va_dcl 1188 #endif /* __STDC__ */ 1189 1190 { 1191 SM_VA_LOCAL_DECL 1192 1193 SM_VA_START(ap, fmt); 1194 sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap); 1195 SM_VA_END(ap); 1196 SYSLOG_RET; 1197 } 1198