Defined 19010101T010101Z-99991231T235959Z consts.
[citadel.git] / webcit-ng / server / caldav_reports.c
index 08972cb860b71cec655b5bd58da33c98fcc194e0..1a6b931f6c689fd88c26798c669ab0c786104002 100644 (file)
@@ -4,8 +4,11 @@
 
 #include "webcit.h"
 
-#define CAL "urn:ietf:params:xml:ns:caldav:"           // Shorthand for the XML namespace of CalDAV
-#define CALLEN sizeof(CAL)-1                           // And the length of that string
+#define CALDAV "urn:ietf:params:xml:ns:caldav:"                // Shorthand for the XML namespace of CalDAV
+#define CALDAVLEN sizeof(CALDAV)-1                     // And the length of that string
+
+const char *the_beginning_of_time      = "19010101T010101Z";
+const char *the_end_of_time            = "99991231T235959Z";
 
 // A CalDAV REPORT can only be one type.  This is stored in the report_type member.
 enum cr_type {
@@ -14,7 +17,6 @@ enum cr_type {
        cr_freebusy_query
 };
 
-
 // Data type for CalDAV Report Parameters.
 // As we slog our way through the XML we learn what the client is asking for
 // and build up the contents of this data type.
@@ -42,45 +44,45 @@ void caldav_xml_start(void *data, const char *el, const char **attr) {
 #endif
 
        // RFC4791 7.8 "calendar-query" REPORT - Client will send a lot of search criteria.
-       if (!strcasecmp(el, CAL"calendar-query")) {
+       if (!strcasecmp(el, CALDAV"calendar-query")) {
                crp->report_type = cr_calendar_query;
        }
 
        // RFC4791 7.9 "calendar-multiget" REPORT - Client will supply a list of specific hrefs.
-       else if (!strcasecmp(el, CAL"calendar-multiget")) {
+       else if (!strcasecmp(el, CALDAV"calendar-multiget")) {
                crp->report_type = cr_calendar_multiget;
        }
 
        // RFC4791 7.10 "free-busy-query" REPORT
-       else if (!strcasecmp(el, CAL"free-busy-query")) {
+       else if (!strcasecmp(el, CALDAV"free-busy-query")) {
                crp->report_type = cr_freebusy_query;
        }
 
        // RFC4791 9.7 create a filter array if this query contains a "filter" stanza
-       else if (!strcasecmp(el, CAL"filter")) {
+       else if (!strcasecmp(el, CALDAV"filter")) {
                crp->filters = array_new(SIZ);
                crp->filter_nest = crp->comp_filter_nesting_level;
        }
 
        // Handle the filters defined in RFC4791 9.7.1 through 9.7.5
-       else if (       (       (!strcasecmp(el, CAL"comp-filter"))
-                               || (!strcasecmp(el, CAL"prop-filter"))
-                               || (!strcasecmp(el, CAL"param-filter"))
-                               || (!strcasecmp(el, CAL"is-not-defined"))
-                               || (!strcasecmp(el, CAL"text-match"))
-                               || (!strcasecmp(el, CAL"time-range"))
+       else if (       (       (!strcasecmp(el, CALDAV"comp-filter"))
+                               || (!strcasecmp(el, CALDAV"prop-filter"))
+                               || (!strcasecmp(el, CALDAV"param-filter"))
+                               || (!strcasecmp(el, CALDAV"is-not-defined"))
+                               || (!strcasecmp(el, CALDAV"text-match"))
+                               || (!strcasecmp(el, CALDAV"time-range"))
                        )
                        && (crp->filters)                       // Make sure we actually allocated an array
        ) {
 
-               if (!strcasecmp(el, CAL"comp-filter")) {
+               if (!strcasecmp(el, CALDAV"comp-filter")) {
                        ++crp->comp_filter_nesting_level;
                }
 
                char newfilter[SIZ];
                int a = 0;
                int len = snprintf(newfilter, SIZ, "%d|", crp->comp_filter_nesting_level - crp->filter_nest - 1);
-               len += snprintf(&newfilter[len], SIZ-len, "%s", &el[CALLEN]);           // filter name without the namespace
+               len += snprintf(&newfilter[len], SIZ-len, "%s", &el[CALDAVLEN]);                // filter name without the namespace
                while (attr[a]) {
                        len += snprintf(&newfilter[len], SIZ-len, "|%s", attr[a++]);    // now save the attributes
                }
@@ -101,7 +103,7 @@ void caldav_xml_end(void *data, const char *el) {
        // end logging
 #endif
 
-       if (!strcasecmp(el, CAL"comp-filter")) {
+       if (!strcasecmp(el, CALDAV"comp-filter")) {
                --crp->comp_filter_nesting_level;
        }
 
@@ -257,6 +259,33 @@ void caldav_report_one_item(struct http_transaction *h, struct ctdlsession *c, S
 }
 
 
+// Compare function for "time-range" tests (RFC4791 section 9.9)
+// Returns nonzero if the supplied icalcomponent occurs within the specified time range
+int caldav_time_range_filter_matches(icalcomponent *cal, char *start_str, char *end_str) {
+
+       struct icaltimetype start = icaltime_from_string(start_str);
+       syslog(LOG_DEBUG, "   search start: \033[36m%16s\033[0m (%s)", icaltime_as_ical_string_r(start), icaltime_get_tzid(start));
+
+       struct icaltimetype end = icaltime_from_string(end_str);
+       syslog(LOG_DEBUG, "   search   end: \033[36m%16s\033[0m (%s)", icaltime_as_ical_string_r(end), icaltime_get_tzid(end));
+
+       // IMPLEMENTATION NOTE:
+       // ical_ctdl_is_overlap() works because icaltime_compare() is really smart.
+       // It looks at the time zone of the dtstart/dtend and can apparently go back up the icalcomponent
+       // hierarchy to find its time zone data.  I tested this by creating an event with a fictional
+       // time zone and it did the right thing.  It even showed the fictional name to me.  This saves us
+       // from having to convert everything to UTC before comparing.  Nice!
+
+       icaltimetype dts = icalcomponent_get_dtstart(cal);
+       syslog(LOG_DEBUG, "component start: \033[36m%16s\033[0m (%s)", icaltime_as_ical_string_r(dts), icaltime_get_tzid(dts));
+
+       icaltimetype dte = icalcomponent_get_dtend(cal);
+       syslog(LOG_DEBUG, "component   end: \033[36m%16s\033[0m (%s)", icaltime_as_ical_string_r(dte), icaltime_get_tzid(dte));
+
+       return(ical_ctdl_is_overlap(dts, dte, start, end));     // We have a convenience function for this.
+}
+
+
 // Recursive function to apply CalDAV FILTERS to a calendar item.
 // Returns zero if the calendar item was disqualified by a filter, nonzero if the calendar item still qualifies.
 int caldav_apply_filters(void *cal, Array *filters, int apply_at_level) {
@@ -371,10 +400,24 @@ int caldav_apply_filters(void *cal, Array *filters, int apply_at_level) {
                        syslog(LOG_DEBUG, "text match filter at level %d FIXME not implemented yet", this_rule_level);
                }
 
-               else if (!strcasecmp(t[1], "time-range")) {
-                       syslog(LOG_DEBUG, "time range filter at level %d FIXME not implemented yet", this_rule_level);
-                       for (int i=0; i<num_tokens; ++i) {
-                               syslog(LOG_DEBUG, "token %2d : <%s>", i, t[i]);
+               else if (!strcasecmp(t[1], "time-range")) {                     // RFC4791 9.9
+                       syslog(LOG_DEBUG, "time range filter at level %d FIXME add recur", this_rule_level);
+                       for (int i=2; (i+1)<num_tokens; i+=2) {
+                               char *tr_start  = (char *)the_beginning_of_time;        // default if not specified
+                               char *tr_end    = (char *)the_end_of_time;              // default if not specified
+                               if (!strcasecmp(t[i], "start")) {
+                                       tr_start = t[i+1];
+                               }
+                               else if (!strcasecmp(t[i], "end")) {
+                                       tr_end = t[i+1];
+                               }
+                               if (caldav_time_range_filter_matches(cal, tr_start, tr_end)) {
+                                       syslog(LOG_DEBUG, "time range matches");
+                               }
+                               else {
+                                       syslog(LOG_DEBUG, "time range does not match -- rejecting");
+                                       qual = 0;
+                               }
                        }
                }