calendar.c revision 5311
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 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 char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 42#endif /* not lint */ 43 44#include <sys/param.h> 45#include <sys/time.h> 46#include <sys/stat.h> 47#include <sys/uio.h> 48#include <sys/wait.h> 49 50#include <ctype.h> 51#include <err.h> 52#include <errno.h> 53#include <fcntl.h> 54#include <pwd.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include <tzfile.h> 59#include <unistd.h> 60 61#include "pathnames.h" 62 63struct passwd *pw; 64int doall; 65 66void cal __P((void)); 67void closecal __P((FILE *)); 68int getday __P((char *)); 69int getfield __P((char *, char **, int *)); 70int getmonth __P((char *)); 71int isnow __P((char *)); 72FILE *opencal __P((void)); 73void settime __P((void)); 74void usage __P((void)); 75 76int 77main(argc, argv) 78 int argc; 79 char *argv[]; 80{ 81 extern int optind; 82 int ch; 83 char *s; 84 85 while ((ch = getopt(argc, argv, "-a")) != EOF) 86 switch (ch) { 87 case '-': /* backward contemptible */ 88 case 'a': 89 if (getuid()) { 90 errno = EPERM; 91 err(1, NULL); 92 } 93 doall = 1; 94 break; 95 case '?': 96 default: 97 usage(); 98 } 99 argc -= optind; 100 argv += optind; 101 102 if (argc) 103 usage(); 104 105 settime(); 106 if (doall) 107 while ((pw = getpwent()) != NULL) { 108 (void)setegid(pw->pw_gid); 109 (void)seteuid(pw->pw_uid); 110 if (!chdir(pw->pw_dir)) 111 cal(); 112 (void)seteuid(0); 113 } 114 else { 115 if ((s = getenv("HOME")) != NULL) 116 chdir(s); 117 cal(); 118 } 119 exit(0); 120} 121 122void 123cal() 124{ 125 register int printing; 126 register char *p; 127 FILE *fp; 128 int ch; 129 char buf[2048 + 1]; 130 131 if ((fp = opencal()) == NULL) 132 return; 133 for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) { 134 if ((p = strchr(buf, '\n')) != NULL) 135 *p = '\0'; 136 else 137 while ((ch = getchar()) != '\n' && ch != EOF); 138 if (buf[0] == '\0') 139 continue; 140 if (buf[0] != '\t') 141 printing = isnow(buf) ? 1 : 0; 142 if (printing) 143 (void)fprintf(fp, "%s\n", buf); 144 } 145 closecal(fp); 146} 147 148struct iovec header[] = { 149 "From: ", 6, 150 NULL, 0, 151 " (Reminder Service)\nTo: ", 24, 152 NULL, 0, 153 "\nSubject: ", 10, 154 NULL, 0, 155 "'s Calendar\nPrecedence: bulk\n\n", 30, 156}; 157 158/* 1-based month, 0-based days, cumulative */ 159int daytab[][14] = { 160 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364, 161 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 162}; 163struct tm *tp; 164int *cumdays, offset, yrdays; 165char dayname[10]; 166 167void 168settime() 169{ 170 time_t now; 171 172 (void)time(&now); 173 tp = localtime(&now); 174 if (isleap(tp->tm_year + 1900)) { 175 yrdays = DAYSPERLYEAR; 176 cumdays = daytab[1]; 177 } else { 178 yrdays = DAYSPERNYEAR; 179 cumdays = daytab[0]; 180 } 181 /* Friday displays Monday's events */ 182 offset = tp->tm_wday == 5 ? 3 : 1; 183 header[5].iov_base = dayname; 184 header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp); 185} 186 187/* 188 * Possible date formats include any combination of: 189 * 3-charmonth (January, Jan, Jan) 190 * 3-charweekday (Friday, Monday, mon.) 191 * numeric month or day (1, 2, 04) 192 * 193 * Any character may separate them, or they may not be separated. Any line, 194 * following a line that is matched, that starts with "whitespace", is shown 195 * along with the matched line. 196 */ 197int 198isnow(endp) 199 char *endp; 200{ 201 int day, flags, month, v1, v2; 202 203#define F_ISMONTH 0x01 204#define F_ISDAY 0x02 205 flags = 0; 206 /* didn't recognize anything, skip it */ 207 if (!(v1 = getfield(endp, &endp, &flags))) 208 return (0); 209 if (flags & F_ISDAY || v1 > 12) { 210 /* found a day */ 211 day = v1; 212 /* if no recognizable month, assume just a day alone */ 213 if (!(month = getfield(endp, &endp, &flags))) 214 month = tp->tm_mon + 1; 215 } else if (flags & F_ISMONTH) { 216 month = v1; 217 /* if no recognizable day, assume the first */ 218 if (!(day = getfield(endp, &endp, &flags))) 219 day = 1; 220 } else { 221 v2 = getfield(endp, &endp, &flags); 222 if (flags & F_ISMONTH) { 223 day = v1; 224 month = v2; 225 } else { 226 /* F_ISDAY set, v2 > 12, or no way to tell */ 227 month = v1; 228 /* if no recognizable day, assume the first */ 229 day = v2 ? v2 : 1; 230 } 231 } 232 if (flags & F_ISDAY) 233 day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); 234 day = cumdays[month] + day; 235 236 /* if today or today + offset days */ 237 if (day >= tp->tm_yday && day <= tp->tm_yday + offset) 238 return (1); 239 /* if number of days left in this year + days to event in next year */ 240 if (yrdays - tp->tm_yday + day <= offset) 241 return (1); 242 return (0); 243} 244 245int 246getfield(p, endp, flags) 247 char *p, **endp; 248 int *flags; 249{ 250 int val; 251 char *start, savech; 252 253 for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 254 if (*p == '*') { /* `*' is current month */ 255 *flags |= F_ISMONTH; 256 *endp = p+1; 257 return (tp->tm_mon + 1); 258 } 259 if (isdigit(*p)) { 260 val = strtol(p, &p, 10); /* if 0, it's failure */ 261 for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 262 *endp = p; 263 return (val); 264 } 265 for (start = p; isalpha(*++p);); 266 savech = *p; 267 *p = '\0'; 268 if ((val = getmonth(start)) != 0) 269 *flags |= F_ISMONTH; 270 else if ((val = getday(start)) != 0) 271 *flags |= F_ISDAY; 272 else { 273 *p = savech; 274 return (0); 275 } 276 for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); 277 *endp = p; 278 return (val); 279} 280 281char path[MAXPATHLEN + 1]; 282 283FILE * 284opencal() 285{ 286 int fd, pdes[2]; 287 288 /* open up calendar file as stdin */ 289 if (!freopen("calendar", "r", stdin)) { 290 if (doall) 291 return (NULL); 292 errx(1, "no calendar file."); 293 } 294 if (pipe(pdes) < 0) 295 return (NULL); 296 switch (vfork()) { 297 case -1: /* error */ 298 (void)close(pdes[0]); 299 (void)close(pdes[1]); 300 return (NULL); 301 case 0: 302 /* child -- stdin already setup, set stdout to pipe input */ 303 if (pdes[1] != STDOUT_FILENO) { 304 (void)dup2(pdes[1], STDOUT_FILENO); 305 (void)close(pdes[1]); 306 } 307 (void)close(pdes[0]); 308 execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL); 309 (void)fprintf(stderr, 310 "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno)); 311 _exit(1); 312 } 313 /* parent -- set stdin to pipe output */ 314 (void)dup2(pdes[0], STDIN_FILENO); 315 (void)close(pdes[0]); 316 (void)close(pdes[1]); 317 318 /* not reading all calendar files, just set output to stdout */ 319 if (!doall) 320 return (stdout); 321 322 /* set output to a temporary file, so if no output don't send mail */ 323 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 324 if ((fd = mkstemp(path)) < 0) 325 return (NULL); 326 return (fdopen(fd, "w+")); 327} 328 329void 330closecal(fp) 331 FILE *fp; 332{ 333 struct stat sbuf; 334 int nread, pdes[2], status; 335 char buf[1024]; 336 337 if (!doall) 338 return; 339 340 (void)rewind(fp); 341 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 342 goto done; 343 if (pipe(pdes) < 0) 344 goto done; 345 switch (vfork()) { 346 case -1: /* error */ 347 (void)close(pdes[0]); 348 (void)close(pdes[1]); 349 goto done; 350 case 0: 351 /* child -- set stdin to pipe output */ 352 if (pdes[0] != STDIN_FILENO) { 353 (void)dup2(pdes[0], STDIN_FILENO); 354 (void)close(pdes[0]); 355 } 356 (void)close(pdes[1]); 357 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 358 "\"Reminder Service\"", "-f", "root", NULL); 359 (void)fprintf(stderr, 360 "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno)); 361 _exit(1); 362 } 363 /* parent -- write to pipe input */ 364 (void)close(pdes[0]); 365 366 header[1].iov_base = header[3].iov_base = pw->pw_name; 367 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); 368 writev(pdes[1], header, 7); 369 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 370 (void)write(pdes[1], buf, nread); 371 (void)close(pdes[1]); 372done: (void)fclose(fp); 373 (void)unlink(path); 374 while (wait(&status) >= 0); 375} 376 377static char *months[] = { 378 "jan", "feb", "mar", "apr", "may", "jun", 379 "jul", "aug", "sep", "oct", "nov", "dec", NULL, 380}; 381 382int 383getmonth(s) 384 register char *s; 385{ 386 register char **p; 387 388 for (p = months; *p; ++p) 389 if (!strncasecmp(s, *p, 3)) 390 return ((p - months) + 1); 391 return (0); 392} 393 394static char *days[] = { 395 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, 396}; 397 398int 399getday(s) 400 register char *s; 401{ 402 register char **p; 403 404 for (p = days; *p; ++p) 405 if (!strncasecmp(s, *p, 3)) 406 return ((p - days) + 1); 407 return (0); 408} 409 410void 411usage() 412{ 413 (void)fprintf(stderr, "usage: calendar [-a]\n"); 414 exit(1); 415} 416