libkcal

icalformatimpl.cpp

00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include <qdatetime.h>
00024 #include <qstring.h>
00025 #include <qptrlist.h>
00026 #include <qfile.h>
00027 #include <cstdlib>
00028 
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 
00032 extern "C" {
00033   #include <ical.h>
00034   #include <icalss.h>
00035   #include <icalparser.h>
00036   #include <icalrestriction.h>
00037 }
00038 
00039 #include "calendar.h"
00040 #include "journal.h"
00041 #include "icalformat.h"
00042 #include "icalformatimpl.h"
00043 #include "compat.h"
00044 
00045 #define _ICAL_VERSION "2.0"
00046 
00047 using namespace KCal;
00048 
00049 /* Static helpers */
00050 static QDateTime ICalDate2QDate(const icaltimetype& t)
00051 {
00052   // Outlook sends dates starting from 1601-01-01, but QDate()
00053   // can only handle dates starting 1752-09-14.
00054   const int year = (t.year>=1754) ? t.year : 1754;
00055   return QDateTime(QDate(year,t.month,t.day), QTime(t.hour,t.minute,t.second));
00056 }
00057 
00058 static void _dumpIcaltime( const icaltimetype& t)
00059 {
00060   kdDebug(5800) << "--- Y: " << t.year << " M: " << t.month << " D: " << t.day
00061       << endl;
00062   kdDebug(5800) << "--- H: " << t.hour << " M: " << t.minute << " S: " << t.second
00063       << endl;
00064   kdDebug(5800) << "--- isUtc: " << icaltime_is_utc( t )<< endl;
00065   kdDebug(5800) << "--- zoneId: " << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) )<< endl;
00066 }
00067 
00068 const int gSecondsPerMinute = 60;
00069 const int gSecondsPerHour   = gSecondsPerMinute * 60;
00070 const int gSecondsPerDay    = gSecondsPerHour   * 24;
00071 const int gSecondsPerWeek   = gSecondsPerDay    * 7;
00072 
00073 ICalFormatImpl::ICalFormatImpl( ICalFormat *parent ) :
00074   mParent( parent ), mCompat( new Compat )
00075 {
00076 }
00077 
00078 ICalFormatImpl::~ICalFormatImpl()
00079 {
00080   delete mCompat;
00081 }
00082 
00083 class ICalFormatImpl::ToComponentVisitor : public IncidenceBase::Visitor
00084 {
00085   public:
00086     ToComponentVisitor( ICalFormatImpl *impl, Scheduler::Method m ) : mImpl( impl ), mComponent( 0 ), mMethod( m ) {}
00087 
00088     bool visit( Event *e ) { mComponent = mImpl->writeEvent( e ); return true; }
00089     bool visit( Todo *e ) { mComponent = mImpl->writeTodo( e ); return true; }
00090     bool visit( Journal *e ) { mComponent = mImpl->writeJournal( e ); return true; }
00091     bool visit( FreeBusy *fb ) { mComponent = mImpl->writeFreeBusy( fb, mMethod ); return true; }
00092 
00093     icalcomponent *component() { return mComponent; }
00094 
00095   private:
00096     ICalFormatImpl *mImpl;
00097     icalcomponent *mComponent;
00098     Scheduler::Method mMethod;
00099 };
00100 
00101 icalcomponent *ICalFormatImpl::writeIncidence( IncidenceBase *incidence, Scheduler::Method method )
00102 {
00103   ToComponentVisitor v( this, method );
00104   if ( incidence->accept(v) )
00105     return v.component();
00106   else return 0;
00107 }
00108 
00109 icalcomponent *ICalFormatImpl::writeTodo(Todo *todo)
00110 {
00111   QString tmpStr;
00112   QStringList tmpStrList;
00113 
00114   icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
00115 
00116   writeIncidence(vtodo,todo);
00117 
00118   // due date
00119   if (todo->hasDueDate()) {
00120     icaltimetype due;
00121     if (todo->doesFloat()) {
00122       due = writeICalDate(todo->dtDue(true).date());
00123     } else {
00124       due = writeICalDateTime(todo->dtDue(true));
00125     }
00126     icalcomponent_add_property(vtodo,icalproperty_new_due(due));
00127   }
00128 
00129   // start time
00130   if ( todo->hasStartDate() || todo->doesRecur() ) {
00131     icaltimetype start;
00132     if (todo->doesFloat()) {
00133 //      kdDebug(5800) << " Incidence " << todo->summary() << " floats." << endl;
00134       start = writeICalDate(todo->dtStart(true).date());
00135     } else {
00136 //      kdDebug(5800) << " incidence " << todo->summary() << " has time." << endl;
00137       start = writeICalDateTime(todo->dtStart(true));
00138     }
00139     icalcomponent_add_property(vtodo,icalproperty_new_dtstart(start));
00140   }
00141 
00142   // completion date
00143   if (todo->isCompleted()) {
00144     if (!todo->hasCompletedDate()) {
00145       // If todo was created by KOrganizer <2.2 it has no correct completion
00146       // date. Set it to now.
00147       todo->setCompleted(QDateTime::currentDateTime());
00148     }
00149     icaltimetype completed = writeICalDateTime(todo->completed());
00150     icalcomponent_add_property(vtodo,icalproperty_new_completed(completed));
00151   }
00152 
00153   icalcomponent_add_property(vtodo,
00154       icalproperty_new_percentcomplete(todo->percentComplete()));
00155 
00156   if( todo->doesRecur() ) {
00157     icalcomponent_add_property(vtodo,
00158         icalproperty_new_recurrenceid( writeICalDateTime( todo->dtDue())));
00159   }
00160 
00161   return vtodo;
00162 }
00163 
00164 icalcomponent *ICalFormatImpl::writeEvent(Event *event)
00165 {
00166 #if 0
00167   kdDebug(5800) << "Write Event '" << event->summary() << "' (" << event->uid()
00168                 << ")" << endl;
00169 #endif
00170 
00171   QString tmpStr;
00172   QStringList tmpStrList;
00173 
00174   icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
00175 
00176   writeIncidence(vevent,event);
00177 
00178   // start time
00179   icaltimetype start;
00180   if (event->doesFloat()) {
00181 //    kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
00182     start = writeICalDate(event->dtStart().date());
00183   } else {
00184 //    kdDebug(5800) << " incidence " << event->summary() << " has time." << endl;
00185     start = writeICalDateTime(event->dtStart());
00186   }
00187   icalcomponent_add_property(vevent,icalproperty_new_dtstart(start));
00188 
00189   if (event->hasEndDate()) {
00190     // End time.
00191     // RFC2445 says that if DTEND is present, it has to be greater than DTSTART.
00192     icaltimetype end;
00193     if (event->doesFloat()) {
00194 //      kdDebug(5800) << " Event " << event->summary() << " floats." << endl;
00195 //      if (event->dtEnd().date() != event->dtStart().date()) {
00196         // +1 day because end date is non-inclusive.
00197         end = writeICalDate( event->dtEnd().date().addDays( 1 ) );
00198         icalcomponent_add_property(vevent,icalproperty_new_dtend(end));
00199 //      }
00200     } else {
00201 //      kdDebug(5800) << " Event " << event->summary() << " has time." << endl;
00202       if (event->dtEnd() != event->dtStart()) {
00203         end = writeICalDateTime(event->dtEnd());
00204         icalcomponent_add_property(vevent,icalproperty_new_dtend(end));
00205       }
00206     }
00207   }
00208 
00209 // TODO: resources
00210 #if 0
00211   // resources
00212   tmpStrList = anEvent->resources();
00213   tmpStr = tmpStrList.join(";");
00214   if (!tmpStr.isEmpty())
00215     addPropValue(vevent, VCResourcesProp, tmpStr.utf8());
00216 
00217 #endif
00218 
00219   // Transparency
00220   switch( event->transparency() ) {
00221   case Event::Transparent:
00222     icalcomponent_add_property(
00223       vevent,
00224       icalproperty_new_transp( ICAL_TRANSP_TRANSPARENT ) );
00225     break;
00226   case Event::Opaque:
00227     icalcomponent_add_property(
00228       vevent,
00229       icalproperty_new_transp( ICAL_TRANSP_OPAQUE ) );
00230     break;
00231   }
00232 
00233   return vevent;
00234 }
00235 
00236 icalcomponent *ICalFormatImpl::writeFreeBusy(FreeBusy *freebusy,
00237                                              Scheduler::Method method)
00238 {
00239 #if QT_VERSION >= 300
00240   kdDebug(5800) << "icalformatimpl: writeFreeBusy: startDate: "
00241     << freebusy->dtStart().toString("ddd MMMM d yyyy: h:m:s ap") << " End Date: "
00242     << freebusy->dtEnd().toString("ddd MMMM d yyyy: h:m:s ap") << endl;
00243 #endif
00244 
00245   icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT);
00246 
00247   writeIncidenceBase(vfreebusy,freebusy);
00248 
00249   icalcomponent_add_property(vfreebusy, icalproperty_new_dtstart(
00250       writeICalDateTime(freebusy->dtStart())));
00251 
00252   icalcomponent_add_property(vfreebusy, icalproperty_new_dtend(
00253       writeICalDateTime(freebusy->dtEnd())));
00254 
00255   if (method == Scheduler::Request) {
00256     icalcomponent_add_property(vfreebusy,icalproperty_new_uid(
00257        freebusy->uid().utf8()));
00258   }
00259 
00260   //Loops through all the periods in the freebusy object
00261   QValueList<Period> list = freebusy->busyPeriods();
00262   QValueList<Period>::Iterator it;
00263   icalperiodtype period;
00264   for (it = list.begin(); it!= list.end(); ++it) {
00265     period.start = writeICalDateTime((*it).start());
00266     if ( (*it).hasDuration() ) {
00267       period.duration = writeICalDuration( (*it).duration().asSeconds() );
00268     } else {
00269       period.end = writeICalDateTime((*it).end());
00270     }
00271     icalcomponent_add_property(vfreebusy, icalproperty_new_freebusy(period) );
00272   }
00273 
00274   return vfreebusy;
00275 }
00276 
00277 icalcomponent *ICalFormatImpl::writeJournal(Journal *journal)
00278 {
00279   icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT);
00280 
00281   writeIncidence(vjournal,journal);
00282 
00283   // start time
00284   if (journal->dtStart().isValid()) {
00285     icaltimetype start;
00286     if (journal->doesFloat()) {
00287 //      kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
00288       start = writeICalDate(journal->dtStart().date());
00289     } else {
00290 //      kdDebug(5800) << " incidence " << event->summary() << " has time." << endl;
00291       start = writeICalDateTime(journal->dtStart());
00292     }
00293     icalcomponent_add_property(vjournal,icalproperty_new_dtstart(start));
00294   }
00295 
00296   return vjournal;
00297 }
00298 
00299 void ICalFormatImpl::writeIncidence(icalcomponent *parent,Incidence *incidence)
00300 {
00301   if ( incidence->schedulingID() != incidence->uid() )
00302     // We need to store the UID in here. The rawSchedulingID will
00303     // go into the iCal UID component
00304     incidence->setCustomProperty( "LIBKCAL", "ID", incidence->uid() );
00305   else
00306     incidence->removeCustomProperty( "LIBKCAL", "ID" );
00307 
00308   // pilot sync stuff
00309 // TODO: move this application-specific code to kpilot
00310   if (incidence->pilotId()) {
00311     incidence->setNonKDECustomProperty("X-PILOTID", QString::number(incidence->pilotId()));
00312     incidence->setNonKDECustomProperty("X-PILOTSTAT", QString::number(incidence->syncStatus()));
00313   }
00314 
00315   writeIncidenceBase(parent,incidence);
00316 
00317   // creation date
00318   icalcomponent_add_property(parent,icalproperty_new_created(
00319       writeICalDateTime(incidence->created())));
00320 
00321   // unique id
00322   // If the scheduling ID is different from the real UID, the real
00323   // one is stored on X-REALID above
00324   icalcomponent_add_property(parent,icalproperty_new_uid(
00325       incidence->schedulingID().utf8()));
00326 
00327   // revision
00328   icalcomponent_add_property(parent,icalproperty_new_sequence(
00329       incidence->revision()));
00330 
00331   // last modification date
00332   icalcomponent_add_property(parent,icalproperty_new_lastmodified(
00333       writeICalDateTime(incidence->lastModified())));
00334 
00335   // description
00336   if (!incidence->description().isEmpty()) {
00337     icalcomponent_add_property(parent,icalproperty_new_description(
00338         incidence->description().utf8()));
00339   }
00340 
00341   // summary
00342   if (!incidence->summary().isEmpty()) {
00343     icalcomponent_add_property(parent,icalproperty_new_summary(
00344         incidence->summary().utf8()));
00345   }
00346 
00347   // location
00348   if (!incidence->location().isEmpty()) {
00349     icalcomponent_add_property(parent,icalproperty_new_location(
00350         incidence->location().utf8()));
00351   }
00352 
00353   // status
00354   icalproperty_status status = ICAL_STATUS_NONE;
00355   switch (incidence->status()) {
00356     case Incidence::StatusTentative:    status = ICAL_STATUS_TENTATIVE;  break;
00357     case Incidence::StatusConfirmed:    status = ICAL_STATUS_CONFIRMED;  break;
00358     case Incidence::StatusCompleted:    status = ICAL_STATUS_COMPLETED;  break;
00359     case Incidence::StatusNeedsAction:  status = ICAL_STATUS_NEEDSACTION;  break;
00360     case Incidence::StatusCanceled:     status = ICAL_STATUS_CANCELLED;  break;
00361     case Incidence::StatusInProcess:    status = ICAL_STATUS_INPROCESS;  break;
00362     case Incidence::StatusDraft:        status = ICAL_STATUS_DRAFT;  break;
00363     case Incidence::StatusFinal:        status = ICAL_STATUS_FINAL;  break;
00364     case Incidence::StatusX: {
00365       icalproperty* p = icalproperty_new_status(ICAL_STATUS_X);
00366       icalvalue_set_x(icalproperty_get_value(p), incidence->statusStr().utf8());
00367       icalcomponent_add_property(parent, p);
00368       break;
00369     }
00370     case Incidence::StatusNone:
00371     default:
00372       break;
00373   }
00374   if (status != ICAL_STATUS_NONE)
00375     icalcomponent_add_property(parent, icalproperty_new_status(status));
00376 
00377   // secrecy
00378   icalproperty_class secClass;
00379   switch (incidence->secrecy()) {
00380     case Incidence::SecrecyPublic:
00381       secClass = ICAL_CLASS_PUBLIC;
00382       break;
00383     case Incidence::SecrecyConfidential:
00384       secClass = ICAL_CLASS_CONFIDENTIAL;
00385       break;
00386     case Incidence::SecrecyPrivate:
00387     default:
00388       secClass = ICAL_CLASS_PRIVATE;
00389       break;
00390   }
00391   icalcomponent_add_property(parent,icalproperty_new_class(secClass));
00392 
00393   // priority
00394   icalcomponent_add_property(parent,icalproperty_new_priority(
00395       incidence->priority()));
00396 
00397   // categories
00398   QStringList categories = incidence->categories();
00399   QStringList::Iterator it;
00400   for(it = categories.begin(); it != categories.end(); ++it ) {
00401     icalcomponent_add_property(parent,icalproperty_new_categories((*it).utf8()));
00402   }
00403 
00404   // related event
00405   if ( !incidence->relatedToUid().isEmpty() ) {
00406     icalcomponent_add_property(parent,icalproperty_new_relatedto(
00407         incidence->relatedToUid().utf8()));
00408   }
00409 
00410   kdDebug(5800) << "Write recurrence for '" << incidence->summary() << "' (" << incidence->uid()
00411             << ")" << endl;
00412 
00413   RecurrenceRule::List rrules( incidence->recurrence()->rRules() );
00414   RecurrenceRule::List::ConstIterator rit;
00415   for ( rit = rrules.begin(); rit != rrules.end(); ++rit ) {
00416     icalcomponent_add_property( parent, icalproperty_new_rrule(
00417                                 writeRecurrenceRule( (*rit) ) ) );
00418   }
00419 
00420   RecurrenceRule::List exrules( incidence->recurrence()->exRules() );
00421   RecurrenceRule::List::ConstIterator exit;
00422   for ( exit = exrules.begin(); exit != exrules.end(); ++exit ) {
00423     icalcomponent_add_property( parent, icalproperty_new_rrule(
00424                                 writeRecurrenceRule( (*exit) ) ) );
00425   }
00426 
00427   DateList dateList = incidence->recurrence()->exDates();
00428   DateList::ConstIterator exIt;
00429   for(exIt = dateList.begin(); exIt != dateList.end(); ++exIt) {
00430     icalcomponent_add_property(parent,icalproperty_new_exdate(
00431         writeICalDate(*exIt)));
00432   }
00433   DateTimeList dateTimeList = incidence->recurrence()->exDateTimes();
00434   DateTimeList::ConstIterator extIt;
00435   for(extIt = dateTimeList.begin(); extIt != dateTimeList.end(); ++extIt) {
00436     icalcomponent_add_property(parent,icalproperty_new_exdate(
00437         writeICalDateTime(*extIt)));
00438   }
00439 
00440 
00441   dateList = incidence->recurrence()->rDates();
00442   DateList::ConstIterator rdIt;
00443   for( rdIt = dateList.begin(); rdIt != dateList.end(); ++rdIt) {
00444      icalcomponent_add_property( parent, icalproperty_new_rdate(
00445          writeICalDatePeriod(*rdIt) ) );
00446   }
00447   dateTimeList = incidence->recurrence()->rDateTimes();
00448   DateTimeList::ConstIterator rdtIt;
00449   for( rdtIt = dateTimeList.begin(); rdtIt != dateTimeList.end(); ++rdtIt) {
00450      icalcomponent_add_property( parent, icalproperty_new_rdate(
00451          writeICalDateTimePeriod(*rdtIt) ) );
00452   }
00453 
00454   // attachments
00455   Attachment::List attachments = incidence->attachments();
00456   Attachment::List::ConstIterator atIt;
00457   for ( atIt = attachments.begin(); atIt != attachments.end(); ++atIt )
00458     icalcomponent_add_property( parent, writeAttachment( *atIt ) );
00459 
00460   // alarms
00461   Alarm::List::ConstIterator alarmIt;
00462   for ( alarmIt = incidence->alarms().begin();
00463         alarmIt != incidence->alarms().end(); ++alarmIt ) {
00464     if ( (*alarmIt)->enabled() ) {
00465 //      kdDebug(5800) << "Write alarm for " << incidence->summary() << endl;
00466       icalcomponent_add_component( parent, writeAlarm( *alarmIt ) );
00467     }
00468   }
00469 
00470   // duration
00471   if (incidence->hasDuration()) {
00472     icaldurationtype duration;
00473     duration = writeICalDuration( incidence->duration() );
00474     icalcomponent_add_property(parent,icalproperty_new_duration(duration));
00475   }
00476 }
00477 
00478 void ICalFormatImpl::writeIncidenceBase( icalcomponent *parent,
00479                                          IncidenceBase * incidenceBase )
00480 {
00481   icalcomponent_add_property( parent, icalproperty_new_dtstamp(
00482       writeICalDateTime( QDateTime::currentDateTime() ) ) );
00483 
00484   // organizer stuff
00485   icalcomponent_add_property( parent, writeOrganizer( incidenceBase->organizer() ) );
00486 
00487   // attendees
00488   if ( incidenceBase->attendeeCount() > 0 ) {
00489     Attendee::List::ConstIterator it;
00490     for( it = incidenceBase->attendees().begin();
00491          it != incidenceBase->attendees().end(); ++it ) {
00492       icalcomponent_add_property( parent, writeAttendee( *it ) );
00493     }
00494   }
00495 
00496   // comments
00497   QStringList comments = incidenceBase->comments();
00498   for (QStringList::Iterator it=comments.begin(); it!=comments.end(); ++it) {
00499     icalcomponent_add_property(parent, icalproperty_new_comment((*it).utf8()));
00500   }
00501 
00502   // custom properties
00503   writeCustomProperties( parent, incidenceBase );
00504 }
00505 
00506 void ICalFormatImpl::writeCustomProperties(icalcomponent *parent,CustomProperties *properties)
00507 {
00508   QMap<QCString, QString> custom = properties->customProperties();
00509   for (QMap<QCString, QString>::Iterator c = custom.begin();  c != custom.end();  ++c) {
00510     icalproperty *p = icalproperty_new_x(c.data().utf8());
00511     icalproperty_set_x_name(p,c.key());
00512     icalcomponent_add_property(parent,p);
00513   }
00514 }
00515 
00516 icalproperty *ICalFormatImpl::writeOrganizer( const Person &organizer )
00517 {
00518   icalproperty *p = icalproperty_new_organizer("MAILTO:" + organizer.email().utf8());
00519 
00520   if (!organizer.name().isEmpty()) {
00521     icalproperty_add_parameter( p, icalparameter_new_cn(organizer.name().utf8()) );
00522   }
00523   // TODO: Write dir, senty-by and language
00524 
00525   return p;
00526 }
00527 
00528 
00529 icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee)
00530 {
00531   icalproperty *p = icalproperty_new_attendee("mailto:" + attendee->email().utf8());
00532 
00533   if (!attendee->name().isEmpty()) {
00534     icalproperty_add_parameter(p,icalparameter_new_cn(attendee->name().utf8()));
00535   }
00536 
00537 
00538   icalproperty_add_parameter(p,icalparameter_new_rsvp(
00539           attendee->RSVP() ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE ));
00540 
00541   icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
00542   switch (attendee->status()) {
00543     default:
00544     case Attendee::NeedsAction:
00545       status = ICAL_PARTSTAT_NEEDSACTION;
00546       break;
00547     case Attendee::Accepted:
00548       status = ICAL_PARTSTAT_ACCEPTED;
00549       break;
00550     case Attendee::Declined:
00551       status = ICAL_PARTSTAT_DECLINED;
00552       break;
00553     case Attendee::Tentative:
00554       status = ICAL_PARTSTAT_TENTATIVE;
00555       break;
00556     case Attendee::Delegated:
00557       status = ICAL_PARTSTAT_DELEGATED;
00558       break;
00559     case Attendee::Completed:
00560       status = ICAL_PARTSTAT_COMPLETED;
00561       break;
00562     case Attendee::InProcess:
00563       status = ICAL_PARTSTAT_INPROCESS;
00564       break;
00565   }
00566   icalproperty_add_parameter(p,icalparameter_new_partstat(status));
00567 
00568   icalparameter_role role = ICAL_ROLE_REQPARTICIPANT;
00569   switch (attendee->role()) {
00570     case Attendee::Chair:
00571       role = ICAL_ROLE_CHAIR;
00572       break;
00573     default:
00574     case Attendee::ReqParticipant:
00575       role = ICAL_ROLE_REQPARTICIPANT;
00576       break;
00577     case Attendee::OptParticipant:
00578       role = ICAL_ROLE_OPTPARTICIPANT;
00579       break;
00580     case Attendee::NonParticipant:
00581       role = ICAL_ROLE_NONPARTICIPANT;
00582       break;
00583   }
00584   icalproperty_add_parameter(p,icalparameter_new_role(role));
00585 
00586   if (!attendee->uid().isEmpty()) {
00587     icalparameter* icalparameter_uid = icalparameter_new_x(attendee->uid().utf8());
00588     icalparameter_set_xname(icalparameter_uid,"X-UID");
00589     icalproperty_add_parameter(p,icalparameter_uid);
00590   }
00591 
00592   return p;
00593 }
00594 
00595 icalproperty *ICalFormatImpl::writeAttachment(Attachment *att)
00596 {
00597   icalattach *attach;
00598   if (att->isUri())
00599       attach = icalattach_new_from_url( att->uri().utf8().data());
00600   else
00601       attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0);
00602   icalproperty *p = icalproperty_new_attach(attach);
00603 
00604   if ( !att->mimeType().isEmpty() ) {
00605     icalproperty_add_parameter( p,
00606         icalparameter_new_fmttype( att->mimeType().utf8().data() ) );
00607   }
00608 
00609   if ( att->isBinary() ) {
00610     icalproperty_add_parameter( p,
00611         icalparameter_new_value( ICAL_VALUE_BINARY ) );
00612     icalproperty_add_parameter( p,
00613         icalparameter_new_encoding( ICAL_ENCODING_BASE64 ) );
00614   }
00615 
00616   if ( att->showInline() ) {
00617     icalparameter* icalparameter_inline = icalparameter_new_x( "inline" );
00618     icalparameter_set_xname( icalparameter_inline, "X-CONTENT-DISPOSITION" );
00619     icalproperty_add_parameter( p, icalparameter_inline );
00620   }
00621 
00622   if ( !att->label().isEmpty() ) {
00623     icalparameter* icalparameter_label = icalparameter_new_x( att->label().utf8() );
00624     icalparameter_set_xname( icalparameter_label, "X-LABEL" );
00625     icalproperty_add_parameter( p, icalparameter_label );
00626   }
00627 
00628   return p;
00629 }
00630 
00631 icalrecurrencetype ICalFormatImpl::writeRecurrenceRule( RecurrenceRule *recur )
00632 {
00633 //  kdDebug(5800) << "ICalFormatImpl::writeRecurrenceRule()" << endl;
00634 
00635   icalrecurrencetype r;
00636   icalrecurrencetype_clear(&r);
00637 
00638   switch( recur->recurrenceType() ) {
00639     case RecurrenceRule::rSecondly:
00640       r.freq = ICAL_SECONDLY_RECURRENCE;
00641       break;
00642     case RecurrenceRule::rMinutely:
00643       r.freq = ICAL_MINUTELY_RECURRENCE;
00644       break;
00645     case RecurrenceRule::rHourly:
00646       r.freq = ICAL_HOURLY_RECURRENCE;
00647       break;
00648     case RecurrenceRule::rDaily:
00649       r.freq = ICAL_DAILY_RECURRENCE;
00650       break;
00651     case RecurrenceRule::rWeekly:
00652       r.freq = ICAL_WEEKLY_RECURRENCE;
00653       break;
00654     case RecurrenceRule::rMonthly:
00655       r.freq = ICAL_MONTHLY_RECURRENCE;
00656       break;
00657     case RecurrenceRule::rYearly:
00658       r.freq = ICAL_YEARLY_RECURRENCE;
00659       break;
00660     default:
00661       r.freq = ICAL_NO_RECURRENCE;
00662       kdDebug(5800) << "ICalFormatImpl::writeRecurrence(): no recurrence" << endl;
00663       break;
00664   }
00665 
00666   int index = 0;
00667   QValueList<int> bys;
00668   QValueList<int>::ConstIterator it;
00669 
00670   // Now write out the BY* parts:
00671   bys = recur->bySeconds();
00672   index = 0;
00673   for ( it = bys.begin(); it != bys.end(); ++it ) {
00674     r.by_second[index++] = *it;
00675   }
00676 
00677   bys = recur->byMinutes();
00678   index = 0;
00679   for ( it = bys.begin(); it != bys.end(); ++it ) {
00680     r.by_minute[index++] = *it;
00681   }
00682 
00683   bys = recur->byHours();
00684   index = 0;
00685   for ( it = bys.begin(); it != bys.end(); ++it ) {
00686     r.by_hour[index++] = *it;
00687   }
00688 
00689   bys = recur->byMonthDays();
00690   index = 0;
00691   for ( it = bys.begin(); it != bys.end(); ++it ) {
00692     r.by_month_day[index++] = icalrecurrencetype_day_position( (*it) * 8 );
00693   }
00694 
00695   bys = recur->byYearDays();
00696   index = 0;
00697   for ( it = bys.begin(); it != bys.end(); ++it ) {
00698     r.by_year_day[index++] = *it;
00699   }
00700 
00701   bys = recur->byWeekNumbers();
00702   index = 0;
00703   for ( it = bys.begin(); it != bys.end(); ++it ) {
00704      r.by_week_no[index++] = *it;
00705   }
00706 
00707   bys = recur->byMonths();
00708   index = 0;
00709   for ( it = bys.begin(); it != bys.end(); ++it ) {
00710     r.by_month[index++] = *it;
00711   }
00712 
00713   bys = recur->bySetPos();
00714   index = 0;
00715   for ( it = bys.begin(); it != bys.end(); ++it ) {
00716      r.by_set_pos[index++] = *it;
00717   }
00718 
00719 
00720   QValueList<RecurrenceRule::WDayPos> byd = recur->byDays();
00721   int day;
00722   index = 0;
00723   for ( QValueList<RecurrenceRule::WDayPos>::ConstIterator dit = byd.begin();
00724         dit != byd.end(); ++dit ) {
00725     day = (*dit).day() % 7 + 1;     // convert from Monday=1 to Sunday=1
00726     if ( (*dit).pos() < 0 ) {
00727       day += (-(*dit).pos())*8;
00728       day = -day;
00729     } else {
00730       day += (*dit).pos()*8;
00731     }
00732     r.by_day[index++] = day;
00733   }
00734 
00735   r.week_start = static_cast<icalrecurrencetype_weekday>(
00736                                              recur->weekStart()%7 + 1);
00737 
00738   if ( recur->frequency() > 1 ) {
00739     // Dont' write out INTERVAL=1, because that's the default anyway
00740     r.interval = recur->frequency();
00741   }
00742 
00743   if ( recur->duration() > 0 ) {
00744     r.count = recur->duration();
00745   } else if ( recur->duration() == -1 ) {
00746     r.count = 0;
00747   } else {
00748     if ( recur->doesFloat() )
00749       r.until = writeICalDate(recur->endDt().date());
00750     else
00751       r.until = writeICalDateTime(recur->endDt());
00752   }
00753 
00754 // Debug output
00755 #if 0
00756   const char *str = icalrecurrencetype_as_string(&r);
00757   if (str) {
00758     kdDebug(5800) << " String: " << str << endl;
00759   } else {
00760     kdDebug(5800) << " No String" << endl;
00761   }
00762 #endif
00763 
00764   return r;
00765 }
00766 
00767 
00768 icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm)
00769 {
00770 kdDebug(5800) << " ICalFormatImpl::writeAlarm" << endl;
00771   icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT);
00772 
00773   icalproperty_action action;
00774   icalattach *attach = 0;
00775 
00776   switch (alarm->type()) {
00777     case Alarm::Procedure:
00778       action = ICAL_ACTION_PROCEDURE;
00779       attach = icalattach_new_from_url(QFile::encodeName(alarm->programFile()).data());
00780       icalcomponent_add_property(a,icalproperty_new_attach(attach));
00781       if (!alarm->programArguments().isEmpty()) {
00782         icalcomponent_add_property(a,icalproperty_new_description(alarm->programArguments().utf8()));
00783       }
00784       break;
00785     case Alarm::Audio:
00786       action = ICAL_ACTION_AUDIO;
00787 kdDebug(5800) << " It's an audio action, file: " << alarm->audioFile() << endl;
00788       if (!alarm->audioFile().isEmpty()) {
00789         attach = icalattach_new_from_url(QFile::encodeName( alarm->audioFile() ).data());
00790         icalcomponent_add_property(a,icalproperty_new_attach(attach));
00791       }
00792       break;
00793     case Alarm::Email: {
00794       action = ICAL_ACTION_EMAIL;
00795       QValueList<Person> addresses = alarm->mailAddresses();
00796       for (QValueList<Person>::Iterator ad = addresses.begin();  ad != addresses.end();  ++ad) {
00797         icalproperty *p = icalproperty_new_attendee("MAILTO:" + (*ad).email().utf8());
00798         if (!(*ad).name().isEmpty()) {
00799           icalproperty_add_parameter(p,icalparameter_new_cn((*ad).name().utf8()));
00800         }
00801         icalcomponent_add_property(a,p);
00802       }
00803       icalcomponent_add_property(a,icalproperty_new_summary(alarm->mailSubject().utf8()));
00804       icalcomponent_add_property(a,icalproperty_new_description(alarm->mailText().utf8()));
00805       QStringList attachments = alarm->mailAttachments();
00806       if (attachments.count() > 0) {
00807         for (QStringList::Iterator at = attachments.begin();  at != attachments.end();  ++at) {
00808           attach = icalattach_new_from_url(QFile::encodeName( *at ).data());
00809           icalcomponent_add_property(a,icalproperty_new_attach(attach));
00810         }
00811       }
00812       break;
00813     }
00814     case Alarm::Display:
00815       action = ICAL_ACTION_DISPLAY;
00816       icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8()));
00817       break;
00818     case Alarm::Invalid:
00819     default:
00820       kdDebug(5800) << "Unknown type of alarm" << endl;
00821       action = ICAL_ACTION_NONE;
00822       break;
00823   }
00824   icalcomponent_add_property(a,icalproperty_new_action(action));
00825 
00826   // Trigger time
00827   icaltriggertype trigger;
00828   if ( alarm->hasTime() ) {
00829     trigger.time = writeICalDateTime(alarm->time());
00830     trigger.duration = icaldurationtype_null_duration();
00831   } else {
00832     trigger.time = icaltime_null_time();
00833     Duration offset;
00834     if ( alarm->hasStartOffset() )
00835       offset = alarm->startOffset();
00836     else
00837       offset = alarm->endOffset();
00838     trigger.duration = icaldurationtype_from_int( offset.asSeconds() );
00839   }
00840   icalproperty *p = icalproperty_new_trigger(trigger);
00841   if ( alarm->hasEndOffset() )
00842     icalproperty_add_parameter(p,icalparameter_new_related(ICAL_RELATED_END));
00843   icalcomponent_add_property(a,p);
00844 
00845   // Repeat count and duration
00846   if (alarm->repeatCount()) {
00847     icalcomponent_add_property(a,icalproperty_new_repeat(alarm->repeatCount()));
00848     icalcomponent_add_property(a,icalproperty_new_duration(
00849                              icaldurationtype_from_int(alarm->snoozeTime()*60)));
00850   }
00851 
00852   // Custom properties
00853   QMap<QCString, QString> custom = alarm->customProperties();
00854   for (QMap<QCString, QString>::Iterator c = custom.begin();  c != custom.end();  ++c) {
00855     icalproperty *p = icalproperty_new_x(c.data().utf8());
00856     icalproperty_set_x_name(p,c.key());
00857     icalcomponent_add_property(a,p);
00858   }
00859 
00860   return a;
00861 }
00862 
00863 Todo *ICalFormatImpl::readTodo(icalcomponent *vtodo)
00864 {
00865   Todo *todo = new Todo;
00866 
00867   readIncidence(vtodo, 0, todo); // FIXME timezone
00868 
00869   icalproperty *p = icalcomponent_get_first_property(vtodo,ICAL_ANY_PROPERTY);
00870 
00871 //  int intvalue;
00872   icaltimetype icaltime;
00873 
00874   QStringList categories;
00875 
00876   while (p) {
00877     icalproperty_kind kind = icalproperty_isa(p);
00878     switch (kind) {
00879 
00880       case ICAL_DUE_PROPERTY:  // due date
00881         icaltime = icalproperty_get_due(p);
00882         if (icaltime.is_date) {
00883           todo->setDtDue(QDateTime(readICalDate(icaltime),QTime(0,0,0)),true);
00884         } else {
00885           todo->setDtDue(readICalDateTime(icaltime),true);
00886           todo->setFloats(false);
00887         }
00888         todo->setHasDueDate(true);
00889         break;
00890 
00891       case ICAL_COMPLETED_PROPERTY:  // completion date
00892         icaltime = icalproperty_get_completed(p);
00893         todo->setCompleted(readICalDateTime(icaltime));
00894         break;
00895 
00896       case ICAL_PERCENTCOMPLETE_PROPERTY:  // Percent completed
00897         todo->setPercentComplete(icalproperty_get_percentcomplete(p));
00898         break;
00899 
00900       case ICAL_RELATEDTO_PROPERTY:  // related todo (parent)
00901         todo->setRelatedToUid(QString::fromUtf8(icalproperty_get_relatedto(p)));
00902         mTodosRelate.append(todo);
00903         break;
00904 
00905       case ICAL_DTSTART_PROPERTY: {
00906         // Flag that todo has start date. Value is read in by readIncidence().
00907         if ( todo->comments().grep("NoStartDate").count() )
00908           todo->setHasStartDate( false );
00909         else
00910           todo->setHasStartDate( true );
00911         break;
00912       }
00913 
00914       case ICAL_RECURRENCEID_PROPERTY:
00915         icaltime = icalproperty_get_recurrenceid(p);
00916         todo->setDtRecurrence( readICalDateTime(icaltime) );
00917         break;
00918 
00919       default:
00920 //        kdDebug(5800) << "ICALFormat::readTodo(): Unknown property: " << kind
00921 //                  << endl;
00922         break;
00923     }
00924 
00925     p = icalcomponent_get_next_property(vtodo,ICAL_ANY_PROPERTY);
00926   }
00927 
00928   if (mCompat) mCompat->fixEmptySummary( todo );
00929 
00930   return todo;
00931 }
00932 
00933 Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezone )
00934 {
00935   Event *event = new Event;
00936 
00937   // FIXME where is this freed?
00938   icaltimezone *tz = icaltimezone_new();
00939   if ( !icaltimezone_set_component( tz, vtimezone ) ) {
00940     icaltimezone_free( tz, 1 );
00941     tz = 0;
00942   }
00943 
00944   readIncidence( vevent, tz, event);
00945 
00946   icalproperty *p = icalcomponent_get_first_property(vevent,ICAL_ANY_PROPERTY);
00947 
00948 //  int intvalue;
00949   icaltimetype icaltime;
00950 
00951   QStringList categories;
00952   icalproperty_transp transparency;
00953 
00954   bool dtEndProcessed = false;
00955 
00956   while (p) {
00957     icalproperty_kind kind = icalproperty_isa(p);
00958     switch (kind) {
00959 
00960       case ICAL_DTEND_PROPERTY:  // start date and time
00961         icaltime = icalproperty_get_dtend(p);
00962         if (icaltime.is_date) {
00963           // End date is non-inclusive
00964           QDate endDate = readICalDate( icaltime ).addDays( -1 );
00965           if ( mCompat ) mCompat->fixFloatingEnd( endDate );
00966           if ( endDate < event->dtStart().date() ) {
00967             endDate = event->dtStart().date();
00968           }
00969           event->setDtEnd( QDateTime( endDate, QTime( 0, 0, 0 ) ) );
00970         } else {
00971           event->setDtEnd(readICalDateTime(icaltime, tz));
00972           event->setFloats( false );
00973         }
00974         dtEndProcessed = true;
00975         break;
00976 
00977       case ICAL_RELATEDTO_PROPERTY:  // related event (parent)
00978         event->setRelatedToUid(QString::fromUtf8(icalproperty_get_relatedto(p)));
00979         mEventsRelate.append(event);
00980         break;
00981 
00982 
00983       case ICAL_TRANSP_PROPERTY:  // Transparency
00984         transparency = icalproperty_get_transp(p);
00985         if( transparency == ICAL_TRANSP_TRANSPARENT )
00986           event->setTransparency( Event::Transparent );
00987         else
00988           event->setTransparency( Event::Opaque );
00989         break;
00990 
00991       default:
00992 //        kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind
00993 //                  << endl;
00994         break;
00995     }
00996 
00997     p = icalcomponent_get_next_property(vevent,ICAL_ANY_PROPERTY);
00998   }
00999 
01000   // according to rfc2445 the dtend shouldn't be written when it equals
01001   // start date. so assign one equal to start date.
01002   if ( !dtEndProcessed ) {
01003     event->setDtEnd( event->dtStart() );
01004   }
01005 
01006   QString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT");
01007   if (!msade.isNull()) {
01008     bool floats = (msade == QString::fromLatin1("TRUE"));
01009 //    kdDebug(5800) << "ICALFormat::readEvent(): all day event: " << floats << endl;
01010     event->setFloats(floats);
01011     if (floats) {
01012       QDateTime endDate = event->dtEnd();
01013       event->setDtEnd(endDate.addDays(-1));
01014     }
01015   }
01016 
01017   if ( mCompat ) mCompat->fixEmptySummary( event );
01018 
01019   return event;
01020 }
01021 
01022 FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy)
01023 {
01024   FreeBusy *freebusy = new FreeBusy;
01025 
01026   readIncidenceBase(vfreebusy, freebusy);
01027 
01028   icalproperty *p = icalcomponent_get_first_property(vfreebusy,ICAL_ANY_PROPERTY);
01029 
01030   icaltimetype icaltime;
01031   PeriodList periods;
01032 
01033   while (p) {
01034     icalproperty_kind kind = icalproperty_isa(p);
01035     switch (kind) {
01036 
01037       case ICAL_DTSTART_PROPERTY:  // start date and time
01038         icaltime = icalproperty_get_dtstart(p);
01039         freebusy->setDtStart(readICalDateTime(icaltime));
01040         break;
01041 
01042       case ICAL_DTEND_PROPERTY:  // end Date and Time
01043         icaltime = icalproperty_get_dtend(p);
01044         freebusy->setDtEnd(readICalDateTime(icaltime));
01045         break;
01046 
01047       case ICAL_FREEBUSY_PROPERTY: { //Any FreeBusy Times
01048         icalperiodtype icalperiod = icalproperty_get_freebusy(p);
01049         QDateTime period_start = readICalDateTime(icalperiod.start);
01050         if ( !icaltime_is_null_time(icalperiod.end) ) {
01051           QDateTime period_end = readICalDateTime(icalperiod.end);
01052           periods.append( Period(period_start, period_end) );
01053         } else {
01054           Duration duration = readICalDuration( icalperiod.duration );
01055           periods.append( Period(period_start, duration) );
01056         }
01057         break;}
01058 
01059       default:
01060 //        kdDebug(5800) << "ICalFormatImpl::readFreeBusy(): Unknown property: "
01061 //                      << kind << endl;
01062       break;
01063     }
01064     p = icalcomponent_get_next_property(vfreebusy,ICAL_ANY_PROPERTY);
01065   }
01066   freebusy->addPeriods( periods );
01067 
01068   return freebusy;
01069 }
01070 
01071 Journal *ICalFormatImpl::readJournal(icalcomponent *vjournal)
01072 {
01073   Journal *journal = new Journal;
01074 
01075   readIncidence(vjournal, 0, journal); // FIXME tz?
01076 
01077   return journal;
01078 }
01079 
01080 Attendee *ICalFormatImpl::readAttendee(icalproperty *attendee)
01081 {
01082   icalparameter *p = 0;
01083 
01084   QString email = QString::fromUtf8(icalproperty_get_attendee(attendee));
01085 
01086   QString name;
01087   QString uid = QString::null;
01088   p = icalproperty_get_first_parameter(attendee,ICAL_CN_PARAMETER);
01089   if (p) {
01090     name = QString::fromUtf8(icalparameter_get_cn(p));
01091   } else {
01092   }
01093 
01094   bool rsvp=false;
01095   p = icalproperty_get_first_parameter(attendee,ICAL_RSVP_PARAMETER);
01096   if (p) {
01097     icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp(p);
01098     if (rsvpParameter == ICAL_RSVP_TRUE) rsvp = true;
01099   }
01100 
01101   Attendee::PartStat status = Attendee::NeedsAction;
01102   p = icalproperty_get_first_parameter(attendee,ICAL_PARTSTAT_PARAMETER);
01103   if (p) {
01104     icalparameter_partstat partStatParameter = icalparameter_get_partstat(p);
01105     switch(partStatParameter) {
01106       default:
01107       case ICAL_PARTSTAT_NEEDSACTION:
01108         status = Attendee::NeedsAction;
01109         break;
01110       case ICAL_PARTSTAT_ACCEPTED:
01111         status = Attendee::Accepted;
01112         break;
01113       case ICAL_PARTSTAT_DECLINED:
01114         status = Attendee::Declined;
01115         break;
01116       case ICAL_PARTSTAT_TENTATIVE:
01117         status = Attendee::Tentative;
01118         break;
01119       case ICAL_PARTSTAT_DELEGATED:
01120         status = Attendee::Delegated;
01121         break;
01122       case ICAL_PARTSTAT_COMPLETED:
01123         status = Attendee::Completed;
01124         break;
01125       case ICAL_PARTSTAT_INPROCESS:
01126         status = Attendee::InProcess;
01127         break;
01128     }
01129   }
01130 
01131   Attendee::Role role = Attendee::ReqParticipant;
01132   p = icalproperty_get_first_parameter(attendee,ICAL_ROLE_PARAMETER);
01133   if (p) {
01134     icalparameter_role roleParameter = icalparameter_get_role(p);
01135     switch(roleParameter) {
01136       case ICAL_ROLE_CHAIR:
01137         role = Attendee::Chair;
01138         break;
01139       default:
01140       case ICAL_ROLE_REQPARTICIPANT:
01141         role = Attendee::ReqParticipant;
01142         break;
01143       case ICAL_ROLE_OPTPARTICIPANT:
01144         role = Attendee::OptParticipant;
01145         break;
01146       case ICAL_ROLE_NONPARTICIPANT:
01147         role = Attendee::NonParticipant;
01148         break;
01149     }
01150   }
01151 
01152   p = icalproperty_get_first_parameter(attendee,ICAL_X_PARAMETER);
01153   uid = icalparameter_get_xvalue(p);
01154   // This should be added, but there seems to be a libical bug here.
01155   // TODO: does this work now in libical-0.24 or greater?
01156   /*while (p) {
01157    // if (icalparameter_get_xname(p) == "X-UID") {
01158     uid = icalparameter_get_xvalue(p);
01159     p = icalproperty_get_next_parameter(attendee,ICAL_X_PARAMETER);
01160   } */
01161 
01162   return new Attendee( name, email, rsvp, status, role, uid );
01163 }
01164 
01165 Person ICalFormatImpl::readOrganizer( icalproperty *organizer )
01166 {
01167   QString email = QString::fromUtf8(icalproperty_get_organizer(organizer));
01168   if ( email.startsWith("mailto:", false ) ) {
01169     email = email.mid( 7 );
01170   }
01171   QString cn;
01172 
01173   icalparameter *p = icalproperty_get_first_parameter(
01174              organizer, ICAL_CN_PARAMETER );
01175 
01176   if ( p ) {
01177     cn = QString::fromUtf8( icalparameter_get_cn( p ) );
01178   }
01179   Person org( cn, email );
01180   // TODO: Treat sent-by, dir and language here, too
01181   return org;
01182 }
01183 
01184 Attachment *ICalFormatImpl::readAttachment(icalproperty *attach)
01185 {
01186   icalattach *a = icalproperty_get_attach(attach);
01187 
01188   Attachment *attachment = 0;
01189 
01190   int isurl = icalattach_get_is_url (a);
01191   if (isurl == 0)
01192     attachment = new Attachment((const char*)icalattach_get_data(a));
01193   else {
01194     attachment = new Attachment(QString(icalattach_get_url(a)));
01195   }
01196 
01197   icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER);
01198   if (p)
01199     attachment->setMimeType(QString(icalparameter_get_fmttype(p)));
01200 
01201   return attachment;
01202 }
01203 
01204 void ICalFormatImpl::readIncidence(icalcomponent *parent, icaltimezone *tz, Incidence *incidence)
01205 {
01206   readIncidenceBase(parent,incidence);
01207 
01208   icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY);
01209 
01210   const char *text;
01211   int intvalue, inttext;
01212   icaltimetype icaltime;
01213   icaldurationtype icalduration;
01214 
01215   QStringList categories;
01216 
01217   while (p) {
01218     icalproperty_kind kind = icalproperty_isa(p);
01219     switch (kind) {
01220 
01221       case ICAL_CREATED_PROPERTY:
01222         icaltime = icalproperty_get_created(p);
01223         incidence->setCreated(readICalDateTime(icaltime, tz));
01224         break;
01225 
01226       case ICAL_SEQUENCE_PROPERTY:  // sequence
01227         intvalue = icalproperty_get_sequence(p);
01228         incidence->setRevision(intvalue);
01229         break;
01230 
01231       case ICAL_LASTMODIFIED_PROPERTY:  // last modification date
01232         icaltime = icalproperty_get_lastmodified(p);
01233         incidence->setLastModified(readICalDateTime(icaltime, tz));
01234         break;
01235 
01236       case ICAL_DTSTART_PROPERTY:  // start date and time
01237         icaltime = icalproperty_get_dtstart(p);
01238         if (icaltime.is_date) {
01239           incidence->setDtStart(QDateTime(readICalDate(icaltime),QTime(0,0,0)));
01240           incidence->setFloats( true );
01241         } else {
01242           incidence->setDtStart(readICalDateTime(icaltime, tz));
01243           incidence->setFloats( false );
01244         }
01245         break;
01246 
01247       case ICAL_DURATION_PROPERTY:  // start date and time
01248         icalduration = icalproperty_get_duration(p);
01249         incidence->setDuration(readICalDuration(icalduration));
01250         break;
01251 
01252       case ICAL_DESCRIPTION_PROPERTY:  // description
01253         text = icalproperty_get_description(p);
01254         incidence->setDescription(QString::fromUtf8(text));
01255         break;
01256 
01257       case ICAL_SUMMARY_PROPERTY:  // summary
01258         text = icalproperty_get_summary(p);
01259         incidence->setSummary(QString::fromUtf8(text));
01260         break;
01261 
01262       case ICAL_LOCATION_PROPERTY:  // location
01263         text = icalproperty_get_location(p);
01264         incidence->setLocation(QString::fromUtf8(text));
01265         break;
01266 
01267       case ICAL_STATUS_PROPERTY: {  // status
01268         Incidence::Status stat;
01269         switch (icalproperty_get_status(p)) {
01270           case ICAL_STATUS_TENTATIVE:   stat = Incidence::StatusTentative; break;
01271           case ICAL_STATUS_CONFIRMED:   stat = Incidence::StatusConfirmed; break;
01272           case ICAL_STATUS_COMPLETED:   stat = Incidence::StatusCompleted; break;
01273           case ICAL_STATUS_NEEDSACTION: stat = Incidence::StatusNeedsAction; break;
01274           case ICAL_STATUS_CANCELLED:   stat = Incidence::StatusCanceled; break;
01275           case ICAL_STATUS_INPROCESS:   stat = Incidence::StatusInProcess; break;
01276           case ICAL_STATUS_DRAFT:       stat = Incidence::StatusDraft; break;
01277           case ICAL_STATUS_FINAL:       stat = Incidence::StatusFinal; break;
01278           case ICAL_STATUS_X:
01279             incidence->setCustomStatus(QString::fromUtf8(icalvalue_get_x(icalproperty_get_value(p))));
01280             stat = Incidence::StatusX;
01281             break;
01282           case ICAL_STATUS_NONE:
01283           default:                      stat = Incidence::StatusNone; break;
01284         }
01285         if (stat != Incidence::StatusX)
01286           incidence->setStatus(stat);
01287         break;
01288       }
01289 
01290       case ICAL_PRIORITY_PROPERTY:  // priority
01291         intvalue = icalproperty_get_priority( p );
01292         if ( mCompat )
01293           intvalue = mCompat->fixPriority( intvalue );
01294         incidence->setPriority( intvalue );
01295         break;
01296 
01297       case ICAL_CATEGORIES_PROPERTY:  // categories
01298         text = icalproperty_get_categories(p);
01299         categories.append(QString::fromUtf8(text));
01300         break;
01301 
01302       case ICAL_RRULE_PROPERTY:
01303         readRecurrenceRule( p, incidence );
01304         break;
01305 
01306       case ICAL_RDATE_PROPERTY: {
01307         icaldatetimeperiodtype rd = icalproperty_get_rdate( p );
01308         if ( icaltime_is_valid_time( rd.time ) ) {
01309           if ( icaltime_is_date( rd.time ) ) {
01310             incidence->recurrence()->addRDate( readICalDate( rd.time ) );
01311           } else {
01312             incidence->recurrence()->addRDateTime( readICalDateTime(rd.time ) );
01313           }
01314         } else {
01315           // TODO: RDates as period are not yet implemented!
01316         }
01317         break; }
01318 
01319       case ICAL_EXRULE_PROPERTY:
01320         readExceptionRule( p, incidence );
01321         break;
01322 
01323       case ICAL_EXDATE_PROPERTY:
01324         icaltime = icalproperty_get_exdate(p);
01325         if ( icaltime_is_date(icaltime) ) {
01326           incidence->recurrence()->addExDate( readICalDate(icaltime) );
01327         } else {
01328           incidence->recurrence()->addExDateTime( readICalDateTime(icaltime, tz) );
01329         }
01330         break;
01331 
01332       case ICAL_CLASS_PROPERTY:
01333         inttext = icalproperty_get_class(p);
01334         if (inttext == ICAL_CLASS_PUBLIC ) {
01335           incidence->setSecrecy(Incidence::SecrecyPublic);
01336         } else if (inttext == ICAL_CLASS_CONFIDENTIAL ) {
01337           incidence->setSecrecy(Incidence::SecrecyConfidential);
01338         } else {
01339           incidence->setSecrecy(Incidence::SecrecyPrivate);
01340         }
01341         break;
01342 
01343       case ICAL_ATTACH_PROPERTY:  // attachments
01344         incidence->addAttachment(readAttachment(p));
01345         break;
01346 
01347       default:
01348 //        kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind
01349 //                  << endl;
01350         break;
01351     }
01352 
01353     p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY);
01354   }
01355 
01356   // Set the scheduling ID
01357   const QString uid = incidence->customProperty( "LIBKCAL", "ID" );
01358   if ( !uid.isNull() ) {
01359     // The UID stored in incidencebase is actually the scheduling ID
01360     // It has to be stored in the iCal UID component for compatibility
01361     // with other iCal applications
01362     incidence->setSchedulingID( incidence->uid() );
01363     incidence->setUid( uid );
01364   }
01365 
01366   // kpilot stuff
01367 // TODO: move this application-specific code to kpilot
01368   QString kp = incidence->nonKDECustomProperty("X-PILOTID");
01369   if (!kp.isNull()) {
01370     incidence->setPilotId(kp.toInt());
01371   }
01372   kp = incidence->nonKDECustomProperty("X-PILOTSTAT");
01373   if (!kp.isNull()) {
01374     incidence->setSyncStatus(kp.toInt());
01375   }
01376 
01377   // Now that recurrence and exception stuff is completely set up,
01378   // do any backwards compatibility adjustments.
01379   if ( incidence->doesRecur() && mCompat )
01380       mCompat->fixRecurrence( incidence );
01381 
01382   // add categories
01383   incidence->setCategories(categories);
01384 
01385   // iterate through all alarms
01386   for (icalcomponent *alarm = icalcomponent_get_first_component(parent,ICAL_VALARM_COMPONENT);
01387        alarm;
01388        alarm = icalcomponent_get_next_component(parent,ICAL_VALARM_COMPONENT)) {
01389     readAlarm(alarm,incidence);
01390   }
01391   // Fix incorrect alarm settings by other applications (like outloook 9)
01392   if ( mCompat ) mCompat->fixAlarms( incidence );
01393 }
01394 
01395 void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase)
01396 {
01397   icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY);
01398 
01399   while (p) {
01400     icalproperty_kind kind = icalproperty_isa(p);
01401     switch (kind) {
01402 
01403       case ICAL_UID_PROPERTY:  // unique id
01404         incidenceBase->setUid(QString::fromUtf8(icalproperty_get_uid(p)));
01405         break;
01406 
01407       case ICAL_ORGANIZER_PROPERTY:  // organizer
01408         incidenceBase->setOrganizer( readOrganizer(p));
01409         break;
01410 
01411       case ICAL_ATTENDEE_PROPERTY:  // attendee
01412         incidenceBase->addAttendee(readAttendee(p));
01413         break;
01414 
01415       case ICAL_COMMENT_PROPERTY:
01416         incidenceBase->addComment(
01417             QString::fromUtf8(icalproperty_get_comment(p)));
01418         break;
01419 
01420       default:
01421         break;
01422     }
01423 
01424     p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY);
01425   }
01426 
01427   // custom properties
01428   readCustomProperties(parent, incidenceBase);
01429 }
01430 
01431 void ICalFormatImpl::readCustomProperties(icalcomponent *parent,CustomProperties *properties)
01432 {
01433   QMap<QCString, QString> customProperties;
01434 
01435   icalproperty *p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY);
01436 
01437   while (p) {
01438 
01439     QString value = QString::fromUtf8(icalproperty_get_x(p));
01440     const char *name = icalproperty_get_x_name(p);
01441     customProperties[name] = value;
01442     kdDebug(5800) << "Set custom property [" << name << '=' << value << ']' << endl;
01443     p = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY);
01444   }
01445 
01446   properties->setCustomProperties(customProperties);
01447 }
01448 
01449 
01450 
01451 void ICalFormatImpl::readRecurrenceRule(icalproperty *rrule,Incidence *incidence )
01452 {
01453 //  kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl;
01454 
01455   Recurrence *recur = incidence->recurrence();
01456 
01457   struct icalrecurrencetype r = icalproperty_get_rrule(rrule);
01458   dumpIcalRecurrence(r);
01459 
01460   RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/ );
01461   recurrule->setStartDt( incidence->dtStart() );
01462   readRecurrence( r, recurrule );
01463   recur->addRRule( recurrule );
01464 }
01465 
01466 void ICalFormatImpl::readExceptionRule( icalproperty *rrule, Incidence *incidence )
01467 {
01468 //  kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl;
01469 
01470   struct icalrecurrencetype r = icalproperty_get_exrule(rrule);
01471   dumpIcalRecurrence(r);
01472 
01473   RecurrenceRule *recurrule = new RecurrenceRule( /*incidence*/ );
01474   recurrule->setStartDt( incidence->dtStart() );
01475   readRecurrence( r, recurrule );
01476 
01477   Recurrence *recur = incidence->recurrence();
01478   recur->addExRule( recurrule );
01479 }
01480 
01481 void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, RecurrenceRule* recur )
01482 {
01483   // Generate the RRULE string
01484   recur->mRRule = QString( icalrecurrencetype_as_string( const_cast<struct icalrecurrencetype*>(&r) ) );
01485   // Period
01486   switch ( r.freq ) {
01487     case ICAL_SECONDLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rSecondly ); break;
01488     case ICAL_MINUTELY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMinutely ); break;
01489     case ICAL_HOURLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rHourly ); break;
01490     case ICAL_DAILY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rDaily ); break;
01491     case ICAL_WEEKLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rWeekly ); break;
01492     case ICAL_MONTHLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rMonthly ); break;
01493     case ICAL_YEARLY_RECURRENCE: recur->setRecurrenceType( RecurrenceRule::rYearly ); break;
01494     case ICAL_NO_RECURRENCE:
01495     default:
01496         recur->setRecurrenceType( RecurrenceRule::rNone );
01497   }
01498   // Frequency
01499   recur->setFrequency( r.interval );
01500 
01501   // Duration & End Date
01502   if ( !icaltime_is_null_time( r.until ) ) {
01503     icaltimetype t;
01504     t = r.until;
01505     // Convert to the correct time zone! it's in UTC by specification.
01506     QDateTime endDate( readICalDateTime(t) );
01507     recur->setEndDt( endDate );
01508   } else {
01509     if (r.count == 0)
01510       recur->setDuration( -1 );
01511     else
01512       recur->setDuration( r.count );
01513   }
01514 
01515   // Week start setting
01516   int wkst = (r.week_start + 5)%7 + 1;
01517   recur->setWeekStart( wkst );
01518 
01519   // And now all BY*
01520   QValueList<int> lst;
01521   int i;
01522   int index = 0;
01523 
01524 #define readSetByList(rrulecomp,setfunc) \
01525   index = 0; \
01526   lst.clear(); \
01527   while ( (i = r.rrulecomp[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) \
01528     lst.append( i ); \
01529   if ( !lst.isEmpty() ) recur->setfunc( lst );
01530 
01531   // BYSECOND, MINUTE and HOUR, MONTHDAY, YEARDAY, WEEKNUMBER, MONTH
01532   // and SETPOS are standard int lists, so we can treat them with the
01533   // same macro
01534   readSetByList( by_second, setBySeconds );
01535   readSetByList( by_minute, setByMinutes );
01536   readSetByList( by_hour, setByHours );
01537   readSetByList( by_month_day, setByMonthDays );
01538   readSetByList( by_year_day, setByYearDays );
01539   readSetByList( by_week_no, setByWeekNumbers );
01540   readSetByList( by_month, setByMonths );
01541   readSetByList( by_set_pos, setBySetPos );
01542 #undef readSetByList
01543 
01544   // BYDAY is a special case, since it's not an int list
01545   QValueList<RecurrenceRule::WDayPos> wdlst;
01546   short day;
01547   index=0;
01548   while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
01549     RecurrenceRule::WDayPos pos;
01550     pos.setDay( ( icalrecurrencetype_day_day_of_week( day ) + 5 )%7 + 1 );
01551     pos.setPos( icalrecurrencetype_day_position( day ) );
01552 //     kdDebug(5800)<< "    o) By day, index="<<index-1<<", pos="<<pos.Pos<<", day="<<pos.Day<<endl;
01553     wdlst.append( pos );
01554   }
01555   if ( !wdlst.isEmpty() ) recur->setByDays( wdlst );
01556 
01557 
01558   // TODO Store all X- fields of the RRULE inside the recurrence (so they are
01559   // preserved
01560 }
01561 
01562 
01563 void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence)
01564 {
01565   kdDebug(5800) << "Read alarm for " << incidence->summary() << endl;
01566 
01567   Alarm* ialarm = incidence->newAlarm();
01568   ialarm->setRepeatCount(0);
01569   ialarm->setEnabled(true);
01570 
01571   // Determine the alarm's action type
01572   icalproperty *p = icalcomponent_get_first_property(alarm,ICAL_ACTION_PROPERTY);
01573   Alarm::Type type = Alarm::Display;
01574   icalproperty_action action = ICAL_ACTION_DISPLAY;
01575   if ( !p ) {
01576     kdDebug(5800) << "Unknown type of alarm, using default" << endl;
01577 //    return;
01578   } else {
01579 
01580     action = icalproperty_get_action(p);
01581     switch ( action ) {
01582       case ICAL_ACTION_DISPLAY:   type = Alarm::Display;  break;
01583       case ICAL_ACTION_AUDIO:     type = Alarm::Audio;  break;
01584       case ICAL_ACTION_PROCEDURE: type = Alarm::Procedure;  break;
01585       case ICAL_ACTION_EMAIL:     type = Alarm::Email;  break;
01586       default:
01587         kdDebug(5800) << "Unknown type of alarm: " << action << endl;
01588 //        type = Alarm::Invalid;
01589     }
01590   }
01591   ialarm->setType(type);
01592 kdDebug(5800) << " alarm type =" << type << endl;
01593 
01594   p = icalcomponent_get_first_property(alarm,ICAL_ANY_PROPERTY);
01595   while (p) {
01596     icalproperty_kind kind = icalproperty_isa(p);
01597 
01598     switch (kind) {
01599 
01600       case ICAL_TRIGGER_PROPERTY: {
01601         icaltriggertype trigger = icalproperty_get_trigger(p);
01602         if (icaltime_is_null_time(trigger.time)) {
01603           if (icaldurationtype_is_null_duration(trigger.duration)) {
01604             kdDebug(5800) << "ICalFormatImpl::readAlarm(): Trigger has no time and no duration." << endl;
01605           } else {
01606             Duration duration = icaldurationtype_as_int( trigger.duration );
01607             icalparameter *param = icalproperty_get_first_parameter(p,ICAL_RELATED_PARAMETER);
01608             if (param && icalparameter_get_related(param) == ICAL_RELATED_END)
01609               ialarm->setEndOffset(duration);
01610             else
01611               ialarm->setStartOffset(duration);
01612           }
01613         } else {
01614           ialarm->setTime(readICalDateTime(trigger.time));
01615         }
01616         break;
01617       }
01618       case ICAL_DURATION_PROPERTY: {
01619         icaldurationtype duration = icalproperty_get_duration(p);
01620         ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60);
01621         break;
01622       }
01623       case ICAL_REPEAT_PROPERTY:
01624         ialarm->setRepeatCount(icalproperty_get_repeat(p));
01625         break;
01626 
01627       // Only in DISPLAY and EMAIL and PROCEDURE alarms
01628       case ICAL_DESCRIPTION_PROPERTY: {
01629         QString description = QString::fromUtf8(icalproperty_get_description(p));
01630         switch ( action ) {
01631           case ICAL_ACTION_DISPLAY:
01632             ialarm->setText( description );
01633             break;
01634           case ICAL_ACTION_PROCEDURE:
01635             ialarm->setProgramArguments( description );
01636             break;
01637           case ICAL_ACTION_EMAIL:
01638             ialarm->setMailText( description );
01639             break;
01640           default:
01641             break;
01642         }
01643         break;
01644       }
01645       // Only in EMAIL alarm
01646       case ICAL_SUMMARY_PROPERTY:
01647         ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p)));
01648         break;
01649 
01650       // Only in EMAIL alarm
01651       case ICAL_ATTENDEE_PROPERTY: {
01652         QString email = QString::fromUtf8(icalproperty_get_attendee(p));
01653         QString name;
01654         icalparameter *param = icalproperty_get_first_parameter(p,ICAL_CN_PARAMETER);
01655         if (param) {
01656           name = QString::fromUtf8(icalparameter_get_cn(param));
01657         }
01658         ialarm->addMailAddress(Person(name, email));
01659         break;
01660       }
01661       // Only in AUDIO and EMAIL and PROCEDURE alarms
01662       case ICAL_ATTACH_PROPERTY: {
01663         Attachment *attach = readAttachment( p );
01664         if ( attach && attach->isUri() ) {
01665           switch ( action ) {
01666             case ICAL_ACTION_AUDIO:
01667               ialarm->setAudioFile( attach->uri() );
01668               break;
01669             case ICAL_ACTION_PROCEDURE:
01670               ialarm->setProgramFile( attach->uri() );
01671               break;
01672             case ICAL_ACTION_EMAIL:
01673               ialarm->addMailAttachment( attach->uri() );
01674               break;
01675             default:
01676               break;
01677           }
01678         } else {
01679           kdDebug() << "Alarm attachments currently only support URIs, but "
01680                        "no binary data" << endl;
01681         }
01682         delete attach;
01683         break;
01684       }
01685       default:
01686         break;
01687     }
01688 
01689     p = icalcomponent_get_next_property(alarm,ICAL_ANY_PROPERTY);
01690   }
01691 
01692   // custom properties
01693   readCustomProperties(alarm, ialarm);
01694 
01695   // TODO: check for consistency of alarm properties
01696 }
01697 
01698 icaldatetimeperiodtype ICalFormatImpl::writeICalDatePeriod( const QDate &date )
01699 {
01700   icaldatetimeperiodtype t;
01701   t.time = writeICalDate( date );
01702   t.period = icalperiodtype_null_period();
01703   return t;
01704 }
01705 
01706 icaldatetimeperiodtype ICalFormatImpl::writeICalDateTimePeriod( const QDateTime &date )
01707 {
01708   icaldatetimeperiodtype t;
01709   t.time = writeICalDateTime( date );
01710   t.period = icalperiodtype_null_period();
01711   return t;
01712 }
01713 
01714 icaltimetype ICalFormatImpl::writeICalDate(const QDate &date)
01715 {
01716   icaltimetype t = icaltime_null_time();
01717 
01718   t.year = date.year();
01719   t.month = date.month();
01720   t.day = date.day();
01721 
01722   t.hour = 0;
01723   t.minute = 0;
01724   t.second = 0;
01725 
01726   t.is_date = 1;
01727 
01728   t.is_utc = 0;
01729 
01730   t.zone = 0;
01731 
01732   return t;
01733 }
01734 
01735 icaltimetype ICalFormatImpl::writeICalDateTime(const QDateTime &datetime)
01736 {
01737   icaltimetype t = icaltime_null_time();
01738 
01739   t.year = datetime.date().year();
01740   t.month = datetime.date().month();
01741   t.day = datetime.date().day();
01742 
01743   t.hour = datetime.time().hour();
01744   t.minute = datetime.time().minute();
01745   t.second = datetime.time().second();
01746 
01747   t.is_date = 0;
01748   t.zone = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
01749   t.is_utc = 0;
01750 
01751  // _dumpIcaltime( t );
01752   /* The QDateTime we get passed in is to be considered in the timezone of
01753    * the current calendar (mParent's), or, if there is none, to be floating.
01754    * In the later case store a floating time, in the former normalize to utc. */
01755   if (mParent->timeZoneId().isEmpty())
01756     t = icaltime_convert_to_zone( t, 0 ); //make floating timezone
01757   else {
01758     icaltimezone* tz = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
01759     icaltimezone* utc = icaltimezone_get_utc_timezone();
01760     if ( tz != utc ) {
01761       t.zone = tz;
01762       t = icaltime_convert_to_zone( t, utc );
01763     } else {
01764       t.is_utc = 1;
01765       t.zone = utc;
01766     }
01767   }
01768 //  _dumpIcaltime( t );
01769 
01770   return t;
01771 }
01772 
01773 QDateTime ICalFormatImpl::readICalDateTime( icaltimetype& t, icaltimezone* tz )
01774 {
01775 //   kdDebug(5800) << "ICalFormatImpl::readICalDateTime()" << endl;
01776   if ( tz ) {
01777     t.zone = tz;
01778     t.is_utc = (tz == icaltimezone_get_utc_timezone())?1:0;
01779   }
01780   //_dumpIcaltime( t );
01781 
01782   // Convert to view time
01783   if ( !mParent->timeZoneId().isEmpty() && t.zone ) {
01784 //    kdDebug(5800) << "--- Converting time from: " << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) ) << " (" << ICalDate2QDate(t) << ")." << endl;
01785     icaltimezone* viewTimeZone = icaltimezone_get_builtin_timezone ( mParent->timeZoneId().latin1() );
01786     icaltimezone_convert_time(  &t, const_cast<icaltimezone*>( t.zone ), viewTimeZone );
01787 //    kdDebug(5800) << "--- Converted to zone " << mParent->timeZoneId() << " (" << ICalDate2QDate(t) << ")." << endl;
01788   }
01789 
01790   return ICalDate2QDate(t);
01791 }
01792 
01793 QDate ICalFormatImpl::readICalDate(icaltimetype t)
01794 {
01795   return ICalDate2QDate(t).date();
01796 }
01797 
01798 icaldurationtype ICalFormatImpl::writeICalDuration(int seconds)
01799 {
01800   icaldurationtype d;
01801 
01802   d.is_neg  = (seconds<0)?1:0;
01803   if (seconds<0) seconds = -seconds;
01804 
01805   d.weeks    = seconds / gSecondsPerWeek;
01806   seconds   %= gSecondsPerWeek;
01807   d.days     = seconds / gSecondsPerDay;
01808   seconds   %= gSecondsPerDay;
01809   d.hours    = seconds / gSecondsPerHour;
01810   seconds   %= gSecondsPerHour;
01811   d.minutes  = seconds / gSecondsPerMinute;
01812   seconds   %= gSecondsPerMinute;
01813   d.seconds  = seconds;
01814 
01815   return d;
01816 }
01817 
01818 int ICalFormatImpl::readICalDuration(icaldurationtype d)
01819 {
01820   int result = 0;
01821 
01822   result += d.weeks   * gSecondsPerWeek;
01823   result += d.days    * gSecondsPerDay;
01824   result += d.hours   * gSecondsPerHour;
01825   result += d.minutes * gSecondsPerMinute;
01826   result += d.seconds;
01827 
01828   if (d.is_neg) result *= -1;
01829 
01830   return result;
01831 }
01832 
01833 icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal)
01834 {
01835   icalcomponent *calendar;
01836 
01837   // Root component
01838   calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
01839 
01840   icalproperty *p;
01841 
01842   // Product Identifier
01843   p = icalproperty_new_prodid(CalFormat::productId().utf8());
01844   icalcomponent_add_property(calendar,p);
01845 
01846   // TODO: Add time zone
01847 
01848   // iCalendar version (2.0)
01849   p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION));
01850   icalcomponent_add_property(calendar,p);
01851 
01852   // Custom properties
01853   if( cal != 0 )
01854     writeCustomProperties(calendar, cal);
01855 
01856   return calendar;
01857 }
01858 
01859 
01860 
01861 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
01862 // and break it down from its tree-like format into the dictionary format
01863 // that is used internally in the ICalFormatImpl.
01864 bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
01865 {
01866   // this function will populate the caldict dictionary and other event
01867   // lists. It turns vevents into Events and then inserts them.
01868 
01869     if (!calendar) return false;
01870 
01871 // TODO: check for METHOD
01872 
01873   icalproperty *p;
01874 
01875   p = icalcomponent_get_first_property(calendar,ICAL_PRODID_PROPERTY);
01876   if (!p) {
01877     kdDebug(5800) << "No PRODID property found" << endl;
01878     mLoadedProductId = "";
01879   } else {
01880     mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p));
01881 //    kdDebug(5800) << "VCALENDAR prodid: '" << mLoadedProductId << "'" << endl;
01882 
01883     delete mCompat;
01884     mCompat = CompatFactory::createCompat( mLoadedProductId );
01885   }
01886 
01887   p = icalcomponent_get_first_property(calendar,ICAL_VERSION_PROPERTY);
01888   if (!p) {
01889     kdDebug(5800) << "No VERSION property found" << endl;
01890     mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
01891     return false;
01892   } else {
01893     const char *version = icalproperty_get_version(p);
01894 //    kdDebug(5800) << "VCALENDAR version: '" << version << "'" << endl;
01895 
01896     if (strcmp(version,"1.0") == 0) {
01897       kdDebug(5800) << "Expected iCalendar, got vCalendar" << endl;
01898       mParent->setException(new ErrorFormat(ErrorFormat::CalVersion1,
01899                             i18n("Expected iCalendar format")));
01900       return false;
01901     } else if (strcmp(version,"2.0") != 0) {
01902       kdDebug(5800) << "Expected iCalendar, got unknown format" << endl;
01903       mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown));
01904       return false;
01905     }
01906   }
01907 
01908   // custom properties
01909   readCustomProperties(calendar, cal);
01910 
01911 // TODO: set time zone
01912 
01913   // read a VTIMEZONE if there is one
01914   icalcomponent *ctz =
01915     icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
01916 
01917   // Store all events with a relatedTo property in a list for post-processing
01918   mEventsRelate.clear();
01919   mTodosRelate.clear();
01920   // TODO: make sure that only actually added events go to this lists.
01921 
01922   icalcomponent *c;
01923 
01924   // Iterate through all todos
01925   c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT);
01926   while (c) {
01927 //    kdDebug(5800) << "----Todo found" << endl;
01928     Todo *todo = readTodo(c);
01929     if (todo && !cal->todo(todo->uid())) cal->addTodo(todo);
01930     c = icalcomponent_get_next_component(calendar,ICAL_VTODO_COMPONENT);
01931   }
01932 
01933   // Iterate through all events
01934   c = icalcomponent_get_first_component(calendar,ICAL_VEVENT_COMPONENT);
01935   while (c) {
01936 //    kdDebug(5800) << "----Event found" << endl;
01937     Event *event = readEvent(c, ctz);
01938     if (event && !cal->event(event->uid())) cal->addEvent(event);
01939     c = icalcomponent_get_next_component(calendar,ICAL_VEVENT_COMPONENT);
01940   }
01941 
01942   // Iterate through all journals
01943   c = icalcomponent_get_first_component(calendar,ICAL_VJOURNAL_COMPONENT);
01944   while (c) {
01945 //    kdDebug(5800) << "----Journal found" << endl;
01946     Journal *journal = readJournal(c);
01947     if (journal && !cal->journal(journal->uid())) cal->addJournal(journal);
01948     c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT);
01949   }
01950 
01951   // Post-Process list of events with relations, put Event objects in relation
01952   Event::List::ConstIterator eIt;
01953   for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) {
01954     (*eIt)->setRelatedTo( cal->incidence( (*eIt)->relatedToUid() ) );
01955   }
01956   Todo::List::ConstIterator tIt;
01957   for ( tIt = mTodosRelate.begin(); tIt != mTodosRelate.end(); ++tIt ) {
01958     (*tIt)->setRelatedTo( cal->incidence( (*tIt)->relatedToUid() ) );
01959    }
01960 
01961   return true;
01962 }
01963 
01964 QString ICalFormatImpl::extractErrorProperty(icalcomponent *c)
01965 {
01966 //  kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: "
01967 //            << icalcomponent_as_ical_string(c) << endl;
01968 
01969   QString errorMessage;
01970 
01971   icalproperty *error;
01972   error = icalcomponent_get_first_property(c,ICAL_XLICERROR_PROPERTY);
01973   while(error) {
01974     errorMessage += icalproperty_get_xlicerror(error);
01975     errorMessage += "\n";
01976     error = icalcomponent_get_next_property(c,ICAL_XLICERROR_PROPERTY);
01977   }
01978 
01979 //  kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " << errorMessage << endl;
01980 
01981   return errorMessage;
01982 }
01983 
01984 void ICalFormatImpl::dumpIcalRecurrence(icalrecurrencetype r)
01985 {
01986   int i;
01987 
01988   kdDebug(5800) << " Freq: " << r.freq << endl;
01989   kdDebug(5800) << " Until: " << icaltime_as_ical_string(r.until) << endl;
01990   kdDebug(5800) << " Count: " << r.count << endl;
01991   if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
01992     int index = 0;
01993     QString out = " By Day: ";
01994     while((i = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
01995       out.append(QString::number(i) + " ");
01996     }
01997     kdDebug(5800) << out << endl;
01998   }
01999   if (r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
02000     int index = 0;
02001     QString out = " By Month Day: ";
02002     while((i = r.by_month_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
02003       out.append(QString::number(i) + " ");
02004     }
02005     kdDebug(5800) << out << endl;
02006   }
02007   if (r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX) {
02008     int index = 0;
02009     QString out = " By Year Day: ";
02010     while((i = r.by_year_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
02011       out.append(QString::number(i) + " ");
02012     }
02013     kdDebug(5800) << out << endl;
02014   }
02015   if (r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) {
02016     int index = 0;
02017     QString out = " By Month: ";
02018     while((i = r.by_month[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
02019       out.append(QString::number(i) + " ");
02020     }
02021     kdDebug(5800) << out << endl;
02022   }
02023   if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) {
02024     int index = 0;
02025     QString out = " By Set Pos: ";
02026     while((i = r.by_set_pos[index++]) != ICAL_RECURRENCE_ARRAY_MAX) {
02027       kdDebug(5800) << "========= " << i << endl;
02028       out.append(QString::number(i) + " ");
02029     }
02030     kdDebug(5800) << out << endl;
02031   }
02032 }
02033 
02034 icalcomponent *ICalFormatImpl::createScheduleComponent(IncidenceBase *incidence,
02035                                                    Scheduler::Method method)
02036 {
02037   icalcomponent *message = createCalendarComponent();
02038 
02039   icalproperty_method icalmethod = ICAL_METHOD_NONE;
02040 
02041   switch (method) {
02042     case Scheduler::Publish:
02043       icalmethod = ICAL_METHOD_PUBLISH;
02044       break;
02045     case Scheduler::Request:
02046       icalmethod = ICAL_METHOD_REQUEST;
02047       break;
02048     case Scheduler::Refresh:
02049       icalmethod = ICAL_METHOD_REFRESH;
02050       break;
02051     case Scheduler::Cancel:
02052       icalmethod = ICAL_METHOD_CANCEL;
02053       break;
02054     case Scheduler::Add:
02055       icalmethod = ICAL_METHOD_ADD;
02056       break;
02057     case Scheduler::Reply:
02058       icalmethod = ICAL_METHOD_REPLY;
02059       break;
02060     case Scheduler::Counter:
02061       icalmethod = ICAL_METHOD_COUNTER;
02062       break;
02063     case Scheduler::Declinecounter:
02064       icalmethod = ICAL_METHOD_DECLINECOUNTER;
02065       break;
02066     default:
02067       kdDebug(5800) << "ICalFormat::createScheduleMessage(): Unknow method" << endl;
02068       return message;
02069   }
02070 
02071   icalcomponent_add_property(message,icalproperty_new_method(icalmethod));
02072 
02073   icalcomponent *inc = writeIncidence( incidence, method );
02074   /*
02075    * RFC 2446 states in section 3.4.3 ( REPLY to a VTODO ), that
02076    * a REQUEST-STATUS property has to be present. For the other two, event and
02077    * free busy, it can be there, but is optional. Until we do more
02078    * fine grained handling, assume all is well. Note that this is the
02079    * status of the _request_, not the attendee. Just to avoid confusion.
02080    * - till
02081    */
02082   if ( icalmethod == ICAL_METHOD_REPLY ) {
02083     struct icalreqstattype rst;
02084     rst.code = ICAL_2_0_SUCCESS_STATUS;
02085     rst.desc = 0;
02086     rst.debug = 0;
02087     icalcomponent_add_property( inc, icalproperty_new_requeststatus( rst ) );
02088   }
02089   icalcomponent_add_component( message, inc );
02090 
02091   return message;
02092 }
KDE Home | KDE Accessibility Home | Description of Access Keys