[evolution-data-server] evo-I#1014 - Calendar: Correct RRULE's UNTIL value saving



commit 46966e450abad63755f0645c6d257b7627c0c7d1
Author: Milan Crha <mcrha redhat com>
Date:   Wed Aug 26 14:10:01 2020 +0200

    evo-I#1014 - Calendar: Correct RRULE's UNTIL value saving
    
    Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/1014

 src/calendar/backends/file/e-cal-backend-file.c |  27 ++--
 src/calendar/libecal/e-cal-util.c               | 179 ++++++++++++++++++++++--
 src/calendar/libecal/e-cal-util.h               |  17 +++
 src/calendar/libedata-cal/e-cal-meta-backend.c  |   8 +-
 4 files changed, 206 insertions(+), 25 deletions(-)
---
diff --git a/src/calendar/backends/file/e-cal-backend-file.c b/src/calendar/backends/file/e-cal-backend-file.c
index 78e96c6778..af10cb4781 100644
--- a/src/calendar/backends/file/e-cal-backend-file.c
+++ b/src/calendar/backends/file/e-cal-backend-file.c
@@ -2727,15 +2727,13 @@ e_cal_backend_file_modify_objects (ECalBackendSync *backend,
                                        i_cal_time_convert_to_zone_inplace (rid_struct, 
i_cal_time_get_timezone (master_dtstart));
                                }
 
