[evolution-data-server] Bug 760735 - Add RSCALE support (with libical 2.0.0+)



commit 65c85d94d3e5aabfff98794fc58e55426d3c5c5c
Author: Milan Crha <mcrha redhat com>
Date:   Fri Feb 5 16:03:12 2016 +0100

    Bug 760735 - Add RSCALE support (with libical 2.0.0+)

 calendar/libecal/e-cal-client.c     |   28 ++
 calendar/libecal/e-cal-client.h     |    4 +
 calendar/libecal/e-cal-recur.c      |  707 ++++++++++++++++++++++++++++++++++-
 calendar/libecal/e-cal-recur.h      |   27 ++
 configure.ac                        |    4 +
 docs/reference/eds/eds-sections.txt |    2 +
 6 files changed, 767 insertions(+), 5 deletions(-)
---
diff --git a/calendar/libecal/e-cal-client.c b/calendar/libecal/e-cal-client.c
index 32b9895..bab0090 100644
--- a/calendar/libecal/e-cal-client.c
+++ b/calendar/libecal/e-cal-client.c
@@ -2292,6 +2292,34 @@ e_cal_client_resolve_tzid_cb (const gchar *tzid,
        return zone;
 }
 
+/**
+ * e_cal_client_resolve_tzid_sync:
+ * @tzid: ID of the timezone to resolve.
+ * @cal_client: User data for the callback, in this case #ECalClient.
+ *
+ * Resolves TZIDs for the recurrence generator.
+ *
+ * Returns: The timezone identified by the @tzid argument, or %NULL if
+ * it could not be found.
+ *
+ * Since: 3.20
+ */
+icaltimezone *
+e_cal_client_resolve_tzid_sync (const gchar *tzid,
+                               gpointer cal_client,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       icaltimezone *zone = NULL;
+
+       g_return_val_if_fail (E_IS_CAL_CLIENT (cal_client), NULL);
+
+       if (!e_cal_client_get_timezone_sync (cal_client, tzid, &zone, cancellable, error))
+               return NULL;
+
+       return zone;
+}
+
 struct comp_instance {
        ECalComponent *comp;
        time_t start;
diff --git a/calendar/libecal/e-cal-client.h b/calendar/libecal/e-cal-client.h
index 172861b..f7ffce8 100644
--- a/calendar/libecal/e-cal-client.h
+++ b/calendar/libecal/e-cal-client.h
@@ -186,6 +186,10 @@ void               e_cal_client_free_ecalcomp_slist
 
 icaltimezone * e_cal_client_resolve_tzid_cb    (const gchar *tzid,
                                                 gpointer data);
+icaltimezone * e_cal_client_resolve_tzid_sync  (const gchar *tzid,
+                                                gpointer cal_client,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_cal_client_generate_instances (ECalClient *client,
                                                 time_t start,
                                                 time_t end,
diff --git a/calendar/libecal/e-cal-recur.c b/calendar/libecal/e-cal-recur.c
index ffa9c03..e2c895b 100644
--- a/calendar/libecal/e-cal-recur.c
+++ b/calendar/libecal/e-cal-recur.c
@@ -26,8 +26,634 @@
 #include <stdlib.h>
 #include <string.h>
 #include <glib/gi18n-lib.h>
+
 #include "e-cal-recur.h"
 #include "e-cal-time-util.h"
+#include "e-cal-client.h"
+
+static gint
+e_timetype_compare (gconstpointer a,
+                   gconstpointer b)
+{
+       const struct icaltimetype *tta = a;
+       const struct icaltimetype *ttb = b;
+
+       if (!tta || !ttb) {
+               if (tta == ttb)
+                       return 0;
+               if (!tta)
+                       return -1;
+               return 1;
+       }
+
+       if (tta->is_date || ttb->is_date)
+               return icaltime_compare_date_only (*tta, *ttb);
+
+       return icaltime_compare (*tta, *ttb);
+}
+
+static gint
+e_timetype_compare_without_date (gconstpointer a,
+                                gconstpointer b)
+{
+       const struct icaltimetype *tta = a;
+       const struct icaltimetype *ttb = b;
+
+       if (!tta || !ttb) {
+               if (tta == ttb)
+                       return 0;
+               if (!tta)
+                       return -1;
+               return 1;
+       }
+
+       return icaltime_compare (*tta, *ttb);
+}
+
+static guint
+e_timetype_hash (gconstpointer v)
+{
+       const struct icaltimetype *ttv = v;
+       struct icaltimetype tt;
+
+       if (!ttv)
+               return 0;
+
+       tt = icaltime_convert_to_zone (*ttv, icaltimezone_get_utc_timezone ());
+
+       return g_int_hash (&(tt.year)) ^
+               g_int_hash (&(tt.month)) ^
+               g_int_hash (&(tt.day));
+}
+
+typedef struct _EInstanceTime
+{
+       struct icaltimetype tt;
+       gboolean duration_set;
+       gint duration_days;
+       gint duration_seconds;
+} EInstanceTime;
+
+static EInstanceTime *
+e_instance_time_new (const struct icaltimetype *tt,
+                    const struct icaldurationtype *duration)
+{
+       EInstanceTime *it;
+
+       if (!tt)
+               return NULL;
+
+       it = g_new0 (EInstanceTime, 1);
+
+       it->tt = *tt;
+       it->duration_set = duration && !icaldurationtype_is_null_duration (*duration);
+
+       if (it->duration_set) {
+               gint64 dur;
+
+               g_warn_if_fail (!duration->is_neg);
+
+               dur = (gint64) (duration->weeks * 7 + duration->days) * (24 * 60 * 60) +
+                       (duration->hours * 60 * 60) +
+                       (duration->minutes * 60) +
+                       duration->seconds;
+
+               it->duration_days = dur / (24 * 60 * 60);
+               it->duration_seconds = dur % (24 * 60 * 60);
+       }
+
+       return it;
+}
+
+static gint
+e_instance_time_compare (gconstpointer a,
+                        gconstpointer b)
+{
+       const EInstanceTime *ait = a;
+       const EInstanceTime *bit = b;
+
+       if (!ait || !bit) {
+               if (ait == bit)
+                       return 0;
+
+               if (!ait)
+                       return -1;
+
+               return 1;
+       }
+
+       return e_timetype_compare (&(ait->tt), &(bit->tt));
+}
+
+static guint
+e_instance_time_hash (gconstpointer v)
+{
+       const EInstanceTime *it = v;
+
+       if (!it)
+               return 0;
+
+       return e_timetype_hash (&it->tt);
+}
+
+static gboolean
+e_instance_time_equal (gconstpointer v1,
+                      gconstpointer v2)
+{
+       return e_instance_time_compare (v1, v2) == 0;
+}
+
+static gint
+e_instance_time_compare_ptr_array (gconstpointer a,
+                                  gconstpointer b)
+{
+       gconstpointer *aa = (gconstpointer *) a, *bb = (gconstpointer *) b;
+
+       return e_instance_time_compare (*aa, *bb);
+}
+
+static gboolean
+ensure_timezone (icalcomponent *comp,
+                struct icaltimetype *tt,
+                icalproperty_kind prop_kind,
+                icalproperty *prop,
+                ECalRecurResolveTimezoneCb get_tz_callback,
+                gpointer get_tz_callback_user_data,
+                icaltimezone *default_timezone,
+                GCancellable *cancellable,
+                GError **error)
+{
+       icalparameter *param;
+       const gchar *tzid;
+       GError *local_error = NULL;
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
+
+       if (!tt || tt->zone || tt->is_utc)
+               return TRUE;
+
+       if (!prop)
+               prop = icalcomponent_get_first_property (comp, prop_kind);
+       if (!prop)
+               return TRUE;
+
+       param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+       if (!param)
+               return TRUE;
+
+       tzid = icalparameter_get_tzid (param);
+       if (!tzid || !*tzid)
+               return TRUE;
+
+       if (get_tz_callback)
+               tt->zone = get_tz_callback (tzid, get_tz_callback_user_data, cancellable, &local_error);
+       else
+               tt->zone = NULL;
+
+       if (!tt->zone)
+               tt->zone = default_timezone;
+
+       /* Timezone not found is not a fatal error */
+       if (g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND))
+               g_clear_error (&local_error);
+
+       if (local_error) {
+               g_propagate_error (error, local_error);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+apply_duration (struct icaltimetype *tt,
+               const struct icaldurationtype *duration)
+{
+       gint64 days, seconds;
+
+       g_return_if_fail (tt != NULL);
+       g_return_if_fail (duration != NULL);
+
+       if (icaldurationtype_is_null_duration (*duration))
+               return;
+
+       days = duration->days + 7 * duration->weeks;
+       seconds = (duration->hours * 60 * 60) + (duration->minutes * 60) + duration->seconds;
+       days += seconds / (24 * 60 * 60);
+       seconds = seconds % (24 * 60 * 60);
+
+       if (seconds != 0) {
+               tt->is_date = 0;
+       }
+
+       icaltime_adjust (tt,
+               (duration->is_neg ? -1 : 1) * ((gint) days),
+               0, 0,
+               (duration->is_neg ? -1 : 1) * ((gint) seconds));
+}
+
+static gboolean
+intersects_interval (const struct icaltimetype *tt,
+                    const struct icaldurationtype *duration,
+                    gint default_duration_days,
+                    gint default_duration_seconds,
+                    const struct icaltimetype *interval_start,
+                    const struct icaltimetype *interval_end)
+{
+       struct icaltimetype ttstart, ttend;
+
+       if (!tt || !interval_start || !interval_end)
+               return FALSE;
+
+       ttstart = *tt;
+       ttend = ttstart;
+
+       if (duration && !icaldurationtype_is_null_duration (*duration)) {
+               apply_duration (&ttend, duration);
+       } else if (default_duration_days || default_duration_seconds) {
+               if (default_duration_seconds != 0) {
+                       ttend.is_date = 0;
+               }
+
+               icaltime_adjust (&ttend, default_duration_days, 0, 0, default_duration_seconds);
+       }
+
+       return e_timetype_compare_without_date (&ttstart, interval_end) < 0 &&
+              e_timetype_compare_without_date (interval_start, &ttend) < 0;
+}
+
+/**
+ * e_cal_recur_generate_instances_sync:
+ * @comp: an icalcomponent
+ * @interval_start: an interval start, for which generate instances
+ * @interval_end: an interval end, for which generate instances
+ * @callback: a callback to be called for each instance
+ * @callback_user_data: user data for @callback
+ * @get_tz_callback: a callback to call when resolving timezone
+ * @get_tz_callback_user_data: user data for @get_tz_callback
+ * @default_timezone: a default icaltimezone
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Calls the given callback function for each occurrence of the event that
+ * intersects the range between the given @start and @end times (the end time is
+ * not included). Note that the occurrences may start before the given start
+ * time.
+ *
+ * If the callback routine returns FALSE the occurrence generation stops.
+ *
+ * The start and end times are required valid times, start before end time.
+ *
+ * The @get_tz_callback is used to resolve references to timezones. It is passed
+ * a TZID and should return the icaltimezone * corresponding to that TZID. We need to
+ * do this as we access timezones in different ways on the client & server.
+ *
+ * The default_timezone argument is used for DTSTART or DTEND properties that
+ * are DATE values or do not have a TZID (i.e. floating times).
+ *
+ * Returns: %TRUE if successful (when all instances had been returned), %FALSE otherwise.
+ *
+ * Since: 3.20
+ **/
+gboolean
+e_cal_recur_generate_instances_sync (icalcomponent *comp,
+                                    struct icaltimetype interval_start,
+                                    struct icaltimetype interval_end,
+                                    ECalRecurInstanceCb callback,
+                                    gpointer callback_user_data,
+                                    ECalRecurResolveTimezoneCb get_tz_callback,
+                                    gpointer get_tz_callback_user_data,
+                                    icaltimezone *default_timezone,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       struct icaltimetype dtstart, dtend, next;
+       gint64 duration_days, duration_seconds;
+       icalproperty *prop;
+       GHashTable *times;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (comp != NULL, FALSE);
+       g_return_val_if_fail (callback != NULL, FALSE);
+       g_return_val_if_fail (icaltime_compare (interval_start, interval_end) < 0, FALSE);
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
+
+       times = g_hash_table_new_full (e_instance_time_hash, e_instance_time_equal, g_free, NULL);
+
+       dtstart = icalcomponent_get_dtstart (comp);
+       if (!dtstart.is_date) {
+               success = ensure_timezone (comp, &dtstart, ICAL_DTSTART_PROPERTY, NULL,
+                       get_tz_callback, get_tz_callback_user_data, default_timezone, cancellable, error);
+       }
+
+       duration_seconds = 0;
+       dtend = icalcomponent_get_dtend (comp);
+
+       if (icaltime_is_null_time (dtend)) {
+               dtend = icalcomponent_get_due (comp);
+               if (icaltime_is_null_time (dtend)) {
+                       struct icaldurationtype comp_duration;
+
+                       comp_duration = icalcomponent_get_duration (comp);
+
+                       if (!icaldurationtype_is_null_duration (comp_duration)) {
+                               dtend = dtstart;
+
+                               apply_duration (&dtend, &comp_duration);
+                       }
+               }
+
+               /* If there is no DTEND, then if DTSTART is a DATE-TIME value
+                * we use the same time (so we have a single point in time).
+                * If DTSTART is a DATE value we add 1 day. */
+               if (icaltime_is_null_time (dtend)) {
+                       dtend = dtstart;
+
+                       if (dtend.is_date)
+                               icaltime_adjust (&dtend, 1, 0, 0, 0);
+               }
+       } else {
+               /* If both DTSTART and DTEND are DATE values, and they are the
+                * same day, we add 1 day to DTEND. This means that most
+                * events created with the old Evolution behavior will still
+                * work OK. I'm not sure what Outlook does in this case. */
+               if (dtstart.is_date && dtend.is_date) {
+                       if (icaltime_compare_date_only (dtstart, dtend) == 0) {
+                               icaltime_adjust (&dtend, 1, 0, 0, 0);
+                       }
+               }
+       }
+
+       if (success && !icaltime_is_null_time (dtend)) {
+               if (!dtend.is_date)
+                       success = ensure_timezone (comp, &dtend, ICAL_DTEND_PROPERTY, NULL,
+                               get_tz_callback, get_tz_callback_user_data, default_timezone, cancellable, 
error);
+               duration_seconds = (gint64) icaltime_as_timet_with_zone (dtend, dtend.zone) -
+                       (gint64) icaltime_as_timet_with_zone (dtstart, dtstart.zone);
+               if (duration_seconds < 0)
+                       duration_seconds = 0;
+       }
+
+       duration_days = duration_seconds / (24 * 60 * 60);
+       duration_seconds = duration_seconds % (24 * 60 * 60);
+
+       if (success && intersects_interval (&dtstart, NULL, duration_days, duration_seconds, &interval_start, 
&interval_end)) {
+               g_hash_table_insert (times, e_instance_time_new (&dtstart, NULL), NULL);
+       }
+
+       /* If this is a detached instance, then use only the DTSTART value */
+       if (success && icaltime_is_null_time (icalcomponent_get_recurrenceid (comp))) {
+               for (prop = icalcomponent_get_first_property (comp, ICAL_RRULE_PROPERTY);
+                    prop && success;
+                    prop = icalcomponent_get_next_property (comp, ICAL_RRULE_PROPERTY)) {
+                       struct icalrecurrencetype rrule = icalproperty_get_rrule (prop);
+                       icalrecur_iterator *riter;
+
+                       if (!icaltime_is_null_time (rrule.until) && !rrule.until.is_date) {
+                               success = ensure_timezone (comp, &rrule.until, 0, prop,
+                                       get_tz_callback, get_tz_callback_user_data, (icaltimezone *) 
dtstart.zone,
+                                       cancellable, error);
+                               if (!success)
+                                       break;
+                       }
+
+                       if (!icaltime_is_null_time (rrule.until) &&
+                           rrule.until.is_date && !dtstart.is_date) {
+                               icaltime_adjust (&rrule.until, 1, 0, 0, 0);
+                               rrule.until.is_date = 0;
+                               rrule.until.hour = 0;
+                               rrule.until.minute = 0;
+                               rrule.until.second = 0;
+
+                               if (!rrule.until.zone)
+                                       rrule.until.zone = dtstart.zone;
+                       }
+
+                       riter = icalrecur_iterator_new (rrule, dtstart);
+                       if (riter) {
+                               struct icaltimetype prev = icaltime_null_time ();
+
+                               for (next = icalrecur_iterator_next (riter);
+                                    !icaltime_is_null_time (next) && icaltime_compare (next, interval_end) 
<= 0;
+                                    next = icalrecur_iterator_next (riter)) {
+                                       if (!icaltime_is_null_time (prev) &&
+                                           icaltime_compare (next, prev) == 0)
+                                               break;
+                                       prev = next;
+
+                                       if (intersects_interval (&next, NULL, duration_days, 
duration_seconds, &interval_start, &interval_end)) {
+                                               g_hash_table_insert (times, e_instance_time_new (&next, 
NULL), NULL);
+                                       }
+                               }
+
+                               icalrecur_iterator_free (riter);
+                       }
+               }
+
+               for (prop = icalcomponent_get_first_property (comp, ICAL_RDATE_PROPERTY);
+                    prop && success;
+                    prop = icalcomponent_get_next_property (comp, ICAL_RDATE_PROPERTY)) {
+                       struct icaldatetimeperiodtype rdate = icalproperty_get_rdate (prop);
+                       struct icaltimetype tt = icaltime_null_time ();
+                       struct icaldurationtype duration = icaldurationtype_null_duration ();
+
+                       if (!icaltime_is_null_time (rdate.time)) {
+                               tt = rdate.time;
+                       } else if (!icalperiodtype_is_null_period (rdate.period)) {
+                               tt = rdate.period.start;
+
+                               if (!icaltime_is_null_time (rdate.period.end)) {
+                                       time_t diff;
+
+                                       diff = icaltime_as_timet (rdate.period.end) - icaltime_as_timet 
(rdate.period.start);
+                                       if (diff < 0) {
+                                               duration.is_neg = 1;
+                                               diff = diff * (-1);
+                                       }
+
+                                       #define set_and_dec(member, num) G_STMT_START { \
+                                               member = diff / (num); \
+                                               diff = diff % (num); \
+                                               } G_STMT_END
+                                       set_and_dec (duration.weeks, 7 * 24 * 60 * 60);
+                                       set_and_dec (duration.days, 24 * 60 * 60);
+                                       set_and_dec (duration.hours, 60 * 60);
+                                       set_and_dec (duration.minutes, 60);
+                                       set_and_dec (duration.seconds, 1);
+                                       #undef set_and_dec
+
+                                       g_warn_if_fail (diff == 0);
+                               } else if (!icaldurationtype_is_null_duration (rdate.period.duration)) {
+                                       duration = rdate.period.duration;
+                               }
+
+                               if (!icaldurationtype_is_null_duration (duration) &&
+                                   !icaltime_is_null_time (tt)) {
+                                       if (duration.is_neg) {
+                                               apply_duration (&tt, &duration);
+
+                                               duration.is_neg = 0;
+                                       }
+                               }
+                       }
+
+                       if (!icaltime_is_null_time (tt)) {
+                               if (!tt.is_date) {
+                                       success = ensure_timezone (comp, &tt, 0, prop,
+                                               get_tz_callback, get_tz_callback_user_data, (icaltimezone *) 
dtstart.zone,
+                                               cancellable, error);
+                                       if (!success)
+                                               break;
+                               }
+
+                               if (intersects_interval (&tt, &duration, duration_days, duration_seconds, 
&interval_start, &interval_end)) {
+                                       g_hash_table_insert (times, e_instance_time_new (&tt, &duration), 
NULL);
+                               }
+                       }
+               }
+
+               for (prop = icalcomponent_get_first_property (comp, ICAL_EXRULE_PROPERTY);
+                    prop && success;
+                    prop = icalcomponent_get_next_property (comp, ICAL_EXRULE_PROPERTY)) {
+                       struct icalrecurrencetype exrule = icalproperty_get_exrule (prop);
+                       icalrecur_iterator *riter;
+
+                       if (!icaltime_is_null_time (exrule.until) && !exrule.until.is_date) {
+                               success = ensure_timezone (comp, &exrule.until, 0, prop,
+                                       get_tz_callback, get_tz_callback_user_data, (icaltimezone *) 
dtstart.zone,
+                                       cancellable, error);
+                               if (!success)
+                                       break;
+                       }
+
+                       if (!icaltime_is_null_time (exrule.until) &&
+                           exrule.until.is_date && !dtstart.is_date) {
+                               icaltime_adjust (&exrule.until, 1, 0, 0, 0);
+                               exrule.until.is_date = 0;
+                               exrule.until.hour = 0;
+                               exrule.until.minute = 0;
+                               exrule.until.second = 0;
+
+                               if (!exrule.until.zone)
+                                       exrule.until.zone = dtstart.zone;
+                       }
+
+                       riter = icalrecur_iterator_new (exrule, dtstart);
+                       if (riter) {
+                               struct icaltimetype prev = icaltime_null_time ();
+
+                               for (next = icalrecur_iterator_next (riter);
+                                    !icaltime_is_null_time (next) && icaltime_compare (next, interval_end) 
<= 0;
+                                    next = icalrecur_iterator_next (riter)) {
+                                       if (!icaltime_is_null_time (prev) &&
+                                           icaltime_compare (next, prev) == 0)
+                                               break;
+                                       prev = next;
+
+                                       if (intersects_interval (&next, NULL, duration_days, 
duration_seconds, &interval_start, &interval_end)) {
+                                               EInstanceTime it;
+
+                                               it.tt = next;
+                                               it.duration_set = FALSE;
+
+                                               g_hash_table_remove (times, &it);
+                                       }
+                               }
+
+                               icalrecur_iterator_free (riter);
+                       }
+               }
+
+               for (prop = icalcomponent_get_first_property (comp, ICAL_EXDATE_PROPERTY);
+                    prop && success;
+                    prop = icalcomponent_get_next_property (comp, ICAL_EXDATE_PROPERTY)) {
+                       struct icaltimetype exdate = icalproperty_get_exdate (prop);
+
+                       if (icaltime_is_null_time (exdate))
+                               continue;
+
+                       if (!exdate.is_date) {
+                               success = ensure_timezone (comp, &exdate, 0, prop,
+                                       get_tz_callback, get_tz_callback_user_data, (icaltimezone *) 
dtstart.zone,
+                                       cancellable, error);
+                               if (!success)
+                                       break;
+                       }
+
+                       if (!exdate.zone)
+                               exdate.zone = dtstart.zone;
+
+                       if (intersects_interval (&exdate, NULL, duration_days, duration_seconds, 
&interval_start, &interval_end)) {
+                               EInstanceTime it;
+
+                               it.tt = exdate;
+                               it.duration_set = FALSE;
+
+                               g_hash_table_remove (times, &it);
+                       }
+               }
+       }
+
+       if (success && g_hash_table_size (times) > 0) {
+               GPtrArray *times_array;
+               GHashTableIter hiter;
+               gpointer key, value;
+               gint ii;
+
+               times_array = g_ptr_array_sized_new (g_hash_table_size (times));
+
+               g_hash_table_iter_init (&hiter, times);
+               while (g_hash_table_iter_next (&hiter, &key, &value)) {
+                       EInstanceTime *it = key;
+
+                       if (!it)
+                               continue;
+
+                       g_ptr_array_add (times_array, it);
+               }
+
+               g_ptr_array_sort (times_array, e_instance_time_compare_ptr_array);
+
+               for (ii = 0; ii < times_array->len; ii++) {
+                       EInstanceTime *it = g_ptr_array_index (times_array, ii);
+                       struct icaltimetype ttend;
+                       gint dur_days = duration_days, dur_seconds = duration_seconds;
+
+                       if (!it)
+                               continue;
+
+                       ttend = it->tt;
+
+                       if (it->duration_set) {
+                               dur_days = it->duration_days;
+                               dur_seconds = it->duration_seconds;
+                       }
+
+                       if (dur_seconds != 0)
+                               ttend.is_date = 0;
+
+                       icaltime_adjust (&ttend, dur_days, 0, 0, dur_seconds);
+
+                       if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+                               success = FALSE;
+                               break;
+                       }
+
+                       success = callback (comp, it->tt, ttend, callback_user_data, cancellable, error);
+                       if (!success)
+                               break;
+               }
+
+               g_ptr_array_free (times_array, TRUE);
+       }
+
+       g_hash_table_destroy (times);
+
+       return success;
+}
 
 /*
  * Introduction to The Recurrence Generation Functions:
@@ -593,6 +1219,61 @@ static ECalRecurVTable cal_obj_secondly_vtable = {
        cal_obj_bysecond_filter
 };
 
+#ifdef HAVE_LIBICAL_2_0
+struct BackwardCompatibilityData
+{
+       ECalComponent *comp;
+       ECalRecurInstanceFn cb;
+       gpointer cb_data;
+       ECalRecurResolveTimezoneFn tz_cb;
+       gpointer tz_cb_data;
+};
+
+static icaltimezone *
+backward_compatibility_resolve_timezone_cb (const gchar *tzid,
+                                           gpointer user_data,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       struct BackwardCompatibilityData *bcd = user_data;
+
+       if (bcd && bcd->tz_cb)
+               return bcd->tz_cb (tzid, bcd->tz_cb_data);
+
+       return NULL;
+}
+
+static gboolean
+backward_compatibility_instance_cb (icalcomponent *comp,
+                                   struct icaltimetype instance_start,
+                                   struct icaltimetype instance_end,
+                                   gpointer user_data,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       struct BackwardCompatibilityData *bcd = user_data;
+
+       if (bcd && bcd->cb) {
+               time_t istart, iend;
+
+               if (instance_start.zone && !instance_start.is_date)
+                       istart = icaltime_as_timet_with_zone (instance_start, instance_start.zone);
+               else
+                       istart = icaltime_as_timet (instance_start);
+
+               if (instance_end.zone && !instance_end.is_date)
+                       iend = icaltime_as_timet_with_zone (instance_end, instance_end.zone);
+               else
+                       iend = icaltime_as_timet (instance_end);
+
+               return bcd->cb (bcd->comp, istart, iend, bcd->cb_data);
+       }
+
+       return FALSE;
+}
+
+#endif
+
 /**
  * e_cal_recur_generate_instances:
  * @comp: A calendar component object
@@ -621,6 +1302,8 @@ static ECalRecurVTable cal_obj_secondly_vtable = {
  *
  * The default_timezone argument is used for DTSTART or DTEND properties that
  * are DATE values or do not have a TZID (i.e. floating times).
+ *
+ * Note: This will be replaced by e_cal_recur_generate_instances_sync().
  */
 void
 e_cal_recur_generate_instances (ECalComponent *comp,
@@ -632,15 +1315,29 @@ e_cal_recur_generate_instances (ECalComponent *comp,
                                 gpointer tz_cb_data,
                                 icaltimezone *default_timezone)
 {
-#if 0
-       g_print ("In e_cal_recur_generate_instances comp: %p\n", comp);
-       g_print ("  start: %li - %s", start, ctime (&start));
-       g_print ("  end  : %li - %s", end, ctime (&end));
-#endif
+#ifdef HAVE_LIBICAL_2_0
+       struct icaltimetype istart, iend;
+       struct BackwardCompatibilityData bcd;
+
+       bcd.comp = comp;
+       bcd.cb = cb;
+       bcd.cb_data = cb_data;
+       bcd.tz_cb = tz_cb;
+       bcd.tz_cb_data = tz_cb_data;
+
+       istart = icaltime_from_timet_with_zone (start, FALSE, icaltimezone_get_utc_timezone ());
+       iend = icaltime_from_timet_with_zone (end, FALSE, icaltimezone_get_utc_timezone ());
+
+       e_cal_recur_generate_instances_sync (e_cal_component_get_icalcomponent (comp), istart, iend,
+               backward_compatibility_instance_cb, &bcd,
+               backward_compatibility_resolve_timezone_cb, &bcd,
+               default_timezone, NULL, NULL);
+#else
        e_cal_recur_generate_instances_of_rule (
                comp, NULL, start, end,
                cb, cb_data, tz_cb, tz_cb_data,
                default_timezone);
+#endif
 }
 
 /*
diff --git a/calendar/libecal/e-cal-recur.h b/calendar/libecal/e-cal-recur.h
index 9cf7119..1264e1d 100644
--- a/calendar/libecal/e-cal-recur.h
+++ b/calendar/libecal/e-cal-recur.h
@@ -26,10 +26,37 @@
 #ifndef E_CAL_RECUR_H
 #define E_CAL_RECUR_H
 
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libical/ical.h>
 #include <libecal/e-cal-component.h>
 
 G_BEGIN_DECLS
 
+typedef icaltimezone * (* ECalRecurResolveTimezoneCb)  (const gchar *tzid,
+                                                        gpointer user_data,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+
+typedef gboolean (* ECalRecurInstanceCb)               (icalcomponent *comp,
+                                                        struct icaltimetype instance_start,
+                                                        struct icaltimetype instance_end,
+                                                        gpointer user_data,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+
+gboolean       e_cal_recur_generate_instances_sync     (icalcomponent *comp,
+                                                        struct icaltimetype interval_start,
+                                                        struct icaltimetype interval_end,
+                                                        ECalRecurInstanceCb callback,
+                                                        gpointer callback_user_data,
+                                                        ECalRecurResolveTimezoneCb get_tz_callback,
+                                                        gpointer get_tz_callback_user_data,
+                                                        icaltimezone *default_timezone,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+
 typedef gboolean (* ECalRecurInstanceFn) (ECalComponent *comp,
                                         time_t        instance_start,
                                         time_t        instance_end,
diff --git a/configure.ac b/configure.ac
index 8a39f05..9766405 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1533,6 +1533,10 @@ dnl evolution-calendar flags
 dnl ******************************
 EVOLUTION_CALENDAR_DEPS="gio-2.0 libical >= libical_minimum_version libsoup-2.4 libxml-2.0 libsecret-1"
 
+if `$PKG_CONFIG --atleast-version=2.0 libical`; then
+       AC_DEFINE(HAVE_LIBICAL_2_0, 1, [Define if compiled with libical 2.0])
+fi
+
 dnl *****
 dnl libical.pc from libical-0.43 has a bug in it's CFlags.
 dnl It wants apps to include <libical/ical*.h> but it's CFlags make it difficult
diff --git a/docs/reference/eds/eds-sections.txt b/docs/reference/eds/eds-sections.txt
index 1733133..8d9425e 100644
--- a/docs/reference/eds/eds-sections.txt
+++ b/docs/reference/eds/eds-sections.txt
@@ -1056,6 +1056,7 @@ e_cal_client_check_recurrences_no_master
 e_cal_client_free_icalcomp_slist
 e_cal_client_free_ecalcomp_slist
 e_cal_client_resolve_tzid_cb
+e_cal_client_resolve_tzid_sync
 e_cal_client_generate_instances
 e_cal_client_generate_instances_sync
 e_cal_client_generate_instances_for_object
@@ -1336,6 +1337,7 @@ e_cal_component_get_type
 <TITLE>Calendar recurrences</TITLE>
 ECalRecurInstanceFn
 ECalRecurResolveTimezoneFn
+e_cal_recur_generate_instances_sync
 e_cal_recur_generate_instances
 e_cal_recur_obtain_enddate
 e_cal_recur_ensure_end_dates


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