Hello community,

here is the log from the commit of package libkolab for openSUSE:Factory 
checked in at 2015-04-06 00:24:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libkolab (Old)
 and      /work/SRC/openSUSE:Factory/.libkolab.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libkolab"

Changes:
--------
--- /work/SRC/openSUSE:Factory/libkolab/libkolab.changes        2014-12-16 
14:51:00.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.libkolab.new/libkolab.changes   2015-04-06 
00:24:11.000000000 +0200
@@ -1,0 +2,27 @@
+Thu Apr  2 08:29:12 UTC 2015 - [email protected]
+
+- Add upstream patch:
+  + libkolab-0.6.0_check_for_generic_tag.patch
+    Make libkolab compile with upstream kdepimlibs where GENERIC
+    tags are not yet included.
+
+-------------------------------------------------------------------
+Wed Apr  1 17:24:27 UTC 2015 - [email protected]
+
+- New upstream release 0.6.0
+- Added upstream patches:
+  + 0001-Add-support-for-exceptions.patch
+    Add support to read/write exceptions to iCal
+  + 0002-Support-for-THISANDFUTURE.patch
+    Add support to convert ThisAndFuture from and to iCal
+  + 0003-Move-QT_DECLARE_METADATA-logic-to-cmake.patch
+    Fix double declaration of KCalCore::Duration when building with
+    libcalendaring >= 4.9.1
+- Add %bcond_with tests to enable %check section when required
+
+-------------------------------------------------------------------
+Sat Mar 28 12:53:59 UTC 2015 - [email protected]
+
+- Convert %with_kde to %bcond_without kde
+
+-------------------------------------------------------------------

Old:
----
  libkolab-0.5.3.tar.gz

New:
----
  0001-Add-support-for-exceptions.patch
  0002-Support-for-THISANDFUTURE.patch
  0003-Move-QT_DECLARE_METADATA-logic-to-cmake.patch
  libkolab-0.6.0.tar.gz
  libkolab-0.6.0_check_for_generic_tag.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libkolab.spec ++++++
--- /var/tmp/diff_new_pack.ZG1wNs/_old  2015-04-06 00:24:11.000000000 +0200
+++ /var/tmp/diff_new_pack.ZG1wNs/_new  2015-04-06 00:24:11.000000000 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package libkolab
 #
-# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -15,32 +15,42 @@
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
-%define with_kde 1
+
 %if 0%{?suse_version} < 1230 || 0%{?suse_version} == 1315
-%define with_kde 0
+%define _without_kde 1
 %endif
+%bcond_without kde
+%bcond_with tests
 
 %define php_extdir %{_libdir}/php5/extensions
 %define php_confdir %{_sysconfdir}/php5/conf.d
 %define libname %{name}0
 Name:           libkolab
-Version:        0.5.3
+Version:        0.6.0
 Release:        0
 Summary:        Conversions from/to KDE containers
 License:        LGPL-2.0+ and LGPL-3.0+ and AGPL-3.0+
 Group:          Development/Libraries/C and C++
 Url:            https://kolab.org/about/libkolab
 Source:         
http://mirror.kolabsys.com/pub/releases/%{name}-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM 0001-Add-support-for-exceptions.patch
+Patch0001:      0001-Add-support-for-exceptions.patch
+# PATCH-FIX-UPSTREAM 0002-Support-for-THISANDFUTURE.patch
+Patch0002:      0002-Support-for-THISANDFUTURE.patch
+# PATCH-FIX-UPSTREAM 0003-Move-QT_DECLARE_METADATA-logic-to-cmake.patch
+Patch0003:      0003-Move-QT_DECLARE_METADATA-logic-to-cmake.patch
+# PATCH-FIX-UPSTREAM libkolab-0.6.0_check_for_generic_tag.patch
+Patch0100:      libkolab-0.6.0_check_for_generic_tag.patch
 BuildRequires:  boost-devel
 BuildRequires:  cmake >= 2.6
-BuildRequires:  libxerces-c-devel
 BuildRequires:  libkolabxml-devel >= 1.0
 BuildRequires:  libqt4-devel
+BuildRequires:  libxerces-c-devel
 BuildRequires:  php-devel >= 5.3
 BuildRequires:  python-devel
 BuildRequires:  swig >= 2.0
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-%if 0%{?with_kde}
+%if %{with kde}
 BuildRequires:  libkdepimlibs4-devel >= 4.9
 %else
 BuildRequires:  libcalendaring-devel
@@ -90,7 +100,7 @@
 Group:          Development/Libraries/C and C++
 Requires:       %{libname} = %{version}
 Requires:       libXerces-c-devel
-%if 0%{?with_kde}
+%if %{with kde}
 Requires:       libkdepimlibs4-devel >= 4.9
 %else
 Requires:       libcalendaring-devel
@@ -104,6 +114,10 @@
 
 %prep
 %setup -q
+%patch0001 -p1
+%patch0002 -p1
+%patch0003 -p1
+%patch0100 -p1
 
 # Lower cmake requirements for SLE 11
 %if 0%{?suse_version} < 1140
@@ -121,14 +135,18 @@
 # TESTS: require X and most fail anyway
 %cmake \
     -DLibkolabxml_DIR=%{_libdir}/cmake/Libkolabxml \
-%if 0%{?with_kde}
+%if %{with kde}
     -DUSE_LIBCALENDARING=FALSE \
 %else
     -DUSE_LIBCALENDARING=TRUE \
 %endif
     -DPHP_BINDINGS=TRUE -DPHP_INSTALL_DIR=%{php_extdir} \
     -DPYTHON_BINDINGS=TRUE -DPYTHON_INSTALL_DIR=%{python_sitearch} \
+%if %{with tests}
+    -DBUILD_TESTS=TRUE \
+%else
     -DBUILD_TESTS=FALSE \
+%endif
 %if 0%{?suse_version} < 1310
     -DCMAKE_INSTALL_PREFIX="%{_prefix}" \
     -DLIB_INSTALL_DIR=%{_lib} \
@@ -141,16 +159,16 @@
 # cmake: with earlier releases, we have to change dir manually, as 
%%cmake_install is not available
 %if 0%{?suse_version} < 1310
 cd build/
-%{__make} DESTDIR=%{buildroot} install
+make DESTDIR=%{buildroot} install
 %else
 %cmake_install
 %endif
 
