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 * 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 * @(#) Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. 34 * @(#)calendar.c 8.3 (Berkeley) 3/25/94 35 * $FreeBSD: src/usr.bin/calendar/io.c,v 1.13.2.3 2002/08/26 00:32:46 jmallett Exp $ 36 * $DragonFly: src/usr.bin/calendar/io.c,v 1.2 2003/06/17 04:29:25 dillon Exp $ 37 */ 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/uio.h> 44 #include <sys/wait.h> 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <langinfo.h> 49 #include <locale.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #include "pathnames.h" 57 #include "calendar.h" 58 59 60 const char *calendarFile = "calendar"; /* default calendar file */ 61 const char *calendarHomes[] = { ".calendar", _PATH_INCLUDE }; /* HOME */ 62 const char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */ 63 64 struct fixs neaster, npaskha; 65 66 struct iovec header[] = { 67 {"From: ", 6}, 68 {NULL, 0}, 69 {" (Reminder Service)\nTo: ", 24}, 70 {NULL, 0}, 71 {"\nSubject: ", 10}, 72 {NULL, 0}, 73 {"'s Calendar\nPrecedence: bulk\n\n", 30}, 74 }; 75 76 77 void 78 cal() 79 { 80 int printing; 81 char *p; 82 FILE *fp; 83 int ch, l; 84 int month; 85 int day; 86 int var; 87 static int d_first = -1; 88 char buf[2048 + 1]; 89 90 if ((fp = opencal()) == NULL) 91 return; 92 for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) { 93 if ((p = strchr(buf, '\n')) != NULL) 94 *p = '\0'; 95 else 96 while ((ch = getchar()) != '\n' && ch != EOF); 97 for (l = strlen(buf); 98 l > 0 && isspace((unsigned char)buf[l - 1]); 99 l--) 100 ; 101 buf[l] = '\0'; 102 if (buf[0] == '\0') 103 continue; 104 if (strncmp(buf, "LANG=", 5) == 0) { 105 (void) setlocale(LC_ALL, buf + 5); 106 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 107 setnnames(); 108 continue; 109 } 110 if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) { 111 if (neaster.name != NULL) 112 free(neaster.name); 113 if ((neaster.name = strdup(buf + 7)) == NULL) 114 errx(1, "cannot allocate memory"); 115 neaster.len = strlen(buf + 7); 116 continue; 117 } 118 if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) { 119 if (npaskha.name != NULL) 120 free(npaskha.name); 121 if ((npaskha.name = strdup(buf + 7)) == NULL) 122 errx(1, "cannot allocate memory"); 123 npaskha.len = strlen(buf + 7); 124 continue; 125 } 126 if (buf[0] != '\t') { 127 printing = isnow(buf, &month, &day, &var) ? 1 : 0; 128 if ((p = strchr(buf, '\t')) == NULL) 129 continue; 130 if (p > buf && p[-1] == '*') 131 var = 1; 132 if (printing) { 133 struct tm tm; 134 char dbuf[80]; 135 136 if (d_first < 0) 137 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 138 tm.tm_sec = 0; /* unused */ 139 tm.tm_min = 0; /* unused */ 140 tm.tm_hour = 0; /* unused */ 141 tm.tm_wday = 0; /* unused */ 142 tm.tm_mon = month - 1; 143 tm.tm_mday = day; 144 tm.tm_year = tp->tm_year; /* unused */ 145 (void)strftime(dbuf, sizeof(dbuf), 146 d_first ? "%e %b" : "%b %e", 147 &tm); 148 (void)fprintf(fp, "%s%c%s\n", dbuf, 149 var ? '*' : ' ', p); 150 } 151 } 152 else if (printing) 153 fprintf(fp, "%s\n", buf); 154 } 155 closecal(fp); 156 } 157 158 int 159 getfield(p, endp, flags) 160 char *p, **endp; 161 int *flags; 162 { 163 int val, var; 164 char *start, savech; 165 166 for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p); 167 if (*p == '*') { /* `*' is current month */ 168 *flags |= F_ISMONTH; 169 *endp = p+1; 170 return (tp->tm_mon + 1); 171 } 172 if (isdigit((unsigned char)*p)) { 173 val = strtol(p, &p, 10); /* if 0, it's failure */ 174 for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p); 175 *endp = p; 176 return (val); 177 } 178 for (start = p; isalpha((unsigned char)*++p);); 179 180 /* Sunday-1 */ 181 if (*p == '+' || *p == '-') 182 for(; isdigit((unsigned char)*++p);); 183 184 savech = *p; 185 *p = '\0'; 186 187 /* Month */ 188 if ((val = getmonth(start)) != 0) 189 *flags |= F_ISMONTH; 190 191 /* Day */ 192 else if ((val = getday(start)) != 0) { 193 *flags |= F_ISDAY; 194 195 /* variable weekday */ 196 if ((var = getdayvar(start)) != 0) { 197 if (var <=5 && var >= -4) 198 val += var * 10; 199 #ifdef DEBUG 200 printf("var: %d\n", var); 201 #endif 202 } 203 } 204 205 /* Easter */ 206 else if ((val = geteaster(start, tp->tm_year + 1900)) != 0) 207 *flags |= F_EASTER; 208 209 /* Paskha */ 210 else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0) 211 *flags |= F_EASTER; 212 213 /* undefined rest */ 214 else { 215 *p = savech; 216 return (0); 217 } 218 for (*p = savech; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p); 219 *endp = p; 220 return (val); 221 } 222 223 char path[MAXPATHLEN]; 224 225 FILE * 226 opencal() 227 { 228 uid_t uid; 229 size_t i; 230 int fd, found, pdes[2]; 231 struct stat sbuf; 232 233 /* open up calendar file as stdin */ 234 if (!freopen(calendarFile, "r", stdin)) { 235 if (doall) { 236 if (chdir(calendarHomes[0]) != 0) 237 return (NULL); 238 if (stat(calendarNoMail, &sbuf) == 0) 239 return (NULL); 240 if (!freopen(calendarFile, "r", stdin)) 241 return (NULL); 242 } else { 243 chdir(getenv("HOME")); 244 for (found = i = 0; i < sizeof(calendarHomes) / 245 sizeof(calendarHomes[0]); i++) 246 if (chdir(calendarHomes[i]) == 0 && 247 freopen(calendarFile, "r", stdin)) { 248 found = 1; 249 break; 250 } 251 if (!found) 252 errx(1, "no calendar file: ``%s''", calendarFile); 253 } 254 } 255 if (pipe(pdes) < 0) 256 return (NULL); 257 switch (fork()) { 258 case -1: /* error */ 259 (void)close(pdes[0]); 260 (void)close(pdes[1]); 261 return (NULL); 262 case 0: 263 /* child -- stdin already setup, set stdout to pipe input */ 264 if (pdes[1] != STDOUT_FILENO) { 265 (void)dup2(pdes[1], STDOUT_FILENO); 266 (void)close(pdes[1]); 267 } 268 (void)close(pdes[0]); 269 uid = geteuid(); 270 if (setuid(getuid()) < 0) { 271 warnx("first setuid failed"); 272 _exit(1); 273 }; 274 if (setgid(getegid()) < 0) { 275 warnx("setgid failed"); 276 _exit(1); 277 } 278 if (setuid(uid) < 0) { 279 warnx("setuid failed"); 280 _exit(1); 281 } 282 execl(_PATH_CPP, "cpp", "-P", 283 "-traditional", "-nostdinc", /* GCC specific opts */ 284 "-I.", "-I", _PATH_INCLUDE, (char *)NULL); 285 warn(_PATH_CPP); 286 _exit(1); 287 } 288 /* parent -- set stdin to pipe output */ 289 (void)dup2(pdes[0], STDIN_FILENO); 290 (void)close(pdes[0]); 291 (void)close(pdes[1]); 292 293 /* not reading all calendar files, just set output to stdout */ 294 if (!doall) 295 return (stdout); 296 297 /* set output to a temporary file, so if no output don't send mail */ 298 (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 299 if ((fd = mkstemp(path)) < 0) 300 return (NULL); 301 return (fdopen(fd, "w+")); 302 } 303 304 void 305 closecal(fp) 306 FILE *fp; 307 { 308 uid_t uid; 309 struct stat sbuf; 310 int nread, pdes[2], status; 311 char buf[1024]; 312 313 if (!doall) 314 return; 315 316 (void)rewind(fp); 317 if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 318 goto done; 319 if (pipe(pdes) < 0) 320 goto done; 321 switch (fork()) { 322 case -1: /* error */ 323 (void)close(pdes[0]); 324 (void)close(pdes[1]); 325 goto done; 326 case 0: 327 /* child -- set stdin to pipe output */ 328 if (pdes[0] != STDIN_FILENO) { 329 (void)dup2(pdes[0], STDIN_FILENO); 330 (void)close(pdes[0]); 331 } 332 (void)close(pdes[1]); 333 uid = geteuid(); 334 if (setuid(getuid()) < 0) { 335 warnx("setuid failed"); 336 _exit(1); 337 }; 338 if (setgid(getegid()) < 0) { 339 warnx("setgid failed"); 340 _exit(1); 341 } 342 if (setuid(uid) < 0) { 343 warnx("setuid failed"); 344 _exit(1); 345 } 346 execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 347 "\"Reminder Service\"", (char *)NULL); 348 warn(_PATH_SENDMAIL); 349 _exit(1); 350 } 351 /* parent -- write to pipe input */ 352 (void)close(pdes[0]); 353 354 header[1].iov_base = header[3].iov_base = pw->pw_name; 355 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); 356 writev(pdes[1], header, 7); 357 while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 358 (void)write(pdes[1], buf, nread); 359 (void)close(pdes[1]); 360 done: (void)fclose(fp); 361 (void)unlink(path); 362 while (wait(&status) >= 0); 363 } 364