Branch 'c++/master' - 10 commits - c++/lib c++/tests schemas/ical

Christian Mollekopf mollekopf at kolabsys.com
Tue Feb 7 02:08:17 CET 2012


 c++/lib/CMakeLists.txt              |    2 
 c++/lib/DEVELOPMENT                 |   11 
 c++/lib/base64.cpp                  |  123 +++++++
 c++/lib/base64.h                    |    4 
 c++/lib/genericxcalconverions.h     |  115 ++++++
 c++/lib/global_definitions.h        |    3 
 c++/lib/incidence_p.h               |    1 
 c++/lib/kcalconversions.h           |   10 
 c++/lib/kolabcontainers.cpp         |  355 +++++++++++++++++++-
 c++/lib/kolabcontainers.h           |  206 +++++++++--
 c++/lib/kolabformat.cpp             |    2 
 c++/lib/kolabtodo.cpp               |   25 +
 c++/lib/kolabtodo.h                 |    8 
 c++/lib/utils.h                     |    2 
 c++/lib/xcalconversions.h           |  628 +++++++++++++++++++++++++++++++++---
 c++/lib/xcardconversions.h          |    2 
 c++/tests/CMakeLists.txt            |    4 
 c++/tests/bindingstest.cpp          |  165 +++++++--
 c++/tests/bindingstest.h            |   13 
 c++/tests/conversiontest.cpp        |   94 +++++
 c++/tests/conversiontest.h          |   25 +
 c++/tests/serializers.h             |  149 ++++++++
 c++/tests/testfiles/icalEvent.xml   |    3 
 schemas/ical/iCalendar-props.xsd    |    8 
 schemas/ical/iCalendar-valtypes.xsd |    5 
 schemas/ical/kolabformat-xcal.xsd   |   60 +++
 26 files changed, 1855 insertions(+), 168 deletions(-)

New commits:
commit 89b8aaf37945cdaed93ab34274c08672cc475a1b
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Tue Feb 7 02:07:56 2012 +0100

    Implemented alarms.

diff --git a/c++/lib/genericxcalconverions.h b/c++/lib/genericxcalconverions.h
index 7bf3cff..6c879d5 100644
--- a/c++/lib/genericxcalconverions.h
+++ b/c++/lib/genericxcalconverions.h
@@ -224,6 +224,7 @@ typename T::DatePtr toDate(const icalendar_2_0::DateDatetimePropertyType &dtProp
     return date;
 }
 
+
 // template <typename T, typename I> 
 // typename T::DatePtr toDate(const I &dtProperty)
 // {
@@ -290,7 +291,6 @@ std::auto_ptr<I> fromDate(const typename T::DateType &dt)
     return ptr;
 }
 