-mkdir -p %{buildroot}/%{_datadir}/php5
-mv %{buildroot}/%{php_extdir}/*.php %{buildroot}/%{_datadir}/php5/.
+mkdir -p %{buildroot}%{_datadir}/php5
+mv %{buildroot}%{php_extdir}/*.php %{buildroot}%{_datadir}/php5/.
 
-mkdir -p %{buildroot}/%{php_confdir}
-cat >%{buildroot}/%{php_confdir}/kolab.ini <<EOF
+mkdir -p %{buildroot}%{php_confdir}
+cat >%{buildroot}%{php_confdir}/kolab.ini <<EOF
 ; Kolab libraries
 extension=kolabcalendaring.so
 extension=kolabicalendar.so
@@ -158,6 +176,23 @@
 extension=kolabshared.so
 EOF
 
+cat >%{buildroot}%{php_confdir}/kolabdummy.ini <<EOF
+; Kolab libraries
+extension=dummy.so
+EOF
+
+%if %{with tests}
+%check
+cd build/tests
+./benchmarktest || :
+./calendaringtest || :
+./formattest || :
+./freebusytest || :
+./icalendartest || :
+./kcalconversiontest || :
+./upgradetest || :
+%endif
+
 %post -n %{libname} -p /sbin/ldconfig
 
 %postun -n %{libname} -p /sbin/ldconfig
@@ -170,6 +205,7 @@
 %files -n php-%{libname}
 %defattr(-,root,root)
 %config(noreplace) %{php_confdir}/kolab.ini
+%config(noreplace) %{php_confdir}/kolabdummy.ini
 %{_datadir}/php5/kolabcalendaring.php
 %{php_extdir}/kolabcalendaring.so
 %{_datadir}/php5/kolabicalendar.php
@@ -178,6 +214,8 @@
 %{php_extdir}/kolabobject.so
 %{_datadir}/php5/kolabshared.php
 %{php_extdir}/kolabshared.so
+%{_datadir}/php5/dummy.php
+%{php_extdir}/dummy.so
 
 %files -n python-%{libname}
 %defattr(-,root,root)

++++++ 0001-Add-support-for-exceptions.patch ++++++
>From ad9c52c79a3b5de87bfa7a3f45e207e6d6ee1d4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <[email protected]>
Date: Wed, 18 Feb 2015 16:54:47 +0100
Subject: [PATCH 1/2] Add support for exceptions.

Read/Write Exceptions to iCal.

KOLAB: #4618
---
 icalendar/icalendar.cpp                       | 34 +++++++++++++++--
 tests/icalendartest.cpp                       | 53 ++++++++++++++++++++++++++-
 tests/icalendartest.h                         |  3 ++
 tests/testfiles/v3/event/errorswithsameid.ics | 45 +++++++++++++++++++++++
 tests/testfiles/v3/event/exceptions.ics       | 35 ++++++++++++++++++
 5 files changed, 166 insertions(+), 4 deletions(-)
 create mode 100644 tests/testfiles/v3/event/errorswithsameid.ics
 create mode 100644 tests/testfiles/v3/event/exceptions.ics

diff --git a/icalendar/icalendar.cpp b/icalendar/icalendar.cpp
index be7c0e6..3ed41ae 100644
--- a/icalendar/icalendar.cpp
+++ b/icalendar/icalendar.cpp
@@ -39,13 +39,21 @@ std::string toICal(const std::vector<Event> &events)
     KCalCore::Calendar::Ptr calendar(new 
KCalCore::MemoryCalendar(Kolab::Conversion::getTimeSpec(true, std::string())));
     foreach (const Event &event, events) {
         KCalCore::Event::Ptr kcalEvent = Conversion::toKCalCore(event);
-        kcalEvent->setCreated(KDateTime::currentUtcDateTime()); //sets dtstamp
+        if (!kcalEvent->created().isValid()) {
+            kcalEvent->setCreated(KDateTime::currentUtcDateTime()); //sets 
dtstamp
+        }
         calendar->addEvent(kcalEvent);
+        foreach(const Event &exception, event.exceptions()) {
+            KCalCore::Event::Ptr kcalException = 
Conversion::toKCalCore(exception);
+            if (!kcalException->created().isValid()) {
+                kcalException->setCreated(KDateTime::currentUtcDateTime()); 
//sets dtstamp
+            }
+            calendar->addEvent(kcalException);
+        }
     }
     KCalCore::ICalFormat format;
     format.setApplication("libkolab", LIBKOLAB_LIB_VERSION_STRING);
 //     qDebug() << format.createScheduleMessage(calendar->events().first(), 
KCalCore::iTIPRequest);
-
     return Conversion::toStdString(format.toString(calendar));
     
 }
@@ -57,8 +65,28 @@ std::vector< Event > fromICalEvents(const std::string &input)
     format.setApplication("libkolab", LIBKOLAB_LIB_VERSION_STRING);
     format.fromString(calendar, Conversion::fromStdString(input));
     std::vector<Event> events;
+    QMap< QString, int > uidMap;
     foreach (const KCalCore::Event::Ptr &event, calendar->events()) {
-        events.push_back(Conversion::fromKCalCore(*event));
+        Event e = Conversion::fromKCalCore(*event);
+        if (!e.recurrenceID().isValid()) {
+            if (uidMap.contains(event->uid())) {
+                e.setExceptions(events.at(uidMap[event->uid()]).exceptions());
+                events[uidMap[event->uid()]] = e;
+            } else {
+                events.push_back(e);
+                uidMap.insert(event->uid(), events.size()-1);
+            }
+        } else {
+            if (!uidMap.contains(event->uid())) {
+                Event e;
+                e.setUid("");
+                events.push_back(e);
+                uidMap.insert(event->uid(), events.size()-1);
+            }
+            std::vector<Event> exceptions = events.at( 
uidMap[event->uid()]).exceptions();
+            exceptions.push_back(e);
+            events.at(uidMap[event->uid()]).setExceptions(exceptions);
+        }
     }
     return events;
 }
diff --git a/tests/icalendartest.cpp b/tests/icalendartest.cpp
index 1e13d65..b1dd60b 100644
--- a/tests/icalendartest.cpp
+++ b/tests/icalendartest.cpp
@@ -24,6 +24,8 @@
 #include "icalendar/icalendar.h"
 
 #include "testhelpers.h"
+#include "testutils.h"
+#include <kolabformat/kolabobject.h>
 
 void ICalendarTest::testFromICalEvent()
 {
@@ -32,11 +34,60 @@ void ICalendarTest::testFromICalEvent()
     ev1.setStart(Kolab::cDateTime(2011,10,10,12,1,1,true));
     ev1.setEnd(Kolab::cDateTime(2011,10,11,12,1,1,true));
     events.push_back(ev1);
-    events.push_back(ev1);
     const std::vector<Kolab::Event> &result = 
Kolab::fromICalEvents(Kolab::toICal(events));
     qDebug() << QString::fromStdString(Kolab::toICal(result));
 }
 
+void ICalendarTest::testFromICalEventWithExceptions()
+{
+    QFile icalFile( getPath("v3/event/exceptions.ics") );
+    QVERIFY( icalFile.open( QFile::ReadOnly ) );
+    std::vector<Kolab::Event> events = 
Kolab::fromICalEvents(icalFile.readAll().constData());
+    QCOMPARE((int)events.size(), 1);
+    Kolab::Event out = events.at(0);
+    QCOMPARE((int)out.exceptions().size(), 2);
+}
+
+void ICalendarTest::testFromICalEventErrorsWithSameID()
+{
+    /* This is not a really usecase, it should make sure,
+     * that the underlying KCalCore strips out events with same uid/or 
recurrenceID.
+     * 2015/02/18: KCaclCore is only returns one event with one exception for 
the icalFile
+     * testdata/v3/event/errorswithsameid.ics
+     */
+    QFile icalFile( getPath("v3/event/errorswithsameid.ics") );
+    QVERIFY( icalFile.open( QFile::ReadOnly ) );
+    std::vector<Kolab::Event> events = 
Kolab::fromICalEvents(icalFile.readAll().constData());
+    QCOMPARE((int)events.size(), 1);
+    Kolab::Event out = events.at(0);
+    QCOMPARE((int)out.exceptions().size(), 1);
+}
+
+void ICalendarTest::testReadWriteForEventWithExceptions()
+{
+    std::vector<Kolab::Event> events;
+    Kolab::Event ev1;
+    ev1.setUid("uid");
+    ev1.setStart(Kolab::cDateTime(2011,10,10,12,1,1,true));
+    ev1.setEnd(Kolab::cDateTime(2011,10,11,12,1,1,true));
+    std::vector<Kolab::Event> exceptions;
+    Kolab::Event ex1;
+    ex1.setUid(ev1.uid());
+    ex1.setRecurrenceID(Kolab::cDateTime(2011,10,11,12,1,1,true), false);
+    exceptions.push_back(ex1);
+    ev1.setExceptions(exceptions);
+    Kolab::RecurrenceRule rrule;
+    rrule.setInterval(1);
+    rrule.setFrequency(Kolab::RecurrenceRule::Daily);
+    ev1.setRecurrenceRule(rrule);
+    events.push_back(ev1);
+    const std::vector<Kolab::Event> &result = 
Kolab::fromICalEvents(Kolab::toICal(events));
+    Kolab::Event out = result.at(0);
+    QCOMPARE((int)result.size(), 1);
+    QCOMPARE((int)out.exceptions().size(), 1);
+    QCOMPARE(out.exceptions().at(0).recurrenceID(), ex1.recurrenceID());
+}
+
 void ICalendarTest::testToICal()
 {
     std::vector<Kolab::Event> events;
diff --git a/tests/icalendartest.h b/tests/icalendartest.h
index 4f82ecc..7e1f405 100644
--- a/tests/icalendartest.h
+++ b/tests/icalendartest.h
@@ -27,6 +27,9 @@ private slots:
 //     void testEventConflict_data();
     void testToICal();
     void testFromICalEvent();
+    void testFromICalEventWithExceptions();
+    void testReadWriteForEventWithExceptions();
+    void testFromICalEventErrorsWithSameID();
 
     void testToITip();
     void testToIMip();
diff --git a/tests/testfiles/v3/event/errorswithsameid.ics 
b/tests/testfiles/v3/event/errorswithsameid.ics
new file mode 100644
index 0000000..1aa12a3
--- /dev/null
+++ b/tests/testfiles/v3/event/errorswithsameid.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Test//
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Daily
+DTSTART;VALUE=DATE-TIME:20150101T010000Z
+DTEND;VALUE=DATE-TIME:20150101T020000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME;RANGE=THISANDFUTURE:20150103T000000Z
+SUMMARY:thisandfuture
+DTSTART;VALUE=DATE-TIME:20150103T020000Z
+DTEND;VALUE=DATE-TIME:20150103T030000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Invalid second incidence
+DTSTART;VALUE=DATE-TIME:20150101T050000Z
+DTEND;VALUE=DATE-TIME:20150101T060000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME:20150103T000000Z
+SUMMARY: same recurence id like the one with THISANDFUTURE
+DTSTART;VALUE=DATE-TIME:20150103T030000Z
+DTEND;VALUE=DATE-TIME:20150103T040000Z
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
diff --git a/tests/testfiles/v3/event/exceptions.ics 
b/tests/testfiles/v3/event/exceptions.ics
new file mode 100644
index 0000000..60776c6
--- /dev/null
+++ b/tests/testfiles/v3/event/exceptions.ics
@@ -0,0 +1,35 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Test//
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Daily
+DTSTART;VALUE=DATE-TIME:20150101T010000Z
+DTEND;VALUE=DATE-TIME:20150101T020000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME;RANGE=THISANDFUTURE:20150103T000000Z
+SUMMARY:thisandfuture
+DTSTART;VALUE=DATE-TIME:20150103T020000Z
+DTEND;VALUE=DATE-TIME:20150103T030000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME:20150104T000000Z
+SUMMARY: exception
+DTSTART;VALUE=DATE-TIME:20150104T030000Z
+DTEND;VALUE=DATE-TIME:20150104T040000Z
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
-- 
2.1.0

++++++ 0002-Support-for-THISANDFUTURE.patch ++++++
>From b756fa4ceef4b953859878f3c700ed45ecaed409 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <[email protected]>
Date: Wed, 18 Feb 2015 16:50:59 +0100
Subject: [PATCH 2/2] Support for THISANDFUTURE

convert ThisAndFuture from/to iCal.

KOLAB: 4654
---
 conversion/kcalconversion.cpp |  7 ++++---
 tests/icalendartest.cpp       |  3 ++-
 tests/kcalconversiontest.cpp  | 20 ++++++++++++++++++--
 3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/conversion/kcalconversion.cpp b/conversion/kcalconversion.cpp
index a82b6a2..b06a5d0 100644
--- a/conversion/kcalconversion.cpp
+++ b/conversion/kcalconversion.cpp
@@ -661,7 +661,8 @@ void setTodoEvent(KCalCore::Incidence &i, const T &e)
         i.setNonKDECustomProperty(CUSTOM_KOLAB_URL, fromStdString(e.url()));
     }
     if (e.recurrenceID().isValid()) {
-        i.setRecurrenceId(toDate(e.recurrenceID())); //TODO THISANDFUTURE
+        i.setRecurrenceId(toDate(e.recurrenceID()));
+        i.setThisAndFuture(e.thisAndFuture());
     }
     setRecurrence(i, e);
     foreach (const Kolab::Alarm a, e.alarms()) {
@@ -699,7 +700,7 @@ void setTodoEvent(KCalCore::Incidence &i, const T &e)
         alarm->setSnoozeTime(toDuration(a.duration()));
         alarm->setRepeatCount(a.numrepeat());
         alarm->setEnabled(true);
-        i.addAlarm(alarm);       
+        i.addAlarm(alarm);
     }
 }
 
