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