diff --git a/src/xcalconversions.h b/src/xcalconversions.h index 176470c..5303d3d 100644 --- a/src/xcalconversions.h +++ b/src/xcalconversions.h @@ -1,2026 +1,2036 @@ /* * Copyright (C) 2011 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef XCALCONVERSIONS_H #define XCALCONVERSIONS_H #include "global_definitions.h" #include "libkolabxml-version.h" #include #include #include #include #include #include #include #include #include #include #include "kolabcontainers.h" #include "kolabtodo.h" #include "kolabevent.h" #include "kolabjournal.h" #include #include "utils.h" #include "base64.h" #include "shared_conversions.h" namespace Kolab { namespace XCAL { const char* const XCAL_VERSION = "2.0"; const char* const XCAL_NAMESPACE = "urn:ietf:params:xml:ns:icalendar-2.0"; 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 = "COMPLETED"; const char* const COMPLETED_COMPAT = "OPAQUE"; const char* const INPROCESS = "IN-PROCESS"; const char* const CANCELLED = "CANCELLED"; const char* const TENTATIVE = "TENTATIVE"; const char* const CONFIRMED = "CONFIRMED"; const char* const DRAFT = "DRAFT"; const char* const FINAL = "FINAL"; 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 PARTINPROCESS = "IN-PROCESS"; const char* const PARTCOMPLETED = "COMPLETED"; const char* const CHAIR = "CHAIR"; const char* const NONPARTICIPANT = "NON-PARTICIPANT"; const char* const OPTIONAL = "OPT-PARTICIPANT"; const char* const REQUIRED = "REQ-PARTICIPANT"; const char* const DISPLAYALARM = "DISPLAY"; const char* const EMAILALARM = "EMAIL"; const char* const AUDIOALARM = "AUDIO"; const char* const TRANSPARENT = "TRANSPARENT"; const char* const OPAQUE = "OPAQUE"; 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"; const char* const GROUP = "GROUP"; const char* const INDIVIDUAL = "INDIVIDUAL"; const char* const RESOURCE = "RESOURCE"; const char* const UNKNOWN = "UNKNOWN"; const char* const ROOM = "ROOM"; //Alarms const char* const START = "START"; const char* const END = "END"; //Freebusy const char* const BUSY = "BUSY"; const char* const BUSY_TENTATIVE = "BUSY-TENTATIVE"; const char* const BUSY_OUTOFOFFICE = "X-OUT-OF-OFFICE"; using namespace Kolab::Utils; using namespace Kolab::Shared; //=== Generic Conversions === int toInt(const icalendar_2_0::IntegerPropertyType &prop) { return convertToInt(prop.integer()); } std::vector toStringList(const icalendar_2_0::TextListPropertyType &s) { std::vector d; std::copy(s.text().begin(), s.text().end(), std::back_inserter(d)); return d; } template std::auto_ptr fromStringList(const std::vector &list) { std::auto_ptr ptr(new T()); std::copy(list.begin(), list.end(), std::back_inserter(ptr->text())); 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 fromDayPos(const Kolab::DayPos &d) { std::string s; if (d.occurence() != 0) { s.append(boost::lexical_cast(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(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); 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; 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(d.weeks())); s.push_back('W'); } if (d.days() > 0) { s.append(boost::lexical_cast(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(d.hours())); s.push_back('H'); } if (d.minutes() > 0) { s.append(boost::lexical_cast(d.minutes())); s.push_back('M'); } if (d.seconds() > 0) { s.append(boost::lexical_cast(d.seconds())); s.push_back('S'); } } } catch(boost::bad_lexical_cast &) { ERROR("failed to convert duration"); 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(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); return Duration(); } number.clear(); break; case 'M': try { minutes = boost::lexical_cast(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); return Duration(); } number.clear(); break; case 'S': try { seconds = boost::lexical_cast(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); return Duration(); } number.clear(); break; case 'T': break; case 'W': try { weeks = boost::lexical_cast(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); return Duration(); } return Duration(weeks, negative); case 'D': try { days = boost::lexical_cast(number); } catch(boost::bad_lexical_cast &) { ERROR("failed to convert: " + number); return Duration(); } number.clear(); break; case '+': break; case '-': negative = true; break; case 'P': break; - default: - ERROR("failed to convert duration: " + *it); + default: { + std::ostringstream s; + s << "failed to convert duration: " << *it; + ERROR(s.str()); return Duration(); + } } } return Duration(days, hours, minutes, seconds, negative); } template T fromContactReference(const Kolab::ContactReference &c) { T organizer(toMailto(c.email())); typename T::parameters_type p; if (!c.name().empty()) { icalendar_2_0::CnParamType name(c.name()); p.baseParameter().push_back(name); } if (!c.uid().empty()) { icalendar_2_0::DirParamType dir(toURN(c.uid())); p.baseParameter().push_back(dir); } organizer.parameters(p); return organizer; } Kolab::ContactReference toContactReference(const icalendar_2_0::CalAddressPropertyType &cal) { const std::string &email = fromMailto(cal.cal_address());; std::string name; std::string uid; if (cal.parameters()) { for (icalendar_2_0::ArrayOfParameters::baseParameter_const_iterator it((*cal.parameters()).baseParameter().begin()); it != (*cal.parameters()).baseParameter().end(); it++) { if (const icalendar_2_0::CnParamType * tz = dynamic_cast (&*it)) { name = tz->text(); continue; } if (const icalendar_2_0::DirParamType * tz = dynamic_cast (&*it)) { uid = fromURN(tz->uri()); continue; } } } return Kolab::ContactReference(email, name, uid); } template Kolab::Attachment toAttachment(T aProp) { Kolab::Attachment a; std::string mimetype; if (aProp.parameters()) { const icalendar_2_0::AttachPropType ::parameters_type ¶meters = *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 (&*it)) { mimetype = p->text(); } if (const icalendar_2_0::EncodingParamType *p = dynamic_cast (&*it)) { if (p->text() != BASE64) { ERROR("wrong encoding"); return Kolab::Attachment(); } } if (const icalendar_2_0::XlabelParamType *p = dynamic_cast (&*it)) { a.setLabel(p->text()); } } } if (aProp.uri()) { a.setUri(*aProp.uri(), mimetype); } else if (aProp.binary()) { a.setData(base64_decode(*aProp.binary()), mimetype); } else { ERROR("no uri and no data available"); } return a; } 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(a.data().c_str()), static_cast(a.data().length()))); p.baseParameter().push_back(icalendar_2_0::EncodingParamType(BASE64)); } else { ERROR("no uri and no data"); } attachment.parameters(p); return attachment; } //==== cDateTime ==== std::string getTimezone(const icalendar_2_0::ArrayOfParameters ¶meters) { for (icalendar_2_0::DateDatetimePropertyType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) { if (const icalendar_2_0::TzidParamType* tz = dynamic_cast (&*it)) { std::string tzid = tz->text(); if (tzid.find(TZ_PREFIX) != std::string::npos) { tzid.erase(0, strlen(TZ_PREFIX)); } else { WARNING("/kolab.org/ timezone prefix is missing"); } return tzid; } } return std::string(); } cDateTimePtr toDate(const icalendar_2_0::DateDatetimePropertyType &dtProperty) { cDateTimePtr date; if (dtProperty.date_time()) { date = Shared::toDate(*dtProperty.date_time()); } else if (dtProperty.date()) { date = Shared::toDate(*dtProperty.date()); } if (dtProperty.parameters()) { const std::string &tzid = getTimezone(*dtProperty.parameters()); if (tzid.size()) { date->setTimezone(tzid); } } return date; } cDateTimePtr toDate(const icalendar_2_0::UtcDatetimePropertyType &dtProperty) { cDateTimePtr date; if (dtProperty.date_time()) { date = Shared::toDate(*dtProperty.date_time()); } else { //The utc-date-time element shouldn't even exist date = cDateTimePtr(new cDateTime()); ERROR("This element shouldn't even be existing"); //TODO Implement anyways? return date; } date->setUTC(true); return date; } template std::auto_ptr fromDate(const cDateTime &dt) { std::auto_ptr ptr(new I); if (dt.isDateOnly()) { ptr->date(Shared::fromDate(dt)); } else { ptr->date_time(Shared::fromDateTime(dt)); const std::string &timezone = dt.timezone(); if (timezone.size() != 0) { std::string tz(TZ_PREFIX); tz.append(timezone); icalendar_2_0::TzidParamType tzidParam(tz); icalendar_2_0::ArrayOfParameters parameters; parameters.baseParameter().push_back(tzidParam); ptr->parameters(parameters); } } return ptr; } template std::vector toDateTimeList(I datelistProperty) { std::vector list; std::string tzid; if (datelistProperty.parameters()) { tzid = getTimezone(*datelistProperty.parameters()); } if (!datelistProperty.date().empty()) { BOOST_FOREACH(const xml_schema::date &d, datelistProperty.date()) { list.push_back(*Shared::toDate(d)); } } else if (!datelistProperty.date_time().empty()) { BOOST_FOREACH(const xml_schema::date_time &d, datelistProperty.date_time()) { cDateTimePtr date = Shared::toDate(d); if (tzid.size()) { date->setTimezone(tzid); } list.push_back(*date); } } return list; } template std::auto_ptr fromDateTimeList(const std::vector &dtlist) { std::auto_ptr ptr(new I); BOOST_FOREACH(const cDateTime &dt, dtlist) { if (dt.isDateOnly()) { ptr->date().push_back(Shared::fromDate(dt)); } else { ptr->date_time().push_back(Shared::fromDateTime(dt)); } //TODO handle utc } if (!dtlist.empty() && !dtlist.at(0).timezone().empty()) { const std::string &timezone = dtlist.at(0).timezone(); if (timezone.size() != 0) { std::string tz(TZ_PREFIX); tz.append(timezone); icalendar_2_0::TzidParamType tzidParam(tz); icalendar_2_0::ArrayOfParameters parameters; parameters.baseParameter().push_back(tzidParam); ptr->parameters(parameters); } } return ptr; } //---- cDateTime ---- //=== 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; case PartInProcess: return PARTINPROCESS; case PartCompleted: return PARTCOMPLETED; } - ERROR("PartStat not handled: " + status); + std::ostringstream s; + s << "PartStat not handled: " << status; + ERROR(s.str()); 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; } else if (status == PARTINPROCESS) { return PartInProcess; } else if (status == PARTCOMPLETED) { return PartCompleted; } ERROR("PartStat not handled: " + 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; } - ERROR("PartStat not handled: " + status); + std::ostringstream s; + s << "Role not handled: " << status; + ERROR(s.str()); 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; } - ERROR("Unhandled Role " + status); + ERROR("Unhandled status " + status); return Required; } //---------------- //=== Recurrence Rule === typedef std::auto_ptr RecurrencePtr; RecurrenceRule::Frequency mapRecurrenceFrequency(const icalendar_2_0::RecurType::freq_type &freq) { using namespace icalendar_2_0; switch (freq) { case FreqRecurType::YEARLY: return RecurrenceRule::Yearly; case FreqRecurType::MONTHLY: return RecurrenceRule::Monthly; case FreqRecurType::WEEKLY: return RecurrenceRule::Weekly; case FreqRecurType::DAILY: return RecurrenceRule::Daily; case FreqRecurType::HOURLY: return RecurrenceRule::Hourly; case FreqRecurType::MINUTELY: return RecurrenceRule::Minutely; case FreqRecurType::SECONDLY: return RecurrenceRule::Secondly; default: ERROR("invalid unhandled recurrenc type" + freq); } return RecurrenceRule::FreqNone; } icalendar_2_0::RecurType::freq_type mapRecurrenceFrequency(RecurrenceRule::Frequency freq) { using namespace icalendar_2_0; switch (freq) { case RecurrenceRule::Yearly: return FreqRecurType::YEARLY; case RecurrenceRule::Monthly: return FreqRecurType::MONTHLY; case RecurrenceRule::Weekly: return FreqRecurType::WEEKLY; case RecurrenceRule::Daily: return FreqRecurType::DAILY; case RecurrenceRule::Hourly: return FreqRecurType::HOURLY; case RecurrenceRule::Minutely: return FreqRecurType::MINUTELY; case RecurrenceRule::Secondly: return FreqRecurType::SECONDLY; default: ERROR("invalid unhandled recurrenc type"); } return 0; } static void setWeekStart(RecurrencePtr &r, const icalendar_2_0::RecurType::wkst_type &wkst) { using namespace icalendar_2_0; switch (wkst) { case WeekdayRecurType::MO: r->setWeekStart(Kolab::Monday); break; case WeekdayRecurType::TU: r->setWeekStart(Kolab::Tuesday); break; case WeekdayRecurType::WE: r->setWeekStart(Kolab::Wednesday); break; case WeekdayRecurType::TH: r->setWeekStart(Kolab::Thursday); break; case WeekdayRecurType::FR: r->setWeekStart(Kolab::Friday); break; case WeekdayRecurType::SA: r->setWeekStart(Kolab::Saturday); break; case WeekdayRecurType::SU: r->setWeekStart(Kolab::Sunday); break; default: ERROR("invalid unhandled weekday" + wkst); } } static void setByday(RecurrencePtr &r, const icalendar_2_0::RecurType::byday_sequence &list) { std::vector by; for (icalendar_2_0::RecurType::byday_const_iterator it(list.begin()); it != list.end(); it++) { by.push_back(toDayPos(*it)); } r->setByday(by); } template std::vector bylist(const xsd::cxx::tree::sequence &list) { std::vector by; BOOST_FOREACH(const T i, list) { by.push_back(convertToInt(i)); } return by; } RecurrencePtr toRRule(const icalendar_2_0::RecurType &rrule) { using namespace icalendar_2_0; RecurrencePtr r(new RecurrenceRule()); r->setFrequency(mapRecurrenceFrequency(rrule.freq())); if (rrule.until()) { cDateTimePtr date; if ((*rrule.until()).date_time()) { date = Shared::toDate(*(*rrule.until()).date_time()); } else if ((*rrule.until()).date()) { date = Shared::toDate(*(*rrule.until()).date()); } r->setEnd(*date); } else if (rrule.count()) { r->setCount(toInt(*rrule.count())); } if (rrule.interval()) { r->setInterval(toInt(*rrule.interval())); } else { r->setInterval(1); } r->setBysecond(bylist(rrule.bysecond())); r->setByminute(bylist(rrule.byminute())); r->setByhour(bylist(rrule.byhour())); setByday(r, rrule.byday()); r->setBymonthday(bylist(rrule.bymonthday())); r->setByyearday(bylist(rrule.byyearday())); r->setByweekno(bylist(rrule.byweekno())); r->setBymonth(bylist(rrule.bymonth())); if (rrule.wkst()) { setWeekStart(r, *rrule.wkst()); } return r; } //--- Recurrence Rule --- template void setIncidenceProperties(I &inc, const T &prop) { inc.setUid(toString(prop.uid())); inc.setCreated(*toDate(prop.created())); inc.setLastModified(*toDate(prop.dtstamp())); if (prop.sequence()) { inc.setSequence(toInt(*prop.sequence())); } if (prop.class_()) { std::string string(toString(*prop.class_())); Kolab::Classification sec = ClassPublic; if (string == PRIVATE) { sec = ClassPrivate; } else if (string == CONFIDENTIAL) { sec = ClassConfidential; } inc.setClassification(sec); } if (prop.categories()) { inc.setCategories(toStringList(*prop.categories())); } if (prop.dtstart()) { const cDateTimePtr date = toDate(*prop.dtstart()); inc.setStart(*date); } if (prop.summary()) { inc.setSummary(toString(*prop.summary())); } if (prop.description()) { inc.setDescription(toString(*prop.description())); } if (prop.comment()) { inc.setComment(toString(*prop.comment())); } if (prop.status()) { const std::string &status = toString(*prop.status()); if (status == NEEDSACTION) { inc.setStatus(StatusNeedsAction); } else if (status == COMPLETED || status == COMPLETED_COMPAT) { inc.setStatus(StatusCompleted); } else if (status == INPROCESS) { inc.setStatus(StatusInProcess); } else if (status == CANCELLED) { inc.setStatus(StatusCancelled); } else if (status == TENTATIVE) { inc.setStatus(StatusTentative); } else if (status == CONFIRMED) { inc.setStatus(StatusConfirmed); } else if (status == DRAFT) { inc.setStatus(StatusDraft); } else if (status == FINAL) { inc.setStatus(StatusFinal); } else { ERROR("Unhandled status"); } } if (prop.attendee().size()) { std::vector attendees; BOOST_FOREACH(typename T::attendee_type aProp, prop.attendee()) { Kolab::Attendee a; std::string name; if (aProp.parameters()) { const icalendar_2_0::AttendeePropType::parameters_type ¶meters = *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 (&*it)) { name = p->text(); } if (const icalendar_2_0::PartstatParamType * p = dynamic_cast (&*it)) { PartStatus s = mapPartStat(p->text()); if (s != PartNeedsAction) { a.setPartStat(s); } } if (const icalendar_2_0::RoleParamType * p = dynamic_cast (&*it)) { Role s = mapRole(p->text()); if (s != Required) { a.setRole(s); } } if (const icalendar_2_0::RsvpParamType * p = dynamic_cast (&*it)) { a.setRSVP(p->boolean()); } if (const icalendar_2_0::DelegatedToParamType * p = dynamic_cast (&*it)) { std::vector list; BOOST_FOREACH(const icalendar_2_0::CalAddressListParamType::cal_address_type &adr, p->cal_address()) { list.push_back(Shared::toContactReference(adr)); } a.setDelegatedTo(list); } if (const icalendar_2_0::DelegatedFromParamType * p = dynamic_cast (&*it)) { std::vector list; BOOST_FOREACH(const icalendar_2_0::CalAddressListParamType::cal_address_type &adr, p->cal_address()) { list.push_back(Shared::toContactReference(adr)); } a.setDelegatedFrom(list); } if (const icalendar_2_0::CutypeParamType * p = dynamic_cast (&*it)) { if (p->text() == RESOURCE) { a.setCutype(CutypeResource); } else if (p->text() == INDIVIDUAL) { a.setCutype(CutypeIndividual); } else if (p->text() == GROUP) { a.setCutype(CutypeGroup); } else if (p->text() == ROOM) { a.setCutype(CutypeGroup); } else if (p->text() == UNKNOWN) { a.setCutype(CutypeGroup); } else { WARNING("Invalid attendee cutype"); } } } } Kolab::ContactReference ref = toContactReference(aProp); a.setContact(ref); attendees.push_back(a); } inc.setAttendees(attendees); } if (prop.attach().size()) { std::vector attachments; BOOST_FOREACH(typename T::attach_type aProp, prop.attach()) { const Kolab::Attachment &a = toAttachment(aProp); if (!a.isValid()) { ERROR("invalid attachment"); continue; } attachments.push_back(a); } inc.setAttachments(attachments); } if (prop.x_custom().size()) { std::vector customProperties; BOOST_FOREACH(typename T::x_custom_type p, prop.x_custom()) { customProperties.push_back(CustomProperty(p.identifier(), p.value())); } inc.setCustomProperties(customProperties); } } template void setTodoEventProperties(I &inc, const T &prop) { if (prop.rrule()) { RecurrencePtr rrule = toRRule(prop.rrule()->recur()); inc.setRecurrenceRule(*rrule); } if (prop.rdate()) { inc.setRecurrenceDates(toDateTimeList(*prop.rdate())); if (!prop.rdate()->period().empty()) { ERROR("the period element must not be used, ignored."); } } if (prop.exdate()) { inc.setExceptionDates(toDateTimeList(*prop.exdate())); } if (prop.recurrence_id()) { bool thisandfuture = false; if (prop.recurrence_id()->parameters()) { const icalendar_2_0::RecurrenceIdPropType::parameters_type ¶meters = *prop.recurrence_id()->parameters(); for (icalendar_2_0::RecurrenceIdPropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) { if (dynamic_cast (&*it)) { thisandfuture = true; } } } inc.setRecurrenceID(*toDate(*prop.recurrence_id()), thisandfuture); } if (prop.priority()) { inc.setPriority(toInt(*prop.priority())); } if (prop.location()) { inc.setLocation(toString(*prop.location())); } if (prop.organizer()) { inc.setOrganizer(toContactReference(*prop.organizer())); } if (prop.url()) { inc.setUrl((*prop.url()).uri()); } } template T fromList(const std::vector &input) { T list; BOOST_FOREACH(int i, input) { list.push_back(convertToInt(i)); } return list; } std::auto_ptr< icalendar_2_0::RrulePropType > recurrenceProperty(const RecurrenceRule &r) { using namespace icalendar_2_0; std::auto_ptr< RrulePropType > rruleProp(new RrulePropType(mapRecurrenceFrequency(r.frequency()))); RecurPropertyType::recur_type &recur = rruleProp->recur(); const cDateTime &endDate = r.end(); if (endDate.isValid()) { RecurPropertyType::recur_type::until_type until; if (endDate.isDateOnly()) { until.date(Shared::fromDate(endDate)); } else { until.date_time(Shared::fromDateTime(endDate)); } recur.until(until); } else if (r.count() > 0) { recur.count(fromInt(r.count())); } if (r.interval() > 1) { recur.interval(fromInt(r.interval())); } if (!r.bysecond().empty()) { recur.bysecond(fromList(r.bysecond())); } if (!r.byminute().empty()) { recur.byminute(fromList(r.byminute())); } if (!r.byhour().empty()) { recur.byhour(fromList(r.byhour())); } if (!r.byday().empty()) { RecurType::byday_sequence byday; const std::vector &l = r.byday(); BOOST_FOREACH(Kolab::DayPos daypos, l) { byday.push_back(fromDayPos(daypos)); } recur.byday(byday); } if (!r.bymonthday().empty()) { recur.bymonthday(fromList(r.bymonthday())); } if (!r.byyearday().empty()) { recur.byyearday(fromList(r.byyearday())); } if (!r.byweekno().empty()) { recur.byweekno(fromList(r.byweekno())); } if (!r.bymonth().empty()) { recur.bymonth(fromList(r.bymonth())); } return rruleProp; } template void getIncidenceProperties(T &prop, const I &inc) { using namespace icalendar_2_0; typedef T properties; prop.sequence(fromInt(inc.sequence())); switch (inc.classification()) { case Kolab::ClassConfidential: prop.class_(typename properties::class_type(CONFIDENTIAL)); break; case Kolab::ClassPrivate: prop.class_(typename properties::class_type(PRIVATE)); break; default: prop.class_(typename properties::class_type(PUBLIC)); break; } if (!inc.categories().empty()) { prop.categories(*fromStringList(inc.categories())); } if (inc.start().isValid()) { prop.dtstart(fromDate(inc.start())); } if (!inc.summary().empty()) { prop.summary(typename properties::summary_type(inc.summary())); } if (!inc.description().empty()) { prop.description(typename properties::description_type(inc.description())); } if (!inc.comment().empty()) { prop.comment(typename properties::comment_type(inc.comment())); } if (inc.status() != StatusUndefined) { switch (inc.status()) { case StatusNeedsAction: prop.status(typename properties::status_type(NEEDSACTION)); break; case StatusCompleted: prop.status(typename properties::status_type(COMPLETED)); break; case StatusInProcess: prop.status(typename properties::status_type(INPROCESS)); break; case StatusCancelled: prop.status(typename properties::status_type(CANCELLED)); break; case StatusTentative: prop.status(typename properties::status_type(TENTATIVE)); break; case StatusConfirmed: prop.status(typename properties::status_type(CONFIRMED)); break; case StatusDraft: prop.status(typename properties::status_type(DRAFT)); break; case StatusFinal: prop.status(typename properties::status_type(FINAL)); break; - default: - ERROR("unhandled status " + inc.status()); + default: { + std::ostringstream s; + s << "unhandled status " << inc.status(); + ERROR(s.str()); + } } } if (!inc.attendees().empty()) { const std::vector &l = inc.attendees(); BOOST_FOREACH(const Kolab::Attendee &a, l) { const Kolab::ContactReference &c = a.contact(); typename properties::attendee_type attendee = fromContactReference(c); typename properties::attendee_type::parameters_type &p = *attendee.parameters(); 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.delegatedTo().empty()) { icalendar_2_0::DelegatedToParamType delegatedTo; BOOST_FOREACH(const Kolab::ContactReference &ref, a.delegatedTo()) { delegatedTo.cal_address().push_back(CalAddressListParamType::cal_address_type(toMailto(ref.email(), ref.name()))); } p.baseParameter().push_back(delegatedTo); } if (!a.delegatedFrom().empty()) { icalendar_2_0::DelegatedFromParamType delegatedFrom; BOOST_FOREACH(const Kolab::ContactReference &ref, a.delegatedFrom()) { delegatedFrom.cal_address().push_back(CalAddressListParamType::cal_address_type(toMailto(ref.email(), ref.name()))); } p.baseParameter().push_back(delegatedFrom); } if (a.cutype() != CutypeIndividual) { std::string type; switch (a.cutype()) { case CutypeGroup: type = GROUP; break; case CutypeResource: type = RESOURCE; break; case CutypeRoom: type = ROOM; break; case CutypeUnknown: type = UNKNOWN; break; default: WARNING("unknown cutype"); type = INDIVIDUAL; break; } p.baseParameter().push_back(icalendar_2_0::CutypeParamType(type)); } prop.attendee().push_back(attendee); } } if (!inc.attachments().empty()) { const std::vector &l = inc.attachments(); BOOST_FOREACH(const Kolab::Attachment &a, l) { prop.attach().push_back(fromAttachment(a)); } } if (!inc.customProperties().empty()) { const std::vector &l = inc.customProperties(); BOOST_FOREACH(const Kolab::CustomProperty &a, l) { prop.x_custom().push_back(typename properties::x_custom_type(a.identifier, a.value)); } } } template void getTodoEventProperties(T &prop, const I &inc) { using namespace icalendar_2_0; typedef T properties; if (inc.recurrenceRule().isValid()) { const RecurrenceRule &r = inc.recurrenceRule(); prop.rrule(recurrenceProperty(r)); //TODO check if startdate is allDay if recurrence is allDay //TODO check if startdate matches the one of the event (it MUST) } if (!inc.recurrenceDates().empty()) { prop.rdate(fromDateTimeList(inc.recurrenceDates())); } if (!inc.exceptionDates().empty()) { prop.exdate(fromDateTimeList(inc.exceptionDates())); } if (inc.recurrenceID().isValid()) { std::auto_ptr recurrenceId = fromDate(inc.recurrenceID()); if (inc.thisAndFuture()) { if (!recurrenceId->parameters()) { recurrenceId->parameters(typename properties::recurrence_id_type::parameters_type()); } typename properties::recurrence_id_type::parameters_type ¶meters = *recurrenceId->parameters(); //There is maybe already a timezone set icalendar_2_0::RangeParamType range(THISANDFUTURE); parameters.baseParameter().push_back(range); } prop.recurrence_id(recurrenceId); } if (inc.priority() != 0) { prop.priority(typename properties::priority_type(fromInt(inc.priority()))); } if (!inc.location().empty()) { prop.location(typename properties::location_type(inc.location())); } if (inc.organizer().isValid()) { prop.organizer(fromContactReference(inc.organizer())); } if (!inc.url().empty()) { prop.url(typename properties::url_type(inc.url())); } } //=== Alarms === template void setAlarms(typename KolabType::components_type& components, const IncidenceType &incidence) { const std::vector &alarms = incidence.alarms(); BOOST_FOREACH(const Kolab::Alarm &alarm, alarms) { typedef icalendar_2_0::ValarmType::properties_type PropType; PropType::trigger_type trigger; if (alarm.start().isValid()) { if (!alarm.start().isUTC()) { ERROR("alarm start date is not UTC but MUST be UTC"); continue; } trigger.date_time(fromDateTime(alarm.start())); } else { if (!alarm.relativeStart().isValid()) { ERROR("no start and no relativeStart"); 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 p; switch(alarm.type()) { case Kolab::Alarm::DisplayAlarm: p = std::auto_ptr(new PropType(PropType::action_type(DISPLAYALARM), trigger)); p->description(PropType::description_type(alarm.description())); break; case Kolab::Alarm::EMailAlarm: { p = std::auto_ptr(new PropType(PropType::action_type(EMAILALARM), trigger)); p->summary(PropType::summary_type(alarm.summary())); p->description(PropType::description_type(alarm.description())); const std::vector &l = alarm.attendees(); BOOST_FOREACH(const Kolab::ContactReference &attendee, l) { p->attendee().push_back(icalendar_2_0::ContactType(toMailto(attendee.email(), attendee.name()))); } break; } case Kolab::Alarm::AudioAlarm: p = std::auto_ptr(new PropType(PropType::action_type(AUDIOALARM), trigger)); p->description(PropType::description_type(alarm.description())); p->attach(fromAttachment(alarm.audioFile())); break; default: ERROR("invalid alarm"); continue; } if (alarm.duration().isValid()) { p->duration(PropType::duration_type(fromDuration(alarm.duration()))); p->repeat(PropType::repeat_type(fromInt(alarm.numrepeat()))); } components.valarm().push_back(icalendar_2_0::ValarmType(p)); } } template void getAlarms(IncidenceType &incidence, const typename KolabType::components_type &components) { typedef icalendar_2_0::ValarmType::properties_type PropType; std::vector 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()) { ERROR("description is missing"); continue; } alarm = Kolab::Alarm((*prop.description()).text()); } else if (prop.action().text() == EMAILALARM) { std::vector attendees; if (prop.attendee().empty()) { WARNING("No receipents for email alarm"); } for (typename PropType::attendee_const_iterator at(prop.attendee().begin()); at != prop.attendee().end(); at++) { std::string name; const std::string &email = fromMailto((*at).cal_address(), name); attendees.push_back(Kolab::ContactReference(Kolab::ContactReference::EmailReference, email, name)); } if (!prop.description() || !prop.summary()) { ERROR("description or summary is missing"); continue; } alarm = Kolab::Alarm((*prop.summary()).text(), (*prop.description()).text(), attendees); } else if (prop.action().text() == AUDIOALARM) { if (!prop.attach()) { ERROR("audio file is missing"); continue; } const Kolab::Attachment &attach = toAttachment(*prop.attach()); if (!attach.isValid()) { ERROR("audio file is invalid"); continue; } alarm = Kolab::Alarm(attach); } else { ERROR("unknown alarm type " + prop.action().text()); continue; } if (prop.trigger().date_time()) { alarm.setStart(*Shared::toDate(*prop.trigger().date_time())); if (!alarm.start().isUTC()) { ERROR("The start date time must be in UTC "); continue; } } else if (prop.trigger().duration()) { Kolab::Relative relativeTo = Kolab::Start; if (prop.trigger().parameters()) { BOOST_FOREACH(const icalendar_2_0::ArrayOfParameters::baseParameter_type ¶m, (*prop.trigger().parameters()).baseParameter()) { if (const icalendar_2_0::RelatedParamType *rel = dynamic_cast (¶m)) { if (rel->text() == START) { relativeTo = Kolab::Start; } else if (rel->text() == END) { relativeTo = Kolab::End; } else { LOG("relativeTo not specified, default to start "); } } } } alarm.setRelativeStart(toDuration(*prop.trigger().duration()), relativeTo); } else { ERROR("no duration and not starttime "); 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); } //--- Alarms --- ///Trait for incidence properties specialized for Event/Todo/Journal template struct IncidenceTrait; template < > struct IncidenceTrait { typedef icalendar_2_0::KolabEvent KolabType; typedef Kolab::Event IncidenceType; typedef boost::shared_ptr IncidencePtr; static void writeIncidence(icalendar_2_0::KolabEvent& vevent, const Kolab::Event &event) { KolabType::components_type eventComponents; setAlarms(eventComponents, event); if (!eventComponents.valarm().empty()) { vevent.components(eventComponents); } icalendar_2_0::KolabEvent::properties_type &prop = vevent.properties(); getIncidenceProperties(prop, event); getTodoEventProperties(prop, event); if (event.end().isValid()) { prop.dtend(fromDate(event.end())); } else if (event.duration().isValid()) { prop.duration(icalendar_2_0::KolabEvent::properties_type::duration_type(fromDuration(event.duration()))); } if (event.transparency()) { prop.transp( icalendar_2_0::KolabEvent::properties_type::transp_type(TRANSPARENT)); } } static void addIncidence(icalendar_2_0::VcalendarType::components_type &components, icalendar_2_0::KolabEvent inc) //TODO to base trait { components.vevent().push_back(inc); } static void readIncidence(Kolab::Event &event, const icalendar_2_0::KolabEvent& vevent) { const icalendar_2_0::KolabEvent::properties_type &prop = vevent.properties(); if (!prop.dtstart()) { ERROR("Start date is missing, but is mandatory for events"); } setIncidenceProperties(event, prop); setTodoEventProperties(event, prop); if (prop.dtend()) { event.setEnd(*toDate(*prop.dtend())); } else if (prop.duration()) { event.setDuration(toDuration((*prop.duration()).duration())); } if (prop.transp()) { if (toString(*prop.transp()) == TRANSPARENT) { event.setTransparency(true); } else { event.setTransparency(false); if (toString(*prop.transp()) != OPAQUE) { ERROR("wrong transparency value " + toString(*prop.transp())); } } } if (vevent.components()) { getAlarms(event, *vevent.components()); } } static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components) { return components.vevent().begin(); } static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator end(const icalendar_2_0::VcalendarType::components_type &components) { return components.vevent().end(); } static IncidencePtr resolveExceptions(const std::vector &list) { IncidencePtr incidence = *list.begin(); std::vector exceptions; for (std::vector < IncidencePtr >::const_iterator it = list.begin()+1; it != list.end(); it++) { exceptions.push_back(**it); } incidence->setExceptions(exceptions); return incidence; } static void addExceptions(icalendar_2_0::VcalendarType::components_type &components, const Kolab::Event &event, KolabType::properties_type props) { BOOST_FOREACH(const Kolab::Event &exception, event.exceptions()) { KolabType ex(props); writeIncidence(ex, exception); addIncidence(components, ex); } } }; template < > struct IncidenceTrait { typedef icalendar_2_0::KolabTodo KolabType; typedef Kolab::Todo IncidenceType; typedef boost::shared_ptr IncidencePtr; static void writeIncidence(icalendar_2_0::KolabTodo& vevent, const Kolab::Todo &todo) { KolabType::components_type eventComponents; setAlarms(eventComponents, todo); if (!eventComponents.valarm().empty()) { vevent.components(eventComponents); } icalendar_2_0::KolabTodo::properties_type &prop = vevent.properties(); getIncidenceProperties(prop, todo); getTodoEventProperties(prop, todo); if (!todo.relatedTo().empty()) { icalendar_2_0::KolabTodo::properties_type::related_to_sequence list; const std::vector &l = todo.relatedTo(); BOOST_FOREACH(const std::string &relatedTo, l) { list.push_back(icalendar_2_0::KolabTodo::properties_type::related_to_type(relatedTo)); } prop.related_to(list); } if (todo.due().isValid()) { prop.due(fromDate(todo.due())); } if (todo.percentComplete() > 0) { prop.percent_complete(icalendar_2_0::KolabTodo::properties_type::percent_complete_type(fromInt(todo.percentComplete()))); } } static void addIncidence(icalendar_2_0::VcalendarType::components_type &components, icalendar_2_0::KolabTodo inc) //TODO to base trait { components.vtodo().push_back(inc); } static void readIncidence(Kolab::Todo &todo, const icalendar_2_0::KolabTodo& vevent) { const icalendar_2_0::KolabTodo::properties_type &prop = vevent.properties(); setIncidenceProperties(todo, prop); setTodoEventProperties(todo, prop); if (!prop.related_to().empty()) { BOOST_FOREACH(icalendar_2_0::KolabTodo::properties_type::related_to_type p, prop.related_to()) { todo.addRelatedTo(p.text()); } } if (prop.due()) { todo.setDue(*toDate(*prop.due())); } if (prop.percent_complete()) { todo.setPercentComplete(toInt(*prop.percent_complete())); } if (vevent.components()) { getAlarms(todo, *vevent.components()); } } static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components) { return components.vtodo().begin(); } static icalendar_2_0::VcalendarType::components_type::vevent_const_iterator end(const icalendar_2_0::VcalendarType::components_type &components) { return components.vtodo().end(); } static IncidencePtr resolveExceptions(const std::vector &list) { IncidencePtr incidence = *list.begin(); std::vector exceptions; for (std::vector < IncidencePtr >::const_iterator it = list.begin()+1; it != list.end(); it++) { exceptions.push_back(**it); } incidence->setExceptions(exceptions); return incidence; } static void addExceptions(icalendar_2_0::VcalendarType::components_type &components, const Kolab::Todo &event, KolabType::properties_type props) { BOOST_FOREACH(const Kolab::Todo &exception, event.exceptions()) { KolabType ex(props); writeIncidence(ex, exception); addIncidence(components, ex); } } }; template < > struct IncidenceTrait { typedef icalendar_2_0::KolabJournal KolabType; typedef Kolab::Journal IncidenceType; typedef boost::shared_ptr IncidencePtr; static void writeIncidence(icalendar_2_0::KolabJournal& vjournal, const Kolab::Journal &journal) { icalendar_2_0::KolabJournal::properties_type &prop = vjournal.properties(); getIncidenceProperties(prop, journal); } static void addIncidence(icalendar_2_0::VcalendarType::components_type &components, icalendar_2_0::KolabJournal inc) { components.vjournal().push_back(inc); } static void readIncidence(Kolab::Journal &journal, const icalendar_2_0::KolabJournal& vjournal) { const icalendar_2_0::KolabJournal::properties_type &prop = vjournal.properties(); setIncidenceProperties(journal, prop); } static icalendar_2_0::VcalendarType::components_type::vjournal_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components) { return components.vjournal().begin(); } static icalendar_2_0::VcalendarType::components_type::vjournal_const_iterator end(const icalendar_2_0::VcalendarType::components_type &components) { return components.vjournal().end(); } static IncidencePtr resolveExceptions(const std::vector &list) { return *list.begin(); } static void addExceptions(icalendar_2_0::VcalendarType::components_type &, const Kolab::Journal &, KolabType::properties_type) { } }; template < > struct IncidenceTrait { typedef icalendar_2_0::KolabFreebusy KolabType; typedef Kolab::Freebusy IncidenceType; typedef boost::shared_ptr IncidencePtr; static void writeIncidence(icalendar_2_0::KolabFreebusy& vfreebusy, const Kolab::Freebusy &fb) { icalendar_2_0::KolabFreebusy::properties_type &prop = vfreebusy.properties(); if (fb.start().isValid() && fb.end().isValid()) { if ((fb.start().isUTC() || fb.start().isDateOnly()) && (fb.end().isUTC() || fb.end().isDateOnly())) { prop.dtstart(fromDate(fb.start())); prop.dtend(fromDate(fb.end())); } else { WARNING("Start/end is not in UTC, but it MUST be, skipping"); } } if (fb.organizer().isValid()) { prop.organizer(fromContactReference(fb.organizer())); } if (!fb.periods().empty()) { BOOST_FOREACH (const Kolab::FreebusyPeriod &fbPeriod, fb.periods()) { icalendar_2_0::KolabFreebusy::properties_type::freebusy_type fb; icalendar_2_0::BasePropertyType::parameters_type params; std::string fbtype; switch (fbPeriod.type()) { case FreebusyPeriod::Busy: fbtype = BUSY; break; case FreebusyPeriod::Tentative: fbtype = BUSY_TENTATIVE; break; case FreebusyPeriod::OutOfOffice: fbtype = BUSY_OUTOFOFFICE; break; case FreebusyPeriod::Invalid: default: WARNING("Invalid fb type"); continue; } params.baseParameter().push_back(icalendar_2_0::FbtypeParamType(fbtype)); if (!fbPeriod.eventUid().empty() || fbPeriod.eventSummary().empty() || fbPeriod.eventLocation().empty()) { params.baseParameter().push_back(icalendar_2_0::XFBevent(fbPeriod.eventUid(), fbPeriod.eventSummary(), fbPeriod.eventLocation())); } fb.parameters(params); BOOST_FOREACH (const Kolab::Period &period, fbPeriod.periods()) { if (period.start.isDateOnly() || period.end.isDateOnly()) { WARNING("Period is date-only but must be date-time"); continue; } if (!period.start.isUTC() || !period.end.isUTC()) { WARNING("Period is not in UTC, but it MUST be, skipping"); continue; } icalendar_2_0::PeriodType p(Shared::fromDateTime(period.start)); p.end(Shared::fromDateTime(period.end)); fb.period().push_back(p); } prop.freebusy().push_back(fb); } } } static void addIncidence(icalendar_2_0::VcalendarType::components_type &components, icalendar_2_0::KolabFreebusy inc) { components.vfreebusy().push_back(inc); } static void readIncidence(Kolab::Freebusy &freebusy, const icalendar_2_0::KolabFreebusy& vfreebusy) { const icalendar_2_0::KolabFreebusy::properties_type &prop = vfreebusy.properties(); freebusy.setUid(toString(prop.uid())); freebusy.setTimestamp(*toDate(prop.dtstamp())); if (prop.dtstart()) { freebusy.setStart(*toDate(*prop.dtstart())); } if (prop.dtend()) { freebusy.setEnd(*toDate(*prop.dtend())); } if (prop.organizer()) { freebusy.setOrganizer(toContactReference(*prop.organizer())); } if (!prop.freebusy().empty()) { std::vector fbperiods; BOOST_FOREACH(icalendar_2_0::FreebusyPropType aProp, prop.freebusy()) { Kolab::FreebusyPeriod fbPeriod; fbPeriod.setType(Kolab::FreebusyPeriod::Busy); if (aProp.parameters()) { const icalendar_2_0::FreebusyPropType::parameters_type ¶meters = *aProp.parameters(); for (icalendar_2_0::FreebusyPropType::parameters_type::baseParameter_const_iterator it(parameters.baseParameter().begin()); it != parameters.baseParameter().end(); it++) { if (const icalendar_2_0::FbtypeParamType * p = dynamic_cast (&*it)) { if (p->text() == BUSY) { fbPeriod.setType(Kolab::FreebusyPeriod::Busy); } else if (p->text() == BUSY_OUTOFOFFICE) { fbPeriod.setType(Kolab::FreebusyPeriod::OutOfOffice); } else if (p->text() == BUSY_TENTATIVE) { fbPeriod.setType(Kolab::FreebusyPeriod::Tentative); } else { WARNING("Invalid fb type, default to busy"); } } if (const icalendar_2_0::XFBevent * p = dynamic_cast (&*it)) { fbPeriod.setEvent(p->uid(), p->summary(), p->location()); } } } std::vector periods; BOOST_FOREACH(icalendar_2_0::FreebusyPropType::period_type period, aProp.period()) { if (!period.end()) { WARNING("Period end date is required and duration is not supported, skipping period"); continue; } periods.push_back(Kolab::Period(*Shared::toDate(period.start()), *Shared::toDate(*period.end()))); } fbPeriod.setPeriods(periods); fbperiods.push_back(fbPeriod); } freebusy.setPeriods(fbperiods); } } static icalendar_2_0::VcalendarType::components_type::vfreebusy_const_iterator begin(const icalendar_2_0::VcalendarType::components_type &components) { return components.vfreebusy().begin(); } static icalendar_2_0::VcalendarType::components_type::vfreebusy_const_iterator end(const icalendar_2_0::VcalendarType::components_type &components) { return components.vfreebusy().end(); } static IncidencePtr resolveExceptions(const std::vector &list) { return *list.begin(); } static void addExceptions(icalendar_2_0::VcalendarType::components_type &, const Kolab::Freebusy &, KolabType::properties_type) { } }; //////////////////////////////////========================================= template std::string serializeIncidence(const typename T::IncidenceType &incidence, const std::string productid = std::string()) { using namespace icalendar_2_0; typedef typename T::KolabType KolabType; try { typename KolabType::properties_type::uid_type uid( getUID(incidence.uid())); setCreatedUid(uid.text()); typename KolabType::properties_type::dtstamp_type dtstamp; if (incidence.lastModified().isValid()) { dtstamp.date_time(fromDateTime(incidence.lastModified())); } else { dtstamp.date_time(fromDateTime(timestamp())); } typename KolabType::properties_type::created_type created; if (incidence.created().isValid()) { created.date_time(fromDateTime(incidence.created())); } else { created.date_time(fromDateTime(timestamp())); } typename KolabType::properties_type eventProps(uid, created, dtstamp); KolabType inc(eventProps); T::writeIncidence(inc, incidence); VcalendarType::components_type components; T::addIncidence(components, inc); T::addExceptions(components, incidence, eventProps); VcalendarType::properties_type::prodid_type prodid(getProductId(productid)); VcalendarType::properties_type::version_type version(XCAL_VERSION); VcalendarType::properties_type::x_kolab_version_type x_kolab_version(KOLAB_FORMAT_VERSION); VcalendarType::properties_type properties(prodid, version, x_kolab_version); VcalendarType vcalendar(properties, components); IcalendarType icalendar(vcalendar); xml_schema::namespace_infomap map; map[""].name = XCAL_NAMESPACE; std::ostringstream ostringstream; icalendar_2_0::icalendar(ostringstream, icalendar, map); return ostringstream.str(); } catch (const xml_schema::exception& e) { CRITICAL("failed to write Incidence"); } catch (...) { CRITICAL("Unhandled exception"); } return std::string(); } template typename T::IncidencePtr deserializeIncidence(const std::string& s, bool isUrl) { using namespace icalendar_2_0; typedef typename T::IncidencePtr IncidencePtr; typedef typename T::IncidenceType IncidenceType; typedef typename T::KolabType KolabType; try { std::auto_ptr icalendar; if (isUrl) { xsd::cxx::xml::dom::auto_ptr doc = XMLParserWrapper::inst().parseFile(s); if (doc.get()) { icalendar = icalendar_2_0::icalendar(doc); } } else { xsd::cxx::xml::dom::auto_ptr doc = XMLParserWrapper::inst().parseString(s); if (doc.get()) { icalendar = icalendar_2_0::icalendar(doc); } } if (!icalendar.get()) { CRITICAL("Failed to parse calendar!"); return IncidencePtr(); } const icalendar_2_0::VcalendarType &vcalendar = icalendar->vcalendar(); std::vector < IncidencePtr > incidences; for (typename xsd::cxx::tree::sequence< KolabType >::const_iterator it(T::begin(vcalendar.components())); it != T::end(vcalendar.components()); it++) { IncidencePtr e = IncidencePtr(new IncidenceType); const KolabType &event = *it; T::readIncidence(*e, event); incidences.push_back(e); } setProductId( vcalendar.properties().prodid().text() ); setXCalVersion(vcalendar.properties().version().text()); setKolabVersion( vcalendar.properties().x_kolab_version().text() ); if (incidences.empty()) { CRITICAL("no incidence in object"); return IncidencePtr(); } return T::resolveExceptions(incidences); } catch (const xml_schema::exception& e) { std::cerr << e << std::endl; } catch (...) { CRITICAL("Unhandled exception"); } CRITICAL("Failed to read incidence!"); return IncidencePtr(); } template std::string serializeFreebusy(const Kolab::Freebusy &incidence, const std::string productid = std::string()) { using namespace icalendar_2_0; typedef typename T::KolabType KolabType; try { typename KolabType::properties_type::uid_type uid( getUID(incidence.uid())); setCreatedUid(uid.text()); typename KolabType::properties_type::dtstamp_type dtstamp; dtstamp.date_time(fromDateTime(timestamp())); typename KolabType::properties_type eventProps(uid, dtstamp); KolabType inc(eventProps); T::writeIncidence(inc, incidence); VcalendarType::components_type components; T::addIncidence(components, inc); VcalendarType::properties_type::prodid_type prodid(getProductId(productid)); VcalendarType::properties_type::version_type version(XCAL_VERSION); VcalendarType::properties_type::x_kolab_version_type x_kolab_version(KOLAB_FORMAT_VERSION); VcalendarType::properties_type properties(prodid, version, x_kolab_version); VcalendarType vcalendar(properties, components); IcalendarType icalendar(vcalendar); xml_schema::namespace_infomap map; map[""].name = XCAL_NAMESPACE; std::ostringstream ostringstream; icalendar_2_0::icalendar(ostringstream, icalendar, map); return ostringstream.str(); } catch (const xml_schema::exception& e) { std::cerr << e << std::endl; } catch (...) { CRITICAL("Unhandled exception"); } CRITICAL("failed to write Incidence"); return std::string(); } } }//Namespace #endif diff --git a/src/xcardconversions.h b/src/xcardconversions.h index 4efca8f..b88304e 100644 --- a/src/xcardconversions.h +++ b/src/xcardconversions.h @@ -1,1193 +1,1195 @@ /* * Copyright (C) 2011 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef KOLABXCARDCONVERSION_H #define KOLABXCARDCONVERSION_H #include #include #include #include #include #include #include "kolabcontainers.h" #include "global_definitions.h" #include "libkolabxml-version.h" #include "utils.h" #include "kolabcontact.h" #include "shared_conversions.h" namespace Kolab { namespace XCARD { const char* const XCARD_NAMESPACE = "urn:ietf:params:xml:ns:vcard-4.0"; const char* const INDIVIDUAL = "individual"; const char* const GROUP = "group"; const char* const MIME_PGP_KEYS = "application/pgp-keys"; const char* const MIME_PKCS7_MIME = "application/pkcs7-mime"; using namespace Kolab::Utils; template std::string getType(); template <> std::string getType() { return INDIVIDUAL; } template <> std::string getType() { return GROUP; } template xsd::cxx::tree::sequence fromList(const std::vector &input) { xsd::cxx::tree::sequence list; BOOST_FOREACH(const std::string &s, input) { list.push_back(T(s)); } return list; } template xsd::cxx::tree::sequence fromList(const std::vector &input, int preferredIndex) { xsd::cxx::tree::sequence list; int index = 0; BOOST_FOREACH(const std::string &s, input) { T im(s); if(preferredIndex == index) { typename T::parameters_type parameters; parameters.baseParameter().push_back(vcard_4_0::prefParamType(vcard_4_0::prefParamType::integer_default_value())); im.parameters(parameters); } index++; list.push_back(im); } return list; } template std::vector toUriList(const xsd::cxx::tree::sequence &input) { std::vector list; BOOST_FOREACH(const vcard_4_0::UriPropertyType &s, input) { list.push_back(s.uri()); } return list; } template std::vector toTextList(const xsd::cxx::tree::sequence &input) { std::vector list; BOOST_FOREACH(const vcard_4_0::TextPropertyType &s, input) { list.push_back(s.text()); } return list; } std::vector toStringList(const ::xsd::cxx::tree::sequence< ::xml_schema::string > &s) { std::vector d; std::copy(s.begin(), s.end(), std::back_inserter(d)); return d; } ::xsd::cxx::tree::sequence< ::xml_schema::string > fromStringList(const std::vector &s) { ::xsd::cxx::tree::sequence< ::xml_schema::string > d; std::copy(s.begin(), s.end(), std::back_inserter(d)); return d; } std::string fromDate(const cDateTime &dt) { if (!dt.isDateOnly()) { WARNING("fromDate called on date time value"); } std::stringstream s; s.fill('0'); s.width(4); s << dt.year(); s.width(2); s << dt.month(); s.width(2); s << dt.day(); return s.str(); } std::string fromDateTime(const cDateTime &dt) { std::stringstream s; s.fill('0'); s << std::right; s.width(4); s << dt.year(); s.width(2); s << dt.month(); s.width(2); s << dt.day(); s.width(1); if (dt.isDateOnly()) { return s.str(); } s << "T"; s.width(2); s << dt.hour(); s.width(2); s << dt.minute(); s.width(2); s << dt.second(); if (dt.isUTC()) { s << "Z"; } return s.str(); } cDateTime toDateTime(const std::string &input) { int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; bool isUtc = false; try { year = boost::lexical_cast(input.substr(0, 4)); month = boost::lexical_cast(input.substr(4, 2)); day = boost::lexical_cast(input.substr(6, 2)); if (input.size() >= 15) { //Minimum for time if (input.at(8) != 'T') { ERROR("Wrong demiliter"); return cDateTime(); } hour = boost::lexical_cast(input.substr(9, 2)); minute = boost::lexical_cast(input.substr(11, 2)); second = boost::lexical_cast(input.substr(13, 2)); } else { if (input.size() >= 9) { ERROR("Invalid dt " + input); return cDateTime(); } return cDateTime(year, month, day); } if (input.size() >= 16) { if (input.at(15) == 'Z') { isUtc = true; } else { - ERROR("wrong utc char? " + input.at(15)); + std::ostringstream s; + s << "wrong utc char? " << input.at(15); + ERROR(s.str()); return cDateTime(); } } } catch (boost::bad_lexical_cast &c) { ERROR("failed to convert: "+std::string(c.what())); return cDateTime(); } catch (...) { ERROR("failed to convert: unknown exception"); return cDateTime(); } return cDateTime(year, month, day, hour, minute, second, isUtc); } cDateTime toDateTime(const vcard_4_0::DateDatetimePropertyType &prop) { if (prop.date_time()) { return toDateTime(*prop.date_time()); } else if (prop.date()) { return toDateTime(*prop.date()); } ERROR("no date and no datetime"); return cDateTime(); } template T fromDateTime(const Kolab::cDateTime &dt) { T prop; if (dt.isDateOnly()) { prop.date(fromDate(dt)); } else { prop.date_time(fromDateTime(dt)); } return prop; } vcard_4_0::PrefTypeValueType fromCryptoPref(Kolab::Crypto::CryptoPref pref) { switch (pref) { case Kolab::Crypto::Always: return vcard_4_0::CryptoType::encryptpref_type::text_type::Always; case Kolab::Crypto::Ask: return vcard_4_0::CryptoType::encryptpref_type::text_type::Ask; case Kolab::Crypto::IfPossible: return vcard_4_0::CryptoType::encryptpref_type::text_type::IfPossible; case Kolab::Crypto::Never: return vcard_4_0::CryptoType::encryptpref_type::text_type::Never; default: WARNING("unknown encrypt pref"); } return vcard_4_0::CryptoType::encryptpref_type::text_type::Ask; } Kolab::Crypto::CryptoPref toCryptoPref(vcard_4_0::PrefTypeValueType pref) { switch (pref) { case vcard_4_0::CryptoType::encryptpref_type::text_type::Always: return Kolab::Crypto::Always; case vcard_4_0::CryptoType::encryptpref_type::text_type::Ask: return Kolab::Crypto::Ask; case vcard_4_0::CryptoType::encryptpref_type::text_type::IfPossible: return Kolab::Crypto::IfPossible; case vcard_4_0::CryptoType::encryptpref_type::text_type::Never: return Kolab::Crypto::Never; default: WARNING("unknown encrypt pref"); } return Kolab::Crypto::Ask; } vcard_4_0::relatedPropType fromRelated(const Kolab::Related &r) { using namespace vcard_4_0; vcard_4_0::relatedPropType related; if (r.type() == Kolab::Related::Uid) { related.uri(r.uri()); } else { related.text(r.text()); } if (r.relationTypes() != Kolab::Related::NoRelation) { vcard::adr_type::parameters_type::baseParameter_sequence base; vcard::adr_type::parameters_type b; vcard_4_0::typeParamType::text_sequence seq; if (r.relationTypes() & Kolab::Related::Child) { seq.push_back(TypeValueType::child); } if (r.relationTypes() & Kolab::Related::Spouse) { seq.push_back(TypeValueType::spouse); } if (r.relationTypes() & Kolab::Related::Assistant) { seq.push_back(TypeValueType::x_assistant); } if (r.relationTypes() & Kolab::Related::Manager) { seq.push_back(TypeValueType::x_manager); } if (!seq.empty()) { vcard_4_0::typeParamType type; type.text(seq); b.baseParameter().push_back(type); } related.parameters(b); } return related; } Kolab::Related toRelated(const vcard_4_0::relatedPropType &r) { Kolab::Related::DescriptionType type = Kolab::Related::Invalid; std::string textOrUri; if (r.uri()) { type = Kolab::Related::Uid; textOrUri = *r.uri(); } else if (r.text()) { type = Kolab::Related::Text; textOrUri = *r.text(); } else { ERROR("no text and no uri"); return Kolab::Related(); } Kolab::Related related(type, textOrUri); if (r.parameters()) { BOOST_FOREACH(const vcard_4_0::ArrayOfParameters::baseParameter_type ¶m, (*r.parameters()).baseParameter()) { if (const vcard_4_0::typeParamType *rel = dynamic_cast (¶m)) { int types = 0; BOOST_FOREACH(const std::string &s, rel->text()) { if (s == vcard_4_0::TypeValueType(vcard_4_0::TypeValueType::child)) { types |= Kolab::Related::Child; } if (s == vcard_4_0::TypeValueType(vcard_4_0::TypeValueType::spouse)) { types |= Kolab::Related::Spouse; } if (s == vcard_4_0::TypeValueType(vcard_4_0::TypeValueType::x_assistant)) { types |= Kolab::Related::Assistant; } if (s == vcard_4_0::TypeValueType(vcard_4_0::TypeValueType::x_manager)) { types |= Kolab::Related::Manager; } } related.setRelationTypes(types); } } } return related; } vcard_4_0::vcard::adr_type fromAddress(const Kolab::Address &address) { using namespace vcard_4_0; vcard::adr_type a(vcard::adr_type::pobox_type(std::string()/*address.pobox()*/), vcard::adr_type::ext_type(std::string()/*address.ext()*/), vcard::adr_type::street_type(address.street()), vcard::adr_type::locality_type(address.locality()), vcard::adr_type::region_type(address.region()), address.code(), vcard::adr_type::country_type(address.country()) ); vcard::adr_type::parameters_type b; if (address.types()) { vcard_4_0::typeParamType::text_sequence seq; if (address.types() & Kolab::Address::Home) { seq.push_back(TypeValueType::home); } if (address.types() & Kolab::Address::Work) { seq.push_back(TypeValueType::work); } if (!seq.empty()) { vcard_4_0::typeParamType type; type.text(seq); b.baseParameter().push_back(type); } } if (!address.label().empty()) { b.baseParameter().push_back(vcard_4_0::labelParamType(address.label())); } a.parameters(b); return a; } Kolab::Address toAddress(const vcard_4_0::vcard::adr_type &adr, bool *isPreferred = 0) { using namespace vcard_4_0; Address address; if (adr.parameters()) { BOOST_FOREACH(const vcard_4_0::ArrayOfParameters::baseParameter_type ¶m, (*adr.parameters()).baseParameter()) { if (const vcard_4_0::labelParamType *rel = dynamic_cast (¶m)) { address.setLabel(rel->text()); } else if (isPreferred && dynamic_cast (¶m)) { *isPreferred = true; } else if (const vcard_4_0::typeParamType *rel = dynamic_cast (¶m)) { int types = 0; BOOST_FOREACH(const std::string &s, rel->text()) { if (s == TypeValueType(TypeValueType::work)) { types |= Kolab::Telephone::Work; } if (s == TypeValueType(TypeValueType::home)) { types |= Kolab::Telephone::Home; } } address.setTypes(types); } } } address.setCode(adr.code()); address.setCountry(adr.country()); address.setLocality(adr.locality()); address.setRegion(adr.region()); address.setStreet(adr.street()); return address; } std::string toGeoUri(double lat, double lon) { //lexical_cast doesn't work, so we're using stringstream std::stringstream s; s << "geo:"; s.precision(15); //can't use std::numeric_limits::max_digits10 because that's c++ 0x, it should be 17, but that seems to cause rounding problems... no idea why. s << lat << ","; s.precision(15); //Needed to get the right precision somehow... s << lon; return s.str(); } bool fromGeoUri(const std::string &uri, double &lat, double &lon) { if (uri.substr(0,4) != std::string("geo:")) { WARNING("not a geo uri"); return false; } std::size_t pos1 = uri.find(","); if (pos1 == std::string::npos) { WARNING("not a valid geo uri"); return false; } lat = boost::lexical_cast(uri.substr(4, pos1-4)); lon = boost::lexical_cast(uri.substr(pos1+1, uri.size()-pos1-1)); return true; } template void writeCard(vcard_4_0::vcard &vcard, const T &); template <> void writeCard(vcard_4_0::vcard &vcard, const Kolab::Contact &contact) { using namespace vcard_4_0; if (!contact.categories().empty()) { vcard_4_0::vcard::categories_type cat; vcard_4_0::vcard::categories_type::text_sequence seq; const std::vector &l = contact.categories(); BOOST_FOREACH(const std::string &s, l) { seq.push_back(s); } cat.text(seq); vcard.categories(cat); } if (contact.nameComponents().isValid()) { const NameComponents &nc = contact.nameComponents(); vcard::n_type n; n.surname(fromStringList(nc.surnames())); n.given(fromStringList(nc.given())); n.additional(fromStringList(nc.additional())); n.prefix(fromStringList(nc.prefixes())); n.suffix(fromStringList(nc.suffixes())); vcard.n(n); } if (!contact.note().empty()) { vcard.note(vcard::note_type(contact.note())); } if (!contact.freeBusyUrl().empty()) { vcard.fburl(vcard::fburl_type(contact.freeBusyUrl())); } if (!contact.titles().empty()) { vcard.title(fromList(contact.titles())); } if (!contact.affiliations().empty()) { vcard::group_sequence affiliations; const std::vector &l = contact.affiliations(); BOOST_FOREACH(const Affiliation &a, l) { if (a == Affiliation()) { //skip empty ones LOG("skipped empty affiliation"); continue; } affiliationPropType::org_type org; org.text().push_back(a.organisation()); const std::vector &orgUnits = a.organisationalUnits(); BOOST_FOREACH(const std::string &unit, orgUnits) { org.text().push_back(unit); } vcard::group_type group(org); if (!a.logo().empty()) { group.logo(affiliationPropType::logo_type(uriInlineEncoding(a.logo(), a.logoMimetype()))); } group.role(fromList(a.roles())); const std::vector &relateds = a.relateds(); BOOST_FOREACH(const Related &rel, relateds) { group.related().push_back(fromRelated(rel)); } const std::vector
&addersses = a.addresses(); BOOST_FOREACH(const Address &adr, addersses) { group.adr().push_back(fromAddress(adr)); } affiliations.push_back(group); } vcard.group(affiliations); } if (!contact.urls().empty()) { vcard_4_0::vcard::url_sequence urls; const std::vector &l = contact.urls(); BOOST_FOREACH(const Kolab::Url &url, l) { vcard::url_type u(url.url()); if (url.type() == Kolab::Url::Blog) { vcard::adr_type::parameters_type b; vcard_4_0::typeParamType p; p.text().push_back(vcard_4_0::TypeValueType::x_blog); b.baseParameter().push_back(p); u.parameters(b); } urls.push_back(u); } vcard.url(urls); } if (!contact.addresses().empty()) { vcard::adr_sequence adrs; int index = 0; const std::vector
&l = contact.addresses(); BOOST_FOREACH(const Kolab::Address &address, l) { vcard::adr_type a = fromAddress(address); if(contact.addressPreferredIndex() == index) { if (a.parameters()) { (*a.parameters()).baseParameter().push_back(vcard_4_0::prefParamType(vcard_4_0::prefParamType::integer_default_value())); } else { vcard::adr_type::parameters_type b; b.baseParameter().push_back(vcard_4_0::prefParamType(vcard_4_0::prefParamType::integer_default_value())); a.parameters(b); } } index++; adrs.push_back(a); } vcard.adr(adrs); } if (!contact.nickNames().empty()) { vcard::nickname_type::text_sequence textsequence; const std::vector &l = contact.nickNames(); BOOST_FOREACH(const std::string &s, l) { textsequence.push_back(s); } vcard::nickname_type nickName; nickName.text(textsequence); vcard.nickname(nickName); } if (!contact.relateds().empty()) { vcard::related_sequence seq; const std::vector &l = contact.relateds(); BOOST_FOREACH(const Kolab::Related &r, l) { seq.push_back(fromRelated(r)); } vcard.related(seq); } if (contact.bDay().isValid()) { Kolab::cDateTime dt = contact.bDay(); if (dt.isUTC() || !dt.timezone().empty()) { WARNING("Must be local time, local time assumed"); dt.setUTC(false); } vcard.bday(fromDateTime(dt)); } if (contact.anniversary().isValid()) { Kolab::cDateTime dt = contact.anniversary(); if (dt.isUTC() || !dt.timezone().empty()) { WARNING("Must be local time, local time assumed"); dt.setUTC(false); } vcard.anniversary(fromDateTime(dt)); } if (!contact.photo().empty()) { vcard::photo_type photo(vcard_4_0::UriPropertyType::uri_type(uriInlineEncoding(contact.photo(), contact.photoMimetype()))); vcard.photo(photo); } if (contact.gender() != Contact::NotSet) { switch (contact.gender()) { case Contact::NotSpecified: vcard.gender(vcard::gender_type(vcard::gender_type::sex_type::empty)); break; case Contact::Male: vcard.gender(vcard::gender_type(vcard::gender_type::sex_type::M)); break; case Contact::Female: vcard.gender(vcard::gender_type(vcard::gender_type::sex_type::F)); break; default: ERROR("Unhandled gender"); } } if (!contact.languages().empty()) { vcard.lang(fromList(contact.languages())); } if (!contact.telephones().empty()) { vcard::tel_sequence seq; int index = 0; const std::vector &l = contact.telephones(); BOOST_FOREACH(const Kolab::Telephone &t, l) { vcard::tel_type tel(t.number()); vcard_4_0::typeParamType telTypeParam; if (t.types() & Kolab::Telephone::Car) { telTypeParam.text().push_back(TypeValueType::x_car); } if (t.types() & Kolab::Telephone::Cell) { telTypeParam.text().push_back(TypeValueType::cell); } if (t.types() & Kolab::Telephone::Fax) { telTypeParam.text().push_back(TypeValueType::fax); } if (t.types() & Kolab::Telephone::Home) { telTypeParam.text().push_back(TypeValueType::home); } if (t.types() & Kolab::Telephone::Work) { telTypeParam.text().push_back(TypeValueType::work); } if (t.types() & Kolab::Telephone::Text) { telTypeParam.text().push_back(TypeValueType::text); } if (t.types() & Kolab::Telephone::Voice) { telTypeParam.text().push_back(TypeValueType::voice); } if (t.types() & Kolab::Telephone::Video) { telTypeParam.text().push_back(TypeValueType::video); } if (t.types() & Kolab::Telephone::Textphone) { telTypeParam.text().push_back(TypeValueType::textphone); } if (t.types() & Kolab::Telephone::Pager) { telTypeParam.text().push_back(TypeValueType::pager); } vcard::tel_type::parameters_type params; if(contact.telephonesPreferredIndex() == index) { params.baseParameter().push_back(vcard_4_0::prefParamType(vcard_4_0::prefParamType::integer_default_value())); } index++; if (!telTypeParam.text().empty()) { params.baseParameter().push_back(telTypeParam); tel.parameters(params); } seq.push_back(tel); } vcard.tel(seq); } if (!contact.imAddresses().empty()) { vcard.impp(fromList(contact.imAddresses(), contact.imAddressPreferredIndex())); } if (!contact.emailAddresses().empty()) { vcard::email_sequence seq; int index = 0; const std::vector &l = contact.emailAddresses(); BOOST_FOREACH(const Kolab::Email &e, l) { vcard::email_type email(e.address()); vcard_4_0::typeParamType emailTypeParam; if (e.types() & Kolab::Email::Home) { emailTypeParam.text().push_back(TypeValueType::home); } if (e.types() & Kolab::Email::Work) { emailTypeParam.text().push_back(TypeValueType::work); } vcard::tel_type::parameters_type params; if (!emailTypeParam.text().empty()) { params.baseParameter().push_back(emailTypeParam); } if(contact.emailAddressPreferredIndex() == index) { params.baseParameter().push_back(vcard_4_0::prefParamType(vcard_4_0::prefParamType::integer_default_value())); } index++; if (!params.baseParameter().empty()) { email.parameters(params); } seq.push_back(email); } vcard.email(seq); } if (!contact.gpsPos().empty()) { vcard_4_0::vcard::geo_sequence list; const std::vector &l = contact.gpsPos(); BOOST_FOREACH(const Kolab::Geo &g, l) { list.push_back(vcard_4_0::vcard::geo_type(toGeoUri(g.latitude, g.longitude))); } vcard.geo(list); } if (contact.crypto().isValid()) { vcard::x_crypto_type crypto; const Kolab::Crypto &c = contact.crypto(); if (c.allowed()) { vcard::x_crypto_type::allowed_type::text_sequence seq; if (c.allowed() & Kolab::Crypto::PGPinline) { seq.push_back(vcard::x_crypto_type::allowed_type::text_type::PGP_INLINE); } if (c.allowed() & Kolab::Crypto::PGPmime) { seq.push_back(vcard::x_crypto_type::allowed_type::text_type::PGP_MIME); } if (c.allowed() & Kolab::Crypto::SMIME) { seq.push_back(vcard::x_crypto_type::allowed_type::text_type::S_MIME); } if (c.allowed() & Kolab::Crypto::SMIMEopaque) { seq.push_back(vcard::x_crypto_type::allowed_type::text_type::S_MIMEOpaque); } vcard::x_crypto_type::allowed_type allowed; allowed.text(seq); crypto.allowed(allowed); } crypto.encryptpref(fromCryptoPref(c.encryptPref())); crypto.signpref(fromCryptoPref(c.signPref())); vcard.x_crypto(crypto); } if (!contact.keys().empty()) { vcard_4_0::vcard::key_sequence keys; const std::vector &l = contact.keys(); BOOST_FOREACH (const Kolab::Key &k, l) { switch (k.type()) { case Kolab::Key::PGP: keys.push_back(vcard_4_0::keyPropType(uriInlineEncoding(k.key(), MIME_PGP_KEYS))); break; case Kolab::Key::PKCS7_MIME: keys.push_back(vcard_4_0::keyPropType(uriInlineEncoding(k.key(), MIME_PKCS7_MIME))); break; default: LOG("Unhandled/Invalid keytype"); break; } } if (!keys.empty()) { vcard.key(keys); } } } template <> void writeCard(vcard_4_0::vcard &vcard, const Kolab::DistList &distlist) { if (!distlist.members().empty()) { vcard_4_0::vcard::member_sequence members; const std::vector &l = distlist.members(); BOOST_FOREACH (const Kolab::ContactReference &m, l) { if (!m.uid().empty()) { members.push_back(vcard_4_0::vcard::member_type(Shared::toURN(m.uid()))); } else { members.push_back(vcard_4_0::vcard::member_type(Shared::toMailto(m.email(), m.name()))); } } vcard.member(members); } } template std::string serializeCard(const T &card, const std::string prod = std::string()) { using namespace vcard_4_0; clearErrors(); try { vcard_4_0::vcard::uid_type uid(Shared::toURN(getUID(card.uid()))); setCreatedUid(Shared::fromURN(uid.uri())); vcard_4_0::vcard::x_kolab_version_type kolab_version(KOLAB_FORMAT_VERSION); vcard_4_0::vcard::prodid_type prodid(getProductId(prod)); vcard_4_0::vcard::rev_type rev(fromDateTime(timestamp())); vcard_4_0::vcard::kind_type kind(getType()); vcard_4_0::vcard::fn_type fn(card.name()); vcard_4_0::vcard vcard(uid, kolab_version, prodid, rev, kind, fn); writeCard(vcard, card); if (!card.customProperties().empty()) { const std::vector &l = card.customProperties(); BOOST_FOREACH(const Kolab::CustomProperty &a, l) { vcard.x_custom().push_back(vcard_4_0::CustomType(a.identifier, a.value)); } } VcardsType vcards(vcard); xml_schema::namespace_infomap map; map[""].name = XCARD_NAMESPACE; std::ostringstream ostringstream; vcard_4_0::vcards(ostringstream, vcards, map); return ostringstream.str(); } catch (const xml_schema::exception& e) { std::cerr << e << std::endl; } catch (...) { CRITICAL("Unhandled exception"); } CRITICAL("Failed to write vcard!"); return std::string(); } template boost::shared_ptr readCard(const vcard_4_0::VcardsType::vcard_type &vcard); template <> boost::shared_ptr readCard (const vcard_4_0::VcardsType::vcard_type &vcard) { using namespace vcard_4_0; boost::shared_ptr contact(new Kolab::Contact); if (vcard.categories()) { contact->setCategories(toStringList((*vcard.categories()).text())); } if (vcard.n()) { NameComponents nc; nc.setSurnames(toStringList((*vcard.n()).surname())); nc.setGiven(toStringList((*vcard.n()).given())); nc.setPrefixes(toStringList((*vcard.n()).prefix())); nc.setSuffixes(toStringList((*vcard.n()).suffix())); nc.setAdditional(toStringList((*vcard.n()).additional())); contact->setNameComponents(nc); } if (vcard.note()) { contact->setNote((*vcard.note()).text()); } if (vcard.fburl()) { contact->setFreeBusyUrl((*vcard.fburl()).uri()); } if (!vcard.title().empty()) { contact->setTitles(toTextList(vcard.title())); } if (!vcard.group().empty()) { std::vector list; BOOST_FOREACH (const vcard::group_type &group, vcard.group()) { Kolab::Affiliation aff; if (!group.org().text().empty()) { aff.setOrganisation(*group.org().text().begin()); std::vector units; for ( vcard_4_0::NonEmptyTextListPropertyType::text_const_iterator it = ++group.org().text().begin(); it != group.org().text().end(); it++) { units.push_back(*it); } aff.setOrganisationalUnits(units); } else { WARNING("No org present"); } std::string mimetype; if (group.logo()) { const std::string &logo = uriInlineDecoding((*group.logo()).uri(), mimetype); aff.setLogo(logo, mimetype); } aff.setRoles(toTextList(group.role())); std::vector relateds; BOOST_FOREACH(const vcard::group_type::related_type &rel, group.related()) { relateds.push_back(toRelated(rel)); } aff.setRelateds(relateds); std::vector
addresses; BOOST_FOREACH(const vcard::group_type::adr_type &adr, group.adr()) { addresses.push_back(toAddress(adr)); } aff.setAddresses(addresses); list.push_back(aff); } contact->setAffiliations(list); } if (!vcard.url().empty()) { std::vector urls; BOOST_FOREACH(const vcard_4_0::vcard::url_type &url, vcard.url()) { if (url.parameters()) { //We have only one fixed parameter (x-blog) urls.push_back(Kolab::Url(url.uri(), Kolab::Url::Blog)); } else { urls.push_back(Kolab::Url(url.uri())); } } contact->setUrls(urls); } if (!vcard.adr().empty()) { std::vector list; int preferredIndex = -1; int index = 0; BOOST_FOREACH(const vcard::adr_type &adr, vcard.adr()) { bool isPreferred = false; const Kolab::Address &address = toAddress(adr, &isPreferred); if (isPreferred) { preferredIndex = index; } index++; list.push_back(address); } contact->setAddresses(list, preferredIndex); } if (vcard.nickname()) { contact->setNickNames(toTextList((*vcard.nickname()).text())); } if (!vcard.related().empty()) { std::vector list; BOOST_FOREACH(const vcard_4_0::vcard::related_type &r, vcard.related()) { list.push_back(toRelated(r)); } contact->setRelateds(list); } if (vcard.bday()) { contact->setBDay(toDateTime(*vcard.bday())); } if (vcard.anniversary()) { contact->setAnniversary(toDateTime(*vcard.anniversary())); } if (vcard.photo()) { std::string mimetype; const std::string decodedPhoto = uriInlineDecoding((*vcard.photo()).uri(), mimetype); contact->setPhoto(decodedPhoto, mimetype); } if (vcard.gender()) { if ((*vcard.gender()).sex() == vcard::gender_type::sex_type::empty) { contact->setGender(Kolab::Contact::NotSpecified); } else if ((*vcard.gender()).sex() == vcard::gender_type::sex_type::M) { contact->setGender(Kolab::Contact::Male); } else if ((*vcard.gender()).sex() == vcard::gender_type::sex_type::F) { contact->setGender(Kolab::Contact::Female); } } if (!vcard.lang().empty()) { std::vector list; BOOST_FOREACH(const vcard::lang_type l, vcard.lang()) { list.push_back(l.language_tag()); } contact->setLanguages(list); } if (!vcard.tel().empty()) { std::vector list; int preferredIndex = -1; int index = 0; BOOST_FOREACH(const vcard::tel_type &tel, vcard.tel()) { Kolab::Telephone telephone; if (tel.parameters()) { BOOST_FOREACH(const vcard_4_0::ArrayOfParameters::baseParameter_type ¶m, (*tel.parameters()).baseParameter()) { if (dynamic_cast (¶m)) { preferredIndex = index; } else if (const vcard_4_0::typeParamType *rel = dynamic_cast (¶m)) { int types = 0; BOOST_FOREACH(const std::string &s, rel->text()) { if (s == TypeValueType(TypeValueType::work)) { types |= Kolab::Telephone::Work; } if (s == TypeValueType(TypeValueType::home)) { types |= Kolab::Telephone::Home; } if (s == TypeValueType(TypeValueType::text)) { types |= Kolab::Telephone::Text; } if (s == TypeValueType(TypeValueType::voice)) { types |= Kolab::Telephone::Voice; } if (s == TypeValueType(TypeValueType::fax)) { types |= Kolab::Telephone::Fax; } if (s == TypeValueType(TypeValueType::cell)) { types |= Kolab::Telephone::Cell; } if (s == TypeValueType(TypeValueType::video)) { types |= Kolab::Telephone::Video; } if (s == TypeValueType(TypeValueType::pager)) { types |= Kolab::Telephone::Pager; } if (s == TypeValueType(TypeValueType::textphone)) { types |= Kolab::Telephone::Textphone; } if (s == TypeValueType(TypeValueType::x_car)) { types |= Kolab::Telephone::Car; } } telephone.setTypes(types); } } } index++; telephone.setNumber(tel.text()); list.push_back(telephone); } contact->setTelephones(list, preferredIndex); } if (!vcard.impp().empty()) { int preferredIndex = -1; std::vector list; int i = 0; BOOST_FOREACH(const vcard_4_0::UriPropertyType &s, vcard.impp()) { if (s.parameters()) { BOOST_FOREACH(const vcard_4_0::ArrayOfParameters::baseParameter_type ¶m, (*s.parameters()).baseParameter()) { if (dynamic_cast (¶m)) { preferredIndex = i; } } } i++; list.push_back(s.uri()); } contact->setIMaddresses(list, preferredIndex); } if (!vcard.email().empty()) { int preferredIndex = -1; std::vector list; int i = 0; BOOST_FOREACH(const vcard_4_0::TextPropertyType &s, vcard.email()) { Kolab::Email email; if (s.parameters()) { BOOST_FOREACH(const vcard_4_0::ArrayOfParameters::baseParameter_type ¶m, (*s.parameters()).baseParameter()) { if (dynamic_cast (¶m)) { preferredIndex = i; } else if (const vcard_4_0::typeParamType *rel = dynamic_cast (¶m)) { int types = 0; BOOST_FOREACH(const std::string &s, rel->text()) { if (s == TypeValueType(TypeValueType::work)) { types |= Kolab::Email::Work; } if (s == TypeValueType(TypeValueType::home)) { types |= Kolab::Email::Home; } } email.setTypes(types); } } } i++; email.setAddress(s.text()); list.push_back(email); } contact->setEmailAddresses(list, preferredIndex); } if (!vcard.geo().empty()) { std::vector list; BOOST_FOREACH(const vcard_4_0::geoPropType &s, vcard.geo()) { double lon = 0.0; double lat = 0.0; if (fromGeoUri(s.uri(), lat, lon)) { list.push_back(Kolab::Geo(lat, lon)); } } contact->setGPSpos(list); } if (vcard.x_crypto()) { const vcard_4_0::vcard::x_crypto_type &crypto = *vcard.x_crypto(); Kolab::Crypto c; if (crypto.allowed()) { int allowed = 0; BOOST_FOREACH(const vcard_4_0::vcard::x_crypto_type::allowed_type::text_type &m, crypto.allowed()->text()) { if (m == vcard::x_crypto_type::allowed_type::text_type::PGP_INLINE) { allowed |= Kolab::Crypto::PGPinline; } else if (m == vcard::x_crypto_type::allowed_type::text_type::PGP_MIME) { allowed |= Kolab::Crypto::PGPmime; } else if (m == vcard::x_crypto_type::allowed_type::text_type::S_MIME) { allowed |= Kolab::Crypto::SMIME; } else if (m == vcard::x_crypto_type::allowed_type::text_type::S_MIMEOpaque) { allowed |= Kolab::Crypto::SMIMEopaque; } else { WARNING("unknown allowed property"); } } c.setAllowed(allowed); } if (crypto.encryptpref()) { c.setEncryptPref(toCryptoPref(crypto.encryptpref()->text())); } if (crypto.signpref()) { c.setSignPref(toCryptoPref(crypto.signpref()->text())); } contact->setCrypto(c); } if (!vcard.key().empty()) { std::string mimetype; std::vector keys; BOOST_FOREACH(const vcard_4_0::keyPropType &k, vcard.key()) { const std::string &key = uriInlineDecoding(k.uri(), mimetype); if (mimetype == MIME_PGP_KEYS) { keys.push_back(Kolab::Key(key, Kolab::Key::PGP)); } else if (mimetype == MIME_PKCS7_MIME) { keys.push_back(Kolab::Key(key, Kolab::Key::PKCS7_MIME)); } else { WARNING("wrong mimetype on key"); } } contact->setKeys(keys); } return contact; } template <> boost::shared_ptr readCard (const vcard_4_0::VcardsType::vcard_type &vcard) { using namespace vcard_4_0; boost::shared_ptr distlist(new Kolab::DistList); if (!vcard.member().empty()) { std::vector members; BOOST_FOREACH(const vcard_4_0::vcard::member_type & m, vcard.member()) { members.push_back(Shared::toContactReference(m.uri())); } distlist->setMembers(members); } return distlist; } template boost::shared_ptr deserializeCard(const std::string& s, bool isUrl) { clearErrors(); try { std::auto_ptr vcards; if (isUrl) { xsd::cxx::xml::dom::auto_ptr doc = XMLParserWrapper::inst().parseFile(s); if (doc.get()) { vcards = vcard_4_0::vcards(doc); } } else { xsd::cxx::xml::dom::auto_ptr doc = XMLParserWrapper::inst().parseString(s); if (doc.get()) { vcards = vcard_4_0::vcards(doc); } } if (!vcards.get()) { CRITICAL("failed to parse card!"); return boost::shared_ptr(); } boost::shared_ptr card = readCard(vcards->vcard()); card->setUid(Shared::fromURN(vcards->vcard().uid().uri())); card->setName(vcards->vcard().fn().text()); card->setLastModified(toDateTime(vcards->vcard().rev().timestamp())); setProductId( vcards->vcard().prodid().text() ); // setFormatVersion( vcards->vcard().version().text() ); // global_xCardVersion = vcalendar.properties().version().text(); setKolabVersion( vcards->vcard().x_kolab_version().text() ); if (!vcards->vcard().x_custom().empty()) { std::vector customProperties; BOOST_FOREACH(const vcard_4_0::CustomType &p, vcards->vcard().x_custom()) { customProperties.push_back(CustomProperty(p.identifier(), p.value())); } card->setCustomProperties(customProperties); } return card; } catch (const xml_schema::exception& e) { std::cerr << e << std::endl; } catch (...) { CRITICAL("Unhandled exception"); } CRITICAL("Failed to read vcard!"); return boost::shared_ptr(); } } } //Namespace #endif