CalDAV: workarounds for Google Calendar 404 error

SyncEvolution itself is careful to not send a VEVENT with
RECURRENCE-ID unless the parent item is also in the Google Calendar.
If that is done, then the Google CalDAV server reports the item
in a REPORT, but cannot GET or DELETE it (404 error).

Because other clients might get the server into such a state (Android
client was mentioned), we must deal with it nevertheless. Retrieving
such an item uses a REPORT with UID filter as fallback. A
calendar-multiget had the same issue as a single GET. That code is in
the patch, but not enabled.

Deleting has no fallback. The item will simply remain on the server
and no error will be shown to users. Note that the same 404 error
might also occur in case of a concurrent delete by some other client,
so it makes sense to ignore this.
This commit is contained in:
Patrick Ohly 2011-01-27 16:03:23 +01:00
parent 67b43418f8
commit 05ddd8da6b
2 changed files with 88 additions and 2 deletions

View file

@ -439,7 +439,19 @@ std::string CalDAVSource::removeSubItem(const string &davLUID, const std::string
if (*event.m_subids.begin() != subid) {
SE_THROW("event not found");
} else {
removeItem(event.m_DAVluid);
try {
removeItem(event.m_DAVluid);
} catch (const TransportStatusException &ex) {
if (ex.syncMLStatus() == 404) {
// Someone must have created a detached recurrence on
// the server without the master event - or the
// item was already removed while the sync ran.
// Let's log the problem and ignore it.
Exception::log();
} else {
throw;
}
}
}
m_cache.erase(davLUID);
return "";
@ -528,7 +540,79 @@ CalDAVSource::Event &CalDAVSource::loadItem(Event &event)
{
if (!event.m_calendar) {
std::string item;
readItem(event.m_DAVluid, item, true);
try {
readItem(event.m_DAVluid, item, true);
} catch (const TransportStatusException &ex) {
if (ex.syncMLStatus() == 404) {
// Someone must have created a detached recurrence on
// the server without the master event. We avoid that
// with the "Google Child Hack", but have no control
// over other clients. So let's deal with this problem
// after logging it.
Exception::log();
// We know about the event because it showed up in a REPORT.
// So let's use such a REPORT to retrieve the desired item.
// Not as efficient as a GET (and thus not the default), but
// so be it.
#if 0
// This would be fairly efficient, but runs into the same 404 error as a GET.
std::string query =
StringPrintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<C:calendar-multiget xmlns:D=\"DAV:\"\n"
" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n"
"<D:prop>\n"
" <C:calendar-data/>\n"
"</D:prop>\n"
"<D:href><[CDATA[%s]]></D:href>\n"
"</C:calendar-multiget>",
event.m_DAVluid.c_str());
Neon::XMLParser parser;
std::string href, etag;
item = "";
parser.initReportParser(href, etag);
parser.pushHandler(boost::bind(Neon::XMLParser::accept, "urn:ietf:params:xml:ns:caldav", "calendar-data", _2, _3),
boost::bind(Neon::XMLParser::append, boost::ref(item), _2, _3));
Neon::Request report(*getSession(), "REPORT", getCalendar().m_path, query, parser);
report.addHeader("Content-Type", "application/xml; charset=\"utf-8\"");
report.run();
#else
std::string query =
StringPrintf("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
"<C:calendar-query xmlns:D=\"DAV:\"\n"
"xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n"
"<D:prop>\n"
"<D:getetag/>\n"
"<C:calendar-data/>\n"
"</D:prop>\n"
// filter expected by Yahoo! Calendar
"<C:filter>\n"
"<C:comp-filter name=\"VCALENDAR\">\n"
"<C:comp-filter name=\"VEVENT\">\n"
"<C:prop-filter name=\"UID\">\n"
"<C:text-match collation=\"i;octet\"><![CDATA[%s]]></C:text-match>\n"
"</C:prop-filter>\n"
"</C:comp-filter>\n"
"</C:comp-filter>\n"
"</C:filter>\n"
"</C:calendar-query>\n",
event.m_UID.c_str());
string result;
string href, etag;
Neon::XMLParser parser;
parser.initReportParser(href, etag);
item = "";
parser.pushHandler(boost::bind(Neon::XMLParser::accept, "urn:ietf:params:xml:ns:caldav", "calendar-data", _2, _3),
boost::bind(Neon::XMLParser::append, boost::ref(item), _2, _3));
Neon::Request report(*getSession(), "REPORT", getCalendar().m_path, query, parser);
report.addHeader("Depth", "1");
report.addHeader("Content-Type", "application/xml; charset=\"utf-8\"");
report.run();
#endif
} else {
throw;
}
}
Event::unescapeRecurrenceID(item);
event.m_calendar.set(icalcomponent_new_from_string(item.c_str()),
"parsing iCalendar 2.0");

View file

@ -256,6 +256,8 @@ void WebDAVSource::open()
{
// ignore the "Request ends, status 207 class 2xx, error line:" printed by neon
LogRedirect::addIgnoreError(", error line:");
// ignore error messages in returned data
LogRedirect::addIgnoreError("Read block (");
SE_LOG_DEBUG(NULL, NULL, "using libneon %s with %s",
ne_version_string(), Neon::features().c_str());