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