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