Annotation of src/usr.bin/calendar/calendar.c, Revision 1.4
1.4 ! deraadt 1: /* $OpenBSD: calendar.c,v 1.3 1996/09/16 16:36:04 millert Exp $ */
1.1 deraadt 2: /* $NetBSD: calendar.c,v 1.8 1995/09/02 05:38:38 jtc Exp $ */
3:
4: /*
5: * Copyright (c) 1989, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
38: static char copyright[] =
39: "@(#) Copyright (c) 1989, 1993\n\
40: The Regents of the University of California. All rights reserved.\n";
41: #endif /* not lint */
42:
43: #ifndef lint
44: #if 0
45: static char sccsid[] = "@(#)calendar.c 8.4 (Berkeley) 1/7/95";
46: #endif
1.4 ! deraadt 47: static char rcsid[] = "$OpenBSD: calendar.c,v 1.3 1996/09/16 16:36:04 millert Exp $";
1.1 deraadt 48: #endif /* not lint */
49:
50: #include <sys/param.h>
51: #include <sys/time.h>
52: #include <sys/stat.h>
53: #include <sys/uio.h>
54: #include <sys/wait.h>
55:
56: #include <ctype.h>
57: #include <err.h>
58: #include <errno.h>
59: #include <fcntl.h>
60: #include <pwd.h>
61: #include <stdio.h>
62: #include <stdlib.h>
63: #include <string.h>
64: #include <tzfile.h>
65: #include <unistd.h>
66:
67: #include "pathnames.h"
68:
69: struct passwd *pw;
70: int doall;
71:
72: void cal __P((void));
73: void closecal __P((FILE *));
74: int getday __P((char *));
75: int getfield __P((char *, char **, int *));
76: int getmonth __P((char *));
77: int isnow __P((char *));
78: FILE *opencal __P((void));
79: void settime __P((void));
80: void usage __P((void));
81:
82: int
83: main(argc, argv)
84: int argc;
85: char *argv[];
86: {
87: extern int optind;
88: int ch;
89: char *caldir;
90:
91: while ((ch = getopt(argc, argv, "-a")) != EOF)
92: switch (ch) {
93: case '-': /* backward contemptible */
94: case 'a':
95: if (getuid()) {
96: errno = EPERM;
97: err(1, NULL);
98: }
99: doall = 1;
100: break;
101: case '?':
102: default:
103: usage();
104: }
105: argc -= optind;
106: argv += optind;
107:
108: if (argc)
109: usage();
110:
111: settime();
112: if (doall)
113: while ((pw = getpwent()) != NULL) {
114: (void)setegid(pw->pw_gid);
1.4 ! deraadt 115: (void)initgroups(pw->pw_name, pw->pw_gid);
1.1 deraadt 116: (void)seteuid(pw->pw_uid);
117: if (!chdir(pw->pw_dir))
118: cal();
119: (void)seteuid(0);
120: }
121: else if ((caldir = getenv("CALENDAR_DIR")) != NULL) {
122: if(!chdir(caldir))
123: cal();
124: } else
125: cal();
126: exit(0);
127: }
128:
129: void
130: cal()
131: {
132: register int printing;
133: register char *p;
134: FILE *fp;
135: int ch;
136: char buf[2048 + 1];
137:
138: if ((fp = opencal()) == NULL)
139: return;
140: for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
141: if ((p = strchr(buf, '\n')) != NULL)
142: *p = '\0';
143: else
1.4 ! deraadt 144: while ((ch = getchar()) != '\n' && ch != EOF)
! 145: ;
1.1 deraadt 146: if (buf[0] == '\0')
147: continue;
148: if (buf[0] != '\t')
149: printing = isnow(buf) ? 1 : 0;
150: if (printing)
151: (void)fprintf(fp, "%s\n", buf);
152: }
153: closecal(fp);
154: }
155:
156: struct iovec header[] = {
157: "From: ", 6,
158: NULL, 0,
159: " (Reminder Service)\nTo: ", 24,
160: NULL, 0,
161: "\nSubject: ", 10,
162: NULL, 0,
163: "'s Calendar\nPrecedence: bulk\n\n", 30,
164: };
165:
166: /* 1-based month, 0-based days, cumulative */
167: int daytab[][14] = {
168: 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364,
169: 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,
170: };
171: struct tm *tp;
172: int *cumdays, offset, yrdays;
173: char dayname[10];
174:
175: void
176: settime()
177: {
178: time_t now;
179:
180: (void)time(&now);
181: tp = localtime(&now);
182: if (isleap(tp->tm_year + 1900)) {
183: yrdays = DAYSPERLYEAR;
184: cumdays = daytab[1];
185: } else {
186: yrdays = DAYSPERNYEAR;
187: cumdays = daytab[0];
188: }
189: /* Friday displays Monday's events */
190: offset = tp->tm_wday == 5 ? 3 : 1;
191: header[5].iov_base = dayname;
192: header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
193: }
194:
195: /*
196: * Possible date formats include any combination of:
197: * 3-charmonth (January, Jan, Jan)
198: * 3-charweekday (Friday, Monday, mon.)
199: * numeric month or day (1, 2, 04)
200: *
201: * Any character may separate them, or they may not be separated. Any line,
202: * following a line that is matched, that starts with "whitespace", is shown
203: * along with the matched line.
204: */
205: int
206: isnow(endp)
207: char *endp;
208: {
209: int day, flags, month, v1, v2;
210:
211: #define F_ISMONTH 0x01
212: #define F_ISDAY 0x02
213: flags = 0;
214: /* didn't recognize anything, skip it */
215: if (!(v1 = getfield(endp, &endp, &flags)))
216: return (0);
217: if (flags & F_ISDAY || v1 > 12) {
218: /* found a day */
219: day = v1;
220: month = tp->tm_mon + 1;
221: } else if (flags & F_ISMONTH) {
222: month = v1;
223: /* if no recognizable day, assume the first */
224: if (!(day = getfield(endp, &endp, &flags)))
225: day = 1;
226: } else {
227: v2 = getfield(endp, &endp, &flags);
228: if (flags & F_ISMONTH) {
229: day = v1;
230: month = v2;
231: } else {
232: /* F_ISDAY set, v2 > 12, or no way to tell */
233: month = v1;
234: /* if no recognizable day, assume the first */
235: day = v2 ? v2 : 1;
236: }
237: }
238: if (flags & F_ISDAY)
239: day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
240: day = cumdays[month] + day;
241:
242: /* if today or today + offset days */
243: if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
244: return (1);
245: /* if number of days left in this year + days to event in next year */
246: if (yrdays - tp->tm_yday + day <= offset)
247: return (1);
248: return (0);
249: }
250:
251: int
252: getfield(p, endp, flags)
253: char *p, **endp;
254: int *flags;
255: {
256: int val;
257: char *start, savech;
258:
1.4 ! deraadt 259: for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
! 260: ;
1.1 deraadt 261: if (*p == '*') { /* `*' is current month */
262: *flags |= F_ISMONTH;
263: *endp = p+1;
264: return (tp->tm_mon + 1);
265: }
266: if (isdigit(*p)) {
267: val = strtol(p, &p, 10); /* if 0, it's failure */
1.4 ! deraadt 268: for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
! 269: ;
1.1 deraadt 270: *endp = p;
271: return (val);
272: }
1.4 ! deraadt 273: for (start = p; isalpha(*++p);)
! 274: ;
1.1 deraadt 275: savech = *p;
276: *p = '\0';
277: if ((val = getmonth(start)) != 0)
278: *flags |= F_ISMONTH;
279: else if ((val = getday(start)) != 0)
280: *flags |= F_ISDAY;
281: else {
282: *p = savech;
283: return (0);
284: }
1.4 ! deraadt 285: for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
! 286: ;
1.1 deraadt 287: *endp = p;
288: return (val);
289: }
290:
1.4 ! deraadt 291: char path[MAXPATHLEN];
1.1 deraadt 292:
293: FILE *
294: opencal()
295: {
296: int fd, pdes[2];
297:
298: /* open up calendar file as stdin */
299: if (!freopen("calendar", "r", stdin)) {
300: if (doall)
301: return (NULL);
302: errx(1, "no calendar file.");
303: }
304: if (pipe(pdes) < 0)
305: return (NULL);
306: switch (vfork()) {
307: case -1: /* error */
308: (void)close(pdes[0]);
309: (void)close(pdes[1]);
310: return (NULL);
311: case 0:
312: /* child -- stdin already setup, set stdout to pipe input */
313: if (pdes[1] != STDOUT_FILENO) {
314: (void)dup2(pdes[1], STDOUT_FILENO);
315: (void)close(pdes[1]);
316: }
317: (void)close(pdes[0]);
1.4 ! deraadt 318: (void)setuid(geteuid());
! 319: (void)setgid(getegid());
1.1 deraadt 320: execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);
321: warn("execl: %s", _PATH_CPP);
322: _exit(1);
323: }
324: /* parent -- set stdin to pipe output */
325: (void)dup2(pdes[0], STDIN_FILENO);
326: (void)close(pdes[0]);
327: (void)close(pdes[1]);
328:
329: /* not reading all calendar files, just set output to stdout */
330: if (!doall)
331: return (stdout);
332:
333: /* set output to a temporary file, so if no output don't send mail */
1.3 millert 334: (void)snprintf(path, sizeof(path), "%s_calXXXXXX", _PATH_TMP);
1.1 deraadt 335: if ((fd = mkstemp(path)) < 0)
336: return (NULL);
337: return (fdopen(fd, "w+"));
338: }
339:
340: void
341: closecal(fp)
342: FILE *fp;
343: {
344: struct stat sbuf;
345: int nread, pdes[2], status;
346: char buf[1024];
347:
348: if (!doall)
349: return;
350:
351: (void)rewind(fp);
352: if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
353: goto done;
354: if (pipe(pdes) < 0)
355: goto done;
356: switch (vfork()) {
357: case -1: /* error */
358: (void)close(pdes[0]);
359: (void)close(pdes[1]);
360: goto done;
361: case 0:
362: /* child -- set stdin to pipe output */
363: if (pdes[0] != STDIN_FILENO) {
364: (void)dup2(pdes[0], STDIN_FILENO);
365: (void)close(pdes[0]);
366: }
367: (void)close(pdes[1]);
1.4 ! deraadt 368: (void)setuid(geteuid());
! 369: (void)setgid(getegid());
1.1 deraadt 370: execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
371: "\"Reminder Service\"", "-f", "root", NULL);
372: warn("execl: %s", _PATH_SENDMAIL);
373: _exit(1);
374: }
375: /* parent -- write to pipe input */
376: (void)close(pdes[0]);
377:
378: header[1].iov_base = header[3].iov_base = pw->pw_name;
379: header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
380: writev(pdes[1], header, 7);
381: while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
382: (void)write(pdes[1], buf, nread);
383: (void)close(pdes[1]);
384: done: (void)fclose(fp);
385: (void)unlink(path);
1.4 ! deraadt 386: while (wait(&status) >= 0)
! 387: ;
1.1 deraadt 388: }
389:
390: static char *months[] = {
391: "jan", "feb", "mar", "apr", "may", "jun",
392: "jul", "aug", "sep", "oct", "nov", "dec", NULL,
393: };
394:
395: int
396: getmonth(s)
397: register char *s;
398: {
399: register char **p;
400:
401: for (p = months; *p; ++p)
402: if (!strncasecmp(s, *p, 3))
403: return ((p - months) + 1);
404: return (0);
405: }
406:
407: static char *days[] = {
408: "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
409: };
410:
411: int
412: getday(s)
413: register char *s;
414: {
415: register char **p;
416:
417: for (p = days; *p; ++p)
418: if (!strncasecmp(s, *p, 3))
419: return ((p - days) + 1);
420: return (0);
421: }
422:
423: void
424: usage()
425: {
426: (void)fprintf(stderr, "usage: calendar [-a]\n");
427: exit(1);
428: }