@@ -712,7 +713,7 @@ void getTodoEvent(T &i, const I &e)
         
i.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, 
toStdString(e.organizer()->email()), toStdString(e.organizer()->name()))); 
//TODO handle uid too
     }
     i.setUrl(toStdString(e.nonKDECustomProperty(CUSTOM_KOLAB_URL)));
-    i.setRecurrenceID(fromDate(e.recurrenceId()), false); //TODO THISANDFUTURE
+    i.setRecurrenceID(fromDate(e.recurrenceId()), e.thisAndFuture());
     getRecurrence(i, e);
     std::vector <Kolab::Alarm> alarms;
     foreach (const KCalCore::Alarm::Ptr &a, e.alarms()) {
diff --git a/tests/icalendartest.cpp b/tests/icalendartest.cpp
index b1dd60b..bf55031 100644
--- a/tests/icalendartest.cpp
+++ b/tests/icalendartest.cpp
@@ -73,7 +73,7 @@ void ICalendarTest::testReadWriteForEventWithExceptions()
     std::vector<Kolab::Event> exceptions;
     Kolab::Event ex1;
     ex1.setUid(ev1.uid());
-    ex1.setRecurrenceID(Kolab::cDateTime(2011,10,11,12,1,1,true), false);
+    ex1.setRecurrenceID(Kolab::cDateTime(2011,10,11,12,1,1,true), true);
     exceptions.push_back(ex1);
     ev1.setExceptions(exceptions);
     Kolab::RecurrenceRule rrule;
@@ -86,6 +86,7 @@ void ICalendarTest::testReadWriteForEventWithExceptions()
     QCOMPARE((int)result.size(), 1);
     QCOMPARE((int)out.exceptions().size(), 1);
     QCOMPARE(out.exceptions().at(0).recurrenceID(), ex1.recurrenceID());
+    QCOMPARE(out.exceptions().at(0).thisAndFuture(), true);
 }
 
 void ICalendarTest::testToICal()
diff --git a/tests/kcalconversiontest.cpp b/tests/kcalconversiontest.cpp
index c62a8e0..e1d4ca9 100644
--- a/tests/kcalconversiontest.cpp
+++ b/tests/kcalconversiontest.cpp
@@ -162,7 +162,7 @@ void KCalConversionTest::testConversion_data()
         kcal.setDtEnd(toDate(date2));
         kcal.setTransparency(KCalCore::Event::Transparent);
         
-        kcal.setRecurrenceId(toDate(date2)); //TODO THISANDFUTURE
+        kcal.setRecurrenceId(toDate(date2));
         kcal.recurrence()->setDaily(3);
         kcal.recurrence()->setDuration(5);
         kcal.recurrence()->addRDateTime(toDate(date2));
@@ -231,7 +231,7 @@ void KCalConversionTest::testConversion_data()
         rrule.setBymonth(intVector);
         
         kolab.setRecurrenceRule(rrule);
-        kolab.setRecurrenceID(date2, true);
+        kolab.setRecurrenceID(date2, false);
         kolab.setRecurrenceDates(std::vector<Kolab::cDateTime>() << date2 << 
Kolab::cDateTime(date2.year(), date2.month(), date2.day()));
         kolab.setExceptionDates(std::vector<Kolab::cDateTime>() << date3 << 
Kolab::cDateTime(date3.year(), date3.month(), date3.day()));
         
@@ -337,7 +337,21 @@ void KCalConversionTest::testConversion_data()
         
kolab.setSummary(std::string(QString("äöü%@$£é¤¼²°€Š�").toUtf8().constData()));
         
         QTest::newRow("latin1+Unicode") << kcal << kolab;
+    }
+    {
+        KCalCore::Event kcal;
+        kcal.setUid("uid");
+        kcal.setCreated(toDate(date));
+        kcal.setLastModified(toDate(date));
+        kcal.setRecurrenceId(toDate(date));
+        kcal.setThisAndFuture(true);
 
+        Kolab::Event kolab;
+        kolab.setUid("uid");
+        kolab.setCreated(date);
+        kolab.setLastModified(date);
+        kolab.setRecurrenceID(date, true);
+        QTest::newRow("thisandfuture") << kcal << kolab;
     }
 }
 
@@ -361,6 +375,7 @@ void KCalConversionTest::testConversion()
     QCOMPARE(e->transparency(), kcal.transparency());
     QCOMPARE(*e->recurrence(), *kcal.recurrence());
     QCOMPARE(e->recurrenceId(), kcal.recurrenceId());
+    QCOMPARE(e->thisAndFuture(), kcal.thisAndFuture());
     QCOMPARE(e->recurrenceType(), kcal.recurrenceType());
     QCOMPARE(e->summary(), kcal.summary());
     QCOMPARE(e->description(), kcal.description());
@@ -396,6 +411,7 @@ void KCalConversionTest::testConversion()
     
     QCOMPARE(b.recurrenceRule(), kolab.recurrenceRule());
     QCOMPARE(b.recurrenceID(), kolab.recurrenceID());
+    QCOMPARE(b.thisAndFuture(), kolab.thisAndFuture());
     QCOMPARE(b.recurrenceDates(), kolab.recurrenceDates());
     QCOMPARE(b.exceptionDates(), kolab.exceptionDates());
     
-- 
2.1.0

++++++ 0003-Move-QT_DECLARE_METADATA-logic-to-cmake.patch ++++++
>From e397921e73de2926a8572dfed73d2730a3ae456e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <[email protected]>
Date: Thu, 19 Feb 2015 12:32:58 +0100
Subject: [PATCH 3/3] Move QT_DECLARE_METADATA logic to cmake

Because libcalendaring now also have versions that
Q_DECLARE_METATYPE(KCalCore::Duration) already we make it explicit once
within cmake and once inside libcalendaring.
---
 CMakeLists.txt      | 4 ++++
 tests/testhelpers.h | 8 ++------
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a323b32..02783e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,6 +82,10 @@ if("${KdepimLibs_VERSION}" VERSION_GREATER "4.8.40" OR 
USE_LIBCALENDARING)
     add_definitions(-DKDEPIMLIBS_VERSION_DEVEL)
 endif()
 
+if ("${KdepimLibs_VERSION}" VERSION_GREATER  "4.11.52")
+    add_definitions( -DLIBCALENDARING_DURATION_DECLARED)
+endif()
+
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long 
-ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith 
-Wformat-security -fno-exceptions -DQT_NO_EXCEPTIONS -fno-common 
-Woverloaded-virtual -fno-threadsafe-statics -fvisibility=hidden 
-Werror=return-type -fvisibility-inlines-hidden -fexceptions -UQT_NO_EXCEPTIONS 
-fPIC -g" )
 # message("${CMAKE_CXX_FLAGS}")
 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DQT_NO_DEBUG")
diff --git a/tests/testhelpers.h b/tests/testhelpers.h
index f0e3889..822f9bc 100644
--- a/tests/testhelpers.h
+++ b/tests/testhelpers.h
@@ -45,12 +45,8 @@ Q_DECLARE_METATYPE(KCalCore::Event);
 Q_DECLARE_METATYPE(KCalCore::Todo);
 Q_DECLARE_METATYPE(KCalCore::Journal);
 
