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