io.c revision 22473
1/*
2 * Copyright (c) 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1989, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static const char sccsid[] = "@(#)calendar.c  8.3 (Berkeley) 3/25/94";
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <stdio.h>
46#include <ctype.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <unistd.h>
50#include <err.h>
51#include <errno.h>
52#include <locale.h>
53#include <string.h>
54#include <sys/uio.h>
55#include <sys/time.h>
56#include <stdlib.h>
57#include <pwd.h>
58#include <sys/wait.h>
59
60#include "pathnames.h"
61#include "calendar.h"
62
63
64char *calendarFile = "calendar";  /* default calendar file */
65char *calendarHome = ".calendar"; /* HOME */
66char *calendarNoMail = "nomail";  /* don't sent mail if this file exist */
67
68struct fixs neaster, npaskha;
69
70struct iovec header[] = {
71	{"From: ", 6},
72	{NULL, 0},
73	{" (Reminder Service)\nTo: ", 24},
74	{NULL, 0},
75	{"\nSubject: ", 10},
76	{NULL, 0},
77	{"'s Calendar\nPrecedence: bulk\n\n",  30},
78};
79
80
81void
82cal()
83{
84	register int printing;
85	register char *p;
86	FILE *fp;
87	int ch, l;
88	int month;
89	int day;
90	int var;
91	char buf[2048 + 1];
92
93	if ((fp = opencal()) == NULL)
94		return;
95	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
96		if ((p = strchr(buf, '\n')) != NULL)
97			*p = '\0';
98		else
99			while ((ch = getchar()) != '\n' && ch != EOF);
100		for (l = strlen(buf);
101		     l > 0 && isspace((unsigned char)buf[l - 1]);
102		     l--)
103			;
104		buf[l] = '\0';
105		if (buf[0] == '\0')
106			continue;
107		if (strncmp(buf, "LANG=", 5) == 0) {
108			(void) setlocale(LC_ALL, buf + 5);
109			setnnames();
110			continue;
111		}
112		if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) {
113			if (neaster.name != NULL)
114				free(neaster.name);
115			neaster.name = strdup(buf + 7);
116			neaster.len = strlen(buf + 7);
117			continue;
118		}
119		if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) {
120			if (npaskha.name != NULL)
121				free(npaskha.name);
122			npaskha.name = strdup(buf + 7);
123			npaskha.len = strlen(buf + 7);
124			continue;
125		}
126		if (buf[0] != '\t') {
127			printing = isnow(buf, &month, &day, &var) ? 1 : 0;
128			if ((p = strchr(buf, '\t')) == NULL)
129				continue;
130			if (p > buf && p[-1] == '*')
131				var = 1;
132			if (printing) {
133				struct tm tm;
134				char dbuf[30];
135
136				tm.tm_sec = 0;  /* unused */
137				tm.tm_min = 0;  /* unused */
138				tm.tm_hour = 0; /* unused */
139				tm.tm_wday = 0; /* unused */
140				tm.tm_mon = month - 1;
141				tm.tm_mday = day;
142				tm.tm_year = tp->tm_year; /* unused */
143				(void)strftime(dbuf, sizeof(dbuf), "%c", &tm);
144				dbuf[10] = '\0';
145				(void)fprintf(fp, "%s%c%s\n",
146				    dbuf + 4/* skip weekdays */,
147				    var ? '*' : ' ', p);
148			}
149		}
150		else if (printing)
151			fprintf(fp, "%s\n", buf);
152	}
153	closecal(fp);
154}
155
156int
157getfield(p, endp, flags)
158	char *p, **endp;
159	int *flags;
160{
161	int val, var;
162	char *start, savech;
163
164	for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
165	if (*p == '*') {			/* `*' is current month */
166		*flags |= F_ISMONTH;
167		*endp = p+1;
168		return (tp->tm_mon + 1);
169	}
170	if (isdigit((unsigned char)*p)) {
171		val = strtol(p, &p, 10);	/* if 0, it's failure */
172		for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
173		*endp = p;
174		return (val);
175	}
176	for (start = p; isalpha((unsigned char)*++p););
177
178	/* Sunday-1 */
179	if (*p == '+' || *p == '-')
180	    for(; isdigit((unsigned char)*++p););
181
182	savech = *p;
183	*p = '\0';
184
185	/* Month */
186	if ((val = getmonth(start)) != 0)
187		*flags |= F_ISMONTH;
188
189	/* Day */
190	else if ((val = getday(start)) != 0) {
191	    *flags |= F_ISDAY;
192
193	    /* variable weekday */
194	    if ((var = getdayvar(start)) != 0) {
195		if (var <=5 && var >= -4)
196		    val += var * 10;
197#ifdef DEBUG
198		printf("var: %d\n", var);
199#endif
200	    }
201	}
202
203	/* Easter */
204	else if ((val = geteaster(start, tp->tm_year + 1900)) != 0)
205	    *flags |= F_EASTER;
206
207	/* Paskha */
208	else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0)
209	    *flags |= F_EASTER;
210
211	/* undefined rest */
212	else {
213		*p = savech;
214		return (0);
215	}
216	for (*p = savech; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
217	*endp = p;
218	return (val);
219}
220
221char path[MAXPATHLEN + 1];
222
223FILE *
224opencal()
225{
226	uid_t uid;
227	int fd, pdes[2];
228	struct stat sbuf;
229
230	/* open up calendar file as stdin */
231	if (!freopen(calendarFile, "r", stdin)) {
232		if (doall) {
233		    if (chdir(calendarHome) != 0)
234			return (NULL);
235		    if (stat(calendarNoMail, &sbuf) == 0)
236		        return (NULL);
237		    if (!freopen(calendarFile, "r", stdin))
238		        return (NULL);
239		} else {
240		        chdir(getenv("HOME"));
241			if (!(chdir(calendarHome) == 0 &&
242			      freopen(calendarFile, "r", stdin)))
243				errx(1, "no calendar file: ``%s'' or ``~/%s/%s\n", calendarFile, calendarHome, calendarFile);
244		}
245	}
246	if (pipe(pdes) < 0)
247		return (NULL);
248	switch (vfork()) {
249	case -1:			/* error */
250		(void)close(pdes[0]);
251		(void)close(pdes[1]);
252		return (NULL);
253	case 0:
254		/* child -- stdin already setup, set stdout to pipe input */
255		if (pdes[1] != STDOUT_FILENO) {
256			(void)dup2(pdes[1], STDOUT_FILENO);
257			(void)close(pdes[1]);
258		}
259		(void)close(pdes[0]);
260		uid = geteuid();
261		if (setuid(getuid()) < 0) {
262			fprintf(stderr, "calendar: first setuid failed\n");
263			_exit(1);
264		};
265		if (setgid(getegid()) < 0) {
266			fprintf(stderr, "calendar: setegid failed\n");
267			_exit(1);
268		}
269		if (setuid(uid) < 0) {
270			fprintf(stderr, "caelndar: setuid failed\n");
271			_exit(1);
272		}
273		execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);
274		(void)fprintf(stderr,
275		    "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno));
276		_exit(1);
277	}
278	/* parent -- set stdin to pipe output */
279	(void)dup2(pdes[0], STDIN_FILENO);
280	(void)close(pdes[0]);
281	(void)close(pdes[1]);
282
283	/* not reading all calendar files, just set output to stdout */
284	if (!doall)
285		return (stdout);
286
287	/* set output to a temporary file, so if no output don't send mail */
288	(void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
289	if ((fd = mkstemp(path)) < 0)
290		return (NULL);
291	return (fdopen(fd, "w+"));
292}
293
294void
295closecal(fp)
296	FILE *fp;
297{
298	uid_t uid;
299	struct stat sbuf;
300	int nread, pdes[2], status;
301	char buf[1024];
302
303	if (!doall)
304		return;
305
306	(void)rewind(fp);
307	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
308		goto done;
309	if (pipe(pdes) < 0)
310		goto done;
311	switch (vfork()) {
312	case -1:			/* error */
313		(void)close(pdes[0]);
314		(void)close(pdes[1]);
315		goto done;
316	case 0:
317		/* child -- set stdin to pipe output */
318		if (pdes[0] != STDIN_FILENO) {
319			(void)dup2(pdes[0], STDIN_FILENO);
320			(void)close(pdes[0]);
321		}
322		(void)close(pdes[1]);
323		uid = geteuid();
324		if (setuid(getuid()) < 0) {
325			fprintf(stderr, "calendar: setuid failed\n");
326			_exit(1);
327		};
328		if (setgid(getegid()) < 0) {
329			fprintf(stderr, "calendar: setegid failed\n");
330			_exit(1);
331		}
332		if (setuid(uid) < 0) {
333			fprintf(stderr, "caelndar: setuid failed\n");
334			_exit(1);
335		}
336		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
337		    "\"Reminder Service\"", "-f", "root", NULL);
338		(void)fprintf(stderr,
339		    "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno));
340		_exit(1);
341	}
342	/* parent -- write to pipe input */
343	(void)close(pdes[0]);
344
345	header[1].iov_base = header[3].iov_base = pw->pw_name;
346	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
347	writev(pdes[1], header, 7);
348	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
349		(void)write(pdes[1], buf, nread);
350	(void)close(pdes[1]);
351done:	(void)fclose(fp);
352	(void)unlink(path);
353	while (wait(&status) >= 0);
354}
355
356