-#if KDEPIMLIBS_VERSION_MAJOR <= 4
-#if KDEPIMLIBS_VERSION_MINOR <= 11
-#if KDEPIMLIBS_VERSION_PATCH < 52
-Q_DECLARE_METATYPE(KCalCore::Duration);
-#endif
-#endif
+#ifndef LIBCALENDARING_DURATION_DECLARED
+    Q_DECLARE_METATYPE(KCalCore::Duration);
 #endif
 
 namespace QTest {
-- 
2.1.0

++++++ libkolab-0.5.3.tar.gz -> libkolab-0.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/CMakeLists.txt 
new/libkolab-0.6.0/CMakeLists.txt
--- old/libkolab-0.5.3/CMakeLists.txt   2014-08-26 13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/CMakeLists.txt   2015-02-01 18:23:05.000000000 +0100
@@ -31,9 +31,9 @@
 # 0.1.1 (patch release for 0.1.0)
 # 0.2 (0.2 development version towards 0.2.0)
 set(Libkolab_VERSION_MAJOR 0)
-set(Libkolab_VERSION_MINOR 5)
+set(Libkolab_VERSION_MINOR 6)
 # Enable the full x.y.z version only for release versions
-set(Libkolab_VERSION_PATCH 3)
+set(Libkolab_VERSION_PATCH 0)
 set(Libkolab_VERSION 
${Libkolab_VERSION_MAJOR}.${Libkolab_VERSION_MINOR}.${Libkolab_VERSION_PATCH} )
 #set(Libkolab_VERSION ${Libkolab_VERSION_MAJOR}.${Libkolab_VERSION_MINOR} )
 set(Libkolab_VERSION_STRING ${CMAKE_PROJECT_NAME}-${Libkolab_VERSION})
@@ -55,7 +55,7 @@
 include(MacroLogFeature)
 
 # Do the building
-find_package(Libkolabxml 1.0 REQUIRED)
+find_package(Libkolabxml 1.1 REQUIRED)
 macro_log_feature(Libkolabxml_FOUND "Libkolabxml" "Kolab XML Format 3 
serializing library" "http://git.kolab.org/libkolabxml/"; TRUE "1.0" "Required 
for reading/writing Kolab XML Objects")
 
 find_package(Qt4 4.6.0 REQUIRED)
@@ -91,7 +91,7 @@
     set( KDE_LIBRARIES ${Libcalendaring_LIBRARIES} )
     message("${Libcalendaring_INCLUDE_DIRS} ${Libcalendaring_LIBRARIES}")
 else()
-    set( KDE_INCLUDES ${KDEPIMLIBS_INCLUDE_DIRS} ${KDE4_INCLUDE_DIR})
+    set( KDE_INCLUDES ${KDEPIMLIBS_INCLUDE_DIRS} ${KDE4_INCLUDES} )
     set( KDE_LIBRARIES
         ${KDEPIMLIBS_KCALCORE_LIBS}
         ${KDEPIMLIBS_KABC_LIBS}
@@ -119,6 +119,17 @@
 
 configure_file(libkolab-version.h.cmake 
"${CMAKE_BINARY_DIR}/libkolab-version.h" @ONLY)
 
+set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
+set(CMAKE_REQUIRED_INCLUDES "/opt/devel/kolab/include/")
+# include(CheckIncludeFileCXX)
+# check_include_file_cxx(akonadi/tag.h HAVE_TAG_H)
+#check_include_file_cxx doesn't work for some reason, so we use find_path 
instead.
+#we have to make sure that we only search in KDE_INCLUDES though, to not 
accidentally include a system akonadi/tag.h
+#when we're searching for one in libcalendaring.
+find_path(HAVE_TAG_H akonadi/tag.h PATHS ${KDE_INCLUDES} NO_DEFAULT_PATH 
NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
+find_path(HAVE_RELATION_H akonadi/relation.h PATHS ${KDE_INCLUDES} 
NO_DEFAULT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_PATH 
NO_CMAKE_ENVIRONMENT_PATH)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libkolab_config.h.in 
${CMAKE_CURRENT_BINARY_DIR}/libkolab_config.h)
+
 add_subdirectory(kolabformatV2)
 add_subdirectory(conversion)
 add_subdirectory(calendaring)
@@ -180,6 +191,7 @@
     kolabformat/errorhandler.h
     kolabformat/xmlobject.h
     kolabformat/mimeobject.h
+    ${CMAKE_CURRENT_BINARY_DIR}/libkolab_config.h
     conversion/kcalconversion.h
     conversion/kabcconversion.h
     conversion/commonconversion.h
@@ -209,5 +221,6 @@
 
 if(PHP_BINDINGS)
     generatePHPBindings(kolabshared shared.i)
+    generatePHPBindings(dummy dummy.i)
     add_subdirectory(kolabformat/php)
 endif(PHP_BINDINGS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/calendaring/php/test.php 
new/libkolab-0.6.0/calendaring/php/test.php
--- old/libkolab-0.5.3/calendaring/php/test.php 2014-08-26 13:39:06.000000000 
+0200
+++ new/libkolab-0.6.0/calendaring/php/test.php 2015-02-01 18:23:05.000000000 
+0100
@@ -97,6 +97,79 @@
 </icalendar>
 EOF;
 
+$rdates = <<<EOF
+<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">
+  <vcalendar>
+    <properties>
+      <prodid>
+        <text>Roundcube-libkolab-0.9 Libkolabxml-1.1</text>
+      </prodid>
+      <version>
+        <text>2.0</text>
+      </version>
+      <x-kolab-version>
+        <text>3.1.0</text>
+      </x-kolab-version>
+    </properties>
+    <components>
+      <vevent>
+        <properties>
+          <uid>
+            <text>49961C572093EC3FC125799C004A200F-Lotus_Notes_Generated</text>
+          </uid>
+          <created>
+            <date-time>2014-02-28T12:57:42Z</date-time>
+          </created>
+          <dtstamp>
+            <date-time>2014-02-28T12:57:42Z</date-time>
+          </dtstamp>
+          <sequence>
+            <integer>0</integer>
+          </sequence>
+          <class>
+            <text>PUBLIC</text>
+          </class>
+          <dtstart>
+            <parameters>
+              <tzid>
+                <text>/kolab.org/Europe/Amsterdam</text>
+              </tzid>
+            </parameters>
+            <date-time>2012-03-30T04:00:00</date-time>
+          </dtstart>
+          <dtend>
+            <parameters>
+              <tzid>
+                <text>/kolab.org/Europe/Amsterdam</text>
+              </tzid>
+            </parameters>
+            <date-time>2012-03-30T20:00:00</date-time>
+          </dtend>
+          <transp>
+            <text>TRANSPARENT</text>
+          </transp>
+          <rdate>
+            <date>2012-03-30</date>
+            <date>2013-03-30</date>
+            <date>2014-03-30</date>
+            <date>2015-03-30</date>
+            <date>2016-03-30</date>
+            <date>2017-03-30</date>
+            <date>2018-03-30</date>
+            <date>2019-03-30</date>
+            <date>2020-03-30</date>
+            <date>2021-03-30</date>
+          </rdate>
+          <summary>
+            <text>Geburtstag Jane Doe (30.03.1969)</text>
+          </summary>
+        </properties>
+      </vevent>
+    </components>
+  </vcalendar>
+</icalendar>
+EOF;
+
 $e = kolabformat::readEvent($xml, false);
 $ec = new EventCal($e);
 
@@ -131,6 +204,33 @@
        "EventCal::getLastOccurence"
 );
 
+// test event with RDATE list
+
+$e = kolabformat::readEvent($rdates, false);
+$ec = new EventCal($e);
+
+$rstart = new cDateTime(2012,3,1, 0,0,0);
+$next = new cDateTime($ec->getNextOccurence($rstart));
+assertequal(
+       sprintf("%d-%02d-%02d %02d:%02d:%02d", $next->year(), $next->month(), 
$next->day(), $next->hour(), $next->minute(), $next->second()),
+       "2012-03-30 04:00:00",
+       "RDATE first recurrence"
+);
+
+$next = new cDateTime($ec->getNextOccurence($next));
+assertequal(
+       sprintf("%d-%02d-%02d %02d:%02d:%02d", $next->year(), $next->month(), 
$next->day(), $next->hour(), $next->minute(), $next->second()),
+       "2013-03-30 04:00:00",
+       "RDATE next recurrence"
+);
+
+$last = new cDateTime($ec->getLastOccurrence());
+assertequal(
+       sprintf("%d-%02d-%d %02d:%02d:%02d", $last->year(), $last->month(), 
$last->day(), $last->hour(), $last->minute(), $last->second()),
+       "2021-03-30 04:00:00",
+       "RDATE last occurence"
+);
+
 
 // terminate with error status
 exit($errors);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/conversion/timezoneconverter.cpp 
new/libkolab-0.6.0/conversion/timezoneconverter.cpp
--- old/libkolab-0.5.3/conversion/timezoneconverter.cpp 2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/conversion/timezoneconverter.cpp 2015-02-01 
18:23:05.000000000 +0100
@@ -39,13 +39,15 @@
     if (guessedTimezone.isEmpty()) {
         guessedTimezone = fromGMTOffsetTimezone(tz);
     }
-    if (guessedTimezone.isEmpty()) {
-        //slower but also finds outdated zones
-        timezone = KSystemTimeZones::readZone(tz);
-        if (timezone.isValid()) {
-            return tz;
-        }
-    }
+//     if (guessedTimezone.isEmpty()) {
+//         //slower but also finds outdated zones
+//         timezone = KSystemTimeZones::readZone(tz);
+//         if (timezone.isValid()) {
+//             //This thinks all kinds of shit is valid, including 
/etc/localtime. Let's verify again.
+//             qDebug() << "found " << tz;
+//             return tz;
+//         }
+//     }
     Debug() << "Guessed timezone and found: " << guessedTimezone;
     return guessedTimezone;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/dummy.i new/libkolab-0.6.0/dummy.i
--- old/libkolab-0.5.3/dummy.i  1970-01-01 01:00:00.000000000 +0100
+++ new/libkolab-0.6.0/dummy.i  2015-02-01 18:23:05.000000000 +0100
@@ -0,0 +1 @@
+/* This is a dummy plugin that does nothing. See 
https://issues.kolab.org/show_bug.cgi?id=2050 */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/kolabformat/kolabdefinitions.h 
new/libkolab-0.6.0/kolabformat/kolabdefinitions.h
--- old/libkolab-0.5.3/kolabformat/kolabdefinitions.h   2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/kolabformat/kolabdefinitions.h   2015-02-01 
18:23:05.000000000 +0100
@@ -63,6 +63,7 @@
 #define KOLAB_TYPE_DICT    "application/x-vnd.kolab.configuration.dictionary"
 #define KOLAB_TYPE_FREEBUSY    "application/x-vnd.kolab.freebusy"
 #define KOLAB_TYPE_FILE    "application/x-vnd.kolab.file"
+#define KOLAB_TYPE_RELATION "application/x-vnd.kolab.configuration.relation"
 
 enum Version {
     KolabV2,
@@ -78,7 +79,8 @@
     DistlistObject,
     NoteObject,
     DictionaryConfigurationObject,
-    FreebusyObject
+    FreebusyObject,
+    RelationConfigurationObject
 };
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/kolabformat/kolabobject.cpp 
new/libkolab-0.6.0/kolabformat/kolabobject.cpp
--- old/libkolab-0.5.3/kolabformat/kolabobject.cpp      2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/kolabformat/kolabobject.cpp      2015-02-01 
18:23:05.000000000 +0100
@@ -51,6 +51,7 @@
 static inline QString configurationKolabType() { return 
QString::fromLatin1(KOLAB_TYPE_CONFIGURATION); }
 static inline QString dictKolabType() { return 
QString::fromLatin1(KOLAB_TYPE_DICT); }
 static inline QString freebusyKolabType() { return 
QString::fromLatin1(KOLAB_TYPE_FREEBUSY); }
+static inline QString relationKolabType() { return 
QString::fromLatin1(KOLAB_TYPE_RELATION); }
 
 static inline QString xCalMimeType() { return 
QString::fromLatin1(MIME_TYPE_XCAL); };
 static inline QString xCardMimeType() { return 
QString::fromLatin1(MIME_TYPE_XCARD); };
@@ -61,6 +62,92 @@
     return fromXML<KCalCore::Event::Ptr, KolabV2::Event>(xmlData, attachments);
 }
 
+QString ownUrlDecode(QByteArray encodedParam)
+{
+    encodedParam.replace('+', ' ');
+    return QUrl::fromPercentEncoding(encodedParam);
+}
+
+RelationMember parseMemberUrl(const QString &string)
+{
+    if (string.startsWith("urn:uuid:")) {
+        RelationMember member;
+        member.gid = string.mid(9);
+        return member;
+    }
+    QUrl url(QUrl::fromEncoded(string.toLatin1()));
+    QList<QByteArray> path;
+    Q_FOREACH(const QByteArray &fragment, url.encodedPath().split('/')) {
+        path.append(ownUrlDecode(fragment).toUtf8());
+    }
+    // qDebug() << path;
+    bool isShared = false;
+    int start = path.indexOf("user");
+    if (start < 0) {
+        start = path.indexOf("shared");
+        isShared = true;
+    }
+    if (start < 0) {
+        Warning() << "Couldn't find \"user\" or \"shared\" in path: " << path;
+        return RelationMember();
+    }
+    path = path.mid(start + 1);
+    if (path.size() < 2) {
+        Warning() << "Incomplete path: " << path;
+        return RelationMember();
+    }
+    RelationMember member;
+    if (!isShared) {
+        member.user = path.takeFirst();
+    }
+    member.uid = path.takeLast().toLong();
+    member.mailbox = path;
+    member.messageId = ownUrlDecode(url.encodedQueryItemValue("message-id"));
+    member.subject = ownUrlDecode(url.encodedQueryItemValue("subject"));
+    member.date = ownUrlDecode(url.encodedQueryItemValue("date"));
+    // qDebug() << member.uid << member.mailbox;
+    return member;
+}
+
+static QByteArray join(const QList<QByteArray> &list, const QByteArray &c)
+{
+    QByteArray result;
+    Q_FOREACH (const QByteArray &a, list) {
+        result += a + c;
+    }
+    result.chop(c.size());
+    return result;
+}
+
+KOLAB_EXPORT QString generateMemberUrl(const RelationMember &member)
+{
+    if (!member.gid.isEmpty()) {
+        return QString("urn:uuid:%1").arg(member.gid);
+    }
+    QUrl url;
+    url.setScheme("imap");
+    QList<QByteArray> path;
+    path << "/";
+    if (!member.user.isEmpty()) {
+        path << "user";
+        path << QUrl::toPercentEncoding(member.user.toLatin1());
+    } else {
+        path << "shared";
+    }
+    Q_FOREACH(const QByteArray &mb, member.mailbox) {
+        path << QUrl::toPercentEncoding(mb);
+    }
+    path << QByteArray::number(member.uid);
+    url.setEncodedPath("/" + join(path, "/"));
+
+    QList<QPair<QByteArray, QByteArray> > queryItems;
+    queryItems.append(qMakePair(QString::fromLatin1("message-id").toLatin1(), 
QUrl::toPercentEncoding(member.messageId)));
+    queryItems.append(qMakePair(QString::fromLatin1("subject").toLatin1(), 
QUrl::toPercentEncoding(member.subject)));
+    queryItems.append(qMakePair(QString::fromLatin1("date").toLatin1(), 
QUrl::toPercentEncoding(member.date)));
+    url.setEncodedQueryItems(queryItems);
+
+    return QString::fromLatin1(url.toEncoded());
+}
 
 //@cond PRIVATE
 class KolabObjectReader::Private
@@ -77,7 +164,7 @@
 
     ObjectType readKolabV2(const KMime::Message::Ptr &msg, Kolab::ObjectType 
objectType);
     ObjectType readKolabV3(const KMime::Message::Ptr &msg, Kolab::ObjectType 
objectType);
-    
+
     KCalCore::Incidence::Ptr mIncidence;
     KABC::Addressee mAddressee;
     KABC::ContactGroup mContactGroup;
@@ -90,6 +177,14 @@
     ObjectType mOverrideObjectType;
     Version mOverrideVersion;
     bool mDoOverrideVersion;
+
+#ifdef HAVE_RELATION_H
+    Akonadi::Relation mRelation;
+#endif
+#ifdef HAVE_TAG_H
+    Akonadi::Tag mTag;
+    QStringList mTagMembers;
+#endif
 };
 //@endcond
 
@@ -101,7 +196,7 @@
 KolabObjectReader::KolabObjectReader(const KMime::Message::Ptr& msg)
 : d( new KolabObjectReader::Private )
 {
-    d->mObjectType = parseMimeMessage(msg);
+    parseMimeMessage(msg);
 }
 
 KolabObjectReader::~KolabObjectReader()
@@ -138,6 +233,8 @@
         return FreebusyObject;
     } else if (type.contains(dictKolabType())) { //Previous versions appended 
the language to the type
         return DictionaryConfigurationObject;
+    } else if (type == relationKolabType()) {
+        return RelationConfigurationObject;
     }
     Warning() << "Unknown object type: " << type;
     return Kolab::InvalidObject;
