freebsd-dev/vacation/vacation.c

1198 lines
24 KiB
C
Raw Normal View History

/*
2014-01-26 20:46:55 +00:00
* Copyright (c) 1999-2002, 2009 Proofpoint, 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.
*
*/
2002-02-17 21:56:45 +00:00
#include <sm/gen.h>
SM_IDSTR(copyright,
2014-01-26 20:46:55 +00:00
"@(#) Copyright (c) 1999-2002, 2009 Proofpoint, Inc. and its suppliers.\n\
All rights reserved.\n\
Copyright (c) 1983, 1987, 1993\n\
The Regents of the University of California. All rights reserved.\n\
2002-02-17 21:56:45 +00:00
Copyright (c) 1983 Eric P. Allman. All rights reserved.\n")
2014-05-22 03:45:17 +00:00
SM_IDSTR(id, "@(#)$Id: vacation.c,v 8.148 2013-11-22 20:52:02 ca Exp $")
2001-05-28 17:08:52 +00:00
#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 */
2020-07-14 21:40:53 +00:00
#endif
2002-02-17 21:56:45 +00:00
#include <sm/sysexits.h>
2002-02-17 21:56:45 +00:00
#include <sm/cf.h>
#include <sm/mbdb.h>
#include "sendmail/sendmail.h"
2002-02-17 21:56:45 +00:00
#include <sendmail/pathnames.h>
#include "libsmdb/smdb.h"
#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;
2007-11-05 00:04:21 +00:00
gid_t RunAsGid;
char *RunAsUserName;
int Verbose = 2;
2002-02-17 21:56:45 +00:00
bool DontInitGroups = false;
uid_t TrustedUid = 0;
BITMAP256 DontBlameSendmail;
2007-04-09 01:38:51 +00:00
static int readheaders __P((bool));
static bool junkmail __P((char *));
static bool nsearch __P((char *, char *));
static void usage __P((void));
static void setinterval __P((time_t));
static bool recent __P((void));
static void setreply __P((char *, time_t));
static void sendmessage __P((char *, char *, char *));
static void xclude __P((SM_FILE_T *));
/*
** 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
typedef struct alias
{
char *name;
struct alias *next;
} ALIAS;
ALIAS *Names = NULL;
SMDB_DATABASE *Db;
char From[MAXLINE];
2005-02-14 02:30:00 +00:00
bool CloseMBDB = false;
2002-02-17 21:56:45 +00:00
#if defined(__hpux) || defined(__osf__)
# ifndef SM_CONF_SYSLOG_INT
# define SM_CONF_SYSLOG_INT 1
2020-07-14 21:40:53 +00:00
# endif
2002-02-17 21:56:45 +00:00
#endif /* defined(__hpux) || defined(__osf__) */
#if SM_CONF_SYSLOG_INT
# define SYSLOG_RET_T int
# define SYSLOG_RET return 0
2020-07-14 21:40:53 +00:00
#else
2002-02-17 21:56:45 +00:00
# define SYSLOG_RET_T void
# define SYSLOG_RET
2020-07-14 21:40:53 +00:00
#endif
2002-02-17 21:56:45 +00:00
typedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
SYSLOG_T *msglog = syslog;
static SYSLOG_RET_T debuglog __P((int, const char *, ...));
static void eatmsg __P((void));
2002-02-17 21:56:45 +00:00
static void listdb __P((void));
/* exit after reading input */
2005-02-14 02:30:00 +00:00
#define EXITIT(excode) \
{ \
eatmsg(); \
if (CloseMBDB) \
{ \
sm_mbdb_terminate(); \
CloseMBDB = false; \
} \
return excode; \
2002-02-17 21:56:45 +00:00
}
2005-02-14 02:30:00 +00:00
#define EXITM(excode) \
{ \
if (!initdb && !list) \
eatmsg(); \
if (CloseMBDB) \
{ \
sm_mbdb_terminate(); \
CloseMBDB = false; \
} \
exit(excode); \
2002-02-17 21:56:45 +00:00
}
2001-05-28 17:08:52 +00:00
int
main(argc, argv)
int argc;
char **argv;
{
2002-06-11 21:12:04 +00:00
bool alwaysrespond = false;
bool initdb, exclude;
2002-02-17 21:56:45 +00:00
bool runasuser = false;
2002-06-11 21:12:04 +00:00
bool list = false;
int mfail = 0, ufail = 0;
int ch;
int result;
long sff;
time_t interval;
struct passwd *pw;
ALIAS *cur;
2001-05-28 17:08:52 +00:00
char *dbfilename = NULL;
char *msgfilename = NULL;
2002-02-17 21:56:45 +00:00
char *cfpath = NULL;
2010-01-26 04:17:18 +00:00
char *name = NULL;
2002-02-17 21:56:45 +00:00
char *returnaddr = NULL;
SMDB_USER_INFO user_info;
static char rnamebuf[MAXNAME];
extern int optind, opterr;
extern char *optarg;
/* 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] = '\0';
2002-02-17 21:56:45 +00:00
sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
}
else
2002-02-17 21:56:45 +00:00
sm_snprintf(rnamebuf, sizeof rnamebuf,
"Unknown UID %d", (int) RealUid);
RunAsUserName = RealUserName = rnamebuf;
2001-05-28 17:08:52 +00:00
# ifdef LOG_MAIL
openlog("vacation", LOG_PID, LOG_MAIL);
2020-07-14 21:40:53 +00:00
# else
openlog("vacation", LOG_PID);
2020-07-14 21:40:53 +00:00
# endif
opterr = 0;
2002-06-11 21:12:04 +00:00
initdb = false;
2002-02-17 21:56:45 +00:00
exclude = false;
interval = INTERVAL_UNDEF;
*From = '\0';
2002-02-17 21:56:45 +00:00
2002-06-11 21:12:04 +00:00
#define OPTIONS "a:C:df:Iijlm:R:r:s:t:Uxz"
while (mfail == 0 && ufail == 0 &&
(ch = getopt(argc, argv, OPTIONS)) != -1)
{
switch((char)ch)
{
case 'a': /* alias */
2002-02-17 21:56:45 +00:00
cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
if (cur == NULL)
{
mfail++;
break;
}
cur->name = optarg;
cur->next = Names;
Names = cur;
break;
2002-02-17 21:56:45 +00:00
case 'C':
cfpath = optarg;
break;
2001-05-28 17:08:52 +00:00
case 'd': /* debug mode */
2002-02-17 21:56:45 +00:00
msglog = debuglog;
break;
case 'f': /* alternate database */
dbfilename = optarg;
break;
case 'I': /* backward compatible */
case 'i': /* init the database */
2002-06-11 21:12:04 +00:00
initdb = true;
break;
case 'j':
alwaysrespond = true;
break;
case 'l':
2002-06-11 21:12:04 +00:00
list = true; /* list the database */
break;
case 'm': /* alternate message file */
msgfilename = optarg;
break;
2002-02-17 21:56:45 +00:00
case 'R':
returnaddr = optarg;
break;
case 'r':
if (isascii(*optarg) && isdigit(*optarg))
{
interval = atol(optarg) * SECSPERDAY;
if (interval < 0)
ufail++;
}
else
interval = ONLY_ONCE;
break;
case 's': /* alternate sender name */
2002-02-17 21:56:45 +00:00
(void) sm_strlcpy(From, optarg, sizeof From);
break;
case 't': /* SunOS: -t1d (default expire) */
break;
2001-05-28 17:08:52 +00:00
case 'U': /* run as single user mode */
2002-02-17 21:56:45 +00:00
runasuser = true;
2001-05-28 17:08:52 +00:00
break;
case 'x':
2002-02-17 21:56:45 +00:00
exclude = true;
break;
case 'z':
2002-02-17 21:56:45 +00:00
returnaddr = "<>";
break;
case '?':
default:
ufail++;
break;
}
}
argc -= optind;
argv += optind;
if (mfail != 0)
{
msglog(LOG_NOTICE,
"vacation: can't allocate memory for alias.\n");
EXITM(EX_TEMPFAIL);
}
if (ufail != 0)
usage();
if (argc != 1)
{
2002-06-11 21:12:04 +00:00
if (!initdb && !list && !exclude)
usage();
if ((pw = getpwuid(getuid())) == NULL)
{
msglog(LOG_ERR,
"vacation: no such user uid %u.\n", getuid());
EXITM(EX_NOUSER);
}
2010-01-26 04:17:18 +00:00
name = strdup(pw->pw_name);
2001-05-28 17:08:52 +00:00
user_info.smdbu_id = pw->pw_uid;
user_info.smdbu_group_id = pw->pw_gid;
2002-02-17 21:56:45 +00:00
(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
SMDB_MAX_USER_NAME_LEN);
2001-05-28 17:08:52 +00:00
if (chdir(pw->pw_dir) != 0)
{
2002-02-17 21:56:45 +00:00
msglog(LOG_NOTICE,
"vacation: no such directory %s.\n",
2001-05-28 17:08:52 +00:00
pw->pw_dir);
EXITM(EX_NOINPUT);
}
}
2001-05-28 17:08:52 +00:00
else if (runasuser)
{
2010-01-26 04:17:18 +00:00
name = strdup(*argv);
2001-05-28 17:08:52 +00:00
if (dbfilename == NULL || msgfilename == NULL)
{
msglog(LOG_NOTICE,
"vacation: -U requires setting both -f and -m\n");
EXITM(EX_NOINPUT);
}
user_info.smdbu_id = pw->pw_uid;
user_info.smdbu_group_id = pw->pw_gid;
2002-02-17 21:56:45 +00:00
(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
2001-05-28 17:08:52 +00:00
SMDB_MAX_USER_NAME_LEN);
}
else
{
2002-02-17 21:56:45 +00:00
int err;
SM_CF_OPT_T mbdbname;
SM_MBDB_T user;
2002-04-10 03:05:00 +00:00
cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
2002-02-17 21:56:45 +00:00
mbdbname.opt_name = "MailboxDatabase";
mbdbname.opt_val = "pw";
(void) sm_cf_getopt(cfpath, 1, &mbdbname);
err = sm_mbdb_initialize(mbdbname.opt_val);
if (err != EX_OK)
2001-05-28 17:08:52 +00:00
{
2002-02-17 21:56:45 +00:00
msglog(LOG_ERR,
"vacation: can't open mailbox database: %s.\n",
sm_strexit(err));
EXITM(err);
}
2005-02-14 02:30:00 +00:00
CloseMBDB = true;
2002-02-17 21:56:45 +00:00
err = sm_mbdb_lookup(*argv, &user);
if (err == EX_NOUSER)
{
msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
EXITM(EX_NOUSER);
}
if (err != EX_OK)
{
msglog(LOG_ERR,
"vacation: can't read mailbox database: %s.\n",
sm_strexit(err));
EXITM(err);
}
2010-01-26 04:17:18 +00:00
name = strdup(user.mbdb_name);
2002-02-17 21:56:45 +00:00
if (chdir(user.mbdb_homedir) != 0)
{
msglog(LOG_NOTICE,
"vacation: no such directory %s.\n",
user.mbdb_homedir);
2001-05-28 17:08:52 +00:00
EXITM(EX_NOINPUT);
}
2002-02-17 21:56:45 +00:00
user_info.smdbu_id = user.mbdb_uid;
user_info.smdbu_group_id = user.mbdb_gid;
(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
2001-05-28 17:08:52 +00:00
SMDB_MAX_USER_NAME_LEN);
}
2010-01-26 04:17:18 +00:00
if (name == NULL)
{
msglog(LOG_ERR,
"vacation: can't allocate memory for username.\n");
EXITM(EX_OSERR);
}
2001-05-28 17:08:52 +00:00
if (dbfilename == NULL)
dbfilename = VDB;
if (msgfilename == NULL)
msgfilename = VMSG;
sff = SFF_CREAT;
if (getegid() != getgid())
2001-05-28 17:08:52 +00:00
{
2002-02-17 21:56:45 +00:00
/* Allow a set-group-ID vacation binary */
RunAsGid = user_info.smdbu_group_id = getegid();
2002-02-17 21:56:45 +00:00
sff |= SFF_OPENASROOT;
2001-05-28 17:08:52 +00:00
}
2002-04-10 03:05:00 +00:00
if (getuid() == 0)
{
/* Allow root to initialize user's vacation databases */
sff |= SFF_OPENASROOT|SFF_ROOTOK;
/* ... safely */
sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
}
result = smdb_open_database(&Db, dbfilename,
2002-06-11 21:12:04 +00:00
O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
S_IRUSR|S_IWUSR, sff,
SMDB_TYPE_DEFAULT, &user_info, NULL);
if (result != SMDBE_OK)
{
msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
2002-02-17 21:56:45 +00:00
sm_errstring(result));
EXITM(EX_DATAERR);
}
2002-06-11 21:12:04 +00:00
if (list)
{
listdb();
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
exit(EX_OK);
}
if (interval != INTERVAL_UNDEF)
setinterval(interval);
2002-06-11 21:12:04 +00:00
if (initdb && !exclude)
{
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
exit(EX_OK);
}
if (exclude)
{
2002-02-17 21:56:45 +00:00
xclude(smioin);
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
EXITM(EX_OK);
}
2002-02-17 21:56:45 +00:00
if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
{
msglog(LOG_NOTICE,
"vacation: can't allocate memory for username.\n");
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
EXITM(EX_OSERR);
}
cur->name = name;
cur->next = Names;
Names = cur;
2002-06-11 21:12:04 +00:00
result = readheaders(alwaysrespond);
2001-01-21 22:17:06 +00:00
if (result == EX_OK && !recent())
{
time_t now;
(void) time(&now);
setreply(From, now);
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
2002-02-17 21:56:45 +00:00
sendmessage(name, msgfilename, returnaddr);
}
else
2001-01-21 22:17:06 +00:00
(void) Db->smdb_close(Db);
if (result == EX_NOUSER)
result = EX_OK;
exit(result);
}
/*
** EATMSG -- read stdin till EOF
**
** Parameters:
** none.
**
** Returns:
** nothing.
**
*/
2001-05-28 17:08:52 +00:00
static void
eatmsg()
{
/*
** read the rest of the e-mail and ignore it to avoid problems
** with EPIPE in sendmail
*/
while (getc(stdin) != EOF)
continue;
}
/*
** READHEADERS -- read mail headers
**
** Parameters:
2002-06-11 21:12:04 +00:00
** alwaysrespond -- respond regardless of whether msg is to me
**
** Returns:
2001-01-21 22:17:06 +00:00
** a exit code: NOUSER if no reply, OK if reply, * if error
**
** Side Effects:
** may exit().
**
*/
2001-01-21 22:17:06 +00:00
2007-04-09 01:38:51 +00:00
static int
2002-06-11 21:12:04 +00:00
readheaders(alwaysrespond)
bool alwaysrespond;
{
bool tome, cont;
register char *p;
register ALIAS *cur;
char buf[MAXLINE];
2002-06-11 21:12:04 +00:00
cont = false;
tome = alwaysrespond;
2013-04-21 16:35:04 +00:00
while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0 &&
2002-02-17 21:56:45 +00:00
*buf != '\n')
{
switch(*buf)
{
case 'F': /* "From " */
2002-02-17 21:56:45 +00:00
cont = false;
if (strncmp(buf, "From ", 5) == 0)
{
2002-02-17 21:56:45 +00:00
bool quoted = false;
p = buf + 5;
while (*p != '\0')
{
/* escaped character */
if (*p == '\\')
{
p++;
if (*p == '\0')
{
msglog(LOG_NOTICE,
"vacation: badly formatted \"From \" line.\n");
EXITIT(EX_DATAERR);
}
}
else if (*p == '"')
quoted = !quoted;
else if (*p == '\r' || *p == '\n')
break;
else if (*p == ' ' && !quoted)
break;
p++;
}
if (quoted)
{
msglog(LOG_NOTICE,
"vacation: badly formatted \"From \" line.\n");
EXITIT(EX_DATAERR);
}
*p = '\0';
/* ok since both strings have MAXLINE length */
if (*From == '\0')
2002-02-17 21:56:45 +00:00
(void) sm_strlcpy(From, buf + 5,
sizeof From);
if ((p = strchr(buf + 5, '\n')) != NULL)
*p = '\0';
if (junkmail(buf + 5))
2001-01-21 22:17:06 +00:00
EXITIT(EX_NOUSER);
}
break;
case 'P': /* "Precedence:" */
case 'p':
2002-02-17 21:56:45 +00:00
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 != '\0' && isascii(*p) && isspace(*p));
if (*p == '\0')
break;
if (strncasecmp(p, "junk", 4) == 0 ||
strncasecmp(p, "bulk", 4) == 0 ||
strncasecmp(p, "list", 4) == 0)
2001-01-21 22:17:06 +00:00
EXITIT(EX_NOUSER);
break;
case 'C': /* "Cc:" */
case 'c':
if (strncasecmp(buf, "Cc:", 3) != 0)
break;
2002-02-17 21:56:45 +00:00
cont = true;
goto findme;
case 'T': /* "To:" */
case 't':
if (strncasecmp(buf, "To:", 3) != 0)
break;
2002-02-17 21:56:45 +00:00
cont = true;
goto findme;
default:
if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
{
2002-02-17 21:56:45 +00:00
cont = false;
break;
}
findme:
for (cur = Names;
!tome && cur != NULL;
cur = cur->next)
tome = nsearch(cur->name, buf);
}
}
if (!tome)
2001-01-21 22:17:06 +00:00
EXITIT(EX_NOUSER);
if (*From == '\0')
{
msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
EXITIT(EX_DATAERR);
}
2001-01-21 22:17:06 +00:00
EXITIT(EX_OK);
}
/*
** 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?
**
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static bool
nsearch(name, str)
register char *name, *str;
{
register size_t len;
register char *s;
len = strlen(name);
for (s = str; *s != '\0'; ++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))))
2002-02-17 21:56:45 +00:00
return true;
}
2002-02-17 21:56:45 +00:00
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?
**
*/
2001-01-21 22:17:06 +00:00
struct ignore
{
char *name;
size_t len;
};
typedef struct ignore IGNORE_T;
#define MAX_USER_LEN 256 /* maximum length of local part (sender) */
/* delimiters for the local part of an address */
#define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+')
2007-04-09 01:38:51 +00:00
static bool
junkmail(from)
char *from;
{
2001-01-21 22:17:06 +00:00
bool quot;
char *e;
size_t len;
IGNORE_T *cur;
char sender[MAX_USER_LEN];
static IGNORE_T ignore[] =
{
{ "postmaster", 10 },
{ "uucp", 4 },
{ "mailer-daemon", 13 },
{ "mailer", 6 },
2001-01-21 22:17:06 +00:00
{ NULL, 0 }
};
static IGNORE_T ignorepost[] =
{
{ "-request", 8 },
{ "-relay", 6 },
2001-01-21 22:17:06 +00:00
{ "-owner", 6 },
{ NULL, 0 }
};
static IGNORE_T ignorepre[] =
{
{ "owner-", 6 },
{ NULL, 0 }
};
/*
2001-01-21 22:17:06 +00:00
** 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
*/
2002-02-17 21:56:45 +00:00
quot = false;
2001-01-21 22:17:06 +00:00
e = from;
len = 0;
while (*e != '\0' && (quot || !isdelim(*e)))
{
if (*e == '"')
{
quot = !quot;
++e;
continue;
2001-01-21 22:17:06 +00:00
}
if (*e == '\\')
{
if (*(++e) == '\0')
{
/* '\\' at end of string? */
break;
}
if (len < MAX_USER_LEN)
sender[len++] = *e;
++e;
continue;
}
if (*e == '!' && !quot)
{
len = 0;
sender[len] = '\0';
}
else
if (len < MAX_USER_LEN)
sender[len++] = *e;
++e;
}
2001-01-21 22:17:06 +00:00
if (len < MAX_USER_LEN)
sender[len] = '\0';
else
sender[MAX_USER_LEN - 1] = '\0';
if (len <= 0)
2002-02-17 21:56:45 +00:00
return false;
2001-01-21 22:17:06 +00:00
#if 0
if (quot)
2002-02-17 21:56:45 +00:00
return false; /* syntax error... */
2020-07-14 21:40:53 +00:00
#endif
2001-01-21 22:17:06 +00:00
/* test prefixes */
for (cur = ignorepre; cur->name != NULL; ++cur)
{
if (len >= cur->len &&
strncasecmp(cur->name, sender, cur->len) == 0)
2002-02-17 21:56:45 +00:00
return true;
2001-01-21 22:17:06 +00:00
}
/*
** If the name is truncated, don't test the rest.
** We could extract the "tail" of the sender address and
** compare it it ignorepost, however, it seems not worth
** the effort.
** The address surely can't match any entry in ignore[]
** (as long as all of them are shorter than MAX_USER_LEN).
*/
if (len > MAX_USER_LEN)
2002-02-17 21:56:45 +00:00
return false;
2001-01-21 22:17:06 +00:00
/* test full local parts */
for (cur = ignore; cur->name != NULL; ++cur)
2001-01-21 22:17:06 +00:00
{
if (len == cur->len &&
strncasecmp(cur->name, sender, cur->len) == 0)
2002-02-17 21:56:45 +00:00
return true;
2001-01-21 22:17:06 +00:00
}
/* test postfixes */
for (cur = ignorepost; cur->name != NULL; ++cur)
{
if (len >= cur->len &&
2001-01-21 22:17:06 +00:00
strncasecmp(cur->name, e - cur->len - 1,
cur->len) == 0)
2002-02-17 21:56:45 +00:00
return true;
}
2002-02-17 21:56:45 +00:00
return false;
}
#define VIT "__VACATION__INTERVAL__TIMER__"
/*
** RECENT --
** find out if user has gotten a vacation message recently.
**
** Parameters:
** none.
**
** Returns:
2002-02-17 21:56:45 +00:00
** true iff user has gotten a vacation message recently.
**
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static bool
recent()
{
SMDB_DBENT key, data;
time_t then, next;
2002-02-17 21:56:45 +00:00
bool trydomain = false;
int st;
char *domain;
memset(&key, '\0', sizeof key);
memset(&data, '\0', sizeof data);
/* get interval time */
2001-01-21 22:17:06 +00:00
key.data = VIT;
key.size = sizeof(VIT);
st = Db->smdb_get(Db, &key, &data, 0);
if (st != SMDBE_OK)
next = SECSPERDAY * DAYSPERWEEK;
else
2001-01-21 22:17:06 +00:00
memmove(&next, data.data, sizeof(next));
memset(&data, '\0', sizeof data);
/* get record for this address */
2001-01-21 22:17:06 +00:00
key.data = From;
key.size = strlen(From);
do
{
st = Db->smdb_get(Db, &key, &data, 0);
if (st == SMDBE_OK)
{
2001-01-21 22:17:06 +00:00
memmove(&then, data.data, sizeof(then));
if (next == ONLY_ONCE || then == ONLY_ONCE ||
then + next > time(NULL))
2002-02-17 21:56:45 +00:00
return true;
}
if ((trydomain = !trydomain) &&
(domain = strchr(From, '@')) != NULL)
{
2001-01-21 22:17:06 +00:00
key.data = domain;
key.size = strlen(domain);
}
} while (trydomain);
2002-02-17 21:56:45 +00:00
return false;
}
/*
** SETINTERVAL --
** store the reply interval
**
** Parameters:
** interval -- time interval for replies.
**
** Returns:
** nothing.
**
** Side Effects:
** stores the reply interval in database.
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static void
setinterval(interval)
time_t interval;
{
SMDB_DBENT key, data;
memset(&key, '\0', sizeof key);
memset(&data, '\0', sizeof data);
2001-01-21 22:17:06 +00:00
key.data = VIT;
key.size = sizeof(VIT);
data.data = (char*) &interval;
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.
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static void
setreply(from, when)
char *from;
time_t when;
{
SMDB_DBENT key, data;
memset(&key, '\0', sizeof key);
memset(&data, '\0', sizeof data);
2001-01-21 22:17:06 +00:00
key.data = from;
key.size = strlen(from);
data.data = (char*) &when;
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.
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static void
xclude(f)
2002-02-17 21:56:45 +00:00
SM_FILE_T *f;
{
char buf[MAXLINE], *p;
if (f == NULL)
return;
2013-04-21 16:35:04 +00:00
while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) >= 0)
{
if ((p = strchr(buf, '\n')) != NULL)
*p = '\0';
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.
2002-02-17 21:56:45 +00:00
** sender -- use as sender address
**
** Returns:
** nothing.
**
** Side Effects:
** sends vacation reply.
*/
2001-05-28 17:08:52 +00:00
2007-04-09 01:38:51 +00:00
static void
2002-02-17 21:56:45 +00:00
sendmessage(myname, msgfn, sender)
char *myname;
char *msgfn;
2002-02-17 21:56:45 +00:00
char *sender;
{
2002-02-17 21:56:45 +00:00
SM_FILE_T *mfp, *sfp;
int i;
int pvect[2];
2001-05-28 17:08:52 +00:00
char *pv[8];
char buf[MAXLINE];
2002-02-17 21:56:45 +00:00
mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
if (mfp == NULL)
{
if (msgfn[0] == '/')
msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
else
msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
myname, msgfn);
exit(EX_NOINPUT);
}
if (pipe(pvect) < 0)
{
2002-02-17 21:56:45 +00:00
msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
exit(EX_OSERR);
}
2001-05-28 17:08:52 +00:00
pv[0] = "sendmail";
pv[1] = "-oi";
pv[2] = "-f";
2002-02-17 21:56:45 +00:00
if (sender != NULL)
pv[3] = sender;
2001-05-28 17:08:52 +00:00
else
pv[3] = myname;
pv[4] = "--";
pv[5] = From;
pv[6] = NULL;
i = fork();
if (i < 0)
{
2002-02-17 21:56:45 +00:00
msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
exit(EX_OSERR);
}
if (i == 0)
{
(void) dup2(pvect[0], 0);
(void) close(pvect[0]);
(void) close(pvect[1]);
2002-02-17 21:56:45 +00:00
(void) sm_io_close(mfp, SM_TIME_DEFAULT);
2001-05-28 17:08:52 +00:00
(void) execv(_PATH_SENDMAIL, pv);
msglog(LOG_ERR, "vacation: can't exec %s: %s",
2002-02-17 21:56:45 +00:00
_PATH_SENDMAIL, sm_errstring(errno));
exit(EX_UNAVAILABLE);
}
/* check return status of the following calls? XXX */
(void) close(pvect[0]);
2002-02-17 21:56:45 +00:00
if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
(void *) &(pvect[1]),
SM_IO_WRONLY, NULL)) != NULL)
{
2010-01-26 04:17:18 +00:00
#if _FFR_VAC_WAIT4SM
# ifdef WAITUNION
union wait st;
2020-07-14 21:40:53 +00:00
# else
2010-01-26 04:17:18 +00:00
auto int st;
2020-07-14 21:40:53 +00:00
# endif
2010-01-26 04:17:18 +00:00
#endif /* _FFR_VAC_WAIT4SM */
2002-02-17 21:56:45 +00:00
(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
"Auto-Submitted: auto-replied\n");
2013-04-21 16:35:04 +00:00
while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf) >= 0)
2002-02-17 21:56:45 +00:00
(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
(void) sm_io_close(mfp, SM_TIME_DEFAULT);
(void) sm_io_close(sfp, SM_TIME_DEFAULT);
2010-01-26 04:17:18 +00:00
#if _FFR_VAC_WAIT4SM
(void) wait(&st);
2020-07-14 21:40:53 +00:00
#endif
}
else
{
2002-02-17 21:56:45 +00:00
(void) sm_io_close(mfp, SM_TIME_DEFAULT);
msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
exit(EX_UNAVAILABLE);
}
}
2007-04-09 01:38:51 +00:00
static void
usage()
{
2001-05-28 17:08:52 +00:00
msglog(LOG_NOTICE,
2004-08-01 01:04:57 +00:00
"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",
getuid());
exit(EX_USAGE);
}
/*
** LISTDB -- list the contents of the vacation database
**
** Parameters:
** none.
**
** Returns:
** nothing.
*/
static void
listdb()
{
int result;
time_t t;
SMDB_CURSOR *cursor = NULL;
SMDB_DBENT db_key, db_value;
memset(&db_key, '\0', sizeof db_key);
memset(&db_value, '\0', sizeof db_value);
result = Db->smdb_cursor(Db, &cursor, 0);
if (result != SMDBE_OK)
{
2002-02-17 21:56:45 +00:00
sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"vacation: set cursor: %s\n",
sm_errstring(result));
return;
}
while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
{
2002-06-11 21:12:04 +00:00
char *timestamp;
/* skip magic VIT entry */
2003-02-08 20:31:29 +00:00
if (db_key.size == strlen(VIT) + 1 &&
2001-01-21 22:17:06 +00:00
strncmp((char *)db_key.data, VIT,
(int)db_key.size - 1) == 0)
continue;
/* skip bogus values */
2001-01-21 22:17:06 +00:00
if (db_value.size != sizeof t)
{
2002-02-17 21:56:45 +00:00
sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"vacation: %.*s invalid time stamp\n",
(int) db_key.size, (char *) db_key.data);
continue;
}
2001-01-21 22:17:06 +00:00
memcpy(&t, db_value.data, sizeof t);
2001-01-21 22:17:06 +00:00
if (db_key.size > 40)
db_key.size = 40;
2002-06-11 21:12:04 +00:00
if (t <= 0)
{
/* must be an exclude */
timestamp = "(exclusion)\n";
}
else
{
timestamp = ctime(&t);
}
2002-02-17 21:56:45 +00:00
sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
(int) db_key.size, (char *) db_key.data,
2002-06-11 21:12:04 +00:00
timestamp);
memset(&db_key, '\0', sizeof db_key);
memset(&db_value, '\0', sizeof db_value);
}
if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
{
2002-02-17 21:56:45 +00:00
sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"vacation: get value at cursor: %s\n",
sm_errstring(result));
if (cursor != NULL)
{
(void) cursor->smdbc_close(cursor);
cursor = NULL;
}
return;
}
(void) cursor->smdbc_close(cursor);
cursor = NULL;
}
/*
** DEBUGLOG -- write message to standard error
**
** Append a message to the standard error for the convenience of
** end-users debugging without access to the syslog messages.
**
** Parameters:
** i -- syslog log level
** fmt -- string format
**
** Returns:
** nothing.
*/
/*VARARGS2*/
2002-02-17 21:56:45 +00:00
static SYSLOG_RET_T
#ifdef __STDC__
debuglog(int i, const char *fmt, ...)
#else /* __STDC__ */
debuglog(i, fmt, va_alist)
int i;
const char *fmt;
va_dcl
#endif /* __STDC__ */
{
2002-02-17 21:56:45 +00:00
SM_VA_LOCAL_DECL
2002-02-17 21:56:45 +00:00
SM_VA_START(ap, fmt);
sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
SM_VA_END(ap);
SYSLOG_RET;
}