summaryrefslogtreecommitdiff
path: root/pith/ical.c
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2017-12-10 09:24:34 -0700
committerEduardo Chappa <chappa@washington.edu>2017-12-10 09:24:34 -0700
commit480bf4063f7d31b7d2c9ec8edd9fc4280ffb39ee (patch)
tree542f1b86a451bc33e6320dce3ccfed4ea57580a3 /pith/ical.c
parente0ebb8756c000f325a14469eec065654882bd0ac (diff)
downloadalpine-480bf4063f7d31b7d2c9ec8edd9fc4280ffb39ee.tar.xz
Initial commit for branch ical
Diffstat (limited to 'pith/ical.c')
-rw-r--r--pith/ical.c2245
1 files changed, 2245 insertions, 0 deletions
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(&param);
+ *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(&param);
+ 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 */