libkcal

icalformatimpl.cpp

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