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