Try new strategy for CalDAV report filter parsing.
authorArt Cancro <ajc@citadel.org>
Wed, 21 Feb 2024 17:28:11 +0000 (12:28 -0500)
committerArt Cancro <ajc@citadel.org>
Wed, 21 Feb 2024 17:28:11 +0000 (12:28 -0500)
The recursive strategy I initially built looked clever but it's
not going to work because we need to iterate through both the
rules and the components/properties/parameters at the same time.

Reworked the code to go through filters iteratively instead of
recursively.  This protocol is way too complex and the people who
designed it need to be tortured.

webcit-ng/server/caldav_reports.c
webcit-ng/server/webcit.h

index 31fe712bacf66662156fbe47c7c95b9cba5c0b16..05d3b9ef65e9b37c5b92a26f280e9d2b9f063fbf 100644 (file)
@@ -4,9 +4,8 @@
 
 #include "webcit.h"
 
-// Shorthand for the XML namespace of CalDAV
-#define CAL "urn:ietf:params:xml:ns:caldav:"
-#define CALLEN sizeof(CAL)-1
+#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
 
 // A CalDAV REPORT can only be one type.  This is stored in the report_type member.
 enum cr_type {
@@ -273,78 +272,56 @@ void caldav_report_one_item(struct http_transaction *h, struct ctdlsession *c, S
 }
 
 
-// Called by caldav_apply_filters() to apply ONE filter.
-// Returns 0 if the calendar item does not match the filter.
-// Returns 1 if the calendar item DOES match the filter.
-int caldav_apply_one_filter(void *cal, char *filter) {
-       syslog(LOG_DEBUG, "applying filter: %s", filter);
-
-       char this_filter[SIZ];          // we have to copy the filter string because we will destructively tokenize it
-       safestrncpy(this_filter, filter, sizeof(this_filter));
-
-       char *t[10] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } ;
-       char *f = this_filter;
-       int num_tokens = 0;
-       while ( (t[num_tokens]=strtok_r(f, "|", &f)) && (num_tokens<10) ) {
-               ++num_tokens;
-       }
-
-       // BEGIN experimental block -- see what happens when we cast to the wrong type
-
-       //icalcomponent *foo_comp = (icalcomponent *) cal;
-       //icalproperty *foo_prop = (icalproperty *) cal;
-       //icalparameter *foo_param = (icalparameter *) cal;
-
-       // END experimental block
-
-       // Handle the individual filters defined in RFC4791 9.7.1 through 9.7.5
-
-       if (!strcasecmp(t[1], "comp-filter")) {                         // RFC4791 9.7.1 - filter by component
-               syslog(LOG_DEBUG, "component filter FIXME not implemented yet");
-               if (icalcomponent_isa_component(cal)) {
-                       syslog(LOG_DEBUG, "\033[32m yes this is a component \033[0m");
-               }
-               else {
-                       syslog(LOG_DEBUG, "\033[31m no this is not a component \033[0m");
+// 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 f = 0;                                      // filter number iterator
+       int qual = 1;                                   // 0 for disqualify, 1 for qualify
+
+       while ( (f<array_len(filters)) && (qual) ) {
+               syslog(LOG_DEBUG, "caldav_apply_filters(%d) %s", f, array_get_element_at(filters, f) );
+
+               // Tokenize the filter (a future performance hack would be to pre-tokenize instead of storing delimited strings)
+               char this_filter[SIZ];
+               safestrncpy(this_filter, array_get_element_at(filters, f), sizeof(this_filter));
+               char *t[10] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } ;
+               char *ft = this_filter;
+               int num_tokens = 0;
+               while ( (t[num_tokens]=strtok_r(ft, "|", &ft)) && (num_tokens<10) ) {
+                       ++num_tokens;
                }
-       }
+               int level = atoi(t[0]);
 
-       else if (!strcasecmp(t[1], "prop-filter")) {                    // RFC4791 9.7.2 - filter by property
-               syslog(LOG_DEBUG, "property filter FIXME not implemented yet");
-       }
+               // Handle the individual filters defined in RFC4791 9.7.1 through 9.7.5
 
-       else if (!strcasecmp(t[1], "param-filter")) {                   // RFC4791 9.7.3 - filter by parameter
-               syslog(LOG_DEBUG, "parameter filter FIXME not implemented yet");
-       }
-
-       else if (!strcasecmp(t[1], "is-not-defined")) {                 // RFC4791 9.7.4
-               syslog(LOG_DEBUG, "is-not-defined filter FIXME not implemented yet");
-       }
-
-       else if (!strcasecmp(t[1], "text-match")) {                     // RFC4791 9.7.5
-               syslog(LOG_DEBUG, "text match filter FIXME not implemented yet");
-       }
+               if (!strcasecmp(t[1], "comp-filter")) {                         // RFC4791 9.7.1 - filter by component
+                       syslog(LOG_DEBUG, "component filter at level %d FIXME not implemented yet", level);
+                       if (icalcomponent_isa_component(cal)) {
+                               // yes this is a component
+                       }
+               }
 
-       return(1);
-}
+               else if (!strcasecmp(t[1], "prop-filter")) {                    // RFC4791 9.7.2 - filter by property
+                       syslog(LOG_DEBUG, "property filter FIXME not implemented yet");
+               }
 
+               else if (!strcasecmp(t[1], "param-filter")) {                   // RFC4791 9.7.3 - filter by parameter
+                       syslog(LOG_DEBUG, "parameter filter FIXME not implemented yet");
+               }
 
-// 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 index) {
+               else if (!strcasecmp(t[1], "is-not-defined")) {                 // RFC4791 9.7.4
+                       syslog(LOG_DEBUG, "is-not-defined filter FIXME not implemented yet");
+               }
 
-       // Apply *this* filter.
-       if (caldav_apply_one_filter(cal, array_get_element_at(filters, index)) == 0) {
-               return(0);
-       }
+               else if (!strcasecmp(t[1], "text-match")) {                     // RFC4791 9.7.5
+                       syslog(LOG_DEBUG, "text match filter FIXME not implemented yet");
+               }
 
-       // If we get to this point, the current filter has passed, and we move on to the next one.
-       if (index < array_len(filters)-1) {
-               return(caldav_apply_filters(cal, filters, index+1));
+               ++f;
        }
 
-       // If we got this far, every filter passed, and the calendar item qualifies for output.
-       return(1);
+       return(qual);
 }
 
 
@@ -409,7 +386,7 @@ void caldav_report(struct http_transaction *h, struct ctdlsession *c) {
                                int qualify = 1;
 
                                // If there was a filter stanza, run this calendar item through the filters.
-                               qualify = caldav_apply_filters(cal, crp.filters, 0);
+                               qualify = caldav_apply_filters(cal, crp.filters);
                                syslog(LOG_DEBUG, "Message %ld does%s qualify", m, (qualify ? "" : " NOT"));
 
                                // Did this calendar item match the query?  If so, output it.
index 657ac18b222bfc0fcb2dca0799d99c3323e1a157..92738063bfa7ff1302f109981904adee34da7dfb 100644 (file)
@@ -158,7 +158,7 @@ StrBuf *fetch_ical(struct ctdlsession *, long);
 void cal_multiget_out(long, StrBuf *, StrBuf *, StrBuf *);
 void caldav_report_one_item(struct http_transaction *, struct ctdlsession *, StrBuf *, StrBuf *);
 int caldav_apply_one_filter(void *, char *);
-int caldav_apply_filters(void *, Array *, int);
+int caldav_apply_filters(void *, Array *);
 void caldav_report(struct http_transaction *, struct ctdlsession *);
 
 // ctdlclient.c