* When determining calculating tzid for invitations, take into consideration certain...
[citadel.git] / citadel / modules / calendar / serv_calendar.c
index d5df3a666195ea92aa759bd84cf7de55a3e53615..6f36c3434ba967fa7196245089940970a3b4feb6 100644 (file)
@@ -808,51 +808,64 @@ int ical_ctdl_is_overlap(
                        struct icaltimetype t2start,
                        struct icaltimetype t2end
 ) {
-
        if (icaltime_is_null_time(t1start)) return(0);
        if (icaltime_is_null_time(t2start)) return(0);
 
-       /* First, check for all-day events */
-       if (t1start.is_date) {
-               if (!icaltime_compare_date_only(t1start, t2start)) {
-                       return(1);
-               }
-               if (!icaltime_is_null_time(t2end)) {
-                       if (!icaltime_compare_date_only(t1start, t2end)) {
-                               return(1);
-                       }
+       /* if either event lacks end time, assume end = start */
+       if (icaltime_is_null_time(t1end))
+               memcpy(&t1end, &t1start, sizeof(struct icaltimetype));
+       else {
+               if (t1end.is_date && icaltime_compare(t1start, t1end)) {
+                        /*
+                         * the end date is non-inclusive so adjust it by one
+                         * day because our test is inclusive, note that a day is
+                         * not too much because we are talking about all day
+                         * events
+                        * if start = end we assume that nevertheless the whole
+                        * day is meant
+                         */
+                       icaltime_adjust(&t1end, -1, 0, 0, 0);   
                }
        }
 
-       if (t2start.is_date) {
-               if (!icaltime_compare_date_only(t2start, t1start)) {
-                       return(1);
-               }
-               if (!icaltime_is_null_time(t1end)) {
-                       if (!icaltime_compare_date_only(t2start, t1end)) {
-                               return(1);
-                       }
+       if (icaltime_is_null_time(t2end))
+               memcpy(&t2end, &t2start, sizeof(struct icaltimetype));
+       else {
+               if (t2end.is_date && icaltime_compare(t2start, t2end)) {
+                       icaltime_adjust(&t2end, -1, 0, 0, 0);   
                }
        }
 
-       /* Now check for overlaps using date *and* time. */
+       /* First, check for all-day events */
+       if (t1start.is_date || t2start.is_date) {
+               /* If event 1 ends before event 2 starts, we're in the clear. */
+               if (icaltime_compare_date_only(t1end, t2start) < 0) return(0);
+
+               /* If event 2 ends before event 1 starts, we're also ok. */
+               if (icaltime_compare_date_only(t2end, t1start) < 0) return(0);
+
+               return(1);
+       }
 
-       /* First, bail out if either event 1 or event 2 is missing end time. */
-       if (icaltime_is_null_time(t1end)) return(0);
-       if (icaltime_is_null_time(t2end)) return(0);
+       /* lprintf (9, "Comparing t1start %d:%d t1end %d:%d t2start %d:%d t2end %d:%d \n",
+               t1start.hour, t1start.minute, t1end.hour, t1end.minute,
+               t2start.hour, t2start.minute, t2end.hour, t2end.minute);
+       */
+
+       /* Now check for overlaps using date *and* time. */
 
        /* If event 1 ends before event 2 starts, we're in the clear. */
        if (icaltime_compare(t1end, t2start) <= 0) return(0);
+       /* lprintf(9, "first passed\n"); */
 
        /* If event 2 ends before event 1 starts, we're also ok. */
        if (icaltime_compare(t2end, t1start) <= 0) return(0);
+       /* lprintf(9, "second passed\n"); */
 
        /* Otherwise, they overlap. */
        return(1);
 }
 
-
-
 /* 
  * Phase 6 of "hunt for conflicts"
  * called by ical_conflicts_phase5()
@@ -1224,33 +1237,28 @@ void ical_conflicts(long msgnum, char *partnum) {
  * Look for busy time in a VEVENT and add it to the supplied VFREEBUSY.
  *
  * fb                  The VFREEBUSY component to which we are appending
- * top_level_cal       The top-level VCALENDAR component which contains VEVENT to be added
- * cal                 Caller supplies NULL, but we then use this variable for recursion
+ * top_level_cal       The top-level VCALENDAR component which contains a VEVENT to be added
  */
-void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *top_level_cal, icalcomponent *cal) {
+void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *top_level_cal) {
+       icalcomponent *cal;
        icalproperty *p;
        icalvalue *v;
-       struct icalperiodtype this_event_period;
-
-       if (!top_level_cal) return;
-       if (!cal) cal = top_level_cal;
+       struct icalperiodtype this_event_period = icalperiodtype_null_period();
+       icaltimetype dtstart = icaltime_null_time();
+       icaltimetype dtend = icaltime_null_time();
 
-       this_event_period = icalperiodtype_null_period();
+       /* recur variables */
+       icalproperty *rrule = NULL;
+       struct icalrecurrencetype recur;
+       icalrecur_iterator *ritr = NULL;
+       struct icaldurationtype dur;
+       int num_recur = 0;
 
-       /* Convert all time zones to UTC (FIXME this won't work with recurring events) */
-       if (icalcomponent_isa(cal) != ICAL_VCALENDAR_COMPONENT) {
-               ical_dezonify(cal);
-       }
+       if (!top_level_cal) return;
 
-       /* Now boil it down to the VEVENT only (FIXME this won't work with recurring events) */
-       if (icalcomponent_isa(cal) != ICAL_VEVENT_COMPONENT) {
-               ical_add_to_freebusy(fb, top_level_cal,
-                       icalcomponent_get_first_component(
-                               cal, ICAL_VEVENT_COMPONENT
-                       )
-               );
-               return;
-       }
+       /* Find the VEVENT component containing an event */
+       cal = icalcomponent_get_first_component(top_level_cal, ICAL_VEVENT_COMPONENT);
+       if (!cal) return;
 
        /* If this event is not opaque, the user isn't publishing it as
         * busy time, so don't bother doing anything else.
@@ -1265,58 +1273,100 @@ void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *top_level_cal, icalc
                }
        }
 
-       /* Convert the DTSTART and DTEND properties to an icalperiod. */
+       /*
+        * Now begin calculating the event start and end times.
+        */
        p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY);
-       if (p != NULL) {
-               this_event_period.start = icalproperty_get_dtstart(p);
-       }
-
-       p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
-       if (p != NULL) {
-               this_event_period.end = icalproperty_get_dtstart(p);
-       }
-
-       /* Now add it. */
-       icalcomponent_add_property(fb,
-               icalproperty_new_freebusy(this_event_period)
-       );
+       if (!p) return;
+       dtstart = icalproperty_get_dtstart(p);
 
-       /* Make sure the DTSTART property of the freebusy *list* is set to
-        * the DTSTART property of the *earliest event*.
-        */
-       p = icalcomponent_get_first_property(fb, ICAL_DTSTART_PROPERTY);
-       if (p == NULL) {
-               icalcomponent_set_dtstart(fb,
-                       icalcomponent_get_dtstart(cal) );
+       if (icaltime_is_utc(dtstart)) {
+               dtstart.zone = icaltimezone_get_utc_timezone();
        }
        else {
-               if (icaltime_compare(
-                       icalcomponent_get_dtstart(cal),
-                       icalcomponent_get_dtstart(fb)
-                  ) < 0) {
-                       icalcomponent_set_dtstart(fb,
-                               icalcomponent_get_dtstart(cal) );
+               dtstart.zone = icalcomponent_get_timezone(top_level_cal,
+                       icalparameter_get_tzid(
+                               icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
+                       )
+               );
+               if (!dtstart.zone) {
+                       dtstart.zone = get_default_icaltimezone();
                }
        }
 
-       /* Make sure the DTEND property of the freebusy *list* is set to
-        * the DTEND property of the *latest event*.
-        */
-       p = icalcomponent_get_first_property(fb, ICAL_DTEND_PROPERTY);
-       if (p == NULL) {
-               icalcomponent_set_dtend(fb,
-                       icalcomponent_get_dtend(cal) );
+       dtend = icalcomponent_get_dtend(cal);
+       if (!icaltime_is_null_time(dtend)) {
+               dur = icaltime_subtract(dtend, dtstart);
        }
-       else {
-               if (icaltime_compare(
-                       icalcomponent_get_dtend(cal),
-                       icalcomponent_get_dtend(fb)
-                  ) > 0) {
-                       icalcomponent_set_dtend(fb,
-                               icalcomponent_get_dtend(cal) );
-               }
+
+       /* Is a recurrence specified?  If so, get ready to process it... */
+       rrule = ical_ctdl_get_subprop(cal, ICAL_RRULE_PROPERTY);
+       if (rrule) {
+               recur = icalproperty_get_rrule(rrule);
+               ritr = icalrecur_iterator_new(recur, dtstart);
        }
 
+       do {
+               /* Convert the DTSTART and DTEND properties to an icalperiod. */
+               this_event_period.start = dtstart;
+       
+               if (!icaltime_is_null_time(dtend)) {
+                       this_event_period.end = dtend;
+               }
+
+               /* Convert the timestamps to UTC.  It's ok to do this because we've already expanded
+                * recurrences and this data is never going to get used again.
+                */
+               this_event_period.start = icaltime_convert_to_zone(
+                       this_event_period.start,
+                       icaltimezone_get_utc_timezone()
+               );
+               this_event_period.end = icaltime_convert_to_zone(
+                       this_event_period.end,
+                       icaltimezone_get_utc_timezone()
+               );
+       
+               /* Now add it. */
+               icalcomponent_add_property(fb, icalproperty_new_freebusy(this_event_period));
+
+               /* Make sure the DTSTART property of the freebusy *list* is set to
+                * the DTSTART property of the *earliest event*.
+                */
+               p = icalcomponent_get_first_property(fb, ICAL_DTSTART_PROPERTY);
+               if (p == NULL) {
+                       icalcomponent_set_dtstart(fb, this_event_period.start);
+               }
+               else {
+                       if (icaltime_compare(this_event_period.start, icalcomponent_get_dtstart(fb)) < 0) {
+                               icalcomponent_set_dtstart(fb, this_event_period.start);
+                       }
+               }
+       
+               /* Make sure the DTEND property of the freebusy *list* is set to
+                * the DTEND property of the *latest event*.
+                */
+               p = icalcomponent_get_first_property(fb, ICAL_DTEND_PROPERTY);
+               if (p == NULL) {
+                       icalcomponent_set_dtend(fb, this_event_period.end);
+               }
+               else {
+                       if (icaltime_compare(this_event_period.end, icalcomponent_get_dtend(fb)) > 0) {
+                               icalcomponent_set_dtend(fb, this_event_period.end);
+                       }
+               }
+
+               if (rrule) {
+                       dtstart = icalrecur_iterator_next(ritr);
+                       if (!icaltime_is_null_time(dtend)) {
+                               dtend = icaltime_add(dtstart, dur);
+                               dtend.zone = dtstart.zone;
+                               dtend.is_utc = dtstart.is_utc;
+                       }
+                       ++num_recur;
+               }
+
+       } while ( (rrule) && (!icaltime_is_null_time(dtstart)) && (num_recur < MAX_RECUR) ) ;
+       icalrecur_iterator_free(ritr);
 }
 
 
@@ -1350,7 +1400,7 @@ void ical_freebusy_backend(long msgnum, void *data) {
        CtdlFreeMessage(msg);
 
        if (ird.cal) {
-               ical_add_to_freebusy(fb, ird.cal, NULL);        /* Add VEVENT times to VFREEBUSY */
+               ical_add_to_freebusy(fb, ird.cal);              /* Add VEVENT times to VFREEBUSY */
                icalcomponent_free(ird.cal);
        }
 }
@@ -1508,7 +1558,7 @@ void ical_freebusy(char *who) {
        serialized_request = icalcomponent_as_ical_string_r(encaps);
        icalcomponent_free(encaps);     /* Don't need this anymore. */
 
-       cprintf("%d Here is the free/busy data:\n", LISTING_FOLLOWS);
+       cprintf("%d Free/busy for %s\n", LISTING_FOLLOWS, usbuf.fullname);
        if (serialized_request != NULL) {
                client_write(serialized_request, strlen(serialized_request));
                free(serialized_request);
@@ -1613,7 +1663,7 @@ void ical_getics(void)
 
        encaps = icalcomponent_new_vcalendar();
        if (encaps == NULL) {
-               CtdlLogPrintf(CTDL_DEBUG, "ERROR: could not allocate component!\n");
+               CtdlLogPrintf(CTDL_ALERT, "ERROR: could not allocate component!\n");
                cprintf("%d Could not allocate memory\n", ERROR+INTERNAL_ERROR);
                return;
        }
@@ -1691,7 +1741,7 @@ void ical_putics(void)
        }
 
        cprintf("%d Transmit data now\n", SEND_LISTING);
-        calstream = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0, 0);
+       calstream = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0, 0);
        if (calstream == NULL) {
                return;
        }
@@ -1938,11 +1988,13 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
        size_t reqsize;
        icalproperty *p;
        struct icaltimetype t;
-       icaltimezone *attached_zones[5] = { NULL, NULL, NULL, NULL, NULL };
+       const icaltimezone *attached_zones[5] = { NULL, NULL, NULL, NULL, NULL };
        int i;
-       icaltimezone *z;
+       const icaltimezone *z;
        int num_zones_attached = 0;
        int zone_already_attached;
+       icalparameter *tzidp = NULL;
+       const char *tzidc = NULL;
 
        if (cal == NULL) {
                CtdlLogPrintf(CTDL_ERR, "ERROR: trying to reply to NULL event?\n");
@@ -2012,7 +2064,7 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
        /* Encapsulate the VEVENT component into a complete VCALENDAR */
        encaps = icalcomponent_new_vcalendar();
        if (encaps == NULL) {
-               CtdlLogPrintf(CTDL_DEBUG, "ERROR: could not allocate component!\n");
+               CtdlLogPrintf(CTDL_ALERT, "ERROR: could not allocate component!\n");
                icalcomponent_free(the_request);
                return;
        }
@@ -2046,9 +2098,15 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
                  || (icalproperty_isa(p) == ICAL_RECURRENCEID_PROPERTY)
                ) {
                        t = icalproperty_get_dtstart(p);        // it's safe to use dtstart for all of them
-                       CtdlLogPrintf(CTDL_DEBUG, "Found an icaltimetype: %s\n",
-                               icaltime_as_ical_string(t)
-                       );
+
+                       /* Determine the tzid in order for some of the conditions below to work */
+                       tzidp = icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER);
+                       if (tzidp) {
+                               tzidc = icalparameter_get_tzid(tzidp);
+                       }
+                       else {
+                               tzidc = NULL;
+                       }
 
                        /* First see if there's a timezone attached to the data structure itself */
                        if (icaltime_is_utc(t)) {
@@ -2057,40 +2115,29 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
                        else {
                                z = icaltime_get_timezone(t);
                        }
-                       if (z) CtdlLogPrintf(CTDL_DEBUG, "Timezone is present in data structure\n");
 
                        /* If not, try to determine the tzid from the parameter using attached zones */
-                       if (!z) {
-                               z = icalcomponent_get_timezone(top_level_cal,
-                                       icalparameter_get_tzid(
-                                               icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
-                                       )
-                               );
-                               if (z) CtdlLogPrintf(CTDL_DEBUG, "Timezone was found in attached zones\n");
+                       if ((!z) && (tzidc)) {
+                               z = icalcomponent_get_timezone(top_level_cal, tzidc);
                        }
 
                        /* Still no good?  Try our internal database */
-                       if (!z) {
-                               z = icaltimezone_get_builtin_timezone_from_tzid(
-                                       icalparameter_get_tzid(
-                                               icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
-                                       )
-                               );
-                               if (z) CtdlLogPrintf(CTDL_DEBUG, "Timezone was found in internal db\n");
+                       if ((!z) && (tzidc)) {
+                               z = icaltimezone_get_builtin_timezone_from_tzid(tzidc);
                        }
 
                        if (z) {
-                               CtdlLogPrintf(CTDL_DEBUG, "Have valid timezone, need to attach it.\n");
+                               /* We have a valid timezone.  Good.  Now we need to attach it. */
 
                                zone_already_attached = 0;
                                for (i=0; i<5; ++i) {
                                        if (z == attached_zones[i]) {
+                                               /* We've already got this one, no need to attach another. */
                                                ++zone_already_attached;
-                                               CtdlLogPrintf(CTDL_DEBUG, "zone already attached!!\n");
                                        }
                                }
                                if ((!zone_already_attached) && (num_zones_attached < 5)) {
-                                       CtdlLogPrintf(CTDL_DEBUG, "attach zone %d\n", num_zones_attached);
+                                       /* This is a new one, so attach it. */
                                        attached_zones[num_zones_attached++] = z;
                                }
 
@@ -2119,8 +2166,6 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
        icalcomponent_free(encaps);     /* Don't need this anymore. */
        if (serialized_request == NULL) return;
 
-       CtdlLogPrintf(CTDL_DEBUG, "SENDING INVITATIONS:\n%s\n", serialized_request);
-
        reqsize = strlen(serialized_request) + SIZ;
        request_message_text = malloc(reqsize);
        if (request_message_text != NULL) {
@@ -2129,16 +2174,20 @@ void ical_send_out_invitations(icalcomponent *top_level_cal, icalcomponent *cal)
                        serialized_request
                );
 
-               msg = CtdlMakeMessage(&CC->user,
-                       "",                     /* No single recipient here */
-                       "",                     /* No single recipient here */
-                       CC->room.QRname, 0, FMT_RFC822,
-                       "",
-                       "",
+               msg = CtdlMakeMessage(
+                       &CC->user,
+                       NULL,                   /* No single recipient here */
+                       NULL,                   /* No single recipient here */
+                       CC->room.QRname,
+                       0,
+                       FMT_RFC822,
+                       NULL,
+                       NULL,
                        summary_string,         /* Use summary for subject */
                        NULL,
                        request_message_text,
-                       NULL);
+                       NULL
+               );
        
                if (msg != NULL) {
                        valid = validate_recipients(attendees_string, NULL, 0);