vacation.c revision 66494
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) 1998-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.7 2000/09/05 21:48:45 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 51uid_t RealUid; 52gid_t RealGid; 53char *RealUserName; 54uid_t RunAsUid; 55uid_t RunAsGid; 56char *RunAsUserName; 57int Verbose = 2; 58bool DontInitGroups = FALSE; 59uid_t TrustedUid = 0; 60BITMAP256 DontBlameSendmail; 61 62/* 63** VACATION -- return a message to the sender when on vacation. 64** 65** This program is invoked as a message receiver. It returns a 66** message specified by the user to whomever sent the mail, taking 67** care not to return a message too often to prevent "I am on 68** vacation" loops. 69*/ 70 71#define VDB ".vacation" /* vacation database */ 72#define VMSG ".vacation.msg" /* vacation message */ 73#define SECSPERDAY (60 * 60 * 24) 74#define DAYSPERWEEK 7 75 76#ifndef TRUE 77# define TRUE 1 78# define FALSE 0 79#endif /* ! TRUE */ 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 exit(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 void 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) 360 { 361 result = Db->smdb_close(Db); 362 if (!exclude) 363 exit(EX_OK); 364 } 365 366 if (exclude) 367 { 368 xclude(stdin); 369 result = Db->smdb_close(Db); 370 EXITM(EX_OK); 371 } 372 373 if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL) 374 { 375 msglog(LOG_NOTICE, 376 "vacation: can't allocate memory for username.\n"); 377 EXITM(EX_OSERR); 378 } 379 cur->name = name; 380 cur->next = Names; 381 Names = cur; 382 383 readheaders(); 384 if (!recent()) 385 { 386 time_t now; 387 388 (void) time(&now); 389 setreply(From, now); 390 result = Db->smdb_close(Db); 391 sendmessage(name, msgfilename, emptysender); 392 } 393 else 394 result = Db->smdb_close(Db); 395 exit(EX_OK); 396 /* NOTREACHED */ 397 return EX_OK; 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** nothing. 429** 430** Side Effects: 431** may exit(). 432** 433*/ 434void 435readheaders() 436{ 437 bool tome, cont; 438 register char *p; 439 register ALIAS *cur; 440 char buf[MAXLINE]; 441 extern bool junkmail __P((char *)); 442 extern bool nsearch __P((char *, char *)); 443 444 cont = tome = FALSE; 445 while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') 446 { 447 switch(*buf) 448 { 449 case 'F': /* "From " */ 450 cont = FALSE; 451 if (strncmp(buf, "From ", 5) == 0) 452 { 453 bool quoted = FALSE; 454 455 p = buf + 5; 456 while (*p != '\0') 457 { 458 /* escaped character */ 459 if (*p == '\\') 460 { 461 p++; 462 if (*p == '\0') 463 { 464 msglog(LOG_NOTICE, 465 "vacation: badly formatted \"From \" line.\n"); 466 EXITIT(EX_DATAERR); 467 } 468 } 469 else if (*p == '"') 470 quoted = !quoted; 471 else if (*p == '\r' || *p == '\n') 472 break; 473 else if (*p == ' ' && !quoted) 474 break; 475 p++; 476 } 477 if (quoted) 478 { 479 msglog(LOG_NOTICE, 480 "vacation: badly formatted \"From \" line.\n"); 481 EXITIT(EX_DATAERR); 482 } 483 *p = '\0'; 484 485 /* ok since both strings have MAXLINE length */ 486 if (*From == '\0') 487 (void)strlcpy(From, buf + 5, 488 sizeof From); 489 if ((p = strchr(buf + 5, '\n')) != NULL) 490 *p = '\0'; 491 if (junkmail(buf + 5)) 492 EXITIT(EX_OK); 493 } 494 break; 495 496 case 'P': /* "Precedence:" */ 497 case 'p': 498 cont = FALSE; 499 if (strlen(buf) <= 10 || 500 strncasecmp(buf, "Precedence", 10) != 0 || 501 (buf[10] != ':' && buf[10] != ' ' && 502 buf[10] != '\t')) 503 break; 504 if ((p = strchr(buf, ':')) == NULL) 505 break; 506 while (*++p != '\0' && isascii(*p) && isspace(*p)); 507 if (*p == '\0') 508 break; 509 if (strncasecmp(p, "junk", 4) == 0 || 510 strncasecmp(p, "bulk", 4) == 0 || 511 strncasecmp(p, "list", 4) == 0) 512 EXITIT(EX_OK); 513 break; 514 515 case 'C': /* "Cc:" */ 516 case 'c': 517 if (strncasecmp(buf, "Cc:", 3) != 0) 518 break; 519 cont = TRUE; 520 goto findme; 521 522 case 'T': /* "To:" */ 523 case 't': 524 if (strncasecmp(buf, "To:", 3) != 0) 525 break; 526 cont = TRUE; 527 goto findme; 528 529 default: 530 if (!isascii(*buf) || !isspace(*buf) || !cont || tome) 531 { 532 cont = FALSE; 533 break; 534 } 535findme: 536 for (cur = Names; 537 !tome && cur != NULL; 538 cur = cur->next) 539 tome = nsearch(cur->name, buf); 540 } 541 } 542 if (!tome) 543 EXITIT(EX_OK); 544 if (*From == '\0') 545 { 546 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n"); 547 EXITIT(EX_DATAERR); 548 } 549} 550 551/* 552** NSEARCH -- 553** do a nice, slow, search of a string for a substring. 554** 555** Parameters: 556** name -- name to search. 557** str -- string in which to search. 558** 559** Returns: 560** is name a substring of str? 561** 562*/ 563bool 564nsearch(name, str) 565 register char *name, *str; 566{ 567 register size_t len; 568 register char *s; 569 570 len = strlen(name); 571 572 for (s = str; *s != '\0'; ++s) 573 { 574 /* 575 ** Check to make sure that the string matches and 576 ** the previous character is not an alphanumeric and 577 ** the next character after the match is not an alphanumeric. 578 ** 579 ** This prevents matching "eric" to "derick" while still 580 ** matching "eric" to "<eric+detail>". 581 */ 582 583 if (tolower(*s) == tolower(*name) && 584 strncasecmp(name, s, len) == 0 && 585 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) && 586 (!isascii(*(s + len)) || !isalnum(*(s + len)))) 587 return TRUE; 588 } 589 return FALSE; 590} 591 592/* 593** JUNKMAIL -- 594** read the header and return if automagic/junk/bulk/list mail 595** 596** Parameters: 597** from -- sender address. 598** 599** Returns: 600** is this some automated/junk/bulk/list mail? 601** 602*/ 603bool 604junkmail(from) 605 char *from; 606{ 607 register size_t len; 608 register char *p; 609 register struct ignore *cur; 610 static struct ignore 611 { 612 char *name; 613 size_t len; 614 } ignore[] = 615 { 616 { "-request", 8 }, 617 { "postmaster", 10 }, 618 { "uucp", 4 }, 619 { "mailer-daemon", 13 }, 620 { "mailer", 6 }, 621 { "-relay", 6 }, 622 { NULL, 0 } 623 }; 624 625 /* 626 * This is mildly amusing, and I'm not positive it's right; trying 627 * to find the "real" name of the sender, assuming that addresses 628 * will be some variant of: 629 * 630 * From site!site!SENDER%site.domain%site.domain@site.domain 631 */ 632 if ((p = strchr(from, '%')) == NULL && 633 (p = strchr(from, '@')) == NULL) 634 { 635 if ((p = strrchr(from, '!')) != NULL) 636 ++p; 637 else 638 p = from; 639 for (; *p; ++p) 640 continue; 641 } 642 len = p - from; 643 for (cur = ignore; cur->name != NULL; ++cur) 644 { 645 if (len >= cur->len && 646 strncasecmp(cur->name, p - cur->len, cur->len) == 0) 647 return TRUE; 648 } 649 return FALSE; 650} 651 652#define VIT "__VACATION__INTERVAL__TIMER__" 653 654/* 655** RECENT -- 656** find out if user has gotten a vacation message recently. 657** 658** Parameters: 659** none. 660** 661** Returns: 662** TRUE iff user has gotten a vacation message recently. 663** 664*/ 665bool 666recent() 667{ 668 SMDB_DBENT key, data; 669 time_t then, next; 670 bool trydomain = FALSE; 671 int st; 672 char *domain; 673 674 memset(&key, '\0', sizeof key); 675 memset(&data, '\0', sizeof data); 676 677 /* get interval time */ 678 key.data.data = VIT; 679 key.data.size = sizeof(VIT); 680 681 st = Db->smdb_get(Db, &key, &data, 0); 682 if (st != SMDBE_OK) 683 next = SECSPERDAY * DAYSPERWEEK; 684 else 685 memmove(&next, data.data.data, sizeof(next)); 686 687 memset(&data, '\0', sizeof data); 688 689 /* get record for this address */ 690 key.data.data = From; 691 key.data.size = strlen(From); 692 693 do 694 { 695 st = Db->smdb_get(Db, &key, &data, 0); 696 if (st == SMDBE_OK) 697 { 698 memmove(&then, data.data.data, sizeof(then)); 699 if (next == ONLY_ONCE || then == ONLY_ONCE || 700 then + next > time(NULL)) 701 return TRUE; 702 } 703 if ((trydomain = !trydomain) && 704 (domain = strchr(From, '@')) != NULL) 705 { 706 key.data.data = domain; 707 key.data.size = strlen(domain); 708 } 709 } while (trydomain); 710 return FALSE; 711} 712 713/* 714** SETINTERVAL -- 715** store the reply interval 716** 717** Parameters: 718** interval -- time interval for replies. 719** 720** Returns: 721** nothing. 722** 723** Side Effects: 724** stores the reply interval in database. 725*/ 726void 727setinterval(interval) 728 time_t interval; 729{ 730 SMDB_DBENT key, data; 731 732 memset(&key, '\0', sizeof key); 733 memset(&data, '\0', sizeof data); 734 735 key.data.data = VIT; 736 key.data.size = sizeof(VIT); 737 data.data.data = (char*) &interval; 738 data.data.size = sizeof(interval); 739 (void)(Db->smdb_put)(Db, &key, &data, 0); 740} 741 742/* 743** SETREPLY -- 744** store that this user knows about the vacation. 745** 746** Parameters: 747** from -- sender address. 748** when -- last reply time. 749** 750** Returns: 751** nothing. 752** 753** Side Effects: 754** stores user/time in database. 755*/ 756void 757setreply(from, when) 758 char *from; 759 time_t when; 760{ 761 SMDB_DBENT key, data; 762 763 memset(&key, '\0', sizeof key); 764 memset(&data, '\0', sizeof data); 765 766 key.data.data = from; 767 key.data.size = strlen(from); 768 data.data.data = (char*) &when; 769 data.data.size = sizeof(when); 770 (void)(Db->smdb_put)(Db, &key, &data, 0); 771} 772 773/* 774** XCLUDE -- 775** add users to vacation db so they don't get a reply. 776** 777** Parameters: 778** f -- file pointer with list of address to exclude 779** 780** Returns: 781** nothing. 782** 783** Side Effects: 784** stores users in database. 785*/ 786void 787xclude(f) 788 FILE *f; 789{ 790 char buf[MAXLINE], *p; 791 792 if (f == NULL) 793 return; 794 while (fgets(buf, sizeof buf, f)) 795 { 796 if ((p = strchr(buf, '\n')) != NULL) 797 *p = '\0'; 798 setreply(buf, ONLY_ONCE); 799 } 800} 801 802/* 803** SENDMESSAGE -- 804** exec sendmail to send the vacation file to sender 805** 806** Parameters: 807** myname -- user name. 808** msgfn -- name of file with vacation message. 809** emptysender -- use <> as sender address? 810** 811** Returns: 812** nothing. 813** 814** Side Effects: 815** sends vacation reply. 816*/ 817void 818sendmessage(myname, msgfn, emptysender) 819 char *myname; 820 char *msgfn; 821 bool emptysender; 822{ 823 FILE *mfp, *sfp; 824 int i; 825 int pvect[2]; 826 char buf[MAXLINE]; 827 828 mfp = fopen(msgfn, "r"); 829 if (mfp == NULL) 830 { 831 if (msgfn[0] == '/') 832 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn); 833 else 834 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", 835 myname, msgfn); 836 exit(EX_NOINPUT); 837 } 838 if (pipe(pvect) < 0) 839 { 840 msglog(LOG_ERR, "vacation: pipe: %s", errstring(errno)); 841 exit(EX_OSERR); 842 } 843 i = fork(); 844 if (i < 0) 845 { 846 msglog(LOG_ERR, "vacation: fork: %s", errstring(errno)); 847 exit(EX_OSERR); 848 } 849 if (i == 0) 850 { 851 (void) dup2(pvect[0], 0); 852 (void) close(pvect[0]); 853 (void) close(pvect[1]); 854 (void) fclose(mfp); 855 if (emptysender) 856 myname = "<>"; 857 (void) execl(_PATH_SENDMAIL, "sendmail", "-oi", 858 "-f", myname, "--", From, NULL); 859 msglog(LOG_ERR, "vacation: can't exec %s: %s", 860 _PATH_SENDMAIL, errstring(errno)); 861 exit(EX_UNAVAILABLE); 862 } 863 /* check return status of the following calls? XXX */ 864 (void) close(pvect[0]); 865 if ((sfp = fdopen(pvect[1], "w")) != NULL) 866 { 867 (void) fprintf(sfp, "To: %s\n", From); 868 (void) fprintf(sfp, "Auto-Submitted: auto-generated\n"); 869 while (fgets(buf, sizeof buf, mfp)) 870 (void) fputs(buf, sfp); 871 (void) fclose(mfp); 872 (void) fclose(sfp); 873 } 874 else 875 { 876 (void) fclose(mfp); 877 msglog(LOG_ERR, "vacation: can't open pipe to sendmail"); 878 exit(EX_UNAVAILABLE); 879 } 880} 881 882void 883usage() 884{ 885 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", 886 getuid(), 887#if _FFR_DEBUG 888 " [-d]", 889#else /* _FFR_DEBUG */ 890 "", 891#endif /* _FFR_DEBUG */ 892#if _FFR_LISTDB 893 " [-l]" 894#else /* _FFR_LISTDB */ 895 "" 896#endif /* _FFR_LISTDB */ 897 ); 898 exit(EX_USAGE); 899} 900 901#if _FFR_LISTDB 902/* 903** LISTDB -- list the contents of the vacation database 904** 905** Parameters: 906** none. 907** 908** Returns: 909** nothing. 910*/ 911 912static void 913listdb() 914{ 915 int result; 916 time_t t; 917 SMDB_CURSOR *cursor = NULL; 918 SMDB_DBENT db_key, db_value; 919 920 memset(&db_key, '\0', sizeof db_key); 921 memset(&db_value, '\0', sizeof db_value); 922 923 result = Db->smdb_cursor(Db, &cursor, 0); 924 if (result != SMDBE_OK) 925 { 926 fprintf(stderr, "vacation: set cursor: %s\n", 927 errstring(result)); 928 return; 929 } 930 931 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value, 932 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) 933 { 934 /* skip magic VIT entry */ 935 if ((int)db_key.data.size -1 == strlen(VIT) && 936 strncmp((char *)db_key.data.data, VIT, 937 (int)db_key.data.size - 1) == 0) 938 continue; 939 940 /* skip bogus values */ 941 if (db_value.data.size != sizeof t) 942 { 943 fprintf(stderr, "vacation: %.*s invalid time stamp\n", 944 (int) db_key.data.size, 945 (char *) db_key.data.data); 946 continue; 947 } 948 949 memcpy(&t, db_value.data.data, sizeof t); 950 951 if (db_key.data.size > 40) 952 db_key.data.size = 40; 953 954 printf("%-40.*s %-10s", 955 (int) db_key.data.size, (char *) db_key.data.data, 956 ctime(&t)); 957 958 memset(&db_key, '\0', sizeof db_key); 959 memset(&db_value, '\0', sizeof db_value); 960 } 961 962 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) 963 { 964 fprintf(stderr, "vacation: get value at cursor: %s\n", 965 errstring(result)); 966 if (cursor != NULL) 967 { 968 (void) cursor->smdbc_close(cursor); 969 cursor = NULL; 970 } 971 return; 972 } 973 (void) cursor->smdbc_close(cursor); 974 cursor = NULL; 975} 976#endif /* _FFR_LISTDB */ 977 978#if _FFR_DEBUG 979/* 980** DEBUGLOG -- write message to standard error 981** 982** Append a message to the standard error for the convenience of 983** end-users debugging without access to the syslog messages. 984** 985** Parameters: 986** i -- syslog log level 987** fmt -- string format 988** 989** Returns: 990** nothing. 991*/ 992 993/*VARARGS2*/ 994static void 995#ifdef __STDC__ 996debuglog(int i, const char *fmt, ...) 997#else /* __STDC__ */ 998debuglog(i, fmt, va_alist) 999 int i; 1000 const char *fmt; 1001 va_dcl 1002#endif /* __STDC__ */ 1003 1004{ 1005 VA_LOCAL_DECL 1006 1007 VA_START(fmt); 1008 vfprintf(stderr, fmt, ap); 1009 VA_END; 1010} 1011#endif /* _FFR_DEBUG */ 1012 1013/*VARARGS1*/ 1014void 1015#ifdef __STDC__ 1016message(const char *msg, ...) 1017#else /* __STDC__ */ 1018message(msg, va_alist) 1019 const char *msg; 1020 va_dcl 1021#endif /* __STDC__ */ 1022{ 1023 const char *m; 1024 VA_LOCAL_DECL 1025 1026 m = msg; 1027 if (isascii(m[0]) && isdigit(m[0]) && 1028 isascii(m[1]) && isdigit(m[1]) && 1029 isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') 1030 m += 4; 1031 VA_START(msg); 1032 (void) vfprintf(stderr, m, ap); 1033 VA_END; 1034 (void) fprintf(stderr, "\n"); 1035} 1036 1037/*VARARGS1*/ 1038void 1039#ifdef __STDC__ 1040syserr(const char *msg, ...) 1041#else /* __STDC__ */ 1042syserr(msg, va_alist) 1043 const char *msg; 1044 va_dcl 1045#endif /* __STDC__ */ 1046{ 1047 const char *m; 1048 VA_LOCAL_DECL 1049 1050 m = msg; 1051 if (isascii(m[0]) && isdigit(m[0]) && 1052 isascii(m[1]) && isdigit(m[1]) && 1053 isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') 1054 m += 4; 1055 VA_START(msg); 1056 (void) vfprintf(stderr, m, ap); 1057 VA_END; 1058 (void) fprintf(stderr, "\n"); 1059} 1060 1061void 1062dumpfd(fd, printclosed, logit) 1063 int fd; 1064 bool printclosed; 1065 bool logit; 1066{ 1067 return; 1068} 1069