-                               split_icomp = e_cal_util_split_at_instance (icomp, rid_struct, 
master_dtstart);
+                               split_icomp = e_cal_util_split_at_instance_ex (icomp, rid_struct, 
master_dtstart, resolve_tzid_cb, &rtd);
                                if (split_icomp) {
                                        ECalComponent *prev_comp;
 
                                        prev_comp = e_cal_component_clone (obj_data->full_object);
 
-                                       i_cal_time_convert_to_zone_inplace (rid_struct, 
i_cal_timezone_get_utc_timezone ());
-
-                                       e_cal_util_remove_instances (e_cal_component_get_icalcomponent 
(obj_data->full_object), rid_struct, mod);
+                                       e_cal_util_remove_instances_ex (e_cal_component_get_icalcomponent 
(obj_data->full_object), rid_struct, mod, resolve_tzid_cb, &rtd);
                                        e_cal_recur_ensure_end_dates (obj_data->full_object, TRUE, 
resolve_tzid_cb, &rtd, cancellable, NULL);
 
                                        e_cal_backend_notify_component_modified (E_CAL_BACKEND (backend), 
prev_comp, obj_data->full_object);
@@ -2753,7 +2751,7 @@ e_cal_backend_file_modify_objects (ECalBackendSync *backend,
                        } else {
                                ICalTime *rid_struct = i_cal_component_get_recurrenceid (icomp);
 
-                               split_icomp = e_cal_util_split_at_instance (icomp, rid_struct, NULL);
+                               split_icomp = e_cal_util_split_at_instance_ex (icomp, rid_struct, NULL, 
resolve_tzid_cb, &rtd);
 
                                g_object_unref (rid_struct);
                        }
@@ -2880,6 +2878,7 @@ remove_instance (ECalBackendFile *cbfile,
 
        if (rid) {
                ICalTime *rid_struct;
+               ResolveTzidData rtd;
                gpointer value;
 
                /* remove recurrence */
@@ -2954,14 +2953,15 @@ remove_instance (ECalBackendFile *cbfile,
                        if (master_dtstart && i_cal_time_get_timezone (master_dtstart)) {
                                i_cal_time_convert_to_zone_inplace (rid_struct, i_cal_time_get_timezone 
(master_dtstart));
                        }
-
-                       i_cal_time_convert_to_zone_inplace (rid_struct, i_cal_timezone_get_utc_timezone ());
                }
 
-               e_cal_util_remove_instances (
+               resolve_tzid_data_init (&rtd, cbfile->priv->vcalendar);
+
+               e_cal_util_remove_instances_ex (
                        e_cal_component_get_icalcomponent (obj_data->full_object),
-                       rid_struct, E_CAL_OBJ_MOD_THIS);
+                       rid_struct, E_CAL_OBJ_MOD_THIS, resolve_tzid_cb, &rtd);
 
+               resolve_tzid_data_clear (&rtd);
                g_clear_object (&rid_struct);
 
                /* Since we are only removing one instance of recurrence
@@ -3172,6 +3172,7 @@ e_cal_backend_file_remove_objects (ECalBackendSync *backend,
 
                        if (comp) {
                                ICalTime *rid_struct;
+                               ResolveTzidData rtd;
 
                                *old_components = g_slist_prepend (*old_components, e_cal_component_clone 
(comp));
 
@@ -3191,10 +3192,14 @@ e_cal_backend_file_remove_objects (ECalBackendSync *backend,
 
                                        i_cal_time_convert_to_zone_inplace (rid_struct, 
i_cal_timezone_get_utc_timezone ());
                                }
-                               e_cal_util_remove_instances (
+
+                               resolve_tzid_data_init (&rtd, priv->vcalendar);
+
+                               e_cal_util_remove_instances_ex (
                                        e_cal_component_get_icalcomponent (comp),
-                                       rid_struct, mod);
+                                       rid_struct, mod, resolve_tzid_cb, &rtd);
 
+                               resolve_tzid_data_clear (&rtd);
                                g_object_unref (rid_struct);
                        } else {
                                *old_components = g_slist_prepend (*old_components, NULL);
diff --git a/src/calendar/libecal/e-cal-util.c b/src/calendar/libecal/e-cal-util.c
index 9b0f034502..7b34c40305 100644
--- a/src/calendar/libecal/e-cal-util.c
+++ b/src/calendar/libecal/e-cal-util.c
@@ -1294,12 +1294,95 @@ time_matches_rid (const ICalTime *itt,
        return FALSE;
 }
 
+/**
+ * e_cal_util_normalize_rrule_until_value:
+ * @icalcomp: An #ICalComponent
+ * @ttuntil: An UNTIL value to validate
+ * @tz_cb: (closure tz_cb_data) (scope call): The #ECalRecurResolveTimezoneCb to call
+ * @tz_cb_data: (closure): User data to be passed to the @tz_cb callback
+ *
+ * Makes sure the @ttuntil value matches the value type with
+ * the DTSTART value, as required by RFC 5545 section 3.3.10.
+ * Uses @tz_cb with @tz_cb_data to resolve time zones when needed.
+ *
+ * Since: 3.38
+ **/
+void
+e_cal_util_normalize_rrule_until_value (ICalComponent *icalcomp,
+                                       ICalTime *ttuntil,
+                                       ECalRecurResolveTimezoneCb tz_cb,
+                                       gpointer tz_cb_data)
+{
+       ICalProperty *prop;
+
+       g_return_if_fail (I_CAL_IS_COMPONENT (icalcomp));
+       g_return_if_fail (I_CAL_IS_TIME (ttuntil));
+
+       prop = i_cal_component_get_first_property (icalcomp, I_CAL_DTSTART_PROPERTY);
+
+       if (prop) {
+               ICalTime *dtstart;
+
+               dtstart = i_cal_component_get_dtstart (icalcomp);
+
+               if (dtstart) {
+                       if (i_cal_time_is_date (dtstart)) {
+                               i_cal_time_set_time (ttuntil, 0, 0, 0);
+                               i_cal_time_set_is_date (ttuntil, TRUE);
+                       } else {
+                               if (i_cal_time_is_date (ttuntil)) {
+                                       gint hour = 0, minute = 0, second = 0;
+
+                                       i_cal_time_set_is_date (ttuntil, FALSE);
+
+                                       i_cal_time_get_time (dtstart, &hour, &minute, &second);
+                                       i_cal_time_set_time (ttuntil, hour, minute, second);
+                               }
+
+                               if (!i_cal_time_is_utc (dtstart)) {
+                                       ICalParameter *param;
+
+                                       param = i_cal_property_get_first_parameter (prop, 
I_CAL_TZID_PARAMETER);
+
+                                       if (param) {
+                                               const gchar *tzid;
+
+                                               tzid = i_cal_parameter_get_tzid (param);
+
+                                               if (tzid && *tzid && g_ascii_strcasecmp (tzid, "UTC") != 0) {
+                                                       ICalTimezone *tz;
+
+                                                       tz = i_cal_time_get_timezone (dtstart);
+
+                                                       if (!tz && tz_cb)
+                                                               tz = tz_cb (tzid, tz_cb_data, NULL, NULL);
+
+                                                       if (tz) {
+                                                               i_cal_time_set_timezone (ttuntil, tz);
+                                                               i_cal_time_convert_to_zone_inplace (ttuntil, 
i_cal_timezone_get_utc_timezone ());
+                                                       }
+                                               }
+
+                                               g_object_unref (param);
+                                       }
+                               }
+                       }
+
+                       g_object_unref (dtstart);
+               }
+
+               g_object_unref (prop);
+       }
+}
+
 static void
-e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
-                               const ICalTime *rid,
-                               ECalObjModType mod,
-                               gboolean keep_rid,
-                               gboolean can_add_exrule)
+e_cal_util_remove_instances_impl (ICalComponent *icalcomp,
+                                 const ICalTime *rid,
+                                 ECalObjModType mod,
+                                 gboolean keep_rid,
+                                 gboolean can_add_exrule,
+                                 ECalRecurResolveTimezoneCb tz_cb,
+                                 gpointer tz_cb_data)
 {
        ICalProperty *prop;
        ICalTime *itt, *recur;
@@ -1419,6 +1502,7 @@ e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
                                        g_clear_object (&dtstart);
                                } else {
                                        ICalTime *ttuntil;
+                                       gboolean is_date;
 
                                        if (keep_rid && i_cal_time_compare (recur, (ICalTime *) rid) == 0) {
                                                ICalDuration *dur;
@@ -1429,7 +1513,13 @@ e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
                                        } else {
                                                ttuntil = i_cal_time_clone (rid);
                                        }
-                                       i_cal_time_adjust (ttuntil, 0, 0, 0, -1);
+
+                                       e_cal_util_normalize_rrule_until_value (icalcomp, ttuntil, tz_cb, 
tz_cb_data);
+
+                                       is_date = i_cal_time_is_date (ttuntil);
+
+                                       i_cal_time_adjust (ttuntil, is_date ? -1 : 0, 0, 0, is_date ? 0 : -1);
+
                                        i_cal_recurrence_set_until (rule, ttuntil);
                                        g_object_unref (ttuntil);
                                }
@@ -1466,6 +1556,7 @@ e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
                                                ttuntil = i_cal_time_add ((ICalTime *) rid, dur);
                                        }
 
+                                       e_cal_util_normalize_rrule_until_value (icalcomp, ttuntil, tz_cb, 
tz_cb_data);
                                        i_cal_recurrence_set_until (rule, ttuntil);
 
                                        g_clear_object (&ttuntil);
@@ -1501,6 +1592,9 @@ e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
  * @mod: How to interpret @rid
  *
  * Removes one or more instances from @icalcomp according to @rid and @mod.
+ *
+ * Deprecated: 3.38: Use e_cal_util_remove_instances_ex() instead, with provided
+ *    timezone resolve function.
  **/
 void
 e_cal_util_remove_instances (ICalComponent *icalcomp,
@@ -1511,7 +1605,34 @@ e_cal_util_remove_instances (ICalComponent *icalcomp,
        g_return_if_fail (rid != NULL);
        g_return_if_fail (mod != E_CAL_OBJ_MOD_ALL);
 
-       e_cal_util_remove_instances_ex (icalcomp, rid, mod, FALSE, TRUE);
+       e_cal_util_remove_instances_ex (icalcomp, rid, mod, NULL, NULL);
+}
+
+/**
+ * e_cal_util_remove_instances_ex:
+ * @icalcomp: A (recurring) #ICalComponent
+ * @rid: The base RECURRENCE-ID to remove
+ * @mod: How to interpret @rid
+ * @tz_cb: (closure tz_cb_data) (scope call): The #ECalRecurResolveTimezoneCb to call
+ * @tz_cb_data: (closure): User data to be passed to the @tz_cb callback
+ *
+ * Removes one or more instances from @icalcomp according to @rid and @mod.
+ * Uses @tz_cb with @tz_cb_data to resolve time zones when needed.
+ *
+ * Since: 3.38
+ **/
+void
+e_cal_util_remove_instances_ex (ICalComponent *icalcomp,
+                               const ICalTime *rid,
+                               ECalObjModType mod,
+                               ECalRecurResolveTimezoneCb tz_cb,
+                               gpointer tz_cb_data)
+{
+       g_return_if_fail (icalcomp != NULL);
+       g_return_if_fail (rid != NULL);
+       g_return_if_fail (mod != E_CAL_OBJ_MOD_ALL);
+
+       e_cal_util_remove_instances_impl (icalcomp, rid, mod, FALSE, TRUE, tz_cb, tz_cb_data);
 }
 
 /**
@@ -1525,20 +1646,58 @@ e_cal_util_remove_instances (ICalComponent *icalcomp,
  * The instance identified by @rid should exist. The @master_dtstart can be
  * a null time, then it is read from the @icalcomp.
  *
- * Use e_cal_util_remove_instances() with E_CAL_OBJ_MOD_THIS_AND_FUTURE mode
+ * Use e_cal_util_remove_instances_ex() with E_CAL_OBJ_MOD_THIS_AND_FUTURE mode
  * on the @icalcomp to remove the overlapping interval from it, if needed.
  *
  * Free the returned non-NULL component with g_object_unref(), when
  * done with it.
  *
- * Returns: (transfer full) (nullable): the split @icalcom, or %NULL.
+ * Returns: (transfer full) (nullable): the split @icalcomp, or %NULL.
  *
  * Since: 3.16
+ *
+ * Deprecated: 3.38: Use e_cal_util_split_at_instance_ex() instead, with provided
+ *    timezone resolve function.
  **/
 ICalComponent *
 e_cal_util_split_at_instance (ICalComponent *icalcomp,
                              const ICalTime *rid,
                              const ICalTime *master_dtstart)
+{
+       return e_cal_util_split_at_instance_ex (icalcomp, rid, master_dtstart, NULL, NULL);
+}
+
+/**
+ * e_cal_util_split_at_instance_ex:
+ * @icalcomp: A (recurring) #ICalComponent
+ * @rid: The base RECURRENCE-ID to remove
+ * @master_dtstart: (nullable): The DTSTART of the master object
+ * @tz_cb: (closure tz_cb_data) (scope call): The #ECalRecurResolveTimezoneCb to call
+ * @tz_cb_data: (closure): User data to be passed to the @tz_cb callback
+ *
+ * Splits a recurring @icalcomp into two at time @rid. The returned #ICalComponent
+ * is modified @icalcomp which contains recurrences beginning at @rid, inclusive.
+ * The instance identified by @rid should exist. The @master_dtstart can be
+ * a null time, then it is read from the @icalcomp.
+ *
+ * Uses @tz_cb with @tz_cb_data to resolve time zones when needed.
+ *
+ * Use e_cal_util_remove_instances_ex() with E_CAL_OBJ_MOD_THIS_AND_FUTURE mode
+ * on the @icalcomp to remove the overlapping interval from it, if needed.
+ *
+ * Free the returned non-NULL component with g_object_unref(), when
+ * done with it.
+ *
+ * Returns: (transfer full) (nullable): the split @icalcomp, or %NULL.
+ *
+ * Since: 3.38
+ **/
+ICalComponent *
+e_cal_util_split_at_instance_ex (ICalComponent *icalcomp,
+                                const ICalTime *rid,
+                                const ICalTime *master_dtstart,
+                                ECalRecurResolveTimezoneCb tz_cb,
+                                gpointer tz_cb_data)
 {
        ICalProperty *prop;
        struct instance_data instance;
@@ -1568,7 +1727,7 @@ e_cal_util_split_at_instance (ICalComponent *icalcomp,
        /* Make the copy */
        icalcomp = i_cal_component_clone (icalcomp);
 
-       e_cal_util_remove_instances_ex (icalcomp, rid, E_CAL_OBJ_MOD_THIS_AND_PRIOR, TRUE, FALSE);
+       e_cal_util_remove_instances_impl (icalcomp, rid, E_CAL_OBJ_MOD_THIS_AND_PRIOR, TRUE, FALSE, tz_cb, 
tz_cb_data);
 
        start = i_cal_time_clone ((ICalTime *) rid);
 
diff --git a/src/calendar/libecal/e-cal-util.h b/src/calendar/libecal/e-cal-util.h
index 6fcb2172c9..a0d5ea2fbf 100644
--- a/src/calendar/libecal/e-cal-util.h
+++ b/src/calendar/libecal/e-cal-util.h
@@ -268,12 +268,29 @@ gchar *           e_cal_util_component_get_recurid_as_string
                                                (ICalComponent *icalcomp);
 ICalComponent *        e_cal_util_construct_instance   (ICalComponent *icalcomp,
                                                 const ICalTime *rid);
+/* #ifndef EDS_DISABLE_DEPRECATED */ /* Fully deprecate it for 3.40 */
 void           e_cal_util_remove_instances     (ICalComponent *icalcomp,
                                                 const ICalTime *rid,
                                                 ECalObjModType mod);
 ICalComponent *        e_cal_util_split_at_instance    (ICalComponent *icalcomp,
                                                 const ICalTime *rid,
                                                 const ICalTime *master_dtstart);
+/* #endif / * EDS_DISABLE_DEPRECATED */
+void           e_cal_util_normalize_rrule_until_value
+                                               (ICalComponent *icalcomp,
+                                                ICalTime *ttuntil,
+                                                ECalRecurResolveTimezoneCb tz_cb,
+                                                gpointer tz_cb_data);
+void           e_cal_util_remove_instances_ex  (ICalComponent *icalcomp,
+                                                const ICalTime *rid,
+                                                ECalObjModType mod,
+                                                ECalRecurResolveTimezoneCb tz_cb,
+                                                gpointer tz_cb_data);
+ICalComponent *        e_cal_util_split_at_instance_ex (ICalComponent *icalcomp,
+                                                const ICalTime *rid,
+                                                const ICalTime *master_dtstart,
+                                                ECalRecurResolveTimezoneCb tz_cb,
+                                                gpointer tz_cb_data);
 gboolean       e_cal_util_is_first_instance    (ECalComponent *comp,
                                                 const ICalTime *rid,
                                                 ECalRecurResolveTimezoneCb tz_cb,
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 37fa037312..61cfb32289 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -1926,12 +1926,12 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
                        }
 
                        master_dtstart = i_cal_component_get_dtstart (e_cal_component_get_icalcomponent 
(master_comp));
-                       split_icomp = e_cal_util_split_at_instance (icomp, rid, master_dtstart);
+                       split_icomp = e_cal_util_split_at_instance_ex (icomp, rid, master_dtstart, 
e_cal_cache_resolve_timezone_cb, cal_cache);
                        if (split_icomp) {
                                ICalTime *rid_utc;
 
                                rid_utc = i_cal_time_convert_to_zone (rid, i_cal_timezone_get_utc_timezone 
());
-                               e_cal_util_remove_instances (e_cal_component_get_icalcomponent (master_comp), 
rid_utc, mod);
+                               e_cal_util_remove_instances_ex (e_cal_component_get_icalcomponent 
(master_comp), rid_utc, mod, e_cal_cache_resolve_timezone_cb, cal_cache);
                                e_cal_recur_ensure_end_dates (master_comp, TRUE, 
e_cal_cache_resolve_timezone_cb, cal_cache, cancellable, NULL);
 
                                if (out_new_comp) {
@@ -2187,7 +2187,7 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
                                }
 
                                if (master_comp)
-                                       e_cal_util_remove_instances (e_cal_component_get_icalcomponent 
(master_comp), itt, mod);
+                                       e_cal_util_remove_instances_ex (e_cal_component_get_icalcomponent 
(master_comp), itt, mod, e_cal_cache_resolve_timezone_cb, cal_cache);
 
                                g_clear_object (&itt);
                        }
@@ -2220,7 +2220,7 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
                                i_cal_time_convert_to_zone_inplace (itt, i_cal_timezone_get_utc_timezone ());
                        }
 
-                       e_cal_util_remove_instances (e_cal_component_get_icalcomponent (master_comp), itt, 
mod);
+                       e_cal_util_remove_instances_ex (e_cal_component_get_icalcomponent (master_comp), itt, 
mod, e_cal_cache_resolve_timezone_cb, cal_cache);
 
                        fromtt = i_cal_time_as_timet (itt);
 


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