diff --git a/kcal/attendee.cpp b/kcal/attendee.cpp index 3daf2ebe3..059d03e8a 100644 --- a/kcal/attendee.cpp +++ b/kcal/attendee.cpp @@ -1,260 +1,265 @@ /* This file is part of the kcal library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attendee class. @brief Represents information related to an attendee of an Calendar Incidence. @author Cornelius Schumacher \ */ #include "attendee.h" #include #include #include using namespace KCal; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE class KCal::Attendee::Private { public: bool mRSVP; Role mRole; PartStat mStatus; QString mUid; QString mDelegate; QString mDelegator; CustomProperties mCustomProperties; }; //@endcond Attendee::Attendee( const QString &name, const QString &email, bool rsvp, Attendee::PartStat status, Attendee::Role role, const QString &uid ) : d( new Attendee::Private ) { setName( name ); setEmail( email ); d->mRSVP = rsvp; d->mStatus = status; d->mRole = role; d->mUid = uid; } Attendee::Attendee( const Attendee &attendee ) : Person( attendee ), d( new Attendee::Private( *attendee.d ) ) { } Attendee::~Attendee() { delete d; } bool KCal::Attendee::operator==( const Attendee &attendee ) { return ( Person & )*this == ( const Person & )attendee && d->mRSVP == attendee.d->mRSVP && d->mRole == attendee.d->mRole && d->mStatus == attendee.d->mStatus && d->mUid == attendee.d->mUid && d->mDelegate == attendee.d->mDelegate && d->mDelegator == attendee.d->mDelegator; } Attendee &KCal::Attendee::operator=( const Attendee &attendee ) { // check for self assignment if ( &attendee == this ) { return *this; } *d = *attendee.d; setName( attendee.name() ); setEmail( attendee.email() ); return *this; } void Attendee::setRSVP( bool r ) { d->mRSVP = r; } bool Attendee::RSVP() const { return d->mRSVP; } void Attendee::setStatus( Attendee::PartStat status ) { d->mStatus = status; } Attendee::PartStat Attendee::status() const { return d->mStatus; } QString Attendee::statusStr() const { return statusName( d->mStatus ); } QString Attendee::statusName( Attendee::PartStat status ) { switch ( status ) { default: case NeedsAction: return i18nc( "@item event, to-do or journal needs action", "Needs Action" ); break; case Accepted: return i18nc( "@item event, to-do or journal accepted", "Accepted" ); break; case Declined: return i18nc( "@item event, to-do or journal declined", "Declined" ); break; case Tentative: return i18nc( "@item event or to-do tentatively accepted", "Tentative" ); break; case Delegated: return i18nc( "@item event or to-do delegated", "Delegated" ); break; case Completed: return i18nc( "@item to-do completed", "Completed" ); break; case InProcess: return i18nc( "@item to-do in process of being completed", "In Process" ); break; case None: return i18nc( "@item event or to-do status unknown", "Unknown" ); break; } } QStringList Attendee::statusList() { QStringList list; list << statusName( NeedsAction ); list << statusName( Accepted ); list << statusName( Declined ); list << statusName( Tentative ); list << statusName( Delegated ); list << statusName( Completed ); list << statusName( InProcess ); return list; } void Attendee::setRole( Attendee::Role role ) { d->mRole = role; } Attendee::Role Attendee::role() const { return d->mRole; } QString Attendee::roleStr() const { return roleName( d->mRole ); } void Attendee::setUid( const QString &uid ) { d->mUid = uid; } QString Attendee::uid() const { return d->mUid; } QString Attendee::roleName( Attendee::Role role ) { switch ( role ) { case Chair: return i18nc( "@item chairperson", "Chair" ); break; default: case ReqParticipant: return i18nc( "@item participation is required", "Participant" ); break; case OptParticipant: return i18nc( "@item participation is optional", "Optional Participant" ); break; case NonParticipant: return i18nc( "@item non-participant copied for information", "Observer" ); break; } } QStringList Attendee::roleList() { QStringList list; list << roleName( ReqParticipant ); list << roleName( OptParticipant ); list << roleName( NonParticipant ); list << roleName( Chair ); return list; } void Attendee::setDelegate( const QString &delegate ) { d->mDelegate = delegate; } QString Attendee::delegate() const { return d->mDelegate; } void Attendee::setDelegator( const QString &delegator ) { d->mDelegator = delegator; } QString Attendee::delegator() const { return d->mDelegator; } void Attendee::setCustomProperty( const QByteArray &xname, const QString &xvalue ) { d->mCustomProperties.setNonKDECustomProperty( xname, xvalue ); } CustomProperties& Attendee::customProperties() { return d->mCustomProperties; } + +const CustomProperties& Attendee::customProperties() const +{ + return d->mCustomProperties; +} diff --git a/kcal/attendee.h b/kcal/attendee.h index 0f9bc2300..c0843e63f 100644 --- a/kcal/attendee.h +++ b/kcal/attendee.h @@ -1,298 +1,304 @@ /* This file is part of the kcal library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attendee class. @author Cornelius Schumacher \ */ #ifndef KCAL_ATTENDEE_H #define KCAL_ATTENDEE_H #include #include #include "listbase.h" #include "person.h" #include "customproperties.h" namespace KCal { /** @brief Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (to-do). Attendees are people with a name and (optional) email address who are invited to participate in some way in a meeting or task. This class also tracks that status of the invitation: accepted; tentatively accepted; declined; delegated to another person; in-progress; completed. Attendees may optionally be asked to @acronym RSVP ("Respond Please") to the invitation. Note that each attendee be can optionally associated with a @acronym UID (unique identifier) derived from a Calendar Incidence, Email Message, or any other thing you want. */ class KCAL_EXPORT Attendee : private Person { public: using Person::setEmail; using Person::email; using Person::setName; using Person::name; using Person::fullName; /** The different types of participant status. The meaning is specific to the incidence type in context. */ enum PartStat { NeedsAction, /**< Event, to-do or journal needs action (default) */ Accepted, /**< Event, to-do or journal accepted */ Declined, /**< Event, to-do or journal declined */ Tentative, /**< Event or to-do tentatively accepted */ Delegated, /**< Event or to-do delegated */ Completed, /**< To-do completed */ InProcess, /**< To-do in process of being completed */ None }; /** The different types of participation roles. */ enum Role { ReqParticipant, /**< Participation is required (default) */ OptParticipant, /**< Participation is optional */ NonParticipant, /**< Non-Participant; copied for information purposes */ Chair /**< Chairperson */ }; /** List of attendees. */ typedef ListBase List; /** Constructs an attendee consisting of a Person name (@p name) and email address (@p email); invitation status and #Role; an optional @acronym RSVP flag and @acronym UID. @param name is person name of the attendee. @param email is person email address of the attendee. @param rsvp if true, the attendee is requested to reply to invitations. @param status is the #PartStat status of the attendee. @param role is the #Role of the attendee. @param uid is the @acronym UID of the attendee. */ Attendee( const QString &name, const QString &email, bool rsvp = false, PartStat status = None, Role role = ReqParticipant, const QString &uid = QString() ); /** Constructs an attendee by copying another attendee. @param attendee is the attendee to be copied. */ Attendee( const Attendee &attendee ); /** Destroys the attendee. */ ~Attendee(); /** Sets the Role of the attendee to @p role. @param role is the Role to use for the attendee. @see role() */ void setRole( Role role ); /** Returns the Role of the attendee. @see setRole() */ Role role() const; /** Returns the attendee Role as a text string. @see role(), roleName() */ QString roleStr() const; /** Returns the specified #Role @p role as a text string. @param role is a #Role value. @see role(), roleStr() */ static QString roleName( Role role ); /** Returns a list of strings representing each #Role. */ static QStringList roleList(); /** Sets the @acronym UID of the attendee to @p uid. @param uid is the @acronym UID to use for the attendee. @see uid() */ void setUid ( const QString &uid ); /** Returns the @acronym UID of the attendee. @see setUid() */ QString uid() const; /** Sets the #PartStat of the attendee to @p status. @param status is the #PartStat to use for the attendee. @see status() */ void setStatus( PartStat status ); /** Returns the #PartStat of the attendee. @see setStatus() */ PartStat status() const; /** Returns the attendee #PartStat as a text string. @see status(), statusName() */ QString statusStr() const; /** Returns the specified #PartStat @p status as a text string. @param status is a #PartStat value. @see status(), statusStr() */ static QString statusName( PartStat status ); /** Returns a list of strings representing each #PartStat. */ static QStringList statusList(); /** Sets the @acronym RSVP flag of the attendee to @p rsvp. @param rsvp if set (true), the attendee is requested to reply to invitations. @see RSVP() */ void setRSVP( bool rsvp ); /** Returns the attendee @acronym RSVP flag. @see setRSVP() */ bool RSVP() const; /** Compares this with @p attendee for equality. @param attendee the attendee to compare. */ //KDE5: make const bool operator==( const Attendee &attendee ); //krazy:exclude=operators /** Sets the delegate. @param delegate is a string containing a MAILTO URI of those delegated to attend the meeting. @see delegate(), setDelegator(). */ void setDelegate( const QString &delegate ); /** Returns the delegate. @see setDelegate(). */ QString delegate() const; /** Sets the delegator. @param delegator is a string containing a MAILTO URI of those who have delegated their meeting attendance. @see delegator(), setDelegate(). */ void setDelegator( const QString &delegator ); /** Returns the delegator. @see setDelegator(). */ QString delegator() const; /** Adds a custom property. If the property already exists it will be overwritten. @param xname is the name of the property. @param xvalue is its value. @since 4.4 */ void setCustomProperty( const QByteArray &xname, const QString &xvalue ); /** Returns a reference to the CustomProperties object @since 4.4 */ CustomProperties& customProperties(); + + /** + Returns a const reference to the CustomProperties object + @since 4.4 + */ + const CustomProperties& customProperties() const; /** Sets this attendee equal to @p attendee. @param attendee is the attendee to copy. */ Attendee &operator=( const Attendee &attendee ); private: //@cond PRIVATE class Private; Private *const d; //@endcond }; } #endif diff --git a/kcal/icalformat_p.cpp b/kcal/icalformat_p.cpp index 8af163439..5631677c1 100644 --- a/kcal/icalformat_p.cpp +++ b/kcal/icalformat_p.cpp @@ -1,2724 +1,2724 @@ /* This file is part of the kcal library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the internal ICalFormat classes. @brief This class provides the libical dependent functions for ICalFormat. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author David Jarvie \ */ #include "icalformat_p.h" #include "icalformat.h" #include "icaltimezones.h" #include "calendar.h" #include "compat.h" #include "journal.h" extern "C" { #include #include #include } #include #include #include #include #include #include #include #include #include #include #include using namespace KCal; /* Static helpers */ /* static void _dumpIcaltime( const icaltimetype& t) { kDebug() << "--- Y:" << t.year << "M:" << t.month << "D:" << t.day; kDebug() << "--- H:" << t.hour << "M:" << t.minute << "S:" << t.second; kDebug() << "--- isUtc:" << icaltime_is_utc( t ); kDebug() << "--- zoneId:" << icaltimezone_get_tzid( const_cast( t.zone ) ); } */ //@cond PRIVATE static QString quoteForParam( const QString &text ) { QString tmp = text; tmp.remove( '"' ); if ( tmp.contains( ';' ) || tmp.contains( ':' ) || tmp.contains( ',' ) ) { return tmp; // libical quotes in this case already, see icalparameter_as_ical_string() } return QString::fromLatin1( "\"" ) + tmp + QString::fromLatin1( "\"" ); } const int gSecondsPerMinute = 60; const int gSecondsPerHour = gSecondsPerMinute * 60; const int gSecondsPerDay = gSecondsPerHour * 24; const int gSecondsPerWeek = gSecondsPerDay * 7; class ToComponentVisitor : public IncidenceBase::Visitor { public: ToComponentVisitor( ICalFormatImpl *impl, iTIPMethod m ) : mImpl( impl ), mComponent( 0 ), mMethod( m ) {} bool visit( Event *e ) { mComponent = mImpl->writeEvent( e ); return true; } bool visit( Todo *e ) { mComponent = mImpl->writeTodo( e ); return true; } bool visit( Journal *e ) { mComponent = mImpl->writeJournal( e ); return true; } bool visit( FreeBusy *fb ) { mComponent = mImpl->writeFreeBusy( fb, mMethod ); return true; } icalcomponent *component() { return mComponent; } private: ICalFormatImpl *mImpl; icalcomponent *mComponent; iTIPMethod mMethod; }; class ICalFormatImpl::Private { public: Private( ICalFormatImpl *impl, ICalFormat *parent ) : mImpl( impl ), mParent( parent ), mCompat( new Compat ) {} ~Private() { delete mCompat; } void writeIncidenceBase( icalcomponent *parent, IncidenceBase * ); void readIncidenceBase( icalcomponent *parent, IncidenceBase * ); void writeCustomProperties( icalcomponent *parent, CustomProperties * ); void readCustomProperties( icalcomponent *parent, CustomProperties * ); ICalFormatImpl *mImpl; ICalFormat *mParent; QString mLoadedProductId; // PRODID string loaded from calendar file Event::List mEventsRelate; // events with relations Todo::List mTodosRelate; // todos with relations Compat *mCompat; }; //@endcond inline icaltimetype ICalFormatImpl::writeICalUtcDateTime ( const KDateTime &dt ) { return writeICalDateTime( dt.toUtc() ); } ICalFormatImpl::ICalFormatImpl( ICalFormat *parent ) : d( new Private( this, parent ) ) { } ICalFormatImpl::~ICalFormatImpl() { delete d; } QString ICalFormatImpl::loadedProductId() const { return d->mLoadedProductId; } icalcomponent *ICalFormatImpl::writeIncidence( IncidenceBase *incidence, iTIPMethod method ) { ToComponentVisitor v( this, method ); if ( incidence->accept(v) ) { return v.component(); } else { return 0; } } icalcomponent *ICalFormatImpl::writeTodo( Todo *todo, ICalTimeZones *tzlist, ICalTimeZones *tzUsedList ) { QString tmpStr; QStringList tmpStrList; icalcomponent *vtodo = icalcomponent_new( ICAL_VTODO_COMPONENT ); writeIncidence( vtodo, todo, tzlist, tzUsedList ); // due date icalproperty *prop; if ( todo->hasDueDate() ) { icaltimetype due; if ( todo->allDay() ) { due = writeICalDate( todo->dtDue( true ).date() ); prop = icalproperty_new_due(due); } else { prop = writeICalDateTimeProperty( ICAL_DUE_PROPERTY, todo->dtDue(true), tzlist, tzUsedList ); } icalcomponent_add_property( vtodo, prop ); } // start time if ( todo->hasStartDate() ) { icaltimetype start; if ( todo->allDay() ) { start = writeICalDate( todo->dtStart( true ).date() ); prop = icalproperty_new_dtstart( start ); } else { prop = writeICalDateTimeProperty( ICAL_DTSTART_PROPERTY, todo->dtStart( true ), tzlist, tzUsedList ); } icalcomponent_add_property( vtodo, prop ); } // completion date (UTC) if ( todo->isCompleted() ) { if ( !todo->hasCompletedDate() ) { // If the todo was created by KOrganizer<2.2 it does not have // a correct completion date. Set one now. todo->setCompleted( KDateTime::currentUtcDateTime() ); } icaltimetype completed = writeICalUtcDateTime( todo->completed() ); icalcomponent_add_property( vtodo, icalproperty_new_completed ( completed ) ); } icalcomponent_add_property( vtodo, icalproperty_new_percentcomplete( todo->percentComplete() ) ); if ( todo->recurs() ) { icalcomponent_add_property( vtodo, writeICalDateTimeProperty( ICAL_RECURRENCEID_PROPERTY, todo->dtDue(), tzlist, tzUsedList ) ); } return vtodo; } icalcomponent *ICalFormatImpl::writeEvent( Event *event, ICalTimeZones *tzlist, ICalTimeZones *tzUsedList ) { icalcomponent *vevent = icalcomponent_new( ICAL_VEVENT_COMPONENT ); writeIncidence( vevent, event, tzlist, tzUsedList ); // start time icalproperty *prop; icaltimetype start; if ( event->allDay() ) { start = writeICalDate( event->dtStart().date() ); prop = icalproperty_new_dtstart( start ); } else { prop = writeICalDateTimeProperty( ICAL_DTSTART_PROPERTY, event->dtStart(), tzlist, tzUsedList ); } icalcomponent_add_property( vevent, prop ); if ( event->hasEndDate() ) { // End time. // RFC2445 says that if DTEND is present, it has to be greater than DTSTART. icaltimetype end; KDateTime dt = event->dtEnd(); if ( event->allDay() ) { // +1 day because end date is non-inclusive. end = writeICalDate( dt.date().addDays( 1 ) ); icalcomponent_add_property( vevent, icalproperty_new_dtend(end) ); } else { if ( dt != event->dtStart() ) { icalcomponent_add_property( vevent, writeICalDateTimeProperty( ICAL_DTEND_PROPERTY, dt, tzlist, tzUsedList ) ); } } } // TODO: resources #if 0 // resources QStringList tmpStrList = anEvent->resources(); QString tmpStr = tmpStrList.join( ";" ); if ( !tmpStr.isEmpty() ) { addPropValue( vevent, VCResourcesProp, tmpStr.toUtf8() ); } #endif // Transparency switch( event->transparency() ) { case Event::Transparent: icalcomponent_add_property( vevent, icalproperty_new_transp( ICAL_TRANSP_TRANSPARENT ) ); break; case Event::Opaque: icalcomponent_add_property( vevent, icalproperty_new_transp( ICAL_TRANSP_OPAQUE ) ); break; } return vevent; } icalcomponent *ICalFormatImpl::writeFreeBusy( FreeBusy *freebusy, iTIPMethod method ) { icalcomponent *vfreebusy = icalcomponent_new( ICAL_VFREEBUSY_COMPONENT ); d->writeIncidenceBase( vfreebusy, freebusy ); icalcomponent_add_property( vfreebusy, icalproperty_new_dtstart( writeICalUtcDateTime( freebusy->dtStart() ) ) ); icalcomponent_add_property( vfreebusy, icalproperty_new_dtend( writeICalUtcDateTime( freebusy->dtEnd() ) ) ); if ( method == iTIPRequest ) { icalcomponent_add_property( vfreebusy, icalproperty_new_uid( freebusy->uid().toUtf8() ) ); } //Loops through all the periods in the freebusy object QList list = freebusy->busyPeriods(); icalperiodtype period = icalperiodtype_null_period(); for ( int i = 0, count = list.count(); i < count; ++i ) { period.start = writeICalUtcDateTime( list[i].start() ); if ( list[i].hasDuration() ) { period.duration = writeICalDuration( list[i].duration() ); } else { period.end = writeICalUtcDateTime( list[i].end() ); } icalcomponent_add_property( vfreebusy, icalproperty_new_freebusy( period ) ); } return vfreebusy; } icalcomponent *ICalFormatImpl::writeJournal( Journal *journal, ICalTimeZones *tzlist, ICalTimeZones *tzUsedList ) { icalcomponent *vjournal = icalcomponent_new( ICAL_VJOURNAL_COMPONENT ); writeIncidence( vjournal, journal, tzlist, tzUsedList ); // start time icalproperty *prop; KDateTime dt = journal->dtStart(); if ( dt.isValid() ) { icaltimetype start; if ( journal->allDay() ) { start = writeICalDate( dt.date() ); prop = icalproperty_new_dtstart( start ); } else { prop = writeICalDateTimeProperty( ICAL_DTSTART_PROPERTY, dt, tzlist, tzUsedList ); } icalcomponent_add_property( vjournal, prop ); } return vjournal; } void ICalFormatImpl::writeIncidence( icalcomponent *parent, Incidence *incidence, ICalTimeZones *tzlist, ICalTimeZones *tzUsedList ) { if ( incidence->schedulingID() != incidence->uid() ) { // We need to store the UID in here. The rawSchedulingID will // go into the iCal UID component incidence->setCustomProperty( "LIBKCAL", "ID", incidence->uid() ); } else { incidence->removeCustomProperty( "LIBKCAL", "ID" ); } d->writeIncidenceBase( parent, incidence ); // creation date icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_CREATED_PROPERTY, incidence->created() ) ); // unique id // If the scheduling ID is different from the real UID, the real // one is stored on X-REALID above if ( !incidence->schedulingID().isEmpty() ) { icalcomponent_add_property( parent, icalproperty_new_uid( incidence->schedulingID().toUtf8() ) ); } // revision if ( incidence->revision() > 0 ) { // 0 is default, so don't write that out icalcomponent_add_property( parent, icalproperty_new_sequence( incidence->revision() ) ); } // last modification date if ( incidence->lastModified().isValid() ) { icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_LASTMODIFIED_PROPERTY, incidence->lastModified() ) ); } // description if ( !incidence->description().isEmpty() ) { icalcomponent_add_property( parent, writeDescription( incidence->description(), incidence->descriptionIsRich() ) ); } // summary if ( !incidence->summary().isEmpty() ) { icalcomponent_add_property( parent, writeSummary( incidence->summary(), incidence->summaryIsRich() ) ); } // location if ( !incidence->location().isEmpty() ) { icalcomponent_add_property( parent, writeLocation( incidence->location(), incidence->locationIsRich() ) ); } // status icalproperty_status status = ICAL_STATUS_NONE; switch ( incidence->status() ) { case Incidence::StatusTentative: status = ICAL_STATUS_TENTATIVE; break; case Incidence::StatusConfirmed: status = ICAL_STATUS_CONFIRMED; break; case Incidence::StatusCompleted: status = ICAL_STATUS_COMPLETED; break; case Incidence::StatusNeedsAction: status = ICAL_STATUS_NEEDSACTION; break; case Incidence::StatusCanceled: status = ICAL_STATUS_CANCELLED; break; case Incidence::StatusInProcess: status = ICAL_STATUS_INPROCESS; break; case Incidence::StatusDraft: status = ICAL_STATUS_DRAFT; break; case Incidence::StatusFinal: status = ICAL_STATUS_FINAL; break; case Incidence::StatusX: { icalproperty *p = icalproperty_new_status( ICAL_STATUS_X ); icalvalue_set_x( icalproperty_get_value( p ), incidence->statusStr().toUtf8() ); icalcomponent_add_property( parent, p ); break; } case Incidence::StatusNone: default: break; } if ( status != ICAL_STATUS_NONE ) { icalcomponent_add_property( parent, icalproperty_new_status( status ) ); } // secrecy icalproperty_class secClass; switch ( incidence->secrecy() ) { case Incidence::SecrecyPublic: secClass = ICAL_CLASS_PUBLIC; break; case Incidence::SecrecyConfidential: secClass = ICAL_CLASS_CONFIDENTIAL; break; case Incidence::SecrecyPrivate: default: secClass = ICAL_CLASS_PRIVATE; break; } if ( secClass != ICAL_CLASS_PUBLIC ) { icalcomponent_add_property( parent, icalproperty_new_class( secClass ) ); } // geo if ( incidence->hasGeo() ) { icalgeotype geo; geo.lat = incidence->geoLatitude(); geo.lon = incidence->geoLongitude(); icalcomponent_add_property( parent, icalproperty_new_geo( geo ) ); } // priority if ( incidence->priority() > 0 ) { // 0 is undefined priority icalcomponent_add_property( parent, icalproperty_new_priority( incidence->priority() ) ); } // categories QString categories = incidence->categories().join( "," ); if ( !categories.isEmpty() ) { icalcomponent_add_property( parent, icalproperty_new_categories( categories.toUtf8() ) ); } // related event if ( !incidence->relatedToUid().isEmpty() ) { icalcomponent_add_property( parent, icalproperty_new_relatedto( incidence->relatedToUid().toUtf8() ) ); } RecurrenceRule::List rrules( incidence->recurrence()->rRules() ); RecurrenceRule::List::ConstIterator rit; for ( rit = rrules.constBegin(); rit != rrules.constEnd(); ++rit ) { icalcomponent_add_property( parent, icalproperty_new_rrule( writeRecurrenceRule( (*rit) ) ) ); } RecurrenceRule::List exrules( incidence->recurrence()->exRules() ); RecurrenceRule::List::ConstIterator exit; for ( exit = exrules.constBegin(); exit != exrules.constEnd(); ++exit ) { icalcomponent_add_property( parent, icalproperty_new_exrule( writeRecurrenceRule( (*exit) ) ) ); } DateList dateList = incidence->recurrence()->exDates(); DateList::ConstIterator exIt; for ( exIt = dateList.constBegin(); exIt != dateList.constEnd(); ++exIt ) { icalcomponent_add_property( parent, icalproperty_new_exdate( writeICalDate(*exIt) ) ); } DateTimeList dateTimeList = incidence->recurrence()->exDateTimes(); DateTimeList::ConstIterator extIt; for ( extIt = dateTimeList.constBegin(); extIt != dateTimeList.constEnd(); ++extIt ) { icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_EXDATE_PROPERTY, *extIt, tzlist, tzUsedList ) ); } dateList = incidence->recurrence()->rDates(); DateList::ConstIterator rdIt; for ( rdIt = dateList.constBegin(); rdIt != dateList.constEnd(); ++rdIt ) { icalcomponent_add_property( parent, icalproperty_new_rdate( writeICalDatePeriod(*rdIt) ) ); } dateTimeList = incidence->recurrence()->rDateTimes(); DateTimeList::ConstIterator rdtIt; for ( rdtIt = dateTimeList.constBegin(); rdtIt != dateTimeList.constEnd(); ++rdtIt ) { icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_RDATE_PROPERTY, *rdtIt, tzlist, tzUsedList ) ); } // attachments Attachment::List attachments = incidence->attachments(); Attachment::List::ConstIterator atIt; for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) { icalcomponent_add_property( parent, writeAttachment( *atIt ) ); } // alarms Alarm::List::ConstIterator alarmIt; for ( alarmIt = incidence->alarms().constBegin(); alarmIt != incidence->alarms().constEnd(); ++alarmIt ) { if ( (*alarmIt)->enabled() ) { icalcomponent_add_component( parent, writeAlarm( *alarmIt ) ); } } // duration if ( incidence->hasDuration() ) { icaldurationtype duration; duration = writeICalDuration( incidence->duration() ); icalcomponent_add_property( parent, icalproperty_new_duration( duration ) ); } } //@cond PRIVATE void ICalFormatImpl::Private::writeIncidenceBase( icalcomponent *parent, IncidenceBase *incidenceBase ) { icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_DTSTAMP_PROPERTY, KDateTime::currentUtcDateTime() ) ); // organizer stuff if ( !incidenceBase->organizer().isEmpty() ) { icalproperty *p = mImpl->writeOrganizer( incidenceBase->organizer() ); if ( p ) { icalcomponent_add_property( parent, p ); } } // attendees if ( incidenceBase->attendeeCount() > 0 ) { Attendee::List::ConstIterator it; for ( it = incidenceBase->attendees().constBegin(); it != incidenceBase->attendees().constEnd(); ++it ) { icalproperty *p = mImpl->writeAttendee( *it ); if ( p ) { icalcomponent_add_property( parent, p ); } } } // comments QStringList comments = incidenceBase->comments(); for ( QStringList::const_iterator it = comments.constBegin(); it != comments.constEnd(); ++it ) { icalcomponent_add_property( parent, icalproperty_new_comment( (*it).toUtf8() ) ); } // custom properties writeCustomProperties( parent, incidenceBase ); } void ICalFormatImpl::Private::writeCustomProperties( icalcomponent *parent, CustomProperties *properties ) { const QMap custom = properties->customProperties(); for ( QMap::ConstIterator c = custom.begin(); c != custom.end(); ++c ) { icalproperty *p = icalproperty_new_x( c.value().toUtf8() ); if ( !c.key().startsWith( "X-KDE" ) && //krazy:exclude=strings !c.key().startsWith( "X-LibKCal" ) && //krazy:exclude=strings !c.key().startsWith( "X-MICROSOFT" ) && //krazy:exclude=strings !c.key().startsWith( "X-MOZ" ) && //krazy:exclude=strings !c.key().startsWith( "X-PILOT" ) ) { //krazy:exclude=strings // use text values for the typical X-FOO property. // except for vendor specific X-FOO properties. icalvalue *text = icalvalue_new_text( c.value().toUtf8().data() ); icalproperty_set_value( p, text ); } icalproperty_set_x_name( p, c.key() ); icalcomponent_add_property( parent, p ); } } //@endcond icalproperty *ICalFormatImpl::writeOrganizer( const Person &organizer ) { if ( organizer.email().isEmpty() ) { return 0; } icalproperty *p = icalproperty_new_organizer( "MAILTO:" + organizer.email().toUtf8() ); if ( !organizer.name().isEmpty() ) { icalproperty_add_parameter( p, icalparameter_new_cn( quoteForParam( organizer.name() ).toUtf8() ) ); } // TODO: Write dir, sent-by and language return p; } icalproperty *ICalFormatImpl::writeDescription( const QString &description, bool isRich ) { icalproperty *p = icalproperty_new_description( description.toUtf8() ); if ( isRich ) { icalproperty_add_parameter( p, icalparameter_new_from_string( "X-KDE-TEXTFORMAT=HTML" ) ); } return p; } icalproperty *ICalFormatImpl::writeSummary( const QString &summary, bool isRich ) { icalproperty *p = icalproperty_new_summary( summary.toUtf8() ); if ( isRich ) { icalproperty_add_parameter( p, icalparameter_new_from_string( "X-KDE-TEXTFORMAT=HTML" ) ); } return p; } icalproperty *ICalFormatImpl::writeLocation( const QString &location, bool isRich ) { icalproperty *p = icalproperty_new_location( location.toUtf8() ); if ( isRich ) { icalproperty_add_parameter( p, icalparameter_new_from_string( "X-KDE-TEXTFORMAT=HTML" ) ); } return p; } icalproperty *ICalFormatImpl::writeAttendee( Attendee *attendee ) { if ( attendee->email().isEmpty() ) { return 0; } icalproperty *p = icalproperty_new_attendee( "mailto:" + attendee->email().toUtf8() ); if ( !attendee->name().isEmpty() ) { icalproperty_add_parameter( p, icalparameter_new_cn( quoteForParam( attendee->name() ).toUtf8() ) ); } icalproperty_add_parameter( p, icalparameter_new_rsvp( attendee->RSVP() ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE ) ); icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION; switch ( attendee->status() ) { default: case Attendee::NeedsAction: status = ICAL_PARTSTAT_NEEDSACTION; break; case Attendee::Accepted: status = ICAL_PARTSTAT_ACCEPTED; break; case Attendee::Declined: status = ICAL_PARTSTAT_DECLINED; break; case Attendee::Tentative: status = ICAL_PARTSTAT_TENTATIVE; break; case Attendee::Delegated: status = ICAL_PARTSTAT_DELEGATED; break; case Attendee::Completed: status = ICAL_PARTSTAT_COMPLETED; break; case Attendee::InProcess: status = ICAL_PARTSTAT_INPROCESS; break; } icalproperty_add_parameter( p, icalparameter_new_partstat( status ) ); icalparameter_role role = ICAL_ROLE_REQPARTICIPANT; switch ( attendee->role() ) { case Attendee::Chair: role = ICAL_ROLE_CHAIR; break; default: case Attendee::ReqParticipant: role = ICAL_ROLE_REQPARTICIPANT; break; case Attendee::OptParticipant: role = ICAL_ROLE_OPTPARTICIPANT; break; case Attendee::NonParticipant: role = ICAL_ROLE_NONPARTICIPANT; break; } icalproperty_add_parameter( p, icalparameter_new_role( role ) ); if ( !attendee->uid().isEmpty() ) { icalparameter *icalparameter_uid = icalparameter_new_x( attendee->uid().toUtf8() ); icalparameter_set_xname( icalparameter_uid, "X-UID" ); icalproperty_add_parameter( p, icalparameter_uid ); } if ( !attendee->delegate().isEmpty() ) { icalparameter *icalparameter_delegate = icalparameter_new_delegatedto( attendee->delegate().toUtf8() ); icalproperty_add_parameter( p, icalparameter_delegate ); } if ( !attendee->delegator().isEmpty() ) { icalparameter *icalparameter_delegator = icalparameter_new_delegatedfrom( attendee->delegator().toUtf8() ); icalproperty_add_parameter( p, icalparameter_delegator ); } - QMap customMap = attendee->customProperties().customProperties(); + const QMap customMap = attendee->customProperties().customProperties(); for( QMap::ConstIterator cit = customMap.constBegin(); cit != customMap.constEnd(); ++cit ) { icalparameter *icalparameter_x = icalparameter_new_x( cit.value().toUtf8() ); icalparameter_set_xname( icalparameter_x, cit.key() ); icalproperty_add_parameter( p, icalparameter_x ); } return p; } icalproperty *ICalFormatImpl::writeAttachment( Attachment *att ) { icalattach *attach; if ( att->isUri() ) { attach = icalattach_new_from_url( att->uri().toUtf8().data() ); } else { attach = icalattach_new_from_data ( ( unsigned char * )att->data(), 0, 0 ); } icalproperty *p = icalproperty_new_attach( attach ); if ( !att->mimeType().isEmpty() ) { icalproperty_add_parameter( p, icalparameter_new_fmttype( att->mimeType().toUtf8().data() ) ); } if ( att->isBinary() ) { icalproperty_add_parameter( p, icalparameter_new_value( ICAL_VALUE_BINARY ) ); icalproperty_add_parameter( p, icalparameter_new_encoding( ICAL_ENCODING_BASE64 ) ); } if ( att->showInline() ) { icalparameter *icalparameter_inline = icalparameter_new_x( "inline" ); icalparameter_set_xname( icalparameter_inline, "X-CONTENT-DISPOSITION" ); icalproperty_add_parameter( p, icalparameter_inline ); } if ( !att->label().isEmpty() ) { icalparameter *icalparameter_label = icalparameter_new_x( att->label().toUtf8() ); icalparameter_set_xname( icalparameter_label, "X-LABEL" ); icalproperty_add_parameter( p, icalparameter_label ); } if ( att->isLocal() ) { icalparameter *icalparameter_local = icalparameter_new_x( "local" ); icalparameter_set_xname( icalparameter_local, "X-KONTACT-TYPE" ); icalproperty_add_parameter( p, icalparameter_local ); } return p; } icalrecurrencetype ICalFormatImpl::writeRecurrenceRule( RecurrenceRule *recur ) { icalrecurrencetype r; icalrecurrencetype_clear( &r ); switch( recur->recurrenceType() ) { case RecurrenceRule::rSecondly: r.freq = ICAL_SECONDLY_RECURRENCE; break; case RecurrenceRule::rMinutely: r.freq = ICAL_MINUTELY_RECURRENCE; break; case RecurrenceRule::rHourly: r.freq = ICAL_HOURLY_RECURRENCE; break; case RecurrenceRule::rDaily: r.freq = ICAL_DAILY_RECURRENCE; break; case RecurrenceRule::rWeekly: r.freq = ICAL_WEEKLY_RECURRENCE; break; case RecurrenceRule::rMonthly: r.freq = ICAL_MONTHLY_RECURRENCE; break; case RecurrenceRule::rYearly: r.freq = ICAL_YEARLY_RECURRENCE; break; default: r.freq = ICAL_NO_RECURRENCE; kDebug() << "no recurrence"; break; } int index = 0; QList bys; QList::ConstIterator it; // Now write out the BY* parts: bys = recur->bySeconds(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_second[index++] = static_cast( *it ); } bys = recur->byMinutes(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_minute[index++] = static_cast( *it ); } bys = recur->byHours(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_hour[index++] = static_cast( *it ); } bys = recur->byMonthDays(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { short d = static_cast( ( *it ) * 8 ); r.by_month_day[index++] = static_cast( icalrecurrencetype_day_position( d ) ); } bys = recur->byYearDays(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_year_day[index++] = static_cast( *it ); } bys = recur->byWeekNumbers(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_week_no[index++] = static_cast( *it ); } bys = recur->byMonths(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_month[index++] = static_cast( *it ); } bys = recur->bySetPos(); index = 0; for ( it = bys.constBegin(); it != bys.constEnd(); ++it ) { r.by_set_pos[index++] = static_cast( *it ); } QList byd = recur->byDays(); int day; index = 0; for ( QList::ConstIterator dit = byd.constBegin(); dit != byd.constEnd(); ++dit ) { day = (*dit).day() % 7 + 1; // convert from Monday=1 to Sunday=1 if ( (*dit).pos() < 0 ) { day += ( -(*dit).pos() ) * 8; day = -day; } else { day += (*dit).pos() * 8; } r.by_day[index++] = static_cast( day ); } r.week_start = static_cast( recur->weekStart() % 7 + 1 ); if ( recur->frequency() > 1 ) { // Dont' write out INTERVAL=1, because that's the default anyway r.interval = static_cast( recur->frequency() ); } if ( recur->duration() > 0 ) { r.count = recur->duration(); } else if ( recur->duration() == -1 ) { r.count = 0; } else { if ( recur->allDay() ) { r.until = writeICalDate( recur->endDt().date() ); } else { r.until = writeICalUtcDateTime( recur->endDt() ); } } return r; } icalcomponent *ICalFormatImpl::writeAlarm( Alarm *alarm ) { icalcomponent *a = icalcomponent_new( ICAL_VALARM_COMPONENT ); icalproperty_action action; icalattach *attach = 0; switch ( alarm->type() ) { case Alarm::Procedure: action = ICAL_ACTION_PROCEDURE; attach = icalattach_new_from_url( QFile::encodeName( alarm->programFile() ).data() ); icalcomponent_add_property( a, icalproperty_new_attach( attach ) ); if ( !alarm->programArguments().isEmpty() ) { icalcomponent_add_property( a, icalproperty_new_description( alarm->programArguments().toUtf8() ) ); } break; case Alarm::Audio: action = ICAL_ACTION_AUDIO; if ( !alarm->audioFile().isEmpty() ) { attach = icalattach_new_from_url( QFile::encodeName( alarm->audioFile() ).data() ); icalcomponent_add_property( a, icalproperty_new_attach( attach ) ); } break; case Alarm::Email: { action = ICAL_ACTION_EMAIL; const QList addresses = alarm->mailAddresses(); for ( QList::ConstIterator ad = addresses.constBegin(); ad != addresses.constEnd(); ++ad ) { if ( !(*ad).email().isEmpty() ) { icalproperty *p = icalproperty_new_attendee( "MAILTO:" + (*ad).email().toUtf8() ); if ( !(*ad).name().isEmpty() ) { icalproperty_add_parameter( p, icalparameter_new_cn( quoteForParam( (*ad).name() ).toUtf8() ) ); } icalcomponent_add_property( a, p ); } } icalcomponent_add_property( a, icalproperty_new_summary( alarm->mailSubject().toUtf8() ) ); icalcomponent_add_property( a, icalproperty_new_description( alarm->mailText().toUtf8() ) ); QStringList attachments = alarm->mailAttachments(); if ( attachments.count() > 0 ) { for ( QStringList::const_iterator at = attachments.constBegin(); at != attachments.constEnd(); ++at ) { attach = icalattach_new_from_url( QFile::encodeName( *at ).data() ); icalcomponent_add_property( a, icalproperty_new_attach( attach ) ); } } break; } case Alarm::Display: action = ICAL_ACTION_DISPLAY; icalcomponent_add_property( a, icalproperty_new_description( alarm->text().toUtf8() ) ); break; case Alarm::Invalid: default: kDebug() << "Unknown type of alarm"; action = ICAL_ACTION_NONE; break; } icalcomponent_add_property( a, icalproperty_new_action( action ) ); // Trigger time icaltriggertype trigger; if ( alarm->hasTime() ) { trigger.time = writeICalUtcDateTime( alarm->time() ); trigger.duration = icaldurationtype_null_duration(); } else { trigger.time = icaltime_null_time(); Duration offset; if ( alarm->hasStartOffset() ) { offset = alarm->startOffset(); } else { offset = alarm->endOffset(); } trigger.duration = writeICalDuration( offset ); } icalproperty *p = icalproperty_new_trigger( trigger ); if ( alarm->hasEndOffset() ) { icalproperty_add_parameter( p, icalparameter_new_related( ICAL_RELATED_END ) ); } icalcomponent_add_property( a, p ); // Repeat count and duration if ( alarm->repeatCount() ) { icalcomponent_add_property( a, icalproperty_new_repeat( alarm->repeatCount() ) ); icalcomponent_add_property( a, icalproperty_new_duration( writeICalDuration( alarm->snoozeTime() ) ) ); } // Custom properties const QMap custom = alarm->customProperties(); for ( QMap::ConstIterator c = custom.begin(); c != custom.end(); ++c ) { icalproperty *p = icalproperty_new_x( c.value().toUtf8() ); icalproperty_set_x_name( p, c.key() ); icalcomponent_add_property( a, p ); } return a; } Todo *ICalFormatImpl::readTodo( icalcomponent *vtodo, ICalTimeZones *tzlist ) { Todo *todo = new Todo; readIncidence( vtodo, todo, tzlist ); icalproperty *p = icalcomponent_get_first_property( vtodo, ICAL_ANY_PROPERTY ); while ( p ) { icalproperty_kind kind = icalproperty_isa(p); switch ( kind ) { case ICAL_DUE_PROPERTY: { // due date/time KDateTime kdt = readICalDateTimeProperty( p, tzlist ); todo->setDtDue( kdt, true ); todo->setHasDueDate( true ); todo->setAllDay( kdt.isDateOnly() ); break; } case ICAL_COMPLETED_PROPERTY: // completion date/time todo->setCompleted( readICalDateTimeProperty( p, tzlist ) ); break; case ICAL_PERCENTCOMPLETE_PROPERTY: // Percent completed todo->setPercentComplete( icalproperty_get_percentcomplete( p ) ); break; case ICAL_RELATEDTO_PROPERTY: // related todo (parent) todo->setRelatedToUid( QString::fromUtf8( icalproperty_get_relatedto( p ) ) ); d->mTodosRelate.append( todo ); break; case ICAL_DTSTART_PROPERTY: // Flag that todo has start date. Value is read in by readIncidence(). if ( todo->comments().filter( "NoStartDate" ).count() ) { todo->setHasStartDate( false ); } else { todo->setHasStartDate( true ); } break; case ICAL_RECURRENCEID_PROPERTY: todo->setDtRecurrence( readICalDateTimeProperty( p, tzlist ) ); break; default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property( vtodo, ICAL_ANY_PROPERTY ); } if ( d->mCompat ) { d->mCompat->fixEmptySummary( todo ); } return todo; } Event *ICalFormatImpl::readEvent( icalcomponent *vevent, ICalTimeZones *tzlist ) { Event *event = new Event; readIncidence( vevent, event, tzlist ); icalproperty *p = icalcomponent_get_first_property( vevent, ICAL_ANY_PROPERTY ); bool dtEndProcessed = false; while ( p ) { icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_DTEND_PROPERTY: { // end date and time KDateTime kdt = readICalDateTimeProperty( p, tzlist ); if ( kdt.isDateOnly() ) { // End date is non-inclusive QDate endDate = kdt.date().addDays( -1 ); if ( d->mCompat ) { d->mCompat->fixFloatingEnd( endDate ); } if ( endDate < event->dtStart().date() ) { endDate = event->dtStart().date(); } event->setDtEnd( KDateTime( endDate, event->dtStart().timeSpec() ) ); } else { event->setDtEnd( kdt ); event->setAllDay( false ); } dtEndProcessed = true; break; } case ICAL_RELATEDTO_PROPERTY: // related event (parent) event->setRelatedToUid( QString::fromUtf8( icalproperty_get_relatedto( p ) ) ); d->mEventsRelate.append( event ); break; case ICAL_TRANSP_PROPERTY: // Transparency { icalproperty_transp transparency = icalproperty_get_transp( p ); if ( transparency == ICAL_TRANSP_TRANSPARENT ) { event->setTransparency( Event::Transparent ); } else { event->setTransparency( Event::Opaque ); } break; } default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property( vevent, ICAL_ANY_PROPERTY ); } // according to rfc2445 the dtend shouldn't be written when it equals // start date. so assign one equal to start date. if ( !dtEndProcessed && !event->hasDuration() ) { event->setDtEnd( event->dtStart() ); event->setHasEndDate( false ); } QString msade = event->nonKDECustomProperty( "X-MICROSOFT-CDO-ALLDAYEVENT" ); if ( !msade.isEmpty() ) { bool allDay = ( msade == QLatin1String( "TRUE" ) ); event->setAllDay( allDay ); } if ( d->mCompat ) { d->mCompat->fixEmptySummary( event ); } return event; } FreeBusy *ICalFormatImpl::readFreeBusy( icalcomponent *vfreebusy ) { FreeBusy *freebusy = new FreeBusy; d->readIncidenceBase( vfreebusy, freebusy ); icalproperty *p = icalcomponent_get_first_property( vfreebusy, ICAL_ANY_PROPERTY ); FreeBusyPeriod::List periods; while ( p ) { icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_DTSTART_PROPERTY: // start date and time (UTC) freebusy->setDtStart( readICalUtcDateTimeProperty( p ) ); break; case ICAL_DTEND_PROPERTY: // end Date and Time (UTC) freebusy->setDtEnd( readICalUtcDateTimeProperty( p ) ); break; case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times (UTC) { icalperiodtype icalperiod = icalproperty_get_freebusy( p ); KDateTime period_start = readICalUtcDateTime( p, icalperiod.start ); FreeBusyPeriod period; if ( !icaltime_is_null_time( icalperiod.end ) ) { KDateTime period_end = readICalUtcDateTime( p, icalperiod.end ); period = FreeBusyPeriod( period_start, period_end ); } else { Duration duration ( readICalDuration( icalperiod.duration ) ); period = FreeBusyPeriod( period_start, duration ); } QByteArray param = icalproperty_get_parameter_as_string( p, "X-SUMMARY" ); period.setSummary( QString::fromUtf8( KCodecs::base64Decode( param ) ) ); param = icalproperty_get_parameter_as_string( p, "X-LOCATION" ); period.setLocation( QString::fromUtf8( KCodecs::base64Decode( param ) ) ); periods.append( period ); break; } default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property( vfreebusy, ICAL_ANY_PROPERTY ); } freebusy->addPeriods( periods ); return freebusy; } Journal *ICalFormatImpl::readJournal( icalcomponent *vjournal, ICalTimeZones *tzlist ) { Journal *journal = new Journal; readIncidence( vjournal, journal, tzlist ); return journal; } Attendee *ICalFormatImpl::readAttendee( icalproperty *attendee ) { // the following is a hack to support broken calendars (like WebCalendar 1.0.x) // that include non-RFC-compliant attendees. Otherwise libical 0.42 asserts. if ( !icalproperty_get_value( attendee ) ) { return 0; } icalparameter *p = 0; QString email = QString::fromUtf8( icalproperty_get_attendee( attendee ) ); if ( email.startsWith( QLatin1String( "mailto:" ), Qt::CaseInsensitive ) ) { email = email.mid( 7 ); } QString name; QString uid; p = icalproperty_get_first_parameter( attendee, ICAL_CN_PARAMETER ); if ( p ) { name = QString::fromUtf8( icalparameter_get_cn( p ) ); } else { } bool rsvp = false; p = icalproperty_get_first_parameter( attendee, ICAL_RSVP_PARAMETER ); if ( p ) { icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp( p ); if ( rsvpParameter == ICAL_RSVP_TRUE ) { rsvp = true; } } Attendee::PartStat status = Attendee::NeedsAction; p = icalproperty_get_first_parameter( attendee, ICAL_PARTSTAT_PARAMETER ); if ( p ) { icalparameter_partstat partStatParameter = icalparameter_get_partstat( p ); switch( partStatParameter ) { default: case ICAL_PARTSTAT_NEEDSACTION: status = Attendee::NeedsAction; break; case ICAL_PARTSTAT_ACCEPTED: status = Attendee::Accepted; break; case ICAL_PARTSTAT_DECLINED: status = Attendee::Declined; break; case ICAL_PARTSTAT_TENTATIVE: status = Attendee::Tentative; break; case ICAL_PARTSTAT_DELEGATED: status = Attendee::Delegated; break; case ICAL_PARTSTAT_COMPLETED: status = Attendee::Completed; break; case ICAL_PARTSTAT_INPROCESS: status = Attendee::InProcess; break; } } Attendee::Role role = Attendee::ReqParticipant; p = icalproperty_get_first_parameter( attendee, ICAL_ROLE_PARAMETER ); if ( p ) { icalparameter_role roleParameter = icalparameter_get_role( p ); switch( roleParameter ) { case ICAL_ROLE_CHAIR: role = Attendee::Chair; break; default: case ICAL_ROLE_REQPARTICIPANT: role = Attendee::ReqParticipant; break; case ICAL_ROLE_OPTPARTICIPANT: role = Attendee::OptParticipant; break; case ICAL_ROLE_NONPARTICIPANT: role = Attendee::NonParticipant; break; } } p = icalproperty_get_first_parameter( attendee, ICAL_X_PARAMETER ); QMap custom; while ( p ) { QString xname = QString( icalparameter_get_xname( p ) ).toUpper(); QString xvalue = QString::fromUtf8( icalparameter_get_xvalue( p ) ); if ( xname == "X-UID" ) { uid = xvalue; } else { custom[xname.toUtf8()] = xvalue; } p = icalproperty_get_next_parameter( attendee, ICAL_X_PARAMETER ); } Attendee *a = new Attendee( name, email, rsvp, status, role, uid ); a->customProperties().setCustomProperties( custom ); p = icalproperty_get_first_parameter( attendee, ICAL_DELEGATEDTO_PARAMETER ); if ( p ) { a->setDelegate( icalparameter_get_delegatedto( p ) ); } p = icalproperty_get_first_parameter( attendee, ICAL_DELEGATEDFROM_PARAMETER ); if ( p ) { a->setDelegator( icalparameter_get_delegatedfrom( p ) ); } return a; } Person ICalFormatImpl::readOrganizer( icalproperty *organizer ) { QString email = QString::fromUtf8( icalproperty_get_organizer( organizer ) ); if ( email.startsWith( QLatin1String( "mailto:" ), Qt::CaseInsensitive ) ) { email = email.mid( 7 ); } QString cn; icalparameter *p = icalproperty_get_first_parameter( organizer, ICAL_CN_PARAMETER ); if ( p ) { cn = QString::fromUtf8( icalparameter_get_cn( p ) ); } Person org( cn, email ); // TODO: Treat sent-by, dir and language here, too return org; } Attachment *ICalFormatImpl::readAttachment( icalproperty *attach ) { Attachment *attachment = 0; const char *p; icalvalue *value = icalproperty_get_value( attach ); switch( icalvalue_isa( value ) ) { case ICAL_ATTACH_VALUE: { icalattach *a = icalproperty_get_attach( attach ); if ( !icalattach_get_is_url( a ) ) { p = (const char *)icalattach_get_data( a ); if ( p ) { attachment = new Attachment( p ); } } else { p = icalattach_get_url( a ); if ( p ) { attachment = new Attachment( QString::fromUtf8( p ) ); } } break; } case ICAL_BINARY_VALUE: { icalattach *a = icalproperty_get_attach( attach ); p = (const char *)icalattach_get_data( a ); if ( p ) { attachment = new Attachment( p ); } break; } case ICAL_URI_VALUE: p = icalvalue_get_uri( value ); attachment = new Attachment( QString::fromUtf8( p ) ); break; default: break; } if ( attachment ) { icalparameter *p = icalproperty_get_first_parameter( attach, ICAL_FMTTYPE_PARAMETER ); if ( p ) { attachment->setMimeType( QString( icalparameter_get_fmttype( p ) ) ); } p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER ); while ( p ) { QString xname = QString( icalparameter_get_xname( p ) ).toUpper(); QString xvalue = QString::fromUtf8( icalparameter_get_xvalue( p ) ); if ( xname == "X-CONTENT-DISPOSITION" ) { attachment->setShowInline( xvalue.toLower() == "inline" ); } if ( xname == "X-LABEL" ) { attachment->setLabel( xvalue ); } if ( xname == "X-KONTACT-TYPE" ) { attachment->setLocal( xvalue.toLower() == "local" ); } p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER ); } p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER ); while ( p ) { if ( strncmp( icalparameter_get_xname( p ), "X-LABEL", 7 ) == 0 ) { attachment->setLabel( QString::fromUtf8( icalparameter_get_xvalue( p ) ) ); } p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER ); } } return attachment; } void ICalFormatImpl::readIncidence( icalcomponent *parent, Incidence *incidence, ICalTimeZones *tzlist ) { d->readIncidenceBase( parent, incidence ); icalproperty *p = icalcomponent_get_first_property( parent, ICAL_ANY_PROPERTY ); const char *text; int intvalue, inttext; icaldurationtype icalduration; KDateTime kdt; QStringList categories; while ( p ) { icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_CREATED_PROPERTY: incidence->setCreated( readICalDateTimeProperty( p, tzlist ) ); break; case ICAL_SEQUENCE_PROPERTY: // sequence intvalue = icalproperty_get_sequence( p ); incidence->setRevision( intvalue ); break; case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time incidence->setLastModified( readICalDateTimeProperty( p, tzlist ) ); break; case ICAL_DTSTART_PROPERTY: // start date and time kdt = readICalDateTimeProperty( p, tzlist ); incidence->setDtStart( kdt ); incidence->setAllDay( kdt.isDateOnly() ); break; case ICAL_DURATION_PROPERTY: // start date and time icalduration = icalproperty_get_duration( p ); incidence->setDuration( readICalDuration( icalduration ) ); break; case ICAL_DESCRIPTION_PROPERTY: // description { QString textStr = QString::fromUtf8( icalproperty_get_description( p ) ); if ( !textStr.isEmpty() ) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string( p, "X-KDE-TEXTFORMAT" ) ); if ( !valStr.compare( "HTML", Qt::CaseInsensitive ) ) { incidence->setDescription( textStr, true ); } else { incidence->setDescription( textStr, false ); } } } break; case ICAL_SUMMARY_PROPERTY: // summary { QString textStr = QString::fromUtf8( icalproperty_get_summary( p ) ); if ( !textStr.isEmpty() ) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string( p, "X-KDE-TEXTFORMAT" ) ); if ( !valStr.compare( "HTML", Qt::CaseInsensitive ) ) { incidence->setSummary( textStr, true ); } else { incidence->setSummary( textStr, false ); } } } break; case ICAL_LOCATION_PROPERTY: // location { if ( !icalproperty_get_value( p ) ) { //Fix for #191472. This is a pre-crash guard in case libical was //compiled in superstrict mode (--enable-icalerrors-are-fatal) //TODO: pre-crash guard other property getters too. break; } QString textStr = QString::fromUtf8( icalproperty_get_location( p ) ); if ( !textStr.isEmpty() ) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string( p, "X-KDE-TEXTFORMAT" ) ); if ( !valStr.compare( "HTML", Qt::CaseInsensitive ) ) { incidence->setLocation( textStr, true ); } else { incidence->setLocation( textStr, false ); } } } break; case ICAL_STATUS_PROPERTY: // status { Incidence::Status stat; switch ( icalproperty_get_status( p ) ) { case ICAL_STATUS_TENTATIVE: stat = Incidence::StatusTentative; break; case ICAL_STATUS_CONFIRMED: stat = Incidence::StatusConfirmed; break; case ICAL_STATUS_COMPLETED: stat = Incidence::StatusCompleted; break; case ICAL_STATUS_NEEDSACTION: stat = Incidence::StatusNeedsAction; break; case ICAL_STATUS_CANCELLED: stat = Incidence::StatusCanceled; break; case ICAL_STATUS_INPROCESS: stat = Incidence::StatusInProcess; break; case ICAL_STATUS_DRAFT: stat = Incidence::StatusDraft; break; case ICAL_STATUS_FINAL: stat = Incidence::StatusFinal; break; case ICAL_STATUS_X: incidence->setCustomStatus( QString::fromUtf8( icalvalue_get_x( icalproperty_get_value( p ) ) ) ); stat = Incidence::StatusX; break; case ICAL_STATUS_NONE: default: stat = Incidence::StatusNone; break; } if ( stat != Incidence::StatusX ) { incidence->setStatus( stat ); } break; } case ICAL_GEO_PROPERTY: // geo { icalgeotype geo = icalproperty_get_geo( p ); incidence->setGeoLatitude( geo.lat ); incidence->setGeoLongitude( geo.lon ); incidence->setHasGeo( true ); break; } case ICAL_PRIORITY_PROPERTY: // priority intvalue = icalproperty_get_priority( p ); if ( d->mCompat ) { intvalue = d->mCompat->fixPriority( intvalue ); } incidence->setPriority( intvalue ); break; case ICAL_CATEGORIES_PROPERTY: // categories { // We have always supported multiple CATEGORIES properties per component // even though the RFC seems to indicate only 1 is permitted. // We can't change that -- in order to retain backwards compatibility. text = icalproperty_get_categories( p ); const QString val = QString::fromUtf8( text ); foreach ( const QString &cat, val.split( ',', QString::SkipEmptyParts ) ) { // ensure no duplicates if ( !categories.contains( cat ) ) { categories.append( cat ); } } break; } case ICAL_RRULE_PROPERTY: readRecurrenceRule( p, incidence ); break; case ICAL_RDATE_PROPERTY: kdt = readICalDateTimeProperty( p, tzlist ); if ( kdt.isValid() ) { if ( kdt.isDateOnly() ) { incidence->recurrence()->addRDate( kdt.date() ); } else { incidence->recurrence()->addRDateTime( kdt ); } } else { // TODO: RDates as period are not yet implemented! } break; case ICAL_EXRULE_PROPERTY: readExceptionRule( p, incidence ); break; case ICAL_EXDATE_PROPERTY: kdt = readICalDateTimeProperty( p, tzlist ); if ( kdt.isDateOnly() ) { incidence->recurrence()->addExDate( kdt.date() ); } else { incidence->recurrence()->addExDateTime( kdt ); } break; case ICAL_CLASS_PROPERTY: inttext = icalproperty_get_class( p ); if ( inttext == ICAL_CLASS_PUBLIC ) { incidence->setSecrecy( Incidence::SecrecyPublic ); } else if ( inttext == ICAL_CLASS_CONFIDENTIAL ) { incidence->setSecrecy( Incidence::SecrecyConfidential ); } else { incidence->setSecrecy( Incidence::SecrecyPrivate ); } break; case ICAL_ATTACH_PROPERTY: // attachments incidence->addAttachment( readAttachment( p ) ); break; default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property( parent, ICAL_ANY_PROPERTY ); } // Set the scheduling ID const QString uid = incidence->customProperty( "LIBKCAL", "ID" ); if ( !uid.isNull() ) { // The UID stored in incidencebase is actually the scheduling ID // It has to be stored in the iCal UID component for compatibility // with other iCal applications incidence->setSchedulingID( incidence->uid() ); incidence->setUid( uid ); } // Now that recurrence and exception stuff is completely set up, // do any backwards compatibility adjustments. if ( incidence->recurs() && d->mCompat ) { d->mCompat->fixRecurrence( incidence ); } // add categories incidence->setCategories( categories ); // iterate through all alarms for ( icalcomponent *alarm = icalcomponent_get_first_component( parent, ICAL_VALARM_COMPONENT ); alarm; alarm = icalcomponent_get_next_component( parent, ICAL_VALARM_COMPONENT ) ) { readAlarm( alarm, incidence, tzlist ); } // Fix incorrect alarm settings by other applications (like outloook 9) if ( d->mCompat ) { d->mCompat->fixAlarms( incidence ); } } //@cond PRIVATE void ICalFormatImpl::Private::readIncidenceBase( icalcomponent *parent, IncidenceBase *incidenceBase ) { icalproperty *p = icalcomponent_get_first_property( parent, ICAL_ANY_PROPERTY ); while ( p ) { icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_UID_PROPERTY: // unique id incidenceBase->setUid( QString::fromUtf8( icalproperty_get_uid( p ) ) ); break; case ICAL_ORGANIZER_PROPERTY: // organizer incidenceBase->setOrganizer( mImpl->readOrganizer( p ) ); break; case ICAL_ATTENDEE_PROPERTY: // attendee incidenceBase->addAttendee( mImpl->readAttendee( p ) ); break; case ICAL_COMMENT_PROPERTY: incidenceBase->addComment( QString::fromUtf8( icalproperty_get_comment( p ) ) ); break; default: break; } p = icalcomponent_get_next_property( parent, ICAL_ANY_PROPERTY ); } // custom properties readCustomProperties( parent, incidenceBase ); } void ICalFormatImpl::Private::readCustomProperties( icalcomponent *parent, CustomProperties *properties ) { QMap customProperties; QString lastProperty; icalproperty *p = icalcomponent_get_first_property( parent, ICAL_X_PROPERTY ); while ( p ) { QString value = QString::fromUtf8( icalproperty_get_x( p ) ); const char *name = icalproperty_get_x_name( p ); if ( lastProperty != name ) { customProperties[name] = value; } else { customProperties[name] = customProperties[name].append( "," ).append( value ); } p = icalcomponent_get_next_property( parent, ICAL_X_PROPERTY ); lastProperty = name; } properties->setCustomProperties( customProperties ); } //@endcond void ICalFormatImpl::readRecurrenceRule( icalproperty *rrule, Incidence *incidence ) { Recurrence *recur = incidence->recurrence(); struct icalrecurrencetype r = icalproperty_get_rrule( rrule ); // dumpIcalRecurrence(r); RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/); recurrule->setStartDt( incidence->dtStart() ); readRecurrence( r, recurrule ); recur->addRRule( recurrule ); } void ICalFormatImpl::readExceptionRule( icalproperty *rrule, Incidence *incidence ) { struct icalrecurrencetype r = icalproperty_get_exrule( rrule ); // dumpIcalRecurrence(r); RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/); recurrule->setStartDt( incidence->dtStart() ); readRecurrence( r, recurrule ); Recurrence *recur = incidence->recurrence(); recur->addExRule( recurrule ); } void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, RecurrenceRule *recur ) { // Generate the RRULE string recur->setRRule( QString( icalrecurrencetype_as_string( const_cast( &r ) ) ) ); // Period switch ( r.freq ) { case ICAL_SECONDLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rSecondly ); break; case ICAL_MINUTELY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMinutely ); break; case ICAL_HOURLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rHourly ); break; case ICAL_DAILY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rDaily ); break; case ICAL_WEEKLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rWeekly ); break; case ICAL_MONTHLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMonthly ); break; case ICAL_YEARLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rYearly ); break; case ICAL_NO_RECURRENCE: default: recur->setRecurrenceType( RecurrenceRule::rNone ); } // Frequency recur->setFrequency( r.interval ); // Duration & End Date if ( !icaltime_is_null_time( r.until ) ) { icaltimetype t = r.until; recur->setEndDt( readICalUtcDateTime( 0, t ) ); } else { if ( r.count == 0 ) { recur->setDuration( -1 ); } else { recur->setDuration( r.count ); } } // Week start setting short wkst = static_cast( ( r.week_start + 5 ) % 7 + 1 ); recur->setWeekStart( wkst ); // And now all BY* QList lst; int i; int index = 0; //@cond PRIVATE #define readSetByList( rrulecomp, setfunc ) \ index = 0; \ lst.clear(); \ while ( ( i = r.rrulecomp[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { \ lst.append( i ); \ } \ if ( !lst.isEmpty() ) { \ recur->setfunc( lst ); \ } //@endcond // BYSECOND, MINUTE and HOUR, MONTHDAY, YEARDAY, WEEKNUMBER, MONTH // and SETPOS are standard int lists, so we can treat them with the // same macro readSetByList( by_second, setBySeconds ); readSetByList( by_minute, setByMinutes ); readSetByList( by_hour, setByHours ); readSetByList( by_month_day, setByMonthDays ); readSetByList( by_year_day, setByYearDays ); readSetByList( by_week_no, setByWeekNumbers ); readSetByList( by_month, setByMonths ); readSetByList( by_set_pos, setBySetPos ); #undef readSetByList // BYDAY is a special case, since it's not an int list QList wdlst; short day; index = 0; while ( ( day = r.by_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { RecurrenceRule::WDayPos pos; pos.setDay( static_cast( ( icalrecurrencetype_day_day_of_week( day ) + 5 ) % 7 + 1 ) ); pos.setPos( icalrecurrencetype_day_position( day ) ); wdlst.append( pos ); } if ( !wdlst.isEmpty() ) { recur->setByDays( wdlst ); } // TODO: Store all X- fields of the RRULE inside the recurrence (so they are // preserved } void ICalFormatImpl::readAlarm( icalcomponent *alarm, Incidence *incidence, ICalTimeZones *tzlist ) { Alarm *ialarm = incidence->newAlarm(); ialarm->setRepeatCount( 0 ); ialarm->setEnabled( true ); // Determine the alarm's action type icalproperty *p = icalcomponent_get_first_property( alarm, ICAL_ACTION_PROPERTY ); Alarm::Type type = Alarm::Display; icalproperty_action action = ICAL_ACTION_DISPLAY; if ( !p ) { kDebug() << "Unknown type of alarm, using default"; // TODO: do something about unknown alarm type? } else { action = icalproperty_get_action( p ); switch ( action ) { case ICAL_ACTION_DISPLAY: type = Alarm::Display; break; case ICAL_ACTION_AUDIO: type = Alarm::Audio; break; case ICAL_ACTION_PROCEDURE: type = Alarm::Procedure; break; case ICAL_ACTION_EMAIL: type = Alarm::Email; break; default: break; // TODO: do something about invalid alarm type? } } ialarm->setType( type ); p = icalcomponent_get_first_property( alarm, ICAL_ANY_PROPERTY ); while ( p ) { icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_TRIGGER_PROPERTY: { icaltriggertype trigger = icalproperty_get_trigger( p ); if ( !icaltime_is_null_time( trigger.time ) ) { //set the trigger to a specific time (which is not in rfc2445, btw) ialarm->setTime( readICalUtcDateTime( p, trigger.time, tzlist ) ); } else { //set the trigger to an offset from the incidence start or end time. if ( !icaldurationtype_is_bad_duration( trigger.duration ) ) { Duration duration( readICalDuration( trigger.duration ) ); icalparameter *param = icalproperty_get_first_parameter( p, ICAL_RELATED_PARAMETER ); if ( param && icalparameter_get_related( param ) == ICAL_RELATED_END ) { ialarm->setEndOffset( duration ); } else { ialarm->setStartOffset( duration ); } } else { // a bad duration was encountered, just set a 0 duration from start ialarm->setStartOffset( Duration( 0 ) ); } } break; } case ICAL_DURATION_PROPERTY: { icaldurationtype duration = icalproperty_get_duration( p ); ialarm->setSnoozeTime( readICalDuration( duration ) ); break; } case ICAL_REPEAT_PROPERTY: ialarm->setRepeatCount( icalproperty_get_repeat( p ) ); break; case ICAL_DESCRIPTION_PROPERTY: { // Only in DISPLAY and EMAIL and PROCEDURE alarms QString description = QString::fromUtf8( icalproperty_get_description( p ) ); switch ( action ) { case ICAL_ACTION_DISPLAY: ialarm->setText( description ); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramArguments( description ); break; case ICAL_ACTION_EMAIL: ialarm->setMailText( description ); break; default: break; } break; } case ICAL_SUMMARY_PROPERTY: // Only in EMAIL alarm ialarm->setMailSubject( QString::fromUtf8( icalproperty_get_summary( p ) ) ); break; case ICAL_ATTENDEE_PROPERTY: { // Only in EMAIL alarm QString email = QString::fromUtf8( icalproperty_get_attendee( p ) ); if ( email.startsWith( QLatin1String( "mailto:" ), Qt::CaseInsensitive ) ) { email = email.mid( 7 ); } QString name; icalparameter *param = icalproperty_get_first_parameter( p, ICAL_CN_PARAMETER ); if ( param ) { name = QString::fromUtf8( icalparameter_get_cn( param ) ); } ialarm->addMailAddress( Person( name, email ) ); break; } case ICAL_ATTACH_PROPERTY: { // Only in AUDIO and EMAIL and PROCEDURE alarms Attachment *attach = readAttachment( p ); if ( attach && attach->isUri() ) { switch ( action ) { case ICAL_ACTION_AUDIO: ialarm->setAudioFile( attach->uri() ); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramFile( attach->uri() ); break; case ICAL_ACTION_EMAIL: ialarm->addMailAttachment( attach->uri() ); break; default: break; } } else { kDebug() << "Alarm attachments currently only support URIs," << "but no binary data"; } delete attach; break; } default: break; } p = icalcomponent_get_next_property( alarm, ICAL_ANY_PROPERTY ); } // custom properties d->readCustomProperties( alarm, ialarm ); // TODO: check for consistency of alarm properties } icaldatetimeperiodtype ICalFormatImpl::writeICalDatePeriod( const QDate &date ) { icaldatetimeperiodtype t; t.time = writeICalDate( date ); t.period = icalperiodtype_null_period(); return t; } icaltimetype ICalFormatImpl::writeICalDate( const QDate &date ) { icaltimetype t = icaltime_null_time(); t.year = date.year(); t.month = date.month(); t.day = date.day(); t.hour = 0; t.minute = 0; t.second = 0; t.is_date = 1; t.is_utc = 0; t.zone = 0; return t; } icaltimetype ICalFormatImpl::writeICalDateTime( const KDateTime &datetime ) { icaltimetype t = icaltime_null_time(); t.year = datetime.date().year(); t.month = datetime.date().month(); t.day = datetime.date().day(); t.hour = datetime.time().hour(); t.minute = datetime.time().minute(); t.second = datetime.time().second(); t.is_date = 0; t.zone = 0; // zone is NOT set t.is_utc = datetime.isUtc() ? 1 : 0; // _dumpIcaltime( t ); return t; } icalproperty *ICalFormatImpl::writeICalDateTimeProperty( const icalproperty_kind type, const KDateTime &dt, ICalTimeZones *tzlist, ICalTimeZones *tzUsedList ) { icaltimetype t; switch ( type ) { case ICAL_DTSTAMP_PROPERTY: case ICAL_CREATED_PROPERTY: case ICAL_LASTMODIFIED_PROPERTY: t = writeICalDateTime( dt.toUtc() ); break; default: t = writeICalDateTime( dt ); break; } icalproperty *p; switch ( type ) { case ICAL_DTSTAMP_PROPERTY: p = icalproperty_new_dtstamp( t ); break; case ICAL_CREATED_PROPERTY: p = icalproperty_new_created( t ); break; case ICAL_LASTMODIFIED_PROPERTY: p = icalproperty_new_lastmodified( t ); break; case ICAL_DTSTART_PROPERTY: // start date and time p = icalproperty_new_dtstart( t ); break; case ICAL_DTEND_PROPERTY: // end date and time p = icalproperty_new_dtend( t ); break; case ICAL_DUE_PROPERTY: p = icalproperty_new_due( t ); break; case ICAL_RECURRENCEID_PROPERTY: p = icalproperty_new_recurrenceid( t ); break; case ICAL_EXDATE_PROPERTY: p = icalproperty_new_exdate( t ); break; default: { icaldatetimeperiodtype tp; tp.time = t; tp.period = icalperiodtype_null_period(); switch ( type ) { case ICAL_RDATE_PROPERTY: p = icalproperty_new_rdate( tp ); break; default: return 0; } } } KTimeZone ktz; if ( !t.is_utc ) { ktz = dt.timeZone(); } if ( ktz.isValid() ) { if ( tzlist ) { ICalTimeZone tz = tzlist->zone( ktz.name() ); if ( !tz.isValid() ) { // The time zone isn't in the list of known zones for the calendar // - add it to the calendar's zone list ICalTimeZone tznew( ktz ); tzlist->add( tznew ); tz = tznew; } if ( tzUsedList ) { tzUsedList->add( tz ); } } icalproperty_add_parameter( p, icalparameter_new_tzid( ktz.name().toUtf8() ) ); } return p; } KDateTime ICalFormatImpl::readICalDateTime( icalproperty *p, const icaltimetype &t, ICalTimeZones *tzlist, bool utc ) { // kDebug(); // _dumpIcaltime( t ); KDateTime::Spec timeSpec; if ( t.is_utc || t.zone == icaltimezone_get_utc_timezone() ) { timeSpec = KDateTime::UTC; // the time zone is UTC utc = false; // no need to convert to UTC } else { if ( !tzlist ) { utc = true; // should be UTC, but it isn't } icalparameter *param = p ? icalproperty_get_first_parameter( p, ICAL_TZID_PARAMETER ) : 0; const char *tzid = param ? icalparameter_get_tzid( param ) : 0; if ( !tzid ) { timeSpec = KDateTime::ClockTime; } else { QString tzidStr = QString::fromUtf8( tzid ); ICalTimeZone tz; if ( tzlist ) { tz = tzlist->zone( tzidStr ); } if ( !tz.isValid() ) { // The time zone is not in the existing list for the calendar. // Try to read it from the system or libical databases. ICalTimeZoneSource tzsource; ICalTimeZone newtz = tzsource.standardZone( tzidStr ); if ( newtz.isValid() && tzlist ) { tzlist->add( newtz ); } tz = newtz; } timeSpec = tz.isValid() ? KDateTime::Spec( tz ) : KDateTime::LocalZone; } } KDateTime result( QDate( t.year, t.month, t.day ), QTime( t.hour, t.minute, t.second ), timeSpec ); return utc ? result.toUtc() : result; } QDate ICalFormatImpl::readICalDate( icaltimetype t ) { return QDate( t.year, t.month, t.day ); } KDateTime ICalFormatImpl::readICalDateTimeProperty( icalproperty *p, ICalTimeZones *tzlist, bool utc ) { icaldatetimeperiodtype tp; icalproperty_kind kind = icalproperty_isa( p ); switch ( kind ) { case ICAL_CREATED_PROPERTY: // UTC date/time tp.time = icalproperty_get_created( p ); utc = true; break; case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time tp.time = icalproperty_get_lastmodified( p ); utc = true; break; case ICAL_DTSTART_PROPERTY: // start date and time (UTC for freebusy) tp.time = icalproperty_get_dtstart( p ); break; case ICAL_DTEND_PROPERTY: // end date and time (UTC for freebusy) tp.time = icalproperty_get_dtend( p ); break; case ICAL_DUE_PROPERTY: // due date/time tp.time = icalproperty_get_due( p ); break; case ICAL_COMPLETED_PROPERTY: // UTC completion date/time tp.time = icalproperty_get_completed( p ); utc = true; break; case ICAL_RECURRENCEID_PROPERTY: tp.time = icalproperty_get_recurrenceid( p ); break; case ICAL_EXDATE_PROPERTY: tp.time = icalproperty_get_exdate( p ); break; default: switch ( kind ) { case ICAL_RDATE_PROPERTY: tp = icalproperty_get_rdate( p ); break; default: return KDateTime(); } if ( !icaltime_is_valid_time( tp.time ) ) { return KDateTime(); // a time period was found (not implemented yet) } break; } if ( tp.time.is_date ) { return KDateTime( readICalDate( tp.time ), KDateTime::Spec::ClockTime() ); } else { return readICalDateTime( p, tp.time, tzlist, utc ); } } icaldurationtype ICalFormatImpl::writeICalDuration( const Duration &duration ) { // should be able to use icaldurationtype_from_int(), except we know // that some older tools do not properly support weeks. So we never // set a week duration, only days icaldurationtype d; int value = duration.value(); d.is_neg = ( value < 0 ) ? 1 : 0; if ( value < 0 ) { value = -value; } if ( duration.isDaily() ) { d.weeks = 0; d.days = value; d.hours = d.minutes = d.seconds = 0; } else { d.weeks = 0; d.days = value / gSecondsPerDay; value %= gSecondsPerDay; d.hours = value / gSecondsPerHour; value %= gSecondsPerHour; d.minutes = value / gSecondsPerMinute; value %= gSecondsPerMinute; d.seconds = value; } return d; } Duration ICalFormatImpl::readICalDuration( icaldurationtype d ) { int days = d.weeks * 7; days += d.days; int seconds = d.hours * gSecondsPerHour; seconds += d.minutes * gSecondsPerMinute; seconds += d.seconds; if ( seconds ) { seconds += days * gSecondsPerDay; if ( d.is_neg ) { seconds = -seconds; } return Duration( seconds, Duration::Seconds ); } else { if ( d.is_neg ) { days = -days; } return Duration( days, Duration::Days ); } } icalcomponent *ICalFormatImpl::createCalendarComponent( Calendar *cal ) { icalcomponent *calendar; // Root component calendar = icalcomponent_new( ICAL_VCALENDAR_COMPONENT ); icalproperty *p; // Product Identifier p = icalproperty_new_prodid( CalFormat::productId().toUtf8() ); icalcomponent_add_property( calendar, p ); // iCalendar version (2.0) p = icalproperty_new_version( const_cast(_ICAL_VERSION) ); icalcomponent_add_property( calendar, p ); // Add time zone if ( cal && cal->timeZones() ) { const ICalTimeZones::ZoneMap zmaps = cal->timeZones()->zones(); for ( ICalTimeZones::ZoneMap::ConstIterator it=zmaps.constBegin(); it != zmaps.constEnd(); ++it ) { icaltimezone *icaltz = (*it).icalTimezone(); if ( !icaltz ) { kError() << "bad time zone"; } else { icalcomponent *tz = icalcomponent_new_clone( icaltimezone_get_component( icaltz ) ); icalcomponent_add_component( calendar, tz ); icaltimezone_free( icaltz, 1 ); } } } // Custom properties if( cal != 0 ) { d->writeCustomProperties( calendar, cal ); } return calendar; } // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. // and break it down from its tree-like format into the dictionary format // that is used internally in the ICalFormatImpl. bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar ) { // this function will populate the caldict dictionary and other event // lists. It turns vevents into Events and then inserts them. if ( !calendar ) { return false; } // TODO: check for METHOD icalproperty *p; p = icalcomponent_get_first_property( calendar, ICAL_PRODID_PROPERTY ); if ( !p ) { kDebug() << "No PRODID property found"; d->mLoadedProductId = ""; } else { d->mLoadedProductId = QString::fromUtf8( icalproperty_get_prodid( p ) ); delete d->mCompat; d->mCompat = CompatFactory::createCompat( d->mLoadedProductId ); } p = icalcomponent_get_first_property( calendar, ICAL_VERSION_PROPERTY ); if ( !p ) { kDebug() << "No VERSION property found"; d->mParent->setException( new ErrorFormat( ErrorFormat::CalVersionUnknown ) ); return false; } else { const char *version = icalproperty_get_version( p ); if ( !version ) { kDebug() << "No VERSION property found"; d->mParent->setException( new ErrorFormat( ErrorFormat::CalVersionUnknown, i18n( "No VERSION property found" ) ) ); return false; } if ( strcmp( version, "1.0" ) == 0 ) { kDebug() << "Expected iCalendar, got vCalendar"; d->mParent->setException( new ErrorFormat( ErrorFormat::CalVersion1, i18n( "Expected iCalendar, got vCalendar format" ) ) ); return false; } else if ( strcmp( version, "2.0" ) != 0 ) { kDebug() << "Expected iCalendar, got unknown format"; d->mParent->setException( new ErrorFormat( ErrorFormat::CalVersionUnknown, i18n( "Expected iCalendar, got unknown format" ) ) ); return false; } } // Populate the calendar's time zone collection with all VTIMEZONE components ICalTimeZones *tzlist = cal->timeZones(); ICalTimeZoneSource tzs; tzs.parse( calendar, *tzlist ); // custom properties d->readCustomProperties( calendar, cal ); // Store all events with a relatedTo property in a list for post-processing d->mEventsRelate.clear(); d->mTodosRelate.clear(); // TODO: make sure that only actually added events go to this lists. icalcomponent *c; // Iterate through all todos cal->beginBatchAdding(); c = icalcomponent_get_first_component( calendar, ICAL_VTODO_COMPONENT ); while ( c ) { Todo *todo = readTodo( c, tzlist ); if ( todo ) { Todo *old = cal->todo( todo->uid() ); if ( old ) { cal->deleteTodo( old ); d->mTodosRelate.removeAll( old ); } cal->addTodo( todo ); } c = icalcomponent_get_next_component( calendar, ICAL_VTODO_COMPONENT ); } // Iterate through all events c = icalcomponent_get_first_component( calendar, ICAL_VEVENT_COMPONENT ); while ( c ) { Event *event = readEvent( c, tzlist ); if ( event ) { Event *old = cal->event( event->uid() ); if ( old ) { cal->deleteEvent( old ); d->mEventsRelate.removeAll( old ); } cal->addEvent( event ); } c = icalcomponent_get_next_component( calendar, ICAL_VEVENT_COMPONENT ); } // Iterate through all journals c = icalcomponent_get_first_component( calendar, ICAL_VJOURNAL_COMPONENT ); while ( c ) { Journal *journal = readJournal( c, tzlist ); if ( journal ) { Journal *old = cal->journal( journal->uid() ); if ( old ) { cal->deleteJournal( old ); } cal->addJournal( journal ); } c = icalcomponent_get_next_component( calendar, ICAL_VJOURNAL_COMPONENT ); } cal->endBatchAdding(); // Post-Process list of events with relations, put Event objects in relation Event::List::ConstIterator eIt; for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) { (*eIt)->setRelatedTo( cal->incidence( (*eIt)->relatedToUid() ) ); } Todo::List::ConstIterator tIt; for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) { (*tIt)->setRelatedTo( cal->incidence( (*tIt)->relatedToUid() ) ); } // TODO: Remove any previous time zones no longer referenced in the calendar return true; } QString ICalFormatImpl::extractErrorProperty( icalcomponent *c ) { QString errorMessage; icalproperty *error; error = icalcomponent_get_first_property( c, ICAL_XLICERROR_PROPERTY ); while ( error ) { errorMessage += icalproperty_get_xlicerror( error ); errorMessage += '\n'; error = icalcomponent_get_next_property( c, ICAL_XLICERROR_PROPERTY ); } return errorMessage; } void ICalFormatImpl::dumpIcalRecurrence( icalrecurrencetype r ) { int i; kDebug() << " Freq:" << int( r.freq ); kDebug() << " Until:" << icaltime_as_ical_string( r.until ); kDebug() << " Count:" << r.count; if ( r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Day: "; while ( ( i = r.by_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } kDebug() << out; } if ( r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Month Day: "; while ( ( i = r.by_month_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } kDebug() << out; } if ( r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Year Day: "; while ( ( i = r.by_year_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } kDebug() << out; } if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Month: "; while ( ( i = r.by_month[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } kDebug() << out; } if ( r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Set Pos: "; while ( ( i = r.by_set_pos[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { kDebug() << "=========" << i; out.append( QString::number( i ) + ' ' ); } kDebug() << out; } } icalcomponent *ICalFormatImpl::createScheduleComponent( IncidenceBase *incidence, iTIPMethod method ) { icalcomponent *message = createCalendarComponent(); #if 0 // // Old versions of Outlook 2003 cannot handle TZIDs. So rely on UTC times. // Keep the code here in case we ever need it. // // Create VTIMEZONE components for this incidence ICalTimeZones zones; if ( incidence ) { if ( incidence->type() == "Event" ) { Event *ev = static_cast( incidence ); if ( ev ) { if ( ev->dtStart().isValid() ) { zones.add( ICalTimeZone( ev->dtStart().timeZone() ) ); } if ( ev->hasEndDate() && ev->dtEnd().isValid() ) { zones.add( ICalTimeZone( ev->dtEnd().timeZone() ) ); } } } else if ( incidence->type() == "Todo" ) { Todo *t = static_cast( incidence ); if ( t ) { if ( t->hasStartDate() && t->dtStart().isValid() ) { zones.add( ICalTimeZone( t->dtStart( true ).timeZone() ) ); } if ( t->hasDueDate() && t->dtDue().isValid() ) { zones.add( ICalTimeZone( t->dtDue().timeZone() ) ); } } } else if ( incidence->type() == "Journal" ) { Journal *j = static_cast( incidence ); if ( j ) { if ( j->dtStart().isValid() ) { zones.add( ICalTimeZone( j->dtStart().timeZone() ) ); } } } const ICalTimeZones::ZoneMap zmaps = zones.zones(); for ( ICalTimeZones::ZoneMap::ConstIterator it=zmaps.constBegin(); it != zmaps.constEnd(); ++it ) { icaltimezone *icaltz = (*it).icalTimezone(); if ( !icaltz ) { kError() << "bad time zone"; } else { icalcomponent *tz = icalcomponent_new_clone( icaltimezone_get_component( icaltz ) ); icalcomponent_add_component( message, tz ); icaltimezone_free( icaltz, 1 ); } } } #else if ( incidence ) { if ( incidence->type() == "Event" ) { Event *ev = static_cast( incidence ); if ( ev ) { if ( ev->dtStart().isValid() ) { ev->setDtStart( ev->dtStart().toUtc() ); } if ( ev->hasEndDate() && ev->dtEnd().isValid() ) { ev->setDtEnd( ev->dtEnd().toUtc() ); } } } else if ( incidence->type() == "Todo" ) { Todo *t = static_cast( incidence ); if ( t ) { if ( t->hasStartDate() && t->dtStart().isValid() ) { t->setDtStart( t->dtStart().toUtc() ); } if ( t->hasDueDate() && t->dtDue().isValid() ) { t->setDtDue( t->dtDue().toUtc() ); } } } else if ( incidence->type() == "Journal" ) { Journal *j = static_cast( incidence ); if ( j ) { if ( j->dtStart().isValid() ) { j->setDtStart( j->dtStart().toUtc() ); } } } } #endif icalproperty_method icalmethod = ICAL_METHOD_NONE; switch ( method ) { case iTIPPublish: icalmethod = ICAL_METHOD_PUBLISH; break; case iTIPRequest: icalmethod = ICAL_METHOD_REQUEST; break; case iTIPRefresh: icalmethod = ICAL_METHOD_REFRESH; break; case iTIPCancel: icalmethod = ICAL_METHOD_CANCEL; break; case iTIPAdd: icalmethod = ICAL_METHOD_ADD; break; case iTIPReply: icalmethod = ICAL_METHOD_REPLY; break; case iTIPCounter: icalmethod = ICAL_METHOD_COUNTER; break; case iTIPDeclineCounter: icalmethod = ICAL_METHOD_DECLINECOUNTER; break; default: kDebug() << "Unknown method"; return message; } icalcomponent_add_property( message, icalproperty_new_method( icalmethod ) ); icalcomponent *inc = writeIncidence( incidence, method ); /* * RFC 2446 states in section 3.4.3 ( REPLY to a VTODO ), that * a REQUEST-STATUS property has to be present. For the other two, event and * free busy, it can be there, but is optional. Until we do more * fine grained handling, assume all is well. Note that this is the * status of the _request_, not the attendee. Just to avoid confusion. * - till */ if ( icalmethod == ICAL_METHOD_REPLY ) { struct icalreqstattype rst; rst.code = ICAL_2_0_SUCCESS_STATUS; rst.desc = 0; rst.debug = 0; icalcomponent_add_property( inc, icalproperty_new_requeststatus( rst ) ); } icalcomponent_add_component( message, inc ); return message; }