* Implemented recurrence in freebusy display. todo: convert timezones to utc...
[citadel.git] / citadel / modules / calendar / serv_calendar.c
index cfafb7f2828a67c47be14db2161fbcca6f203936..9d859b5ee196ff8bfe29adeff0a9af6c7745ffd4 100644 (file)
@@ -316,7 +316,8 @@ void ical_send_a_reply(icalcomponent *request, char *action) {
 
 /*
  * Callback function for mime parser that hunts for calendar content types
- * and turns them into calendar objects
+ * and turns them into calendar objects.  If something is found, it is placed
+ * in ird->cal, and the caller now owns that memory and is responsible for freeing it.
  */
 void ical_locate_part(char *name, char *filename, char *partnum, char *disp,
                void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
@@ -874,13 +875,13 @@ int ical_conflicts_phase6(struct icaltimetype t1start,
 
        /* debugging cruft *
        time_t tt;
-       tt = icaltime_as_timet(t1start);
+       tt = icaltime_as_timet_with_zone(t1start, t1start.zone);
        CtdlLogPrintf(CTDL_DEBUG, "PROPOSED START: %s", ctime(&tt));
-       tt = icaltime_as_timet(t1end);
+       tt = icaltime_as_timet_with_zone(t1end, t1end.zone);
        CtdlLogPrintf(CTDL_DEBUG, "  PROPOSED END: %s", ctime(&tt));
-       tt = icaltime_as_timet(t2start);
+       tt = icaltime_as_timet_with_zone(t2start, t2start.zone);
        CtdlLogPrintf(CTDL_DEBUG, "EXISTING START: %s", ctime(&tt));
-       tt = icaltime_as_timet(t2end);
+       tt = icaltime_as_timet_with_zone(t2end, t2end.zone);
        CtdlLogPrintf(CTDL_DEBUG, "  EXISTING END: %s", ctime(&tt));
        * debugging cruft */
 
@@ -939,10 +940,37 @@ void ical_conflicts_phase5(struct icaltimetype t1start,
        p = ical_ctdl_get_subprop(existing_event, ICAL_DTSTART_PROPERTY);
        if (p == NULL) return;
        if (p != NULL) t2start = icalproperty_get_dtstart(p);
+       if (icaltime_is_utc(t2start)) {
+               t2start.zone = icaltimezone_get_utc_timezone();
+       }
+       else {
+               t2start.zone = icalcomponent_get_timezone(existing_event,
+                       icalparameter_get_tzid(
+                               icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
+                       )
+               );
+               if (!t2start.zone) {
+                       t2start.zone = get_default_icaltimezone();
+               }
+       }
 
        p = ical_ctdl_get_subprop(existing_event, ICAL_DTEND_PROPERTY);
        if (p != NULL) {
                t2end = icalproperty_get_dtend(p);
+
+               if (icaltime_is_utc(t2end)) {
+                       t2end.zone = icaltimezone_get_utc_timezone();
+               }
+               else {
+                       t2end.zone = icalcomponent_get_timezone(existing_event,
+                               icalparameter_get_tzid(
+                                       icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
+                               )
+                       );
+                       if (!t2end.zone) {
+                               t2end.zone = get_default_icaltimezone();
+                       }
+               }
                dur = icaltime_subtract(t2end, t2start);
        }
 
@@ -950,7 +978,6 @@ void ical_conflicts_phase5(struct icaltimetype t1start,
        if (rrule) {
                recur = icalproperty_get_rrule(rrule);
                ritr = icalrecur_iterator_new(recur, t2start);
-               CtdlLogPrintf(CTDL_DEBUG, "Recurrence found: %s\n", icalrecurrencetype_as_string(&recur));
        }
 
        do {
@@ -967,26 +994,25 @@ void ical_conflicts_phase5(struct icaltimetype t1start,
                if (ical_conflicts_phase6(t1start, t1end, t2start, t2end,
                   existing_msgnum, conflict_event_uid, conflict_event_summary, compare_uid))
                {
-                       CtdlLogPrintf(CTDL_DEBUG, "Hit a conflict after %d iterations\n", num_recur);
-                       num_recur = MAX_RECUR + 1;      /* force it out of scope */
+                       num_recur = MAX_RECUR + 1;      /* force it out of scope, no need to continue */
                }
 
                if (rrule) {
                        t2start = icalrecur_iterator_next(ritr);
                        if (!icaltime_is_null_time(t2end)) {
+                               const icaltimezone *hold_zone = t2end.zone;
                                t2end = icaltime_add(t2start, dur);
+                               t2end.zone = hold_zone;
                        }
                        ++num_recur;
                }
 
                if (icaltime_compare(t2start, t1end) < 0) {
-                       CtdlLogPrintf(CTDL_DEBUG, "Went out of scope after %d iterations\n", num_recur);
                        num_recur = MAX_RECUR + 1;      /* force it out of scope */
                }
 
        } while ( (rrule) && (!icaltime_is_null_time(t2start)) && (num_recur < MAX_RECUR) );
        icalrecur_iterator_free(ritr);
-       if (num_recur > 0) CtdlLogPrintf(CTDL_DEBUG, "Iterated over existing event %d times.\n", num_recur);
 }
 
 
@@ -1024,10 +1050,38 @@ void ical_conflicts_phase4(icalcomponent *proposed_event,
        p = ical_ctdl_get_subprop(proposed_event, ICAL_DTSTART_PROPERTY);
        if (p == NULL) return;
        if (p != NULL) t1start = icalproperty_get_dtstart(p);
+       if (icaltime_is_utc(t1start)) {
+               t1start.zone = icaltimezone_get_utc_timezone();
+       }
+       else {
+               t1start.zone = icalcomponent_get_timezone(proposed_event,
+                       icalparameter_get_tzid(
+                               icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
+                       )
+               );
+               if (!t1start.zone) {
+                       t1start.zone = get_default_icaltimezone();
+               }
+       }
        
        p = ical_ctdl_get_subprop(proposed_event, ICAL_DTEND_PROPERTY);
        if (p != NULL) {
                t1end = icalproperty_get_dtend(p);
+
+               if (icaltime_is_utc(t1end)) {
+                       t1end.zone = icaltimezone_get_utc_timezone();
+               }
+               else {
+                       t1end.zone = icalcomponent_get_timezone(proposed_event,
+                               icalparameter_get_tzid(
+                                       icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER)
+                               )
+                       );
+                       if (!t1end.zone) {
+                               t1end.zone = get_default_icaltimezone();
+                       }
+               }
+
                dur = icaltime_subtract(t1end, t1start);
        }
 
@@ -1035,7 +1089,6 @@ void ical_conflicts_phase4(icalcomponent *proposed_event,
        if (rrule) {
                recur = icalproperty_get_rrule(rrule);
                ritr = icalrecur_iterator_new(recur, t1start);
-               CtdlLogPrintf(CTDL_DEBUG, "Recurrence found: %s\n", icalrecurrencetype_as_string(&recur));
        }
 
        p = ical_ctdl_get_subprop(proposed_event, ICAL_UID_PROPERTY);
@@ -1049,14 +1102,15 @@ void ical_conflicts_phase4(icalcomponent *proposed_event,
                if (rrule) {
                        t1start = icalrecur_iterator_next(ritr);
                        if (!icaltime_is_null_time(t1end)) {
+                               const icaltimezone *hold_zone = t1end.zone;
                                t1end = icaltime_add(t1start, dur);
+                               t1end.zone = hold_zone;
                        }
                        ++num_recur;
                }
 
        } while ( (rrule) && (!icaltime_is_null_time(t1start)) && (num_recur < MAX_RECUR) );
        icalrecur_iterator_free(ritr);
-       if (num_recur > 0) CtdlLogPrintf(CTDL_DEBUG, "Iterated over proposed event %d times.\n", num_recur);
 }
 
 
@@ -1136,7 +1190,7 @@ void ical_conflicts(long msgnum, char *partnum) {
 
        msg = CtdlFetchMessage(msgnum, 1);
        if (msg == NULL) {
-               cprintf("%d Message %ld not found.\n",
+               cprintf("%d Message %ld not found\n",
                        ERROR + ILLEGAL_VALUE,
                        (long)msgnum
                );
@@ -1160,35 +1214,38 @@ void ical_conflicts(long msgnum, char *partnum) {
                icalcomponent_free(ird.cal);
                return;
        }
-       else {
-               cprintf("%d No calendar object found\n", ERROR + ROOM_NOT_FOUND);
-               return;
-       }
 
-       /* should never get here */
+       cprintf("%d No calendar object found\n", ERROR + ROOM_NOT_FOUND);
 }
 
 
 
 /*
  * 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 a VEVENT to be added
  */
-void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *cal) {
+void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *top_level_cal) {
+       icalcomponent *cal;
        icalproperty *p;
        icalvalue *v;
-       struct icalperiodtype my_period;
+       struct icalperiodtype this_event_period = icalperiodtype_null_period();
+       icaltimetype dtstart = icaltime_null_time();
+       icaltimetype dtend = icaltime_null_time();
 
-       if (cal == NULL) return;
-       my_period = icalperiodtype_null_period();
+       /* recur variables */
+       icalproperty *rrule = NULL;
+       struct icalrecurrencetype recur;
+       icalrecur_iterator *ritr = NULL;
+       struct icaldurationtype dur;
+       int num_recur = 0;
 
-       if (icalcomponent_isa(cal) != ICAL_VEVENT_COMPONENT) {
-               ical_add_to_freebusy(fb,
-                       icalcomponent_get_first_component(
-                               cal, ICAL_VEVENT_COMPONENT
-                       )
-               );
-               return;
-       }
+       if (!top_level_cal) 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.
@@ -1203,58 +1260,77 @@ void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *cal) {
                }
        }
 
-       /* Convert the DTSTART and DTEND properties to an icalperiod. */
-       p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY);
-       if (p != NULL) {
-               my_period.start = icalproperty_get_dtstart(p);
+       /*
+        * Now begin calculating the event start and end times.
+        */
+       dtstart = icalcomponent_get_dtstart(cal);
+       if (icaltime_is_null_time(dtstart)) return;
+
+       dtend = icalcomponent_get_dtend(cal);
+       if (!icaltime_is_null_time(dtend)) {
+               dur = icaltime_subtract(dtend, dtstart);
        }
 
-       p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
-       if (p != NULL) {
-               my_period.end = icalproperty_get_dtstart(p);
+       /* 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);
        }
 
-       /* Now add it. */
-       icalcomponent_add_property(fb,
-               icalproperty_new_freebusy(my_period)
-       );
+       do {
 
-       /* 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) );
-       }
-       else {
-               if (icaltime_compare(
-                       icalcomponent_get_dtstart(cal),
-                       icalcomponent_get_dtstart(fb)
-                  ) < 0) {
-                       icalcomponent_set_dtstart(fb,
-                               icalcomponent_get_dtstart(cal) );
+
+               // FIXME add timezone conversion, we are currently outputting floating times
+
+
+
+               /* 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;
                }
-       }
+       
+               /* Now add it. */
+               icalcomponent_add_property(fb, icalproperty_new_freebusy(this_event_period));
 
-       /* 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) );
-       }
-       else {
-               if (icaltime_compare(
-                       icalcomponent_get_dtend(cal),
-                       icalcomponent_get_dtend(fb)
-                  ) > 0) {
-                       icalcomponent_set_dtend(fb,
-                               icalcomponent_get_dtend(cal) );
+               /* 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, dtstart);
+               }
+               else {
+                       if (icaltime_compare(dtstart, icalcomponent_get_dtstart(fb)) < 0) {
+                               icalcomponent_set_dtstart(fb, dtstart);
+                       }
+               }
+       
+               /* 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, dtend);
+               }
+               else {
+                       if (icaltime_compare(dtend, icalcomponent_get_dtend(fb)) > 0) {
+                               icalcomponent_set_dtend(fb, dtend);
+                       }
+               }
+
+               if (rrule) {
+                       dtstart = icalrecur_iterator_next(ritr);
+                       if (!icaltime_is_null_time(dtend)) {
+                               dtend = icaltime_add(dtstart, dur);
+                       }
+                       ++num_recur;
                }
-       }
 
+       } while ( (rrule) && (!icaltime_is_null_time(dtstart)) && (num_recur < MAX_RECUR) ) ;
+       icalrecur_iterator_free(ritr);
 }
 
 
@@ -1268,11 +1344,11 @@ void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *cal) {
  *
  */
 void ical_freebusy_backend(long msgnum, void *data) {
-       icalcomponent *cal;
+       icalcomponent *fb;
        struct CtdlMessage *msg = NULL;
        struct ical_respond_data ird;
 
-       cal = (icalcomponent *)data;
+       fb = (icalcomponent *)data;             /* User-supplied data will be the VFREEBUSY component */
 
        msg = CtdlFetchMessage(msgnum, 1);
        if (msg == NULL) return;
@@ -1287,12 +1363,10 @@ void ical_freebusy_backend(long msgnum, void *data) {
        );
        CtdlFreeMessage(msg);
 
-       if (ird.cal == NULL) return;
-
-       ical_add_to_freebusy(cal, ird.cal);
-
-       /* Now free the memory. */
-       icalcomponent_free(ird.cal);
+       if (ird.cal) {
+               ical_add_to_freebusy(fb, ird.cal);              /* Add VEVENT times to VFREEBUSY */
+               icalcomponent_free(ird.cal);
+       }
 }
 
 
@@ -1448,7 +1522,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);
@@ -1631,7 +1705,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;
        }