4 commits - c++/lib c++/tests libkolab/kolabformatV2

Christian Mollekopf mollekopf at kolabsys.com
Wed Mar 21 16:05:27 CET 2012


 c++/lib/CMakeLists.txt                      |    2 
 c++/lib/kolabcontact.cpp                    |    6 
 c++/lib/kolabcontact.h                      |   14 
 c++/lib/kolabformat.cpp                     |    4 
 c++/lib/xcardconversions.h                  |   46 -
 c++/tests/bindingstest.cpp                  |    6 
 c++/tests/conversiontest.cpp                |   16 
 c++/tests/conversiontest.h                  |    2 
 libkolab/kolabformatV2/contact.cpp          | 1208 ++++++++++++++++++++++++++++
 libkolab/kolabformatV2/contact.h            |  274 ++++++
 libkolab/kolabformatV2/distributionlist.cpp |  242 +++++
 libkolab/kolabformatV2/distributionlist.h   |   96 ++
 libkolab/kolabformatV2/event.cpp            |  221 +++++
 libkolab/kolabformatV2/event.h              |  101 ++
 libkolab/kolabformatV2/incidence.cpp        |  976 ++++++++++++++++++++++
 libkolab/kolabformatV2/incidence.h          |  166 +++
 libkolab/kolabformatV2/journal.cpp          |  184 ++++
 libkolab/kolabformatV2/journal.h            |  101 ++
 libkolab/kolabformatV2/kolabbase.cpp        |  500 +++++++++++
 libkolab/kolabformatV2/kolabbase.h          |  183 ++++
 libkolab/kolabformatV2/note.cpp             |  230 +++++
 libkolab/kolabformatV2/note.h               |  109 ++
 libkolab/kolabformatV2/task.cpp             |  455 ++++++++++
 libkolab/kolabformatV2/task.h               |  143 +++
 24 files changed, 5271 insertions(+), 14 deletions(-)

New commits:
commit 4d22a0e2c137b1d9ae02cd11f1291fd139c4c1f2
Merge: b94b1ac c2f4ccf
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Wed Mar 21 16:05:22 2012 +0100

    Merge branch 'master' of ssh://git.kolabsys.com/git/libkolabxml



commit b94b1acd370b007cc49ce2ec7799743360074e9f
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Wed Mar 21 16:05:03 2012 +0100

    Stricter compiling options, and complying to them.

diff --git a/c++/lib/CMakeLists.txt b/c++/lib/CMakeLists.txt
index ac961a8..44caba9 100644
--- a/c++/lib/CMakeLists.txt
+++ b/c++/lib/CMakeLists.txt
@@ -9,7 +9,7 @@ add_library(kolabxml SHARED kolabformat.cpp kolabcontainers.cpp kolabnote.cpp ko
 target_link_libraries(kolabxml ${XERCES_C} ${Boost_LIBRARIES})
 
 #For the core library we can be stricter when compiling. This doesn't work with the auto generated code though.
-set_target_properties(kolabxml PROPERTIES COMPILE_FLAGS "-Wl,--no-undefined -Werror ")
+set_target_properties(kolabxml PROPERTIES COMPILE_FLAGS "-Wall -Wextra -Werror -Wfatal-errors -Wl,--no-undefined")
 set_target_properties(kolabxml PROPERTIES VERSION 3.0.0 SOVERSION 0)
 install(TARGETS kolabxml LIBRARY DESTINATION lib)
 
diff --git a/c++/lib/kolabformat.cpp b/c++/lib/kolabformat.cpp
index d7ac396..98a42a2 100644
--- a/c++/lib/kolabformat.cpp
+++ b/c++/lib/kolabformat.cpp
@@ -142,7 +142,7 @@ std::string writeNote(const Note &note)
     return Kolab::KolabObjects::serializeObject<Kolab::Note>(note);
 }
 
-Configuration readConfiguration(const std::string& s, bool isUrl)
+Configuration readConfiguration(const std::string& /*s*/, bool /*isUrl*/)
 {
 //     boost::shared_ptr <Kolab::Configuration> ptr = deserializeConfiguration(s, isUrl);
 //     if (!ptr.get()) {
@@ -152,7 +152,7 @@ Configuration readConfiguration(const std::string& s, bool isUrl)
     return Configuration();
 }
 
