summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2018-02-24 16:53:39 -0700
committerEduardo Chappa <chappa@washington.edu>2018-02-24 16:53:39 -0700
commiteefc696ec87ae287803aa68032649e5a7a9b89d0 (patch)
tree1a0be044bb1a4d542cf3c722e8b6131a92915621
parenta6441d6f5b283ba98ba2524a64cf5704cd47cb0b (diff)
parent4675349e255a189126ab4922c7b95f761e673c64 (diff)
downloadalpine-eefc696ec87ae287803aa68032649e5a7a9b89d0.tar.xz
Merge branch 'ical'
-rw-r--r--alpine/alpine.c5
-rw-r--r--alpine/mailpart.c210
-rw-r--r--alpine/mailview.c9
-rw-r--r--alpine/smime.c9
-rwxr-xr-xlibtool2
-rw-r--r--pith/Makefile35
-rw-r--r--pith/Makefile.am4
-rw-r--r--pith/Makefile.in35
-rw-r--r--pith/body.c70
-rw-r--r--pith/body.h23
-rw-r--r--pith/ical.c2365
-rw-r--r--pith/ical.h18
-rw-r--r--pith/icaltype.h477
-rw-r--r--pith/mailpart.h6
-rw-r--r--pith/mailview.c350
-rw-r--r--pith/mailview.h4
-rw-r--r--pith/mimedesc.c6
-rw-r--r--pith/pine.hlp2
-rw-r--r--pith/smime.c63
-rw-r--r--pith/smime.h12
-rw-r--r--po/Makefile.in8
21 files changed, 3588 insertions, 125 deletions
diff --git a/alpine/alpine.c b/alpine/alpine.c
index a9c9158a..81792f16 100644
--- a/alpine/alpine.c
+++ b/alpine/alpine.c
@@ -24,6 +24,7 @@ static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washingt
#include "../pith/options.h"
#include "../pith/list.h"
#include "../pith/conf.h"
+#include "../pith/body.h"
#include "osdep/debuging.h"
#include "osdep/termout.gen.h"
@@ -331,9 +332,7 @@ main(int argc, char **argv)
mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
-#ifdef SMIME
- mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
-#endif
+ mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_body_sparep);
init_pinerc(pine_state, &init_pinerc_debugging);
diff --git a/alpine/mailpart.c b/alpine/mailpart.c
index be8cdcd2..b7d2d3e2 100644
--- a/alpine/mailpart.c
+++ b/alpine/mailpart.c
@@ -67,7 +67,8 @@ static char rcsid[] = "$Id: mailpart.c 1074 2008-06-04 00:08:43Z hubert@u.washin
#include "../pith/ablookup.h"
#include "../pith/options.h"
#include "../pith/smime.h"
-
+#include "../pith/ical.h"
+#include "../pith/body.h"
/*
* Information used to paint and maintain a line on the attachment
@@ -143,6 +144,7 @@ int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *,
int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
void display_vcard_att(long, ATTACH_S *, int);
+void display_vcalendar_att(long, ATTACH_S *, int);
void display_attach_info(long, ATTACH_S *);
void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
@@ -2045,6 +2047,8 @@ display_attachment(long int msgno, ATTACH_S *a, int flags)
}
else if(MIME_VCARD_A(a))
display_vcard_att(msgno, a, flags);
+ else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
+ display_vcalendar_att(msgno, a, flags);
else if(a->body->type == TYPETEXT){
do{
rv = display_text_att(msgno, a, flags);
@@ -2881,6 +2885,210 @@ display_vcard_att(long int msgno, ATTACH_S *a, int flags)
so_give(&in_store);
}
+void
+display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
+{
+ BODY *b;
+ VCALENDAR_S *vcal = NULL;
+ char *b64text, *caltext;
+ unsigned long callen;
+ STORE_S *in_store, *out_store = NULL;
+ HANDLE_S *handles = NULL;
+ URL_HILITE_S uh;
+ gf_io_t gc, pc;
+ char *errstr = NULL, tmp[MAILTMPLEN], *p;
+ int cmd, i;
+ VEVENT_SUMMARY_S *vesy; /* vevent summary */
+
+ 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);
+
+ vesy = ical_vevent_summary(vcal);
+
+ if(vesy == NULL){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error parsing event"));
+ return;
+ }
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for Calendar"));
+ return;
+ }
+
+ if(vesy->cancel){
+ so_puts(in_store, _("This event was cancelled"));
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->priority){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
+ _("Priority: "), vesy->priority,
+ vesy->priority == 5 ? _("(Normal)")
+ : (vesy->priority < 5 ? _("(High)")
+ : _("(Low)")));
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->summary){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("Summary: "), vesy->summary);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->sender){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("Sender: "), vesy->sender);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->organizer){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("Organizer: "), vesy->organizer);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ 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);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->evstart){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("Start Date: "), vesy->evstart);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->duration){
+ for(i = 0; vesy->duration[i] != NULL; i++){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("Duration: "), vesy->duration[i]);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+ } else if(vesy->evend){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ _("End Date: "), vesy->evend);
+ so_puts(in_store, tmp_20k_buf);
+ so_puts(in_store, "\015\012");
+ }
+
+ if(vesy->description){
+ char c;
+ int j, empty;
+
+ so_puts(in_store, "\015\012");
+
+ for(i = 0; vesy->description[i] != NULL; i++){
+ so_puts(in_store, _("Description: "));
+ /* Check if empty description */
+ empty = 1;
+ for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
+ c = vesy->description[i][j];
+ if(c != '\n' && c != ' ' && c != '\t')
+ empty = 0;
+ }
+ if(empty){
+ so_puts(in_store, _("[ No description provided ]"));
+ so_puts(in_store, "\015\012");
+ }
+ else {
+ for(j =0; vesy->description[i][j] != '\0'; j++){
+ c = vesy->description[i][j];
+ if(c == '\n'){
+ so_puts(in_store, "\015\012");
+ continue;
+ }
+ so_writec(c, in_store);
+ }
+ }
+ so_puts(in_store, "\015\012");
+ }
+ }
+
+ if(vesy->attendee){
+ so_puts(in_store, "\015\012");
+ so_puts(in_store, _("List of Attendees:"));
+ so_puts(in_store, "\015\012");
+ for(i = 0; vesy->attendee[i] != NULL; i++){
+ so_puts(in_store, vesy->attendee[i]);
+ so_puts(in_store, "\015\012");
+ }
+ }
+
+ so_puts(in_store, "\015\012\015\012");
+
+ do{
+ if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ so_seek(in_store, 0L, 0);
+
+ init_handles(&handles);
+ gf_filter_init();
+
+ if(F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,&handles,0)));
+
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
+ ps_global->ttyo->screen_cols,
+ NULL, 0, GFW_HANDLES));
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+
+ errstr = gf_pipe(gc, pc);
+
+ gf_clear_so_readc(in_store);
+
+ if(!errstr){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("This event was tagged as a %s entry by the sender"), vesy->class);
+ errstr = format_editorial(tmp_20k_buf, ps_global->ttyo->screen_cols, 0, NULL, pc);
+ }
+
+ gf_clear_so_writec(out_store);
+
+ if(!errstr)
+ cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
+ CharStar, handles, a, flags | DA_RESIZE);
+
+ free_handles(&handles);
+ so_give(&out_store);
+ }
+ else
+ errstr = _("Error allocating space");
+ }
+ while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
+
+ if(errstr)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't format entry : %s"), errstr);
+
+ so_give(&in_store);
+}
+
/*----------------------------------------------------------------------
Display attachment information
diff --git a/alpine/mailview.c b/alpine/mailview.c
index e60c9558..a51565ca 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/alpine/smime.c b/alpine/smime.c
index 2fe22a36..2982397c 100644
--- a/alpine/smime.c
+++ b/alpine/smime.c
@@ -36,6 +36,7 @@ static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washingto
#include "../pith/list.h"
#include "../pith/mailcmd.h"
#include "../pith/tempfile.h"
+#include "../pith/body.h"
#include "radio.h"
#include "keymenu.h"
#include "mailcmd.h"
@@ -116,8 +117,8 @@ smime_check(BODY *body)
}
if(rv > 0) return rv;
if(body->sparep)
- p7 = get_smime_sparep_type(body->sparep) == P7Type
- ? (PKCS7 *)get_smime_sparep_data(body->sparep)
+ p7 = get_body_sparep_type(body->sparep) == P7Type
+ ? (PKCS7 *)get_body_sparep_data(body->sparep)
: NULL;
if(p7 && (PKCS7_type_is_signed(p7) || PKCS7_type_is_enveloped(p7)))
rv += 1;
@@ -237,8 +238,8 @@ format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc)
format_smime_info(pass, &p->body, msgno, pc);
}
if(body->sparep)
- p7 = get_smime_sparep_type(body->sparep) == P7Type
- ? (PKCS7 *)get_smime_sparep_data(body->sparep)
+ p7 = get_body_sparep_type(body->sparep) == P7Type
+ ? (PKCS7 *)get_body_sparep_data(body->sparep)
: NULL;
if(p7){
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/Makefile.am b/pith/Makefile.am
index 57d8aa65..76a1feeb 100644
--- a/pith/Makefile.am
+++ b/pith/Makefile.am
@@ -21,9 +21,9 @@ BUILT_SOURCES = helptext.h helptext.c
noinst_PROGRAMS = help_h_gen help_c_gen
-libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \
+libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c body.c charset.c \
color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \
- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
+ filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
diff --git a/pith/Makefile.in b/pith/Makefile.in
index 990661c2..e9589b70 100644
--- a/pith/Makefile.in
+++ b/pith/Makefile.in
@@ -121,21 +121,22 @@ libpith_a_AR = $(AR) $(ARFLAGS)
libpith_a_LIBADD =
am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \
addrbook.$(OBJEXT) addrstring.$(OBJEXT) adrbklib.$(OBJEXT) \
- bldaddr.$(OBJEXT) charset.$(OBJEXT) color.$(OBJEXT) \
- conf.$(OBJEXT) context.$(OBJEXT) copyaddr.$(OBJEXT) \
- detoken.$(OBJEXT) detach.$(OBJEXT) editorial.$(OBJEXT) \
- escapes.$(OBJEXT) filter.$(OBJEXT) flag.$(OBJEXT) \
- folder.$(OBJEXT) handle.$(OBJEXT) help.$(OBJEXT) \
- helpindx.$(OBJEXT) hist.$(OBJEXT) icache.$(OBJEXT) \
- imap.$(OBJEXT) init.$(OBJEXT) keyword.$(OBJEXT) ldap.$(OBJEXT) \
- list.$(OBJEXT) mailcap.$(OBJEXT) mailcmd.$(OBJEXT) \
- mailindx.$(OBJEXT) maillist.$(OBJEXT) mailview.$(OBJEXT) \
- margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \
- msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \
- pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \
- remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \
- save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \
- send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
+ bldaddr.$(OBJEXT) body.$(OBJEXT) charset.$(OBJEXT) \
+ color.$(OBJEXT) conf.$(OBJEXT) context.$(OBJEXT) \
+ copyaddr.$(OBJEXT) detoken.$(OBJEXT) detach.$(OBJEXT) \
+ editorial.$(OBJEXT) escapes.$(OBJEXT) filter.$(OBJEXT) \
+ flag.$(OBJEXT) folder.$(OBJEXT) handle.$(OBJEXT) \
+ help.$(OBJEXT) helpindx.$(OBJEXT) hist.$(OBJEXT) \
+ icache.$(OBJEXT) ical.$(OBJEXT) imap.$(OBJEXT) init.$(OBJEXT) \
+ keyword.$(OBJEXT) ldap.$(OBJEXT) list.$(OBJEXT) \
+ mailcap.$(OBJEXT) mailcmd.$(OBJEXT) mailindx.$(OBJEXT) \
+ maillist.$(OBJEXT) mailview.$(OBJEXT) margin.$(OBJEXT) \
+ mimedesc.$(OBJEXT) mimetype.$(OBJEXT) msgno.$(OBJEXT) \
+ newmail.$(OBJEXT) news.$(OBJEXT) pattern.$(OBJEXT) \
+ pipe.$(OBJEXT) readfile.$(OBJEXT) remote.$(OBJEXT) \
+ reply.$(OBJEXT) rfc2231.$(OBJEXT) save.$(OBJEXT) \
+ search.$(OBJEXT) sequence.$(OBJEXT) send.$(OBJEXT) \
+ sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \
strlst.$(OBJEXT) takeaddr.$(OBJEXT) tempfile.$(OBJEXT) \
text.$(OBJEXT) thread.$(OBJEXT) adjtime.$(OBJEXT) \
@@ -431,9 +432,9 @@ top_srcdir = @top_srcdir@
SUBDIRS = osdep charconv
noinst_LIBRARIES = libpith.a
BUILT_SOURCES = helptext.h helptext.c
-libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \
+libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c body.c charset.c \
color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \
- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
+ filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c ical.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
diff --git a/pith/body.c b/pith/body.c
new file mode 100644
index 00000000..7d935810
--- /dev/null
+++ b/pith/body.c
@@ -0,0 +1,70 @@
+/*
+ * ========================================================================
+ * Copyright 2013-2017 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/body.h"
+#include "../pith/smime.h"
+#include "../pith/ical.h"
+
+void *
+create_body_sparep(SpareType stype, void *s)
+{
+ BODY_SPARE_S *rv;
+
+ rv = fs_get(sizeof(BODY_SPARE_S));
+ rv->sptype = stype;
+ rv->data = s;
+ return (void *) rv;
+}
+
+SpareType
+get_body_sparep_type(void *s)
+{
+ return ((BODY_SPARE_S *)s)->sptype;
+}
+
+void *
+get_body_sparep_data(void *s)
+{
+ return ((BODY_SPARE_S *)s)->data;
+}
+
+void
+free_body_sparep(void **sparep)
+{
+ char *s;
+ SIZEDTEXT *st;
+ VCALENDAR_S *vcal;
+
+ if(sparep && *sparep){
+ switch(get_body_sparep_type(*sparep)){
+#ifdef SMIME
+ case P7Type: PKCS7_free((PKCS7 *) get_body_sparep_data(*sparep));
+ break;
+#endif /* SMIME */
+ case CharType: s = (char *)get_body_sparep_data(*sparep);
+ fs_give((void **) &s);
+ break;
+ case SizedText: st = (SIZEDTEXT *)get_body_sparep_data(*sparep);
+ fs_give((void **) &st->data);
+ fs_give((void **) &st);
+ break;
+ case iCalType: vcal = (VCALENDAR_S *)get_body_sparep_data(*sparep);
+ ical_free_vcalendar((void **) &vcal);
+ break;
+ default : break;
+ }
+ ((BODY_SPARE_S *)(*sparep))->data = NULL;
+ fs_give(sparep);
+ }
+}
diff --git a/pith/body.h b/pith/body.h
new file mode 100644
index 00000000..c8955a92
--- /dev/null
+++ b/pith/body.h
@@ -0,0 +1,23 @@
+#ifndef PITH_BODY_SPARE_INCLUDED
+#define PITH_BODY_SPARE_INCLUDED
+typedef enum
+ {CharType,
+ SizedText,
+#ifdef SMIME
+ P7Type,
+#endif
+ iCalType
+} SpareType;
+
+typedef struct body_sparep_t {
+ SpareType sptype;
+ void *data;
+} BODY_SPARE_S;
+
+void *create_body_sparep(SpareType, void *);
+void free_body_sparep(void **);
+void *get_body_sparep_data(void *);
+SpareType get_body_sparep_type(void *);
+
+
+#endif /* PITH_BODY_SPARE_INCLUDED */
diff --git a/pith/ical.c b/pith/ical.c
new file mode 100644
index 00000000..bfad8078
--- /dev/null
+++ b/pith/ical.c
@@ -0,0 +1,2365 @@
+#include "../pith/headers.h"
+#include "../pith/mailpart.h"
+#include "../pith/store.h"
+#include "../pith/ical.h"
+#ifdef STANDALONE
+#include "readfile.h"
+#include "mem.h"
+char *lf2crlf(char *);
+#endif /* STANDALONE */
+
+typedef struct ical_iana_comp_s {
+ char *comp; /* component name */
+ size_t len; /* size of component name (strlen(x->comp)) */
+ int pos; /* position of this component in comp array */
+ void *(*parse)(char **); /* parser */
+ void (*give)(void **); /* free memory */
+} ICAL_COMP_S;
+
+typedef struct ical_iana_prop_s {
+ char *prop; /* component name */
+ size_t len; /* size of component name (strlen(x->comp)) */
+ int pos; /* location of this component in the prop array */
+ void (*parse)(); /* parser */
+ void (*give)(void **); /* free memory */
+} ICAL_PROP_S;
+
+int ical_january_first(int); /* args: year */
+void ical_adjust_date(struct tm *, VTIMEZONE_S *);
+
+void ical_initialize(void);
+
+int ical_non_ascii_valid(unsigned char);
+char *ical_unfold_line(char *);
+ICLINE_S *ical_parse_line(char **, char *);
+
+ICLINE_S *ical_cline_cpy(ICLINE_S *);
+ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param);
+
+char *ical_get_value(char **);
+char *ical_decode(char *, unsigned short);
+
+/* pase component */
+void *ical_parse_vcalendar(char **);
+void *ical_parse_vevent(char **);
+void *ical_parse_vtodo(char **);
+void *ical_parse_vjournal(char **);
+void *ical_parse_vfreebusy(char **);
+void *ical_parse_vtimezone(char **);
+void *ical_parse_valarm(char **);
+void *ical_parse_timezone(char **);
+ICAL_S *ical_parse_unknown_comp(char **, int);
+ICAL_S *ical_parse_generic_comp(char **, int);
+
+/* free components */
+void ical_free_vevent(void **);
+void ical_free_vtodo(void **);
+void ical_free_vjournal(void **);
+void ical_free_vfreebusy(void **);
+void ical_free_vtimezone(void **);
+void ical_free_timezone(void **);
+void ical_free_valarm(void **);
+void ical_free_unknown_comp(ICAL_S **);
+
+/* parse properties */
+void ical_cline_from_token(void **, char **, char *);
+void ical_gencline_from_token(void **, char **, char *);
+
+void ical_parse_rrule(RRULE_S **, char **);
+void ical_parse_time(struct tm *, char **, char *);
+void ical_parse_offset(int *, char **, char *);
+
+void ical_parse_freq(Freq_value *, char *);
+void ical_parse_weekday_list(BYWKDY_S **, char *);
+void ical_parse_number_list(BYWKDY_S **, char *);
+void ical_parse_interval(unsigned long *, char *);
+
+int ical_get_number_value(char *, int, int);
+void ical_set_date(ICLINE_S *, VTIMEZONE_S *);
+void ical_set_date_vevent(void *, void *);
+
+/* free properties */
+void ical_free_prop(void **, ICAL_PROP_S *);
+void ical_free_null(void **);
+void ical_free_cline(void **);
+void ical_free_param(ICAL_PARAMETER_S **);
+void ical_free_gencline(void **);
+void ical_free_rrule(void **);
+void ical_free_weekday_list(void **);
+
+/* globals */
+struct tm day_zero; /* date for january 1, 1601 */
+int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+#define UTF8_COMPLETE (1)
+#define NEED_MORE (2)
+
+char *ical_buf;
+unsigned long ical_buf_len;
+
+
+/* parsing structures */
+
+/* this is the list of V-components to a Calendar from RFC 5545 */
+ICAL_COMP_S ical_comp[] = {
+ {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar},
+ {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone},
+ {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent},
+ {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo},
+ {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal},
+ {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm},
+ {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy},
+ {NULL, 0, VUnknown, NULL, 0}
+};
+
+/* array for properties */
+ICAL_PROP_S rrule_prop[] = {
+ {"FREQ", 4, RRFreq, ical_parse_freq, ical_free_null},
+ {"UNTIL", 5, RRUntil, ical_parse_freq, 0},
+ {"COUNT", 5, RRCount, ical_cline_from_token, ical_free_cline},
+ {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_free_null},
+ {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list},
+ {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list},
+ {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list},
+ {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list},
+ {"BYWEEKNO", 8, RRByweekno, 0, 0},
+ {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list},
+ {"BYSETPOS", 8, RRBysetpos, 0, 0},
+ {"BYWKST", 6, RRWkst, 0, 0},
+ {"BYMONTHDAY",
+ 10, RRBymonthday, 0, 0},
+ {"BYYEARDAY", 9, RRByyearday, 0, 0},
+ {NULL, 0, RRUnknown, 0, 0}
+};
+
+ICAL_PROP_S event_prop[] = {
+ {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline},
+ {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline},
+ {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline},
+ {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline},
+ {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline},
+ {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline},
+ {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline},
+ {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline},
+ {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline},
+ {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline},
+ {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline},
+ {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline},
+ {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline},
+ {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline},
+ {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline},
+ {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline},
+ {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline},
+ {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule},
+ {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline},
+ {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline},
+ {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline},
+ {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline},
+ {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline},
+ {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline},
+ {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline},
+ {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline},
+ {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline},
+ {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline},
+ {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline},
+ {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline},
+ {NULL, 0, EvUnknown, 0, 0}
+};
+
+ICAL_PROP_S tz_comp[] = {
+ {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline},
+ {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline},
+ {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline},
+ {NULL, 0, TZCUnknown, 0, 0}
+};
+
+ICAL_PROP_S tz_prop[] = {
+ {"DTSTART", 7, TZPDtstart, ical_parse_time, 0},
+ {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, 0},
+ {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, 0},
+ {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule},
+ {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline},
+ {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline},
+ {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline},
+ {NULL, 0, TZPUnknown, 0, 0}
+};
+
+ICAL_PROP_S alarm_prop[] = {
+ {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline},
+ {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline},
+ {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline},
+ {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline},
+ {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline},
+ {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline},
+ {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline},
+ {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline},
+ {NULL, 0, AlUnknown, 0, 0}
+};
+
+/* some useful macros for character analysis */
+
+#define ical_wspace(X) \
+ ((X) == ' ' || (X) == '\t')
+
+#define ical_name_allowed_char(X) \
+ (((X) >= 'A' && (X) <= 'Z') || \
+ ((X) >= 'a' && (X) <= 'z') || \
+ (X) == '-' )
+
+#define ical_control(X) \
+ (((X) >= 0x00 && (X) <= 0x08) || \
+ ((X) >= 0x0A && (X) <= 0x1F) || \
+ (X) == 0x7F)
+
+#define ical_safe_char(X) \
+ (ical_non_ascii_valid(X) \
+ || ical_wspace(X) \
+ || (X) == 0x21 \
+ || ((X) >= 0x23 && (X) <= 0x2B) \
+ || ((X) >= 0x2D && (X) <= 0x39) \
+ || ((X) >= 0x3C && (X) <= 0x7E))
+
+#define ical_qsafe_char(X) \
+ (ical_non_ascii_valid((X)) \
+ || ical_wspace(X) \
+ || (X) == 0x21 \
+ || ((X) >= 0x23 && (X) <= 0x7E))
+
+#define ical_value_char(X) \
+ (ical_non_ascii_valid(X) \
+ || ical_wspace(X) \
+ || ((X) >= 0x21 && (X) <= 0x7E))
+
+/* Finally, here begins the code. */
+
+char *
+ical_decode(char *text, unsigned short encoding)
+{
+ char *t;
+ unsigned long callen;
+ if(encoding == ENCQUOTEDPRINTABLE){
+ t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
+ if(t != NULL){
+ strncpy(text, t, strlen(t));
+ fs_give((void **) &t);
+ }
+ }
+ return text;
+}
+
+
+/* Return code:
+ 0 - if no errors
+ -1 - if an error occured
+ Args: a pointer to the text. If there is an error, the text is not modified.
+ */
+int
+ical_remove_escapes(char **textp)
+{
+ char *text, *s, *t;
+ int rv = 0;
+ int escaped;
+
+ if(textp == NULL) return 0;
+
+ t = cpystr(*textp); /* work on a copy of the text */
+ /* the variable text below points to the beginning of the filtered text */
+ for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
+ if(*s == '\\' && escaped == 0){
+ escaped = 1;
+ continue;
+ }
+ if(escaped){
+ switch(*s){
+ case '\\':
+ case ',':
+ case ';':
+ *t++ = *s;
+ break;
+
+ case 'n':
+ case 'N':
+ *t++ = '\n';
+ break;
+ default: rv = -1;
+ break;
+ }
+ escaped = 0;
+ }
+ else *t++ = *s;
+ }
+ *t = '\0'; /* tie off filtered text */
+ t = text; /* reset t to the beginning */
+ if(rv == -1)
+ fs_give((void **) &t);
+ else{
+ strncpy(*textp, t, strlen(t)); /* overwrite given text with filtered text */
+ (*textp)[strlen(t)] = '\0';
+ }
+ return rv;
+}
+
+void ical_debug(char *fcn, char *text)
+{
+ char piece[50];
+ strncpy(piece, text, 49);
+ dprint((9, "%s: %s", fcn, piece));
+}
+
+/***
+ *** FREE MEMORY FUNCTIONS
+ ***/
+
+void
+ical_free_param(ICAL_PARAMETER_S **param)
+{
+ if(param == NULL || *param == NULL)
+ return;
+
+ if((*param)->name) fs_give((void **) &(*param)->name);
+ if((*param)->value) fs_give((void **) &(*param)->value);
+ if((*param)->next) ical_free_param(&(*param)->next);
+ fs_give((void **)param);
+}
+
+void
+ical_free_null(void **n)
+{
+ if(n != NULL) *n = NULL;
+}
+
+void
+ical_free_cline(void **icv)
+{
+ ICLINE_S **ic = (ICLINE_S **) icv;
+
+ if(ic == NULL || *ic == NULL)
+ return;
+
+ if((*ic)->token) fs_give((void **) &(*ic)->token);
+ if((*ic)->param) ical_free_param(&(*ic)->param);
+ if((*ic)->value) fs_give((void **) &(*ic)->value);
+ fs_give(icv);
+}
+
+void
+ical_free_gencline(void **giclpv)
+{
+ GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv;
+
+ if(giclp == NULL || *giclp == NULL) return;
+
+ if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline);
+ if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next);
+}
+
+void
+ical_free_vcalendar(void **vcalpv)
+{
+ VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
+
+ if(vcalp == NULL || *vcalp == NULL) return;
+
+ if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
+ if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
+ if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
+ if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
+ if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
+ if((*vcalp)->comp){
+ Cal_comp i;
+ for(i = 0; i < VUnknown; i++)
+ if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
+ fs_give((*vcalp)->comp);
+ }
+ if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
+ fs_give(vcalpv);
+}
+
+void
+ical_free_vevent(void **veventpv)
+{
+ VEVENT_S **veventp = (VEVENT_S **) veventpv;
+
+ if(veventp == NULL || *veventp == NULL) return;
+
+ ical_free_prop((*veventp)->prop, event_prop);
+ if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
+ if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
+ fs_give(veventpv);
+}
+
+void
+ical_free_rrule(void **rrulepv)
+{
+ RRULE_S **rrulep = (RRULE_S **) rrulepv;
+
+ if(rrulep && *rrulep){
+ ical_free_prop((*rrulep)->prop, rrule_prop);
+ ical_free_param(&(*rrulep)->param);
+ fs_give(rrulepv);
+ }
+}
+
+void
+ical_free_weekday_list(void **wkdylv)
+{
+ BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
+
+ if(wkdyl == NULL) return;
+
+ if((*wkdyl)->next)
+ ical_free_weekday_list((void **) &(*wkdyl)->next);
+
+ fs_give(wkdylv);
+}
+
+
+void
+ical_free_vtodo(void **vtodopv)
+{
+}
+
+void
+ical_free_vjournal(void **vjournalpv)
+{
+}
+
+void
+ical_free_vfreebusy(void **vfbpv)
+{
+}
+
+void
+ical_free_prop(void **propv, ICAL_PROP_S *aux_comp)
+{
+ int i, j;
+
+ if(propv == NULL) return;
+
+ for(i = 0; aux_comp[i].prop != NULL; i++)
+ if(propv[i]){
+ for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
+ if(aux_comp[j].give) (aux_comp[j].give)(&propv[i]);
+ }
+ fs_give(propv);
+}
+
+
+void
+ical_free_vtimezone(void **vtzpv)
+{
+ VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
+ TZ_comp i,j;
+
+ if(vtzp == NULL || *vtzp == NULL) return;
+
+ ical_free_prop((*vtzp)->prop, tz_comp);
+
+ if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop);
+ if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc);
+ if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc);
+ fs_give(vtzpv);
+}
+
+void
+ical_free_timezone(void **tzpv)
+{
+ ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv;
+
+ if(tzp == NULL || *tzp == NULL) return;
+
+ ical_free_prop((*tzp)->prop, tz_prop);
+ if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop);
+ if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next);
+ fs_give(tzpv);
+}
+
+void ical_free_valarm(void **valarmpv)
+{
+}
+
+void
+ical_free_unknown_comp(ICAL_S **icalp)
+{
+ int i;
+ if(icalp == NULL || *icalp == NULL) return;
+ for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
+ if(ical_comp[i].give)
+ (ical_comp[i].give)(&(*icalp)->value);
+ else
+ ical_free_gencline((void **) &(*icalp)->value);
+ fs_give((void **)&(*icalp)->comp);
+ ical_free_unknown_comp(&(*icalp)->next);
+ ical_free_unknown_comp(&(*icalp)->branch);
+ fs_give((void **)icalp);
+}
+
+char *ical_unfold_line(char *line)
+{
+ int i, j;
+
+ if(line == NULL)
+ return NULL;
+
+ for(i = 0, j = 0; line[j] != '\0';)
+ switch(line[j]){
+ case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
+ j += 3; /* get past white space */
+ continue;
+ }
+ default : line[i++] = line[j++];
+ }
+ line[i] = '\0';
+ return line;
+}
+
+ICAL_PARAMETER_S *
+ical_get_parameter(char **line)
+{
+ ICAL_PARAMETER_S *param = NULL;
+ char *s;
+
+ if(line == NULL || *line == NULL)
+ return NULL;
+
+ for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
+
+ if(*s == '='){
+ int quoted;
+ char c;
+
+ param = fs_get(sizeof(ICAL_PARAMETER_S));
+ memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
+ *s = '\0';
+ param->name = cpystr(*line);
+ *s = '=';
+ *line = s+1; /* step over '=' */
+ quoted = **line == '"' ? 1 : 0;
+ if(quoted != 0){
+ for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
+ if(*s != '"'){ /* error, do not parse this line */
+ ical_free_param(&param);
+ *line = strchr(s, ':'); /* reset line to closest ':' */
+ return NULL;
+ }
+ }
+ else
+ for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
+ c = *s;
+ *s = '\0';
+ param->value = cpystr(*line);
+ *s = c; /* restore character */
+ *line = quoted ? s + 1 : s;
+
+ if(**line == ';'){
+ ++*line;
+ param->next = ical_get_parameter(line);
+ }
+ }
+ return param;
+}
+
+char *ical_get_value(char **line)
+{
+ char *s, *t;
+
+ if(line == NULL || *line == NULL)
+ return NULL;
+
+ for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
+ if(*s == '\r'){
+ *s = '\0';
+ t = cpystr(*line);
+ *s = '\r';
+ *line = s+2;
+ }
+ else{
+ t = NULL;
+ s = strchr(*line, '\r');
+ if(s != NULL)
+ *line = s + 2;
+ }
+ return t;
+}
+
+ICAL_PARAMETER_S *
+ical_parameter_cpy(ICAL_PARAMETER_S *param)
+{
+ ICAL_PARAMETER_S *rv;
+
+ if(param == NULL) return NULL;
+
+ rv = fs_get(sizeof(ICAL_PARAMETER_S));
+ memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
+
+ if(param->name) rv->name = cpystr(param->name);
+ if(param->value) rv->value = cpystr(param->value);
+ if(param->next) rv->next = ical_parameter_cpy(param->next);
+
+ return rv;
+}
+
+ICLINE_S *
+ical_cline_cpy(ICLINE_S *icl)
+{
+ ICLINE_S *rv;
+
+ if(icl == NULL)
+ return NULL;
+
+ rv = fs_get(sizeof(ICLINE_S));
+ memset((void *)rv, 0, sizeof(ICLINE_S));
+
+ if(icl->token) rv->token = cpystr(icl->token);
+ if(icl->param) rv->param = ical_parameter_cpy(icl->param);
+ if(icl->value) rv->value = cpystr(icl->value);
+
+ return rv;
+}
+
+/* Given a \r\n-ending line (called *text), isolate the ocurrence
+ * of the token in that line.
+ * Return the token, and modify the pointer to *text to point to the
+ * end of the token. Modify sep to contain the character following
+ * the token
+ * ical-line = token ':'/';' rest of the line\r\n
+ * on error return null, and set *text to the next line, if possible.
+ */
+char *
+ical_isolate_token(char **text, char *sep)
+{
+ char *s, *t;
+
+ for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
+ /* only followed by parameter or value */
+ if(*s == ':' || *s == ';'){
+ *sep = *s;
+ *s = '\0'; /* isolate token at pointer s */
+ *text = s;
+ }
+ else{ /* bad data - bail out of here */
+ t = NULL;
+ if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
+ *text = NULL;
+ else /* move to next line */
+ *text = s + 2;
+ }
+ return t;
+}
+
+
+VCALENDAR_S *
+ical_parse_text(char *text)
+{
+ char *s;
+ VCALENDAR_S *vcal = NULL;
+
+ ical_debug("ical_parse_text", text);
+ ical_initialize();
+
+ text = ical_unfold_line(text);
+ for(s = text; s && *s != '\0'; s++){
+ if(*s != 'B' && *s != 'b')
+ continue;
+ if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
+ s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
+ vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
+ break;
+ }
+ }
+ return vcal;
+}
+
+void
+ical_parse_time(struct tm *ic_date, char **text, char *token)
+{
+ ICLINE_S *icl;
+
+ icl = ical_parse_line(text, token);
+ ical_parse_date(icl->value, ic_date);
+ ical_free_cline((void **) &icl);
+}
+
+void
+ical_parse_interval(unsigned long *longv, char *value)
+{
+ *longv = atoi(value);
+}
+
+
+void
+ical_parse_offset(int *offsetv, char **text, char *token)
+{
+ ICLINE_S *icl;
+ char *value;
+ int h, m, offset;
+
+ icl = ical_parse_line(text, token);
+
+ if(*icl->value == '+' || *icl->value == '-')
+ value = icl->value + 1;
+ else
+ value = icl->value;
+
+ h = ical_get_number_value(value, 0, 2);
+ m = ical_get_number_value(value, 2, 4);
+
+ offset = 60*(60*h + m);
+ if(*icl->value == '-')
+ offset *= -1;
+
+ *offsetv = offset;
+ ical_free_cline((void **) &icl);
+}
+
+void
+ical_cline_from_token(void **iclv, char **text, char *token)
+{
+ ICLINE_S **icl = (ICLINE_S **)iclv;
+
+ if(*icl == NULL)
+ *icl = ical_parse_line(text, token);
+ else {
+ ICLINE_S *ic = ical_parse_line(text, token);
+ ical_free_cline((void **)&ic);
+ }
+}
+
+void
+ical_gencline_from_token(void **giclv, char **text, char *token)
+{
+ GEN_ICLINE_S *gicl;
+
+ if(!struncmp(*text, token, strlen(token))){
+ gicl = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
+ ical_cline_from_token((void **) &gicl->cline, text, token);
+ ical_gencline_from_token((void **) &gicl->next, text, token);
+ *giclv = (void *) gicl;
+ }
+}
+
+/***
+ *** PARSE COMPONENT FUNCTIONS
+ ***/
+
+void *
+ical_parse_vcalendar(char **text)
+{
+ char *s, *t;
+ char c;
+ VCALENDAR_S *vcal;
+ void *v;
+
+ dprint((9, "ical_parse_vcalendar:\n"));
+ ical_debug("ical_parse_vcalendar", *text);
+
+ vcal = fs_get(sizeof(VCALENDAR_S));
+ memset((void *)vcal, 0, sizeof(VCALENDAR_S));
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+
+ *t = c; /* restore character */
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ int i;
+ switch(*s){
+ case 'B':
+ case 'b': if(!struncmp(s+1, "EGIN", 4)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ for(i = 0; ical_comp[i].comp
+ && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
+ || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
+
+ if(ical_comp[i].parse){
+ s += ical_comp[i].len + 2;
+ v = (ical_comp[i].parse)(&s);
+ if(vcal->comp == NULL){
+ vcal->comp = fs_get(VUnknown*sizeof(void *));
+ memset((void *) vcal->comp, 0, VUnknown*sizeof(void *));
+ }
+
+ if(vcal->comp[ical_comp[i].pos] == NULL)
+ vcal->comp[ical_comp[i].pos] = v;
+ else
+ (ical_comp[i].give)(&v);
+ } else {
+ v = (void *) ical_parse_unknown_comp(&s, 0);
+ if(vcal->uk_comp == NULL)
+ vcal->uk_comp = (ICAL_S *) v;
+ else{
+ ICAL_S *ic;
+ for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
+ ic->branch = (ICAL_S *) v;
+ }
+ }
+ } else ukn++;
+ break;
+
+ case 'C':
+ case 'c': if(!struncmp(s+1, "ALSCALE", 7))
+ ical_cline_from_token((void **) &vcal->calscale, &s, "CALSCALE");
+ else ukn++;
+ break;
+
+ case 'E':
+ case 'e': if(!struncmp(s+1, "ND", 2)){
+ *t = c;
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, "VCALENDAR\r\n", 11)){
+ *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
+ return (void *) vcal;
+ }
+// else ukn++; FIX THIS, this is not quite right
+ } else ukn++;
+ break;
+
+ case 'M':
+ case 'm': if(!struncmp(s+1, "ETHOD", 5))
+ ical_cline_from_token((void **) &vcal->method, &s, "METHOD");
+ else ukn++;
+ break;
+
+ case 'P':
+ case 'p': if(!struncmp(s+1, "RODID", 5))
+ ical_cline_from_token((void **) &vcal->prodid, &s, "PRODID");
+ else ukn++;
+ break;
+
+ case 'V':
+ case 'v': if(!struncmp(s+1, "ERSION", 6)){
+ ical_cline_from_token((void **) &vcal->version, &s, "VERSION");
+ } else ukn++;
+ break;
+
+ default : ukn++;
+ break;
+ } /* end of switch(*s) */
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(vcal->uk_prop == NULL){
+ vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
+ vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gcl;
+ for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
+ gcl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
+ gcl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ *text = s;
+
+ /* ok, we have parsed the vcalendar, now parse some special properties */
+ /* start by parsing dates */
+ ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
+ return (void *) vcal;
+}
+
+void *
+ical_parse_vevent(char **text)
+{
+ char *s, *t;
+ char c;
+ VEVENT_S *vevent;
+
+ ical_debug("ical_parse_vevent", *text);
+ vevent = fs_get(sizeof(VEVENT_S));
+ memset((void *)vevent, 0, sizeof(VEVENT_S));
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+ *t = c; /* restore separator */
+
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ if(!struncmp(s, "BEGIN", 5)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ if(!struncmp(s, "VALARM\r\n", 8)){
+ s += 8; /* 8 = strlen("VALARM\r\n"); */
+ if(vevent->valarm == NULL)
+ vevent->valarm = ical_parse_valarm(&s);
+ else{
+ VALARM_S *valrm;
+ for(valrm = vevent->valarm; valrm && valrm->next;
+ valrm = valrm->next);
+ valrm->next = ical_parse_valarm(&s);
+ }
+ } else {
+ ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
+ ical_free_unknown_comp(&uk_comp);
+ }
+ } else if(!struncmp(s, "END", t-s-1)){
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, "VEVENT\r\n",8)){
+ *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
+ return (void *) vevent;
+ }
+ } else{ Event_prop i;
+ for(i = 0; i < EvUnknown; i++)
+ if(!struncmp(s, event_prop[i].prop, t-s))
+ break;
+ if(event_prop[i].parse){
+ if(vevent->prop == NULL){
+ vevent->prop = fs_get(EvUnknown*sizeof(void *));
+ memset((void *)vevent->prop, 0, EvUnknown*sizeof(void *));
+ }
+ (event_prop[i].parse)(&vevent->prop[event_prop[i].pos], &s, event_prop[i].prop);
+ }
+ else
+ ukn++;
+ }
+
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(vevent->uk_prop == NULL){
+ vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
+ vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gcl;
+ for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
+ gcl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
+ gcl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ *text = s;
+ return (void *) vevent;
+}
+
+void *
+ical_parse_vtimezone(char **text)
+{
+ char *s, *t;
+ char c;
+ void *v;
+ VTIMEZONE_S *vtz;
+
+ ical_debug("ical_parse_vtimezone", *text);
+ vtz = fs_get(sizeof(VTIMEZONE_S));
+ memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+ *t = c; /* restore separator */
+
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ if(!struncmp(s, "BEGIN", 5)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ if(!struncmp(s, "STANDARD\r\n", 10)){
+ s += 10; /* 10 = strlen("STANDARD\r\n"); */
+ v = ical_parse_timezone(&s);
+ if(vtz->standardc == NULL)
+ vtz->standardc = (ICAL_TZPROP_S *) v;
+ else{
+ ICAL_TZPROP_S *dl;
+ for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
+ dl->next = (ICAL_TZPROP_S *) v;
+ }
+ } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
+ s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
+ v = ical_parse_timezone(&s);
+ if(vtz->daylightc == NULL)
+ vtz->daylightc = (ICAL_TZPROP_S *) v;
+ else{
+ ICAL_TZPROP_S *dl;
+ for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
+ dl->next = (ICAL_TZPROP_S *) v;
+ }
+ } else {
+ ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
+ ical_free_unknown_comp(&uk_comp);
+ }
+ } else if(!struncmp(s, "END", t-s-1)){
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, "VTIMEZONE\r\n",11)){
+ *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
+ return (void *) vtz;
+ }
+ } else{ TZ_comp i;
+ for(i = 0; i < TZCUnknown; i++)
+ if(!struncmp(s, tz_comp[i].prop, t-s))
+ break;
+ if(tz_comp[i].parse){
+ if(vtz->prop == NULL){
+ vtz->prop = fs_get(TZCUnknown*sizeof(void *));
+ memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
+ }
+ (tz_comp[i].parse)(&vtz->prop[tz_comp[i].pos], &s, tz_comp[i].prop);
+ }
+ else
+ ukn++;
+ }
+
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(vtz->uk_prop == NULL){
+ vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
+ vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gcl;
+ for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
+ gcl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
+ gcl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ *text = s;
+ return (void *) vtz;
+}
+
+void *
+ical_parse_timezone(char **text)
+{
+ char *s, *t;
+ char c;
+ ICAL_TZPROP_S *tzprop;
+
+ ical_debug("ical_parse_timezone", *text);
+ tzprop = fs_get(sizeof(ICAL_TZPROP_S));
+ memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+ *t = c; /* restore separator */
+
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ if(!struncmp(s, "BEGIN", 5)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
+ ical_free_unknown_comp(&uk_comp);
+ } else if(!struncmp(s, "END", t-s-1)){
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, "STANDARD\r\n", 10)
+ || !struncmp(s, "DAYLIGHT\r\n", 10)){
+ *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
+ return (void *) tzprop;
+ }
+ } else{ TZ_prop i;
+ for(i = 0; i < TZPUnknown; i++)
+ if(!struncmp(s, tz_prop[i].prop, t-s))
+ break;
+ if(tz_prop[i].parse){
+ if(tzprop->prop == NULL){
+ tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
+ memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
+ }
+ (tz_prop[i].parse)(&tzprop->prop[tz_prop[i].pos], &s, tz_prop[i].prop);
+ }
+ else
+ ukn++;
+ }
+
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(tzprop->uk_prop == NULL){
+ tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
+ tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gcl;
+ for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
+ gcl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
+ gcl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ *text = s;
+ return (void *) tzprop;
+}
+
+void *
+ical_parse_valarm(char **text)
+{
+ char *s, *t;
+ char c;
+ VALARM_S *valarm;
+
+ ical_debug("ical_parse_valarm", *text);
+ valarm = fs_get(sizeof(VALARM_S));
+ memset((void *)valarm, 0, sizeof(VALARM_S));
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+ *t = c; /* restore separator */
+
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ if(!struncmp(s, "BEGIN", 5)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
+ ical_free_unknown_comp(&uk_comp);
+ } else if(!struncmp(s, "END", t-s-1)){
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, "ALARM\r\n", 7)){
+ *text = s + 7; /* 7 = strlen("ALARM\r\n") */
+ return (void *) valarm;
+ }
+ } else{ Alarm_prop i;
+ for(i = 0; i < AlUnknown; i++)
+ if(!struncmp(s, alarm_prop[i].prop, t-s))
+ break;
+ if(alarm_prop[i].parse){
+ if(valarm->prop == NULL){
+ valarm->prop = fs_get(AlUnknown*sizeof(void *));
+ memset((void *)valarm->prop, 0, AlUnknown*sizeof(void *));
+ }
+ (alarm_prop[i].parse)(&valarm->prop[alarm_prop[i].pos], &s, alarm_prop[i].prop);
+ }
+ else
+ ukn++;
+ }
+
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(valarm->uk_prop == NULL){
+ valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
+ valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gcl;
+ for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
+ gcl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
+ gcl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ *text = s;
+ return (void *) valarm;
+}
+
+void *
+ical_parse_vtodo(char **text)
+{
+ return NULL;
+}
+
+void *
+ical_parse_vjournal(char **text)
+{
+ return NULL;
+}
+
+void *
+ical_parse_vfreebusy(char **text)
+{
+ return NULL;
+}
+
+ICAL_S *
+ical_parse_generic_comp(char **text, int level)
+{
+ ICAL_S *ical;
+ char *s, *t;
+ char *token = NULL;
+ GEN_ICLINE_S *gcl = NULL;
+ char c;
+
+ ical_debug("ical_parse_generic_comp", *text);
+ ical = fs_get(sizeof(ICAL_S));
+ memset((void *)ical, 0, sizeof(ICAL_S));
+
+ ical->comp = ical_get_value(text);
+ token = fs_get(strlen(ical->comp) + 2 + 1);
+ sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
+
+ /* s must always point the the beginning of a line */
+ for(s = *text; s && *s != '\0';){
+ t = s;
+ s = ical_isolate_token(&t, &c);
+ if(s == NULL){
+ if(t != NULL)
+ s = t;
+ continue;
+ }
+
+ *t = c; /* restore character */
+ if(s){ /* figure out the token */
+ int ukn = 0; /* unknown token */
+ switch(*s){
+ case 'B':
+ case 'b': if(!struncmp(s+1, "EGIN", 4)){
+ s += 6; /* 6 = strlen("BEGIN:") */
+ if(ical->next == NULL)
+ ical->next = ical_parse_unknown_comp(&s, level+1);
+ else{
+ ICAL_S *b;
+ int i;
+
+ for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
+ if(b->branch == NULL)
+ b->branch = ical_parse_unknown_comp(&s, level+1);
+ else {
+ for(; b && b->branch; b = b->branch);
+ b->branch = ical_parse_unknown_comp(&s, level+1);
+ }
+ }
+ } else ukn++;
+ break;
+
+ case 'E':
+ case 'e': if(!struncmp(s+1, "ND", 2)){
+ *t = c;
+ s += 4; /* 4 = strlen("END:") */
+ if(!struncmp(s, token, strlen(token))){
+ *text = s + strlen(token);
+ ical->value = (void *) gcl;
+ return ical;
+ }
+ } else ukn++;
+ break;
+
+ default : ukn++;
+ break;
+ } /* end of switch(*s) */
+ if(ukn){
+ if(ical_buf_len < t - s){
+ fs_resize((void **)&ical_buf, t-s+1);
+ ical_buf_len = t-s;
+ }
+ *t = '\0';
+ strcpy(ical_buf, s);
+ *t = c;
+ if(gcl == NULL){
+ gcl = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
+ gcl->cline = ical_parse_line(&s, ical_buf);
+ }
+ else{
+ GEN_ICLINE_S *gencl;
+ for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
+ gencl->next = fs_get(sizeof(GEN_ICLINE_S));
+ memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
+ gencl->next->cline = ical_parse_line(&s, ical_buf);
+ }
+ }
+ } /* end of if(s) */
+ }
+
+ ical->value = (void *) gcl;
+ *text = s;
+ return ical;
+}
+
+ICAL_S *
+ical_parse_unknown_comp(char **text, int level)
+{
+ ICAL_S *ical;
+ int i;
+
+ ical_debug("ical_parse_unknown_comp", *text);
+ for(i = 0; ical_comp[i].comp
+ && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
+ || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
+
+ if(ical_comp[i].parse){
+ *text += ical_comp[i].len + 2;
+ ical = fs_get(sizeof(ICAL_S));
+ memset((void *)ical, 0, sizeof(ICAL_S));
+ ical->comp = cpystr(ical_comp[i].comp);
+ ical->value = (ical_comp[i].parse)(text);
+ } else
+ ical = ical_parse_generic_comp(text, level);
+
+ return ical;
+}
+
+ICLINE_S *
+ical_parse_line(char **text, char *name)
+{
+ ICLINE_S *ic;
+ char *s = *text;
+
+ ic = fs_get(sizeof(ICLINE_S));
+ memset((void *)ic, 0, sizeof(ICLINE_S));
+
+ ic->token = cpystr(name);
+ s += strlen(name);
+ if(*s == ';'){
+ s++;
+ ic->param = ical_get_parameter(&s);
+ }
+ if(*s == ':'){
+ s++;
+ ic->value = ical_get_value(&s);
+ }
+
+ *text = s;
+ return ic;
+}
+
+/***
+ *** PARSE PROPERTY FUNCTIONS
+ ***/
+
+void
+ical_parse_freq(Freq_value *fval, char *text)
+{
+ if(fval == NULL) return;
+
+ *fval = FUnknown;
+
+ if(text == NULL) return;
+
+ if(!strucmp(text, "SECONDLY")) *fval = FSecondly;
+ else if(!strucmp(text, "MINUTELY")) *fval = FMinutely;
+ else if(!strucmp(text, "HOURLY")) *fval = FHourly;
+ else if(!strucmp(text, "DAILY")) *fval = FDaily;
+ else if(!strucmp(text, "WEEKLY")) *fval = FWeekly;
+ else if(!strucmp(text, "MONTHLY")) *fval = FMonthly;
+ else if(!strucmp(text, "YEARLY")) *fval = FYearly;
+}
+
+void
+ical_parse_weekday_list(BYWKDY_S **bywkdyp, char *wklist)
+{
+ BYWKDY_S *bywkdy, *w;
+ char *s, *t, c;
+ int done;
+ size_t len;
+
+ if(bywkdyp == NULL) return;
+ *bywkdyp = bywkdy = NULL;
+ if(wklist == NULL) return;
+
+ for(t = s = wklist; done == 0; s++){
+ if(*s != ',' && *s != '\0')
+ continue;
+ c = *s;
+ if(c == ',')
+ *s = '\0';
+ else /* c == '\0' */
+ done++;
+ len = strlen(t);
+ if(len > 1){
+ for(w = bywkdy; w && w->next; w = w->next);
+ w = fs_get(sizeof(BYWKDY_S));
+ memset((void *)w, 0, sizeof(BYWKDY_S));
+ if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
+ else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
+ else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
+ else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
+ else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
+ else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
+ else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
+// t[len - 2] = '\0';
+ if(*t != '\0')
+ w->value = strtoul(t, &t, 10);
+ if(bywkdy == NULL)
+ bywkdy = w;
+ }
+ *s = c;
+ if(*s == ',')
+ t = s + 1;
+ }
+
+ if(*bywkdyp)
+ ical_free_weekday_list((void **)&bywkdy);
+ else
+ *bywkdyp = bywkdy;
+}
+
+void
+ical_parse_number_list(BYWKDY_S **bynop, char *nolist)
+{
+ BYWKDY_S *byno, *n;
+ char *s, *t, c;
+ int done;
+
+ if(bynop == NULL) return;
+ *bynop = byno = NULL;
+ if(nolist == NULL) return;
+
+ for(t = s = nolist; done == 0; s++){
+ if(*s != ',' && *s != '\0')
+ continue;
+ c = *s;
+ if(c == ',')
+ *s = '\0';
+ else /* c == '\0' */
+ done++;
+
+ for(n = byno; n && n->next; n = n->next);
+ n = fs_get(sizeof(BYWKDY_S));
+ memset((void *)n, 0, sizeof(BYWKDY_S));
+ n->value = strtoul(t, &t, 10);
+ if(byno == NULL)
+ byno = n;
+ *s = c;
+ if(*s == ',')
+ t = s + 1;
+ }
+
+ if(*bynop)
+ ical_free_weekday_list((void **)&byno);
+ else
+ *bynop = byno;
+}
+
+void
+ical_parse_rrule(RRULE_S **rrulep, char **text)
+{
+ RRULE_S *rrule;
+ ICLINE_S *icl;
+ char *s;
+ ICAL_PARAMETER_S *param, *p;
+ int i;
+
+ if(rrulep == NULL
+ || text == NULL || *text == NULL || struncmp(*text, "RRULE", 5)) return;
+ rrule = fs_get(sizeof(RRULE_S));
+ memset((void *) rrule, 0, sizeof(RRULE_S));
+
+ /* recurring rules are special. First, we parse the icline that contains it */
+ icl = ical_parse_line(text, "RRULE");
+
+ /* now we copy the parameters that it contains */
+ rrule->param = ical_parameter_cpy(icl->param);
+
+ /* then we parse icl->value as if it was a parameter */
+ s = icl->value;
+ param = ical_get_parameter(&s);
+
+ /* now we check which values were given, and fill the prop array */
+ rrule->prop = fs_get(RRUnknown*sizeof(void *));
+ memset((void *) rrule->prop, 0, RRUnknown*sizeof(void *));
+
+ for(p = param; p != NULL; p = p->next){
+ for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
+ if(rrule_prop[i].parse)
+ (rrule_prop[i].parse)(&rrule->prop[rrule_prop[i].pos], p->value);
+ }
+
+ ical_free_param(&param);
+ ical_free_cline((void **)&icl);
+ if(*rrulep)
+ ical_free_rrule((void **)&rrule);
+ else
+ *rrulep = rrule;
+}
+
+/*** UTF-8 for ICAL ***/
+
+int
+ical_non_ascii_valid(unsigned char c)
+{
+ static unsigned char icu[6];
+ static int utf8_len = 0;
+ static int utf8_type = 0;
+ int rv;
+
+ if(utf8_len == 0)
+ utf8_type = (c >= 0xF0 && c <= 0xF4)
+ ? 4 : (c >= 0xE0 && c <= 0xEF)
+ ? 3 : (c >= 0xC2 && c <= 0xDF)
+ ? 2 : 0;
+
+ if(utf8_type == 0)
+ return 0;
+
+ icu[utf8_len++] = c; /* count it */
+
+ if(utf8_type == 2){
+ if(utf8_len < 2)
+ rv = NEED_MORE;
+ else if(utf8_len == 2){
+ rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
+ && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
+ utf8_len = 0;
+ }
+ } else if (utf8_type == 3){
+ if(utf8_len < 3)
+ rv = NEED_MORE;
+ else{
+ if(icu[0] == 0xE0)
+ rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
+ else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
+ rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
+ else if(icu[0] == 0xED)
+ rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
+ else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
+ rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
+ utf8_len = 0;
+ }
+ } else if (utf8_type == 4){
+ if(utf8_len < 4)
+ rv = NEED_MORE;
+ else{
+ if(icu[0] == 0xF0)
+ rv = (icu[1] >= 0x90 && icu[1] <= 0xBF)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF)
+ && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
+ else if(icu[0] >= 0xF1 && icu[0] <= 0xF3)
+ rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF)
+ && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
+ else if(icu[0] == 0xF4)
+ rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
+ && (icu[2] >= 0x80 && icu[2] <= 0xBF)
+ && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
+ utf8_len = 0;
+ }
+ }
+ return rv;
+}
+
+int
+ical_get_number_value(char *value, int beg_pos, int end_pos)
+{
+ char c, *err;
+ int rv;
+
+ c = value[end_pos];
+ value[end_pos] = '\0';
+ rv = strtoul(value + beg_pos, &err, 10);
+ if(err != NULL && *err != '\0') return -1;
+ value[end_pos] = c;
+ return rv;
+}
+
+void
+ical_free_duration(ICAL_DURATION_S **ic_d)
+{
+ if(ic_d == NULL || *ic_d == NULL)
+ return;
+
+ if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
+ fs_give((void **)ic_d);
+}
+
+/* returns 0 if no error, -1 if some error */
+int
+ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
+{
+ int i, j, rv = 0;
+
+ if(value == NULL || ic_d == NULL) return -1;
+
+ memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
+
+ if(value[i = 0] == '-'){
+ i++;
+ ic_d->sign = 1;
+ } else if(value[i] == '+')
+ i++;
+
+ if(value[i++] == 'P'){
+ for(j = i; value[j] != '\0' && value[j] != ','; j++){
+ if(!isdigit(value[j]))
+ switch(value[j]){
+ case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
+ i = ++j;
+ break;
+ case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
+ i = ++j;
+ break;
+ case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
+ i = ++j;
+ break;
+ case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
+ i = ++j;
+ break;
+ case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
+ i = ++j;
+ break;
+ case 'T': i = j + 1;
+ break;
+ default: rv = -1;
+ break;
+ }
+ }
+ }
+ else
+ rv = -1;
+
+ if(value[j++] == ','){
+ ICAL_DURATION_S next;
+ rv = ical_parse_duration(value+j, &next);
+ ic_d->next = &next;
+ }
+
+ return rv;
+}
+
+/* return -1 if any error,
+ 0 if value has the DATE-TIME form
+ 1 if value has the DATE form only
+ */
+int
+ical_parse_date(char *value, struct tm *t)
+{
+ int i, rv;
+ struct tm Tm;
+
+ rv = -1;
+ if(t == NULL) return rv;
+ memset((void *)&Tm, 0, sizeof(struct tm));
+
+ if(value == NULL) return rv;
+
+ rv = 0; /* assume DATE-TIME format */
+ /* a simple check for the format of the string */
+ for(i = 0; isdigit(value[i]); i++);
+ if (i == 8 && value[i] == '\0')
+ rv = 1;
+ else
+ if (i != 8 || value[i++] != 'T') return -1;
+ if(rv == 0) {
+ for(; isdigit(value[i]); i++);
+ if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
+ return -1;
+ }
+
+ Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
+ Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
+ Tm.tm_mday = ical_get_number_value(value, 6, 8);
+ if(rv == 0){
+ Tm.tm_hour = ical_get_number_value(value, 9, 11);
+ Tm.tm_min = ical_get_number_value(value, 11, 13);
+ Tm.tm_sec = ical_get_number_value(value, 13, 15);
+ }
+ *t = Tm;
+
+ return (t->tm_mon > 11 || t->tm_mon < 0
+ || t->tm_mday > 31 || t->tm_mday < 0
+ || t->tm_hour > 23 || t->tm_hour < 0
+ || t->tm_min > 59 || t->tm_min < 0
+ || t->tm_sec > 60 || t->tm_sec < 0)
+ ? - 1 : rv;
+}
+
+void
+ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
+{
+ int date_form; /* date forms from section 3.3.4 in RFC 5545 */
+ ICAL_PARAMETER_S *param;
+ char *tz = NULL;
+ struct tm ic_date;
+ time_t t;
+
+ if(icl == NULL) return;
+
+ for(param = icl->param; param != NULL; param = param->next)
+ if(!strucmp(param->name, "TZID"))
+ tz = param->value;
+
+ if(tz != NULL)
+ date_form = 3; /* local time with timezone */
+ else if(icl->value[strlen(icl->value)-1] == 'Z')
+ date_form = 2; /* utc time */
+ else date_form = 1; /* local time */
+
+ ical_parse_date(icl->value, &ic_date);
+ ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
+
+ switch(date_form){
+ case 1: break;
+ case 2: ical_adjust_date(&ic_date, vtz);
+ break;
+ case 3: break;
+ default: alpine_panic ("Impossible date_form");
+ }
+}
+
+ICAL_TZPROP_S *
+ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
+{
+ struct tm standard, daylight;
+
+
+}
+
+
+
+/* adjusts time to given time zone */
+void
+ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
+{
+ char *tzname = NULL;
+ ICLINE_S *icl;
+ ICAL_TZPROP_S *cur_std_day;
+
+ if(vtz == NULL)
+ return;
+
+ if(vtz->prop){
+ if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
+ tzname = cpystr(icl->value);
+ }
+
+//+++ cur_std_day = ical_std_or_daylight(date, vtz);
+}
+
+void
+ical_set_date_vevent(void *veventv, void *vtzv)
+{
+ VEVENT_S *vevent = (VEVENT_S *) veventv;
+ VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
+
+ if(vevent){
+ ical_set_date(vevent->prop[EvDtstamp], vtz);
+ ical_set_date(vevent->prop[EvDtstart], vtz);
+ ical_set_date(vevent->prop[EvDtend], vtz);
+ }
+}
+
+#define LEAP_YEAR(X) ((((X) % 4 == 0) \
+ && (((X) % 100 != 0) || ((X) % 400 == 0))) \
+ || (X) == 1700)
+
+#define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
+
+/* given a year, after day_zero, return the day
+ * of the week of the first of january of that year. On error,
+ * return a negative number.
+ * Assumption: day_zero is the date of january 1, of some year.
+ */
+int
+ical_january_first(int year)
+{
+ int i, january_first;
+
+ if(year < day_zero.tm_year) return -1; /* not supported */
+
+ year += 1900;
+ january_first = day_zero.tm_wday;
+ for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
+ january_first += CAL_OFFSET(i-1);
+
+ return january_first % 7;
+}
+
+/* given a month, week day, and year, return all days of the month
+ * that have that day as the week day. For example, return all
+ * sundays in november 2012.
+ */
+int *
+ical_day_from_week(int month, Weekday day, int year)
+{
+ int *rv = NULL;
+ int fday, nday;
+ Weekday wday;
+ int i;
+
+ fday = ical_first_of_month(month, year);
+ year += 1900; /* restore year */
+ if(year == 1752 && month == 8){
+ fday = 9;
+ } else {
+ for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
+ ;
+ rv = fs_get(6*sizeof(int));
+ memset((void *)&rv, 0, 6*sizeof(int));
+ for(i = 0; nday <= month_len[month]; i++){
+ rv[i] = nday;
+ nday += 7;
+ }
+ if(LEAP_YEAR(year) && month == 1 && nday == 29)
+ rv[i] = nday;
+ }
+
+ return rv;
+}
+
+
+/* given a month and a year, return the weekday of the first of the
+ * month in that year.
+ * return value: on error -1, otherwise the day of the week.
+ */
+int
+ical_first_of_month(int month, int year)
+{
+ int i, d;
+
+ if((d = ical_january_first(year)) < 0)
+ return -1;
+
+ year += 1900;
+ for(i = 0; i < month; i++)
+ d += month_len[i];
+
+ if(LEAP_YEAR(year) && month >= 2)
+ d += 1;
+
+ if(year == 1752 && month >= 9)
+ d -= 11;
+
+ return d % 7;
+}
+
+/* given a day, month and year, return the weekday of that day
+ * return value: on error -1, otherwise the day of the week.
+ */
+int ical_day_of_week(struct tm date)
+{
+ int d;
+
+ if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
+ return -1;
+
+ d += date.tm_mday - 1;
+
+ if(date.tm_year + 1900 == 1752){
+ if(date.tm_mday > 2 && date.tm_mday < 14)
+ return -1;
+ if(date.tm_mday >= 14)
+ d -= 11;
+ }
+ return d % 7;
+}
+
+
+/* given an initial date dtstart, and a recurring rule, rrule,
+ * adjust the date to the first date on the same year, when
+ * the rule actually starts
+ */
+struct tm
+adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
+{
+ struct tm t;
+
+ memset((void *) &t, 0, sizeof(struct tm));
+ t.tm_year = dtstart->tm_year; /* same year */
+ if(rrule->prop[RRFreq]){
+ }
+ if(rrule->prop[RRCount]){
+ }
+ else if(rrule->prop[RRInterval]){
+ }
+ if(rrule->prop[RRBysecond]){
+ BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
+ for (seco = sec; seco != NULL; seco = seco->next)
+ if(seco == sec) t.tm_sec = seco->value;
+ else if (seco->value < t.tm_sec)
+ t.tm_sec = seco->value;
+ }
+ if (rrule->prop[RRByminute]){
+ BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
+ for (seco = sec; seco != NULL; seco = seco->next)
+ if(seco == sec) t.tm_min = seco->value;
+ else if (seco->value < t.tm_sec)
+ t.tm_min = seco->value;
+ }
+ if (rrule->prop[RRByhour]){
+ BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
+ for (seco = sec; seco != NULL; seco = seco->next)
+ if(seco == sec) t.tm_hour = seco->value;
+ else if (seco->value < t.tm_sec)
+ t.tm_hour = seco->value;
+ }
+ if (rrule->prop[RRByday]){
+ }
+ if (rrule->prop[RRByweekno]){
+ }
+ if (rrule->prop[RRBymonthday]){
+ }
+ if (rrule->prop[RRByyearday]){
+ }
+ if (rrule->prop[RRByweekno]){
+ }
+ if (rrule->prop[RRBymonth]){
+ BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
+ for (mo = m; mo != NULL; mo = mo->next)
+ if(mo == m) t.tm_mon = mo->value - 1;
+ else if (mo->value - 1 < t.tm_mon)
+ t.tm_mon = mo->value - 1;
+ }
+ if (rrule->prop[RRBysetpos]){
+ }
+ if (rrule->prop[RRWkst]){
+ }
+}
+
+void
+ical_initialize(void)
+{
+ ical_buf_len = 1024;
+ ical_buf = fs_get(ical_buf_len+1);
+
+ memset((void *) &day_zero, 0, sizeof(struct tm));
+ day_zero.tm_year = 1601 - 1900;
+ day_zero.tm_mday = 1;
+ day_zero.tm_wday = 4;
+}
+
+/* we create a summary of the event, and pass that back as
+ an ical parameter
+ */
+VEVENT_SUMMARY_S *
+ical_vevent_summary(VCALENDAR_S *vcal)
+{
+ VEVENT_SUMMARY_S *rv;
+ ICLINE_S *method;
+ VEVENT_S *vevent;
+ GEN_ICLINE_S *gicl;
+ ICLINE_S *icl;
+ char *k;
+
+ if(vcal == NULL) return NULL;
+
+ method = vcal->method;
+ vevent = (VEVENT_S *) vcal->comp[VEvent];
+
+ if(vevent == NULL || vevent->prop == NULL)
+ return NULL;
+
+ rv = fs_get(sizeof(VEVENT_SUMMARY_S));
+ memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
+
+ if(method != NULL && !strucmp(method->value, "CANCEL"))
+ rv->cancel++;
+
+ if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
+ rv->priority = atoi(icl->value);
+
+ if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL)
+ rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
+
+ if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
+ rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
+ else
+ rv->class = cpystr(_("PUBLIC"));
+
+ if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
+ char *cn, *sender, *address;
+ ICAL_PARAMETER_S *param;
+
+ cn = sender = address = NULL;
+ for(param = icl->param; param != NULL; param = param->next)
+ if(!strucmp(param->name, "CN"))
+ cn = param->value;
+ else if(!strucmp(param->name, "SENT-BY"))
+ sender = param->value;
+
+ if(sender != NULL){
+ if(!struncmp(sender, "MAILTO:", 7))
+ sender += 7;
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
+ rv->sender = cpystr(tmp_20k_buf);
+ }
+
+ if((address = icl->value) != NULL){
+ if(!struncmp(address, "MAILTO:", 7))
+ address += 7;
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
+ cn ? cn : "", cn ? " " : "",
+ address ? address : _("Unknown address"));
+ rv->organizer = cpystr(tmp_20k_buf);
+ }
+ } /* end of if(organizer) */
+
+ if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL)
+ rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
+
+ if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
+ struct tm ic_date;
+ char tmp[200];
+ int icd; /* ical date return value */
+
+ memset((void *)&ic_date, 0, sizeof(struct tm));
+ icd = ical_parse_date(icl->value, &ic_date);
+ if(icd >= 0){
+ ic_date.tm_wday = ical_day_of_week(ic_date);
+ switch(icd){
+ case 0: /* DATE-TIME */
+ our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
+ break;
+ case 1: /* DATE */
+ our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
+ break;
+ default: alpine_panic("Unhandled ical date format");
+ break;
+ }
+ }
+ else{
+ strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
+ tmp[sizeof(tmp) - 1] = '\0';
+ }
+ rv->evstart = cpystr(icl->value ? tmp : _("Unknown Start Date"));
+ } /* end of if dtstart */
+
+ if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
+ int i, done = 0;
+ ICAL_DURATION_S ic_d, icd2;
+ if(ical_parse_duration(icl->value, &ic_d) == 0){
+ char tmp[MAILTMPLEN+1];
+
+ for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
+ rv->duration = fs_get((i+1)*sizeof(char *));
+ i = 0;
+
+ do {
+ tmp[0] = '\0';
+
+ if(ic_d.weeks > 0)
+ utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
+ "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
+ if(ic_d.days > 0)
+ utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
+ "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
+ if(ic_d.hours > 0)
+ utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
+ "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
+ if(ic_d.minutes > 0)
+ utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
+ "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
+ if(ic_d.seconds > 0)
+ utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
+ "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
+
+ tmp[MAILTMPLEN] = '\0';
+ rv->duration[i++] = cpystr(tmp);
+
+ if(ic_d.next != NULL)
+ ic_d = *ic_d.next;
+ else
+ done++;
+ } while (done == 0);
+ rv->duration[i] = NULL;
+ }
+ } /* end of DURATION */
+ else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
+ struct tm ic_date;
+ char tmp[200];
+ int icd;
+
+ memset((void *)&ic_date, 0, sizeof(struct tm));
+ icd = ical_parse_date(icl->value, &ic_date);
+ if(icd >= 0){
+ ic_date.tm_wday = ical_day_of_week(ic_date);
+ switch(icd){
+ case 0: /* DATE-TIME */
+ our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
+ break;
+ case 1: /* DATE */
+ our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
+ break;
+ default: alpine_panic("Unhandled ical date format");
+ break;
+ }
+ }
+ else{
+ strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
+ tmp[sizeof(tmp) - 1] = '\0';
+ }
+ rv->evend = cpystr(icl->value ? tmp : _("Unknown End Date"));
+ } /* end of if dtend */
+
+ if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
+ int nattendees, i;
+
+ for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
+ rv->attendee = fs_get((nattendees+1)*sizeof(char *));
+
+ gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
+ for(i = 0; gicl != NULL; gicl = gicl->next, i++){
+ char *role, *partstat, *rsvp;
+ char *cn, *mailto;
+ ICAL_PARAMETER_S *param;
+
+ icl = gicl->cline;
+ role = partstat = rsvp = cn = mailto = NULL;
+ for(param = icl->param; param != NULL; param = param->next){
+ if(!strucmp(param->name, "ROLE")){
+ if(!strucmp(param->value, "REQ-PARTICIPANT"))
+ role = _("[Required]");
+ else if(!strucmp(param->value, "OPT-PARTICIPANT"))
+ role = _("[Optional]");
+ else if(!strucmp(param->value, "NON-PARTICIPANT"))
+ role = _("[Informed]");
+ else if(!strucmp(param->value, "CHAIR"))
+ role = _("[ Chair ]");
+ else
+ role = param->value;
+ }
+ else if(!strucmp(param->name, "PARTSTAT")){
+ if(!strucmp(param->value, "NEEDS-ACTION"))
+ partstat = _("[Need-Reply]");
+ else if(!strucmp(param->value, "ACCEPTED"))
+ partstat = _("[ Accepted ]");
+ else if(!strucmp(param->value, "DECLINED"))
+ partstat = _("[ Declined ]");
+ else if(!strucmp(param->value, "TENTATIVE"))
+ partstat = _("[ Tentative]");
+ else if(!strucmp(param->value, "DELEGATED"))
+ partstat = _("[ Delegated]");
+ else
+ partstat = param->value;
+ }
+ else if(!strucmp(param->name, "RSVP"))
+ rsvp = param->value;
+ else if(!strucmp(param->name, "CN"))
+ cn = param->value;
+ }
+ if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
+ mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
+ if(!strucmp(cn, mailto))
+ cn = "";
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
+ role && *role ? role : "",
+ role && *role ? " " : "",
+ partstat ? partstat : _("[Unknown Reply]"),
+ " ",
+ cn && *cn ? cn : "",
+ cn && *cn ? " " : "",
+ mailto ? mailto : _("Unknown address"));
+ rv->attendee[i] = cpystr(tmp_20k_buf);
+ }
+ rv->attendee[i] = NULL;
+ } /* end of ATTENDEES */
+
+ if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
+ char *s, *t, *u, *v;
+ int i, escaped;
+
+ if(icl->value == NULL){
+ free_vevent_summary(&rv);
+ return NULL;
+ }
+
+ v = cpystr(icl->value); /* process a copy of icl->value */
+
+ for(i = 1, escaped = 0, s = v; s && *s; s++){
+ if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
+ if(escaped){
+ if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
+ free_vevent_summary(&rv);
+ fs_give((void **)&v);
+ return NULL;
+ }
+ escaped = 0;
+ continue;
+ }
+ if(*s == ',') i++; /* a non-scaped comma is a new value for text */
+ }
+
+ rv->description = fs_get((i+1)*sizeof(char *));
+ i = 0;
+ for (s = t = u = v, escaped = 0; *t != '\0'; t++){
+ if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
+ if(escaped){
+ switch(*t){
+ case '\\':
+ case ',':
+ case ';':
+ *u++ = *t;
+ break;
+ case 'n':
+ case 'N':
+ *u++ = '\n';
+ break;
+ default: free_vevent_summary(&rv);
+ fs_give((void **)&v);
+ return NULL;
+ }
+ escaped = 0;
+ continue;
+ }
+ if(*t == ','){
+ *u = '\0';
+ rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
+ s = u = t+1;
+ } else
+ *u++ = *t;
+ }
+ *u = '\0';
+ rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
+ rv->description[i] = NULL;
+ fs_give((void **)&v);
+ } /* end of if(description) */
+
+ return rv;
+}
+
+void
+free_vevent_summary(VEVENT_SUMMARY_S **vesy)
+{
+ int i;
+ if(vesy == NULL || *vesy == NULL) return;
+
+ if((*vesy)->class) fs_give((void **)&(*vesy)->class);
+ if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
+ if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
+ if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
+ if((*vesy)->location) fs_give((void **)&(*vesy)->location);
+ if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
+ if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
+ if((*vesy)->duration){
+ for(i = 0; (*vesy)->duration[i] != NULL; i++)
+ fs_give((void **) &(*vesy)->duration[i]);
+ fs_give((void **) (*vesy)->duration);
+ }
+ if((*vesy)->attendee){
+ for(i = 0; (*vesy)->attendee[i] != NULL; i++)
+ fs_give((void **) &(*vesy)->attendee[i]);
+ fs_give((void **) (*vesy)->attendee);
+ }
+ if((*vesy)->description){
+ for(i = 0; (*vesy)->description[i] != NULL; i++)
+ fs_give((void **) &(*vesy)->description[i]);
+ fs_give((void **) (*vesy)->description);
+ }
+ fs_give((void **) vesy);
+}
+
+#ifdef STANDALONE
+void ical_print_line(ICLINE_S icl)
+{
+ ICAL_PARAMETER_S *p;
+ printf("token=%s, ", icl.token);
+ for(p = icl.param; p != NULL; p = p->next){
+ if(p->name) printf("param->name=%s, ", p->name);
+ if(p->value) printf("param->value=%s, ", p->value);
+ }
+ printf("value=%s", icl.value);
+ printf("%c", '\n');
+}
+
+#define CRLFCOUNT 100
+
+char *lf2crlf(char *text)
+{
+ char *rv, *s;
+ int i, len;
+
+ if(text == NULL || *text == '\0')
+ return NULL;
+
+ len = strlen(text);
+ rv = fs_get(len+1);
+
+ for(i = 0, s = text; *s;){
+ if(i == len - 1){
+ len += CRLFCOUNT;
+ fs_resize((void *)&rv, len);
+ }
+ if(*s == '\n')
+ rv[i++] = '\r';
+ rv[i++] = *s++;
+ }
+ rv[i] = '\0';
+ fs_give((void **)&text);
+ text = rv;
+
+ return rv;
+}
+
+int main (int argc, char *argv[])
+{
+ char *text;
+ VCALENDAR_S *vcal;
+
+ if(argc != 2){
+ fprintf(stderr, "Only give one argument: File name to read\n");
+ _exit(1);
+ }
+
+ if(readfile(argv[1], &text, NULL) < 0){
+ fprintf(stderr, "Can not read %s succesfully\n", argv[1]);
+ _exit(2);
+ }
+
+ text = lf2crlf(text);
+ vcal = ical_parse_text(text);
+
+ ical_free_vcalendar((void **)&vcal);
+ return 0;
+}
+
+
+#endif /* STANDALONE */
diff --git a/pith/ical.h b/pith/ical.h
new file mode 100644
index 00000000..7ff5865a
--- /dev/null
+++ b/pith/ical.h
@@ -0,0 +1,18 @@
+#ifndef PITH_ICAL_INCLUDED
+#define PITH_ICAL_INCLUDED
+
+#include "../pith/icaltype.h"
+
+void ical_free_vcalendar(void **);
+void ical_free_duration(ICAL_DURATION_S **ic_d);
+int ical_first_of_month(int, int); /* args: month, year - in that order */
+int ical_day_of_week(struct tm); /* args: a time structure */
+int ical_parse_date(char *, struct tm *);
+int ical_parse_duration(char *, ICAL_DURATION_S *);
+int ical_remove_escapes(char **);
+VEVENT_SUMMARY_S *ical_vevent_summary(VCALENDAR_S *);
+void free_vevent_summary(VEVENT_SUMMARY_S **);
+
+VCALENDAR_S *ical_parse_text(char *); /* this is the entry point */
+
+#endif /* ifndef PITH_ICAL_INCLUDED */
diff --git a/pith/icaltype.h b/pith/icaltype.h
new file mode 100644
index 00000000..8060a412
--- /dev/null
+++ b/pith/icaltype.h
@@ -0,0 +1,477 @@
+#ifndef ICALTYPE_INCLUDED
+#define ICALTYPE_INCLUDED
+
+#ifdef STANDALONE
+#define ADDRESS struct mail_address
+
+ADDRESS {
+ char *personal; /* personal name phrase */
+ char *adl; /* at-domain-list source route */
+ char *mailbox; /* mailbox name */
+ char *host; /* domain name of mailbox's host */
+ char *error; /* error in address from SMTP module */
+ struct {
+ char *type; /* address type (default "rfc822") */
+ char *addr; /* address as xtext */
+ } orcpt;
+ ADDRESS *next; /* pointer to next address in list */
+};
+#endif /* STANDALONE */
+
+/* this is the PARAMETER struct from mail.h, but with possibility of
+ * extension */
+typedef struct ical_param_s {
+ char *name;
+ char *value;
+ struct ical_param_s *next;
+} ICAL_PARAMETER_S;
+
+#define ICAL_S struct ical_s
+
+ICAL_S {
+ char *comp; /* component name */
+ void *value;
+ ICAL_S *branch; /* component in same level */
+ ICAL_S *next; /* component at next level */
+};
+
+typedef struct icline_s {
+ char *token;
+ ICAL_PARAMETER_S *param;
+ char *value; /* this could need further processing, so consider this a raw value */
+} ICLINE_S;
+
+typedef struct gen_icline_s {
+ ICLINE_S *cline;
+ struct gen_icline_s *next;
+} GEN_ICLINE_S;
+
+typedef enum {FSecondly, FMinutely, FHourly, FDaily, FWeekly,
+ FMonthly, FYearly, FUnknown} Freq_value;
+
+/* believe it or not, I did not need to copy these values from
+ * RFC 5545! */
+typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday,
+ Friday, Saturday} Weekday;
+
+typedef struct ical_weekday_s {
+ int value; /* number value being parsed */
+ Weekday wd;
+ struct ical_weekday_s *next;
+} BYWKDY_S;
+
+typedef struct ical_rrule_s {
+#if 0
+ Freq_value freq; /* required, at most one */
+ ICLINE_S *until; /* optional, must not occur with count */
+ ICLINE_S *count; /* optional, must not occur with until */
+ unsigned long interval; /* optional, at most one */
+ BYWKDY_S *bysecond; /* optional, at most one */
+ ICLINE_S *byminute; /* optional, at most one */
+ ICLINE_S *byhour; /* optional, at most one */
+ BYWKDY_S *byday; /* optional, at most one */
+ ICLINE_S *bymonthday; /* optional, at most one */
+ ICLINE_S *byyearday; /* optional, at most one */
+ ICLINE_S *byweekno; /* optional, at most one */
+ BYWKDY_S *bymonth; /* optional, at most one, only use value */
+ ICLINE_S *bysetpos; /* optional, at most one */
+ ICLINE_S *wkst; /* optional, at most one */
+#endif /* if 0 */
+ void **prop; /* the list of properties of a recurring rule */
+ ICAL_PARAMETER_S *param;
+} RRULE_S;
+
+typedef struct valarm_s {
+#if 0
+ ICLINE_S *action; /* required, at most one */
+ ICLINE_S *trigger; /* required, at most one */
+ ICLINE_S *duration; /* audio,display,email.optional, at most one. Must occur with repeat */
+ ICLINE_S *repeat; /* audio,display,email.optional, at most one. Must occur with duration */
+ ICLINE_S *description; /* display,email.required, at most one */
+ ICLINE_S *summary; /* email.required, at most one */
+ GEN_ICLINE_S *attach; /* audio.optional, at most one;email.optional, may occur more than once */
+ GEN_ICLINE_S *attendee; /* email.required, may occur more than once */
+#endif /* if 0 */
+ void **prop; /* an array of all properties of an alarm */
+ GEN_ICLINE_S *uk_prop; /* optional, may occur more than once */
+ struct valarm_s *next;
+} VALARM_S;
+
+typedef struct vevent_s {
+#if 0
+ ICLINE_S *dtstamp; /* required, at most one */
+ ICLINE_S *uid; /* required, at most one */
+ ICLINE_S *dtstart; /* required if METHOD not specified, at most one */
+ ICLINE_S *class; /* optional, at most one */
+ ICLINE_S *created; /* optional, at most one */
+ ICLINE_S *description;/* optional, at most one */
+ ICLINE_S *geo; /* optional, at most one */
+ ICLINE_S *lastmod; /* optional, at most one */
+ ICLINE_S *location; /* optional, at most one */
+ ICLINE_S *organizer; /* optional, at most one */
+ ICLINE_S *priority; /* optional, at most one */
+ ICLINE_S *seq; /* optional, at most one */
+ ICLINE_S *status; /* optional, at most one */
+ ICLINE_S *summary; /* optional, at most one */
+ ICLINE_S *transp; /* optional, at most one */
+ ICLINE_S *url; /* optional, at most one */
+ ICLINE_S *recurid; /* optional, at most one */
+ RRULE_S *rrule; /* optional, at most one */
+ ICLINE_S *dtend; /* optional, at most one, exclude duration */
+ ICLINE_S *duration; /* optional, at most one, exclude dtend */
+ GEN_ICLINE_S *attach; /* optional, may occur more than once */
+ GEN_ICLINE_S *attendee; /* optional, may occur more than once */
+ GEN_ICLINE_S *categories; /* optional, may occur more than once */
+ GEN_ICLINE_S *comment; /* optional, may occur more than once */
+ GEN_ICLINE_S *contact; /* optional, may occur more than once */
+ GEN_ICLINE_S *exdate; /* optional, may occur more than once */
+ GEN_ICLINE_S *rstatus; /* optional, may occur more than once */
+ GEN_ICLINE_S *related; /* optional, may occur more than once */
+ GEN_ICLINE_S *resources; /* optional, may occur more than once */
+ GEN_ICLINE_S *rdate; /* optional, may occur more than once */
+#endif /* if 0 */
+ void **prop; /* the properties of an event component */
+ GEN_ICLINE_S *uk_prop; /* unknown values */
+ VALARM_S *valarm; /* possible valarm */
+} VEVENT_S;
+
+typedef struct vtodo_s {
+ ICLINE_S *dtstamp; /* required, at most one */
+ ICLINE_S *uid; /* required, at most one */
+ ICLINE_S *class; /* optional, at most one */
+ ICLINE_S *completed; /* optional, at most one */
+ ICLINE_S *created; /* optional, at most one */
+ ICLINE_S *description;/* optional, at most one */
+ ICLINE_S *dtstart; /* optional, at most one */
+ ICLINE_S *geo; /* optional, at most one */
+ ICLINE_S *lastmod; /* optional, at most one */
+ ICLINE_S *location; /* optional, at most one */
+ ICLINE_S *organizer; /* optional, at most one */
+ ICLINE_S *percent; /* optional, at most one */
+ ICLINE_S *priority; /* optional, at most one */
+ ICLINE_S *recurid; /* optional, at most one */
+ ICLINE_S *seq; /* optional, at most one */
+ ICLINE_S *status; /* optional, at most one */
+ ICLINE_S *summary; /* optional, at most one */
+ ICLINE_S *url; /* optional, at most one */
+ RRULE_S *rrule; /* optional, at most one */
+ ICLINE_S *due; /* optional, at most one, but exclude duration */
+ ICLINE_S *duration; /* optional, at most one, but exclude due, and add dtstart */
+ ICLINE_S *attach; /* optional, can appear more than once */
+ ICLINE_S *attendee; /* optional, can appear more than once */
+ ICLINE_S *categories; /* optional, can appear more than once */
+ ICLINE_S *comment; /* optional, can appear more than once */
+ ICLINE_S *contact; /* optional, can appear more than once */
+ ICLINE_S *exdate; /* optional, can appear more than once */
+ ICLINE_S *rstatus; /* optional, can appear more than once */
+ ICLINE_S *related; /* optional, can appear more than once */
+ ICLINE_S *resources; /* optional, can appear more than once */
+ ICLINE_S *rdate; /* optional, can appear more than once */
+ ICAL_PARAMETER_S *unknown; /* unknown values */
+ VALARM_S *valarm; /* optional valarm */
+} VTODO_S;
+
+typedef struct journal_s {
+ ICLINE_S *dtstamp; /* required, at most one */
+ ICLINE_S *uid; /* required, at most one */
+ ICLINE_S *class; /* optional, at most one */
+ ICLINE_S *created; /* optional, at most one */
+ ICLINE_S *dtstart; /* optional, at most one */
+ ICLINE_S *lastmod; /* optional, at most one */
+ ICLINE_S *organizer; /* optional, at most one */
+ ICLINE_S *recurid; /* optional, at most one */
+ ICLINE_S *seq; /* optional, at most one */
+ ICLINE_S *status; /* optional, at most one */
+ ICLINE_S *summary; /* optional, at most one */
+ ICLINE_S *url; /* optional, at most one */
+ RRULE_S *rrule; /* optional, at most one */
+ ICLINE_S *attach; /* optional, may occur more than once */
+ ICLINE_S *attendee; /* optional, may occur more than once */
+ ICLINE_S *categories; /* optional, may occur more than once */
+ ICLINE_S *comment; /* optional, may occur more than once */
+ ICLINE_S *contact; /* optional, may occur more than once */
+ ICLINE_S *description;/* optional, may occur more than once */
+ ICLINE_S *exdate; /* optional, may occur more than once */
+ ICLINE_S *related; /* optional, may occur more than once */
+ ICLINE_S *rdate; /* optional, may occur more than once */
+ ICLINE_S *rstatus; /* optional, may occur more than once */
+ ICAL_PARAMETER_S *unknown; /* unknown values */
+} VJOURNAL_S;
+
+typedef struct freebusy_s {
+ ICLINE_S *dtstamp; /* required, at most one */
+ ICLINE_S *uid; /* required, at most one */
+ ICLINE_S *contact; /* optional, at most one */
+ ICLINE_S *dtstart; /* optional, at most one */
+ ICLINE_S *dtend; /* optional, at most one */
+ ICLINE_S *organizer; /* optional, at most one */
+ ICLINE_S *url; /* optional, at most one */
+ ICLINE_S *attendee; /* optional, may appear more than oncece */
+ ICLINE_S *comment; /* optional, may appear more than oncece */
+ ICLINE_S *freebusy; /* optional, may appear more than oncece */
+ ICLINE_S *rstatus; /* optional, may appear more than once */
+ ICAL_PARAMETER_S *unknown; /* unknown values */
+} VFREEBUSY_S;
+
+typedef struct ical_tzprop_s {
+#if 0
+ struct tm dtstart; /* required, at most one */
+ int tzoffsetto; /* required, at most one */
+ int tzoffsetfrom; /* required, at most one */
+ RRULE_S *rrule; /* optional, at most one */
+ GEN_ICLINE_S *comment; /* optional, may appear more than once */
+ GEN_ICLINE_S *rdate; /* optional, may appear more than once */
+ GEN_ICLINE_S *tzname; /* optional, may appear more than once */
+#endif /* if 0 */
+ void **prop; /* the bunch of timezone properties */
+ GEN_ICLINE_S *uk_prop; /* optional, may appear more than once */
+ struct ical_tzprop_s *next;
+} ICAL_TZPROP_S;
+
+typedef struct vtimezone_s {
+#if 0
+ ICLINE_S *tzid; /* required, at most one */
+ ICLINE_S *last_mod; /* optional, at most one */
+ ICLINE_S *tzurl; /* optional, at most one */
+#endif /* if 0 */
+ void **prop; /* array of timezone properties */
+ ICAL_TZPROP_S *standardc; /* optional, may appear more than once */
+ ICAL_TZPROP_S *daylightc; /* optional, may appear more than once */
+ GEN_ICLINE_S *uk_prop; /* optional, may appear more than once */
+} VTIMEZONE_S;
+
+typedef struct vcalendar_s {
+ ICLINE_S *prodid; /* required, at most one */
+ ICLINE_S *version; /* required, at most one */
+ ICLINE_S *calscale; /* optional, at most one */
+ ICLINE_S *method; /* optional, at most one */
+ GEN_ICLINE_S *uk_prop;/* in case there is an unknown property */
+ void **comp; /* an array with the components of a calendar */
+ ICAL_S *uk_comp; /* in case there is an unknown component */
+ unsigned short encoding; /* save the original encoding. */
+} VCALENDAR_S;
+
+
+typedef enum {Individual, Group, Resource, Room, CUUnknown} CUType;
+typedef enum {EventNeedsAction, EventAccepted, EventDeclined, EventTentative,
+ EventDelegated, EventUnknown,
+ TodoNeedsAction, TodoAccepted, TodoDeclined, TodoTentative,
+ TodoDelegated, TodoCompleted, TodoInProgress, TodoUnknown,
+ JournalNeedsAction, JournalAccepted, JournalDeclined,
+ JournalUnknown} PartStat;
+typedef enum {RelBegin, RelEnd, RelUnknown} Related;
+typedef enum {Parent, Child, Sibling} RelType;
+typedef enum {ReqPart, Chair, OptPart, NonPart, RoleUnknown} Role;
+typedef enum {RSVPFalse, RSVPTrue, RSVPUnknown} RSVP;
+typedef enum {Binary, Boolean, CalAddress, Date, DateTime, Duration,
+ Float, Integer, Period, Recur, _Text, Time, Uri,
+ UtcOffset, ValueUnknown} Value;
+
+typedef struct icalpar_s {
+ ICLINE_S *altrep; /* altrep uri, inside dquote RFC 2392, RFC 2616 and RFC 2818 */
+ ICLINE_S *cn; /* common name */
+ CUType cutype; /* calendar user type: individual, group, resource, room, unknown */
+ ADDRESS *delegated_from; /* person(s) who delegated their participation */
+ ADDRESS *delegated_to; /* person that was delegated participation */
+ ICLINE_S *dir; /* reference to a directory entry associated with the calendar user specified by the property. */
+ unsigned short encoding; /* encoding, either 8bit or base64 */
+ unsigned short type; /* type for FMTTYPE */
+ ICLINE_S *subtype; /* subtype for FMTTYPE */
+ ICLINE_S *fbtype; /* FreeBusy type */
+ ICLINE_S *language; /* language */
+ ADDRESS *member; /* group or list membership of the calendar user specified by the property */
+ PartStat partstat; /* participation status for the calendar user specified by the property. */
+ ICLINE_S *range; /* this and future */
+ Related related; /* relationship of the alarm trigger with respect to the start or end of the calendar component. */
+ RelType reltype; /* type of hierarchical relationship associated with the calendar component specified by the property. */
+ Role role; /* participation role for the calendar user specified by the property. */
+ RSVP rsvp; /* whether there is an expectation of a favor of a reply from the calendar user specified by the property value. */
+ ADDRESS *sentby; /* the calendar user that is acting on behalf of the calendar user specified by the property. */
+ ICLINE_S *tzid; /* the identifier for the time zone definition for a time component in the property value. */
+ Value value; /* specify the value type format for a property value. */
+} ICALPAR_S;
+
+typedef struct ical_duration_s {
+ unsigned sign:1; /* sign = 0 for positive, sign = 1 for negative */
+ unsigned weeks;
+ unsigned days;
+ unsigned hours;
+ unsigned minutes;
+ unsigned seconds;
+ struct ical_duration_s *next; /* another duration */
+} ICAL_DURATION_S;
+
+/* altrepparam = "ALTREP" "=" DQUOTE uri DQUOTE
+ * cnparam = "CN" "=" param-value
+ * cutypeparam = "CUTYPE" "="
+ ("INDIVIDUAL" ; An individual
+ / "GROUP" ; A group of individuals
+ / "RESOURCE" ; A physical resource
+ / "ROOM" ; A room resource
+ / "UNKNOWN" ; Otherwise not known
+ / x-name ; Experimental type
+ / iana-token) ; Other IANA-registered
+ ; type
+ * delfromparam = "DELEGATED-FROM" "=" DQUOTE cal-address
+ DQUOTE *("," DQUOTE cal-address DQUOTE)
+
+ * deltoparam = "DELEGATED-TO" "=" DQUOTE cal-address DQUOTE
+ *("," DQUOTE cal-address DQUOTE)
+
+ * dirparam = "DIR" "=" DQUOTE uri DQUOTE
+
+ * encodingparam = "ENCODING" "="
+ ( "8BIT"
+ ; "8bit" text encoding is defined in [RFC2045]
+ / "BASE64"
+ ; "BASE64" binary encoding format is defined in [RFC4648]
+ )
+ * fmttypeparam = "FMTTYPE" "=" type-name "/" subtype-name
+ ; Where "type-name" and "subtype-name" are
+ ; defined in Section 4.2 of [RFC4288].
+
+ * fbtypeparam = "FBTYPE" "=" ("FREE" / "BUSY"
+ / "BUSY-UNAVAILABLE" / "BUSY-TENTATIVE"
+ / x-name
+ ; Some experimental iCalendar free/busy type.
+ / iana-token)
+ ; Some other IANA-registered iCalendar free/busy type.
+
+ * languageparam = "LANGUAGE" "=" language
+
+ language = Language-Tag
+ ; As defined in [RFC5646].
+
+ * memberparam = "MEMBER" "=" DQUOTE cal-address DQUOTE
+ *("," DQUOTE cal-address DQUOTE)
+
+ * partstatparam = "PARTSTAT" "="
+ (partstat-event
+ / partstat-todo
+ / partstat-jour)
+
+ partstat-event = ("NEEDS-ACTION" ; Event needs action
+ / "ACCEPTED" ; Event accepted
+ / "DECLINED" ; Event declined
+ / "TENTATIVE" ; Event tentatively
+ ; accepted
+ / "DELEGATED" ; Event delegated
+ / x-name ; Experimental status
+ / iana-token) ; Other IANA-registered
+ ; status
+ ; These are the participation statuses for a "VEVENT".
+ ; Default is NEEDS-ACTION.
+
+ partstat-todo = ("NEEDS-ACTION" ; To-do needs action
+ / "ACCEPTED" ; To-do accepted
+ / "DECLINED" ; To-do declined
+ / "TENTATIVE" ; To-do tentatively
+ ; accepted
+ / "DELEGATED" ; To-do delegated
+ / "COMPLETED" ; To-do completed
+ ; COMPLETED property has
+ ; DATE-TIME completed
+ / "IN-PROCESS" ; To-do in process of
+ ; being completed
+ / x-name ; Experimental status
+ / iana-token) ; Other IANA-registered
+ ; status
+ ; These are the participation statuses for a "VTODO".
+ ; Default is NEEDS-ACTION.
+
+
+
+ partstat-jour = ("NEEDS-ACTION" ; Journal needs action
+ / "ACCEPTED" ; Journal accepted
+ / "DECLINED" ; Journal declined
+ / x-name ; Experimental status
+ / iana-token) ; Other IANA-registered
+ ; status
+ ; These are the participation statuses for a "VJOURNAL".
+ ; Default is NEEDS-ACTION.
+
+ * rangeparam = "RANGE" "=" "THISANDFUTURE"
+ ; To specify the instance specified by the recurrence identifier
+ ; and all subsequent recurrence instances.
+
+ * trigrelparam = "RELATED" "="
+ ("START" ; Trigger off of start
+ / "END") ; Trigger off of end
+
+ * reltypeparam = "RELTYPE" "="
+ ("PARENT" ; Parent relationship - Default
+ / "CHILD" ; Child relationship
+ / "SIBLING" ; Sibling relationship
+ / iana-token ; Some other IANA-registered
+ ; iCalendar relationship type
+ / x-name) ; A non-standard, experimental
+ ; relationship type
+
+ * roleparam = "ROLE" "="
+ ("CHAIR" ; Indicates chair of the
+ ; calendar entity
+ / "REQ-PARTICIPANT" ; Indicates a participant whose
+ ; participation is required
+ / "OPT-PARTICIPANT" ; Indicates a participant whose
+ ; participation is optional
+ / "NON-PARTICIPANT" ; Indicates a participant who
+ ; is copied for information
+ ; purposes only
+ / x-name ; Experimental role
+ / iana-token) ; Other IANA role
+ ; Default is REQ-PARTICIPANT
+
+ * rsvpparam = "RSVP" "=" ("TRUE" / "FALSE")
+ ; Default is FALSE
+
+ * sentbyparam = "SENT-BY" "=" DQUOTE cal-address DQUOTE
+
+ * tzidparam = "TZID" "=" [tzidprefix] paramtext
+
+ tzidprefix = "/"
+
+ */
+
+typedef struct vevent_summary_s {
+ int cancel:1;
+ int priority;
+ char *class;
+ char *summary;
+ char *sender;
+ char *organizer;
+ char *location;
+ char *evstart;
+ char *evend;
+ char **duration;
+ char **attendee;
+ char **description;
+} VEVENT_SUMMARY_S;
+
+typedef enum {VCalendar = 0, VTimeZone, VEvent, VTodo, VJournal,
+ VAlarm, VFreeBusy, VUnknown} Cal_comp;
+
+typedef enum {EvDtstamp = 0, EvUid, EvDtstart, EvClass, EvCreated,
+ EvDescription, EvGeo, EvLastMod, EvLocation,
+ EvOrganizer, EvPriority, EvSequence, EvStatus,
+ EvSummary, EvTransp, EvUrl, EvRecurrence, EvRrule,
+ EvDtend, EvDuration, EvAttach, EvAttendee, EvCategories,
+ EvComment, EvContact, EvExdate, EvRstatus, EvRelated,
+ EvResources, EvRdate,
+ EvUnknown} Event_prop;
+
+typedef enum {TZCid = 0, TZCLastMod, TZCUrl, TZCUnknown} TZ_comp;
+
+typedef enum {TZPDtstart = 0, TZPOffsetto, TZPOffsetfrom,
+ TZPRrule, TZPComment, TZPRdate, TZPTzname, TZPUnknown} TZ_prop;
+
+typedef enum {AlAction = 0, AlTrigger, AlDuration, AlRepeat,
+ AlDescription, AlSummary, AlAttach, AlAttendee,
+ AlUnknown} Alarm_prop;
+
+typedef enum {RRFreq, RRUntil, RRCount, RRInterval,
+ RRBysecond, RRByminute, RRByhour, RRByday,
+ RRByweekno, RRBymonth, RRBysetpos, RRWkst,
+ RRBymonthday, RRByyearday,
+ RRUnknown} RRule_prop;
+
+#endif /* ifndef ICALTYPE_INCLUDED */
diff --git a/pith/mailpart.h b/pith/mailpart.h
index aad3fdc4..076ed174 100644
--- a/pith/mailpart.h
+++ b/pith/mailpart.h
@@ -32,6 +32,9 @@
|| ((t) == TYPETEXT \
&& (s) && !strucmp((s),"X-VCARD")))
+/* Is this a VCalendar type? */
+#define MIME_VCALENDAR(t,s) ((t) == TYPETEXT && (s) && !strucmp((s),"CALENDAR"))
+
/* Is this a multipart signed? */
#define MIME_MULT_SIGNED(t,s) ((t) == TYPEMULTIPART && (s) && !strucmp((s),"signed"))
@@ -44,6 +47,9 @@
/* Is this a vCard attachment? */
#define MIME_VCARD_A(a) MIME_VCARD((a)->body->type, (a)->body->subtype)
+/* Is this a VCalendar attachment? */
+#define MIME_VCALENDAR_A(a) MIME_VCALENDAR((a)->body->type, (a)->body->subtype)
+
/* exported protoypes */
diff --git a/pith/mailview.c b/pith/mailview.c
index ad3efd6d..dc9b29b6 100644
--- a/pith/mailview.c
+++ b/pith/mailview.c
@@ -25,6 +25,9 @@ static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washin
#include "headers.h"
+#include "../pith/ical.h"
+#include "../pith/body.h"
+#include "../pith/mailpart.h"
#include "../pith/mailview.h"
#include "../pith/conf.h"
#include "../pith/msgno.h"
@@ -123,7 +126,7 @@ int find_field(char **, char *, size_t);
int embed_color(COLOR_PAIR *, gf_io_t);
COLOR_PAIR *get_cur_embedded_color(void);
void clear_cur_embedded_color(void);
-
+void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
@@ -192,8 +195,10 @@ format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
if(!(body == NULL
|| (ps_global->full_header == 2
- && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))))
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
format_attachment_list(msgno, body, handlesp, flgs, width, pc);
+ format_calendar(msgno, body, handlesp, flgs, width, pc);
+ }
/* write delimiter and body */
if(gf_puts(NEWLINE, pc)
@@ -210,6 +215,328 @@ format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
return(0);
}
+void
+format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
+{
+ int avail, m1, m2, hwid, i, partwid, padwid;
+ int s1, s2, dwid;
+ int *margin;
+ char padding[1024];
+ VEVENT_SUMMARY_S *vesy; /* vevent summary */
+
+ vesy = ical_vevent_summary(vcal);
+
+ if(vesy == NULL) return;
+
+ if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
+ cflags |= ~FC_FULL;
+
+ avail = width;
+ margin = (cflags & FC_FULL) ? NULL
+ : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
+
+ m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
+ avail -= m1;
+
+ m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
+ avail -= m2;
+
+ hwid = MAX(avail, 0);
+ padwid = 0;
+
+ if(cflags & FC_SUMMARY){
+ i = utf8_width(_("Calendar Entry:"));
+ partwid = MIN(i, hwid);
+ if(m1 > 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp_20k_buf, pc))
+ return;
+ }
+
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
+ partwid, partwid, _("Calendar Entry:"),
+ padwid, padwid, "");
+
+ if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
+ return;
+ }
+ else
+ partwid = 0;
+
+ if(m1 > 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp_20k_buf, pc))
+ return;
+ }
+
+ avail = width - m1 - m2;
+
+ s1 = MAX(MIN(1, avail), 0);
+ avail -= s1;
+
+ dwid = MAX(MIN(1, avail), 0);
+ avail -= dwid;
+
+ s2 = MAX(MIN(1, avail), 0);
+ avail -= s2;
+
+ if(cflags & FC_SUMMARY)
+ utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
+ s1, s1, "", dwid, dwid, "", s2, s2, "");
+ else
+ padding[0] = '\0';
+
+ if(vesy->cancel){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ padding, _("This event was cancelled!"));
+ if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ (*pc)(TAG_EMBED);
+ (*pc)(TAG_BOLDOFF);
+ }
+ }
+
+ if(vesy->organizer){
+ if(vesy->sender != NULL){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("Sent-by: "), vesy->sender);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("Organizer: "), vesy->organizer);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ } /* end of if(organizer) */
+
+ if(vesy->location){
+ ical_remove_escapes(&vesy->location);
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("Location: "), vesy->location);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ } /* end of if location */
+
+ if(vesy->evstart){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("Start Date: "), vesy->evstart);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ } /* end of if dtstart */
+
+ if(vesy->duration){
+ int i;
+
+ for(i = 0; vesy->duration[i] != NULL; i++){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("Duration: "), vesy->duration[i]);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ }
+ } /* end of DURATION */
+ else if(vesy->evend){
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding, _("End Date: "), vesy->evend);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ } else { /* end of if dtend */
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
+ padding, _("No duration nor end time found for this event"));
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ } /* end of else for if (duration) and if (dtend) */
+
+ if(vesy->attendee){
+#define MAX_DISPLAYED 3
+ int i;
+
+ for(i = 0; vesy->attendee[i] != NULL; i++){
+ if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
+ break;
+
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ padding,
+ _("Attendee: "), vesy->attendee[i]);
+ gf_puts(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ }
+ if(cflags & FC_SUMMARY){
+ COLOR_PAIR *lastc = NULL;
+ char numbuf[50];
+ int thisdescwid;
+ COLOR_PAIR *hdrcolor = NULL;
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ && ps_global->VAR_NORM_FORE_COLOR
+ && ps_global->VAR_NORM_BACK_COLOR
+ && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))){
+
+ if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
+ if(!pico_is_good_colorpair(hdrcolor))
+ free_color_pair(&hdrcolor);
+ }
+ }
+
+ if(!(!hdrcolor || embed_color(hdrcolor, pc)))
+ return;
+
+ gf_puts(padding, pc);
+ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
+
+ if(handlesp){
+ char buf[16], color[64];
+ int l;
+ HANDLE_S *h;
+
+ h = new_handle(handlesp);
+ h->type = Attach;
+ h->h.attach = a;
+
+ snprintf(buf, sizeof(buf), "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+
+ if(!(flgs & FM_NOCOLOR)
+ && handle_start_color(color, sizeof(color), &l, 1)){
+ lastc = get_cur_embedded_color();
+ if(!gf_nputs(color, (long) l, pc))
+ return;
+ }
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
+ && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
+ return;
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
+ return;
+ } else
+ tmp_20k_buf[0] = '\0';
+
+ if(!format_env_puts(tmp_20k_buf, pc))
+ return;
+
+ if(handlesp){
+ if(lastc){
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return;
+ }
+
+ if(!embed_color(lastc, pc))
+ return;
+
+ free_color_pair(&lastc);
+ }
+ else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return;
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
+ return;
+ }
+
+ if(padwid > 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
+ if(!gf_puts(tmp_20k_buf, pc))
+ return;
+ }
+
+ if(!gf_puts(NEWLINE, pc))
+ return;
+ }
+ } /* end of ATTENDEES */
+
+ free_vevent_summary(&vesy);
+
+ avail = width - m1 -2;
+
+ dwid = MAX(MIN(40, avail), 0);
+ avail -= dwid;
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
+ repeat_char(dwid, '-'));
+ gf_puts(tmp_20k_buf, pc);
+}
+
+int
+format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
+{
+ char *rawtext, *caltext;
+ unsigned long callen;
+ VCALENDAR_S *vcal = NULL;
+ ATTACH_S *a;
+ BODY *b;
+
+ if(flgs & FM_NEW_MESS) {
+ zero_atmts(ps_global->atmts);
+ describe_mime(body, "", 1, 1, 0, flgs);
+ }
+
+ for(a = ps_global->atmts; a->description != NULL; a++){
+ if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
+ b = mail_body (ps_global->mail_stream, msgno, a->number);
+ if(b == NULL){
+ gf_puts(_("Error fetching calendar body part"), pc);
+ gf_puts(NEWLINE, pc);
+ continue;
+ }
+ if(b->sparep == NULL){
+ rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
+ if(rawtext == NULL || *rawtext == '\0'){
+ gf_puts(_("Error fetching calendar text"), pc);
+ gf_puts(NEWLINE, pc);
+ continue;
+ }
+ rawtext[callen] = '\0'; /* chop off cookie */
+ switch(b->encoding){
+ case ENCBASE64:
+ caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
+ if(caltext == NULL){
+ gf_puts(_("Error in calendar base64 encoding"), pc);
+ gf_puts(NEWLINE, pc);
+ continue;
+ }
+ break;
+
+ case ENCQUOTEDPRINTABLE:
+ caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
+ if(caltext == NULL){
+ gf_puts(_("Error in calendar quoted printable encoding"), pc);
+ gf_puts(NEWLINE, pc);
+ continue;
+ }
+ break;
+
+ default: caltext = cpystr(rawtext);
+ }
+ if(caltext != NULL){
+ vcal = ical_parse_text(caltext);
+ if(vcal != NULL) vcal->encoding = b->encoding;
+ b->sparep = create_body_sparep(iCalType, (void *) vcal);
+ fs_give((void **) &caltext);
+ }
+ }
+ else if(get_body_sparep_type(b->sparep) == iCalType)
+ vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
+ if(vcal != NULL && vcal->comp != NULL){
+ if(vcal->comp[VEvent] != NULL){
+ format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
+ } /* else another type of entry in the calendar */
+ }
+ gf_puts(NEWLINE, pc);
+ }
+ }
+ return 0;
+}
+
char *
format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
@@ -336,6 +663,8 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f
/*======== Now loop through formatting all the parts =======*/
for(a = ps_global->atmts; a->description != NULL; a++) {
+ if(MIME_VCALENDAR(a->body->type, a->body->subtype))
+ continue;
if(a->body->type == TYPEMULTIPART){
#ifdef SMIME
@@ -424,8 +753,11 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f
#endif /* SMIME */
)
&& !(flgs & FM_NOEDITORIAL)){
- tmp1 = a->body->description ? a->body->description
- : "Attached Text";
+ if(MIME_VCALENDAR(a->body->type, a->body->subtype))
+ tmp1 = "Calendar entry";
+ else
+ tmp1 = a->body->description ? a->body->description
+ : "Attached Text";
description = iutf8ncpy((char *)(tmp_20k_buf+10000),
(char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
@@ -437,8 +769,9 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f
&& gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
return("Write Error");
}
-
- error_found += decode_text(a, msgno, pc, handlesp,
+ /* skip calendar */
+ if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
+ error_found += decode_text(a, msgno, pc, handlesp,
(flgs & FM_DISPLAY) ? InLine : QStatus,
flgs);
break;
@@ -604,6 +937,9 @@ format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs
/*----- Figure max display widths -----*/
for(a = ps_global->atmts; a->description != NULL; a++){
+ if(MIME_VCALENDAR(a->body->type, a->body->subtype))
+ continue;
+
if((n = utf8_width(a->number)) > maxnumwid)
maxnumwid = n;
@@ -656,6 +992,8 @@ format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs
char numbuf[50];
int thisdescwid, padwid;
+ if(MIME_VCALENDAR(a->body->type, a->body->subtype))
+ continue;
#ifdef SMIME
if(a->body->type == TYPEMULTIPART
&& (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
diff --git a/pith/mailview.h b/pith/mailview.h
index 3767cc32..6a933f24 100644
--- a/pith/mailview.h
+++ b/pith/mailview.h
@@ -45,6 +45,9 @@
#define FM_HTML 0x4000 /* filter/preserve HTML markup */
#define FM_HTMLIMAGES 0x8000 /* filter/preserve HTML IMG tags */
+/* Format Calendar Flags */
+#define FC_SUMMARY 0x0001 /* Display only a summary event */
+#define FC_FULL 0x0002 /* Display full details of event */
#define SIGDASHES "-- "
#define START_SIG_BLOCK 2
@@ -123,6 +126,7 @@ typedef struct header_s {
int format_message(long, ENVELOPE *, BODY *, HANDLE_S **, int, gf_io_t);
int format_attachment_list(long int, BODY *, HANDLE_S **, int, int, gf_io_t);
char *format_body(long int, BODY *, HANDLE_S **, HEADER_S *, int, int, gf_io_t);
+int format_calendar(long int, BODY *, HANDLE_S **, int, int, gf_io_t);
int url_hilite(long, char *, LT_INS_S **, void *);
int handle_start_color(char *, size_t, int *, int);
int handle_end_color(char *, size_t, int *);
diff --git a/pith/mimedesc.c b/pith/mimedesc.c
index ab47770d..6d5999c4 100644
--- a/pith/mimedesc.c
+++ b/pith/mimedesc.c
@@ -252,7 +252,8 @@ describe_mime(struct mail_bodystruct *body, char *prefix, int num,
* be expensive to test...
*/
if((body->type == TYPETEXT && !named)
- || MIME_VCARD(body->type,body->subtype)){
+ || MIME_VCARD(body->type,body->subtype)
+ || MIME_VCALENDAR(body->type, body->subtype)){
a->test_deferred = 1;
a->can_display = MCD_INTERNAL;
}
@@ -264,7 +265,8 @@ describe_mime(struct mail_bodystruct *body, char *prefix, int num,
/*
* Deferred means we can display it
*/
- a->shown = ((a->can_display & MCD_INTERNAL)
+ a->shown = MIME_VCALENDAR(body->type, body->subtype)
+ || ((a->can_display & MCD_INTERNAL)
&& !MIME_VCARD(body->type,body->subtype)
&& (!named || multalt
|| (body->type == TYPETEXT && num == 1
diff --git a/pith/pine.hlp b/pith/pine.hlp
index b8aae319..c3c2de06 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any
reasonable place to be called from.
Dummy change to get revision in pine.hlp
============= h_revision =================
-Alpine Commit 245 2018-01-21 16:52:38
+Alpine Commit 249 2018-02-24 16:53:18
============= h_news =================
<HTML>
<HEAD>
diff --git a/pith/smime.c b/pith/smime.c
index 78596b3b..725337e7 100644
--- a/pith/smime.c
+++ b/pith/smime.c
@@ -42,6 +42,7 @@ static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washingto
#include "../pith/tempfile.h"
#include "../pith/readfile.h"
#include "../pith/remote.h"
+#include "../pith/body.h"
#ifdef PASSFILE
#include "../pith/imap.h"
#endif /* PASSFILE */
@@ -94,30 +95,6 @@ static X509_STORE *s_cert_store;
/* State management for randomness functions below */
static int seeded = 0;
-void *
-create_smime_sparep(SpareType stype, void *s)
-{
- SMIME_SPARE_S *rv;
-
- rv = fs_get(sizeof(SMIME_SPARE_S));
- rv->sptype = stype;
- rv->data = s;
- return (void *) rv;
-}
-
-SpareType
-get_smime_sparep_type(void *s)
-{
- return ((SMIME_SPARE_S *)s)->sptype;
-}
-
-void *
-get_smime_sparep_data(void *s)
-{
- return ((SMIME_SPARE_S *)s)->data;
-}
-
-
#ifdef PASSFILE
/*
* This code does not work in Windows, because of the PASSFILE thing, so
@@ -2877,30 +2854,6 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
return result;
}
-
-void
-free_smime_body_sparep(void **sparep)
-{
- char *s;
- SIZEDTEXT *st;
- if(sparep && *sparep){
- switch(get_smime_sparep_type(*sparep)){
- case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
- break;
- case CharType: s = (char *)get_smime_sparep_data(*sparep);
- fs_give((void **) &s);
- break;
- case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
- fs_give((void **) &st->data);
- fs_give((void **) &st);
- break;
- default : break;
- }
- ((SMIME_SPARE_S *)(*sparep))->data = NULL;
- fs_give(sparep);
- }
-}
-
/* Big comment, explaining the mess that exists out there, and how we deal
with it, and also how we solve the problems that are created this way.
@@ -3085,9 +3038,9 @@ do_detached_signature_verify(BODY *b, long msgno, char *section)
* to check the validity of the signature
*/
if(b->sparep){
- if(get_smime_sparep_type(b->sparep) == SizedText){
+ if(get_body_sparep_type(b->sparep) == SizedText){
/* bodytext includes mimetext */
- st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
+ st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
bodytext = (char *) st->data;
bodylen = st->size;
mimetext = NULL;
@@ -3205,7 +3158,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section)
b->description = cpystr(what_we_did);
- b->sparep = create_smime_sparep(P7Type, p7);
+ b->sparep = create_body_sparep(P7Type, p7);
p = b->nested.part;
@@ -3342,8 +3295,8 @@ do_decoding(BODY *b, long msgno, const char *section)
*/
if(b->sparep){
- if(get_smime_sparep_type(b->sparep) == P7Type)
- p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
+ if(get_body_sparep_type(b->sparep) == P7Type)
+ p7 = (PKCS7*) get_body_sparep_data(b->sparep);
}
else{
p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
@@ -3357,7 +3310,7 @@ do_decoding(BODY *b, long msgno, const char *section)
* Save the PKCS7 object for later dealings by the user interface.
* It will be cleaned up when the body is garbage collected.
*/
- b->sparep = create_smime_sparep(P7Type, p7);
+ b->sparep = create_body_sparep(P7Type, p7);
}
dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
@@ -3470,7 +3423,7 @@ do_decoding(BODY *b, long msgno, const char *section)
st = fs_get(sizeof(SIZEDTEXT));
st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
- body->sparep = create_smime_sparep(SizedText, (void *)st);
+ body->sparep = create_body_sparep(SizedText, (void *)st);
}
else
q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
diff --git a/pith/smime.h b/pith/smime.h
index 2e3ff913..d06ff764 100644
--- a/pith/smime.h
+++ b/pith/smime.h
@@ -36,14 +36,6 @@
#define OUR_PKCS7_ENCLOSURE_SUBTYPE "x-pkcs7-enclosure"
-typedef enum {P7Type, CharType, SizedText} SpareType;
-
-typedef struct smime_sparep_t {
- SpareType sptype;
- void *data;
-} SMIME_SPARE_S;
-
-
/* exported protoypes */
int smime_validate_cert(X509 *cert, long *error);
int encrypt_file(char *fp, char *text, PERSONAL_CERT *pc);
@@ -51,7 +43,6 @@ char *decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc);
int is_pkcs7_body(BODY *b);
int fiddle_smime_message(BODY *b, long msgno);
int encrypt_outgoing_message(METAENV *header, BODY **bodyP);
-void free_smime_body_sparep(void **sparep);
int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp);
void gf_puts_uline(char *txt, gf_io_t pc);
PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri);
@@ -83,9 +74,6 @@ void mark_cert_deleted(WhichCerts ctype, int num, unsigned state);
unsigned get_cert_deleted(WhichCerts ctype, int num);
int smime_expunge_cert(WhichCerts ctype);
int add_file_to_container(WhichCerts ctype, char *fpath, char *altname);
-void *create_smime_sparep(SpareType stype, void *s);
-SpareType get_smime_sparep_type(void *s);
-void *get_smime_sparep_data(void *s);
STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error, int *level);
CertList *certlist_from_personal_certs(PERSONAL_CERT *pc);
int smime_path(char *rpath, char *fpath, size_t len);
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)