diff options
Diffstat (limited to 'pith')
-rw-r--r-- | pith/Makefile | 35 | ||||
-rw-r--r-- | pith/Makefile.am | 4 | ||||
-rw-r--r-- | pith/Makefile.in | 35 | ||||
-rw-r--r-- | pith/body.c | 70 | ||||
-rw-r--r-- | pith/body.h | 23 | ||||
-rw-r--r-- | pith/ical.c | 2365 | ||||
-rw-r--r-- | pith/ical.h | 18 | ||||
-rw-r--r-- | pith/icaltype.h | 477 | ||||
-rw-r--r-- | pith/mailpart.h | 6 | ||||
-rw-r--r-- | pith/mailview.c | 350 | ||||
-rw-r--r-- | pith/mailview.h | 4 | ||||
-rw-r--r-- | pith/mimedesc.c | 6 | ||||
-rw-r--r-- | pith/pine.hlp | 2 | ||||
-rw-r--r-- | pith/smime.c | 63 | ||||
-rw-r--r-- | pith/smime.h | 12 |
15 files changed, 3358 insertions, 112 deletions
diff --git a/pith/Makefile b/pith/Makefile index 166f5117..d9f3594a 100644 --- a/pith/Makefile +++ b/pith/Makefile @@ -121,21 +121,22 @@ libpith_a_AR = $(AR) $(ARFLAGS) libpith_a_LIBADD = am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \ addrbook.$(OBJEXT) addrstring.$(OBJEXT) adrbklib.$(OBJEXT) \ - bldaddr.$(OBJEXT) charset.$(OBJEXT) color.$(OBJEXT) \ - conf.$(OBJEXT) context.$(OBJEXT) copyaddr.$(OBJEXT) \ - detoken.$(OBJEXT) detach.$(OBJEXT) editorial.$(OBJEXT) \ - escapes.$(OBJEXT) filter.$(OBJEXT) flag.$(OBJEXT) \ - folder.$(OBJEXT) handle.$(OBJEXT) help.$(OBJEXT) \ - helpindx.$(OBJEXT) hist.$(OBJEXT) icache.$(OBJEXT) \ - imap.$(OBJEXT) init.$(OBJEXT) keyword.$(OBJEXT) ldap.$(OBJEXT) \ - list.$(OBJEXT) mailcap.$(OBJEXT) mailcmd.$(OBJEXT) \ - mailindx.$(OBJEXT) maillist.$(OBJEXT) mailview.$(OBJEXT) \ - margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ - msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ - pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ - remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \ - save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ - send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ + bldaddr.$(OBJEXT) body.$(OBJEXT) charset.$(OBJEXT) \ + color.$(OBJEXT) conf.$(OBJEXT) context.$(OBJEXT) \ + copyaddr.$(OBJEXT) detoken.$(OBJEXT) detach.$(OBJEXT) \ + editorial.$(OBJEXT) escapes.$(OBJEXT) filter.$(OBJEXT) \ + flag.$(OBJEXT) folder.$(OBJEXT) handle.$(OBJEXT) \ + help.$(OBJEXT) helpindx.$(OBJEXT) hist.$(OBJEXT) \ + icache.$(OBJEXT) ical.$(OBJEXT) imap.$(OBJEXT) init.$(OBJEXT) \ + keyword.$(OBJEXT) ldap.$(OBJEXT) list.$(OBJEXT) \ + mailcap.$(OBJEXT) mailcmd.$(OBJEXT) mailindx.$(OBJEXT) \ + maillist.$(OBJEXT) mailview.$(OBJEXT) margin.$(OBJEXT) \ + mimedesc.$(OBJEXT) mimetype.$(OBJEXT) msgno.$(OBJEXT) \ + newmail.$(OBJEXT) news.$(OBJEXT) pattern.$(OBJEXT) \ + pipe.$(OBJEXT) readfile.$(OBJEXT) remote.$(OBJEXT) \ + reply.$(OBJEXT) rfc2231.$(OBJEXT) save.$(OBJEXT) \ + search.$(OBJEXT) sequence.$(OBJEXT) send.$(OBJEXT) \ + sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ strlst.$(OBJEXT) takeaddr.$(OBJEXT) tempfile.$(OBJEXT) \ text.$(OBJEXT) thread.$(OBJEXT) adjtime.$(OBJEXT) \ @@ -431,9 +432,9 @@ top_srcdir = .. SUBDIRS = osdep charconv noinst_LIBRARIES = libpith.a BUILT_SOURCES = helptext.h helptext.c -libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \ +libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c body.c charset.c \ color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \ - filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ + filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ diff --git a/pith/Makefile.am b/pith/Makefile.am index 57d8aa65..76a1feeb 100644 --- a/pith/Makefile.am +++ b/pith/Makefile.am @@ -21,9 +21,9 @@ BUILT_SOURCES = helptext.h helptext.c noinst_PROGRAMS = help_h_gen help_c_gen -libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \ +libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c body.c charset.c \ color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \ - filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ + filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ diff --git a/pith/Makefile.in b/pith/Makefile.in index 990661c2..e9589b70 100644 --- a/pith/Makefile.in +++ b/pith/Makefile.in @@ -121,21 +121,22 @@ libpith_a_AR = $(AR) $(ARFLAGS) libpith_a_LIBADD = am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \ addrbook.$(OBJEXT) addrstring.$(OBJEXT) adrbklib.$(OBJEXT) \ - bldaddr.$(OBJEXT) charset.$(OBJEXT) color.$(OBJEXT) \ - conf.$(OBJEXT) context.$(OBJEXT) copyaddr.$(OBJEXT) \ - detoken.$(OBJEXT) detach.$(OBJEXT) editorial.$(OBJEXT) \ - escapes.$(OBJEXT) filter.$(OBJEXT) flag.$(OBJEXT) \ - folder.$(OBJEXT) handle.$(OBJEXT) help.$(OBJEXT) \ - helpindx.$(OBJEXT) hist.$(OBJEXT) icache.$(OBJEXT) \ - imap.$(OBJEXT) init.$(OBJEXT) keyword.$(OBJEXT) ldap.$(OBJEXT) \ - list.$(OBJEXT) mailcap.$(OBJEXT) mailcmd.$(OBJEXT) \ - mailindx.$(OBJEXT) maillist.$(OBJEXT) mailview.$(OBJEXT) \ - margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ - msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ - pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ - remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \ - save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ - send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ + bldaddr.$(OBJEXT) body.$(OBJEXT) charset.$(OBJEXT) \ + color.$(OBJEXT) conf.$(OBJEXT) context.$(OBJEXT) \ + copyaddr.$(OBJEXT) detoken.$(OBJEXT) detach.$(OBJEXT) \ + editorial.$(OBJEXT) escapes.$(OBJEXT) filter.$(OBJEXT) \ + flag.$(OBJEXT) folder.$(OBJEXT) handle.$(OBJEXT) \ + help.$(OBJEXT) helpindx.$(OBJEXT) hist.$(OBJEXT) \ + icache.$(OBJEXT) ical.$(OBJEXT) imap.$(OBJEXT) init.$(OBJEXT) \ + keyword.$(OBJEXT) ldap.$(OBJEXT) list.$(OBJEXT) \ + mailcap.$(OBJEXT) mailcmd.$(OBJEXT) mailindx.$(OBJEXT) \ + maillist.$(OBJEXT) mailview.$(OBJEXT) margin.$(OBJEXT) \ + mimedesc.$(OBJEXT) mimetype.$(OBJEXT) msgno.$(OBJEXT) \ + newmail.$(OBJEXT) news.$(OBJEXT) pattern.$(OBJEXT) \ + pipe.$(OBJEXT) readfile.$(OBJEXT) remote.$(OBJEXT) \ + reply.$(OBJEXT) rfc2231.$(OBJEXT) save.$(OBJEXT) \ + search.$(OBJEXT) sequence.$(OBJEXT) send.$(OBJEXT) \ + sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ strlst.$(OBJEXT) takeaddr.$(OBJEXT) tempfile.$(OBJEXT) \ text.$(OBJEXT) thread.$(OBJEXT) adjtime.$(OBJEXT) \ @@ -431,9 +432,9 @@ top_srcdir = @top_srcdir@ SUBDIRS = osdep charconv noinst_LIBRARIES = libpith.a BUILT_SOURCES = helptext.h helptext.c -libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \ +libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c body.c charset.c \ color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \ - filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ + filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ diff --git a/pith/body.c b/pith/body.c new file mode 100644 index 00000000..7d935810 --- /dev/null +++ b/pith/body.c @@ -0,0 +1,70 @@ +/* + * ======================================================================== + * Copyright 2013-2017 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include "../pith/headers.h" +#include "../pith/body.h" +#include "../pith/smime.h" +#include "../pith/ical.h" + +void * +create_body_sparep(SpareType stype, void *s) +{ + BODY_SPARE_S *rv; + + rv = fs_get(sizeof(BODY_SPARE_S)); + rv->sptype = stype; + rv->data = s; + return (void *) rv; +} + +SpareType +get_body_sparep_type(void *s) +{ + return ((BODY_SPARE_S *)s)->sptype; +} + +void * +get_body_sparep_data(void *s) +{ + return ((BODY_SPARE_S *)s)->data; +} + +void +free_body_sparep(void **sparep) +{ + char *s; + SIZEDTEXT *st; + VCALENDAR_S *vcal; + + if(sparep && *sparep){ + switch(get_body_sparep_type(*sparep)){ +#ifdef SMIME + case P7Type: PKCS7_free((PKCS7 *) get_body_sparep_data(*sparep)); + break; +#endif /* SMIME */ + case CharType: s = (char *)get_body_sparep_data(*sparep); + fs_give((void **) &s); + break; + case SizedText: st = (SIZEDTEXT *)get_body_sparep_data(*sparep); + fs_give((void **) &st->data); + fs_give((void **) &st); + break; + case iCalType: vcal = (VCALENDAR_S *)get_body_sparep_data(*sparep); + ical_free_vcalendar((void **) &vcal); + break; + default : break; + } + ((BODY_SPARE_S *)(*sparep))->data = NULL; + fs_give(sparep); + } +} diff --git a/pith/body.h b/pith/body.h new file mode 100644 index 00000000..c8955a92 --- /dev/null +++ b/pith/body.h @@ -0,0 +1,23 @@ +#ifndef PITH_BODY_SPARE_INCLUDED +#define PITH_BODY_SPARE_INCLUDED +typedef enum + {CharType, + SizedText, +#ifdef SMIME + P7Type, +#endif + iCalType +} SpareType; + +typedef struct body_sparep_t { + SpareType sptype; + void *data; +} BODY_SPARE_S; + +void *create_body_sparep(SpareType, void *); +void free_body_sparep(void **); +void *get_body_sparep_data(void *); +SpareType get_body_sparep_type(void *); + + +#endif /* PITH_BODY_SPARE_INCLUDED */ diff --git a/pith/ical.c b/pith/ical.c new file mode 100644 index 00000000..bfad8078 --- /dev/null +++ b/pith/ical.c @@ -0,0 +1,2365 @@ +#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 **); +char *ical_decode(char *, unsigned short); + +/* 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. */ + +char * +ical_decode(char *text, unsigned short encoding) +{ + char *t; + unsigned long callen; + if(encoding == ENCQUOTEDPRINTABLE){ + t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen); + if(t != NULL){ + strncpy(text, t, strlen(t)); + fs_give((void **) &t); + } + } + return text; +} + + +/* Return code: + 0 - if no errors + -1 - if an error occured + Args: a pointer to the text. If there is an error, the text is not modified. + */ +int +ical_remove_escapes(char **textp) +{ + char *text, *s, *t; + int rv = 0; + int escaped; + + if(textp == NULL) return 0; + + t = cpystr(*textp); /* work on a copy of the text */ + /* the variable text below points to the beginning of the filtered text */ + for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){ + if(*s == '\\' && escaped == 0){ + escaped = 1; + continue; + } + if(escaped){ + switch(*s){ + case '\\': + case ',': + case ';': + *t++ = *s; + break; + + case 'n': + case 'N': + *t++ = '\n'; + break; + default: rv = -1; + break; + } + escaped = 0; + } + else *t++ = *s; + } + *t = '\0'; /* tie off filtered text */ + t = text; /* reset t to the beginning */ + if(rv == -1) + fs_give((void **) &t); + else{ + strncpy(*textp, t, strlen(t)); /* overwrite given text with filtered text */ + (*textp)[strlen(t)] = '\0'; + } + return rv; +} + +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; + } + else{ + t = NULL; + s = strchr(*line, '\r'); + if(s != NULL) + *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 unsigned 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 value has the DATE-TIME form + 1 if value has the DATE form only + */ +int +ical_parse_date(char *value, struct tm *t) +{ + int i, rv; + struct tm Tm; + + rv = -1; + if(t == NULL) return rv; + memset((void *)&Tm, 0, sizeof(struct tm)); + + if(value == NULL) return rv; + + rv = 0; /* assume DATE-TIME format */ + /* a simple check for the format of the string */ + for(i = 0; isdigit(value[i]); i++); + if (i == 8 && value[i] == '\0') + rv = 1; + else + if (i != 8 || value[i++] != 'T') return -1; + if(rv == 0) { + 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); + if(rv == 0){ + 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 : rv; +} + +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; + char *k; + + 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]; + int icd; /* ical date return value */ + + memset((void *)&ic_date, 0, sizeof(struct tm)); + icd = ical_parse_date(icl->value, &ic_date); + if(icd >= 0){ + ic_date.tm_wday = ical_day_of_week(ic_date); + switch(icd){ + case 0: /* DATE-TIME */ + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + break; + case 1: /* DATE */ + our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date); + break; + default: alpine_panic("Unhandled ical date format"); + break; + } + } + else{ + strncpy(tmp, _("Error while parsing event date"), sizeof(tmp)); + tmp[sizeof(tmp) - 1] = '\0'; + } + 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]; + int icd; + + memset((void *)&ic_date, 0, sizeof(struct tm)); + icd = ical_parse_date(icl->value, &ic_date); + if(icd >= 0){ + ic_date.tm_wday = ical_day_of_week(ic_date); + switch(icd){ + case 0: /* DATE-TIME */ + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + break; + case 1: /* DATE */ + our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date); + break; + default: alpine_panic("Unhandled ical date format"); + break; + } + } + else{ + strncpy(tmp, _("Error while parsing event date"), sizeof(tmp)); + tmp[sizeof(tmp) - 1] = '\0'; + } + 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]"), + " ", + 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 == 0){ 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 == 0){ 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(ical_decode(s, vcal->encoding)); + s = u = t+1; + } else + *u++ = *t; + } + *u = '\0'; + rv->description[i++] = cpystr(ical_decode(s, vcal->encoding)); + 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 */ diff --git a/pith/ical.h b/pith/ical.h new file mode 100644 index 00000000..7ff5865a --- /dev/null +++ b/pith/ical.h @@ -0,0 +1,18 @@ +#ifndef PITH_ICAL_INCLUDED +#define PITH_ICAL_INCLUDED + +#include "../pith/icaltype.h" + +void ical_free_vcalendar(void **); +void ical_free_duration(ICAL_DURATION_S **ic_d); +int ical_first_of_month(int, int); /* args: month, year - in that order */ +int ical_day_of_week(struct tm); /* args: a time structure */ +int ical_parse_date(char *, struct tm *); +int ical_parse_duration(char *, ICAL_DURATION_S *); +int ical_remove_escapes(char **); +VEVENT_SUMMARY_S *ical_vevent_summary(VCALENDAR_S *); +void free_vevent_summary(VEVENT_SUMMARY_S **); + +VCALENDAR_S *ical_parse_text(char *); /* this is the entry point */ + +#endif /* ifndef PITH_ICAL_INCLUDED */ diff --git a/pith/icaltype.h b/pith/icaltype.h new file mode 100644 index 00000000..8060a412 --- /dev/null +++ b/pith/icaltype.h @@ -0,0 +1,477 @@ +#ifndef ICALTYPE_INCLUDED +#define ICALTYPE_INCLUDED + +#ifdef STANDALONE +#define ADDRESS struct mail_address + +ADDRESS { + char *personal; /* personal name phrase */ + char *adl; /* at-domain-list source route */ + char *mailbox; /* mailbox name */ + char *host; /* domain name of mailbox's host */ + char *error; /* error in address from SMTP module */ + struct { + char *type; /* address type (default "rfc822") */ + char *addr; /* address as xtext */ + } orcpt; + ADDRESS *next; /* pointer to next address in list */ +}; +#endif /* STANDALONE */ + +/* this is the PARAMETER struct from mail.h, but with possibility of + * extension */ +typedef struct ical_param_s { + char *name; + char *value; + struct ical_param_s *next; +} ICAL_PARAMETER_S; + +#define ICAL_S struct ical_s + +ICAL_S { + char *comp; /* component name */ + void *value; + ICAL_S *branch; /* component in same level */ + ICAL_S *next; /* component at next level */ +}; + +typedef struct icline_s { + char *token; + ICAL_PARAMETER_S *param; + char *value; /* this could need further processing, so consider this a raw value */ +} ICLINE_S; + +typedef struct gen_icline_s { + ICLINE_S *cline; + struct gen_icline_s *next; +} GEN_ICLINE_S; + +typedef enum {FSecondly, FMinutely, FHourly, FDaily, FWeekly, + FMonthly, FYearly, FUnknown} Freq_value; + +/* believe it or not, I did not need to copy these values from + * RFC 5545! */ +typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday, + Friday, Saturday} Weekday; + +typedef struct ical_weekday_s { + int value; /* number value being parsed */ + Weekday wd; + struct ical_weekday_s *next; +} BYWKDY_S; + +typedef struct ical_rrule_s { +#if 0 + Freq_value freq; /* required, at most one */ + ICLINE_S *until; /* optional, must not occur with count */ + ICLINE_S *count; /* optional, must not occur with until */ + unsigned long interval; /* optional, at most one */ + BYWKDY_S *bysecond; /* optional, at most one */ + ICLINE_S *byminute; /* optional, at most one */ + ICLINE_S *byhour; /* optional, at most one */ + BYWKDY_S *byday; /* optional, at most one */ + ICLINE_S *bymonthday; /* optional, at most one */ + ICLINE_S *byyearday; /* optional, at most one */ + ICLINE_S *byweekno; /* optional, at most one */ + BYWKDY_S *bymonth; /* optional, at most one, only use value */ + ICLINE_S *bysetpos; /* optional, at most one */ + ICLINE_S *wkst; /* optional, at most one */ +#endif /* if 0 */ + void **prop; /* the list of properties of a recurring rule */ + ICAL_PARAMETER_S *param; +} RRULE_S; + +typedef struct valarm_s { +#if 0 + ICLINE_S *action; /* required, at most one */ + ICLINE_S *trigger; /* required, at most one */ + ICLINE_S *duration; /* audio,display,email.optional, at most one. Must occur with repeat */ + ICLINE_S *repeat; /* audio,display,email.optional, at most one. Must occur with duration */ + ICLINE_S *description; /* display,email.required, at most one */ + ICLINE_S *summary; /* email.required, at most one */ + GEN_ICLINE_S *attach; /* audio.optional, at most one;email.optional, may occur more than once */ + GEN_ICLINE_S *attendee; /* email.required, may occur more than once */ +#endif /* if 0 */ + void **prop; /* an array of all properties of an alarm */ + GEN_ICLINE_S *uk_prop; /* optional, may occur more than once */ + struct valarm_s *next; +} VALARM_S; + +typedef struct vevent_s { +#if 0 + ICLINE_S *dtstamp; /* required, at most one */ + ICLINE_S *uid; /* required, at most one */ + ICLINE_S *dtstart; /* required if METHOD not specified, at most one */ + ICLINE_S *class; /* optional, at most one */ + ICLINE_S *created; /* optional, at most one */ + ICLINE_S *description;/* optional, at most one */ + ICLINE_S *geo; /* optional, at most one */ + ICLINE_S *lastmod; /* optional, at most one */ + ICLINE_S *location; /* optional, at most one */ + ICLINE_S *organizer; /* optional, at most one */ + ICLINE_S *priority; /* optional, at most one */ + ICLINE_S *seq; /* optional, at most one */ + ICLINE_S *status; /* optional, at most one */ + ICLINE_S *summary; /* optional, at most one */ + ICLINE_S *transp; /* optional, at most one */ + ICLINE_S *url; /* optional, at most one */ + ICLINE_S *recurid; /* optional, at most one */ + RRULE_S *rrule; /* optional, at most one */ + ICLINE_S *dtend; /* optional, at most one, exclude duration */ + ICLINE_S *duration; /* optional, at most one, exclude dtend */ + GEN_ICLINE_S *attach; /* optional, may occur more than once */ + GEN_ICLINE_S *attendee; /* optional, may occur more than once */ + GEN_ICLINE_S *categories; /* optional, may occur more than once */ + GEN_ICLINE_S *comment; /* optional, may occur more than once */ + GEN_ICLINE_S *contact; /* optional, may occur more than once */ + GEN_ICLINE_S *exdate; /* optional, may occur more than once */ + GEN_ICLINE_S *rstatus; /* optional, may occur more than once */ + GEN_ICLINE_S *related; /* optional, may occur more than once */ + GEN_ICLINE_S *resources; /* optional, may occur more than once */ + GEN_ICLINE_S *rdate; /* optional, may occur more than once */ +#endif /* if 0 */ + void **prop; /* the properties of an event component */ + GEN_ICLINE_S *uk_prop; /* unknown values */ + VALARM_S *valarm; /* possible valarm */ +} VEVENT_S; + +typedef struct vtodo_s { + ICLINE_S *dtstamp; /* required, at most one */ + ICLINE_S *uid; /* required, at most one */ + ICLINE_S *class; /* optional, at most one */ + ICLINE_S *completed; /* optional, at most one */ + ICLINE_S *created; /* optional, at most one */ + ICLINE_S *description;/* optional, at most one */ + ICLINE_S *dtstart; /* optional, at most one */ + ICLINE_S *geo; /* optional, at most one */ + ICLINE_S *lastmod; /* optional, at most one */ + ICLINE_S *location; /* optional, at most one */ + ICLINE_S *organizer; /* optional, at most one */ + ICLINE_S *percent; /* optional, at most one */ + ICLINE_S *priority; /* optional, at most one */ + ICLINE_S *recurid; /* optional, at most one */ + ICLINE_S *seq; /* optional, at most one */ + ICLINE_S *status; /* optional, at most one */ + ICLINE_S *summary; /* optional, at most one */ + ICLINE_S *url; /* optional, at most one */ + RRULE_S *rrule; /* optional, at most one */ + ICLINE_S *due; /* optional, at most one, but exclude duration */ + ICLINE_S *duration; /* optional, at most one, but exclude due, and add dtstart */ + ICLINE_S *attach; /* optional, can appear more than once */ + ICLINE_S *attendee; /* optional, can appear more than once */ + ICLINE_S *categories; /* optional, can appear more than once */ + ICLINE_S *comment; /* optional, can appear more than once */ + ICLINE_S *contact; /* optional, can appear more than once */ + ICLINE_S *exdate; /* optional, can appear more than once */ + ICLINE_S *rstatus; /* optional, can appear more than once */ + ICLINE_S *related; /* optional, can appear more than once */ + ICLINE_S *resources; /* optional, can appear more than once */ + ICLINE_S *rdate; /* optional, can appear more than once */ + ICAL_PARAMETER_S *unknown; /* unknown values */ + VALARM_S *valarm; /* optional valarm */ +} VTODO_S; + +typedef struct journal_s { + ICLINE_S *dtstamp; /* required, at most one */ + ICLINE_S *uid; /* required, at most one */ + ICLINE_S *class; /* optional, at most one */ + ICLINE_S *created; /* optional, at most one */ + ICLINE_S *dtstart; /* optional, at most one */ + ICLINE_S *lastmod; /* optional, at most one */ + ICLINE_S *organizer; /* optional, at most one */ + ICLINE_S *recurid; /* optional, at most one */ + ICLINE_S *seq; /* optional, at most one */ + ICLINE_S *status; /* optional, at most one */ + ICLINE_S *summary; /* optional, at most one */ + ICLINE_S *url; /* optional, at most one */ + RRULE_S *rrule; /* optional, at most one */ + ICLINE_S *attach; /* optional, may occur more than once */ + ICLINE_S *attendee; /* optional, may occur more than once */ + ICLINE_S *categories; /* optional, may occur more than once */ + ICLINE_S *comment; /* optional, may occur more than once */ + ICLINE_S *contact; /* optional, may occur more than once */ + ICLINE_S *description;/* optional, may occur more than once */ + ICLINE_S *exdate; /* optional, may occur more than once */ + ICLINE_S *related; /* optional, may occur more than once */ + ICLINE_S *rdate; /* optional, may occur more than once */ + ICLINE_S *rstatus; /* optional, may occur more than once */ + ICAL_PARAMETER_S *unknown; /* unknown values */ +} VJOURNAL_S; + +typedef struct freebusy_s { + ICLINE_S *dtstamp; /* required, at most one */ + ICLINE_S *uid; /* required, at most one */ + ICLINE_S *contact; /* optional, at most one */ + ICLINE_S *dtstart; /* optional, at most one */ + ICLINE_S *dtend; /* optional, at most one */ + ICLINE_S *organizer; /* optional, at most one */ + ICLINE_S *url; /* optional, at most one */ + ICLINE_S *attendee; /* optional, may appear more than oncece */ + ICLINE_S *comment; /* optional, may appear more than oncece */ + ICLINE_S *freebusy; /* optional, may appear more than oncece */ + ICLINE_S *rstatus; /* optional, may appear more than once */ + ICAL_PARAMETER_S *unknown; /* unknown values */ +} VFREEBUSY_S; + +typedef struct ical_tzprop_s { +#if 0 + struct tm dtstart; /* required, at most one */ + int tzoffsetto; /* required, at most one */ + int tzoffsetfrom; /* required, at most one */ + RRULE_S *rrule; /* optional, at most one */ + GEN_ICLINE_S *comment; /* optional, may appear more than once */ + GEN_ICLINE_S *rdate; /* optional, may appear more than once */ + GEN_ICLINE_S *tzname; /* optional, may appear more than once */ +#endif /* if 0 */ + void **prop; /* the bunch of timezone properties */ + GEN_ICLINE_S *uk_prop; /* optional, may appear more than once */ + struct ical_tzprop_s *next; +} ICAL_TZPROP_S; + +typedef struct vtimezone_s { +#if 0 + ICLINE_S *tzid; /* required, at most one */ + ICLINE_S *last_mod; /* optional, at most one */ + ICLINE_S *tzurl; /* optional, at most one */ +#endif /* if 0 */ + void **prop; /* array of timezone properties */ + ICAL_TZPROP_S *standardc; /* optional, may appear more than once */ + ICAL_TZPROP_S *daylightc; /* optional, may appear more than once */ + GEN_ICLINE_S *uk_prop; /* optional, may appear more than once */ +} VTIMEZONE_S; + +typedef struct vcalendar_s { + ICLINE_S *prodid; /* required, at most one */ + ICLINE_S *version; /* required, at most one */ + ICLINE_S *calscale; /* optional, at most one */ + ICLINE_S *method; /* optional, at most one */ + GEN_ICLINE_S *uk_prop;/* in case there is an unknown property */ + void **comp; /* an array with the components of a calendar */ + ICAL_S *uk_comp; /* in case there is an unknown component */ + unsigned short encoding; /* save the original encoding. */ +} VCALENDAR_S; + + +typedef enum {Individual, Group, Resource, Room, CUUnknown} CUType; +typedef enum {EventNeedsAction, EventAccepted, EventDeclined, EventTentative, + EventDelegated, EventUnknown, + TodoNeedsAction, TodoAccepted, TodoDeclined, TodoTentative, + TodoDelegated, TodoCompleted, TodoInProgress, TodoUnknown, + JournalNeedsAction, JournalAccepted, JournalDeclined, + JournalUnknown} PartStat; +typedef enum {RelBegin, RelEnd, RelUnknown} Related; +typedef enum {Parent, Child, Sibling} RelType; +typedef enum {ReqPart, Chair, OptPart, NonPart, RoleUnknown} Role; +typedef enum {RSVPFalse, RSVPTrue, RSVPUnknown} RSVP; +typedef enum {Binary, Boolean, CalAddress, Date, DateTime, Duration, + Float, Integer, Period, Recur, _Text, Time, Uri, + UtcOffset, ValueUnknown} Value; + +typedef struct icalpar_s { + ICLINE_S *altrep; /* altrep uri, inside dquote RFC 2392, RFC 2616 and RFC 2818 */ + ICLINE_S *cn; /* common name */ + CUType cutype; /* calendar user type: individual, group, resource, room, unknown */ + ADDRESS *delegated_from; /* person(s) who delegated their participation */ + ADDRESS *delegated_to; /* person that was delegated participation */ + ICLINE_S *dir; /* reference to a directory entry associated with the calendar user specified by the property. */ + unsigned short encoding; /* encoding, either 8bit or base64 */ + unsigned short type; /* type for FMTTYPE */ + ICLINE_S *subtype; /* subtype for FMTTYPE */ + ICLINE_S *fbtype; /* FreeBusy type */ + ICLINE_S *language; /* language */ + ADDRESS *member; /* group or list membership of the calendar user specified by the property */ + PartStat partstat; /* participation status for the calendar user specified by the property. */ + ICLINE_S *range; /* this and future */ + Related related; /* relationship of the alarm trigger with respect to the start or end of the calendar component. */ + RelType reltype; /* type of hierarchical relationship associated with the calendar component specified by the property. */ + Role role; /* participation role for the calendar user specified by the property. */ + RSVP rsvp; /* whether there is an expectation of a favor of a reply from the calendar user specified by the property value. */ + ADDRESS *sentby; /* the calendar user that is acting on behalf of the calendar user specified by the property. */ + ICLINE_S *tzid; /* the identifier for the time zone definition for a time component in the property value. */ + Value value; /* specify the value type format for a property value. */ +} ICALPAR_S; + +typedef struct ical_duration_s { + unsigned sign:1; /* sign = 0 for positive, sign = 1 for negative */ + unsigned weeks; + unsigned days; + unsigned hours; + unsigned minutes; + unsigned seconds; + struct ical_duration_s *next; /* another duration */ +} ICAL_DURATION_S; + +/* altrepparam = "ALTREP" "=" DQUOTE uri DQUOTE + * cnparam = "CN" "=" param-value + * cutypeparam = "CUTYPE" "=" + ("INDIVIDUAL" ; An individual + / "GROUP" ; A group of individuals + / "RESOURCE" ; A physical resource + / "ROOM" ; A room resource + / "UNKNOWN" ; Otherwise not known + / x-name ; Experimental type + / iana-token) ; Other IANA-registered + ; type + * delfromparam = "DELEGATED-FROM" "=" DQUOTE cal-address + DQUOTE *("," DQUOTE cal-address DQUOTE) + + * deltoparam = "DELEGATED-TO" "=" DQUOTE cal-address DQUOTE + *("," DQUOTE cal-address DQUOTE) + + * dirparam = "DIR" "=" DQUOTE uri DQUOTE + + * encodingparam = "ENCODING" "=" + ( "8BIT" + ; "8bit" text encoding is defined in [RFC2045] + / "BASE64" + ; "BASE64" binary encoding format is defined in [RFC4648] + ) + * fmttypeparam = "FMTTYPE" "=" type-name "/" subtype-name + ; Where "type-name" and "subtype-name" are + ; defined in Section 4.2 of [RFC4288]. + + * fbtypeparam = "FBTYPE" "=" ("FREE" / "BUSY" + / "BUSY-UNAVAILABLE" / "BUSY-TENTATIVE" + / x-name + ; Some experimental iCalendar free/busy type. + / iana-token) + ; Some other IANA-registered iCalendar free/busy type. + + * languageparam = "LANGUAGE" "=" language + + language = Language-Tag + ; As defined in [RFC5646]. + + * memberparam = "MEMBER" "=" DQUOTE cal-address DQUOTE + *("," DQUOTE cal-address DQUOTE) + + * partstatparam = "PARTSTAT" "=" + (partstat-event + / partstat-todo + / partstat-jour) + + partstat-event = ("NEEDS-ACTION" ; Event needs action + / "ACCEPTED" ; Event accepted + / "DECLINED" ; Event declined + / "TENTATIVE" ; Event tentatively + ; accepted + / "DELEGATED" ; Event delegated + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VEVENT". + ; Default is NEEDS-ACTION. + + partstat-todo = ("NEEDS-ACTION" ; To-do needs action + / "ACCEPTED" ; To-do accepted + / "DECLINED" ; To-do declined + / "TENTATIVE" ; To-do tentatively + ; accepted + / "DELEGATED" ; To-do delegated + / "COMPLETED" ; To-do completed + ; COMPLETED property has + ; DATE-TIME completed + / "IN-PROCESS" ; To-do in process of + ; being completed + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VTODO". + ; Default is NEEDS-ACTION. + + + + partstat-jour = ("NEEDS-ACTION" ; Journal needs action + / "ACCEPTED" ; Journal accepted + / "DECLINED" ; Journal declined + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VJOURNAL". + ; Default is NEEDS-ACTION. + + * rangeparam = "RANGE" "=" "THISANDFUTURE" + ; To specify the instance specified by the recurrence identifier + ; and all subsequent recurrence instances. + + * trigrelparam = "RELATED" "=" + ("START" ; Trigger off of start + / "END") ; Trigger off of end + + * reltypeparam = "RELTYPE" "=" + ("PARENT" ; Parent relationship - Default + / "CHILD" ; Child relationship + / "SIBLING" ; Sibling relationship + / iana-token ; Some other IANA-registered + ; iCalendar relationship type + / x-name) ; A non-standard, experimental + ; relationship type + + * roleparam = "ROLE" "=" + ("CHAIR" ; Indicates chair of the + ; calendar entity + / "REQ-PARTICIPANT" ; Indicates a participant whose + ; participation is required + / "OPT-PARTICIPANT" ; Indicates a participant whose + ; participation is optional + / "NON-PARTICIPANT" ; Indicates a participant who + ; is copied for information + ; purposes only + / x-name ; Experimental role + / iana-token) ; Other IANA role + ; Default is REQ-PARTICIPANT + + * rsvpparam = "RSVP" "=" ("TRUE" / "FALSE") + ; Default is FALSE + + * sentbyparam = "SENT-BY" "=" DQUOTE cal-address DQUOTE + + * tzidparam = "TZID" "=" [tzidprefix] paramtext + + tzidprefix = "/" + + */ + +typedef struct vevent_summary_s { + int cancel:1; + int priority; + char *class; + char *summary; + char *sender; + char *organizer; + char *location; + char *evstart; + char *evend; + char **duration; + char **attendee; + char **description; +} VEVENT_SUMMARY_S; + +typedef enum {VCalendar = 0, VTimeZone, VEvent, VTodo, VJournal, + VAlarm, VFreeBusy, VUnknown} Cal_comp; + +typedef enum {EvDtstamp = 0, EvUid, EvDtstart, EvClass, EvCreated, + EvDescription, EvGeo, EvLastMod, EvLocation, + EvOrganizer, EvPriority, EvSequence, EvStatus, + EvSummary, EvTransp, EvUrl, EvRecurrence, EvRrule, + EvDtend, EvDuration, EvAttach, EvAttendee, EvCategories, + EvComment, EvContact, EvExdate, EvRstatus, EvRelated, + EvResources, EvRdate, + EvUnknown} Event_prop; + +typedef enum {TZCid = 0, TZCLastMod, TZCUrl, TZCUnknown} TZ_comp; + +typedef enum {TZPDtstart = 0, TZPOffsetto, TZPOffsetfrom, + TZPRrule, TZPComment, TZPRdate, TZPTzname, TZPUnknown} TZ_prop; + +typedef enum {AlAction = 0, AlTrigger, AlDuration, AlRepeat, + AlDescription, AlSummary, AlAttach, AlAttendee, + AlUnknown} Alarm_prop; + +typedef enum {RRFreq, RRUntil, RRCount, RRInterval, + RRBysecond, RRByminute, RRByhour, RRByday, + RRByweekno, RRBymonth, RRBysetpos, RRWkst, + RRBymonthday, RRByyearday, + RRUnknown} RRule_prop; + +#endif /* ifndef ICALTYPE_INCLUDED */ diff --git a/pith/mailpart.h b/pith/mailpart.h index aad3fdc4..076ed174 100644 --- a/pith/mailpart.h +++ b/pith/mailpart.h @@ -32,6 +32,9 @@ || ((t) == TYPETEXT \ && (s) && !strucmp((s),"X-VCARD"))) +/* Is this a VCalendar type? */ +#define MIME_VCALENDAR(t,s) ((t) == TYPETEXT && (s) && !strucmp((s),"CALENDAR")) + /* Is this a multipart signed? */ #define MIME_MULT_SIGNED(t,s) ((t) == TYPEMULTIPART && (s) && !strucmp((s),"signed")) @@ -44,6 +47,9 @@ /* Is this a vCard attachment? */ #define MIME_VCARD_A(a) MIME_VCARD((a)->body->type, (a)->body->subtype) +/* Is this a VCalendar attachment? */ +#define MIME_VCALENDAR_A(a) MIME_VCALENDAR((a)->body->type, (a)->body->subtype) + /* exported protoypes */ diff --git a/pith/mailview.c b/pith/mailview.c index ad3efd6d..dc9b29b6 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -25,6 +25,9 @@ static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washin #include "headers.h" +#include "../pith/ical.h" +#include "../pith/body.h" +#include "../pith/mailpart.h" #include "../pith/mailview.h" #include "../pith/conf.h" #include "../pith/msgno.h" @@ -123,7 +126,7 @@ int find_field(char **, char *, size_t); int embed_color(COLOR_PAIR *, gf_io_t); COLOR_PAIR *get_cur_embedded_color(void); void clear_cur_embedded_color(void); - +void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int); @@ -192,8 +195,10 @@ format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body, if(!(body == NULL || (ps_global->full_header == 2 - && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))) + && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){ format_attachment_list(msgno, body, handlesp, flgs, width, pc); + format_calendar(msgno, body, handlesp, flgs, width, pc); + } /* write delimiter and body */ if(gf_puts(NEWLINE, pc) @@ -210,6 +215,328 @@ format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body, return(0); } +void +format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags) +{ + int avail, m1, m2, hwid, i, partwid, padwid; + int s1, s2, dwid; + int *margin; + char padding[1024]; + VEVENT_SUMMARY_S *vesy; /* vevent summary */ + + vesy = ical_vevent_summary(vcal); + + if(vesy == NULL) return; + + if((cflags & FC_SUMMARY) && (cflags & FC_FULL)) + cflags |= ~FC_FULL; + + avail = width; + margin = (cflags & FC_FULL) ? NULL + : (flgs & FM_NOINDENT) ? NULL : format_view_margin(); + + m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0); + avail -= m1; + + m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0); + avail -= m2; + + hwid = MAX(avail, 0); + padwid = 0; + + if(cflags & FC_SUMMARY){ + i = utf8_width(_("Calendar Entry:")); + partwid = MIN(i, hwid); + if(m1 > 0){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, ""); + if(!gf_puts(tmp_20k_buf, pc)) + return; + } + + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s", + partwid, partwid, _("Calendar Entry:"), + padwid, padwid, ""); + + if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc)) + return; + } + else + partwid = 0; + + if(m1 > 0){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, ""); + if(!gf_puts(tmp_20k_buf, pc)) + return; + } + + avail = width - m1 - m2; + + s1 = MAX(MIN(1, avail), 0); + avail -= s1; + + dwid = MAX(MIN(1, avail), 0); + avail -= dwid; + + s2 = MAX(MIN(1, avail), 0); + avail -= s2; + + if(cflags & FC_SUMMARY) + utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s", + s1, s1, "", dwid, dwid, "", s2, s2, ""); + else + padding[0] = '\0'; + + if(vesy->cancel){ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", + padding, _("This event was cancelled!")); + if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){ + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + (*pc)(TAG_EMBED); + (*pc)(TAG_BOLDOFF); + } + } + + if(vesy->organizer){ + if(vesy->sender != NULL){ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("Sent-by: "), vesy->sender); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } + + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("Organizer: "), vesy->organizer); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } /* end of if(organizer) */ + + if(vesy->location){ + ical_remove_escapes(&vesy->location); + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("Location: "), vesy->location); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } /* end of if location */ + + if(vesy->evstart){ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("Start Date: "), vesy->evstart); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } /* end of if dtstart */ + + if(vesy->duration){ + int i; + + for(i = 0; vesy->duration[i] != NULL; i++){ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("Duration: "), vesy->duration[i]); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } + } /* end of DURATION */ + else if(vesy->evend){ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, _("End Date: "), vesy->evend); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } else { /* end of if dtend */ + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", + padding, _("No duration nor end time found for this event")); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } /* end of else for if (duration) and if (dtend) */ + + if(vesy->attendee){ +#define MAX_DISPLAYED 3 + int i; + + for(i = 0; vesy->attendee[i] != NULL; i++){ + if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED) + break; + + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", + padding, + _("Attendee: "), vesy->attendee[i]); + gf_puts(tmp_20k_buf, pc); + gf_puts(NEWLINE, pc); + } + if(cflags & FC_SUMMARY){ + COLOR_PAIR *lastc = NULL; + char numbuf[50]; + int thisdescwid; + COLOR_PAIR *hdrcolor = NULL; + + if((flgs & FM_DISPLAY) + && !(flgs & FM_NOCOLOR) + && pico_usingcolor() + && ps_global->VAR_HEADER_GENERAL_FORE_COLOR + && ps_global->VAR_HEADER_GENERAL_BACK_COLOR + && ps_global->VAR_NORM_FORE_COLOR + && ps_global->VAR_NORM_BACK_COLOR + && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR, + ps_global->VAR_NORM_FORE_COLOR) + || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR, + ps_global->VAR_NORM_BACK_COLOR))){ + + if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR, + ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){ + if(!pico_is_good_colorpair(hdrcolor)) + free_color_pair(&hdrcolor); + } + } + + if(!(!hdrcolor || embed_color(hdrcolor, pc))) + return; + + gf_puts(padding, pc); + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details")); + + if(handlesp){ + char buf[16], color[64]; + int l; + HANDLE_S *h; + + h = new_handle(handlesp); + h->type = Attach; + h->h.attach = a; + + snprintf(buf, sizeof(buf), "%d", h->key); + buf[sizeof(buf)-1] = '\0'; + + if(!(flgs & FM_NOCOLOR) + && handle_start_color(color, sizeof(color), &l, 1)){ + lastc = get_cur_embedded_color(); + if(!gf_nputs(color, (long) l, pc)) + return; + } + else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global) + && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)))) + return; + + if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) + && (*pc)(strlen(buf)) && gf_puts(buf, pc))) + return; + } else + tmp_20k_buf[0] = '\0'; + + if(!format_env_puts(tmp_20k_buf, pc)) + return; + + if(handlesp){ + if(lastc){ + if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){ + if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF))) + return; + } + + if(!embed_color(lastc, pc)) + return; + + free_color_pair(&lastc); + } + else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF))) + return; + + if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF))) + return; + } + + if(padwid > 0){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, ""); + if(!gf_puts(tmp_20k_buf, pc)) + return; + } + + if(!gf_puts(NEWLINE, pc)) + return; + } + } /* end of ATTENDEES */ + + free_vevent_summary(&vesy); + + avail = width - m1 -2; + + dwid = MAX(MIN(40, avail), 0); + avail -= dwid; + + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "", + repeat_char(dwid, '-')); + gf_puts(tmp_20k_buf, pc); +} + +int +format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc) +{ + char *rawtext, *caltext; + unsigned long callen; + VCALENDAR_S *vcal = NULL; + ATTACH_S *a; + BODY *b; + + if(flgs & FM_NEW_MESS) { + zero_atmts(ps_global->atmts); + describe_mime(body, "", 1, 1, 0, flgs); + } + + for(a = ps_global->atmts; a->description != NULL; a++){ + if(MIME_VCALENDAR(a->body->type, a->body->subtype)){ + b = mail_body (ps_global->mail_stream, msgno, a->number); + if(b == NULL){ + gf_puts(_("Error fetching calendar body part"), pc); + gf_puts(NEWLINE, pc); + continue; + } + if(b->sparep == NULL){ + rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); + if(rawtext == NULL || *rawtext == '\0'){ + gf_puts(_("Error fetching calendar text"), pc); + gf_puts(NEWLINE, pc); + continue; + } + rawtext[callen] = '\0'; /* chop off cookie */ + switch(b->encoding){ + case ENCBASE64: + caltext = rfc822_base64(rawtext, strlen(rawtext), &callen); + if(caltext == NULL){ + gf_puts(_("Error in calendar base64 encoding"), pc); + gf_puts(NEWLINE, pc); + continue; + } + break; + + case ENCQUOTEDPRINTABLE: + caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen); + if(caltext == NULL){ + gf_puts(_("Error in calendar quoted printable encoding"), pc); + gf_puts(NEWLINE, pc); + continue; + } + break; + + default: caltext = cpystr(rawtext); + } + if(caltext != NULL){ + vcal = ical_parse_text(caltext); + if(vcal != NULL) vcal->encoding = b->encoding; + b->sparep = create_body_sparep(iCalType, (void *) vcal); + fs_give((void **) &caltext); + } + } + else if(get_body_sparep_type(b->sparep) == iCalType) + vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep); + if(vcal != NULL && vcal->comp != NULL){ + if(vcal->comp[VEvent] != NULL){ + format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY); + } /* else another type of entry in the calendar */ + } + gf_puts(NEWLINE, pc); + } + } + return 0; +} + char * format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc) @@ -336,6 +663,8 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f /*======== Now loop through formatting all the parts =======*/ for(a = ps_global->atmts; a->description != NULL; a++) { + if(MIME_VCALENDAR(a->body->type, a->body->subtype)) + continue; if(a->body->type == TYPEMULTIPART){ #ifdef SMIME @@ -424,8 +753,11 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f #endif /* SMIME */ ) && !(flgs & FM_NOEDITORIAL)){ - tmp1 = a->body->description ? a->body->description - : "Attached Text"; + if(MIME_VCALENDAR(a->body->type, a->body->subtype)) + tmp1 = "Calendar entry"; + else + tmp1 = a->body->description ? a->body->description + : "Attached Text"; description = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000); @@ -437,8 +769,9 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc))) return("Write Error"); } - - error_found += decode_text(a, msgno, pc, handlesp, + /* skip calendar */ + if(!MIME_VCALENDAR(a->body->type, a->body->subtype)) + error_found += decode_text(a, msgno, pc, handlesp, (flgs & FM_DISPLAY) ? InLine : QStatus, flgs); break; @@ -604,6 +937,9 @@ format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs /*----- Figure max display widths -----*/ for(a = ps_global->atmts; a->description != NULL; a++){ + if(MIME_VCALENDAR(a->body->type, a->body->subtype)) + continue; + if((n = utf8_width(a->number)) > maxnumwid) maxnumwid = n; @@ -656,6 +992,8 @@ format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs char numbuf[50]; int thisdescwid, padwid; + if(MIME_VCALENDAR(a->body->type, a->body->subtype)) + continue; #ifdef SMIME if(a->body->type == TYPEMULTIPART && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)) diff --git a/pith/mailview.h b/pith/mailview.h index 3767cc32..6a933f24 100644 --- a/pith/mailview.h +++ b/pith/mailview.h @@ -45,6 +45,9 @@ #define FM_HTML 0x4000 /* filter/preserve HTML markup */ #define FM_HTMLIMAGES 0x8000 /* filter/preserve HTML IMG tags */ +/* Format Calendar Flags */ +#define FC_SUMMARY 0x0001 /* Display only a summary event */ +#define FC_FULL 0x0002 /* Display full details of event */ #define SIGDASHES "-- " #define START_SIG_BLOCK 2 @@ -123,6 +126,7 @@ typedef struct header_s { int format_message(long, ENVELOPE *, BODY *, HANDLE_S **, int, gf_io_t); int format_attachment_list(long int, BODY *, HANDLE_S **, int, int, gf_io_t); char *format_body(long int, BODY *, HANDLE_S **, HEADER_S *, int, int, gf_io_t); +int format_calendar(long int, BODY *, HANDLE_S **, int, int, gf_io_t); int url_hilite(long, char *, LT_INS_S **, void *); int handle_start_color(char *, size_t, int *, int); int handle_end_color(char *, size_t, int *); diff --git a/pith/mimedesc.c b/pith/mimedesc.c index ab47770d..6d5999c4 100644 --- a/pith/mimedesc.c +++ b/pith/mimedesc.c @@ -252,7 +252,8 @@ describe_mime(struct mail_bodystruct *body, char *prefix, int num, * be expensive to test... */ if((body->type == TYPETEXT && !named) - || MIME_VCARD(body->type,body->subtype)){ + || MIME_VCARD(body->type,body->subtype) + || MIME_VCALENDAR(body->type, body->subtype)){ a->test_deferred = 1; a->can_display = MCD_INTERNAL; } @@ -264,7 +265,8 @@ describe_mime(struct mail_bodystruct *body, char *prefix, int num, /* * Deferred means we can display it */ - a->shown = ((a->can_display & MCD_INTERNAL) + a->shown = MIME_VCALENDAR(body->type, body->subtype) + || ((a->can_display & MCD_INTERNAL) && !MIME_VCARD(body->type,body->subtype) && (!named || multalt || (body->type == TYPETEXT && num == 1 diff --git a/pith/pine.hlp b/pith/pine.hlp index b8aae319..c3c2de06 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 245 2018-01-21 16:52:38 +Alpine Commit 249 2018-02-24 16:53:18 ============= h_news ================= <HTML> <HEAD> diff --git a/pith/smime.c b/pith/smime.c index 78596b3b..725337e7 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -42,6 +42,7 @@ static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washingto #include "../pith/tempfile.h" #include "../pith/readfile.h" #include "../pith/remote.h" +#include "../pith/body.h" #ifdef PASSFILE #include "../pith/imap.h" #endif /* PASSFILE */ @@ -94,30 +95,6 @@ static X509_STORE *s_cert_store; /* State management for randomness functions below */ static int seeded = 0; -void * -create_smime_sparep(SpareType stype, void *s) -{ - SMIME_SPARE_S *rv; - - rv = fs_get(sizeof(SMIME_SPARE_S)); - rv->sptype = stype; - rv->data = s; - return (void *) rv; -} - -SpareType -get_smime_sparep_type(void *s) -{ - return ((SMIME_SPARE_S *)s)->sptype; -} - -void * -get_smime_sparep_data(void *s) -{ - return ((SMIME_SPARE_S *)s)->data; -} - - #ifdef PASSFILE /* * This code does not work in Windows, because of the PASSFILE thing, so @@ -2877,30 +2854,6 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent) return result; } - -void -free_smime_body_sparep(void **sparep) -{ - char *s; - SIZEDTEXT *st; - if(sparep && *sparep){ - switch(get_smime_sparep_type(*sparep)){ - case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep)); - break; - case CharType: s = (char *)get_smime_sparep_data(*sparep); - fs_give((void **) &s); - break; - case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep); - fs_give((void **) &st->data); - fs_give((void **) &st); - break; - default : break; - } - ((SMIME_SPARE_S *)(*sparep))->data = NULL; - fs_give(sparep); - } -} - /* Big comment, explaining the mess that exists out there, and how we deal with it, and also how we solve the problems that are created this way. @@ -3085,9 +3038,9 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) * to check the validity of the signature */ if(b->sparep){ - if(get_smime_sparep_type(b->sparep) == SizedText){ + if(get_body_sparep_type(b->sparep) == SizedText){ /* bodytext includes mimetext */ - st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep); + st = (SIZEDTEXT *) get_body_sparep_data(b->sparep); bodytext = (char *) st->data; bodylen = st->size; mimetext = NULL; @@ -3205,7 +3158,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) b->description = cpystr(what_we_did); - b->sparep = create_smime_sparep(P7Type, p7); + b->sparep = create_body_sparep(P7Type, p7); p = b->nested.part; @@ -3342,8 +3295,8 @@ do_decoding(BODY *b, long msgno, const char *section) */ if(b->sparep){ - if(get_smime_sparep_type(b->sparep) == P7Type) - p7 = (PKCS7*) get_smime_sparep_data(b->sparep); + if(get_body_sparep_type(b->sparep) == P7Type) + p7 = (PKCS7*) get_body_sparep_data(b->sparep); } else{ p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1"); @@ -3357,7 +3310,7 @@ do_decoding(BODY *b, long msgno, const char *section) * Save the PKCS7 object for later dealings by the user interface. * It will be cleaned up when the body is garbage collected. */ - b->sparep = create_smime_sparep(P7Type, p7); + b->sparep = create_body_sparep(P7Type, p7); } dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7))); @@ -3470,7 +3423,7 @@ do_decoding(BODY *b, long msgno, const char *section) st = fs_get(sizeof(SIZEDTEXT)); st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */ st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4); - body->sparep = create_smime_sparep(SizedText, (void *)st); + body->sparep = create_body_sparep(SizedText, (void *)st); } else q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list.")); diff --git a/pith/smime.h b/pith/smime.h index 2e3ff913..d06ff764 100644 --- a/pith/smime.h +++ b/pith/smime.h @@ -36,14 +36,6 @@ #define OUR_PKCS7_ENCLOSURE_SUBTYPE "x-pkcs7-enclosure" -typedef enum {P7Type, CharType, SizedText} SpareType; - -typedef struct smime_sparep_t { - SpareType sptype; - void *data; -} SMIME_SPARE_S; - - /* exported protoypes */ int smime_validate_cert(X509 *cert, long *error); int encrypt_file(char *fp, char *text, PERSONAL_CERT *pc); @@ -51,7 +43,6 @@ char *decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc); int is_pkcs7_body(BODY *b); int fiddle_smime_message(BODY *b, long msgno); int encrypt_outgoing_message(METAENV *header, BODY **bodyP); -void free_smime_body_sparep(void **sparep); int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp); void gf_puts_uline(char *txt, gf_io_t pc); PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri); @@ -83,9 +74,6 @@ void mark_cert_deleted(WhichCerts ctype, int num, unsigned state); unsigned get_cert_deleted(WhichCerts ctype, int num); int smime_expunge_cert(WhichCerts ctype); int add_file_to_container(WhichCerts ctype, char *fpath, char *altname); -void *create_smime_sparep(SpareType stype, void *s); -SpareType get_smime_sparep_type(void *s); -void *get_smime_sparep_data(void *s); STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error, int *level); CertList *certlist_from_personal_certs(PERSONAL_CERT *pc); int smime_path(char *rpath, char *fpath, size_t len); |