diff options
Diffstat (limited to 'src/ical.c')
-rw-r--r-- | src/ical.c | 311 |
1 files changed, 175 insertions, 136 deletions
@@ -1,7 +1,7 @@ /* * Calcurse - text-based organizer * - * Copyright (c) 2004-2020 calcurse Development Team <misc@calcurse.org> + * Copyright (c) 2004-2023 calcurse Development Team <misc@calcurse.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -213,8 +213,11 @@ static void ical_export_note(FILE *stream, char *name) int has_desc, has_prop, i; asprintf(¬e_file, "%s/%s", path_notes, name); - if (!(fp = fopen(note_file, "r"))) + if (!(fp = fopen(note_file, "r")) || ungetc(getc(fp), fp) == EOF) { + if (fp) + fclose(fp); return; + } string_init(¬e); while (fgets(lbuf, BUFSIZ, fp)) string_catf(¬e, "%s", lbuf); @@ -518,6 +521,10 @@ ical_store_event(char *mesg, char *note, time_t day, time_t end, struct event *ev; struct recur_event *rev; + if (!mesg) + mesg = mem_strdup(_("(empty)")); + EXIT_IF(!mesg, _("ical_store_event: out of memory")); + /* * Repeating event. The end day is ignored, and the event becomes * one-day even if multi-day. @@ -570,6 +577,10 @@ ical_store_apoint(char *mesg, char *note, time_t start, long dur, struct recur_apoint *rapt; time_t day; + if (!mesg) + mesg = mem_strdup(_("(empty)")); + EXIT_IF(!mesg, _("ical_store_event: out of memory")); + if (has_alarm) state |= APOINT_NOTIFY; if (rpt) { @@ -582,7 +593,7 @@ ical_store_apoint(char *mesg, char *note, time_t start, long dur, * calcurse until day must be changed to the day before. */ if (rpt->until) { - day = update_time_in_date(rpt->until, 0, 0); + day = DAY(rpt->until); if (recur_item_find_occurrence(start, dur, rpt, NULL, day, NULL) && get_item_time(rpt->until) < get_item_time(start)) @@ -680,7 +691,7 @@ static int ical_readline(FILE * fdi, char *buf, char *lstore, unsigned *ln) while (fgets(lstore, BUFSIZ, fdi) != NULL) { (*ln)++; if ((eol = strchr(lstore, '\n')) != NULL) { - if (*(eol - 1) == '\r') + if (strlen(lstore) > 1 && *(eol - 1) == '\r') *(eol - 1) = '\0'; else *eol = '\0'; @@ -1053,7 +1064,7 @@ static struct rpt *ical_read_rrule(FILE *log, char *rrulestr, time_t start, int *count) { - char freqstr[8]; + char freqstr[8], datestr[17]; struct rpt *rpt; char *p, *q; @@ -1082,7 +1093,7 @@ static struct rpt *ical_read_rrule(FILE *log, char *rrulestr, /* FREQ rule part */ if ((p = strstr(rrulestr, "FREQ="))) { - if (sscanf(p, "FREQ=%s", freqstr) != 1) { + if (sscanf(p, "FREQ=%7s", freqstr) != 1) { ical_log(log, ICAL_VEVENT, itemline, _("frequency not set in rrule.")); (*noskipped)++; @@ -1134,7 +1145,14 @@ static struct rpt *ical_read_rrule(FILE *log, char *rrulestr, } if ((p = strstr(rrulestr, "UNTIL="))) { - rpt->until = ical_datetime2time_t(strchr(p, '=') + 1, NULL, type); + if (sscanf(p, "UNTIL=%16s", datestr) != 1) { + ical_log(log, ICAL_VEVENT, itemline, + _("missing until value.")); + (*noskipped)++; + mem_free(rpt); + return NULL; + } + rpt->until = ical_datetime2time_t(datestr, NULL, type); if (!rpt->until) { ical_log(log, ICAL_VEVENT, itemline, _("invalid until format.")); @@ -1218,11 +1236,6 @@ ical_read_exdate(llist_t * exc, FILE * log, char *exstr, unsigned *noskipped, time_t t; int n; - if (type == UNDEFINED) { - ical_log(log, ICAL_VEVENT, itemline, - _("need DTSTART to determine event type.")); - goto cleanup; - } if (type != ical_get_type(exstr)) { ical_log(log, ICAL_VEVENT, itemline, _("invalid exception date value type.")); @@ -1327,12 +1340,9 @@ static char *ical_read_summary(char *line, unsigned *noskipped, } /* An event summary is one line only. */ - if (strchr(summary, '\n')) { - ical_log(log, item_type, itemline, _("line break in summary.")); - (*noskipped)++; - mem_free(summary); - summary = NULL; - } + for (p = summary; *p; p++) + if (*p == '\n') + *p = ' '; leave: return summary; } @@ -1346,8 +1356,9 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, const int ITEMLINE = *lineno - !feof(fdi); ical_vevent_e vevent_type; ical_property_e property; - char *p, *note = NULL, *tmp, *tzid; - struct string s; + char *p, *note, *tzid; + char *dtstart, *dtend, *duration, *rrule; + struct string s, exdate; struct { llist_t exc; struct rpt *rpt; @@ -1357,12 +1368,13 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, long dur; int has_alarm; } vevent; - int skip_alarm, has_note, separator; + int skip_alarm, has_note, separator, has_exdate; vevent_type = UNDEFINED; memset(&vevent, 0, sizeof vevent); LLIST_INIT(&vevent.exc); - skip_alarm = has_note = separator = 0; + note = dtstart = dtend = duration = rrule = NULL; + skip_alarm = has_note = separator = has_exdate =0; while (ical_readline(fdi, buf, lstore, lineno)) { note = NULL; property = NO_PROPERTY; @@ -1376,16 +1388,112 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, continue; } if (starts_with_ci(buf, "END:VEVENT")) { - if (!vevent.mesg) { + /* DTSTART and related properties (picked up earlier). */ + if (!dtstart) { ical_log(log, ICAL_VEVENT, ITEMLINE, - _("could not retrieve item summary.")); + _("item start date not defined.")); goto skip; } - if (vevent.start == 0) { + vevent_type = ical_get_type(dtstart); + if ((tzid = ical_get_tzid(dtstart)) && + vevent_type == APPOINTMENT) { + if (vevent.imp) { + asprintf(&p, "%s, TZID=%s", + vevent.imp, tzid); + mem_free(vevent.imp); + vevent.imp = p; + } else + asprintf(&vevent.imp, "TZID=%s", tzid); + has_note = separator = 1; + } + p = ical_get_value(dtstart); + if (!p) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("malformed start time line.")); + goto skip; + } + vevent.start = ical_datetime2time_t(p, tzid, vevent_type); + if (tzid) { + mem_free(tzid); + tzid = NULL; + } + if (!vevent.start) { ical_log(log, ICAL_VEVENT, ITEMLINE, - _("item start date is not defined.")); + _("invalid or malformed event " + "start time.")); goto skip; } + /* DTEND */ + if (!dtend) + goto duration; + if (vevent_type != ical_get_type(dtend)) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("invalid end time value type.")); + goto skip; + } + tzid = ical_get_tzid(dtend); + p = ical_get_value(dtend); + if (!p) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("malformed end time line.")); + goto skip; + } + vevent.end = ical_datetime2time_t(p, tzid, vevent_type); + if (tzid) { + mem_free(tzid); + tzid = NULL; + } + if (!vevent.end) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("malformed event end time.")); + goto skip; + } + if (vevent.end <= vevent.start) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("end must be later than start.")); + goto skip; + } + duration: + if (!duration) + goto rrule; + if (vevent.end) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("either end or duration.")); + goto skip; + } + p = ical_get_value(duration); + if (!p) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("malformed duration line.")); + goto skip; + } + vevent.dur = ical_dur2long(p, vevent_type); + if (!vevent.dur) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("invalid duration.")); + goto skip; + } + rrule: + if (!rrule) + goto exdate; + vevent.rpt = ical_read_rrule(log, rrule, noskipped, + ITEMLINE, vevent_type, vevent.start, + &vevent.count); + if (!vevent.rpt) + goto cleanup; + exdate: + if (!has_exdate) + goto duration_end; + if (!rrule) { + ical_log(log, ICAL_VEVENT, ITEMLINE, + _("exception date, but no recurrence " + "rule.")); + goto skip; + } + if (!ical_read_exdate(&vevent.exc, log, exdate.buf, + noskipped, ITEMLINE, vevent_type)) + goto cleanup; + duration_end: /* An APPOINTMENT must always have a duration. */ if (vevent_type == APPOINTMENT && !vevent.dur) { vevent.dur = vevent.end ? @@ -1402,10 +1510,10 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, char *md = _("multi-day event changed " "to one-day event"); if (vevent.imp) { - asprintf(&tmp, "%s, %s", + asprintf(&p, "%s, %s", vevent.imp, md); mem_free(vevent.imp); - vevent.imp = tmp; + vevent.imp = p; } else asprintf(&vevent.imp, "%s", md); has_note = separator = 1; @@ -1417,6 +1525,7 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, if (vevent.desc) { string_catf(&s, "%s", vevent.desc); mem_free(vevent.desc); + vevent.desc = NULL; } if (separator) string_catf(&s, SEPARATOR); @@ -1424,16 +1533,19 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, string_catf(&s, _("Location: %s"), vevent.loc); mem_free(vevent.loc); + vevent.loc = NULL; } if (vevent.comm) { string_catf(&s, _("Comment: %s"), vevent.comm); mem_free(vevent.comm); + vevent.comm = NULL; } if (vevent.imp) { string_catf(&s, ("Import: %s\n"), vevent.imp); mem_free(vevent.imp); + vevent.imp = NULL; } vevent.note = generate_note(string_buf(&s)); mem_free(s.buf); @@ -1444,7 +1556,7 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, char *msg; dur = vevent_type == EVENT ? -1 : vevent.dur; - day = update_time_in_date(vevent.start, 0, 0); + day = DAY(vevent.start); msg = _("rrule does not match start day (%s)."); if (vevent.count) { @@ -1494,115 +1606,28 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, } if (starts_with_ci(buf, "DTSTART")) { /* - * DTSTART has a value type: either DATE-TIME (by - * default) or DATE. Properties DTEND, DURATION and - * EXDATE and rrule part UNTIL must agree. - * Assume that DTSTART comes before the others even - * though RFC 5545 allows any order. + * DTSTART has a value type: either DATE-TIME or DATE. * In calcurse DATE-TIME implies an appointment, DATE an * event. + * Properties DTEND, DURATION and EXDATE and rrule part + * UNTIL must match the DTSTART value type. */ - vevent_type = ical_get_type(buf); - if ((tzid = ical_get_tzid(buf)) && - vevent_type == APPOINTMENT) { - /* Add note on TZID. */ - if (vevent.imp) { - asprintf(&tmp, "%s, TZID=%s", - vevent.imp, tzid); - mem_free(vevent.imp); - vevent.imp = tmp; - } else - asprintf(&vevent.imp, "TZID=%s", tzid); - has_note = separator = 1; - } - p = ical_get_value(buf); - if (!p) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("malformed start time line.")); - goto skip; - } - - vevent.start = ical_datetime2time_t(p, tzid, vevent_type); - if (tzid) - mem_free(tzid); - if (!vevent.start) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("invalid or malformed event " - "start time.")); - goto skip; - } + asprintf(&dtstart, "%s", buf); } else if (starts_with_ci(buf, "DTEND")) { - if (vevent.dur) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("either end or duration.")); - goto skip; - } - if (vevent_type == UNDEFINED) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("need DTSTART to determine " - "event type.")); - goto skip; - } - if (vevent_type != ical_get_type(buf)) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("invalid end time value type.")); - goto skip; - } - tzid = ical_get_tzid(buf); - p = ical_get_value(buf); - if (!p) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("malformed end time line.")); - goto skip; - } - - vevent.end = ical_datetime2time_t(p, tzid, vevent_type); - if (tzid) - mem_free(tzid); - if (!vevent.end) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("malformed event end time.")); - goto skip; - } - if (vevent.end <= vevent.start) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("end must be later than start.")); - goto skip; - } + asprintf(&dtend, "%s", buf); } else if (starts_with_ci(buf, "DURATION")) { - if (vevent.end) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("either end or duration.")); - goto skip; - } - if (vevent_type == UNDEFINED) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("need DTSTART to determine " - "event type.")); - goto skip; - } - p = ical_get_value(buf); - if (!p) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("malformed duration line.")); - goto skip; - } - vevent.dur = ical_dur2long(p, vevent_type); - if (!vevent.dur) { - ical_log(log, ICAL_VEVENT, ITEMLINE, - _("invalid duration.")); - goto skip; - } + asprintf(&duration, "%s", buf); } else if (starts_with_ci(buf, "RRULE")) { - vevent.rpt = ical_read_rrule(log, buf, noskipped, - ITEMLINE, vevent_type, vevent.start, - &vevent.count); - if (!vevent.rpt) - goto cleanup; + asprintf(&rrule, "%s", buf); } else if (starts_with_ci(buf, "EXDATE")) { - if (!ical_read_exdate(&vevent.exc, log, buf, noskipped, - ITEMLINE, vevent_type)) - goto cleanup; + if (!has_exdate) { + has_exdate = 1; + string_init(&exdate); + string_catf(&exdate, "%s", buf); + } else { + p = ical_get_value(buf); + string_catf(&exdate, ",%s", p); + } } else if (starts_with_ci(buf, "SUMMARY")) { vevent.mesg = ical_read_summary(buf, noskipped, ICAL_VEVENT, ITEMLINE, log); @@ -1646,10 +1671,10 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, case COMMENT: /* There may be more than one. */ if (vevent.comm) { - asprintf(&tmp, "%sComment: %s", + asprintf(&p, "%sComment: %s", vevent.comm, note); mem_free(vevent.comm); - vevent.comm = tmp; + vevent.comm = p; } else vevent.comm = note; break; @@ -1663,6 +1688,16 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents, skip: (*noskipped)++; cleanup: + if (dtstart) + mem_free(dtstart); + if (dtend) + mem_free(dtend); + if (duration) + mem_free(duration); + if (rrule) + mem_free(rrule); + if (has_exdate) + mem_free(exdate.buf); if (note) mem_free(note); if (vevent.desc) @@ -1686,7 +1721,7 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped, { const int ITEMLINE = *lineno - !feof(fdi); ical_property_e property; - char *note = NULL, *tmp; + char *p, *note; struct string s; struct { char *mesg, *desc, *loc, *comm, *note; @@ -1696,6 +1731,7 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped, int skip_alarm, has_note, separator; memset(&vtodo, 0, sizeof vtodo); + note = NULL; skip_alarm = has_note = separator = 0; while (ical_readline(fdi, buf, lstore, lineno)) { note = NULL; @@ -1721,6 +1757,7 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped, if (vtodo.desc) { string_catf(&s, "%s", vtodo.desc); mem_free(vtodo.desc); + vtodo.desc = NULL; } if (separator) string_catf(&s, SEPARATOR); @@ -1728,11 +1765,13 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped, string_catf(&s, _("Location: %s"), vtodo.loc); mem_free(vtodo.loc); + vtodo.loc = NULL; } if (vtodo.comm) { string_catf(&s, _("Comment: %s"), vtodo.comm); mem_free(vtodo.comm); + vtodo.comm = NULL; } vtodo.note = generate_note(string_buf(&s)); mem_free(s.buf); @@ -1796,10 +1835,10 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped, case COMMENT: /* There may be more than one. */ if (vtodo.comm) { - asprintf(&tmp, "%sComment: %s", + asprintf(&p, "%sComment: %s", vtodo.comm, note); mem_free(vtodo.comm); - vtodo.comm = tmp; + vtodo.comm = p; } else vtodo.comm = note; break; |