/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
/*
* Possible date formats include any combination of:
* 3-charmonth (January, Jan, Jan)
* 3-charweekday (Friday, Monday, mon.)
* numeric month or day (1, 2, 04)
*
* Any character may separate them, or they may not be separated. Any line,
* following a line that is matched, that starts with "whitespace", is shown
* along with the matched line.
*/
static bool
isnow(char *endp)
{
int day;
int flags;
int month;
int v1;
int v2;
flags = 0;
/* didn't recognize anything, skip it */
if (!(v1 = getfield(endp, &endp, &flags)))
return false;
if ((flags & (F_ISDAY|F_ISDOW)) || v1 > 12) {
/* found a day */
day = v1;
/* if no recognizable month, assume wildcard ('*') month */
if ((month = getfield(endp, &endp, &flags)) == 0) {
flags |= F_ISMONTH | F_WILDMONTH;
month = tp->tm_mon + 1;
}
} else if (flags & F_ISMONTH) {
month = v1;
/* if no recognizable day, assume the first */
if ((day = getfield(endp, &endp, &flags)) == 0)
day = 1;
} else {
v2 = getfield(endp, &endp, &flags);
if (flags & F_ISMONTH) {
day = v1;
month = v2;
} else {
/* F_ISDAY set, v2 > 12, or no way to tell */
month = v1;
/* if no recognizable day, assume the first */
day = v2 ? v2 : 1;
}
}
/* if month is out of range, treat it as '*' */
if (month < 1 || month > 12) {
flags |= F_ISMONTH | F_WILDMONTH;
month = tp->tm_mon + 1;
}
if (flags & F_WILDMONTH && flags & F_WILDDAY)
return true;
if (flags & F_WILDMONTH && flags & F_ISDAY && day == tp->tm_mday)
return true;
if (flags & F_WILDMONTH && flags & F_ISDOW && day == tp->tm_wday + 1)
return true;
if (flags & F_ISDOW)
day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
day = cumdays[month] + day;
/* if today or today + offset days */
if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
return true;
/* if number of days left in this year + days to event in next year */
if (yrdays - tp->tm_yday + day <= offset)
return true;
return false;
}
static int
getfield(char *p, char **endp, int *flags)
{
int val;
char *start;
char savech;
/*
* note this macro has an arg that isn't used ... it is retained
* (it is believed) to make the macro call look more "natural"
* and suggest at the call site what is happening.
*/
#define FLDCHAR(a) (*p != '\0' && !isdigit((unsigned char)*p) && \
!isalpha((unsigned char)*p) && *p != '*')
val = 0;
for (/*EMPTY*/; FLDCHAR(*p); ++p)
continue;
if (*p == '*') { /* `*' is current month */
if (!(*flags & F_ISMONTH)) {
*flags |= F_ISMONTH | F_WILDMONTH;
*endp = p + 1;
return tp->tm_mon + 1;
} else {
*flags |= F_ISDAY | F_WILDDAY;
*endp = p + 1;
return 1;
}
}
if (isdigit((unsigned char)*p)) {
val = (int)strtol(p, &p, 10); /* if 0, it's failure */
for (/*EMPTY*/; FLDCHAR(*p); ++p)
continue;
*endp = p;
return val;
}
for (start = p; *p != '\0' && isalpha((unsigned char)*p); p++)
continue;
/* not reading all calendar files, just set output to stdout */
if (!doall)
return stdout;
/*
* Set output to a temporary file, so if no output
* don't send mail.
*/
(void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
if ((fd = mkstemp(path)) == -1) {
warn("Cannot create temporary file");
return NULL;
}
return fdopen(fd, "w+");
}
/*NOTREACHED*/
}
static int
tryopen(const char *pathname, int flags)
{
int fd, serrno, zero;
struct stat st;
/*
* XXX: cpp_restricted has inverted sense; it is false by default,
* and -x sets it to true. CPP_RESTRICTED is set in the environment
* if cpp_restricted is false... go figure. This should be fixed
* later.
*/
if (doall && cpp_restricted == false) {
/*
* We are running with the user's euid, so they can't
* cause any mayhem (e.g. opening rewinding tape
* devices) that they couldn't do easily enough on
* their own. All we really need to worry about is opens
* that hang, because that would DoS the calendar run.
*/
fd = open(pathname, flags | O_NONBLOCK);
if (fd == -1) {
return -1;
}
if (fstat(fd, &st) == -1) {
serrno = errno;
close(fd);
errno = serrno;
return -1;
}
if (S_ISCHR(st.st_mode) ||
S_ISBLK(st.st_mode) ||
S_ISFIFO(st.st_mode)) {
close(fd);
/* Call shenanigans in the daily output */
errno = EPERM;
warn("%s: %s", pw->pw_name, pathname);
errno = EPERM;
return -1;
}
if (S_ISDIR(st.st_mode)) {
/* Don't warn about this */
close(fd);
errno = EISDIR;
return -1;
}
if (!S_ISREG(st.st_mode)) {
/* There shouldn't be other cases to go here */
close(fd);
errno = EINVAL;
return -1;
}
zero = 0;
if (ioctl(fd, FIONBIO, &zero) == -1) {
serrno = errno;
warn("%s: %s: FIONBIO", pw->pw_name, pathname);
close(fd);
errno = serrno;
return -1;
}
return fd;
} else {
return open(pathname, flags);
}
}
static void
closecal(FILE *fp)
{
struct stat sbuf;
ssize_t nread;
int pdes[2];
int status;
char buf[1024];
if (!doall)
return;
(void)rewind(fp);
if (fstat(fileno(fp), &sbuf) == -1 || sbuf.st_size == 0)
goto done;
if (pipe(pdes) == -1)
goto done;
switch (fork()) {
case -1:
/* error */
(void)close(pdes[0]);
(void)close(pdes[1]);
break;
case 0:
/* child -- set stdin to pipe output */
if (pdes[0] != STDIN_FILENO) {
(void)dup2(pdes[0], STDIN_FILENO);
(void)close(pdes[0]);
}
(void)close(pdes[1]);
if (doall) {
/* become the user properly */
changeuser();
}
(void)execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
"\"Reminder Service\"", "-f", "root", NULL);
err(EXIT_FAILURE, "Cannot exec `%s'", _PATH_SENDMAIL);
/*NOTREACHED*/
default:
/* parent -- write to pipe input */
(void)close(pdes[0]);