[evolution] I#262 - Calendar: Ability to change RSVP response



commit 40e3a2f10fd07002d0cbbbcbb3484526d17777c5
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 4 17:56:32 2021 +0100

    I#262 - Calendar: Ability to change RSVP response
    
    Can be found in the context menu of a meeting, when applicable.
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/262

 data/ui/evolution-calendars.ui                   |  10 +
 src/calendar/gui/e-cal-ops.c                     |   7 +-
 src/calendar/gui/e-calendar-view.c               |  13 +-
 src/calendar/gui/e-comp-editor.c                 |  27 +-
 src/calendar/gui/itip-utils.c                    | 503 ++++++++++++++++++-----
 src/calendar/gui/itip-utils.h                    |  49 ++-
 src/modules/calendar/e-cal-base-shell-content.h  |   3 +-
 src/modules/calendar/e-cal-shell-content.c       |   8 +
 src/modules/calendar/e-cal-shell-view-actions.c  | 124 +++++-
 src/modules/calendar/e-cal-shell-view-actions.h  |  14 +
 src/modules/calendar/e-cal-shell-view-memopad.c  |   4 +-
 src/modules/calendar/e-cal-shell-view-taskpad.c  |   4 +-
 src/modules/calendar/e-cal-shell-view.c          |  11 +
 src/modules/calendar/e-memo-shell-view-actions.c |   4 +-
 src/modules/calendar/e-task-shell-view-actions.c |   4 +-
 src/modules/itip-formatter/itip-view.c           | 200 ++-------
 16 files changed, 664 insertions(+), 321 deletions(-)
---
diff --git a/data/ui/evolution-calendars.ui b/data/ui/evolution-calendars.ui
index 2fc4eb5070..e33af2e726 100644
--- a/data/ui/evolution-calendars.ui
+++ b/data/ui/evolution-calendars.ui
@@ -102,6 +102,16 @@
     <menuitem action='event-popup-save-as'/>
     <menuitem action='event-popup-print'/>
     <separator/>
+    <menu action='event-popup-rsvp-submenu'>
+      <menuitem action='event-popup-rsvp-accept'/>
+      <menuitem action='event-popup-rsvp-tentative'/>
+      <menuitem action='event-popup-rsvp-decline'/>
+      <separator/>
+      <menuitem action='event-popup-rsvp-accept-1'/>
+      <menuitem action='event-popup-rsvp-tentative-1'/>
+      <menuitem action='event-popup-rsvp-decline-1'/>
+    </menu>
+    <separator/>
     <menuitem action='popup-cut-clipboard'/>
     <menuitem action='popup-copy-clipboard'/>
     <menuitem action='popup-paste-clipboard'/>
diff --git a/src/calendar/gui/e-cal-ops.c b/src/calendar/gui/e-cal-ops.c
index b98f919dd8..7ec9b1a6ae 100644
--- a/src/calendar/gui/e-cal-ops.c
+++ b/src/calendar/gui/e-cal-ops.c
@@ -71,8 +71,11 @@ cal_ops_manage_send_component (ECalModel *model,
                                &strip_alarms, &only_new_attendees);
 
                if (can_send)
-                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_REQUEST, comp, client,
-                               NULL, NULL, NULL, strip_alarms, only_new_attendees, mod == E_CAL_OBJ_MOD_ALL);
+                       itip_send_component_with_model (model, I_CAL_METHOD_REQUEST, comp, client,
+                               NULL, NULL, NULL,
+                               (strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
+                               (only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0) |
+                               (mod == E_CAL_OBJ_MOD_ALL ? E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT : 
0));
        }
 
        g_clear_object (&comp);
diff --git a/src/calendar/gui/e-calendar-view.c b/src/calendar/gui/e-calendar-view.c
index 95211fb39b..e562940852 100644
--- a/src/calendar/gui/e-calendar-view.c
+++ b/src/calendar/gui/e-calendar-view.c
@@ -221,9 +221,9 @@ calendar_view_delete_event (ECalendarView *cal_view,
                                e_cal_component_range_free (range);
                        }
 
-                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_CANCEL,
+                       itip_send_component_with_model (model, I_CAL_METHOD_CANCEL,
                                comp, event->comp_data->client, NULL, NULL,
-                               NULL, TRUE, FALSE, FALSE);
+                               NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS);
                }
 
                uid = e_cal_component_get_uid (comp);
@@ -667,8 +667,8 @@ calendar_view_component_created_cb (ECalModel *model,
            (itip_organizer_is_user (registry, comp, client) ||
             itip_sentby_is_user (registry, comp, client)) &&
             e_cal_dialogs_send_component ((GtkWindow *) toplevel, client, comp, TRUE, &strip_alarms, NULL)) {
-               itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_REQUEST,
-                       comp, client, NULL, NULL, NULL, strip_alarms, FALSE, FALSE);
+               itip_send_component_with_model (model, I_CAL_METHOD_REQUEST,
+                       comp, client, NULL, NULL, NULL, (strip_alarms ? 
E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0));
        }
 
        g_object_unref (comp);
