-rw-r--r-- | libkcal/icalformatimpl.cpp | 2173 |
1 files changed, 2173 insertions, 0 deletions
diff --git a/libkcal/icalformatimpl.cpp b/libkcal/icalformatimpl.cpp new file mode 100644 index 0000000..e5c27a0 --- a/dev/null +++ b/libkcal/icalformatimpl.cpp @@ -0,0 +1,2173 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qfile.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> + +extern "C" { + #include <ical.h> + #include <icalss.h> + #include <icalparser.h> + #include <icalrestriction.h> +} + +#include "calendar.h" +#include "journal.h" +#include "icalformat.h" +#include "icalformatimpl.h" +#include "compat.h" + +#define _ICAL_VERSION "2.0" + +using namespace KCal; + +const int gSecondsPerMinute = 60; +const int gSecondsPerHour = gSecondsPerMinute * 60; +const int gSecondsPerDay = gSecondsPerHour * 24; +const int gSecondsPerWeek = gSecondsPerDay * 7; + +ICalFormatImpl::ICalFormatImpl( ICalFormat *parent ) : + mParent( parent ), mCalendarVersion( 0 ) +{ + mCompat = new Compat; +} + +ICalFormatImpl::~ICalFormatImpl() +{ + delete mCompat; +} + +class ToStringVisitor : public Incidence::Visitor +{ + public: + ToStringVisitor( ICalFormatImpl *impl ) : mImpl( impl ), mComponent( 0 ) {} + + 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; } + + icalcomponent *component() { return mComponent; } + + private: + ICalFormatImpl *mImpl; + icalcomponent *mComponent; +}; + +icalcomponent *ICalFormatImpl::writeIncidence(Incidence *incidence) +{ + ToStringVisitor v( this ); + incidence->accept(v); + return v.component(); +} + +icalcomponent *ICalFormatImpl::writeTodo(Todo *todo) +{ + QString tmpStr; + QStringList tmpStrList; + + icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); + + writeIncidence(vtodo,todo); + + // due date + if (todo->hasDueDate()) { + icaltimetype due; + if (todo->doesFloat()) { + due = writeICalDate(todo->dtDue().date()); + } else { + due = writeICalDateTime(todo->dtDue()); + } + icalcomponent_add_property(vtodo,icalproperty_new_due(due)); + } + + // start time + if (todo->hasStartDate()) { + icaltimetype start; + if (todo->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << todo->summary() << " floats." << endl; + start = writeICalDate(todo->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << todo->summary() << " has time." << endl; + start = writeICalDateTime(todo->dtStart()); + } + icalcomponent_add_property(vtodo,icalproperty_new_dtstart(start)); + } + + // completion date + if (todo->isCompleted()) { + if (!todo->hasCompletedDate()) { + // If todo was created by KOrganizer <2.2 it has no correct completion + // date. Set it to now. + todo->setCompleted(QDateTime::currentDateTime()); + } + icaltimetype completed = writeICalDateTime(todo->completed()); + icalcomponent_add_property(vtodo,icalproperty_new_completed(completed)); + } + + icalcomponent_add_property(vtodo, + icalproperty_new_percentcomplete(todo->percentComplete())); + + return vtodo; +} + +icalcomponent *ICalFormatImpl::writeEvent(Event *event) +{ + kdDebug(5800) << "Write Event '" << event->summary() << "' (" << event->uid() + << ")" << endl; + + QString tmpStr; + QStringList tmpStrList; + + icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); + + writeIncidence(vevent,event); + + // start time + icaltimetype start; + if (event->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl; + start = writeICalDate(event->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << endl; + start = writeICalDateTime(event->dtStart()); + } + icalcomponent_add_property(vevent,icalproperty_new_dtstart(start)); + + if (event->hasEndDate()) { + // end time + icaltimetype end; + if (event->doesFloat()) { +// kdDebug(5800) << "§§ Event " << event->summary() << " floats." << endl; + // +1 day because end date is non-inclusive. + end = writeICalDate( event->dtEnd().date().addDays( 1 ) ); + } else { +// kdDebug(5800) << "§§ Event " << event->summary() << " has time." << endl; + end = writeICalDateTime(event->dtEnd()); + } + icalcomponent_add_property(vevent,icalproperty_new_dtend(end)); + } + +// TODO: attachments, resources +#if 0 + // attachments + tmpStrList = anEvent->attachments(); + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) + addPropValue(vevent, VCAttachProp, (*it).utf8()); + + // resources + tmpStrList = anEvent->resources(); + tmpStr = tmpStrList.join(";"); + if (!tmpStr.isEmpty()) + addPropValue(vevent, VCResourcesProp, tmpStr.utf8()); + +#endif + + // Transparency + switch( event->transparency() ) { + case Event::Transparent: + icalcomponent_add_property(vevent, icalproperty_new_transp("TRANSPARENT")); + break; + case Event::Opaque: + icalcomponent_add_property(vevent, icalproperty_new_transp("OPAQUE")); + break; + } + + return vevent; +} + +icalcomponent *ICalFormatImpl::writeFreeBusy(FreeBusy *freebusy, + Scheduler::Method method) +{ +#if QT_VERSION >= 300 + kdDebug(5800) << "icalformatimpl: writeFreeBusy: startDate: " + << freebusy->dtStart().toString("ddd MMMM d yyyy: h:m:s ap") << " End Date: " + << freebusy->dtEnd().toString("ddd MMMM d yyyy: h:m:s ap") << endl; +#endif + + icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); + + writeIncidenceBase(vfreebusy,freebusy); + + icalcomponent_add_property(vfreebusy, icalproperty_new_dtstart( + writeICalDateTime(freebusy->dtStart()))); + + icalcomponent_add_property(vfreebusy, icalproperty_new_dtend( + writeICalDateTime(freebusy->dtEnd()))); + + if (method == Scheduler::Request) { + icalcomponent_add_property(vfreebusy,icalproperty_new_uid( + freebusy->uid().utf8())); + } + + //Loops through all the periods in the freebusy object + QValueList<Period> list = freebusy->busyPeriods(); + QValueList<Period>::Iterator it; + icalperiodtype period; + for (it = list.begin(); it!= list.end(); ++it) { + period.start = writeICalDateTime((*it).start()); + period.end = writeICalDateTime((*it).end()); + icalcomponent_add_property(vfreebusy, icalproperty_new_freebusy(period) ); + } + + return vfreebusy; +} + +icalcomponent *ICalFormatImpl::writeJournal(Journal *journal) +{ + icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT); + + writeIncidence(vjournal,journal); + + // start time + if (journal->dtStart().isValid()) { + icaltimetype start; + if (journal->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl; + start = writeICalDate(journal->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << endl; + start = writeICalDateTime(journal->dtStart()); + } + icalcomponent_add_property(vjournal,icalproperty_new_dtstart(start)); + } + + return vjournal; +} + +void ICalFormatImpl::writeIncidence(icalcomponent *parent,Incidence *incidence) +{ + // pilot sync stuff +// TODO: move this application-specific code to kpilot + if (incidence->pilotId()) { + incidence->setNonKDECustomProperty("X-PILOTID", QString::number(incidence->pilotId())); + incidence->setNonKDECustomProperty("X-PILOTSTAT", QString::number(incidence->syncStatus())); + } + if (incidence->zaurusId() >= 0) { + incidence->setNonKDECustomProperty("X-ZAURUSID", QString::number(incidence->zaurusId())); + } + + if (incidence->zaurusUid() > 0) { + incidence->setNonKDECustomProperty("X-ZAURUSUID", QString::number(incidence->zaurusUid())); + } + if (incidence->zaurusStat() > 0) { + incidence->setNonKDECustomProperty("X-ZAURUSSTAT", QString::number(incidence->zaurusStat())); + } + + writeIncidenceBase(parent,incidence); + if (incidence->cancelled()) { + icalcomponent_add_property(parent,icalproperty_new_status(ICAL_STATUS_CANCELLED)); + } + + // creation date + icalcomponent_add_property(parent,icalproperty_new_created( + writeICalDateTime(incidence->created()))); + + // unique id + icalcomponent_add_property(parent,icalproperty_new_uid( + incidence->uid().utf8())); + + // revision + icalcomponent_add_property(parent,icalproperty_new_sequence( + incidence->revision())); + + // last modification date + icalcomponent_add_property(parent,icalproperty_new_lastmodified( + writeICalDateTime(incidence->lastModified()))); + + // description + if (!incidence->description().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_description( + incidence->description().utf8())); + } + + // summary + if (!incidence->summary().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_summary( + incidence->summary().utf8())); + } + + // location + if (!incidence->location().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_location( + incidence->location().utf8())); + } + +// TODO: + // status +// addPropValue(parent, VCStatusProp, incidence->getStatusStr().utf8()); + + // secrecy + const char *classStr; + switch (incidence->secrecy()) { + case Incidence::SecrecyPublic: + classStr = "PUBLIC"; + break; + case Incidence::SecrecyConfidential: + classStr = "CONFIDENTIAL"; + break; + case Incidence::SecrecyPrivate: + default: + classStr = "PRIVATE"; + break; + } + icalcomponent_add_property(parent,icalproperty_new_class(classStr)); + + // priority + icalcomponent_add_property(parent,icalproperty_new_priority( + incidence->priority())); + + // categories + QStringList categories = incidence->categories(); + QStringList::Iterator it; + for(it = categories.begin(); it != categories.end(); ++it ) { + icalcomponent_add_property(parent,icalproperty_new_categories((*it).utf8())); + } +// TODO: Ensure correct concatenation of categories properties. + +/* + // categories + tmpStrList = incidence->getCategories(); + tmpStr = ""; + QString catStr; + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) { + catStr = *it; + if (catStr[0] == ' ') + tmpStr += catStr.mid(1); + else + tmpStr += catStr; + // this must be a ';' character as the vCalendar specification requires! + // vcc.y has been hacked to translate the ';' to a ',' when the vcal is + // read in. + tmpStr += ";"; + } + if (!tmpStr.isEmpty()) { + tmpStr.truncate(tmpStr.length()-1); + icalcomponent_add_property(parent,icalproperty_new_categories( + writeText(incidence->getCategories().join(";")))); + } +*/ + + // related event + if (incidence->relatedTo()) { + icalcomponent_add_property(parent,icalproperty_new_relatedto( + incidence->relatedTo()->uid().utf8())); + } + + // recurrence rule stuff + Recurrence *recur = incidence->recurrence(); + if (recur->doesRecur()) { + + icalcomponent_add_property(parent,writeRecurrenceRule(recur)); + } + + // recurrence excpetion dates + DateList dateList = incidence->exDates(); + DateList::ConstIterator exIt; + for(exIt = dateList.begin(); exIt != dateList.end(); ++exIt) { + icalcomponent_add_property(parent,icalproperty_new_exdate( + writeICalDate(*exIt))); + } + + // attachments + QPtrList<Attachment> attachments = incidence->attachments(); + for (Attachment *at = attachments.first(); at; at = attachments.next()) + icalcomponent_add_property(parent,writeAttachment(at)); + + // alarms + QPtrList<Alarm> alarms = incidence->alarms(); + Alarm* alarm; + for (alarm = alarms.first(); alarm; alarm = alarms.next()) { + if (alarm->enabled()) { + kdDebug(5800) << "Write alarm for " << incidence->summary() << endl; + icalcomponent_add_component(parent,writeAlarm(alarm)); + } + } + + // duration + +// turned off as it always is set to PTS0 (and must not occur together with DTEND + +// if (incidence->hasDuration()) { +// icaldurationtype duration; +// duration = writeICalDuration(incidence->duration()); +// icalcomponent_add_property(parent,icalproperty_new_duration(duration)); +// } +} + +void ICalFormatImpl::writeIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase) +{ + icalcomponent_add_property(parent,icalproperty_new_dtstamp( + writeICalDateTime(QDateTime::currentDateTime()))); + + // organizer stuff + icalcomponent_add_property(parent,icalproperty_new_organizer( + ("MAILTO:" + incidenceBase->organizer()).utf8())); + + // attendees + if (incidenceBase->attendeeCount() != 0) { + QPtrList<Attendee> al = incidenceBase->attendees(); + QPtrListIterator<Attendee> ai(al); + for (; ai.current(); ++ai) { + icalcomponent_add_property(parent,writeAttendee(ai.current())); + } + } + + // custom properties + writeCustomProperties(parent, incidenceBase); +} + +void ICalFormatImpl::writeCustomProperties(icalcomponent *parent,CustomProperties *properties) +{ + QMap<QCString, QString> custom = properties->customProperties(); + for (QMap<QCString, QString>::Iterator c = custom.begin(); c != custom.end(); ++c) { + icalproperty *p = icalproperty_new_x(c.data().utf8()); + icalproperty_set_x_name(p,c.key()); + icalcomponent_add_property(parent,p); + } +} + +icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee) +{ + icalproperty *p = icalproperty_new_attendee("mailto:" + attendee->email().utf8()); + + if (!attendee->name().isEmpty()) { + icalproperty_add_parameter(p,icalparameter_new_cn(attendee->name().utf8())); + } + + + 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().utf8()); + icalparameter_set_xname(icalparameter_uid,"X-UID"); + icalproperty_add_parameter(p,icalparameter_uid); + } + + return p; +} + +icalproperty *ICalFormatImpl::writeAttachment(Attachment *att) +{ + icalattachtype* attach = icalattachtype_new(); + if (att->isURI()) + icalattachtype_set_url(attach, att->uri().utf8().data()); + else + icalattachtype_set_base64(attach, att->data(), 0); + + icalproperty *p = icalproperty_new_attach(attach); + + if (!att->mimeType().isEmpty()) + icalproperty_add_parameter(p,icalparameter_new_fmttype(att->mimeType().utf8().data())); + + if (att->isBinary()) { + icalproperty_add_parameter(p,icalparameter_new_value(ICAL_VALUE_BINARY)); + icalproperty_add_parameter(p,icalparameter_new_encoding(ICAL_ENCODING_BASE64)); + } + return p; +} + +icalproperty *ICalFormatImpl::writeRecurrenceRule(Recurrence *recur) +{ +// kdDebug(5800) << "ICalFormatImpl::writeRecurrenceRule()" << endl; + + icalrecurrencetype r; + + icalrecurrencetype_clear(&r); + + int index = 0; + int index2 = 0; + + QPtrList<Recurrence::rMonthPos> tmpPositions; + QPtrList<int> tmpDays; + int *tmpDay; + Recurrence::rMonthPos *tmpPos; + bool datetime = false; + int day; + int i; + + switch(recur->doesRecur()) { + case Recurrence::rMinutely: + r.freq = ICAL_MINUTELY_RECURRENCE; + datetime = true; + break; + case Recurrence::rHourly: + r.freq = ICAL_HOURLY_RECURRENCE; + datetime = true; + break; + case Recurrence::rDaily: + r.freq = ICAL_DAILY_RECURRENCE; + break; + case Recurrence::rWeekly: + r.freq = ICAL_WEEKLY_RECURRENCE; + r.week_start = static_cast<icalrecurrencetype_weekday>(recur->weekStart()%7 + 1); + for (i = 0; i < 7; i++) { + if (recur->days().testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + r.by_day[index++] = icalrecurrencetype_day_day_of_week(day); + } + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rMonthlyPos: + r.freq = ICAL_MONTHLY_RECURRENCE; + + tmpPositions = recur->monthPositions(); + for (tmpPos = tmpPositions.first(); + tmpPos; + tmpPos = tmpPositions.next()) { + for (i = 0; i < 7; i++) { + if (tmpPos->rDays.testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + day += tmpPos->rPos*8; + if (tmpPos->negative) day = -day; + r.by_day[index++] = day; + } + } + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rMonthlyDay: + r.freq = ICAL_MONTHLY_RECURRENCE; + + tmpDays = recur->monthDays(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_month_day[index++] = icalrecurrencetype_day_position(*tmpDay*8);//*tmpDay); + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rYearlyMonth: + case Recurrence::rYearlyPos: + r.freq = ICAL_YEARLY_RECURRENCE; + + tmpDays = recur->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_month[index++] = *tmpDay; + } +// r.by_set_pos[index] = ICAL_RECURRENCE_ARRAY_MAX; + if (recur->doesRecur() == Recurrence::rYearlyPos) { + tmpPositions = recur->monthPositions(); + for (tmpPos = tmpPositions.first(); + tmpPos; + tmpPos = tmpPositions.next()) { + for (i = 0; i < 7; i++) { + if (tmpPos->rDays.testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + day += tmpPos->rPos*8; + if (tmpPos->negative) day = -day; + r.by_day[index2++] = day; + } + } + } +// r.by_day[index2] = ICAL_RECURRENCE_ARRAY_MAX; + } + break; + case Recurrence::rYearlyDay: + r.freq = ICAL_YEARLY_RECURRENCE; + + tmpDays = recur->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_year_day[index++] = *tmpDay; + } +// r.by_year_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + default: + r.freq = ICAL_NO_RECURRENCE; + kdDebug(5800) << "ICalFormatImpl::writeRecurrence(): no recurrence" << endl; + break; + } + + r.interval = recur->frequency(); + + if (recur->duration() > 0) { + r.count = recur->duration(); + } else if (recur->duration() == -1) { + r.count = 0; + } else { + if (datetime) + r.until = writeICalDateTime(recur->endDateTime()); + else + r.until = writeICalDate(recur->endDate()); + } + +// Debug output +#if 0 + const char *str = icalrecurrencetype_as_string(&r); + if (str) { + kdDebug(5800) << " String: " << str << endl; + } else { + kdDebug(5800) << " No String" << endl; + } +#endif + + return icalproperty_new_rrule(r); +} + +icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm) +{ + icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT); + + icalproperty_action action; + icalattachtype *attach = 0; + + switch (alarm->type()) { + case Alarm::Procedure: + action = ICAL_ACTION_PROCEDURE; + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName(alarm->programFile()).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + if (!alarm->programArguments().isEmpty()) { + icalcomponent_add_property(a,icalproperty_new_description(alarm->programArguments().utf8())); + } + break; + case Alarm::Audio: + action = ICAL_ACTION_AUDIO; + if (!alarm->audioFile().isEmpty()) { + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName( alarm->audioFile() ).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + } + break; + case Alarm::Email: { + action = ICAL_ACTION_EMAIL; + QValueList<Person> addresses = alarm->mailAddresses(); + for (QValueList<Person>::Iterator ad = addresses.begin(); ad != addresses.end(); ++ad) { + icalproperty *p = icalproperty_new_attendee("MAILTO:" + (*ad).email().utf8()); + if (!(*ad).name().isEmpty()) { + icalproperty_add_parameter(p,icalparameter_new_cn((*ad).name().utf8())); + } + icalcomponent_add_property(a,p); + } + icalcomponent_add_property(a,icalproperty_new_summary(alarm->mailSubject().utf8())); + icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8())); + QStringList attachments = alarm->mailAttachments(); + if (attachments.count() > 0) { + for (QStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at) { + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName( *at ).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + } + } + break; + } + case Alarm::Display: + action = ICAL_ACTION_DISPLAY; + icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8())); + break; + case Alarm::Invalid: + default: + kdDebug(5800) << "Unknown type of alarm" << endl; + action = ICAL_ACTION_NONE; + break; + } + icalcomponent_add_property(a,icalproperty_new_action(action)); + + // Trigger time + icaltriggertype trigger; + if ( alarm->hasTime() ) { + trigger.time = writeICalDateTime(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 = icaldurationtype_from_int( offset.asSeconds() ); + } + 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( + icaldurationtype_from_int(alarm->snoozeTime()*60))); + } + + // Custom properties + QMap<QCString, QString> custom = alarm->customProperties(); + for (QMap<QCString, QString>::Iterator c = custom.begin(); c != custom.end(); ++c) { + icalproperty *p = icalproperty_new_x(c.data().utf8()); + icalproperty_set_x_name(p,c.key()); + icalcomponent_add_property(a,p); + } + + return a; +} + +Todo *ICalFormatImpl::readTodo(icalcomponent *vtodo) +{ + Todo *todo = new Todo; + + readIncidence(vtodo,todo); + + icalproperty *p = icalcomponent_get_first_property(vtodo,ICAL_ANY_PROPERTY); + +// int intvalue; + icaltimetype icaltime; + + QStringList categories; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DUE_PROPERTY: // due date + icaltime = icalproperty_get_due(p); + if (icaltime.is_date) { + todo->setDtDue(QDateTime(readICalDate(icaltime),QTime(0,0,0))); + todo->setFloats(true); + + } else { + todo->setDtDue(readICalDateTime(icaltime)); + todo->setFloats(false); + } + todo->setHasDueDate(true); + break; + + case ICAL_COMPLETED_PROPERTY: // completion date + icaltime = icalproperty_get_completed(p); + todo->setCompleted(readICalDateTime(icaltime)); + 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))); + mTodosRelate.append(todo); + break; + + case ICAL_DTSTART_PROPERTY: + // Flag that todo has start date. Value is read in by readIncidence(). + todo->setHasStartDate(true); + break; + + default: +// kdDebug(5800) << "ICALFormat::readTodo(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(vtodo,ICAL_ANY_PROPERTY); + } + + return todo; +} + +Event *ICalFormatImpl::readEvent(icalcomponent *vevent) +{ + Event *event = new Event; + event->setFloats(false); + + readIncidence(vevent,event); + + icalproperty *p = icalcomponent_get_first_property(vevent,ICAL_ANY_PROPERTY); + +// int intvalue; + icaltimetype icaltime; + + QStringList categories; + QString transparency; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DTEND_PROPERTY: // start date and time + icaltime = icalproperty_get_dtend(p); + if (icaltime.is_date) { + event->setFloats( true ); + // End date is non-inclusive + QDate endDate = readICalDate( icaltime ).addDays( -1 ); + mCompat->fixFloatingEnd( endDate ); + if ( endDate < event->dtStart().date() ) { + endDate = event->dtStart().date(); + } + event->setDtEnd( QDateTime( endDate, QTime( 0, 0, 0 ) ) ); + } else { + event->setDtEnd(readICalDateTime(icaltime)); + } + break; + +// TODO: + // at this point, there should be at least a start or end time. + // fix up for events that take up no time but have a time associated +#if 0 + if (!(vo = isAPropertyOf(vevent, VCDTstartProp))) + anEvent->setDtStart(anEvent->dtEnd()); + if (!(vo = isAPropertyOf(vevent, VCDTendProp))) + anEvent->setDtEnd(anEvent->dtStart()); +#endif + +// TODO: exdates +#if 0 + // recurrence exceptions + if ((vo = isAPropertyOf(vevent, VCExDateProp)) != 0) { + anEvent->setExDates(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } +#endif + +#if 0 + // secrecy + if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) { + anEvent->setSecrecy(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + else + anEvent->setSecrecy("PUBLIC"); + + // attachments + tmpStrList.clear(); + initPropIterator(&voi, vevent); + while (moreIteration(&voi)) { + vo = nextVObject(&voi); + if (strcmp(vObjectName(vo), VCAttachProp) == 0) { + tmpStrList.append(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + } + anEvent->setAttachments(tmpStrList); + + // resources + if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) { + QString resources = (s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + tmpStrList.clear(); + index1 = 0; + index2 = 0; + QString resource; + while ((index2 = resources.find(';', index1)) != -1) { + resource = resources.mid(index1, (index2 - index1)); + tmpStrList.append(resource); + index1 = index2; + } + anEvent->setResources(tmpStrList); + } +#endif + + case ICAL_RELATEDTO_PROPERTY: // releated event (parent) + event->setRelatedToUid(QString::fromUtf8(icalproperty_get_relatedto(p))); + mEventsRelate.append(event); + break; + + + case ICAL_TRANSP_PROPERTY: // Transparency + transparency = QString::fromUtf8(icalproperty_get_transp(p)); + if( transparency == "TRANSPARENT" ) + event->setTransparency( Event::Transparent ); + else + event->setTransparency( Event::Opaque ); + break; + + default: +// kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(vevent,ICAL_ANY_PROPERTY); + } + + QString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); + if (!msade.isNull()) { + bool floats = (msade == QString::fromLatin1("TRUE")); + kdDebug(5800) << "ICALFormat::readEvent(): all day event: " << floats << endl; + event->setFloats(floats); + if (floats) { + QDateTime endDate = event->dtEnd(); + event->setDtEnd(endDate.addDays(-1)); + } + } + + // some stupid vCal exporters ignore the standard and use Description + // instead of Summary for the default field. Correct for this. + if (event->summary().isEmpty() && + !(event->description().isEmpty())) { + QString tmpStr = event->description().simplifyWhiteSpace(); + event->setDescription(""); + event->setSummary(tmpStr); + } + + return event; +} + +FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) +{ + FreeBusy *freebusy = new FreeBusy; + + readIncidenceBase(vfreebusy,freebusy); + + icalproperty *p = icalcomponent_get_first_property(vfreebusy,ICAL_ANY_PROPERTY); + + icaltimetype icaltime; + icalperiodtype icalperiod; + QDateTime period_start, period_end; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DTSTART_PROPERTY: // start date and time + icaltime = icalproperty_get_dtstart(p); + freebusy->setDtStart(readICalDateTime(icaltime)); + break; + + case ICAL_DTEND_PROPERTY: // start End Date and Time + icaltime = icalproperty_get_dtend(p); + freebusy->setDtEnd(readICalDateTime(icaltime)); + break; + + case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times + icalperiod = icalproperty_get_freebusy(p); + period_start = readICalDateTime(icalperiod.start); + period_end = readICalDateTime(icalperiod.end); + freebusy->addPeriod(period_start, period_end); + break; + + default: + kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind + << endl; + break; + } + p = icalcomponent_get_next_property(vfreebusy,ICAL_ANY_PROPERTY); + } + + return freebusy; +} + +Journal *ICalFormatImpl::readJournal(icalcomponent *vjournal) +{ + Journal *journal = new Journal; + + readIncidence(vjournal,journal); + + return journal; +} + +Attendee *ICalFormatImpl::readAttendee(icalproperty *attendee) +{ + icalparameter *p = 0; + + QString email = QString::fromUtf8(icalproperty_get_attendee(attendee)); + + QString name; + QString uid = QString::null; + 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); + uid = icalparameter_get_xvalue(p); + // This should be added, but there seems to be a libical bug here. + /*while (p) { + // if (icalparameter_get_xname(p) == "X-UID") { + uid = icalparameter_get_xvalue(p); + p = icalproperty_get_next_parameter(attendee,ICAL_X_PARAMETER); + } */ + + return new Attendee( name, email, rsvp, status, role, uid ); +} + +Attachment *ICalFormatImpl::readAttachment(icalproperty *attach) +{ + icalattachtype *a = icalproperty_get_attach(attach); + icalparameter_value v = ICAL_VALUE_NONE; + icalparameter_encoding e = ICAL_ENCODING_NONE; + + Attachment *attachment = 0; + + icalparameter *vp = icalproperty_get_first_parameter(attach, ICAL_VALUE_PARAMETER); + if (vp) + v = icalparameter_get_value(vp); + + icalparameter *ep = icalproperty_get_first_parameter(attach, ICAL_ENCODING_PARAMETER); + if (ep) + e = icalparameter_get_encoding(ep); + + if (v == ICAL_VALUE_BINARY && e == ICAL_ENCODING_BASE64) + attachment = new Attachment(icalattachtype_get_base64(a)); + else if ((v == ICAL_VALUE_NONE || v == ICAL_VALUE_URI) && (e == ICAL_ENCODING_NONE || e == ICAL_ENCODING_8BIT)) { + attachment = new Attachment(QString(icalattachtype_get_url(a))); + } else { + kdWarning(5800) << "Unsupported attachment format, discarding it!" << endl; + return 0; + } + + icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); + if (p) + attachment->setMimeType(QString(icalparameter_get_fmttype(p))); + + return attachment; +} +#include <qtextcodec.h> +void ICalFormatImpl::readIncidence(icalcomponent *parent,Incidence *incidence) +{ + readIncidenceBase(parent,incidence); + + icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); + bool readrec = false; + const char *text; + int intvalue; + icaltimetype icaltime; + icaldurationtype icalduration; + struct icalrecurrencetype rectype; + QStringList categories; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_CREATED_PROPERTY: + icaltime = icalproperty_get_created(p); + incidence->setCreated(readICalDateTime(icaltime)); + break; + + case ICAL_SEQUENCE_PROPERTY: // sequence + intvalue = icalproperty_get_sequence(p); + incidence->setRevision(intvalue); + break; + + case ICAL_LASTMODIFIED_PROPERTY: // last modification date + icaltime = icalproperty_get_lastmodified(p); + incidence->setLastModified(readICalDateTime(icaltime)); + break; + + case ICAL_DTSTART_PROPERTY: // start date and time + icaltime = icalproperty_get_dtstart(p); + if (icaltime.is_date) { + incidence->setDtStart(QDateTime(readICalDate(icaltime),QTime(0,0,0))); + incidence->setFloats(true); + } else { + incidence->setDtStart(readICalDateTime(icaltime)); + } + break; + + case ICAL_DURATION_PROPERTY: // start date and time + icalduration = icalproperty_get_duration(p); + incidence->setDuration(readICalDuration(icalduration)); + break; + + case ICAL_DESCRIPTION_PROPERTY: // description + text = icalproperty_get_description(p); + incidence->setDescription(QString::fromUtf8(text)); + break; + + case ICAL_SUMMARY_PROPERTY: // summary + { + text = icalproperty_get_summary(p); + incidence->setSummary(QString::fromUtf8(text)); + } + break; + case ICAL_STATUS_PROPERTY: // summary + { + if ( ICAL_STATUS_CANCELLED == icalproperty_get_status(p) ) + incidence->setCancelled( true ); + } + break; + + case ICAL_LOCATION_PROPERTY: // location + text = icalproperty_get_location(p); + incidence->setLocation(QString::fromUtf8(text)); + break; + +#if 0 + // status + if ((vo = isAPropertyOf(vincidence, VCStatusProp)) != 0) { + incidence->setStatus(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + else + incidence->setStatus("NEEDS ACTION"); +#endif + + case ICAL_PRIORITY_PROPERTY: // priority + intvalue = icalproperty_get_priority(p); + incidence->setPriority(intvalue); + break; + + case ICAL_CATEGORIES_PROPERTY: // categories + text = icalproperty_get_categories(p); + categories.append(QString::fromUtf8(text)); + break; + //******************************************* + case ICAL_RRULE_PROPERTY: + // we do need (maybe )start datetime of incidence for recurrence + // such that we can read recurrence only after we read incidence completely + readrec = true; + rectype = icalproperty_get_rrule(p); + break; + + case ICAL_EXDATE_PROPERTY: + icaltime = icalproperty_get_exdate(p); + incidence->addExDate(readICalDate(icaltime)); + break; + + case ICAL_CLASS_PROPERTY: + text = icalproperty_get_class(p); + if (strcmp(text,"PUBLIC") == 0) { + incidence->setSecrecy(Incidence::SecrecyPublic); + } else if (strcmp(text,"CONFIDENTIAL") == 0) { + incidence->setSecrecy(Incidence::SecrecyConfidential); + } else { + incidence->setSecrecy(Incidence::SecrecyPrivate); + } + break; + + case ICAL_ATTACH_PROPERTY: // attachments + incidence->addAttachment(readAttachment(p)); + break; + + default: +// kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); + } + if ( readrec ) { + readRecurrenceRule(rectype,incidence); + } + // kpilot stuff +// TODO: move this application-specific code to kpilot + QString kp = incidence->nonKDECustomProperty("X-PILOTID"); + if (!kp.isNull()) { + incidence->setPilotId(kp.toInt()); + } + kp = incidence->nonKDECustomProperty("X-PILOTSTAT"); + if (!kp.isNull()) { + incidence->setSyncStatus(kp.toInt()); + } + kp = incidence->nonKDECustomProperty("X-ZAURUSID"); + if (!kp.isNull()) { + incidence->setZaurusId(kp.toInt()); + } + + kp = incidence->nonKDECustomProperty("X-ZAURUSUID"); + if (!kp.isNull()) { + incidence->setZaurusUid(kp.toInt()); + } + + kp = incidence->nonKDECustomProperty("X-ZAURUSSTAT"); + if (!kp.isNull()) { + incidence->setZaurusStat(kp.toInt()); + } + + // Cancel backwards compatibility mode for subsequent changes by the application + incidence->recurrence()->setCompatVersion(); + + // 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); + } +} + +void ICalFormatImpl::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(QString::fromUtf8(icalproperty_get_organizer(p))); + break; + + case ICAL_ATTENDEE_PROPERTY: // attendee + incidenceBase->addAttendee(readAttendee(p)); + break; + + default: + break; + } + + p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); + } + + // custom properties + readCustomProperties(parent, incidenceBase); +} + +void ICalFormatImpl::readCustomProperties(icalcomponent *parent,CustomProperties *properties) +{ + QMap<QCString, QString> customProperties; + + icalproperty *p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY); + + while (p) { + + QString value = QString::fromUtf8(icalproperty_get_x(p)); + customProperties[icalproperty_get_name(p)] = value; + + p = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY); + } + + properties->setCustomProperties(customProperties); +} + +void ICalFormatImpl::readRecurrenceRule(struct icalrecurrencetype rrule,Incidence *incidence) +{ +// kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl; + + Recurrence *recur = incidence->recurrence(); + recur->setCompatVersion(mCalendarVersion); + recur->unsetRecurs(); + + struct icalrecurrencetype r = rrule; + + dumpIcalRecurrence(r); + readRecurrence( r, recur, incidence); +} + +void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, Recurrence* recur, Incidence *incidence) +{ + int wkst; + int index = 0; + short day = 0; + QBitArray qba(7); + int frequ = r.freq; + int interv = r.interval; + // preprocessing for odd recurrence definitions + + if ( r.freq == ICAL_MONTHLY_RECURRENCE ) { + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) { + interv = 12; + } + } + if ( r.freq == ICAL_YEARLY_RECURRENCE ) { + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX && r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { + frequ = ICAL_MONTHLY_RECURRENCE; + interv = 12; + } + } + + switch (frequ) { + case ICAL_MINUTELY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setMinutely(interv,readICalDateTime(r.until)); + } else { + if (r.count == 0) + recur->setMinutely(interv,-1); + else + recur->setMinutely(interv,r.count); + } + break; + case ICAL_HOURLY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setHourly(interv,readICalDateTime(r.until)); + } else { + if (r.count == 0) + recur->setHourly(interv,-1); + else + recur->setHourly(interv,r.count); + } + break; + case ICAL_DAILY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setDaily(interv,readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setDaily(interv,-1); + else + recur->setDaily(interv,r.count); + } + break; + case ICAL_WEEKLY_RECURRENCE: + // kdDebug(5800) << "WEEKLY_RECURRENCE" << endl; + wkst = (r.week_start + 5)%7 + 1; + if (!icaltime_is_null_time(r.until)) { + recur->setWeekly(interv,qba,readICalDate(r.until),wkst); + } else { + if (r.count == 0) + recur->setWeekly(interv,qba,-1,wkst); + else + recur->setWeekly(interv,qba,r.count,wkst); + } + if ( r.by_day[0] == ICAL_RECURRENCE_ARRAY_MAX) { + int wday = incidence->dtStart().date().dayOfWeek ()-1; + //qDebug("weekly error found "); + qba.setBit(wday); + } else { + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << " " << day << endl; + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + } + } + break; + case ICAL_MONTHLY_RECURRENCE: + + if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setMonthly(Recurrence::rMonthlyPos,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setMonthly(Recurrence::rMonthlyPos,interv,-1); + else + recur->setMonthly(Recurrence::rMonthlyPos,interv,r.count); + } + bool useSetPos = false; + short pos = 0; + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----a " << index << ": " << day << endl; + pos = icalrecurrencetype_day_position(day); + if (pos) { + day = icalrecurrencetype_day_day_of_week(day); + QBitArray ba(7); // don't wipe qba + ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + recur->addMonthlyPos(pos,ba); + } else { + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + useSetPos = true; + } + } + if (useSetPos) { + if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addMonthlyPos(r.by_set_pos[0],qba); + } + } + } else if (r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setMonthly(Recurrence::rMonthlyDay,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setMonthly(Recurrence::rMonthlyDay,interv,-1); + else + recur->setMonthly(Recurrence::rMonthlyDay,interv,r.count); + } + while((day = r.by_month_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----b " << day << endl; + recur->addMonthlyDay(day); + } + } + break; + case ICAL_YEARLY_RECURRENCE: + if (r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyDay,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyDay,interv,-1); + else + recur->setYearly(Recurrence::rYearlyDay,interv,r.count); + } + while((day = r.by_year_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyNum(day); + } + } else if ( true /*r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX*/) { + if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyPos,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyPos,interv,-1); + else + recur->setYearly(Recurrence::rYearlyPos,interv,r.count); + } + bool useSetPos = false; + short pos = 0; + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----a " << index << ": " << day << endl; + pos = icalrecurrencetype_day_position(day); + if (pos) { + day = icalrecurrencetype_day_day_of_week(day); + QBitArray ba(7); // don't wipe qba + ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + recur->addYearlyMonthPos(pos,ba); + } else { + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + useSetPos = true; + } + } + if (useSetPos) { + if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyMonthPos(r.by_set_pos[0],qba); + } + } + } else { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyMonth,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyMonth,interv,-1); + else + recur->setYearly(Recurrence::rYearlyMonth,interv,r.count); + } + } + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { + index = 0; + while((day = r.by_month[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyNum(day); + } + } else { + recur->addYearlyNum(incidence->dtStart().date().month()); + } + } + break; + default: + ; + break; + } +} + +void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence) +{ + //kdDebug(5800) << "Read alarm for " << incidence->summary() << endl; + + 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); + if ( !p ) { + return; + } + + icalproperty_action action = icalproperty_get_action(p); + Alarm::Type type = Alarm::Display; + 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: + ; + return; + } + 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)) { + if (icaldurationtype_is_null_duration(trigger.duration)) { + kdDebug(5800) << "ICalFormatImpl::readAlarm(): Trigger has no time and no duration." << endl; + } else { + Duration duration = icaldurationtype_as_int( 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 { + ialarm->setTime(readICalDateTime(trigger.time)); + } + break; + } + case ICAL_DURATION_PROPERTY: { + icaldurationtype duration = icalproperty_get_duration(p); + ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60); + break; + } + case ICAL_REPEAT_PROPERTY: + ialarm->setRepeatCount(icalproperty_get_repeat(p)); + break; + + // Only in DISPLAY and EMAIL and PROCEDURE alarms + case ICAL_DESCRIPTION_PROPERTY: { + 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; + } + // Only in EMAIL alarm + case ICAL_SUMMARY_PROPERTY: + ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p))); + break; + + // Only in EMAIL alarm + case ICAL_ATTENDEE_PROPERTY: { + QString email = QString::fromUtf8(icalproperty_get_attendee(p)); + 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; + } + // Only in AUDIO and EMAIL and PROCEDURE alarms + case ICAL_ATTACH_PROPERTY: { + icalattachtype *attach = icalproperty_get_attach(p); + QString url = QFile::decodeName(icalattachtype_get_url(attach)); + switch ( action ) { + case ICAL_ACTION_AUDIO: + ialarm->setAudioFile( url ); + break; + case ICAL_ACTION_PROCEDURE: + ialarm->setProgramFile( url ); + break; + case ICAL_ACTION_EMAIL: + ialarm->addMailAttachment( url ); + break; + default: + break; + } + break; + } + default: + break; + } + + p = icalcomponent_get_next_property(alarm,ICAL_ANY_PROPERTY); + } + + // custom properties + readCustomProperties(alarm, ialarm); + + // TODO: check for consistency of alarm properties +} + +icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) +{ + icaltimetype t; + + 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 QDateTime &dt ) +{ + icaltimetype t; + t.is_date = 0; + t.zone = 0; + QDateTime datetime; + if ( mParent->utc() ) { + int offset = KGlobal::locale()->localTimeOffset( dt ); + datetime = dt.addSecs ( -offset*60); + t.is_utc = 1; + } + else { + datetime = dt; + t.is_utc = 0; + + } + 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(); + + //qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); + +// if ( mParent->utc() ) { +// datetime = KGlobal::locale()->localTime( dt ); +// qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); +// if (mParent->timeZoneId().isEmpty()) +// t = icaltime_as_utc(t, 0); +// else +// t = icaltime_as_utc(t,mParent->timeZoneId().local8Bit()); +// } + + return t; +} + +QDateTime ICalFormatImpl::readICalDateTime(icaltimetype t) +{ + QDateTime dt (QDate(t.year,t.month,t.day), + QTime(t.hour,t.minute,t.second) ); + + if (t.is_utc) { + int offset = KGlobal::locale()->localTimeOffset( dt ); + dt = dt.addSecs ( offset*60); + } + + return dt; +} + +QDate ICalFormatImpl::readICalDate(icaltimetype t) +{ + return QDate(t.year,t.month,t.day); +} + +icaldurationtype ICalFormatImpl::writeICalDuration(int seconds) +{ + icaldurationtype d; + + d.weeks = seconds % gSecondsPerWeek; + seconds -= d.weeks * gSecondsPerWeek; + d.days = seconds % gSecondsPerDay; + seconds -= d.days * gSecondsPerDay; + d.hours = seconds % gSecondsPerHour; + seconds -= d.hours * gSecondsPerHour; + d.minutes = seconds % gSecondsPerMinute; + seconds -= d.minutes * gSecondsPerMinute; + d.seconds = seconds; + d.is_neg = 0; + + return d; +} + +int ICalFormatImpl::readICalDuration(icaldurationtype d) +{ + int result = 0; + + result += d.weeks * gSecondsPerWeek; + result += d.days * gSecondsPerDay; + result += d.hours * gSecondsPerHour; + result += d.minutes * gSecondsPerMinute; + result += d.seconds; + + if (d.is_neg) result *= -1; + + return result; +} + +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().utf8()); + icalcomponent_add_property(calendar,p); + + // TODO: Add time zone + + // iCalendar version (2.0) + p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION)); + icalcomponent_add_property(calendar,p); + + // Custom properties + if( cal != 0 ) + 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 +#if 0 + if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) { + char *methodType = 0; + methodType = fakeCString(vObjectUStringZValue(curVO)); + if (mEnableDialogs) + KMessageBox::information(mTopWidget, + i18n("This calendar is an iTIP transaction of type \"%1\".") + .arg(methodType), + i18n("%1: iTIP Transaction").arg(CalFormat::application())); + delete methodType; + } +#endif + + icalproperty *p; + + p = icalcomponent_get_first_property(calendar,ICAL_PRODID_PROPERTY); + if (!p) { +// TODO: does no PRODID really matter? +// mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); +// return false; + mLoadedProductId = ""; + mCalendarVersion = 0; + } else { + mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p)); + mCalendarVersion = CalFormat::calendarVersion(mLoadedProductId); + + delete mCompat; + mCompat = CompatFactory::createCompat( mLoadedProductId ); + } + +// TODO: check for unknown PRODID +#if 0 + if (!mCalendarVersion + && CalFormat::productId() != mLoadedProductId) { + // warn the user that we might have trouble reading non-known calendar. + if (mEnableDialogs) + KMessageBox::information(mTopWidget, + i18n("This vCalendar file was not created by KOrganizer " + "or any other product we support. Loading anyway..."), + i18n("%1: Unknown vCalendar Vendor").arg(CalFormat::application())); + } +#endif + + p = icalcomponent_get_first_property(calendar,ICAL_VERSION_PROPERTY); + if (!p) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); + return false; + } else { + const char *version = icalproperty_get_version(p); + + if (strcmp(version,"1.0") == 0) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersion1, + i18n("Expected iCalendar format"))); + return false; + } else if (strcmp(version,"2.0") != 0) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); + return false; + } + } + + +// TODO: check for calendar format version +#if 0 + // warn the user we might have trouble reading this unknown version. + if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + if (strcmp(_VCAL_VERSION, s) != 0) + if (mEnableDialogs) + KMessageBox::sorry(mTopWidget, + i18n("This vCalendar file has version %1.\n" + "We only support %2.") + .arg(s).arg(_VCAL_VERSION), + i18n("%1: Unknown vCalendar Version").arg(CalFormat::application())); + deleteStr(s); + } +#endif + + // custom properties + readCustomProperties(calendar, cal); + +// TODO: set time zone +#if 0 + // set the time zone + if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + cal->setTimeZone(s); + deleteStr(s); + } +#endif + + // Store all events with a relatedTo property in a list for post-processing + mEventsRelate.clear(); + mTodosRelate.clear(); + // TODO: make sure that only actually added ecvens go to this lists. + + icalcomponent *c; + + // Iterate through all todos + c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT); + while (c) { +// kdDebug(5800) << "----Todo found" << endl; + Todo *todo = readTodo(c); + if (!cal->todo(todo->uid())) 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) { +// kdDebug(5800) << "----Event found" << endl; + Event *event = readEvent(c); + if (!cal->event(event->uid())) 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) { +// kdDebug(5800) << "----Journal found" << endl; + Journal *journal = readJournal(c); + if (!cal->journal(journal->uid())) cal->addJournal(journal); + c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT); + } + +#if 0 + initPropIterator(&i, vcal); + + // go through all the vobjects in the vcal + while (moreIteration(&i)) { + curVO = nextVObject(&i); + + /************************************************************************/ + + // now, check to see that the object is an event or todo. + if (strcmp(vObjectName(curVO), VCEventProp) == 0) { + + if ((curVOProp = isAPropertyOf(curVO, KPilotStatusProp)) != 0) { + char *s; + s = fakeCString(vObjectUStringZValue(curVOProp)); + // check to see if event was deleted by the kpilot conduit + if (atoi(s) == Event::SYNCDEL) { + deleteStr(s); + goto SKIP; + } + deleteStr(s); + } + + // this code checks to see if we are trying to read in an event + // that we already find to be in the calendar. If we find this + // to be the case, we skip the event. + if ((curVOProp = isAPropertyOf(curVO, VCUniqueStringProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVOProp)); + QString tmpStr(s); + deleteStr(s); + + if (cal->event(tmpStr)) { + goto SKIP; + } + if (cal->todo(tmpStr)) { + goto SKIP; + } + } + + if ((!(curVOProp = isAPropertyOf(curVO, VCDTstartProp))) && + (!(curVOProp = isAPropertyOf(curVO, VCDTendProp)))) { + kdDebug(5800) << "found a VEvent with no DTSTART and no DTEND! Skipping..." << endl; + goto SKIP; + } + + anEvent = VEventToEvent(curVO); + // we now use addEvent instead of insertEvent so that the + // signal/slot get connected. + if (anEvent) + cal->addEvent(anEvent); + else { + // some sort of error must have occurred while in translation. + goto SKIP; + } + } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) { + anEvent = VTodoToEvent(curVO); + cal->addTodo(anEvent); + } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) || + (strcmp(vObjectName(curVO), VCProdIdProp) == 0) || + (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) { + // do nothing, we know these properties and we want to skip them. + // we have either already processed them or are ignoring them. + ; + } else { + ; + } + SKIP: + ; + } // while +#endif + + // Post-Process list of events with relations, put Event objects in relation + Event *ev; + for ( ev=mEventsRelate.first(); ev != 0; ev=mEventsRelate.next() ) { + ev->setRelatedTo(cal->event(ev->relatedToUid())); + } + Todo *todo; + for ( todo=mTodosRelate.first(); todo != 0; todo=mTodosRelate.next() ) { + todo->setRelatedTo(cal->todo(todo->relatedToUid())); + } + + return true; +} + +QString ICalFormatImpl::extractErrorProperty(icalcomponent *c) +{ +// kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " +// << icalcomponent_as_ical_string(c) << endl; + + 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); + } + +// kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " << errorMessage << endl; + + return errorMessage; +} + +void ICalFormatImpl::dumpIcalRecurrence(icalrecurrencetype r) +{ + int i; + + + 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) + " "); + } + } + 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) + " "); + } + } + 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) + " "); + } + } + 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) + " "); + } + } + 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) { + out.append(QString::number(i) + " "); + } + } +} + +icalcomponent *ICalFormatImpl::createScheduleComponent(IncidenceBase *incidence, + Scheduler::Method method) +{ + icalcomponent *message = createCalendarComponent(); + + icalproperty_method icalmethod = ICAL_METHOD_NONE; + + switch (method) { + case Scheduler::Publish: + icalmethod = ICAL_METHOD_PUBLISH; + break; + case Scheduler::Request: + icalmethod = ICAL_METHOD_REQUEST; + break; + case Scheduler::Refresh: + icalmethod = ICAL_METHOD_REFRESH; + break; + case Scheduler::Cancel: + icalmethod = ICAL_METHOD_CANCEL; + break; + case Scheduler::Add: + icalmethod = ICAL_METHOD_ADD; + break; + case Scheduler::Reply: + icalmethod = ICAL_METHOD_REPLY; + break; + case Scheduler::Counter: + icalmethod = ICAL_METHOD_COUNTER; + break; + case Scheduler::Declinecounter: + icalmethod = ICAL_METHOD_DECLINECOUNTER; + break; + default: + + return message; + } + + icalcomponent_add_property(message,icalproperty_new_method(icalmethod)); + + // TODO: check, if dynamic cast is required + if(incidence->type() == "Todo") { + Todo *todo = static_cast<Todo *>(incidence); + icalcomponent_add_component(message,writeTodo(todo)); + } + if(incidence->type() == "Event") { + Event *event = static_cast<Event *>(incidence); + icalcomponent_add_component(message,writeEvent(event)); + } + if(incidence->type() == "FreeBusy") { + FreeBusy *freebusy = static_cast<FreeBusy *>(incidence); + icalcomponent_add_component(message,writeFreeBusy(freebusy, method)); + } + + return message; +} |