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