xref: /dragonfly/usr.bin/calendar/io.c (revision 07a2f99c)
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  * @(#) Copyright (c) 1989, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)calendar.c  8.3 (Berkeley) 3/25/94
35  * $FreeBSD: src/usr.bin/calendar/io.c,v 1.21 2007/06/09 05:54:13 grog Exp $
36  */
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/uio.h>
42 #include <sys/wait.h>
43 
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <langinfo.h>
48 #include <locale.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include "pathnames.h"
56 #include "calendar.h"
57 
58 const char *calendarFile = "calendar.all";  /* default calendar file */
59 const char *calendarHomes[] = { ".calendar", _PATH_INCLUDE }; /* HOME */
60 const char *calendarNoMail = "nomail";  /* don't send mail if this file exist */
61 
62 struct fixs neaster, npaskha;
63 
64 char	hdr_from[] = "From: ";
65 char	hdr_to[] = " (Reminder Service)\nTo: ";
66 char	hdr_subj[] = "\nSubject: ";
67 char	hdr_prec[] = "'s Calendar\nPrecedence: bulk\n\n";
68 
69 struct iovec header[] = {
70 	{hdr_from, 6},
71 	{NULL, 0},
72 	{hdr_to, 24},
73 	{NULL, 0},
74 	{hdr_subj, 10},
75 	{NULL, 0},
76 	{hdr_prec, 30},
77 };
78 
79 static FILE	*opencal(void);
80 static void	 closecal(FILE *);
81 
82 void
83 cal(void)
84 {
85 	int printing;
86 	char *p;
87 	FILE *fp;
88 	int ch, l;
89 	int month;
90 	int day;
91 	int var;
92 	static int d_first = -1;
93 	char buf[2048 + 1];
94 	struct event *events = NULL;
95 
96 	if ((fp = opencal()) == NULL)
97 		return;
98 	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
99 		if ((p = strchr(buf, '\n')) != NULL)
100 			*p = '\0';
101 		else
102 			while ((ch = getchar()) != '\n' && ch != EOF);
103 		for (l = strlen(buf);
104 		     l > 0 && isspace((unsigned char)buf[l - 1]);
105 		     l--)
106 			;
107 		buf[l] = '\0';
108 		if (buf[0] == '\0')
109 			continue;
110 		if (strncmp(buf, "LANG=", 5) == 0) {
111 			setlocale(LC_ALL, buf + 5);
112 			d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
113 			setnnames();
114 			continue;
115 		}
116 		if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) {
117 			if (neaster.name != NULL)
118 				free(neaster.name);
119 			if ((neaster.name = strdup(buf + 7)) == NULL)
120 				errx(EXIT_FAILURE, "cannot allocate memory");
121 			neaster.len = strlen(buf + 7);
122 			continue;
123 		}
124 		if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) {
125 			if (npaskha.name != NULL)
126 				free(npaskha.name);
127 			if ((npaskha.name = strdup(buf + 7)) == NULL)
128 				errx(EXIT_FAILURE, "cannot allocate memory");
129 			npaskha.len = strlen(buf + 7);
130 			continue;
131 		}
132 		if (buf[0] != '\t') {
133 			printing = isnow(buf, &month, &day, &var) ? 1 : 0;
134 			if ((p = strchr(buf, '\t')) == NULL)
135 				continue;
136 			if (p > buf && p[-1] == '*')
137 				var = 1;
138 			if (printing) {
139 				struct tm tm;
140 				char dbuf[80];
141 
142 				if (d_first < 0)
143 					d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
144 				tm.tm_sec = 0;  /* unused */
145 				tm.tm_min = 0;  /* unused */
146 				tm.tm_hour = 0; /* unused */
147 				tm.tm_wday = 0; /* unused */
148 				tm.tm_mon = month - 1;
149 				tm.tm_mday = day;
150 				tm.tm_year = tp->tm_year; /* unused */
151 				strftime(dbuf, sizeof(dbuf),
152 				    d_first ? "%e %b" : "%b %e",
153 				    &tm);
154 				events = event_add(events, month, day, dbuf, var, p);
155 			}
156 		}
157 		else if (printing)
158 			event_continue(events, buf);
159 	}
160 
161 	event_print_all(fp, events);
162 	closecal(fp);
163 }
164 
165 /*
166  * Functions to handle buffered calendar events.
167  */
168 struct event *
169 event_add(struct event *events, int month, int day, char *date, int var, char *txt)
170 {
171 	struct event *e;
172 
173 	e = (struct event *)calloc(1, sizeof(struct event));
174 	if (e == NULL)
175 		errx(EXIT_FAILURE, "event_add: cannot allocate memory");
176 	e->month = month;
177 	e->day = day;
178 	e->var = var;
179 	e->date = strdup(date);
180 	if (e->date == NULL)
181 		errx(EXIT_FAILURE, "event_add: cannot allocate memory");
182 	e->text = strdup(txt);
183 	if (e->text == NULL)
184 		errx(EXIT_FAILURE, "event_add: cannot allocate memory");
185 	e->next = events;
186 
187 	return e;
188 }
189 
190 void
191 event_continue(struct event *e, char *txt)
192 {
193 	char *text;
194 
195 	text = strdup(e->text);
196 	if (text == NULL)
197 		errx(EXIT_FAILURE, "event_continue: cannot allocate memory");
198 
199 	free(e->text);
200 	e->text = (char *)malloc(strlen(text) + strlen(txt) + 3);
201 	if (e->text == NULL)
202 		errx(EXIT_FAILURE, "event_continue: cannot allocate memory");
203 	strcpy(e->text, text);
204 	strcat(e->text, "\n");
205 	strcat(e->text, txt);
206 	free(text);
207 
208 	return;
209 }
210 
211 void
212 event_print_all(FILE *fp, struct event *events)
213 {
214 	struct event *e, *e_next;
215 	int daycount = f_dayAfter + f_dayBefore;
216 	int daycounter;
217 	int day, month;
218 
219 	for (daycounter = 0; daycounter <= daycount; daycounter++) {
220 		day = tp->tm_yday - f_dayBefore + daycounter;
221 		if (day < 0) day += yrdays;
222 		if (day >= yrdays) day -= yrdays;
223 
224 		month = 1;
225 		while (month <= 12) {
226 			if (day <= cumdays[month])
227 				break;
228 			month++;
229 		}
230 		month--;
231 		day -= cumdays[month];
232 
233 #ifdef DEBUG
234 		fprintf(stderr,"event_print_allmonth: %d, day: %d\n",month,day);
235 #endif
236 
237 		for (e = events; e != NULL; e = e_next ) {
238 			e_next = e->next;
239 
240 			if (month != e->month || day != e->day)
241 				continue;
242 
243 			fprintf(fp, "%s%c%s\n", e->date,
244 			    e->var ? '*' : ' ', e->text);
245 		}
246 	}
247 }
248 
249 int
250 getfield(char *p, char **endp, int *flags)
251 {
252 	int val, var;
253 	char *start, savech;
254 
255 	for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p)
256                && *p != '*'; ++p);
257 	if (*p == '*') {			/* `*' is current month */
258 		*flags |= F_ISMONTH;
259 		*endp = p+1;
260 		return(tp->tm_mon + 1);
261 	}
262 	if (isdigit((unsigned char)*p)) {
263 		val = strtol(p, &p, 10);	/* if 0, it's failure */
264 		for (; !isdigit((unsigned char)*p)
265                        && !isalpha((unsigned char)*p) && *p != '*'; ++p);
266 		*endp = p;
267 		return(val);
268 	}
269 	for (start = p; isalpha((unsigned char)*++p););
270 
271 	/* Sunday-1 */
272 	if (*p == '+' || *p == '-')
273 	    for(; isdigit((unsigned char)*++p););
274 
275 	savech = *p;
276 	*p = '\0';
277 
278 	/* Month */
279 	if ((val = getmonth(start)) != 0)
280 		*flags |= F_ISMONTH;
281 
282 	/* Day */
283 	else if ((val = getday(start)) != 0) {
284 	    *flags |= F_ISDAY;
285 
286 	    /* variable weekday */
287 	    if ((var = getdayvar(start)) != 0) {
288 		if (var <=5 && var >= -4)
289 		    val += var * 10;
290 #ifdef DEBUG
291 		printf("var: %d\n", var);
292 #endif
293 	    }
294 	}
295 
296 	/* Easter */
297 	else if ((val = geteaster(start, tp->tm_year + 1900)) != 0)
298 	    *flags |= F_EASTER;
299 
300 	/* Paskha */
301 	else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0)
302 	    *flags |= F_EASTER;
303 
304 	/* undefined rest */
305 	else {
306 		*p = savech;
307 		return (0);
308 	}
309 	for (*p = savech; !isdigit((unsigned char)*p)
310                && !isalpha((unsigned char)*p) && *p != '*'; ++p);
311 	*endp = p;
312 	return(val);
313 }
314 
315 static char path[MAXPATHLEN];
316 
317 static FILE *
318 opencal(void)
319 {
320 	uid_t uid;
321 	size_t i;
322 	int fd, found, pdes[2];
323 	struct stat sbuf;
324 
325 	/* open up calendar file as stdin */
326 	if (!freopen(calendarFile, "r", stdin)) {
327 		if (doall) {
328 		    if (chdir(calendarHomes[0]) != 0)
329 			return(NULL);
330 		    if (stat(calendarNoMail, &sbuf) == 0)
331 		        return(NULL);
332 		    if (!freopen(calendarFile, "r", stdin))
333 		        return(NULL);
334 		} else {
335 		        chdir(getenv("HOME"));
336 			for (found = i = 0; i < sizeof(calendarHomes) /
337 			    sizeof(calendarHomes[0]); i++)
338 			    if (chdir(calendarHomes[i]) == 0 &&
339 			          freopen(calendarFile, "r", stdin)) {
340 				    found = 1;
341 				    break;
342 			    }
343 			if (!found)
344 			    errx(EXIT_FAILURE, "no calendar file: ``%s''", calendarFile);
345 		}
346 	}
347 	if (pipe(pdes) < 0)
348 		return(NULL);
349 	switch (fork()) {
350 	case -1:			/* error */
351 		close(pdes[0]);
352 		close(pdes[1]);
353 		return(NULL);
354 	case 0:
355 		/* child -- stdin already setup, set stdout to pipe input */
356 		if (pdes[1] != STDOUT_FILENO) {
357 			dup2(pdes[1], STDOUT_FILENO);
358 			close(pdes[1]);
359 		}
360 		close(pdes[0]);
361 		uid = geteuid();
362 		if (setuid(getuid()) < 0) {
363 			warnx("first setuid failed");
364 			_exit(EXIT_FAILURE);
365 		}
366 		if (setgid(getegid()) < 0) {
367 			warnx("setgid failed");
368 			_exit(EXIT_FAILURE);
369 		}
370 		if (setuid(uid) < 0) {
371 			warnx("setuid failed");
372 			_exit(EXIT_FAILURE);
373 		}
374 		execl(_PATH_CPP, "cpp", "-P",
375 		    "-traditional", "-nostdinc",	/* GCC specific opts */
376 		    "-I.", "-I", _PATH_INCLUDE, NULL);
377 		warn(_PATH_CPP);
378 		_exit(EXIT_FAILURE);
379 	}
380 	/* parent -- set stdin to pipe output */
381 	dup2(pdes[0], STDIN_FILENO);
382 	close(pdes[0]);
383 	close(pdes[1]);
384 
385 	/* not reading all calendar files, just set output to stdout */
386 	if (!doall)
387 		return(stdout);
388 
389 	/* set output to a temporary file, so if no output don't send mail */
390 	snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
391 	if ((fd = mkstemp(path)) < 0)
392 		return(NULL);
393 	return(fdopen(fd, "w+"));
394 }
395 
396 static void
397 closecal(FILE *fp)
398 {
399 	uid_t uid;
400 	struct stat sbuf;
401 	int nread, pdes[2], status;
402 	char buf[1024];
403 
404 	if (!doall)
405 		return;
406 
407 	rewind(fp);
408 	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
409 		goto done;
410 	if (pipe(pdes) < 0)
411 		goto done;
412 	switch (fork()) {
413 	case -1:			/* error */
414 		close(pdes[0]);
415 		close(pdes[1]);
416 		goto done;
417 	case 0:
418 		/* child -- set stdin to pipe output */
419 		if (pdes[0] != STDIN_FILENO) {
420 			dup2(pdes[0], STDIN_FILENO);
421 			close(pdes[0]);
422 		}
423 		close(pdes[1]);
424 		uid = geteuid();
425 		if (setuid(getuid()) < 0) {
426 			warnx("setuid failed");
427 			_exit(EXIT_FAILURE);
428 		}
429 		if (setgid(getegid()) < 0) {
430 			warnx("setgid failed");
431 			_exit(EXIT_FAILURE);
432 		}
433 		if (setuid(uid) < 0) {
434 			warnx("setuid failed");
435 			_exit(EXIT_FAILURE);
436 		}
437 		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
438 		    "\"Reminder Service\"", NULL);
439 		warn(_PATH_SENDMAIL);
440 		_exit(EXIT_FAILURE);
441 	}
442 	/* parent -- write to pipe input */
443 	close(pdes[0]);
444 
445 	header[1].iov_base = header[3].iov_base = pw->pw_name;
446 	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
447 	writev(pdes[1], header, 7);
448 	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
449 		write(pdes[1], buf, nread);
450 	close(pdes[1]);
451 done:	fclose(fp);
452 	unlink(path);
453 	while (wait(&status) >= 0);
454 }
455