@@ -162,6 +259,8 @@
             return KOLAB_TYPE_NOTE;
         case DictionaryConfigurationObject:
             return KOLAB_TYPE_CONFIGURATION;
+        case RelationConfigurationObject:
+            return KOLAB_TYPE_RELATION;
         default:
             Critical() << "unknown type "<< type;
     }
@@ -181,6 +280,7 @@
             return MIME_TYPE_XCARD;
         case NoteObject:
         case DictionaryConfigurationObject:
+        case RelationConfigurationObject:
             return MIME_TYPE_KOLAB;
         default:
             Critical() << "unknown type "<< type;
@@ -326,6 +426,44 @@
             mFreebusy = fb;
         }
             break;
+#ifdef HAVE_TAG_H
+        case RelationConfigurationObject: {
+            const Kolab::Configuration &configuration = 
Kolab::readConfiguration(xml, false);
+            const Kolab::Relation &relation = configuration.relation();
+
+            if (relation.type() == "tag") {
+                mTag = Akonadi::Tag();
+                mTag.setName(Conversion::fromStdString(relation.name()));
+                
mTag.setGid(Conversion::fromStdString(configuration.uid()).toLatin1());
+                mTag.setType(Akonadi::Tag::GENERIC);
+
+                mTagMembers.reserve(relation.members().size());
+                foreach (const std::string &member, relation.members()) {
+                    mTagMembers << Conversion::fromStdString(member);
+                }
+            } else if (relation.type() == "generic") {
+#ifdef HAVE_RELATION_H
+                if (relation.members().size() == 2) {
+                    mRelation = Akonadi::Relation();
+                    
mRelation.setRemoteId(Conversion::fromStdString(configuration.uid()).toLatin1());
+                    mRelation.setType(Akonadi::Relation::GENERIC);
+
+                    mTagMembers.reserve(relation.members().size());
+                    foreach (const std::string &member, relation.members()) {
+                        mTagMembers << Conversion::fromStdString(member);
+                    }
+                } else {
+                    Critical() << "generic relation had wrong number of 
members:" << relation.members().size();
+                    printMessageDebugInfo(msg);
+                }
+#endif
+            } else {
+                Critical() << "unknown configuration object type" << 
relation.type();
+                printMessageDebugInfo(msg);
+            }
+        }
+            break;
+#endif
         default:
             Critical() << "no kolab object found ";
             printMessageDebugInfo(msg);
