[balsa] [Patch] ical improvements



commit ad3f57b29bd184a23661c0bee403f98320d95116
Author: Albrecht Dreß <albrecht dress arcor de>
Date:   Sat Feb 1 14:08:55 2020 -0500

    [Patch] ical improvements
    
    * README, configure.ac, meson.build: require libical >= 2.0.0 instead of 3.0.0;
    * libbalsa/rfc2445.c: libbalsa_vevent_reply(): add several optional fields
        to the “REPLY” message as to work around a M$ Exchange/Outlook bug;
    * src/balsa-mime-widget-vcalendar.c: balsa_vevent_widget():
        ref the VCal object containing the time zone data if we show
        the buttons for sending a reply;
      vevent_reply(): construct the reply as multipart/alternative,
        store it the the sentbox.

 ChangeLog                         | 13 +++++++
 README                            |  2 +-
 configure.ac                      |  2 +-
 libbalsa/rfc2445.c                | 24 ++++++++++++
 meson.build                       |  2 +-
 src/balsa-mime-widget-vcalendar.c | 80 +++++++++++++++++++++++++++------------
 6 files changed, 96 insertions(+), 27 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 139c201c5..b4ff2d365 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2020-02-01  Albrecht Dreß  <albrecht dress arcor de>
+
+       [Patch] ical improvements
+
+       * README, configure.ac, meson.build: require libical >= 2.0.0 instead of 3.0.0
+       * libbalsa/rfc2445.c: libbalsa_vevent_reply(): add several
+       optional fields to the “REPLY” message as to work around a M$
+       Exchange/Outlook bug
+       * src/balsa-mime-widget-vcalendar.c: balsa_vevent_widget():
+       ref the VCal object containing the time zone data if we show
+       the buttons for sending a reply; vevent_reply(): construct he
+       reply as multipart/alternative, store it the the sentbox
+
 2020-02-01  Peter Bloomfield  <pbloomfield bellsouth net>
 
        mailbox: Remove another idle handler when closing the mailbox
diff --git a/README b/README
index 8e6dd3ba2..d6d318d1a 100644
--- a/README
+++ b/README
@@ -46,7 +46,7 @@ Basically, Balsa requires
 - gthread-2.0
 - gnutls >= 3.0
 - gpgme >= 1.6.0
-- libical >= 3.0.0
+- libical >= 2.0.0
 - fribidi
 
 --disable-more-warnings
diff --git a/configure.ac b/configure.ac
index 0ab69bb36..080678d73 100644
--- a/configure.ac
+++ b/configure.ac
@@ -230,7 +230,7 @@ gio-2.0
 gthread-2.0
 gnutls >= 3.0
 fribidi
