io.c revision 207703
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 31static 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 38static char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 39#endif 40#endif 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD: head/usr.bin/calendar/io.c 207703 2010-05-06 16:54:46Z ache $"); 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 62const char *calendarFile = "calendar"; /* default calendar file */ 63const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ 64const char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */ 65 66char path[MAXPATHLEN]; 67 68struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; 69struct 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 } 80void 81cal(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 return; 112 while (fgets(buf, sizeof(buf), stdin) != NULL) { 113 if ((pp = strchr(buf, '\n')) != NULL) 114 *pp = '\0'; 115 else 116 /* Flush this line */ 117 while ((ch = getchar()) != '\n' && ch != EOF); 118 for (l = strlen(buf); 119 l > 0 && isspace((unsigned char)buf[l - 1]); 120 l--) 121 ; 122 buf[l] = '\0'; 123 if (buf[0] == '\0') 124 continue; 125 126 /* Parse special definitions: LANG, Easter, Paskha etc */ 127 if (strncmp(buf, "LANG=", 5) == 0) { 128 (void)setlocale(LC_ALL, buf + 5); 129 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 130 setnnames(); 131 continue; 132 } 133 REPLACE("Easter=", 7, neaster); 134 REPLACE("Paskha=", 7, npaskha); 135 REPLACE("ChineseNewYear=", 15, ncny); 136 REPLACE("NewMoon=", 8, nnewmoon); 137 REPLACE("FullMoon=", 9, nfullmoon); 138 REPLACE("MarEquinox=", 11, nmarequinox); 139 REPLACE("SepEquinox=", 11, nsepequinox); 140 REPLACE("JunSolstice=", 12, njunsolstice); 141 REPLACE("DecSolstice=", 12, ndecsolstice); 142 if (strncmp(buf, "SEQUENCE=", 9) == 0) { 143 setnsequences(buf + 9); 144 continue; 145 } 146 147 /* 148 * If the line starts with a tab, the data has to be 149 * added to the previous line 150 */ 151 if (buf[0] == '\t') { 152 for (i = 0; i < count; i++) 153 event_continue(events[i], buf); 154 continue; 155 } 156 157 /* Get rid of leading spaces (non-standard) */ 158 while (isspace((unsigned char)buf[0])) 159 memcpy(buf, buf + 1, strlen(buf)); 160 161 /* No tab in the line, then not a valid line */ 162 if ((pp = strchr(buf, '\t')) == NULL) 163 continue; 164 165 /* Trim spaces in front of the tab */ 166 while (isspace((unsigned char)pp[-1])) 167 pp--; 168 169 p = *pp; 170 *pp = '\0'; 171 if ((count = parsedaymonth(buf, year, month, day, &flags, 172 extradata)) == 0) 173 continue; 174 *pp = p; 175 if (count < 0) { 176 /* Show error status based on return value */ 177 fprintf(stderr, "Ignored: %s\n", buf); 178 if (count == -1) 179 continue; 180 count = -count + 1; 181 } 182 183 /* Find the last tab */ 184 while (pp[1] == '\t') 185 pp++; 186 187 if (d_first < 0) 188 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 189 190 for (i = 0; i < count; i++) { 191 tm.tm_mon = month[i] - 1; 192 tm.tm_mday = day[i]; 193 tm.tm_year = year[i] - 1900; 194 (void)strftime(dbuf, sizeof(dbuf), 195 d_first ? "%e %b" : "%b %e", &tm); 196 if (debug) 197 fprintf(stderr, "got %s\n", pp); 198 events[i] = event_add(year[i], month[i], day[i], dbuf, 199 ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, 200 extradata[i]); 201 } 202 } 203 204 event_print_all(fp); 205 closecal(fp); 206} 207 208FILE * 209opencal(void) 210{ 211 uid_t uid; 212 size_t i; 213 int fd, found, pdes[2]; 214 struct stat sbuf; 215 216 /* open up calendar file as stdin */ 217 if (!freopen(calendarFile, "r", stdin)) { 218 if (doall) { 219 if (chdir(calendarHomes[0]) != 0) 220 return (NULL); 221 if (stat(calendarNoMail, &sbuf) == 0) 222 return (NULL); 223 if (!freopen(calendarFile, "r", stdin)) 224 return (NULL); 225 } else { 226 char *home = getenv("HOME"); 227 if (home == NULL || *home == '\0') 228 errx(1, "cannot get home directory"); 229 chdir(home); 230 for (found = i = 0; i < sizeof(calendarHomes) / 231 sizeof(calendarHomes[0]); i++) 232 if (chdir(calendarHomes[i]) == 0 && 233 freopen(calendarFile, "r", stdin)) { 234 found = 1; 235 break; 236 } 237 if (!found) 238 errx(1, 239 "can't open calendar file \"%s\": %s (%d)", 240 calendarFile, strerror(errno), errno); 241 } 242 } 243 if (pipe(pdes) < 0) 244 return (NULL); 245 switch (fork()) { 246 case -1: /* error */ 247 (void)close(pdes[0]); 248 (void)close(pdes[1]); 249 return (NULL); 250 case 0: 251 /* child -- stdin already setup, set stdout to pipe input */ 252 if (pdes[1] != STDOUT_FILENO) { 253 (void)dup2(pdes[1], STDOUT_FILENO); 254 (void)close(pdes[1]); 255 } 256 (void)close(pdes[0]); 257 uid = geteuid(); 258 if (setuid(getuid()) < 0) { 259 warnx("first setuid failed"); 260 _exit(1); 261 }; 262 if (setgid(getegid()) < 0) { 263 warnx("setgid failed"); 264 _exit(1); 265 } 266 if (setuid(uid) < 0) { 267 warnx("setuid failed"); 268 _exit(1); 269 } 270 execl(_PATH_CPP, "cpp", "-P", 271 "-traditional", "-nostdinc", /* GCC specific opts */ 272 "-I.", "-I", _PATH_INCLUDE, (char *)NULL); 273 warn(_PATH_CPP); 274 _exit(1); 275 } 276 /* parent -- set stdin to pipe output */ 277 (void)dup2(pdes[0], STDIN_FILENO); 278 (void)close(pdes[0]); 279 (void)close(pdes[1]); 280 281 /* not reading all calendar files, just set output to stdout */ 282 if (!doall) 283 return (stdout); 284 285 /* set output to a temporary file, so if no output don't send mail */ 286 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 287 if ((fd = mkstemp(path)) < 0) 288 return (NULL); 289 return (fdopen(fd, "w+")); 290} 291 292void 293closecal(FILE *fp) 294{ 295 uid_t uid; 296 struct stat sbuf; 297 int nread, pdes[2], status; 298 char buf[1024]; 299 300 if (!doall) 301 return; 302 303 rewind(fp); 304 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 305 goto done; 306 if (pipe(pdes) < 0) 307 goto done; 308 switch (fork()) { 309 case -1: /* error */ 310 (void)close(pdes[0]); 311 (void)close(pdes[1]); 312 goto done; 313 case 0: 314 /* child -- set stdin to pipe output */ 315 if (pdes[0] != STDIN_FILENO) { 316 (void)dup2(pdes[0], STDIN_FILENO); 317 (void)close(pdes[0]); 318 } 319 (void)close(pdes[1]); 320 uid = geteuid(); 321 if (setuid(getuid()) < 0) { 322 warnx("setuid failed"); 323 _exit(1); 324 }; 325 if (setgid(getegid()) < 0) { 326 warnx("setgid failed"); 327 _exit(1); 328 } 329 if (setuid(uid) < 0) { 330 warnx("setuid failed"); 331 _exit(1); 332 } 333 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 334 "\"Reminder Service\"", (char *)NULL); 335 warn(_PATH_SENDMAIL); 336 _exit(1); 337 } 338 /* parent -- write to pipe input */ 339 (void)close(pdes[0]); 340 341 write(pdes[1], "From: \"Reminder Service\" <", 26); 342 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 343 write(pdes[1], ">\nTo: <", 7); 344 write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 345 write(pdes[1], ">\nSubject: ", 12); 346 write(pdes[1], dayname, strlen(dayname)); 347 write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); 348 349 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 350 (void)write(pdes[1], buf, nread); 351 (void)close(pdes[1]); 352done: (void)fclose(fp); 353 (void)unlink(path); 354 while (wait(&status) >= 0); 355} 356