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