From 480bf4063f7d31b7d2c9ec8edd9fc4280ffb39ee Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 10 Dec 2017 09:24:34 -0700 Subject: Initial commit for branch ical --- pith/Makefile.am | 4 +- pith/Makefile.in | 35 +- pith/body.c | 70 ++ pith/body.h | 23 + pith/ical.c | 2245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pith/ical.h | 17 + pith/icaltype.h | 476 ++++++++++++ pith/mailpart.h | 6 + pith/mailview.c | 316 +++++++- pith/mailview.h | 4 + pith/mimedesc.c | 6 +- pith/smime.c | 63 +- pith/smime.h | 12 - 13 files changed, 3183 insertions(+), 94 deletions(-) create mode 100644 pith/body.c create mode 100644 pith/body.h create mode 100644 pith/ical.c create mode 100644 pith/ical.h create mode 100644 pith/icaltype.h (limited to 'pith') 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..8364d384 --- /dev/null +++ b/pith/ical.c @@ -0,0 +1,2245 @@ +#include "../pith/headers.h" +#include "../pith/mailpart.h" +#include "../pith/store.h" +#include "../pith/ical.h" +#ifdef STANDALONE +#include "readfile.h" +#include "mem.h" +char *lf2crlf(char *); +#endif /* STANDALONE */ + +typedef struct ical_iana_comp_s { + char *comp; /* component name */ + size_t len; /* size of component name (strlen(x->comp)) */ + int pos; /* position of this component in comp array */ + void *(*parse)(char **); /* parser */ + void (*give)(void **); /* free memory */ +} ICAL_COMP_S; + +typedef struct ical_iana_prop_s { + char *prop; /* component name */ + size_t len; /* size of component name (strlen(x->comp)) */ + int pos; /* location of this component in the prop array */ + void (*parse)(); /* parser */ + void (*give)(void **); /* free memory */ +} ICAL_PROP_S; + +int ical_january_first(int); /* args: year */ +void ical_adjust_date(struct tm *, VTIMEZONE_S *); + +void ical_initialize(void); + +int ical_non_ascii_valid(unsigned char); +char *ical_unfold_line(char *); +ICLINE_S *ical_parse_line(char **, char *); + +ICLINE_S *ical_cline_cpy(ICLINE_S *); +ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param); + +char *ical_get_value(char **); + +/* pase component */ +void *ical_parse_vcalendar(char **); +void *ical_parse_vevent(char **); +void *ical_parse_vtodo(char **); +void *ical_parse_vjournal(char **); +void *ical_parse_vfreebusy(char **); +void *ical_parse_vtimezone(char **); +void *ical_parse_valarm(char **); +void *ical_parse_timezone(char **); +ICAL_S *ical_parse_unknown_comp(char **, int); +ICAL_S *ical_parse_generic_comp(char **, int); + +/* free components */ +void ical_free_vevent(void **); +void ical_free_vtodo(void **); +void ical_free_vjournal(void **); +void ical_free_vfreebusy(void **); +void ical_free_vtimezone(void **); +void ical_free_timezone(void **); +void ical_free_valarm(void **); +void ical_free_unknown_comp(ICAL_S **); + +/* parse properties */ +void ical_cline_from_token(void **, char **, char *); +void ical_gencline_from_token(void **, char **, char *); + +void ical_parse_rrule(RRULE_S **, char **); +void ical_parse_time(struct tm *, char **, char *); +void ical_parse_offset(int *, char **, char *); + +void ical_parse_freq(Freq_value *, char *); +void ical_parse_weekday_list(BYWKDY_S **, char *); +void ical_parse_number_list(BYWKDY_S **, char *); +void ical_parse_interval(unsigned long *, char *); + +int ical_get_number_value(char *, int, int); +void ical_set_date(ICLINE_S *, VTIMEZONE_S *); +void ical_set_date_vevent(void *, void *); + +/* free properties */ +void ical_free_prop(void **, ICAL_PROP_S *); +void ical_free_null(void **); +void ical_free_cline(void **); +void ical_free_param(ICAL_PARAMETER_S **); +void ical_free_gencline(void **); +void ical_free_rrule(void **); +void ical_free_weekday_list(void **); + +/* globals */ +struct tm day_zero; /* date for january 1, 1601 */ +int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define UTF8_COMPLETE (1) +#define NEED_MORE (2) + +char *ical_buf; +unsigned long ical_buf_len; + + +/* parsing structures */ + +/* this is the list of V-components to a Calendar from RFC 5545 */ +ICAL_COMP_S ical_comp[] = { + {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar}, + {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone}, + {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent}, + {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo}, + {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal}, + {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm}, + {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy}, + {NULL, 0, VUnknown, NULL, 0} +}; + +/* array for properties */ +ICAL_PROP_S rrule_prop[] = { + {"FREQ", 4, RRFreq, ical_parse_freq, ical_free_null}, + {"UNTIL", 5, RRUntil, ical_parse_freq, 0}, + {"COUNT", 5, RRCount, ical_cline_from_token, ical_free_cline}, + {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_free_null}, + {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list}, + {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list}, + {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list}, + {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list}, + {"BYWEEKNO", 8, RRByweekno, 0, 0}, + {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list}, + {"BYSETPOS", 8, RRBysetpos, 0, 0}, + {"BYWKST", 6, RRWkst, 0, 0}, + {"BYMONTHDAY", + 10, RRBymonthday, 0, 0}, + {"BYYEARDAY", 9, RRByyearday, 0, 0}, + {NULL, 0, RRUnknown, 0, 0} +}; + +ICAL_PROP_S event_prop[] = { + {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline}, + {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline}, + {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline}, + {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline}, + {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline}, + {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline}, + {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline}, + {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline}, + {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline}, + {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline}, + {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline}, + {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline}, + {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline}, + {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline}, + {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline}, + {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline}, + {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline}, + {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule}, + {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline}, + {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline}, + {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline}, + {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline}, + {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline}, + {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline}, + {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline}, + {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline}, + {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline}, + {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline}, + {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline}, + {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, EvUnknown, 0, 0} +}; + +ICAL_PROP_S tz_comp[] = { + {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline}, + {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline}, + {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline}, + {NULL, 0, TZCUnknown, 0, 0} +}; + +ICAL_PROP_S tz_prop[] = { + {"DTSTART", 7, TZPDtstart, ical_parse_time, 0}, + {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, 0}, + {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, 0}, + {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule}, + {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline}, + {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline}, + {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, TZPUnknown, 0, 0} +}; + +ICAL_PROP_S alarm_prop[] = { + {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline}, + {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline}, + {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline}, + {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline}, + {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline}, + {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline}, + {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline}, + {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline}, + {NULL, 0, AlUnknown, 0, 0} +}; + +/* some useful macros for character analysis */ + +#define ical_wspace(X) \ + ((X) == ' ' || (X) == '\t') + +#define ical_name_allowed_char(X) \ + (((X) >= 'A' && (X) <= 'Z') || \ + ((X) >= 'a' && (X) <= 'z') || \ + (X) == '-' ) + +#define ical_control(X) \ + (((X) >= 0x00 && (X) <= 0x08) || \ + ((X) >= 0x0A && (X) <= 0x1F) || \ + (X) == 0x7F) + +#define ical_safe_char(X) \ + (ical_non_ascii_valid(X) \ + || ical_wspace(X) \ + || (X) == 0x21 \ + || ((X) >= 0x23 && (X) <= 0x2B) \ + || ((X) >= 0x2D && (X) <= 0x39) \ + || ((X) >= 0x3C && (X) <= 0x7E)) + +#define ical_qsafe_char(X) \ + (ical_non_ascii_valid((X)) \ + || ical_wspace(X) \ + || (X) == 0x21 \ + || ((X) >= 0x23 && (X) <= 0x7E)) + +#define ical_value_char(X) \ + (ical_non_ascii_valid(X) \ + || ical_wspace(X) \ + || ((X) >= 0x21 && (X) <= 0x7E)) + +/* Finally, here begins the code. */ + +void ical_debug(char *fcn, char *text) +{ + char piece[50]; + strncpy(piece, text, 49); + dprint((9, "%s: %s", fcn, piece)); +} + +/*** + *** FREE MEMORY FUNCTIONS + ***/ + +void +ical_free_param(ICAL_PARAMETER_S **param) +{ + if(param == NULL || *param == NULL) + return; + + if((*param)->name) fs_give((void **) &(*param)->name); + if((*param)->value) fs_give((void **) &(*param)->value); + if((*param)->next) ical_free_param(&(*param)->next); + fs_give((void **)param); +} + +void +ical_free_null(void **n) +{ + if(n != NULL) *n = NULL; +} + +void +ical_free_cline(void **icv) +{ + ICLINE_S **ic = (ICLINE_S **) icv; + + if(ic == NULL || *ic == NULL) + return; + + if((*ic)->token) fs_give((void **) &(*ic)->token); + if((*ic)->param) ical_free_param(&(*ic)->param); + if((*ic)->value) fs_give((void **) &(*ic)->value); + fs_give(icv); +} + +void +ical_free_gencline(void **giclpv) +{ + GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv; + + if(giclp == NULL || *giclp == NULL) return; + + if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline); + if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next); +} + +void +ical_free_vcalendar(void **vcalpv) +{ + VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv; + + if(vcalp == NULL || *vcalp == NULL) return; + + if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid); + if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version); + if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale); + if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method); + if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop); + if((*vcalp)->comp){ + Cal_comp i; + for(i = 0; i < VUnknown; i++) + if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]); + fs_give((*vcalp)->comp); + } + if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp); + fs_give(vcalpv); +} + +void +ical_free_vevent(void **veventpv) +{ + VEVENT_S **veventp = (VEVENT_S **) veventpv; + + if(veventp == NULL || *veventp == NULL) return; + + ical_free_prop((*veventp)->prop, event_prop); + if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop); + if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm); + fs_give(veventpv); +} + +void +ical_free_rrule(void **rrulepv) +{ + RRULE_S **rrulep = (RRULE_S **) rrulepv; + + if(rrulep && *rrulep){ + ical_free_prop((*rrulep)->prop, rrule_prop); + ical_free_param(&(*rrulep)->param); + fs_give(rrulepv); + } +} + +void +ical_free_weekday_list(void **wkdylv) +{ + BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv; + + if(wkdyl == NULL) return; + + if((*wkdyl)->next) + ical_free_weekday_list((void **) &(*wkdyl)->next); + + fs_give(wkdylv); +} + + +void +ical_free_vtodo(void **vtodopv) +{ +} + +void +ical_free_vjournal(void **vjournalpv) +{ +} + +void +ical_free_vfreebusy(void **vfbpv) +{ +} + +void +ical_free_prop(void **propv, ICAL_PROP_S *aux_comp) +{ + int i, j; + + if(propv == NULL) return; + + for(i = 0; aux_comp[i].prop != NULL; i++) + if(propv[i]){ + for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++); + if(aux_comp[j].give) (aux_comp[j].give)(&propv[i]); + } + fs_give(propv); +} + + +void +ical_free_vtimezone(void **vtzpv) +{ + VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv; + TZ_comp i,j; + + if(vtzp == NULL || *vtzp == NULL) return; + + ical_free_prop((*vtzp)->prop, tz_comp); + + if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop); + if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc); + if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc); + fs_give(vtzpv); +} + +void +ical_free_timezone(void **tzpv) +{ + ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv; + + if(tzp == NULL || *tzp == NULL) return; + + ical_free_prop((*tzp)->prop, tz_prop); + if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop); + if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next); + fs_give(tzpv); +} + +void ical_free_valarm(void **valarmpv) +{ +} + +void +ical_free_unknown_comp(ICAL_S **icalp) +{ + int i; + if(icalp == NULL || *icalp == NULL) return; + for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++); + if(ical_comp[i].give) + (ical_comp[i].give)(&(*icalp)->value); + else + ical_free_gencline((void **) &(*icalp)->value); + fs_give((void **)&(*icalp)->comp); + ical_free_unknown_comp(&(*icalp)->next); + ical_free_unknown_comp(&(*icalp)->branch); + fs_give((void **)icalp); +} + +char *ical_unfold_line(char *line) +{ + int i, j; + + if(line == NULL) + return NULL; + + for(i = 0, j = 0; line[j] != '\0';) + switch(line[j]){ + case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){ + j += 3; /* get past white space */ + continue; + } + default : line[i++] = line[j++]; + } + line[i] = '\0'; + return line; +} + +ICAL_PARAMETER_S * +ical_get_parameter(char **line) +{ + ICAL_PARAMETER_S *param = NULL; + char *s; + + if(line == NULL || *line == NULL) + return NULL; + + for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++); + + if(*s == '='){ + int quoted; + char c; + + param = fs_get(sizeof(ICAL_PARAMETER_S)); + memset((void *)param, 0, sizeof(ICAL_PARAMETER_S)); + *s = '\0'; + param->name = cpystr(*line); + *s = '='; + *line = s+1; /* step over '=' */ + quoted = **line == '"' ? 1 : 0; + if(quoted != 0){ + for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++); + if(*s != '"'){ /* error, do not parse this line */ + ical_free_param(¶m); + *line = strchr(s, ':'); /* reset line to closest ':' */ + return NULL; + } + } + else + for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++); + c = *s; + *s = '\0'; + param->value = cpystr(*line); + *s = c; /* restore character */ + *line = quoted ? s + 1 : s; + + if(**line == ';'){ + ++*line; + param->next = ical_get_parameter(line); + } + } + return param; +} + +char *ical_get_value(char **line) +{ + char *s, *t; + + if(line == NULL || *line == NULL) + return NULL; + + for (s = *line; *s && ical_value_char((unsigned char) *s); s++); + if(*s == '\r'){ + *s = '\0'; + t = cpystr(*line); + *s = '\r'; + *line = s+2; + } + return t; +} + +ICAL_PARAMETER_S * +ical_parameter_cpy(ICAL_PARAMETER_S *param) +{ + ICAL_PARAMETER_S *rv; + + if(param == NULL) return NULL; + + rv = fs_get(sizeof(ICAL_PARAMETER_S)); + memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S)); + + if(param->name) rv->name = cpystr(param->name); + if(param->value) rv->value = cpystr(param->value); + if(param->next) rv->next = ical_parameter_cpy(param->next); + + return rv; +} + +ICLINE_S * +ical_cline_cpy(ICLINE_S *icl) +{ + ICLINE_S *rv; + + if(icl == NULL) + return NULL; + + rv = fs_get(sizeof(ICLINE_S)); + memset((void *)rv, 0, sizeof(ICLINE_S)); + + if(icl->token) rv->token = cpystr(icl->token); + if(icl->param) rv->param = ical_parameter_cpy(icl->param); + if(icl->value) rv->value = cpystr(icl->value); + + return rv; +} + +/* Given a \r\n-ending line (called *text), isolate the ocurrence + * of the token in that line. + * Return the token, and modify the pointer to *text to point to the + * end of the token. Modify sep to contain the character following + * the token + * ical-line = token ':'/';' rest of the line\r\n + * on error return null, and set *text to the next line, if possible. + */ +char * +ical_isolate_token(char **text, char *sep) +{ + char *s, *t; + + for(t = s = *text; *t && ical_name_allowed_char(*s); s++); + /* only followed by parameter or value */ + if(*s == ':' || *s == ';'){ + *sep = *s; + *s = '\0'; /* isolate token at pointer s */ + *text = s; + } + else{ /* bad data - bail out of here */ + t = NULL; + if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL) + *text = NULL; + else /* move to next line */ + *text = s + 2; + } + return t; +} + + +VCALENDAR_S * +ical_parse_text(char *text) +{ + char *s; + VCALENDAR_S *vcal = NULL; + + ical_debug("ical_parse_text", text); + ical_initialize(); + + text = ical_unfold_line(text); + for(s = text; s && *s != '\0'; s++){ + if(*s != 'B' && *s != 'b') + continue; + if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){ + s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */ + vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s); + break; + } + } + return vcal; +} + +void +ical_parse_time(struct tm *ic_date, char **text, char *token) +{ + ICLINE_S *icl; + + icl = ical_parse_line(text, token); + ical_parse_date(icl->value, ic_date); + ical_free_cline((void **) &icl); +} + +void +ical_parse_interval(unsigned long *longv, char *value) +{ + *longv = atoi(value); +} + + +void +ical_parse_offset(int *offsetv, char **text, char *token) +{ + ICLINE_S *icl; + char *value; + int h, m, offset; + + icl = ical_parse_line(text, token); + + if(*icl->value == '+' || *icl->value == '-') + value = icl->value + 1; + else + value = icl->value; + + h = ical_get_number_value(value, 0, 2); + m = ical_get_number_value(value, 2, 4); + + offset = 60*(60*h + m); + if(*icl->value == '-') + offset *= -1; + + *offsetv = offset; + ical_free_cline((void **) &icl); +} + +void +ical_cline_from_token(void **iclv, char **text, char *token) +{ + ICLINE_S **icl = (ICLINE_S **)iclv; + + if(*icl == NULL) + *icl = ical_parse_line(text, token); + else { + ICLINE_S *ic = ical_parse_line(text, token); + ical_free_cline((void **)&ic); + } +} + +void +ical_gencline_from_token(void **giclv, char **text, char *token) +{ + GEN_ICLINE_S *gicl; + + if(!struncmp(*text, token, strlen(token))){ + gicl = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *) gicl, 0, sizeof(GEN_ICLINE_S)); + ical_cline_from_token((void **) &gicl->cline, text, token); + ical_gencline_from_token((void **) &gicl->next, text, token); + *giclv = (void *) gicl; + } +} + +/*** + *** PARSE COMPONENT FUNCTIONS + ***/ + +void * +ical_parse_vcalendar(char **text) +{ + char *s, *t; + char c; + VCALENDAR_S *vcal; + void *v; + + dprint((9, "ical_parse_vcalendar:\n")); + ical_debug("ical_parse_vcalendar", *text); + + vcal = fs_get(sizeof(VCALENDAR_S)); + memset((void *)vcal, 0, sizeof(VCALENDAR_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + + *t = c; /* restore character */ + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + int i; + switch(*s){ + case 'B': + case 'b': if(!struncmp(s+1, "EGIN", 4)){ + s += 6; /* 6 = strlen("BEGIN:") */ + for(i = 0; ical_comp[i].comp + && (struncmp(s, ical_comp[i].comp, ical_comp[i].len) + || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++); + + if(ical_comp[i].parse){ + s += ical_comp[i].len + 2; + v = (ical_comp[i].parse)(&s); + if(vcal->comp == NULL){ + vcal->comp = fs_get(VUnknown*sizeof(void *)); + memset((void *) vcal->comp, 0, VUnknown*sizeof(void *)); + } + + if(vcal->comp[ical_comp[i].pos] == NULL) + vcal->comp[ical_comp[i].pos] = v; + else + (ical_comp[i].give)(&v); + } else { + v = (void *) ical_parse_unknown_comp(&s, 0); + if(vcal->uk_comp == NULL) + vcal->uk_comp = (ICAL_S *) v; + else{ + ICAL_S *ic; + for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch); + ic->branch = (ICAL_S *) v; + } + } + } else ukn++; + break; + + case 'C': + case 'c': if(!struncmp(s+1, "ALSCALE", 7)) + ical_cline_from_token((void **) &vcal->calscale, &s, "CALSCALE"); + else ukn++; + break; + + case 'E': + case 'e': if(!struncmp(s+1, "ND", 2)){ + *t = c; + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VCALENDAR\r\n", 11)){ + *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */ + return (void *) vcal; + } +// else ukn++; FIX THIS, this is not quite right + } else ukn++; + break; + + case 'M': + case 'm': if(!struncmp(s+1, "ETHOD", 5)) + ical_cline_from_token((void **) &vcal->method, &s, "METHOD"); + else ukn++; + break; + + case 'P': + case 'p': if(!struncmp(s+1, "RODID", 5)) + ical_cline_from_token((void **) &vcal->prodid, &s, "PRODID"); + else ukn++; + break; + + case 'V': + case 'v': if(!struncmp(s+1, "ERSION", 6)){ + ical_cline_from_token((void **) &vcal->version, &s, "VERSION"); + } else ukn++; + break; + + default : ukn++; + break; + } /* end of switch(*s) */ + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vcal->uk_prop == NULL){ + vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vcal->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + + /* ok, we have parsed the vcalendar, now parse some special properties */ + /* start by parsing dates */ + ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]); + return (void *) vcal; +} + +void * +ical_parse_vevent(char **text) +{ + char *s, *t; + char c; + VEVENT_S *vevent; + + ical_debug("ical_parse_vevent", *text); + vevent = fs_get(sizeof(VEVENT_S)); + memset((void *)vevent, 0, sizeof(VEVENT_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(!struncmp(s, "VALARM\r\n", 8)){ + s += 8; /* 8 = strlen("VALARM\r\n"); */ + if(vevent->valarm == NULL) + vevent->valarm = ical_parse_valarm(&s); + else{ + VALARM_S *valrm; + for(valrm = vevent->valarm; valrm && valrm->next; + valrm = valrm->next); + valrm->next = ical_parse_valarm(&s); + } + } else { + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VEVENT\r\n",8)){ + *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */ + return (void *) vevent; + } + } else{ Event_prop i; + for(i = 0; i < EvUnknown; i++) + if(!struncmp(s, event_prop[i].prop, t-s)) + break; + if(event_prop[i].parse){ + if(vevent->prop == NULL){ + vevent->prop = fs_get(EvUnknown*sizeof(void *)); + memset((void *)vevent->prop, 0, EvUnknown*sizeof(void *)); + } + (event_prop[i].parse)(&vevent->prop[event_prop[i].pos], &s, event_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vevent->uk_prop == NULL){ + vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vevent->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) vevent; +} + +void * +ical_parse_vtimezone(char **text) +{ + char *s, *t; + char c; + void *v; + VTIMEZONE_S *vtz; + + ical_debug("ical_parse_vtimezone", *text); + vtz = fs_get(sizeof(VTIMEZONE_S)); + memset((void *)vtz, 0, sizeof(VTIMEZONE_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(!struncmp(s, "STANDARD\r\n", 10)){ + s += 10; /* 10 = strlen("STANDARD\r\n"); */ + v = ical_parse_timezone(&s); + if(vtz->standardc == NULL) + vtz->standardc = (ICAL_TZPROP_S *) v; + else{ + ICAL_TZPROP_S *dl; + for(dl = vtz->standardc; dl && dl->next; dl = dl->next); + dl->next = (ICAL_TZPROP_S *) v; + } + } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){ + s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */ + v = ical_parse_timezone(&s); + if(vtz->daylightc == NULL) + vtz->daylightc = (ICAL_TZPROP_S *) v; + else{ + ICAL_TZPROP_S *dl; + for(dl = vtz->daylightc; dl && dl->next; dl = dl->next); + dl->next = (ICAL_TZPROP_S *) v; + } + } else { + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "VTIMEZONE\r\n",11)){ + *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */ + return (void *) vtz; + } + } else{ TZ_comp i; + for(i = 0; i < TZCUnknown; i++) + if(!struncmp(s, tz_comp[i].prop, t-s)) + break; + if(tz_comp[i].parse){ + if(vtz->prop == NULL){ + vtz->prop = fs_get(TZCUnknown*sizeof(void *)); + memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *)); + } + (tz_comp[i].parse)(&vtz->prop[tz_comp[i].pos], &s, tz_comp[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(vtz->uk_prop == NULL){ + vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S)); + vtz->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) vtz; +} + +void * +ical_parse_timezone(char **text) +{ + char *s, *t; + char c; + ICAL_TZPROP_S *tzprop; + + ical_debug("ical_parse_timezone", *text); + tzprop = fs_get(sizeof(ICAL_TZPROP_S)); + memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "STANDARD\r\n", 10) + || !struncmp(s, "DAYLIGHT\r\n", 10)){ + *text = s + 10; /* 10 = strlen("STANDARD\r\n") */ + return (void *) tzprop; + } + } else{ TZ_prop i; + for(i = 0; i < TZPUnknown; i++) + if(!struncmp(s, tz_prop[i].prop, t-s)) + break; + if(tz_prop[i].parse){ + if(tzprop->prop == NULL){ + tzprop->prop = fs_get(TZPUnknown*sizeof(void *)); + memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *)); + } + (tz_prop[i].parse)(&tzprop->prop[tz_prop[i].pos], &s, tz_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(tzprop->uk_prop == NULL){ + tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S)); + tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) tzprop; +} + +void * +ical_parse_valarm(char **text) +{ + char *s, *t; + char c; + VALARM_S *valarm; + + ical_debug("ical_parse_valarm", *text); + valarm = fs_get(sizeof(VALARM_S)); + memset((void *)valarm, 0, sizeof(VALARM_S)); + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + *t = c; /* restore separator */ + + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + if(!struncmp(s, "BEGIN", 5)){ + s += 6; /* 6 = strlen("BEGIN:") */ + ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0); + ical_free_unknown_comp(&uk_comp); + } else if(!struncmp(s, "END", t-s-1)){ + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, "ALARM\r\n", 7)){ + *text = s + 7; /* 7 = strlen("ALARM\r\n") */ + return (void *) valarm; + } + } else{ Alarm_prop i; + for(i = 0; i < AlUnknown; i++) + if(!struncmp(s, alarm_prop[i].prop, t-s)) + break; + if(alarm_prop[i].parse){ + if(valarm->prop == NULL){ + valarm->prop = fs_get(AlUnknown*sizeof(void *)); + memset((void *)valarm->prop, 0, AlUnknown*sizeof(void *)); + } + (alarm_prop[i].parse)(&valarm->prop[alarm_prop[i].pos], &s, alarm_prop[i].prop); + } + else + ukn++; + } + + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(valarm->uk_prop == NULL){ + valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S)); + valarm->uk_prop->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gcl; + for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next); + gcl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S)); + gcl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + *text = s; + return (void *) valarm; +} + +void * +ical_parse_vtodo(char **text) +{ + return NULL; +} + +void * +ical_parse_vjournal(char **text) +{ + return NULL; +} + +void * +ical_parse_vfreebusy(char **text) +{ + return NULL; +} + +ICAL_S * +ical_parse_generic_comp(char **text, int level) +{ + ICAL_S *ical; + char *s, *t; + char *token = NULL; + GEN_ICLINE_S *gcl = NULL; + char c; + + ical_debug("ical_parse_generic_comp", *text); + ical = fs_get(sizeof(ICAL_S)); + memset((void *)ical, 0, sizeof(ICAL_S)); + + ical->comp = ical_get_value(text); + token = fs_get(strlen(ical->comp) + 2 + 1); + sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */ + + /* s must always point the the beginning of a line */ + for(s = *text; s && *s != '\0';){ + t = s; + s = ical_isolate_token(&t, &c); + if(s == NULL){ + if(t != NULL) + s = t; + continue; + } + + *t = c; /* restore character */ + if(s){ /* figure out the token */ + int ukn = 0; /* unknown token */ + switch(*s){ + case 'B': + case 'b': if(!struncmp(s+1, "EGIN", 4)){ + s += 6; /* 6 = strlen("BEGIN:") */ + if(ical->next == NULL) + ical->next = ical_parse_unknown_comp(&s, level+1); + else{ + ICAL_S *b; + int i; + + for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++); + if(b->branch == NULL) + b->branch = ical_parse_unknown_comp(&s, level+1); + else { + for(; b && b->branch; b = b->branch); + b->branch = ical_parse_unknown_comp(&s, level+1); + } + } + } else ukn++; + break; + + case 'E': + case 'e': if(!struncmp(s+1, "ND", 2)){ + *t = c; + s += 4; /* 4 = strlen("END:") */ + if(!struncmp(s, token, strlen(token))){ + *text = s + strlen(token); + ical->value = (void *) gcl; + return ical; + } + } else ukn++; + break; + + default : ukn++; + break; + } /* end of switch(*s) */ + if(ukn){ + if(ical_buf_len < t - s){ + fs_resize((void **)&ical_buf, t-s+1); + ical_buf_len = t-s; + } + *t = '\0'; + strcpy(ical_buf, s); + *t = c; + if(gcl == NULL){ + gcl = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gcl, 0, sizeof(GEN_ICLINE_S)); + gcl->cline = ical_parse_line(&s, ical_buf); + } + else{ + GEN_ICLINE_S *gencl; + for (gencl = gcl; gencl && gencl->next; gencl = gencl->next); + gencl->next = fs_get(sizeof(GEN_ICLINE_S)); + memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S)); + gencl->next->cline = ical_parse_line(&s, ical_buf); + } + } + } /* end of if(s) */ + } + + ical->value = (void *) gcl; + *text = s; + return ical; +} + +ICAL_S * +ical_parse_unknown_comp(char **text, int level) +{ + ICAL_S *ical; + int i; + + ical_debug("ical_parse_unknown_comp", *text); + for(i = 0; ical_comp[i].comp + && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len) + || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++); + + if(ical_comp[i].parse){ + *text += ical_comp[i].len + 2; + ical = fs_get(sizeof(ICAL_S)); + memset((void *)ical, 0, sizeof(ICAL_S)); + ical->comp = cpystr(ical_comp[i].comp); + ical->value = (ical_comp[i].parse)(text); + } else + ical = ical_parse_generic_comp(text, level); + + return ical; +} + +ICLINE_S * +ical_parse_line(char **text, char *name) +{ + ICLINE_S *ic; + char *s = *text; + + ic = fs_get(sizeof(ICLINE_S)); + memset((void *)ic, 0, sizeof(ICLINE_S)); + + ic->token = cpystr(name); + s += strlen(name); + if(*s == ';'){ + s++; + ic->param = ical_get_parameter(&s); + } + if(*s == ':'){ + s++; + ic->value = ical_get_value(&s); + } + + *text = s; + return ic; +} + +/*** + *** PARSE PROPERTY FUNCTIONS + ***/ + +void +ical_parse_freq(Freq_value *fval, char *text) +{ + if(fval == NULL) return; + + *fval = FUnknown; + + if(text == NULL) return; + + if(!strucmp(text, "SECONDLY")) *fval = FSecondly; + else if(!strucmp(text, "MINUTELY")) *fval = FMinutely; + else if(!strucmp(text, "HOURLY")) *fval = FHourly; + else if(!strucmp(text, "DAILY")) *fval = FDaily; + else if(!strucmp(text, "WEEKLY")) *fval = FWeekly; + else if(!strucmp(text, "MONTHLY")) *fval = FMonthly; + else if(!strucmp(text, "YEARLY")) *fval = FYearly; +} + +void +ical_parse_weekday_list(BYWKDY_S **bywkdyp, char *wklist) +{ + BYWKDY_S *bywkdy, *w; + char *s, *t, c; + int done; + size_t len; + + if(bywkdyp == NULL) return; + *bywkdyp = bywkdy = NULL; + if(wklist == NULL) return; + + for(t = s = wklist; done == 0; s++){ + if(*s != ',' && *s != '\0') + continue; + c = *s; + if(c == ',') + *s = '\0'; + else /* c == '\0' */ + done++; + len = strlen(t); + if(len > 1){ + for(w = bywkdy; w && w->next; w = w->next); + w = fs_get(sizeof(BYWKDY_S)); + memset((void *)w, 0, sizeof(BYWKDY_S)); + if(!strucmp(t+len-2, "SU")) w->wd = Sunday; + else if(!strucmp(t+len-2, "MO")) w->wd = Monday; + else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday; + else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday; + else if(!strucmp(t+len-2, "TH")) w->wd = Thursday; + else if(!strucmp(t+len-2, "FR")) w->wd = Friday; + else if(!strucmp(t+len-2, "SA")) w->wd = Saturday; +// t[len - 2] = '\0'; + if(*t != '\0') + w->value = strtoul(t, &t, 10); + if(bywkdy == NULL) + bywkdy = w; + } + *s = c; + if(*s == ',') + t = s + 1; + } + + if(*bywkdyp) + ical_free_weekday_list((void **)&bywkdy); + else + *bywkdyp = bywkdy; +} + +void +ical_parse_number_list(BYWKDY_S **bynop, char *nolist) +{ + BYWKDY_S *byno, *n; + char *s, *t, c; + int done; + + if(bynop == NULL) return; + *bynop = byno = NULL; + if(nolist == NULL) return; + + for(t = s = nolist; done == 0; s++){ + if(*s != ',' && *s != '\0') + continue; + c = *s; + if(c == ',') + *s = '\0'; + else /* c == '\0' */ + done++; + + for(n = byno; n && n->next; n = n->next); + n = fs_get(sizeof(BYWKDY_S)); + memset((void *)n, 0, sizeof(BYWKDY_S)); + n->value = strtoul(t, &t, 10); + if(byno == NULL) + byno = n; + *s = c; + if(*s == ',') + t = s + 1; + } + + if(*bynop) + ical_free_weekday_list((void **)&byno); + else + *bynop = byno; +} + +void +ical_parse_rrule(RRULE_S **rrulep, char **text) +{ + RRULE_S *rrule; + ICLINE_S *icl; + char *s; + ICAL_PARAMETER_S *param, *p; + int i; + + if(rrulep == NULL + || text == NULL || *text == NULL || struncmp(*text, "RRULE", 5)) return; + rrule = fs_get(sizeof(RRULE_S)); + memset((void *) rrule, 0, sizeof(RRULE_S)); + + /* recurring rules are special. First, we parse the icline that contains it */ + icl = ical_parse_line(text, "RRULE"); + + /* now we copy the parameters that it contains */ + rrule->param = ical_parameter_cpy(icl->param); + + /* then we parse icl->value as if it was a parameter */ + s = icl->value; + param = ical_get_parameter(&s); + + /* now we check which values were given, and fill the prop array */ + rrule->prop = fs_get(RRUnknown*sizeof(void *)); + memset((void *) rrule->prop, 0, RRUnknown*sizeof(void *)); + + for(p = param; p != NULL; p = p->next){ + for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++); + if(rrule_prop[i].parse) + (rrule_prop[i].parse)(&rrule->prop[rrule_prop[i].pos], p->value); + } + + ical_free_param(¶m); + ical_free_cline((void **)&icl); + if(*rrulep) + ical_free_rrule((void **)&rrule); + else + *rrulep = rrule; +} + +/*** UTF-8 for ICAL ***/ + +int +ical_non_ascii_valid(unsigned char c) +{ + static char icu[6]; + static int utf8_len = 0; + static int utf8_type = 0; + int rv; + + if(utf8_len == 0) + utf8_type = (c >= 0xF0 && c <= 0xF4) + ? 4 : (c >= 0xE0 && c <= 0xEF) + ? 3 : (c >= 0xC2 && c <= 0xDF) + ? 2 : 0; + + if(utf8_type == 0) + return 0; + + icu[utf8_len++] = c; /* count it */ + + if(utf8_type == 2){ + if(utf8_len < 2) + rv = NEED_MORE; + else if(utf8_len == 2){ + rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF) + && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } else if (utf8_type == 3){ + if(utf8_len < 3) + rv = NEED_MORE; + else{ + if(icu[0] == 0xE0) + rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xE1 && icu[0] <= 0xEC) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] == 0xED) + rv = (icu[1] >= 0x80 && icu[1] <= 0x9F) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xE1 && icu[0] <= 0xEC) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } else if (utf8_type == 4){ + if(utf8_len < 4) + rv = NEED_MORE; + else{ + if(icu[0] == 0xF0) + rv = (icu[1] >= 0x90 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] >= 0xF1 && icu[0] <= 0xF3) + rv = (icu[1] >= 0x80 && icu[1] <= 0xBF) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + else if(icu[0] == 0xF4) + rv = (icu[1] >= 0x80 && icu[1] <= 0x8F) + && (icu[2] >= 0x80 && icu[2] <= 0xBF) + && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0; + utf8_len = 0; + } + } + return rv; +} + +int +ical_get_number_value(char *value, int beg_pos, int end_pos) +{ + char c, *err; + int rv; + + c = value[end_pos]; + value[end_pos] = '\0'; + rv = strtoul(value + beg_pos, &err, 10); + if(err != NULL && *err != '\0') return -1; + value[end_pos] = c; + return rv; +} + +void +ical_free_duration(ICAL_DURATION_S **ic_d) +{ + if(ic_d == NULL || *ic_d == NULL) + return; + + if((*ic_d)->next) ical_free_duration(&(*ic_d)->next); + fs_give((void **)ic_d); +} + +/* returns 0 if no error, -1 if some error */ +int +ical_parse_duration(char *value, ICAL_DURATION_S *ic_d) +{ + int i, j, rv = 0; + + if(value == NULL || ic_d == NULL) return -1; + + memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S)); + + if(value[i = 0] == '-'){ + i++; + ic_d->sign = 1; + } else if(value[i] == '+') + i++; + + if(value[i++] == 'P'){ + for(j = i; value[j] != '\0' && value[j] != ','; j++){ + if(!isdigit(value[j])) + switch(value[j]){ + case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'D': ic_d->days = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'H': ic_d->hours = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1); + i = ++j; + break; + case 'T': i = j + 1; + break; + default: rv = -1; + break; + } + } + } + else + rv = -1; + + if(value[j++] == ','){ + ICAL_DURATION_S next; + rv = ical_parse_duration(value+j, &next); + ic_d->next = &next; + } + + return rv; +} + +/* return -1 if any error, 0 if no error */ +int +ical_parse_date(char *value, struct tm *t) +{ + int i; + struct tm Tm; + + if(t == NULL) return -1; + memset((void *)&Tm, 0, sizeof(struct tm)); + + if(value == NULL) return -1; + + /* a simple check for the format of the string */ + for(i = 0; isdigit(value[i]); i++); + if (i != 8 || value[i++] != 'T') return -1; + for(; isdigit(value[i]); i++); + if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0'))) + return -1; + + Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900; + Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1; + Tm.tm_mday = ical_get_number_value(value, 6, 8); + Tm.tm_hour = ical_get_number_value(value, 9, 11); + Tm.tm_min = ical_get_number_value(value, 11, 13); + Tm.tm_sec = ical_get_number_value(value, 13, 15); + *t = Tm; + + return (t->tm_mon > 11 || t->tm_mon < 0 + || t->tm_mday > 31 || t->tm_mday < 0 + || t->tm_hour > 23 || t->tm_hour < 0 + || t->tm_min > 59 || t->tm_min < 0 + || t->tm_sec > 60 || t->tm_sec < 0) + ? - 1 : 0; +} + +void +ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz) +{ + int date_form; /* date forms from section 3.3.4 in RFC 5545 */ + ICAL_PARAMETER_S *param; + char *tz = NULL; + struct tm ic_date; + time_t t; + + if(icl == NULL) return; + + for(param = icl->param; param != NULL; param = param->next) + if(!strucmp(param->name, "TZID")) + tz = param->value; + + if(tz != NULL) + date_form = 3; /* local time with timezone */ + else if(icl->value[strlen(icl->value)-1] == 'Z') + date_form = 2; /* utc time */ + else date_form = 1; /* local time */ + + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */ + + switch(date_form){ + case 1: break; + case 2: ical_adjust_date(&ic_date, vtz); + break; + case 3: break; + default: alpine_panic ("Impossible date_form"); + } +} + +ICAL_TZPROP_S * +ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz) +{ + struct tm standard, daylight; + + +} + + + +/* adjusts time to given time zone */ +void +ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz) +{ + char *tzname = NULL; + ICLINE_S *icl; + ICAL_TZPROP_S *cur_std_day; + + if(vtz == NULL) + return; + + if(vtz->prop){ + if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL) + tzname = cpystr(icl->value); + } + +//+++ cur_std_day = ical_std_or_daylight(date, vtz); +} + +void +ical_set_date_vevent(void *veventv, void *vtzv) +{ + VEVENT_S *vevent = (VEVENT_S *) veventv; + VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv; + + if(vevent){ + ical_set_date(vevent->prop[EvDtstamp], vtz); + ical_set_date(vevent->prop[EvDtstart], vtz); + ical_set_date(vevent->prop[EvDtend], vtz); + } +} + +#define LEAP_YEAR(X) ((((X) % 4 == 0) \ + && (((X) % 100 != 0) || ((X) % 400 == 0))) \ + || (X) == 1700) + +#define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1)) + +/* given a year, after day_zero, return the day + * of the week of the first of january of that year. On error, + * return a negative number. + * Assumption: day_zero is the date of january 1, of some year. + */ +int +ical_january_first(int year) +{ + int i, january_first; + + if(year < day_zero.tm_year) return -1; /* not supported */ + + year += 1900; + january_first = day_zero.tm_wday; + for(i = 1900 + day_zero.tm_year + 1; i <= year; i++) + january_first += CAL_OFFSET(i-1); + + return january_first % 7; +} + +/* given a month, week day, and year, return all days of the month + * that have that day as the week day. For example, return all + * sundays in november 2012. + */ +int * +ical_day_from_week(int month, Weekday day, int year) +{ + int *rv = NULL; + int fday, nday; + Weekday wday; + int i; + + fday = ical_first_of_month(month, year); + year += 1900; /* restore year */ + if(year == 1752 && month == 8){ + fday = 9; + } else { + for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++) + ; + rv = fs_get(6*sizeof(int)); + memset((void *)&rv, 0, 6*sizeof(int)); + for(i = 0; nday <= month_len[month]; i++){ + rv[i] = nday; + nday += 7; + } + if(LEAP_YEAR(year) && month == 1 && nday == 29) + rv[i] = nday; + } + + return rv; +} + + +/* given a month and a year, return the weekday of the first of the + * month in that year. + * return value: on error -1, otherwise the day of the week. + */ +int +ical_first_of_month(int month, int year) +{ + int i, d; + + if((d = ical_january_first(year)) < 0) + return -1; + + year += 1900; + for(i = 0; i < month; i++) + d += month_len[i]; + + if(LEAP_YEAR(year) && month >= 2) + d += 1; + + if(year == 1752 && month >= 9) + d -= 11; + + return d % 7; +} + +/* given a day, month and year, return the weekday of that day + * return value: on error -1, otherwise the day of the week. + */ +int ical_day_of_week(struct tm date) +{ + int d; + + if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0) + return -1; + + d += date.tm_mday - 1; + + if(date.tm_year + 1900 == 1752){ + if(date.tm_mday > 2 && date.tm_mday < 14) + return -1; + if(date.tm_mday >= 14) + d -= 11; + } + return d % 7; +} + + +/* given an initial date dtstart, and a recurring rule, rrule, + * adjust the date to the first date on the same year, when + * the rule actually starts + */ +struct tm +adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule) +{ + struct tm t; + + memset((void *) &t, 0, sizeof(struct tm)); + t.tm_year = dtstart->tm_year; /* same year */ + if(rrule->prop[RRFreq]){ + } + if(rrule->prop[RRCount]){ + } + else if(rrule->prop[RRInterval]){ + } + if(rrule->prop[RRBysecond]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_sec = seco->value; + else if (seco->value < t.tm_sec) + t.tm_sec = seco->value; + } + if (rrule->prop[RRByminute]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_min = seco->value; + else if (seco->value < t.tm_sec) + t.tm_min = seco->value; + } + if (rrule->prop[RRByhour]){ + BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco; + for (seco = sec; seco != NULL; seco = seco->next) + if(seco == sec) t.tm_hour = seco->value; + else if (seco->value < t.tm_sec) + t.tm_hour = seco->value; + } + if (rrule->prop[RRByday]){ + } + if (rrule->prop[RRByweekno]){ + } + if (rrule->prop[RRBymonthday]){ + } + if (rrule->prop[RRByyearday]){ + } + if (rrule->prop[RRByweekno]){ + } + if (rrule->prop[RRBymonth]){ + BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo; + for (mo = m; mo != NULL; mo = mo->next) + if(mo == m) t.tm_mon = mo->value - 1; + else if (mo->value - 1 < t.tm_mon) + t.tm_mon = mo->value - 1; + } + if (rrule->prop[RRBysetpos]){ + } + if (rrule->prop[RRWkst]){ + } +} + +void +ical_initialize(void) +{ + ical_buf_len = 1024; + ical_buf = fs_get(ical_buf_len+1); + + memset((void *) &day_zero, 0, sizeof(struct tm)); + day_zero.tm_year = 1601 - 1900; + day_zero.tm_mday = 1; + day_zero.tm_wday = 4; +} + +/* we create a summary of the event, and pass that back as + an ical parameter + */ +VEVENT_SUMMARY_S * +ical_vevent_summary(VCALENDAR_S *vcal) +{ + VEVENT_SUMMARY_S *rv; + ICLINE_S *method; + VEVENT_S *vevent; + GEN_ICLINE_S *gicl; + ICLINE_S *icl; + + if(vcal == NULL) return NULL; + + method = vcal->method; + vevent = (VEVENT_S *) vcal->comp[VEvent]; + + if(vevent == NULL || vevent->prop == NULL) + return NULL; + + rv = fs_get(sizeof(VEVENT_SUMMARY_S)); + memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S)); + + if(method != NULL && !strucmp(method->value, "CANCEL")) + rv->cancel++; + + if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL) + rv->priority = atoi(icl->value); + + if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL) + rv->summary = cpystr(icl->value ? icl->value : _("No Summary")); + + if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL) + rv->class = cpystr(icl->value ? icl->value : _("PUBLIC")); + else + rv->class = cpystr(_("PUBLIC")); + + if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){ + char *cn, *sender, *address; + ICAL_PARAMETER_S *param; + + cn = sender = address = NULL; + for(param = icl->param; param != NULL; param = param->next) + if(!strucmp(param->name, "CN")) + cn = param->value; + else if(!strucmp(param->name, "SENT-BY")) + sender = param->value; + + if(sender != NULL){ + if(!struncmp(sender, "MAILTO:", 7)) + sender += 7; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender); + rv->sender = cpystr(tmp_20k_buf); + } + + if((address = icl->value) != NULL){ + if(!struncmp(address, "MAILTO:", 7)) + address += 7; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>", + cn ? cn : "", cn ? " " : "", + address ? address : _("Unknown address")); + rv->organizer = cpystr(tmp_20k_buf); + } + } /* end of if(organizer) */ + + if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL) + rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed")); + + if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){ + struct tm ic_date; + char tmp[200]; + memset((void *)&ic_date, 0, sizeof(struct tm)); + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + rv->evstart = cpystr(icl->value ? tmp : _("Unknown Start Date")); + } /* end of if dtstart */ + + if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){ + int i, done = 0; + ICAL_DURATION_S ic_d, icd2; + if(ical_parse_duration(icl->value, &ic_d) == 0){ + char tmp[MAILTMPLEN+1]; + + for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++); + rv->duration = fs_get((i+1)*sizeof(char *)); + i = 0; + + do { + tmp[0] = '\0'; + + if(ic_d.weeks > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks")); + if(ic_d.days > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days")); + if(ic_d.hours > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours")); + if(ic_d.minutes > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes")); + if(ic_d.seconds > 0) + utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp), + "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds")); + + tmp[MAILTMPLEN] = '\0'; + rv->duration[i++] = cpystr(tmp); + + if(ic_d.next != NULL) + ic_d = *ic_d.next; + else + done++; + } while (done == 0); + rv->duration[i] = NULL; + } + } /* end of DURATION */ + else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){ + struct tm ic_date; + char tmp[200]; + + memset((void *)&ic_date, 0, sizeof(struct tm)); + ical_parse_date(icl->value, &ic_date); + ic_date.tm_wday = ical_day_of_week(ic_date); + our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + rv->evend = cpystr(icl->value ? tmp : _("Unknown End Date")); + } /* end of if dtend */ + + if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){ + int nattendees, i; + + for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++); + rv->attendee = fs_get((nattendees+1)*sizeof(char *)); + + gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]; + for(i = 0; gicl != NULL; gicl = gicl->next, i++){ + char *role, *partstat, *rsvp; + char *cn, *mailto; + ICAL_PARAMETER_S *param; + + icl = gicl->cline; + role = partstat = rsvp = cn = mailto = NULL; + for(param = icl->param; param != NULL; param = param->next){ + if(!strucmp(param->name, "ROLE")){ + if(!strucmp(param->value, "REQ-PARTICIPANT")) + role = _("[Required]"); + else if(!strucmp(param->value, "OPT-PARTICIPANT")) + role = _("[Optional]"); + else if(!strucmp(param->value, "NON-PARTICIPANT")) + role = _("[Informed]"); + else if(!strucmp(param->value, "CHAIR")) + role = _("[ Chair ]"); + else + role = param->value; + } + else if(!strucmp(param->name, "PARTSTAT")){ + if(!strucmp(param->value, "NEEDS-ACTION")) + partstat = _("[Need-Reply]"); + else if(!strucmp(param->value, "ACCEPTED")) + partstat = _("[ Accepted ]"); + else if(!strucmp(param->value, "DECLINED")) + partstat = _("[ Declined ]"); + else if(!strucmp(param->value, "TENTATIVE")) + partstat = _("[ Tentative]"); + else if(!strucmp(param->value, "DELEGATED")) + partstat = _("[ Delegated]"); + else + partstat = param->value; + } + else if(!strucmp(param->name, "RSVP")) + rsvp = param->value; + else if(!strucmp(param->name, "CN")) + cn = param->value; + } + if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:"))) + mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */ + if(!strucmp(cn, mailto)) + cn = ""; + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>", + role && *role ? role : "", + role && *role ? " " : "", + partstat ? partstat : _("[Unknown Reply]"), + partstat ? " " : "", + cn && *cn ? cn : "", + cn && *cn ? " " : "", + mailto ? mailto : _("Unknown address")); + rv->attendee[i] = cpystr(tmp_20k_buf); + } + rv->attendee[i] = NULL; + } /* end of ATTENDEES */ + + if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){ + char *s, *t, *u, *v; + int i, escaped; + + if(icl->value == NULL){ + free_vevent_summary(&rv); + return NULL; + } + + v = cpystr(icl->value); /* process a copy of icl->value */ + + for(i = 1, escaped = 0, s = v; s && *s; s++){ + if(*s == '\\'){ escaped = 1; continue; } + if(escaped){ + if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){ + free_vevent_summary(&rv); + fs_give((void **)&v); + return NULL; + } + escaped = 0; + continue; + } + if(*s == ',') i++; /* a non-scaped comma is a new value for text */ + } + + rv->description = fs_get((i+1)*sizeof(char *)); + i = 0; + for (s = t = u = v, escaped = 0; *t != '\0'; t++){ + if(*t == '\\'){ escaped = 1; continue; } + if(escaped){ + switch(*t){ + case '\\': + case ',': + case ';': + *u++ = *t; + break; + case 'n': + case 'N': + *u++ = '\n'; + break; + default: free_vevent_summary(&rv); + fs_give((void **)&v); + return NULL; + } + escaped = 0; + continue; + } + if(*t == ','){ + *u = '\0'; + rv->description[i++] = cpystr(s); + s = u = t+1; + } else + *u++ = *t; + } + *u = '\0'; + rv->description[i++] = cpystr(s); + rv->description[i] = NULL; + fs_give((void **)&v); + } /* end of if(description) */ + + return rv; +} + +void +free_vevent_summary(VEVENT_SUMMARY_S **vesy) +{ + int i; + if(vesy == NULL || *vesy == NULL) return; + + if((*vesy)->class) fs_give((void **)&(*vesy)->class); + if((*vesy)->summary) fs_give((void **)&(*vesy)->summary); + if((*vesy)->sender) fs_give((void **)&(*vesy)->sender); + if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer); + if((*vesy)->location) fs_give((void **)&(*vesy)->location); + if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart); + if((*vesy)->evend) fs_give((void **)&(*vesy)->evend); + if((*vesy)->duration){ + for(i = 0; (*vesy)->duration[i] != NULL; i++) + fs_give((void **) &(*vesy)->duration[i]); + fs_give((void **) (*vesy)->duration); + } + if((*vesy)->attendee){ + for(i = 0; (*vesy)->attendee[i] != NULL; i++) + fs_give((void **) &(*vesy)->attendee[i]); + fs_give((void **) (*vesy)->attendee); + } + if((*vesy)->description){ + for(i = 0; (*vesy)->description[i] != NULL; i++) + fs_give((void **) &(*vesy)->description[i]); + fs_give((void **) (*vesy)->description); + } + fs_give((void **) vesy); +} + +#ifdef STANDALONE +void ical_print_line(ICLINE_S icl) +{ + ICAL_PARAMETER_S *p; + printf("token=%s, ", icl.token); + for(p = icl.param; p != NULL; p = p->next){ + if(p->name) printf("param->name=%s, ", p->name); + if(p->value) printf("param->value=%s, ", p->value); + } + printf("value=%s", icl.value); + printf("%c", '\n'); +} + +#define CRLFCOUNT 100 + +char *lf2crlf(char *text) +{ + char *rv, *s; + int i, len; + + if(text == NULL || *text == '\0') + return NULL; + + len = strlen(text); + rv = fs_get(len+1); + + for(i = 0, s = text; *s;){ + if(i == len - 1){ + len += CRLFCOUNT; + fs_resize((void *)&rv, len); + } + if(*s == '\n') + rv[i++] = '\r'; + rv[i++] = *s++; + } + rv[i] = '\0'; + fs_give((void **)&text); + text = rv; + + return rv; +} + +int main (int argc, char *argv[]) +{ + char *text; + VCALENDAR_S *vcal; + + if(argc != 2){ + fprintf(stderr, "Only give one argument: File name to read\n"); + _exit(1); + } + + if(readfile(argv[1], &text, NULL) < 0){ + fprintf(stderr, "Can not read %s succesfully\n", argv[1]); + _exit(2); + } + + text = lf2crlf(text); + vcal = ical_parse_text(text); + + ical_free_vcalendar((void **)&vcal); + return 0; +} + + +#endif /* STANDALONE */ diff --git a/pith/ical.h b/pith/ical.h new file mode 100644 index 00000000..9da4516f --- /dev/null +++ b/pith/ical.h @@ -0,0 +1,17 @@ +#ifndef ICAL_INCLUDED +#define 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 *); +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 ICAL_INCLUDED */ diff --git a/pith/icaltype.h b/pith/icaltype.h new file mode 100644 index 00000000..0474464f --- /dev/null +++ b/pith/icaltype.h @@ -0,0 +1,476 @@ +#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 */ +} 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..450d3f85 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,294 @@ 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){ + 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(F_ON(F_VIEW_SEL_ATTACH, ps_global) && 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(F_ON(F_VIEW_SEL_ATTACH, ps_global) && 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); + gf_puts(NEWLINE, pc); +} + +int +format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc) +{ + char *b64text, *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->sparep == NULL){ + b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); + b64text[callen] = '\0'; /* chop off cookie */ + caltext = rfc822_base64(b64text, strlen(b64text), &callen); + vcal = ical_parse_text(caltext); + b->sparep = create_body_sparep(iCalType, (void *) vcal); + } + 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 +629,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 +719,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 +735,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 +903,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 +958,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/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); -- cgit v1.2.3-70-g09d2 From 1c6f750f40551447f0ea214c993845238fd729cd Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Thu, 14 Dec 2017 17:25:10 -0700 Subject: temporary commit while I update master --- alpine/ical.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ alpine/ical.h | 6 ++ alpine/mailview.c | 9 ++ pith/ical.h | 6 +- pith/mailview.c | 2 +- 5 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 alpine/ical.c create mode 100644 alpine/ical.h (limited to 'pith') diff --git a/alpine/ical.c b/alpine/ical.c new file mode 100644 index 00000000..2d7547c2 --- /dev/null +++ b/alpine/ical.c @@ -0,0 +1,252 @@ +/* + * ======================================================================== + * Copyright 2013-2017 Eduardo Chappa + * Copyright 2008 University of Washington + * + * 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 "headers.h" + +#include "titlebar.h" + +static struct headerentry headents_templ[]={ + /* TRANSLATORS: these are the headings for setting up a collection of + folders, PATH is a filesystem path, VIEW is sort of a technical + term that can be used to restrict the View to fewer folders */ + {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick, 12, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, + {"Server : ", N_("Server"), h_composer_cntxt_server, 12, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, + {"Path : ", N_("Path"), h_composer_cntxt_path, 12, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, + {"View : ", N_("View"), h_composer_cntxt_view, 12, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, + {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE} +}; +#define AC_NICK 0 +#define AC_SERV 1 +#define AC_PATH 2 +#define AC_VIEW 3 + +void +ical_compose_reply(char *url) +{ + + +//char * +//context_edit_screen(struct pine *ps, char *func, char *def_nick, +// char *def_serv, char *def_path, char *def_view) +//{ + int editor_result, i, j; + char servpart[MAILTMPLEN], new_cntxt[MAILTMPLEN]; + char pathpart[MAILTMPLEN], allbutnick[MAILTMPLEN]; + char tmp[MAILTMPLEN], *nick, *serv, *path, *view, + *return_cntxt = NULL, *val, *p; + char nickpmt[100], servpmt[100], pathpmt[100], viewpmt[100]; + int indent; + PICO pbf; + STORE_S *msgso; + NETMBX mb; + + standard_picobuf_setup(&pbf); + pbf.pine_flags |= P_NOBODY; + pbf.exittest = exit_collection_add; + pbf.canceltest = (func && !strucmp(func, "EDIT")) ? cancel_collection_edit + : cancel_collection_add; + pbf.pine_anchor = set_titlebar(_("CALENDAR EVENT"), + ps_global->mail_stream, + ps_global->context_current, + ps_global->cur_folder,ps_global->msgmap, + 0, FolderName, 0, 0, NULL); + + /* An informational message */ + if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ + pbf.msgtext = (void *) so_text(msgso); + so_puts(msgso, + _("\n Fill in the fields above to add a Folder Collection to your")); + so_puts(msgso, + _("\n COLLECTION LIST screen.")); + so_puts(msgso, + _("\n Use the \"^G\" command to get help specific to each item, and")); + so_puts(msgso, + _("\n use \"^X\" when finished.")); + } + + + pbf.headents = (struct headerentry *)fs_get((sizeof(headents_templ) + /sizeof(struct headerentry)) + * sizeof(struct headerentry)); + memset((void *) pbf.headents, 0, + (sizeof(headents_templ)/sizeof(struct headerentry)) + * sizeof(struct headerentry)); + + for(i = 0; headents_templ[i].prompt; i++) + pbf.headents[i] = headents_templ[i]; + + indent = utf8_width(_("Nickname")) + 2; + + nick = cpystr(def_nick ? def_nick : ""); + pbf.headents[AC_NICK].realaddr = &nick; + pbf.headents[AC_NICK].maxlen = strlen(nick); + utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", indent, indent, _("Nickname")); + pbf.headents[AC_NICK].prompt = nickpmt; + pbf.headents[AC_NICK].prwid = indent+2; + + serv = cpystr(def_serv ? def_serv : ""); + pbf.headents[AC_SERV].realaddr = &serv; + pbf.headents[AC_SERV].maxlen = strlen(serv); + utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", indent, indent, _("Server")); + pbf.headents[AC_SERV].prompt = servpmt; + pbf.headents[AC_SERV].prwid = indent+2; + + path = cpystr(def_path ? def_path : ""); + pbf.headents[AC_PATH].realaddr = &path; + pbf.headents[AC_PATH].maxlen = strlen(path); + pbf.headents[AC_PATH].bldr_private = (void *) 0; + utf8_snprintf(pathpmt, sizeof(pathpmt), "%-*.*w: ", indent, indent, _("Path")); + pbf.headents[AC_PATH].prompt = pathpmt; + pbf.headents[AC_PATH].prwid = indent+2; + + view = cpystr(def_view ? def_view : ""); + pbf.headents[AC_VIEW].realaddr = &view; + pbf.headents[AC_VIEW].maxlen = strlen(view); + utf8_snprintf(viewpmt, sizeof(viewpmt), "%-*.*w: ", indent, indent, _("View")); + pbf.headents[AC_VIEW].prompt = viewpmt; + pbf.headents[AC_VIEW].prwid = indent+2; + + /* + * If this is new context, setup to query IMAP server + * for location of personal namespace. + */ + if(!(def_nick || def_serv || def_path || def_view)){ + pbf.headents[AC_SERV].builder = build_namespace; + pbf.headents[AC_SERV].affected_entry = &pbf.headents[AC_PATH]; + pbf.headents[AC_SERV].bldr_private = (void *) 0; + } + + /* pass to pico and let user change them */ + editor_result = pico(&pbf); + + if(editor_result & COMP_GOTHUP){ + hup_signal(); + } + else{ + fix_windsize(ps_global); + init_signals(); + } + + if(editor_result & COMP_CANCEL){ + cmd_cancelled(func); + } + else if(editor_result & COMP_EXIT){ + servpart[0] = pathpart[0] = new_cntxt[0] = allbutnick[0] = '\0'; + if(serv && *serv){ + if(serv[0] == '{' && serv[strlen(serv)-1] == '}'){ + strncpy(servpart, serv, sizeof(servpart)-1); + servpart[sizeof(servpart)-1] = '\0'; + } + else + snprintf(servpart, sizeof(servpart), "{%s}", serv); + + if(mail_valid_net_parse(servpart, &mb)){ + if(!struncmp(mb.service, "nntp", 4) + && (!path || strncmp(path, "#news.", 6))) + strncat(servpart, "#news.", sizeof(servpart)-1-strlen(servpart)); + } + else + alpine_panic("Unexpected invalid server"); + } + else + servpart[0] = '\0'; + + servpart[sizeof(servpart)-1] = '\0'; + + new_cntxt[0] = '\0'; + if(nick && *nick){ + val = quote_if_needed(nick); + if(val){ + strncpy(new_cntxt, val, sizeof(new_cntxt)-2); + new_cntxt[sizeof(new_cntxt)-2] = '\0'; + if(val != nick) + fs_give((void **)&val); + + strncat(new_cntxt, " ", sizeof(new_cntxt)-strlen(new_cntxt)-1); + new_cntxt[sizeof(new_cntxt)-1] = '\0'; + } + } + + p = allbutnick; + sstrncpy(&p, servpart, sizeof(allbutnick)-1-(p-allbutnick)); + allbutnick[sizeof(allbutnick)-1] = '\0'; + + if(path){ + val = quote_brackets_if_needed(path); + if(val){ + strncpy(pathpart, val, sizeof(pathpart)-1); + pathpart[sizeof(pathpart)-1] = '\0'; + if(val != path) + fs_give((void **)&val); + } + + if(pbf.headents[AC_PATH].bldr_private != (void *) 0){ + strncat(pathpart, (char *) pbf.headents[AC_PATH].bldr_private, + sizeof(pathpart)-strlen(pathpart)-1); + pathpart[sizeof(pathpart)-1] = '\0'; + } + } + + sstrncpy(&p, pathpart, sizeof(allbutnick)-1-(p-allbutnick)); + allbutnick[sizeof(allbutnick)-1] = '\0'; + + if(view[0] != '[' && sizeof(allbutnick)-1-(p-allbutnick) > 0){ + *p++ = '['; + *p = '\0'; + } + + sstrncpy(&p, view, sizeof(allbutnick)-1-(p-allbutnick)); + allbutnick[sizeof(allbutnick)-1] = '\0'; + if((j=strlen(view)) < 2 || (view[j-1] != ']' && + sizeof(allbutnick)-1-(p-allbutnick) > 0)){ + *p++ = ']'; + *p = '\0'; + } + + val = quote_if_needed(allbutnick); + if(val){ + strncat(new_cntxt, val, sizeof(new_cntxt)-1-strlen(new_cntxt)); + new_cntxt[sizeof(new_cntxt)-1] = '\0'; + + if(val != allbutnick) + fs_give((void **)&val); + } + + return_cntxt = cpystr(new_cntxt); + } + + for(i = 0; headents_templ[i].prompt; i++) + fs_give((void **) pbf.headents[i].realaddr); + + if(pbf.headents[AC_PATH].bldr_private != (void *) 0) + fs_give(&pbf.headents[AC_PATH].bldr_private); + + fs_give((void **) &pbf.headents); + + standard_picobuf_teardown(&pbf); + + if(msgso) + so_give(&msgso); + + return(return_cntxt); +} + diff --git a/alpine/ical.h b/alpine/ical.h new file mode 100644 index 00000000..419209e4 --- /dev/null +++ b/alpine/ical.h @@ -0,0 +1,6 @@ +#ifndef ALPINE_ICAL_INCLUDED +#define ALPINE_ICAL_INCLUDED + +void ical_compose_reply(char *); + +#endif /* ALPINE_ICAL_INCLUDED */ diff --git a/alpine/mailview.c b/alpine/mailview.c index e60c9558..c22f40a4 100644 --- a/alpine/mailview.c +++ b/alpine/mailview.c @@ -174,6 +174,7 @@ int url_launch(HANDLE_S *); int url_launch_too_long(int); char *url_external_handler(HANDLE_S *, int); void url_mailto_addr(ADDRESS **, char *); +int ical_send_reply(char *); int url_local_phone_home(char *); int url_local_imap(char *); int url_local_nntp(char *); @@ -1605,6 +1606,7 @@ url_local_handler(char *s) {"ldap://", 7, url_local_ldap}, #endif {"news:", 5, url_local_news}, + {"x-alpine-ical:", 14, ical_send_reply}, {"x-alpine-phone-home:", 20, url_local_phone_home}, {"x-alpine-gripe:", 15, gripe_gripe_to}, {"x-alpine-help:", 14, url_local_helper}, @@ -2162,6 +2164,13 @@ url_local_fragment(char *fragment) return(1); } +int +ical_send_reply(char *url) +{ + ical_compose_reply(url + strlen("x-alpine-ical:")); + return 2; +} + int url_local_phone_home(char *URL) diff --git a/pith/ical.h b/pith/ical.h index 9da4516f..04d0cc08 100644 --- a/pith/ical.h +++ b/pith/ical.h @@ -1,5 +1,5 @@ -#ifndef ICAL_INCLUDED -#define ICAL_INCLUDED +#ifndef PITH_ICAL_INCLUDED +#define PITH_ICAL_INCLUDED #include "../pith/icaltype.h" @@ -14,4 +14,4 @@ void free_vevent_summary(VEVENT_SUMMARY_S **); VCALENDAR_S *ical_parse_text(char *); /* this is the entry point */ -#endif /* ifndef ICAL_INCLUDED */ +#endif /* ifndef PITH_ICAL_INCLUDED */ diff --git a/pith/mailview.c b/pith/mailview.c index 450d3f85..1d71e7a0 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -390,7 +390,7 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int return; gf_puts(padding, pc); - utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", _("[More Details]")); + utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details")); if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){ char buf[16], color[64]; -- cgit v1.2.3-70-g09d2 From 29798bd68fc0b892fce0fe49ec8859485e3b531b Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Fri, 22 Dec 2017 22:31:48 -0700 Subject: * Fix parsing of date for DTSTART and DTEND. The parser only recognized DATE-TIME format, now it recognizes DATE format. Reported by Holger Trapp. * Add escape parser to event location (switch "\," to "," etc.) * Start work to support reply of invitations, but then I realize that I need to start work in sending event invitations before I do that, so I will. --- alpine/mailpart.c | 1 + alpine/mailview.c | 2 +- libtool | 2 +- pith/Makefile | 35 +++++++------- pith/ical.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++-------- pith/ical.h | 1 + pith/mailview.c | 16 +++++++ pith/pine.hlp | 2 +- po/Makefile.in | 8 ++-- 9 files changed, 159 insertions(+), 44 deletions(-) (limited to 'pith') diff --git a/alpine/mailpart.c b/alpine/mailpart.c index 49ea053b..b7d2d3e2 100644 --- a/alpine/mailpart.c +++ b/alpine/mailpart.c @@ -2962,6 +2962,7 @@ display_vcalendar_att(long int msgno, ATTACH_S *a, int flags) } if(vesy->location){ + ical_remove_escapes(&vesy->location); utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", _("Location: "), vesy->location); so_puts(in_store, tmp_20k_buf); diff --git a/alpine/mailview.c b/alpine/mailview.c index c22f40a4..a51565ca 100644 --- a/alpine/mailview.c +++ b/alpine/mailview.c @@ -2167,7 +2167,7 @@ url_local_fragment(char *fragment) int ical_send_reply(char *url) { - ical_compose_reply(url + strlen("x-alpine-ical:")); +// ical_compose_reply(url + strlen("x-alpine-ical:")); return 2; } diff --git a/libtool b/libtool index ae869e54..9a48e8fe 100755 --- a/libtool +++ b/libtool @@ -1,7 +1,7 @@ #! /bin/sh # libtool - Provide generalized library-building support services. -# Generated automatically by config.status (alpine) 2.21.99 +# Generated automatically by config.status (alpine) 2.21.9 # Libtool was configured on host linux.suse: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # 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/ical.c b/pith/ical.c index 8364d384..b3fe67e2 100644 --- a/pith/ical.c +++ b/pith/ical.c @@ -231,6 +231,57 @@ ICAL_PROP_S alarm_prop[] = { /* Finally, here begins the code. */ +/* 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]; @@ -1602,31 +1653,43 @@ ical_parse_duration(char *value, ICAL_DURATION_S *ic_d) return rv; } -/* return -1 if any error, 0 if no error */ +/* 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; + int i, rv; struct tm Tm; - if(t == NULL) return -1; + rv = -1; + if(t == NULL) return rv; memset((void *)&Tm, 0, sizeof(struct tm)); - if(value == NULL) return -1; + 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++] != 'T') return -1; - for(; isdigit(value[i]); i++); - if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0'))) - return -1; + 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); - 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); + 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 @@ -1634,7 +1697,7 @@ ical_parse_date(char *value, struct tm *t) || t->tm_hour > 23 || t->tm_hour < 0 || t->tm_min > 59 || t->tm_min < 0 || t->tm_sec > 60 || t->tm_sec < 0) - ? - 1 : 0; + ? - 1 : rv; } void @@ -1962,10 +2025,27 @@ ical_vevent_summary(VCALENDAR_S *vcal) 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)); - ical_parse_date(icl->value, &ic_date); - ic_date.tm_wday = ical_day_of_week(ic_date); - our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + 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 */ @@ -2012,11 +2092,27 @@ ical_vevent_summary(VCALENDAR_S *vcal) 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)); - ical_parse_date(icl->value, &ic_date); - ic_date.tm_wday = ical_day_of_week(ic_date); - our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date); + 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 */ @@ -2095,7 +2191,7 @@ ical_vevent_summary(VCALENDAR_S *vcal) v = cpystr(icl->value); /* process a copy of icl->value */ for(i = 1, escaped = 0, s = v; s && *s; s++){ - if(*s == '\\'){ escaped = 1; continue; } + if(*s == '\\' && escaped == 0){ escaped = 1; continue; } if(escaped){ if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){ free_vevent_summary(&rv); @@ -2111,7 +2207,7 @@ ical_vevent_summary(VCALENDAR_S *vcal) rv->description = fs_get((i+1)*sizeof(char *)); i = 0; for (s = t = u = v, escaped = 0; *t != '\0'; t++){ - if(*t == '\\'){ escaped = 1; continue; } + if(*t == '\\' && escaped == 0){ escaped = 1; continue; } if(escaped){ switch(*t){ case '\\': diff --git a/pith/ical.h b/pith/ical.h index 04d0cc08..7ff5865a 100644 --- a/pith/ical.h +++ b/pith/ical.h @@ -9,6 +9,7 @@ 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 **); diff --git a/pith/mailview.c b/pith/mailview.c index 1d71e7a0..dcdd183d 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -312,6 +312,7 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int } /* 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); @@ -483,10 +484,25 @@ format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int w 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){ b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); + if(b64text == NULL){ + gf_puts(_("Error fetching calendar text"), pc); + gf_puts(NEWLINE, pc); + continue; + } b64text[callen] = '\0'; /* chop off cookie */ caltext = rfc822_base64(b64text, strlen(b64text), &callen); + if(caltext == NULL){ + gf_puts(_("Error in calendar base64 encoding"), pc); + gf_puts(NEWLINE, pc); + continue; + } vcal = ical_parse_text(caltext); b->sparep = create_body_sparep(iCalType, (void *) vcal); } diff --git a/pith/pine.hlp b/pith/pine.hlp index 0fa01415..ef58c44d 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 240 2017-12-09 16:20:34 +Alpine Commit 243 2017-12-22 22:31:03 ============= h_news ================= diff --git a/po/Makefile.in b/po/Makefile.in index 9041c567..ab48d37d 100644 --- a/po/Makefile.in +++ b/po/Makefile.in @@ -11,7 +11,7 @@ # Origin: gettext-0.16 PACKAGE = alpine -VERSION = 2.21.99 +VERSION = 2.21.9 PACKAGE_BUGREPORT = chappa@washington.edu SHELL = /bin/sh @@ -34,12 +34,12 @@ INSTALL_DATA = ${INSTALL} -m 644 # We use $(mkdir_p). # In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as # "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, -# ${SHELL} /run/media/echappa/Work/alpine/alpinegit/install-sh does not start with $(SHELL), so we add it. +# ${SHELL} /run/media/chappa/Work/alpine/alpinegit/install-sh does not start with $(SHELL), so we add it. # In automake >= 1.10, $(MKDIR_P) is derived from ${MKDIR_P}, which is defined # either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake # versions, $(mkinstalldirs) and $(install_sh) are unused. -mkinstalldirs = $(SHELL) ${SHELL} /run/media/echappa/Work/alpine/alpinegit/install-sh -d -install_sh = $(SHELL) ${SHELL} /run/media/echappa/Work/alpine/alpinegit/install-sh +mkinstalldirs = $(SHELL) ${SHELL} /run/media/chappa/Work/alpine/alpinegit/install-sh -d +install_sh = $(SHELL) ${SHELL} /run/media/chappa/Work/alpine/alpinegit/install-sh MKDIR_P = /usr/bin/mkdir -p mkdir_p = $(MKDIR_P) -- cgit v1.2.3-70-g09d2 From 5cb80f3539fd43b785638b676c7ba242bda2f75d Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 24 Dec 2017 19:13:39 -0700 Subject: * If the calendar invitation is part of the body of a message instead of an attachment, Alpine would crash. Reported by Stefan Mueller. * In the previous situation, if a DESCRIPTION is encoded in quoted printable, it needs to be decoded twice (once as part of the calendar onvitation and another as part of the DESCRIPTION text, so when decoding we need to decode quoted-printable, then remove escapes characters, then decode quoted-printable and transform \r\n to \n. Whew!) --- pith/ical.c | 21 ++++++++++++++++++--- pith/icaltype.h | 1 + pith/mailview.c | 40 +++++++++++++++++++++++++++------------- pith/pine.hlp | 2 +- 4 files changed, 47 insertions(+), 17 deletions(-) (limited to 'pith') diff --git a/pith/ical.c b/pith/ical.c index b3fe67e2..75212514 100644 --- a/pith/ical.c +++ b/pith/ical.c @@ -37,6 +37,7 @@ 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 **); @@ -231,6 +232,19 @@ ICAL_PROP_S alarm_prop[] = { /* 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); + strncpy(text, t, strlen(t)); + } + return text; +} + + /* Return code: 0 - if no errors -1 - if an error occured @@ -1965,6 +1979,7 @@ ical_vevent_summary(VCALENDAR_S *vcal) VEVENT_S *vevent; GEN_ICLINE_S *gicl; ICLINE_S *icl; + char *k; if(vcal == NULL) return NULL; @@ -2170,7 +2185,7 @@ ical_vevent_summary(VCALENDAR_S *vcal) role && *role ? role : "", role && *role ? " " : "", partstat ? partstat : _("[Unknown Reply]"), - partstat ? " " : "", + " ", cn && *cn ? cn : "", cn && *cn ? " " : "", mailto ? mailto : _("Unknown address")); @@ -2228,13 +2243,13 @@ ical_vevent_summary(VCALENDAR_S *vcal) } if(*t == ','){ *u = '\0'; - rv->description[i++] = cpystr(s); + rv->description[i++] = cpystr(ical_decode(s, vcal->encoding)); s = u = t+1; } else *u++ = *t; } *u = '\0'; - rv->description[i++] = cpystr(s); + rv->description[i++] = cpystr(ical_decode(s, vcal->encoding)); rv->description[i] = NULL; fs_give((void **)&v); } /* end of if(description) */ diff --git a/pith/icaltype.h b/pith/icaltype.h index 0474464f..8060a412 100644 --- a/pith/icaltype.h +++ b/pith/icaltype.h @@ -248,6 +248,7 @@ typedef struct vcalendar_s { 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; diff --git a/pith/mailview.c b/pith/mailview.c index dcdd183d..984b560f 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -464,13 +464,13 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "", repeat_char(dwid, '-')); gf_puts(tmp_20k_buf, pc); - gf_puts(NEWLINE, pc); +// gf_puts(NEWLINE, pc); } int format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc) { - char *b64text, *caltext; + char *rawtext, *caltext; unsigned long callen; VCALENDAR_S *vcal = NULL; ATTACH_S *a; @@ -490,20 +490,34 @@ format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int w continue; } if(b->sparep == NULL){ - b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); - if(b64text == NULL){ - gf_puts(_("Error fetching calendar text"), pc); - gf_puts(NEWLINE, pc); - continue; + rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); + if(rawtext == NULL){ + gf_puts(_("Error fetching calendar text"), pc); + gf_puts(NEWLINE, pc); + continue; } - b64text[callen] = '\0'; /* chop off cookie */ - caltext = rfc822_base64(b64text, strlen(b64text), &callen); - if(caltext == NULL){ - gf_puts(_("Error in calendar base64 encoding"), 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; } vcal = ical_parse_text(caltext); + if(vcal != NULL) vcal->encoding = b->encoding; b->sparep = create_body_sparep(iCalType, (void *) vcal); } else if(get_body_sparep_type(b->sparep) == iCalType) diff --git a/pith/pine.hlp b/pith/pine.hlp index ef58c44d..79cb3c9f 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 243 2017-12-22 22:31:03 +Alpine Commit 244 2017-12-24 19:13:32 ============= h_news ================= -- cgit v1.2.3-70-g09d2 From 965befd7936f8a788b950d650b76f34f606fc1e1 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 24 Dec 2017 21:54:31 -0700 Subject: * A few fixes to ical.c and mailview.c to avoid memory leaks, and add support for encodings other than quoted-printable and base64. --- pith/ical.c | 5 ++++- pith/mailview.c | 11 ++++++++--- pith/pine.hlp | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'pith') diff --git a/pith/ical.c b/pith/ical.c index 75212514..64731a96 100644 --- a/pith/ical.c +++ b/pith/ical.c @@ -239,7 +239,10 @@ ical_decode(char *text, unsigned short encoding) unsigned long callen; if(encoding == ENCQUOTEDPRINTABLE){ t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen); - strncpy(text, t, strlen(t)); + if(t != NULL){ + strncpy(text, t, strlen(t)); + fs_give((void **) &t); + } } return text; } diff --git a/pith/mailview.c b/pith/mailview.c index 984b560f..1d92bd91 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -515,10 +515,15 @@ format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int w 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); } - vcal = ical_parse_text(caltext); - if(vcal != NULL) vcal->encoding = b->encoding; - b->sparep = create_body_sparep(iCalType, (void *) vcal); } else if(get_body_sparep_type(b->sparep) == iCalType) vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep); diff --git a/pith/pine.hlp b/pith/pine.hlp index 79cb3c9f..2fa13844 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 244 2017-12-24 19:13:32 +Alpine Commit 245 2017-12-24 21:54:15 ============= h_news ================= -- cgit v1.2.3-70-g09d2 From f313219416ff6d95f2b66120365516f4367ca8d5 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Mon, 8 Jan 2018 23:34:41 -0700 Subject: * Make sure handles to see details of events are visible regardless of if the feature for activating handles in attachments is enabled or not. --- pith/mailview.c | 5 ++--- pith/pine.hlp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'pith') diff --git a/pith/mailview.c b/pith/mailview.c index 1d92bd91..9e8e8de7 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -393,7 +393,7 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int gf_puts(padding, pc); utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details")); - if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){ + if(handlesp){ char buf[16], color[64]; int l; HANDLE_S *h; @@ -424,7 +424,7 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int if(!format_env_puts(tmp_20k_buf, pc)) return; - if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){ + if(handlesp){ if(lastc){ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF))) @@ -464,7 +464,6 @@ format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "", repeat_char(dwid, '-')); gf_puts(tmp_20k_buf, pc); -// gf_puts(NEWLINE, pc); } int diff --git a/pith/pine.hlp b/pith/pine.hlp index 2fa13844..845f6214 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 2017-12-24 21:54:15 +Alpine Commit 246 2018-01-08 23:34:22 ============= h_news ================= -- cgit v1.2.3-70-g09d2 From 2fb79bff5c3ca10c65d0a426d1d4da4e1e3247c6 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Fri, 26 Jan 2018 18:08:48 -0700 Subject: * If a calendar entry has an entry that is not encoded in UTF-8, Alpine fails to parse that specific field, and may assign a value that uses unallocated memory, making Alpine crash when quitting and freeing that memory. Reported by Stefan Mueller. --- pith/ical.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'pith') diff --git a/pith/ical.c b/pith/ical.c index 64731a96..d2c8e2ba 100644 --- a/pith/ical.c +++ b/pith/ical.c @@ -573,6 +573,12 @@ char *ical_get_value(char **line) *s = '\r'; *line = s+2; } + else{ + t = NULL; + s = strchr(*line, '\r'); + if(s != NULL) + *line = s + 2; + } return t; } -- cgit v1.2.3-70-g09d2 From f07b51ee1d9b95c4936e30c204f87a1d396a0f87 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Wed, 7 Feb 2018 14:54:49 -0700 Subject: * Change in type of icu[6] variable in pith/ical.c from char to unsigned char to handle UTF-8. --- pith/ical.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pith') diff --git a/pith/ical.c b/pith/ical.c index d2c8e2ba..bfad8078 100644 --- a/pith/ical.c +++ b/pith/ical.c @@ -1534,7 +1534,7 @@ ical_parse_rrule(RRULE_S **rrulep, char **text) int ical_non_ascii_valid(unsigned char c) { - static char icu[6]; + static unsigned char icu[6]; static int utf8_len = 0; static int utf8_type = 0; int rv; -- cgit v1.2.3-70-g09d2 From e937001007df277faca143d3edce6138ee143faa Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Mon, 12 Feb 2018 20:49:25 -0700 Subject: * Crash when the body of a calendar is empty. Reported by Stefan Mueller. --- pith/mailview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pith') diff --git a/pith/mailview.c b/pith/mailview.c index 9e8e8de7..dc9b29b6 100644 --- a/pith/mailview.c +++ b/pith/mailview.c @@ -490,7 +490,7 @@ format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int w } if(b->sparep == NULL){ rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); - if(rawtext == NULL){ + if(rawtext == NULL || *rawtext == '\0'){ gf_puts(_("Error fetching calendar text"), pc); gf_puts(NEWLINE, pc); continue; -- cgit v1.2.3-70-g09d2