-libical >= 3.0.0
+libical >= 2.0.0
 ])
 
 PKG_CHECK_MODULES(BALSA_AB, [
diff --git a/libbalsa/rfc2445.c b/libbalsa/rfc2445.c
index ee358b236..17c1e3ed6 100644
--- a/libbalsa/rfc2445.c
+++ b/libbalsa/rfc2445.c
@@ -860,6 +860,9 @@ libbalsa_vevent_recurrence_str(LibBalsaVEvent *event, const gchar *format_str)
  * According to RFC 5546, Sect. 3.2.3., the REPLY shall include the following components and properties:
  * METHOD: MUST be REPLY
  * VEVENT: ATTENDEE, DTSTAMP, ORGANIZER, RECURRENCE-ID (if present in the request), UID, SEQUENCE (if 
present in the request)
+ *
+ * However, M$ Outlook/Exchange is broken and does not process these standard-compliant parts correctly.  
The trivial fix is to add
+ * several optional fields (I did not figure out which are needed to work around the issue, so we just add 
them all...).
  */
 gchar *
 libbalsa_vevent_reply(const LibBalsaVEvent   *event,
@@ -922,6 +925,27 @@ libbalsa_vevent_reply(const LibBalsaVEvent   *event,
        icalcomponent_add_property(ev_data, icalproperty_new_sequence(event->sequence));
     }
 
+    /* the following fields are *optional* in a reply according to RFC 5546, Sect. 3.2.3, but are apparently 
required by broken
+     * software like Exchange/Outlook */
+    if (icaltime_is_null_time(event->start) == 0) {
+       icalcomponent_add_property(ev_data, icalproperty_new_dtstart(event->start));
+    }
+    if (icaltime_is_null_time(event->end) == 0) {
+       icalcomponent_add_property(ev_data, icalproperty_new_dtend(event->end));
+    }
+    if (event->summary != NULL) {
+       icalcomponent_add_property(ev_data, icalproperty_new_summary(event->summary));
+    }
+    if (event->description != NULL) {
+       icalcomponent_add_property(ev_data, icalproperty_new_description(event->description));
+    }
+    if (event->status != ICAL_STATUS_NONE) {
+       icalcomponent_add_property(ev_data, icalproperty_new_status(event->status));
+    }
+    if (event->location != NULL) {
+       icalcomponent_add_property(ev_data, icalproperty_new_location(event->location));
+    }
+
     reply_str = icalcomponent_as_ical_string(reply);
     icalcomponent_free(reply);
     return reply_str;
diff --git a/meson.build b/meson.build
index 64e1ca627..ec05689c9 100644
--- a/meson.build
+++ b/meson.build
@@ -136,7 +136,7 @@ gthread_dep = dependency('gthread-2.0')
 gnutls_dep  = dependency('gnutls', version : '>= 3.0')
 gpgme_dep   = dependency('gpgme', version : '>= 1.6.0')
 fribidi_dep = dependency('fribidi')
-libical_dep = dependency('libical', version : '>= 3.0.0')
+libical_dep = dependency('libical', version : '>= 2.0.0')
 
 # Dependencies for balsa
 balsa_deps = [glib_dep,
diff --git a/src/balsa-mime-widget-vcalendar.c b/src/balsa-mime-widget-vcalendar.c
index d1410c25e..e9f814489 100644
--- a/src/balsa-mime-widget-vcalendar.c
+++ b/src/balsa-mime-widget-vcalendar.c
@@ -31,9 +31,10 @@
 #include "balsa-mime-widget-callbacks.h"
 
 
-static GtkWidget *balsa_vevent_widget(LibBalsaVEvent * event,
-                                     gboolean may_reply,
-                                     InternetAddress * sender);
+static GtkWidget *balsa_vevent_widget(LibBalsaVEvent  *event,
+                                                                         LibBalsaVCal    *vcal,
+                                                                         gboolean         may_reply,
+                                                                         InternetAddress *sender);
 static void vevent_reply(GObject * button, GtkWidget * box);
 
 
@@ -103,7 +104,7 @@ balsa_mime_widget_new_vcalendar(BalsaMessage * bm,
 
        frame = gtk_frame_new(NULL);
         gtk_container_add(GTK_CONTAINER(mw), frame);
-       event = balsa_vevent_widget(libbalsa_vcal_vevent(vcal_obj, event_no), may_reply, sender);
+       event = balsa_vevent_widget(libbalsa_vcal_vevent(vcal_obj, event_no), vcal_obj, may_reply, sender);
         gtk_container_set_border_width(GTK_CONTAINER(event), 6U);
        gtk_container_add(GTK_CONTAINER(frame), event);
     }
@@ -168,8 +169,8 @@ balsa_mime_widget_new_vcalendar(BalsaMessage * bm,
     } while (0)
 
 static GtkWidget *
-balsa_vevent_widget(LibBalsaVEvent * event, gboolean may_reply,
-                   InternetAddress * sender)
+balsa_vevent_widget(LibBalsaVEvent *event, LibBalsaVCal *vcal, gboolean may_reply,
+                   InternetAddress *sender)
 {
     GtkGrid *grid;
     int row = 0;
@@ -252,7 +253,6 @@ balsa_vevent_widget(LibBalsaVEvent * event, gboolean may_reply,
        GtkWidget *button;
 
        /* add the callback data to the event object */
-       g_object_ref(event);
        g_object_set_data_full(G_OBJECT(event), "ev:sender",
                               internet_address_to_string(sender, FALSE),
                               (GDestroyNotify) g_free);
@@ -271,8 +271,12 @@ balsa_vevent_widget(LibBalsaVEvent * event, gboolean may_reply,
        gtk_container_add(GTK_CONTAINER(box), bbox);
 
        button = gtk_button_new_with_label(_("Accept"));
-       g_object_set_data_full(G_OBJECT(button), "event", event,
-                               (GDestroyNotify) g_object_unref);
+       g_object_set_data(G_OBJECT(button), "event", event);
+
+       /* Note: we must ref the full VCal object here as time zone information is stored in it.  Only 
ref'ing the event would thus
+        * lead to a segfault when a reply message is actually created. */
+       g_object_set_data_full(G_OBJECT(button), "vcal", g_object_ref(vcal),
+               (GDestroyNotify) g_object_unref);
        g_object_set_data(G_OBJECT(button), "mode",
                          GINT_TO_POINTER(ICAL_PARTSTAT_ACCEPTED));
        g_signal_connect(button, "clicked",
@@ -300,6 +304,22 @@ balsa_vevent_widget(LibBalsaVEvent * event, gboolean may_reply,
        return GTK_WIDGET(grid);
 }
 
+#define BUFFER_APPEND(b, l, s)                                                 \
+       G_STMT_START {                                                                          \
+               if (s != NULL) {                                                                \
+                       g_string_append_printf(b, "%s %s\n", l, s);     \
+               }                                                                                             
  \
+    } G_STMT_END
+
+#define BUFFER_APPEND_DATE(b, l, e, i)                                                                       
          \
+       G_STMT_START {                                                                                        
                                  \
+       gchar *_dstr = libbalsa_vevent_time_str(e, i, balsa_app.date_string);   \
+       if (_dstr != NULL) {                                                                                  
                  \
+               BUFFER_APPEND(b, l, _dstr);                                                                   
                  \
+            g_free(_dstr);                                                                     \
+        }                                                                                      \
+    } G_STMT_END
+
 static void
 vevent_reply(GObject * button, GtkWidget * box)
 {
@@ -312,8 +332,8 @@ vevent_reply(GObject * button, GtkWidget * box)
     LibBalsaMessageHeaders *headers;
     LibBalsaMessageBody *body;
     const gchar *summary;
+    GString *textbuf;
     gchar *dummy;
-    gchar **params;
     GError *error = NULL;
     LibBalsaMsgCreateResult result;
     LibBalsaIdentity *ident;
@@ -344,26 +364,38 @@ vevent_reply(GObject * button, GtkWidget * box)
                            libbalsa_vcal_part_stat_to_str(pstat));
     libbalsa_message_set_subject(message, dummy);
     g_free(dummy);
+    libbalsa_message_set_subtype(message, "alternative");
 
-    /* the only message part is the calendar object */
+    /* add an informational text part */
     body = libbalsa_message_body_new(message);
-    body->buffer =
-       libbalsa_vevent_reply(event,
-                             INTERNET_ADDRESS_MAILBOX(ia)->addr,
-                             pstat);
-    if (body->buffer == NULL) return;
     body->charset = g_strdup("utf-8");
-    body->content_type = g_strdup("text/calendar");
+    body->content_type = NULL;
+    textbuf = g_string_new(NULL);
+    g_string_append_printf(textbuf, _("%s %s the following iTIP calendar request:\n\n"),
+       internet_address_get_name(ia), libbalsa_vcal_part_stat_to_str(pstat));
+    BUFFER_APPEND(textbuf, _("Summary:"), libbalsa_vevent_summary(event));
+    BUFFER_APPEND_DATE(textbuf, _("Start:"), event, VEVENT_DATETIME_START);
+    BUFFER_APPEND_DATE(textbuf, _("End:"), event, VEVENT_DATETIME_END);
+    BUFFER_APPEND(textbuf, _("Location:"), libbalsa_vevent_location(event));
+    BUFFER_APPEND(textbuf, _("Description:"), libbalsa_vevent_description(event));
+    body->buffer = g_string_free(textbuf, FALSE);
+    if (balsa_app.wordwrap) {
+        libbalsa_wrap_string(body->buffer, balsa_app.wraplength);
+    }
     libbalsa_message_append_part(message, body);
 
-    /* set the text/calendar parameters */
-    params = g_new(gchar *, 3);
-    params[0] = g_strdup("method");
-    params[1] = g_strdup("reply");
-    params[2] = NULL;
-    libbalsa_message_add_parameters(message, params);
+    /* the next message part is the calendar object */
+    body = libbalsa_message_body_new(message);
+    body->buffer = libbalsa_vevent_reply(event, INTERNET_ADDRESS_MAILBOX(ia)->addr, pstat);
+    if (body->buffer == NULL) {
+       g_object_unref(message);
+       return;
+    }
+    body->charset = g_strdup("utf-8");
+    body->content_type = g_strdup("text/calendar; method=reply");
+    libbalsa_message_append_part(message, body);
 
-    result = libbalsa_message_send(message, balsa_app.outbox, NULL,
+    result = libbalsa_message_send(message, balsa_app.outbox, balsa_app.sentbox,
                                   balsa_find_sentbox_by_url,
                                   libbalsa_identity_get_smtp_server(ident),
                                   balsa_app.send_progress_dialog,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]