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