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