-std::string writeConfiguration(const Configuration &config)
+std::string writeConfiguration(const Configuration &/*config*/)
 {
 //     return serializeConfiguration(config);
 return std::string();
diff --git a/c++/lib/xcardconversions.h b/c++/lib/xcardconversions.h
index 226e07d..eeaa3b8 100644
--- a/c++/lib/xcardconversions.h
+++ b/c++/lib/xcardconversions.h
@@ -302,7 +302,7 @@ vcard_4_0::relatedPropType fromRelated(const Kolab::Related &r)
 
 Kolab::Related toRelated(const vcard_4_0::relatedPropType &r)
 {
-    Kolab::Related::DescriptionType type;
+    Kolab::Related::DescriptionType type = Kolab::Related::Invalid;
     std::string textOrUri;
     if (r.uri()) {
         type = Kolab::Related::Uid;
@@ -312,6 +312,7 @@ Kolab::Related toRelated(const vcard_4_0::relatedPropType &r)
         textOrUri = *r.text();
     } else {
         ERROR("no text and no uri");
+        return Kolab::Related();
     }
     Kolab::Related related(type, textOrUri);
     if (r.parameters()) {


commit 00f5299b4fde9db148998066af9c44af98982a41
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Tue Mar 20 00:31:22 2012 +0100

    Copied Kolab Format implementation from kdepim-runtime/resources/kolabproxy/
    
    Copied from commit 4db1c180a37713293ee126deed1f97473ab64618
    
    It's being moved out of kdepim-runtime so we can start using it for the upgrade-tool.

diff --git a/libkolab/kolabformatV2/contact.cpp b/libkolab/kolabformatV2/contact.cpp
new file mode 100644
index 0000000..e421629
--- /dev/null
+++ b/libkolab/kolabformatV2/contact.cpp
@@ -0,0 +1,1208 @@
+/*
+    This file is part of libkabc and/or kaddressbook.
+    Copyright (c) 2004 Klarälvdalens Datakonsult AB
+        <info at klaralvdalens-datakonsult.se>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "contact.h"
+
+#include <kabc/addressee.h>
+#include <kcalcore/freebusyurlstore.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <QFile>
+#include <float.h>
+
+using namespace Kolab;
+
+static const char* s_pictureAttachmentName = "kolab-picture.png";
+static const char* s_logoAttachmentName = "kolab-logo.png";
+static const char* s_soundAttachmentName = "sound";
+static const char* s_unhandledTagAppName = "KOLABUNHANDLED"; // no hyphens in appnames!
+
+// saving (addressee->xml)
+Contact::Contact( const KABC::Addressee* addr )
+  : mHasGeo( false )
+{
+  setFields( addr );
+}
+
+// loading (xml->addressee)
+Contact::Contact( const QString& xml )
+  : mHasGeo( false )
+{
+  load( xml );
+}
+
+Contact::~Contact()
+{
+}
+
+void Contact::setGivenName( const QString& name )
+{
+  mGivenName = name;
+}
+
+QString Contact::givenName() const
+{
+  return mGivenName;
+}
+
+void Contact::setMiddleNames( const QString& names )
+{
+  mMiddleNames = names;
+}
+
+QString Contact::middleNames() const
+{
+  return mMiddleNames;
+}
+
+void Contact::setLastName( const QString& name )
+{
+  mLastName = name;
+}
+
+QString Contact::lastName() const
+{
+  return mLastName;
+}
+
+void Contact::setFullName( const QString& name )
+{
+  mFullName = name;
+}
+
+QString Contact::fullName() const
+{
+  return mFullName;
+}
+
+void Contact::setInitials( const QString& initials )
+{
+  mInitials = initials;
+}
+
+QString Contact::initials() const
+{
+  return mInitials;
+}
+
+void Contact::setPrefix( const QString& prefix )
+{
+  mPrefix = prefix;
+}
+
+QString Contact::prefix() const
+{
+  return mPrefix;
+}
+
+void Contact::setSuffix( const QString& suffix )
+{
+  mSuffix = suffix;
+}
+
+QString Contact::suffix() const
+{
+  return mSuffix;
+}
+
+void Contact::setRole( const QString& role )
+{
+  mRole = role;
+}
+
+QString Contact::role() const
+{
+  return mRole;
+}
+
+void Contact::setFreeBusyUrl( const QString& fbUrl )
+{
+  mFreeBusyUrl = fbUrl;
+}
+
+QString Contact::freeBusyUrl() const
+{
+  return mFreeBusyUrl;
+}
+
+void Contact::setOrganization( const QString& organization )
+{
+  mOrganization = organization;
+}
+
+QString Contact::organization() const
+{
+  return mOrganization;
+}
+
+void Contact::setWebPage( const QString& url )
+{
+  mWebPage = url;
+}
+
+QString Contact::webPage() const
+{
+  return mWebPage;
+}
+
+void Contact::setIMAddress( const QString& imAddress )
+{
+  mIMAddress = imAddress;
+}
+
+QString Contact::imAddress() const
+{
+  return mIMAddress;
+}
+
+void Contact::setDepartment( const QString& department )
+{
+  mDepartment = department;
+}
+
+QString Contact::department() const
+{
+  return mDepartment;
+}
+
+void Contact::setOfficeLocation( const QString& location )
+{
+  mOfficeLocation = location;
+}
+
+QString Contact::officeLocation() const
+{
+  return mOfficeLocation;
+}
+
+void Contact::setProfession( const QString& profession )
+{
+  mProfession = profession;
+}
+
+QString Contact::profession() const
+{
+  return mProfession;
+}
+
+void Contact::setTitle( const QString& title )
+{
+  mTitle = title;
+}
+
+QString Contact::title() const
+{
+  return mTitle;
+}
+
+void Contact::setManagerName( const QString& name )
+{
+  mManagerName = name;
+}
+
+QString Contact::managerName() const
+{
+  return mManagerName;
+}
+
+void Contact::setAssistant( const QString& name )
+{
+  mAssistant = name;
+}
+
+QString Contact::assistant() const
+{
+  return mAssistant;
+}
+
+void Contact::setNickName( const QString& name )
+{
+  mNickName = name;
+}
+
+QString Contact::nickName() const
+{
+  return mNickName;
+}
+
+void Contact::setSpouseName( const QString& name )
+{
+  mSpouseName = name;
+}
+
+QString Contact::spouseName() const
+{
+  return mSpouseName;
+}
+
+void Contact::setBirthday( const QDate& date )
+{
+  mBirthday = date;
+}
+
+QDate Contact::birthday() const
+{
+  return mBirthday;
+}
+
+void Contact::setAnniversary( const QDate& date )
+{
+  mAnniversary = date;
+}
+
+QDate Contact::anniversary() const
+{
+  return mAnniversary;
+}
+
+void Contact::setChildren( const QString& children )
+{
+  mChildren = children;
+}
+
+QString Contact::children() const
+{
+  return mChildren;
+}
+
+void Contact::setGender( const QString& gender )
+{
+  mGender = gender;
+}
+
+QString Contact::gender() const
+{
+  return mGender;
+}
+
+void Contact::setLanguage( const QString& language )
+{
+  mLanguage = language;
+}
+
+QString Contact::language() const
+{
+  return mLanguage;
+}
+
+void Contact::addPhoneNumber( const PhoneNumber& number )
+{
+  mPhoneNumbers.append( number );
+}
+
+QList<Contact::PhoneNumber>& Contact::phoneNumbers()
+{
+  return mPhoneNumbers;
+}
+
+const QList<Contact::PhoneNumber>& Contact::phoneNumbers() const
+{
+  return mPhoneNumbers;
+}
+
+void Contact::addEmail( const Email& email )
+{
+  mEmails.append( email );
+}
+
+QList<Contact::Email>& Contact::emails()
+{
+  return mEmails;
+}
+
+QString Contact::fullEmail() const
+{
+  return mFullEmail;
+}
+
+const QList<Contact::Email>& Contact::emails() const
+{
+  return mEmails;
+}
+
+void Contact::addAddress( const Contact::Address& address )
+{
+  mAddresses.append( address );
+}
+
+QList<Contact::Address>& Contact::addresses()
+{
+  return mAddresses;
+}
+
+const QList<Contact::Address>& Contact::addresses() const
+{
+  return mAddresses;
+}
+
+void Contact::setPreferredAddress( const QString& address )
+{
+  mPreferredAddress = address;
+}
+
+QString Contact::preferredAddress() const
+{
+  return mPreferredAddress;
+}
+
+bool Contact::loadNameAttribute( QDomElement& element )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "given-name" )
+        setGivenName( e.text() );
+      else if ( tagName == "middle-names" )
+        setMiddleNames( e.text() );
+      else if ( tagName == "last-name" )
+        setLastName( e.text() );
+      else if ( tagName == "full-name" )
+        setFullName( e.text() );
+      else if ( tagName == "initials" )
+        setInitials( e.text() );
+      else if ( tagName == "prefix" )
+        setPrefix( e.text() );
+      else if ( tagName == "suffix" )
+        setSuffix( e.text() );
+      else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+void Contact::saveNameAttribute( QDomElement& element ) const
+{
+  QDomElement e = element.ownerDocument().createElement( "name" );
+  element.appendChild( e );
+
+  writeString( e, "given-name", givenName() );
+  writeString( e, "middle-names", middleNames() );
+  writeString( e, "last-name", lastName() );
+  writeString( e, "full-name", fullName() );
+  writeString( e, "initials", initials() );
+  writeString( e, "prefix", prefix() );
+  writeString( e, "suffix", suffix() );
+}
+
+bool Contact::loadPhoneAttribute( QDomElement& element )
+{
+  PhoneNumber number;
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "type" )
+        number.type = e.text();
+      else if ( tagName == "number" )
+        number.number = e.text();
+      else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  addPhoneNumber( number );
+  return true;
+}
+
+void Contact::savePhoneAttributes( QDomElement& element ) const
+{
+  QList<PhoneNumber>::ConstIterator it = mPhoneNumbers.constBegin();
+  for ( ; it != mPhoneNumbers.constEnd(); ++it ) {
+    QDomElement e = element.ownerDocument().createElement( "phone" );
+    element.appendChild( e );
+    const PhoneNumber& p = *it;
+    writeString( e, "type", p.type );
+    writeString( e, "number", p.number );
+  }
+}
+
+void Contact::saveEmailAttributes( QDomElement& element ) const
+{
+  QList<Email>::ConstIterator it = mEmails.constBegin();
+  for ( ; it != mEmails.constEnd(); ++it )
+    saveEmailAttribute( element, *it );
+}
+
+void Contact::loadCustomAttributes( QDomElement& element )
+{
+  Custom custom;
+  custom.app = element.attribute( "app" );
+  custom.name = element.attribute( "name" );
+  custom.value = element.attribute( "value" );
+  mCustomList.append( custom );
+}
+
+void Contact::saveCustomAttributes( QDomElement& element ) const
+{
+  QList<Custom>::ConstIterator it = mCustomList.constBegin();
+  for ( ; it != mCustomList.constEnd(); ++it ) {
+    Q_ASSERT( !(*it).name.isEmpty() );
+    if ( (*it).app == s_unhandledTagAppName ) {
+      writeString( element, (*it).name, (*it).value );
+    } else {
+      // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+      QDomElement e = element.ownerDocument().createElement( "x-custom" );
+      element.appendChild( e );
+      e.setAttribute( "app", (*it).app );
+      e.setAttribute( "name", (*it).name );
+      e.setAttribute( "value", (*it).value );
+    }
+  }
+}
+
+bool Contact::loadAddressAttribute( QDomElement& element )
+{
+  Address address;
+
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "type" )
+        address.type = e.text();
+      else if ( tagName == "x-kde-type" )
+        address.kdeAddressType = e.text().toInt();
+      else if ( tagName == "street" )
+        address.street = e.text();
+      else if ( tagName == "pobox" )
+        address.pobox = e.text();
+      else if ( tagName == "locality" )
+        address.locality = e.text();
+      else if ( tagName == "region" )
+        address.region = e.text();
+      else if ( tagName == "postal-code" )
+        address.postalCode = e.text();
+      else if ( tagName == "country" )
+        address.country = e.text();
+      else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  addAddress( address );
+  return true;
+}
+
+void Contact::saveAddressAttributes( QDomElement& element ) const
+{
+  QList<Address>::ConstIterator it = mAddresses.constBegin();
+  for ( ; it != mAddresses.constEnd(); ++it ) {
+    QDomElement e = element.ownerDocument().createElement( "address" );
+    element.appendChild( e );
+    const Address& a = *it;
+    writeString( e, "type", a.type );
+    writeString( e, "x-kde-type", QString::number( a.kdeAddressType ) );
+    if ( !a.street.isEmpty() )
+      writeString( e, "street", a.street );
+    if ( !a.pobox.isEmpty() )
+      writeString( e, "pobox", a.pobox );
+    if ( !a.locality.isEmpty() )
+    writeString( e, "locality", a.locality );
+    if ( !a.region.isEmpty() )
+      writeString( e, "region", a.region );
+    if ( !a.postalCode.isEmpty() )
+      writeString( e, "postal-code", a.postalCode );
+    if ( !a.country.isEmpty() )
+      writeString( e, "country", a.country );
+  }
+}
+
+bool Contact::loadAttribute( QDomElement& element )
+{
+  const QString tagName = element.tagName();
+  switch ( tagName[0].toLatin1() ) {
+  case 'a':
+    if ( tagName == "address" )
+      return loadAddressAttribute( element );
+    if ( tagName == "assistant" ) {
+      setAssistant( element.text() );
+      return true;
+    }
+    if ( tagName == "anniversary" ) {
+      if ( !element.text().isEmpty() )
+        setAnniversary( stringToDate( element.text() ) );
+      return true;
+    }
+    break;
+  case 'b':
+    if ( tagName == "birthday" ) {
+      if ( !element.text().isEmpty() )
+        setBirthday( stringToDate( element.text() ) );
+      return true;
+    }
+    break;
+  case 'c':
+    if ( tagName == "children" ) {
+      setChildren( element.text() );
+      return true;
+    }
+    break;
+  case 'd':
+    if ( tagName == "department" ) {
+      setDepartment( element.text() );
+      return true;
+    }
+    break;
+  case 'e':
+    if ( tagName == "email" ) {
+      Email email;
+      if ( loadEmailAttribute( element, email ) ) {
+        addEmail( email );
+        return true;
+      } else
+        return false;
+    }
+    break;
+  case 'f':
+    if ( tagName == "free-busy-url" ) {
+      setFreeBusyUrl( element.text() );
+      return true;
+    }
+    break;
+  case 'g':
+    if ( tagName == "gender" ) {
+      setGender( element.text() );
+      return true;
+    }
+    break;
+  case 'i':
+    if ( tagName == "im-address" ) {
+      setIMAddress( element.text() );
+      return true;
+    }
+    break;
+  case 'j':
+   if ( tagName == "job-title" ) {
+     // see saveAttributes: <job-title> is mapped to the Role field
+      setTitle( element.text() );
+      return true;
+    }
+    break;
+  case 'l':
+    if ( tagName == "language" ) {
+      setLanguage( element.text() );
+      return true;
+    }
+    if ( tagName == "latitude" ) {
+      setLatitude( element.text().toFloat() );
+      mHasGeo = true;
+      return true;
+    }
+    if ( tagName == "longitude" ) {
+      setLongitude( element.text().toFloat() );
+      mHasGeo = true;
+    }
+    break;
+  case 'm':
+    if ( tagName == "manager-name" ) {
+      setManagerName( element.text() );
+      return true;
+    }
+  case 'n':
+    if ( tagName == "name" )
+      return loadNameAttribute( element );
+    if ( tagName == "nick-name" ) {
+      setNickName( element.text() );
+      return true;
+    }
+    break;
+  case 'o':
+    if ( tagName == "organization" ) {
+      setOrganization( element.text() );
+      return true;
+    }
+    if ( tagName == "office-location" ) {
+      setOfficeLocation( element.text() );
+      return true;
+    }
+    break;
+  case 'p':
+    if ( tagName == "profession" ) {
+      setProfession( element.text() );
+      return true;
+    }
+    if ( tagName == "picture" ) {
+      mPictureAttachmentName = element.text();
+      return true;
+    }
+    if ( tagName == "phone" ) {
+      return loadPhoneAttribute( element );
+      return true;
+    }
+    if ( tagName == "preferred-address" ) {
+      setPreferredAddress( element.text() );
+      return true;
+    }
+    break;
+  case 'r':
+    if ( tagName == "role" ) {
+      setRole( element.text() );
+      return true;
+    }
+    break;
+  case 's':
+    if ( tagName == "spouse-name" ) {
+      setSpouseName( element.text() );
+      return true;
+    }
+    break;
+  case 'x':
+    if ( tagName == "x-logo" ) {
+      mLogoAttachmentName = element.text();
+      return true;
+    }
+    if ( tagName == "x-sound" ) {
+      mSoundAttachmentName = element.text();
+      return true;
+    }
+    if ( tagName == "x-custom" ) {
+      loadCustomAttributes( element );
+      return true;
+    }
+    if ( tagName == "x-title" ) {
+      setTitle( element.text() );
+      return true;
+    }
+    break;
+  case 'w':
+    if ( tagName == "web-page" ) {
+      setWebPage( element.text() );
+      return true;
+    }
+    break;
+  default:
+    break;
+  }
+  return KolabBase::loadAttribute( element );
+}
+
+bool Contact::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  KolabBase::saveAttributes( element );
+  saveNameAttribute( element );
+  writeString( element, "free-busy-url", freeBusyUrl() );
+  writeString( element, "organization", organization() );
+  writeString( element, "web-page", webPage() );
+  writeString( element, "im-address", imAddress() );
+  writeString( element, "department", department() );
+  writeString( element, "office-location", officeLocation() );
+  writeString( element, "profession", profession() );
+  writeString( element, "role", role() );
+  writeString( element, "job-title", title() );
+  writeString( element, "manager-name", managerName() );
+  writeString( element, "assistant", assistant() );
+  writeString( element, "nick-name", nickName() );
+  writeString( element, "spouse-name", spouseName() );
+  writeString( element, "birthday", dateToString( birthday() ) );
+  writeString( element, "anniversary", dateToString( anniversary() ) );
+  if ( !picture().isNull() )
+    writeString( element, "picture", mPictureAttachmentName );
+  if ( !logo().isNull() )
+    writeString( element, "x-logo", mLogoAttachmentName );
+  if ( !sound().isNull() )
+    writeString( element, "x-sound", mSoundAttachmentName );
+  writeString( element, "children", children() );
+  writeString( element, "gender", gender() );
+  writeString( element, "language", language() );
+  savePhoneAttributes( element );
+  saveEmailAttributes( element );
+  saveAddressAttributes( element );
+  writeString( element, "preferred-address", preferredAddress() );
+  if ( mHasGeo ) {
+    writeString( element, "latitude", QString::number( latitude(), 'g', DBL_DIG ) );
+    writeString( element, "longitude", QString::number( longitude(), 'g', DBL_DIG ) );
+  }
+  saveCustomAttributes( element );
+
+  return true;
+}
+
+bool Contact::loadXML( const QDomDocument& document )
+{
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "contact" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected contact",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      if ( !loadAttribute( e ) ) {
+        // Unhandled tag - save for later storage
+        //kDebug() <<"Saving unhandled tag" << e.tagName();
+        Custom c;
+        c.app = s_unhandledTagAppName;
+        c.name = e.tagName();
+        c.value = e.text();
+        mCustomList.append( c );
+      }
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+QString Contact::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement("contact" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  document.appendChild( element );
+  return document.toString();
+}
+
+static QString addressTypeToString( int /*KABC::Address::Type*/ type )
+{
+  if ( type & KABC::Address::Home )
+    return "home";
+  if ( type & KABC::Address::Work )
+    return "business";
+  return "other";
+}
+
+static int addressTypeFromString( const QString& type )
+{
+  if ( type == "home" )
+    return KABC::Address::Home;
+  if ( type == "business" )
+    return KABC::Address::Work;
+  // well, this shows "other" in the editor, which is what we want...
+  return KABC::Address::Dom | KABC::Address::Intl | KABC::Address::Postal | KABC::Address::Parcel;
+}
+
+static QStringList phoneTypeToString( KABC::PhoneNumber::Type type )
+{
+  // KABC has a bitfield, i.e. the same phone number can be used for work and home
+  // and fax and cellphone etc. etc.
+  // So when saving we need to create as many tags as bits that were set.
+  QStringList types;
+  if ( type & KABC::PhoneNumber::Fax ) {
+    if ( type & KABC::PhoneNumber::Home )
+      types << "homefax";
+    else // assume work -- if ( type & KABC::PhoneNumber::Work )
+      types << "businessfax";
+    type = type & ~KABC::PhoneNumber::Home;
+    type = type & ~KABC::PhoneNumber::Work;
+  }
+
+  // To support both "home1" and "home2", map Home+Pref to home1
+  if ( ( type & KABC::PhoneNumber::Home ) && ( type & KABC::PhoneNumber::Pref ) )
+  {
+      types << "home1";
+      type = type & ~KABC::PhoneNumber::Home;
+      type = type & ~KABC::PhoneNumber::Pref;
+  }
+  // To support both "business1" and "business2", map Work+Pref to business1
+  if ( ( type & KABC::PhoneNumber::Work ) && ( type & KABC::PhoneNumber::Pref ) )
+  {
+      types << "business1";
+      type = type & ~KABC::PhoneNumber::Work;
+      type = type & ~KABC::PhoneNumber::Pref;
+  }
+
+
+  if ( type & KABC::PhoneNumber::Home )
+    types << "home2";
+  if ( type & KABC::PhoneNumber::Msg ) // Msg==messaging
+    types << "company";
+  if ( type & KABC::PhoneNumber::Work )
+    types << "business2";
+  if ( type & KABC::PhoneNumber::Pref )
+    types << "primary";
+  if ( type & KABC::PhoneNumber::Voice )
+    types << "callback"; // ##
+  if ( type & KABC::PhoneNumber::Cell )
+    types << "mobile";
+  if ( type & KABC::PhoneNumber::Video )
+    types << "radio"; // ##
+  if ( type & KABC::PhoneNumber::Bbs )
+    types << "ttytdd";
+  if ( type & KABC::PhoneNumber::Modem )
+    types << "telex"; // #
+  if ( type & KABC::PhoneNumber::Car )
+    types << "car";
+  if ( type & KABC::PhoneNumber::Isdn )
+    types << "isdn";
+  if ( type & KABC::PhoneNumber::Pcs )
+    types << "assistant"; // ## Assistant is e.g. secretary
+  if ( type & KABC::PhoneNumber::Pager )
+    types << "pager";
+  return types;
+}
+
+static KABC::PhoneNumber::Type phoneTypeFromString( const QString& type )
+{
+  if ( type == "homefax" )
+    return KABC::PhoneNumber::Home | KABC::PhoneNumber::Fax;
+  if ( type == "businessfax" )
+    return KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax;
+  if ( type == "business1" )
+    return KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref;
+  if ( type == "business2" )
+    return KABC::PhoneNumber::Work;
+  if ( type == "home1" )
+    return KABC::PhoneNumber::Home | KABC::PhoneNumber::Pref;
+  if ( type == "home2" )
+    return KABC::PhoneNumber::Home;
+  if ( type == "company" )
+    return KABC::PhoneNumber::Msg;
+  if ( type == "primary" )
+    return KABC::PhoneNumber::Pref;
+  if ( type == "callback" )
+    return KABC::PhoneNumber::Voice;
+  if ( type == "mobile" )
+    return KABC::PhoneNumber::Cell;
+  if ( type == "radio" )
+    return KABC::PhoneNumber::Video;
+  if ( type == "ttytdd" )
+    return KABC::PhoneNumber::Bbs;
+  if ( type == "telex" )
+    return KABC::PhoneNumber::Modem;
+  if ( type == "car" )
+    return KABC::PhoneNumber::Car;
+  if ( type == "isdn" )
+    return KABC::PhoneNumber::Isdn;
+  if ( type == "assistant" )
+    return KABC::PhoneNumber::Pcs;
+  if ( type == "pager" )
+    return KABC::PhoneNumber::Pager;
+  return KABC::PhoneNumber::Home; // whatever
+}
+
+static const char* s_knownCustomFields[] = {
+  "X-IMAddress",
+  "X-Office",
+  "X-Profession",
+  "X-ManagersName",
+  "X-AssistantsName",
+  "X-SpousesName",
+  "X-Anniversary",
+  "DistributionList",
+  0
+};
+
+
+// The saving is addressee -> Contact -> xml, this is the first part
+void Contact::setFields( const KABC::Addressee* addressee )
+{
+  KolabBase::setFields( addressee );
+
+  setGivenName( addressee->givenName() );
+  setMiddleNames( addressee->additionalName() );
+  setLastName( addressee->familyName() );
+  setFullName( addressee->formattedName() );
+  setPrefix( addressee->prefix() );
+  setSuffix( addressee->suffix() );
+  setOrganization( addressee->organization() );
+  setWebPage( addressee->url().url() );
+  setIMAddress( addressee->custom( "KADDRESSBOOK", "X-IMAddress" ) );
+  setDepartment( addressee->department());
+  setOfficeLocation( addressee->custom( "KADDRESSBOOK", "X-Office" ) );
+  setProfession( addressee->custom( "KADDRESSBOOK", "X-Profession" ) );
+  setRole( addressee->role() );
+  setTitle( addressee->title() );
+  setManagerName( addressee->custom( "KADDRESSBOOK", "X-ManagersName" ) );
+  setAssistant( addressee->custom( "KADDRESSBOOK", "X-AssistantsName" ) );
+  setNickName( addressee->nickName() );
+  setSpouseName( addressee->custom( "KADDRESSBOOK", "X-SpousesName" ) );
+  if ( !addressee->birthday().isNull() )
+    setBirthday( addressee->birthday().date() );
+  const QString& anniversary = addressee->custom( "KADDRESSBOOK", "X-Anniversary" );
+  if ( !anniversary.isEmpty() )
+    setAnniversary( stringToDate( anniversary  ) );
+
+  const QStringList emails = addressee->emails();
+  // Conversion problem here:
+  // KABC::Addressee has only one full name and N addresses, but the XML format
+  // has N times (fullname+address). So we just copy the fullname over and ignore it on loading.
+  for ( QStringList::ConstIterator it = emails.constBegin(); it != emails.constEnd(); ++it ) {
+    Email email;
+    email.displayName = fullName();
+    email.smtpAddress = *it;
+    addEmail( email );
+  }
+
+  // save formatted full email for later usage
+  mFullEmail = addressee->fullEmail();
+
+  // Now the real-world addresses
+  QString preferredAddress = "home";
+  const KABC::Address::List addresses = addressee->addresses();
+  for ( KABC::Address::List::ConstIterator it = addresses.constBegin() ; it != addresses.constEnd(); ++it ) {
+    Address address;
+    address.kdeAddressType = (*it).type();
+    address.type = addressTypeToString( address.kdeAddressType );
+    address.street = (*it).street();
+    address.pobox = (*it).postOfficeBox();
+    address.locality = (*it).locality();
+    address.region = (*it).region();
+    address.postalCode = (*it).postalCode();
+    address.country = (*it).country();
+    // ## TODO not in the XML format: extended address info.
+    // ## KDE-specific tags? Or hiding those fields? Or adding a warning?
+    addAddress( address );
+    if ( address.kdeAddressType & KABC::Address::Pref ) {
+      preferredAddress = address.type; // home, business or other
+    }
+  }
+  setPreferredAddress( preferredAddress );
+
+  const KABC::PhoneNumber::List phones = addressee->phoneNumbers();
+  for ( KABC::PhoneNumber::List::ConstIterator it = phones.constBegin(); it != phones.constEnd(); ++it ) {
+    // Create a tag per phone type set in the bitfield
+    QStringList types = phoneTypeToString( (*it).type() );
+    for( QStringList::ConstIterator typit = types.constBegin(); typit != types.constEnd(); ++typit ) {
+      PhoneNumber phoneNumber;
+      phoneNumber.type = *typit;
+      phoneNumber.number = (*it).number();
+      addPhoneNumber( phoneNumber );
+    }
+  }
+
+  setPicture( loadPictureFromAddressee( addressee->photo() ) );
+  mPictureAttachmentName = addressee->custom( "KOLAB", "PictureAttachmentName" );
+  if ( mPictureAttachmentName.isEmpty() )
+    mPictureAttachmentName = s_pictureAttachmentName;
+
+  setLogo( loadPictureFromAddressee( addressee->logo() ) );
+  mLogoAttachmentName = addressee->custom( "KOLAB", "LogoAttachmentName" );
+  if ( mLogoAttachmentName.isEmpty() )
+    mLogoAttachmentName = s_logoAttachmentName;
+
+  setSound( loadSoundFromAddressee( addressee->sound() ) );
+  mSoundAttachmentName = addressee->custom( "KOLAB", "SoundAttachmentName" );
+  if ( mSoundAttachmentName.isEmpty() )
+    mSoundAttachmentName = s_soundAttachmentName;
+
+  if ( addressee->geo().isValid() ) {
+    setLatitude( addressee->geo().latitude() );
+    setLongitude( addressee->geo().longitude() );
+    mHasGeo = true;
+  }
+
+  // Other KADDRESSBOOK custom fields than those already handled
+  //    (includes e.g. crypto settings, and extra im addresses)
+  QStringList knownCustoms;
+  for ( const char** p = s_knownCustomFields; *p; ++p )
+    knownCustoms << QString::fromLatin1( *p );
+  QStringList customs = addressee->customs();
+  for( QStringList::ConstIterator it = customs.constBegin(); it != customs.constEnd(); ++it ) {
+    // KABC::Addressee doesn't offer a real way to iterate over customs, other than splitting strings ourselves
+    // The format is "app-name:value".
+    int pos = (*it).indexOf( '-' );
+    if ( pos == -1 ) continue;
+    QString app = (*it).left( pos );
+    if ( app == "KOLAB" ) continue;
+    QString name = (*it).mid( pos + 1 );
+    pos = name.indexOf( ':' );
+    if ( pos == -1 ) continue;
+    QString value = name.mid( pos + 1 );
+    name = name.left( pos );
+    if ( !knownCustoms.contains( name ) ) {
+      //kDebug() <<"app=" << app <<" name=" << name <<" value=" << value;
+      Custom c;
+      if ( app != "KADDRESSBOOK" ) // that's the default
+        c.app = app;
+      c.name = name;
+      c.value = value;
+      mCustomList.append( c );
+    }
+  }
+
+  QString url = KCalCore::FreeBusyUrlStore::self()->readUrl( addressee->preferredEmail() );
+  if ( !url.isEmpty() ) {
+    setFreeBusyUrl( url );
+  }
+
+  // Those fields, although defined in Addressee, are not used in KDE
+  // (e.g. not visible in kaddressbook/addresseeeditorwidget.cpp)
+  // So it doesn't matter much if we don't have them in the XML.
+  // mailer, timezone, productId, sortString, agent, rfc2426 name()
+
+  // Things KAddressBook can't handle, so they are saved as unhandled tags:
+  // initials, children, gender, language
+}
+
+
+// The loading is: xml -> Contact -> addressee, this is the second part
+void Contact::saveTo( KABC::Addressee* addressee )
+{
+  // TODO: This needs the same set of TODOs as the setFields method
+  KolabBase::saveTo( addressee );
+
+  addressee->setGivenName( givenName() );
+  addressee->setAdditionalName( middleNames() );
+  addressee->setFamilyName( lastName() );
+  addressee->setFormattedName( fullName() );
+  addressee->setPrefix( prefix() );
+  addressee->setSuffix( suffix() );
+  addressee->setOrganization( organization() );
+  addressee->setUrl( webPage() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-IMAddress", imAddress() );
+  addressee->setDepartment( department() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-Office", officeLocation() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-Profession", profession() );
+  addressee->setRole( role() );
+  addressee->setTitle( title() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-ManagersName", managerName() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-AssistantsName", assistant() );
+  addressee->setNickName( nickName() );
+  addressee->insertCustom( "KADDRESSBOOK", "X-SpousesName", spouseName() );
+  if ( birthday().isValid() )
+    addressee->setBirthday( QDateTime( birthday() ) );
+
+  if ( anniversary().isValid() )
+    addressee->insertCustom( "KADDRESSBOOK", "X-Anniversary",
+                             dateToString( anniversary() ) );
+  else
+    addressee->removeCustom( "KADDRESSBOOK", "X-Anniversary" );
+
+  // We need to store both the original attachment name and the picture data into the addressee.
+  // This is important, otherwise we would save the image under another attachment name w/o deleting the original one!
+  if ( !mPicture.isNull() )
+    addressee->setPhoto( KABC::Picture( mPicture ) );
+  // Note that we must save the filename in all cases, so that removing the picture
+  // actually deletes the attachment.
+  addressee->insertCustom( "KOLAB", "PictureAttachmentName", mPictureAttachmentName );
+  if ( !mLogo.isNull() )
+    addressee->setLogo( KABC::Picture( mLogo ) );
+  addressee->insertCustom( "KOLAB", "LogoAttachmentName", mLogoAttachmentName );
+  if ( !mSound.isNull() )
+    addressee->setSound( KABC::Sound( mSound ) );
+  addressee->insertCustom( "KOLAB", "SoundAttachmentName", mSoundAttachmentName );
+
+  if ( mHasGeo )
+    addressee->setGeo( KABC::Geo( mLatitude, mLongitude ) );
+
+  QStringList emailAddresses;
+  for ( QList<Email>::ConstIterator it = mEmails.constBegin(); it != mEmails.constEnd(); ++it ) {
+    // we can't do anything with (*it).displayName
+    emailAddresses.append( (*it).smtpAddress );
+  }
+  addressee->setEmails( emailAddresses );
+
+  for ( QList<Address>::ConstIterator it = mAddresses.constBegin(); it != mAddresses.constEnd(); ++it ) {
+    KABC::Address address;
+    int type = (*it).kdeAddressType;
+    if ( type == -1 ) { // no kde-specific type available
+      type = addressTypeFromString( (*it).type );
+      if ( (*it).type == mPreferredAddress )
+        type |= KABC::Address::Pref;
+    }
+    address.setType( static_cast<KABC::Address::Type>(type) );
+    address.setStreet( (*it).street );
+    address.setPostOfficeBox( (*it).pobox );
+    address.setLocality( (*it).locality );
+    address.setRegion( (*it).region );
+    address.setPostalCode( (*it).postalCode );
+    address.setCountry( (*it).country );
+    addressee->insertAddress( address );
+  }
+
+  for ( QList<PhoneNumber>::ConstIterator it = mPhoneNumbers.constBegin(); it != mPhoneNumbers.constEnd(); ++it ) {
+    KABC::PhoneNumber number;
+    number.setType( phoneTypeFromString( (*it).type ) );
+    number.setNumber( (*it).number );
+    addressee->insertPhoneNumber( number );
+  }
+
+  for( QList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) {
+    QString app = (*it).app.isEmpty() ? QString::fromLatin1( "KADDRESSBOOK" ) : (*it).app;
+    addressee->insertCustom( app, (*it).name, (*it).value );
+  }
+  //kDebug() << addressee->customs();
+}
+
+QImage Contact::loadPictureFromAddressee( const KABC::Picture& picture )
+{
+  QImage img;
+  if ( !picture.isIntern() && !picture.url().isEmpty() ) {
+    QString tmpFile;
+    if ( KIO::NetAccess::download( picture.url(), tmpFile, 0 /*no widget known*/ ) ) {
+      img.load( tmpFile );
+      KIO::NetAccess::removeTempFile( tmpFile );
+    }
+  } else
+    img = picture.data();
+  return img;
+}
+
+QByteArray Kolab::Contact::loadSoundFromAddressee( const KABC::Sound& sound )
+{
+  QByteArray data;
+  if ( !sound.isIntern() && !sound.url().isEmpty() ) {
+    QString tmpFile;
+    if ( KIO::NetAccess::download( sound.url(), tmpFile, 0 /*no widget known*/ ) ) {
+      QFile f( tmpFile );
+      if ( f.open( QIODevice::ReadOnly ) ) {
+        data = f.readAll();
+        f.close();
+      }
+      KIO::NetAccess::removeTempFile( tmpFile );
+    }
+  } else
+    data = sound.data();
+  return data;
+}
+
+QString Kolab::Contact::productID() const
+{
+  // TODO: When KAB has the version number in a header file, don't hardcode (Bo)
+  // Or we could use Addressee::productID? (David)
+  return "KAddressBook 3.3, Kolab resource";
+}
diff --git a/libkolab/kolabformatV2/contact.h b/libkolab/kolabformatV2/contact.h
new file mode 100644
index 0000000..ba88a77
--- /dev/null
+++ b/libkolab/kolabformatV2/contact.h
@@ -0,0 +1,274 @@
+/*
+    This file is part of libkabc and/or kaddressbook.
+    Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+        <info at klaralvdalens-datakonsult.se>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLABCONTACT_H
+#define KOLABCONTACT_H
+
+#include "kolabbase.h"
+#include <qimage.h>
+
+namespace KABC {
+  class Addressee;
+  class Picture;
+  class Sound;
+}
+
+namespace Kolab {
+
+class Contact : public KolabBase {
+public:
+  struct PhoneNumber {
+  public:
+    QString type;
+    QString number;
+  };
+
+  struct Address {
+  public:
+    Address() : kdeAddressType( -1 )
+    {
+    }
+    int kdeAddressType; // KABC::Address::Type
+    QString type;       // kolab-compliant address type: home, work or other
+    QString street;
+    QString pobox;
+    QString locality;
+    QString region;
+    QString postalCode;
+    QString country;
+  };
+
+  explicit Contact( const KABC::Addressee* address );
+  Contact( const QString& xml );
+  ~Contact();
+
+  void saveTo( KABC::Addressee* address );
+
+  QString type() const { return "Contact"; }
+
+  void setGivenName( const QString& name );
+  QString givenName() const;
+
+  void setMiddleNames( const QString& names );
+  QString middleNames() const;
+
+  void setLastName( const QString& name );
+  QString lastName() const;
+
+  void setFullName( const QString& name );
+  QString fullName() const;
+
+  void setInitials( const QString& initials );
+  QString initials() const;
+
+  void setPrefix( const QString& prefix );
+  QString prefix() const;
+
+  void setSuffix( const QString& suffix );
+  QString suffix() const;
+
+  void setRole( const QString& role );
+  QString role() const;
+
+  void setFreeBusyUrl( const QString& fbUrl );
+  QString freeBusyUrl() const;
+
+  void setOrganization( const QString& organization );
+  QString organization() const;
+
+  void setWebPage( const QString& url );
+  QString webPage() const;
+
+  void setIMAddress( const QString& imAddress );
+  QString imAddress() const;
+
+  void setDepartment( const QString& department );
+  QString department() const;
+
+  void setOfficeLocation( const QString& location );
+  QString officeLocation() const;
+
+  void setProfession( const QString& profession );
+  QString profession() const;
+
+  void setTitle( const QString& title );
+  QString title() const;
+
+  void setManagerName( const QString& name );
+  QString managerName() const;
+
+  void setAssistant( const QString& name );
+  QString assistant() const;
+
+  void setNickName( const QString& name );
+  QString nickName() const;
+
+  void setSpouseName( const QString& name );
+  QString spouseName() const;
+
+  void setBirthday( const QDate& date );
+  QDate birthday() const;
+
+  void setAnniversary( const QDate& date );
+  QDate anniversary() const;
+
+  void setPicture( const QImage& image) { mPicture = image; }
+  QString pictureAttachmentName() const { return mPictureAttachmentName; }
+  QImage picture() const { return mPicture; }
+
+  void setLogo( const QImage& image ) { mLogo = image; }
+  QString logoAttachmentName() const { return mLogoAttachmentName; }
+  QImage logo() const { return mLogo; }
+
+  void setSound( const QByteArray& sound ) { mSound = sound; }
+  QString soundAttachmentName() const { return mSoundAttachmentName; }
+  QByteArray sound() const { return mSound; }
+
+  void setChildren( const QString& children );
+  QString children() const;
+
+  void setGender( const QString& gender );
+  QString gender() const;
+
+  void setLanguage( const QString& language );
+  QString language() const;
+
+  void addPhoneNumber( const PhoneNumber& number );
+  QList<PhoneNumber>& phoneNumbers();
+  const QList<PhoneNumber>& phoneNumbers() const;
+
+  void addEmail( const Email& email );
+  QList<Email>& emails();
+  const QList<Email>& emails() const;
+
+  QString fullEmail() const;
+
+  void addAddress( const Address& address );
+  QList<Address>& addresses();
+  const QList<Address>& addresses() const;
+
+  // which address is preferred: home or business or other
+  void setPreferredAddress( const QString& address );
+  QString preferredAddress() const;
+
+  float latitude() const { return mLatitude; }
+  void setLatitude( float latitude ) { mLatitude = latitude; }
+
+  float longitude() const { return mLongitude; }
+  void setLongitude( float longitude ) { mLongitude = longitude; }
+
+  // Load the attributes of this class
+  bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  bool saveAttributes( QDomElement& ) const;
+
+  // Load this note by reading the XML file
+  bool loadXML( const QDomDocument& xml );
+
+  // Serialize this note to an XML string
+  QString saveXML() const;
+
+protected:
+  void setFields( const KABC::Addressee* );
+
+private:
+  bool loadNameAttribute( QDomElement& element );
+  void saveNameAttribute( QDomElement& element ) const;
+
+  bool loadPhoneAttribute( QDomElement& element );
+  void savePhoneAttributes( QDomElement& element ) const;
+
+  void saveEmailAttributes( QDomElement& element ) const;
+
+  bool loadAddressAttribute( QDomElement& element );
+  void saveAddressAttributes( QDomElement& element ) const;
+
+  void loadCustomAttributes( QDomElement& element );
+  void saveCustomAttributes( QDomElement& element ) const;
+
+  QImage loadPictureFromAddressee( const KABC::Picture& picture );
+
+  QByteArray loadSoundFromAddressee( const KABC::Sound& sound );
+
+  QString productID() const;
+
+  QString mGivenName;
+  QString mMiddleNames;
+  QString mLastName;
+  QString mFullName;
+  QString mInitials;
+  QString mPrefix;
+  QString mSuffix;
+  QString mRole;
+  QString mFreeBusyUrl;
+  QString mOrganization;
+  QString mWebPage;
+  QString mIMAddress;
+  QString mDepartment;
+  QString mOfficeLocation;
+  QString mProfession;
+  QString mTitle;
+  QString mManagerName;
+  QString mAssistant;
+  QString mNickName;
+  QString mSpouseName;
+  QDate mBirthday;
+  QDate mAnniversary;
+  QImage mPicture;
+  QImage mLogo;
+  QByteArray mSound;
+  QString mPictureAttachmentName;
+  QString mLogoAttachmentName;
+  QString mSoundAttachmentName;
+  QString mChildren;
+  QString mGender;
+  QString mLanguage;
+  QList<PhoneNumber> mPhoneNumbers;
+  QList<Email> mEmails;
+  QString mFullEmail;
+  QList<Address> mAddresses;
+  QString mPreferredAddress;
+  float mLatitude;
+  float mLongitude;
+  bool mHasGeo;
+  struct Custom {
+    QString app;
+    QString name;
+    QString value;
+  };
+  QList<Custom> mCustomList;
+};
+
+}
+
+#endif // KOLABCONTACT_H
diff --git a/libkolab/kolabformatV2/distributionlist.cpp b/libkolab/kolabformatV2/distributionlist.cpp
new file mode 100644
index 0000000..840c5a8
--- /dev/null
+++ b/libkolab/kolabformatV2/distributionlist.cpp
@@ -0,0 +1,242 @@
+/*
+    This file is part of Akonadi KolabProxy.
+    Copyright (c) 2009 Kevin Krammer <kevin.krammer at gmx.at>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "distributionlist.h"
+
+#include <akonadi/itemfetchjob.h>
+#include <akonadi/itemfetchscope.h>
+#include <kabc/addressee.h>
+#include <kabc/contactgroup.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+static const char* s_unhandledTagAppName = "KOLABUNHANDLED"; // no hyphens in appnames!
+
+// saving (contactgroup->xml)
+DistributionList::DistributionList( const KABC::ContactGroup* contactGroup )
+{
+  setFields( contactGroup );
+}
+
+// loading (xml->contactgroup)
+DistributionList::DistributionList( const QString& xml )
+{
+  load( xml );
+}
+
+DistributionList::~DistributionList()
+{
+}
+
+void DistributionList::setName( const QString& name )
+{
+  mName = name;
+}
+
+QString DistributionList::name() const
+{
+  return mName;
+}
+
+void Kolab::DistributionList::loadDistrListMember( const QDomElement& element )
+{
+  Member member;
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+      if ( tagName == "display-name" )
+        member.displayName = e.text();
+      else if ( tagName == "smtp-address" )
+        member.email = e.text();
+    }
+  }
+  mDistrListMembers.append( member );
+}
+
+void DistributionList::saveDistrListMembers( QDomElement& element ) const
+{
+  QList<Member>::ConstIterator it = mDistrListMembers.constBegin();
+  for( ; it != mDistrListMembers.constEnd(); ++it ) {
+    QDomElement e = element.ownerDocument().createElement( "member" );
+    element.appendChild( e );
+    const Member& m = *it;
+    writeString( e, "display-name", m.displayName );
+    writeString( e, "smtp-address", m.email );
+  }
+}
+
+bool DistributionList::loadAttribute( QDomElement& element )
+{
+  const QString tagName = element.tagName();
+  switch ( tagName[0].toLatin1() ) {
+  case 'd':
+    if ( tagName == "display-name" ) {
+      setName( element.text() );
+      return true;
+    }
+    break;
+  case 'm':
+    if ( tagName == "member" ) {
+      loadDistrListMember( element );
+      return true;
+    }
+    break;
+  default:
+    break;
+  }
+  return KolabBase::loadAttribute( element );
+}
+
+bool DistributionList::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  KolabBase::saveAttributes( element );
+  writeString( element, "display-name", name() );
+  saveDistrListMembers( element );
+
+  return true;
+}
+
+bool DistributionList::loadXML( const QDomDocument& document )
+{
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "distribution-list" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected distribution-list",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      if ( !loadAttribute( e ) ) {
+        // Unhandled tag - save for later storage
+        //kDebug() <<"Saving unhandled tag" << e.tagName();
+        Custom c;
+        c.app = s_unhandledTagAppName;
+        c.name = e.tagName();
+        c.value = e.text();
+        mCustomList.append( c );
+      }
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+QString DistributionList::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement( "distribution-list" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  document.appendChild( element );
+  return document.toString();
+}
+
+QString DistributionList::productID() const
+{
+  // TODO should we get name/version from desktop file?
+  return QLatin1String( "Akonadi Kolab Proxy" );
+}
+
+// The saving is contactgroup -> DistributionList -> xml, this is the first part
+void DistributionList::setFields( const KABC::ContactGroup* contactGroup )
+{
+  KolabBase::setFields( contactGroup );
+
+  setName( contactGroup->name() );
+
+  // explicit contact data
+  for ( uint index = 0; index < contactGroup->dataCount(); ++index ) {
+    const KABC::ContactGroup::Data& data = contactGroup->data( index );
+
+    Member m;
+    m.displayName = data.name();
+    m.email = data.email();
+
+    mDistrListMembers.append( m );
+  }
+
+  // Hopefully all resources are available during saving, so we can look up
+  // in the addressbook to get name+email from the UID.
+  // TODO proxy should at least know the addressees it created
+  for ( uint index = 0; index < contactGroup->contactReferenceCount(); ++index ) {
+    const KABC::ContactGroup::ContactReference& reference = contactGroup->contactReference( index );
+
+    const Akonadi::Item item( reference.uid().toLongLong() );
+    Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( item );
+    job->fetchScope().fetchFullPayload();
+    if ( !job->exec() )
+      continue;
+
+    const Akonadi::Item::List items = job->items();
+    if ( items.count() != 1 )
+      continue;
+
+    const KABC::Addressee addressee = job->items().first().payload<KABC::Addressee>();
+
+    if ( !addressee.isEmpty() ) {
+      Member m;
+      m.displayName = addressee.formattedName();
+      m.email = reference.preferredEmail();
+      if ( m.email.isEmpty() )
+        m.email = addressee.preferredEmail();
+
+      mDistrListMembers.append( m );
+    }
+  }
+}
+
+// The loading is: xml -> DistributionList -> contactgroup, this is the second part
+void DistributionList::saveTo( KABC::ContactGroup* contactGroup )
+{
+  KolabBase::saveTo( contactGroup );
+
+  contactGroup->setName( name() );
+
+  QList<Member>::ConstIterator mit = mDistrListMembers.constBegin();
+  for ( ; mit != mDistrListMembers.constEnd(); ++mit ) {
+    const KABC::ContactGroup::Data data( (*mit).displayName, (*mit).email );
+    contactGroup->append( data );
+  }
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkolab/kolabformatV2/distributionlist.h b/libkolab/kolabformatV2/distributionlist.h
new file mode 100644
index 0000000..33dc61d
--- /dev/null
+++ b/libkolab/kolabformatV2/distributionlist.h
@@ -0,0 +1,96 @@
+/*
+    This file is part of Akonadi KolabProxy
+    Copyright (c) 2009 <kevin.krammer at gmx.at>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLABDISTRIBUTIONLIST_H
+#define KOLABDISTRIBUTIONLIST_H
+
+#include "kolabbase.h"
+
+namespace KABC {
+  class ContactGroup;
+}
+
+namespace Kolab {
+
+class DistributionList : public KolabBase {
+public:
+  explicit DistributionList( const KABC::ContactGroup* contactGroup );
+  DistributionList( const QString& xml );
+  ~DistributionList();
+
+  void saveTo( KABC::ContactGroup* contactGroup );
+
+  QString type() const { return "DistributionList"; }
+
+  void setName( const QString& name );
+  QString name() const;
+
+  // Load the attributes of this class
+  bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  bool saveAttributes( QDomElement& ) const;
+
+  // Load this note by reading the XML file
+  bool loadXML( const QDomDocument& xml );
+
+  // Serialize this note to an XML string
+  QString saveXML() const;
+
+  QString productID() const;
+
+protected:
+  void setFields( const KABC::ContactGroup* );
+
+private:
+  void loadDistrListMember( const QDomElement& element );
+  void saveDistrListMembers( QDomElement& element ) const;
+
+  QString mName;
+
+  struct Custom {
+    QString app;
+    QString name;
+    QString value;
+  };
+  QList<Custom> mCustomList;
+
+  struct Member {
+    QString displayName;
+    QString email;
+  };
+  QList<Member> mDistrListMembers;
+};
+
+}
+
+#endif // KOLABDISTRIBUTIONLIST_H
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkolab/kolabformatV2/event.cpp b/libkolab/kolabformatV2/event.cpp
new file mode 100644
index 0000000..103ced3
--- /dev/null
+++ b/libkolab/kolabformatV2/event.cpp
@@ -0,0 +1,221 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "event.h"
+
+#include <kcalcore/event.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Event::Ptr Event::fromXml( const QDomDocument& xmlDoc, const QString& tz)
+{
+  Event event( tz );
+  event.loadXML( xmlDoc );
+  KCalCore::Event::Ptr kcalEvent( new KCalCore::Event() );
+  event.saveTo( kcalEvent );
+  return kcalEvent;
+}
+
+QString Event::eventToXML( const KCalCore::Event::Ptr &kcalEvent, const QString& tz  )
+{
+  Event event( tz, kcalEvent );
+  return event.saveXML();
+}
+
+Event::Event( const QString& tz, const KCalCore::Event::Ptr &event )
+  : Incidence( tz, event ),
+  mShowTimeAs( KCalCore::Event::Opaque ), mHasEndDate( false )
+{
+  if ( event ) {
+    setFields( event );
+  }
+}
+
+Event::~Event()
+{
+}
+
+void Event::setTransparency( KCalCore::Event::Transparency transparency )
+{
+  mShowTimeAs = transparency;
+}
+
+KCalCore::Event::Transparency Event::transparency() const
+{
+  return mShowTimeAs;
+}
+
+void Event::setEndDate( const KDateTime& date )
+{
+  mEndDate = date;
+  mHasEndDate = true;
+  if ( mFloatingStatus == AllDay )
+    kDebug() <<"ERROR: Time on end date but no time on the event";
+  mFloatingStatus = HasTime;
+}
+
+void Event::setEndDate( const QDate& date )
+{
+  mEndDate = KDateTime( date );
+  mHasEndDate = true;
+  if ( mFloatingStatus == HasTime )
+    kDebug() <<"ERROR: No time on end date but time on the event";
+  mFloatingStatus = AllDay;
+}
+
+void Event::setEndDate( const QString& endDate )
+{
+  if ( endDate.length() > 10 )
+    // This is a date + time
+    setEndDate( stringToDateTime( endDate ) );
+  else
+    // This is only a date
+    setEndDate( stringToDate( endDate ) );
+}
+
+KDateTime Event::endDate() const
+{
+  return mEndDate;
+}
+
+bool Event::loadAttribute( QDomElement& element )
+{
+  // This method doesn't handle the color-label tag yet
+  QString tagName = element.tagName();
+
+  if ( tagName == "show-time-as" ) {
+    // TODO: Support tentative and outofoffice
+    if ( element.text() == "free" )
+      setTransparency( KCalCore::Event::Transparent );
+    else
+      setTransparency( KCalCore::Event::Opaque );
+  } else if ( tagName == "end-date" )
+    setEndDate( element.text() );
+  else
+    return Incidence::loadAttribute( element );
+
+  // We handled this
+  return true;
+}
+
+bool Event::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  Incidence::saveAttributes( element );
+
+  // TODO: Support tentative and outofoffice
+  if ( transparency() == KCalCore::Event::Transparent )
+    writeString( element, "show-time-as", "free" );
+  else
+    writeString( element, "show-time-as", "busy" );
+  if ( mHasEndDate ) {
+    if ( mFloatingStatus == HasTime )
+      writeString( element, "end-date", dateTimeToString( endDate() ) );
+    else
+      writeString( element, "end-date", dateToString( endDate().date() ) );
+  }
+
+  return true;
+}
+
+
+bool Event::loadXML( const QDomDocument& document )
+{
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "event" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected event",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      loadAttribute( e );
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+QString Event::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement( "event" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  document.appendChild( element );
+  return document.toString();
+}
+
+void Event::setFields( const KCalCore::Event::Ptr &event )
+{
+  Incidence::setFields( event );
+
+  // note: if hasEndDate() is false and hasDuration() is true
+  // dtEnd() returns start+duration
+  if ( event->hasEndDate() || event->hasDuration() ) {
+    if ( event->allDay() ) {
+      // This is an all-day event. Don't timezone move this one
+      mFloatingStatus = AllDay;
+      setEndDate( event->dtEnd().date() );
+    } else {
+      mFloatingStatus = HasTime;
+      setEndDate( localToUTC( event->dtEnd() ) );
+    }
+  } else {
+    mHasEndDate = false;
+  }
+  setTransparency( event->transparency() );
+}
+
+void Event::saveTo( const KCalCore::Event::Ptr &event )
+{
+  Incidence::saveTo( event );
+
+  event->setHasEndDate( mHasEndDate );
+  if ( mHasEndDate ) {
+    if ( mFloatingStatus == AllDay )
+      // This is an all-day event. Don't timezone move this one
+      event->setDtEnd( endDate() );
+    else
+      event->setDtEnd( utcToLocal( endDate() ) );
+  }
+  event->setTransparency( transparency() );
+}
diff --git a/libkolab/kolabformatV2/event.h b/libkolab/kolabformatV2/event.h
new file mode 100644
index 0000000..acc327b
--- /dev/null
+++ b/libkolab/kolabformatV2/event.h
@@ -0,0 +1,101 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLAB_EVENT_H
+#define KOLAB_EVENT_H
+
+#include "incidence.h"
+
+#include <kcalcore/event.h>
+
+class QDomElement;
+
+
+namespace Kolab {
+
+/**
+ * This class represents an event, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::Event.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Event : public Incidence {
+public:
+  /// Use this to parse an xml string to a event entry
+  /// The caller is responsible for deleting the returned event
+  static KCalCore::Event::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz);
+
+  /// Use this to get an xml string describing this event entry
+  static QString eventToXML( const KCalCore::Event::Ptr &, const QString& tz );
+
+  /// Create a event object and
+  explicit Event( const QString& tz,
+                  const KCalCore::Event::Ptr &event = KCalCore::Event::Ptr() );
+  virtual ~Event();
+
+  void saveTo( const KCalCore::Event::Ptr &event );
+
+  virtual QString type() const { return "Event"; }
+
+  virtual void setTransparency( KCalCore::Event::Transparency transparency );
+  virtual KCalCore::Event::Transparency transparency() const;
+
+  virtual void setEndDate( const KDateTime& date );
+  virtual void setEndDate( const QDate& date );
+  virtual void setEndDate( const QString& date );
+  virtual KDateTime endDate() const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+  // Load this event by reading the XML file
+  virtual bool loadXML( const QDomDocument& xml );
+
+  // Serialize this event to an XML string
+  virtual QString saveXML() const;
+
+protected:
+  // Read all known fields from this ical incidence
+  void setFields( const KCalCore::Event::Ptr & );
+
+  KCalCore::Event::Transparency mShowTimeAs;
+  KDateTime mEndDate;
+  bool mHasEndDate;
+};
+
+}
+
+#endif // KOLAB_EVENT_H
diff --git a/libkolab/kolabformatV2/incidence.cpp b/libkolab/kolabformatV2/incidence.cpp
new file mode 100644
index 0000000..9f4323f
--- /dev/null
+++ b/libkolab/kolabformatV2/incidence.cpp
@@ -0,0 +1,976 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "incidence.h"
+#include "akonadi-version.h"
+
+#include <QList>
+
+#include <kcalcore/journal.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include <QBitArray>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( const QString& tz, const KCalCore::Incidence::Ptr &incidence )
+  : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false )
+{
+  Q_UNUSED( incidence );
+}
+
+Incidence::~Incidence()
+{
+}
+
+void Incidence::setSummary( const QString& summary )
+{
+  mSummary = summary;
+}
+
+QString Incidence::summary() const
+{
+  return mSummary;
+}
+
+void Incidence::setLocation( const QString& location )
+{
+  mLocation = location;
+}
+
+QString Incidence::location() const
+{
+  return mLocation;
+}
+
+void Incidence::setOrganizer( const Email& organizer )
+{
+  mOrganizer = organizer;
+}
+
+KolabBase::Email Incidence::organizer() const
+{
+  return mOrganizer;
+}
+
+void Incidence::setStartDate( const KDateTime& startDate )
+{
+  mStartDate = startDate;
+  if ( mFloatingStatus == AllDay )
+    kDebug() <<"ERROR: Time on start date but no time on the event";
+  mFloatingStatus = HasTime;
+}
+
+void Incidence::setStartDate( const QDate& startDate )
+{
+  mStartDate = KDateTime( startDate );
+  if ( mFloatingStatus == HasTime )
+    kDebug() <<"ERROR: No time on start date but time on the event";
+  mFloatingStatus = AllDay;
+}
+
+void Incidence::setStartDate( const QString& startDate )
+{
+  if ( startDate.length() > 10 )
+    // This is a date + time
+    setStartDate( stringToDateTime( startDate ) );
+  else
+    // This is only a date
+    setStartDate( stringToDate( startDate ) );
+}
+
+KDateTime Incidence::startDate() const
+{
+  return mStartDate;
+}
+
+void Incidence::setAlarm( float alarm )
+{
+  mAlarm = alarm;
+  mHasAlarm = true;
+}
+
+float Incidence::alarm() const
+{
+  return mAlarm;
+}
+
+Incidence::Recurrence Incidence::recurrence() const
+{
+  return mRecurrence;
+}
+
+void Incidence::addAttendee( const Attendee& attendee )
+{
+  mAttendees.append( attendee );
+}
+
+QList<Incidence::Attendee>& Incidence::attendees()
+{
+  return mAttendees;
+}
+
+const QList<Incidence::Attendee>& Incidence::attendees() const
+{
+  return mAttendees;
+}
+
+void Incidence::setInternalUID( const QString& iuid )
+{
+  mInternalUID = iuid;
+}
+
+QString Incidence::internalUID() const
+{
+  return mInternalUID;
+}
+
+bool Incidence::loadAttendeeAttribute( QDomElement& element,
+                                       Attendee& attendee )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "display-name" )
+        attendee.displayName = e.text();
+      else if ( tagName == "smtp-address" )
+        attendee.smtpAddress = e.text();
+      else if ( tagName == "status" )
+        attendee.status = e.text();
+      else if ( tagName == "request-response" )
+        // This sets reqResp to false, if the text is "false". Otherwise it
+        // sets it to true. This means the default setting is true.
+        attendee.requestResponse = ( e.text().toLower() != "false" );
+      else if ( tagName == "invitation-sent" )
+        // Like above, only this defaults to false
+        attendee.invitationSent = ( e.text().toLower() != "true" );
+      else if ( tagName == "role" )
+        attendee.role = e.text();
+      else if ( tagName == "delegated-to" )
+        attendee.delegate = e.text();
+      else if ( tagName == "delegated-from" )
+        attendee.delegator = e.text();
+      else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+void Incidence::saveAttendeeAttribute( QDomElement& element,
+                                       const Attendee& attendee ) const
+{
+  QDomElement e = element.ownerDocument().createElement( "attendee" );
+  element.appendChild( e );
+  writeString( e, "display-name", attendee.displayName );
+  writeString( e, "smtp-address", attendee.smtpAddress );
+  writeString( e, "status", attendee.status );
+  writeString( e, "request-response",
+               ( attendee.requestResponse ? "true" : "false" ) );
+  writeString( e, "invitation-sent",
+               ( attendee.invitationSent ? "true" : "false" ) );
+  writeString( e, "role", attendee.role );
+  writeString( e, "delegated-to", attendee.delegate );
+  writeString( e, "delegated-from", attendee.delegator );
+}
+
+void Incidence::saveAttendees( QDomElement& element ) const
+{
+  foreach ( const Attendee& attendee, mAttendees )
+    saveAttendeeAttribute( element, attendee );
+}
+
+void Incidence::saveAttachments( QDomElement& element ) const
+{
+  foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+    if ( a->isUri() ) {
+      writeString( element, "link-attachment", a->uri() );
+    } else if ( a->isBinary() ) {
+      writeString( element, "inline-attachment", a->label() );
+    }
+  }
+}
+
+void Incidence::saveAlarms( QDomElement& element ) const
+{
+  if ( mAlarms.isEmpty() ) return;
+
+  QDomElement list = element.ownerDocument().createElement( "advanced-alarms" );
+  element.appendChild( list );
+  foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+    QDomElement e = list.ownerDocument().createElement( "alarm" );
+    list.appendChild( e );
+
+    writeString( e, "enabled", a->enabled() ? "1" : "0" );
+    if ( a->hasStartOffset() ) {
+      writeString( e, "start-offset", QString::number( a->startOffset().asSeconds()/60 ) );
+    }
+    if ( a->hasEndOffset() ) {
+      writeString( e, "end-offset", QString::number( a->endOffset().asSeconds()/60 ) );
+    }
+    if ( a->repeatCount() ) {
+      writeString( e, "repeat-count", QString::number( a->repeatCount() ) );
+      writeString( e, "repeat-interval", QString::number( a->snoozeTime().asSeconds() ) );
+    }
+
+    switch ( a->type() ) {
+    case KCalCore::Alarm::Invalid:
+      break;
+    case KCalCore::Alarm::Display:
+      e.setAttribute( "type", "display" );
+      writeString( e, "text", a->text() );
+      break;
+    case KCalCore::Alarm::Procedure:
+      e.setAttribute( "type", "procedure" );
+      writeString( e, "program", a->programFile() );
+      writeString( e, "arguments", a->programArguments() );
+      break;
+    case KCalCore::Alarm::Email:
+    {
+      e.setAttribute( "type", "email" );
+      QDomElement addresses = e.ownerDocument().createElement( "addresses" );
+      e.appendChild( addresses );
+      foreach ( const KCalCore::Person::Ptr &person, a->mailAddresses() ) {
+        writeString( addresses, "address", person->fullName() );
+      }
+      writeString( e, "subject", a->mailSubject() );
+      writeString( e, "mail-text", a->mailText() );
+      QDomElement attachments = e.ownerDocument().createElement( "attachments" );
+      e.appendChild( attachments );
+      foreach ( const QString &attachment, a->mailAttachments() ) {
+        writeString( attachments, "attachment", attachment );
+      }
+      break;
+    }
+    case KCalCore::Alarm::Audio:
+      e.setAttribute( "type", "audio" );
+      writeString( e, "file", a->audioFile() );
+      break;
+    default:
+      kWarning() << "Unhandled alarm type:" << a->type();
+      break;
+    }
+  }
+}
+
+void Incidence::saveRecurrence( QDomElement& element ) const
+{
+  QDomElement e = element.ownerDocument().createElement( "recurrence" );
+  element.appendChild( e );
+  e.setAttribute( "cycle", mRecurrence.cycle );
+  if ( !mRecurrence.type.isEmpty() )
+    e.setAttribute( "type", mRecurrence.type );
+  writeString( e, "interval", QString::number( mRecurrence.interval ) );
+  foreach ( const QString& recurrence, mRecurrence.days ) {
+    writeString( e, "day", recurrence );
+  }
+  if ( !mRecurrence.dayNumber.isEmpty() )
+    writeString( e, "daynumber", mRecurrence.dayNumber );
+  if ( !mRecurrence.month.isEmpty() )
+    writeString( e, "month", mRecurrence.month );
+  if ( !mRecurrence.rangeType.isEmpty() ) {
+    QDomElement range = element.ownerDocument().createElement( "range" );
+    e.appendChild( range );
+    range.setAttribute( "type", mRecurrence.rangeType );
+    QDomText t = element.ownerDocument().createTextNode( mRecurrence.range );
+    range.appendChild( t );
+  }
+  foreach ( const QDate& date, mRecurrence.exclusions ) {
+    writeString( e, "exclusion", dateToString( date ) );
+  }
+}
+
+void Incidence::loadRecurrence( const QDomElement& element )
+{
+  mRecurrence.interval = 0;
+  mRecurrence.cycle = element.attribute( "cycle" );
+  mRecurrence.type = element.attribute( "type" );
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+      if ( tagName == "interval" ) {
+        //kolab/issue4229, sometimes  the interval value can be empty
+        if ( e.text().isEmpty() || e.text().toInt() <= 0 ) {
+          mRecurrence.interval = 1;
+        } else {
+          mRecurrence.interval = e.text().toInt();
+        }
+      }
+      else if ( tagName == "day" ) // can be present multiple times
+        mRecurrence.days.append( e.text() );
+      else if ( tagName == "daynumber" )
+        mRecurrence.dayNumber = e.text();
+      else if ( tagName == "month" )
+        mRecurrence.month = e.text();
+      else if ( tagName == "range" ) {
+        mRecurrence.rangeType = e.attribute( "type" );
+        mRecurrence.range = e.text();
+      } else if ( tagName == "exclusion" ) {
+        mRecurrence.exclusions.append( stringToDate( e.text() ) );
+      } else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    }
+  }
+}
+
+static void loadAddressesHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "address" ) {
+        a->addMailAddress( KCalCore::Person::fromFullName( e.text() ) );
+      } else {
+        kWarning() << "Unhandled tag" << tagName;
+      }
+    }
+  }
+}
+
+static void loadAttachmentsHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "attachment" ) {
+        a->addMailAttachment( e.text() );
+      } else {
+        kWarning() << "Unhandled tag" << tagName;
+      }
+    }
+  }
+}
+
+static void loadAlarmHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "start-offset" ) {
+        a->setStartOffset( e.text().toInt()*60 );
+      } else if ( tagName == "end-offset" ) {
+        a->setEndOffset( e.text().toInt()*60 );
+      } else if ( tagName == "repeat-count" ) {
+        a->setRepeatCount( e.text().toInt() );
+      } else if ( tagName == "repeat-interval" ) {
+        a->setSnoozeTime( e.text().toInt() );
+      } else if ( tagName == "text" ) {
+        a->setText( e.text() );
+      } else if ( tagName == "program" ) {
+        a->setProgramFile( e.text() );
+      } else if ( tagName == "arguments" ) {
+        a->setProgramArguments( e.text() );
+      } else if ( tagName == "addresses" ) {
+        loadAddressesHelper( e, a );
+      } else if ( tagName == "subject" ) {
+        a->setMailSubject( e.text() );
+      } else if ( tagName == "mail-text" ) {
+        a->setMailText( e.text() );
+      } else if ( tagName == "attachments" ) {
+        loadAttachmentsHelper( e, a );
+      } else if ( tagName == "file" ) {
+        a->setAudioFile( e.text() );
+      } else if ( tagName == "enabled" ) {
+        a->setEnabled( e.text().toInt() != 0 );
+      } else {
+        kWarning() << "Unhandled tag" << tagName;
+      }
+    }
+  }
+}
+
+void Incidence::loadAlarms( const QDomElement& element )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      QString tagName = e.tagName();
+
+      if ( tagName == "alarm" ) {
+        KCalCore::Alarm::Ptr a = KCalCore::Alarm::Ptr( new KCalCore::Alarm( 0 ) );
+        a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise.
+        QString type = e.attribute( "type" );
+        if ( type == "display" ) {
+          a->setType( KCalCore::Alarm::Display );
+        } else if ( type == "procedure" ) {
+          a->setType( KCalCore::Alarm::Procedure );
+        } else if ( type == "email" ) {
+          a->setType( KCalCore::Alarm::Email );
+        } else if ( type == "audio" ) {
+          a->setType( KCalCore::Alarm::Audio );
+        } else {
+          kWarning() << "Unhandled alarm type:" << type;
+        }
+
+        loadAlarmHelper( e, a );
+        mAlarms << a;
+      } else {
+        kWarning() << "Unhandled tag" << tagName;
+      }
+    }
+  }
+}
+
+bool Incidence::loadAttribute( QDomElement& element )
+{
+  QString tagName = element.tagName();
+
+  if ( tagName == "summary" )
+    setSummary( element.text() );
+  else if ( tagName == "location" )
+    setLocation( element.text() );
+  else if ( tagName == "organizer" ) {
+    Email email;
+    if ( loadEmailAttribute( element, email ) ) {
+      setOrganizer( email );
+      return true;
+    } else
+      return false;
+  } else if ( tagName == "start-date" )
+    setStartDate( element.text() );
+  else if ( tagName == "recurrence" )
+    loadRecurrence( element );
+  else if ( tagName == "attendee" ) {
+    Attendee attendee;
+    if ( loadAttendeeAttribute( element, attendee ) ) {
+      addAttendee( attendee );
+      return true;
+    } else
+      return false;
+  } else if ( tagName == "link-attachment" ) {
+    mAttachments.push_back( KCalCore::Attachment::Ptr( new KCalCore::Attachment( element.text() ) ) );
+  } else if ( tagName == "alarm" )
+    // Alarms should be minutes before. Libkcal uses event time + alarm time
+    setAlarm( - element.text().toInt() );
+  else if ( tagName == "advanced-alarms" )
+    loadAlarms( element );
+  else if ( tagName == "x-kde-internaluid" )
+    setInternalUID( element.text() );
+  else if ( tagName == "x-custom" ) {
+    loadCustomAttributes( element );
+  } else if ( tagName == "inline-attachment"  ) {
+    // we handle that separately later on, so no need to create a KolabUnhandled entry for it
+  } else {
+    bool ok = KolabBase::loadAttribute( element );
+    if ( !ok ) {
+        // Unhandled tag - save for later storage
+        kDebug() <<"Saving unhandled tag" << element.tagName();
+        Custom c;
+        c.key = QByteArray( "X-KDE-KolabUnhandled-" ) + element.tagName().toLatin1();
+        c.value = element.text();
+        mCustomList.append( c );
+    }
+  }
+  // We handled this
+  return true;
+}
+
+bool Incidence::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  KolabBase::saveAttributes( element );
+
+  if ( mFloatingStatus == HasTime )
+    writeString( element, "start-date", dateTimeToString( startDate() ) );
+  else
+    writeString( element, "start-date", dateToString( startDate().date() ) );
+  writeString( element, "summary", summary() );
+  writeString( element, "location", location() );
+  saveEmailAttribute( element, organizer(), "organizer" );
+  if ( !mRecurrence.cycle.isEmpty() )
+    saveRecurrence( element );
+  saveAttendees( element );
+  saveAttachments( element );
+  if ( mHasAlarm ) {
+    // Alarms should be minutes before. Libkcal uses event time + alarm time
+    int alarmTime = qRound( -alarm() );
+    writeString( element, "alarm", QString::number( alarmTime ) );
+  }
+  saveAlarms( element );
+  writeString( element, "x-kde-internaluid", internalUID() );
+  saveCustomAttributes( element );
+  return true;
+}
+
+void Incidence::saveCustomAttributes( QDomElement& element ) const
+{
+  foreach ( const Custom& custom, mCustomList ) {
+    QString key( custom.key );
+    Q_ASSERT( !key.isEmpty() );
+    if ( key.startsWith( QLatin1String( "X-KDE-KolabUnhandled-" ) ) ) {
+      key = key.mid( strlen( "X-KDE-KolabUnhandled-" ) );
+      writeString( element, key, custom.value );
+    } else {
+      // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+      QDomElement e = element.ownerDocument().createElement( "x-custom" );
+      element.appendChild( e );
+      e.setAttribute( "key", key );
+      e.setAttribute( "value", custom.value );
+    }
+  }
+}
+
+void Incidence::loadCustomAttributes( QDomElement& element )
+{
+  Custom custom;
+  custom.key = element.attribute( "key" ).toLatin1();
+  custom.value = element.attribute( "value" );
+  mCustomList.append( custom );
+}
+
+static KCalCore::Attendee::PartStat attendeeStringToStatus( const QString& s )
+{
+  if ( s == "none" )
+    return KCalCore::Attendee::NeedsAction;
+  if ( s == "tentative" )
+    return KCalCore::Attendee::Tentative;
+  if ( s == "declined" )
+    return KCalCore::Attendee::Declined;
+  if ( s == "delegated" )
+    return KCalCore::Attendee::Delegated;
+
+  // Default:
+  return KCalCore::Attendee::Accepted;
+}
+
+static QString attendeeStatusToString( KCalCore::Attendee::PartStat status )
+{
+  switch( status ) {
+  case KCalCore::Attendee::NeedsAction:
+    return "none";
+  case KCalCore::Attendee::Accepted:
+    return "accepted";
+  case KCalCore::Attendee::Declined:
+    return "declined";
+  case KCalCore::Attendee::Tentative:
+    return "tentative";
+  case KCalCore::Attendee::Delegated:
+    return "delegated";
+  case KCalCore::Attendee::Completed:
+  case KCalCore::Attendee::InProcess:
+    // These don't have any meaning in the Kolab format, so just use:
+    return "accepted";
+  default:
+    // Default for the case that there are more added later:
+    return "accepted";
+  }
+}
+
+static KCalCore::Attendee::Role attendeeStringToRole( const QString& s )
+{
+  if ( s == "optional" )
+    return KCalCore::Attendee::OptParticipant;
+  if ( s == "resource" )
+    return KCalCore::Attendee::NonParticipant;
+  return KCalCore::Attendee::ReqParticipant;
+}
+
+static QString attendeeRoleToString( KCalCore::Attendee::Role role )
+{
+  switch( role ) {
+  case KCalCore::Attendee::ReqParticipant:
+    return "required";
+  case KCalCore::Attendee::OptParticipant:
+    return "optional";
+  case KCalCore::Attendee::Chair:
+    // We don't have the notion of chair, so use
+    return "required";
+  case KCalCore::Attendee::NonParticipant:
+    // In Kolab, a non-participant is a resource
+    return "resource";
+  }
+
+  // Default for the case that there are more added later:
+  return "required";
+}
+
+static const char *s_weekDayName[] =
+{
+  "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"
+};
+
+static const char *s_monthName[] =
+{
+  "january", "february", "march", "april", "may", "june", "july",
+  "august", "september", "october", "november", "december"
+};
+
+void Incidence::setRecurrence( KCalCore::Recurrence* recur )
+{
+  mRecurrence.interval = recur->frequency();
+  switch ( recur->recurrenceType() ) {
+  case KCalCore::Recurrence::rMinutely: // Not handled by the kolab XML
+    mRecurrence.cycle = "minutely";
+    break;
+  case KCalCore::Recurrence::rHourly:  // Not handled by the kolab XML
+    mRecurrence.cycle = "hourly";
+    break;
+  case KCalCore::Recurrence::rDaily:
+    mRecurrence.cycle = "daily";
+    break;
+  case KCalCore::Recurrence::rWeekly: // every X weeks
+    mRecurrence.cycle = "weekly";
+    {
+      QBitArray arr = recur->days();
+      for ( uint idx = 0 ; idx < 7 ; ++idx )
+        if ( arr.testBit( idx ) )
+          mRecurrence.days.append( s_weekDayName[idx] );
+    }
+    break;
+  case KCalCore::Recurrence::rMonthlyPos: {
+    mRecurrence.cycle = "monthly";
+    mRecurrence.type = "weekday";
+    QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions();
+    if ( !monthPositions.isEmpty() ) {
+      KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+      // TODO: Handle multiple days in the same week
+      mRecurrence.dayNumber = QString::number( monthPos.pos() );
+      mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+        // Not (properly) handled(?): monthPos.negative (nth days before end of month)
+    }
+    break;
+  }
+  case KCalCore::Recurrence::rMonthlyDay: {
+    mRecurrence.cycle = "monthly";
+    mRecurrence.type = "daynumber";
+    QList<int> monthDays = recur->monthDays();
+    // ####### Kolab XML limitation: only the first month day is used
+    if ( !monthDays.isEmpty() )
+      mRecurrence.dayNumber = QString::number( monthDays.first() );
+    break;
+  }
+  case KCalCore::Recurrence::rYearlyMonth: // (day n of Month Y)
+  {
+    mRecurrence.cycle = "yearly";
+    mRecurrence.type = "monthday";
+    QList<int> rmd = recur->yearDates();
+    int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+    mRecurrence.dayNumber = QString::number( day );
+    QList<int> months = recur->yearMonths();
+    if ( !months.isEmpty() )
+      mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+    break;
+  }
+  case KCalCore::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook
+    mRecurrence.cycle = "yearly";
+    mRecurrence.type = "yearday";
+    mRecurrence.dayNumber = QString::number( recur->yearDays().first() );
+    break;
+  case KCalCore::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+    mRecurrence.cycle = "yearly";
+    mRecurrence.type = "weekday";
+    QList<int> months = recur->yearMonths();
+    if ( !months.isEmpty() )
+      mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+    QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions();
+    if ( !monthPositions.isEmpty() ) {
+      KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+      // TODO: Handle multiple days in the same week
+      mRecurrence.dayNumber = QString::number( monthPos.pos() );
+      mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+
+      //mRecurrence.dayNumber = QString::number( *recur->yearNums().getFirst() );
+      // Not handled: monthPos.negative (nth days before end of month)
+    }
+    break;
+  }
+  int howMany = recur->duration();
+  if ( howMany > 0 ) {
+    mRecurrence.rangeType = "number";
+    mRecurrence.range = QString::number( howMany );
+  } else if ( howMany == 0 ) {
+    mRecurrence.rangeType = "date";
+    mRecurrence.range = dateToString( recur->endDate() );
+  } else {
+    mRecurrence.rangeType = "none";
+  }
+}
+
+void Incidence::setFields( const KCalCore::Incidence::Ptr &incidence )
+{
+  KolabBase::setFields( incidence );
+
+  if ( incidence->allDay() ) {
+    // This is a all-day event. Don't timezone move this one
+    mFloatingStatus = AllDay;
+    setStartDate( incidence->dtStart().date() );
+  } else {
+    mFloatingStatus = HasTime;
+    setStartDate( localToUTC( incidence->dtStart() ) );
+  }
+
+  setSummary( incidence->summary() );
+  setLocation( incidence->location() );
+
+  // Alarm
+  mHasAlarm = false; // Will be set to true, if we actually have one
+  if ( incidence->hasEnabledAlarms() ) {
+    const KCalCore::Alarm::List& alarms = incidence->alarms();
+    if ( !alarms.isEmpty() ) {
+      const KCalCore::Alarm::Ptr alarm = alarms.first();
+      if ( alarm->hasStartOffset() ) {
+        int dur = alarm->startOffset().asSeconds();
+        setAlarm( (float)dur / 60.0 );
+      }
+    }
+  }
+
+  Email org( incidence->organizer()->name(), incidence->organizer()->email() );
+  setOrganizer( org );
+
+  // Attendees:
+  KCalCore::Attendee::List attendees = incidence->attendees();
+  foreach ( KCalCore::Attendee::Ptr kcalAttendee, attendees ) {
+    Attendee attendee;
+
+    attendee.displayName = kcalAttendee->name();
+    attendee.smtpAddress = kcalAttendee->email();
+    attendee.status = attendeeStatusToString( kcalAttendee->status() );
+    attendee.requestResponse = kcalAttendee->RSVP();
+    // TODO: KCalCore::Attendee::mFlag is not accessible
+    // attendee.invitationSent = kcalAttendee->mFlag;
+    // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field?
+    attendee.role = attendeeRoleToString( kcalAttendee->role() );
+    attendee.delegate = kcalAttendee->delegate();
+    attendee.delegator = kcalAttendee->delegator();
+
+    addAttendee( attendee );
+  }
+
+  mAttachments.clear();
+
+  // Attachments
+  KCalCore::Attachment::List attachments = incidence->attachments();
+  foreach ( KCalCore::Attachment::Ptr a, attachments ) {
+    mAttachments.push_back( a );
+  }
+
+  mAlarms.clear();
+
+  // Alarms
+  KCalCore::Alarm::List alarms = incidence->alarms();
+  foreach ( KCalCore::Alarm::Ptr a, alarms ) {
+    mAlarms.push_back( a );
+  }
+
+  if ( incidence->recurs() ) {
+    setRecurrence( incidence->recurrence() );
+    mRecurrence.exclusions = incidence->recurrence()->exDates();
+  }
+
+  // Handle the scheduling ID
+  if ( incidence->schedulingID() == incidence->uid() ) {
+    // There is no scheduling ID
+    setInternalUID( QString::null );	//krazy:exclude=nullstrassign for old broken gcc
+  } else {
+    // We've internally been using a different uid, so save that as the
+    // temporary (internal) uid and restore the original uid, the one that
+    // is used in the folder and the outside world
+    setUid( incidence->schedulingID() );
+    setInternalUID( incidence->uid() );
+  }
+
+  // Unhandled tags and other custom properties (see libkcal/customproperties.h)
+  const QMap<QByteArray, QString> map = incidence->customProperties();
+  QMap<QByteArray, QString>::ConstIterator cit = map.begin();
+  for ( ; cit != map.end() ; ++cit ) {
+    Custom c;
+    c.key = cit.key();
+    c.value = cit.value();
+    mCustomList.append( c );
+  }
+}
+
+static QBitArray daysListToBitArray( const QStringList& days )
+{
+  QBitArray arr( 7 );
+  arr.fill( false );
+  foreach ( const QString& day, days ) {
+    for ( uint i = 0; i < 7 ; ++i )
+      if ( day == s_weekDayName[i] )
+        arr.setBit( i, true );
+  }
+  return arr;
+}
+
+
+void Incidence::saveTo( const KCalCore::Incidence::Ptr &incidence )
+{
+  KolabBase::saveTo( incidence );
+
+  if ( mFloatingStatus == AllDay ) {
+    // This is an all-day event. Don't timezone move this one
+    incidence->setDtStart( startDate() );
+    incidence->setAllDay( true );
+  } else {
+    incidence->setDtStart( utcToLocal( startDate() ) );
+    incidence->setAllDay( false );
+  }
+
+  incidence->setSummary( summary() );
+  incidence->setLocation( location() );
+
+  if ( mHasAlarm && mAlarms.isEmpty() ) {
+    KCalCore::Alarm::Ptr alarm = incidence->newAlarm();
+    alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
+    alarm->setEnabled( true );
+    alarm->setType( KCalCore::Alarm::Display );
+  } else if ( !mAlarms.isEmpty() ) {
+    foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+      a->setParent( incidence.data() );
+      incidence->addAlarm( a );
+    }
+  }
+
+  if ( organizer().displayName.isEmpty() )
+    incidence->setOrganizer( organizer().smtpAddress );
+  else
+    incidence->setOrganizer( organizer().displayName + '<'
+                             + organizer().smtpAddress + '>' );
+
+  incidence->clearAttendees();
+  foreach ( const Attendee& attendee, mAttendees ) {
+    KCalCore::Attendee::PartStat status = attendeeStringToStatus( attendee.status );
+    KCalCore::Attendee::Role role = attendeeStringToRole( attendee.role );
+    KCalCore::Attendee::Ptr a( new KCalCore::Attendee( attendee.displayName,
+                                            attendee.smtpAddress,
+                                            attendee.requestResponse,
+                                            status, role ) );
+    a->setDelegate( attendee.delegate );
+    a->setDelegator( attendee.delegator );
+    incidence->addAttendee( a );
+  }
+
+  incidence->clearAttachments();
+  foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+    // TODO should we copy?
+    incidence->addAttachment( a );
+  }
+
+  if ( !mRecurrence.cycle.isEmpty() ) {
+    KCalCore::Recurrence* recur = incidence->recurrence(); // yeah, this creates it
+    // done below recur->setFrequency( mRecurrence.interval );
+    if ( mRecurrence.cycle == "minutely" ) {
+      recur->setMinutely( mRecurrence.interval );
+    } else if ( mRecurrence.cycle == "hourly" ) {
+      recur->setHourly( mRecurrence.interval );
+    } else if ( mRecurrence.cycle == "daily" ) {
+      recur->setDaily( mRecurrence.interval );
+    } else if ( mRecurrence.cycle == "weekly" ) {
+      QBitArray rDays = daysListToBitArray( mRecurrence.days );
+      recur->setWeekly( mRecurrence.interval, rDays );
+    } else if ( mRecurrence.cycle == "monthly" ) {
+      recur->setMonthly( mRecurrence.interval );
+      if ( mRecurrence.type == "weekday" ) {
+        recur->addMonthlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+      } else if ( mRecurrence.type == "daynumber" ) {
+        recur->addMonthlyDate( mRecurrence.dayNumber.toInt() );
+      } else kWarning() <<"Unhandled monthly recurrence type" << mRecurrence.type;
+    } else if ( mRecurrence.cycle == "yearly" ) {
+      recur->setYearly( mRecurrence.interval );
+      if ( mRecurrence.type == "monthday" ) {
+        recur->addYearlyDate( mRecurrence.dayNumber.toInt() );
+				for ( int i = 0; i < 12; ++i )
+          if ( s_monthName[ i ] == mRecurrence.month )
+            recur->addYearlyMonth( i+1 );
+      } else if ( mRecurrence.type == "yearday" ) {
+        recur->addYearlyDay( mRecurrence.dayNumber.toInt() );
+      } else if ( mRecurrence.type == "weekday" ) {
+			  for ( int i = 0; i < 12; ++i )
+          if ( s_monthName[ i ] == mRecurrence.month )
+            recur->addYearlyMonth( i+1 );
+        recur->addYearlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+      } else kWarning() <<"Unhandled yearly recurrence type" << mRecurrence.type;
+    } else kWarning() <<"Unhandled recurrence cycle" << mRecurrence.cycle;
+
+    if ( mRecurrence.rangeType == "number" ) {
+      recur->setDuration( mRecurrence.range.toInt() );
+    } else if ( mRecurrence.rangeType == "date" ) {
+      recur->setEndDate( stringToDate( mRecurrence.range ) );
+    } // "none" is default since tje set*ly methods set infinite recurrence
+
+    incidence->recurrence()->setExDates( mRecurrence.exclusions );
+
+  }
+  /* If we've stored a uid to be used internally instead of the real one
+   * (to deal with duplicates of events in different folders) before, then
+   * restore it, so it does not change. Keep the original uid around for
+   * scheduling purposes. */
+  if ( !internalUID().isEmpty() ) {
+    incidence->setUid( internalUID() );
+    incidence->setSchedulingID( uid() );
+  }
+
+  foreach ( const Custom& custom, mCustomList ) {
+    incidence->setNonKDECustomProperty( custom.key, custom.value );
+  }
+
+}
+
+QString Incidence::productID() const
+{
+  return QString( "Akonadi %1, Kolab resource" ).arg( AKONADI_VERSION );
+}
+
+// Unhandled KCalCore::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+
diff --git a/libkolab/kolabformatV2/incidence.h b/libkolab/kolabformatV2/incidence.h
new file mode 100644
index 0000000..2b96776
--- /dev/null
+++ b/libkolab/kolabformatV2/incidence.h
@@ -0,0 +1,166 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLAB_INCIDENCE_H
+#define KOLAB_INCIDENCE_H
+
+#include <kcalcore/incidence.h>
+
+#include "kolabbase.h"
+
+class QDomElement;
+
+namespace Kolab {
+
+/**
+ * This abstract class represents an incidence which has the shared
+ * fields, of events and tasks and knows how to load/save these
+ * from/to XML, and from/to a KCalCore::Incidence.
+ */
+class Incidence : public KolabBase {
+public:
+  struct Recurrence {
+    QString cycle;
+    QString type;
+    int interval;
+    QStringList days; // list of days-of-the-week
+    QString dayNumber;
+    QString month;
+    QString rangeType;
+    QString range; // date or number or nothing
+    QList<QDate> exclusions;
+  };
+
+  struct Attendee : Email {
+    Attendee() : requestResponse( true ), invitationSent( false ) {}
+    QString status;
+    bool requestResponse;
+    bool invitationSent;
+    QString role;
+    QString delegate;
+    QString delegator;
+  };
+
+  explicit Incidence( const QString& tz, const KCalCore::Incidence::Ptr &incidence = KCalCore::Incidence::Ptr() );
+
+public:
+  virtual ~Incidence();
+
+  void saveTo( const KCalCore::Incidence::Ptr &incidence );
+
+  virtual void setSummary( const QString& summary );
+  virtual QString summary() const;
+
+  virtual void setLocation( const QString& location );
+  virtual QString location() const;
+
+  virtual void setOrganizer( const Email& organizer );
+  virtual Email organizer() const;
+
+  virtual void setStartDate( const KDateTime& startDate );
+  virtual void setStartDate( const QDate& startDate );
+  virtual void setStartDate( const QString& startDate );
+  virtual KDateTime startDate() const;
+
+  virtual void setAlarm( float alarm );
+  virtual float alarm() const;
+
+  virtual void setRecurrence( KCalCore::Recurrence* recur );
+  virtual Recurrence recurrence() const;
+
+  virtual void addAttendee( const Attendee& attendee );
+  QList<Attendee>& attendees();
+  const QList<Attendee>& attendees() const;
+
+   virtual QString type() const { return "Incidence"; }
+ /**
+   * The internal uid is used as the uid inside KOrganizer whenever
+   * two or more events with the same uid appear, which KOrganizer
+   * can't handle. To avoid keep that interal uid from changing all the
+   * time, it is persisted in the XML between a save and the next load.
+   */
+  void setInternalUID( const QString& iuid );
+  QString internalUID() const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+protected:
+  enum FloatingStatus { Unset, AllDay, HasTime };
+
+  // Read all known fields from this ical incidence
+  void setFields( const KCalCore::Incidence::Ptr & );
+
+  bool loadAttendeeAttribute( QDomElement&, Attendee& );
+  void saveAttendeeAttribute( QDomElement& element,
+                              const Attendee& attendee ) const;
+  void saveAttendees( QDomElement& element ) const;
+  void saveAttachments( QDomElement& element ) const;
+
+  void loadAlarms( const QDomElement& element );
+  void saveAlarms( QDomElement& element ) const;
+
+  void loadRecurrence( const QDomElement& element );
+  void saveRecurrence( QDomElement& element ) const;
+  void saveCustomAttributes( QDomElement& element ) const;
+  void loadCustomAttributes( QDomElement& element );
+
+  QString productID() const;
+
+  QString mSummary;
+  QString mLocation;
+  Email mOrganizer;
+  KDateTime mStartDate;
+  FloatingStatus mFloatingStatus;
+  float mAlarm;
+  bool mHasAlarm;
+  Recurrence mRecurrence;
+  QList<Attendee> mAttendees;
+  QList<KCalCore::Alarm::Ptr> mAlarms;
+  QList<KCalCore::Attachment::Ptr> mAttachments;
+  QString mInternalUID;
+
+  struct Custom {
+    QByteArray key;
+    QString value;
+  };
+  QList<Custom> mCustomList;
+
+};
+
+}
+
+#endif // KOLAB_INCIDENCE_H
diff --git a/libkolab/kolabformatV2/journal.cpp b/libkolab/kolabformatV2/journal.cpp
new file mode 100644
index 0000000..c9c2c1b
--- /dev/null
+++ b/libkolab/kolabformatV2/journal.cpp
@@ -0,0 +1,184 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "journal.h"
+#include "akonadi-version.h"
+
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Journal::Ptr Journal::fromXml( const QDomDocument& xmlDoc, const QString& tz )
+{
+  Journal journal( tz );
+  journal.loadXML( xmlDoc );
+  KCalCore::Journal::Ptr kcalJournal( new KCalCore::Journal() );
+  journal.saveTo( kcalJournal );
+  return kcalJournal;
+}
+
+QString Journal::journalToXML( const KCalCore::Journal::Ptr &kcalJournal, const QString& tz )
+{
+  Journal journal( tz, kcalJournal );
+  return journal.saveXML();
+}
+
+Journal::Journal( const QString& tz, const KCalCore::Journal::Ptr &journal )
+  : KolabBase( tz )
+{
+  if ( journal ) {
+    setFields( journal );
+  }
+}
+
+Journal::~Journal()
+{
+}
+
+void Journal::setSummary( const QString& summary )
+{
+  mSummary = summary;
+}
+
+QString Journal::summary() const
+{
+  return mSummary;
+}
+
+void Journal::setStartDate( const KDateTime& startDate )
+{
+  mStartDate = startDate;
+}
+
+KDateTime Journal::startDate() const
+{
+  return mStartDate;
+}
+
+void Journal::setEndDate( const KDateTime& endDate )
+{
+  mEndDate = endDate;
+}
+
+KDateTime Journal::endDate() const
+{
+  return mEndDate;
+}
+
+bool Journal::loadAttribute( QDomElement& element )
+{
+  QString tagName = element.tagName();
+
+  if ( tagName == "summary" )
+    setSummary( element.text() );
+  else if ( tagName == "start-date" )
+    setStartDate( stringToDateTime( element.text() ) );
+  else
+    // Not handled here
+    return KolabBase::loadAttribute( element );
+
+  // We handled this
+  return true;
+}
+
+bool Journal::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  KolabBase::saveAttributes( element );
+
+  writeString( element, "summary", summary() );
+  writeString( element, "start-date", dateTimeToString( startDate() ) );
+
+  return true;
+}
+
+
+bool Journal::loadXML( const QDomDocument& document )
+{
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "journal" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected Journal",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      if ( !loadAttribute( e ) ) {
+        // Unhandled tag - save for later storage
+        //qDebug( "Unhandled tag: %s", e.toCString().data() );
+      }
+    } else
+      qDebug( "Node is not a comment or an element???" );
+  }
+
+  return true;
+}
+
+QString Journal::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement( "journal" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  document.appendChild( element );
+  return document.toString();
+}
+
+void Journal::saveTo( const KCalCore::Journal::Ptr &journal )
+{
+  KolabBase::saveTo( journal );
+
+  journal->setSummary( summary() );
+  journal->setDtStart( utcToLocal( startDate() ) );
+}
+
+void Journal::setFields( const KCalCore::Journal::Ptr &journal )
+{
+  // Set baseclass fields
+  KolabBase::setFields( journal );
+
+  // Set our own fields
+  setSummary( journal->summary() );
+  setStartDate( localToUTC( journal->dtStart() ) );
+}
+
+QString Journal::productID() const
+{
+  return QString( "Akonadi " ) + AKONADI_VERSION + ", Kolab resource";
+}
diff --git a/libkolab/kolabformatV2/journal.h b/libkolab/kolabformatV2/journal.h
new file mode 100644
index 0000000..a049c86
--- /dev/null
+++ b/libkolab/kolabformatV2/journal.h
@@ -0,0 +1,101 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLAB_JOURNAL_H
+#define KOLAB_JOURNAL_H
+
+#include <kcalcore/journal.h>
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace Kolab {
+
+/**
+ * This class represents a journal entry, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Journal : public KolabBase {
+public:
+  /// Use this to parse an xml string to a journal entry
+  /// The caller is responsible for deleting the returned journal
+  static KCalCore::Journal::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz );
+
+  /// Use this to get an xml string describing this journal entry
+  static QString journalToXML( const KCalCore::Journal::Ptr &, const QString& tz );
+
+  explicit Journal( const QString& tz, const KCalCore::Journal::Ptr &journal = KCalCore::Journal::Ptr() );
+  virtual ~Journal();
+
+  virtual QString type() const { return "Journal"; }
+
+  void saveTo( const KCalCore::Journal::Ptr &journal );
+
+  virtual void setSummary( const QString& summary );
+  virtual QString summary() const;
+
+  virtual void setStartDate( const KDateTime& startDate );
+  virtual KDateTime startDate() const;
+
+  virtual void setEndDate( const KDateTime& endDate );
+  virtual KDateTime endDate() const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+  // Load this journal by reading the XML file
+  virtual bool loadXML( const QDomDocument& xml );
+
+  // Serialize this journal to an XML string
+  virtual QString saveXML() const;
+
+protected:
+  // Read all known fields from this ical journal
+  void setFields( const KCalCore::Journal::Ptr & );
+
+  QString productID() const;
+
+  QString mSummary;
+  KDateTime mStartDate;
+  KDateTime mEndDate;
+};
+
+}
+
+#endif // KOLAB_JOURNAL_H
diff --git a/libkolab/kolabformatV2/kolabbase.cpp b/libkolab/kolabformatV2/kolabbase.cpp
new file mode 100644
index 0000000..4315f3e
--- /dev/null
+++ b/libkolab/kolabformatV2/kolabbase.cpp
@@ -0,0 +1,500 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "kolabbase.h"
+
+#include <kabc/addressee.h>
+#include <kabc/contactgroup.h>
+#include <kcalcore/incidence.h>
+#include <kcalcore/journal.h>
+#include <ksystemtimezone.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+KolabBase::KolabBase( const QString& tz )
+  : mCreationDate( QDateTime::currentDateTime() ),
+    mLastModified( KDateTime::currentUtcDateTime() ),
+    mSensitivity( Public ),
+    mTimeZone( KSystemTimeZones::zone( tz ) ),
+    mHasPilotSyncId( false ),  mHasPilotSyncStatus( false )
+{
+}
+
+KolabBase::~KolabBase()
+{
+}
+
+void KolabBase::setFields( const KCalCore::Incidence::Ptr &incidence )
+{
+  // So far unhandled KCalCore::IncidenceBase fields:
+  // mPilotID, mSyncStatus, mFloats
+
+  setUid( incidence->uid() );
+  setBody( incidence->description() );
+  setCategories( incidence->categoriesStr() );
+  setCreationDate( localToUTC( incidence->created() ) );
+  setLastModified( incidence->lastModified() );
+  setSensitivity( static_cast<Sensitivity>( incidence->secrecy() ) );
+  // TODO: Attachments
+}
+
+void KolabBase::saveTo( const KCalCore::Incidence::Ptr &incidence ) const
+{
+  incidence->setUid( uid() );
+  incidence->setDescription( body() );
+  incidence->setCategories( categories() );
+  incidence->setCreated( utcToLocal( creationDate() ) );
+  incidence->setLastModified( lastModified() );
+  switch( sensitivity() ) {
+  case 1:
+    incidence->setSecrecy( KCalCore::Incidence::SecrecyPrivate );
+    break;
+  case 2:
+    incidence->setSecrecy( KCalCore::Incidence::SecrecyConfidential );
+    break;
+  default:
+    incidence->setSecrecy( KCalCore::Incidence::SecrecyPublic );
+    break;
+  }
+
+  // TODO: Attachments
+}
+
+void KolabBase::setFields( const KABC::Addressee* addressee )
+{
+  // An addressee does not have a creation date, so somehow we should
+  // make one, if this is a new entry
+
+  setUid( addressee->uid() );
+  setBody( addressee->note() );
+  setCategories( addressee->categories().join( "," ) );
+
+  // Set creation-time and last-modification-time
+  const QString creationString = addressee->custom( "KOLAB", "CreationDate" );
+  kDebug() <<"Creation time string:" << creationString;
+  KDateTime creationDate;
+  if ( creationString.isEmpty() ) {
+    creationDate = KDateTime::currentDateTime(KDateTime::Spec( mTimeZone ) );
+    kDebug() <<"Creation date set to current time";
+  }
+  else {
+    creationDate = stringToDateTime( creationString );
+    kDebug() <<"Creation date loaded";
+  }
+  KDateTime modified = KDateTime( addressee->revision(), mTimeZone );
+  if ( !modified.isValid() )
+    modified = KDateTime::currentUtcDateTime();
+  setLastModified( modified );
+  if ( modified < creationDate ) {
+    // It's not possible that the modification date is earlier than creation
+    creationDate = modified;
+    kDebug() <<"Creation date set to modification date";
+  }
+  setCreationDate( creationDate );
+  const QString newCreationDate = dateTimeToString( creationDate );
+  if ( creationString != newCreationDate ) {
+    // We modified the creation date, so store it for future reference
+    const_cast<KABC::Addressee*>( addressee )
+      ->insertCustom( "KOLAB", "CreationDate", newCreationDate );
+    kDebug() <<"Creation date modified. New one:" << newCreationDate;
+  }
+
+  switch( addressee->secrecy().type() ) {
+  case KABC::Secrecy::Private:
+    setSensitivity( Private );
+    break;
+  case KABC::Secrecy::Confidential:
+    setSensitivity( Confidential );
+    break;
+  default:
+    setSensitivity( Public );
+  }
+
+  // TODO: Attachments
+}
+
+void KolabBase::saveTo( KABC::Addressee* addressee ) const
+{
+  addressee->setUid( uid() );
+  addressee->setNote( body() );
+  addressee->setCategories( categories().split( ',', QString::SkipEmptyParts ) );
+  addressee->setRevision( lastModified().toZone( mTimeZone ).dateTime() );
+  addressee->insertCustom( "KOLAB", "CreationDate",
+                           dateTimeToString( creationDate() ) );
+
+  switch( sensitivity() ) {
+  case Private:
+    addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Private ) );
+    break;
+  case Confidential:
+    addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Confidential ) );
+    break;
+  default:
+    addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Public ) );
+    break;
+  }
+  // TODO: Attachments
+}
+
+void KolabBase::setFields( const KABC::ContactGroup* contactGroup )
+{
+  // A contactgroup does not have a creation date, so somehow we should
+  // make one, if this is a new entry
+
+  setUid( contactGroup->id() );
+
+  // Set creation-time and last-modification-time
+  KDateTime creationDate = KDateTime::currentDateTime( KDateTime::Spec( mTimeZone ) );
+  kDebug() <<"Creation date set to current time";
+
+  KDateTime modified = KDateTime::currentUtcDateTime();
+  setLastModified( modified );
+  if ( modified < creationDate ) {
+    // It's not possible that the modification date is earlier than creation
+    creationDate = modified;
+    kDebug() <<"Creation date set to modification date";
+  }
+  setCreationDate( creationDate );
+}
+
+void KolabBase::saveTo( KABC::ContactGroup* contactGroup ) const
+{
+  contactGroup->setId( uid() );
+}
+
+void KolabBase::setUid( const QString& uid )
+{
+  mUid = uid;
+}
+
+QString KolabBase::uid() const
+{
+  return mUid;
+}
+
+void KolabBase::setBody( const QString& body )
+{
+  mBody = body;
+}
+
+QString KolabBase::body() const
+{
+  return mBody;
+}
+
+void KolabBase::setCategories( const QString& categories )
+{
+  mCategories = categories;
+}
+
+QString KolabBase::categories() const
+{
+  return mCategories;
+}
+
+void KolabBase::setCreationDate( const KDateTime& date )
+{
+  mCreationDate = date;
+}
+
+KDateTime KolabBase::creationDate() const
+{
+  return mCreationDate;
+}
+
+void KolabBase::setLastModified( const KDateTime& date )
+{
+  mLastModified = date;
+}
+
+KDateTime KolabBase::lastModified() const
+{
+  return mLastModified;
+}
+
+void KolabBase::setSensitivity( Sensitivity sensitivity )
+{
+  mSensitivity = sensitivity;
+}
+
+KolabBase::Sensitivity KolabBase::sensitivity() const
+{
+  return mSensitivity;
+}
+
+void KolabBase::setPilotSyncId( unsigned long id )
+{
+  mHasPilotSyncId = true;
+  mPilotSyncId = id;
+}
+
+bool KolabBase::hasPilotSyncId() const
+{
+  return mHasPilotSyncId;
+}
+
+unsigned long KolabBase::pilotSyncId() const
+{
+  return mPilotSyncId;
+}
+
+void KolabBase::setPilotSyncStatus( int status )
+{
+  mHasPilotSyncStatus = true;
+  mPilotSyncStatus = status;
+}
+
+bool KolabBase::hasPilotSyncStatus() const
+{
+  return mHasPilotSyncStatus;
+}
+
+int KolabBase::pilotSyncStatus() const
+{
+  return mPilotSyncStatus;
+}
+
+bool KolabBase::loadEmailAttribute( QDomElement& element, Email& email )
+{
+  for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      const QString tagName = e.tagName();
+
+      if ( tagName == "display-name" )
+        email.displayName = e.text();
+      else if ( tagName == "smtp-address" )
+        email.smtpAddress = e.text();
+      else
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+void KolabBase::saveEmailAttribute( QDomElement& element, const Email& email,
+                                    const QString& tagName ) const
+{
+  QDomElement e = element.ownerDocument().createElement( tagName );
+  element.appendChild( e );
+  writeString( e, "display-name", email.displayName );
+  writeString( e, "smtp-address", email.smtpAddress );
+}
+
+bool KolabBase::loadAttribute( QDomElement& element )
+{
+  const QString tagName = element.tagName();
+  switch ( tagName[0].toLatin1() ) {
+  case 'u':
+    if ( tagName == "uid" ) {
+      setUid( element.text() );
+      return true;
+    }
+    break;
+  case 'b':
+    if ( tagName == "body" ) {
+      setBody( element.text() );
+      return true;
+    }
+    break;
+  case 'c':
+    if ( tagName == "categories" ) {
+      setCategories( element.text() );
+      return true;
+    }
+    if ( tagName == "creation-date" ) {
+      setCreationDate( stringToDateTime( element.text() ) );
+      return true;
+    }
+    break;
+  case 'l':
+    if ( tagName == "last-modification-date" ) {
+      setLastModified( stringToDateTime( element.text() ) );
+      return true;
+    }
+    break;
+  case 's':
+    if ( tagName == "sensitivity" ) {
+      setSensitivity( stringToSensitivity( element.text() ) );
+      return true;
+    }
+    break;
+  case 'p':
+    if ( tagName == "product-id" )
+      return true; // ignore this field
+    if ( tagName == "pilot-sync-id" ) {
+      setPilotSyncId( element.text().toULong() );
+      return true;
+    }
+    if ( tagName == "pilot-sync-status" ) {
+      setPilotSyncStatus( element.text().toInt() );
+      return true;
+    }
+    break;
+  default:
+    break;
+  }
+  return false;
+}
+
+bool KolabBase::saveAttributes( QDomElement& element ) const
+{
+  writeString( element, "product-id", productID() );
+  writeString( element, "uid", uid() );
+  writeString( element, "body", body() );
+  writeString( element, "categories", categories() );
+  writeString( element, "creation-date", dateTimeToString( creationDate().toUtc() ) );
+  writeString( element, "last-modification-date", dateTimeToString( lastModified().toUtc() ) );
+  writeString( element, "sensitivity", sensitivityToString( sensitivity() ) );
+  if ( hasPilotSyncId() )
+    writeString( element, "pilot-sync-id", QString::number( pilotSyncId() ) );
+  if ( hasPilotSyncStatus() )
+    writeString( element, "pilot-sync-status", QString::number( pilotSyncStatus() ) );
+  return true;
+}
+
+bool KolabBase::load( const QString& xml )
+{
+  const QDomDocument document = loadDocument( xml );
+  if ( document.isNull() )
+    return false;
+  // XML file loaded into tree. Now parse it
+  return loadXML( document );
+}
+
+QDomDocument KolabBase::loadDocument( const QString& xmlData )
+{
+  QString errorMsg;
+  int errorLine, errorColumn;
+  QDomDocument document;
+  bool ok = document.setContent( xmlData, &errorMsg, &errorLine, &errorColumn );
+
+  if ( !ok ) {
+    qWarning( "Error loading document: %s, line %d, column %d", qPrintable( errorMsg ), errorLine, errorColumn );
+    return QDomDocument();
+  }
+
+  return document;
+}
+
+QDomDocument KolabBase::domTree()
+{
+  QDomDocument document;
+
+  QString p = "version=\"1.0\" encoding=\"UTF-8\"";
+  document.appendChild(document.createProcessingInstruction( "xml", p ) );
+
+  return document;
+}
+
+
+QString KolabBase::dateTimeToString( const KDateTime& time )
+{
+  return time.toString( KDateTime::ISODate );
+}
+
+QString KolabBase::dateToString( const QDate& date )
+{
+  return date.toString( Qt::ISODate );
+}
+
+KDateTime KolabBase::stringToDateTime( const QString& _date )
+{
+  const QString date( _date );
+  return KDateTime::fromString( date, KDateTime::ISODate );
+}
+
+QDate KolabBase::stringToDate( const QString& date )
+{
+  return QDate::fromString( date, Qt::ISODate );
+}
+
+QString KolabBase::sensitivityToString( Sensitivity s )
+{
+  switch( s ) {
+  case Private: return "private";
+  case Confidential: return "confidential";
+  case Public: return "public";
+  }
+
+  return "What what what???";
+}
+
+KolabBase::Sensitivity KolabBase::stringToSensitivity( const QString& s )
+{
+  if ( s == "private" )
+    return Private;
+  if ( s == "confidential" )
+    return Confidential;
+  return Public;
+}
+
+QString KolabBase::colorToString( const QColor& color )
+{
+  // Color is in the format "#RRGGBB"
+  return color.name();
+}
+
+QColor KolabBase::stringToColor( const QString& s )
+{
+  return QColor( s );
+}
+
+void KolabBase::writeString( QDomElement& element, const QString& tag,
+                             const QString& tagString )
+{
+  if ( !tagString.isEmpty() ) {
+    QDomElement e = element.ownerDocument().createElement( tag );
+    QDomText t = element.ownerDocument().createTextNode( tagString );
+    e.appendChild( t );
+    element.appendChild( e );
+  }
+}
+
+KDateTime KolabBase::localToUTC( const KDateTime& time ) const
+{
+  return time.toUtc();
+}
+
+KDateTime KolabBase::utcToLocal( const KDateTime& time ) const
+{
+  KDateTime dt = time;
+  dt.setTimeSpec( KDateTime::UTC );
+  return dt;
+}
diff --git a/libkolab/kolabformatV2/kolabbase.h b/libkolab/kolabformatV2/kolabbase.h
new file mode 100644
index 0000000..5044bc3
--- /dev/null
+++ b/libkolab/kolabformatV2/kolabbase.h
@@ -0,0 +1,183 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLABBASE_H
+#define KOLABBASE_H
+
+
+#include <kcalcore/incidence.h>
+
+#include <KDateTime>
+#include <KTimeZone>
+
+#include <QColor>
+#include <qdom.h>
+
+namespace KABC {
+  class Addressee;
+  class ContactGroup;
+}
+
+namespace Kolab {
+
+class KolabBase {
+public:
+  struct Email {
+  public:
+    Email( const QString& name = QString(),
+           const QString& email = QString() )
+      : displayName( name ), smtpAddress( email )
+    {
+    }
+
+    QString displayName;
+    QString smtpAddress;
+  };
+
+  enum Sensitivity { Public = 0, Private = 1, Confidential = 2 };
+
+  explicit KolabBase( const QString& time_zone = QString() );
+  virtual ~KolabBase();
+
+  // Return a string identifying this type
+  virtual QString type() const = 0;
+
+  virtual void setUid( const QString& uid );
+  virtual QString uid() const;
+
+  virtual void setBody( const QString& body );
+  virtual QString body() const;
+
+  virtual void setCategories( const QString& categories );
+  virtual QString categories() const;
+
+  virtual void setCreationDate( const KDateTime& date );
+  virtual KDateTime creationDate() const;
+
+  virtual void setLastModified( const KDateTime& date );
+  virtual KDateTime lastModified() const;
+
+  virtual void setSensitivity( Sensitivity sensitivity );
+  virtual Sensitivity sensitivity() const;
+
+  virtual void setPilotSyncId( unsigned long id );
+  virtual bool hasPilotSyncId() const;
+  virtual unsigned long pilotSyncId() const;
+
+  virtual void setPilotSyncStatus( int status );
+  virtual bool hasPilotSyncStatus() const;
+  virtual int pilotSyncStatus() const;
+
+  // String - Date conversion methods
+  static QString dateTimeToString( const KDateTime& time );
+  static QString dateToString( const QDate& date );
+  static KDateTime stringToDateTime( const QString& time );
+  static QDate stringToDate( const QString& date );
+
+  // String - Sensitivity conversion methods
+  static QString sensitivityToString( Sensitivity );
+  static Sensitivity stringToSensitivity( const QString& );
+
+  // String - Color conversion methods
+  static QString colorToString( const QColor& );
+  static QColor stringToColor( const QString& );
+
+  // Load this object by reading the XML file
+  bool load( const QString& xml );
+  static QDomDocument loadDocument( const QString& xmlData );
+
+  // Load this QDomDocument
+  virtual bool loadXML( const QDomDocument& xml ) = 0;
+
+  // Serialize this object to an XML string
+  virtual QString saveXML() const = 0;
+
+protected:
+  /// Read all known fields from this ical incidence
+  void setFields( const KCalCore::Incidence::Ptr & );
+
+  /// Save all known fields into this ical incidence
+  void saveTo( const KCalCore::Incidence::Ptr & ) const;
+
+  /// Read all known fields from this contact
+  void setFields( const KABC::Addressee* );
+
+  /// Save all known fields into this contact
+  void saveTo( KABC::Addressee* ) const;
+
+  /// Read all known fields from this contact group
+  void setFields( const KABC::ContactGroup* );
+
+  /// Save all known fields into this contact groupd
+  void saveTo( KABC::ContactGroup* ) const;
+
+  // This just makes the initial dom tree with version and doctype
+  static QDomDocument domTree();
+
+  bool loadEmailAttribute( QDomElement& element, Email& email );
+
+  void saveEmailAttribute( QDomElement& element, const Email& email,
+                           const QString& tagName = "email" ) const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+  // Return the product ID
+  virtual QString productID() const = 0;
+
+  // Write a string tag
+  static void writeString( QDomElement&, const QString&, const QString& );
+
+  KDateTime localToUTC( const KDateTime& time ) const;
+  KDateTime utcToLocal( const KDateTime& time ) const;
+
+  QString mUid;
+  QString mBody;
+  QString mCategories;
+  KDateTime mCreationDate;
+  KDateTime mLastModified;
+  Sensitivity mSensitivity;
+  KTimeZone mTimeZone;
+
+  // KPilot synchronization stuff
+  bool mHasPilotSyncId,  mHasPilotSyncStatus;
+  unsigned long mPilotSyncId;
+  int mPilotSyncStatus;
+};
+
+}
+
+#endif // KOLABBASE_H
diff --git a/libkolab/kolabformatV2/note.cpp b/libkolab/kolabformatV2/note.cpp
new file mode 100644
index 0000000..65afebe
--- /dev/null
+++ b/libkolab/kolabformatV2/note.cpp
@@ -0,0 +1,230 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "note.h"
+#include "akonadi-version.h"
+
+#include <kcalcore/journal.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Journal::Ptr Note::xmlToJournal( const QString& xml )
+{
+  Note note;
+  note.load( xml );
+  KCalCore::Journal::Ptr journal( new KCalCore::Journal() );
+  note.saveTo( journal );
+  return journal;
+}
+
+QString Note::journalToXML( const KCalCore::Journal::Ptr &journal )
+{
+  Note note( journal );
+  return note.saveXML();
+}
+
+Note::Note( const KCalCore::Journal::Ptr &journal ) : mRichText( false )
+{
+  if ( journal )
+    setFields( journal );
+}
+
+Note::~Note()
+{
+}
+
+void Note::setSummary( const QString& summary )
+{
+  mSummary = summary;
+}
+
+QString Note::summary() const
+{
+  return mSummary;
+}
+
+void Note::setBackgroundColor( const QColor& bgColor )
+{
+  mBackgroundColor = bgColor;
+}
+
+QColor Note::backgroundColor() const
+{
+  return mBackgroundColor;
+}
+
+void Note::setForegroundColor( const QColor& fgColor )
+{
+  mForegroundColor = fgColor;
+}
+
+QColor Note::foregroundColor() const
+{
+  return mForegroundColor;
+}
+
+void Note::setRichText( bool richText )
+{
+  mRichText = richText;
+}
+
+bool Note::richText() const
+{
+  return mRichText;
+}
+
+bool Note::loadAttribute( QDomElement& element )
+{
+  QString tagName = element.tagName();
+  if ( tagName == "summary" )
+    setSummary( element.text() );
+  else if ( tagName == "foreground-color" )
+    setForegroundColor( stringToColor( element.text() ) );
+  else if ( tagName == "background-color" )
+    setBackgroundColor( stringToColor( element.text() ) );
+  else if ( tagName == "knotes-richtext" )
+    mRichText = ( element.text() == "true" );
+  else
+    return KolabBase::loadAttribute( element );
+
+  // We handled this
+  return true;
+}
+
+bool Note::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  KolabBase::saveAttributes( element );
+
+  // Save the elements
+#if 0
+  QDomComment c = element.ownerDocument().createComment( "Note specific attributes" );
+  element.appendChild( c );
+#endif
+
+  writeString( element, "summary", summary() );
+  if ( foregroundColor().isValid() )
+    writeString( element, "foreground-color", colorToString( foregroundColor() ) );
+  if ( backgroundColor().isValid() )
+    writeString( element, "background-color", colorToString( backgroundColor() ) );
+  writeString( element, "knotes-richtext", mRichText ? "true" : "false" );
+
+  return true;
+}
+
+
+bool Note::loadXML( const QDomDocument& document )
+{
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "note" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected note",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      if ( !loadAttribute( e ) )
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  return true;
+}
+
+QString Note::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement( "note" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  document.appendChild( element );
+  return document.toString();
+}
+
+void Note::setFields( const KCalCore::Journal::Ptr &journal )
+{
+  KolabBase::setFields( journal );
+
+  setSummary( journal->summary() );
+
+  QString property = journal->customProperty( "KNotes", "BgColor" );
+  if ( !property.isEmpty() ) {
+    setBackgroundColor( property );
+  } else {
+    setBackgroundColor( "yellow" );
+  }
+
+  property = journal->customProperty( "KNotes", "FgColor" );
+  if ( !property.isEmpty() ) {
+    setForegroundColor( property );
+  } else {
+    setForegroundColor( "black" );
+  }
+
+  property = journal->customProperty( "KNotes", "RichText" );
+  if ( !property.isEmpty() ) {
+    setRichText( property == "true" ? true : false );
+  } else {
+    setRichText( "false" );
+  }
+}
+
+void Note::saveTo( const KCalCore::Journal::Ptr &journal )
+{
+  KolabBase::saveTo( journal );
+
+  // TODO: background and foreground
+  journal->setSummary( summary() );
+  if ( foregroundColor().isValid() )
+    journal->setCustomProperty( "KNotes", "FgColor",
+                                colorToString( foregroundColor() ) );
+  if ( backgroundColor().isValid() )
+    journal->setCustomProperty( "KNotes", "BgColor",
+                                colorToString( backgroundColor() ) );
+  journal->setCustomProperty( "KNotes", "RichText",
+                              richText() ? "true" : "false" );
+}
+
+QString Note::productID() const
+{
+  return QString( "KNotes %1, Kolab resource" ).arg( AKONADI_VERSION );
+}
diff --git a/libkolab/kolabformatV2/note.h b/libkolab/kolabformatV2/note.h
new file mode 100644
index 0000000..b0291c4
--- /dev/null
+++ b/libkolab/kolabformatV2/note.h
@@ -0,0 +1,109 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLAB_NOTE_H
+#define KOLAB_NOTE_H
+
+#include <kcalcore/journal.h>
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace Kolab {
+
+/**
+ * This class represents a note, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Note : public KolabBase {
+public:
+  /// Use this to parse an xml string to a journal entry
+  /// The caller is responsible for deleting the returned journal
+    static KCalCore::Journal::Ptr xmlToJournal( const QString& xml );
+
+  /// Use this to get an xml string describing this journal entry
+    static QString journalToXML( const KCalCore::Journal::Ptr & );
+
+  /// Create a note object and
+  explicit Note( const KCalCore::Journal::Ptr &journal = KCalCore::Journal::Ptr() );
+  virtual ~Note();
+
+  void saveTo( const KCalCore::Journal::Ptr &journal );
+
+  virtual QString type() const { return "Note"; }
+
+  virtual void setSummary( const QString& summary );
+  virtual QString summary() const;
+
+  virtual void setBackgroundColor( const QColor& bgColor );
+  virtual QColor backgroundColor() const;
+
+  virtual void setForegroundColor( const QColor& fgColor );
+  virtual QColor foregroundColor() const;
+
+  virtual void setRichText( bool richText );
+  virtual bool richText() const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+  // Load this note by reading the XML file
+  virtual bool loadXML( const QDomDocument& xml );
+
+  // Serialize this note to an XML string
+  virtual QString saveXML() const;
+
+protected:
+  // Read all known fields from this ical incidence
+  void setFields( const KCalCore::Journal::Ptr & );
+
+  // Save all known fields into this ical incidence
+  void saveTo( const KCalCore::Incidence::Ptr & ) const;
+
+  QString productID() const;
+
+  QString mSummary;
+  QColor mBackgroundColor;
+  QColor mForegroundColor;
+  bool mRichText;
+};
+
+}
+
+#endif // KOLAB_NOTE_H
diff --git a/libkolab/kolabformatV2/task.cpp b/libkolab/kolabformatV2/task.cpp
new file mode 100644
index 0000000..974fdb2
--- /dev/null
+++ b/libkolab/kolabformatV2/task.cpp
@@ -0,0 +1,455 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "task.h"
+
+#include <kcalcore/todo.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+// Kolab Storage Specification:
+//    "The priority can be a number between 1 and 5, with 1 being the highest priority."
+// iCalendar (RFC 2445):
+//    "The priority is specified as an integer in the range
+//     zero to nine. A value of zero specifies an
+//     undefined priority. A value of one is the
+//     highest priority. A value of nine is the lowest
+//     priority."
+
+static int kcalPriorityToKolab( const int kcalPriority )
+{
+  if ( kcalPriority >= 0 && kcalPriority <= 9 ) {
+    // We'll map undefined (0) to 3 (default)
+    //                                   0  1  2  3  4  5  6  7  8  9
+    static const int priorityMap[10] = { 3, 1, 1, 2, 2, 3, 3, 4, 4, 5 };
+    return priorityMap[kcalPriority];
+  }
+  else {
+    kWarning() << "Got invalid priority" << kcalPriority;
+    return 3;
+  }
+}
+
+static int kolabPrioritytoKCal( const int kolabPriority )
+{
+  if ( kolabPriority >= 1 && kolabPriority <= 5 ) {
+    //                                  1  2  3  4  5
+    static const int priorityMap[5] = { 1, 3, 5, 7, 9 };
+    return priorityMap[kolabPriority - 1];
+  }
+  else {
+    kWarning() << "Got invalid priority" << kolabPriority;
+    return 5;
+  }
+}
+
+KCalCore::Todo::Ptr Task::fromXml( const QDomDocument& xmlDoc, const QString& tz )
+{
+  Task task( tz );
+  task.loadXML( xmlDoc );
+  KCalCore::Todo::Ptr todo(  new KCalCore::Todo() );
+  task.saveTo( todo );
+  return todo;
+}
+
+QString Task::taskToXML( const KCalCore::Todo::Ptr &todo, const QString& tz )
+{
+  Task task( tz, todo );
+  return task.saveXML();
+}
+
+Task::Task( const QString& tz, const KCalCore::Todo::Ptr &task )
+  : Incidence( tz, task ),
+    mPriority( 5 ), mPercentCompleted( 0 ),
+    mStatus( KCalCore::Incidence::StatusNone ),
+    mHasStartDate( false ), mHasDueDate( false ),
+    mHasCompletedDate( false )
+{
+  if ( task ) {
+    setFields( task );
+  }
+}
+
+Task::~Task()
+{
+}
+
+void Task::setPriority( int priority )
+{
+  mPriority = priority;
+}
+
+int Task::priority() const
+{
+  return mPriority;
+}
+
+void Task::setPercentCompleted( int percent )
+{
+  mPercentCompleted = percent;
+}
+
+int Task::percentCompleted() const
+{
+  return mPercentCompleted;
+}
+
+void Task::setStatus( KCalCore::Incidence::Status status )
+{
+  mStatus = status;
+}
+
+KCalCore::Incidence::Status Task::status() const
+{
+  return mStatus;
+}
+
+void Task::setParent( const QString& parentUid )
+{
+  mParent = parentUid;
+}
+
+QString Task::parent() const
+{
+  return mParent;
+}
+
+void Task::setDueDate( const KDateTime &date )
+{
+  mDueDate = date;
+  mHasDueDate = true;
+}
+
+void Task::setDueDate( const QDate &date )
+{
+  mDueDate = KDateTime( date );
+  mHasDueDate = true;
+  mFloatingStatus = AllDay;
+}
+
+void Task::setDueDate( const QString &date )
+{
+  if ( date.length() > 10 ) {
+    // This is a date + time
+     setDueDate( stringToDateTime( date ) );
+  } else {
+     // This is only a date
+    setDueDate( stringToDate( date ) );
+  }
+}
+
+KDateTime Task::dueDate() const
+{
+  return mDueDate;
+}
+
+void Task::setHasStartDate( bool v )
+{
+  mHasStartDate = v;
+}
+
+bool Task::hasStartDate() const
+{
+  return mHasStartDate;
+}
+
+bool Task::hasDueDate() const
+{
+  return mHasDueDate;
+}
+
+void Task::setCompletedDate( const KDateTime& date )
+{
+  mCompletedDate = date;
+  mHasCompletedDate = true;
+}
+
+KDateTime Task::completedDate() const
+{
+  return mCompletedDate;
+}
+
+bool Task::hasCompletedDate() const
+{
+  return mHasCompletedDate;
+}
+
+bool Task::loadAttribute( QDomElement& element )
+{
+  QString tagName = element.tagName();
+
+  if ( tagName == "priority" ) {
+    bool ok;
+    mKolabPriorityFromDom = element.text().toInt( &ok );
+    if ( !ok || mKolabPriorityFromDom < 1 || mKolabPriorityFromDom > 5 ) {
+      kWarning() << "Invalid \"priority\" value:" << element.text();
+      mKolabPriorityFromDom = -1;
+    }
+  } else if ( tagName == "x-kcal-priority" ) {
+    bool ok;
+    mKCalPriorityFromDom = element.text().toInt( &ok );
+    if ( !ok || mKCalPriorityFromDom < 0 || mKCalPriorityFromDom > 9 ) {
+      kWarning() << "Invalid \"x-kcal-priority\" value:" << element.text();
+      mKCalPriorityFromDom = -1;
+    }
+  } else if ( tagName == "completed" ) {
+    bool ok;
+    int percent = element.text().toInt( &ok );
+    if ( !ok || percent < 0 || percent > 100 )
+      percent = 0;
+    setPercentCompleted( percent );
+  } else if ( tagName == "status" ) {
+    if ( element.text() == "in-progress" )
+      setStatus( KCalCore::Incidence::StatusInProcess );
+    else if ( element.text() == "completed" )
+      setStatus( KCalCore::Incidence::StatusCompleted );
+    else if ( element.text() == "waiting-on-someone-else" )
+      setStatus( KCalCore::Incidence::StatusNeedsAction );
+    else if ( element.text() == "deferred" )
+      // Guessing a status here
+      setStatus( KCalCore::Incidence::StatusCanceled );
+    else
+      // Default
+      setStatus( KCalCore::Incidence::StatusNone );
+  } else if ( tagName == "due-date" ) {
+    setDueDate( element.text() );
+  } else if ( tagName == "parent" ) {
+    setParent( element.text() );
+  } else if ( tagName == "x-completed-date" ) {
+    setCompletedDate( stringToDateTime( element.text() ) );
+  } else if ( tagName == "start-date" ) {
+    setHasStartDate( true );
+    setStartDate( element.text() );
+  } else
+    return Incidence::loadAttribute( element );
+
+  // We handled this
+  return true;
+}
+
+bool Task::saveAttributes( QDomElement& element ) const
+{
+  // Save the base class elements
+  Incidence::saveAttributes( element );
+
+  // We need to save x-kcal-priority as well, since the Kolab priority can only save values from
+  // 1 to 5, but we have values from 0 to 9, and do not want to loose them
+  writeString( element, "priority", QString::number( kcalPriorityToKolab( priority() ) ) );
+  writeString( element, "x-kcal-priority", QString::number( priority() ) );
+
+  writeString( element, "completed", QString::number( percentCompleted() ) );
+
+  switch( status() ) {
+  case KCalCore::Incidence::StatusInProcess:
+    writeString( element, "status", "in-progress" );
+    break;
+  case KCalCore::Incidence::StatusCompleted:
+    writeString( element, "status", "completed" );
+    break;
+  case KCalCore::Incidence::StatusNeedsAction:
+    writeString( element, "status", "waiting-on-someone-else" );
+    break;
+  case KCalCore::Incidence::StatusCanceled:
+    writeString( element, "status", "deferred" );
+    break;
+  case KCalCore::Incidence::StatusNone:
+    writeString( element, "status", "not-started" );
+    break;
+  case KCalCore::Incidence::StatusTentative:
+  case KCalCore::Incidence::StatusConfirmed:
+  case KCalCore::Incidence::StatusDraft:
+  case KCalCore::Incidence::StatusFinal:
+  case KCalCore::Incidence::StatusX:
+    // All of these are saved as StatusNone.
+    writeString( element, "status", "not-started" );
+    break;
+  }
+
+  if ( hasDueDate() ) {
+    if ( mFloatingStatus == HasTime ) {
+      writeString( element, "due-date", dateTimeToString( dueDate() ) );
+    } else {
+      writeString( element, "due-date", dateToString( dueDate().date() ) );
+    }
+  }
+
+  if ( !parent().isNull() ) {
+    writeString( element, "parent", parent() );
+  }
+
+  if ( hasCompletedDate() && percentCompleted() == 100 ) {
+    writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+  }
+
+  return true;
+}
+
+
+bool Task::loadXML( const QDomDocument& document )
+{
+  mKolabPriorityFromDom = -1;
+  mKCalPriorityFromDom = -1;
+
+  QDomElement top = document.documentElement();
+
+  if ( top.tagName() != "task" ) {
+    qWarning( "XML error: Top tag was %s instead of the expected task",
+              top.tagName().toAscii().data() );
+    return false;
+  }
+  setHasStartDate( false ); // todo's don't necessarily have one
+
+  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+    if ( n.isComment() )
+      continue;
+    if ( n.isElement() ) {
+      QDomElement e = n.toElement();
+      if ( !loadAttribute( e ) )
+        // TODO: Unhandled tag - save for later storage
+        kDebug() <<"Warning: Unhandled tag" << e.tagName();
+    } else
+      kDebug() <<"Node is not a comment or an element???";
+  }
+
+  decideAndSetPriority();
+  return true;
+}
+
+QString Task::saveXML() const
+{
+  QDomDocument document = domTree();
+  QDomElement element = document.createElement( "task" );
+  element.setAttribute( "version", "1.0" );
+  saveAttributes( element );
+  if ( !hasStartDate() && startDate().isValid() ) {
+    // events and journals always have a start date, but tasks don't.
+    // Remove the entry done by the inherited save above, because we
+    // don't have one.
+    QDomNodeList l = element.elementsByTagName( "start-date" );
+    Q_ASSERT( l.count() == 1 );
+    element.removeChild( l.item( 0 ) );
+  }
+  document.appendChild( element );
+  return document.toString();
+}
+
+void Task::setFields( const KCalCore::Todo::Ptr &task )
+{
+  Incidence::setFields( task );
+
+  setPriority( task->priority() );
+  setPercentCompleted( task->percentComplete() );
+  setStatus( task->status() );
+  setHasStartDate( task->hasStartDate() );
+
+  if ( task->hasDueDate() ) {
+    if ( task->allDay() ) {
+      // This is a floating task. Don't timezone move this one
+      mFloatingStatus = AllDay;
+      setDueDate( KDateTime( task->dtDue().date() ) );
+    } else {
+      mFloatingStatus = HasTime;
+      setDueDate( localToUTC( task->dtDue() ) );
+    }
+  } else {
+    mHasDueDate = false;
+  }
+
+  if ( !task->relatedTo().isEmpty() ) {
+    setParent( task->relatedTo() );
+  } else{
+    setParent( QString() );
+  }
+
+  if ( task->hasCompletedDate() && task->percentComplete() == 100 ) {
+    setCompletedDate( localToUTC( task->completed() ) );
+  } else {
+    mHasCompletedDate = false;
+  }
+}
+
+void Task::decideAndSetPriority()
+{
+  // If we have both Kolab and KCal values in the XML, we prefer the KCal value, but only if the
+  // values are still in sync
+  if  ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom != -1 ) {
+    const bool inSync = ( kcalPriorityToKolab( mKCalPriorityFromDom ) == mKolabPriorityFromDom );
+    if ( inSync ) {
+      setPriority( mKCalPriorityFromDom );
+    }
+    else {
+      // Out of sync, some other client changed the Kolab priority, so we have to ignore our
+      // KCal priority
+      setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+    }
+  }
+
+  // Only KCal priority set, use that.
+  else if ( mKolabPriorityFromDom == -1 && mKCalPriorityFromDom != -1 ) {
+    kWarning() << "No Kolab priority found, only the KCal priority!";
+    setPriority( mKCalPriorityFromDom );
+  }
+
+  // Only Kolab priority set, use that
+  else if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom == -1 ) {
+    setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+  }
+
+  // No priority set, use the default
+  else {
+    // According the RFC 2445, we should use 0 here, for undefined priority, but AFAIK KOrganizer
+    // doesn't support that, so we'll use 5.
+    setPriority( 5 );
+  }
+}
+
+void Task::saveTo( const KCalCore::Todo::Ptr &task )
+{
+  Incidence::saveTo( task );
+
+  task->setPriority( priority() );
+  task->setPercentComplete( percentCompleted() );
+  task->setStatus( status() );
+  task->setHasStartDate( hasStartDate() );
+  task->setHasDueDate( hasDueDate() );
+  if ( hasDueDate() )
+    task->setDtDue( utcToLocal( dueDate() ) );
+
+  if ( !parent().isEmpty() ) {
+    task->setRelatedTo( parent() );
+  }
+
+  if ( hasCompletedDate() && task->percentComplete() == 100 )
+    task->setCompleted( utcToLocal( mCompletedDate ) );
+}
diff --git a/libkolab/kolabformatV2/task.h b/libkolab/kolabformatV2/task.h
new file mode 100644
index 0000000..bafc160
--- /dev/null
+++ b/libkolab/kolabformatV2/task.h
@@ -0,0 +1,143 @@
+/*
+    This file is part of the kolab resource - the implementation of the
+    Kolab storage format. See www.kolab.org for documentation on this.
+
+    Copyright (c) 2004 Bo Thorsen <bo at sonofthor.dk>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#ifndef KOLAB_TASK_H
+#define KOLAB_TASK_H
+
+#include <incidence.h>
+
+#include <kcalcore/todo.h>
+#include <kcalcore/incidence.h>
+
+class QDomElement;
+
+namespace KCal {
+  class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a task, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::Todo.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Task : public Incidence {
+public:
+  /// Use this to parse an xml string to a task entry
+  /// The caller is responsible for deleting the returned task
+  static KCalCore::Todo::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz/*, KCalCore::ResourceKolab *res = 0,
+                                const QString& subResource = QString(), quint32 sernum = 0 */);
+
+  /// Use this to get an xml string describing this task entry
+  static QString taskToXML( const KCalCore::Todo::Ptr &, const QString& tz );
+
+  explicit Task( /*KCalCore::ResourceKolab *res, const QString& subResource, quint32 sernum,*/
+    const QString& tz, const KCalCore::Todo::Ptr &todo = KCalCore::Todo::Ptr() );
+  virtual ~Task();
+
+  virtual QString type() const { return "Task"; }
+
+  void saveTo( const KCalCore::Todo::Ptr &todo );
+
+  virtual void setPriority( int priority );
+  virtual int priority() const;
+
+  virtual void setPercentCompleted( int percent );
+  virtual int percentCompleted() const;
+
+  virtual void setStatus( KCalCore::Incidence::Status status );
+  virtual KCalCore::Incidence::Status status() const;
+
+  virtual void setParent( const QString& parentUid );
+  virtual QString parent() const;
+
+  virtual void setHasStartDate( bool );
+  virtual bool hasStartDate() const;
+
+  virtual void setDueDate( const KDateTime& date );
+  virtual void setDueDate( const QString &date );
+  virtual void setDueDate( const QDate &date );
+  virtual KDateTime dueDate() const;
+  virtual bool hasDueDate() const;
+
+  virtual void setCompletedDate( const KDateTime& date );
+  virtual KDateTime completedDate() const;
+  virtual bool hasCompletedDate() const;
+
+  // Load the attributes of this class
+  virtual bool loadAttribute( QDomElement& );
+
+  // Save the attributes of this class
+  virtual bool saveAttributes( QDomElement& ) const;
+
+  // Load this task by reading the XML file
+  virtual bool loadXML( const QDomDocument& xml );
+
+  // Serialize this task to an XML string
+  virtual QString saveXML() const;
+
+protected:
+  // Read all known fields from this ical todo
+  void setFields( const KCalCore::Todo::Ptr & );
+
+  // This sets the priority of this task by looking at mKolabPriorityFromDom and
+  // mKCalPriorityFromDom.
+  void decideAndSetPriority();
+
+  // This is the KCal priority, not the Kolab priority.
+  // See kcalPriorityToKolab() and kolabPrioritytoKCal().
+  int mPriority;
+
+  // Those priority values are the raw values read by loadAttribute().
+  // They will be converted later in decideAndSetPriority().
+  int mKolabPriorityFromDom;
+  int mKCalPriorityFromDom;
+
+  int mPercentCompleted;
+  KCalCore::Incidence::Status mStatus;
+  QString mParent;
+
+  bool mHasStartDate;
+
+  bool mHasDueDate;
+  KDateTime mDueDate;
+
+  bool mHasCompletedDate;
+  KDateTime mCompletedDate;
+};
+
+}
+
+#endif // KOLAB_TASK_H


