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 fprintf(stderr, "Ignored: %s\n", buf); 180 if (count == -1) 181 continue; 182 count = -count + 1; 183 } 184 185 /* Find the last tab */ 186 while (pp[1] == '\t') 187 pp++; 188 189 if (d_first < 0) 190 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 191 192 for (i = 0; i < count; i++) { 193 tm.tm_mon = month[i] - 1; 194 tm.tm_mday = day[i]; 195 tm.tm_year = year[i] - 1900; 196 (void)strftime(dbuf, sizeof(dbuf), 197 d_first ? "%e %b" : "%b %e", &tm); 198 if (debug) 199 fprintf(stderr, "got %s\n", pp); 200 events[i] = event_add(year[i], month[i], day[i], dbuf, 201 ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, 202 extradata[i]); 203 } 204 } 205 206 event_print_all(fp); 207 closecal(fp); 208 free(extradata); 209 } 210 211 FILE * 212 opencal(void) 213 { 214 uid_t uid; 215 size_t i; 216 int fd, found, pdes[2]; 217 struct stat sbuf; 218 219 /* open up calendar file as stdin */ 220 if (!freopen(calendarFile, "r", stdin)) { 221 if (doall) { 222 if (chdir(calendarHomes[0]) != 0) 223 return (NULL); 224 if (stat(calendarNoMail, &sbuf) == 0) 225 return (NULL); 226 if (!freopen(calendarFile, "r", stdin)) 227 return (NULL); 228 } else { 229 char *home = getenv("HOME"); 230 if (home == NULL || *home == '\0') 231 errx(1, "cannot get home directory"); 232 if (chdir(home) != 0) 233 errx(1, "cannot enter home directory"); 234 for (found = i = 0; i < sizeof(calendarHomes) / 235 sizeof(calendarHomes[0]); i++) 236 if (chdir(calendarHomes[i]) == 0 && 237 freopen(calendarFile, "r", stdin)) { 238 found = 1; 239 break; 240 } 241 if (!found) 242 errx(1, 243 "can't open calendar file \"%s\": %s (%d)", 244 calendarFile, strerror(errno), errno); 245 } 246 } 247 if (pipe(pdes) < 0) 248 return (NULL); 249 switch (fork()) { 250 case -1: /* error */ 251 (void)close(pdes[0]); 252 (void)close(pdes[1]); 253 return (NULL); 254 case 0: 255 /* child -- stdin already setup, set stdout to pipe input */ 256 if (pdes[1] != STDOUT_FILENO) { 257 (void)dup2(pdes[1], STDOUT_FILENO); 258 (void)close(pdes[1]); 259 } 260 (void)close(pdes[0]); 261 uid = geteuid(); 262 if (setuid(getuid()) < 0) { 263 warnx("first setuid failed"); 264 _exit(1); 265 }; 266 if (setgid(getegid()) < 0) { 267 warnx("setgid failed"); 268 _exit(1); 269 } 270 if (setuid(uid) < 0) { 271 warnx("setuid failed"); 272 _exit(1); 273 } 274 execl(_PATH_CPP, "cpp", "-P", 275 "-traditional", "-nostdinc", /* GCC specific opts */ 276 "-I.", "-I", _PATH_INCLUDE, (char *)NULL); 277 warn(_PATH_CPP); 278 _exit(1); 279 } 280 /* parent -- set stdin to pipe output */ 281 (void)dup2(pdes[0], STDIN_FILENO); 282 (void)close(pdes[0]); 283 (void)close(pdes[1]); 284 285 /* not reading all calendar files, just set output to stdout */ 286 if (!doall) 287 return (stdout); 288 289 /* set output to a temporary file, so if no output don't send mail */ 290 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 291 if ((fd = mkstemp(path)) < 0) 292 return (NULL); 293 return (fdopen(fd, "w+")); 294 } 295 296 void 297 closecal(FILE *fp) 298 { 299 uid_t uid; 300 struct stat sbuf; 301 int nread, pdes[2], status; 302 char buf[1024]; 303 304 if (!doall) 305 return; 306 307 rewind(fp); 308 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 309 goto done; 310 if (pipe(pdes) < 0) 311 goto done; 312 switch (fork()) { 313 case -1: /* error */ 314 (void)close(pdes[0]); 315 (void)close(pdes[1]); 316 goto done; 317 case 0: 318 /* child -- set stdin to pipe output */ 319 if (pdes[0] != STDIN_FILENO) { 320 (void)dup2(pdes[0], STDIN_FILENO); 321 (void)close(pdes[0]); 322 } 323 (void)close(pdes[1]); 324 uid = geteuid(); 325 if (setuid(getuid()) < 0) { 326 warnx("setuid failed"); 327 _exit(1); 328 }; 329 if (setgid(getegid()) < 0) { 330 warnx("setgid failed"); 331 _exit(1); 332 } 333 if (setuid(uid) < 0) { 334 warnx("setuid failed"); 335 _exit(1); 336 } 337 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 338 "\"Reminder Service\"", (char *)NULL); 339 warn(_PATH_SENDMAIL); 340 _exit(1); 341 } 342 /* parent -- write to pipe input */ 343 (void)close(pdes[0]); 344 345 write(pdes[1], "From: \"Reminder Service\" <", 26); 346 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 347 write(pdes[1], ">\nTo: <", 7); 348 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 349 write(pdes[1], ">\nSubject: ", 12); 350 write(pdes[1], dayname, strlen(dayname)); 351 write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); 352 353 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 354 (void)write(pdes[1], buf, nread); 355 (void)close(pdes[1]); 356 done: (void)fclose(fp); 357 (void)unlink(path); 358 while (wait(&status) >= 0); 359 } 360