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