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