CalDAV: added Google Calendar alarm hack
Google Calendar adds a VALARM when a VEVENT is created for the first time on the server via CalDAV. This is not what we want, if the VEVENT had no VALARM, that's how it should be stored. As a workaround we detect this special case (new VEVENT without VALARM) and send the original data again. Having to resend with higher SEQUENCE and LAST-MODIFIED/DTSTAMP values makes this a bit complex and slow, because we need to get the actual data from the server (cannot guess what time stamps were assigned). This leads to an interesting question: if the clock on the local side is unsynchronized, its LAST-MODIFIED time stamps might end up being lower than the values on the server, which prevents sending local updates. There's currently no solution for this in the backend.
This commit is contained in:
parent
f4eab38b01
commit
60b63645b8
|
@ -202,17 +202,20 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
InsertItemResult res;
|
||||
// Yahoo expects resource names to match UID + ".ics".
|
||||
std::string name = newEvent->m_UID + ".ics";
|
||||
std::string buffer;
|
||||
const std::string *data;
|
||||
if (!settings().googleChildHack() || subid.empty()) {
|
||||
// avoid re-encoding item data
|
||||
res = insertItem(name, item, true);
|
||||
data = &item;
|
||||
} else {
|
||||
// sanitize item first: when adding child event without parent,
|
||||
// then the RECURRENCE-ID confuses Google
|
||||
eptr<char> icalstr(ical_strdup(icalcomponent_as_ical_string(newEvent->m_calendar)));
|
||||
std::string data = icalstr.get();
|
||||
Event::escapeRecurrenceID(data);
|
||||
res = insertItem(name, data, true);
|
||||
buffer = icalstr.get();
|
||||
Event::escapeRecurrenceID(buffer);
|
||||
data = &buffer;
|
||||
}
|
||||
res = insertItem(name, *data, true);
|
||||
subres.m_uid = res.m_luid;
|
||||
subres.m_subid = subid;
|
||||
subres.m_revision = res.m_revision;
|
||||
|
@ -232,10 +235,58 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
icalcomponent_merge_component(event.m_calendar,
|
||||
newEvent->m_calendar.release()); // function destroys merged calendar
|
||||
} else {
|
||||
// add to cache
|
||||
newEvent->m_DAVluid = res.m_luid;
|
||||
newEvent->m_etag = res.m_revision;
|
||||
m_cache[newEvent->m_DAVluid] = newEvent;
|
||||
// Google Calendar adds a default alarm each time a VEVENT is added
|
||||
// anew. Avoid that by resending our data if necessary (= no alarm set).
|
||||
if (settings().googleAlarmHack() &&
|
||||
!icalcomponent_get_first_component(firstcomp, ICAL_VALARM_COMPONENT)) {
|
||||
// add to cache, then update it
|
||||
newEvent->m_DAVluid = res.m_luid;
|
||||
newEvent->m_etag = res.m_revision;
|
||||
m_cache[newEvent->m_DAVluid] = newEvent;
|
||||
|
||||
// potentially need to know sequence and mod time on server:
|
||||
// keep pointer (clears pointer in newEvent),
|
||||
// then get and parse new copy from server
|
||||
eptr<icalcomponent> calendar = newEvent->m_calendar;
|
||||
|
||||
if (settings().googleUpdateHack()) {
|
||||
loadItem(*newEvent);
|
||||
|
||||
// increment in original data
|
||||
newEvent->m_sequence++;
|
||||
newEvent->m_lastmodtime++;
|
||||
Event::setSequence(firstcomp, newEvent->m_sequence);
|
||||
icalproperty *lastmod = icalcomponent_get_first_property(firstcomp, ICAL_LASTMODIFIED_PROPERTY);
|
||||
if (lastmod) {
|
||||
lastmodtime = icaltime_from_timet(newEvent->m_lastmodtime, false);
|
||||
icalproperty_set_lastmodified(lastmod, lastmodtime);
|
||||
}
|
||||
icalproperty *dtstamp = icalcomponent_get_first_property(firstcomp, ICAL_DTSTAMP_PROPERTY);
|
||||
if (dtstamp) {
|
||||
icalproperty_set_dtstamp(dtstamp, lastmodtime);
|
||||
}
|
||||
// re-encode below
|
||||
data = &buffer;
|
||||
}
|
||||
bool mangleRecurrenceID = settings().googleChildHack() && !subid.empty();
|
||||
if (data == &buffer || mangleRecurrenceID) {
|
||||
eptr<char> icalstr(ical_strdup(icalcomponent_as_ical_string(calendar)));
|
||||
buffer = icalstr.get();
|
||||
}
|
||||
if (mangleRecurrenceID) {
|
||||
Event::escapeRecurrenceID(buffer);
|
||||
}
|
||||
SE_LOG_DEBUG(NULL, NULL, "resending VEVENT to get rid of VALARM");
|
||||
res = insertItem(name, *data, true);
|
||||
newEvent->m_etag =
|
||||
subres.m_revision = res.m_revision;
|
||||
newEvent->m_calendar = calendar;
|
||||
} else {
|
||||
// add to cache without further changes
|
||||
newEvent->m_DAVluid = res.m_luid;
|
||||
newEvent->m_etag = res.m_revision;
|
||||
m_cache[newEvent->m_DAVluid] = newEvent;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (subid != knownSubID) {
|
||||
|
@ -490,6 +541,14 @@ CalDAVSource::Event &CalDAVSource::loadItem(Event &event)
|
|||
if (sequence > event.m_sequence) {
|
||||
event.m_sequence = sequence;
|
||||
}
|
||||
icalproperty *lastmod = icalcomponent_get_first_property(comp, ICAL_LASTMODIFIED_PROPERTY);
|
||||
if (lastmod) {
|
||||
icaltimetype lastmodtime = icalproperty_get_lastmodified(lastmod);
|
||||
time_t mod = icaltime_as_timet(lastmodtime);
|
||||
if (mod > event.m_lastmodtime) {
|
||||
event.m_lastmodtime = mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return event;
|
||||
|
|
|
@ -76,7 +76,8 @@ class CalDAVSource : public WebDAVSource,
|
|||
class Event : boost::noncopyable {
|
||||
public:
|
||||
Event() :
|
||||
m_sequence(0)
|
||||
m_sequence(0),
|
||||
m_lastmodtime(0)
|
||||
{}
|
||||
|
||||
/** the ID used by WebDAVSource */
|
||||
|
@ -91,6 +92,9 @@ class CalDAVSource : public WebDAVSource,
|
|||
/** maximum sequence number of any sub item */
|
||||
long m_sequence;
|
||||
|
||||
/** maximum modification time of any sub item */
|
||||
time_t m_lastmodtime;
|
||||
|
||||
/**
|
||||
* the list of simplified RECURRENCE-IDs (without time zone,
|
||||
* see icalTime2Str()), empty string for VEVENT without
|
||||
|
|
|
@ -75,14 +75,20 @@ class Settings {
|
|||
* if true, then manipulate SEQUENCE and LAST-MODIFIED properties
|
||||
* so that Google CalDAV server accepts updates
|
||||
*/
|
||||
virtual bool googleUpdateHack() = 0;
|
||||
virtual bool googleUpdateHack() const = 0;
|
||||
|
||||
/**
|
||||
* if true, then avoid RECURRENCE-ID in sub items without
|
||||
* corresponding parent by replacing it with
|
||||
* X-SYNCEVOLUTION-RECURRENCE-ID
|
||||
*/
|
||||
virtual bool googleChildHack() = 0;
|
||||
virtual bool googleChildHack() const = 0;
|
||||
|
||||
/**
|
||||
* if true, then check whether server has added an unwanted alarm
|
||||
* and resend to get rid of it
|
||||
*/
|
||||
virtual bool googleAlarmHack() const = 0;
|
||||
|
||||
/**
|
||||
* use this to create a boost_shared pointer for a
|
||||
|
|
|
@ -24,11 +24,14 @@ class ContextSettings : public Neon::Settings {
|
|||
std::string m_url;
|
||||
bool m_googleUpdateHack;
|
||||
bool m_googleChildHack;
|
||||
|
||||
bool m_googleAlarmHack;
|
||||
|
||||
public:
|
||||
ContextSettings(const boost::shared_ptr<const SyncConfig> &context) :
|
||||
m_context(context)
|
||||
m_context(context),
|
||||
m_googleUpdateHack(false),
|
||||
m_googleChildHack(false),
|
||||
m_googleAlarmHack(false)
|
||||
{
|
||||
if (m_context) {
|
||||
vector<string> urls = m_context->getSyncURL();
|
||||
|
@ -55,6 +58,12 @@ public:
|
|||
m_googleUpdateHack = true;
|
||||
} else if (boost::iequals(*flag, "ChildHack")) {
|
||||
m_googleChildHack = true;
|
||||
} else if (boost::iequals(*flag, "AlarmHack")) {
|
||||
m_googleAlarmHack = true;
|
||||
} else if (boost::iequals(*flag, "Google")) {
|
||||
m_googleUpdateHack =
|
||||
m_googleChildHack =
|
||||
m_googleAlarmHack = true;
|
||||
} else {
|
||||
SE_THROW(StringPrintf("unknown SyncEvolution flag %s in URL %s",
|
||||
std::string(flag->begin(), flag->end()).c_str(),
|
||||
|
@ -92,8 +101,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual bool googleUpdateHack() { return m_googleUpdateHack; }
|
||||
virtual bool googleChildHack() { return m_googleChildHack; }
|
||||
virtual bool googleUpdateHack() const { return m_googleUpdateHack; }
|
||||
virtual bool googleChildHack() const { return m_googleChildHack; }
|
||||
virtual bool googleAlarmHack() const { return m_googleChildHack; }
|
||||
|
||||
virtual void getCredentials(const std::string &realm,
|
||||
std::string &username,
|
||||
|
|
Loading…
Reference in a new issue