@@ -452,6 +590,35 @@
     return d->mFreebusy;
 }
 
+#ifdef HAVE_TAG_H
+bool KolabObjectReader::isTag() const
+{
+    return !d->mTag.gid().isEmpty();
+}
+
+Akonadi::Tag KolabObjectReader::getTag() const
+{
+    return d->mTag;
+}
+
+QStringList KolabObjectReader::getTagMembers() const
+{
+    return d->mTagMembers;
+}
+#endif
+
+#ifdef HAVE_RELATION_H
+bool KolabObjectReader::isRelation() const
+{
+    return d->mRelation.isValid();
+}
+
+Akonadi::Relation KolabObjectReader::getRelation() const
+{
+    return d->mRelation;
+}
+#endif
+
 
 //Normalize incidences before serializing them
 KCalCore::Incidence::Ptr normalizeIncidence(KCalCore::Incidence::Ptr original)
@@ -630,9 +797,58 @@
     return  Mime::createMessage(Conversion::fromStdString(freebusy.uid()), 
xCalMimeType(), freebusyKolabType(), 
Conversion::fromStdString(v3String).toUtf8(), true, getProductId(productId));
 }
 
+#ifdef HAVE_TAG_H
+KMime::Message::Ptr writeRelationHelper(const Kolab::Relation &relation, const 
QByteArray &uid, const QString &productId)
+{
+    Kolab::Configuration configuration(relation); //TODO preserve 
creation/lastModified date
+    configuration.setUid(uid.constData());
+    const std::string &v3String = Kolab::writeConfiguration(configuration, 
Conversion::toStdString(getProductId(productId)));
+    ErrorHandler::handleLibkolabxmlErrors();
+    return  
Mime::createMessage(Conversion::fromStdString(configuration.uid()), 
kolabMimeType(), relationKolabType(), 
Conversion::fromStdString(v3String).toUtf8(), true, getProductId(productId));
+}
+
+KMime::Message::Ptr KolabObjectWriter::writeTag(const Akonadi::Tag &tag, const 
QStringList &members, Version v, const QString &productId)
+{
+    ErrorHandler::clearErrors();
+    if (v != KolabV3) {
+        Critical() << "only v3 implementation available";
+    }
+
+    Kolab::Relation relation(Conversion::toStdString(tag.name()), "tag");
+    std::vector<std::string> m;
+    m.reserve(members.count());
+    foreach (const QString &member, members) {
+        m.push_back(Conversion::toStdString(member));
+    }
+    relation.setMembers(m);
+
+    return writeRelationHelper(relation, tag.gid(), productId);
+}
+#endif
+
+#ifdef HAVE_RELATION_H
+KMime::Message::Ptr KolabObjectWriter::writeRelation(const Akonadi::Relation 
&relation, const QStringList &items, Version v, const QString &productId)
+{
+    ErrorHandler::clearErrors();
+    if (v != KolabV3) {
+        Critical() << "only v3 implementation available";
+    }
 
+    if (items.size() != 2) {
+        Critical() << "Wrong number of members for generic relation.";
+        return KMime::Message::Ptr();
+    }
 
+    Kolab::Relation kolabRelation(std::string(), "generic");
+    std::vector<std::string> m;
+    m.reserve(2);
+    m.push_back(Conversion::toStdString(items.at(0)));
+    m.push_back(Conversion::toStdString(items.at(1)));
+    kolabRelation.setMembers(m);
 
+    return writeRelationHelper(kolabRelation, relation.remoteId(), productId);
+}
+#endif
 
 
 }; //Namespace
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/kolabformat/kolabobject.h 
new/libkolab-0.6.0/kolabformat/kolabobject.h
--- old/libkolab-0.5.3/kolabformat/kolabobject.h        2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/kolabformat/kolabobject.h        2015-02-01 
18:23:05.000000000 +0100
@@ -20,6 +20,15 @@
 
 #include <kolab_export.h>
 
+#include <libkolab_config.h>
+
+#ifdef HAVE_TAG_H
+#include <akonadi/item.h>
+#include <akonadi/tag.h>
+#endif
+#ifdef HAVE_RELATION_H
+#include <akonadi/relation.h>
+#endif
 #include <kabc/addressee.h>
 #include <kabc/contactgroup.h>
 #include <kcalcore/incidence.h>
@@ -37,6 +46,18 @@
 
 KOLAB_EXPORT KCalCore::Event::Ptr readV2EventXML(const QByteArray &xmlData, 
QStringList &attachments);
 
+struct KOLAB_EXPORT RelationMember {
+    QString messageId;
+    QString subject;
+    QString date;
+    QList<QByteArray> mailbox;
+    QString user;
+    qint64 uid;
+    QString gid;
+};
+KOLAB_EXPORT RelationMember parseMemberUrl(const QString &url);
+KOLAB_EXPORT QString generateMemberUrl(const RelationMember &url);
+
 /**
  * Class to read Kolab Mime files
  * 
@@ -87,9 +108,20 @@
     KMime::Message::Ptr getNote() const;
     QStringList getDictionary(QString &lang) const;
     Freebusy getFreebusy() const;
+#ifdef HAVE_TAG_H
+    bool isTag() const;
+    Akonadi::Tag getTag() const;
+    QStringList getTagMembers() const;
+#endif
+#ifdef HAVE_RELATION_H
+    bool isRelation() const;
+    Akonadi::Relation getRelation() const;
+#endif
 
 private:
     //@cond PRIVATE
+    KolabObjectReader(const KolabObjectReader &other);
+    KolabObjectReader &operator=(const KolabObjectReader &rhs);
     class Private;
     Private *const d;
     //@endcond
@@ -111,6 +143,12 @@
     static KMime::Message::Ptr writeNote(const KMime::Message::Ptr &, Version 
v = KolabV3, const QString &productId = QString());
     static KMime::Message::Ptr writeDictionary(const QStringList &, const 
QString &lang, Version v = KolabV3, const QString &productId = QString());
     static KMime::Message::Ptr writeFreebusy(const Kolab::Freebusy &, Version 
v = KolabV3, const QString &productId = QString());
+#ifdef HAVE_TAG_H
+    static KMime::Message::Ptr writeTag(const Akonadi::Tag &, const 
QStringList &items, Version v = KolabV3, const QString &productId = QString());
+#endif
+#ifdef HAVE_RELATION_H
+    static KMime::Message::Ptr writeRelation(const Akonadi::Relation &, const 
QStringList &items, Version v = KolabV3, const QString &productId = QString());
+#endif
     
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/libkolab_config.h.in 
new/libkolab-0.6.0/libkolab_config.h.in
--- old/libkolab-0.5.3/libkolab_config.h.in     1970-01-01 01:00:00.000000000 
+0100
+++ new/libkolab-0.6.0/libkolab_config.h.in     2015-02-01 18:23:05.000000000 
+0100
@@ -0,0 +1,6 @@
+/* This file is generated from libkolab_config.h.cmake. */
+
+/* Whether akonadi/tag.h exists. */
+#cmakedefine HAVE_TAG_H 1
+/* Whether akonadi/relation.h exists. */
+#cmakedefine HAVE_RELATION_H 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/tests/kcalconversiontest.cpp 
new/libkolab-0.6.0/tests/kcalconversiontest.cpp
--- old/libkolab-0.5.3/tests/kcalconversiontest.cpp     2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/tests/kcalconversiontest.cpp     2015-02-01 
18:23:05.000000000 +0100
@@ -504,8 +504,6 @@
     QTest::addColumn<KABC::Addressee>( "kcal" );
     QTest::addColumn<Kolab::Contact>( "kolab" );
     
