1205821Sedwin/*- 213840Swosch * Copyright (c) 1989, 1993, 1994 313840Swosch * The Regents of the University of California. All rights reserved. 413840Swosch * 513840Swosch * Redistribution and use in source and binary forms, with or without 613840Swosch * modification, are permitted provided that the following conditions 713840Swosch * are met: 813840Swosch * 1. Redistributions of source code must retain the above copyright 913840Swosch * notice, this list of conditions and the following disclaimer. 1013840Swosch * 2. Redistributions in binary form must reproduce the above copyright 1113840Swosch * notice, this list of conditions and the following disclaimer in the 1213840Swosch * documentation and/or other materials provided with the distribution. 1313840Swosch * 4. Neither the name of the University nor the names of its contributors 1413840Swosch * may be used to endorse or promote products derived from this software 1513840Swosch * without specific prior written permission. 1613840Swosch * 1713840Swosch * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1813840Swosch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1913840Swosch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2013840Swosch * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2113840Swosch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2213840Swosch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2313840Swosch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2413840Swosch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2513840Swosch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2613840Swosch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2713840Swosch * SUCH DAMAGE. 2813840Swosch */ 2913840Swosch 3013840Swosch#ifndef lint 3115714Sachestatic const char copyright[] = 3213840Swosch"@(#) Copyright (c) 1989, 1993\n\ 3313840Swosch The Regents of the University of California. All rights reserved.\n"; 3487235Smarkm#endif 3513840Swosch 3687628Sdwmalone#if 0 3713840Swosch#ifndef lint 3887628Sdwmalonestatic char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 3987235Smarkm#endif 4087628Sdwmalone#endif 4113840Swosch 4287628Sdwmalone#include <sys/cdefs.h> 4387628Sdwmalone__FBSDID("$FreeBSD$"); 4487628Sdwmalone 4513840Swosch#include <sys/param.h> 4687235Smarkm#include <sys/stat.h> 4787235Smarkm#include <sys/wait.h> 4813840Swosch#include <ctype.h> 4913840Swosch#include <err.h> 5013840Swosch#include <errno.h> 5174583Sache#include <langinfo.h> 5215714Sache#include <locale.h> 5387235Smarkm#include <pwd.h> 54285291Sbapt#include <stdbool.h> 55285291Sbapt#define _WITH_GETLINE 5687235Smarkm#include <stdio.h> 5787235Smarkm#include <stdlib.h> 5813840Swosch#include <string.h> 59285291Sbapt#include <stringlist.h> 6087235Smarkm#include <unistd.h> 6113840Swosch 6213840Swosch#include "pathnames.h" 6313840Swosch#include "calendar.h" 6413840Swosch 65285291Sbaptenum { 66285291Sbapt T_OK = 0, 67285291Sbapt T_ERR, 68285291Sbapt T_PROCESS, 69285291Sbapt}; 70285291Sbapt 71181322Sedwinconst char *calendarFile = "calendar"; /* default calendar file */ 72241737Sedstatic const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ 73241737Sedstatic const char *calendarNoMail = "nomail";/* don't sent mail if file exist */ 7413840Swosch 75241737Sedstatic char path[MAXPATHLEN]; 76181322Sedwin 77205821Sedwinstruct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; 78205821Sedwinstruct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; 7915720Sache 80285291Sbaptstatic int cal_parse(FILE *in, FILE *out); 81285291Sbapt 82285291Sbaptstatic StringList *definitions = NULL; 83285291Sbaptstatic struct event *events[MAXCOUNT]; 84285291Sbaptstatic char *extradata[MAXCOUNT]; 85285291Sbapt 86285291Sbaptstatic void 87285291Sbapttrimlr(char **buf) 88285291Sbapt{ 89285291Sbapt char *walk = *buf; 90285291Sbapt 91285291Sbapt while (isspace(*walk)) 92285291Sbapt walk++; 93285291Sbapt while (isspace(walk[strlen(walk) -1])) 94285291Sbapt walk[strlen(walk) -1] = '\0'; 95285291Sbapt 96285291Sbapt *buf = walk; 97285291Sbapt} 98285291Sbapt 99285291Sbaptstatic FILE * 100285291Sbaptcal_fopen(const char *file) 101285291Sbapt{ 102285291Sbapt FILE *fp; 103285291Sbapt char *home = getenv("HOME"); 104285291Sbapt unsigned int i; 105285291Sbapt 106285291Sbapt if (home == NULL || *home == '\0') { 107285291Sbapt warnx("Cannot get home directory"); 108285291Sbapt return (NULL); 109285291Sbapt } 110285291Sbapt 111285291Sbapt if (chdir(home) != 0) { 112285291Sbapt warnx("Cannot enter home directory"); 113285291Sbapt return (NULL); 114285291Sbapt } 115285291Sbapt 116285291Sbapt for (i = 0; i < sizeof(calendarHomes)/sizeof(calendarHomes[0]) ; i++) { 117285291Sbapt if (chdir(calendarHomes[i]) != 0) 118285291Sbapt continue; 119285291Sbapt 120285291Sbapt if ((fp = fopen(file, "r")) != NULL) 121285291Sbapt return (fp); 122285291Sbapt } 123285291Sbapt 124285291Sbapt warnx("can't open calendar file \"%s\"", file); 125285291Sbapt 126285291Sbapt return (NULL); 127285291Sbapt} 128285291Sbapt 129285291Sbaptstatic int 130285291Sbapttoken(char *line, FILE *out, bool *skip) 131285291Sbapt{ 132285291Sbapt char *walk, c, a; 133285291Sbapt 134285291Sbapt if (strncmp(line, "endif", 5) == 0) { 135285291Sbapt *skip = false; 136285291Sbapt return (T_OK); 137285291Sbapt } 138285291Sbapt 139285291Sbapt if (*skip) 140285291Sbapt return (T_OK); 141285291Sbapt 142285291Sbapt if (strncmp(line, "include", 7) == 0) { 143285291Sbapt walk = line + 7; 144285291Sbapt 145285291Sbapt trimlr(&walk); 146285291Sbapt 147285291Sbapt if (*walk == '\0') { 148285291Sbapt warnx("Expecting arguments after #include"); 149285291Sbapt return (T_ERR); 150285291Sbapt } 151285291Sbapt 152285291Sbapt if (*walk != '<' && *walk != '\"') { 153285291Sbapt warnx("Excecting '<' or '\"' after #include"); 154285291Sbapt return (T_ERR); 155285291Sbapt } 156285291Sbapt 157285291Sbapt a = *walk; 158285291Sbapt walk++; 159285291Sbapt c = walk[strlen(walk) - 1]; 160285291Sbapt 161285291Sbapt switch(c) { 162285291Sbapt case '>': 163285291Sbapt if (a != '<') { 164285291Sbapt warnx("Unterminated include expecting '\"'"); 165285291Sbapt return (T_ERR); 166285291Sbapt } 167285291Sbapt break; 168285291Sbapt case '\"': 169285291Sbapt if (a != '\"') { 170285291Sbapt warnx("Unterminated include expecting '>'"); 171285291Sbapt return (T_ERR); 172285291Sbapt } 173285291Sbapt break; 174285291Sbapt default: 175285291Sbapt warnx("Unterminated include expecting '%c'", 176285291Sbapt a == '<' ? '>' : '\"' ); 177285291Sbapt return (T_ERR); 178285291Sbapt } 179285291Sbapt walk[strlen(walk) - 1] = '\0'; 180285291Sbapt 181285291Sbapt if (cal_parse(cal_fopen(walk), out)) 182285291Sbapt return (T_ERR); 183285291Sbapt 184285291Sbapt return (T_OK); 185285291Sbapt } 186285291Sbapt 187285291Sbapt if (strncmp(line, "define", 6) == 0) { 188285291Sbapt if (definitions == NULL) 189285291Sbapt definitions = sl_init(); 190285291Sbapt walk = line + 6; 191285291Sbapt trimlr(&walk); 192285291Sbapt 193285291Sbapt if (*walk == '\0') { 194285291Sbapt warnx("Expecting arguments after #define"); 195285291Sbapt return (T_ERR); 196285291Sbapt } 197285291Sbapt 198285291Sbapt sl_add(definitions, strdup(walk)); 199285291Sbapt return (T_OK); 200285291Sbapt } 201285291Sbapt 202285291Sbapt if (strncmp(line, "ifndef", 6) == 0) { 203285291Sbapt walk = line + 6; 204285291Sbapt trimlr(&walk); 205285291Sbapt 206285291Sbapt if (*walk == '\0') { 207285291Sbapt warnx("Expecting arguments after #ifndef"); 208285291Sbapt return (T_ERR); 209285291Sbapt } 210285291Sbapt 211285291Sbapt if (definitions != NULL && sl_find(definitions, walk) != NULL) 212285291Sbapt *skip = true; 213285291Sbapt 214285291Sbapt return (T_OK); 215285291Sbapt } 216285291Sbapt 217285291Sbapt return (T_PROCESS); 218285291Sbapt 219285291Sbapt} 220285291Sbapt 221205821Sedwin#define REPLACE(string, slen, struct_) \ 222205821Sedwin if (strncasecmp(buf, (string), (slen)) == 0 && buf[(slen)]) { \ 223205821Sedwin if (struct_.name != NULL) \ 224205821Sedwin free(struct_.name); \ 225205821Sedwin if ((struct_.name = strdup(buf + (slen))) == NULL) \ 226205821Sedwin errx(1, "cannot allocate memory"); \ 227205821Sedwin struct_.len = strlen(buf + (slen)); \ 228205821Sedwin continue; \ 229205821Sedwin } 230285291Sbaptstatic int 231285291Sbaptcal_parse(FILE *in, FILE *out) 23213840Swosch{ 233285291Sbapt char *line = NULL; 234285291Sbapt char *buf; 235285291Sbapt size_t linecap = 0; 236285291Sbapt ssize_t linelen; 237285291Sbapt ssize_t l; 238285291Sbapt static int d_first = -1; 239285291Sbapt static int count = 0; 240285291Sbapt int i; 241205821Sedwin int month[MAXCOUNT]; 242205821Sedwin int day[MAXCOUNT]; 243205821Sedwin int year[MAXCOUNT]; 244285291Sbapt bool skip = false; 245285291Sbapt char dbuf[80]; 246285291Sbapt char *pp, p; 247285291Sbapt struct tm tm; 248205821Sedwin int flags; 24913840Swosch 250205821Sedwin /* Unused */ 251205821Sedwin tm.tm_sec = 0; 252205821Sedwin tm.tm_min = 0; 253205821Sedwin tm.tm_hour = 0; 254205821Sedwin tm.tm_wday = 0; 255205821Sedwin 256285291Sbapt if (in == NULL) 257285291Sbapt return (1); 258285291Sbapt 259285291Sbapt while ((linelen = getline(&line, &linecap, in)) > 0) { 260285291Sbapt if (linelen == 0) 261255715Sdb continue; 262285291Sbapt 263285291Sbapt if (*line == '#') { 264285291Sbapt switch (token(line+1, out, &skip)) { 265285291Sbapt case T_ERR: 266285291Sbapt free(line); 267285291Sbapt return (1); 268285291Sbapt case T_OK: 269285291Sbapt continue; 270285291Sbapt case T_PROCESS: 271285291Sbapt break; 272285291Sbapt default: 273285291Sbapt break; 274285291Sbapt } 275285291Sbapt } 276285291Sbapt 277285291Sbapt if (skip) 278285291Sbapt continue; 279285291Sbapt 280285291Sbapt buf = line; 281285291Sbapt for (l = linelen; 28215714Sache l > 0 && isspace((unsigned char)buf[l - 1]); 28315714Sache l--) 28415714Sache ; 28515714Sache buf[l] = '\0'; 28613840Swosch if (buf[0] == '\0') 28713840Swosch continue; 288205821Sedwin 289205821Sedwin /* Parse special definitions: LANG, Easter, Paskha etc */ 29015714Sache if (strncmp(buf, "LANG=", 5) == 0) { 291181322Sedwin (void)setlocale(LC_ALL, buf + 5); 29274583Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 29315714Sache setnnames(); 29415714Sache continue; 29515714Sache } 296205821Sedwin REPLACE("Easter=", 7, neaster); 297205821Sedwin REPLACE("Paskha=", 7, npaskha); 298205821Sedwin REPLACE("ChineseNewYear=", 15, ncny); 299205821Sedwin REPLACE("NewMoon=", 8, nnewmoon); 300205821Sedwin REPLACE("FullMoon=", 9, nfullmoon); 301205821Sedwin REPLACE("MarEquinox=", 11, nmarequinox); 302205821Sedwin REPLACE("SepEquinox=", 11, nsepequinox); 303205821Sedwin REPLACE("JunSolstice=", 12, njunsolstice); 304205821Sedwin REPLACE("DecSolstice=", 12, ndecsolstice); 305205821Sedwin if (strncmp(buf, "SEQUENCE=", 9) == 0) { 306205821Sedwin setnsequences(buf + 9); 30715720Sache continue; 30815720Sache } 30915723Sache 310181322Sedwin /* 311205821Sedwin * If the line starts with a tab, the data has to be 312205821Sedwin * added to the previous line 313181322Sedwin */ 314205821Sedwin if (buf[0] == '\t') { 315205821Sedwin for (i = 0; i < count; i++) 316205821Sedwin event_continue(events[i], buf); 317205821Sedwin continue; 318170447Sgrog } 319170447Sgrog 320205821Sedwin /* Get rid of leading spaces (non-standard) */ 321207701Sache while (isspace((unsigned char)buf[0])) 322207701Sache memcpy(buf, buf + 1, strlen(buf)); 323170447Sgrog 324205821Sedwin /* No tab in the line, then not a valid line */ 325205821Sedwin if ((pp = strchr(buf, '\t')) == NULL) 326205821Sedwin continue; 327170447Sgrog 328205821Sedwin /* Trim spaces in front of the tab */ 329207703Sache while (isspace((unsigned char)pp[-1])) 330205821Sedwin pp--; 331205821Sedwin 332205821Sedwin p = *pp; 333205821Sedwin *pp = '\0'; 334205821Sedwin if ((count = parsedaymonth(buf, year, month, day, &flags, 335205821Sedwin extradata)) == 0) 336205821Sedwin continue; 337205821Sedwin *pp = p; 338205821Sedwin if (count < 0) { 339205821Sedwin /* Show error status based on return value */ 340227370Sgrog if (debug) 341227370Sgrog fprintf(stderr, "Ignored: %s\n", buf); 342205821Sedwin if (count == -1) 343170447Sgrog continue; 344205821Sedwin count = -count + 1; 345170447Sgrog } 346170447Sgrog 347205821Sedwin /* Find the last tab */ 348205821Sedwin while (pp[1] == '\t') 349205821Sedwin pp++; 35013840Swosch 351205821Sedwin if (d_first < 0) 352205821Sedwin d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 353170447Sgrog 354205821Sedwin for (i = 0; i < count; i++) { 355205821Sedwin tm.tm_mon = month[i] - 1; 356205821Sedwin tm.tm_mday = day[i]; 357205821Sedwin tm.tm_year = year[i] - 1900; 358205821Sedwin (void)strftime(dbuf, sizeof(dbuf), 359205821Sedwin d_first ? "%e %b" : "%b %e", &tm); 360205821Sedwin if (debug) 361205821Sedwin fprintf(stderr, "got %s\n", pp); 362205821Sedwin events[i] = event_add(year[i], month[i], day[i], dbuf, 363205821Sedwin ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, 364205821Sedwin extradata[i]); 365181322Sedwin } 36613840Swosch } 36713840Swosch 368285291Sbapt free(line); 369285291Sbapt fclose(in); 370285291Sbapt 371285291Sbapt return (0); 372285291Sbapt} 373285291Sbapt 374285291Sbaptvoid 375285291Sbaptcal(void) 376285291Sbapt{ 377285291Sbapt FILE *fpin; 378285291Sbapt FILE *fpout; 379285291Sbapt int i; 380285291Sbapt 381285291Sbapt for (i = 0; i < MAXCOUNT; i++) 382285291Sbapt extradata[i] = (char *)calloc(1, 20); 383285291Sbapt 384285291Sbapt 385285291Sbapt if ((fpin = opencalin()) == NULL) 386285291Sbapt return; 387285291Sbapt 388285291Sbapt if ((fpout = opencalout()) == NULL) { 389285291Sbapt fclose(fpin); 390285291Sbapt return; 391285291Sbapt } 392285291Sbapt 393285291Sbapt if (cal_parse(fpin, fpout)) 394285291Sbapt return; 395285291Sbapt 396255715Sdb event_print_all(fpout); 397255715Sdb closecal(fpout); 39813840Swosch} 39913840Swosch 40013840SwoschFILE * 401255715Sdbopencalin(void) 40213840Swosch{ 40313840Swosch struct stat sbuf; 404255715Sdb FILE *fpin; 40513840Swosch 406255715Sdb /* open up calendar file */ 407255715Sdb if ((fpin = fopen(calendarFile, "r")) == NULL) { 40813840Swosch if (doall) { 409181322Sedwin if (chdir(calendarHomes[0]) != 0) 410181322Sedwin return (NULL); 411181322Sedwin if (stat(calendarNoMail, &sbuf) == 0) 412181322Sedwin return (NULL); 413255715Sdb if ((fpin = fopen(calendarFile, "r")) == NULL) 414181322Sedwin return (NULL); 41513840Swosch } else { 416285291Sbapt fpin = cal_fopen(calendarFile); 41713840Swosch } 41813840Swosch } 419255715Sdb return (fpin); 420255715Sdb} 42113840Swosch 422255715SdbFILE * 423255715Sdbopencalout(void) 424255715Sdb{ 425255715Sdb int fd; 426255715Sdb 42713840Swosch /* not reading all calendar files, just set output to stdout */ 42813840Swosch if (!doall) 42913840Swosch return (stdout); 43013840Swosch 43113840Swosch /* set output to a temporary file, so if no output don't send mail */ 432255715Sdb snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); 43313840Swosch if ((fd = mkstemp(path)) < 0) 43413840Swosch return (NULL); 43513840Swosch return (fdopen(fd, "w+")); 43613840Swosch} 43713840Swosch 43813840Swoschvoid 439169343Sdwmaloneclosecal(FILE *fp) 44013840Swosch{ 44122473Smpp uid_t uid; 44213840Swosch struct stat sbuf; 44313840Swosch int nread, pdes[2], status; 44413840Swosch char buf[1024]; 44513840Swosch 44613840Swosch if (!doall) 44713840Swosch return; 44813840Swosch 449200628Srse rewind(fp); 45013840Swosch if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) 45113840Swosch goto done; 45213840Swosch if (pipe(pdes) < 0) 45313840Swosch goto done; 45440302Sdes switch (fork()) { 45513840Swosch case -1: /* error */ 45613840Swosch (void)close(pdes[0]); 45713840Swosch (void)close(pdes[1]); 45813840Swosch goto done; 45913840Swosch case 0: 46013840Swosch /* child -- set stdin to pipe output */ 46113840Swosch if (pdes[0] != STDIN_FILENO) { 46213840Swosch (void)dup2(pdes[0], STDIN_FILENO); 46313840Swosch (void)close(pdes[0]); 46413840Swosch } 46513840Swosch (void)close(pdes[1]); 46622473Smpp uid = geteuid(); 46722473Smpp if (setuid(getuid()) < 0) { 46826839Scharnier warnx("setuid failed"); 46922473Smpp _exit(1); 47022473Smpp }; 47122473Smpp if (setgid(getegid()) < 0) { 47226839Scharnier warnx("setgid failed"); 47322473Smpp _exit(1); 47422473Smpp } 47522473Smpp if (setuid(uid) < 0) { 47626839Scharnier warnx("setuid failed"); 47722473Smpp _exit(1); 47822473Smpp } 47913840Swosch execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", 48079452Sbrian "\"Reminder Service\"", (char *)NULL); 48126839Scharnier warn(_PATH_SENDMAIL); 48213840Swosch _exit(1); 48313840Swosch } 48413840Swosch /* parent -- write to pipe input */ 48513840Swosch (void)close(pdes[0]); 48613840Swosch 487205821Sedwin write(pdes[1], "From: \"Reminder Service\" <", 26); 488205821Sedwin write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 489205821Sedwin write(pdes[1], ">\nTo: <", 7); 490205821Sedwin write(pdes[1], pw->pw_name, strlen(pw->pw_name)); 491222755Sjh write(pdes[1], ">\nSubject: ", 11); 492205821Sedwin write(pdes[1], dayname, strlen(dayname)); 493205821Sedwin write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); 494205821Sedwin 49513840Swosch while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) 49613840Swosch (void)write(pdes[1], buf, nread); 49713840Swosch (void)close(pdes[1]); 49813840Swoschdone: (void)fclose(fp); 49913840Swosch (void)unlink(path); 50013840Swosch while (wait(&status) >= 0); 50113840Swosch} 502