@@ -833,8 +833,9 @@ paste_clipboard_data_free (gpointer ptr)
                                    (itip_organizer_is_user (registry, comp, comp_data->client) ||
                                    itip_sentby_is_user (registry, comp, comp_data->client))
                                    && e_cal_dialogs_cancel_component ((GtkWindow *) pcd->top_level, 
comp_data->client, comp, TRUE))
-                                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_CANCEL,
-                                               comp, comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
+                                       itip_send_component_with_model (model, I_CAL_METHOD_CANCEL,
+                                               comp, comp_data->client, NULL, NULL, NULL,
+                                               E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
                                uid = e_cal_component_get_uid (comp);
                                if (e_cal_component_is_instance (comp)) {
diff --git a/src/calendar/gui/e-comp-editor.c b/src/calendar/gui/e-comp-editor.c
index 011582d1b8..2e3372fa0c 100644
--- a/src/calendar/gui/e-comp-editor.c
+++ b/src/calendar/gui/e-comp-editor.c
@@ -302,8 +302,8 @@ typedef struct _SaveData {
        gchar *alert_arg_0;
 
        gboolean object_created;
-       ECalComponentItipMethod first_send;
-       ECalComponentItipMethod second_send;
+       ICalPropertyMethod first_send;
+       ICalPropertyMethod second_send;
        ECalComponent *send_comp;
        EActivity *send_activity;
        gboolean strip_alarms;
@@ -359,7 +359,7 @@ save_data_free (SaveData *sd)
 
 static gboolean
 ece_send_process_method (SaveData *sd,
-                        ECalComponentItipMethod send_method,
+                        ICalPropertyMethod send_method,
                         ECalComponent *send_comp,
                         ESourceRegistry *registry,
                         GCancellable *cancellable,
@@ -370,7 +370,7 @@ ece_send_process_method (SaveData *sd,
 
        g_return_val_if_fail (sd != NULL, FALSE);
        g_return_val_if_fail (E_IS_CAL_COMPONENT (send_comp), FALSE);
-       g_return_val_if_fail (send_method != E_CAL_COMPONENT_METHOD_NONE, FALSE);
+       g_return_val_if_fail (send_method != I_CAL_METHOD_NONE, FALSE);
 
        if (e_cal_component_has_attachments (send_comp) &&
            e_client_check_capability (E_CLIENT (sd->target_client), 
E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
@@ -402,8 +402,9 @@ ece_send_process_method (SaveData *sd,
 
        itip_send_component (
                registry, send_method, send_comp, sd->target_client,
-               NULL, mime_attach_list, NULL, sd->strip_alarms,
-               sd->only_new_attendees, FALSE,
+               NULL, mime_attach_list, NULL,
+               (sd->strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
+               (sd->only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0),
                cancellable, callback, user_data);
 
        return TRUE;
@@ -433,7 +434,7 @@ ecep_first_send_processed_cb (GObject *source_object,
        g_return_if_fail (sd != NULL);
 
        sd->success = itip_send_component_finish (result, &sd->error);
-       if (!sd->success || sd->second_send == E_CAL_COMPONENT_METHOD_NONE) {
+       if (!sd->success || sd->second_send == I_CAL_METHOD_NONE) {
                save_data_free (sd);
        } else {
                sd->success = ece_send_process_method (sd, sd->second_send, sd->send_comp,
@@ -590,14 +591,14 @@ ece_save_component_done (gpointer ptr)
                        if ((itip_organizer_is_user (registry, comp, sd->target_client) ||
                             itip_sentby_is_user (registry, comp, sd->target_client))) {
                                if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL)
-                                       sd->first_send = E_CAL_COMPONENT_METHOD_PUBLISH;
+                                       sd->first_send = I_CAL_METHOD_PUBLISH;
                                else
-                                       sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST;
+                                       sd->first_send = I_CAL_METHOD_REQUEST;
                        } else {
-                               sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST;
+                               sd->first_send = I_CAL_METHOD_REQUEST;
 
                                if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0)
-                                       sd->second_send = E_CAL_COMPONENT_METHOD_REPLY;
+                                       sd->second_send = I_CAL_METHOD_REPLY;
                        }
 
                        sd->mime_attach_list = ece_get_mime_attach_list (sd->comp_editor);
@@ -1013,8 +1014,8 @@ ece_save_component (ECompEditor *comp_editor,
                     itip_sentby_is_user (registry, comp, sd->target_client)));
        sd->close_after_save = close_after_save;
        sd->recur_mod = recur_mod;
-       sd->first_send = E_CAL_COMPONENT_METHOD_NONE;
-       sd->second_send = E_CAL_COMPONENT_METHOD_NONE;
+       sd->first_send = I_CAL_METHOD_NONE;
+       sd->second_send = I_CAL_METHOD_NONE;
        sd->success = FALSE;
 
        source_display_name = e_util_get_source_full_name (e_shell_get_registry (comp_editor->priv->shell),
diff --git a/src/calendar/gui/itip-utils.c b/src/calendar/gui/itip-utils.c
index b724204b50..f4f7742a51 100644
--- a/src/calendar/gui/itip-utils.c
+++ b/src/calendar/gui/itip-utils.c
@@ -42,28 +42,6 @@
 
 #define d(x)
 
-static const gchar *itip_methods[] = {
-       "PUBLISH",
-       "REQUEST",
-       "REPLY",
-       "ADD",
-       "CANCEL",
-       "RERESH",
-       "COUNTER",
-       "DECLINECOUNTER"
-};
-
-static ICalPropertyMethod itip_methods_enum[] = {
-    I_CAL_METHOD_PUBLISH,
-    I_CAL_METHOD_REQUEST,
-    I_CAL_METHOD_REPLY,
-    I_CAL_METHOD_ADD,
-    I_CAL_METHOD_CANCEL,
-    I_CAL_METHOD_REFRESH,
-    I_CAL_METHOD_COUNTER,
-    I_CAL_METHOD_DECLINECOUNTER
-};
-
 /**
  * itip_get_default_name_and_address:
  * @registry: an #ESourceRegistry
@@ -488,6 +466,104 @@ html_new_lines_for (const gchar *string)
        return joined;
 }
 
+gboolean
+itip_attendee_is_user (ESourceRegistry *registry,
+                      ECalComponent *comp,
+                      ECalClient *cal_client)
+{
+       ESource *source;
+       GSList *attendees;
+       ECalComponentAttendee *attendee = NULL;
+       GList *list, *link;
+       const gchar *extension_name;
+       gchar *address = NULL;
+
+       attendees = e_cal_component_get_attendees (comp);
+
+       if (cal_client)
+               e_client_get_backend_property_sync (
+                       E_CLIENT (cal_client),
+                       E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
+                       &address, NULL, NULL);
+
+       if (address && *address) {
+               attendee = get_attendee (attendees, address, NULL);
+
+               if (attendee) {
+                       g_slist_free_full (attendees, e_cal_component_attendee_free);
+                       g_free (address);
+
+                       return TRUE;
+               }
+
+               attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, NULL);
+
+               if (attendee) {
+                       g_slist_free_full (attendees, e_cal_component_attendee_free);
+                       g_free (address);
+
+                       return TRUE;
+               }
+       }
+
+       g_free (address);
+       address = NULL;
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+       list = e_source_registry_list_enabled (registry, extension_name);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESourceMailIdentity *extension;
+               GHashTable *aliases;
+
+               source = E_SOURCE (link->data);
+
+               extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+               extension = e_source_get_extension (source, extension_name);
+
+               address = e_source_mail_identity_dup_address (extension);
+
+               aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
+
+               attendee = get_attendee (attendees, address, aliases);
+               if (attendee != NULL) {
+                       g_slist_free_full (attendees, e_cal_component_attendee_free);
+
+                       if (aliases)
+                               g_hash_table_destroy (aliases);
+                       g_free (address);
+
+                       g_list_free_full (list, g_object_unref);
+
+                       return TRUE;
+               }
+
+               /* If the account was not found in the attendees list, then
+                * let's check the 'sentby' fields of the attendees if we can
+                * find the account. */
+               attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, aliases);
+               if (attendee) {
+                       g_slist_free_full (attendees, e_cal_component_attendee_free);
+
+                       if (aliases)
+                               g_hash_table_destroy (aliases);
+                       g_free (address);
+
+                       g_list_free_full (list, g_object_unref);
+
+                       return TRUE;
+               }
+
+               if (aliases)
+                       g_hash_table_destroy (aliases);
+               g_free (address);
+       }
+
+       g_list_free_full (list, g_object_unref);
+
+       return FALSE;
+}
+
 gchar *
 itip_get_comp_attendee (ESourceRegistry *registry,
                         ECalComponent *comp,
@@ -680,7 +756,7 @@ foreach_tzid_callback (ICalParameter *param,
 }
 
 static ICalComponent *
-comp_toplevel_with_zones (ECalComponentItipMethod method,
+comp_toplevel_with_zones (ICalPropertyMethod method,
                          const GSList *ecomps,
                          ECalClient *cal_client,
                          ICalComponent *zones)
@@ -692,7 +768,7 @@ comp_toplevel_with_zones (ECalComponentItipMethod method,
 
        top_level = e_cal_util_new_top_level ();
 
-       prop = i_cal_property_new_method (itip_methods_enum[method]);
+       prop = i_cal_property_new_method (method);
        i_cal_component_take_property (top_level, prop);
 
        tz_data.tzids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
@@ -729,7 +805,7 @@ users_has_attendee (const GSList *users,
 }
 
 static gchar *
-comp_from (ECalComponentItipMethod method,
+comp_from (ICalPropertyMethod method,
            ECalComponent *comp,
            ESourceRegistry *registry,
           gchar **from_name)
@@ -741,11 +817,11 @@ comp_from (ECalComponentItipMethod method,
        gchar *sender = NULL;
 
        switch (method) {
-       case E_CAL_COMPONENT_METHOD_PUBLISH:
-       case E_CAL_COMPONENT_METHOD_REQUEST:
+       case I_CAL_METHOD_PUBLISH:
+       case I_CAL_METHOD_REQUEST:
                return itip_get_comp_attendee (registry, comp, NULL);
 
-       case E_CAL_COMPONENT_METHOD_REPLY:
+       case I_CAL_METHOD_REPLY:
                sender = itip_get_comp_attendee (registry, comp, NULL);
                if (sender != NULL)
                        return sender;
@@ -754,9 +830,9 @@ comp_from (ECalComponentItipMethod method,
                /* coverity[fallthrough] */
                /* falls through */
 
-       case E_CAL_COMPONENT_METHOD_CANCEL:
+       case I_CAL_METHOD_CANCEL:
 
-       case E_CAL_COMPONENT_METHOD_ADD:
+       case I_CAL_METHOD_ADD:
 
                organizer = e_cal_component_get_organizer (comp);
                if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
@@ -792,7 +868,7 @@ comp_from (ECalComponentItipMethod method,
 
 static EDestination **
 comp_to_list (ESourceRegistry *registry,
-              ECalComponentItipMethod method,
+              ICalPropertyMethod method,
               ECalComponent *comp,
               const GSList *users,
               gboolean reply_all,
@@ -811,8 +887,8 @@ comp_to_list (ESourceRegistry *registry,
        } convert;
 
        switch (method) {
-       case E_CAL_COMPONENT_METHOD_REQUEST:
-       case E_CAL_COMPONENT_METHOD_CANCEL:
+       case I_CAL_METHOD_REQUEST:
+       case I_CAL_METHOD_CANCEL:
                attendees = e_cal_component_get_attendees (comp);
                len = g_slist_length (attendees);
                if (len <= 0) {
@@ -872,7 +948,7 @@ comp_to_list (ESourceRegistry *registry,
                                continue;
                        else if (e_cal_component_attendee_get_partstat (att) == I_CAL_PARTSTAT_DELEGATED &&
                                 !e_cal_component_attendee_get_rsvp (att) &&
-                                method == E_CAL_COMPONENT_METHOD_REQUEST) {
+                                method == I_CAL_METHOD_REQUEST) {
                                        const gchar *delegatedto;
 
                                        delegatedto = e_cal_component_attendee_get_delegatedto (att);
@@ -893,7 +969,7 @@ comp_to_list (ESourceRegistry *registry,
                e_cal_component_organizer_free (organizer);
                break;
 
-       case E_CAL_COMPONENT_METHOD_REPLY:
+       case I_CAL_METHOD_REPLY:
 
                if (reply_all) {
                        attendees = e_cal_component_get_attendees (comp);
@@ -969,10 +1045,10 @@ comp_to_list (ESourceRegistry *registry,
                }
                break;
 
-       case E_CAL_COMPONENT_METHOD_ADD:
-       case E_CAL_COMPONENT_METHOD_REFRESH:
-       case E_CAL_COMPONENT_METHOD_COUNTER:
-       case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
+       case I_CAL_METHOD_ADD:
+       case I_CAL_METHOD_REFRESH:
+       case I_CAL_METHOD_COUNTER:
+       case I_CAL_METHOD_DECLINECOUNTER:
                organizer = e_cal_component_get_organizer (comp);
                if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
                        e_cal_component_organizer_free (organizer);
@@ -1031,7 +1107,7 @@ comp_to_list (ESourceRegistry *registry,
                e_cal_component_organizer_free (organizer);
 
                break;
-       case E_CAL_COMPONENT_METHOD_PUBLISH:
+       case I_CAL_METHOD_PUBLISH:
                if (users) {
                        const GSList *list;
 
@@ -1060,7 +1136,7 @@ comp_to_list (ESourceRegistry *registry,
 
 static gchar *
 comp_subject (ESourceRegistry *registry,
-              ECalComponentItipMethod method,
+              ICalPropertyMethod method,
               ECalComponent *comp)
 {
        ECalComponentText *caltext;
@@ -1093,15 +1169,15 @@ comp_subject (ESourceRegistry *registry,
        }
 
        switch (method) {
-       case E_CAL_COMPONENT_METHOD_PUBLISH:
-       case E_CAL_COMPONENT_METHOD_REQUEST:
+       case I_CAL_METHOD_PUBLISH:
+       case I_CAL_METHOD_REQUEST:
                /* FIXME: If this is an update to a previous
                 * PUBLISH or REQUEST, then
                        prefix = U_("Updated");
                 */
                break;
 
-       case E_CAL_COMPONENT_METHOD_REPLY:
+       case I_CAL_METHOD_REPLY:
                alist = e_cal_component_get_attendees (comp);
                sender = itip_get_comp_attendee (registry, comp, NULL);
                if (sender) {
@@ -1113,8 +1189,8 @@ comp_subject (ESourceRegistry *registry,
                                sentby = e_cal_component_attendee_get_sentby (a);
 
                                if ((sender && *sender) && (
-                                   (value && g_ascii_strcasecmp (itip_strip_mailto (value), sender)) ||
-                                   (sentby && g_ascii_strcasecmp (itip_strip_mailto (sentby), sender))))
+                                   (value && !g_ascii_strcasecmp (itip_strip_mailto (value), sender)) ||
+                                   (sentby && !g_ascii_strcasecmp (itip_strip_mailto (sentby), sender))))
                                        break;
                        }
                        g_free (sender);
@@ -1158,35 +1234,35 @@ comp_subject (ESourceRegistry *registry,
                }
                break;
 
-       case E_CAL_COMPONENT_METHOD_ADD:
+       case I_CAL_METHOD_ADD:
                /* Translators: This is part of the subject line of a
                 * meeting request or update email.  The full subject
                 * line would be: "Updated: Meeting Name". */
                prefix = C_("Meeting", "Updated");
                break;
 
-       case E_CAL_COMPONENT_METHOD_CANCEL:
+       case I_CAL_METHOD_CANCEL:
                /* Translators: This is part of the subject line of a
                 * meeting request or update email.  The full subject
                 * line would be: "Cancel: Meeting Name". */
                prefix = C_("Meeting", "Cancel");
                break;
 
-       case E_CAL_COMPONENT_METHOD_REFRESH:
+       case I_CAL_METHOD_REFRESH:
                /* Translators: This is part of the subject line of a
                 * meeting request or update email.  The full subject
                 * line would be: "Refresh: Meeting Name". */
                prefix = C_("Meeting", "Refresh");
                break;
 
-       case E_CAL_COMPONENT_METHOD_COUNTER:
+       case I_CAL_METHOD_COUNTER:
                /* Translators: This is part of the subject line of a
                 * meeting request or update email.  The full subject
                 * line would be: "Counter-proposal: Meeting Name". */
                prefix = C_("Meeting", "Counter-proposal");
                break;
 
-       case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
+       case I_CAL_METHOD_DECLINECOUNTER:
                /* Translators: This is part of the subject line of a
                 * meeting request or update email.  The full subject
                 * line would be: "Declined: Meeting Name". */
@@ -1209,7 +1285,7 @@ comp_subject (ESourceRegistry *registry,
 
 static gchar *
 comp_content_type (ECalComponent *comp,
-                   ECalComponentItipMethod method)
+                   ICalPropertyMethod method)
 {
        const gchar *name;
 
@@ -1220,7 +1296,7 @@ comp_content_type (ECalComponent *comp,
 
        return g_strdup_printf (
                "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
-               name, itip_methods[method]);
+               name, i_cal_property_method_to_string (method));
 }
 
 static const gchar *
@@ -1279,7 +1355,7 @@ comp_description (ECalComponent *comp,
 }
 
 static gboolean
-comp_server_send_sync (ECalComponentItipMethod method,
+comp_server_send_sync (ICalPropertyMethod method,
                       const GSList *ecomps,
                       ECalClient *cal_client,
                       ICalComponent *zones,
@@ -1613,7 +1689,7 @@ remove_schedule_status_parameters (ECalComponent *comp)
 
 static ECalComponent *
 comp_compliant_one (ESourceRegistry *registry,
-                   ECalComponentItipMethod method,
+                   ICalPropertyMethod method,
                    ECalComponent *comp,
                    ECalClient *client,
                    ICalComponent *zones,
@@ -1717,29 +1793,29 @@ comp_compliant_one (ESourceRegistry *registry,
 
        /* Comply with itip spec */
        switch (method) {
-       case E_CAL_COMPONENT_METHOD_PUBLISH:
+       case I_CAL_METHOD_PUBLISH:
                comp_sentby (clone, client, registry);
                e_cal_component_set_attendees (clone, NULL);
                break;
-       case E_CAL_COMPONENT_METHOD_REQUEST:
+       case I_CAL_METHOD_REQUEST:
                comp_sentby (clone, client, registry);
                break;
-       case E_CAL_COMPONENT_METHOD_CANCEL:
+       case I_CAL_METHOD_CANCEL:
                comp_sentby (clone, client, registry);
                break;
-       case E_CAL_COMPONENT_METHOD_REPLY:
+       case I_CAL_METHOD_REPLY:
                break;
-       case E_CAL_COMPONENT_METHOD_ADD:
+       case I_CAL_METHOD_ADD:
                break;
-       case E_CAL_COMPONENT_METHOD_REFRESH:
+       case I_CAL_METHOD_REFRESH:
                /* Need to remove almost everything */
                temp_clone = comp_minimal (registry, clone, TRUE);
                g_object_unref (clone);
                clone = temp_clone;
                break;
-       case E_CAL_COMPONENT_METHOD_COUNTER:
+       case I_CAL_METHOD_COUNTER:
                break;
-       case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
+       case I_CAL_METHOD_DECLINECOUNTER:
                /* Need to remove almost everything */
                temp_clone = comp_minimal (registry, clone, FALSE);
                g_object_unref (clone);
@@ -1756,7 +1832,7 @@ comp_compliant_one (ESourceRegistry *registry,
 
 static gboolean
 comp_compliant (ESourceRegistry *registry,
-               ECalComponentItipMethod method,
+               ICalPropertyMethod method,
                GSList *ecomps,
                gboolean unref_orig_ecomp,
                ECalClient *client,
@@ -1887,7 +1963,7 @@ find_enabled_identity (ESourceRegistry *registry,
 
 static gchar *
 get_identity_uid_for_from (EShell *shell,
-                          ECalComponentItipMethod method,
+                          ICalPropertyMethod method,
                           ECalComponent *comp,
                           ECalClient *cal_client,
                           gchar **identity_name,
@@ -1972,15 +2048,13 @@ master_first_cmp (gconstpointer ptr1,
 
 typedef struct {
        ESourceRegistry *registry;
-       ECalComponentItipMethod method;
+       ICalPropertyMethod method;
        GSList *send_comps; /* ECalComponent * */
        ECalClient *cal_client;
        ICalComponent *zones;
        GSList *attachments_list;
        GSList *users;
-       gboolean strip_alarms;
-       gboolean only_new_attendees;
-       gboolean ensure_master_object;
+       EItipSendComponentFlags flags;
 
        gboolean completed;
        gboolean success;
@@ -2016,13 +2090,93 @@ itip_send_component_begin (ItipSendComponentData *isc,
 
        isc->completed = FALSE;
 
-       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules 
(isc->cal_client)) {
+       if ((isc->flags & (E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED |
+                          E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED |
+                          E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_TENTATIVE)) != 0 &&
+           isc->send_comps && !isc->send_comps->next) {
+               ICalComponent *toplevel, *icomp;
+               ICalParameterPartstat partstat;
+               GSList *link;
+               gchar *attendee;
+
+               if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED) != 0)
+                       partstat = I_CAL_PARTSTAT_ACCEPTED;
+               else if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED) != 0)
+                       partstat = I_CAL_PARTSTAT_DECLINED;
+               else
+                       partstat = I_CAL_PARTSTAT_TENTATIVE;
+
+               if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT) != 0 && isc->send_comps) {
+                       /* Ensure we send the master object with its detached instances, not the instance 
only */
+                       GSList *ecalcomps = NULL;
+                       const gchar *uid;
+
+                       uid = e_cal_component_get_uid (isc->send_comps->data);
+
+                       if (e_cal_client_get_objects_for_uid_sync (isc->cal_client, uid, &ecalcomps, 
cancellable, NULL) && ecalcomps) {
+                               GSList *old_send_comps = isc->send_comps;
+
+                               isc->send_comps = g_slist_sort (ecalcomps, master_first_cmp);
+
+                               cal_comp_util_copy_new_attendees (isc->send_comps->data, 
old_send_comps->data);
+
+                               g_slist_free_full (old_send_comps, g_object_unref);
+                       }
+
+                       isc->flags &= ~E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT;
+               }
+
+               attendee = itip_get_comp_attendee (isc->registry, isc->send_comps->data, isc->cal_client);
+
+               icomp = e_cal_component_get_icalcomponent (isc->send_comps->data);
+               itip_utils_prepare_attendee_response (isc->registry, icomp, attendee, partstat);
+               itip_utils_update_cdo_replytime (icomp);
+
+               toplevel = i_cal_component_new_vcalendar ();
+               i_cal_component_set_method (toplevel, isc->method);
+
+               for (link = isc->send_comps; link; link = g_slist_next (link)) {
+                       ECalComponent *comp = link->data;
+
+                       icomp = e_cal_component_get_icalcomponent (comp);
+
+                       if (icomp)
+                               i_cal_component_take_component (toplevel, i_cal_component_clone (icomp));
+               }
+
+               isc->success = e_cal_client_receive_objects_sync (isc->cal_client, toplevel, 
E_CAL_OPERATION_FLAG_NONE, cancellable, error);
+
+               g_object_unref (toplevel);
+
+               if (!isc->success) {
+                       isc->completed = TRUE;
+                       g_free (attendee);
+                       return;
+               }
+
+               /* Include only the 'attendee' and use the first component as well in the reply */
+               if (isc->method == I_CAL_METHOD_REPLY) {
+                       while (g_slist_next (isc->send_comps)) {
+                               ECalComponent *comp = isc->send_comps->next->data;
+
+                               isc->send_comps = g_slist_remove (isc->send_comps, comp);
+
+                               g_clear_object (&comp);
+                       }
+
+                       itip_utils_remove_all_but_attendee (e_cal_component_get_icalcomponent 
(isc->send_comps->data), attendee);
+               }
+
+               g_free (attendee);
+       }
+
+       if (isc->method != I_CAL_METHOD_PUBLISH && e_cal_client_check_save_schedules (isc->cal_client)) {
                isc->success = TRUE;
                isc->completed = TRUE;
                return;
        }
 
-       if (isc->ensure_master_object && isc->send_comps) {
+       if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT) != 0 && isc->send_comps) {
                /* Ensure we send the master object with its detached instances, not the instance only */
                GSList *ecalcomps = NULL;
                const gchar *uid;
@@ -2059,7 +2213,7 @@ itip_send_component_begin (ItipSendComponentData *isc,
        }
 
        /* Give the server a chance to manipulate the comp */
-       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
+       if (isc->method != I_CAL_METHOD_PUBLISH) {
                d (printf ("itip-utils.c: itip_send_component_begin: calling comp_server_send_sync... \n"));
                if (!comp_server_send_sync (isc->method, isc->send_comps, isc->cal_client, isc->zones, 
&isc->users, cancellable, error)) {
                        isc->success = FALSE;
@@ -2069,7 +2223,7 @@ itip_send_component_begin (ItipSendComponentData *isc,
        }
 
        /* check whether backend could handle sending requests/updates */
-       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH &&
+       if (isc->method != I_CAL_METHOD_PUBLISH &&
            e_client_check_capability (E_CLIENT (isc->cal_client), E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
                isc->success = TRUE;
                isc->completed = TRUE;
@@ -2201,7 +2355,8 @@ itip_send_component_complete (ItipSendComponentData *isc)
        identity_uid = get_identity_uid_for_from (shell, isc->method, isc->send_comps->data, isc->cal_client, 
&identity_name, &identity_address);
 
        /* Tidy up the comp */
-       if (!comp_compliant (isc->registry, isc->method, isc->send_comps, TRUE, isc->cal_client, isc->zones, 
default_zone, isc->strip_alarms)) {
+       if (!comp_compliant (isc->registry, isc->method, isc->send_comps, TRUE, isc->cal_client, isc->zones, 
default_zone,
+                            (isc->flags & E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS) != 0)) {
                g_free (identity_uid);
                g_free (identity_name);
                g_free (identity_address);
@@ -2211,9 +2366,9 @@ itip_send_component_complete (ItipSendComponentData *isc)
        /* Recipients */
        destinations = comp_to_list (
                isc->registry, isc->method, isc->send_comps->data, isc->users, FALSE,
-               isc->only_new_attendees ? g_object_get_data (
+               (isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES) != 0 ? g_object_get_data (
                G_OBJECT (isc->send_comps->data), "new-attendees") : NULL);
-       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
+       if (isc->method != I_CAL_METHOD_PUBLISH) {
                if (destinations == NULL) {
                        /* We sent them all via the server */
                        isc->success = TRUE;
@@ -2237,7 +2392,7 @@ itip_send_component_complete (ItipSendComponentData *isc)
        ccd->event_body_text = NULL;
        ccd->attachments_list = isc->attachments_list;
        ccd->send_comps = isc->send_comps;
-       ccd->show_only = isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users;
+       ccd->show_only = isc->method == I_CAL_METHOD_PUBLISH && !isc->users;
 
        isc->attachments_list = NULL;
        isc->send_comps = NULL;
@@ -2276,15 +2431,13 @@ itip_send_component_thread (EAlertSinkThreadJobData *job_data,
 
 void
 itip_send_component_with_model (ECalModel *model,
-                               ECalComponentItipMethod method,
+                               ICalPropertyMethod method,
                                ECalComponent *send_comp,
                                ECalClient *cal_client,
                                ICalComponent *zones,
                                GSList *attachments_list,
                                GSList *users,
-                               gboolean strip_alarms,
-                               gboolean only_new_attendees,
-                               gboolean ensure_master_object)
+                               EItipSendComponentFlags flags)
 {
        ESourceRegistry *registry;
        ECalDataModel *data_model;
@@ -2337,9 +2490,7 @@ itip_send_component_with_model (ECalModel *model,
                        link->data = g_strdup (link->data);
                }
        }
-       isc->strip_alarms = strip_alarms;
-       isc->only_new_attendees = only_new_attendees;
-       isc->ensure_master_object = ensure_master_object;
+       isc->flags = flags;
        isc->success = FALSE;
        isc->completed = FALSE;
 
@@ -2354,7 +2505,7 @@ itip_send_component_with_model (ECalModel *model,
 
 gboolean
 itip_send_comp_sync (ESourceRegistry *registry,
-                    ECalComponentItipMethod method,
+                    ICalPropertyMethod method,
                     ECalComponent *send_comp,
                     ECalClient *cal_client,
                     ICalComponent *zones,
@@ -2376,8 +2527,8 @@ itip_send_comp_sync (ESourceRegistry *registry,
        isc.zones = zones;
        isc.attachments_list = attachments_list;
        isc.users = users;
-       isc.strip_alarms = strip_alarms;
-       isc.only_new_attendees = only_new_attendees;
+       isc.flags = (strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
+                   (only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0);
 
        isc.completed = FALSE;
        isc.success = FALSE;
@@ -2406,15 +2557,13 @@ itip_send_component_task_thread (GTask *task,
 
 void
 itip_send_component (ESourceRegistry *registry,
-                    ECalComponentItipMethod method,
+                    ICalPropertyMethod method,
                     ECalComponent *send_comp,
                     ECalClient *cal_client,
                     ICalComponent *zones,
                     GSList *attachments_list,
                     GSList *users,
-                    gboolean strip_alarms,
-                    gboolean only_new_attendees,
-                    gboolean ensure_master_object,
+                    EItipSendComponentFlags flags,
                     GCancellable *cancellable,
                     GAsyncReadyCallback callback,
                     gpointer user_data)
@@ -2438,10 +2587,7 @@ itip_send_component (ESourceRegistry *registry,
                        link->data = g_strdup (link->data);
                }
        }
-       isc->strip_alarms = strip_alarms;
-       isc->only_new_attendees = only_new_attendees;
-       isc->ensure_master_object = ensure_master_object;
-
+       isc->flags = flags;
        isc->completed = FALSE;
        isc->success = FALSE;
 
@@ -2475,7 +2621,7 @@ itip_send_component_finish (GAsyncResult *result,
 
 gboolean
 reply_to_calendar_comp (ESourceRegistry *registry,
-                        ECalComponentItipMethod method,
+                        ICalPropertyMethod method,
                         ECalComponent *send_comp,
                         ECalClient *cal_client,
                         gboolean reply_all,
@@ -2792,3 +2938,168 @@ itip_component_has_recipients (ECalComponent *comp)
 
        return res;
 }
+
+void
+itip_utils_update_cdo_replytime (ICalComponent *icomp)
+{
+       ICalTime *stamp;
+       gchar *str;
+
+       g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
+
+       while (e_cal_util_component_remove_x_property (icomp, "X-MICROSOFT-CDO-REPLYTIME")) {
+               /* Delete all existing X-MICROSOFT-CDO-REPLYTIME properties first */
+       }
+
+       /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
+        * the user accepted/declined the request. (Outlook ignores
+        * SEQUENCE in REPLY reponses and instead requires that each
+        * updated response have a later REPLYTIME than the previous
+        * one.) This also ends up getting saved in our own copy of
+        * the meeting, though there's currently no way to see that
+        * information (unless it's being saved to an Exchange folder
+        * and you then look at it in Outlook).
+        */
+       stamp = i_cal_time_new_current_with_zone (i_cal_timezone_get_utc_timezone ());
+       str = i_cal_time_as_ical_string (stamp);
+       e_cal_util_component_set_x_property (icomp, "X-MICROSOFT-CDO-REPLYTIME", str);
+       g_clear_object (&stamp);
+       g_free (str);
+}
+
+ICalProperty *
+itip_utils_find_attendee_property (ICalComponent *icomp,
+                                  const gchar *address)
+{
+       ICalProperty *prop;
+
+       if (!address || !*address)
+               return NULL;
+
+       for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
+            prop;
+            g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, 
I_CAL_ATTENDEE_PROPERTY)) {
+               gchar *attendee;
+               gchar *text;
+
+               attendee = i_cal_property_get_value_as_string (prop);
+
+                if (!attendee)
+                       continue;
+
+               text = g_strdup (itip_strip_mailto (attendee));
+               text = g_strstrip (text);
+               if (text && !g_ascii_strcasecmp (address, text)) {
+                       g_free (text);
+                       g_free (attendee);
+                       break;
+               }
+               g_free (text);
+               g_free (attendee);
+       }
+
+       return prop;
+}
+
+void
+itip_utils_prepare_attendee_response (ESourceRegistry *registry,
+                                     ICalComponent *icomp,
+                                     const gchar *address,
+                                     ICalParameterPartstat partstat)
+{
+       ICalProperty *prop;
+
+       prop = itip_utils_find_attendee_property (icomp, address);
+       if (prop) {
+               ICalParameter *param;
+
+               i_cal_property_remove_parameter_by_kind (prop, I_CAL_PARTSTAT_PARAMETER);
+               param = i_cal_parameter_new_partstat (partstat);
+               if (param)
+                       i_cal_property_take_parameter (prop, param);
+
+               g_object_unref (prop);
+       } else {
+               ICalParameter *param;
+
+               if (address && *address) {
+                       gchar *mailto_uri;
+
+                       mailto_uri = g_strconcat ("mailto:";, itip_strip_mailto (address), NULL);
+                       prop = i_cal_property_new_attendee (mailto_uri);
+                       g_free (mailto_uri);
+
+                       param = i_cal_parameter_new_role (I_CAL_ROLE_OPTPARTICIPANT);
+                       i_cal_property_take_parameter (prop, param);
+
+                       param = i_cal_parameter_new_partstat (partstat);
+                       if (param)
+                               i_cal_property_take_parameter (prop, param);
+
+                       i_cal_component_take_property (icomp, prop);
+               } else {
+                       gchar *default_name = NULL;
+                       gchar *default_address = NULL;
+                       gchar *mailto_uri;
+
+                       itip_get_default_name_and_address (
+                               registry, &default_name, &default_address);
+
+                       mailto_uri = g_strconcat ("mailto:";, itip_strip_mailto (default_address), NULL);
+                       prop = i_cal_property_new_attendee (mailto_uri);
+                       g_free (mailto_uri);
+
+                       if (default_name && *default_name && g_strcmp0 (default_name, default_address) != 0) {
+                               param = i_cal_parameter_new_cn (default_name);
+                               i_cal_property_take_parameter (prop, param);
+                       }
+
+                       param = i_cal_parameter_new_role (I_CAL_ROLE_REQPARTICIPANT);
+                       i_cal_property_take_parameter (prop, param);
+
+                       param = i_cal_parameter_new_partstat (partstat);
+                       if (param)
+                               i_cal_property_take_parameter (prop, param);
+
+                       i_cal_component_take_property (icomp, prop);
+
+                       g_free (default_name);
+                       g_free (default_address);
+               }
+       }
+}
+
+gboolean
+itip_utils_remove_all_but_attendee (ICalComponent *icomp,
+                                   const gchar *attendee)
+{
+       ICalProperty *prop;
+       GSList *remove = NULL, *link;
+       gboolean found = FALSE;
+
+       g_return_val_if_fail (I_CAL_IS_COMPONENT (icomp), FALSE);
+       g_return_val_if_fail (attendee != NULL, FALSE);
+
+       for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
+            prop;
+            prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
+               const gchar *address = i_cal_property_get_attendee (prop);
+
+               if (found || g_ascii_strcasecmp (itip_strip_mailto (address), attendee) != 0) {
+                       remove = g_slist_prepend (remove, prop);
+               } else {
+                       found = TRUE;
+                       g_object_unref (prop);
+               }
+       }
+
+       for (link = remove; link; link = g_slist_next (link)) {
+               prop = link->data;
+
+               i_cal_component_remove_property (icomp, prop);
+       }
+
+       g_slist_free_full (remove, g_object_unref);
+
+       return found;
+}
diff --git a/src/calendar/gui/itip-utils.h b/src/calendar/gui/itip-utils.h
index b6b89664ac..d2aba970f3 100644
--- a/src/calendar/gui/itip-utils.h
+++ b/src/calendar/gui/itip-utils.h
@@ -26,16 +26,14 @@
 G_BEGIN_DECLS
 
 typedef enum {
-       E_CAL_COMPONENT_METHOD_NONE = -1,
-       E_CAL_COMPONENT_METHOD_PUBLISH,
-       E_CAL_COMPONENT_METHOD_REQUEST,
-       E_CAL_COMPONENT_METHOD_REPLY,
-       E_CAL_COMPONENT_METHOD_ADD,
-       E_CAL_COMPONENT_METHOD_CANCEL,
-       E_CAL_COMPONENT_METHOD_REFRESH,
-       E_CAL_COMPONENT_METHOD_COUNTER,
-       E_CAL_COMPONENT_METHOD_DECLINECOUNTER
-} ECalComponentItipMethod;
+       E_ITIP_SEND_COMPONENT_FLAG_NONE                         = 0,
+       E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS                 = 1 << 0,
+       E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES           = 1 << 1,
+       E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT         = 1 << 2,
+       E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED       = 1 << 3,
+       E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED       = 1 << 4,
+       E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_TENTATIVE      = 1 << 5
+} EItipSendComponentFlags;
 
 struct CalMimeAttach {
        gchar *filename;
@@ -69,11 +67,14 @@ gboolean    itip_sentby_is_user             (ESourceRegistry *registry,
                                                 ECalClient *cal_client);
 gboolean       itip_has_any_attendees          (ECalComponent *comp);
 const gchar *  itip_strip_mailto               (const gchar *address);
+gboolean       itip_attendee_is_user           (ESourceRegistry *registry,
+                                                ECalComponent *comp,
+                                                ECalClient *cal_client);
 gchar *                itip_get_comp_attendee          (ESourceRegistry *registry,
                                                 ECalComponent *comp,
                                                 ECalClient *cal_client);
 gboolean       itip_send_comp_sync             (ESourceRegistry *registry,
-                                                ECalComponentItipMethod method,
+                                                ICalPropertyMethod method,
                                                 ECalComponent *send_comp,
                                                 ECalClient *cal_client,
                                                 ICalComponent *zones,
@@ -84,25 +85,21 @@ gboolean    itip_send_comp_sync             (ESourceRegistry *registry,
                                                 GCancellable *cancellable,
                                                 GError **error);
 void           itip_send_component_with_model  (ECalModel *model,
-                                                ECalComponentItipMethod method,
+                                                ICalPropertyMethod method,
                                                 ECalComponent *send_comp,
                                                 ECalClient *cal_client,
                                                 ICalComponent *zones,
                                                 GSList *attachments_list,
                                                 GSList *users,
-                                                gboolean strip_alarms,
-                                                gboolean only_new_attendees,
-                                                gboolean ensure_master_object);
+                                                EItipSendComponentFlags flags);
 void           itip_send_component             (ESourceRegistry *registry,
-                                                ECalComponentItipMethod method,
+                                                ICalPropertyMethod method,
                                                 ECalComponent *send_comp,
                                                 ECalClient *cal_client,
                                                 ICalComponent *zones,
                                                 GSList *attachments_list,
                                                 GSList *users,
-                                                gboolean strip_alarms,
-                                                gboolean only_new_attendees,
-                                                gboolean ensure_master_object,
+                                                EItipSendComponentFlags flags,
                                                 GCancellable *cancellable,
                                                 GAsyncReadyCallback callback,
                                                 gpointer user_data);
@@ -113,7 +110,7 @@ gboolean    itip_publish_begin              (ECalComponent *pub_comp,
                                                 gboolean cloned,
                                                 ECalComponent **clone);
 gboolean       reply_to_calendar_comp          (ESourceRegistry *registry,
-                                                ECalComponentItipMethod method,
+                                                ICalPropertyMethod method,
                                                 ECalComponent *send_comp,
                                                 ECalClient *cal_client,
                                                 gboolean reply_all,
@@ -121,6 +118,18 @@ gboolean   reply_to_calendar_comp          (ESourceRegistry *registry,
                                                 GSList *attachments_list);
 gboolean       itip_is_component_valid         (ICalComponent *icomp);
 gboolean       itip_component_has_recipients   (ECalComponent *comp);
+void           itip_utils_update_cdo_replytime (ICalComponent *icomp);
+gboolean       itip_utils_remove_all_but_attendee
+                                               (ICalComponent *icomp,
+                                                const gchar *attendee);
+ICalProperty * itip_utils_find_attendee_property
+                                               (ICalComponent *icomp,
+                                                const gchar *address);
+void           itip_utils_prepare_attendee_response
+                                               (ESourceRegistry *registry,
+                                                ICalComponent *icomp,
+                                                const gchar *address,
+                                                ICalParameterPartstat partstat);
 
 G_END_DECLS
 
diff --git a/src/modules/calendar/e-cal-base-shell-content.h b/src/modules/calendar/e-cal-base-shell-content.h
index b362c40c06..541f77d000 100644
--- a/src/modules/calendar/e-cal-base-shell-content.h
+++ b/src/modules/calendar/e-cal-base-shell-content.h
@@ -63,7 +63,8 @@ enum {
        E_CAL_BASE_SHELL_CONTENT_SELECTION_CAN_ASSIGN     = 1 << 8,
        E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_COMPLETE   = 1 << 9,
        E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE = 1 << 10,
-       E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_URL        = 1 << 11
+       E_CAL_BASE_SHELL_CONTENT_SELECTION_HAS_URL        = 1 << 11,
+       E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ATTENDEE    = 1 << 12
 };
 
 struct _ECalBaseShellContent {
diff --git a/src/modules/calendar/e-cal-shell-content.c b/src/modules/calendar/e-cal-shell-content.c
index 7588f1b8af..ee926390d2 100644
--- a/src/modules/calendar/e-cal-shell-content.c
+++ b/src/modules/calendar/e-cal-shell-content.c
@@ -965,6 +965,7 @@ cal_shell_content_check_state (EShellContent *shell_content)
        gboolean selection_is_instance = FALSE;
        gboolean selection_is_meeting = FALSE;
        gboolean selection_is_organizer = FALSE;
+       gboolean selection_is_attendee = FALSE;
        gboolean selection_is_recurring = FALSE;
        gboolean selection_can_delegate = FALSE;
        guint32 state = 0;
@@ -1056,6 +1057,11 @@ cal_shell_content_check_state (EShellContent *shell_content)
                        (!selection_is_organizer &&
                         !icomp_is_delegated));
 
+               selection_is_attendee = !selection_is_organizer &&
+                       selection_is_meeting &&
+                       !icomp_is_delegated &&
+                       itip_attendee_is_user (registry, comp, client);
+
                g_free (user_email);
                g_object_unref (comp);
        }
@@ -1074,6 +1080,8 @@ cal_shell_content_check_state (EShellContent *shell_content)
                state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_MEETING;
        if (selection_is_organizer)
                state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ORGANIZER;
+       if (selection_is_attendee)
+               state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ATTENDEE;
        if (selection_is_recurring)
                state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_RECURRING;
        if (selection_can_delegate)
diff --git a/src/modules/calendar/e-cal-shell-view-actions.c b/src/modules/calendar/e-cal-shell-view-actions.c
index efb2f95773..6ef5225d83 100644
--- a/src/modules/calendar/e-cal-shell-view-actions.c
+++ b/src/modules/calendar/e-cal-shell-view-actions.c
@@ -755,8 +755,8 @@ action_event_forward_cb (GtkAction *action,
        g_return_if_fail (component != NULL);
 
        itip_send_component_with_model (e_calendar_view_get_model (calendar_view),
-               E_CAL_COMPONENT_METHOD_PUBLISH, component, client,
-               NULL, NULL, NULL, TRUE, FALSE, TRUE);
+               I_CAL_METHOD_PUBLISH, component, client,
+               NULL, NULL, NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
        g_object_unref (component);
        g_list_free (selected);
@@ -784,6 +784,75 @@ action_event_popup_new_cb (GtkAction *action,
                (e_shell_view_is_active (E_SHELL_VIEW (cal_shell_view)) ? 0 : 
E_NEW_APPOINTMENT_FLAG_FORCE_CURRENT_TIME));
 }
 
+static void
+action_event_popup_rsvp_response_cb (GtkAction *action,
+                                    ECalShellView *cal_shell_view)
+{
+       ECalShellContent *cal_shell_content;
+       ECalendarView *calendar_view;
+       ECalendarViewEvent *event;
+       ECalClient *client;
+       ECalComponent *comp;
+       ECalModel *model;
+       ICalParameterPartstat partstat;
+       ICalComponent *clone;
+       GList *selected;
+       const gchar *action_name;
+       gboolean ensure_master_object;
+
+       cal_shell_content = cal_shell_view->priv->cal_shell_content;
+       calendar_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
+       action_name = gtk_action_get_name (action);
+
+       if (g_strcmp0 (action_name, "event-popup-rsvp-accept") == 0 ||
+           g_strcmp0 (action_name, "event-popup-rsvp-accept-1") == 0)
+               partstat = I_CAL_PARTSTAT_ACCEPTED;
+       else if (g_strcmp0 (action_name, "event-popup-rsvp-decline") == 0 ||
+                g_strcmp0 (action_name, "event-popup-rsvp-decline-1") == 0)
+               partstat = I_CAL_PARTSTAT_DECLINED;
+       else if (g_strcmp0 (action_name, "event-popup-rsvp-tentative") == 0 ||
+                g_strcmp0 (action_name, "event-popup-rsvp-tentative-1") == 0)
+               partstat = I_CAL_PARTSTAT_TENTATIVE;
+       else {
+               g_warning ("%s: Do not know what to do with '%s'", G_STRFUNC, action_name);
+       }
+
+       selected = e_calendar_view_get_selected_events (calendar_view);
+       g_return_if_fail (g_list_length (selected) == 1);
+
+       event = selected->data;
+
+       g_list_free (selected);
+
+       if (!is_comp_data_valid (event))
+               return;
+
+       client = event->comp_data->client;
+       model = e_calendar_view_get_model (calendar_view);
+
+       clone = i_cal_component_clone (event->comp_data->icalcomp);
+       comp = e_cal_component_new_from_icalcomponent (clone);
+
+       if (!comp) {
+               g_warn_if_reached ();
+               return;
+       }
+
+       ensure_master_object = (e_cal_util_component_is_instance (clone) ||
+                               e_cal_util_component_has_recurrences (clone)) &&
+                               !g_str_has_suffix (action_name, "-1");
+
+       itip_send_component_with_model (model, I_CAL_METHOD_REPLY,
+               comp, client, NULL, NULL, NULL,
+               E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS |
+               (ensure_master_object ? E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT : 0) |
+               (partstat == I_CAL_PARTSTAT_ACCEPTED ? E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED : 0) 
|
+               (partstat == I_CAL_PARTSTAT_DECLINED ? E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED : 0) 
|
+               (partstat == I_CAL_PARTSTAT_TENTATIVE ? E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_TENTATIVE : 
0));
+
+       g_clear_object (&comp);
+}
+
 typedef struct {
        ECalClient *client;
        gchar *remove_uid;
@@ -1029,7 +1098,7 @@ cal_shell_view_actions_reply (ECalShellView *cal_shell_view,
        component = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
 
        reply_to_calendar_comp (
-               registry, E_CAL_COMPONENT_METHOD_REPLY,
+               registry, I_CAL_METHOD_REPLY,
                component, client, reply_all, NULL, NULL);
 
        g_object_unref (component);
@@ -1392,6 +1461,55 @@ static GtkActionEntry calendar_entries[] = {
          N_("Create a new meeting"),
          G_CALLBACK (action_event_popup_new_cb) },
 
+       { "event-popup-rsvp-submenu",
+         NULL,
+         N_("Send _RSVP"),
+         NULL,
+         N_("Send a meeting response"),
+         NULL },
+
+       { "event-popup-rsvp-accept",
+         NULL,
+         N_("_Accept"),
+         NULL,
+         N_("Accept meeting request"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
+       { "event-popup-rsvp-accept-1",
+         NULL,
+         N_("A_ccept this instance"),
+         NULL,
+         N_("Accept meeting request for selected instance only"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
+       { "event-popup-rsvp-decline",
+         NULL,
+         N_("_Decline"),
+         NULL,
+         N_("Decline meeting request"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
+       { "event-popup-rsvp-decline-1",
+         NULL,
+         N_("D_ecline this instance"),
+         NULL,
+         N_("Decline meeting request for selected instance only"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
+       { "event-popup-rsvp-tentative",
+         NULL,
+         N_("_Tentatively accept"),
+         NULL,
+         N_("Tentatively accept meeting request"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
+       { "event-popup-rsvp-tentative-1",
+         NULL,
+         N_("Te_ntatively accept this instance"),
+         NULL,
+         N_("Tentatively accept meeting request for selected instance only"),
+         G_CALLBACK (action_event_popup_rsvp_response_cb) },
+
        { "event-move",
          NULL,
          N_("Mo_ve to Calendar…"),
diff --git a/src/modules/calendar/e-cal-shell-view-actions.h b/src/modules/calendar/e-cal-shell-view-actions.h
index 577f894beb..ea47a0ce02 100644
--- a/src/modules/calendar/e-cal-shell-view-actions.h
+++ b/src/modules/calendar/e-cal-shell-view-actions.h
@@ -104,6 +104,20 @@
        E_SHELL_WINDOW_ACTION ((window), "event-occurrence-movable")
 #define E_SHELL_WINDOW_ACTION_EVENT_POPUP_MEETING_NEW(window) \
        E_SHELL_WINDOW_ACTION ((window), "event-popup-meeting-new")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_SUBMENU(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-submenu")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_ACCEPT(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-accept")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_ACCEPT_1(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-accept-1")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_DECLINE(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-decline")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_DECLINE_1(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-decline-1")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_TENTATIVE(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-tentative")
+#define E_SHELL_WINDOW_ACTION_EVENT_POPUP_RSVP_TENTATIVE_1(window) \
+       E_SHELL_WINDOW_ACTION ((window), "event-popup-rsvp-tentative-1")
 
 /* Memo Pad Actions */
 #define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_FORWARD(window) \
diff --git a/src/modules/calendar/e-cal-shell-view-memopad.c b/src/modules/calendar/e-cal-shell-view-memopad.c
index 2b9bce1dad..83053cae95 100644
--- a/src/modules/calendar/e-cal-shell-view-memopad.c
+++ b/src/modules/calendar/e-cal-shell-view-memopad.c
@@ -50,8 +50,8 @@ action_calendar_memopad_forward_cb (GtkAction *action,
        g_return_if_fail (comp != NULL);
 
        itip_send_component_with_model (e_memo_table_get_model (memo_table),
-               E_CAL_COMPONENT_METHOD_PUBLISH, comp, comp_data->client,
-               NULL, NULL, NULL, TRUE, FALSE, TRUE);
+               I_CAL_METHOD_PUBLISH, comp, comp_data->client,
+               NULL, NULL, NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
        g_object_unref (comp);
 }
diff --git a/src/modules/calendar/e-cal-shell-view-taskpad.c b/src/modules/calendar/e-cal-shell-view-taskpad.c
index 467a64728b..0f495a7eac 100644
--- a/src/modules/calendar/e-cal-shell-view-taskpad.c
+++ b/src/modules/calendar/e-cal-shell-view-taskpad.c
@@ -79,8 +79,8 @@ action_calendar_taskpad_forward_cb (GtkAction *action,
        g_return_if_fail (comp != NULL);
 
        itip_send_component_with_model (e_task_table_get_model (task_table),
-               E_CAL_COMPONENT_METHOD_PUBLISH, comp, comp_data->client,
-               NULL, NULL, NULL, TRUE, FALSE, TRUE);
+               I_CAL_METHOD_PUBLISH, comp, comp_data->client,
+               NULL, NULL, NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
        g_object_unref (comp);
 }
diff --git a/src/modules/calendar/e-cal-shell-view.c b/src/modules/calendar/e-cal-shell-view.c
index 4242435c75..1c8bb6ed9d 100644
--- a/src/modules/calendar/e-cal-shell-view.c
+++ b/src/modules/calendar/e-cal-shell-view.c
@@ -276,6 +276,7 @@ cal_shell_view_update_actions (EShellView *shell_view)
        gboolean selection_is_instance;
        gboolean selection_is_meeting;
        gboolean selection_is_recurring;
+       gboolean selection_is_attendee;
        gboolean selection_can_delegate;
        gboolean single_event_selected;
        gboolean refresh_supported;
@@ -328,6 +329,8 @@ cal_shell_view_update_actions (EShellView *shell_view)
                (state & E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_INSTANCE);
        selection_is_meeting =
                (state & E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_MEETING);
+       selection_is_attendee =
+               (state & E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ATTENDEE);
        selection_is_recurring =
                (state & E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_RECURRING);
        selection_can_delegate =
@@ -494,6 +497,14 @@ cal_shell_view_update_actions (EShellView *shell_view)
        action = ACTION (EVENT_POPUP_MEETING_NEW);
        gtk_action_set_visible (action, has_mail_identity);
 
+       action = ACTION (EVENT_POPUP_RSVP_SUBMENU);
+       gtk_action_set_visible (action, selection_is_attendee);
+
+       sensitive = selection_is_instance || selection_is_recurring;
+       gtk_action_set_visible (ACTION (EVENT_POPUP_RSVP_ACCEPT_1), sensitive);
+       gtk_action_set_visible (ACTION (EVENT_POPUP_RSVP_DECLINE_1), sensitive);
+       gtk_action_set_visible (ACTION (EVENT_POPUP_RSVP_TENTATIVE_1), sensitive);
+
        gtk_action_set_sensitive (ACTION (CALENDAR_GO_BACK), !is_list_view);
        gtk_action_set_sensitive (ACTION (CALENDAR_GO_FORWARD), !is_list_view);
        gtk_action_set_sensitive (ACTION (CALENDAR_GO_TODAY), !is_list_view);
diff --git a/src/modules/calendar/e-memo-shell-view-actions.c 
b/src/modules/calendar/e-memo-shell-view-actions.c
index 3a9bd899a3..d8a2bfd086 100644
--- a/src/modules/calendar/e-memo-shell-view-actions.c
+++ b/src/modules/calendar/e-memo-shell-view-actions.c
@@ -76,8 +76,8 @@ action_memo_forward_cb (GtkAction *action,
        g_return_if_fail (comp != NULL);
 
        itip_send_component_with_model (e_memo_table_get_model (memo_table),
-               E_CAL_COMPONENT_METHOD_PUBLISH, comp,
-               comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
+               I_CAL_METHOD_PUBLISH, comp,
+               comp_data->client, NULL, NULL, NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
        g_object_unref (comp);
 }
diff --git a/src/modules/calendar/e-task-shell-view-actions.c 
b/src/modules/calendar/e-task-shell-view-actions.c
index d90cfbe888..9d5196b758 100644
--- a/src/modules/calendar/e-task-shell-view-actions.c
+++ b/src/modules/calendar/e-task-shell-view-actions.c
@@ -99,8 +99,8 @@ action_task_forward_cb (GtkAction *action,
        g_return_if_fail (comp != NULL);
 
        itip_send_component_with_model (e_task_table_get_model (task_table),
-               E_CAL_COMPONENT_METHOD_PUBLISH, comp,
-               comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
+               I_CAL_METHOD_PUBLISH, comp,
+               comp_data->client, NULL, NULL, NULL, E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS | 
E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT);
 
        g_object_unref (comp);
 }
diff --git a/src/modules/itip-formatter/itip-view.c b/src/modules/itip-formatter/itip-view.c
index f3ff4876cd..9bfaa1c94b 100644
--- a/src/modules/itip-formatter/itip-view.c
+++ b/src/modules/itip-formatter/itip-view.c
@@ -3028,40 +3028,6 @@ typedef struct {
 
 static gboolean check_is_instance (ICalComponent *icomp);
 
-static ICalProperty *
-find_attendee (ICalComponent *icomp,
-               const gchar *address)
-{
-       ICalProperty *prop;
-
-       if (!address)
-               return NULL;
-
-       for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
-            prop;
-            g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, 
I_CAL_ATTENDEE_PROPERTY)) {
-               gchar *attendee;
-               gchar *text;
-
-               attendee = i_cal_property_get_value_as_string (prop);
-
-                if (!attendee)
-                       continue;
-
-               text = g_strdup (itip_strip_mailto (attendee));
-               text = g_strstrip (text);
-               if (text && !g_ascii_strcasecmp (address, text)) {
-                       g_free (text);
-                       g_free (attendee);
-                       break;
-               }
-               g_free (text);
-               g_free (attendee);
-       }
-
-       return prop;
-}
-
 static ICalProperty *
 find_attendee_if_sentby (ICalComponent *icomp,
                          const gchar *address)
@@ -3150,7 +3116,7 @@ find_to_address (ItipView *view,
                extension = e_source_get_extension (source, extension_name);
                address = e_source_mail_identity_dup_address (extension);
 
-               prop = find_attendee (icomp, address);
+               prop = itip_utils_find_attendee_property (icomp, address);
                if (!prop) {
                        GHashTable *aliases;
 
@@ -3164,7 +3130,7 @@ find_to_address (ItipView *view,
                                        const gchar *alias_address = key;
 
                                        if (alias_address && *alias_address) {
-                                               prop = find_attendee (icomp, alias_address);
+                                               prop = itip_utils_find_attendee_property (icomp, 
alias_address);
                                                if (prop) {
                                                        g_free (address);
                                                        address = g_strdup (alias_address);
@@ -4457,76 +4423,6 @@ find_server (ItipView *view,
        g_free (rid);
 }
 
-static gboolean
-change_status (ESourceRegistry *registry,
-              ICalComponent *icomp,
-              const gchar *address,
-              ICalParameterPartstat partstat)
-{
-       ICalProperty *prop;
-
-       prop = find_attendee (icomp, address);
-       if (prop) {
-               ICalParameter *param;
-
-               i_cal_property_remove_parameter_by_kind (prop, I_CAL_PARTSTAT_PARAMETER);
-               param = i_cal_parameter_new_partstat (partstat);
-               if (param)
-                       i_cal_property_take_parameter (prop, param);
-
-               g_object_unref (prop);
-       } else {
-               ICalParameter *param;
-
-               if (address && *address) {
-                       gchar *mailto_uri;
-
-                       mailto_uri = g_strconcat ("mailto:";, itip_strip_mailto (address), NULL);
-                       prop = i_cal_property_new_attendee (mailto_uri);
-                       g_free (mailto_uri);
-
-                       param = i_cal_parameter_new_role (I_CAL_ROLE_OPTPARTICIPANT);
-                       i_cal_property_take_parameter (prop, param);
-
-                       param = i_cal_parameter_new_partstat (partstat);
-                       if (param)
-                               i_cal_property_take_parameter (prop, param);
-
-                       i_cal_component_take_property (icomp, prop);
-               } else {
-                       gchar *default_name = NULL;
-                       gchar *default_address = NULL;
-                       gchar *mailto_uri;
-
-                       itip_get_default_name_and_address (
-                               registry, &default_name, &default_address);
-
-                       mailto_uri = g_strconcat ("mailto:";, itip_strip_mailto (default_address), NULL);
-                       prop = i_cal_property_new_attendee (mailto_uri);
-                       g_free (mailto_uri);
-
-                       if (default_name && *default_name && g_strcmp0 (default_name, default_address) != 0) {
-                               param = i_cal_parameter_new_cn (default_name);
-                               i_cal_property_take_parameter (prop, param);
-                       }
-
-                       param = i_cal_parameter_new_role (I_CAL_ROLE_REQPARTICIPANT);
-                       i_cal_property_take_parameter (prop, param);
-
-                       param = i_cal_parameter_new_partstat (partstat);
-                       if (param)
-                               i_cal_property_take_parameter (prop, param);
-
-                       i_cal_component_take_property (icomp, prop);
-
-                       g_free (default_name);
-                       g_free (default_address);
-               }
-       }
-
-       return TRUE;
-}
-
 static void
 message_foreach_part (CamelMimePart *part,
                       GSList **part_list)
@@ -4776,7 +4672,7 @@ finish_message_delete_with_rsvp (ItipView *view,
 
                if (itip_send_comp_sync (
                                view->priv->registry,
-                               E_CAL_COMPONENT_METHOD_REPLY,
+                               I_CAL_METHOD_REPLY,
                                comp, view->priv->current_client,
                                view->priv->top_level, NULL, NULL, TRUE, FALSE, NULL, NULL) &&
                                view->priv->folder) {
@@ -4976,32 +4872,12 @@ static void
 update_item (ItipView *view,
              ItipViewResponse response)
 {
-       ICalTime *stamp;
        ICalComponent *toplevel_clone, *clone;
        gboolean remove_alarms;
        ECalComponent *clone_comp;
-       gchar *str;
 
        claim_progress_saving_changes (view);
-
-       while (e_cal_util_component_remove_x_property (view->priv->ical_comp, "X-MICROSOFT-CDO-REPLYTIME")) {
-               /* Delete all existing X-MICROSOFT-CDO-REPLYTIME properties first */
-       }
-
-       /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
-        * the user accepted/declined the request. (Outlook ignores
-        * SEQUENCE in REPLY reponses and instead requires that each
-        * updated response have a later REPLYTIME than the previous
-        * one.) This also ends up getting saved in our own copy of
-        * the meeting, though there's currently no way to see that
-        * information (unless it's being saved to an Exchange folder
-        * and you then look at it in Outlook).
-        */
-       stamp = i_cal_time_new_current_with_zone (i_cal_timezone_get_utc_timezone ());
-       str = i_cal_time_as_ical_string (stamp);
-       e_cal_util_component_set_x_property (view->priv->ical_comp, "X-MICROSOFT-CDO-REPLYTIME", str);
-       g_clear_object (&stamp);
-       g_free (str);
+       itip_utils_update_cdo_replytime (view->priv->ical_comp);
 
        toplevel_clone = i_cal_component_clone (view->priv->top_level);
        clone = i_cal_component_clone (view->priv->ical_comp);
@@ -5157,24 +5033,14 @@ static void
 set_attendee (ECalComponent *comp,
               const gchar *address)
 {
-       ICalProperty *prop;
        ICalComponent *icomp;
        gboolean found = FALSE;
 
        icomp = e_cal_component_get_icalcomponent (comp);
-
-       for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
-            prop;
-            g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, 
I_CAL_ATTENDEE_PROPERTY)) {
-               const gchar *attendee = i_cal_property_get_attendee (prop);
-
-               if (!(g_str_equal (itip_strip_mailto (attendee), address)))
-                       i_cal_component_remove_property (icomp, prop);
-               else
-                       found = TRUE;
-       }
+       found = itip_utils_remove_all_but_attendee (icomp, address);
 
        if (!found) {
+               ICalProperty *prop;
                ICalParameter *param;
                gchar *temp = g_strdup_printf ("mailto:%s";, address);
 
@@ -5201,7 +5067,7 @@ set_attendee (ECalComponent *comp,
 
 static gboolean
 send_comp_to_attendee (ESourceRegistry *registry,
-                       ECalComponentItipMethod method,
+                       ICalPropertyMethod method,
                        ECalComponent *comp,
                        const gchar *user,
                        ECalClient *client,
@@ -5251,12 +5117,12 @@ remove_delegate (ItipView *view,
        /* send cancellation notice to delegate */
        status = send_comp_to_attendee (
                view->priv->registry,
-               E_CAL_COMPONENT_METHOD_CANCEL, view->priv->comp,
+               I_CAL_METHOD_CANCEL, view->priv->comp,
                delegate, view->priv->current_client, comment);
        if (status != 0) {
                send_comp_to_attendee (
                        view->priv->registry,
-                       E_CAL_COMPONENT_METHOD_REQUEST, view->priv->comp,
+                       I_CAL_METHOD_REQUEST, view->priv->comp,
                        delegator, view->priv->current_client, comment);
        }
        if (status != 0) {
@@ -5358,10 +5224,10 @@ update_attendee_status_icomp (ItipView *view,
                        ICalProperty *prop, *del_prop = NULL, *delto = NULL;
                        EShell *shell = e_shell_get_default ();
 
-                       prop = find_attendee (icomp, itip_strip_mailto (e_cal_component_attendee_get_value 
(a)));
+                       prop = itip_utils_find_attendee_property (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_value (a)));
                        if ((e_cal_component_attendee_get_partstat (a) == I_CAL_PARTSTAT_DELEGATED) &&
-                           (del_prop = find_attendee (org_icomp, itip_strip_mailto 
(e_cal_component_attendee_get_delegatedto (a)))) &&
-                           !(delto = find_attendee (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_delegatedto (a))))) {
+                           (del_prop = itip_utils_find_attendee_property (org_icomp, itip_strip_mailto 
(e_cal_component_attendee_get_delegatedto (a)))) &&
+                           !(delto = itip_utils_find_attendee_property (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_delegatedto (a))))) {
                                gint response;
                                delegate = i_cal_property_get_attendee (del_prop);
                                response = e_alert_run_dialog_for_args (
@@ -5400,7 +5266,7 @@ update_attendee_status_icomp (ItipView *view,
                                                itip_strip_mailto (e_cal_component_attendee_get_value (a)), 
NULL);
                                        if (response == GTK_RESPONSE_YES) {
                                                /* Already declared in this function */
-                                               ICalProperty *prop = find_attendee (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_value (a)));
+                                               ICalProperty *prop = itip_utils_find_attendee_property 
(icomp, itip_strip_mailto (e_cal_component_attendee_get_value (a)));
                                                i_cal_component_take_property (icomp, i_cal_property_clone 
(prop));
                                        } else if (response == GTK_RESPONSE_NO) {
                                                remove_delegate (
@@ -5419,7 +5285,7 @@ update_attendee_status_icomp (ItipView *view,
                                        "org.gnome.itip-formatter:add-unknown-attendee", NULL);
 
                                if (response == GTK_RESPONSE_YES) {
-                                       change_status (
+                                       itip_utils_prepare_attendee_response (
                                                view->priv->registry, icomp,
                                                itip_strip_mailto (e_cal_component_attendee_get_value (a)),
                                                e_cal_component_attendee_get_partstat (a));
@@ -5438,15 +5304,15 @@ update_attendee_status_icomp (ItipView *view,
                                        /* *prop already declared in this function */
                                        ICalProperty *subprop, *new_prop;
 
-                                       subprop = find_attendee (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_value (a)));
+                                       subprop = itip_utils_find_attendee_property (icomp, itip_strip_mailto 
(e_cal_component_attendee_get_value (a)));
                                        i_cal_component_remove_property (icomp, subprop);
                                        g_clear_object (&subprop);
 
-                                       new_prop = find_attendee (org_icomp, itip_strip_mailto 
(e_cal_component_attendee_get_value (a)));
+                                       new_prop = itip_utils_find_attendee_property (org_icomp, 
itip_strip_mailto (e_cal_component_attendee_get_value (a)));
                                        i_cal_component_take_property (icomp, i_cal_property_clone 
(new_prop));
                                        g_clear_object (&new_prop);
                                } else {
-                                       change_status (
+                                       itip_utils_prepare_attendee_response (
                                                view->priv->registry, icomp,
                                                itip_strip_mailto (e_cal_component_attendee_get_value (a)),
                                                e_cal_component_attendee_get_partstat (a));
@@ -5463,7 +5329,7 @@ update_attendee_status_icomp (ItipView *view,
                e_cal_component_commit_sequence (comp);
                itip_send_comp_sync (
                        view->priv->registry,
-                       E_CAL_COMPONENT_METHOD_REQUEST,
+                       I_CAL_METHOD_REQUEST,
                        comp, view->priv->current_client,
                        NULL, NULL, NULL, TRUE, FALSE, NULL, NULL);
        }
@@ -5595,7 +5461,7 @@ send_item (ItipView *view)
        if (comp != NULL) {
                itip_send_comp_sync (
                        view->priv->registry,
-                       E_CAL_COMPONENT_METHOD_REQUEST,
+                       I_CAL_METHOD_REQUEST,
                        comp, view->priv->current_client,
                        NULL, NULL, NULL, TRUE, FALSE, NULL, NULL);
                g_object_unref (comp);
@@ -5943,7 +5809,7 @@ extract_itip_data (ItipView *view,
                        view->priv->registry, comp, NULL);
                g_clear_object (&comp);
 
-               prop = find_attendee (view->priv->ical_comp, my_address);
+               prop = itip_utils_find_attendee_property (view->priv->ical_comp, my_address);
                if (!prop)
                        prop = find_attendee_if_sentby (view->priv->ical_comp, my_address);
                if (prop) {
@@ -6112,7 +5978,6 @@ view_response_cb (ItipView *view,
                   ItipViewResponse response,
                   gpointer user_data)
 {
-       gboolean status = FALSE;
        ICalProperty *prop;
        ECalComponentTransparency trans;
 
@@ -6153,44 +6018,35 @@ view_response_cb (ItipView *view,
        switch (response) {
                case ITIP_VIEW_RESPONSE_ACCEPT:
                        if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
-                               status = change_status (
+                               itip_utils_prepare_attendee_response (
                                        view->priv->registry,
                                        view->priv->ical_comp,
                                        view->priv->to_address,
                                        I_CAL_PARTSTAT_ACCEPTED);
-                       else
-                               status = TRUE;
-                       if (status) {
-                               update_item (view, response);
-                       }
+                       update_item (view, response);
                        break;
                case ITIP_VIEW_RESPONSE_TENTATIVE:
-                       status = change_status (
+                       itip_utils_prepare_attendee_response (
                                        view->priv->registry,
                                        view->priv->ical_comp,
                                        view->priv->to_address,
                                        I_CAL_PARTSTAT_TENTATIVE);
-                       if (status) {
-                               update_item (view, response);
-                       }
+                       update_item (view, response);
                        break;
                case ITIP_VIEW_RESPONSE_DECLINE:
-                       if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
-                               status = change_status (
+                       if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+                               itip_utils_prepare_attendee_response (
                                        view->priv->registry,
                                        view->priv->ical_comp,
                                        view->priv->to_address,
                                        I_CAL_PARTSTAT_DECLINED);
-                       else {
+                       } else {
                                prop = i_cal_property_new_x ("1");
                                i_cal_property_set_x_name (prop, "X-GW-DECLINED");
                                i_cal_component_take_property (view->priv->ical_comp, prop);
-                               status = TRUE;
                        }
 
-                       if (status) {
-                               update_item (view, response);
-                       }
+                       update_item (view, response);
                        break;
                case ITIP_VIEW_RESPONSE_UPDATE:
                        update_attendee_status (view);


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