focus on "REST first" and build around that. The server code is well layered
with as little spaghetti as possible.
-Please don't mess with this yet. I'm only pushing it upstream so it gets backed up.
+This is a work in progress and not ready for production use. It is not feature
+complete.
## Design goals
* Hold as little state as possible
* Require NO cleanup. Killing the process lets the OS reclaim all resources.
-* As much as possible, resources should be freed by just coming back down the stack.
- Avoid global variables and thread-local variables as much as possible.
+* As much as possible, resources should be freed by just coming back down the
+ stack. Avoid global variables and thread-local variables as much as possible.
* Readability of the code is more important than shaving off a few CPU cycles.
-* Throw sensitive data such as passwords back and forth in clear text.
- If you want privacy, encrypt the whole session. Anything else is false security.
+* Throw sensitive data such as passwords back and forth in clear text. If you
+ want privacy, encrypt the whole session. Anything else is false security.
REST format URLs will generally take the form of:
* FontAwesome for icons
## We are NOT using
-* Your favorite javascript library
-* Your favorite CSS framework
+* Third party javascript libraries and/or CSS frameworks
// Compare function for "time-range" tests (RFC4791 section 9.9)
// Returns nonzero if the supplied icalcomponent occurs within the specified time range
+//
+// 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!
+//
int caldav_time_range_filter_matches(icalcomponent *cal, char *start_str, char *end_str) {
+ struct icaltimetype dtstart = icalcomponent_get_dtstart(cal);
+ struct icaltimetype dtend = icalcomponent_get_dtend(cal);
- // syslog(LOG_DEBUG, "caldav_time_range_filter_matches() comparing:\n\033[35m%s\033[0m", icalcomponent_as_ical_string(cal));
+ struct icaltimetype search_start = icaltime_from_string(start_str);
+ syslog(LOG_DEBUG, " search start: \033[36m%-16s\033[0m (%s)", icaltime_as_ical_string_r(search_start), icaltime_get_tzid(search_start));
- // NOTE TO ME:
- // Recurrence info is available at this level. We can handle it here.
+ struct icaltimetype search_end = icaltime_from_string(end_str);
+ syslog(LOG_DEBUG, " search end: \033[36m%-16s\033[0m (%s)", icaltime_as_ical_string_r(search_end), icaltime_get_tzid(search_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!
+ // If it is a recurring event, RRULE is available at this level. We can handle it here.
- 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));
+ icalproperty *rrule;
+ rrule = icalcomponent_get_first_property(cal, ICAL_RRULE_PROPERTY);
+ if (rrule) {
+ if (icaltime_is_null_time(dtend)) {
+ dtend = dtstart;
+ }
+ struct icaldurationtype dur = icaltime_subtract(dtend, dtstart); // recurrences need duration to find dtend
+ struct icalrecurrencetype recur = icalproperty_get_rrule(rrule);
+
+ icalrecur_iterator *ritr = icalrecur_iterator_new(recur, dtstart); // iterate through recurrences
+ int rcount = 0;
+ syslog(LOG_DEBUG, "\033[7m RECURRENCE: \033[0m");
+ while (dtstart = icalrecur_iterator_next(ritr), !icaltime_is_null_time(dtstart)) {
+ dtend = icaltime_add(dtstart, dur);
+ syslog(LOG_DEBUG, "recurrence %3d start: \033[36m%-16s\033[0m (%s)", rcount, icaltime_as_ical_string_r(dtstart), icaltime_get_tzid(dtstart));
+ syslog(LOG_DEBUG, "recurrence %3d end: \033[36m%-16s\033[0m (%s)", rcount, icaltime_as_ical_string_r(dtend), icaltime_get_tzid(dtend));
+
+ // Does THIS recurrence match the query?
+ if (ical_ctdl_is_overlap(dtstart, dtend, search_start, search_end)) {
+ icalrecur_iterator_free(ritr);
+ return(1);
+ }
- 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));
+ ++rcount;
+ }
- 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));
+ icalrecur_iterator_free(ritr);
+ return(0); // compared all recurrences, no match was found for any of them
+ }
- return(ical_ctdl_is_overlap(dts, dte, start, end)); // We have a convenience function for this.
+ // For non recurring events, do a simple time range compare.
+ syslog(LOG_DEBUG, "event start: \033[36m%-16s\033[0m (%s)", icaltime_as_ical_string_r(dtstart), icaltime_get_tzid(dtstart));
+ syslog(LOG_DEBUG, "event end: \033[36m%-16s\033[0m (%s)", icaltime_as_ical_string_r(dtend), icaltime_get_tzid(dtend));
+ return(ical_ctdl_is_overlap(dtstart, dtend, search_start, search_end)); // We have a convenience function for this.
}