aboutsummaryrefslogtreecommitdiffstats
path: root/src/ical.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ical.c')
-rw-r--r--src/ical.c311
1 files changed, 175 insertions, 136 deletions
diff --git a/src/ical.c b/src/ical.c
index 5847b88..a8ce0a4 100644
--- a/src/ical.c
+++ b/src/ical.c
@@ -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(&note_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(&note);
while (fgets(lbuf, BUFSIZ, fp))
string_catf(&note, "%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;