1 /* 2 * Copyright (c) 1999-2001 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 8.131 2001/12/12 00:02:42 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, NULL); 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 374 result = smdb_open_database(&Db, dbfilename, 375 O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), 376 S_IRUSR|S_IWUSR, sff, 377 SMDB_TYPE_DEFAULT, &user_info, NULL); 378 if (result != SMDBE_OK) 379 { 380 msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename, 381 sm_errstring(result)); 382 EXITM(EX_DATAERR); 383 } 384 385 if (lflag) 386 { 387 listdb(); 388 (void) Db->smdb_close(Db); 389 exit(EX_OK); 390 } 391 392 if (interval != INTERVAL_UNDEF) 393 setinterval(interval); 394 395 if (iflag && !exclude) 396 { 397 (void) Db->smdb_close(Db); 398 exit(EX_OK); 399 } 400 401 if (exclude) 402 { 403 xclude(smioin); 404 (void) Db->smdb_close(Db); 405 EXITM(EX_OK); 406 } 407 408 if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL) 409 { 410 msglog(LOG_NOTICE, 411 "vacation: can't allocate memory for username.\n"); 412 (void) Db->smdb_close(Db); 413 EXITM(EX_OSERR); 414 } 415 cur->name = name; 416 cur->next = Names; 417 Names = cur; 418 419 result = readheaders(); 420 if (result == EX_OK && !recent()) 421 { 422 time_t now; 423 424 (void) time(&now); 425 setreply(From, now); 426 (void) Db->smdb_close(Db); 427 sendmessage(name, msgfilename, returnaddr); 428 } 429 else 430 (void) Db->smdb_close(Db); 431 if (result == EX_NOUSER) 432 result = EX_OK; 433 exit(result); 434 } 435 436 /* 437 ** EATMSG -- read stdin till EOF 438 ** 439 ** Parameters: 440 ** none. 441 ** 442 ** Returns: 443 ** nothing. 444 ** 445 */ 446 447 static void 448 eatmsg() 449 { 450 /* 451 ** read the rest of the e-mail and ignore it to avoid problems 452 ** with EPIPE in sendmail 453 */ 454 while (getc(stdin) != EOF) 455 continue; 456 } 457 458 /* 459 ** READHEADERS -- read mail headers 460 ** 461 ** Parameters: 462 ** none. 463 ** 464 ** Returns: 465 ** a exit code: NOUSER if no reply, OK if reply, * if error 466 ** 467 ** Side Effects: 468 ** may exit(). 469 ** 470 */ 471 472 int 473 readheaders() 474 { 475 bool tome, cont; 476 register char *p; 477 register ALIAS *cur; 478 char buf[MAXLINE]; 479 extern bool junkmail __P((char *)); 480 extern bool nsearch __P((char *, char *)); 481 482 cont = tome = false; 483 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) && 484 *buf != '\n') 485 { 486 switch(*buf) 487 { 488 case 'F': /* "From " */ 489 cont = false; 490 if (strncmp(buf, "From ", 5) == 0) 491 { 492 bool quoted = false; 493 494 p = buf + 5; 495 while (*p != '\0') 496 { 497 /* escaped character */ 498 if (*p == '\\') 499 { 500 p++; 501 if (*p == '\0') 502 { 503 msglog(LOG_NOTICE, 504 "vacation: badly formatted \"From \" line.\n"); 505 EXITIT(EX_DATAERR); 506 } 507 } 508 else if (*p == '"') 509 quoted = !quoted; 510 else if (*p == '\r' || *p == '\n') 511 break; 512 else if (*p == ' ' && !quoted) 513 break; 514 p++; 515 } 516 if (quoted) 517 { 518 msglog(LOG_NOTICE, 519 "vacation: badly formatted \"From \" line.\n"); 520 EXITIT(EX_DATAERR); 521 } 522 *p = '\0'; 523 524 /* ok since both strings have MAXLINE length */ 525 if (*From == '\0') 526 (void) sm_strlcpy(From, buf + 5, 527 sizeof From); 528 if ((p = strchr(buf + 5, '\n')) != NULL) 529 *p = '\0'; 530 if (junkmail(buf + 5)) 531 EXITIT(EX_NOUSER); 532 } 533 break; 534 535 case 'P': /* "Precedence:" */ 536 case 'p': 537 cont = false; 538 if (strlen(buf) <= 10 || 539 strncasecmp(buf, "Precedence", 10) != 0 || 540 (buf[10] != ':' && buf[10] != ' ' && 541 buf[10] != '\t')) 542 break; 543 if ((p = strchr(buf, ':')) == NULL) 544 break; 545 while (*++p != '\0' && isascii(*p) && isspace(*p)); 546 if (*p == '\0') 547 break; 548 if (strncasecmp(p, "junk", 4) == 0 || 549 strncasecmp(p, "bulk", 4) == 0 || 550 strncasecmp(p, "list", 4) == 0) 551 EXITIT(EX_NOUSER); 552 break; 553 554 case 'C': /* "Cc:" */ 555 case 'c': 556 if (strncasecmp(buf, "Cc:", 3) != 0) 557 break; 558 cont = true; 559 goto findme; 560 561 case 'T': /* "To:" */ 562 case 't': 563 if (strncasecmp(buf, "To:", 3) != 0) 564 break; 565 cont = true; 566 goto findme; 567 568 default: 569 if (!isascii(*buf) || !isspace(*buf) || !cont || tome) 570 { 571 cont = false; 572 break; 573 } 574 findme: 575 for (cur = Names; 576 !tome && cur != NULL; 577 cur = cur->next) 578 tome = nsearch(cur->name, buf); 579 } 580 } 581 if (!tome) 582 EXITIT(EX_NOUSER); 583 if (*From == '\0') 584 { 585 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n"); 586 EXITIT(EX_DATAERR); 587 } 588 EXITIT(EX_OK); 589 } 590 591 /* 592 ** NSEARCH -- 593 ** do a nice, slow, search of a string for a substring. 594 ** 595 ** Parameters: 596 ** name -- name to search. 597 ** str -- string in which to search. 598 ** 599 ** Returns: 600 ** is name a substring of str? 601 ** 602 */ 603 604 bool 605 nsearch(name, str) 606 register char *name, *str; 607 { 608 register size_t len; 609 register char *s; 610 611 len = strlen(name); 612 613 for (s = str; *s != '\0'; ++s) 614 { 615 /* 616 ** Check to make sure that the string matches and 617 ** the previous character is not an alphanumeric and 618 ** the next character after the match is not an alphanumeric. 619 ** 620 ** This prevents matching "eric" to "derick" while still 621 ** matching "eric" to "<eric+detail>". 622 */ 623 624 if (tolower(*s) == tolower(*name) && 625 strncasecmp(name, s, len) == 0 && 626 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) && 627 (!isascii(*(s + len)) || !isalnum(*(s + len)))) 628 return true; 629 } 630 return false; 631 } 632 633 /* 634 ** JUNKMAIL -- 635 ** read the header and return if automagic/junk/bulk/list mail 636 ** 637 ** Parameters: 638 ** from -- sender address. 639 ** 640 ** Returns: 641 ** is this some automated/junk/bulk/list mail? 642 ** 643 */ 644 645 struct ignore 646 { 647 char *name; 648 size_t len; 649 }; 650 651 typedef struct ignore IGNORE_T; 652 653 #define MAX_USER_LEN 256 /* maximum length of local part (sender) */ 654 655 /* delimiters for the local part of an address */ 656 #define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+') 657 658 bool 659 junkmail(from) 660 char *from; 661 { 662 bool quot; 663 char *e; 664 size_t len; 665 IGNORE_T *cur; 666 char sender[MAX_USER_LEN]; 667 static IGNORE_T ignore[] = 668 { 669 { "postmaster", 10 }, 670 { "uucp", 4 }, 671 { "mailer-daemon", 13 }, 672 { "mailer", 6 }, 673 { NULL, 0 } 674 }; 675 676 static IGNORE_T ignorepost[] = 677 { 678 { "-request", 8 }, 679 { "-relay", 6 }, 680 { "-owner", 6 }, 681 { NULL, 0 } 682 }; 683 684 static IGNORE_T ignorepre[] = 685 { 686 { "owner-", 6 }, 687 { NULL, 0 } 688 }; 689 690 /* 691 ** This is mildly amusing, and I'm not positive it's right; trying 692 ** to find the "real" name of the sender, assuming that addresses 693 ** will be some variant of: 694 ** 695 ** From site!site!SENDER%site.domain%site.domain@site.domain 696 */ 697 698 quot = false; 699 e = from; 700 len = 0; 701 while (*e != '\0' && (quot || !isdelim(*e))) 702 { 703 if (*e == '"') 704 { 705 quot = !quot; 706 ++e; 707 continue; 708 } 709 if (*e == '\\') 710 { 711 if (*(++e) == '\0') 712 { 713 /* '\\' at end of string? */ 714 break; 715 } 716 if (len < MAX_USER_LEN) 717 sender[len++] = *e; 718 ++e; 719 continue; 720 } 721 if (*e == '!' && !quot) 722 { 723 len = 0; 724 sender[len] = '\0'; 725 } 726 else 727 if (len < MAX_USER_LEN) 728 sender[len++] = *e; 729 ++e; 730 } 731 if (len < MAX_USER_LEN) 732 sender[len] = '\0'; 733 else 734 sender[MAX_USER_LEN - 1] = '\0'; 735 736 if (len <= 0) 737 return false; 738 #if 0 739 if (quot) 740 return false; /* syntax error... */ 741 #endif /* 0 */ 742 743 /* test prefixes */ 744 for (cur = ignorepre; cur->name != NULL; ++cur) 745 { 746 if (len >= cur->len && 747 strncasecmp(cur->name, sender, cur->len) == 0) 748 return true; 749 } 750 751 /* 752 ** If the name is truncated, don't test the rest. 753 ** We could extract the "tail" of the sender address and 754 ** compare it it ignorepost, however, it seems not worth 755 ** the effort. 756 ** The address surely can't match any entry in ignore[] 757 ** (as long as all of them are shorter than MAX_USER_LEN). 758 */ 759 760 if (len > MAX_USER_LEN) 761 return false; 762 763 /* test full local parts */ 764 for (cur = ignore; cur->name != NULL; ++cur) 765 { 766 if (len == cur->len && 767 strncasecmp(cur->name, sender, cur->len) == 0) 768 return true; 769 } 770 771 /* test postfixes */ 772 for (cur = ignorepost; cur->name != NULL; ++cur) 773 { 774 if (len >= cur->len && 775 strncasecmp(cur->name, e - cur->len - 1, 776 cur->len) == 0) 777 return true; 778 } 779 return false; 780 } 781 782 #define VIT "__VACATION__INTERVAL__TIMER__" 783 784 /* 785 ** RECENT -- 786 ** find out if user has gotten a vacation message recently. 787 ** 788 ** Parameters: 789 ** none. 790 ** 791 ** Returns: 792 ** true iff user has gotten a vacation message recently. 793 ** 794 */ 795 796 bool 797 recent() 798 { 799 SMDB_DBENT key, data; 800 time_t then, next; 801 bool trydomain = false; 802 int st; 803 char *domain; 804 805 memset(&key, '\0', sizeof key); 806 memset(&data, '\0', sizeof data); 807 808 /* get interval time */ 809 key.data = VIT; 810 key.size = sizeof(VIT); 811 812 st = Db->smdb_get(Db, &key, &data, 0); 813 if (st != SMDBE_OK) 814 next = SECSPERDAY * DAYSPERWEEK; 815 else 816 memmove(&next, data.data, sizeof(next)); 817 818 memset(&data, '\0', sizeof data); 819 820 /* get record for this address */ 821 key.data = From; 822 key.size = strlen(From); 823 824 do 825 { 826 st = Db->smdb_get(Db, &key, &data, 0); 827 if (st == SMDBE_OK) 828 { 829 memmove(&then, data.data, sizeof(then)); 830 if (next == ONLY_ONCE || then == ONLY_ONCE || 831 then + next > time(NULL)) 832 return true; 833 } 834 if ((trydomain = !trydomain) && 835 (domain = strchr(From, '@')) != NULL) 836 { 837 key.data = domain; 838 key.size = strlen(domain); 839 } 840 } while (trydomain); 841 return false; 842 } 843 844 /* 845 ** SETINTERVAL -- 846 ** store the reply interval 847 ** 848 ** Parameters: 849 ** interval -- time interval for replies. 850 ** 851 ** Returns: 852 ** nothing. 853 ** 854 ** Side Effects: 855 ** stores the reply interval in database. 856 */ 857 858 void 859 setinterval(interval) 860 time_t interval; 861 { 862 SMDB_DBENT key, data; 863 864 memset(&key, '\0', sizeof key); 865 memset(&data, '\0', sizeof data); 866 867 key.data = VIT; 868 key.size = sizeof(VIT); 869 data.data = (char*) &interval; 870 data.size = sizeof(interval); 871 (void) (Db->smdb_put)(Db, &key, &data, 0); 872 } 873 874 /* 875 ** SETREPLY -- 876 ** store that this user knows about the vacation. 877 ** 878 ** Parameters: 879 ** from -- sender address. 880 ** when -- last reply time. 881 ** 882 ** Returns: 883 ** nothing. 884 ** 885 ** Side Effects: 886 ** stores user/time in database. 887 */ 888 889 void 890 setreply(from, when) 891 char *from; 892 time_t when; 893 { 894 SMDB_DBENT key, data; 895 896 memset(&key, '\0', sizeof key); 897 memset(&data, '\0', sizeof data); 898 899 key.data = from; 900 key.size = strlen(from); 901 data.data = (char*) &when; 902 data.size = sizeof(when); 903 (void) (Db->smdb_put)(Db, &key, &data, 0); 904 } 905 906 /* 907 ** XCLUDE -- 908 ** add users to vacation db so they don't get a reply. 909 ** 910 ** Parameters: 911 ** f -- file pointer with list of address to exclude 912 ** 913 ** Returns: 914 ** nothing. 915 ** 916 ** Side Effects: 917 ** stores users in database. 918 */ 919 920 void 921 xclude(f) 922 SM_FILE_T *f; 923 { 924 char buf[MAXLINE], *p; 925 926 if (f == NULL) 927 return; 928 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf)) 929 { 930 if ((p = strchr(buf, '\n')) != NULL) 931 *p = '\0'; 932 setreply(buf, ONLY_ONCE); 933 } 934 } 935 936 /* 937 ** SENDMESSAGE -- 938 ** exec sendmail to send the vacation file to sender 939 ** 940 ** Parameters: 941 ** myname -- user name. 942 ** msgfn -- name of file with vacation message. 943 ** sender -- use as sender address 944 ** 945 ** Returns: 946 ** nothing. 947 ** 948 ** Side Effects: 949 ** sends vacation reply. 950 */ 951 952 void 953 sendmessage(myname, msgfn, sender) 954 char *myname; 955 char *msgfn; 956 char *sender; 957 { 958 SM_FILE_T *mfp, *sfp; 959 int i; 960 int pvect[2]; 961 char *pv[8]; 962 char buf[MAXLINE]; 963 964 mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL); 965 if (mfp == NULL) 966 { 967 if (msgfn[0] == '/') 968 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn); 969 else 970 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", 971 myname, msgfn); 972 exit(EX_NOINPUT); 973 } 974 if (pipe(pvect) < 0) 975 { 976 msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno)); 977 exit(EX_OSERR); 978 } 979 pv[0] = "sendmail"; 980 pv[1] = "-oi"; 981 pv[2] = "-f"; 982 if (sender != NULL) 983 pv[3] = sender; 984 else 985 pv[3] = myname; 986 pv[4] = "--"; 987 pv[5] = From; 988 pv[6] = NULL; 989 i = fork(); 990 if (i < 0) 991 { 992 msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno)); 993 exit(EX_OSERR); 994 } 995 if (i == 0) 996 { 997 (void) dup2(pvect[0], 0); 998 (void) close(pvect[0]); 999 (void) close(pvect[1]); 1000 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1001 (void) execv(_PATH_SENDMAIL, pv); 1002 msglog(LOG_ERR, "vacation: can't exec %s: %s", 1003 _PATH_SENDMAIL, sm_errstring(errno)); 1004 exit(EX_UNAVAILABLE); 1005 } 1006 /* check return status of the following calls? XXX */ 1007 (void) close(pvect[0]); 1008 if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 1009 (void *) &(pvect[1]), 1010 SM_IO_WRONLY, NULL)) != NULL) 1011 { 1012 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From); 1013 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, 1014 "Auto-Submitted: auto-replied\n"); 1015 while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf)) 1016 (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf); 1017 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1018 (void) sm_io_close(sfp, SM_TIME_DEFAULT); 1019 } 1020 else 1021 { 1022 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1023 msglog(LOG_ERR, "vacation: can't open pipe to sendmail"); 1024 exit(EX_UNAVAILABLE); 1025 } 1026 } 1027 1028 void 1029 usage() 1030 { 1031 char *retusage; 1032 1033 #if _FFR_RETURN_ADDR 1034 retusage = "[-R returnaddr] "; 1035 #else /* _FFR_RETURN_ADDR */ 1036 retusage = ""; 1037 #endif /* _FFR_RETURN_ADDR */ 1038 1039 msglog(LOG_NOTICE, 1040 "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", 1041 getuid(), retusage); 1042 exit(EX_USAGE); 1043 } 1044 1045 /* 1046 ** LISTDB -- list the contents of the vacation database 1047 ** 1048 ** Parameters: 1049 ** none. 1050 ** 1051 ** Returns: 1052 ** nothing. 1053 */ 1054 1055 static void 1056 listdb() 1057 { 1058 int result; 1059 time_t t; 1060 SMDB_CURSOR *cursor = NULL; 1061 SMDB_DBENT db_key, db_value; 1062 1063 memset(&db_key, '\0', sizeof db_key); 1064 memset(&db_value, '\0', sizeof db_value); 1065 1066 result = Db->smdb_cursor(Db, &cursor, 0); 1067 if (result != SMDBE_OK) 1068 { 1069 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1070 "vacation: set cursor: %s\n", 1071 sm_errstring(result)); 1072 return; 1073 } 1074 1075 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value, 1076 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) 1077 { 1078 /* skip magic VIT entry */ 1079 if ((int)db_key.size - 1 == strlen(VIT) && 1080 strncmp((char *)db_key.data, VIT, 1081 (int)db_key.size - 1) == 0) 1082 continue; 1083 1084 /* skip bogus values */ 1085 if (db_value.size != sizeof t) 1086 { 1087 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1088 "vacation: %.*s invalid time stamp\n", 1089 (int) db_key.size, (char *) db_key.data); 1090 continue; 1091 } 1092 1093 memcpy(&t, db_value.data, sizeof t); 1094 1095 if (db_key.size > 40) 1096 db_key.size = 40; 1097 1098 sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s", 1099 (int) db_key.size, (char *) db_key.data, 1100 ctime(&t)); 1101 1102 memset(&db_key, '\0', sizeof db_key); 1103 memset(&db_value, '\0', sizeof db_value); 1104 } 1105 1106 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) 1107 { 1108 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1109 "vacation: get value at cursor: %s\n", 1110 sm_errstring(result)); 1111 if (cursor != NULL) 1112 { 1113 (void) cursor->smdbc_close(cursor); 1114 cursor = NULL; 1115 } 1116 return; 1117 } 1118 (void) cursor->smdbc_close(cursor); 1119 cursor = NULL; 1120 } 1121 1122 /* 1123 ** DEBUGLOG -- write message to standard error 1124 ** 1125 ** Append a message to the standard error for the convenience of 1126 ** end-users debugging without access to the syslog messages. 1127 ** 1128 ** Parameters: 1129 ** i -- syslog log level 1130 ** fmt -- string format 1131 ** 1132 ** Returns: 1133 ** nothing. 1134 */ 1135 1136 /*VARARGS2*/ 1137 static SYSLOG_RET_T 1138 #ifdef __STDC__ 1139 debuglog(int i, const char *fmt, ...) 1140 #else /* __STDC__ */ 1141 debuglog(i, fmt, va_alist) 1142 int i; 1143 const char *fmt; 1144 va_dcl 1145 #endif /* __STDC__ */ 1146 1147 { 1148 SM_VA_LOCAL_DECL 1149 1150 SM_VA_START(ap, fmt); 1151 sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap); 1152 SM_VA_END(ap); 1153 SYSLOG_RET; 1154 } 1155