-    Kolab::cDateTime date(2011,2,2,12,11,10,true);
-    Kolab::cDateTime date2(2011,2,2,12,12,10,true);
     {
         KABC::Addressee kcal;
         kcal.setUid("uid");
@@ -520,6 +518,20 @@
     {
         KABC::Addressee kcal;
         kcal.setUid("uid");
+        kcal.setFormattedName("name");
+        kcal.setBirthday(QDateTime(QDate(2012,2,2)));
+
+        //Because QDateTime doesn't know date-only values we always end up 
with a date-time
+        Kolab::Contact kolab;
+        kolab.setUid("uid");
+        kolab.setName("name");
+        kolab.setBDay(Kolab::cDateTime(2012,2,2,0,0,0));
+
+        QTest::newRow("bday") << kcal << kolab;
+    }
+    {
+        KABC::Addressee kcal;
+        kcal.setUid("uid");
         //The first address is always the preferred
         kcal.setEmails(QStringList() << "[email protected]" << 
"[email protected]");
         kcal.insertCustom("KOLAB", "[email protected]", 
"home,work");
@@ -554,12 +566,14 @@
     foreach (const QString &mail, e.emails()) {
         QCOMPARE(e.custom(QLatin1String("KOLAB"), 
QString::fromLatin1("EmailTypes%1").arg(mail)), 
kcal.custom(QLatin1String("KOLAB"), 
QString::fromLatin1("EmailTypes%1").arg(mail)));
     }
+    QCOMPARE(e.birthday(), kcal.birthday());
     
     const Kolab::Contact &b = fromKABC(kcal);
     QCOMPARE(b.uid(), kolab.uid());
     QCOMPARE(b.name(), kolab.name());
     QCOMPARE(b.emailAddresses(), kolab.emailAddresses());
     QCOMPARE(b.emailAddressPreferredIndex(), 
kolab.emailAddressPreferredIndex());
+    QCOMPARE(b.bDay(), kolab.bDay());
 }
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/tests/kolabobjecttest.cpp 
new/libkolab-0.6.0/tests/kolabobjecttest.cpp
--- old/libkolab-0.5.3/tests/kolabobjecttest.cpp        2014-08-26 
13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/tests/kolabobjecttest.cpp        2015-02-01 
18:23:05.000000000 +0100
@@ -80,6 +80,92 @@
     QCOMPARE(Kolab::ErrorHandler::instance().error(), 
Kolab::ErrorHandler::Critical);
 }
 
+void KolabObjectTest::parseRelationMembers()
+{
+    {
+        QString 
memberString("imap:///user/jan.aachen%40lhm.klab.cc/INBOX/20?message-id=%3Cf06aa3345a25005380b47547ad161d36%40lhm.klab.cc%3E&subject=Re%3A+test&date=Tue%2C+12+Aug+2014+20%3A42%3A59+%2B0200");
+        Kolab::RelationMember member = Kolab::parseMemberUrl(memberString);
+
+        QString result = Kolab::generateMemberUrl(member);
+        qDebug() << result;
+        result.replace(QLatin1String("%20"),QLatin1String("+"));
+        QCOMPARE(result, memberString);
+    }
+
+    //user namespace by uid
+    {
+
+        Kolab::RelationMember member;
+        member.uid = 20;
+        member.mailbox = QList<QByteArray>() << "INBOX";
+        member.user = "[email protected]";
+        member.messageId = "messageid";
+        member.date = "date";
+        member.subject = "subject";
+        QString url = Kolab::generateMemberUrl(member);
+        qDebug() << url;
+        Kolab::RelationMember result = Kolab::parseMemberUrl(url);
+        QCOMPARE(result.uid, member.uid);
+        QCOMPARE(result.mailbox, member.mailbox);
+        QCOMPARE(result.user, member.user);
+        QCOMPARE(result.messageId, member.messageId);
+        QCOMPARE(result.date, member.date);
+        QCOMPARE(result.subject, member.subject);
+    }
+
+    //shared namespace by uid
+    {
+
+        Kolab::RelationMember member;
+        member.uid = 20;
+        member.mailbox = QList<QByteArray>() << "foo" << "bar";
+        member.messageId = "messageid";
+        member.date = "date";
+        member.subject = "subject";
+        QString url = Kolab::generateMemberUrl(member);
+        qDebug() << url;
+        Kolab::RelationMember result = Kolab::parseMemberUrl(url);
+        QCOMPARE(result.uid, member.uid);
+        QCOMPARE(result.mailbox, member.mailbox);
+        QVERIFY(result.user.isEmpty());
+        QCOMPARE(result.messageId, member.messageId);
+        QCOMPARE(result.date, member.date);
+        QCOMPARE(result.subject, member.subject);
+    }
+
+    //by uuid/gid
+    {
+
+        Kolab::RelationMember member;
+        member.gid = "fooobar";
+        QString url = Kolab::generateMemberUrl(member);
+        qDebug() << url;
+        Kolab::RelationMember result = Kolab::parseMemberUrl(url);
+        QCOMPARE(result.gid, member.gid);
+    }
+
+    // chars to en/decode
+    {
+
+        Kolab::RelationMember member;
+        member.uid = 20;
+        member.mailbox = QList<QByteArray>() << "spaces in folders" << "+^,:@";
+        member.user = "john.doe:^@example.org";
+        member.messageId = "messageid+^,:@";
+        member.date = "date+^,:@";
+        member.subject = "subject+^,:@";
+
+        QString url = Kolab::generateMemberUrl(member);
+        qDebug() << url;
+        Kolab::RelationMember result = Kolab::parseMemberUrl(url);
+        QCOMPARE(result.uid, member.uid);
+        QCOMPARE(result.mailbox, member.mailbox);
+        QCOMPARE(result.user, member.user);
+        QCOMPARE(result.messageId, member.messageId);
+        QCOMPARE(result.date, member.date);
+        QCOMPARE(result.subject, member.subject);
+    }
+}
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/tests/kolabobjecttest.h 
new/libkolab-0.6.0/tests/kolabobjecttest.h
--- old/libkolab-0.5.3/tests/kolabobjecttest.h  2014-08-26 13:39:06.000000000 
+0200
+++ new/libkolab-0.6.0/tests/kolabobjecttest.h  2015-02-01 18:23:05.000000000 
+0100
@@ -27,6 +27,7 @@
     void preserveUnicode();
     void dontCrashWithEmptyOrganizer();
     void dontCrashWithEmptyIncidence();
+    void parseRelationMembers();
 };
 
 #endif // KOLABOBJECTTEST_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libkolab-0.5.3/tests/testfiles/v3/contacts/distlist.vcf.mime 
new/libkolab-0.6.0/tests/testfiles/v3/contacts/distlist.vcf.mime
--- old/libkolab-0.5.3/tests/testfiles/v3/contacts/distlist.vcf.mime    
2014-08-26 13:39:06.000000000 +0200
+++ new/libkolab-0.6.0/tests/testfiles/v3/contacts/distlist.vcf.mime    
2015-02-01 18:23:05.000000000 +0100
@@ -1,5 +1,5 @@
 Date: Mon, 23 Apr 2012 12:46:37 +0200
-X-Kolab-Type: application/x-vnd.kolab.contact.distlist
+X-Kolab-Type: application/x-vnd.kolab.distribution-list
 X-Kolab-Mime-Version: 3.0
 User-Agent: Libkolab-0.1.0
 Content-Type: multipart/mixed; boundary="nextPart1365947.WmFcbPlLFA"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/tests/timezonetest.cpp 
new/libkolab-0.6.0/tests/timezonetest.cpp
--- old/libkolab-0.5.3/tests/timezonetest.cpp   2014-08-26 13:39:06.000000000 
+0200
+++ new/libkolab-0.6.0/tests/timezonetest.cpp   2015-02-01 18:23:05.000000000 
+0100
@@ -85,6 +85,9 @@
     QTest::newRow( "13" ) << QString::fromLatin1("(GMT-11:00) Midway Island, 
Samoa");
     QTest::newRow( "14" ) << QString::fromLatin1("W. Europe Standard Time");
     QTest::newRow( "15" ) << QString::fromLatin1("(GMT+1.00) 
Sarajevo/Warsaw/Zagreb");
+    //Lotus notes uses it's own set of specifiers
+//     QTest::newRow( "Lotus Notes" ) << QString::fromLatin1("W. Europe");
+//     QTest::newRow( "Google UTC offset" ) << 
QString::fromLatin1("2013-10-23T04:00:00+02:00");
 }
 
 void TimezoneTest::testFromHardcodedList()
@@ -109,57 +112,65 @@
     QCOMPARE(result->dtStart().timeZone().name(), 
KTimeZone(QLatin1String("Africa/Lagos")).name());
 }
 
