WebDAV: use CTag for quick change detection

The CTag mechanism allows to quickly check whether data has changed.
With WebDAV and the recent infrastructure changes in
SyncSourceRevisions/TrackingSyncSource, that is straight-forward.

With CalDAV it is a bit more complicated because the m_cache needs to
be populated for some of the operations to succeed. This is
accomplished via the setAll[Sub]Items() calls.
This commit is contained in:
Patrick Ohly 2011-06-22 17:38:32 +02:00
parent 415e33bada
commit 6a4b2a124d
4 changed files with 60 additions and 3 deletions

View file

@ -130,6 +130,28 @@ int CalDAVSource::appendItem(SubRevisionMap_t &revisions,
return 0;
}
void CalDAVSource::setAllSubItems(const SubRevisionMap_t &revisions)
{
if (!m_cache.m_initialized) {
// populate our cache (without data) from the information cached
// for us
BOOST_FOREACH(const SubSyncSource::SubRevisionMap_t::value_type &subentry,
revisions) {
const std::string &luid = subentry.first;
const std::string &rev = subentry.second.first;
boost::shared_ptr<Event> &event = m_cache[luid];
event.reset(new Event);
event->m_DAVluid = luid;
event->m_etag = rev;
// We don't know the real UID (may be different from resource name == DAVluid),
// sequence and last-modified. This information will have to be filled
// in by loadItem() when some operation on this event needs it.
event->m_subids = subentry.second.second;
}
m_cache.m_initialized = true;
}
}
SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid, const std::string &callerSubID,
const std::string &item)
{
@ -179,7 +201,10 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
}
std::string subid = *newEvent->m_subids.begin();
// determine whether we already know the merged item even though our caller didn't
// Determine whether we already know the merged item even though
// our caller didn't. That additional safe guard will fail if
// setAllSubItems() was used to fill the cache, because then the m_UID
// values in the cache will not be set yet. That should be okay.
std::string davLUID = luid;
std::string knownSubID = callerSubID;
if (davLUID.empty()) {
@ -706,11 +731,17 @@ CalDAVSource::Event &CalDAVSource::loadItem(Event &event)
Event::unescapeRecurrenceID(item);
event.m_calendar.set(icalcomponent_new_from_string((char *)item.c_str()), // hack for old libical
"parsing iCalendar 2.0");
// sequence number might have been increased by last save,
// so check it again
// Sequence number/last-modified might have been increased by last save.
// Or the cache was populated by setAllSubItems(), which doesn't give
// us the information. In that case, UID might also still be unknown.
// Either way, check it again.
for (icalcomponent *comp = icalcomponent_get_first_component(event.m_calendar, ICAL_VEVENT_COMPONENT);
comp;
comp = icalcomponent_get_next_component(event.m_calendar, ICAL_VEVENT_COMPONENT)) {
if (event.m_UID.empty()) {
event.m_UID = Event::getUID(comp);
}
long sequence = Event::getSequence(comp);
if (sequence > event.m_sequence) {
event.m_sequence = sequence;

View file

@ -34,7 +34,9 @@ class CalDAVSource : public WebDAVSource,
/* implementation of SubSyncSource interface */
virtual void begin() { contactServer(); }
virtual void endSubSync(bool success) { if (success) { storeServerInfos(); } }
virtual std::string subDatabaseRevision() { return databaseRevision(); }
virtual void listAllSubItems(SubRevisionMap_t &revisions);
virtual void setAllSubItems(const SubRevisionMap_t &revisions);
virtual SubItemResult insertSubItem(const std::string &uid, const std::string &subid,
const std::string &item);
virtual void readSubItem(const std::string &uid, const std::string &subid, std::string &item);

View file

@ -881,6 +881,29 @@ void WebDAVSource::storeServerInfos()
}
}
/**
* See https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-ctag.txt
*/
static const ne_propname getctag[] = {
{ "http://calendarserver.org/ns/", "getctag" },
{ NULL, NULL }
};
std::string WebDAVSource::databaseRevision()
{
Timespec deadline = createDeadline();
Neon::Session::PropfindPropCallback_t callback =
boost::bind(&WebDAVSource::openPropCallback,
this, _1, _2, _3, _4);
m_davProps[m_calendar.m_path]["http://calendarserver.org/ns/:getctag"] = "";
m_session->propfindProp(m_calendar.m_path, 0, getctag, callback, deadline);
// Fatal communication problems will be reported via exceptions.
// Once we get here, invalid or incomplete results can be
// treated as "don't have revision string".
string ctag = m_davProps[m_calendar.m_path]["http://calendarserver.org/ns/:getctag"];
return ctag;
}
static const ne_propname getetag[] = {
{ "DAV:", "getetag" },

View file

@ -79,6 +79,7 @@ class WebDAVSource : public TrackingSyncSource, private boost::noncopyable
}
/* implementation of TrackingSyncSource interface */
virtual std::string databaseRevision();
virtual void listAllItems(RevisionMap_t &revisions);
virtual InsertItemResult insertItem(const string &luid, const std::string &item, bool raw);
void readItem(const std::string &luid, std::string &item, bool raw);