commit f2b9cd3925bc7ca9aa5680fbfe1e28fc407b6143
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Mon Mar 19 23:55:18 2012 +0100

    Implemented geo uri.

diff --git a/c++/lib/kolabcontact.cpp b/c++/lib/kolabcontact.cpp
index eaa2ab8..a959a2c 100644
--- a/c++/lib/kolabcontact.cpp
+++ b/c++/lib/kolabcontact.cpp
@@ -152,7 +152,7 @@ struct Contact::Private
     int imAddressPreferredIndex;
     std::vector<std::string> emailAddresses;
     int emailAddressPreferredIndex;
-    std::vector<std::string> gpsPos;
+    std::vector<Geo> gpsPos;
     Crypto crypto;
     std::vector<CustomProperty> customProperties;
 };
@@ -431,12 +431,12 @@ int Contact::emailAddressPreferredIndex() const
     return d->emailAddressPreferredIndex;
 }
 
-void Contact::setGPSpos(const std::vector< std::string >& pos)
+void Contact::setGPSpos(const std::vector< Geo >& pos)
 {
     d->gpsPos = pos;
 }
 
-std::vector< std::string > Contact::gpsPos() const
+std::vector< Geo > Contact::gpsPos() const
 {
     return d->gpsPos;
 }
diff --git a/c++/lib/kolabcontact.h b/c++/lib/kolabcontact.h
index bf365fb..e4164dd 100644
--- a/c++/lib/kolabcontact.h
+++ b/c++/lib/kolabcontact.h
@@ -236,6 +236,16 @@ private:
     CryptoPref mEncryptPref;
 };
 
