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