vacation.c
上传用户:xu_441
上传日期:2007-01-04
资源大小:1640k
文件大小:16k
- /*
- * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1987, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1983 Eric P. Allman. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
- #ifndef lint
- static char copyright[] =
- "@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.n
- All rights reserved.n
- Copyright (c) 1983, 1987, 1993n
- The Regents of the University of California. All rights reserved.n
- Copyright (c) 1983 Eric P. Allman. All rights reserved.n";
- #endif /* ! lint */
- #ifndef lint
- static char id[] = "@(#)$Id: vacation.c,v 8.57 1999/11/07 05:26:18 gshapiro Exp $";
- #endif /* ! lint */
- #include <ctype.h>
- #include <stdlib.h>
- #include <syslog.h>
- #include <time.h>
- #include <unistd.h>
- #ifdef EX_OK
- # undef EX_OK /* unistd.h may have another use for this */
- #endif /* EX_OK */
- #include <sysexits.h>
- #if defined(sun) && !defined(BSD) && !defined(SOLARIS)
- # include <pathnames.h>
- #endif /* sun && ! BSD && ! SOLARIS */
- #include "sendmail/sendmail.h"
- #include "libsmdb/smdb.h"
- #if defined(__hpux) && !defined(HPUX11)
- # undef syslog /* Undo hard_syslog conf.h change */
- #endif /* defined(__hpux) && !defined(HPUX11) */
- #ifndef _PATH_SENDMAIL
- # define _PATH_SENDMAIL "/usr/lib/sendmail"
- #endif /* ! _PATH_SENDMAIL */
- #define ONLY_ONCE ((time_t) 0) /* send at most one reply */
- #define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */
- uid_t RealUid;
- gid_t RealGid;
- char *RealUserName;
- uid_t RunAsUid;
- uid_t RunAsGid;
- char *RunAsUserName;
- int Verbose = 2;
- bool DontInitGroups = FALSE;
- uid_t TrustedUid = 0;
- BITMAP256 DontBlameSendmail;
- /*
- ** VACATION -- return a message to the sender when on vacation.
- **
- ** This program is invoked as a message receiver. It returns a
- ** message specified by the user to whomever sent the mail, taking
- ** care not to return a message too often to prevent "I am on
- ** vacation" loops.
- */
- #define VDB ".vacation" /* vacation database */
- #define VMSG ".vacation.msg" /* vacation message */
- #define SECSPERDAY (60 * 60 * 24)
- #define DAYSPERWEEK 7
- #ifndef TRUE
- # define TRUE 1
- # define FALSE 0
- #endif /* ! TRUE */
- #ifndef __P
- # ifdef __STDC__
- # define __P(protos) protos
- # else /* __STDC__ */
- # define __P(protos) ()
- # define const
- # endif /* __STDC__ */
- #endif /* ! __P */
- typedef struct alias
- {
- char *name;
- struct alias *next;
- } ALIAS;
- ALIAS *Names = NULL;
- SMDB_DATABASE *Db;
- char From[MAXLINE];
- int
- main(argc, argv)
- int argc;
- char **argv;
- {
- bool iflag, emptysender, exclude;
- int ch;
- int result;
- time_t interval;
- struct passwd *pw;
- ALIAS *cur;
- char *dbfilename = VDB;
- char *msgfilename = VMSG;
- SMDB_USER_INFO user_info;
- static char rnamebuf[MAXNAME];
- extern int optind, opterr;
- extern char *optarg;
- extern void usage __P((void));
- extern void setinterval __P((time_t));
- extern void readheaders __P((void));
- extern bool recent __P((void));
- extern void setreply __P((char *, time_t));
- extern void sendmessage __P((char *, char *, bool));
- extern void xclude __P((FILE *));
- /* Vars needed to link with smutil */
- clrbitmap(DontBlameSendmail);
- RunAsUid = RealUid = getuid();
- RunAsGid = RealGid = getgid();
- pw = getpwuid(RealUid);
- if (pw != NULL)
- {
- if (strlen(pw->pw_name) > MAXNAME - 1)
- pw->pw_name[MAXNAME] = '';
- snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
- }
- else
- snprintf(rnamebuf, sizeof rnamebuf,
- "Unknown UID %d", (int) RealUid);
- RunAsUserName = RealUserName = rnamebuf;
- opterr = 0;
- iflag = FALSE;
- emptysender = FALSE;
- exclude = FALSE;
- interval = INTERVAL_UNDEF;
- *From = '';
- while ((ch = getopt(argc, argv, "a:f:Iim:r:s:xz")) != -1)
- {
- switch((char)ch)
- {
- case 'a': /* alias */
- cur = (ALIAS *)malloc((u_int)sizeof(ALIAS));
- if (cur == NULL)
- {
- syslog(LOG_NOTICE,
- "vacation: can't allocate memory for alias %s.n",
- optarg);
- break;
- }
- cur->name = optarg;
- cur->next = Names;
- Names = cur;
- break;
- case 'f': /* alternate database */
- dbfilename = optarg;
- break;
- case 'I': /* backward compatible */
- case 'i': /* init the database */
- iflag = TRUE;
- break;
- case 'm': /* alternate message file */
- msgfilename = optarg;
- break;
- case 'r':
- if (isascii(*optarg) && isdigit(*optarg))
- {
- interval = atol(optarg) * SECSPERDAY;
- if (interval < 0)
- usage();
- }
- else
- interval = ONLY_ONCE;
- break;
- case 's': /* alternate sender name */
- (void) strlcpy(From, optarg, sizeof From);
- break;
- case 'x':
- exclude = TRUE;
- break;
- case 'z':
- emptysender = TRUE;
- break;
- case '?':
- default:
- usage();
- break;
- }
- }
- argc -= optind;
- argv += optind;
- if (argc != 1)
- {
- if (!iflag && !exclude)
- usage();
- if ((pw = getpwuid(getuid())) == NULL)
- {
- syslog(LOG_ERR,
- "vacation: no such user uid %u.n", getuid());
- exit(EX_NOUSER);
- }
- }
- else if ((pw = getpwnam(*argv)) == NULL)
- {
- syslog(LOG_ERR, "vacation: no such user %s.n", *argv);
- exit(EX_NOUSER);
- }
- if (chdir(pw->pw_dir) != 0)
- {
- syslog(LOG_NOTICE,
- "vacation: no such directory %s.n", pw->pw_dir);
- exit(EX_NOINPUT);
- }
- user_info.smdbu_id = pw->pw_uid;
- user_info.smdbu_group_id = pw->pw_gid;
- (void) strlcpy(user_info.smdbu_name, pw->pw_name,
- SMDB_MAX_USER_NAME_LEN);
- result = smdb_open_database(&Db, dbfilename,
- O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
- S_IRUSR|S_IWUSR, SFF_CREAT,
- SMDB_TYPE_DEFAULT, &user_info, NULL);
- if (result != SMDBE_OK)
- {
- syslog(LOG_NOTICE, "vacation: %s: %sn", dbfilename,
- errstring(errno));
- exit(EX_DATAERR);
- }
- if (interval != INTERVAL_UNDEF)
- setinterval(interval);
- if (iflag)
- {
- result = Db->smdb_close(Db);
- if (!exclude)
- exit(EX_OK);
- }
- if (exclude)
- {
- xclude(stdin);
- result = Db->smdb_close(Db);
- exit(EX_OK);
- }
- if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL)
- {
- syslog(LOG_NOTICE,
- "vacation: can't allocate memory for username.n");
- exit(EX_OSERR);
- }
- cur->name = pw->pw_name;
- cur->next = Names;
- Names = cur;
- readheaders();
- if (!recent())
- {
- time_t now;
- (void) time(&now);
- setreply(From, now);
- result = Db->smdb_close(Db);
- sendmessage(pw->pw_name, msgfilename, emptysender);
- }
- else
- result = Db->smdb_close(Db);
- exit(EX_OK);
- /* NOTREACHED */
- return EX_OK;
- }
- /*
- ** READHEADERS -- read mail headers
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** nothing.
- **
- */
- void
- readheaders()
- {
- bool tome, cont;
- register char *p;
- register ALIAS *cur;
- char buf[MAXLINE];
- extern bool junkmail __P((char *));
- extern bool nsearch __P((char *, char *));
- cont = tome = FALSE;
- while (!tome && fgets(buf, sizeof(buf), stdin) && *buf != 'n')
- {
- switch(*buf)
- {
- case 'F': /* "From " */
- cont = FALSE;
- if (strncmp(buf, "From ", 5) == 0)
- {
- bool quoted = FALSE;
- p = buf + 5;
- while (*p != '')
- {
- /* escaped character */
- if (*p == '\')
- {
- p++;
- if (*p == '')
- {
- syslog(LOG_NOTICE,
- "vacation: badly formatted "From " line.n");
- exit(EX_DATAERR);
- }
- }
- else if (*p == '"')
- quoted = !quoted;
- else if (*p == 'r' || *p == 'n')
- break;
- else if (*p == ' ' && !quoted)
- break;
- p++;
- }
- if (quoted)
- {
- syslog(LOG_NOTICE,
- "vacation: badly formatted "From " line.n");
- exit(EX_DATAERR);
- }
- *p = '';
- /* ok since both strings have MAXLINE length */
- if (*From == '')
- (void)strlcpy(From, buf + 5,
- sizeof From);
- if ((p = strchr(buf + 5, 'n')) != NULL)
- *p = '';
- if (junkmail(buf + 5))
- exit(EX_OK);
- }
- break;
- case 'P': /* "Precedence:" */
- cont = FALSE;
- if (strlen(buf) <= 10 ||
- strncasecmp(buf, "Precedence", 10) != 0 ||
- (buf[10] != ':' && buf[10] != ' ' &&
- buf[10] != 't'))
- break;
- if ((p = strchr(buf, ':')) == NULL)
- break;
- while (*++p != '' && isascii(*p) && isspace(*p));
- if (*p == '')
- break;
- if (strncasecmp(p, "junk", 4) == 0 ||
- strncasecmp(p, "bulk", 4) == 0 ||
- strncasecmp(p, "list", 4) == 0)
- exit(EX_OK);
- break;
- case 'C': /* "Cc:" */
- if (strncmp(buf, "Cc:", 3) != 0)
- break;
- cont = TRUE;
- goto findme;
- case 'T': /* "To:" */
- if (strncmp(buf, "To:", 3) != 0)
- break;
- cont = TRUE;
- goto findme;
- default:
- if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
- {
- cont = FALSE;
- break;
- }
- findme:
- for (cur = Names;
- !tome && cur != NULL;
- cur = cur->next)
- tome = nsearch(cur->name, buf);
- }
- }
- if (!tome)
- exit(EX_OK);
- if (*From == '')
- {
- syslog(LOG_NOTICE, "vacation: no initial "From " line.n");
- exit(EX_DATAERR);
- }
- }
- /*
- ** NSEARCH --
- ** do a nice, slow, search of a string for a substring.
- **
- ** Parameters:
- ** name -- name to search.
- ** str -- string in which to search.
- **
- ** Returns:
- ** is name a substring of str?
- **
- */
- bool
- nsearch(name, str)
- register char *name, *str;
- {
- register size_t len;
- register char *s;
- len = strlen(name);
- for (s = str; *s != ''; ++s)
- {
- /*
- ** Check to make sure that the string matches and
- ** the previous character is not an alphanumeric and
- ** the next character after the match is not an alphanumeric.
- **
- ** This prevents matching "eric" to "derick" while still
- ** matching "eric" to "<eric+detail>".
- */
- if (tolower(*s) == tolower(*name) &&
- strncasecmp(name, s, len) == 0 &&
- (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
- (!isascii(*(s + len)) || !isalnum(*(s + len))))
- return TRUE;
- }
- return FALSE;
- }
- /*
- ** JUNKMAIL --
- ** read the header and return if automagic/junk/bulk/list mail
- **
- ** Parameters:
- ** from -- sender address.
- **
- ** Returns:
- ** is this some automated/junk/bulk/list mail?
- **
- */
- bool
- junkmail(from)
- char *from;
- {
- register size_t len;
- register char *p;
- register struct ignore *cur;
- static struct ignore
- {
- char *name;
- size_t len;
- } ignore[] =
- {
- { "-request", 8 },
- { "postmaster", 10 },
- { "uucp", 4 },
- { "mailer-daemon", 13 },
- { "mailer", 6 },
- { "-relay", 6 },
- { NULL, 0 }
- };
- /*
- * This is mildly amusing, and I'm not positive it's right; trying
- * to find the "real" name of the sender, assuming that addresses
- * will be some variant of:
- *
- * From site!site!SENDER%site.domain%site.domain@site.domain
- */
- if ((p = strchr(from, '%')) == NULL &&
- (p = strchr(from, '@')) == NULL)
- {
- if ((p = strrchr(from, '!')) != NULL)
- ++p;
- else
- p = from;
- for (; *p; ++p)
- continue;
- }
- len = p - from;
- for (cur = ignore; cur->name != NULL; ++cur)
- {
- if (len >= cur->len &&
- strncasecmp(cur->name, p - cur->len, cur->len) == 0)
- return TRUE;
- }
- return FALSE;
- }
- #define VIT "__VACATION__INTERVAL__TIMER__"
- /*
- ** RECENT --
- ** find out if user has gotten a vacation message recently.
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** TRUE iff user has gotten a vacation message recently.
- **
- */
- bool
- recent()
- {
- SMDB_DBENT key, data;
- time_t then, next;
- bool trydomain = FALSE;
- int st;
- char *domain;
- memset(&key, '', sizeof key);
- memset(&data, '', sizeof data);
- /* get interval time */
- key.data.data = VIT;
- key.data.size = sizeof(VIT);
- st = Db->smdb_get(Db, &key, &data, 0);
- if (st != SMDBE_OK)
- next = SECSPERDAY * DAYSPERWEEK;
- else
- memmove(&next, data.data.data, sizeof(next));
- memset(&data, '', sizeof data);
- /* get record for this address */
- key.data.data = From;
- key.data.size = strlen(From);
- do
- {
- st = Db->smdb_get(Db, &key, &data, 0);
- if (st == SMDBE_OK)
- {
- memmove(&then, data.data.data, sizeof(then));
- if (next == ONLY_ONCE || then == ONLY_ONCE ||
- then + next > time(NULL))
- return TRUE;
- }
- if ((trydomain = !trydomain) &&
- (domain = strchr(From, '@')) != NULL)
- {
- key.data.data = domain;
- key.data.size = strlen(domain);
- }
- } while (trydomain);
- return FALSE;
- }
- /*
- ** SETINTERVAL --
- ** store the reply interval
- **
- ** Parameters:
- ** interval -- time interval for replies.
- **
- ** Returns:
- ** nothing.
- **
- ** Side Effects:
- ** stores the reply interval in database.
- */
- void
- setinterval(interval)
- time_t interval;
- {
- SMDB_DBENT key, data;
- memset(&key, '', sizeof key);
- memset(&data, '', sizeof data);
- key.data.data = VIT;
- key.data.size = sizeof(VIT);
- data.data.data = (char*) &interval;
- data.data.size = sizeof(interval);
- (void)(Db->smdb_put)(Db, &key, &data, 0);
- }
- /*
- ** SETREPLY --
- ** store that this user knows about the vacation.
- **
- ** Parameters:
- ** from -- sender address.
- ** when -- last reply time.
- **
- ** Returns:
- ** nothing.
- **
- ** Side Effects:
- ** stores user/time in database.
- */
- void
- setreply(from, when)
- char *from;
- time_t when;
- {
- SMDB_DBENT key, data;
- memset(&key, '', sizeof key);
- memset(&data, '', sizeof data);
- key.data.data = from;
- key.data.size = strlen(from);
- data.data.data = (char*) &when;
- data.data.size = sizeof(when);
- (void)(Db->smdb_put)(Db, &key, &data, 0);
- }
- /*
- ** XCLUDE --
- ** add users to vacation db so they don't get a reply.
- **
- ** Parameters:
- ** f -- file pointer with list of address to exclude
- **
- ** Returns:
- ** nothing.
- **
- ** Side Effects:
- ** stores users in database.
- */
- void
- xclude(f)
- FILE *f;
- {
- char buf[MAXLINE], *p;
- if (f == NULL)
- return;
- while (fgets(buf, sizeof buf, f))
- {
- if ((p = strchr(buf, 'n')) != NULL)
- *p = '';
- setreply(buf, ONLY_ONCE);
- }
- }
- /*
- ** SENDMESSAGE --
- ** exec sendmail to send the vacation file to sender
- **
- ** Parameters:
- ** myname -- user name.
- ** msgfn -- name of file with vacation message.
- ** emptysender -- use <> as sender address?
- **
- ** Returns:
- ** nothing.
- **
- ** Side Effects:
- ** sends vacation reply.
- */
- void
- sendmessage(myname, msgfn, emptysender)
- char *myname;
- char *msgfn;
- bool emptysender;
- {
- FILE *mfp, *sfp;
- int i;
- int pvect[2];
- char buf[MAXLINE];
- mfp = fopen(msgfn, "r");
- if (mfp == NULL)
- {
- if (msgfn[0] == '/')
- syslog(LOG_NOTICE, "vacation: no %s file.n", msgfn);
- else
- syslog(LOG_NOTICE, "vacation: no ~%s/%s file.n",
- myname, msgfn);
- exit(EX_NOINPUT);
- }
- if (pipe(pvect) < 0)
- {
- syslog(LOG_ERR, "vacation: pipe: %s", strerror(errno));
- exit(EX_OSERR);
- }
- i = fork();
- if (i < 0)
- {
- syslog(LOG_ERR, "vacation: fork: %s", strerror(errno));
- exit(EX_OSERR);
- }
- if (i == 0)
- {
- (void) dup2(pvect[0], 0);
- (void) close(pvect[0]);
- (void) close(pvect[1]);
- (void) fclose(mfp);
- if (emptysender)
- myname = "<>";
- (void) execl(_PATH_SENDMAIL, "sendmail", "-f", myname, "--",
- From, NULL);
- syslog(LOG_ERR, "vacation: can't exec %s: %s",
- _PATH_SENDMAIL, errstring(errno));
- exit(EX_UNAVAILABLE);
- }
- /* check return status of the following calls? XXX */
- (void) close(pvect[0]);
- if ((sfp = fdopen(pvect[1], "w")) != NULL)
- {
- (void) fprintf(sfp, "To: %sn", From);
- (void) fprintf(sfp, "Auto-Submitted: auto-generatedn");
- while (fgets(buf, sizeof buf, mfp))
- (void) fputs(buf, sfp);
- (void) fclose(mfp);
- (void) fclose(sfp);
- }
- else
- {
- (void) fclose(mfp);
- syslog(LOG_ERR, "vacation: can't open pipe to sendmail");
- exit(EX_UNAVAILABLE);
- }
- }
- void
- usage()
- {
- syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] [-f db] [-m msg] [-r interval] [-s sender] [-x] [-z] loginn",
- getuid());
- exit(EX_USAGE);
- }
- /*VARARGS1*/
- void
- #ifdef __STDC__
- message(const char *msg, ...)
- #else /* __STDC__ */
- message(msg, va_alist)
- const char *msg;
- va_dcl
- #endif /* __STDC__ */
- {
- const char *m;
- VA_LOCAL_DECL
- m = msg;
- if (isascii(m[0]) && isdigit(m[0]) &&
- isascii(m[1]) && isdigit(m[1]) &&
- isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
- m += 4;
- VA_START(msg);
- (void) vfprintf(stderr, m, ap);
- VA_END;
- (void) fprintf(stderr, "n");
- }
- /*VARARGS1*/
- void
- #ifdef __STDC__
- syserr(const char *msg, ...)
- #else /* __STDC__ */
- syserr(msg, va_alist)
- const char *msg;
- va_dcl
- #endif /* __STDC__ */
- {
- const char *m;
- VA_LOCAL_DECL
- m = msg;
- if (isascii(m[0]) && isdigit(m[0]) &&
- isascii(m[1]) && isdigit(m[1]) &&
- isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
- m += 4;
- VA_START(msg);
- (void) vfprintf(stderr, m, ap);
- VA_END;
- (void) fprintf(stderr, "n");
- }
- void
- dumpfd(fd, printclosed, logit)
- int fd;
- bool printclosed;
- bool logit;
- {
- return;
- }