1 /* $NetBSD: calendar.c,v 1.9 1997/06/20 08:11:34 lukem 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.9 1997/06/20 08:11:34 lukem 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 (; *p != '\0' && !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p) 257 ; 258 if (*p == '*') { /* `*' is current month */ 259 *flags |= F_ISMONTH; 260 *endp = p+1; 261 return (tp->tm_mon + 1); 262 } 263 if (isdigit(*p)) { 264 val = strtol(p, &p, 10); /* if 0, it's failure */ 265 for (; *p != '\0' && !isdigit(*p) && !isalpha(*p) && *p != '*'; 266 ++p) 267 ; 268 *endp = p; 269 return (val); 270 } 271 for (start = p; *p != '\0' && isalpha(*++p);) 272 ; 273 savech = *p; 274 *p = '\0'; 275 if ((val = getmonth(start)) != 0) 276 *flags |= F_ISMONTH; 277 else if ((val = getday(start)) != 0) 278 *flags |= F_ISDAY; 279 else { 280 *p = savech; 281 return (0); 282 } 283 for (*p = savech; 284 *p != '\0' && !isdigit(*p) && !isalpha(*p) && *p != '*'; 285 ++p) 286 ; 287 *endp = p; 288 return (val); 289 } 290 291 char path[MAXPATHLEN + 1]; 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]); 318 execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL); 319 warn("execl: %s", _PATH_CPP); 320 _exit(1); 321 } 322 /* parent -- set stdin to pipe output */ 323 (void)dup2(pdes[0], STDIN_FILENO); 324 (void)close(pdes[0]); 325 (void)close(pdes[1]); 326 327 /* not reading all calendar files, just set output to stdout */ 328 if (!doall) 329 return (stdout); 330 331 /* set output to a temporary file, so if no output don't send mail */ 332 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 333 if ((fd = mkstemp(path)) < 0) 334 return (NULL); 335 return (fdopen(fd, "w+")); 336 } 337 338 void 339 closecal(fp) 340 FILE *fp; 341 { 342 struct stat sbuf; 343 int nread, pdes[2], status; 344 char buf[1024]; 345 346 if (!doall) 347 return; 348 349 (void)rewind(fp); 350 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 351 goto done; 352 if (pipe(pdes) < 0) 353 goto done; 354 switch (vfork()) { 355 case -1: /* error */ 356 (void)close(pdes[0]); 357 (void)close(pdes[1]); 358 goto done; 359 case 0: 360 /* child -- set stdin to pipe output */ 361 if (pdes[0] != STDIN_FILENO) { 362 (void)dup2(pdes[0], STDIN_FILENO); 363 (void)close(pdes[0]); 364 } 365 (void)close(pdes[1]); 366 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 367 "\"Reminder Service\"", "-f", "root", NULL); 368 warn("execl: %s", _PATH_SENDMAIL); 369 _exit(1); 370 } 371 /* parent -- write to pipe input */ 372 (void)close(pdes[0]); 373 374 header[1].iov_base = header[3].iov_base = pw->pw_name; 375 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); 376 writev(pdes[1], header, 7); 377 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 378 (void)write(pdes[1], buf, nread); 379 (void)close(pdes[1]); 380 done: (void)fclose(fp); 381 (void)unlink(path); 382 while (wait(&status) >= 0); 383 } 384 385 static char *months[] = { 386 "jan", "feb", "mar", "apr", "may", "jun", 387 "jul", "aug", "sep", "oct", "nov", "dec", NULL, 388 }; 389 390 int 391 getmonth(s) 392 register char *s; 393 { 394 register char **p; 395 396 for (p = months; *p; ++p) 397 if (!strncasecmp(s, *p, 3)) 398 return ((p - months) + 1); 399 return (0); 400 } 401 402 static char *days[] = { 403 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, 404 }; 405 406 int 407 getday(s) 408 register char *s; 409 { 410 register char **p; 411 412 for (p = days; *p; ++p) 413 if (!strncasecmp(s, *p, 3)) 414 return ((p - days) + 1); 415 return (0); 416 } 417 418 void 419 usage() 420 { 421 (void)fprintf(stderr, "usage: calendar [-a]\n"); 422 exit(1); 423 } 424