-
 template <typename T>
 void setDateTimeProperty(icalendar_2_0::DateDatetimePropertyType &date, const typename T::DateType &dt)
 {
@@ -436,8 +436,6 @@ template <typename T> struct IncidenceTrait;
 
 
 
-
-
 template <typename T>
 typename T::IncidencePtr deserializeIncidence(const std::string& s, bool isUrl)
 {
@@ -500,6 +498,10 @@ typename T::IncidencePtr deserializeIncidence(const std::string& s, bool isUrl)
 }
 
 
+const char* const DISPLAYALARM = "DISPLAY";
+const char* const EMAILALARM = "EMAIL";
+const char* const AUDIOALARM = "AUDIO";
+
 
 template <typename T>
 std::string serializeIncidence(const typename T::IncidenceType &incidence, const std::string productid = std::string()) {
@@ -511,6 +513,7 @@ std::string serializeIncidence(const typename T::IncidenceType &incidence, const
     try {
 
         typename KolabType::components_type eventComponents;
+        T::setComponents(eventComponents, incidence);
 
         typename KolabType::properties_type::uid_type uid(IC::uid(incidence));
         typename KolabType::properties_type::dtstamp_type dtstamp;
@@ -519,8 +522,12 @@ std::string serializeIncidence(const typename T::IncidenceType &incidence, const
         created.date_time(IC::created(incidence));
         typename KolabType::properties_type eventProps(uid, created, dtstamp);
         
-        KolabType inc(eventProps, eventComponents);
+        KolabType inc(eventProps);
+        if (!eventComponents.valarm().empty()) {
+            inc.components(eventComponents);
+        }
         T::writeIncidence(inc, incidence);
+
         
         VcalendarType::components_type components;
         T::addIncidence(components, inc);
diff --git a/c++/lib/kcalconversions.h b/c++/lib/kcalconversions.h
index 00aa254..d805194 100644
--- a/c++/lib/kcalconversions.h
+++ b/c++/lib/kcalconversions.h
@@ -545,18 +545,18 @@ void getIncidenceProperties(T &prop, const KCalCore::Incidence &inc)
     
     switch (inc.secrecy()) {
         case Incidence::SecrecyConfidential:
-            prop.class_(properties::class_type("CONFIDENTIAL"));
+            prop.class_(typename T::class_type("CONFIDENTIAL"));
             break;
         case Incidence::SecrecyPrivate:
-            prop.class_(properties::class_type("PRIVATE"));
+            prop.class_(typename T::class_type("PRIVATE"));
             break;
         default:
-            prop.class_(properties::class_type("PUBLIC"));
+            prop.class_(typename T::class_type("PUBLIC"));
             break;
     }
 
     if (inc.dtStart().isValid()) {
-        properties::dtstart_type dtstart;
+        typename T::dtstart_type dtstart;
         setDateTimeProperty<KCalCoreTypes>(dtstart, inc.dtStart());
         prop.dtstart(dtstart);
     }
@@ -607,7 +607,7 @@ template < > struct IncidenceTrait <KCalCore::Event>
         getIncidenceProperties<icalendar_2_0::KolabEvent::properties_type>(prop, event);
 
         if (event.hasEndDate() && event.dtEnd().isValid()) {
-            icalendar_2_0::properties::dtend_type dtend;
+            icalendar_2_0::KolabEvent::properties_type::dtend_type dtend;
             setDateTimeProperty<KCalCoreTypes>(dtend, event.dtEnd());
             prop.dtend(dtend);
         } else if (event.hasDuration()) {
diff --git a/c++/lib/kolabcontainers.cpp b/c++/lib/kolabcontainers.cpp
index 2a5b6bd..f9ca69a 100644
--- a/c++/lib/kolabcontainers.cpp
+++ b/c++/lib/kolabcontainers.cpp
@@ -178,15 +178,12 @@ bool DateTime::isUTC() const
 
 std::string DateTime::timezone() const
 {
-        return d->timezone;
+    return d->timezone;
 }
 
 bool DateTime::isValid() const
 {
-    if (d->year >= 0 && d->month >= 0 && d->day >= 0) {
-        return true;
-    }
-    return false;
+    return (d->year >= 0 && d->month >= 0 && d->day >= 0);
 }
 
 struct RecurrenceRule::Private
@@ -518,13 +515,10 @@ Attachment::~Attachment()
 
 bool Attachment::operator==(const Kolab::Attachment &other) const
 {
-    if ( d->uri == other.uri() &&
+    return ( d->uri == other.uri() &&
         d->data == other.data() &&
         d->label == other.label() &&
-        d->mimetype == other.mimetype() ) {
-        return true;
-    }
-    return false;
+        d->mimetype == other.mimetype() );
 }
 
 void Attachment::setUri(const std::string &uri, const std::string& mimetype)
@@ -564,6 +558,165 @@ std::string Attachment::data() const
     return d->data;
 }
 
+bool Attachment::isValid() const
+{
+    return !d->mimetype.empty(); //TODO use isValid variable
+}
+
+
+
+struct Alarm::Private
+{
+    Private(): relativeTo(Start),
+    numrepeat(0),
+    type(Alarm::InvalidAlarm) {};
+    std::string text;
+    Attachment audioFile;
+    std::string summary;
+    std::vector<std::string> attendees;
+    DateTime start;
+    Duration relativeDuration;
+    Relative relativeTo;
+    Duration duration;
+    int numrepeat;
+    Type type;
+    
+};
+
+Alarm::Alarm()
+:   d(new Alarm::Private)
+{
+}
+
+Alarm::Alarm(const std::string &text)
+:   d(new Alarm::Private)
+{
+    d->text = text;
+    d->type = DisplayAlarm;
+}
+
+
+Alarm::Alarm(const Kolab::Attachment& audio)
+:   d(new Alarm::Private)
+{
+    d->audioFile = audio;
+    d->type = AudioAlarm;
+}
+
+Alarm::Alarm(const std::string& summary, const std::string& description, const std::vector< std::string > attendees)
+:   d(new Alarm::Private)
+{
+    d->summary = summary;
+    d->text = description;
+    d->attendees = attendees;
+    d->type = EMailAlarm;
+    
+}
+
+Alarm::Alarm(const Kolab::Alarm &other)
+:   d(new Alarm::Private)
+{
+    *d = *other.d;
+}
+
+void Alarm::operator=(const Kolab::Alarm &other)
+{
+    *d = *other.d;
+}
+
+Alarm::~Alarm()
+{
+}
+
+bool Alarm::operator==(const Kolab::Alarm &other) const
+{
+    return ( d->text == other.description() &&
+        d->text == other.description() &&
+        d->audioFile == other.audioFile() &&
+        d->summary == other.summary() &&
+        d->attendees == other.attendees() &&
+        d->start == other.start() &&
+        d->relativeDuration == other.relativeStart() &&
+        d->relativeTo == other.relativeTo() &&
+        d->duration == other.duration() &&
+        d->numrepeat == other.numrepeat() );
+}
+
+std::string Alarm::text() const
+{
+    return d->text;
+}
+
+Attachment Alarm::audioFile() const
+{
+    return d->audioFile;
+}
+
+std::string Alarm::summary() const
+{
+    return d->summary;
+}
+
+std::string Alarm::description() const
+{
+    return d->text;
+}
+
+std::vector< std::string > Alarm::attendees() const
+{
+    return d->attendees;
+}
+
+void Alarm::setStart(const Kolab::DateTime &start)
+{
+    d->start = start;
+}
+
+DateTime Alarm::start() const
+{
+    return d->start;
+}
+
+void Alarm::setRelativeStart(const Kolab::Duration &duration, Relative relativeTo)
+{
+    d->relativeDuration = duration;
+    d->relativeTo = relativeTo;
+}
+
+Duration Alarm::relativeStart() const
+{
+    return d->relativeDuration;
+}
+
+Relative Alarm::relativeTo() const
+{
+    return d->relativeTo;
+}
+
+void Alarm::setDuration(const Kolab::Duration &duration, int numrepeat)
+{
+    d->numrepeat = numrepeat;
+    d->duration = duration;
+}
+
+Duration Alarm::duration() const
+{
+    return d->duration;
+}
+
+int Alarm::numrepeat() const
+{
+    return d->numrepeat;
+}
+
+Alarm::Type Alarm::type() const
+{
+    return d->type;
+}
+
+
+
+
 
 
 }//Namespace
diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index c4d3dd1..6f43ac8 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -99,21 +99,118 @@ private:
     bool mIsValid;
 };
 
-enum Related {
+class Attachment {
+public:
+    Attachment();
+    Attachment(const Kolab::Attachment &);
+    ~Attachment();
+
+    void operator=(const Attachment &);
+    bool operator==(const Attachment &) const;
+
+    void setUri(const std::string &uri, const std::string &mimetype);
+    std::string uri() const;
+
+     ///Un-encoded binary content, Implies embedded, will be encoded
+     void setData(const std::string &, const std::string &mimetype);
+     ///Decoded binary content.
+     std::string data() const;
+     //TODO add possibility to set already encoded data and uri to be embedded as performance/convenience improvement?
+//     ///Base64 encoded binary content, Implies embedded
+//      void setEncodedData(const std::string &, const std::string &mimetype);
+
+    std::string mimetype() const;
+
+    ///User visible label
+    void setLabel(const std::string &);
+    std::string label() const;
+    
+    bool isValid() const;
+private:
+    struct Private;
+    boost::scoped_ptr<Private> d;
+};
+
+enum Relative {
     Start,
     End
 };
 
-struct Alarm {
-    //TODO
-//     void setRelativeStart(const Duration &, Related);
-//     void setStart(const DateTime &);
-//     void setDuration(const Duration &, int numrepeat);
-//     
-//     //The following alarm types are mutual exclusive (implement as constructor?)
-//     void setDisplayAlarm(const std::string &text);
-//     void setAudioAlarm(const Attachment &audio);
-//     void setEmailAlarm(const std::string &summary, const std::string &description, const std::vector<std::string> attendees);
+struct Duration {
+    Duration():mWeeks(0), mDays(0), mHours(0), mMinutes(0), mSeconds(0), mNegative(false), valid(false){};
+    Duration(int weeks, bool negative = false): mWeeks(weeks), mDays(0), mHours(0), mMinutes(0), mSeconds(0), mNegative(negative), valid(true){};
+    Duration(int days, int hours, int minutes, int seconds, bool negative = false): mWeeks(0), mDays(days), mHours(hours), mMinutes(minutes), mSeconds(seconds), mNegative(negative), valid(true){};
+    bool operator==(const Duration &other) const{ return (mWeeks == other.mWeeks && 
+                                                            mDays == other.mDays &&
+                                                            mHours == other.mHours &&
+                                                            mMinutes == other.mMinutes &&
+                                                            mSeconds == other.mSeconds &&
+                                                            mNegative == other.mNegative &&
+                                                            valid == other.valid );};
+    int weeks() const { return mWeeks; };
+    int days() const { return mDays; };
+    int hours() const { return mHours; };
+    int minutes() const { return mMinutes; };
+    int seconds() const { return mSeconds; };
+
+    bool isNegative() const { return mNegative; };
+    bool isValid() const { return valid; };
+private:
+    int mWeeks;
+    int mDays;
+    int mHours;
+    int mMinutes;
+    int mSeconds;
+    bool mNegative;
+    bool valid;
+};
+
+class Alarm {
+public:
+    enum Type {
+        InvalidAlarm,
+        EMailAlarm,
+        DisplayAlarm,
+        AudioAlarm
+    };
+    
+    Alarm();
+    Alarm(const Alarm &);
+    ~Alarm();
+
+    void operator=(const Alarm &);
+    bool operator==(const Alarm &other) const;
+
+    ///EMail Alarm
+    Alarm(const std::string &summary, const std::string &description, const std::vector<std::string> attendees);
+    std::string summary() const;
+    std::string description() const;
+    std::vector<std::string> attendees() const;
+
+    ///Display Alarm
+    Alarm(const std::string &text);
+    std::string text() const;
+
+    ///Audio Alarm
+    Alarm(const Attachment &audio);
+    Attachment audioFile() const;
+    
+    void setRelativeStart(const Duration &, Relative);
+    Duration relativeStart() const;
+    Relative relativeTo() const;
+    
+    void setStart(const DateTime &);
+    DateTime start() const;
+
+    void setDuration(const Duration &, int numrepeat);
+    Duration duration() const;
+    int numrepeat() const;
+    
+    Type type() const;
+
+private:
+    struct Private;
+    boost::scoped_ptr<Private> d;
 };
 
 
@@ -183,34 +280,6 @@ private:
     boost::scoped_ptr<Private> d;
 };
 
-struct Duration {
-    Duration():valid(false){};
-    Duration(int weeks, bool negative = false): mWeeks(weeks), mDays(0), mHours(0), mMinutes(0), mSeconds(0), mNegative(negative), valid(true){};
-    Duration(int days, int hours, int minutes, int seconds, bool negative = false): mWeeks(0), mDays(days), mHours(hours), mMinutes(minutes), mSeconds(seconds), mNegative(negative), valid(true){};
-    bool operator==(const Duration &other) const{ return (mWeeks == other.mWeeks && 
-                                                            mDays == other.mDays &&
-                                                            mHours == other.mHours &&
-                                                            mMinutes == other.mMinutes &&
-                                                            mSeconds == other.mSeconds &&
-                                                            mNegative == other.mNegative &&
-                                                            valid == other.valid );};
-    int weeks() const { return mWeeks; };
-    int days() const { return mDays; };
-    int hours() const { return mHours; };
-    int minutes() const { return mMinutes; };
-    int seconds() const { return mSeconds; };
-
-    bool isNegative() const { return mNegative; };
-    bool isValid() const { return valid; };
-private:
-    int mWeeks;
-    int mDays;
-    int mHours;
-    int mMinutes;
-    int mSeconds;
-    bool mNegative;
-    bool valid;
-};
 
 enum PartStatus {
     PartNeedsAction,
@@ -260,37 +329,6 @@ private:
 std::vector<Attendee> operator<<(std::vector<Attendee>, const Attendee&);
 
 
-class Attachment {
-public:
-    Attachment();
-    Attachment(const Kolab::Attachment &);
-    ~Attachment();
-
-    void operator=(const Attachment &);
-    bool operator==(const Attachment &) const;
-
-    void setUri(const std::string &uri, const std::string &mimetype);
-    std::string uri() const;
-
-     ///Un-encoded binary content, Implies embedded, will be encoded
-     void setData(const std::string &, const std::string &mimetype);
-     ///Decoded binary content.
-     std::string data() const;
-     //TODO add possibility to set already encoded data and uri to be embedded as performance/convenience improvement?
-//     ///Base64 encoded binary content, Implies embedded
-//      void setEncodedData(const std::string &, const std::string &mimetype);
-
-    std::string mimetype() const;
-
-    ///User visible label
-    void setLabel(const std::string &);
-    std::string label() const;
-
-private:
-    struct Private;
-    boost::scoped_ptr<Private> d;
-};
-
 struct CustomProperty {
     CustomProperty(const std::string &i, const std::string &v)
     : identifier(i), value(v) {};
diff --git a/c++/lib/kolabtodo.cpp b/c++/lib/kolabtodo.cpp
index 7beec79..1775895 100644
--- a/c++/lib/kolabtodo.cpp
+++ b/c++/lib/kolabtodo.cpp
@@ -315,4 +315,14 @@ std::vector< CustomProperty > Todo::customProperties() const
     return d->customProperties;
 }
 
+void Todo::setAlarms(const std::vector< Alarm > &alarms)
+{
+    d->alarms = alarms;
+}
+
+std::vector< Alarm > Todo::alarms() const
+{
+    return d->alarms;
+}
+
 }
\ No newline at end of file
diff --git a/c++/lib/kolabtodo.h b/c++/lib/kolabtodo.h
index 4533f93..1271bc0 100644
--- a/c++/lib/kolabtodo.h
+++ b/c++/lib/kolabtodo.h
@@ -106,6 +106,10 @@ public:
     
     void setCustomProperties(const std::vector<CustomProperty> &);
     std::vector<CustomProperty> customProperties() const;
+    
+    void setAlarms(const std::vector<Alarm> &);
+    std::vector<Alarm> alarms() const;
+    
 private:
     struct Private;
     boost::scoped_ptr<Private> d;
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index f9ae5c3..752c162 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -559,6 +559,42 @@ void setCalAddress(const icalendar_2_0::CalAddressPropertyType &cal, std::string
 //     }
 // }
 
+template <typename T>
+Kolab::Attachment toAttachment(T aProp)
+{
+    Kolab::Attachment a;
+    std::string mimetype;
+    if (aProp.parameters()) {
+        const icalendar_2_0::AttachPropType ::parameters_type &parameters = *aProp.parameters();
+        for (icalendar_2_0::AttachPropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) {
+            if (const icalendar_2_0::FmttypeParamType *p = dynamic_cast<const icalendar_2_0::FmttypeParamType*> (&*it)) {
+                mimetype = p->text();
+            }
+            if (const icalendar_2_0::EncodingParamType *p = dynamic_cast<const icalendar_2_0::EncodingParamType*> (&*it)) {
+                if (p->text() != BASE64) {
+                    std::cerr << "wrong encoding";
+                    return Kolab::Attachment();
+                }
+            }
+            if (const icalendar_2_0::XlabelParamType *p = dynamic_cast<const icalendar_2_0::XlabelParamType*> (&*it)) {
+                a.setLabel(p->text());
+            }
+        }
+    }
+    if (mimetype.empty()) {
+        std::cerr << "no mimetype" << std::endl;
+    }
+
+    if (aProp.uri()) {
+        a.setUri(*aProp.uri(), mimetype);
+    } else if (aProp.binary()) {
+        a.setData(base64_decode(*aProp.binary()), mimetype);
+    } else {
+        std::cerr << "not uri and no data available" << std::endl;
+    }
+    return a;
+}
+
 
 template <typename I, typename T>
 void setIncidenceProperties(I &inc, const T &prop)
@@ -670,7 +706,7 @@ void setIncidenceProperties(I &inc, const T &prop)
     
     if (prop.attendee().size()) {
         std::vector<Kolab::Attendee> attendees;
-        BOOST_FOREACH(icalendar_2_0::properties::attendee_type aProp, prop.attendee()) {
+        BOOST_FOREACH(typename T::attendee_type aProp, prop.attendee()) {
             Kolab::Attendee a(toString(aProp.cal_address()));
             if (aProp.parameters()) {
                 const icalendar_2_0::AttendeePropType::parameters_type &parameters = *aProp.parameters();
@@ -705,32 +741,11 @@ void setIncidenceProperties(I &inc, const T &prop)
     
     if (prop.attach().size()) {
         std::vector<Kolab::Attachment> attachments;
-        BOOST_FOREACH(icalendar_2_0::properties::attach_type aProp, prop.attach()) {
-            Kolab::Attachment a;
-            std::string mimetype;
-            if (aProp.parameters()) {
-                const icalendar_2_0::AttachPropType ::parameters_type &parameters = *aProp.parameters();
-                for (icalendar_2_0::AttachPropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) {
-                    if (const icalendar_2_0::FmttypeParamType *p = dynamic_cast<const icalendar_2_0::FmttypeParamType*> (&*it)) {
-                        mimetype = p->text();
-                    }
-                    if (const icalendar_2_0::EncodingParamType *p = dynamic_cast<const icalendar_2_0::EncodingParamType*> (&*it)) {
-                        if (p->text() != BASE64) {
-                            std::cout << "wrong encoding";
-                            continue;
-                        }
-                    }
-                    if (const icalendar_2_0::XlabelParamType *p = dynamic_cast<const icalendar_2_0::XlabelParamType*> (&*it)) {
-                        a.setLabel(p->text());
-                    }
-                }
-            }
-
-            if (aProp.uri()) {
-                a.setUri(*aProp.uri(), mimetype);
-            }
-            if (aProp.binary()) {
-                a.setData(base64_decode(*aProp.binary()), mimetype);
+        BOOST_FOREACH(typename T::attach_type aProp, prop.attach()) {
+            const Kolab::Attachment &a = toAttachment<typename T::attach_type>(aProp);
+            if (!a.isValid()) {
+                std::cerr << "invalid attachment" << std::endl;
+                continue;
             }
             attachments.push_back(a);
         }
@@ -739,7 +754,7 @@ void setIncidenceProperties(I &inc, const T &prop)
     
     if (prop.x_custom().size()) {
         std::vector<Kolab::CustomProperty> customProperties;
-        BOOST_FOREACH(icalendar_2_0::properties::x_custom_type p, prop.x_custom()) {
+        BOOST_FOREACH(typename T::x_custom_type p, prop.x_custom()) {
             customProperties.push_back(CustomProperty(p.identifier(), p.value()));
         }
         inc.setCustomProperties(customProperties);
@@ -822,7 +837,27 @@ std::auto_ptr< icalendar_2_0::RrulePropType > recurrenceProperty(const Recurrenc
     return rruleProp;
 }
 
-
+icalendar_2_0::AttachPropType fromAttachment(const Kolab::Attachment &a)
+{
+    icalendar_2_0::AttachPropType attachment;
+    icalendar_2_0::AttachPropType::parameters_type p;
+    p.baseParameter().push_back(icalendar_2_0::FmttypeParamType(a.mimetype()));
+    if (!a.label().empty()) {
+        p.baseParameter().push_back(icalendar_2_0::XlabelParamType(a.label()));
+    }
+    
+    if (!a.uri().empty()) {
+        attachment.uri(a.uri());
+    } else  if (!a.data().empty()) {
+        attachment.binary(base64_encode(reinterpret_cast<const unsigned char*>(a.data().c_str()), a.data().length()));
+        p.baseParameter().push_back(icalendar_2_0::EncodingParamType(BASE64));
+    } else {
+        std::cerr << "no uri and no data" << std::endl;
+    }
+    
+    attachment.parameters(p);
+    return attachment;
+}
 
 
 template <typename T, typename I>
@@ -979,26 +1014,8 @@ void getIncidenceProperties(T &prop, const I &inc)
     }
     
     if (!inc.attachments().empty()) {
-        
         BOOST_FOREACH(const Kolab::Attachment &a, inc.attachments()) {
-            typename properties::attach_type attachment;
-            typename properties::attach_type::parameters_type p;
-            
-            p.baseParameter().push_back(icalendar_2_0::FmttypeParamType(a.mimetype()));
-            
-            if (!a.label().empty()) {
-                p.baseParameter().push_back(icalendar_2_0::XlabelParamType(a.label()));
-            }
-            
-            if (!a.uri().empty()) {
-                attachment.uri(a.uri());
-            } else  if (!a.data().empty()) {
-                attachment.binary(base64_encode(reinterpret_cast<const unsigned char*>(a.data().c_str()), a.data().length()));
-                p.baseParameter().push_back(icalendar_2_0::EncodingParamType(BASE64));
-            }
-            
-            attachment.parameters(p);
-            prop.attach().push_back(attachment);
+            prop.attach().push_back(fromAttachment(a));
         }
     }
     
@@ -1010,6 +1027,158 @@ void getIncidenceProperties(T &prop, const I &inc)
 
 }
 
+const char* const START = "START";
+const char* const END = "END";
+
+template <typename KolabType, typename IncidenceType>
+void setAlarms(typename KolabType::components_type& components, const IncidenceType &incidence)
+{
+    typedef DateTimeConverter< DateTime> DC;
+    BOOST_FOREACH(const Kolab::Alarm &alarm, incidence.alarms()) {
+        typedef icalendar_2_0::ValarmType::properties_type PropType;
+        
+        PropType::trigger_type trigger;
+        if (alarm.start().isValid()) {
+            
+            trigger.date_time(DC::fromDateTime(alarm.start()));
+        } else {
+            if (!alarm.relativeStart().isValid()) {
+                std::cerr << "no start and no relativeStart" << std::endl;
+                continue;
+            }
+            trigger.duration(PropType::trigger_type::duration_type(fromDuration(alarm.relativeStart())));
+            icalendar_2_0::ArrayOfParameters parameters;
+            if (alarm.relativeTo() == Kolab::End) {
+                parameters.baseParameter().push_back(icalendar_2_0::RelatedParamType(END));
+            } else {
+                parameters.baseParameter().push_back(icalendar_2_0::RelatedParamType(START));
+            }
+            trigger.parameters(parameters);
+        }
+
+        std::auto_ptr<PropType> p;
+        switch(alarm.type()) {
+            case Kolab::Alarm::DisplayAlarm:
+                p = std::auto_ptr<PropType>(new PropType(PropType::action_type(DISPLAYALARM), trigger));
+                p->description(PropType::description_type(alarm.description()));
+                break;
+            case Kolab::Alarm::EMailAlarm:
+                p = std::auto_ptr<PropType>(new PropType(PropType::action_type(EMAILALARM), trigger));
+                p->summary(PropType::summary_type(alarm.summary()));
+                p->description(PropType::description_type(alarm.description()));
+                BOOST_FOREACH(const std::string &attendee, alarm.attendees()) {
+                    p->attendee().push_back(icalendar_2_0::ContactType(attendee));
+                }
+                break;
+            case Kolab::Alarm::AudioAlarm:
+                p = std::auto_ptr<PropType>(new PropType(PropType::action_type(AUDIOALARM), trigger));
+                p->description(PropType::description_type(alarm.description()));
+                p->attach(fromAttachment(alarm.audioFile()));
+                break;
+            default:
+                std::cerr << "invalid alarm" << std::endl;
+                continue;
+        }
+        if (alarm.duration().isValid()) {
+            p->duration(PropType::duration_type(fromDuration(alarm.duration())));
+            p->repeat(PropType::repeat_type(fromInt<PropType::repeat_type::integer_type>(alarm.numrepeat())));
+        }
+
+        components.valarm().push_back(icalendar_2_0::ValarmType(p));
+    }
+}
+
+// typename KolabTypes::DatePtr toDate(const icalendar_2_0::DateTimeType &dtProperty)
+// {
+//     typedef DateTimeConverter<Kolab::DateTime> DC;
+//     typename KolabTypes::DatePtr date;
+//     if (dtProperty.date_time()) {
+//         date = DC::toDate(dtProperty.date_time());
+//     }
+//     return date;
+// }
+
+
+template <typename IncidenceType, typename KolabType>
+void getAlarms(IncidenceType &incidence, const typename KolabType::components_type &components)
+{
+    typedef icalendar_2_0::ValarmType::properties_type PropType;
+    std::vector<Kolab::Alarm> alarms;
+    BOOST_FOREACH(const typename KolabType::components_type::valarm_type &valarm, components.valarm()) {
+        const icalendar_2_0::ValarmType::properties_type &prop = valarm.properties();
+
+        Kolab::Alarm alarm;
+        if (prop.action().text() == DISPLAYALARM) {
+            if (!prop.description()) {
+                std::cerr << "description is missing" << std::endl;
+                continue;
+            }
+            alarm = Kolab::Alarm((*prop.description()).text());
+        } else if (prop.action().text() == EMAILALARM) {
+            std::vector<std::string> attendees;
+            for (typename PropType::attendee_const_iterator at(prop.attendee().begin()); at != prop.attendee().end(); at++) {
+                attendees.push_back((*at).cal_address());
+            }
+            if (!prop.description() || !prop.summary()) {
+                std::cerr << "description or summary is missing" << std::endl;
+                continue;
+            }
+            alarm = Kolab::Alarm((*prop.summary()).text(), (*prop.description()).text(), attendees);
+        } else if (prop.action().text() == AUDIOALARM) {
+            if (!prop.attach()) {
+                std::cerr << "audio file is missing" << std::endl;
+                continue;
+            }
+            const Kolab::Attachment &attach = toAttachment<icalendar_2_0::properties::attach_type>(*prop.attach());
+            if (!attach.isValid()) {
+                std::cerr << "audio file is invalid" << std::endl;
+                continue;
+            }
+            alarm = Kolab::Alarm(attach);
+        } else {
+            std::cerr << "unknown alarm type " << prop.action().text() << std::endl;
+            continue;
+        }
+        
+        if (prop.trigger().date_time()) {
+            alarm.setStart(*DateTimeConverter<Kolab::DateTime>::toDate(*prop.trigger().date_time()));
+            if (!alarm.start().isUTC()) {
+                std::cerr << "The start date time must be in UTC "<< std::endl;
+                continue;
+            }
+        } else if (prop.trigger().duration()) {
+            Kolab::Relative relativeTo = Kolab::Start;
+            if (prop.trigger().parameters()) {
+                BOOST_FOREACH(const icalendar_2_0::ArrayOfParameters::baseParameter_type &param, (*prop.trigger().parameters()).baseParameter()) {
+                    if (const icalendar_2_0::RelatedParamType *rel = dynamic_cast<const icalendar_2_0::RelatedParamType*> (&param)) {
+                        if (rel->text() == START) {
+                            relativeTo = Kolab::Start;
+                        } else if (rel->text() == END) {
+                            relativeTo = Kolab::End;
+                        } else {
+                            std::cerr << "relativeTo not specified, default to start " << std::endl;
+                        }
+                    }
+                }
+            }
+            
+            alarm.setRelativeStart(toDuration(*prop.trigger().duration()), relativeTo);
+        } else {
+            std::cerr << "no duration and not starttime " << std::endl;
+            continue;
+        }
+        if (prop.duration()) {
+            int repeat = 0;
+            if (prop.repeat()) {
+                repeat = toInt(*prop.repeat());
+            }
+            alarm.setDuration(toDuration((*prop.duration()).duration()), repeat); //TODO check duration?
+        }
+        alarms.push_back(alarm);
+    }
+    incidence.setAlarms(alarms);
+}
+
 template <typename T> struct IncidenceConverter < Incidence, T >
 {
     typedef DateTimeConverter< DateTime> DC;
@@ -1092,6 +1261,14 @@ template < > struct IncidenceTrait <Kolab::Event>
                 }
             }
         }
+        if (vevent.components()) {
+            getAlarms<IncidenceType, KolabType>(event, *vevent.components());
+        }
+    }
+    
+    static void setComponents(KolabType::components_type& components, const Kolab::Event &incidence)
+    {
+        setAlarms<KolabType, IncidenceType>(components, incidence);
     }
      
     static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components)
@@ -1156,6 +1333,14 @@ template < > struct IncidenceTrait <Kolab::Todo>
         if (prop.percent_complete()) {
             todo.setPercentComplete(toInt(*prop.percent_complete()));
         }
+        if (vevent.components()) {
+            getAlarms<IncidenceType, KolabType>(todo, *vevent.components());
+        }
+    }
+    
+    static void setComponents(KolabType::components_type& components, const Kolab::Todo &incidence)
+    {
+        setAlarms<KolabType, IncidenceType>(components, incidence);
     }
      
     static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components)
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 454a965..b696f9b 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -16,6 +16,7 @@
 #include <lib/kolabkcalconversion.h>
 #include <iostream>
 #include <fstream>
+#include "serializers.h"
 
 //TODO remove
 void BindingsTest::writeContact()
@@ -171,6 +172,23 @@ void setIncidence(T &ev)
     properties.push_back(Kolab::CustomProperty("ident", "value"));
     properties.push_back(Kolab::CustomProperty("ident", "value"));
     ev.setCustomProperties(properties);
+    
+    std::vector<Kolab::Alarm> alarms;
+    Kolab::Alarm dispAlarm("ident");
+    dispAlarm.setRelativeStart(Kolab::Duration(3, true), Kolab::Start);
+    alarms.push_back(dispAlarm);
+    std::vector<std::string> att;
+    att.push_back("attendee1");
+    att.push_back("attendee2");
+    Kolab::Alarm emailAlarm("ident", "value", att);
+    emailAlarm.setStart(Kolab::DateTime(2003,2,3,2,3,4, true));
+    alarms.push_back(emailAlarm);
+    Kolab::Attachment audiofile;
+    audiofile.setUri("ksdjlksdflj", "sdkljdfl");
+    Kolab::Alarm audio(audiofile);
+    audio.setStart(Kolab::DateTime(2003,2,3,2,3,4, true));
+    alarms.push_back(audio);
+    ev.setAlarms(alarms);
 }
 
 template <typename T>
@@ -220,6 +238,7 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(ev.attendees(), re.attendees());
     QCOMPARE(ev.attachments(), re.attachments());
     QCOMPARE(ev.customProperties(), re.customProperties());
+    QCOMPARE(ev.alarms(), re.alarms());
 }
 
 
diff --git a/c++/tests/serializers.h b/c++/tests/serializers.h
index 30bef2d..8548b62 100644
--- a/c++/tests/serializers.h
+++ b/c++/tests/serializers.h
@@ -99,6 +99,49 @@ namespace QTest {
         ba += ")";
         return qstrdup(ba.data());
     }
+    
+    template<>
+    char *toString(const Kolab::Attachment &a)
+    {
+        QByteArray ba = "Kolab::Attachment(";
+        ba += QString::fromStdString(a.uri()).toAscii() + ", " + QString::fromStdString(a.mimetype()).toAscii()+ ", " + 
+        QString::fromStdString(a.label()).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<Kolab::Attachment> &v)
+    {
+        QByteArray ba = "vector<Kolab::Attachment>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ "\n";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const Kolab::Alarm &a)
+    {
+        QByteArray ba = "Kolab::Alarm(";
+        ba += QByteArray::number(a.type()) + ", " + QString::fromStdString(a.summary()).toAscii()+ ", " + 
+        QString::fromStdString(a.text()).toAscii()+", " +toString(a.duration()) + ", " + QByteArray::number(a.numrepeat())+  ", " + toString(a.start()) +
+        + toString(a.relativeStart())  + ", " +  QByteArray::number(a.relativeTo()) + toString(a.audioFile()) +  ", " + toString(a.attendees());
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<Kolab::Alarm> &v)
+    {
+        QByteArray ba = "vector<Kolab::Alarm>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ "\n";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
 
  }
  
diff --git a/c++/tests/testfiles/icalEvent.xml b/c++/tests/testfiles/icalEvent.xml
index 1c72be8..f18773e 100644
--- a/c++/tests/testfiles/icalEvent.xml
+++ b/c++/tests/testfiles/icalEvent.xml
@@ -59,7 +59,6 @@ Test.</text>
              </description>
 
            </properties>
-           <components/>
          </vevent>
 <!--         <vevent>
            <properties>
@@ -90,4 +89,4 @@ Test.</text>
          </vevent>-->
        </components>
      </vcalendar>
-</icalendar>
\ No newline at end of file
+</icalendar>
diff --git a/schemas/ical/kolabformat-xcal.xsd b/schemas/ical/kolabformat-xcal.xsd
index 0d7ecc6..5a5ec95 100644
--- a/schemas/ical/kolabformat-xcal.xsd
+++ b/schemas/ical/kolabformat-xcal.xsd
@@ -47,6 +47,32 @@
         </xs:complexContent>
     </xs:complexType>
 
+    <xs:complexType name="ContactType">
+    <xs:complexContent mixed="false">
+      <xs:extension base="CalAddressPropertyType"/>
+    </xs:complexContent>
+  </xs:complexType>
+    
+    <!-- 3.6.6 Alarm Component -->
+    <xs:complexType name="ValarmType" mixed="false">
+        <xs:sequence>
+            <xs:element name="properties">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="action" type="ActionPropType"/>
+                        <xs:element name="summary" type="TextPropertyType" minOccurs="0"/>
+                        <xs:element name="description" type="TextPropertyType" minOccurs="0"/>
+                        <xs:element name="attendee" type="ContactType" minOccurs="0" maxOccurs="unbounded"/>
+                        <xs:element name="attach" type="AttachPropType" minOccurs="0"/>
+                        <xs:element name="trigger" type="TriggerPropType"/>
+                        <xs:element name="duration" type="DurationPropType" minOccurs="0"/>
+                        <xs:element name="repeat" type="RepeatPropType" minOccurs="0"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
     <xs:complexType name="KolabEvent" >
         <xs:sequence>
             <xs:element name="properties">
@@ -78,9 +104,11 @@
                     </xs:sequence>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="components">
+            <xs:element name="components" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
-                <!--TODO valarm-->
+                    <xs:sequence>
+                        <xs:element name="valarm" type="ValarmType" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
                 </xs:complexType>
             </xs:element>
         </xs:sequence>
@@ -117,9 +145,11 @@
                     </xs:sequence>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="components">
+            <xs:element name="components" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
-                <!--TODO valarm-->
+                    <xs:sequence>
+                        <xs:element name="valarm" type="ValarmType" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
                 </xs:complexType>
             </xs:element>
         </xs:sequence>


commit 5de2c196da184d0b5aabbd62c134cd2698bd5801
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Tue Feb 7 02:04:16 2012 +0100

    regex didn't work for weeks for some reason, this way it does. It's not entirely clear to me why though...

diff --git a/schemas/ical/iCalendar-valtypes.xsd b/schemas/ical/iCalendar-valtypes.xsd
index bb64480..8b91dfa 100644
--- a/schemas/ical/iCalendar-valtypes.xsd
+++ b/schemas/ical/iCalendar-valtypes.xsd
@@ -99,7 +99,7 @@
       </xs:documentation>
     </xs:annotation>
     <xs:restriction base="xs:string">
-      <xs:pattern value="(\+|\-)?P((\d+Y)?(\d+M)?(\d+D)?T?(\d+H)?(\d+M)?(\d+S)?)|(\d+W)"/>
+      <xs:pattern value="(\+|\-)?P((\d+W)|((\d+Y)?(\d+M)?(\d+D)?T?(\d+H)?(\d+M)?(\d+S)?))"/>
     </xs:restriction>
   </xs:simpleType>
   


commit c41ebac13d4b4796d224d71c8850b9eea502e7b5
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sun Feb 5 23:23:07 2012 +0100

    ByDay parser/serializer

diff --git a/c++/lib/kolabcontainers.cpp b/c++/lib/kolabcontainers.cpp
index ac702e2..2a5b6bd 100644
--- a/c++/lib/kolabcontainers.cpp
+++ b/c++/lib/kolabcontainers.cpp
@@ -244,12 +244,12 @@ RecurrenceRule::Frequency RecurrenceRule::frequency() const
     return d->freq;
 }
 
-void RecurrenceRule::setWeekStart(RecurrenceRule::Weekday weekstart)
+void RecurrenceRule::setWeekStart(Kolab::Weekday weekstart)
 {
     d->weekstart = weekstart;
 }
 
-RecurrenceRule::Weekday RecurrenceRule::weekStart() const
+Kolab::Weekday RecurrenceRule::weekStart() const
 {
     return d->weekstart;
 }
diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index 1d28a2d..c4d3dd1 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -76,10 +76,27 @@ enum Status {
     Final
 };
 
+enum Weekday {
+    Monday,
+    Tuesday,
+    Wednesday,
+    Thursday,
+    Friday,
+    Saturday,
+    Sunday
+};
+
 struct DayPos {
-    void operator=(const DayPos &){};
-    bool operator==(const DayPos &) const{ return false;};
-    //TODO
+    DayPos(): mIsValid(false){};
+    DayPos(int occurrence, Weekday weekday): mOccurrence(occurrence), mWeekday(weekday), mIsValid(true){};
+    bool operator==(const DayPos &other) const { return mOccurrence == other.mOccurrence && mWeekday == other.mWeekday; };
+    int occurence() const { return mOccurrence; };
+    Weekday weekday() const { return mWeekday; };
+    bool isValid() { return mIsValid; };
+private:
+    int mOccurrence;
+    Weekday mWeekday;
+    bool mIsValid;
 };
 
 enum Related {
@@ -123,16 +140,6 @@ public:
     void setFrequency(Frequency);
     Frequency frequency() const;
     
-    enum Weekday {
-        Monday,
-        Tuesday,
-        Wednesday,
-        Thursday,
-        Friday,
-        Saturday,
-        Sunday
-    };
-    
     void setWeekStart(Weekday);
     Weekday weekStart() const;
     
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index 03a44cb..f9ae5c3 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -65,6 +65,102 @@ std::string toString(const icalendar_2_0::TextPropertyType &s)
     return s.text();
 }
 
+
+const char* const MO = "MO";
+const char* const TU = "TU";
+const char* const WE = "WE";
+const char* const TH = "TH";
+const char* const FR = "FR";
+const char* const SA = "SA";
+const char* const SU = "SU";
+
+std::string fromDayPos(const Kolab::DayPos &d)
+{   
+    std::string s;
+    if (d.occurence() != 0) {
+        s.append(boost::lexical_cast<std::string>(d.occurence()));
+    }
+    switch (d.weekday()) {
+        case Kolab::Monday:
+            s.append(MO);
+            break;
+        case Kolab::Tuesday:
+            s.append(TU);
+            break;
+        case Kolab::Wednesday:
+            s.append(WE);
+            break;
+        case Kolab::Thursday:
+            s.append(TH);
+            break;
+        case Kolab::Friday:
+            s.append(FR);
+            break;
+        case Kolab::Saturday:
+            s.append(SA);
+            break;
+        case Kolab::Sunday:
+            s.append(SU);
+            break;
+    }
+    return s;
+}
+
+Kolab::DayPos toDayPos(const std::string &s)
+{
+    std::string number;
+    bool gotOccurrence = false;
+    int occurrence = 0;
+    for (std::string::const_iterator it = s.begin(); it != s.end(); it++) {
+        switch(*it) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '+':
+            case '-':
+                number.push_back(*it);
+                break;
+            default:
+                if (!gotOccurrence && !number.empty()) {
+                    try {
+                        occurrence = boost::lexical_cast<int>(number);
+                    } catch(boost::bad_lexical_cast &) {
+                        std::cerr << "failed to convert: " <<  number <<  std::endl;
+                        return DayPos();
+                    }
+                    number.clear();
+                }
+                gotOccurrence = true;
+                number.push_back(*it);
+                break;
+        }
+    }
+
+    if (number == MO) {
+        return DayPos(occurrence, Kolab::Monday);
+    } else if (number == TU) {
+        return DayPos(occurrence, Kolab::Tuesday);
+    } else if (number == WE) {
+        return DayPos(occurrence, Kolab::Wednesday);
+    } else if (number == TH) {
+        return DayPos(occurrence, Kolab::Thursday);
+    } else if (number == FR) {
+        return DayPos(occurrence, Kolab::Friday);
+    } else if (number == SA) {
+        return DayPos(occurrence, Kolab::Saturday);
+    } else if (number == SU) {
+        return DayPos(occurrence, Kolab::Sunday);
+    }
+    return DayPos();
+}
+
 std::string fromDuration(const Kolab::Duration &d)
 {
     std::string s;
@@ -330,25 +426,25 @@ template <> struct RecurrenceConverter < RecurrenceRule >
 
         switch (wkst) {
             case WeekdayRecurType::MO:
-                r->setWeekStart(RecurrenceRule::Monday);
+                r->setWeekStart(Kolab::Monday);
                 break;
             case WeekdayRecurType::TU:
-                r->setWeekStart(RecurrenceRule::Tuesday);
+                r->setWeekStart(Kolab::Tuesday);
                 break;
             case WeekdayRecurType::WE:
-                r->setWeekStart(RecurrenceRule::Wednesday);
+                r->setWeekStart(Kolab::Wednesday);
                 break;
             case WeekdayRecurType::TH:
-                r->setWeekStart(RecurrenceRule::Thursday);
+                r->setWeekStart(Kolab::Thursday);
                 break;
             case WeekdayRecurType::FR:
-                r->setWeekStart(RecurrenceRule::Friday);
+                r->setWeekStart(Kolab::Friday);
                 break;
             case WeekdayRecurType::SA:
-                r->setWeekStart(RecurrenceRule::Saturday);
+                r->setWeekStart(Kolab::Saturday);
                 break;
             case WeekdayRecurType::SU:
-                r->setWeekStart(RecurrenceRule::Sunday);
+                r->setWeekStart(Kolab::Sunday);
                 break;
             default:
                 std::cout << "invalid unhandled weekday" << wkst;
@@ -401,11 +497,7 @@ template <> struct RecurrenceConverter < RecurrenceRule >
     {
         std::vector<DayPos> by;
         for (icalendar_2_0::RecurType::byday_const_iterator it(list.begin()); it != list.end(); it++) {
-            //TODO implement parser for format
-//             switch () {
-//                 
-//             }
-            //by.append(convertToInt<xml_schema::non_negative_integer>(*it));
+            by.push_back(toDayPos(*it));
         }
         r->setByday(by);
     }
@@ -704,7 +796,11 @@ std::auto_ptr< icalendar_2_0::RrulePropType > recurrenceProperty(const Recurrenc
     }
     
     if (!r.byday().empty()) {
-        //TODO
+        RecurType::byday_sequence byday;
+        BOOST_FOREACH(const Kolab::DayPos &daypos, r.byday()) {
+            byday.push_back(fromDayPos(daypos));
+        }
+        recur.byday(byday);
     }
     
     if (!r.bymonthday().empty()) {
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 4779def..454a965 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -107,7 +107,11 @@ void setIncidence(T &ev)
     rule.setBysecond(list);
     rule.setByminute(list);
     rule.setByhour(list);
-//     rule.setByday(list); //TODO
+    std::vector<Kolab::DayPos> byday;
+    byday.push_back(Kolab::DayPos(15, Kolab::Friday));
+    byday.push_back(Kolab::DayPos(0, Kolab::Monday));
+    byday.push_back(Kolab::DayPos(-3, Kolab::Monday));
+    rule.setByday(byday);
     rule.setBymonthday(list);
     rule.setByyearday(list);
     rule.setByweekno(list);
@@ -195,7 +199,7 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(r1.byminute(), r2.byminute());
     QCOMPARE(r1.byhour(), r2.byhour());
 //     QEXPECT_FAIL("", "Implement Day parser", Continue);
-//     QCOMPARE(r1.byday(), r2.byday());
+    QCOMPARE(r1.byday(), r2.byday());
     QCOMPARE(r1.bymonthday(), r2.bymonthday());
     QCOMPARE(r1.byyearday(), r2.byyearday());
     QCOMPARE(r1.byweekno(), r2.byweekno());
diff --git a/c++/tests/conversiontest.cpp b/c++/tests/conversiontest.cpp
index df19a75..cb9367c 100644
--- a/c++/tests/conversiontest.cpp
+++ b/c++/tests/conversiontest.cpp
@@ -6,6 +6,7 @@
 #include "serializers.h"
 
 Q_DECLARE_METATYPE(Kolab::Duration);
+Q_DECLARE_METATYPE(Kolab::DayPos);
  
 void ConversionTest::durationParserTest_data()
 {
@@ -46,6 +47,45 @@ void ConversionTest::durationSerializerTest()
 }
 
 
+void ConversionTest::dayPosParserTest_data()
+{
+    QTest::addColumn<Kolab::DayPos>("expected");
+    QTest::addColumn<QString>("string");
+
+    QTest::newRow("positive") << Kolab::DayPos(15, Kolab::Wednesday) << "15WE";
+    QTest::newRow("positive with +") << Kolab::DayPos(15, Kolab::Wednesday) << "+15WE";
+    QTest::newRow("negative") << Kolab::DayPos(-15, Kolab::Wednesday) << "-15WE";
+    QTest::newRow("all occurrences") << Kolab::DayPos(0, Kolab::Wednesday) << "WE";
+
+}
+
+void ConversionTest::dayPosParserTest()
+{
+    QFETCH(QString, string);
+    QFETCH(Kolab::DayPos, expected);
+    const Kolab::DayPos result = Kolab::toDayPos(string.toStdString());
+    QCOMPARE(result, expected);
+}
+
+void ConversionTest::dayPosSerializerTest_data()
+{
+    QTest::addColumn<QString>("expected");
+    QTest::addColumn<Kolab::DayPos>("daypos");
+
+    QTest::newRow("pos") << "15WE" << Kolab::DayPos(15, Kolab::Wednesday);
+    QTest::newRow("negative") << "-15WE" << Kolab::DayPos(-15, Kolab::Wednesday);
+    QTest::newRow("all occurrences") << "WE" << Kolab::DayPos(0, Kolab::Wednesday);
+
+}
+
+void ConversionTest::dayPosSerializerTest()
+{
+    QFETCH(Kolab::DayPos, daypos);
+    QFETCH(QString, expected);
+    const std::string result = Kolab::fromDayPos(daypos);
+    QCOMPARE(QString::fromStdString(result), expected);
+}
+
 
 QTEST_MAIN( ConversionTest )
 
diff --git a/c++/tests/conversiontest.h b/c++/tests/conversiontest.h
index 989ec95..8672b99 100644
--- a/c++/tests/conversiontest.h
+++ b/c++/tests/conversiontest.h
@@ -14,6 +14,12 @@ class ConversionTest : public QObject
     
     void durationSerializerTest_data();
     void durationSerializerTest();
+    
+    void dayPosParserTest_data();
+    void dayPosParserTest();
+    
+    void dayPosSerializerTest_data();
+    void dayPosSerializerTest();
 };
 
 #endif // CONVERSIONTEST_H
diff --git a/c++/tests/serializers.h b/c++/tests/serializers.h
index a4d386b..30bef2d 100644
--- a/c++/tests/serializers.h
+++ b/c++/tests/serializers.h
@@ -90,6 +90,15 @@ namespace QTest {
         ba += ")";
         return qstrdup(ba.data());
     }
+    
+    template<>
+    char *toString(const Kolab::DayPos &dt)
+    {
+        QByteArray ba = "Kolab::DayPos(";
+        ba += QByteArray::number(dt.occurence()) + ", " + QByteArray::number(dt.weekday());
+        ba += ")";
+        return qstrdup(ba.data());
+    }
 
  }
  
diff --git a/schemas/ical/iCalendar-valtypes.xsd b/schemas/ical/iCalendar-valtypes.xsd
index b0ae12c..bb64480 100644
--- a/schemas/ical/iCalendar-valtypes.xsd
+++ b/schemas/ical/iCalendar-valtypes.xsd
@@ -215,7 +215,8 @@
   
   <xs:simpleType name="BydayRecurType">
     <xs:restriction base="xs:string">
-        <xs:pattern value="((\-)?N)?(SU|MO|TU|WE|TH|FR|SA)"/>
+      <!--For some reason N doesn't match the number and was therefore replaced by \d\d? (number are only up to 53 valid)-->
+        <xs:pattern value="((\-|\+)?\d\d?)?(SU|MO|TU|WE|TH|FR|SA)"/>
     </xs:restriction>
   </xs:simpleType>
   


commit 674acc50ee572df31cd121d44034be27a8158b0e
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sun Feb 5 22:07:34 2012 +0100

    RelatedTo property, Duration, comments

diff --git a/c++/lib/DEVELOPMENT b/c++/lib/DEVELOPMENT
index 7002b31..466c494 100644
--- a/c++/lib/DEVELOPMENT
+++ b/c++/lib/DEVELOPMENT
@@ -1,3 +1,8 @@
+== General Design Notes==
+
+* Although many values are optional, the library doesn't differentiate between set and not set values. The approach taken is to not write out values which are still on the default value in order to keep objects small.
+The downside of this is that an xml object could be modified although no values have been modified (because default values are not written out again).
+
 == Libraries ==
 
 === libkolabxml ===
@@ -18,7 +23,7 @@ Library to provide converters to KCalCore containers as well as advanced functio
 === kolabxmlkcal (deprecated) ===
 
 First approach which does a direct mapping from xml to KCalCore containers. 
-It tourned out to be quite complicated to write generic enough code which would work for both, the Kolab containers and the KCalCore containers.
+It turned out to be quite complicated to write generic enough code which would work for both, the Kolab containers and the KCalCore containers.
 Also the performance and memory overhead of the additional intermediate representation should be minimal, therefore this approach was abandonend.
 
 == Code architecture ==
@@ -45,7 +50,7 @@ Providing non-const references to the value through an accessor function doesn't
 Therefore providing a setter + a getter which returns by copy seems the safest way. A const ref getter could be provided if needed (I doubt it would ever help).
 
 The containers do not use inheritance to ensure maximum compatibilty with SWIG (although SWIG provides inheritance for some languages).
-They we're also designed to be easily copieable and make therefore minimal use of pointers. Due to this design taking a copy of a container should be very efficient and problems like object slicing can not occur.
+They were also designed to be easily copieable and make therefore minimal use of pointers. Due to this design taking a copy of a container should be very efficient and problems like object slicing can not occur.
 
 The containers are meant to not contain any logic, all logic should be implemented in the serialization functions.
 
@@ -86,4 +91,4 @@ For further performance improvements read this: http://xerces.apache.org/xerces2
 All exceptions which could be thrown are caught within the serializing functions. There shouldn't be any uncaught exceptions because the bindings code doesn't handle that.
 If an error occurs a message is printed to the standard error output and a default constructed value is returned.
 
-Writing doesn't provide any validation (as it is hardly useful). For debugging purposes the document can be parsed again after writing. Validity is only ensured by typesafety, but range errors are easily possible.
\ No newline at end of file
+Writing doesn't provide any validation (as it is hardly useful for the reason that it's anyways not possible to correct the mistake). For debugging purposes the document can be parsed again after writing. Validity is only ensured by typesafety, but range errors are easily possible.
\ No newline at end of file
diff --git a/c++/lib/incidence_p.h b/c++/lib/incidence_p.h
index 5cfe7b2..9c8bfce 100644
--- a/c++/lib/incidence_p.h
+++ b/c++/lib/incidence_p.h
@@ -37,6 +37,7 @@ namespace Kolab {
         int sequence;
         Classification classification;
         std::vector< std::string > categories;
+        std::vector< std::string > relatedTo;
         DateTime start;
         
         DateTime recurrenceID;
diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index 89f5d84..1d28a2d 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -78,6 +78,7 @@ enum Status {
 
 struct DayPos {
     void operator=(const DayPos &){};
+    bool operator==(const DayPos &) const{ return false;};
     //TODO
 };
 
@@ -176,7 +177,32 @@ private:
 };
 
 struct Duration {
-    //TODO
+    Duration():valid(false){};
+    Duration(int weeks, bool negative = false): mWeeks(weeks), mDays(0), mHours(0), mMinutes(0), mSeconds(0), mNegative(negative), valid(true){};
+    Duration(int days, int hours, int minutes, int seconds, bool negative = false): mWeeks(0), mDays(days), mHours(hours), mMinutes(minutes), mSeconds(seconds), mNegative(negative), valid(true){};
+    bool operator==(const Duration &other) const{ return (mWeeks == other.mWeeks && 
+                                                            mDays == other.mDays &&
+                                                            mHours == other.mHours &&
+                                                            mMinutes == other.mMinutes &&
+                                                            mSeconds == other.mSeconds &&
+                                                            mNegative == other.mNegative &&
+                                                            valid == other.valid );};
+    int weeks() const { return mWeeks; };
+    int days() const { return mDays; };
+    int hours() const { return mHours; };
+    int minutes() const { return mMinutes; };
+    int seconds() const { return mSeconds; };
+
+    bool isNegative() const { return mNegative; };
+    bool isValid() const { return valid; };
+private:
+    int mWeeks;
+    int mDays;
+    int mHours;
+    int mMinutes;
+    int mSeconds;
+    bool mNegative;
+    bool valid;
 };
 
 enum PartStatus {
diff --git a/c++/lib/kolabformat.cpp b/c++/lib/kolabformat.cpp
index ed61bec..16eb19e 100644
--- a/c++/lib/kolabformat.cpp
+++ b/c++/lib/kolabformat.cpp
@@ -18,9 +18,7 @@
 #include "kolabformat.h"
 
 #include <iostream>
-#include "kolabcontainers.h"
 #include "xcalconversions.h"
-#include "kolabtodo.h"
 
 #include "xcardconversions.h"
 
diff --git a/c++/lib/kolabtodo.cpp b/c++/lib/kolabtodo.cpp
index 36894b0..7beec79 100644
--- a/c++/lib/kolabtodo.cpp
+++ b/c++/lib/kolabtodo.cpp
@@ -118,6 +118,21 @@ std::vector< std::string > Todo::categories() const
     return d->categories;
 }
 
+void Todo::setRelatedTo(const std::vector< std::string > &related)
+{
+    d->relatedTo = related;
+}
+
+void Todo::addRelatedTo(const std::string &related)
+{
+    d->relatedTo.push_back(related);
+}
+
+std::vector< std::string > Todo::relatedTo() const
+{
+    return d->relatedTo;
+}
+
 void Todo::setStart(const Kolab::DateTime &start)
 {
     d->start = start;
diff --git a/c++/lib/kolabtodo.h b/c++/lib/kolabtodo.h
index a023f20..4533f93 100644
--- a/c++/lib/kolabtodo.h
+++ b/c++/lib/kolabtodo.h
@@ -51,6 +51,10 @@ public:
     void addCategory(const std::string &);
     std::vector<std::string> categories() const;
     
+    void setRelatedTo(const std::vector<std::string> &);
+    void addRelatedTo(const std::string &);
+    std::vector<std::string> relatedTo() const;
+    
     void setStart(const DateTime &);
     DateTime start() const;
     
diff --git a/c++/lib/utils.h b/c++/lib/utils.h
index 549b2da..d0cf739 100644
--- a/c++/lib/utils.h
+++ b/c++/lib/utils.h
@@ -19,7 +19,7 @@
 #define UTILS_H
 
 #include <string>
-#include <kolabcontainers.h>
+#include "kolabcontainers.h"
 
 namespace Kolab {
 
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index 048666d..03a44cb 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -19,15 +19,21 @@
 #define XCALCONVERSIONS_H
 
 #include "genericxcalconverions.h"
-#include "kolabcontainers.h"
+
 #include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "kolabcontainers.h"
 #include "kolabtodo.h"
+#include "kolabevent.h"
 #include "utils.h"
 #include "base64.h"
+
 namespace Kolab {
 
 class Incidence;
 
+//TODO we can get rid of this construct (was used for the kcalcore/kolab in parallel masterplan)
 struct KolabTypes
 {
     typedef DateTime DateType;
@@ -53,11 +59,138 @@ std::auto_ptr<T> fromStringList(const std::vector<std::string> &list)
     return ptr;
 }
 
+//TODO doesn't seem very useful after all, remove
 std::string toString(const icalendar_2_0::TextPropertyType &s)
 {
     return s.text();
 }
 
+std::string fromDuration(const Kolab::Duration &d)
+{
+    std::string s;
+    if (!d.isValid()) {
+        return s;
+    }
+    if (d.isNegative()) {
+        s.push_back('-');
+    }
+    s.push_back('P');
+    try {
+        if (d.weeks() > 0) {
+            s.append(boost::lexical_cast<std::string>(d.weeks()));
+            s.push_back('W');
+        }
+        if (d.days() > 0) {
+            s.append(boost::lexical_cast<std::string>(d.days()));
+            s.push_back('D');
+        }
+        if (d.hours() > 0 || d.minutes() > 0 || d.seconds() > 0) {
+            s.push_back('T');
+            if (d.hours() > 0) {
+                s.append(boost::lexical_cast<std::string>(d.hours()));
+                s.push_back('H');
+            }
+            if (d.minutes() > 0) {
+                s.append(boost::lexical_cast<std::string>(d.minutes()));
+                s.push_back('M');
+            }
+            if (d.seconds() > 0) {
+                s.append(boost::lexical_cast<std::string>(d.seconds()));
+                s.push_back('S');
+            }
+        }
+        
+    } catch(boost::bad_lexical_cast &) { 
+        std::cerr << "failed to convert duration"<<  std::endl;
+        return std::string();
+    }
+    return s;
+}
+
+Kolab::Duration toDuration(const icalendar_2_0::DurationValueType &d)
+{
+    int weeks = 0;
+    int days = 0;
+    int hours = 0;
+    int minutes = 0;
+    int seconds = 0;
+    bool negative = false;
+
+    std::string number;
+
+    for (std::string::const_iterator it = d.begin(); it != d.end(); it++) {
+        switch(*it) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                number.push_back(*it);
+                break;
+            case 'H':
+                try {
+                    hours = boost::lexical_cast<int>(number);
+                } catch(boost::bad_lexical_cast &) {
+                    std::cerr << "failed to convert: " <<  number <<  std::endl;
+                    return Duration();
+                }
+                number.clear();
+                break;
+            case 'M':
+                try {
+                    minutes = boost::lexical_cast<int>(number);
+                } catch(boost::bad_lexical_cast &) {
+                    std::cerr << "failed to convert: " << number <<  std::endl;
+                    return Duration();
+                }
+                number.clear();
+                break;
+            case 'S':
+                try {
+                    seconds = boost::lexical_cast<int>(number);
+                } catch(boost::bad_lexical_cast &) { 
+                    std::cerr << "failed to convert: " << number <<  std::endl;
+                    return Duration();
+                }
+                number.clear();
+                break;
+            case 'T':
+                break;
+            case 'W':
+                try {
+                    weeks = boost::lexical_cast<int>(number);
+                } catch(boost::bad_lexical_cast &) { 
+                    std::cerr << "failed to convert: " << number <<  std::endl;
+                    return Duration();
+                }
+                return Duration(weeks, negative);
+            case 'D':
+                try {
+                    days = boost::lexical_cast<int>(number);
+                } catch(boost::bad_lexical_cast &) { 
+                    std::cerr << "failed to convert: " << number <<  std::endl;
+                    return Duration();
+                }
+                number.clear();
+                break;
+            case '+':
+                break;
+            case '-':
+                negative = true;
+                break;
+            case 'P':
+                break;
+            default:
+                std::cerr << "error: " << *it << std::endl;
+        }
+    }
+    return Duration(days, hours, minutes, seconds, negative);
+}
 // template <typename T>
 // std::auto_ptr<T> fromString(const std::string &s)
 // {
@@ -327,6 +460,13 @@ void setCalAddress(const icalendar_2_0::CalAddressPropertyType &cal, std::string
     email = cal.cal_address();
 }
 
+// template <typename T> setProperty(T::parameters_type::baseParameter_const_iterator it, void (*set)(std::string))
+// {
+//     if (const T * p = dynamic_cast<const T*> (&*it)) {
+//         set(p->text());
+//     }
+// }
+
 
 template <typename I, typename T>
 void setIncidenceProperties(I &inc, const T &prop)
@@ -815,9 +955,9 @@ template < > struct IncidenceTrait <Kolab::Event>
 
         if (event.end().isValid()) {
             prop.dtend(fromDate<KolabTypes, KolabType::properties_type::dtend_type>(event.end()));
-        }/* else if (event.duration().isValid()) {
-            //TODO
-        }*/
+        } else if (event.duration().isValid()) {
+            prop.duration(KolabType::properties_type::duration_type(fromDuration(event.duration())));
+        }
         
         if (event.transparency()) {
             prop.transp( KolabType::properties_type::transp_type(TRANSPARENT));
@@ -837,17 +977,15 @@ template < > struct IncidenceTrait <Kolab::Event>
 
         if (prop.dtend()) {
             event.setEnd(*toDate<KolabTypes>(*prop.dtend()));
-//             event.setDtEnd(*toDate<KCalCoreTypes>(*prop.dtend()));
-//             if (event.dtEnd().timeType() != event.dtStart().timeType()) {
-//                 kWarning() << "dtEnd has wrong timespec";
-//             }
+            if (event.end().isUTC() != event.end().isUTC() && 
+                event.end().timezone() != event.end().timezone() &&
+                event.end().isDateOnly() != event.end().isDateOnly()) {
+                std::cerr << "dtEnd has wrong timespec" << std::endl;;
+            }
         } else if (prop.duration()) {
-            //TODO implement
-    //          KCalCore::Duration duration.;
-    //          event.setDuration(duration << prop.duration());
+            event.setDuration(toDuration((*prop.duration()).duration()));
         }
-        //TODO check for equality of timespecs
-        
+
         if (prop.transp()) {
             if (toString(*prop.transp()) == TRANSPARENT) {
                 event.setTransparency(true);
@@ -885,12 +1023,19 @@ template < > struct IncidenceTrait <Kolab::Todo>
         
         getIncidenceProperties<KolabType::properties_type>(prop, todo);
 
+        if (!todo.relatedTo().empty()) {
+            KolabType::properties_type::related_to_sequence list;
+            BOOST_FOREACH(std::string relatedTo, todo.relatedTo()) {
+                list.push_back(KolabType::properties_type::related_to_type(relatedTo));
+            }
+            prop.related_to(list);
+        }
         if (todo.due().isValid()) {
             prop.due(fromDate<KolabTypes, KolabType::properties_type::due_type>(todo.due()));
         }
-//         if (todo.transparency()) {
-//             prop.transp( icalendar_2_0::properties::transp_type(TRANSPARENT));
-//         }
+        if (todo.percentComplete() > 0) {
+            prop.percent_complete(KolabType::properties_type::percent_complete_type(fromInt<icalendar_2_0::IntegerPropertyType::integer_type>(todo.percentComplete())));
+        }
     }
     
     static void addIncidence(icalendar_2_0::VcalendarType::components_type &components, KolabType inc) //TODO to base trait
@@ -904,16 +1049,17 @@ template < > struct IncidenceTrait <Kolab::Todo>
 
         setIncidenceProperties<Kolab::Todo, KolabType::properties_type>(todo, prop);
 
-        if (prop.related_to()) {
-            //TODO
+        if (!prop.related_to().empty()) {
+            BOOST_FOREACH(KolabType::properties_type::related_to_type p, prop.related_to()) {
+                todo.addRelatedTo(p.text());
+            }
         }
         if (prop.due()) {
             todo.setDue(*toDate<KolabTypes>(*prop.due()));
         }
         if (prop.percent_complete()) {
-            //TODO
+            todo.setPercentComplete(toInt(*prop.percent_complete()));
         }
-
     }
      
     static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components)
diff --git a/c++/tests/CMakeLists.txt b/c++/tests/CMakeLists.txt
index c3413f7..9a78104 100644
--- a/c++/tests/CMakeLists.txt
+++ b/c++/tests/CMakeLists.txt
@@ -12,6 +12,10 @@ if (QT4_FOUND AND KDECORE_FOUND AND KCALCORE_FOUND)
     QT4_AUTOMOC(kcalconversiontest.cpp)
     add_executable(kcalconversiontest kcalconversiontest.cpp ${CMAKE_CURRENT_BINARY_DIR}/${KCALCONVERSIONTEST_MOC})
     target_link_libraries(kcalconversiontest ${QT_QTTEST_LIBRARY} ${QT_QTCORE_LIBRARY} kolabxmlkcal ${XERCES_C})
+
+    QT4_AUTOMOC(conversiontest.cpp)
+    add_executable(conversiontest conversiontest.cpp ${CMAKE_CURRENT_BINARY_DIR}/${CONVERSIONTEST_MOC})
+    target_link_libraries(conversiontest ${QT_QTTEST_LIBRARY} ${QT_QTCORE_LIBRARY} kolabxml ${XERCES_C})
 else()
     message(WARNING "Could not build tests because kdepimlibs, kdelibs or qt is missing")
 endif()
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 91a0683..4779def 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -17,84 +17,6 @@
 #include <iostream>
 #include <fstream>
 
-namespace QTest {
-    template<>
-    char *toString(const Kolab::DateTime &dt)
-    {
-        QByteArray ba = "Kolab::DateTime(";
-        ba += QByteArray::number(dt.year()) + ", " + QByteArray::number(dt.month())+ ", " + QByteArray::number(dt.day()) + ", ";
-        ba += QByteArray::number(dt.hour()) + ", " + QByteArray::number(dt.minute()) + ", " + QByteArray::number(dt.second())+ ", ";
-        ba += QString(dt.isUTC()?QString("UTC"):QString("TZ: "+QString::fromStdString(dt.timezone()))).toAscii();
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-
-    template<>
-    char *toString(const std::vector<Kolab::DateTime> &v)
-    {
-        QByteArray ba = "vector<Kolab::DateTime>(";
-        for (int i = 0; i < v.size(); i++) {
-            ba += QByteArray(toString(v.at(i)))+ ", ";
-        }
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-    
-    template<>
-    char *toString(const Kolab::Attendee &a)
-    {
-        QByteArray ba = "Kolab::Attendee(";
-        ba += QString::fromStdString(a.email()).toAscii() + ", " + QString::fromStdString(a.name()).toAscii()+ ", " + 
-        QByteArray::number(a.partStat()) + ", " + QByteArray::number(a.role())  + ", " + QByteArray::number(a.rsvp())  + ", " + 
-        QString::fromStdString(a.uid()).toAscii();
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-    
-    template<>
-    char *toString(const std::vector<Kolab::Attendee> &v)
-    {
-        QByteArray ba = "vector<Kolab::Attendee>(";
-        for (int i = 0; i < v.size(); i++) {
-            ba += QByteArray(toString(v.at(i)))+ ", ";
-        }
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-     
-    template<>
-    char *toString(const std::string &s)
-    {
-        QByteArray ba = "string(";
-        ba += QString::fromStdString(s).toAscii();
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-    
-    template<>
-    char *toString(const std::vector<std::string> &v)
-    {
-        QByteArray ba = "vector<std::string>(";
-        for (int i = 0; i < v.size(); i++) {
-            ba += QByteArray(toString(v.at(i)))+ ", ";
-        }
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-    
-    template<>
-    char *toString(const std::vector<int> &v)
-    {
-        QByteArray ba = "vector<int>(";
-        for (int i = 0; i < v.size(); i++) {
-            ba += QString::number(v.at(i)).toAscii()+ ", ";
-        }
-        ba += ")";
-        return qstrdup(ba.data());
-    }
-
- }
- 
 //TODO remove
 void BindingsTest::writeContact()
 {
@@ -183,6 +105,14 @@ void setIncidence(T &ev)
     list.push_back(1);
     list.push_back(3);
     rule.setBysecond(list);
+    rule.setByminute(list);
+    rule.setByhour(list);
+//     rule.setByday(list); //TODO
+    rule.setBymonthday(list);
+    rule.setByyearday(list);
+    rule.setByweekno(list);
+    rule.setBymonth(list);
+    
     ev.setRecurrenceRule(rule);
     ev.addRecurrenceDate(Kolab::DateTime("US/Eastern", 2006,1,6,12,0,0));
     ev.addExceptionDate(Kolab::DateTime("US/Eastern", 2006,1,6,12,0,0));
@@ -262,6 +192,14 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(r1.count(), r2.count());
     QCOMPARE(r1.end(), r2.end());
     QCOMPARE(r1.bysecond(), r2.bysecond());
+    QCOMPARE(r1.byminute(), r2.byminute());
+    QCOMPARE(r1.byhour(), r2.byhour());
+//     QEXPECT_FAIL("", "Implement Day parser", Continue);
+//     QCOMPARE(r1.byday(), r2.byday());
+    QCOMPARE(r1.bymonthday(), r2.bymonthday());
+    QCOMPARE(r1.byyearday(), r2.byyearday());
+    QCOMPARE(r1.byweekno(), r2.byweekno());
+    QCOMPARE(r1.bymonth(), r2.bymonth());
         
     //Rest
     QCOMPARE(ev.recurrenceDates(), re.recurrenceDates());
@@ -287,11 +225,10 @@ void BindingsTest::eventCompletness()
     Kolab::Event ev;
     setIncidence(ev);
     ev.setEnd(Kolab::DateTime("US/Eastern", 2006,1,8,12,0,0));
-    //TODO duration
     ev.setTransparency(true);
     
     std::string result = Kolab::writeEvent(ev);
-    //std::cout << result << endl;
+    std::cout << result << endl;
     Kolab::Event e = Kolab::readEvent(result, false);
     const Kolab::Event &re = e;
     checkIncidence(ev, re);
@@ -303,18 +240,36 @@ void BindingsTest::eventCompletness()
     
 }
 
+void BindingsTest::eventDuration()
+{
+    Kolab::Event ev;
+    ev.setDuration(Kolab::Duration(11,22,33,44, true));
+
+    const std::string result = Kolab::writeEvent(ev);
+//     std::cout << result << endl;
+    const Kolab::Event e = Kolab::readEvent(result, false);
+    QVERIFY(ev.duration().isValid());
+    QCOMPARE(ev.duration(), e.duration());
+}
+
 void BindingsTest::todoCompletness()
 {
     Kolab::Todo ev;
     setIncidence(ev);
     ev.setDue(Kolab::DateTime("US/Eastern", 2006,1,8,12,0,0));
+    ev.addRelatedTo("rel1");
+    ev.addRelatedTo("rel2");
+    ev.setPercentComplete(50);
     
     std::string result = Kolab::writeTodo(ev);
-//     std::cout << result << endl;
+    std::cout << result << endl;
     Kolab::Todo e = Kolab::readTodo(result, false);
     const Kolab::Todo &re = e;
     checkIncidence(ev, re);
     QCOMPARE(ev.due(), re.due());
+    QCOMPARE(ev.relatedTo(), re.relatedTo());
+    QCOMPARE(ev.percentComplete(), re.percentComplete());
+
     
 }
 
diff --git a/c++/tests/bindingstest.h b/c++/tests/bindingstest.h
index 2146c48..aa140b1 100644
--- a/c++/tests/bindingstest.h
+++ b/c++/tests/bindingstest.h
@@ -4,6 +4,18 @@
 #include <QtCore/QObject>
 #include <QtTest/QtTest>
 
+/*
+ * The test are roundtrip tests, which simply write an object out and read it again. The two objects are then compared for equality.
+ * This assumes that containers are working (comparison operators and adding/removing values).
+ * 
+ * Testing it properly would mean to add loads of testfiles in text so we could:
+ * serialize => compare to text representation
+ * deserialize => check values
+ * 
+ * If we would do this ideally for every property on every type that would result in a lot of work.
+ *
+ */ 
+
 class BindingsTest : public QObject
 {
   Q_OBJECT
@@ -18,6 +30,7 @@ class BindingsTest : public QObject
     void roundtripReverseEvent();
     void BenchmarkRoundtrip();
     void eventCompletness();
+    void eventDuration();
     void todoCompletness();
     
     void BenchmarkRoundtripKolab();
diff --git a/c++/tests/conversiontest.cpp b/c++/tests/conversiontest.cpp
new file mode 100644
index 0000000..df19a75
--- /dev/null
+++ b/c++/tests/conversiontest.cpp
@@ -0,0 +1,54 @@
+#include "conversiontest.h"
+
+#include <QtTest/QtTest>
+#include <lib/xcalconversions.h>
+
+#include "serializers.h"
+
+Q_DECLARE_METATYPE(Kolab::Duration);
+ 
+void ConversionTest::durationParserTest_data()
+{
+    QTest::addColumn<Kolab::Duration>("expected");
+    QTest::addColumn<QString>("string");
+
+    QTest::newRow("Time") << Kolab::Duration(0,2,3,4, false) << "+PT2H3M4S";
+    QTest::newRow("Day") << Kolab::Duration(1,2,3,4, false) << "+P1DT2H3M4S";
+    QTest::newRow("Week") << Kolab::Duration(1, false) << "+P1W";
+    QTest::newRow("Week Multidigit, negative") << Kolab::Duration(23, true) << "-P23W";
+}
+
+void ConversionTest::durationParserTest()
+{
+    QFETCH(QString, string);
+    QFETCH(Kolab::Duration, expected);
+    const Kolab::Duration result = Kolab::toDuration(string.toStdString());
+    QCOMPARE(result, expected);
+}
+
+void ConversionTest::durationSerializerTest_data()
+{
+    QTest::addColumn<QString>("expected");
+    QTest::addColumn<Kolab::Duration>("duration");
+
+    QTest::newRow("Time") << "PT2H3M4S" << Kolab::Duration(0,2,3,4, false);
+    QTest::newRow("Day") << "P1DT2H3M4S" << Kolab::Duration(1,2,3,4, false);
+    QTest::newRow("Week") << "P1W" << Kolab::Duration(1, false);
+    QTest::newRow("Week Multidigit, negative") << "-P23W" << Kolab::Duration(23, true);
+}
+
+void ConversionTest::durationSerializerTest()
+{
+    QFETCH(Kolab::Duration, duration);
+    QFETCH(QString, expected);
+    const std::string result = Kolab::fromDuration(duration);
+    QCOMPARE(QString::fromStdString(result), expected);
+}
+
+
+
+QTEST_MAIN( ConversionTest )
+
+#include "conversiontest.moc"
+
+
diff --git a/c++/tests/conversiontest.h b/c++/tests/conversiontest.h
new file mode 100644
index 0000000..989ec95
--- /dev/null
+++ b/c++/tests/conversiontest.h
@@ -0,0 +1,19 @@
+
+#ifndef CONVERSIONTEST_H
+#define CONVERSIONTEST_H
+
+#include <QObject>
+
+class ConversionTest : public QObject
+{
+  Q_OBJECT
+  private slots:
+
+    void durationParserTest_data();
+    void durationParserTest();
+    
+    void durationSerializerTest_data();
+    void durationSerializerTest();
+};
+
+#endif // CONVERSIONTEST_H
diff --git a/c++/tests/serializers.h b/c++/tests/serializers.h
new file mode 100644
index 0000000..a4d386b
--- /dev/null
+++ b/c++/tests/serializers.h
@@ -0,0 +1,97 @@
+#ifndef SERIALIZERS_H
+#define SERIALIZERS_H
+
+#include <QByteArray>
+#include <QString>
+#include <lib/kolabformat.h>
+
+namespace QTest {
+    template<>
+    char *toString(const Kolab::DateTime &dt)
+    {
+        QByteArray ba = "Kolab::DateTime(";
+        ba += QByteArray::number(dt.year()) + ", " + QByteArray::number(dt.month())+ ", " + QByteArray::number(dt.day()) + ", ";
+        ba += QByteArray::number(dt.hour()) + ", " + QByteArray::number(dt.minute()) + ", " + QByteArray::number(dt.second())+ ", ";
+        ba += QString(dt.isUTC()?QString("UTC"):QString("TZ: "+QString::fromStdString(dt.timezone()))).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+
+    template<>
+    char *toString(const std::vector<Kolab::DateTime> &v)
+    {
+        QByteArray ba = "vector<Kolab::DateTime>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const Kolab::Attendee &a)
+    {
+        QByteArray ba = "Kolab::Attendee(";
+        ba += QString::fromStdString(a.email()).toAscii() + ", " + QString::fromStdString(a.name()).toAscii()+ ", " + 
+        QByteArray::number(a.partStat()) + ", " + QByteArray::number(a.role())  + ", " + QByteArray::number(a.rsvp())  + ", " + 
+        QString::fromStdString(a.uid()).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<Kolab::Attendee> &v)
+    {
+        QByteArray ba = "vector<Kolab::Attendee>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+     
+    template<>
+    char *toString(const std::string &s)
+    {
+        QByteArray ba = "string(";
+        ba += QString::fromStdString(s).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<std::string> &v)
+    {
+        QByteArray ba = "vector<std::string>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<int> &v)
+    {
+        QByteArray ba = "vector<int>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QString::number(v.at(i)).toAscii()+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const Kolab::Duration &dt)
+    {
+        QByteArray ba = "Kolab::Duration(";
+        ba += QByteArray::number(dt.weeks()) + ", " + QByteArray::number(dt.days())+ ", " + QByteArray::number(dt.hours()) + ", ";
+        ba += QByteArray::number(dt.minutes()) + ", " + QByteArray::number(dt.seconds()) + ", " + QByteArray::number(dt.isNegative());
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+
+ }
+ 
+#endif
+ 
\ No newline at end of file
diff --git a/schemas/ical/iCalendar-props.xsd b/schemas/ical/iCalendar-props.xsd
index 86130ab..07d25d6 100644
--- a/schemas/ical/iCalendar-props.xsd
+++ b/schemas/ical/iCalendar-props.xsd
@@ -620,15 +620,15 @@
   </xs:complexType>
     
   <!-- 3.8.4.5 Related-To -->
-  <!-- Before extensions to allow different value types
+<!--   Before extensions to allow different value types-->
   <xs:complexType name="RelatedToPropType">
     <xs:complexContent mixed="false">
       <xs:extension base="xcal:TextPropertyType"/>
     </xs:complexContent>
   </xs:complexType>
-   -->
    
-  <xs:complexType name="RelatedToPropType">
+   
+<!--  <xs:complexType name="RelatedToPropType">
     <xs:complexContent mixed="false">
       <xs:extension base="xcal:BasePropertyType">
         <xs:choice> 
@@ -638,7 +638,7 @@
         </xs:choice>
       </xs:extension>
     </xs:complexContent>
-  </xs:complexType>
+  </xs:complexType>-->
     
   <!-- 3.8.4.6 Uniform Resource Locator -->
   <xs:complexType name="UrlPropType">
diff --git a/schemas/ical/kolabformat-xcal.xsd b/schemas/ical/kolabformat-xcal.xsd
index 3d58fd4..0d7ecc6 100644
--- a/schemas/ical/kolabformat-xcal.xsd
+++ b/schemas/ical/kolabformat-xcal.xsd
@@ -97,7 +97,7 @@
                         <xs:element name="sequence" type="SequencePropType" minOccurs="0"/>
                         <xs:element name="class" type="ClassPropType" minOccurs="0"/>
                         <xs:element name="categories" type="CategoriesPropType" minOccurs="0"/>
-                        <xs:element name="related-to" type="RelatedToPropType" minOccurs="0"/>
+                        <xs:element name="related-to" type="RelatedToPropType" minOccurs="0"  maxOccurs="unbounded"/>
                         <xs:element name="dtstart" type="DtstartPropType" minOccurs="0"/>
                         <xs:element name="due" type="DuePropType" minOccurs="0"/>
                         <xs:element name="rrule" type="RrulePropType" minOccurs="0"/>
@@ -136,7 +136,7 @@
                         <xs:element name="sequence" type="SequencePropType" minOccurs="0"/>
                         <xs:element name="class" type="ClassPropType" minOccurs="0"/>
                         <xs:element name="categories" type="CategoriesPropType" minOccurs="0"/>
-                        <xs:element name="related-to" type="RelatedToPropType" minOccurs="0"/>
+                        <xs:element name="related-to" type="RelatedToPropType" minOccurs="0" maxOccurs="unbounded"/>
                         <xs:element name="dtstart" type="DtstartPropType" minOccurs="0"/>
                         <xs:element name="summary" type="SummaryPropType" minOccurs="0"/>
                         <xs:element name="description" type="DescriptionPropType" minOccurs="0"/>


commit 6b19db33fe30854780de837287e1a52238f4a0e0
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sat Feb 4 16:11:25 2012 +0100

    CustomProperties

diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index e605e2d..89f5d84 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -259,6 +259,10 @@ private:
 };
 
 struct CustomProperty {
+    CustomProperty(const std::string &i, const std::string &v)
+    : identifier(i), value(v) {};
+
+    bool operator==(const CustomProperty &other) const{ return (identifier == other.identifier && value == other.value);};
     std::string identifier;
     std::string value;
 };
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index 2004aa9..048666d 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -506,7 +506,11 @@ void setIncidenceProperties(I &inc, const T &prop)
     }
     
     if (prop.x_custom().size()) {
-        //TODO
+        std::vector<Kolab::CustomProperty> customProperties;
+        BOOST_FOREACH(icalendar_2_0::properties::x_custom_type p, prop.x_custom()) {
+            customProperties.push_back(CustomProperty(p.identifier(), p.value()));
+        }
+        inc.setCustomProperties(customProperties);
     }
 
 }
@@ -763,7 +767,9 @@ void getIncidenceProperties(T &prop, const I &inc)
     }
     
     if (!inc.customProperties().empty()) {
-        //TODO
+        BOOST_FOREACH(const Kolab::CustomProperty &a, inc.customProperties()) {
+            prop.x_custom().push_back(typename properties::x_custom_type(a.identifier, a.value));
+        }
     }
 
 }
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 21d2f7b..91a0683 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -233,7 +233,10 @@ void setIncidence(T &ev)
     attachments.push_back(attach3);
     
     ev.setAttachments(attachments);
-    //x-prop
+    std::vector<Kolab::CustomProperty> properties;
+    properties.push_back(Kolab::CustomProperty("ident", "value"));
+    properties.push_back(Kolab::CustomProperty("ident", "value"));
+    ev.setCustomProperties(properties);
 }
 
 template <typename T>
@@ -274,6 +277,7 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(ev.organizerName(), re.organizerName());
     QCOMPARE(ev.attendees(), re.attendees());
     QCOMPARE(ev.attachments(), re.attachments());
+    QCOMPARE(ev.customProperties(), re.customProperties());
 }
 
 


commit 972afa598d7b2ea2fcc8c69f6761e237defdd9fd
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sat Feb 4 15:51:44 2012 +0100

    test multiple attendees

diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 970eada..21d2f7b 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -201,9 +201,8 @@ void setIncidence(T &ev)
     attendee.setRSVP(true);
     attendee.setUid("uid");
     
-    ev.setAttendees(std::vector<Kolab::Attendee>() << attendee);
-    //Attendee
-    //attach
+    ev.setAttendees(std::vector<Kolab::Attendee>() << attendee << attendee);
+    
     std::vector<Kolab::Attachment> attachments;
     
     Kolab::Attachment attach;


commit 4623b9662aef6fa2d2401edcf8a5e0a15ded2d83
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sat Feb 4 15:50:17 2012 +0100

    x-uid param

diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index d406485..2004aa9 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -461,10 +461,9 @@ void setIncidenceProperties(I &inc, const T &prop)
                     if (const icalendar_2_0::RsvpParamType * p = dynamic_cast<const icalendar_2_0::RsvpParamType*> (&*it)) {
                         a.setRSVP(p->boolean());
                     }
-//                     if (const icalendar_2_0::UriParameterType * p = dynamic_cast<const icalendar_2_0::UriParameterType*> (&*it)) {
-//                         a.setUid(p->);
-//                     }
-//TODO x-uid
+                    if (const icalendar_2_0::XuidParamType * p = dynamic_cast<const icalendar_2_0::XuidParamType*> (&*it)) {
+                        a.setUid(p->text());
+                    }
                 }
             }
             attendees.push_back(a);
@@ -729,10 +728,9 @@ void getIncidenceProperties(T &prop, const I &inc)
                 p.baseParameter().push_back(icalendar_2_0::RsvpParamType(true));
             }
             
-//             if (a.uid().empty()) {
-//                 p.baseParameter().push_back(icalendar_2_0::UidPropType(a.uid()));
-//             }
-//TODO x-uid
+            if (!a.uid().empty()) {
+                p.baseParameter().push_back(icalendar_2_0::XuidParamType(a.uid()));
+            }
 
             attendee.parameters(p);
             prop.attendee().push_back(attendee);
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index f6daa1a..970eada 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -273,7 +273,6 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(ev.location(), re.location());
     QCOMPARE(ev.organizerEmail(), re.organizerEmail());
     QCOMPARE(ev.organizerName(), re.organizerName());
-    QEXPECT_FAIL("", "Implement x-uid", Continue);
     QCOMPARE(ev.attendees(), re.attendees());
     QCOMPARE(ev.attachments(), re.attachments());
 }
diff --git a/schemas/ical/kolabformat-xcal.xsd b/schemas/ical/kolabformat-xcal.xsd
index 2614c5f..3d58fd4 100644
--- a/schemas/ical/kolabformat-xcal.xsd
+++ b/schemas/ical/kolabformat-xcal.xsd
@@ -15,6 +15,13 @@
     </xs:complexType>
     <xs:element name="x-label" type="XlabelParamType" substitutionGroup="baseParameter"/>
 
+    <xs:complexType name="XuidParamType" mixed="false">
+        <xs:complexContent>
+            <xs:extension base="TextParameterType"/>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="x-uid" type="XuidParamType" substitutionGroup="baseParameter"/>
+
 <!--            -->
 
     <xs:complexType name="KolabVersion" >


commit 86455935eb5b667709b3240b2c7fe09dfb110768
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Sat Feb 4 15:49:43 2012 +0100

    Attachments

diff --git a/c++/lib/CMakeLists.txt b/c++/lib/CMakeLists.txt
index e715b36..b48e630 100644
--- a/c++/lib/CMakeLists.txt
+++ b/c++/lib/CMakeLists.txt
@@ -9,7 +9,7 @@ SET_SOURCE_FILES_PROPERTIES(${SCHEMA_SOURCEFILES} PROPERTIES GENERATED 1)
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC " ) #always generate shared libraries with -fPIC
 
 #Library with serialization/deserialization code and kolab-containers
-add_library(kolabxml SHARED kolabformat.cpp kolabcontainers.cpp kolabevent.cpp kolabtodo.cpp utils.cpp ../compiled/XMLParserWrapper.cpp ../compiled/grammar-input-stream.cxx ${SCHEMA_SOURCEFILES})
+add_library(kolabxml SHARED kolabformat.cpp kolabcontainers.cpp kolabevent.cpp kolabtodo.cpp utils.cpp base64.cpp ../compiled/XMLParserWrapper.cpp ../compiled/grammar-input-stream.cxx ${SCHEMA_SOURCEFILES})
 target_link_libraries(kolabxml ${XERCES_C})
 
 #For the core library we can be stricter when compiling. This doesn't work with the auto generated code though.
diff --git a/c++/lib/base64.cpp b/c++/lib/base64.cpp
new file mode 100644
index 0000000..071b05c
--- /dev/null
+++ b/c++/lib/base64.cpp
@@ -0,0 +1,123 @@
+/* 
+   base64.cpp and base64.h
+
+   Copyright (C) 2004-2008 René Nyffenegger
+
+   This source code is provided 'as-is', without any express or implied
+   warranty. In no event will the author be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this source code must not be misrepresented; you must not
+      claim that you wrote the original source code. If you use this source code
+      in a product, an acknowledgment in the product documentation would be
+      appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+      misrepresented as being the original source code.
+
+   3. This notice may not be removed or altered from any source distribution.
+
+   René Nyffenegger rene.nyffenegger at adp-gmbh.ch
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars = 
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+  return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+  std::string ret;
+  int i = 0;
+  int j = 0;
+  unsigned char char_array_3[3];
+  unsigned char char_array_4[4];
+
+  while (in_len--) {
+    char_array_3[i++] = *(bytes_to_encode++);
+    if (i == 3) {
+      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+      char_array_4[3] = char_array_3[2] & 0x3f;
+
+      for(i = 0; (i <4) ; i++)
+        ret += base64_chars[char_array_4[i]];
+      i = 0;
+    }
+  }
+
+  if (i)
+  {
+    for(j = i; j < 3; j++)
+      char_array_3[j] = '\0';
+
+    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+    char_array_4[3] = char_array_3[2] & 0x3f;
+
+    for (j = 0; (j < i + 1); j++)
+      ret += base64_chars[char_array_4[j]];
+
+    while((i++ < 3))
+      ret += '=';
+
+  }
+
+  return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+  int in_len = encoded_string.size();
+  int i = 0;
+  int j = 0;
+  int in_ = 0;
+  unsigned char char_array_4[4], char_array_3[3];
+  std::string ret;
+
+  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+    char_array_4[i++] = encoded_string[in_]; in_++;
+    if (i ==4) {
+      for (i = 0; i <4; i++)
+        char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+      for (i = 0; (i < 3); i++)
+        ret += char_array_3[i];
+      i = 0;
+    }
+  }
+
+  if (i) {
+    for (j = i; j <4; j++)
+      char_array_4[j] = 0;
+
+    for (j = 0; j <4; j++)
+      char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+  }
+
+  return ret;
+}
\ No newline at end of file
diff --git a/c++/lib/base64.h b/c++/lib/base64.h
new file mode 100644
index 0000000..ceb1357
--- /dev/null
+++ b/c++/lib/base64.h
@@ -0,0 +1,4 @@
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
\ No newline at end of file
diff --git a/c++/lib/genericxcalconverions.h b/c++/lib/genericxcalconverions.h
index 190a316..7bf3cff 100644
--- a/c++/lib/genericxcalconverions.h
+++ b/c++/lib/genericxcalconverions.h
@@ -40,6 +40,8 @@ const char* const TZ_PREFIX = "/kolab.org/";
 
 const char* const THISANDFUTURE = "THISANDFUTURE";
 
+const char* const BASE64 = "BASE64";
+
 const char* const NEEDSACTION = "NEEDS-ACTION";
 const char* const COMPLETED = "OPAQUE";
 const char* const INPROCESS = "IN-PROCESS";
diff --git a/c++/lib/kolabcontainers.cpp b/c++/lib/kolabcontainers.cpp
index eb01ed0..ac702e2 100644
--- a/c++/lib/kolabcontainers.cpp
+++ b/c++/lib/kolabcontainers.cpp
@@ -488,8 +488,81 @@ std::vector<Attendee> operator<<(std::vector<Attendee> v, const Attendee &a)
 
 
 
+struct Attachment::Private
+{
+    std::string uri;
+    std::string data;
+    std::string mimetype;
+    std::string label;
+};
+
+Attachment::Attachment()
+:   d(new Attachment::Private)
+{
+}
 
+Attachment::Attachment(const Kolab::Attachment &other)
+:   d(new Attachment::Private)
+{
+    *d = *other.d;
+}
+
+void Attachment::operator=(const Kolab::Attachment &other)
+{
+    *d = *other.d;
+}
+
+Attachment::~Attachment()
+{
+}
+
+bool Attachment::operator==(const Kolab::Attachment &other) const
+{
+    if ( d->uri == other.uri() &&
+        d->data == other.data() &&
+        d->label == other.label() &&
+        d->mimetype == other.mimetype() ) {
+        return true;
+    }
+    return false;
+}
 
+void Attachment::setUri(const std::string &uri, const std::string& mimetype)
+{
+    d->uri = uri;
+    d->mimetype = mimetype;
+}
+
+std::string Attachment::uri() const
+{
+    return d->uri;
+}
+
+std::string Attachment::mimetype() const
+{
+    return d->mimetype;
+}
+
+void Attachment::setLabel(const std::string &label)
+{
+    d->label = label;
+}
+
+std::string Attachment::label() const
+{
+    return d->label;
+}
+
+void Attachment::setData(const std::string &data, const std::string& mimetype)
+{
+    d->data = data;
+    d->mimetype = mimetype;
+}
+
+std::string Attachment::data() const
+{
+    return d->data;
+}
 
 
 
diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index 928448b..e605e2d 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -228,19 +228,34 @@ std::vector<Attendee> operator<<(std::vector<Attendee>, const Attendee&);
 
 
 class Attachment {
-// public:
-//     /**
-//      * If @param embedd is set, the file is embedded during serialization.
-//      */
-//     void setFile(const std::string uri, const std::string &mimetype, bool embedd);
-//     ///Unencodedn binary content, Implies embedded
-//     void setUnencoded(const std::string &, const std::string &mimetype);
+public:
+    Attachment();
+    Attachment(const Kolab::Attachment &);
+    ~Attachment();
+
+    void operator=(const Attachment &);
+    bool operator==(const Attachment &) const;
+
+    void setUri(const std::string &uri, const std::string &mimetype);
+    std::string uri() const;
+
+     ///Un-encoded binary content, Implies embedded, will be encoded
+     void setData(const std::string &, const std::string &mimetype);
+     ///Decoded binary content.
+     std::string data() const;
+     //TODO add possibility to set already encoded data and uri to be embedded as performance/convenience improvement?
 //     ///Base64 encoded binary content, Implies embedded
-//     void setEncoded(const std::string &, const std::string &mimetype);
-//     ///User visible label
-//     void setLabel(const std::string &);
-//TODO
+//      void setEncodedData(const std::string &, const std::string &mimetype);
+
+    std::string mimetype() const;
 
+    ///User visible label
+    void setLabel(const std::string &);
+    std::string label() const;
+
+private:
+    struct Private;
+    boost::scoped_ptr<Private> d;
 };
 
 struct CustomProperty {
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index cd137bd..d406485 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -23,6 +23,7 @@
 #include <boost/foreach.hpp>
 #include "kolabtodo.h"
 #include "utils.h"
+#include "base64.h"
 namespace Kolab {
 
 class Incidence;
@@ -472,7 +473,37 @@ void setIncidenceProperties(I &inc, const T &prop)
     }
     
     if (prop.attach().size()) {
-        //TODO
+        std::vector<Kolab::Attachment> attachments;
+        BOOST_FOREACH(icalendar_2_0::properties::attach_type aProp, prop.attach()) {
+            Kolab::Attachment a;
+            std::string mimetype;
+            if (aProp.parameters()) {
+                const icalendar_2_0::AttachPropType ::parameters_type &parameters = *aProp.parameters();
+                for (icalendar_2_0::AttachPropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) {
+                    if (const icalendar_2_0::FmttypeParamType *p = dynamic_cast<const icalendar_2_0::FmttypeParamType*> (&*it)) {
+                        mimetype = p->text();
+                    }
+                    if (const icalendar_2_0::EncodingParamType *p = dynamic_cast<const icalendar_2_0::EncodingParamType*> (&*it)) {
+                        if (p->text() != BASE64) {
+                            std::cout << "wrong encoding";
+                            continue;
+                        }
+                    }
+                    if (const icalendar_2_0::XlabelParamType *p = dynamic_cast<const icalendar_2_0::XlabelParamType*> (&*it)) {
+                        a.setLabel(p->text());
+                    }
+                }
+            }
+
+            if (aProp.uri()) {
+                a.setUri(*aProp.uri(), mimetype);
+            }
+            if (aProp.binary()) {
+                a.setData(base64_decode(*aProp.binary()), mimetype);
+            }
+            attachments.push_back(a);
+        }
+        inc.setAttachments(attachments);
     }
     
     if (prop.x_custom().size()) {
@@ -710,7 +741,27 @@ void getIncidenceProperties(T &prop, const I &inc)
     }
     
     if (!inc.attachments().empty()) {
-        //TODO
+        
+        BOOST_FOREACH(const Kolab::Attachment &a, inc.attachments()) {
+            typename properties::attach_type attachment;
+            typename properties::attach_type::parameters_type p;
+            
+            p.baseParameter().push_back(icalendar_2_0::FmttypeParamType(a.mimetype()));
+            
+            if (!a.label().empty()) {
+                p.baseParameter().push_back(icalendar_2_0::XlabelParamType(a.label()));
+            }
+            
+            if (!a.uri().empty()) {
+                attachment.uri(a.uri());
+            } else  if (!a.data().empty()) {
+                attachment.binary(base64_encode(reinterpret_cast<const unsigned char*>(a.data().c_str()), a.data().length()));
+                p.baseParameter().push_back(icalendar_2_0::EncodingParamType(BASE64));
+            }
+            
+            attachment.parameters(p);
+            prop.attach().push_back(attachment);
+        }
     }
     
     if (!inc.customProperties().empty()) {
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 7cf085d..f6daa1a 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -14,6 +14,8 @@
 #include <lib/kolabformat.h>
 #include <kdebug.h>
 #include <lib/kolabkcalconversion.h>
+#include <iostream>
+#include <fstream>
 
 namespace QTest {
     template<>
@@ -202,6 +204,36 @@ void setIncidence(T &ev)
     ev.setAttendees(std::vector<Kolab::Attendee>() << attendee);
     //Attendee
     //attach
+    std::vector<Kolab::Attachment> attachments;
+    
+    Kolab::Attachment attach;
+    attach.setData("data????*?*?*?*?*?", "mimetype");
+    attach.setLabel("label");
+    attachments.push_back(attach);
+    
+    Kolab::Attachment attach2;
+    attach2.setUri("../../tests/testfiles/icalEvent.xml", "mimetype");
+    attach2.setLabel("labe2l");
+    attachments.push_back(attach2);
+    
+    Kolab::Attachment attach3;
+    using namespace std;
+    ifstream file ("../../tests/testfiles/icalEvent.xml", ios::in|ios::binary|ios::ate);
+    if (file.is_open()) {
+        int size = file.tellg();
+        char *memblock = new char [size];
+        file.seekg (0, ios::beg);
+        file.read (memblock, size);
+        file.close();
+
+        attach3.setData(string(memblock, size), "mimetype");
+
+        delete[] memblock;
+    }
+    attach3.setLabel("labe3l");
+    attachments.push_back(attach3);
+    
+    ev.setAttachments(attachments);
     //x-prop
 }
 
@@ -243,6 +275,7 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(ev.organizerName(), re.organizerName());
     QEXPECT_FAIL("", "Implement x-uid", Continue);
     QCOMPARE(ev.attendees(), re.attendees());
+    QCOMPARE(ev.attachments(), re.attachments());
 }
 
 
diff --git a/schemas/ical/kolabformat-xcal.xsd b/schemas/ical/kolabformat-xcal.xsd
index 3fe2af5..2614c5f 100644
--- a/schemas/ical/kolabformat-xcal.xsd
+++ b/schemas/ical/kolabformat-xcal.xsd
@@ -6,6 +6,17 @@
 
     <xs:include schemaLocation="iCalendar-props.xsd" />
 
+<!--Custom Parameters-->
+
+    <xs:complexType name="XlabelParamType" mixed="false">
+        <xs:complexContent>
+            <xs:extension base="TextParameterType"/>
+        </xs:complexContent>
+    </xs:complexType>
+    <xs:element name="x-label" type="XlabelParamType" substitutionGroup="baseParameter"/>
+
+<!--            -->
+
     <xs:complexType name="KolabVersion" >
         <xs:complexContent mixed="false">
             <xs:extension base="BasePropertyType">


commit 0ee9d0e06b484dc70b7d7b47fa72f104617e22bc
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Fri Feb 3 22:22:26 2012 +0100

    Attendee implementation with tests

diff --git a/c++/lib/genericxcalconverions.h b/c++/lib/genericxcalconverions.h
index 524a5ae..190a316 100644
--- a/c++/lib/genericxcalconverions.h
+++ b/c++/lib/genericxcalconverions.h
@@ -31,6 +31,7 @@
 
 #include <fstream>
 #include <iostream>
+#include "kolabcontainers.h"
 
 const char* const XCAL_VERSION = "2.0";
 const char* const XCAL_NAMESPACE = "urn:ietf:params:xml:ns:icalendar-2.0";
@@ -52,8 +53,23 @@ const char* const CONFIDENTIAL = "CONFIDENTIAL";
 const char* const PRIVATE = "PRIVATE";
 const char* const PUBLIC = "PUBLIC";
 
+const char* const PARTACCEPTED = "ACCEPTED";
+const char* const PARTDECLINED = "DECLINED";
+const char* const PARTDELEGATED = "DELEGATED";
+const char* const PARTNEEDSACTION = "NEEDS-ACTION";
+const char* const PARTTENTATIVE = "TENTATIVE";
+
+const char* const CHAIR = "CHAIR";
+const char* const NONPARTICIPANT = "NON-PARTICIPANT";
+const char* const OPTIONAL = "OPT-PARTICIPANT";
+const char* const REQUIRED = "REQ-PARTICIPANT";
+
+
 namespace Kolab {
 
+
+//=== Generic ===
+    
 template <typename T>
 int convertToInt(T integer)
 {
@@ -91,6 +107,82 @@ int toInt(const icalendar_2_0::IntegerPropertyType &prop)
     return convertToInt<icalendar_2_0::IntegerPropertyType::integer_type>(prop.integer());
 }
 
+//----------------
+
+//=== Attendee ===
+
+std::string mapPartStat(PartStatus status)
+{
+    switch (status) {
+        case PartAccepted:
+            return PARTACCEPTED;
+        case PartDeclined:
+            return PARTDECLINED;
+        case PartDelegated:
+            return PARTDELEGATED;
+        case PartNeedsAction:
+            return PARTNEEDSACTION;
+        case PartTentative:
+            return PARTTENTATIVE;
+    }
+    std::cout << "PartStat not handled: " << status;
+    return std::string();
+}
+
+PartStatus mapPartStat(const std::string &status)
+{
+    if (status == PARTACCEPTED) {
+        return PartAccepted;
+    } else if (status == PARTDECLINED) {
+        return PartDeclined;
+    } else if (status == PARTDELEGATED) {
+        return PartDelegated;
+    } else if (status == PARTNEEDSACTION) {
+        return PartNeedsAction;
+    } else if (status == PARTTENTATIVE) {
+        return PartTentative;
+    }
+    std::cout << "Unhandled partstatus" << status;
+    return PartNeedsAction;
+}
+
+std::string mapRole(Role status)
+{
+    switch (status) {
+        case Chair:
+            return std::string(CHAIR);
+        case NonParticipant:
+            return NONPARTICIPANT;
+        case Optional:
+            return OPTIONAL;
+        case Required:
+            return REQUIRED;
+    }
+    std::cout << "PartStat not handled: " << status;
+    return std::string();
+}
+
+Role mapRole(const std::string &status)
+{
+    if (status == CHAIR) {
+        return Chair;
+    } else if (status == NONPARTICIPANT) {
+        return NonParticipant;
+    } else if (status == OPTIONAL) {
+        return Optional;
+    } else if (status == REQUIRED) {
+        return Required;
+    }
+    std::cout << "Unhandled Role" << status;
+    return Required;
+}
+
+
+
+
+
+//----------------
+
 ///trait to convert date-time values
 template <typename T> 
 struct DateTimeConverter;
diff --git a/c++/lib/kolabcontainers.cpp b/c++/lib/kolabcontainers.cpp
index 7f9fdaf..eb01ed0 100644
--- a/c++/lib/kolabcontainers.cpp
+++ b/c++/lib/kolabcontainers.cpp
@@ -229,8 +229,6 @@ void RecurrenceRule::operator=(const Kolab::RecurrenceRule &other)
     *d = *other.d;
 }
 
-
-
 RecurrenceRule::~RecurrenceRule()
 {
     
@@ -377,6 +375,119 @@ bool RecurrenceRule::isValid() const
 
 
 
+struct Attendee::Private
+{
+    Private()
+    : partStat(PartNeedsAction),
+    role(Required),
+    rsvp(false){};
+    
+    std::string email;
+    std::string name;
+    PartStatus partStat;
+    std::string uid;
+    Role role;
+    bool rsvp;
+};
+
+Attendee::Attendee(const std::string& email)
+:   d(new Attendee::Private)
+{
+   d->email = email; 
+}
+
+Attendee::Attendee(const Kolab::Attendee &other)
+:   d(new Attendee::Private)
+{
+    *d = *other.d;
+}
+
+void Attendee::operator=(const Kolab::Attendee &other)
+{
+    *d = *other.d;
+}
+
+Attendee::~Attendee()
+{
+    
+}
+
+bool Attendee::operator==(const Kolab::Attendee &other) const
+{
+    if ( d->email == other.email() &&
+        d->name == other.name() &&
+        d->partStat == other.partStat() &&
+        d->uid == other.uid() &&
+        d->role == other.role() &&
+        d->rsvp== other.rsvp()) {
+        return true;
+    }
+    return false;
+}
+
+std::string Attendee::email() const
+{
+    return d->email;
+}
+
+void Attendee::setName(const std::string &name)
+{
+    d->name = name;
+}
+
+std::string Attendee::name() const
+{
+    return d->name;
+}
+
+void Attendee::setPartStat(PartStatus partStat)
+{
+    d->partStat = partStat;
+}
+
+PartStatus Attendee::partStat() const
+{
+    return d->partStat;
+}
+
+void Attendee::setUid(const std::string &uid)
+{
+    d->uid = uid;
+}
+
+std::string Attendee::uid() const
+{
+    return d->uid;
+}
+
+void Attendee::setRole(Role role)
+{
+    d->role = role;
+}
+
+Role Attendee::role() const
+{
+    return d->role;
+}
+
+void Attendee::setRSVP(bool rsvp)
+{
+    d->rsvp = rsvp;
+}
+
+bool Attendee::rsvp() const
+{
+    return d->rsvp;
+}
+
+std::vector<Attendee> operator<<(std::vector<Attendee> v, const Attendee &a)
+{
+    v.push_back(a);
+    return v;
+}
+
+
+
 
 
 
diff --git a/c++/lib/kolabcontainers.h b/c++/lib/kolabcontainers.h
index ca8a7a5..928448b 100644
--- a/c++/lib/kolabcontainers.h
+++ b/c++/lib/kolabcontainers.h
@@ -195,14 +195,38 @@ enum Role {
 };
 
 class Attendee {
-// public:
-//     Attendee(const std::string &email);
-//     void setName(const std::string &);
-//     void setPartStat(PartStatus);
-//     void setUid(const std::string &);
-//     void setRole(Role);
+public:
+    Attendee(const std::string &email);
+    Attendee(const Attendee &);
+    ~Attendee();
+
+    void operator=(const Attendee &);
+    bool operator==(const Attendee &) const;
+
+    std::string email() const;
+
+    void setName(const std::string &);
+    std::string name() const;
+
+    void setPartStat(PartStatus);
+    PartStatus partStat() const;
+
+    void setUid(const std::string &);
+    std::string uid() const;
+
+    void setRole(Role);
+    Role role() const;
+
+    void setRSVP(bool);
+    bool rsvp() const;
+private:
+    struct Private;
+    boost::scoped_ptr<Private> d;
 };
 
+std::vector<Attendee> operator<<(std::vector<Attendee>, const Attendee&);
+
+
 class Attachment {
 // public:
 //     /**
diff --git a/c++/lib/xcalconversions.h b/c++/lib/xcalconversions.h
index e5eb50c..cd137bd 100644
--- a/c++/lib/xcalconversions.h
+++ b/c++/lib/xcalconversions.h
@@ -436,7 +436,39 @@ void setIncidenceProperties(I &inc, const T &prop)
     }
     
     if (prop.attendee().size()) {
-        //TODO
+        std::vector<Kolab::Attendee> attendees;
+        BOOST_FOREACH(icalendar_2_0::properties::attendee_type aProp, prop.attendee()) {
+            Kolab::Attendee a(toString(aProp.cal_address()));
+            if (aProp.parameters()) {
+                const icalendar_2_0::AttendeePropType::parameters_type &parameters = *aProp.parameters();
+                for (icalendar_2_0::AttendeePropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) {
+                    if (const icalendar_2_0::CnParamType * p = dynamic_cast<const icalendar_2_0::CnParamType*> (&*it)) {
+                        a.setName(p->text());
+                    }
+                    if (const icalendar_2_0::PartstatParamType * p = dynamic_cast<const icalendar_2_0::PartstatParamType*> (&*it)) {
+                        PartStatus s = mapPartStat(p->text());
+                        if (s != PartNeedsAction) {
+                            a.setPartStat(s);
+                        }
+                    }
+                    if (const icalendar_2_0::RoleParamType * p = dynamic_cast<const icalendar_2_0::RoleParamType*> (&*it)) {
+                        Role s = mapRole(p->text());
+                        if (s != Required) {
+                            a.setRole(s);
+                        }
+                    }
+                    if (const icalendar_2_0::RsvpParamType * p = dynamic_cast<const icalendar_2_0::RsvpParamType*> (&*it)) {
+                        a.setRSVP(p->boolean());
+                    }
+//                     if (const icalendar_2_0::UriParameterType * p = dynamic_cast<const icalendar_2_0::UriParameterType*> (&*it)) {
+//                         a.setUid(p->);
+//                     }
+//TODO x-uid
+                }
+            }
+            attendees.push_back(a);
+        }
+        inc.setAttendees(attendees);
     }
     
     if (prop.attach().size()) {
@@ -521,6 +553,8 @@ std::auto_ptr< icalendar_2_0::RrulePropType > recurrenceProperty(const Recurrenc
 }
 
 
+
+
 template <typename T, typename I>
 void getIncidenceProperties(T &prop, const I &inc)
 {
@@ -639,8 +673,40 @@ void getIncidenceProperties(T &prop, const I &inc)
         prop.organizer(organizer);
     }
     
-    if (!inc.attendees().empty()) {
-        //TODO
+    if (!inc.attendees().empty()) {        
+        
+        BOOST_FOREACH(const Kolab::Attendee &a, inc.attendees()) {
+            typename properties::attendee_type attendee(a.email());
+            
+            typename properties::attendee_type::parameters_type p;
+            if (!a.name().empty()) {
+                icalendar_2_0::CnParamType name(a.name());
+                p.baseParameter().push_back(name);
+            }
+            
+            std::string stat = mapPartStat(a.partStat());
+            if (!stat.empty()) {
+                p.baseParameter().push_back(icalendar_2_0::PartstatParamType(stat));
+            }
+            
+            std::string r = mapRole(a.role());
+            if (!r.empty()) {
+                p.baseParameter().push_back(icalendar_2_0::RoleParamType(r));
+            }
+            
+            if (a.rsvp()) {
+                p.baseParameter().push_back(icalendar_2_0::RsvpParamType(true));
+            }
+            
+//             if (a.uid().empty()) {
+//                 p.baseParameter().push_back(icalendar_2_0::UidPropType(a.uid()));
+//             }
+//TODO x-uid
+
+            attendee.parameters(p);
+            prop.attendee().push_back(attendee);
+
+        }
     }
     
     if (!inc.attachments().empty()) {
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index f8b5619..7cf085d 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -16,33 +16,91 @@
 #include <lib/kolabkcalconversion.h>
 
 namespace QTest {
-     template<>
-     char *toString(const Kolab::DateTime &dt)
-     {
-         QByteArray ba = "DateTime(";
-         ba += QByteArray::number(dt.year()) + ", " + QByteArray::number(dt.month())+ ", " + QByteArray::number(dt.day()) + ", ";
-         ba += QByteArray::number(dt.hour()) + ", " + QByteArray::number(dt.minute()) + ", " + QByteArray::number(dt.second())+ ", ";
-         ba += QString(dt.isUTC()?QString("UTC"):QString("TZ: "+QString::fromStdString(dt.timezone()))).toAscii();
-         ba += ")";
-         return qstrdup(ba.data());
-     }
+    template<>
+    char *toString(const Kolab::DateTime &dt)
+    {
+        QByteArray ba = "Kolab::DateTime(";
+        ba += QByteArray::number(dt.year()) + ", " + QByteArray::number(dt.month())+ ", " + QByteArray::number(dt.day()) + ", ";
+        ba += QByteArray::number(dt.hour()) + ", " + QByteArray::number(dt.minute()) + ", " + QByteArray::number(dt.second())+ ", ";
+        ba += QString(dt.isUTC()?QString("UTC"):QString("TZ: "+QString::fromStdString(dt.timezone()))).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+
+    template<>
+    char *toString(const std::vector<Kolab::DateTime> &v)
+    {
+        QByteArray ba = "vector<Kolab::DateTime>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const Kolab::Attendee &a)
+    {
+        QByteArray ba = "Kolab::Attendee(";
+        ba += QString::fromStdString(a.email()).toAscii() + ", " + QString::fromStdString(a.name()).toAscii()+ ", " + 
+        QByteArray::number(a.partStat()) + ", " + QByteArray::number(a.role())  + ", " + QByteArray::number(a.rsvp())  + ", " + 
+        QString::fromStdString(a.uid()).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<Kolab::Attendee> &v)
+    {
+        QByteArray ba = "vector<Kolab::Attendee>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
      
     template<>
-     char *toString(const std::string &s)
-     {
-         QByteArray ba = "string(";
-         ba += QString::fromStdString(s).toAscii();
-         ba += ")";
-         return qstrdup(ba.data());
-     }
+    char *toString(const std::string &s)
+    {
+        QByteArray ba = "string(";
+        ba += QString::fromStdString(s).toAscii();
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<std::string> &v)
+    {
+        QByteArray ba = "vector<std::string>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QByteArray(toString(v.at(i)))+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+    
+    template<>
+    char *toString(const std::vector<int> &v)
+    {
+        QByteArray ba = "vector<int>(";
+        for (int i = 0; i < v.size(); i++) {
+            ba += QString::number(v.at(i)).toAscii()+ ", ";
+        }
+        ba += ")";
+        return qstrdup(ba.data());
+    }
+
  }
  
+//TODO remove
 void BindingsTest::writeContact()
 {
     Kolab::Contact contact;
     std::cout << Kolab::writeContact(contact) << std::endl;
 }
 
+//TODO remove
 void BindingsTest::readEvent()
 {
     const Kolab::Event &event = Kolab::readEvent("../../tests/testfiles/icalEvent.xml", true);
@@ -57,6 +115,7 @@ void BindingsTest::readEvent()
     QCOMPARE(event.summary(), std::string("Event #2"));
 }
 
+//TODO remove
 void BindingsTest::writeEvent()
 {
     Kolab::Event event;
@@ -70,6 +129,7 @@ void BindingsTest::writeEvent()
     std::cout << Kolab::writeEvent(event) << std::endl;
 }
 
+//TODO remove
 void BindingsTest::roundtripEvent()
 {
     Kolab::Event event;
@@ -84,6 +144,7 @@ void BindingsTest::roundtripEvent()
     std::cout << result << std::endl;
 }
 
+//TODO remove
 void BindingsTest::roundtripReverseEvent()
 {
     const Kolab::Event &event = Kolab::readEvent("../../tests/testfiles/icalEvent.xml", true);
@@ -130,6 +191,15 @@ void setIncidence(T &ev)
     ev.setStatus(Kolab::Confirmed);
     ev.setLocation("location");
     ev.setOrganizer("email","organizer");
+    
+    Kolab::Attendee attendee("email");
+    attendee.setName("name");
+    attendee.setPartStat(Kolab::PartDelegated);
+    attendee.setRole(Kolab::Chair);
+    attendee.setRSVP(true);
+    attendee.setUid("uid");
+    
+    ev.setAttendees(std::vector<Kolab::Attendee>() << attendee);
     //Attendee
     //attach
     //x-prop
@@ -138,15 +208,17 @@ void setIncidence(T &ev)
 template <typename T>
 void checkIncidence(const T &ev, const T &re)
 {
+    //Common to all
+    
     QCOMPARE(ev.uid(), re.uid());
     QCOMPARE(ev.created(), re.created());
-    QVERIFY(re.lastModified().isValid());
+    QVERIFY(re.lastModified().isValid()); //TODO can we check this better?
     QCOMPARE(ev.sequence(), re.sequence());
     QCOMPARE(ev.classification(), re.classification());
-    QCOMPARE(ev.categories().size(), re.categories().size());
-    QCOMPARE(ev.categories().at(0), re.categories().at(0));
+    QCOMPARE(ev.categories(), re.categories());
     QCOMPARE(ev.start(), re.start());
     
+    //Recurrence
     const Kolab::RecurrenceRule &r1 = ev.recurrenceRule();
     const Kolab::RecurrenceRule &r2 = re.recurrenceRule();
     QCOMPARE(r1.isValid(), r2.isValid());
@@ -155,14 +227,11 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(r1.weekStart(), r2.weekStart());
     QCOMPARE(r1.count(), r2.count());
     QCOMPARE(r1.end(), r2.end());
-    QCOMPARE(r1.bysecond().size(), r2.bysecond().size());
-    QCOMPARE(r1.bysecond().at(0), r2.bysecond().at(0));
-    QCOMPARE(r1.bysecond().at(1), r2.bysecond().at(1));
-    
-    QCOMPARE(ev.recurrenceDates().size(), re.recurrenceDates().size());
-    QCOMPARE(ev.recurrenceDates().at(0), re.recurrenceDates().at(0));
-    QCOMPARE(ev.exceptionDates().size(), re.exceptionDates().size());
-    QCOMPARE(ev.exceptionDates().at(0), re.exceptionDates().at(0));
+    QCOMPARE(r1.bysecond(), r2.bysecond());
+        
+    //Rest
+    QCOMPARE(ev.recurrenceDates(), re.recurrenceDates());
+    QCOMPARE(ev.exceptionDates(), re.exceptionDates());
     QCOMPARE(ev.recurrenceID(), re.recurrenceID());
     QCOMPARE(ev.thisAndFuture(), re.thisAndFuture());
     QCOMPARE(ev.summary(), re.summary());
@@ -172,6 +241,8 @@ void checkIncidence(const T &ev, const T &re)
     QCOMPARE(ev.location(), re.location());
     QCOMPARE(ev.organizerEmail(), re.organizerEmail());
     QCOMPARE(ev.organizerName(), re.organizerName());
+    QEXPECT_FAIL("", "Implement x-uid", Continue);
+    QCOMPARE(ev.attendees(), re.attendees());
 }
 
 
@@ -185,11 +256,14 @@ void BindingsTest::eventCompletness()
     ev.setTransparency(true);
     
     std::string result = Kolab::writeEvent(ev);
-//     std::cout << result << endl;
+    //std::cout << result << endl;
     Kolab::Event e = Kolab::readEvent(result, false);
     const Kolab::Event &re = e;
     checkIncidence(ev, re);
     QCOMPARE(ev.end(), re.end());
+//     QEXPECT_FAIL("", "Will fix in the next release", Continue);
+//     QVERIFY(ev.duration().isValid());
+//     QCOMPARE(ev.duration(), re.duration());
     QCOMPARE(ev.transparency(), re.transparency());
     
 }


commit e842f6a292ecf16a6789ea0180118dc8ea2638e3
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date:   Fri Feb 3 22:21:03 2012 +0100

    Use separate version numbers for implementation and implemented format specification.

diff --git a/c++/lib/genericxcalconverions.h b/c++/lib/genericxcalconverions.h
index 39973fa..524a5ae 100644
--- a/c++/lib/genericxcalconverions.h
+++ b/c++/lib/genericxcalconverions.h
@@ -385,6 +385,8 @@ typename T::IncidencePtr deserializeIncidence(const std::string& s, bool isUrl)
             T::readIncidence(*e, event);
             incidences.push_back(e);
         }
+        
+        //TODO x_kolab_version
 
         //TODO resolve events, exceptions can be identified based on the recurrence-id attribute
 //         foreach (KCalCore::Event * event, events) {
@@ -429,9 +431,9 @@ std::string serializeIncidence(const typename T::IncidenceType &incidence, const
         VcalendarType::components_type components;
         T::addIncidence(components, inc);
         
-        VcalendarType::properties_type::prodid_type prodid(productid+KOLAB_LIBNAME);
+        VcalendarType::properties_type::prodid_type prodid(productid+KOLAB_LIBNAME+KOLAB_LIB_VERSION); //FIXME own version field for lib version
         VcalendarType::properties_type::version_type version(XCAL_VERSION);
-        VcalendarType::properties_type::x_kolab_version_type x_kolab_version(KOLAB_VERSION);
+        VcalendarType::properties_type::x_kolab_version_type x_kolab_version(KOLAB_FORMAT_VERSION);
         
         VcalendarType::properties_type properties(prodid, version, x_kolab_version);
         
diff --git a/c++/lib/global_definitions.h b/c++/lib/global_definitions.h
index 5c2f170..38d31dd 100644
--- a/c++/lib/global_definitions.h
+++ b/c++/lib/global_definitions.h
@@ -19,6 +19,7 @@
 #define GLOBAL_DEFINITIONS_H
 
 const char* const KOLAB_LIBNAME = "libkolabxml";
-const char* const KOLAB_VERSION = "2.9.0";
+const char* const KOLAB_LIB_VERSION = "0.9";
+const char* const KOLAB_FORMAT_VERSION = "2.9.0";
 
 #endif
diff --git a/c++/lib/xcardconversions.h b/c++/lib/xcardconversions.h
index c672f93..9a89863 100644
--- a/c++/lib/xcardconversions.h
+++ b/c++/lib/xcardconversions.h
@@ -81,7 +81,7 @@ std::string serializeCard(const T &card, const std::string prod = std::string())
 
     try {
         vcard_4_0::vcard::uid_type uid(getUID(card.uid()));
-        vcard_4_0::vcard::x_kolab_version_type kolab_version(KOLAB_VERSION);
+        vcard_4_0::vcard::x_kolab_version_type kolab_version(KOLAB_FORMAT_VERSION);
         vcard_4_0::vcard::prodid_type prodid(prod+KOLAB_LIBNAME);
         vcard_4_0::vcard::rev_type rev(fromDateTime(getCurrentTime()));
         vcard_4_0::vcard::kind_type kind(getType<T>());





More information about the commits mailing list