1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)calendar.c 4.11 (Berkeley) 10/12/90"; 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; 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 day = cumdays[month] + day; 186 187 /* if today or today + offset days */ 188 if (day >= tp->tm_yday && day <= tp->tm_yday + offset) 189 return(1); 190 /* if number of days left in this year + days to event in next year */ 191 if (yrdays - tp->tm_yday + day <= offset) 192 return(1); 193 return(0); 194 } 195 196 getfield(p, endp, flags) 197 char *p, **endp; 198 int *flags; 199 { 200 int val; 201 char *start, savech; 202 203 if (*p == '*') { /* `*' is current month */ 204 *flags |= F_ISMONTH; 205 return(tp->tm_mon); 206 } 207 if (isdigit(*p)) { 208 val = strtol(p, &p, 10); /* if 0, it's failure */ 209 for (; !isdigit(*p) && !isalpha(*p); ++p); 210 *endp = p; 211 return(val); 212 } 213 for (start = p; isalpha(*++p);); 214 savech = *p; 215 *p = '\0'; 216 if (val = getmonth(start)) 217 *flags |= F_ISMONTH; 218 else if (val = getday(start)) 219 *flags |= F_ISDAY; 220 else 221 return(0); 222 for (*p = savech; !isdigit(*p) && !isalpha(*p); ++p); 223 *endp = p; 224 return(val); 225 } 226 227 char path[MAXPATHLEN + 1]; 228 229 FILE * 230 opencal() 231 { 232 int fd, pdes[2]; 233 char *mktemp(); 234 235 /* open up calendar file as stdin */ 236 if (!freopen("calendar", "r", stdin)) { 237 if (doall) 238 return((FILE *)NULL); 239 (void)fprintf(stderr, "calendar: no calendar file.\n"); 240 exit(1); 241 } 242 if (pipe(pdes) < 0) 243 return(NULL); 244 switch (vfork()) { 245 case -1: /* error */ 246 (void)close(pdes[0]); 247 (void)close(pdes[1]); 248 return(NULL); 249 case 0: 250 /* child -- stdin already setup, set stdout to pipe input */ 251 if (pdes[1] != STDOUT_FILENO) { 252 (void)dup2(pdes[1], STDOUT_FILENO); 253 (void)close(pdes[1]); 254 } 255 (void)close(pdes[0]); 256 execl(_PATH_CPP, "cpp", "-I.", _PATH_INCLUDE, NULL); 257 _exit(1); 258 } 259 /* parent -- set stdin to pipe output */ 260 (void)dup2(pdes[0], STDIN_FILENO); 261 (void)close(pdes[0]); 262 (void)close(pdes[1]); 263 264 /* not reading all calendar files, just set output to stdout */ 265 if (!doall) 266 return(stdout); 267 268 /* set output to a temporary file, so if no output don't send mail */ 269 (void)sprintf(path, "%s/_calXXXXXX", _PATH_TMP); 270 if ((fd = mkstemp(path)) < 0) 271 return(NULL); 272 return(fdopen(fd, "w+")); 273 } 274 275 closecal(fp) 276 FILE *fp; 277 { 278 struct stat sbuf; 279 int nread, pdes[2], status; 280 char buf[1024], *mktemp(); 281 282 if (!doall) 283 return; 284 285 (void)rewind(fp); 286 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 287 goto done; 288 if (pipe(pdes) < 0) 289 goto done; 290 switch (vfork()) { 291 case -1: /* error */ 292 (void)close(pdes[0]); 293 (void)close(pdes[1]); 294 goto done; 295 case 0: 296 /* child -- set stdin to pipe output */ 297 if (pdes[0] != STDIN_FILENO) { 298 (void)dup2(pdes[0], STDIN_FILENO); 299 (void)close(pdes[0]); 300 } 301 (void)close(pdes[1]); 302 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 303 "\"Reminder Service\"", "-f", "root", NULL); 304 (void)fprintf(stderr, "calendar: %s: %s.\n", 305 _PATH_SENDMAIL, strerror(errno)); 306 _exit(1); 307 } 308 /* parent -- write to pipe input */ 309 (void)close(pdes[0]); 310 311 header[1].iov_base = header[3].iov_base = pw->pw_name; 312 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); 313 writev(pdes[1], header, 7); 314 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 315 (void)write(pdes[1], buf, nread); 316 (void)close(pdes[1]); 317 done: (void)fclose(fp); 318 (void)unlink(path); 319 while (wait(&status) >= 0); 320 } 321 322 static char *months[] = { 323 "jan", "feb", "mar", "apr", "may", "jun", 324 "jul", "aug", "sep", "oct", "nov", "dec", NULL, 325 }; 326 getmonth(s) 327 register char *s; 328 { 329 register char **p; 330 331 for (p = months; *p; ++p) 332 if (!strncasecmp(s, *p, 3)) 333 return((p - months) + 1); 334 return(0); 335 } 336 337 static char *days[] = { 338 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, 339 }; 340 getday(s) 341 register char *s; 342 { 343 register char **p; 344 345 for (p = days; *p; ++p) 346 if (!strncasecmp(s, *p, 3)) 347 return((p - days) + 1); 348 return(0); 349 } 350 351 usage() 352 { 353 (void)fprintf(stderr, "usage: calendar [-a]\n"); 354 exit(1); 355 } 356