Merge remote branch 'origin/syncevolution-1-2-branch'
Conflicts: configure.ac test/ClientTest.cpp test/testcases/eds_event.ics.funambol.tem.patch Conflicts because of version number and updated test cases resp. local delete optimization. ActiveSync backend had to be adapted to modified InsertItemResult: now it requests a merge when it detects duplicates, like the CalDAV backend already did on the 1.2 branch.
This commit is contained in:
commit
a7f7c8eacf
301
NEWS
301
NEWS
|
@ -1,3 +1,304 @@
|
|||
SyncEvolution 1.1.1 -> 1.2, 13.10.2011
|
||||
======================================
|
||||
|
||||
The major new feature of the 1.2 release is support for non-SyncML
|
||||
protocols in general and CalDAV/CardDAV in particular. ActiveSync
|
||||
support is in development and will be in 1.3. These protocols are
|
||||
implemented as backends which are combined with other backends by
|
||||
SyncEvolution in a so called "local sync". The GTK sync-ui does not
|
||||
yet support configuring non-SyncML protocols. See the README.rst and
|
||||
man page for more information on how to use the new feature via the
|
||||
command line.
|
||||
|
||||
Properties not supported by SyncML servers can now be preserved
|
||||
locally in two-way synchronization (BMC #15030). This depends on
|
||||
information about what properties a SyncML server supports ("CtCap"),
|
||||
which is typically not provided by servers. SyncEvolution contains a
|
||||
copy of that information for Google Contacts (BMC #15029).
|
||||
|
||||
Akonadi backend and KWallet support were merged. They are not included
|
||||
yet in syncevolution.org binaries. To use them compile from source.
|
||||
|
||||
The configuration format was updated to solve a conceptual problem
|
||||
inherited with the legacy property names: the "type" property had
|
||||
multiple, sometimes conflicting roles. For example, setting the
|
||||
preferred data format for sync with one peer might have changed the
|
||||
backend selection for some other peer (BMC #1023). Now
|
||||
"backend/databaseFormat/syncFormat/forceSyncFormat" replace
|
||||
"type". "type" is still accepted by the command line as alias.
|
||||
|
||||
Upgrading from releases before 1.2:
|
||||
|
||||
Old configurations can still be read. But writing, as it happens
|
||||
during a sync, must migrate the configuration first. Release 1.2
|
||||
automatically migrates configurations. The old configurations
|
||||
will still be available (see "syncevolution --print-configs") but must
|
||||
be renamed manually to use them again under their original names with
|
||||
older SyncEvolution releases.
|
||||
|
||||
|
||||
Other changes:
|
||||
|
||||
* Using the --sync-property and --source-property command line options is
|
||||
optional, just specifying the property assignment is enough.
|
||||
|
||||
* syncevo-http-server was enhanced considerably. See http://syncevolution.org/wiki/http-server-howto
|
||||
|
||||
* support NetworkManager API >= 0.9 (BMC #19470)
|
||||
|
||||
* syncevolution.org binaries: now compatible with Debian Testing/libnotify.so.4 (BMC #22668)
|
||||
|
||||
libnotify is not linked directly into syncevo-dbus-server in the
|
||||
syncevolution.org binaries. Instead libnotify.so.1 till .so.4
|
||||
(current Debian Testing) are opened opened dynamically and the
|
||||
necessary functions are looked up via dlsym(). Not finding the
|
||||
libraries or the functions silently disables this notification
|
||||
mechanism.
|
||||
|
||||
* Sync mode is recorded when running in SyncML server mode (BMC #2786).
|
||||
|
||||
* syncevo-dbus-server automatically stops when some of its libraries
|
||||
are updated and restarts if auto-syncing is on (BMC #14955).
|
||||
|
||||
* Added code for Buteo, mKCal and QtContacts in MeeGo.
|
||||
|
||||
Buteo and mKCal were removed again from MeeGo, so the code
|
||||
is obsolete. The QtContacts backend may be still be useful
|
||||
to access items via that API, but for syncing on MeeGo
|
||||
the normal EDS backend is used since MeeGo reverted back
|
||||
to EDS as PIM storage.
|
||||
|
||||
* "databasePassword" source property: lookup failure in keyring (BMC #22937)
|
||||
|
||||
The databasePassword also wasn't looked up at all when doing item operations
|
||||
via the command line.
|
||||
|
||||
When configuring sources for an HTTP server, the config name typically
|
||||
is just the context (@foo). When using the config in the HTTP server,
|
||||
the config name is the peer inside that context (client@foo). Because
|
||||
the GNOME keyring lookup keys for the "databasePassword" (more
|
||||
specifically, the object name) contained the full config name which
|
||||
was different in both cases, looking up the saved password failed.
|
||||
|
||||
The solution is to normalize the config name (to accomodate for
|
||||
different ways of spelling it) and use only the context, with @ as
|
||||
before. This will break existing setups where the object name in the
|
||||
keyring (incorrectly) includes the full config name. In that case just
|
||||
configure the source again to set the password anew.
|
||||
|
||||
* Evolution Calendar: fixed detached recurrence support (BMC #22940)
|
||||
|
||||
When manipulating a meeting series with more than one detached
|
||||
recurrence certain sequences of operations could incorrectly fail
|
||||
with "UID already exists".
|
||||
|
||||
* iCalendar 2.0: must set VALUE in EXDATE (part of BMC #22940)
|
||||
|
||||
EXDATE has a VALUE parameter, which wasn't defined in the XML
|
||||
profile. Didn't seem to matter at all in practice, but wasn't
|
||||
standard-compliant.
|
||||
|
||||
* GTK sync-ui: wrap sync service descriptions (BMC #7199)
|
||||
|
||||
Descriptions of different sync services are not fully visible unless
|
||||
word-wrapping gets enabled.
|
||||
|
||||
* CalDAV/CardDAV + local storage: avoid empty properties
|
||||
|
||||
The main motivation for this change is that a recent Apple Calendar
|
||||
server rejects vCards with empty BDAY property. Another reason is that
|
||||
keeping the data as small as possible is desirable by itself.
|
||||
|
||||
Sending an empty property serves as a hint for the peer that the
|
||||
property is supported. This is not necessary when storing an item in a
|
||||
backend. Therefore this commit disables empty properties for all
|
||||
backends which do not themselves set the m_backendRule Synthesis info
|
||||
value.
|
||||
|
||||
* Google Contacts: ensure that first/middle/name are set when storing in EDS (BMC #20864)
|
||||
|
||||
Evolution and the MeeGo UX assume that first/middle/last name are set.
|
||||
That is not the case when a contact is created in the Google Contacts
|
||||
web interface. Such contacts are sent by Google without the N
|
||||
property.
|
||||
|
||||
SyncEvolution now tries to recreate the name components from the FN
|
||||
string, by splitting at word boundaries and assuming "<first>
|
||||
<middle> <last>" or "<last>, <first>" format. Obviously this
|
||||
heuristic fails for some locales.
|
||||
|
||||
* Evolution Calendar: fixed error handling for broken TZIDs
|
||||
|
||||
* Sony Ericsson: use ISO-8859-1 for all devices (BMC #14414)
|
||||
|
||||
Passing invalid UTF-8 strings into libecal caused glib to
|
||||
abort syncevo-dbus-server.
|
||||
|
||||
* auto sync: show all failed syncs except for temporary network errors (BMC #21888)
|
||||
|
||||
Notifications were meant to be shown for all errors except temporary
|
||||
ones. This has never been implemented correctly since the feature was
|
||||
introduced: instead of hiding known temporary errors, all errors except
|
||||
500 (fatal error) were suppressed.
|
||||
|
||||
* vCard: inline local photo data (BMC #19661)
|
||||
|
||||
Some platforms (Maemo, MeeGo) store photos in separate files. Now SyncEvolution
|
||||
efficiently includes that photo data in the generated vCard right before sending
|
||||
it to a peer; previously it sent a useless local file:// URI. The Maemo port
|
||||
has a less efficient workaround for that which now should be obsolete.
|
||||
|
||||
* syncevo-dbus-server: online status wrong without Network Manager or ConnMan (BMC #21543)
|
||||
|
||||
When neither Network Manager nor ConnMan are running, network presence was "not
|
||||
online". This prevented running automatic syncs.
|
||||
|
||||
|
||||
For developers:
|
||||
|
||||
* modified backend API
|
||||
- ClientTestConfig modernized
|
||||
- InsertItemResult::m_merged turned from boolean to enum
|
||||
|
||||
* testing and compilation changes; for example, the minimum version of
|
||||
libsynthesis is now checked at configure time instead of failing at
|
||||
runtime due to missing features in the Synthesis engine
|
||||
|
||||
|
||||
SyncEvolution 1.1.99.7 -> 1.2, 13.10.2011
|
||||
=========================================
|
||||
|
||||
Some more bug fixes and testing improvements.
|
||||
|
||||
* fixed potential invalid memory access in add<->add conflict handling
|
||||
* fixed memory leak in workaround for EDS bug
|
||||
* CalDAV/CardDAV: handle ETags without quotation marks (eGroupware)
|
||||
* updated README: warning about sync direction moved to --sync option
|
||||
|
||||
|
||||
SyncEvolution 1.1.99.6 -> 1.1.99.7, 15.09.2011
|
||||
==============================================
|
||||
|
||||
Mostly bug fixes again. Some are a bit more intrusive, thus another
|
||||
pre-release.
|
||||
|
||||
* syncevolution.org binaries: now compatible with Debian Testing/libnotify.so.4 (BMC #22668)
|
||||
|
||||
libnotify is not linked directly into syncevo-dbus-server in the
|
||||
syncevolution.org binaries. Instead libnotify.so.1 till .so.4
|
||||
(current Debian Testing) are opened opened dynamically and the
|
||||
necessary functions are looked up via dlsym(). Not finding the
|
||||
libraries or the functions silently disables this notification
|
||||
mechanism.
|
||||
|
||||
* calendar sync: better handling for add<->add conflicts (partly fixes BMC #22783)
|
||||
|
||||
When both sides of a sync have added the same event, the sync must
|
||||
determine which one is more recent instead of blindly overwriting
|
||||
always the same side. Such conflicts are typically rare except for
|
||||
enterprise scenarios where meeting invitiations are processed
|
||||
automatically by a groupware (Exchange, Google Calendar/Mail, ...)
|
||||
and then the attendee status is updated on one side.
|
||||
|
||||
SyncEvolution now does the necessary age comparison and preserves the more
|
||||
recent data for most properties. In some properties the data from both
|
||||
sides is preserved by concatenating the text (description, location, ...).
|
||||
It remains to be seen whether that is really desirable. Also, sync statistics
|
||||
are slightly off: the incoming item is counted as "added" even though it
|
||||
gets turned into an update.
|
||||
|
||||
* item operations: authentication problem for WebDAV when using keyring (BMC #21311)
|
||||
|
||||
The password still wasn't looked up in the keyring when using
|
||||
--import/export/delete-items.
|
||||
|
||||
* "databasePassword" source property: lookup failure in keyring (BMC #22937)
|
||||
|
||||
The databasePassword also wasn't looked up at all when doing item operations
|
||||
via the command line.
|
||||
|
||||
When configuring sources for an HTTP server, the config name typically
|
||||
is just the context (@foo). When using the config in the HTTP server,
|
||||
the config name is the peer inside that context (client@foo). Because
|
||||
the GNOME keyring lookup keys for the "databasePassword" (more
|
||||
specifically, the object name) contained the full config name which
|
||||
was different in both cases, looking up the saved password failed.
|
||||
|
||||
The solution is to normalize the config name (to accomodate for
|
||||
different ways of spelling it) and use only the context, with @ as
|
||||
before. This will break existing setups where the object name in the
|
||||
keyring (incorrectly) includes the full config name. In that case just
|
||||
configure the source again to set the password anew.
|
||||
|
||||
* Evolution Calendar: fixed detached recurrence support (BMC #22940)
|
||||
|
||||
When manipulating a meeting series with more than one detached
|
||||
recurrence certain sequences of operations could incorrectly fail
|
||||
with "UID already exists".
|
||||
|
||||
* iCalendar 2.0: must set VALUE in EXDATE (part of BMC #22940)
|
||||
|
||||
EXDATE has a VALUE parameter, which wasn't defined in the XML
|
||||
profile. Didn't seem to matter at all in practice, but wasn't
|
||||
standard-compliant.
|
||||
|
||||
* GTK sync-ui: wrap sync service descriptions (BMC #7199)
|
||||
|
||||
Descriptions of different sync services are not fully visible unless
|
||||
word-wrapping gets enabled.
|
||||
|
||||
* source configs: don't check "backend" unless it is needed
|
||||
|
||||
When using a config which has sources with a backend type set which is
|
||||
not currently available, an error was thrown even if those sources
|
||||
weren't even part of the current operation (for example, syncing
|
||||
another source which is currently supported).
|
||||
|
||||
* config migration: avoid name conflicts and auto syncing of old configs (BMC #22691)
|
||||
|
||||
When (auto-)migrating a config, it was possible that a name for the
|
||||
peer, say foo.old, was chosen for the renamed config although there
|
||||
was already such a config, for example foo.old in ~/.sync4j. Besides
|
||||
being confusing for users, this also led to a bug in the code where it
|
||||
copied from the older config with the foo.old name.
|
||||
|
||||
The main problem fixed is the disabling of auto syncing
|
||||
in the old config. Otherwise it was still used by syncevo-dbus-server
|
||||
for syncing, which triggered another auto-migration, ad infinitum...
|
||||
|
||||
* auto syncing: must check whether enabled when looking at unknown URLs (part of BMC #22691)
|
||||
|
||||
"syncURL = insert your URL here" with "autoSync = 0" did lead to auto
|
||||
sync attempts although it wasn't enabled. A check for "auto syncing
|
||||
enabled" was missing for the "unknown transport" case.
|
||||
|
||||
* CalDAV/CardDAV + local storage: avoid empty properties
|
||||
|
||||
The main motivation for this change is that a recent Apple Calendar
|
||||
server rejects vCards with empty BDAY property. Another reason is that
|
||||
keeping the data as small as possible is desirable by itself.
|
||||
|
||||
Sending an empty property serves as a hint for the peer that the
|
||||
property is supported. This is not necessary when storing an item in a
|
||||
backend. Therefore this commit disables empty properties for all
|
||||
backends which do not themselves set the m_backendRule Synthesis info
|
||||
value.
|
||||
|
||||
* Apple CardDAV: apply PHOTO import/export scripts by default
|
||||
|
||||
A recent Apple Calendar server (correctly) rejects the invalid
|
||||
PHOTO;TYPE=unknown: property in a vCard. This internal representation
|
||||
must be cleared before serializing the field list.
|
||||
|
||||
* for developers: modified backend API
|
||||
- ClientTestConfig modernized
|
||||
- InsertItemResult::m_merged turned from boolean to enum
|
||||
|
||||
* testing and compilation changes; for example, the minimum version of
|
||||
libsynthesis is now checked at configure time instead of failing at
|
||||
runtime due to missing features in the Synthesis engine
|
||||
|
||||
|
||||
SyncEvolution 1.1.99.5 -> 1.1.99.6, 17.08.2011
|
||||
==============================================
|
||||
|
||||
|
|
16
README.rst
16
README.rst
|
@ -274,6 +274,15 @@ a list of valid values.
|
|||
for a `refresh-from-server` or `refresh-from-client` sync which
|
||||
clears all data at one end and copies all items from the other.
|
||||
|
||||
**Warning:** in local sync (CalDAV/CardDAV/ActiveSync, ...) and
|
||||
direct sync with a phone, the sync is started by the side which acts
|
||||
as server. Therefore the ``from-server`` variants
|
||||
(``one-way-from-server``, ``refresh-from-server``) transfer data
|
||||
from the sync config into the target config (see "Synchronization
|
||||
beyond SyncML" below) resp. to a phone. The ``from-client`` variants
|
||||
transfer in the other direction, even if the target config happens
|
||||
to access data on a remote server.
|
||||
|
||||
--print-servers|--print-configs|--print-peers
|
||||
Prints the names of all configured peers to stdout. There is no
|
||||
difference between these options, the are just aliases.
|
||||
|
@ -661,13 +670,6 @@ source in the target config. The ``sync`` property in the sync config
|
|||
defines the direction of the data flow. It can be set temporarily when
|
||||
starting a synchronzation with the sync config.
|
||||
|
||||
**Warning:** in local sync, the sync config side acts as
|
||||
server. Therefore the ``from-server`` variants
|
||||
(``one-way-from-server``, ``refresh-from-server``) transfer data
|
||||
from the sync config into the target config. The ``from-client``
|
||||
variants transfer in the other direction, even if the target config
|
||||
happens to access data on a remote server.
|
||||
|
||||
**Warning:** because the client in the local sync starts the sync,
|
||||
``preventSlowSync=0`` must be set in the target config to have an effect.
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ dnl Invoke autogen.sh to produce a configure script.
|
|||
#
|
||||
# Starting with the 1.1 release cycle, the rpm-style
|
||||
# .99 pseudo-version number is used to mark a pre-release.
|
||||
AC_INIT([syncevolution], [m4_esyscmd([build/gen-git-version.sh 1.1.99.6])])
|
||||
AC_INIT([syncevolution], [m4_esyscmd([build/gen-git-version.sh 1.2])])
|
||||
# STABLE_VERSION=1.0.1+
|
||||
AC_SUBST(STABLE_VERSION)
|
||||
|
||||
|
@ -25,7 +25,7 @@ SE_CHECK_FOR_STABLE_RELEASE
|
|||
|
||||
# Minimum version of libsynthesis as defined in its
|
||||
# configure script and thus .pc files:
|
||||
define([SYNTHESIS_MIN_VERSION], [3.4.0.16.1])
|
||||
define([SYNTHESIS_MIN_VERSION], [3.4.0.16.4])
|
||||
|
||||
# Line above is patched by gen-autotools.sh. Handle
|
||||
# both "yes" and "no".
|
||||
|
|
|
@ -417,7 +417,7 @@ SyncSourceRaw::InsertItemResult ActiveSyncCalendarSource::insertItem(const std::
|
|||
}
|
||||
}
|
||||
|
||||
bool merged = false;
|
||||
InsertItemResultState state;
|
||||
if (easid.empty()) {
|
||||
// New VEVENT; should not be part of an existing merged item
|
||||
// ("meeting series").
|
||||
|
@ -430,7 +430,7 @@ SyncSourceRaw::InsertItemResult ActiveSyncCalendarSource::insertItem(const std::
|
|||
Event &event = loadItem(*it->second);
|
||||
if (event.m_subids.find(subid) != event.m_subids.end()) {
|
||||
// was already in that item but caller didn't seem to know
|
||||
merged = true;
|
||||
state = ITEM_MERGED;
|
||||
} else {
|
||||
// add to merged item
|
||||
event.m_subids.insert(subid);
|
||||
|
@ -452,10 +452,11 @@ SyncSourceRaw::InsertItemResult ActiveSyncCalendarSource::insertItem(const std::
|
|||
Event &event = findItem(easid);
|
||||
if (event.m_subids.size() == 1 &&
|
||||
*event.m_subids.begin() == subid) {
|
||||
// special case: no need to load old data, replace it outright
|
||||
// special case: no need to load old data, replace or request merge immediately
|
||||
event.m_calendar = newEvent->m_calendar;
|
||||
if (easid != callerEasID) {
|
||||
merged = true;
|
||||
state = ITEM_NEEDS_MERGE;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
// populate event
|
||||
|
@ -472,13 +473,13 @@ SyncSourceRaw::InsertItemResult ActiveSyncCalendarSource::insertItem(const std::
|
|||
}
|
||||
}
|
||||
if (easid != callerEasID) {
|
||||
// caller didn't know final UID: if found, then tell him that
|
||||
// we merged the item for him, if not, then don't complain about
|
||||
// caller didn't know final UID: if found, then tell him to
|
||||
// merge the items, if not, then don't complain about
|
||||
// it not being found (like we do when the item should exist
|
||||
// but doesn't)
|
||||
if (removeme) {
|
||||
merged = true;
|
||||
icalcomponent_remove_component(event.m_calendar, removeme);
|
||||
state = ITEM_NEEDS_MERGE;
|
||||
goto done;
|
||||
} else {
|
||||
event.m_subids.insert(subid);
|
||||
}
|
||||
|
@ -501,15 +502,16 @@ SyncSourceRaw::InsertItemResult ActiveSyncCalendarSource::insertItem(const std::
|
|||
|
||||
// TODO: avoid updating item on server immediately?
|
||||
InsertItemResult res = ActiveSyncSource::insertItem(event.m_easid, data);
|
||||
if (res.m_merged ||
|
||||
if (res.m_state == ITEM_MERGED ||
|
||||
res.m_luid != event.m_easid) {
|
||||
// should not merge with anything, if so, our cache was invalid
|
||||
SE_THROW("CalDAV item not updated as expected");
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return SyncSourceRaw::InsertItemResult(createLUID(easid, subid),
|
||||
"", merged);
|
||||
"", state);
|
||||
}
|
||||
|
||||
void ActiveSyncCalendarSource::readItem(const std::string &luid, std::string &item)
|
||||
|
@ -599,7 +601,7 @@ void ActiveSyncCalendarSource::deleteItem(const string &luid)
|
|||
// TODO: avoid updating the item immediately
|
||||
eptr<char> icalstr(ical_strdup(icalcomponent_as_ical_string(event.m_calendar)));
|
||||
InsertItemResult res = ActiveSyncSource::insertItem(easid, icalstr.get());
|
||||
if (res.m_merged ||
|
||||
if (res.m_state != ITEM_OKAY ||
|
||||
res.m_luid != easid) {
|
||||
SE_THROW("unexpected result of removing sub event");
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ TrackingSyncSource::InsertItemResult AkonadiSyncSource::insertItem(const std::st
|
|||
ItemCreateJob *createJob = new ItemCreateJob(item, m_collection);
|
||||
if (!createJob->exec()) {
|
||||
throwError(string("storing new item ") + luid);
|
||||
return InsertItemResult("", "", false);
|
||||
return InsertItemResult("", "", ITEM_OKAY);
|
||||
}
|
||||
item = createJob->item();
|
||||
} else {
|
||||
|
@ -215,7 +215,7 @@ TrackingSyncSource::InsertItemResult AkonadiSyncSource::insertItem(const std::st
|
|||
// TODO: check that the item has not been updated in the meantime
|
||||
if (!modifyJob->exec()) {
|
||||
throwError(string("updating item ") + luid);
|
||||
return InsertItemResult("", "", false);
|
||||
return InsertItemResult("", "", ITEM_OKAY);
|
||||
}
|
||||
item = modifyJob->item();
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ TrackingSyncSource::InsertItemResult AkonadiSyncSource::insertItem(const std::st
|
|||
// above will take care of this
|
||||
return InsertItemResult(QByteArray::number(item.id()).constData(),
|
||||
QByteArray::number(item.revision()).constData(),
|
||||
false);
|
||||
ITEM_OKAY);
|
||||
}
|
||||
|
||||
void AkonadiSyncSource::removeItem(const string &luid)
|
||||
|
|
|
@ -56,6 +56,32 @@ class unrefECalObjectList {
|
|||
}
|
||||
};
|
||||
|
||||
bool EvolutionCalendarSource::LUIDs::containsLUID(const ItemID &id) const
|
||||
{
|
||||
const_iterator it = findUID(id.m_uid);
|
||||
return it != end() &&
|
||||
it->second.find(id.m_rid) != it->second.end();
|
||||
}
|
||||
|
||||
void EvolutionCalendarSource::LUIDs::insertLUID(const ItemID &id)
|
||||
{
|
||||
(*this)[id.m_uid].insert(id.m_rid);
|
||||
}
|
||||
|
||||
void EvolutionCalendarSource::LUIDs::eraseLUID(const ItemID &id)
|
||||
{
|
||||
iterator it = find(id.m_uid);
|
||||
if (it != end()) {
|
||||
set<string>::iterator it2 = it->second.find(id.m_rid);
|
||||
if (it2 != it->second.end()) {
|
||||
it->second.erase(it2);
|
||||
if (it->second.empty()) {
|
||||
erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int granularity()
|
||||
{
|
||||
// This long delay is necessary in combination
|
||||
|
@ -254,7 +280,7 @@ void EvolutionCalendarSource::listAllItems(RevisionMap_t &revisions)
|
|||
string luid = id.getLUID();
|
||||
string modTime = getItemModTime(ecomp);
|
||||
|
||||
m_allLUIDs.insert(luid);
|
||||
m_allLUIDs.insertLUID(id);
|
||||
revisions[luid] = modTime;
|
||||
nextItem = nextItem->next;
|
||||
}
|
||||
|
@ -274,7 +300,7 @@ void EvolutionCalendarSource::readItem(const string &luid, std::string &item, bo
|
|||
EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(const string &luid, const std::string &item, bool raw)
|
||||
{
|
||||
bool update = !luid.empty();
|
||||
bool merged = false;
|
||||
InsertItemResultState state = ITEM_OKAY;
|
||||
bool detached = false;
|
||||
string newluid = luid;
|
||||
string data = item;
|
||||
|
@ -389,14 +415,14 @@ EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(co
|
|||
// gets used twice during a sync (examples: add + add, delete + add),
|
||||
// which should never happen.
|
||||
newluid = id.getLUID();
|
||||
if (m_allLUIDs.find(newluid) != m_allLUIDs.end()) {
|
||||
merged = true;
|
||||
if (m_allLUIDs.containsLUID(id)) {
|
||||
state = ITEM_NEEDS_MERGE;
|
||||
} else {
|
||||
// if this is a detached recurrence, then we
|
||||
// must use e_cal_modify_object() below if
|
||||
// the parent already exists
|
||||
// the parent or any other child already exists
|
||||
if (!id.m_rid.empty() &&
|
||||
m_allLUIDs.find(ItemID::getLUID(id.m_uid, "")) != m_allLUIDs.end()) {
|
||||
m_allLUIDs.containsUID(id.m_uid)) {
|
||||
detached = true;
|
||||
} else {
|
||||
// Creating the parent while children are already in
|
||||
|
@ -420,7 +446,7 @@ EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(co
|
|||
ItemID newid(!id.m_uid.empty() ? id.m_uid : uid, id.m_rid);
|
||||
newluid = newid.getLUID();
|
||||
modTime = getItemModTime(newid);
|
||||
m_allLUIDs.insert(newluid);
|
||||
m_allLUIDs.insertLUID(newid);
|
||||
} else {
|
||||
throwError("storing new item", gerror);
|
||||
}
|
||||
|
@ -438,7 +464,7 @@ EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(co
|
|||
}
|
||||
}
|
||||
|
||||
if (update || merged || detached) {
|
||||
if (update || state != ITEM_NEEDS_MERGE || detached) {
|
||||
ItemID id(newluid);
|
||||
bool isParent = id.m_rid.empty();
|
||||
|
||||
|
@ -475,11 +501,13 @@ EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(co
|
|||
// Therefore we have to use CALOBJ_MOD_ALL, but that removes
|
||||
// children.
|
||||
bool hasChildren = false;
|
||||
BOOST_FOREACH(ItemID existingId, m_allLUIDs) {
|
||||
if (existingId.m_uid == id.m_uid &&
|
||||
existingId.m_rid.size()) {
|
||||
hasChildren = true;
|
||||
break;
|
||||
LUIDs::const_iterator it = m_allLUIDs.find(id.m_uid);
|
||||
if (it != m_allLUIDs.end()) {
|
||||
BOOST_FOREACH(const string &rid, it->second) {
|
||||
if (!rid.empty()) {
|
||||
hasChildren = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,17 +554,17 @@ EvolutionCalendarSource::InsertItemResult EvolutionCalendarSource::insertItem(co
|
|||
modTime = getItemModTime(newid);
|
||||
}
|
||||
|
||||
return InsertItemResult(newluid, modTime, merged);
|
||||
return InsertItemResult(newluid, modTime, state);
|
||||
}
|
||||
|
||||
EvolutionCalendarSource::ICalComps_t EvolutionCalendarSource::removeEvents(const string &uid, bool returnOnlyChildren)
|
||||
{
|
||||
ICalComps_t events;
|
||||
|
||||
BOOST_FOREACH(const string &luid, m_allLUIDs) {
|
||||
ItemID id(luid);
|
||||
|
||||
if (id.m_uid == uid) {
|
||||
LUIDs::const_iterator it = m_allLUIDs.find(uid);
|
||||
if (it != m_allLUIDs.end()) {
|
||||
BOOST_FOREACH(const string &rid, it->second) {
|
||||
ItemID id(uid, rid);
|
||||
icalcomponent *icomp = retrieveItem(id);
|
||||
if (icomp) {
|
||||
if (id.m_rid.empty() && returnOnlyChildren) {
|
||||
|
@ -582,11 +610,21 @@ void EvolutionCalendarSource::removeItem(const string &luid)
|
|||
ICalComps_t children = removeEvents(id.m_uid, true);
|
||||
|
||||
// recreate children
|
||||
bool first = true;
|
||||
BOOST_FOREACH(boost::shared_ptr< eptr<icalcomponent> > &icalcomp, children) {
|
||||
char *uid;
|
||||
if (first) {
|
||||
char *uid;
|
||||
|
||||
if (!e_cal_create_object(m_calendar, *icalcomp, &uid, &gerror)) {
|
||||
throwError(string("recreating item ") + luid, gerror);
|
||||
if (!e_cal_create_object(m_calendar, *icalcomp, &uid, &gerror)) {
|
||||
throwError(string("recreating first item ") + luid, gerror);
|
||||
}
|
||||
first = false;
|
||||
} else {
|
||||
if (!e_cal_modify_object(m_calendar, *icalcomp,
|
||||
CALOBJ_MOD_THIS,
|
||||
&gerror)) {
|
||||
throwError(string("recreating following item ") + luid, gerror);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(!e_cal_remove_object_with_mod(m_calendar,
|
||||
|
@ -603,7 +641,7 @@ void EvolutionCalendarSource::removeItem(const string &luid)
|
|||
throwError(string("deleting item " ) + luid, gerror);
|
||||
}
|
||||
}
|
||||
m_allLUIDs.erase(luid);
|
||||
m_allLUIDs.eraseLUID(id);
|
||||
|
||||
if (!id.m_rid.empty()) {
|
||||
// Removing the child may have modified the parent.
|
||||
|
@ -642,8 +680,21 @@ icalcomponent *EvolutionCalendarSource::retrieveItem(const ItemID &id)
|
|||
if (!comp) {
|
||||
throwError(string("retrieving item: ") + id.getLUID());
|
||||
}
|
||||
eptr<icalcomponent> ptr(comp);
|
||||
|
||||
return comp;
|
||||
/*
|
||||
* EDS bug: if a parent doesn't exist while a child does, and we ask
|
||||
* for the parent, we are sent the (first?) child. Detect this and
|
||||
* turn it into a "not found" error.
|
||||
*/
|
||||
if (id.m_rid.empty()) {
|
||||
struct icaltimetype rid = icalcomponent_get_recurrenceid(comp);
|
||||
if (!icaltime_is_null_time(rid)) {
|
||||
throwError(string("retrieving item: got child instead of parent: ") + id.m_uid);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr.release();
|
||||
}
|
||||
|
||||
string EvolutionCalendarSource::retrieveItemAsString(const ItemID &id)
|
||||
|
|
|
@ -179,7 +179,15 @@ class EvolutionCalendarSource : public EvolutionSyncSource,
|
|||
* implemented without the troublesome querying of the EDS
|
||||
* backend.
|
||||
*/
|
||||
set<string> m_allLUIDs;
|
||||
class LUIDs : public map< string, set<string> > {
|
||||
public:
|
||||
bool containsUID(const std::string &uid) const { return findUID(uid) != end(); }
|
||||
const_iterator findUID(const std::string &uid) const { return find(uid); }
|
||||
|
||||
bool containsLUID(const ItemID &id) const;
|
||||
void insertLUID(const ItemID &id);
|
||||
void eraseLUID(const ItemID &id);
|
||||
} m_allLUIDs;
|
||||
|
||||
/**
|
||||
* A list of ref-counted smart pointers to icalcomponents.
|
||||
|
|
|
@ -330,7 +330,7 @@ EvolutionContactSource::insertItem(const string &uid, const std::string &item, b
|
|||
throwError("no UID for contact");
|
||||
}
|
||||
string newrev = getRevision(newuid);
|
||||
return InsertItemResult(newuid, newrev, false);
|
||||
return InsertItemResult(newuid, newrev, ITEM_OKAY);
|
||||
} else {
|
||||
throwError(uid.empty() ?
|
||||
"storing new contact" :
|
||||
|
@ -341,7 +341,7 @@ EvolutionContactSource::insertItem(const string &uid, const std::string &item, b
|
|||
throwError(string("failure parsing vcard " ) + item);
|
||||
}
|
||||
// not reached!
|
||||
return InsertItemResult("", "", false);
|
||||
return InsertItemResult("", "", ITEM_OKAY);
|
||||
}
|
||||
|
||||
void EvolutionContactSource::removeItem(const string &uid)
|
||||
|
|
|
@ -122,7 +122,7 @@ EvolutionCalendarSource::InsertItemResult EvolutionMemoSource::insertItem(const
|
|||
}
|
||||
|
||||
bool update = !luid.empty();
|
||||
bool merged = false;
|
||||
InsertItemResultState state = ITEM_OKAY;
|
||||
string newluid = luid;
|
||||
string modTime;
|
||||
|
||||
|
@ -170,28 +170,31 @@ EvolutionCalendarSource::InsertItemResult EvolutionMemoSource::insertItem(const
|
|||
}
|
||||
|
||||
GError *gerror = NULL;
|
||||
char *uid = NULL;
|
||||
|
||||
if (!update) {
|
||||
if(!e_cal_create_object(m_calendar, subcomp, &uid, &gerror)) {
|
||||
const char *uid = NULL;
|
||||
|
||||
if(!e_cal_create_object(m_calendar, subcomp, (char **)&uid, &gerror)) {
|
||||
if (gerror->domain == E_CALENDAR_ERROR &&
|
||||
gerror->code == E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS) {
|
||||
// Deal with error due to adding already existing item.
|
||||
// Should never happen for plain text journal entries because
|
||||
// they have no embedded ID, but who knows...
|
||||
merged = true;
|
||||
state = ITEM_NEEDS_MERGE;
|
||||
uid = icalcomponent_get_uid(subcomp);
|
||||
if (!uid) {
|
||||
throwError("storing new memo item, no UID set", gerror);
|
||||
}
|
||||
g_clear_error(&gerror);
|
||||
} else {
|
||||
throwError( "storing new memo item", gerror );
|
||||
}
|
||||
} else {
|
||||
ItemID id(uid, "");
|
||||
newluid = id.getLUID();
|
||||
}
|
||||
ItemID id(uid, "");
|
||||
newluid = id.getLUID();
|
||||
if (state != ITEM_NEEDS_MERGE) {
|
||||
modTime = getItemModTime(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (update || merged) {
|
||||
} else {
|
||||
ItemID id(newluid);
|
||||
|
||||
// ensure that the component has the right UID
|
||||
|
@ -207,7 +210,7 @@ EvolutionCalendarSource::InsertItemResult EvolutionMemoSource::insertItem(const
|
|||
modTime = getItemModTime(newid);
|
||||
}
|
||||
|
||||
return InsertItemResult(newluid, modTime, merged);
|
||||
return InsertItemResult(newluid, modTime, state);
|
||||
}
|
||||
|
||||
bool EvolutionMemoSource::isNativeType(const char *type)
|
||||
|
|
|
@ -233,7 +233,7 @@ TrackingSyncSource::InsertItemResult FileSyncSource::insertItem(const string &ui
|
|||
|
||||
return InsertItemResult(newuid,
|
||||
getATimeString(filename),
|
||||
false /* true if adding item was turned into update */);
|
||||
ITEM_OKAY);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -433,8 +433,9 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
Event &event = loadItem(*it->second);
|
||||
event.m_etag = res.m_revision;
|
||||
if (event.m_subids.find(subid) != event.m_subids.end()) {
|
||||
// was already in that item but caller didn't seem to know
|
||||
subres.m_merged = true;
|
||||
// was already in that item but caller didn't seem to know,
|
||||
// and now we replaced the data on the CalDAV server
|
||||
subres.m_state = ITEM_REPLACED;
|
||||
} else {
|
||||
// add to merged item
|
||||
event.m_subids.insert(subid);
|
||||
|
@ -548,14 +549,11 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
}
|
||||
}
|
||||
if (davLUID != luid) {
|
||||
// caller didn't know final UID: if found, the tell him that
|
||||
// we merged the item for him, if not, then don't complain about
|
||||
// it not being found (like we do when the item should exist
|
||||
// but doesn't)
|
||||
// caller didn't know final UID: if found, then tell him to
|
||||
// merge the data and try again
|
||||
if (removeme) {
|
||||
subres.m_merged = true;
|
||||
icalcomponent_remove_component(event.m_calendar, removeme);
|
||||
icalcomponent_free(removeme);
|
||||
subres.m_state = ITEM_NEEDS_MERGE;
|
||||
goto done;
|
||||
} else {
|
||||
event.m_subids.insert(subid);
|
||||
}
|
||||
|
@ -586,7 +584,7 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
try {
|
||||
SE_LOG_DEBUG(this, NULL, "updating VEVENT");
|
||||
InsertItemResult res = insertItem(event.m_DAVluid, data, true);
|
||||
if (res.m_merged ||
|
||||
if (res.m_state != ITEM_OKAY ||
|
||||
res.m_luid != event.m_DAVluid) {
|
||||
// should not merge with anything, if so, our cache was invalid
|
||||
SE_THROW("CalDAV item not updated as expected");
|
||||
|
@ -654,7 +652,7 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
eptr<char> icalstr(ical_strdup(icalcomponent_as_ical_string(event.m_calendar)));
|
||||
std::string data = icalstr.get();
|
||||
InsertItemResult res = insertItem(event.m_DAVluid, data, true);
|
||||
if (res.m_merged ||
|
||||
if (res.m_state != ITEM_OKAY ||
|
||||
res.m_luid != event.m_DAVluid) {
|
||||
// should not merge with anything, if so, our cache was invalid
|
||||
SE_THROW("CalDAV item not updated as expected");
|
||||
|
@ -667,6 +665,7 @@ SubSyncSource::SubItemResult CalDAVSource::insertSubItem(const std::string &luid
|
|||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return subres;
|
||||
}
|
||||
|
||||
|
@ -850,7 +849,7 @@ std::string CalDAVSource::removeSubItem(const string &davLUID, const std::string
|
|||
} else {
|
||||
res = insertItem(davLUID, icalstr.get(), true);
|
||||
}
|
||||
if (res.m_merged ||
|
||||
if (res.m_state != ITEM_OKAY ||
|
||||
res.m_luid != davLUID) {
|
||||
SE_THROW("unexpected result of removing sub event");
|
||||
}
|
||||
|
|
|
@ -1020,7 +1020,7 @@ TrackingSyncSource::InsertItemResult WebDAVSource::insertItem(const string &uid,
|
|||
{
|
||||
std::string new_uid;
|
||||
std::string rev;
|
||||
bool update = false; /* true if adding item was turned into update */
|
||||
InsertItemResultState state = ITEM_OKAY;
|
||||
|
||||
Timespec deadline = createDeadline(); // no resending if left empty
|
||||
m_session->startOperation("PUT", deadline);
|
||||
|
@ -1087,7 +1087,7 @@ TrackingSyncSource::InsertItemResult WebDAVSource::insertItem(const string &uid,
|
|||
SE_LOG_DEBUG(NULL, NULL, "new item mapped to %s", real_luid.c_str());
|
||||
new_uid = real_luid;
|
||||
// TODO: find a better way of detecting unexpected updates.
|
||||
// update = true;
|
||||
// state = ...
|
||||
} else if (!rev.empty()) {
|
||||
// Yahoo Contacts returns an etag, but no href. For items
|
||||
// that were really created as requested, that's okay. But
|
||||
|
@ -1114,7 +1114,7 @@ TrackingSyncSource::InsertItemResult WebDAVSource::insertItem(const string &uid,
|
|||
new_uid.c_str(),
|
||||
revisions.begin()->first.c_str());
|
||||
new_uid = revisions.begin()->first;
|
||||
update = true;
|
||||
state = ITEM_REPLACED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1182,7 +1182,7 @@ TrackingSyncSource::InsertItemResult WebDAVSource::insertItem(const string &uid,
|
|||
}
|
||||
}
|
||||
|
||||
return InsertItemResult(new_uid, rev, update);
|
||||
return InsertItemResult(new_uid, rev, state);
|
||||
}
|
||||
|
||||
std::string WebDAVSource::ETag2Rev(const std::string &etag)
|
||||
|
@ -1191,7 +1191,9 @@ std::string WebDAVSource::ETag2Rev(const std::string &etag)
|
|||
if (boost::starts_with(res, "W/")) {
|
||||
res.erase(0, 2);
|
||||
}
|
||||
if (res.size() >= 2) {
|
||||
if (res.size() >= 2 &&
|
||||
res[0] == '"' &&
|
||||
res[res.size() - 1] == '"') {
|
||||
res = res.substr(1, res.size() - 2);
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -141,7 +141,7 @@ TrackingSyncSource::InsertItemResult XMLRPCSyncSource::insertItem(const string &
|
|||
|
||||
return InsertItemResult((*it).first,
|
||||
xmlrpc_c::value_string((*it).second),
|
||||
false);
|
||||
ITEM_OKAY);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1969,6 +1969,7 @@ sync_config_widget_init (SyncConfigWidget *self)
|
|||
self->description_label = gtk_label_new ("");
|
||||
gtk_misc_set_alignment (GTK_MISC (self->description_label), 0.0, 0.5);
|
||||
gtk_widget_set_size_request (self->description_label, 700, -1);
|
||||
gtk_label_set_line_wrap (GTK_LABEL (self->description_label), TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (tmp_box), self->description_label, FALSE, FALSE, 0);
|
||||
|
||||
tmp_box = gtk_hbox_new (FALSE, 0);
|
||||
|
|
|
@ -1167,6 +1167,22 @@ bool Cmdline::run() {
|
|||
sysync::TSyError err;
|
||||
#define CHECK_ERROR(_op) if (err) { SE_THROW_EXCEPTION_STATUS(StatusException, string(source->getName()) + ": " + (_op), SyncMLStatus(err)); }
|
||||
|
||||
// acquire passwords before doing anything (interactive password
|
||||
// access not supported for the command line)
|
||||
{
|
||||
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
|
||||
BOOST_FOREACH(const ConfigProperty *prop, registry) {
|
||||
prop->checkPassword(*context, m_server, *context->getProperties());
|
||||
}
|
||||
}
|
||||
{
|
||||
ConfigPropertyRegistry ®istry = SyncSourceConfig::getRegistry();
|
||||
BOOST_FOREACH(const ConfigProperty *prop, registry) {
|
||||
prop->checkPassword(*context, m_server, *context->getProperties(),
|
||||
source->getName(), sourceNodes.getProperties());
|
||||
}
|
||||
}
|
||||
|
||||
source->open();
|
||||
const SyncSource::Operations &ops = source->getOperations();
|
||||
if (m_printItems) {
|
||||
|
@ -1176,11 +1192,6 @@ bool Cmdline::run() {
|
|||
source->throwError("reading items not supported");
|
||||
}
|
||||
|
||||
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
|
||||
BOOST_FOREACH(const ConfigProperty *prop, registry) {
|
||||
prop->checkPassword(*context, m_server, *context->getProperties());
|
||||
}
|
||||
|
||||
err = ops.m_startDataRead("", "");
|
||||
CHECK_ERROR("reading items");
|
||||
list<string> luids;
|
||||
|
|
|
@ -288,12 +288,15 @@ SyncSourceRaw::InsertItemResult MapSyncSource::insertItem(const std::string &lui
|
|||
{
|
||||
StringPair ids = splitLUID(luid);
|
||||
SubSyncSource::SubItemResult res = m_sub->insertSubItem(ids.first, ids.second, item);
|
||||
SubRevisionEntry &entry = m_revisions[res.m_mainid];
|
||||
entry.m_uid = res.m_uid;
|
||||
entry.m_revision = res.m_revision;
|
||||
entry.m_subids.insert(res.m_subid);
|
||||
// anything changed?
|
||||
if (res.m_state != ITEM_NEEDS_MERGE) {
|
||||
SubRevisionEntry &entry = m_revisions[res.m_mainid];
|
||||
entry.m_uid = res.m_uid;
|
||||
entry.m_revision = res.m_revision;
|
||||
entry.m_subids.insert(res.m_subid);
|
||||
}
|
||||
return SyncSourceRaw::InsertItemResult(createLUID(res.m_mainid, res.m_subid),
|
||||
res.m_revision, res.m_merged);
|
||||
res.m_revision, res.m_state);
|
||||
}
|
||||
|
||||
void MapSyncSource::readItem(const std::string &luid, std::string &item)
|
||||
|
|
|
@ -66,7 +66,7 @@ class SubSyncSource : virtual public SyncSourceBase
|
|||
class SubItemResult {
|
||||
public:
|
||||
SubItemResult() :
|
||||
m_merged(false)
|
||||
m_state(ITEM_OKAY)
|
||||
{}
|
||||
|
||||
/**
|
||||
|
@ -78,25 +78,25 @@ class SubSyncSource : virtual public SyncSourceBase
|
|||
* @param uid an arbitrary string, stored, but not used by MapSyncSource;
|
||||
* used in the CalDAV backend to associate mainid (= resource path)
|
||||
* with UID (= part of the item content, but with special semantic)
|
||||
* @param merged set this to true if an existing sub item was updated instead of adding it
|
||||
* @param state report about what was done with the data
|
||||
*/
|
||||
SubItemResult(const string &mainid,
|
||||
const string &subid,
|
||||
const string &revision,
|
||||
const string &uid,
|
||||
bool merged) :
|
||||
InsertItemResultState state) :
|
||||
m_mainid(mainid),
|
||||
m_subid(subid),
|
||||
m_revision(revision),
|
||||
m_uid(uid),
|
||||
m_merged(merged)
|
||||
m_state(state)
|
||||
{}
|
||||
|
||||
string m_mainid;
|
||||
string m_subid;
|
||||
string m_revision;
|
||||
string m_uid;
|
||||
bool m_merged;
|
||||
InsertItemResultState m_state;
|
||||
};
|
||||
|
||||
SubSyncSource() : m_parent(NULL) {}
|
||||
|
|
|
@ -2587,7 +2587,11 @@ ConfigPasswordKey DatabasePasswordConfigProperty::getPasswordKey(const string &d
|
|||
{
|
||||
ConfigPasswordKey key;
|
||||
key.user = sourcePropUser.getProperty(*sourceConfigNode);
|
||||
key.object = serverName;
|
||||
std::string configName = SyncConfig::normalizeConfigString(serverName, SyncConfig::NORMALIZE_LONG_FORMAT);
|
||||
std::string peer, context;
|
||||
SyncConfig::splitConfigString(configName, peer, context);
|
||||
key.object = "@";
|
||||
key.object += context;
|
||||
key.object += " ";
|
||||
key.object += sourceName;
|
||||
key.object += " backend";
|
||||
|
|
|
@ -1950,11 +1950,11 @@ void SyncContext::initSources(SourceList &sourceList)
|
|||
BOOST_FOREACH(const string &name, configuredSources) {
|
||||
boost::shared_ptr<PersistentSyncSourceConfig> sc(getSyncSourceConfig(name));
|
||||
SyncSourceNodes source = getSyncSourceNodes (name);
|
||||
SourceType sourceType = SyncSource::getSourceType(source);
|
||||
// is the source enabled?
|
||||
string sync = sc->getSync();
|
||||
bool enabled = sync != "disabled";
|
||||
if (enabled) {
|
||||
SourceType sourceType = SyncSource::getSourceType(source);
|
||||
if (sourceType.m_backend == "virtual") {
|
||||
//This is a virtual sync source, check and enable the referenced
|
||||
//sub syncsources here
|
||||
|
@ -1995,12 +1995,12 @@ void SyncContext::initSources(SourceList &sourceList)
|
|||
boost::shared_ptr<PersistentSyncSourceConfig> sc(getSyncSourceConfig(name));
|
||||
|
||||
SyncSourceNodes source = getSyncSourceNodes (name);
|
||||
SourceType sourceType = SyncSource::getSourceType(source);
|
||||
|
||||
// is the source enabled?
|
||||
string sync = sc->getSync();
|
||||
bool enabled = sync != "disabled";
|
||||
if (enabled) {
|
||||
SourceType sourceType = SyncSource::getSourceType(source);
|
||||
if (sourceType.m_backend != "virtual") {
|
||||
SyncSourceParams params(name,
|
||||
source,
|
||||
|
|
|
@ -668,6 +668,19 @@ sysync::TSyError SyncSourceSerialize::insertItemAsKey(sysync::KeyH aItemKey, sys
|
|||
InsertItemResult inserted =
|
||||
insertItem(!aID ? "" : aID->item, data.get());
|
||||
newID->item = StrAlloc(inserted.m_luid.c_str());
|
||||
switch (inserted.m_state) {
|
||||
case ITEM_OKAY:
|
||||
break;
|
||||
case ITEM_REPLACED:
|
||||
res = sysync::DB_DataReplaced;
|
||||
break;
|
||||
case ITEM_MERGED:
|
||||
res = sysync::DB_DataMerged;
|
||||
break;
|
||||
case ITEM_NEEDS_MERGE:
|
||||
res = sysync::DB_Conflict;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -1420,6 +1420,53 @@ class SyncSourceDelete : virtual public SyncSourceBase {
|
|||
sysync::TSyError deleteItemSynthesis(sysync::cItemID aID);
|
||||
};
|
||||
|
||||
enum InsertItemResultState {
|
||||
/**
|
||||
* item added or updated as requested
|
||||
*/
|
||||
ITEM_OKAY,
|
||||
|
||||
/**
|
||||
* When a backend is asked to add an item and recognizes
|
||||
* that the item matches an already existing item, it may
|
||||
* replace that item instead of creating a duplicate. In this
|
||||
* case it must return ITEM_REPLACED and set the luid/revision
|
||||
* of that updated item.
|
||||
*
|
||||
* This can happen when such an item was added concurrently to
|
||||
* the running sync or, more likely, was reported as new by
|
||||
* the backend and the engine failed to find the match because
|
||||
* it doesn't know about some special semantic, like iCalendar
|
||||
* 2.0 UID).
|
||||
*
|
||||
* Note that depending on the age of the items, the older data
|
||||
* will replace the more recent one when always using item
|
||||
* replacement.
|
||||
*/
|
||||
ITEM_REPLACED,
|
||||
|
||||
/**
|
||||
* Same as ITEM_REPLACED, except that the backend did some
|
||||
* modifications to the data that was sent to it before
|
||||
* storing it, like merging it with the existing item. The
|
||||
* engine will treat the updated item as modified and send
|
||||
* back the update to the peer as soon as possible. In server
|
||||
* mode that will be in the same sync session, in a client in
|
||||
* the next session (client cannot send changes after having
|
||||
* received data from the server).
|
||||
*/
|
||||
ITEM_MERGED,
|
||||
|
||||
/**
|
||||
* As before, a match against an existing item was detected.
|
||||
* By returning this state and the luid of the matched item
|
||||
* (revision not needed) the engine is instructed to do the
|
||||
* necessary data comparison and merging itself. Useful when a
|
||||
* backend can't do the necessary merging itself.
|
||||
*/
|
||||
ITEM_NEEDS_MERGE
|
||||
};
|
||||
|
||||
/**
|
||||
* an interface for reading and writing items in the internal
|
||||
* format; see SyncSourceSerialize for an explanation
|
||||
|
@ -1429,26 +1476,26 @@ class SyncSourceRaw : virtual public SyncSourceBase {
|
|||
class InsertItemResult {
|
||||
public:
|
||||
InsertItemResult() :
|
||||
m_merged(false)
|
||||
m_state(ITEM_OKAY)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @param luid the LUID after the operation; during an update the LUID must
|
||||
* not be changed, so return the original one here
|
||||
* @param revision the revision string after the operation; leave empty if not used
|
||||
* @param merged set this to true if an existing item was updated instead of adding it
|
||||
* @param state report about what was done with the data
|
||||
*/
|
||||
InsertItemResult(const string &luid,
|
||||
const string &revision,
|
||||
bool merged) :
|
||||
InsertItemResultState state) :
|
||||
m_luid(luid),
|
||||
m_revision(revision),
|
||||
m_merged(merged)
|
||||
m_state(state)
|
||||
{}
|
||||
|
||||
string m_luid;
|
||||
string m_revision;
|
||||
bool m_merged;
|
||||
InsertItemResultState m_state;
|
||||
};
|
||||
|
||||
/** same as SyncSourceSerialize::insertItem(), but with internal format */
|
||||
|
|
|
@ -140,14 +140,18 @@ std::string TrackingSyncSource::endSync(bool success)
|
|||
TrackingSyncSource::InsertItemResult TrackingSyncSource::insertItem(const std::string &luid, const std::string &item)
|
||||
{
|
||||
InsertItemResult res = insertItem(luid, item, false);
|
||||
updateRevision(*m_trackingNode, luid, res.m_luid, res.m_revision);
|
||||
if (res.m_state != ITEM_NEEDS_MERGE) {
|
||||
updateRevision(*m_trackingNode, luid, res.m_luid, res.m_revision);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
TrackingSyncSource::InsertItemResult TrackingSyncSource::insertItemRaw(const std::string &luid, const std::string &item)
|
||||
{
|
||||
InsertItemResult res = insertItem(luid, item, true);
|
||||
updateRevision(*m_trackingNode, luid, res.m_luid, res.m_revision);
|
||||
if (res.m_state != ITEM_NEEDS_MERGE) {
|
||||
updateRevision(*m_trackingNode, luid, res.m_luid, res.m_revision);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -384,6 +384,9 @@
|
|||
<parameter name="TZID" default="no" show="yes">
|
||||
<value field="EXDATES" conversion="TZID"/>
|
||||
</parameter>
|
||||
<parameter name="VALUE" default="no" show="yes">
|
||||
<value field="EXDATES" conversion="VALUETYPE"/>
|
||||
</parameter>
|
||||
</property>
|
||||
|
||||
<property name="EXDATE" values="list" suppressempty="yes" onlyformode="old" delayedparsing="1" valueseparator=";" altvalueseparator=",">
|
||||
|
|
|
@ -133,12 +133,21 @@ class TestingSyncSourcePtr : public std::auto_ptr<TestingSyncSource>
|
|||
typedef std::auto_ptr<TestingSyncSource> base_t;
|
||||
|
||||
static StringMap m_anchors;
|
||||
static std::string m_testName;
|
||||
|
||||
public:
|
||||
TestingSyncSourcePtr() {}
|
||||
TestingSyncSourcePtr(TestingSyncSource *source) :
|
||||
base_t(source)
|
||||
{
|
||||
// reset anchors each time a new test starts,
|
||||
// because it avoids interactions between tests
|
||||
std::string testName = getCurrentTest();
|
||||
if (testName != m_testName) {
|
||||
m_anchors.clear();
|
||||
m_testName = testName;
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(source);
|
||||
source->open();
|
||||
string node = source->getTrackingNode()->getName();
|
||||
|
@ -180,6 +189,7 @@ public:
|
|||
};
|
||||
|
||||
StringMap TestingSyncSourcePtr::m_anchors;
|
||||
std::string TestingSyncSourcePtr::m_testName;
|
||||
|
||||
bool SyncOptions::defaultWBXML()
|
||||
{
|
||||
|
@ -329,6 +339,16 @@ std::string LocalTests::insert(CreateSource createSource, const std::string &dat
|
|||
SOURCE_ASSERT_NO_FAILURE(source.get(), res = source->insertItemRaw("", mangled));
|
||||
CPPUNIT_ASSERT(!res.m_luid.empty());
|
||||
|
||||
bool updated = false;
|
||||
if (res.m_state == ITEM_NEEDS_MERGE) {
|
||||
// conflict detected, overwrite existing item as done in the past
|
||||
std::string luid = res.m_luid;
|
||||
SOURCE_ASSERT_NO_FAILURE(source.get(), res = source->insertItemRaw(luid, mangled));
|
||||
CPPUNIT_ASSERT_EQUAL(luid, res.m_luid);
|
||||
CPPUNIT_ASSERT(res.m_state == ITEM_OKAY);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// delete source again
|
||||
CPPUNIT_ASSERT_NO_THROW(source.reset());
|
||||
|
||||
|
@ -337,7 +357,7 @@ std::string LocalTests::insert(CreateSource createSource, const std::string &dat
|
|||
// - a new item was added
|
||||
// - the item was matched against an existing one
|
||||
CPPUNIT_ASSERT_NO_THROW(source.reset(createSource()));
|
||||
CPPUNIT_ASSERT_EQUAL(numItems + (res.m_merged ? 0 : 1),
|
||||
CPPUNIT_ASSERT_EQUAL(numItems + ((res.m_state == ITEM_REPLACED || res.m_state == ITEM_MERGED || updated) ? 0 : 1),
|
||||
countItems(source.get()));
|
||||
CPPUNIT_ASSERT(countNewItems(source.get()) == 0);
|
||||
CPPUNIT_ASSERT(countUpdatedItems(source.get()) == 0);
|
||||
|
@ -394,8 +414,11 @@ void LocalTests::update(CreateSource createSource, const std::string &data, bool
|
|||
SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getAllItems().begin());
|
||||
CPPUNIT_ASSERT(it != source->getAllItems().end());
|
||||
string luid = *it;
|
||||
SOURCE_ASSERT_NO_FAILURE(source.get(), source->insertItemRaw(luid, config.m_mangleItem(data, true)));
|
||||
SyncSourceRaw::InsertItemResult res;
|
||||
SOURCE_ASSERT_NO_FAILURE(source.get(), res = source->insertItemRaw(luid, config.m_mangleItem(data, true)));
|
||||
CPPUNIT_ASSERT_NO_THROW(source.reset());
|
||||
CPPUNIT_ASSERT_EQUAL(luid, res.m_luid);
|
||||
CPPUNIT_ASSERT_EQUAL(ITEM_OKAY, res.m_state);
|
||||
|
||||
if (!check) {
|
||||
return;
|
||||
|
@ -720,7 +743,8 @@ void LocalTests::testLocalDeleteAll() {
|
|||
|
||||
// clean database, then insert
|
||||
void LocalTests::testComplexInsert() {
|
||||
testLocalDeleteAll();
|
||||
CPPUNIT_ASSERT(config.m_createSourceA);
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
testSimpleInsert();
|
||||
testIterateTwice();
|
||||
}
|
||||
|
@ -729,8 +753,10 @@ void LocalTests::testComplexInsert() {
|
|||
void LocalTests::testLocalUpdate() {
|
||||
// check additional requirements
|
||||
CPPUNIT_ASSERT(!config.m_updateItem.empty());
|
||||
CPPUNIT_ASSERT(config.m_createSourceA);
|
||||
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
|
||||
testLocalDeleteAll();
|
||||
testSimpleInsert();
|
||||
CPPUNIT_ASSERT_NO_THROW(update(createSourceA, config.m_updateItem));
|
||||
}
|
||||
|
@ -741,8 +767,9 @@ void LocalTests::testChanges() {
|
|||
|
||||
// check additional requirements
|
||||
CPPUNIT_ASSERT(config.m_createSourceB);
|
||||
CPPUNIT_ASSERT(config.m_createSourceA);
|
||||
|
||||
testLocalDeleteAll();
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
testSimpleInsert();
|
||||
|
||||
// clean changes in sync source B by creating and closing it
|
||||
|
@ -861,8 +888,9 @@ void LocalTests::testImport() {
|
|||
CPPUNIT_ASSERT(config.m_dump);
|
||||
CPPUNIT_ASSERT(config.m_compare);
|
||||
CPPUNIT_ASSERT(!config.m_testcases.empty());
|
||||
CPPUNIT_ASSERT(config.m_createSourceA);
|
||||
|
||||
testLocalDeleteAll();
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
|
||||
// import via sync source A
|
||||
TestingSyncSourcePtr source;
|
||||
|
@ -892,7 +920,7 @@ void LocalTests::testImportDelete() {
|
|||
|
||||
// delete again, because it was observed that this did not
|
||||
// work right with calendars in SyncEvolution
|
||||
testLocalDeleteAll();
|
||||
CPPUNIT_ASSERT_NO_THROW(deleteAll(createSourceA));
|
||||
}
|
||||
|
||||
// test change tracking with large number of items
|
||||
|
@ -1811,6 +1839,12 @@ void SyncTests::addTests(bool isFirstSource) {
|
|||
ADD_TEST(SyncTests, testSlowSyncSemantic);
|
||||
ADD_TEST(SyncTests, testComplexRefreshFromServerSemantic);
|
||||
ADD_TEST(SyncTests, testDeleteBothSides);
|
||||
if (config.m_updateItem.find("UID:") != std::string::npos &&
|
||||
config.m_updateItem.find("LAST-MODIFIED:") != std::string::npos &&
|
||||
sources.size() == 1) {
|
||||
ADD_TEST(SyncTests, testAddBothSides);
|
||||
ADD_TEST(SyncTests, testAddBothSidesRefresh);
|
||||
}
|
||||
|
||||
// only add when testing individual source,
|
||||
// test data not guaranteed to be available for all sources
|
||||
|
@ -3072,6 +3106,207 @@ void SyncTests::testDeleteBothSides()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - clean A, server, B
|
||||
* - create an item on A
|
||||
* - sync A
|
||||
* - create a modified version of the item on B
|
||||
* - sync B
|
||||
*
|
||||
* Depends on UID and LAST-MODIFIED in item data, i.e., iCalendar 2.0.
|
||||
* Uses the normal "insertItem" test case. Only works for a single source.
|
||||
*
|
||||
* The server must not duplicate the item *and* preserve the modified
|
||||
* properties.
|
||||
*
|
||||
* Temporary: because conflict resolution is server-dependent, such a strict
|
||||
* test fails. For example, with SyncEvolution 1.2 as server, DESCRIPTION and
|
||||
* LOCATION end up being concatenated (merge=lines mode). The test now avoids
|
||||
* using different data, with the expected outcome that only one item
|
||||
* is present at the end and no unnecessary data transfers happen (only true
|
||||
* for SyncEvolution server).
|
||||
*
|
||||
* A similar situation occurs on the client side, but it is harder to
|
||||
* trigger: the updated item must be added to the client's database
|
||||
* after it has reported its changes. Because if it happens earlier,
|
||||
* it would send an Add to the server and the server would have to
|
||||
* resolve the add<->add conflict, as in this test here.
|
||||
*/
|
||||
|
||||
// using updated item data makes the test harder to pass:
|
||||
// server must use exactly the right item, which currently
|
||||
// is not the case for SyncEvolution
|
||||
bool addBothSidesUsesUpdateItem = true;
|
||||
|
||||
// SyncEvolution passes with addBothSidesUsesUpdateItem == true
|
||||
// if we avoid changes to those properties in the iCalendar test
|
||||
// set which currently use merge=lines.
|
||||
bool addBothSidesNoMergeLines=true;
|
||||
|
||||
// if true, relax expectations for updates from server:
|
||||
// may or may not send one
|
||||
bool addBothSidesMayUpdate = false;
|
||||
|
||||
// if true, then accept that the Synthesis server mode counts
|
||||
// Add commands as "added items" even if they are turned into updates
|
||||
bool addBothSidesAddStatsBroken = false;
|
||||
|
||||
void SyncTests::testAddBothSides()
|
||||
{
|
||||
deleteAll();
|
||||
accessClientB->deleteAll();
|
||||
|
||||
std::string insertItem = sources[0].second->config.m_insertItem;
|
||||
std::string updateItem = sources[0].second->config.m_updateItem;
|
||||
|
||||
if (addBothSidesNoMergeLines) {
|
||||
boost::replace_all(updateItem, "LOCATION:big meeting room", "LOCATION:my office");
|
||||
boost::replace_all(updateItem, "DESCRIPTION:nice to see you", "DESCRIPTION:let's talk<<REVISION>>");
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_NO_THROW(sources[0].second->insert(sources[0].second->createSourceA,
|
||||
insertItem));
|
||||
|
||||
doSync("send-old",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// insert updated item data on B
|
||||
std::string data;
|
||||
CPPUNIT_ASSERT_NO_THROW(accessClientB->sources[0].second->insert(accessClientB->sources[0].second->createSourceA,
|
||||
addBothSidesUsesUpdateItem ?
|
||||
updateItem:
|
||||
insertItem,
|
||||
false,
|
||||
&data));
|
||||
|
||||
// As far as the client knows, it is adding an item;
|
||||
// server not expected to send back an update (our data was more recent
|
||||
// and completely overwrites the server's data).
|
||||
// When acting as server, we do the duplicate detection and thus know
|
||||
// more about the actual outcome.
|
||||
accessClientB->doSync("send-update",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
isServerMode() ?
|
||||
CheckSyncReport(addBothSidesAddStatsBroken ? -1 : 0,0,0,
|
||||
|
||||
0,
|
||||
addBothSidesMayUpdate ? -1 :
|
||||
addBothSidesUsesUpdateItem ? 1 : 0,
|
||||
0,
|
||||
|
||||
true, SYNC_TWO_WAY) :
|
||||
CheckSyncReport(0,
|
||||
addBothSidesMayUpdate ? -1 : 0,
|
||||
0,
|
||||
|
||||
// client doesn't know that the add
|
||||
// was an update, in contrast to server
|
||||
1,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// update sent to client A
|
||||
doSync("update",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,
|
||||
addBothSidesMayUpdate ? -1 :
|
||||
addBothSidesUsesUpdateItem ? 1 : 0,
|
||||
0,
|
||||
|
||||
0,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// nothing necessary for client B
|
||||
accessClientB->doSync("nop",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// now compare client A against reference data
|
||||
TestingSyncSourcePtr copy;
|
||||
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(sources[0].second->createSourceB()));
|
||||
sources[0].second->compareDatabases(*copy, &data, (void *)NULL);
|
||||
CPPUNIT_ASSERT_NO_THROW(copy.reset());
|
||||
}
|
||||
|
||||
/**
|
||||
* compared to testAddBothSides the age of the items is reversed now;
|
||||
* a server which always copies the client's data passes testAddBothSides
|
||||
* but fails here
|
||||
*/
|
||||
void SyncTests::testAddBothSidesRefresh()
|
||||
{
|
||||
deleteAll();
|
||||
accessClientB->deleteAll();
|
||||
|
||||
std::string insertItem = sources[0].second->config.m_insertItem;
|
||||
std::string updateItem = sources[0].second->config.m_updateItem;
|
||||
|
||||
if (addBothSidesNoMergeLines) {
|
||||
boost::replace_all(updateItem, "LOCATION:big meeting room", "LOCATION:my office");
|
||||
boost::replace_all(updateItem, "DESCRIPTION:nice to see you", "DESCRIPTION:let's talk<<REVISION>>");
|
||||
}
|
||||
|
||||
// insert initial item data on B
|
||||
CPPUNIT_ASSERT_NO_THROW(accessClientB->sources[0].second->insert(accessClientB->sources[0].second->createSourceA,
|
||||
insertItem));
|
||||
|
||||
// sleep one second to ensure that it's mangled LAST-MODIFIED is older than
|
||||
// the one from the next item, inserted on A
|
||||
sleep(1);
|
||||
|
||||
// more recent data sent to server first
|
||||
std::string data;
|
||||
CPPUNIT_ASSERT_NO_THROW(sources[0].second->insert(sources[0].second->createSourceA,
|
||||
addBothSidesUsesUpdateItem ?
|
||||
updateItem :
|
||||
insertItem,
|
||||
false,
|
||||
&data));
|
||||
doSync("send-new",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// As far as the client knows, it is adding an item;
|
||||
// server expected to send back an update (client's data was out-dated);
|
||||
// When acting as server, we do the duplicate detection and thus
|
||||
// know more about the actual outcome.
|
||||
accessClientB->doSync("send-old",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
isServerMode() ?
|
||||
CheckSyncReport(addBothSidesAddStatsBroken ? -1 : 0,
|
||||
addBothSidesMayUpdate ? -1 :
|
||||
addBothSidesUsesUpdateItem ? 1 : 0,
|
||||
0,
|
||||
|
||||
0,
|
||||
addBothSidesMayUpdate ? -1 : 0,
|
||||
0,
|
||||
|
||||
true, SYNC_TWO_WAY) :
|
||||
CheckSyncReport(0,
|
||||
addBothSidesMayUpdate ? -1 :
|
||||
addBothSidesUsesUpdateItem ? 1 : 0,
|
||||
0,
|
||||
|
||||
// client doesn't know that add was
|
||||
// an update
|
||||
1,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// update sent to client A (result of merge)
|
||||
doSync("nopA",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,addBothSidesMayUpdate ? -1 : 0,0, 0,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// nothing necessary for client B (already synchronized completely above in one sync)
|
||||
accessClientB->doSync("nopB",
|
||||
SyncOptions(SYNC_TWO_WAY,
|
||||
CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY)));
|
||||
|
||||
// now compare client A against reference data
|
||||
TestingSyncSourcePtr copy;
|
||||
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(sources[0].second->createSourceB()));
|
||||
sources[0].second->compareDatabases(*copy, &data, (void *)NULL);
|
||||
CPPUNIT_ASSERT_NO_THROW(copy.reset());
|
||||
}
|
||||
|
||||
/**
|
||||
* - adds parent on client A
|
||||
* - syncs A
|
||||
|
@ -4586,7 +4821,7 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
"VERSION:2.0\n"
|
||||
"BEGIN:VEVENT\n"
|
||||
"SUMMARY:phone meeting\n"
|
||||
"SUMMARY:phone meeting - old\n"
|
||||
"DTEND:20060406T163000Z\n"
|
||||
"DTSTART:20060406T160000Z\n"
|
||||
"UID:1234567890!@#$%^&*()<>@dummy\n"
|
||||
|
@ -4605,7 +4840,7 @@ void ClientTest::getTestData(const char *type, Config &config)
|
|||
"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
|
||||
"VERSION:2.0\n"
|
||||
"BEGIN:VEVENT\n"
|
||||
"SUMMARY:meeting on site\n"
|
||||
"SUMMARY:meeting on site - updated\n"
|
||||
"DTEND:20060406T163000Z\n"
|
||||
"DTSTART:20060406T160000Z\n"
|
||||
"UID:1234567890!@#$%^&*()<>@dummy\n"
|
||||
|
|
|
@ -753,6 +753,8 @@ protected:
|
|||
virtual void testSlowSyncSemantic();
|
||||
virtual void testComplexRefreshFromServerSemantic();
|
||||
virtual void testDeleteBothSides();
|
||||
virtual void testAddBothSides();
|
||||
virtual void testAddBothSidesRefresh();
|
||||
virtual void testLinkedItemsParentChild();
|
||||
virtual void testLinkedItemsChild();
|
||||
virtual void testLinkedItemsChildParent();
|
||||
|
|
|
@ -821,6 +821,10 @@ test = SyncEvolutionTest("googlecalendar", compile,
|
|||
"CLIENT_TEST_SIMPLE_UID=1 " # server gets confused by UID with special characters
|
||||
"CLIENT_TEST_UNIQUE_UID=1 " # server keeps backups and restores old data unless UID is unieque
|
||||
"CLIENT_TEST_MODE=server " # for Client::Sync
|
||||
"CLIENT_TEST_FAILURES="
|
||||
# http://code.google.com/p/google-caldav-issues/issues/detail?id=61 "cannot remove detached recurrence"
|
||||
"Client::Source::google_caldav::LinkedItems_0::testLinkedItemsRemoveNormal,"
|
||||
"Client::Source::google_caldav::LinkedItems_1::testLinkedItemsRemoveNormal,"
|
||||
,
|
||||
testPrefix=options.testprefix)
|
||||
context.add(test)
|
||||
|
@ -856,7 +860,6 @@ test = SyncEvolutionTest("apple", compile,
|
|||
"CLIENT_TEST_WEBDAV='apple caldav carddav' "
|
||||
"CLIENT_TEST_NUM_ITEMS=250 " # test is local, so we can afford a higher number
|
||||
"CLIENT_TEST_ALARM=2400 " # but even with a local server does the test run a long time
|
||||
"CLIENT_TEST_SIMPLE_UID=1 " # server gets confused by UID with special characters
|
||||
"CLIENT_TEST_MODE=server " # for Client::Sync
|
||||
,
|
||||
testPrefix=options.testprefix)
|
||||
|
@ -1002,6 +1005,12 @@ class FunambolTest(SyncEvolutionTest):
|
|||
"eds_task",
|
||||
"eds_memo" ],
|
||||
"CLIENT_TEST_SKIP="
|
||||
# server duplicates items in add<->add conflict because it
|
||||
# does not check UID
|
||||
"Client::Sync::eds_event::testAddBothSides,"
|
||||
"Client::Sync::eds_event::testAddBothSidesRefresh,"
|
||||
"Client::Sync::eds_task::testAddBothSides,"
|
||||
"Client::Sync::eds_task::testAddBothSidesRefresh,"
|
||||
# test cannot pass because we don't have CtCap info about
|
||||
# the Funambol server
|
||||
"Client::Sync::eds_contact::testExtensions,"
|
||||
|
@ -1101,6 +1110,12 @@ mobicaltest = SyncEvolutionTest("mobical", compile,
|
|||
"CLIENT_TEST_NOCHECK_SYNCMODE=1 "
|
||||
"CLIENT_TEST_MAX_ITEMSIZE=2048 "
|
||||
"CLIENT_TEST_SKIP="
|
||||
# server duplicates items in add<->add conflict because it
|
||||
# does not check UID
|
||||
"Client::Sync::eds_event::testAddBothSides,"
|
||||
"Client::Sync::eds_event::testAddBothSidesRefresh,"
|
||||
"Client::Sync::eds_task::testAddBothSides,"
|
||||
"Client::Sync::eds_task::testAddBothSidesRefresh,"
|
||||
"Client::Sync::eds_contact::Retry,"
|
||||
"Client::Sync::eds_contact::Suspend,"
|
||||
"Client::Sync::eds_contact::Resend,"
|
||||
|
@ -1178,6 +1193,12 @@ memotootest = SyncEvolutionTest("memotoo", compile,
|
|||
"CLIENT_TEST_NOCHECK_SYNCMODE=1 "
|
||||
"CLIENT_TEST_NUM_ITEMS=10 "
|
||||
"CLIENT_TEST_SKIP="
|
||||
# server duplicates items in add<->add conflict because it
|
||||
# does not check UID
|
||||
"Client::Sync::eds_event::testAddBothSides,"
|
||||
"Client::Sync::eds_event::testAddBothSidesRefresh,"
|
||||
"Client::Sync::eds_task::testAddBothSides,"
|
||||
"Client::Sync::eds_task::testAddBothSidesRefresh,"
|
||||
"Client::Sync::eds_contact::Retry,"
|
||||
"Client::Sync::eds_contact::Suspend,"
|
||||
# "Client::Sync::eds_contact::testRefreshFromClientSync,"
|
||||
|
|
|
@ -612,3 +612,17 @@
|
|||
...
|
||||
obj:*libgiognutls.so
|
||||
}
|
||||
{
|
||||
gnutls + libneon: ASN buffer
|
||||
Memcheck:Addr4
|
||||
fun:asn1_der_coding
|
||||
...
|
||||
obj:*libneon*
|
||||
}
|
||||
{
|
||||
gnutls certificate: ASN buffer
|
||||
Memcheck:Addr4
|
||||
fun:asn1_der_coding
|
||||
...
|
||||
fun:gnutls_certificate_set_x509_trust_file
|
||||
}
|
|
@ -3,7 +3,7 @@ VERSION:3.0
|
|||
NICKNAME:user17
|
||||
NOTE:triggers parser bug in Funambol 3.0: trailing = is mistaken for soft line break=
|
||||
FN:parserbug=
|
||||
N:parserbug=
|
||||
N:parserbug=;;;;
|
||||
X-EVOLUTION-FILE-AS:parserbug=
|
||||
END:VCARD
|
||||
|
||||
|
@ -12,7 +12,7 @@ VERSION:3.0
|
|||
NICKNAME:user16
|
||||
NOTE:test case with empty email
|
||||
FN:incomplete
|
||||
N:incomplete
|
||||
N:incomplete;;;;
|
||||
EMAIL:
|
||||
X-EVOLUTION-FILE-AS:incomplete
|
||||
END:VCARD
|
||||
|
@ -162,7 +162,7 @@ X-EVOLUTION-MANAGER:John Doe Senior
|
|||
X-EVOLUTION-ASSISTANT:John Doe Junior
|
||||
NICKNAME:user1
|
||||
BDAY:2006-01-08
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test; never sent to a peer
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test\; never sent to a peer
|
||||
X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
X-EVOLUTION-ANNIVERSARY:2006-01-09
|
||||
X-EVOLUTION-SPOUSE:Joan Doe
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
@@ -163,7 +163,7 @@
|
||||
NICKNAME:user1
|
||||
BDAY:2006-01-08
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test\; never sent to a peer
|
||||
-X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
+X-TEST;PARAMETER1=nonquoted:Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
X-EVOLUTION-ANNIVERSARY:2006-01-09
|
||||
X-EVOLUTION-SPOUSE:Joan Doe
|
||||
NOTE:This is a test case which uses almost all Evolution fields.
|
|
@ -3,7 +3,7 @@
|
|||
NICKNAME:user17
|
||||
NOTE:triggers parser bug in Funambol 3.0: trailing = is mistaken for soft line break=
|
||||
-FN:parserbug=
|
||||
N:parserbug=
|
||||
N:parserbug=;;;;
|
||||
X-EVOLUTION-FILE-AS:parserbug=
|
||||
END:VCARD
|
||||
@@ -11,7 +10,6 @@
|
||||
|
@ -11,7 +11,7 @@
|
|||
NICKNAME:user16
|
||||
NOTE:test case with empty email
|
||||
-FN:incomplete
|
||||
N:incomplete
|
||||
N:incomplete;;;;
|
||||
EMAIL:
|
||||
X-EVOLUTION-FILE-AS:incomplete
|
||||
@@ -24,7 +22,6 @@
|
||||
|
@ -113,7 +113,7 @@
|
|||
END:VCARD
|
||||
@@ -164,51 +153,35 @@
|
||||
BDAY:2006-01-08
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test; never sent to a peer
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test\; never sent to a peer
|
||||
X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
-X-EVOLUTION-ANNIVERSARY:2006-01-09
|
||||
-X-EVOLUTION-SPOUSE:Joan Doe
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
@@ -4,6 +4,7 @@
|
||||
NOTE:triggers parser bug in Funambol 3.0: trailing = is mistaken for soft line break=
|
||||
FN:parserbug=
|
||||
N:parserbug=
|
||||
N:parserbug=;;;;
|
||||
+URL:http://john.doe.com
|
||||
X-EVOLUTION-FILE-AS:parserbug=
|
||||
END:VCARD
|
||||
|
||||
@@ -14,6 +15,7 @@
|
||||
FN:incomplete
|
||||
N:incomplete
|
||||
N:incomplete;;;;
|
||||
EMAIL:
|
||||
+URL:http://john.doe.com
|
||||
X-EVOLUTION-FILE-AS:incomplete
|
||||
|
@ -82,7 +82,7 @@
|
|||
NICKNAME:user1
|
||||
-BDAY:2006-01-08
|
||||
+BDAY:2007-03-01
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test; never sent to a peer
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test\; never sent to a peer
|
||||
X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
X-EVOLUTION-ANNIVERSARY:2006-01-09
|
||||
@@ -184,7 +193,6 @@
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
X-EVOLUTION-ASSISTANT:John Doe Junior
|
||||
NICKNAME:user1
|
||||
BDAY:2006-01-08
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test; never sent to a peer
|
||||
X-FOOBAR-EXTENSION;X-FOOBAR-PARAMETER=foobar:has to be stored internally by engine and preserved in testExtensions test\; never sent to a peer
|
||||
-X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Content with\nMultiple\nText lines\nand national chars: äöü
|
||||
+X-TEST;PARAMETER1=nonquoted;PARAMETER2="quoted because of spaces":Text with\nMultiple\nText lines\nand national chars: äöü
|
||||
X-EVOLUTION-ANNIVERSARY:2006-01-09
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
X-AIM;X-EVOLUTION-UI-SLOT=1:AIM JOHN
|
||||
X-YAHOO;X-EVOLUTION-UI-SLOT=2:YAHOO JDOE
|
||||
X-ICQ;X-EVOLUTION-UI-SLOT=3:ICQ JD
|
||||
@@ -211,226 +120,3 @@
|
||||
@@ -210,226 +119,3 @@
|
||||
X-SKYPE:SKYPE DOE
|
||||
X-SIP:SIP DOE
|
||||
END:VCARD
|
||||
|
|
|
@ -263,6 +263,101 @@ DESCRIPTION:second instance modified
|
|||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080413T100000Z
|
||||
DTEND:20080413T103000Z
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
SUMMARY:Recurring: Modified II
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
RECURRENCE-ID:20080420T090000Z
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-XX@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080406T090000Z
|
||||
DTEND:20080406T093000Z
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:2
|
||||
SUMMARY:Recurring 2
|
||||
DESCRIPTION:recurs each Sunday\, 10 times
|
||||
CLASS:PUBLIC
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;UNTIL=20080608T090000Z
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193241
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-XX@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080413T090000Z
|
||||
DTEND:20080413T093000Z
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
SUMMARY:Recurring 2: Modified
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
RECURRENCE-ID:20080413T090000Z
|
||||
DESCRIPTION:second instance modified\, single detached recurrence
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-YY@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080413T090000Z
|
||||
DTEND:20080413T093000Z
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
SUMMARY:Recurring 3: Modified
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
RECURRENCE-ID:20080413T090000Z
|
||||
DESCRIPTION:second instance modified
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-YY@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080413T100000Z
|
||||
DTEND:20080413T103000Z
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
SUMMARY:Recurring 3: Modified II
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
RECURRENCE-ID:20080420T090000Z
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
|
|
|
@ -134,8 +134,8 @@
|
|||
CREATED:20060416T205003Z
|
||||
LAST-MODIFIED:20060416T205003Z
|
||||
END:VEVENT
|
||||
@@ -262,37 +147,3 @@
|
||||
DESCRIPTION:second instance modified
|
||||
@@ -357,37 +242,3 @@
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
-
|
||||
|
|
|
@ -125,8 +125,8 @@
|
|||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193241
|
||||
END:VEVENT
|
||||
@@ -262,37 +237,3 @@
|
||||
DESCRIPTION:second instance modified
|
||||
@@ -357,37 +332,3 @@
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
-
|
||||
|
|
|
@ -198,7 +198,7 @@
|
|||
TRANSP:TRANSPARENT
|
||||
SEQUENCE:4
|
||||
SUMMARY:all fields
|
||||
@@ -204,34 +313,29 @@
|
||||
@@ -205,34 +313,29 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
|
@ -253,7 +253,7 @@
|
|||
TRANSP:OPAQUE
|
||||
SEQUENCE:2
|
||||
SUMMARY:Recurring
|
||||
@@ -246,52 +350,36 @@
|
||||
@@ -247,18 +350,36 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
|
@ -293,6 +293,157 @@
|
|||
DESCRIPTION:second instance modified
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -266,18 +387,36 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
+BEGIN:VTIMEZONE
|
||||
+TZID:/softwarestudio.org/Olson_20011030_5/Europe/Berlin
|
||||
+X-LIC-LOCATION:Europe/Berlin
|
||||
+BEGIN:DAYLIGHT
|
||||
+TZOFFSETFROM:+0100
|
||||
+TZOFFSETTO:+0200
|
||||
+TZNAME:CEST
|
||||
+DTSTART:19700329T020000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
||||
+END:DAYLIGHT
|
||||
+BEGIN:STANDARD
|
||||
+TZOFFSETFROM:+0200
|
||||
+TZOFFSETTO:+0100
|
||||
+TZNAME:CET
|
||||
+DTSTART:19701025T030000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
+END:STANDARD
|
||||
+END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080413T100000Z
|
||||
-DTEND:20080413T103000Z
|
||||
+DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080413T120000
|
||||
+DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080413T123000
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
SUMMARY:Recurring: Modified II
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080420T090000Z
|
||||
+RECURRENCE-ID;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080420T110000
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -285,11 +424,29 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
+BEGIN:VTIMEZONE
|
||||
+TZID:/softwarestudio.org/Olson_20011030_5/Europe/Berlin
|
||||
+X-LIC-LOCATION:Europe/Berlin
|
||||
+BEGIN:DAYLIGHT
|
||||
+TZOFFSETFROM:+0100
|
||||
+TZOFFSETTO:+0200
|
||||
+TZNAME:CEST
|
||||
+DTSTART:19700329T020000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
||||
+END:DAYLIGHT
|
||||
+BEGIN:STANDARD
|
||||
+TZOFFSETFROM:+0200
|
||||
+TZOFFSETTO:+0100
|
||||
+TZNAME:CET
|
||||
+DTSTART:19701025T030000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
+END:STANDARD
|
||||
+END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-XX@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080406T090000Z
|
||||
-DTEND:20080406T093000Z
|
||||
+DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080406T110000
|
||||
+DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080406T113000
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:2
|
||||
SUMMARY:Recurring 2
|
||||
@@ -304,90 +461,36 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
+BEGIN:VTIMEZONE
|
||||
+TZID:/softwarestudio.org/Olson_20011030_5/Europe/Berlin
|
||||
+X-LIC-LOCATION:Europe/Berlin
|
||||
+BEGIN:DAYLIGHT
|
||||
+TZOFFSETFROM:+0100
|
||||
+TZOFFSETTO:+0200
|
||||
+TZNAME:CEST
|
||||
+DTSTART:19700329T020000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
||||
+END:DAYLIGHT
|
||||
+BEGIN:STANDARD
|
||||
+TZOFFSETFROM:+0200
|
||||
+TZOFFSETTO:+0100
|
||||
+TZNAME:CET
|
||||
+DTSTART:19701025T030000
|
||||
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
+END:STANDARD
|
||||
+END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-XX@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080413T090000Z
|
||||
-DTEND:20080413T093000Z
|
||||
+DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080413T110000
|
||||
+DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080413T113000
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:7
|
||||
-SUMMARY:Recurring 2: Modified
|
||||
+SUMMARY:Recurring: Modified
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080413T090000Z
|
||||
+RECURRENCE-ID;TZID=/softwarestudio.org/Olson_20011030_5/Europe/Berlin:20080413T110000
|
||||
DESCRIPTION:second instance modified\, single detached recurrence
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
-
|
||||
-BEGIN:VCALENDAR
|
||||
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
-VERSION:2.0
|
||||
-BEGIN:VEVENT
|
||||
-UID:20080407T193125Z-19554-727-1-50-YY@gollum
|
||||
-DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080413T090000Z
|
||||
-DTEND:20080413T093000Z
|
||||
-TRANSP:OPAQUE
|
||||
-SEQUENCE:7
|
||||
-SUMMARY:Recurring 3: Modified
|
||||
-CLASS:PUBLIC
|
||||
-CREATED:20080407T193241Z
|
||||
-LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080413T090000Z
|
||||
-DESCRIPTION:second instance modified
|
||||
-END:VEVENT
|
||||
-END:VCALENDAR
|
||||
-
|
||||
-BEGIN:VCALENDAR
|
||||
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
-VERSION:2.0
|
||||
-BEGIN:VEVENT
|
||||
-UID:20080407T193125Z-19554-727-1-50-YY@gollum
|
||||
-DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080413T100000Z
|
||||
-DTEND:20080413T103000Z
|
||||
-TRANSP:OPAQUE
|
||||
-SEQUENCE:7
|
||||
-SUMMARY:Recurring 3: Modified II
|
||||
-CLASS:PUBLIC
|
||||
-CREATED:20080407T193241Z
|
||||
-LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080420T090000Z
|
||||
-DESCRIPTION:third instance modified\, different time
|
||||
-END:VEVENT
|
||||
-END:VCALENDAR
|
||||
-
|
||||
-BEGIN:VCALENDAR
|
||||
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
SUMMARY:meeting invitation
|
||||
CLASS:PUBLIC
|
||||
ORGANIZER;CN=Patrick Ohly:MAILTO:Patrick.Ohly@gmx.de
|
||||
@@ -231,14 +211,14 @@
|
||||
@@ -231,14 +211,16 @@
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
|
@ -153,27 +153,49 @@
|
|||
CLASS:PUBLIC
|
||||
-RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;UNTIL=20080608T090000Z
|
||||
+RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;UNTIL=20080608T110000
|
||||
+EXDATE;VALUE=DATE:20080413
|
||||
+EXDATE;VALUE=DATE:20080420
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193241
|
||||
END:VEVENT
|
||||
@@ -248,7 +228,7 @@
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
-UID:20080407T193125Z-19554-727-1-50@gollum
|
||||
+UID:20080407T193125Z-19554-727-1-50@gollum-mod
|
||||
DTSTAMP:20080407T193125Z
|
||||
DTSTART:20080413T090000Z
|
||||
DTEND:20080413T093000Z
|
||||
@@ -258,7 +238,6 @@
|
||||
@@ -258,7 +240,7 @@
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080413T090000Z
|
||||
+RECURRENCE-ID:20080413T110000
|
||||
DESCRIPTION:second instance modified
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -266,28 +245,11 @@
|
||||
@@ -277,7 +259,7 @@
|
||||
CLASS:PUBLIC
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193647
|
||||
-RECURRENCE-ID:20080420T090000Z
|
||||
+RECURRENCE-ID:20080420T110000
|
||||
DESCRIPTION:third instance modified\, different time
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -288,14 +270,15 @@
|
||||
BEGIN:VEVENT
|
||||
UID:20080407T193125Z-19554-727-1-50-XX@gollum
|
||||
DTSTAMP:20080407T193125Z
|
||||
-DTSTART:20080406T090000Z
|
||||
-DTEND:20080406T093000Z
|
||||
+DTSTART:20080406T110000
|
||||
+DTEND:20080406T113000
|
||||
TRANSP:OPAQUE
|
||||
SEQUENCE:2
|
||||
SUMMARY:Recurring 2
|
||||
DESCRIPTION:recurs each Sunday\, 10 times
|
||||
CLASS:PUBLIC
|
||||
-RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;UNTIL=20080608T090000Z
|
||||
+RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;UNTIL=20080608T110000
|
||||
+EXDATE;VALUE=DATE:20080413
|
||||
CREATED:20080407T193241Z
|
||||
LAST-MODIFIED:20080407T193241
|
||||
END:VEVENT
|
||||
@@ -361,28 +344,11 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
@@ -265,28 +186,11 @@
|
||||
@@ -361,28 +281,11 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
-DTSTART:19700329T020000
|
||||
-RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
||||
-END:DAYLIGHT
|
||||
-BEGIN:STANDARD
|
||||
-TZOFFSETFROM:+0200
|
||||
-TZOFFSETTO:+0100
|
||||
-TZNAME:CET
|
||||
-DTSTART:19701025T030000
|
||||
-RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
+TZID:Asia/Shanghai
|
||||
+BEGIN:STANDARD
|
||||
+DTSTART:19670101T000000
|
||||
|
@ -21,12 +27,7 @@
|
|||
+BEGIN:VTIMEZONE
|
||||
+TZID:/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai
|
||||
+X-LIC-LOCATION:Asia/Shanghai
|
||||
BEGIN:STANDARD
|
||||
-TZOFFSETFROM:+0200
|
||||
-TZOFFSETTO:+0100
|
||||
-TZNAME:CET
|
||||
-DTSTART:19701025T030000
|
||||
-RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
+BEGIN:STANDARD
|
||||
+TZNAME:CST
|
||||
+DTSTART:19700914T230000
|
||||
+TZOFFSETFROM:+0800
|
||||
|
|
Loading…
Reference in New Issue