-void TimezoneTest::testKolabObjectReader()
-{
-    const Kolab::Version version = Kolab::KolabV3;
-    const Kolab::ObjectType type = Kolab::EventObject;
-    QString icalFileName = 
TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezone.ics"); //To compare
-    QString mimeFileName = 
TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezoneV3.mime"); //For 
parsing
-
-    //Parse mime message
-    bool ok = false;
-    const KMime::Message::Ptr &msg = readMimeFile( mimeFileName, ok );
-    QVERIFY(ok);
-    Kolab::KolabObjectReader reader;
-    Kolab::ObjectType t = reader.parseMimeMessage(msg);
-    QCOMPARE(t, type);
-    QCOMPARE(reader.getVersion(), version);
-    QCOMPARE(Kolab::ErrorHandler::instance().error(), 
Kolab::ErrorHandler::Debug);
-
-    KCalCore::Incidence::Ptr convertedIncidence = reader.getIncidence();
-    kDebug() << "read incidence";
-
-    //Parse ICalFile for comparison
-    QFile icalFile( icalFileName );
-    QVERIFY( icalFile.open( QFile::ReadOnly ) );
-    KCalCore::ICalFormat format;
-    KCalCore::Incidence::Ptr realIncidence( format.fromString( 
QString::fromUtf8( icalFile.readAll() ) ) );
-
-    // fix up the converted incidence for comparisson
-    normalizeIncidence(convertedIncidence);
-    normalizeIncidence(realIncidence);
-
-    // recurrence objects are created on demand, but 
KCalCore::Incidence::operator==() doesn't take that into account
-    // so make sure both incidences have one
-    realIncidence->recurrence();
-    convertedIncidence->recurrence();
-
-    realIncidence->setLastModified(convertedIncidence->lastModified());
-
-    //The following test is just for debugging and not really relevant
-    if ( *(realIncidence.data()) != *(convertedIncidence.data()) ) {
-        showDiff(format.toString( realIncidence ), format.toString( 
convertedIncidence ));
-    }
-    QVERIFY( *(realIncidence.data()) ==  *(convertedIncidence.data()) );
-}
+// void TimezoneTest::testKolabObjectReader()
+// {
+//     const Kolab::Version version = Kolab::KolabV3;
+//     const Kolab::ObjectType type = Kolab::EventObject;
+//     QString icalFileName = 
TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezone.ics"); //To compare
+//     QString mimeFileName = 
TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezoneV3.mime"); //For 
parsing
+// 
+//     //Parse mime message
+//     bool ok = false;
+//     const KMime::Message::Ptr &msg = readMimeFile( mimeFileName, ok );
+//     QVERIFY(ok);
+//     Kolab::KolabObjectReader reader;
+//     Kolab::ObjectType t = reader.parseMimeMessage(msg);
+//     QCOMPARE(t, type);
+//     QCOMPARE(reader.getVersion(), version);
+//     QCOMPARE(Kolab::ErrorHandler::instance().error(), 
Kolab::ErrorHandler::Debug);
+// 
+//     KCalCore::Incidence::Ptr convertedIncidence = reader.getIncidence();
+//     kDebug() << "read incidence";
+// 
+//     //Parse ICalFile for comparison
+//     QFile icalFile( icalFileName );
+//     QVERIFY( icalFile.open( QFile::ReadOnly ) );
+//     KCalCore::ICalFormat format;
+//     KCalCore::Incidence::Ptr realIncidence( format.fromString( 
QString::fromUtf8( icalFile.readAll() ) ) );
+// 
+//     // fix up the converted incidence for comparisson
+//     normalizeIncidence(convertedIncidence);
+//     normalizeIncidence(realIncidence);
+// 
+//     // recurrence objects are created on demand, but 
KCalCore::Incidence::operator==() doesn't take that into account
+//     // so make sure both incidences have one
+//     realIncidence->recurrence();
+//     convertedIncidence->recurrence();
+// 
+//     realIncidence->setLastModified(convertedIncidence->lastModified());
+// 
+//     //The following test is just for debugging and not really relevant
+//     if ( *(realIncidence.data()) != *(convertedIncidence.data()) ) {
+//         showDiff(format.toString( realIncidence ), format.toString( 
convertedIncidence ));
+//     }
+//     QVERIFY( *(realIncidence.data()) ==  *(convertedIncidence.data()) );
+// }
 
 void TimezoneTest::testFindLegacyTimezone()
 {
     const QString normalized = 
TimezoneConverter::normalizeTimezone("US/Pacific");
     kDebug() << normalized;
+    QEXPECT_FAIL("", "Currently broken", Continue);
     QVERIFY(!normalized.isEmpty());
 }
 
+void TimezoneTest::testIgnoreInvalidTimezone()
+{
+    const QString normalized = 
TimezoneConverter::normalizeTimezone("FOOOOBAR");
+    kDebug() << normalized;
+    QVERIFY(normalized.isEmpty());
+}
+
 void TimezoneTest::testTimezoneDaemonAvailable()
 {
     //With KDE it should be available and with libcalendaring it should return 
true
@@ -171,9 +182,35 @@
     const Kolab::cDateTime expected(2013, 10, 23, 2, 0 ,0, true);
     const KDateTime input(KDateTime::fromString("2013-10-23T04:00:00+02:00", 
KDateTime::RFC3339Date));
     const Kolab::cDateTime result = Kolab::Conversion::fromDate(input);
+    QVERIFY(!Kolab::ErrorHandler::instance().errorOccured());
     QCOMPARE(result, expected);
 }
 
+void TimezoneTest::localTimezone()
+{
+    {
+        const Kolab::cDateTime result = 
Kolab::Conversion::fromDate(KDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), 
KDateTime::LocalZone));
+        QVERIFY(!result.timezone().empty());
+        QVERIFY(!Kolab::ErrorHandler::instance().errorOccured());
+    }
+    {
+        const Kolab::cDateTime result = 
Kolab::Conversion::fromDate(KDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), 
KDateTime::ClockTime));
+        QVERIFY(!Kolab::ErrorHandler::instance().errorOccured());
+    }
+    {
+        const Kolab::cDateTime result = 
Kolab::Conversion::fromDate(KDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), 
KDateTime::TimeZone));
+        QVERIFY(result.timezone().empty());
+        QVERIFY(!Kolab::ErrorHandler::instance().errorOccured());
+    }
+    {
+        KDateTime dt(QDate(2013, 10, 10), QTime(2, 0, 0), 
KTimeZone("/etc/localzone"));
+        const Kolab::cDateTime result = Kolab::Conversion::fromDate(dt);
+        kDebug() << result.timezone();
+        QVERIFY(result.timezone().empty());
+        QVERIFY(!Kolab::ErrorHandler::instance().errorOccured());
+    }
+}
+
 QTEST_MAIN( TimezoneTest )
 
 #include "timezonetest.moc"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkolab-0.5.3/tests/timezonetest.h 
new/libkolab-0.6.0/tests/timezonetest.h
--- old/libkolab-0.5.3/tests/timezonetest.h     2014-08-26 13:39:06.000000000 
+0200
+++ new/libkolab-0.6.0/tests/timezonetest.h     2015-02-01 18:23:05.000000000 
+0100
@@ -32,10 +32,12 @@
     void testFromHardcodedList_data();
     void testFromHardcodedList();
     void testKolabObjectWriter();
-    void testKolabObjectReader();
+    // void testKolabObjectReader();
     void testFindLegacyTimezone();
+    void testIgnoreInvalidTimezone();
     void testTimezoneDaemonAvailable();
     void testUTCOffset();
+    void localTimezone();
 };
 
 #endif // TIMEZONETEST_H

++++++ libkolab-0.6.0_check_for_generic_tag.patch ++++++
>From 34edc0a846b87570c06a5942b458adb720a34a9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <[email protected]>
Date: Tue, 10 Feb 2015 10:59:18 +0100
Subject: Make libkolab compile with upstream kdepimlibs

Because GENERIC tags have not entered upstream, we have to test for
support. Our integration branch not supports a feature flag to indicate
the availability of that feature.

KOLAB: #4448

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 365edac..bd53e27 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -78,6 +78,11 @@ add_definitions( 
-DKDEPIMLIBS_VERSION_MAJOR=${KdepimLibs_VERSION_MAJOR} )
 add_definitions( -DKDEPIMLIBS_VERSION_MINOR=${KdepimLibs_VERSION_MINOR} )
 add_definitions( -DKDEPIMLIBS_VERSION_PATCH=${KdepimLibs_VERSION_PATCH} )
 
+#Tag::GENERIC is only available at the moment at the kolab/integration branches
+if (KDEPIMLIBS_HAS_GENERIC_TAG)
+    add_definitions( -DKDEPIMLIBS_HAS_GENERIC_TAG)
+endif()
+
 if("${KdepimLibs_VERSION}" VERSION_GREATER "4.8.40" OR USE_LIBCALENDARING)
     add_definitions(-DKDEPIMLIBS_VERSION_DEVEL)
 endif()
diff --git a/kolabformat/kolabobject.cpp b/kolabformat/kolabobject.cpp
index 681b4a5..e5c404d 100644
--- a/kolabformat/kolabobject.cpp
+++ b/kolabformat/kolabobject.cpp
@@ -435,8 +435,11 @@ ObjectType KolabObjectReader::Private::readKolabV3(const 
KMime::Message::Ptr &ms
                 mTag = Akonadi::Tag();
                 mTag.setName(Conversion::fromStdString(relation.name()));
                 
mTag.setGid(Conversion::fromStdString(configuration.uid()).toLatin1());
+#ifdef KDEPIMLIBS_HAS_GENERIC_TAG
                 mTag.setType(Akonadi::Tag::GENERIC);
-
+#else
+                mTag.setType(Akonadi::Tag::PLAIN);
+#endif
                 mTagMembers.reserve(relation.members().size());
                 foreach (const std::string &member, relation.members()) {
                     mTagMembers << Conversion::fromStdString(member);
-- 
cgit v0.10.2


Reply via email to