1 /*- 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. 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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1989, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif 35 36 #if 0 37 #ifndef lint 38 static char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 39 #endif 40 #endif 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/wait.h> 48 #include <ctype.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <langinfo.h> 52 #include <locale.h> 53 #include <pwd.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "pathnames.h" 60 #include "calendar.h" 61 62 const char *calendarFile = "calendar"; /* default calendar file */ 63 const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ 64 const char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */ 65 66 char path[MAXPATHLEN]; 67 68 struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; 69 struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; 70 71 #define REPLACE(string, slen, struct_) \ 72 if (strncasecmp(buf, (string), (slen)) == 0 && buf[(slen)]) { \ 73 if (struct_.name != NULL) \ 74 free(struct_.name); \ 75 if ((struct_.name = strdup(buf + (slen))) == NULL) \ 76 errx(1, "cannot allocate memory"); \ 77 struct_.len = strlen(buf + (slen)); \ 78 continue; \ 79 } 80 void 81 cal(void) 82 { 83 char *pp, p; 84 FILE *fp; 85 int ch, l; 86 int count, i; 87 int month[MAXCOUNT]; 88 int day[MAXCOUNT]; 89 int year[MAXCOUNT]; 90 char **extradata; /* strings of 20 length */ 91 int flags; 92 static int d_first = -1; 93 char buf[2048 + 1]; 94 struct event *events[MAXCOUNT]; 95 struct tm tm; 96 char dbuf[80]; 97 98 extradata = (char **)calloc(MAXCOUNT, sizeof(char *)); 99 for (i = 0; i < MAXCOUNT; i++) { 100 extradata[i] = (char *)calloc(1, 20); 101 } 102 103 /* Unused */ 104 tm.tm_sec = 0; 105 tm.tm_min = 0; 106 tm.tm_hour = 0; 107 tm.tm_wday = 0; 108 109 count = 0; 110 if ((fp = opencal()) == NULL) { 111 free(extradata); 112 return; 113 } 114 while (fgets(buf, sizeof(buf), stdin) != NULL) { 115 if ((pp = strchr(buf, '\n')) != NULL) 116 *pp = '\0'; 117 else 118 /* Flush this line */ 119 while ((ch = getchar()) != '\n' && ch != EOF); 120 for (l = strlen(buf); 121 l > 0 && isspace((unsigned char)buf[l - 1]); 122 l--) 123 ; 124 buf[l] = '\0'; 125 if (buf[0] == '\0') 126 continue; 127 128 /* Parse special definitions: LANG, Easter, Paskha etc */ 129 if (strncmp(buf, "LANG=", 5) == 0) { 130 (void)setlocale(LC_ALL, buf + 5); 131 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 132 setnnames(); 133 continue; 134 } 135 REPLACE("Easter=", 7, neaster); 136 REPLACE("Paskha=", 7, npaskha); 137 REPLACE("ChineseNewYear=", 15, ncny); 138 REPLACE("NewMoon=", 8, nnewmoon); 139 REPLACE("FullMoon=", 9, nfullmoon); 140 REPLACE("MarEquinox=", 11, nmarequinox); 141 REPLACE("SepEquinox=", 11, nsepequinox); 142 REPLACE("JunSolstice=", 12, njunsolstice); 143 REPLACE("DecSolstice=", 12, ndecsolstice); 144 if (strncmp(buf, "SEQUENCE=", 9) == 0) { 145 setnsequences(buf + 9); 146 continue; 147 } 148 149 /* 150 * If the line starts with a tab, the data has to be 151 * added to the previous line 152 */ 153 if (buf[0] == '\t') { 154 for (i = 0; i < count; i++) 155 event_continue(events[i], buf); 156 continue; 157 } 158 159 /* Get rid of leading spaces (non-standard) */ 160 while (isspace((unsigned char)buf[0])) 161 memcpy(buf, buf + 1, strlen(buf)); 162 163 /* No tab in the line, then not a valid line */ 164 if ((pp = strchr(buf, '\t')) == NULL) 165 continue; 166 167 /* Trim spaces in front of the tab */ 168 while (isspace((unsigned char)pp[-1])) 169 pp--; 170 171 p = *pp; 172 *pp = '\0'; 173 if ((count = parsedaymonth(buf, year, month, day, &flags, 174 extradata)) == 0) 175 continue; 176 *pp = p; 177 if (count < 0) { 178 /* Show error status based on return value */ 179 if (debug) 180 fprintf(stderr, "Ignored: %s\n", buf); 181 if (count == -1) 182 continue; 183 count = -count + 1; 184 } 185 186 /* Find the last tab */ 187 while (pp[1] == '\t') 188 pp++; 189 190 if (d_first < 0) 191 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 192 193 for (i = 0; i < count; i++) { 194 tm.tm_mon = month[i] - 1; 195 tm.tm_mday = day[i]; 196 tm.tm_year = year[i] - 1900; 197 (void)strftime(dbuf, sizeof(dbuf), 198 d_first ? "%e %b" : "%b %e", &tm); 199 if (debug) 200 fprintf(stderr, "got %s\n", pp); 201 events[i] = event_add(year[i], month[i], day[i], dbuf, 202 ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, 203 extradata[i]); 204 } 205 } 206 207 event_print_all(fp); 208 closecal(fp); 209 free(extradata); 210 } 211 212 FILE * 213 opencal(void) 214 { 215 uid_t uid; 216 size_t i; 217 int fd, found, pdes[2]; 218 struct stat sbuf; 219 220 /* open up calendar file as stdin */ 221 if (!freopen(calendarFile, "r", stdin)) { 222 if (doall) { 223 if (chdir(calendarHomes[0]) != 0) 224 return (NULL); 225 if (stat(calendarNoMail, &sbuf) == 0) 226 return (NULL); 227 if (!freopen(calendarFile, "r", stdin)) 228 return (NULL); 229 } else { 230 char *home = getenv("HOME"); 231 if (home == NULL || *home == '\0') 232 errx(1, "cannot get home directory"); 233 if (chdir(home) != 0) 234 errx(1, "cannot enter home directory"); 235 for (found = i = 0; i < sizeof(calendarHomes) / 236 sizeof(calendarHomes[0]); i++) 237 if (chdir(calendarHomes[i]) == 0 && 238 freopen(calendarFile, "r", stdin)) { 239 found = 1; 240 break; 241 } 242 if (!found) 243 errx(1, 244 "can't open calendar file \"%s\": %s (%d)", 245 calendarFile, strerror(errno), errno); 246 } 247 } 248 if (pipe(pdes) < 0) 249 return (NULL); 250 switch (fork()) { 251 case -1: /* error */ 252 (void)close(pdes[0]); 253 (void)close(pdes[1]); 254 return (NULL); 255 case 0: 256 /* child -- stdin already setup, set stdout to pipe input */ 257 if (pdes[1] != STDOUT_FILENO) { 258 (void)dup2(pdes[1], STDOUT_FILENO); 259 (void)close(pdes[1]); 260 } 261 (void)close(pdes[0]); 262 uid = geteuid(); 263 if (setuid(getuid()) < 0) { 264 warnx("first setuid failed"); 265 _exit(1); 266 }; 267 if (setgid(getegid()) < 0) { 268 warnx("setgid failed"); 269 _exit(1); 270 } 271 if (setuid(uid) < 0) { 272 warnx("setuid failed"); 273 _exit(1); 274 } 275 execl(_PATH_CPP, "cpp", "-P", 276 "-traditional", "-nostdinc", /* GCC specific opts */ 277 "-I.", "-I", _PATH_INCLUDE, (char *)NULL); 278 warn(_PATH_CPP); 279 _exit(1); 280 } 281 /* parent -- set stdin to pipe output */ 282 (void)dup2(pdes[0], STDIN_FILENO); 283 (void)close(pdes[0]); 284 (void)close(pdes[1]); 285 286 /* not reading all calendar files, just set output to stdout */ 287 if (!doall) 288 return (stdout); 289 290 /* set output to a temporary file, so if no output don't send mail */ 291 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 292 if ((fd = mkstemp(path)) < 0) 293 return (NULL); 294 return (fdopen(fd, "w+")); 295 } 296 297 void 298 closecal(FILE *fp) 299 { 300 uid_t uid; 301 struct stat sbuf; 302 int nread, pdes[2], status; 303 char buf[1024]; 304 305 if (!doall) 306 return; 307 308 rewind(fp); 309 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 310 goto done; 311 if (pipe(pdes) < 0) 312 goto done; 313 switch (fork()) { 314 case -1: /* error */ 315 (void)close(pdes[0]); 316 (void)close(pdes[1]); 317 goto done; 318 case 0: 319 /* child -- set stdin to pipe output */ 320 if (pdes[0] != STDIN_FILENO) { 321 (void)dup2(pdes[0], STDIN_FILENO); 322 (void)close(pdes[0]); 323 } 324 (void)close(pdes[1]); 325 uid = geteuid(); 326 if (setuid(getuid()) < 0) { 327 warnx("setuid failed"); 328 _exit(1); 329 }; 330 if (setgid(getegid()) < 0) { 331 warnx("setgid failed"); 332 _exit(1); 333 } 334 if (setuid(uid) < 0) { 335 warnx("setuid failed"); 336 _exit(1); 337 } 338 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 339 "\"Reminder Service\"", (char *)NULL); 340 warn(_PATH_SENDMAIL); 341 _exit(1); 342 } 343 /* parent -- write to pipe input */ 344 (void)close(pdes[0]); 345 346 write(pdes[1], "From: \"Reminder Service\" <", 26); 347 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 348 write(pdes[1], ">\nTo: <", 7); 349 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 350 write(pdes[1], ">\nSubject: ", 11); 351 write(pdes[1], dayname, strlen(dayname)); 352 write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); 353 354 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 355 (void)write(pdes[1], buf, nread); 356 (void)close(pdes[1]); 357 done: (void)fclose(fp); 358 (void)unlink(path); 359 while (wait(&status) >= 0); 360 } 361