vacation.c revision 203004
1251881Speter/* 2251881Speter * Copyright (c) 1999-2002, 2009 Sendmail, Inc. and its suppliers. 3251881Speter * All rights reserved. 4251881Speter * Copyright (c) 1983, 1987, 1993 5251881Speter * The Regents of the University of California. All rights reserved. 6251881Speter * Copyright (c) 1983 Eric P. Allman. All rights reserved. 7251881Speter * 8251881Speter * By using this file, you agree to the terms and conditions set 9251881Speter * forth in the LICENSE file which can be found at the top level of 10251881Speter * the sendmail distribution. 11251881Speter * 12251881Speter */ 13251881Speter 14251881Speter#include <sm/gen.h> 15251881Speter 16251881SpeterSM_IDSTR(copyright, 17251881Speter"@(#) Copyright (c) 1999-2002, 2009 Sendmail, Inc. and its suppliers.\n\ 18251881Speter All rights reserved.\n\ 19251881Speter Copyright (c) 1983, 1987, 1993\n\ 20251881Speter The Regents of the University of California. All rights reserved.\n\ 21251881Speter Copyright (c) 1983 Eric P. Allman. All rights reserved.\n") 22251881Speter 23251881SpeterSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.146 2009/08/07 21:28:39 ca Exp $") 24251881Speter 25251881Speter 26251881Speter#include <ctype.h> 27251881Speter#include <stdlib.h> 28251881Speter#include <syslog.h> 29251881Speter#include <time.h> 30251881Speter#include <unistd.h> 31251881Speter#ifdef EX_OK 32251881Speter# undef EX_OK /* unistd.h may have another use for this */ 33251881Speter#endif /* EX_OK */ 34251881Speter#include <sm/sysexits.h> 35251881Speter 36251881Speter#include <sm/cf.h> 37251881Speter#include <sm/mbdb.h> 38251881Speter#include "sendmail/sendmail.h" 39251881Speter#include <sendmail/pathnames.h> 40251881Speter#include "libsmdb/smdb.h" 41251881Speter 42251881Speter#define ONLY_ONCE ((time_t) 0) /* send at most one reply */ 43251881Speter#define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */ 44251881Speter 45251881Speteruid_t RealUid; 46251881Spetergid_t RealGid; 47251881Speterchar *RealUserName; 48251881Speteruid_t RunAsUid; 49251881Spetergid_t RunAsGid; 50251881Speterchar *RunAsUserName; 51251881Speterint Verbose = 2; 52251881Speterbool DontInitGroups = false; 53251881Speteruid_t TrustedUid = 0; 54251881SpeterBITMAP256 DontBlameSendmail; 55251881Speter 56251881Speterstatic int readheaders __P((bool)); 57251881Speterstatic bool junkmail __P((char *)); 58251881Speterstatic bool nsearch __P((char *, char *)); 59251881Speterstatic void usage __P((void)); 60251881Speterstatic void setinterval __P((time_t)); 61251881Speterstatic bool recent __P((void)); 62251881Speterstatic void setreply __P((char *, time_t)); 63251881Speterstatic void sendmessage __P((char *, char *, char *)); 64251881Speterstatic void xclude __P((SM_FILE_T *)); 65251881Speter 66251881Speter/* 67251881Speter** VACATION -- return a message to the sender when on vacation. 68251881Speter** 69251881Speter** This program is invoked as a message receiver. It returns a 70251881Speter** message specified by the user to whomever sent the mail, taking 71251881Speter** care not to return a message too often to prevent "I am on 72251881Speter** vacation" loops. 73251881Speter*/ 74251881Speter 75251881Speter#define VDB ".vacation" /* vacation database */ 76251881Speter#define VMSG ".vacation.msg" /* vacation message */ 77251881Speter#define SECSPERDAY (60 * 60 * 24) 78251881Speter#define DAYSPERWEEK 7 79251881Speter 80251881Spetertypedef struct alias 81251881Speter{ 82251881Speter char *name; 83251881Speter struct alias *next; 84251881Speter} ALIAS; 85251881Speter 86251881SpeterALIAS *Names = NULL; 87251881Speter 88251881SpeterSMDB_DATABASE *Db; 89251881Speter 90251881Speterchar From[MAXLINE]; 91251881Speterbool CloseMBDB = false; 92251881Speter 93251881Speter#if defined(__hpux) || defined(__osf__) 94251881Speter# ifndef SM_CONF_SYSLOG_INT 95251881Speter# define SM_CONF_SYSLOG_INT 1 96251881Speter# endif /* SM_CONF_SYSLOG_INT */ 97251881Speter#endif /* defined(__hpux) || defined(__osf__) */ 98251881Speter 99251881Speter#if SM_CONF_SYSLOG_INT 100251881Speter# define SYSLOG_RET_T int 101251881Speter# define SYSLOG_RET return 0 102251881Speter#else /* SM_CONF_SYSLOG_INT */ 103251881Speter# define SYSLOG_RET_T void 104251881Speter# define SYSLOG_RET 105251881Speter#endif /* SM_CONF_SYSLOG_INT */ 106251881Speter 107251881Spetertypedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...)); 108251881SpeterSYSLOG_T *msglog = syslog; 109251881Speterstatic SYSLOG_RET_T debuglog __P((int, const char *, ...)); 110251881Speterstatic void eatmsg __P((void)); 111251881Speterstatic void listdb __P((void)); 112251881Speter 113251881Speter/* exit after reading input */ 114251881Speter#define EXITIT(excode) \ 115251881Speter{ \ 116251881Speter eatmsg(); \ 117251881Speter if (CloseMBDB) \ 118251881Speter { \ 119251881Speter sm_mbdb_terminate(); \ 120251881Speter CloseMBDB = false; \ 121251881Speter } \ 122251881Speter return excode; \ 123251881Speter} 124251881Speter 125251881Speter#define EXITM(excode) \ 126251881Speter{ \ 127251881Speter if (!initdb && !list) \ 128251881Speter eatmsg(); \ 129251881Speter if (CloseMBDB) \ 130251881Speter { \ 131251881Speter sm_mbdb_terminate(); \ 132251881Speter CloseMBDB = false; \ 133251881Speter } \ 134251881Speter exit(excode); \ 135251881Speter} 136251881Speter 137251881Speterint 138251881Spetermain(argc, argv) 139251881Speter int argc; 140251881Speter char **argv; 141251881Speter{ 142251881Speter bool alwaysrespond = false; 143251881Speter bool initdb, exclude; 144251881Speter bool runasuser = false; 145251881Speter bool list = false; 146251881Speter int mfail = 0, ufail = 0; 147251881Speter int ch; 148251881Speter int result; 149251881Speter long sff; 150251881Speter time_t interval; 151251881Speter struct passwd *pw; 152251881Speter ALIAS *cur; 153251881Speter char *dbfilename = NULL; 154251881Speter char *msgfilename = NULL; 155251881Speter char *cfpath = NULL; 156251881Speter char *name = NULL; 157251881Speter char *returnaddr = NULL; 158251881Speter SMDB_USER_INFO user_info; 159251881Speter static char rnamebuf[MAXNAME]; 160251881Speter extern int optind, opterr; 161251881Speter extern char *optarg; 162251881Speter 163251881Speter /* Vars needed to link with smutil */ 164251881Speter clrbitmap(DontBlameSendmail); 165251881Speter RunAsUid = RealUid = getuid(); 166251881Speter RunAsGid = RealGid = getgid(); 167251881Speter pw = getpwuid(RealUid); 168251881Speter if (pw != NULL) 169251881Speter { 170251881Speter if (strlen(pw->pw_name) > MAXNAME - 1) 171251881Speter pw->pw_name[MAXNAME] = '\0'; 172251881Speter sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); 173251881Speter } 174251881Speter else 175251881Speter sm_snprintf(rnamebuf, sizeof rnamebuf, 176251881Speter "Unknown UID %d", (int) RealUid); 177251881Speter RunAsUserName = RealUserName = rnamebuf; 178251881Speter 179251881Speter# ifdef LOG_MAIL 180251881Speter openlog("vacation", LOG_PID, LOG_MAIL); 181251881Speter# else /* LOG_MAIL */ 182251881Speter openlog("vacation", LOG_PID); 183251881Speter# endif /* LOG_MAIL */ 184251881Speter 185251881Speter opterr = 0; 186251881Speter initdb = false; 187251881Speter exclude = false; 188251881Speter interval = INTERVAL_UNDEF; 189251881Speter *From = '\0'; 190251881Speter 191251881Speter 192251881Speter#define OPTIONS "a:C:df:Iijlm:R:r:s:t:Uxz" 193251881Speter 194251881Speter while (mfail == 0 && ufail == 0 && 195251881Speter (ch = getopt(argc, argv, OPTIONS)) != -1) 196251881Speter { 197251881Speter switch((char)ch) 198251881Speter { 199251881Speter case 'a': /* alias */ 200251881Speter cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS)); 201251881Speter if (cur == NULL) 202251881Speter { 203251881Speter mfail++; 204251881Speter break; 205251881Speter } 206251881Speter cur->name = optarg; 207251881Speter cur->next = Names; 208251881Speter Names = cur; 209251881Speter break; 210251881Speter 211251881Speter case 'C': 212251881Speter cfpath = optarg; 213251881Speter break; 214251881Speter 215251881Speter case 'd': /* debug mode */ 216251881Speter msglog = debuglog; 217251881Speter break; 218251881Speter 219251881Speter case 'f': /* alternate database */ 220251881Speter dbfilename = optarg; 221251881Speter break; 222251881Speter 223251881Speter case 'I': /* backward compatible */ 224251881Speter case 'i': /* init the database */ 225251881Speter initdb = true; 226251881Speter break; 227251881Speter 228251881Speter case 'j': 229251881Speter alwaysrespond = true; 230251881Speter break; 231251881Speter 232251881Speter case 'l': 233251881Speter list = true; /* list the database */ 234251881Speter break; 235251881Speter 236251881Speter case 'm': /* alternate message file */ 237251881Speter msgfilename = optarg; 238251881Speter break; 239251881Speter 240251881Speter case 'R': 241251881Speter returnaddr = optarg; 242251881Speter break; 243251881Speter 244251881Speter case 'r': 245251881Speter if (isascii(*optarg) && isdigit(*optarg)) 246251881Speter { 247251881Speter interval = atol(optarg) * SECSPERDAY; 248251881Speter if (interval < 0) 249251881Speter ufail++; 250251881Speter } 251251881Speter else 252251881Speter interval = ONLY_ONCE; 253251881Speter break; 254251881Speter 255251881Speter case 's': /* alternate sender name */ 256251881Speter (void) sm_strlcpy(From, optarg, sizeof From); 257251881Speter break; 258251881Speter 259251881Speter case 't': /* SunOS: -t1d (default expire) */ 260251881Speter break; 261251881Speter 262251881Speter case 'U': /* run as single user mode */ 263251881Speter runasuser = true; 264251881Speter break; 265251881Speter 266251881Speter case 'x': 267251881Speter exclude = true; 268251881Speter break; 269251881Speter 270251881Speter case 'z': 271251881Speter returnaddr = "<>"; 272251881Speter break; 273251881Speter 274251881Speter case '?': 275251881Speter default: 276251881Speter ufail++; 277251881Speter break; 278251881Speter } 279251881Speter } 280251881Speter argc -= optind; 281251881Speter argv += optind; 282251881Speter 283251881Speter if (mfail != 0) 284251881Speter { 285251881Speter msglog(LOG_NOTICE, 286251881Speter "vacation: can't allocate memory for alias.\n"); 287251881Speter EXITM(EX_TEMPFAIL); 288251881Speter } 289251881Speter if (ufail != 0) 290251881Speter usage(); 291251881Speter 292251881Speter if (argc != 1) 293251881Speter { 294251881Speter if (!initdb && !list && !exclude) 295251881Speter usage(); 296251881Speter if ((pw = getpwuid(getuid())) == NULL) 297251881Speter { 298251881Speter msglog(LOG_ERR, 299251881Speter "vacation: no such user uid %u.\n", getuid()); 300251881Speter EXITM(EX_NOUSER); 301251881Speter } 302251881Speter name = strdup(pw->pw_name); 303251881Speter user_info.smdbu_id = pw->pw_uid; 304251881Speter user_info.smdbu_group_id = pw->pw_gid; 305251881Speter (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, 306251881Speter SMDB_MAX_USER_NAME_LEN); 307251881Speter if (chdir(pw->pw_dir) != 0) 308251881Speter { 309251881Speter msglog(LOG_NOTICE, 310251881Speter "vacation: no such directory %s.\n", 311251881Speter pw->pw_dir); 312251881Speter EXITM(EX_NOINPUT); 313251881Speter } 314251881Speter } 315251881Speter else if (runasuser) 316251881Speter { 317251881Speter name = strdup(*argv); 318251881Speter if (dbfilename == NULL || msgfilename == NULL) 319251881Speter { 320251881Speter msglog(LOG_NOTICE, 321251881Speter "vacation: -U requires setting both -f and -m\n"); 322251881Speter EXITM(EX_NOINPUT); 323251881Speter } 324251881Speter user_info.smdbu_id = pw->pw_uid; 325251881Speter user_info.smdbu_group_id = pw->pw_gid; 326251881Speter (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, 327251881Speter SMDB_MAX_USER_NAME_LEN); 328251881Speter } 329251881Speter else 330251881Speter { 331251881Speter int err; 332251881Speter SM_CF_OPT_T mbdbname; 333251881Speter SM_MBDB_T user; 334251881Speter 335251881Speter cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath); 336251881Speter mbdbname.opt_name = "MailboxDatabase"; 337251881Speter mbdbname.opt_val = "pw"; 338251881Speter (void) sm_cf_getopt(cfpath, 1, &mbdbname); 339251881Speter err = sm_mbdb_initialize(mbdbname.opt_val); 340251881Speter if (err != EX_OK) 341251881Speter { 342251881Speter msglog(LOG_ERR, 343251881Speter "vacation: can't open mailbox database: %s.\n", 344251881Speter sm_strexit(err)); 345251881Speter EXITM(err); 346251881Speter } 347251881Speter CloseMBDB = true; 348251881Speter err = sm_mbdb_lookup(*argv, &user); 349251881Speter if (err == EX_NOUSER) 350251881Speter { 351251881Speter msglog(LOG_ERR, "vacation: no such user %s.\n", *argv); 352251881Speter EXITM(EX_NOUSER); 353251881Speter } 354251881Speter if (err != EX_OK) 355251881Speter { 356251881Speter msglog(LOG_ERR, 357251881Speter "vacation: can't read mailbox database: %s.\n", 358251881Speter sm_strexit(err)); 359251881Speter EXITM(err); 360251881Speter } 361251881Speter name = strdup(user.mbdb_name); 362251881Speter if (chdir(user.mbdb_homedir) != 0) 363251881Speter { 364251881Speter msglog(LOG_NOTICE, 365251881Speter "vacation: no such directory %s.\n", 366251881Speter user.mbdb_homedir); 367251881Speter EXITM(EX_NOINPUT); 368251881Speter } 369251881Speter user_info.smdbu_id = user.mbdb_uid; 370251881Speter user_info.smdbu_group_id = user.mbdb_gid; 371251881Speter (void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name, 372251881Speter SMDB_MAX_USER_NAME_LEN); 373251881Speter } 374251881Speter if (name == NULL) 375251881Speter { 376251881Speter msglog(LOG_ERR, 377251881Speter "vacation: can't allocate memory for username.\n"); 378251881Speter EXITM(EX_OSERR); 379251881Speter } 380251881Speter 381251881Speter if (dbfilename == NULL) 382251881Speter dbfilename = VDB; 383251881Speter if (msgfilename == NULL) 384251881Speter msgfilename = VMSG; 385251881Speter 386251881Speter sff = SFF_CREAT; 387251881Speter if (getegid() != getgid()) 388251881Speter { 389251881Speter /* Allow a set-group-ID vacation binary */ 390251881Speter RunAsGid = user_info.smdbu_group_id = getegid(); 391251881Speter sff |= SFF_OPENASROOT; 392251881Speter } 393251881Speter if (getuid() == 0) 394251881Speter { 395251881Speter /* Allow root to initialize user's vacation databases */ 396251881Speter sff |= SFF_OPENASROOT|SFF_ROOTOK; 397251881Speter 398251881Speter /* ... safely */ 399251881Speter sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY; 400251881Speter } 401251881Speter 402251881Speter 403251881Speter result = smdb_open_database(&Db, dbfilename, 404251881Speter O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0), 405251881Speter S_IRUSR|S_IWUSR, sff, 406251881Speter SMDB_TYPE_DEFAULT, &user_info, NULL); 407251881Speter if (result != SMDBE_OK) 408251881Speter { 409251881Speter msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename, 410251881Speter sm_errstring(result)); 411251881Speter EXITM(EX_DATAERR); 412251881Speter } 413251881Speter 414251881Speter if (list) 415251881Speter { 416251881Speter listdb(); 417251881Speter (void) Db->smdb_close(Db); 418251881Speter exit(EX_OK); 419251881Speter } 420251881Speter 421251881Speter if (interval != INTERVAL_UNDEF) 422251881Speter setinterval(interval); 423251881Speter 424251881Speter if (initdb && !exclude) 425251881Speter { 426251881Speter (void) Db->smdb_close(Db); 427251881Speter exit(EX_OK); 428251881Speter } 429251881Speter 430251881Speter if (exclude) 431251881Speter { 432251881Speter xclude(smioin); 433251881Speter (void) Db->smdb_close(Db); 434251881Speter EXITM(EX_OK); 435251881Speter } 436251881Speter 437251881Speter if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL) 438251881Speter { 439251881Speter msglog(LOG_NOTICE, 440251881Speter "vacation: can't allocate memory for username.\n"); 441251881Speter (void) Db->smdb_close(Db); 442251881Speter EXITM(EX_OSERR); 443251881Speter } 444251881Speter cur->name = name; 445251881Speter cur->next = Names; 446251881Speter Names = cur; 447251881Speter 448251881Speter result = readheaders(alwaysrespond); 449251881Speter if (result == EX_OK && !recent()) 450251881Speter { 451251881Speter time_t now; 452251881Speter 453251881Speter (void) time(&now); 454251881Speter setreply(From, now); 455251881Speter (void) Db->smdb_close(Db); 456251881Speter sendmessage(name, msgfilename, returnaddr); 457251881Speter } 458251881Speter else 459251881Speter (void) Db->smdb_close(Db); 460251881Speter if (result == EX_NOUSER) 461251881Speter result = EX_OK; 462251881Speter exit(result); 463251881Speter} 464251881Speter 465251881Speter/* 466251881Speter** EATMSG -- read stdin till EOF 467251881Speter** 468251881Speter** Parameters: 469251881Speter** none. 470251881Speter** 471251881Speter** Returns: 472251881Speter** nothing. 473251881Speter** 474251881Speter*/ 475251881Speter 476251881Speterstatic void 477251881Spetereatmsg() 478251881Speter{ 479251881Speter /* 480251881Speter ** read the rest of the e-mail and ignore it to avoid problems 481251881Speter ** with EPIPE in sendmail 482251881Speter */ 483251881Speter while (getc(stdin) != EOF) 484251881Speter continue; 485251881Speter} 486251881Speter 487251881Speter/* 488251881Speter** READHEADERS -- read mail headers 489251881Speter** 490251881Speter** Parameters: 491251881Speter** alwaysrespond -- respond regardless of whether msg is to me 492251881Speter** 493251881Speter** Returns: 494251881Speter** a exit code: NOUSER if no reply, OK if reply, * if error 495251881Speter** 496251881Speter** Side Effects: 497251881Speter** may exit(). 498251881Speter** 499251881Speter*/ 500251881Speter 501251881Speterstatic int 502251881Speterreadheaders(alwaysrespond) 503251881Speter bool alwaysrespond; 504251881Speter{ 505251881Speter bool tome, cont; 506251881Speter register char *p; 507251881Speter register ALIAS *cur; 508251881Speter char buf[MAXLINE]; 509251881Speter 510251881Speter cont = false; 511251881Speter tome = alwaysrespond; 512251881Speter while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) && 513251881Speter *buf != '\n') 514251881Speter { 515251881Speter switch(*buf) 516251881Speter { 517251881Speter case 'F': /* "From " */ 518251881Speter cont = false; 519251881Speter if (strncmp(buf, "From ", 5) == 0) 520251881Speter { 521251881Speter bool quoted = false; 522251881Speter 523251881Speter p = buf + 5; 524251881Speter while (*p != '\0') 525251881Speter { 526251881Speter /* escaped character */ 527251881Speter if (*p == '\\') 528251881Speter { 529251881Speter p++; 530251881Speter if (*p == '\0') 531251881Speter { 532251881Speter msglog(LOG_NOTICE, 533251881Speter "vacation: badly formatted \"From \" line.\n"); 534251881Speter EXITIT(EX_DATAERR); 535251881Speter } 536251881Speter } 537251881Speter else if (*p == '"') 538251881Speter quoted = !quoted; 539251881Speter else if (*p == '\r' || *p == '\n') 540251881Speter break; 541251881Speter else if (*p == ' ' && !quoted) 542251881Speter break; 543251881Speter p++; 544251881Speter } 545251881Speter if (quoted) 546251881Speter { 547251881Speter msglog(LOG_NOTICE, 548251881Speter "vacation: badly formatted \"From \" line.\n"); 549251881Speter EXITIT(EX_DATAERR); 550251881Speter } 551251881Speter *p = '\0'; 552251881Speter 553251881Speter /* ok since both strings have MAXLINE length */ 554251881Speter if (*From == '\0') 555251881Speter (void) sm_strlcpy(From, buf + 5, 556251881Speter sizeof From); 557251881Speter if ((p = strchr(buf + 5, '\n')) != NULL) 558251881Speter *p = '\0'; 559251881Speter if (junkmail(buf + 5)) 560251881Speter EXITIT(EX_NOUSER); 561251881Speter } 562251881Speter break; 563251881Speter 564251881Speter case 'P': /* "Precedence:" */ 565251881Speter case 'p': 566251881Speter cont = false; 567251881Speter if (strlen(buf) <= 10 || 568251881Speter strncasecmp(buf, "Precedence", 10) != 0 || 569251881Speter (buf[10] != ':' && buf[10] != ' ' && 570251881Speter buf[10] != '\t')) 571251881Speter break; 572251881Speter if ((p = strchr(buf, ':')) == NULL) 573251881Speter break; 574251881Speter while (*++p != '\0' && isascii(*p) && isspace(*p)); 575251881Speter if (*p == '\0') 576251881Speter break; 577251881Speter if (strncasecmp(p, "junk", 4) == 0 || 578251881Speter strncasecmp(p, "bulk", 4) == 0 || 579251881Speter strncasecmp(p, "list", 4) == 0) 580251881Speter EXITIT(EX_NOUSER); 581251881Speter break; 582251881Speter 583251881Speter case 'C': /* "Cc:" */ 584251881Speter case 'c': 585251881Speter if (strncasecmp(buf, "Cc:", 3) != 0) 586251881Speter break; 587251881Speter cont = true; 588251881Speter goto findme; 589251881Speter 590251881Speter case 'T': /* "To:" */ 591251881Speter case 't': 592 if (strncasecmp(buf, "To:", 3) != 0) 593 break; 594 cont = true; 595 goto findme; 596 597 default: 598 if (!isascii(*buf) || !isspace(*buf) || !cont || tome) 599 { 600 cont = false; 601 break; 602 } 603findme: 604 for (cur = Names; 605 !tome && cur != NULL; 606 cur = cur->next) 607 tome = nsearch(cur->name, buf); 608 } 609 } 610 if (!tome) 611 EXITIT(EX_NOUSER); 612 if (*From == '\0') 613 { 614 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n"); 615 EXITIT(EX_DATAERR); 616 } 617 EXITIT(EX_OK); 618} 619 620/* 621** NSEARCH -- 622** do a nice, slow, search of a string for a substring. 623** 624** Parameters: 625** name -- name to search. 626** str -- string in which to search. 627** 628** Returns: 629** is name a substring of str? 630** 631*/ 632 633static bool 634nsearch(name, str) 635 register char *name, *str; 636{ 637 register size_t len; 638 register char *s; 639 640 len = strlen(name); 641 642 for (s = str; *s != '\0'; ++s) 643 { 644 /* 645 ** Check to make sure that the string matches and 646 ** the previous character is not an alphanumeric and 647 ** the next character after the match is not an alphanumeric. 648 ** 649 ** This prevents matching "eric" to "derick" while still 650 ** matching "eric" to "<eric+detail>". 651 */ 652 653 if (tolower(*s) == tolower(*name) && 654 strncasecmp(name, s, len) == 0 && 655 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) && 656 (!isascii(*(s + len)) || !isalnum(*(s + len)))) 657 return true; 658 } 659 return false; 660} 661 662/* 663** JUNKMAIL -- 664** read the header and return if automagic/junk/bulk/list mail 665** 666** Parameters: 667** from -- sender address. 668** 669** Returns: 670** is this some automated/junk/bulk/list mail? 671** 672*/ 673 674struct ignore 675{ 676 char *name; 677 size_t len; 678}; 679 680typedef struct ignore IGNORE_T; 681 682#define MAX_USER_LEN 256 /* maximum length of local part (sender) */ 683 684/* delimiters for the local part of an address */ 685#define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+') 686 687static bool 688junkmail(from) 689 char *from; 690{ 691 bool quot; 692 char *e; 693 size_t len; 694 IGNORE_T *cur; 695 char sender[MAX_USER_LEN]; 696 static IGNORE_T ignore[] = 697 { 698 { "postmaster", 10 }, 699 { "uucp", 4 }, 700 { "mailer-daemon", 13 }, 701 { "mailer", 6 }, 702 { NULL, 0 } 703 }; 704 705 static IGNORE_T ignorepost[] = 706 { 707 { "-request", 8 }, 708 { "-relay", 6 }, 709 { "-owner", 6 }, 710 { NULL, 0 } 711 }; 712 713 static IGNORE_T ignorepre[] = 714 { 715 { "owner-", 6 }, 716 { NULL, 0 } 717 }; 718 719 /* 720 ** This is mildly amusing, and I'm not positive it's right; trying 721 ** to find the "real" name of the sender, assuming that addresses 722 ** will be some variant of: 723 ** 724 ** From site!site!SENDER%site.domain%site.domain@site.domain 725 */ 726 727 quot = false; 728 e = from; 729 len = 0; 730 while (*e != '\0' && (quot || !isdelim(*e))) 731 { 732 if (*e == '"') 733 { 734 quot = !quot; 735 ++e; 736 continue; 737 } 738 if (*e == '\\') 739 { 740 if (*(++e) == '\0') 741 { 742 /* '\\' at end of string? */ 743 break; 744 } 745 if (len < MAX_USER_LEN) 746 sender[len++] = *e; 747 ++e; 748 continue; 749 } 750 if (*e == '!' && !quot) 751 { 752 len = 0; 753 sender[len] = '\0'; 754 } 755 else 756 if (len < MAX_USER_LEN) 757 sender[len++] = *e; 758 ++e; 759 } 760 if (len < MAX_USER_LEN) 761 sender[len] = '\0'; 762 else 763 sender[MAX_USER_LEN - 1] = '\0'; 764 765 if (len <= 0) 766 return false; 767#if 0 768 if (quot) 769 return false; /* syntax error... */ 770#endif /* 0 */ 771 772 /* test prefixes */ 773 for (cur = ignorepre; cur->name != NULL; ++cur) 774 { 775 if (len >= cur->len && 776 strncasecmp(cur->name, sender, cur->len) == 0) 777 return true; 778 } 779 780 /* 781 ** If the name is truncated, don't test the rest. 782 ** We could extract the "tail" of the sender address and 783 ** compare it it ignorepost, however, it seems not worth 784 ** the effort. 785 ** The address surely can't match any entry in ignore[] 786 ** (as long as all of them are shorter than MAX_USER_LEN). 787 */ 788 789 if (len > MAX_USER_LEN) 790 return false; 791 792 /* test full local parts */ 793 for (cur = ignore; cur->name != NULL; ++cur) 794 { 795 if (len == cur->len && 796 strncasecmp(cur->name, sender, cur->len) == 0) 797 return true; 798 } 799 800 /* test postfixes */ 801 for (cur = ignorepost; cur->name != NULL; ++cur) 802 { 803 if (len >= cur->len && 804 strncasecmp(cur->name, e - cur->len - 1, 805 cur->len) == 0) 806 return true; 807 } 808 return false; 809} 810 811#define VIT "__VACATION__INTERVAL__TIMER__" 812 813/* 814** RECENT -- 815** find out if user has gotten a vacation message recently. 816** 817** Parameters: 818** none. 819** 820** Returns: 821** true iff user has gotten a vacation message recently. 822** 823*/ 824 825static bool 826recent() 827{ 828 SMDB_DBENT key, data; 829 time_t then, next; 830 bool trydomain = false; 831 int st; 832 char *domain; 833 834 memset(&key, '\0', sizeof key); 835 memset(&data, '\0', sizeof data); 836 837 /* get interval time */ 838 key.data = VIT; 839 key.size = sizeof(VIT); 840 841 st = Db->smdb_get(Db, &key, &data, 0); 842 if (st != SMDBE_OK) 843 next = SECSPERDAY * DAYSPERWEEK; 844 else 845 memmove(&next, data.data, sizeof(next)); 846 847 memset(&data, '\0', sizeof data); 848 849 /* get record for this address */ 850 key.data = From; 851 key.size = strlen(From); 852 853 do 854 { 855 st = Db->smdb_get(Db, &key, &data, 0); 856 if (st == SMDBE_OK) 857 { 858 memmove(&then, data.data, sizeof(then)); 859 if (next == ONLY_ONCE || then == ONLY_ONCE || 860 then + next > time(NULL)) 861 return true; 862 } 863 if ((trydomain = !trydomain) && 864 (domain = strchr(From, '@')) != NULL) 865 { 866 key.data = domain; 867 key.size = strlen(domain); 868 } 869 } while (trydomain); 870 return false; 871} 872 873/* 874** SETINTERVAL -- 875** store the reply interval 876** 877** Parameters: 878** interval -- time interval for replies. 879** 880** Returns: 881** nothing. 882** 883** Side Effects: 884** stores the reply interval in database. 885*/ 886 887static void 888setinterval(interval) 889 time_t interval; 890{ 891 SMDB_DBENT key, data; 892 893 memset(&key, '\0', sizeof key); 894 memset(&data, '\0', sizeof data); 895 896 key.data = VIT; 897 key.size = sizeof(VIT); 898 data.data = (char*) &interval; 899 data.size = sizeof(interval); 900 (void) (Db->smdb_put)(Db, &key, &data, 0); 901} 902 903/* 904** SETREPLY -- 905** store that this user knows about the vacation. 906** 907** Parameters: 908** from -- sender address. 909** when -- last reply time. 910** 911** Returns: 912** nothing. 913** 914** Side Effects: 915** stores user/time in database. 916*/ 917 918static void 919setreply(from, when) 920 char *from; 921 time_t when; 922{ 923 SMDB_DBENT key, data; 924 925 memset(&key, '\0', sizeof key); 926 memset(&data, '\0', sizeof data); 927 928 key.data = from; 929 key.size = strlen(from); 930 data.data = (char*) &when; 931 data.size = sizeof(when); 932 (void) (Db->smdb_put)(Db, &key, &data, 0); 933} 934 935/* 936** XCLUDE -- 937** add users to vacation db so they don't get a reply. 938** 939** Parameters: 940** f -- file pointer with list of address to exclude 941** 942** Returns: 943** nothing. 944** 945** Side Effects: 946** stores users in database. 947*/ 948 949static void 950xclude(f) 951 SM_FILE_T *f; 952{ 953 char buf[MAXLINE], *p; 954 955 if (f == NULL) 956 return; 957 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf)) 958 { 959 if ((p = strchr(buf, '\n')) != NULL) 960 *p = '\0'; 961 setreply(buf, ONLY_ONCE); 962 } 963} 964 965/* 966** SENDMESSAGE -- 967** exec sendmail to send the vacation file to sender 968** 969** Parameters: 970** myname -- user name. 971** msgfn -- name of file with vacation message. 972** sender -- use as sender address 973** 974** Returns: 975** nothing. 976** 977** Side Effects: 978** sends vacation reply. 979*/ 980 981static void 982sendmessage(myname, msgfn, sender) 983 char *myname; 984 char *msgfn; 985 char *sender; 986{ 987 SM_FILE_T *mfp, *sfp; 988 int i; 989 int pvect[2]; 990 char *pv[8]; 991 char buf[MAXLINE]; 992 993 mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL); 994 if (mfp == NULL) 995 { 996 if (msgfn[0] == '/') 997 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn); 998 else 999 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", 1000 myname, msgfn); 1001 exit(EX_NOINPUT); 1002 } 1003 if (pipe(pvect) < 0) 1004 { 1005 msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno)); 1006 exit(EX_OSERR); 1007 } 1008 pv[0] = "sendmail"; 1009 pv[1] = "-oi"; 1010 pv[2] = "-f"; 1011 if (sender != NULL) 1012 pv[3] = sender; 1013 else 1014 pv[3] = myname; 1015 pv[4] = "--"; 1016 pv[5] = From; 1017 pv[6] = NULL; 1018 i = fork(); 1019 if (i < 0) 1020 { 1021 msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno)); 1022 exit(EX_OSERR); 1023 } 1024 if (i == 0) 1025 { 1026 (void) dup2(pvect[0], 0); 1027 (void) close(pvect[0]); 1028 (void) close(pvect[1]); 1029 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1030 (void) execv(_PATH_SENDMAIL, pv); 1031 msglog(LOG_ERR, "vacation: can't exec %s: %s", 1032 _PATH_SENDMAIL, sm_errstring(errno)); 1033 exit(EX_UNAVAILABLE); 1034 } 1035 /* check return status of the following calls? XXX */ 1036 (void) close(pvect[0]); 1037 if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 1038 (void *) &(pvect[1]), 1039 SM_IO_WRONLY, NULL)) != NULL) 1040 { 1041#if _FFR_VAC_WAIT4SM 1042# ifdef WAITUNION 1043 union wait st; 1044# else /* WAITUNION */ 1045 auto int st; 1046# endif /* WAITUNION */ 1047#endif /* _FFR_VAC_WAIT4SM */ 1048 1049 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From); 1050 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, 1051 "Auto-Submitted: auto-replied\n"); 1052 while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf)) 1053 (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf); 1054 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1055 (void) sm_io_close(sfp, SM_TIME_DEFAULT); 1056#if _FFR_VAC_WAIT4SM 1057 (void) wait(&st); 1058#endif /* _FFR_VAC_WAIT4SM */ 1059 } 1060 else 1061 { 1062 (void) sm_io_close(mfp, SM_TIME_DEFAULT); 1063 msglog(LOG_ERR, "vacation: can't open pipe to sendmail"); 1064 exit(EX_UNAVAILABLE); 1065 } 1066} 1067 1068static void 1069usage() 1070{ 1071 msglog(LOG_NOTICE, 1072 "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-j] [-l] [-m msg] [-R returnaddr] [-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n", 1073 getuid()); 1074 exit(EX_USAGE); 1075} 1076 1077/* 1078** LISTDB -- list the contents of the vacation database 1079** 1080** Parameters: 1081** none. 1082** 1083** Returns: 1084** nothing. 1085*/ 1086 1087static void 1088listdb() 1089{ 1090 int result; 1091 time_t t; 1092 SMDB_CURSOR *cursor = NULL; 1093 SMDB_DBENT db_key, db_value; 1094 1095 memset(&db_key, '\0', sizeof db_key); 1096 memset(&db_value, '\0', sizeof db_value); 1097 1098 result = Db->smdb_cursor(Db, &cursor, 0); 1099 if (result != SMDBE_OK) 1100 { 1101 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1102 "vacation: set cursor: %s\n", 1103 sm_errstring(result)); 1104 return; 1105 } 1106 1107 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value, 1108 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) 1109 { 1110 char *timestamp; 1111 1112 /* skip magic VIT entry */ 1113 if (db_key.size == strlen(VIT) + 1 && 1114 strncmp((char *)db_key.data, VIT, 1115 (int)db_key.size - 1) == 0) 1116 continue; 1117 1118 /* skip bogus values */ 1119 if (db_value.size != sizeof t) 1120 { 1121 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1122 "vacation: %.*s invalid time stamp\n", 1123 (int) db_key.size, (char *) db_key.data); 1124 continue; 1125 } 1126 1127 memcpy(&t, db_value.data, sizeof t); 1128 1129 if (db_key.size > 40) 1130 db_key.size = 40; 1131 1132 if (t <= 0) 1133 { 1134 /* must be an exclude */ 1135 timestamp = "(exclusion)\n"; 1136 } 1137 else 1138 { 1139 timestamp = ctime(&t); 1140 } 1141 sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s", 1142 (int) db_key.size, (char *) db_key.data, 1143 timestamp); 1144 1145 memset(&db_key, '\0', sizeof db_key); 1146 memset(&db_value, '\0', sizeof db_value); 1147 } 1148 1149 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) 1150 { 1151 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 1152 "vacation: get value at cursor: %s\n", 1153 sm_errstring(result)); 1154 if (cursor != NULL) 1155 { 1156 (void) cursor->smdbc_close(cursor); 1157 cursor = NULL; 1158 } 1159 return; 1160 } 1161 (void) cursor->smdbc_close(cursor); 1162 cursor = NULL; 1163} 1164 1165/* 1166** DEBUGLOG -- write message to standard error 1167** 1168** Append a message to the standard error for the convenience of 1169** end-users debugging without access to the syslog messages. 1170** 1171** Parameters: 1172** i -- syslog log level 1173** fmt -- string format 1174** 1175** Returns: 1176** nothing. 1177*/ 1178 1179/*VARARGS2*/ 1180static SYSLOG_RET_T 1181#ifdef __STDC__ 1182debuglog(int i, const char *fmt, ...) 1183#else /* __STDC__ */ 1184debuglog(i, fmt, va_alist) 1185 int i; 1186 const char *fmt; 1187 va_dcl 1188#endif /* __STDC__ */ 1189 1190{ 1191 SM_VA_LOCAL_DECL 1192 1193 SM_VA_START(ap, fmt); 1194 sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap); 1195 SM_VA_END(ap); 1196 SYSLOG_RET; 1197} 1198