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