From 480bf4063f7d31b7d2c9ec8edd9fc4280ffb39ee Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 10 Dec 2017 09:24:34 -0700 Subject: Initial commit for branch ical --- pith/ical.c | 2245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2245 insertions(+) create mode 100644 pith/ical.c (limited to 'pith/ical.c') diff --git a/pith/ical.c b/pith/ical.c new file mode 100644 index 00000000..8364d384 --- /dev/null +++ b/pith/ical.c @@ -0,0 +1,2245 @@ +#include "../pith/headers.h" +#include "../pith/mailpart.h" +#include "../pith/store.h" +#include "../pith/ical.h" +#ifdef STANDALONE +#include "readfile.h" +#include "mem.h" +char *lf2crlf(char *); +#endif /* STANDALONE */ + +typedef struct ical_iana_comp_s { + char *comp; /* component name */ + size_t len; /* size of component name (strlen(x->comp)) */ + int pos; /* position of this component in comp array */ + void *(*parse)(char **); /* parser */ + void (*give)(void **); /* free memory */ +} ICAL_COMP_S; + +typedef struct ical_iana_prop_s { + char *prop; /* component name */ + size_t len; /* size of component name (strlen(x->comp)) */ + int pos; /* location of this component in the prop array */ + void (*parse)(); /* parser */ + void (*give)(void **); /* free memory */ +} ICAL_PROP_S; + +int ical_january_first(int); /* args: year */ +void ical_adjust_date(struct tm *, VTIMEZONE_S *); + +void ical_initialize(void); + +int ical_non_ascii_valid(unsigned char); +char *ical_unfold_line(char *); +ICLINE_S *ical_parse_line(char **, char *); + +ICLINE_S *ical_cline_cpy(ICLINE_S *); +ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param); + +char *ical_get_value(char **); + +/* pase component */ +void *ical_parse_vcalendar(char **); +void *ical_parse_vevent(char **); +void *ical_parse_vtodo(char **); +void *ical_parse_vjournal(char **); +void *ical_parse_vfreebusy(char **); +void *ical_parse_vtimezone(char **); +void *ical_parse_valarm(char **); +void *ical_parse_timezone(char **); +ICAL_S *ical_parse_unknown_comp(char **, int); +ICAL_S *ical_parse_generic_comp(char **, int); + +/* free components */ +void ical_free_vevent(void **); +void ical_free_vtodo(void **); +void ical_free_vjournal(void **); +void ical_free_vfreebusy(void **); +void ical_free_vtimezone(void **); +void ical_free_timezone(void **); +void ical_free_valarm(void **); +void ical_free_unknown_comp(ICAL_S **); + +/* parse properties */ +void ical_cline_from_token(void **, char **, char *); +void ical_gencline_from_token(void **, char **, char *); + +void ical_parse_rrule(RRULE_S **, char **); +void ical_parse_time(struct tm *, char **, char *); +void ical_parse_offset(int *, char **, char *); + +void ical_parse_freq(Freq_value *, char *); +void ical_parse_weekday_list(BYWKDY_S **, char *); +void ical_parse_number_list(BYWKDY_S **, char *); +void ical_parse_interval(unsigned long *, char *); + +int ical_get_number_value(char *, int, int); +void ical_set_date(ICLINE_S *, VTIMEZONE_S *); +void ical_set_date_vevent(void *, void *); + +/* free properties */ +void ical_free_prop(void **, ICAL_PROP_S *); +void ical_free_null(void **); +void ical_free_cline(void **); +void ical_free_param(ICAL_PARAMETER_S **); +void ical_free_gencline(void **); +void ical_free_rrule(void **); +void ical_free_weekday_list(void **); + +/* globals */ +struct tm day_zero; /* date for january 1, 1601 */ +int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define UTF8_COMPLETE (1) +#define NEED_MORE (2) + +char *ical_buf; +unsigned long ical_buf_len; + + +/* parsing structures */ + +/* this is the list of V-components to a Calendar from RFC 5545 */ +ICAL_COMP_S ical_comp[] = { + {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar}, + {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone}, + {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent}, + {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo}, + {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal}, + {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm}, + {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy}, + {NULL, 0, VUnknown, NULL, 0} +}; + +/* array for properties */ +ICAL_PROP_S rrule_prop[] = { + {"FREQ", 4, RRFreq, ical_parse_freq, ical_free_null}, + {"UNTIL", 5, RRUntil, ical_parse_freq, 0}, + {"COUNT", 5, RRCount, ical_cline_from_token, ical_free_cline}, + {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_free_null}, + {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list}, + {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list}, + {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list}, + {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list}, + {"BYWEEKNO", 8, RRByweekno, 0, 0}, + {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list}, + {"BYSETPOS", 8, RRBysetpos, 0, 0}, + {"BYWKST", 6, RRWkst, 0, 0}, + {"BYMONTHDAY", + 10, RRBymonthday, 0, 0}, + {"BYYEARDAY", 9, RRByyearday, 0, 0}, + {NULL, 0, RRUnknown, 0, 0} +}; + +ICAL_PROP_S event_prop[] = { + {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline}, + {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline}, + {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline}, + {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline}, + {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline}, + {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline}, + {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline}, + {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline}, + {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline}, + {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline}, + {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline}, + {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline}, + {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline}, + {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline}, + {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline}, + {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline}, + {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline}, + {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule}, + {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline}, + {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline}, + {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline}, + {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline}, + {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline}, + {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline}, + {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline}, + {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline}, + {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline}, + {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline}, + {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline}, + {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, EvUnknown, 0, 0} +}; + +ICAL_PROP_S tz_comp[] = { + {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline}, + {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline}, + {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline}, + {NULL, 0, TZCUnknown, 0, 0} +}; + +ICAL_PROP_S tz_prop[] = { + {"DTSTART", 7, TZPDtstart, ical_parse_time, 0}, + {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, 0}, + {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, 0}, + {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule}, + {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline}, + {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline}, + {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, TZPUnknown, 0, 0} +}; + +ICAL_PROP_S alarm_prop[] = { + {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline}, + {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline}, + {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline}, + {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline}, + {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline}, + {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline}, + {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline}, + {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, AlUnknown, 0, 0} +}; + +/* some useful macros for character analysis */ + +#define ical_wspace(X) \ + ((X) == ' ' || (X) == '\t') + +#define ical_name_allowed_char(X) \ + (((X) >= 'A' && (X) <= 'Z') || \ + ((X) >= 'a' && (X) <= 'z') || \ + (X) == '-' ) + +#define ical_control(X) \ + (((X) >= 0x00 && (X) <= 0x08) || \ + ((X) >= 0x0A && (X) <= 0x1F) || \ + (X) == 0x7F) + +#define ical_safe_char(X) \ + (ical_non_ascii_valid(X) \ + || ical_wspace(X) \ + || (X) == 0x21 \ + || ((X) >= 0x23 && (X) <= 0x2B) \ + || ((X) >= 0x2D && (X) <= 0x39) \ + || ((X) >= 0x3C && (X) <= 0x7E)) + +#define ical_qsafe_char(X) \ + (ical_non_ascii_valid((X)) \ + || ical_wspace(X) \ + || (X) == 0x21 \ + || ((X) >= 0x23 && (X) <= 0x7E)) + +#define ical_value_char(X) \ + (ical_non_ascii_valid(X) \ + || ical_wspace(X) \ + || ((X) >= 0x21 && (X) <= 0x7E)) + +/* Finally, here begins the code. */ + +void ical_debug(char *fcn, char *text) +{ + char piece[50]; + strncpy(piece, text, 49); + dprint((9, "%s: %s", fcn, piece)); +} + +/*** + *** FREE MEMORY FUNCTIONS + ***/ + +void +ical_free_param(ICAL_PARAMETER_S **param) +{ + if(param == NULL || *param == NULL) + return; + + if((*param)->name) fs_give((void **) &(*param)->name); + if((*param)->value) fs_give((void **) &(*param)->value); + if((*param)->next) ical_free_param(&(*param)->next); + fs_give((void **)param); +} + +void +ical_free_null(void **n) +{ + if(n != NULL) *n = NULL; +} + +void +ical_free_cline(void **icv) +{ + ICLINE_S **ic = (ICLINE_S **) icv; + + if(ic == NULL || *ic == NULL) + return; + + if((*ic)->token) fs_give((void **) &(*ic)->token); + if((*ic)->param) ical_free_param(&(*ic)->param); + if((*ic)->value) fs_give((void **) &(*ic)->value); + fs_give(icv); +} + +void +ical_free_gencline(void **giclpv) +{ + GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv; + + if(giclp == NULL || *giclp == NULL) return; + + if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline); + if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next); +} + +void +ical_free_vcalendar(void **vcalpv) +{ + VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv; + + if(vcalp == NULL || *vcalp == NULL) return; + + if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid); + if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version); + if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale); + if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method); + if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop); + if((*vcalp)->comp){ + Cal_comp i; + for(i = 0; i < VUnknown; i++) + if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]); + fs_give((*vcalp)->comp); + } + if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp); + fs_give(vcalpv); +} + +void +ical_free_vevent(void **veventpv) +{ + VEVENT_S **veventp = (VEVENT_S **) veventpv; + + if(veventp == NULL || *veventp == NULL) return; + + ical_free_prop((*veventp)->prop, event_prop); + if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop); + if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm); + fs_give(veventpv); +} + +void +ical_free_rrule(void **rrulepv) +{ + RRULE_S **rrulep = (RRULE_S **) rrulepv; + + if(rrulep && *rrulep){ + ical_free_prop((*rrulep)->prop, rrule_prop); + ical_free_param(&(*rrulep)->param); + fs_give(rrulepv); + } +} + +void +ical_free_weekday_list(void **wkdylv) +{ + BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv; + + if(wkdyl == NULL) return; + + if((*wkdyl)->next) + ical_free_weekday_list((void **) &(*wkdyl)->next); + + fs_give(wkdylv); +} + + +void +ical_free_vtodo(void **vtodopv) +{ +} + +void +ical_free_vjournal(void **vjournalpv) +{ +} + +void +ical_free_vfreebusy(void **vfbpv) +{ +} + +void +ical_free_prop(void **propv, ICAL_PROP_S *aux_comp) +{ + int i, j; + + if(propv == NULL) return; + + for(i = 0; aux_comp[i].prop != NULL; i++) + if(propv[i]){ + for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++); + if(aux_comp[j].give) (aux_comp[j].give)(&propv[i]); + } + fs_give(propv); +} + + +void +ical_free_vtimezone(void **vtzpv) +{ + VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv; + TZ_comp i,j; + + if(vtzp == NULL || *vtzp == NULL) return; + + ical_free_prop((*vtzp)->prop, tz_comp); + + if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop); + if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc); + if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc); + fs_give(vtzpv); +} + +void +ical_free_timezone(void **tzpv) +{ + ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv; + + if(tzp == NULL || *tzp == NULL) return; + + ical_free_prop((*tzp)->prop, tz_prop); + if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop); + if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next); + fs_give(tzpv); +} + +void ical_free_valarm(void **valarmpv) +{ +} + +void +ical_free_unknown_comp(ICAL_S **icalp) +{ + int i; + if(icalp == NULL || *icalp == NULL) return; + for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++); + if(ical_comp[i].give) + (ical_comp[i].give)(&(*icalp)->value); + else + ical_free_gencline((void **) &(*icalp)->value); + fs_give((void **)&(*icalp)->comp); + ical_free_unknown_comp(&(*icalp)->next); + ical_free_unknown_comp(&(*icalp)->branch); + fs_give((void **)icalp); +} + +char *ical_unfold_line(char *line) +{ + int i, j; + + if(line == NULL) + return NULL; + + for(i = 0, j = 0; line[j] != '\0';) + switch(line[j]){ + case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){ + j += 3; /* get past white space */ + continue; + } + default : line[i++] = line[j++]; + } + line[i] = '\0'; + return line; +} + +ICAL_PARAMETER_S * +ical_get_parameter(char **line) +{ + ICAL_PARAMETER_S *param = NULL; + char *s; + + if(line == NULL || *line == NULL) + return NULL; + + for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++); + + if(*s == '='){ + int quoted; + char c; + + param = fs_get(sizeof(ICAL_PARAMETER_S)); + memset((void *)param, 0, sizeof(ICAL_PARAMETER_S)); + *s = '\0'; + param->name = cpystr(*line); + *s = '='; + *line = s+1; /* step over '=' */ + quoted = **line == '"' ? 1 : 0; + if(quoted != 0){ + for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++); + if(*s != '"'){ /* error, do not parse this line */ + ical_free_param(¶m); + *line = strchr(s, ':'); /* reset line to closest ':' */ + return NULL; + } + } + else + for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++); + c = *s; + *s = '\0'; + param->value = cpystr(*line); + *s = c; /* restore character */ + *line = quoted ? s + 1 : s; + + if(**line == ';'){ + ++*line; + param->next = ical_get_parameter(line); + } + } + return param; +} + +char *ical_get_value(char **line) +{ + char *s, *t; + + if(line == NULL || *line == NULL) + return NULL; + + for (s = *line; *s && ical_value_char((unsigned char) *s); s++); + if(*s == '\r'){ + *s = '\0'; + t = cpystr(*line); + *s = '\r'; + *line = s+2; + } + return t; +} + +ICAL_PARAMETER_S * +ical_parameter_cpy(ICAL_PARAMETER_S *param) +{ + ICAL_PARAMETER_S *rv; + + if(param == NULL) return NULL; + + rv = fs_get(sizeof(ICAL_PARAMETER_S)); + memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S)); + + if(param->name) rv->name = cpystr(param->name); + if(param->value) rv->value = cpystr(param->value); + if(param->next) rv->next = ical_parameter_cpy(param->next); + + return rv; +} + +ICLINE_S * +ical_cline_cpy(ICLINE_S *icl) +{ + ICLINE_S *rv; + + if(icl == NULL) + return NULL; + + rv = fs_get(sizeof(ICLINE_S)); + memset((void *)rv, 0, sizeof(ICLINE_S)); + + if(icl->token) rv->token = cpystr(icl->token); + if(icl->param) rv->param = ical_parameter_cpy(icl->param); + if(icl->value) rv->value = cpystr(icl->value); + + return rv; +} + +/* Given a \r\n-ending line (called *text), isolate the ocurrence + * of the token in that line. + * Return the token, and modify the pointer to *text to point to the + * end of the token. Modify sep to contain the character following + * the token + * ical-line = token ':'/';' rest of the line\r\n + * on error return null, and set *text to the next line, if possible. + */ +char * +ical_isolate_token(char **text, char *sep) +{ + char *s, *t; + + for(t = s = *text; *t && ical_name_allowed_char(*s); s++); + /* only followed by parameter or value */ + if(*s == ':' || *s == ';'){ + *sep = *s; + *s = '\0'; /* isolate token at pointer s */ + *text = s; + } + else{ /* bad data - bail out of here */ + t = NULL; + if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL) + *text = NULL; + else /* move to next line */ + *text = s + 2; + } + return t; +} + + +VCALENDAR_S * +ical_parse_text(char *text) +{ + char *s; + VCALENDAR_S *vcal = NULL; + + ical_debug("ical_parse_text", text); + ical_initialize(); + + text = ical_unfold_line(text); + for(s = text; s && *s != '\0'; s++){ + if(*s != 'B' && *s != 'b') + continue; + if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){ + s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */ + vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s); + break; + } + } + return vcal; +} + +void +ical_parse_time(struct tm *ic_date, char **text, char *token) +{ + ICLINE_S *icl; + + icl = ical_parse_line(text, token); + ical_parse_date(icl->value, ic_date); + ical_free_cline((void **) &icl); +} + +void +ical_parse_interval(unsigned long *longv, char *value) +{ + *longv = atoi(value); +} + + +void +ical_parse_offset(int *offsetv, char **text, char *token) +{ + ICLINE_S *icl; + char *value; + int h, m, offset; + + icl = ical_parse_line(text, token); + + if(*icl->value == '+' || *icl->value == '-') + value = icl->value + 1; + else + value = icl->value; + + h = ical_get_number_value(value, 0, 2); + m = ical_get_number_value(value, 2, 4); + + offset = 60*(60*h + m); + if(*icl->value == '-') + offset *= -1; + + *offsetv = offset; + ical_free_cline((void **) &icl); +} + +void +ical_cline_from_token(void **iclv, char **text, char *token) +{ + ICLINE_S **icl = (ICLINE_S **)iclv; + + if(*icl == NULL) + *icl = ical_parse_line(text, token); + else { + ICLINE_S *ic = ical_parse_line(text, token); + ical_free_cline((void **)&ic); + } +} + +void +ical_gencline_from_token(void **giclv, char **text, char *token) +{ + GEN_ICLINE_S *gicl; + + if(!struncmp(*text, token, strlen(token))){ + gicl = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *) gicl, 0, sizeof(GEN_ICLINE_S)); + ical_cline_from_token((void **) &gicl->cline, text, token); + ical_gencline_from_token((void **) &gicl->next, text, token); + *giclv = (void *) gicl; + } +} + +/*** + *** PARSE COMPONENT FUNCTIONS + ***/ + +void * +ical_parse_vcalendar(char **text) +{ + char *s, *t; + char c; + VCALENDAR_S *vcal; + void *v; + + dprint((9, "ical_parse_vcalendar:\n")); + ical_debug("ical_parse_vcalendar", *text); + + vcal = fs_get(sizeof(VCALENDAR_S)); + memset((void *)vcal, 0, sizeof(VCALENDAR_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + + *t = c; /* restore character */ + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + int i; + switch(*s){ + case 'B': + case 'b': if(!struncmp(s+1, "EGIN", 4)){ + s += 6; /* 6 = strlen("BEGIN:") */ + for(i = 0; ical_comp[i].comp + && (struncmp(s, ical_comp[i].comp, ical_comp[i].len) + || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++); + + if(ical_comp[i].parse){ + s += ical_comp[i].len + 2; + v = (ical_comp[i].parse)(&s); + if(vcal->comp == NULL){ + vcal->comp = fs_get(VUnknown*sizeof(void *)); + memset((void *) vcal->comp, 0, VUnknown*sizeof(void *)); + } + + if(vcal->comp[ical_comp[i].pos] == NULL) + vcal->comp[ical_comp[i].pos] = v; + else + (ical_comp[i].give)(&v); + } else { + v = (void *) ical_parse_unknown_comp(&s, 0); + if(vcal->uk_comp == NULL) + vcal->uk_comp = (ICAL_S *) v; + else{ + ICAL_S *ic; + for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch); + ic->branch = (ICAL_S *) v; + } + } + } else ukn++; + break; + + case 'C': + case 'c': if(!struncmp(s+1, "ALSCALE", 7)) + ical_cline_from_token((void **) &vcal->calscale, &s, "CALSCALE"); + else ukn++; + break; + + case 'E': + case 'e': if(!struncmp(s+1, "ND", 2)){ + *t = c; + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VCALENDAR\r\n", 11)){ + *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */ + return (void *) vcal; + } +// else ukn++; FIX THIS, this is not quite right + } else ukn++; + break; + + case 'M': + case 'm': if(!struncmp(s+1, "ETHOD", 5)) + ical_cline_from_token((void **) &vcal->method, &s, "METHOD"); + else ukn++; + break; + + case 'P': + case 'p': if(!struncmp(s+1, "RODID", 5)) + ical_cline_from_token((void **) &vcal->prodid, &s, "PRODID"); + else ukn++; + break; + + case 'V': + case 'v': if(!struncmp(s+1, "ERSION", 6)){ + ical_cline_from_token((void **) &vcal->version, &s, "VERSION"); + } else ukn++; + break; + + default : ukn++; + break; + } /* end of switch(*s) */ + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vcal->uk_prop == NULL){ + vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vcal->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + + /* ok, we have parsed the vcalendar, now parse some special properties */ + /* start by parsing dates */ + ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]); + return (void *) vcal; +} + +void * +ical_parse_vevent(char **text) +{ + char *s, *t; + char c; + VEVENT_S *vevent; + + ical_debug("ical_parse_vevent", *text); + vevent = fs_get(sizeof(VEVENT_S)); + memset((void *)vevent, 0, sizeof(VEVENT_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(!struncmp(s, "VALARM\r\n", 8)){ + s += 8; /* 8 = strlen("VALARM\r\n"); */ + if(vevent->valarm == NULL) + vevent->valarm = ical_parse_valarm(&s); + else{ + VALARM_S *valrm; + for(valrm = vevent->valarm; valrm && valrm->next; + valrm = valrm->next); + valrm->next = ical_parse_valarm(&s); + } + } else { + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VEVENT\r\n",8)){ + *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */ + return (void *) vevent; + } + } else{ Event_prop i; + for(i = 0; i < EvUnknown; i++) + if(!struncmp(s, event_prop[i].prop, t-s)) + break; + if(event_prop[i].parse){ + if(vevent->prop == NULL){ + vevent->prop = fs_get(EvUnknown*sizeof(void *)); + memset((void *)vevent->prop, 0, EvUnknown*sizeof(void *)); + } + (event_prop[i].parse)(&vevent->prop[event_prop[i].pos], &s, event_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vevent->uk_prop == NULL){ + vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vevent->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) vevent; +} + +void * +ical_parse_vtimezone(char **text) +{ + char *s, *t; + char c; + void *v; + VTIMEZONE_S *vtz; + + ical_debug("ical_parse_vtimezone", *text); + vtz = fs_get(sizeof(VTIMEZONE_S)); + memset((void *)vtz, 0, sizeof(VTIMEZONE_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(!struncmp(s, "STANDARD\r\n", 10)){ + s += 10; /* 10 = strlen("STANDARD\r\n"); */ + v = ical_parse_timezone(&s); + if(vtz->standardc == NULL) + vtz->standardc = (ICAL_TZPROP_S *) v; + else{ + ICAL_TZPROP_S *dl; + for(dl = vtz->standardc; dl && dl->next; dl = dl->next); + dl->next = (ICAL_TZPROP_S *) v; + } + } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){ + s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */ + v = ical_parse_timezone(&s); + if(vtz->daylightc == NULL) + vtz->daylightc = (ICAL_TZPROP_S *) v; + else{ + ICAL_TZPROP_S *dl; + for(dl = vtz->daylightc; dl && dl->next; dl = dl->next); + dl->next = (ICAL_TZPROP_S *) v; + } + } else { + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VTIMEZONE\r\n",11)){ + *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */ + return (void *) vtz; + } + } else{ TZ_comp i; + for(i = 0; i < TZCUnknown; i++) + if(!struncmp(s, tz_comp[i].prop, t-s)) + break; + if(tz_comp[i].parse){ + if(vtz->prop == NULL){ + vtz->prop = fs_get(TZCUnknown*sizeof(void *)); + memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *)); + } + (tz_comp[i].parse)(&vtz->prop[tz_comp[i].pos], &s, tz_comp[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vtz->uk_prop == NULL){ + vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vtz->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) vtz; +} + +void * +ical_parse_timezone(char **text) +{ + char *s, *t; + char c; + ICAL_TZPROP_S *tzprop; + + ical_debug("ical_parse_timezone", *text); + tzprop = fs_get(sizeof(ICAL_TZPROP_S)); + memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "STANDARD\r\n", 10) + || !struncmp(s, "DAYLIGHT\r\n", 10)){ + *text = s + 10; /* 10 = strlen("STANDARD\r\n") */ + return (void *) tzprop; + } + } else{ TZ_prop i; + for(i = 0; i < TZPUnknown; i++) + if(!struncmp(s, tz_prop[i].prop, t-s)) + break; + if(tz_prop[i].parse){ + if(tzprop->prop == NULL){ + tzprop->prop = fs_get(TZPUnknown*sizeof(void *)); + memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *)); + } + (tz_prop[i].parse)(&tzprop->prop[tz_prop[i].pos], &s, tz_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(tzprop->uk_prop == NULL){ + tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S)); + tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) tzprop; +} + +void * +ical_parse_valarm(char **text) +{ + char *s, *t; + char c; + VALARM_S *valarm; + + ical_debug("ical_parse_valarm", *text); + valarm = fs_get(sizeof(VALARM_S)); + memset((void *)valarm, 0, sizeof(VALARM_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "ALARM\r\n", 7)){ + *text = s + 7; /* 7 = strlen("ALARM\r\n") */ + return (void *) valarm; + } + } else{ Alarm_prop i; + for(i = 0; i < AlUnknown; i++) + if(!struncmp(s, alarm_prop[i].prop, t-s)) + break; + if(alarm_prop[i].parse){ + if(valarm->prop == NULL){ + valarm->prop = fs_get(AlUnknown*sizeof(void *)); + memset((void *)valarm->prop, 0, AlUnknown*sizeof(void *)); + } + (alarm_prop[i].parse)(&valarm->prop[alarm_prop[i].pos], &s, alarm_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(valarm->uk_prop == NULL){ + valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S)); + valarm->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) valarm; +} + +void * +ical_parse_vtodo(char **text) +{ + return NULL; +} + +void * +ical_parse_vjournal(char **text) +{ + return NULL; +} + +void * +ical_parse_vfreebusy(char **text) +{ + return NULL; +} + +ICAL_S * +ical_parse_generic_comp(char **text, int level) +{ + ICAL_S *ical; + char *s, *t; + char *token = NULL; + GEN_ICLINE_S *gcl = NULL; + char c; + + ical_debug("ical_parse_generic_comp", *text); + ical = fs_get(sizeof(ICAL_S)); + memset((void *)ical, 0, sizeof(ICAL_S)); + + ical->comp = ical_get_value(text); + token = fs_get(strlen(ical->comp) + 2 + 1); + sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */ + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + + *t = c; /* restore character */ + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + switch(*s){ + case 'B': + case 'b': if(!struncmp(s+1, "EGIN", 4)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(ical->next == NULL) + ical->next = ical_parse_unknown_comp(&s, level+1); + else{ + ICAL_S *b; + int i; + + for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++); + if(b->branch == NULL) + b->branch = ical_parse_unknown_comp(&s, level+1); + else { + for(; b && b->branch; b = b->branch); + b->branch = ical_parse_unknown_comp(&s, level+1); + } + } + } else ukn++; + break; + + case 'E': + case 'e': if(!struncmp(s+1, "ND", 2)){ + *t = c; + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, token, strlen(token))){ + *text = s + strlen(token); + ical->value = (void *) gcl; + return ical; + } + } else ukn++; + break; + + default : ukn++; + break; + } /* end of switch(*s) */ + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(gcl == NULL){ + gcl = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl, 0, sizeof(GEN_ICLINE_S)); + gcl->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gencl; + for (gencl = gcl; gencl && gencl->next; gencl = gencl->next); + gencl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S)); + gencl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + ical->value = (void *) gcl; + *text = s; + return ical; +} + +ICAL_S * +ical_parse_unknown_comp(char **text, int level) +{ + ICAL_S *ical; + int i; + + ical_debug("ical_parse_unknown_comp", *text); + for(i = 0; ical_comp[i].comp + && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len) + || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++); + + if(ical_comp[i].parse){ + *text += ical_comp[i].len + 2; + ical = fs_get(sizeof(ICAL_S)); + memset((void *)ical, 0, sizeof(ICAL_S)); + ical->comp = cpystr(ical_comp[i].comp); + ical->value = (ical_comp[i].parse)(text); + } else + ical = ical_parse_generic_comp(text, level); + + return ical; +} + +ICLINE_S * +ical_parse_line(char **text, char *name) +{ + ICLINE_S *ic; + char *s = *text; + + ic = fs_get(sizeof(ICLINE_S)); + memset((void *)ic, 0, sizeof(ICLINE_S)); + + ic->token = cpystr(name); + s += strlen(name); + if(*s == ';'){ + s++; + ic->param = ical_get_parameter(&s); + } + if(*s == ':'){ + s++; + ic->value = ical_get_value(&s); + } + + *text = s; + return ic; +} + +/*** + *** PARSE PROPERTY FUNCTIONS + ***/ + +void +ical_parse_freq(Freq_value *fval, char *text) +{ + if(fval == NULL) return; + + *fval = FUnknown; + + if(text == NULL) return; + + if(!strucmp(text, "SECONDLY")) *fval = FSecondly; + else if(!strucmp(text, "MINUTELY")) *fval = FMinutely; + else if(!strucmp(text, "HOURLY")) *fval = FHourly; + else if(!strucmp(text, "DAILY")) *fval = FDaily; + else if(!strucmp(text, "WEEKLY")) *fval = FWeekly; + else if(!strucmp(text, "MONTHLY")) *fval = FMonthly; + else if(!strucmp(text, "YEARLY")) *fval = FYearly; +} + +void +ical_parse_weekday_list(BYWKDY_S **bywkdyp, char *wklist) +{ + BYWKDY_S *bywkdy, *w; + char *s, *t, c; + int done; + size_t len; + + if(bywkdyp == NULL) return; + *bywkdyp = bywkdy = NULL; + if(wklist == NULL) return; + + for(t = s = wklist; done == 0; s++){ + if(*s != ',' && *s != '\0') + continue; + c = *s; + if(c == ',') + *s = '\0'; + else /* c == '\0' */ + done++; + len = strlen(t); + if(len > 1){ + for(w = bywkdy; w && w->next; w = w->next); + w = fs_get(sizeof(BYWKDY_S)); + memset((void *)w, 0, sizeof(BYWKDY_S)); + if(!strucmp(t+len-2, "SU")) w->wd = Sunday; + else if(!strucmp(t+len-2, "MO")) w->wd = Monday; + else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday; + else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday; + else if(!strucmp(t+len-2, "TH")) w->wd = Thursday; + else if(!strucmp(t+len-2, "FR")) w->wd = Friday; + else if(!strucmp(t+len-2, "SA")) w->wd = Saturday; +// t[len - 2] = '\0'; + if(*t != '\0') + w->value = strtoul(t, &t, 10); + if(bywkdy == NULL) + bywkdy = w; + } + *s = c; + if(*s == ',') + t = s + 1; + } + + if(*bywkdyp) + ical_free_weekday_list((void **)&bywkdy); + else + *bywkdyp = bywkdy; +} + +void +ical_parse_number_list(BYWKDY_S **bynop, char *nolist) +{ + BYWKDY_S *byno, *n; + char *s, *t, c; + int done; + + if(bynop == NULL) return; + *bynop = byno = NULL; + if(nolist == NULL) return; + + for(t = s = nolist; done == 0; s++){ + if(*s != ',' && *s != '\0') + continue; + c = *s; + if(c == ',') + *s = '\0'; + else /* c == '\0' */ + done++; + + for(n = byno; n && n->next; n = n->next); + n = fs_get(sizeof(BYWKDY_S)); + memset((void *)n, 0, sizeof(BYWKDY_S)); + n->value = strtoul(t, &t, 10); + if(byno == NULL) + byno = n; + *s = c; + if(*s == ',') + t = s + 1; + } + + if(*bynop) + ical_free_weekday_list((void **)&byno); + else + *bynop = byno; +} + +void +ical_parse_rrule(RRULE_S **rrulep, char **text) +{ + RRULE_S *rrule; + ICLINE_S *icl; + char *s; + ICAL_PARAMETER_S *param, *p; + int i; + + if(rrulep == NULL + || text == NULL || *text == NULL || struncmp(*text, "RRULE", 5)) return; + rrule = fs_get(sizeof(RRULE_S)); + memset((void *) rrule, 0, sizeof(RRULE_S)); + + /* recurring rules are special. First, we parse the icline that contains it */ + icl = ical_parse_line(text, "RRULE"); + + /* now we copy the parameters that it contains */ + rrule->param = ical_parameter_cpy(icl->param); + + /* then we parse icl->value as if it was a parameter */ + s = icl->value; + param = ical_get_parameter(&s); + + /* now we check which values were given, and fill the prop array */ + rrule->prop = fs_get(RRUnknown*sizeof(void *)); + memset((void *) rrule->prop, 0, RRUnknown*sizeof(void *)); + + for(p = param; p != NULL; p = p->next){ + for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++); + if(rrule_prop[i].parse) + (rrule_prop[i].parse)(&rrule->prop[rrule_prop[i].pos], p->value); + } + + ical_free_param(¶m); + ical_free_cline((void **)&icl); + if(*rrulep) + ical_free_rrule((void **)&rrule); + else + *rrulep = rrule; +} + +/*** UTF-8 for ICAL ***/ + +int +ical_non_ascii_valid(unsigned char c) +{ + static char icu[6]; + static int utf8_len = 0; + static int utf8_type = 0; + int rv; + + if(utf8_len == 0) + utf8_type = (c >= 0xF0 && c <= 0xF4) + ? 4 : (c >= 0xE0 && c <= 0xEF) + ? 3 : (c >= 0xC2 && c <= 0xDF) + ? 2 : 0; + + if(utf8_type == 0) + return 0; + + icu[utf8_len++] = c; /* count it */ + + if(utf8_type == 2){ + if(utf8_len < 2) + rv = NEED_MORE; + else if(utf8_len == 2){ + rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF) + && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } else if (utf8_type == 3){ + if(utf8_len < 3) + rv = NEED_MORE; + else{ + if(icu[0] == 0xE0) + rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xE1 && icu[0] <= 0xEC) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] == 0xED) + rv = (icu[1] >= 0x80 && icu[1] <= 0x9F) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xE1 && icu[0] <= 0xEC) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } else if (utf8_type == 4){ + if(utf8_len < 4) + rv = NEED_MORE; + else{ + if(icu[0] == 0xF0) + rv = (icu[1] >= 0x90 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xF1 && icu[0] <= 0xF3) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] == 0xF4) + rv = (icu[1] >= 0x80 && icu[1] <= 0x8F) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } + return rv; +} + +int +ical_get_number_value(char *value, int beg_pos, int end_pos) +{ + char c, *err; + int rv; + + c = value[end_pos]; + value[end_pos] = '\0'; + rv = strtoul(value + beg_pos, &err, 10); + if(err != NULL && *err != '\0') return -1; + value[end_pos] = c; + return rv; +} + +void +ical_free_duration(ICAL_DURATION_S **ic_d) +{ + if(ic_d == NULL || *ic_d == NULL) + return; + + if((*ic_d)->next) ical_free_duration(&(*ic_d)->next); + fs_give((void **)ic_d); +} + +/* returns 0 if no error, -1 if some error */ +int +ical_parse_duration(char *value, ICAL_DURATION_S *ic_d) +{ + int i, j, rv = 0; + + if(value == NULL || ic_d == NULL) return -1; + + memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S)); + + if(value[i = 0] == '-'){ + i++; + ic_d->sign = 1; + } else if(value[i] == '+') + i++; + + if(value[i++] == 'P'){ + for(j = i; value[j] != '\0' && value[j] != ','; j++){ + if(!isdigit(value[j])) + switch(value[j]){ + case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'D': ic_d->days = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'H': ic_d->hours = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'T': i = j + 1; + break; + default: rv = -1; + break; + } + } + } + else + rv = -1; + + if(value[j++] == ','){ + ICAL_DURATION_S next; + rv = ical_parse_duration(value+j, &next); + ic_d->next = &next; + } + + return rv; +} + +/* return -1 if any error, 0 if no error */ +int +ical_parse_date(char *value, struct tm *t) +{ + int i; + struct tm Tm; + + if(t == NULL) return -1; + memset((void *)&Tm, 0, sizeof(struct tm)); + + if(value == NULL) return -1; + + /* a simple check for the format of the string */ + for(i = 0; isdigit(value[i]); i++); + if (i != 8 || value[i++] != 'T') return -1; + for(; isdigit(value[i]); i++); + if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0'))) + return -1; + + Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900; + Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1; + Tm.tm_mday = ical_get_number_value(value, 6, 8); + Tm.tm_hour = ical_get_number_value(value, 9, 11); + Tm.tm_min = ical_get_number_value(value, 11, 13); + Tm.tm_sec = ical_get_number_value(value, 13, 15); + *t = Tm; + + return (t->tm_mon > 11 || t->tm_mon < 0 + || t->tm_mday > 31 || t->tm_mday < 0 + || t->tm_hour > 23 || t->tm_hour < 0 + || t->tm_min > 59 || t->tm_min < 0 + || t->tm_sec > 60 || t->tm_sec < 0) + ? - 1 : 0; +} + +void +ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz) +{ + int date_form; /* date forms from section 3.3.4 in RFC 5545 */ + ICAL_PARAMETER_S *param; + char *tz = NULL; + struct tm ic_date; + time_t t; + + if(icl == NULL) return; + + for(param = icl->param; param != NULL; param = param->next) + if(!strucmp(param->name, "TZID")) + tz = param->value; + + if(tz != NULL) + date_form = 3; /* local time with timezone */ + else if(icl->value[strlen(icl->value)-1] == 'Z') + date_form = 2; /* utc time */ + else date_form = 1; /* local time */ + + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */ + + switch(date_form){ + case 1: break; + case 2: ical_adjust_date(&ic_date, vtz); + break; + case 3: break; + default: alpine_panic ("Impossible date_form"); + } +} + +ICAL_TZPROP_S * +ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz) +{ + struct tm standard, daylight; + + +} + + + +/* adjusts time to given time zone */ +void +ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz) +{ + char *tzname = NULL; + ICLINE_S *icl; + ICAL_TZPROP_S *cur_std_day; + + if(vtz == NULL) + return; + + if(vtz->prop){ + if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL) + tzname = cpystr(icl->value); + } + +//+++ cur_std_day = ical_std_or_daylight(date, vtz); +} + +void +ical_set_date_vevent(void *veventv, void *vtzv) +{ + VEVENT_S *vevent = (VEVENT_S *) veventv; + VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv; + + if(vevent){ + ical_set_date(vevent->prop[EvDtstamp], vtz); + ical_set_date(vevent->prop[EvDtstart], vtz); + ical_set_date(vevent->prop[EvDtend], vtz); + } +} + +#define LEAP_YEAR(X) ((((X) % 4 == 0) \ + && (((X) % 100 != 0) || ((X) % 400 == 0))) \ + || (X) == 1700) + +#define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1)) + +/* given a year, after day_zero, return the day + * of the week of the first of january of that year. On error, + * return a negative number. + * Assumption: day_zero is the date of january 1, of some year. + */ +int +ical_january_first(int year) +{ + int i, january_first; + + if(year < day_zero.tm_year) return -1; /* not supported */ + + year += 1900; + january_first = day_zero.tm_wday; + for(i = 1900 + day_zero.tm_year + 1; i <= year; i++) + january_first += CAL_OFFSET(i-1); + + return january_first % 7; +} + +/* given a month, week day, and year, return all days of the month + * that have that day as the week day. For example, return all + * sundays in november 2012. + */ +int * +ical_day_from_week(int month, Weekday day, int year) +{ + int *rv = NULL; + int fday, nday; + Weekday wday; + int i; + + fday = ical_first_of_month(month, year); + year += 1900; /* restore year */ + if(year == 1752 && month == 8){ + fday = 9; + } else { + for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++) + ; + rv = fs_get(6*sizeof(int)); + memset((void *)&rv, 0, 6*sizeof(int)); + for(i = 0; nday <= month_len[month]; i++){ + rv[i] = nday; + nday += 7; + } + if(LEAP_YEAR(year) && month == 1 && nday == 29) + rv[i] = nday; + } + + return rv; +} + + +/* given a month and a year, return the weekday of the first of the + * month in that year. + * return value: on error -1, otherwise the day of the week. + */ +int +ical_first_of_month(int month, int year) +{ + int i, d; + + if((d = ical_january_first(year)) < 0) + return -1; + + year += 1900; + for(i = 0; i < month; i++) + d += month_len[i]; + + if(LEAP_YEAR(year) && month >= 2) + d += 1; + + if(year == 1752 && month >= 9) + d -= 11; + + return d % 7; +} + +/* given a day, month and year, return the weekday of that day + * return value: on error -1, otherwise the day of the week. + */ +int ical_day_of_week(struct tm date) +{ + int d; + + if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0) + return -1; + + d += date.tm_mday - 1; + + if(date.tm_year + 1900 == 1752){ + if(date.tm_mday > 2 && date.tm_mday < 14) + return -1; + if(date.tm_mday >= 14) + d -= 11; + } + return d % 7; +} + + +/* given an initial date dtstart, and a recurring rule, rrule, + * adjust the date to the first date on the same year, when + * the rule actually starts + */ +struct tm +adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule) +{ + struct tm t; + + memset((void *) &t, 0, sizeof(struct tm)); + t.tm_year = dtstart->tm_year; /* same year */ + if(rrule->prop[RRFreq]){ + } + if(rrule->prop[RRCount]){ + } + else if(rrule->prop[RRInterval]){ + } + if(rrule->prop[RRBysecond]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_sec = seco->value; + else if (seco->value < t.tm_sec) + t.tm_sec = seco->value; + } + if (rrule->prop[RRByminute]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_min = seco->value; + else if (seco->value < t.tm_sec) + t.tm_min = seco->value; + } + if (rrule->prop[RRByhour]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_hour = seco->value; + else if (seco->value < t.tm_sec) + t.tm_hour = seco->value; + } + if (rrule->prop[RRByday]){ + } + if (rrule->prop[RRByweekno]){ + } + if (rrule->prop[RRBymonthday]){ + } + if (rrule->prop[RRByyearday]){ + } + if (rrule->prop[RRByweekno]){ + } + if (rrule->prop[RRBymonth]){ + BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo; + for (mo = m; mo != NULL; mo = mo->next) + if(mo == m) t.tm_mon = mo->value - 1; + else if (mo->value - 1 < t.tm_mon) + t.tm_mon = mo->value - 1; + } + if (rrule->prop[RRBysetpos]){ + } + if (rrule->prop[RRWkst]){ + } +} + +void +ical_initialize(void) +{ + ical_buf_len = 1024; + ical_buf = fs_get(ical_buf_len+1); + + memset((void *) &day_zero, 0, sizeof(struct tm)); + day_zero.tm_year = 1601 - 1900; + day_zero.tm_mday = 1; + day_zero.tm_wday = 4; +} + +/* we create a summary of the event, and pass that back as + an ical parameter + */ +VEVENT_SUMMARY_S * +ical_vevent_summary(VCALENDAR_S *vcal) +{ + VEVENT_SUMMARY_S *rv; + ICLINE_S *method; + VEVENT_S *vevent; + GEN_ICLINE_S *gicl; + ICLINE_S *icl; + + if(vcal == NULL) return NULL; + + method = vcal->method; + vevent = (VEVENT_S *) vcal->comp[VEvent]; + + if(vevent == NULL || vevent->prop == NULL) + return NULL; + + rv = fs_get(sizeof(VEVENT_SUMMARY_S)); + memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S)); + + if(method != NULL && !strucmp(method->value, "CANCEL")) + rv->cancel++; + + if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL) + rv->priority = atoi(icl->value); + + if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL) + rv->summary = cpystr(icl->value ? icl->value : _("No Summary")); + + if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL) + rv->class = cpystr(icl->value ? icl->value : _("PUBLIC")); + else + rv->class = cpystr(_("PUBLIC")); + + if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){ + char *cn, *sender, *address; + ICAL_PARAMETER_S *param; + + cn = sender = address = NULL; + for(param = icl->param; param != NULL; param = param->next) + if(!strucmp(param->name, "CN")) + cn = param->value; + else if(!strucmp(param->name, "SENT-BY")) + sender = param->value; + + if(sender != NULL){ + if(!struncmp(sender, "MAILTO:", 7)) + sender += 7; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender); + rv->sender = cpystr(tmp_20k_buf); + } + + if((address = icl->value) != NULL){ + if(!struncmp(address, "MAILTO:", 7)) + address += 7; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>", + cn ? cn : "", cn ? " " : "", + address ? address : _("Unknown address")); + rv->organizer = cpystr(tmp_20k_buf); + } + } /* end of if(organizer) */ + + if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL) + rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed")); + + if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){ + struct tm ic_date; + char tmp[200]; + memset((void *)&ic_date, 0, sizeof(struct tm)); + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + rv->evstart = cpystr(icl->value ? tmp : _("Unknown Start Date")); + } /* end of if dtstart */ + + if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){ + int i, done = 0; + ICAL_DURATION_S ic_d, icd2; + if(ical_parse_duration(icl->value, &ic_d) == 0){ + char tmp[MAILTMPLEN+1]; + + for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++); + rv->duration = fs_get((i+1)*sizeof(char *)); + i = 0; + + do { + tmp[0] = '\0'; + + if(ic_d.weeks > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks")); + if(ic_d.days > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days")); + if(ic_d.hours > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours")); + if(ic_d.minutes > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes")); + if(ic_d.seconds > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds")); + + tmp[MAILTMPLEN] = '\0'; + rv->duration[i++] = cpystr(tmp); + + if(ic_d.next != NULL) + ic_d = *ic_d.next; + else + done++; + } while (done == 0); + rv->duration[i] = NULL; + } + } /* end of DURATION */ + else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){ + struct tm ic_date; + char tmp[200]; + + memset((void *)&ic_date, 0, sizeof(struct tm)); + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + rv->evend = cpystr(icl->value ? tmp : _("Unknown End Date")); + } /* end of if dtend */ + + if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){ + int nattendees, i; + + for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++); + rv->attendee = fs_get((nattendees+1)*sizeof(char *)); + + gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]; + for(i = 0; gicl != NULL; gicl = gicl->next, i++){ + char *role, *partstat, *rsvp; + char *cn, *mailto; + ICAL_PARAMETER_S *param; + + icl = gicl->cline; + role = partstat = rsvp = cn = mailto = NULL; + for(param = icl->param; param != NULL; param = param->next){ + if(!strucmp(param->name, "ROLE")){ + if(!strucmp(param->value, "REQ-PARTICIPANT")) + role = _("[Required]"); + else if(!strucmp(param->value, "OPT-PARTICIPANT")) + role = _("[Optional]"); + else if(!strucmp(param->value, "NON-PARTICIPANT")) + role = _("[Informed]"); + else if(!strucmp(param->value, "CHAIR")) + role = _("[ Chair ]"); + else + role = param->value; + } + else if(!strucmp(param->name, "PARTSTAT")){ + if(!strucmp(param->value, "NEEDS-ACTION")) + partstat = _("[Need-Reply]"); + else if(!strucmp(param->value, "ACCEPTED")) + partstat = _("[ Accepted ]"); + else if(!strucmp(param->value, "DECLINED")) + partstat = _("[ Declined ]"); + else if(!strucmp(param->value, "TENTATIVE")) + partstat = _("[ Tentative]"); + else if(!strucmp(param->value, "DELEGATED")) + partstat = _("[ Delegated]"); + else + partstat = param->value; + } + else if(!strucmp(param->name, "RSVP")) + rsvp = param->value; + else if(!strucmp(param->name, "CN")) + cn = param->value; + } + if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:"))) + mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */ + if(!strucmp(cn, mailto)) + cn = ""; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>", + role && *role ? role : "", + role && *role ? " " : "", + partstat ? partstat : _("[Unknown Reply]"), + partstat ? " " : "", + cn && *cn ? cn : "", + cn && *cn ? " " : "", + mailto ? mailto : _("Unknown address")); + rv->attendee[i] = cpystr(tmp_20k_buf); + } + rv->attendee[i] = NULL; + } /* end of ATTENDEES */ + + if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){ + char *s, *t, *u, *v; + int i, escaped; + + if(icl->value == NULL){ + free_vevent_summary(&rv); + return NULL; + } + + v = cpystr(icl->value); /* process a copy of icl->value */ + + for(i = 1, escaped = 0, s = v; s && *s; s++){ + if(*s == '\\'){ escaped = 1; continue; } + if(escaped){ + if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){ + free_vevent_summary(&rv); + fs_give((void **)&v); + return NULL; + } + escaped = 0; + continue; + } + if(*s == ',') i++; /* a non-scaped comma is a new value for text */ + } + + rv->description = fs_get((i+1)*sizeof(char *)); + i = 0; + for (s = t = u = v, escaped = 0; *t != '\0'; t++){ + if(*t == '\\'){ escaped = 1; continue; } + if(escaped){ + switch(*t){ + case '\\': + case ',': + case ';': + *u++ = *t; + break; + case 'n': + case 'N': + *u++ = '\n'; + break; + default: free_vevent_summary(&rv); + fs_give((void **)&v); + return NULL; + } + escaped = 0; + continue; + } + if(*t == ','){ + *u = '\0'; + rv->description[i++] = cpystr(s); + s = u = t+1; + } else + *u++ = *t; + } + *u = '\0'; + rv->description[i++] = cpystr(s); + rv->description[i] = NULL; + fs_give((void **)&v); + } /* end of if(description) */ + + return rv; +} + +void +free_vevent_summary(VEVENT_SUMMARY_S **vesy) +{ + int i; + if(vesy == NULL || *vesy == NULL) return; + + if((*vesy)->class) fs_give((void **)&(*vesy)->class); + if((*vesy)->summary) fs_give((void **)&(*vesy)->summary); + if((*vesy)->sender) fs_give((void **)&(*vesy)->sender); + if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer); + if((*vesy)->location) fs_give((void **)&(*vesy)->location); + if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart); + if((*vesy)->evend) fs_give((void **)&(*vesy)->evend); + if((*vesy)->duration){ + for(i = 0; (*vesy)->duration[i] != NULL; i++) + fs_give((void **) &(*vesy)->duration[i]); + fs_give((void **) (*vesy)->duration); + } + if((*vesy)->attendee){ + for(i = 0; (*vesy)->attendee[i] != NULL; i++) + fs_give((void **) &(*vesy)->attendee[i]); + fs_give((void **) (*vesy)->attendee); + } + if((*vesy)->description){ + for(i = 0; (*vesy)->description[i] != NULL; i++) + fs_give((void **) &(*vesy)->description[i]); + fs_give((void **) (*vesy)->description); + } + fs_give((void **) vesy); +} + +#ifdef STANDALONE +void ical_print_line(ICLINE_S icl) +{ + ICAL_PARAMETER_S *p; + printf("token=%s, ", icl.token); + for(p = icl.param; p != NULL; p = p->next){ + if(p->name) printf("param->name=%s, ", p->name); + if(p->value) printf("param->value=%s, ", p->value); + } + printf("value=%s", icl.value); + printf("%c", '\n'); +} + +#define CRLFCOUNT 100 + +char *lf2crlf(char *text) +{ + char *rv, *s; + int i, len; + + if(text == NULL || *text == '\0') + return NULL; + + len = strlen(text); + rv = fs_get(len+1); + + for(i = 0, s = text; *s;){ + if(i == len - 1){ + len += CRLFCOUNT; + fs_resize((void *)&rv, len); + } + if(*s == '\n') + rv[i++] = '\r'; + rv[i++] = *s++; + } + rv[i] = '\0'; + fs_give((void **)&text); + text = rv; + + return rv; +} + +int main (int argc, char *argv[]) +{ + char *text; + VCALENDAR_S *vcal; + + if(argc != 2){ + fprintf(stderr, "Only give one argument: File name to read\n"); + _exit(1); + } + + if(readfile(argv[1], &text, NULL) < 0){ + fprintf(stderr, "Can not read %s succesfully\n", argv[1]); + _exit(2); + } + + text = lf2crlf(text); + vcal = ical_parse_text(text); + + ical_free_vcalendar((void **)&vcal); + return 0; +} + + +#endif /* STANDALONE */ -- cgit v1.2.3-54-g00ecf