+struct Geo {
+    Geo(): latitude(0.0), longitude(0.0) {};
+    Geo(double lat, double lon)
+    : latitude(lat), longitude(lon) {};
+    
+    bool operator==(const Geo &other) const{ return (longitude == other.longitude && latitude == other.latitude);};
+    double latitude;
+    double longitude;
+};
+
 
 class DistList {
 public:
@@ -351,8 +361,8 @@ public:
     std::vector<std::string> emailAddresses() const;
     int emailAddressPreferredIndex() const;
     
-    void setGPSpos(const std::vector<std::string> &);
-    std::vector<std::string> gpsPos() const;
+    void setGPSpos(const std::vector<Geo> &);
+    std::vector<Geo> gpsPos() const;
     
     void setCrypto(const Crypto &);
     Crypto crypto() const;
diff --git a/c++/lib/xcardconversions.h b/c++/lib/xcardconversions.h
index 00291e7..226e07d 100644
--- a/c++/lib/xcardconversions.h
+++ b/c++/lib/xcardconversions.h
@@ -24,6 +24,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
+#include <iomanip>
 #include "global_definitions.h"
 #include "utils.h"
 #include "kolabcontact.h"
@@ -404,6 +405,34 @@ Kolab::Address toAddress(const vcard_4_0::vcard::adr_type &adr, bool *isPreferre
     return address;
 }
 
+std::string toGeoUri(double lat, double lon)
+{
+    //lexical_cast doesn't work, so we're using stringstream
+    std::stringstream s;
+    s << "geo:";
+    s.precision(15); //can't use std::numeric_limits<double>::max_digits10 because that's c++ 0x, it should be 17, but that seems to cause rounding problems... no idea why.
+    s << lat << ",";
+    s.precision(15); //Needed to get the right precision somehow...
+    s << lon;
+    return s.str();
+}
+
+bool fromGeoUri(const std::string &uri, double &lat, double &lon)
+{
+    if (uri.substr(0,4) != std::string("geo:")) {
+        WARNING("not a geo uri");
+        return false;
+    }
+    std::size_t pos1 = uri.find(",");
+    if (pos1 == std::string::npos) {
+        WARNING("not a valid geo uri");
+        return false;
+    }
+    lat = boost::lexical_cast<double>(uri.substr(4, pos1-4));
+    lon = boost::lexical_cast<double>(uri.substr(pos1+1, uri.size()-pos1-1));
+    return true;
+}
+
 template <typename T> 
 void writeCard(vcard_4_0::vcard &vcard, const T &);
 
@@ -612,7 +641,11 @@ void writeCard<Kolab::Contact>(vcard_4_0::vcard &vcard, const Kolab::Contact &co
     }
     
     if (!contact.gpsPos().empty()) {
-        vcard.geo(fromList<vcard::geo_type>(contact.gpsPos()));
+        vcard_4_0::vcard::geo_sequence list;
+        BOOST_FOREACH(const Kolab::Geo &g ,contact.gpsPos()) {
+            list.push_back(vcard_4_0::vcard::geo_type(toGeoUri(g.latitude, g.longitude)));
+        }
+        vcard.geo(list);
     }
     
     if (contact.crypto().isValid()) {
@@ -924,9 +957,13 @@ boost::shared_ptr<Kolab::Contact> readCard <Kolab::Contact> (const vcard_4_0::Vc
         contact->setEmailAddresses(list, preferredIndex);
     }
     if (!vcard.geo().empty()) {
-        std::vector<std::string> list;
+        std::vector<Geo> list;
         BOOST_FOREACH(const vcard_4_0::geoPropType &s, vcard.geo()) {
-            list.push_back(s.uri());
+            double lon = 0.0;
+            double lat = 0.0;
+            if (fromGeoUri(s.uri(), lat, lon)) {
+                list.push_back(Kolab::Geo(lat, lon));
+            }
         }
         contact->setGPSpos(list);
     }
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 1c26297..5bd327a 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -422,7 +422,9 @@ void BindingsTest::contactCompletness()
     telephones.push_back(phone);
     telephones.push_back(phone);
     c.setTelephones(telephones, 1);
-    c.setGPSpos(stringlist);
+    std::vector<Kolab::Geo> geo;
+    geo << Kolab::Geo(1.3, -40.3);
+    c.setGPSpos(geo);
     Kolab::Crypto crypto;
     crypto.setAllowed(Kolab::Crypto::PGPinline | Kolab::Crypto::SMIMEopaque);
     crypto.setPGPKey("pgp");
@@ -438,7 +440,7 @@ void BindingsTest::contactCompletness()
     
     const std::string result = Kolab::writeContact(c);
     QVERIFY(Kolab::error() == Kolab::NoError);
-//     std::cout << result << endl;
+    std::cout << result << endl;
     Kolab::Contact e = Kolab::readContact(result, false);
     QVERIFY(Kolab::error() == Kolab::NoError);
     QCOMPARE(e.uid(), c.uid());
diff --git a/c++/tests/conversiontest.cpp b/c++/tests/conversiontest.cpp
index 700c251..0ab7047 100644
--- a/c++/tests/conversiontest.cpp
+++ b/c++/tests/conversiontest.cpp
@@ -207,6 +207,22 @@ void ConversionTest::urnTest()
     QCOMPARE(Kolab::Utils::getError(), Kolab::NoError);
 }
 
+void ConversionTest::geoUriTest()
+{
+    QCOMPARE(Kolab::XCARD::toGeoUri(-34.056, 179.3453), std::string("geo:-34.056,179.3453"));
+    QCOMPARE(Kolab::XCARD::toGeoUri(-34.1, 179.5), std::string("geo:-34.1,179.5"));
+    QCOMPARE(Kolab::XCARD::toGeoUri(-34.0, 179.0), std::string("geo:-34,179"));
+    QCOMPARE(Kolab::XCARD::toGeoUri(-34, 179), std::string("geo:-34,179"));
+    QCOMPARE(Kolab::XCARD::toGeoUri(-34.012342356, 179.3451234553), std::string("geo:-34.012342356,179.3451234553"));
+    
+    double lat, lon;
+    QVERIFY(Kolab::XCARD::fromGeoUri(std::string("geo:-34.056,179.3453"), lat, lon));
+    QCOMPARE(lat, -34.056);
+    QCOMPARE(lon, 179.3453);
+    QCOMPARE(Kolab::Utils::getError(), Kolab::NoError);
+}
+
+
 void ConversionTest::contactReferenceTest()
 {
     Kolab::ContactReference email(Kolab::ContactReference::EmailReference, "mail", "name");
diff --git a/c++/tests/conversiontest.h b/c++/tests/conversiontest.h
index 9c2bc86..516470c 100644
--- a/c++/tests/conversiontest.h
+++ b/c++/tests/conversiontest.h
@@ -37,6 +37,8 @@ class ConversionTest : public QObject
     
     void contactReferenceTest();
     
+    void geoUriTest();
+    
     void threadLocalTest();
 };
 





More information about the commits mailing list