• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.8.3 API Reference
  • KDE Home
  • Contact Us
 

KCalCore Library

vcalformat.cpp
Go to the documentation of this file.
00001 /*
00002   This file is part of the kcalcore library.
00003 
00004   Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005   Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
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 */
00037 #include "vcalformat.h"
00038 #include "calendar.h"
00039 #include "event.h"
00040 #include "exceptions.h"
00041 #include "icaltimezones.h"
00042 #include "todo.h"
00043 #include "versit/vcc.h"
00044 #include "versit/vobject.h"
00045 
00046 #include <KCodecs>
00047 #include <KDebug>
00048 
00049 #include <QtCore/QBitArray>
00050 #include <QtCore/QFile>
00051 #include <QtGui/QTextDocument> // for Qt::escape() and Qt::mightBeRichText()
00052 
00053 using namespace KCalCore;
00054 
00059 //@cond PRIVATE
00060 template <typename K>
00061 void removeAllVCal( QVector< QSharedPointer<K> > &c, const QSharedPointer<K> &x )
00062 {
00063   Q_ASSERT( c.count( x ) == 1 );
00064   c.remove( c.indexOf( x ) );
00065 }
00066 
00067 class KCalCore::VCalFormat::Private
00068 {
00069   public:
00070     Calendar::Ptr mCalendar;
00071     Event::List mEventsRelate;  // Events with relations
00072     Todo::List mTodosRelate;    // To-dos with relations
00073     QSet<QByteArray> mManuallyWrittenExtensionFields; // X- fields that are manually dumped
00074 };
00075 //@endcond
00076 
00077 VCalFormat::VCalFormat() : d( new KCalCore::VCalFormat::Private )
00078 {
00079 #if defined(KCALCORE_FOR_SYMBIAN)
00080   d->mManuallyWrittenExtensionFields << VCRecurrenceIdProp;
00081   d->mManuallyWrittenExtensionFields << EPOCAgendaEntryTypeProp;
00082 #endif
00083   d->mManuallyWrittenExtensionFields << KPilotIdProp;
00084   d->mManuallyWrittenExtensionFields << KPilotStatusProp;
00085 }
00086 
00087 VCalFormat::~VCalFormat()
00088 {
00089   delete d;
00090 }
00091 
00092 bool VCalFormat::load( const Calendar::Ptr &calendar, const QString &fileName )
00093 {
00094   d->mCalendar = calendar;
00095 
00096   clearException();
00097 
00098   VObject *vcal = 0;
00099 
00100   // this is not necessarily only 1 vcal.  Could be many vcals, or include
00101   // a vcard...
00102   vcal = Parse_MIME_FromFileName( const_cast<char *>( QFile::encodeName( fileName ).data() ) );
00103 
00104   if ( !vcal ) {
00105     setException( new Exception( Exception::CalVersionUnknown ) );
00106     return false;
00107   }
00108 
00109   // any other top-level calendar stuff should be added/initialized here
00110 
00111   // put all vobjects into their proper places
00112   QString savedTimeZoneId = d->mCalendar->timeZoneId();
00113   populate( vcal, false, fileName );
00114   d->mCalendar->setTimeZoneId(savedTimeZoneId);
00115 
00116   // clean up from vcal API stuff
00117   cleanVObjects( vcal );
00118   cleanStrTbl();
00119 
00120   return true;
00121 }
00122 
00123 bool VCalFormat::save( const Calendar::Ptr &calendar, const QString &fileName )
00124 {
00125   d->mCalendar = calendar;
00126 
00127   ICalTimeZones *tzlist = d->mCalendar->timeZones();
00128 
00129   QString tmpStr;
00130   VObject *vcal, *vo;
00131 
00132   vcal = newVObject( VCCalProp );
00133 
00134   //  addPropValue(vcal,VCLocationProp, "0.0");
00135   addPropValue( vcal, VCProdIdProp, productId().toLatin1() );
00136   addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
00137 
00138   // TODO STUFF
00139   Todo::List todoList = d->mCalendar->rawTodos();
00140   Todo::List::ConstIterator it;
00141   for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
00142     if ( (*it)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
00143       ICalTimeZone zone = tzlist->zone( (*it)->dtStart().timeZone().name() );
00144       if ( zone.isValid() ) {
00145         QByteArray timezone = zone.vtimezone();
00146         addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toLocal8Bit() );
00147         QString dst = parseDst( timezone );
00148         while ( !dst.isEmpty() ) {
00149           addPropValue( vcal, VCDayLightProp, dst.toLocal8Bit() );
00150           dst = parseDst( timezone );
00151         }
00152       }
00153     }
00154     vo = eventToVTodo( *it );
00155     addVObjectProp( vcal, vo );
00156   }
00157   // EVENT STUFF
00158   Event::List events = d->mCalendar->rawEvents();
00159   Event::List::ConstIterator it2;
00160   for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) {
00161     if ( (*it2)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
00162       ICalTimeZone zone = tzlist->zone( (*it2)->dtStart().timeZone().name() );
00163       if ( zone.isValid() ) {
00164         QByteArray timezone = zone.vtimezone();
00165         addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toLocal8Bit() );
00166         QString dst = parseDst( timezone );
00167         while ( !dst.isEmpty() ) {
00168           addPropValue( vcal, VCDayLightProp, dst.toLocal8Bit() );
00169           dst = parseDst( timezone );
00170         }
00171       }
00172     }
00173     vo = eventToVEvent( *it2 );
00174     addVObjectProp( vcal, vo );
00175   }
00176   writeVObjectToFile( QFile::encodeName( fileName ).data(), vcal );
00177   cleanVObjects( vcal );
00178   cleanStrTbl();
00179 
00180   if ( QFile::exists( fileName ) ) {
00181     return true;
00182   } else {
00183     return false; // error
00184   }
00185 
00186   return false;
00187 }
00188 
00189 bool VCalFormat::fromString( const Calendar::Ptr &calendar, const QString &string,
00190                              bool deleted, const QString &notebook )
00191 {
00192   return fromRawString( calendar, string.toUtf8(), deleted, notebook );
00193 }
00194 
00195 bool VCalFormat::fromRawString( const Calendar::Ptr &calendar, const QByteArray &string,
00196                                 bool deleted, const QString &notebook )
00197 {
00198   d->mCalendar = calendar;
00199 
00200   if ( !string.size() ) {
00201     return false;
00202   }
00203 
00204   VObject *vcal = Parse_MIME( string.data(), string.size() );
00205   if ( !vcal ) {
00206     return false;
00207   }
00208 
00209   VObjectIterator i;
00210   initPropIterator( &i, vcal );
00211 
00212   // put all vobjects into their proper places
00213   QString savedTimeZoneId = d->mCalendar->timeZoneId();
00214   populate( vcal, deleted, notebook );
00215   d->mCalendar->setTimeZoneId(savedTimeZoneId);
00216 
00217   // clean up from vcal API stuff
00218   cleanVObjects( vcal );
00219   cleanStrTbl();
00220 
00221   return true;
00222 }
00223 
00224 QString VCalFormat::toString( const Calendar::Ptr &calendar,
00225                               const QString &notebook, bool deleted )
00226 {
00227   // TODO: Factor out VCalFormat::asString()
00228   d->mCalendar = calendar;
00229 
00230   ICalTimeZones *tzlist = d->mCalendar->timeZones();
00231 
00232   VObject *vo;
00233   VObject *vcal = newVObject( VCCalProp );
00234 
00235   addPropValue( vcal, VCProdIdProp, CalFormat::productId().toLatin1() );
00236   addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
00237 
00238   // TODO STUFF
00239   Todo::List todoList = deleted ? d->mCalendar->deletedTodos() : d->mCalendar->rawTodos();
00240   Todo::List::ConstIterator it;
00241   for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
00242     if ( !deleted || !d->mCalendar->todo( (*it)->uid(), (*it)->recurrenceId() ) ) {
00243       // use existing ones, or really deleted ones
00244       if ( notebook.isEmpty() ||
00245            ( !calendar->notebook(*it).isEmpty() &&
00246              notebook.endsWith( calendar->notebook( *it ) ) ) ) {
00247         if ( (*it)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
00248           ICalTimeZone zone = tzlist->zone( (*it)->dtStart().timeZone().name() );
00249           if ( zone.isValid() ) {
00250             QByteArray timezone = zone.vtimezone();
00251             addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toUtf8() );
00252             QString dst = parseDst( timezone );
00253             while ( !dst.isEmpty() ) {
00254               addPropValue( vcal, VCDayLightProp, dst.toUtf8() );
00255               dst = parseDst( timezone );
00256             }
00257           }
00258         }
00259         vo = eventToVTodo( *it );
00260         addVObjectProp( vcal, vo );
00261       }
00262     }
00263   }
00264 
00265   // EVENT STUFF
00266   Event::List events = deleted ? d->mCalendar->deletedEvents() : d->mCalendar->rawEvents();
00267   Event::List::ConstIterator it2;
00268   for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) {
00269     if ( !deleted || !d->mCalendar->event( (*it2)->uid(), (*it2)->recurrenceId() ) ) {
00270       // use existing ones, or really deleted ones
00271       if ( notebook.isEmpty() ||
00272            ( !calendar->notebook( *it2 ).isEmpty() &&
00273              notebook.endsWith( calendar->notebook( *it2 ) ) ) ) {
00274         if ( (*it2)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
00275           ICalTimeZone zone = tzlist->zone( (*it2)->dtStart().timeZone().name() );
00276           if ( zone.isValid() ) {
00277             QByteArray timezone = zone.vtimezone();
00278             addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toUtf8() );
00279             QString dst = parseDst( timezone );
00280             while ( !dst.isEmpty() ) {
00281               addPropValue( vcal, VCDayLightProp, dst.toUtf8() );
00282               dst = parseDst( timezone );
00283             }
00284           }
00285         }
00286         vo = eventToVEvent( *it2 );
00287         addVObjectProp( vcal, vo );
00288       }
00289     }
00290   }
00291 
00292   char *buf = writeMemVObject( 0, 0, vcal );
00293 
00294   QString result( QString::fromUtf8(buf) );
00295 
00296   deleteStr( buf );
00297 
00298   cleanVObject( vcal );
00299 
00300   return result;
00301 }
00302 
00303 VObject *VCalFormat::eventToVTodo( const Todo::Ptr &anEvent )
00304 {
00305   VObject *vtodo;
00306   QString tmpStr;
00307 
00308   vtodo = newVObject( VCTodoProp );
00309 
00310   // due date
00311   if ( anEvent->hasDueDate() ) {
00312     tmpStr = kDateTimeToISO( anEvent->dtDue(), !anEvent->allDay() );
00313     addPropValue( vtodo, VCDueProp, tmpStr.toUtf8() );
00314   }
00315 
00316   // start date
00317   if ( anEvent->hasStartDate() ) {
00318     tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
00319     addPropValue( vtodo, VCDTstartProp, tmpStr.toUtf8() );
00320   }
00321 
00322   // creation date
00323   tmpStr = kDateTimeToISO( anEvent->created() );
00324   addPropValue( vtodo, VCDCreatedProp, tmpStr.toUtf8() );
00325 
00326   // unique id
00327   addPropValue( vtodo, VCUniqueStringProp,
00328                 anEvent->uid().toUtf8() );
00329 
00330   // revision
00331   tmpStr.sprintf( "%i", anEvent->revision() );
00332   addPropValue( vtodo, VCSequenceProp, tmpStr.toUtf8() );
00333 
00334   // last modification date
00335   tmpStr = kDateTimeToISO( anEvent->lastModified() );
00336   addPropValue( vtodo, VCLastModifiedProp, tmpStr.toUtf8() );
00337 
00338   // organizer stuff
00339   // @TODO: How about the common name?
00340   if ( !anEvent->organizer()->email().isEmpty() ) {
00341     tmpStr = "MAILTO:" + anEvent->organizer()->email();
00342     addPropValue( vtodo, ICOrganizerProp, tmpStr.toUtf8() );
00343   }
00344 
00345   // attendees
00346   if ( anEvent->attendeeCount() > 0 ) {
00347     Attendee::List::ConstIterator it;
00348     Attendee::Ptr curAttendee;
00349     for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd();
00350           ++it ) {
00351       curAttendee = *it;
00352       if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) {
00353         tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
00354       } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
00355         tmpStr = "MAILTO: ";
00356         kDebug() << "warning! this Event has an attendee w/o name or email!";
00357       } else if ( curAttendee->name().isEmpty() ) {
00358         tmpStr = "MAILTO: " + curAttendee->email();
00359       } else {
00360         tmpStr = "MAILTO: " + curAttendee->name();
00361       }
00362       VObject *aProp = addPropValue( vtodo, VCAttendeeProp, tmpStr.toUtf8() );
00363       addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
00364       addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
00365     }
00366   }
00367 
00368   // recurrence rule stuff
00369   const Recurrence *recur = anEvent->recurrence();
00370   if ( recur->recurs() ) {
00371     bool validRecur = true;
00372     QString tmpStr2;
00373     switch ( recur->recurrenceType() ) {
00374     case Recurrence::rDaily:
00375       tmpStr.sprintf( "D%i ", recur->frequency() );
00376       break;
00377     case Recurrence::rWeekly:
00378       tmpStr.sprintf( "W%i ", recur->frequency() );
00379       for ( int i = 0; i < 7; ++i ) {
00380         QBitArray days ( recur->days() );
00381         if ( days.testBit(i) ) {
00382           tmpStr += dayFromNum( i );
00383         }
00384       }
00385       break;
00386     case Recurrence::rMonthlyPos:
00387     {
00388       tmpStr.sprintf( "MP%i ", recur->frequency() );
00389       // write out all rMonthPos's
00390       QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
00391       for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin();
00392             posit != tmpPositions.constEnd(); ++posit ) {
00393         int pos = (*posit).pos();
00394         tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) );
00395         if ( pos < 0 ) {
00396           tmpStr2 += "- ";
00397         } else {
00398           tmpStr2 += "+ ";
00399         }
00400         tmpStr += tmpStr2;
00401         tmpStr += dayFromNum( (*posit).day() - 1 );
00402       }
00403       break;
00404     }
00405     case Recurrence::rMonthlyDay:
00406     {
00407       tmpStr.sprintf( "MD%i ", recur->frequency() );
00408       // write out all rMonthDays;
00409       const QList<int> tmpDays = recur->monthDays();
00410       for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin();
00411             tmpDay != tmpDays.constEnd(); ++tmpDay ) {
00412         tmpStr2.sprintf( "%i ", *tmpDay );
00413         tmpStr += tmpStr2;
00414       }
00415       break;
00416     }
00417     case Recurrence::rYearlyMonth:
00418     {
00419       tmpStr.sprintf( "YM%i ", recur->frequency() );
00420       // write out all the months;'
00421       // TODO: Any way to write out the day within the month???
00422       const QList<int> months = recur->yearMonths();
00423       for ( QList<int>::ConstIterator mit = months.constBegin();
00424             mit != months.constEnd(); ++mit ) {
00425         tmpStr2.sprintf( "%i ", *mit );
00426         tmpStr += tmpStr2;
00427       }
00428       break;
00429     }
00430     case Recurrence::rYearlyDay:
00431     {
00432       tmpStr.sprintf( "YD%i ", recur->frequency() );
00433       // write out all the rYearNums;
00434       const QList<int> tmpDays = recur->yearDays();
00435       for ( QList<int>::ConstIterator tmpDay = tmpDays.begin();
00436             tmpDay != tmpDays.end(); ++tmpDay ) {
00437         tmpStr2.sprintf( "%i ", *tmpDay );
00438         tmpStr += tmpStr2;
00439       }
00440       break;
00441     }
00442     default:
00443       // TODO: Write rYearlyPos and arbitrary rules!
00444       kDebug() << "ERROR, it should never get here in eventToVTodo!";
00445       validRecur = false;
00446       break;
00447     } // switch
00448 
00449     if ( recur->duration() > 0 ) {
00450       tmpStr2.sprintf( "#%i", recur->duration() );
00451       tmpStr += tmpStr2;
00452     } else if ( recur->duration() == -1 ) {
00453       tmpStr += "#0"; // defined as repeat forever
00454     } else {
00455       tmpStr += kDateTimeToISO( recur->endDateTime(), false );
00456     }
00457     // Only write out the rrule if we have a valid recurrence (i.e. a known
00458     // type in thee switch above)
00459     if ( validRecur ) {
00460       addPropValue( vtodo, VCRRuleProp, tmpStr.toUtf8() );
00461     }
00462 
00463   } // event repeats
00464 
00465   // exceptions dates to recurrence
00466   DateList dateList = recur->exDates();
00467   DateList::ConstIterator id;
00468   QString tmpStr2;
00469 
00470   for ( id = dateList.constBegin(); id != dateList.constEnd(); ++id ) {
00471     tmpStr = qDateToISO(*id) + ';';
00472     tmpStr2 += tmpStr;
00473   }
00474   if ( !tmpStr2.isEmpty() ) {
00475     tmpStr2.truncate( tmpStr2.length() - 1 );
00476     addPropValue( vtodo, VCExpDateProp, tmpStr2.toUtf8() );
00477   }
00478   // exceptions datetimes to recurrence
00479   DateTimeList dateTimeList = recur->exDateTimes();
00480   DateTimeList::ConstIterator idt;
00481   tmpStr2.clear();
00482 
00483   for ( idt = dateTimeList.constBegin(); idt != dateTimeList.constEnd(); ++idt ) {
00484     tmpStr = kDateTimeToISO( *idt ) + ';';
00485     tmpStr2 += tmpStr;
00486   }
00487   if ( !tmpStr2.isEmpty() ) {
00488     tmpStr2.truncate( tmpStr2.length() - 1 );
00489     addPropValue( vtodo, VCExpDateProp, tmpStr2.toUtf8() );
00490   }
00491 
00492   // description BL:
00493   if ( !anEvent->description().isEmpty() ) {
00494     QByteArray in = anEvent->description().toUtf8();
00495     QByteArray out;
00496     KCodecs::quotedPrintableEncode( in, out, true );
00497     if ( out != in ) {
00498       VObject *d = addPropValue( vtodo, VCDescriptionProp, out );
00499       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00500       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00501     } else {
00502       addPropValue( vtodo, VCDescriptionProp, in );
00503     }
00504   }
00505 
00506   // summary
00507   if ( !anEvent->summary().isEmpty() ) {
00508     QByteArray in = anEvent->summary().toUtf8();
00509     QByteArray out;
00510     KCodecs::quotedPrintableEncode( in, out, true );
00511     if ( out != in ) {
00512       VObject *d = addPropValue( vtodo, VCSummaryProp, out );
00513       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00514       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00515     } else {
00516       addPropValue( vtodo, VCSummaryProp, in );
00517     }
00518   }
00519 
00520   // location
00521   if ( !anEvent->location().isEmpty() ) {
00522     QByteArray in = anEvent->location().toUtf8();
00523     QByteArray out;
00524     KCodecs::quotedPrintableEncode( in, out, true );
00525     if ( out != in ) {
00526       VObject *d = addPropValue( vtodo, VCLocationProp, out );
00527       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00528       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00529     } else {
00530       addPropValue( vtodo, VCLocationProp, in );
00531     }
00532   }
00533 
00534   // completed status
00535   // backward compatibility, KOrganizer used to interpret only these two values
00536   addPropValue( vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : "NEEDS ACTION" );
00537 
00538   // completion date
00539   if ( anEvent->hasCompletedDate() ) {
00540     tmpStr = kDateTimeToISO( anEvent->completed() );
00541     addPropValue( vtodo, VCCompletedProp, tmpStr.toUtf8() );
00542   }
00543 
00544   // priority
00545   tmpStr.sprintf( "%i", anEvent->priority() );
00546   addPropValue( vtodo, VCPriorityProp, tmpStr.toUtf8() );
00547 
00548   // related event
00549   if ( !anEvent->relatedTo().isEmpty() ) {
00550     addPropValue( vtodo, VCRelatedToProp,
00551                   anEvent->relatedTo().toUtf8() );
00552   }
00553 
00554   // secrecy
00555   const char *text = 0;
00556   switch ( anEvent->secrecy() ) {
00557   case Incidence::SecrecyPublic:
00558     text = "PUBLIC";
00559     break;
00560   case Incidence::SecrecyPrivate:
00561     text = "PRIVATE";
00562     break;
00563   case Incidence::SecrecyConfidential:
00564     text = "CONFIDENTIAL";
00565     break;
00566   }
00567   if ( text ) {
00568     addPropValue( vtodo, VCClassProp, text );
00569   }
00570 
00571   // categories
00572   const QStringList tmpStrList = anEvent->categories();
00573   tmpStr = "";
00574   QString catStr;
00575   QStringList::const_iterator its;
00576   for ( its = tmpStrList.constBegin(); its != tmpStrList.constEnd(); ++its ) {
00577     catStr = *its;
00578     if ( catStr[0] == ' ' ) {
00579       tmpStr += catStr.mid( 1 );
00580     } else {
00581       tmpStr += catStr;
00582     }
00583     // this must be a ';' character as the vCalendar specification requires!
00584     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00585     // read in.
00586     tmpStr += ';';
00587   }
00588   if ( !tmpStr.isEmpty() ) {
00589     tmpStr.truncate( tmpStr.length() - 1 );
00590     addPropValue( vtodo, VCCategoriesProp, tmpStr.toUtf8() );
00591   }
00592 
00593   // alarm stuff
00594   Alarm::List::ConstIterator it;
00595   for ( it = anEvent->alarms().constBegin(); it != anEvent->alarms().constEnd(); ++it ) {
00596     Alarm::Ptr alarm = *it;
00597     if ( alarm->enabled() ) {
00598       VObject *a;
00599       if ( alarm->type() == Alarm::Display ) {
00600         a = addProp( vtodo, VCDAlarmProp );
00601         tmpStr = kDateTimeToISO( alarm->time() );
00602         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00603         addPropValue( a, VCRepeatCountProp, "1" );
00604         if ( alarm->text().isNull() ) {
00605           addPropValue( a, VCDisplayStringProp, "beep!" );
00606         } else {
00607           addPropValue( a, VCDisplayStringProp, alarm->text().toAscii().data() );
00608         }
00609       } else if ( alarm->type() == Alarm::Audio ) {
00610         a = addProp( vtodo, VCAAlarmProp );
00611         tmpStr = kDateTimeToISO( alarm->time() );
00612         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00613         addPropValue( a, VCRepeatCountProp, "1" );
00614         addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
00615       } else if ( alarm->type() == Alarm::Procedure ) {
00616         a = addProp( vtodo, VCPAlarmProp );
00617         tmpStr = kDateTimeToISO( alarm->time() );
00618         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00619         addPropValue( a, VCRepeatCountProp, "1" );
00620         addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
00621       }
00622     }
00623   }
00624 
00625   QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
00626   if ( !pilotId.isEmpty() ) {
00627     // pilot sync stuff
00628     addPropValue( vtodo, KPilotIdProp, pilotId.toUtf8() );
00629     addPropValue( vtodo, KPilotStatusProp,
00630                   anEvent->nonKDECustomProperty( KPilotStatusProp ).toUtf8() );
00631   }
00632 #if defined(KCALCORE_FOR_SYMBIAN)
00633   if ( anEvent->nonKDECustomProperty( EPOCAgendaEntryTypeProp ).isEmpty() ) {
00634     // Propagate braindeath by setting this property also so that
00635     // S60 is happy
00636     addPropValue( vtodo, EPOCAgendaEntryTypeProp, "TODO" );
00637   }
00638 
00639   writeCustomProperties( vtodo, anEvent );
00640 #endif
00641 
00642   return vtodo;
00643 }
00644 
00645 VObject *VCalFormat::eventToVEvent( const Event::Ptr &anEvent )
00646 {
00647   VObject *vevent;
00648   QString tmpStr;
00649 
00650   vevent = newVObject( VCEventProp );
00651 
00652   // start and end time
00653   tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
00654   addPropValue( vevent, VCDTstartProp, tmpStr.toUtf8() );
00655 
00656 #if !defined(KCALCORE_FOR_MEEGO)
00657   // events that have time associated but take up no time should
00658   // not have both DTSTART and DTEND.
00659   if ( anEvent->dtStart() != anEvent->dtEnd() ) {
00660     tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() );
00661     addPropValue( vevent, VCDTendProp, tmpStr.toUtf8() );
00662   }
00663 #else
00664   // N900 and s60-phones need enddate
00665   tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() );
00666   addPropValue( vevent, VCDTendProp, tmpStr.toUtf8() );
00667 #endif
00668 
00669   // creation date
00670   tmpStr = kDateTimeToISO( anEvent->created() );
00671   addPropValue( vevent, VCDCreatedProp, tmpStr.toUtf8() );
00672 
00673   // unique id
00674   addPropValue( vevent, VCUniqueStringProp,
00675                 anEvent->uid().toUtf8() );
00676 
00677   // revision
00678   tmpStr.sprintf( "%i", anEvent->revision() );
00679   addPropValue( vevent, VCSequenceProp, tmpStr.toUtf8() );
00680 
00681   // last modification date
00682   tmpStr = kDateTimeToISO( anEvent->lastModified() );
00683   addPropValue( vevent, VCLastModifiedProp, tmpStr.toUtf8() );
00684 
00685   // attendee and organizer stuff
00686   // TODO: What to do with the common name?
00687   if ( !anEvent->organizer()->email().isEmpty() ) {
00688     tmpStr = "MAILTO:" + anEvent->organizer()->email();
00689     addPropValue( vevent, ICOrganizerProp, tmpStr.toUtf8() );
00690   }
00691 
00692   // TODO: Put this functionality into Attendee class
00693   if ( anEvent->attendeeCount() > 0 ) {
00694     Attendee::List::ConstIterator it;
00695     for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd();
00696           ++it ) {
00697       Attendee::Ptr curAttendee = *it;
00698       if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) {
00699         tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
00700       } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
00701         tmpStr = "MAILTO: ";
00702         kDebug() << "warning! this Event has an attendee w/o name or email!";
00703       } else if ( curAttendee->name().isEmpty() ) {
00704         tmpStr = "MAILTO: " + curAttendee->email();
00705       } else {
00706         tmpStr = "MAILTO: " + curAttendee->name();
00707       }
00708       VObject *aProp = addPropValue( vevent, VCAttendeeProp, tmpStr.toUtf8() );
00709       addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
00710       addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
00711     }
00712   }
00713 
00714   // recurrence rule stuff
00715   const Recurrence *recur = anEvent->recurrence();
00716   if ( recur->recurs() ) {
00717     bool validRecur = true;
00718     QString tmpStr2;
00719     switch ( recur->recurrenceType() ) {
00720     case Recurrence::rDaily:
00721       tmpStr.sprintf( "D%i ", recur->frequency() );
00722       break;
00723     case Recurrence::rWeekly:
00724       tmpStr.sprintf( "W%i ", recur->frequency() );
00725       for ( int i = 0; i < 7; ++i ) {
00726         QBitArray days ( recur->days() );
00727         if ( days.testBit(i) ) {
00728           tmpStr += dayFromNum( i );
00729         }
00730       }
00731       break;
00732     case Recurrence::rMonthlyPos:
00733     {
00734       tmpStr.sprintf( "MP%i ", recur->frequency() );
00735       // write out all rMonthPos's
00736       QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
00737       for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin();
00738             posit != tmpPositions.constEnd(); ++posit ) {
00739         int pos = (*posit).pos();
00740         tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) );
00741         if ( pos < 0 ) {
00742           tmpStr2 += "- ";
00743         } else {
00744           tmpStr2 += "+ ";
00745         }
00746         tmpStr += tmpStr2;
00747         tmpStr += dayFromNum( (*posit).day() - 1 );
00748       }
00749       break;
00750     }
00751     case Recurrence::rMonthlyDay:
00752     {
00753       tmpStr.sprintf( "MD%i ", recur->frequency() );
00754       // write out all rMonthDays;
00755       const QList<int> tmpDays = recur->monthDays();
00756       for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin();
00757             tmpDay != tmpDays.constEnd(); ++tmpDay ) {
00758         tmpStr2.sprintf( "%i ", *tmpDay );
00759         tmpStr += tmpStr2;
00760       }
00761       break;
00762     }
00763     case Recurrence::rYearlyMonth:
00764     {
00765       tmpStr.sprintf( "YM%i ", recur->frequency() );
00766       // write out all the months;'
00767       // TODO: Any way to write out the day within the month???
00768       const QList<int> months = recur->yearMonths();
00769       for ( QList<int>::ConstIterator mit = months.constBegin();
00770             mit != months.constEnd(); ++mit ) {
00771         tmpStr2.sprintf( "%i ", *mit );
00772         tmpStr += tmpStr2;
00773       }
00774       break;
00775     }
00776     case Recurrence::rYearlyDay:
00777     {
00778       tmpStr.sprintf( "YD%i ", recur->frequency() );
00779       // write out all the rYearNums;
00780       const QList<int> tmpDays = recur->yearDays();
00781       for ( QList<int>::ConstIterator tmpDay = tmpDays.begin();
00782             tmpDay != tmpDays.end(); ++tmpDay ) {
00783         tmpStr2.sprintf( "%i ", *tmpDay );
00784         tmpStr += tmpStr2;
00785       }
00786       break;
00787     }
00788     default:
00789       // TODO: Write rYearlyPos and arbitrary rules!
00790       kDebug() << "ERROR, it should never get here in eventToVEvent!";
00791       validRecur = false;
00792       break;
00793     } // switch
00794 
00795     if ( recur->duration() > 0 ) {
00796       tmpStr2.sprintf( "#%i", recur->duration() );
00797       tmpStr += tmpStr2;
00798     } else if ( recur->duration() == -1 ) {
00799       tmpStr += "#0"; // defined as repeat forever
00800     } else {
00801 #if !defined(KCALCORE_FOR_MEEGO)
00802       tmpStr += kDateTimeToISO( recur->endDateTime(), false );
00803 #else
00804       tmpStr +=
00805         kDateTimeToISO( recur->endDateTime().toTimeSpec( d->mCalendar->timeSpec() ), false );
00806 #endif
00807     }
00808     // Only write out the rrule if we have a valid recurrence (i.e. a known
00809     // type in thee switch above)
00810     if ( validRecur ) {
00811       addPropValue( vevent, VCRRuleProp, tmpStr.toUtf8() );
00812     }
00813 
00814   } // event repeats
00815 
00816   // exceptions dates to recurrence
00817   DateList dateList = recur->exDates();
00818   DateList::ConstIterator it;
00819   QString tmpStr2;
00820 
00821   for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) {
00822     tmpStr = qDateToISO(*it) + ';';
00823     tmpStr2 += tmpStr;
00824   }
00825   if ( !tmpStr2.isEmpty() ) {
00826     tmpStr2.truncate( tmpStr2.length() - 1 );
00827     addPropValue( vevent, VCExpDateProp, tmpStr2.toUtf8() );
00828   }
00829   // exceptions datetimes to recurrence
00830   DateTimeList dateTimeList = recur->exDateTimes();
00831   DateTimeList::ConstIterator idt;
00832   tmpStr2.clear();
00833 
00834   for ( idt = dateTimeList.constBegin(); idt != dateTimeList.constEnd(); ++idt ) {
00835     tmpStr = kDateTimeToISO( *idt ) + ';';
00836     tmpStr2 += tmpStr;
00837   }
00838   if ( !tmpStr2.isEmpty() ) {
00839     tmpStr2.truncate( tmpStr2.length() - 1 );
00840     addPropValue( vevent, VCExpDateProp, tmpStr2.toUtf8() );
00841   }
00842 
00843   // description
00844   if ( !anEvent->description().isEmpty() ) {
00845     QByteArray in = anEvent->description().toUtf8();
00846     QByteArray out;
00847     KCodecs::quotedPrintableEncode( in, out, true );
00848     if ( out != in ) {
00849       VObject *d = addPropValue( vevent, VCDescriptionProp, out );
00850       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00851       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00852     } else {
00853       addPropValue( vevent, VCDescriptionProp, in );
00854     }
00855   }
00856 
00857   // summary
00858   if ( !anEvent->summary().isEmpty() ) {
00859     QByteArray in = anEvent->summary().toUtf8();
00860     QByteArray out;
00861     KCodecs::quotedPrintableEncode( in, out, true );
00862     if ( out != in ) {
00863       VObject *d = addPropValue( vevent, VCSummaryProp, out );
00864       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00865       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00866     } else {
00867       addPropValue( vevent, VCSummaryProp, in );
00868     }
00869   }
00870 
00871   // location
00872   if ( !anEvent->location().isEmpty() ) {
00873     QByteArray in = anEvent->location().toUtf8();
00874     QByteArray out;
00875     KCodecs::quotedPrintableEncode( in, out, true );
00876     if ( out != in ) {
00877       VObject *d = addPropValue( vevent, VCLocationProp, out );
00878       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00879       addPropValue( d, VCCharSetProp, VCUtf8Prop );
00880     } else {
00881       addPropValue( vevent, VCLocationProp, in );
00882     }
00883   }
00884 
00885   // status
00886 // TODO: define Event status
00887 //  addPropValue( vevent, VCStatusProp, anEvent->statusStr().toUtf8() );
00888 
00889   // secrecy
00890   const char *text = 0;
00891   switch ( anEvent->secrecy() ) {
00892   case Incidence::SecrecyPublic:
00893     text = "PUBLIC";
00894     break;
00895   case Incidence::SecrecyPrivate:
00896     text = "PRIVATE";
00897     break;
00898   case Incidence::SecrecyConfidential:
00899     text = "CONFIDENTIAL";
00900     break;
00901   }
00902   if ( text ) {
00903     addPropValue( vevent, VCClassProp, text );
00904   }
00905 
00906   // categories
00907   QStringList tmpStrList = anEvent->categories();
00908   tmpStr = "";
00909   QString catStr;
00910   for ( QStringList::const_iterator it = tmpStrList.constBegin(); it != tmpStrList.constEnd();
00911         ++it ) {
00912     catStr = *it;
00913     if ( catStr[0] == ' ' ) {
00914       tmpStr += catStr.mid( 1 );
00915     } else {
00916       tmpStr += catStr;
00917     }
00918     // this must be a ';' character as the vCalendar specification requires!
00919     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00920     // read in.
00921     tmpStr += ';';
00922   }
00923   if ( !tmpStr.isEmpty() ) {
00924     tmpStr.truncate( tmpStr.length() - 1 );
00925     addPropValue( vevent, VCCategoriesProp, tmpStr.toUtf8() );
00926   }
00927 
00928   // attachments
00929   // TODO: handle binary attachments!
00930   Attachment::List attachments = anEvent->attachments();
00931   Attachment::List::ConstIterator atIt;
00932   for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) {
00933     addPropValue( vevent, VCAttachProp, (*atIt)->uri().toUtf8() );
00934   }
00935 
00936   // resources
00937   tmpStrList = anEvent->resources();
00938   tmpStr = tmpStrList.join( ";" );
00939   if ( !tmpStr.isEmpty() ) {
00940     addPropValue( vevent, VCResourcesProp, tmpStr.toUtf8() );
00941   }
00942 
00943   // alarm stuff
00944   Alarm::List::ConstIterator it2;
00945   for ( it2 = anEvent->alarms().constBegin(); it2 != anEvent->alarms().constEnd(); ++it2 ) {
00946     Alarm::Ptr alarm = *it2;
00947     if ( alarm->enabled() ) {
00948       VObject *a;
00949       if ( alarm->type() == Alarm::Display ) {
00950         a = addProp( vevent, VCDAlarmProp );
00951         tmpStr = kDateTimeToISO( alarm->time() );
00952         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00953         addPropValue( a, VCRepeatCountProp, "1" );
00954         if ( alarm->text().isNull() ) {
00955           addPropValue( a, VCDisplayStringProp, "beep!" );
00956         } else {
00957           addPropValue( a, VCDisplayStringProp, alarm->text().toAscii().data() );
00958         }
00959       } else if ( alarm->type() == Alarm::Audio ) {
00960         a = addProp( vevent, VCAAlarmProp );
00961         tmpStr = kDateTimeToISO( alarm->time() );
00962         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00963         addPropValue( a, VCRepeatCountProp, "1" );
00964         addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
00965       }
00966       if ( alarm->type() == Alarm::Procedure ) {
00967         a = addProp( vevent, VCPAlarmProp );
00968         tmpStr = kDateTimeToISO( alarm->time() );
00969         addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
00970         addPropValue( a, VCRepeatCountProp, "1" );
00971         addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
00972       }
00973     }
00974   }
00975 
00976   // priority
00977   tmpStr.sprintf( "%i", anEvent->priority() );
00978   addPropValue( vevent, VCPriorityProp, tmpStr.toUtf8() );
00979 
00980   // transparency
00981   tmpStr.sprintf( "%i", anEvent->transparency() );
00982   addPropValue( vevent, VCTranspProp, tmpStr.toUtf8() );
00983 
00984   // related event
00985   if ( !anEvent->relatedTo().isEmpty() ) {
00986     addPropValue( vevent, VCRelatedToProp, anEvent->relatedTo().toUtf8() );
00987   }
00988 
00989   QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
00990   if ( !pilotId.isEmpty() ) {
00991     // pilot sync stuff
00992     addPropValue( vevent, KPilotIdProp, pilotId.toUtf8() );
00993     addPropValue( vevent, KPilotStatusProp,
00994                   anEvent->nonKDECustomProperty( KPilotStatusProp ).toUtf8() );
00995   }
00996 
00997 #if defined(KCALCORE_FOR_SYMBIAN)
00998   if ( anEvent->nonKDECustomProperty( EPOCAgendaEntryTypeProp ).isEmpty() ) {
00999     // Propagate braindeath by setting this property also so that
01000     // S60 is happy
01001     if ( anEvent->allDay() ) {
01002       addPropValue( vevent, EPOCAgendaEntryTypeProp, "EVENT" );
01003     } else {
01004       addPropValue( vevent, EPOCAgendaEntryTypeProp, "APPOINTMENT" );
01005     }
01006   }
01007 
01008   if ( anEvent->hasRecurrenceId() ) {
01009       tmpStr = kDateTimeToISO( anEvent->recurrenceId(), true );
01010       addPropValue( vevent, VCRecurrenceIdProp, tmpStr.toUtf8() );
01011   }
01012   writeCustomProperties( vevent, anEvent );
01013 #endif
01014 
01015   return vevent;
01016 }
01017 
01018 Todo::Ptr VCalFormat::VTodoToEvent( VObject *vtodo )
01019 {
01020   VObject *vo;
01021   VObjectIterator voi;
01022   char *s;
01023 
01024   Todo::Ptr anEvent( new Todo );
01025 
01026   // creation date
01027   if ( ( vo = isAPropertyOf( vtodo, VCDCreatedProp ) ) != 0 ) {
01028       anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01029       deleteStr( s );
01030   }
01031 
01032   // unique id
01033   vo = isAPropertyOf( vtodo, VCUniqueStringProp );
01034   // while the UID property is preferred, it is not required.  We'll use the
01035   // default Event UID if none is given.
01036   if ( vo ) {
01037     anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
01038     deleteStr( s );
01039   }
01040 
01041   // last modification date
01042   if ( ( vo = isAPropertyOf( vtodo, VCLastModifiedProp ) ) != 0 ) {
01043     anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01044     deleteStr( s );
01045   } else {
01046     anEvent->setLastModified( KDateTime::currentUtcDateTime() );
01047   }
01048 
01049   // organizer
01050   // if our extension property for the event's ORGANIZER exists, add it.
01051   if ( ( vo = isAPropertyOf( vtodo, ICOrganizerProp ) ) != 0 ) {
01052     anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
01053     deleteStr( s );
01054   } else {
01055     if ( d->mCalendar->owner()->name() != "Unknown Name" ) {
01056       anEvent->setOrganizer( d->mCalendar->owner() );
01057     }
01058   }
01059 
01060   // attendees.
01061   initPropIterator( &voi, vtodo );
01062   while ( moreIteration( &voi ) ) {
01063     vo = nextVObject( &voi );
01064     if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
01065       Attendee::Ptr a;
01066       VObject *vp;
01067       s = fakeCString( vObjectUStringZValue( vo ) );
01068       QString tmpStr = QString::fromUtf8( s );
01069       deleteStr( s );
01070       tmpStr = tmpStr.simplified();
01071       int emailPos1, emailPos2;
01072       if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
01073         // both email address and name
01074         emailPos2 = tmpStr.lastIndexOf( '>' );
01075         a = Attendee::Ptr( new Attendee( tmpStr.left( emailPos1 - 1 ),
01076                                          tmpStr.mid( emailPos1 + 1,
01077                                          emailPos2 - ( emailPos1 + 1 ) ) ) );
01078       } else if ( tmpStr.indexOf( '@' ) > 0 ) {
01079         // just an email address
01080           a = Attendee::Ptr( new Attendee( 0, tmpStr ) );
01081       } else {
01082         // just a name
01083         // WTF??? Replacing the spaces of a name and using this as email?
01084         QString email = tmpStr.replace( ' ', '.' );
01085         a = Attendee::Ptr( new Attendee( tmpStr, email ) );
01086       }
01087 
01088       // is there an RSVP property?
01089       if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
01090         a->setRSVP( vObjectStringZValue( vp ) );
01091       }
01092       // is there a status property?
01093       if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
01094         a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
01095       }
01096       // add the attendee
01097       anEvent->addAttendee( a );
01098     }
01099   }
01100 
01101   // description for todo
01102   if ( ( vo = isAPropertyOf( vtodo, VCDescriptionProp ) ) != 0 ) {
01103     s = fakeCString( vObjectUStringZValue( vo ) );
01104     anEvent->setDescription( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
01105     deleteStr( s );
01106   }
01107 
01108   // summary
01109   if ( ( vo = isAPropertyOf( vtodo, VCSummaryProp ) ) ) {
01110     s = fakeCString( vObjectUStringZValue( vo ) );
01111     anEvent->setSummary( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
01112     deleteStr( s );
01113   }
01114 
01115   // location
01116   if ( ( vo = isAPropertyOf( vtodo, VCLocationProp ) ) != 0 ) {
01117     s = fakeCString( vObjectUStringZValue( vo ) );
01118     anEvent->setLocation( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
01119     deleteStr( s );
01120   }
01121 
01122   // completed
01123   // was: status
01124   if ( ( vo = isAPropertyOf( vtodo, VCStatusProp ) ) != 0 ) {
01125     s = fakeCString( vObjectUStringZValue( vo ) );
01126     if ( s && strcmp( s, "COMPLETED" ) == 0 ) {
01127       anEvent->setCompleted( true );
01128     } else {
01129       anEvent->setCompleted( false );
01130     }
01131     deleteStr( s );
01132   } else {
01133     anEvent->setCompleted( false );
01134   }
01135 
01136   // completion date
01137   if ( ( vo = isAPropertyOf( vtodo, VCCompletedProp ) ) != 0 ) {
01138     anEvent->setCompleted( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01139     deleteStr( s );
01140   }
01141 
01142   // priority
01143   if ( ( vo = isAPropertyOf( vtodo, VCPriorityProp ) ) ) {
01144     s = fakeCString( vObjectUStringZValue( vo ) );
01145     if ( s ) {
01146       anEvent->setPriority( atoi( s ) );
01147       deleteStr( s );
01148     }
01149   }
01150 
01151   anEvent->setAllDay( false );
01152 
01153   // due date
01154   if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) {
01155     anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01156     deleteStr( s );
01157     anEvent->setHasDueDate( true );
01158     if ( anEvent->dtDue().time().hour() == 0 &&
01159          anEvent->dtDue().time().minute() == 0 &&
01160          anEvent->dtDue().time().second() == 0 ) {
01161 #if defined(KCALCORE_FOR_MEEGO)
01162       QDate dueDate = anEvent->dtDue().date();
01163       anEvent->setDtDue( KDateTime( dueDate, KDateTime::ClockTime ) );
01164 #endif
01165       anEvent->setAllDay( true );
01166     }
01167   } else {
01168     anEvent->setHasDueDate( false );
01169   }
01170 
01171   // start time
01172   if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) {
01173     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01174     deleteStr( s );
01175     anEvent->setHasStartDate( true );
01176     if ( anEvent->dtStart().time().hour() == 0 &&
01177          anEvent->dtStart().time().minute() == 0 &&
01178          anEvent->dtStart().time().second() == 0 ) {
01179 #if defined(KCALCORE_FOR_MEEGO)
01180       QDate startDate = anEvent->dtStart().date();
01181       anEvent->setDtStart( KDateTime( startDate, KDateTime::ClockTime ) );
01182 #endif
01183       anEvent->setAllDay( true );
01184     }
01185   } else {
01186     anEvent->setHasStartDate( false );
01187   }
01188 
01189   // repeat stuff
01190   if ( ( vo = isAPropertyOf( vtodo, VCRRuleProp ) ) != 0 ) {
01191     QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01192     deleteStr( s );
01193     tmpStr.simplified();
01194     tmpStr = tmpStr.toUpper();
01195     // first, read the type of the recurrence
01196     int typelen = 1;
01197     uint type = Recurrence::rNone;
01198     if ( tmpStr.left(1) == "D" ) {
01199       type = Recurrence::rDaily;
01200     } else if ( tmpStr.left(1) == "W" ) {
01201       type = Recurrence::rWeekly;
01202     } else {
01203       typelen = 2;
01204       if ( tmpStr.left(2) == "MP" ) {
01205         type = Recurrence::rMonthlyPos;
01206       } else if ( tmpStr.left(2) == "MD" ) {
01207         type = Recurrence::rMonthlyDay;
01208       } else if ( tmpStr.left(2) == "YM" ) {
01209         type = Recurrence::rYearlyMonth;
01210       } else if ( tmpStr.left(2) == "YD" ) {
01211         type = Recurrence::rYearlyDay;
01212       }
01213     }
01214 
01215     if ( type != Recurrence::rNone ) {
01216 
01217       // Immediately after the type is the frequency
01218       int index = tmpStr.indexOf( ' ' );
01219       int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
01220       int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
01221       ++index; // advance to beginning of stuff after freq
01222 
01223       // Read the type-specific settings
01224       switch ( type ) {
01225       case Recurrence::rDaily:
01226         anEvent->recurrence()->setDaily(rFreq);
01227         break;
01228 
01229       case Recurrence::rWeekly:
01230       {
01231         QBitArray qba(7);
01232         QString dayStr;
01233         if ( index == last ) {
01234           // e.g. W1 #0
01235           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01236         } else {
01237           // e.g. W1 SU #0
01238           while ( index < last ) {
01239             dayStr = tmpStr.mid( index, 3 );
01240             int dayNum = numFromDay( dayStr );
01241             if ( dayNum >= 0 ) {
01242               qba.setBit( dayNum );
01243             }
01244             index += 3; // advance to next day, or possibly "#"
01245           }
01246         }
01247         anEvent->recurrence()->setWeekly( rFreq, qba );
01248         break;
01249       }
01250 
01251       case Recurrence::rMonthlyPos:
01252       {
01253         anEvent->recurrence()->setMonthly( rFreq );
01254 
01255         QBitArray qba(7);
01256         short tmpPos;
01257         if ( index == last ) {
01258           // e.g. MP1 #0
01259           tmpPos = anEvent->dtStart().date().day() / 7 + 1;
01260           if ( tmpPos == 5 ) {
01261             tmpPos = -1;
01262           }
01263           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01264           anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01265         } else {
01266           // e.g. MP1 1+ SU #0
01267           while ( index < last ) {
01268             tmpPos = tmpStr.mid( index, 1 ).toShort();
01269             index += 1;
01270             if ( tmpStr.mid( index, 1 ) == "-" ) {
01271               // convert tmpPos to negative
01272               tmpPos = 0 - tmpPos;
01273             }
01274             index += 2; // advance to day(s)
01275             while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
01276               int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
01277               qba.setBit( dayNum );
01278               index += 3; // advance to next day, or possibly pos or "#"
01279             }
01280             anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01281             qba.detach();
01282             qba.fill( false ); // clear out
01283           } // while != "#"
01284         }
01285         break;
01286       }
01287 
01288       case Recurrence::rMonthlyDay:
01289         anEvent->recurrence()->setMonthly( rFreq );
01290         if( index == last ) {
01291           // e.g. MD1 #0
01292           short tmpDay = anEvent->dtStart().date().day();
01293           anEvent->recurrence()->addMonthlyDate( tmpDay );
01294         } else {
01295           // e.g. MD1 3 #0
01296           while ( index < last ) {
01297             int index2 = tmpStr.indexOf( ' ', index );
01298             if ( ( tmpStr.mid( ( index2 - 1 ), 1 ) == "-" ) ||
01299                  ( tmpStr.mid( ( index2 - 1 ), 1 ) == "+" ) ) {
01300               index2 = index2 - 1;
01301             }
01302             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01303             index = index2;
01304             if ( tmpStr.mid( index, 1 ) == "-" ) {
01305               tmpDay = 0 - tmpDay;
01306             }
01307             index += 2; // advance the index;
01308             anEvent->recurrence()->addMonthlyDate( tmpDay );
01309           } // while != #
01310         }
01311         break;
01312 
01313       case Recurrence::rYearlyMonth:
01314         anEvent->recurrence()->setYearly( rFreq );
01315 
01316         if ( index == last ) {
01317           // e.g. YM1 #0
01318           short tmpMonth = anEvent->dtStart().date().month();
01319           anEvent->recurrence()->addYearlyMonth( tmpMonth );
01320         } else {
01321           // e.g. YM1 3 #0
01322           while ( index < last ) {
01323             int index2 = tmpStr.indexOf( ' ', index );
01324             short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
01325             index = index2 + 1;
01326             anEvent->recurrence()->addYearlyMonth( tmpMonth );
01327           } // while != #
01328         }
01329         break;
01330 
01331       case Recurrence::rYearlyDay:
01332         anEvent->recurrence()->setYearly( rFreq );
01333 
01334         if ( index == last ) {
01335           // e.g. YD1 #0
01336           short tmpDay = anEvent->dtStart().date().dayOfYear();
01337           anEvent->recurrence()->addYearlyDay( tmpDay );
01338         } else {
01339           // e.g. YD1 123 #0
01340           while ( index < last ) {
01341             int index2 = tmpStr.indexOf( ' ', index );
01342             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01343             index = index2 + 1;
01344             anEvent->recurrence()->addYearlyDay( tmpDay );
01345           } // while != #
01346         }
01347         break;
01348 
01349       default:
01350         break;
01351       }
01352 
01353       // find the last field, which is either the duration or the end date
01354       index = last;
01355       if ( tmpStr.mid( index, 1 ) == "#" ) {
01356         // Nr of occurrences
01357         index++;
01358         int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
01359         if ( rDuration > 0 ) {
01360           anEvent->recurrence()->setDuration( rDuration );
01361         }
01362       } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
01363         KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
01364         anEvent->recurrence()->setEndDateTime( rEndDate );
01365       }
01366     } else {
01367       kDebug() << "we don't understand this type of recurrence!";
01368     } // if known recurrence type
01369   } // repeats
01370 
01371   // recurrence exceptions
01372   if ( ( vo = isAPropertyOf( vtodo, VCExpDateProp ) ) != 0 ) {
01373     s = fakeCString( vObjectUStringZValue( vo ) );
01374     QStringList exDates = QString::fromUtf8( s ).split( ',' );
01375     QStringList::ConstIterator it;
01376     for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
01377       KDateTime exDate = ISOToKDateTime(*it);
01378       if ( exDate.time().hour() == 0 &&
01379            exDate.time().minute() == 0 &&
01380            exDate.time().second() == 0 ) {
01381         anEvent->recurrence()->addExDate( ISOToQDate( *it ) );
01382       } else {
01383         anEvent->recurrence()->addExDateTime( exDate );
01384       }
01385     }
01386     deleteStr( s );
01387   }
01388 
01389   // alarm stuff
01390   if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) {
01391     Alarm::Ptr alarm;
01392     VObject *a;
01393     VObject *b;
01394     a = isAPropertyOf( vo, VCRunTimeProp );
01395     b = isAPropertyOf( vo, VCDisplayStringProp );
01396 
01397     if ( a || b ) {
01398       alarm = anEvent->newAlarm();
01399       if ( a ) {
01400         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01401         deleteStr( s );
01402       }
01403       alarm->setEnabled( true );
01404       if ( b ) {
01405         s = fakeCString( vObjectUStringZValue( b ) );
01406         alarm->setDisplayAlarm( QString( s ) );
01407         deleteStr( s );
01408       } else {
01409         alarm->setDisplayAlarm( QString() );
01410       }
01411     }
01412   }
01413 
01414   if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) {
01415     Alarm::Ptr alarm;
01416     VObject *a;
01417     VObject *b;
01418     a = isAPropertyOf( vo, VCRunTimeProp );
01419     b = isAPropertyOf( vo, VCAudioContentProp );
01420 
01421     if ( a || b ) {
01422       alarm = anEvent->newAlarm();
01423       if ( a ) {
01424         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01425         deleteStr( s );
01426       }
01427       alarm->setEnabled( true );
01428       if ( b ) {
01429         s = fakeCString( vObjectUStringZValue( b ) );
01430         alarm->setAudioAlarm( QFile::decodeName( s ) );
01431         deleteStr( s );
01432       } else {
01433         alarm->setAudioAlarm( QString() );
01434       }
01435     }
01436   }
01437 
01438   if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) {
01439     Alarm::Ptr alarm;
01440     VObject *a;
01441     VObject *b;
01442     a = isAPropertyOf( vo, VCRunTimeProp );
01443     b = isAPropertyOf( vo, VCProcedureNameProp );
01444 
01445     if ( a || b ) {
01446       alarm = anEvent->newAlarm();
01447       if ( a ) {
01448         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01449         deleteStr( s );
01450       }
01451       alarm->setEnabled( true );
01452 
01453       if ( b ) {
01454         s = fakeCString( vObjectUStringZValue( b ) );
01455         alarm->setProcedureAlarm( QFile::decodeName( s ) );
01456         deleteStr( s );
01457       } else {
01458         alarm->setProcedureAlarm( QString() );
01459       }
01460     }
01461   }
01462 
01463   // related todo
01464   if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) {
01465     anEvent->setRelatedTo( s = fakeCString( vObjectUStringZValue( vo ) ) );
01466     deleteStr( s );
01467     d->mTodosRelate.append( anEvent );
01468   }
01469 
01470   // secrecy
01471   Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
01472   if ( ( vo = isAPropertyOf( vtodo, VCClassProp ) ) != 0 ) {
01473     s = fakeCString( vObjectUStringZValue( vo ) );
01474     if ( s && strcmp( s, "PRIVATE" ) == 0 ) {
01475       secrecy = Incidence::SecrecyPrivate;
01476     } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) {
01477       secrecy = Incidence::SecrecyConfidential;
01478     }
01479     deleteStr( s );
01480   }
01481   anEvent->setSecrecy( secrecy );
01482 
01483   // categories
01484   if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) {
01485     s = fakeCString( vObjectUStringZValue( vo ) );
01486     QString categories = QString::fromUtf8( s );
01487     deleteStr( s );
01488     QStringList tmpStrList = categories.split( ';' );
01489     anEvent->setCategories( tmpStrList );
01490   }
01491 
01492   /* PILOT SYNC STUFF */
01493   if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) {
01494     anEvent->setNonKDECustomProperty(
01495       KPilotIdProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01496     deleteStr( s );
01497     if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) {
01498       anEvent->setNonKDECustomProperty(
01499         KPilotStatusProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01500       deleteStr( s );
01501     } else {
01502       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( int( SYNCMOD ) ) );
01503     }
01504   }
01505 
01506   return anEvent;
01507 }
01508 
01509 Event::Ptr VCalFormat::VEventToEvent( VObject *vevent )
01510 {
01511   VObject *vo;
01512   VObjectIterator voi;
01513   char *s;
01514 
01515   Event::Ptr anEvent( new Event );
01516 
01517   // creation date
01518   if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) {
01519     anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01520     deleteStr( s );
01521   }
01522 
01523   // unique id
01524   vo = isAPropertyOf( vevent, VCUniqueStringProp );
01525   // while the UID property is preferred, it is not required.  We'll use the
01526   // default Event UID if none is given.
01527   if ( vo ) {
01528     anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
01529     deleteStr( s );
01530   }
01531 
01532 #if defined(KCALCORE_FOR_SYMBIAN)
01533   // recurrence id
01534   vo = isAPropertyOf( vevent, VCRecurrenceIdProp );
01535   if ( vo ) {
01536     anEvent->setRecurrenceId( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01537     deleteStr( s );
01538   }
01539 #endif
01540 
01541   // revision
01542   // again NSCAL doesn't give us much to work with, so we improvise...
01543   anEvent->setRevision( 0 );
01544   if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) {
01545     s = fakeCString( vObjectUStringZValue( vo ) );
01546     if ( s ) {
01547       anEvent->setRevision( atoi( s ) );
01548       deleteStr( s );
01549     }
01550   }
01551 
01552   // last modification date
01553   if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) {
01554     anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01555     deleteStr( s );
01556   } else {
01557     anEvent->setLastModified( KDateTime::currentUtcDateTime() );
01558   }
01559 
01560   // organizer
01561   // if our extension property for the event's ORGANIZER exists, add it.
01562   if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) {
01563     // FIXME:  Also use the full name, not just the email address
01564     anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
01565     deleteStr( s );
01566   } else {
01567     if ( d->mCalendar->owner()->name() != "Unknown Name" ) {
01568       anEvent->setOrganizer( d->mCalendar->owner() );
01569     }
01570   }
01571 
01572   // deal with attendees.
01573   initPropIterator( &voi, vevent );
01574   while ( moreIteration( &voi ) ) {
01575     vo = nextVObject( &voi );
01576     if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
01577       Attendee::Ptr a;
01578       VObject *vp;
01579       s = fakeCString( vObjectUStringZValue( vo ) );
01580       QString tmpStr = QString::fromUtf8( s );
01581       deleteStr( s );
01582       tmpStr = tmpStr.simplified();
01583       int emailPos1, emailPos2;
01584       if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
01585         // both email address and name
01586         emailPos2 = tmpStr.lastIndexOf( '>' );
01587         a = Attendee::Ptr( new Attendee( tmpStr.left( emailPos1 - 1 ),
01588                                          tmpStr.mid( emailPos1 + 1,
01589                                          emailPos2 - ( emailPos1 + 1 ) ) ) );
01590       } else if ( tmpStr.indexOf( '@' ) > 0 ) {
01591         // just an email address
01592         a = Attendee::Ptr( new Attendee( 0, tmpStr ) );
01593       } else {
01594         // just a name
01595         QString email = tmpStr.replace( ' ', '.' );
01596         a = Attendee::Ptr( new Attendee( tmpStr, email ) );
01597       }
01598 
01599       // is there an RSVP property?
01600       if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
01601         a->setRSVP( vObjectStringZValue( vp ) );
01602       }
01603       // is there a status property?
01604       if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
01605         a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
01606       }
01607       // add the attendee
01608       anEvent->addAttendee( a );
01609     }
01610   }
01611 
01612   // This isn't strictly true.  An event that doesn't have a start time
01613   // or an end time isn't all-day, it has an anchor in time but it doesn't
01614   // "take up" any time.
01615   /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
01616       (isAPropertyOf(vevent, VCDTendProp) == 0)) {
01617     anEvent->setAllDay(true);
01618     } else {
01619     }*/
01620 
01621   anEvent->setAllDay( false );
01622 
01623   // start time
01624   if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) {
01625     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01626     deleteStr( s );
01627 
01628     if ( anEvent->dtStart().time().hour() == 0 &&
01629          anEvent->dtStart().time().minute() == 0 &&
01630          anEvent->dtStart().time().second() == 0 ) {
01631 #if defined(KCALCORE_FOR_MEEGO)
01632       QDate startDate = anEvent->dtStart().date();
01633       anEvent->setDtStart( KDateTime( startDate, KDateTime::ClockTime ) );
01634 #endif
01635       anEvent->setAllDay( true );
01636     }
01637   }
01638 
01639   // stop time
01640   if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) {
01641     anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01642     deleteStr( s );
01643 
01644     if ( anEvent->dtEnd().time().hour() == 0 &&
01645          anEvent->dtEnd().time().minute() == 0 &&
01646          anEvent->dtEnd().time().second() == 0 ) {
01647 #if defined(KCALCORE_FOR_MEEGO)
01648       QDate endDate = anEvent->dtEnd().date();
01649       anEvent->setDtEnd( KDateTime( endDate, KDateTime::ClockTime ) );
01650 #endif
01651       anEvent->setAllDay( true );
01652     }
01653   }
01654 #if defined(KCALCORE_FOR_MEEGO)
01655   if ( anEvent->allDay() ) {
01656     if ( anEvent->dtEnd() == anEvent->dtStart() ) {
01657       anEvent->setDtEnd( anEvent->dtEnd().addDays( 1 ) );
01658     }
01659   }
01660 #endif
01661 
01662   // at this point, there should be at least a start or end time.
01663   // fix up for events that take up no time but have a time associated
01664   if ( !isAPropertyOf( vevent, VCDTstartProp ) ) {
01665     anEvent->setDtStart( anEvent->dtEnd() );
01666   }
01667   if ( ! isAPropertyOf( vevent, VCDTendProp ) ) {
01668     anEvent->setDtEnd( anEvent->dtStart() );
01669   }
01670 
01672 
01673   // repeat stuff
01674   if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) {
01675     QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01676     deleteStr( s );
01677     tmpStr.simplified();
01678     tmpStr = tmpStr.toUpper();
01679     // first, read the type of the recurrence
01680     int typelen = 1;
01681     uint type = Recurrence::rNone;
01682     if ( tmpStr.left(1) == "D" ) {
01683       type = Recurrence::rDaily;
01684     } else if ( tmpStr.left(1) == "W" ) {
01685       type = Recurrence::rWeekly;
01686     } else {
01687       typelen = 2;
01688       if ( tmpStr.left(2) == "MP" ) {
01689         type = Recurrence::rMonthlyPos;
01690       } else if ( tmpStr.left(2) == "MD" ) {
01691         type = Recurrence::rMonthlyDay;
01692       } else if ( tmpStr.left(2) == "YM" ) {
01693         type = Recurrence::rYearlyMonth;
01694       } else if ( tmpStr.left(2) == "YD" ) {
01695         type = Recurrence::rYearlyDay;
01696       }
01697     }
01698 
01699     if ( type != Recurrence::rNone ) {
01700 
01701       // Immediately after the type is the frequency
01702       int index = tmpStr.indexOf( ' ' );
01703       int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
01704       int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
01705       ++index; // advance to beginning of stuff after freq
01706 
01707       // Read the type-specific settings
01708       switch ( type ) {
01709       case Recurrence::rDaily:
01710         anEvent->recurrence()->setDaily(rFreq);
01711         break;
01712 
01713       case Recurrence::rWeekly:
01714       {
01715         QBitArray qba(7);
01716         QString dayStr;
01717         if ( index == last ) {
01718           // e.g. W1 #0
01719           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01720         } else {
01721           // e.g. W1 SU #0
01722           while ( index < last ) {
01723             dayStr = tmpStr.mid( index, 3 );
01724             int dayNum = numFromDay( dayStr );
01725             if ( dayNum >= 0 ) {
01726               qba.setBit( dayNum );
01727             }
01728             index += 3; // advance to next day, or possibly "#"
01729           }
01730         }
01731         anEvent->recurrence()->setWeekly( rFreq, qba );
01732         break;
01733       }
01734 
01735       case Recurrence::rMonthlyPos:
01736       {
01737         anEvent->recurrence()->setMonthly( rFreq );
01738 
01739         QBitArray qba(7);
01740         short tmpPos;
01741         if ( index == last ) {
01742           // e.g. MP1 #0
01743           tmpPos = anEvent->dtStart().date().day() / 7 + 1;
01744           if ( tmpPos == 5 ) {
01745             tmpPos = -1;
01746           }
01747           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01748           anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01749         } else {
01750           // e.g. MP1 1+ SU #0
01751           while ( index < last ) {
01752             tmpPos = tmpStr.mid( index, 1 ).toShort();
01753             index += 1;
01754             if ( tmpStr.mid( index, 1 ) == "-" ) {
01755               // convert tmpPos to negative
01756               tmpPos = 0 - tmpPos;
01757             }
01758             index += 2; // advance to day(s)
01759             while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
01760               int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
01761               qba.setBit( dayNum );
01762               index += 3; // advance to next day, or possibly pos or "#"
01763             }
01764             anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01765             qba.detach();
01766             qba.fill( false ); // clear out
01767           } // while != "#"
01768         }
01769         break;
01770       }
01771 
01772       case Recurrence::rMonthlyDay:
01773         anEvent->recurrence()->setMonthly( rFreq );
01774         if( index == last ) {
01775           // e.g. MD1 #0
01776           short tmpDay = anEvent->dtStart().date().day();
01777           anEvent->recurrence()->addMonthlyDate( tmpDay );
01778         } else {
01779           // e.g. MD1 3 #0
01780           while ( index < last ) {
01781             int index2 = tmpStr.indexOf( ' ', index );
01782             if ( ( tmpStr.mid( ( index2 - 1 ), 1 ) == "-" ) ||
01783                  ( tmpStr.mid( ( index2 - 1 ), 1 ) == "+" ) ) {
01784               index2 = index2 - 1;
01785             }
01786             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01787             index = index2;
01788             if ( tmpStr.mid( index, 1 ) == "-" ) {
01789               tmpDay = 0 - tmpDay;
01790             }
01791             index += 2; // advance the index;
01792             anEvent->recurrence()->addMonthlyDate( tmpDay );
01793           } // while != #
01794         }
01795         break;
01796 
01797       case Recurrence::rYearlyMonth:
01798         anEvent->recurrence()->setYearly( rFreq );
01799 
01800         if ( index == last ) {
01801           // e.g. YM1 #0
01802           short tmpMonth = anEvent->dtStart().date().month();
01803           anEvent->recurrence()->addYearlyMonth( tmpMonth );
01804         } else {
01805           // e.g. YM1 3 #0
01806           while ( index < last ) {
01807             int index2 = tmpStr.indexOf( ' ', index );
01808             short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
01809             index = index2 + 1;
01810             anEvent->recurrence()->addYearlyMonth( tmpMonth );
01811           } // while != #
01812         }
01813         break;
01814 
01815       case Recurrence::rYearlyDay:
01816         anEvent->recurrence()->setYearly( rFreq );
01817 
01818         if ( index == last ) {
01819           // e.g. YD1 #0
01820           short tmpDay = anEvent->dtStart().date().dayOfYear();
01821           anEvent->recurrence()->addYearlyDay( tmpDay );
01822         } else {
01823           // e.g. YD1 123 #0
01824           while ( index < last ) {
01825             int index2 = tmpStr.indexOf( ' ', index );
01826             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01827             index = index2 + 1;
01828             anEvent->recurrence()->addYearlyDay( tmpDay );
01829           } // while != #
01830         }
01831         break;
01832 
01833       default:
01834         break;
01835       }
01836 
01837       // find the last field, which is either the duration or the end date
01838       index = last;
01839       if ( tmpStr.mid( index, 1 ) == "#" ) {
01840         // Nr of occurrences
01841         index++;
01842         int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
01843         if ( rDuration > 0 ) {
01844           anEvent->recurrence()->setDuration( rDuration );
01845         }
01846       } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
01847         KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
01848         anEvent->recurrence()->setEndDateTime( rEndDate );
01849       }
01850 // anEvent->recurrence()->dump();
01851 
01852     } else {
01853       kDebug() << "we don't understand this type of recurrence!";
01854     } // if known recurrence type
01855   } // repeats
01856 
01857   // recurrence exceptions
01858   if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) {
01859     s = fakeCString( vObjectUStringZValue( vo ) );
01860     QStringList exDates = QString::fromUtf8( s ).split( ',' );
01861     QStringList::ConstIterator it;
01862     for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
01863       KDateTime exDate = ISOToKDateTime(*it);
01864       if ( exDate.time().hour() == 0 &&
01865            exDate.time().minute() == 0 &&
01866            exDate.time().second() == 0 ) {
01867         anEvent->recurrence()->addExDate( ISOToQDate( *it ) );
01868       } else {
01869         anEvent->recurrence()->addExDateTime( exDate );
01870       }
01871     }
01872     deleteStr( s );
01873   }
01874 
01875   // summary
01876   if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) {
01877     s = fakeCString( vObjectUStringZValue( vo ) );
01878     anEvent->setSummary( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
01879     deleteStr( s );
01880   }
01881 
01882   // description
01883   if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) {
01884     s = fakeCString( vObjectUStringZValue( vo ) );
01885     bool isRich = Qt::mightBeRichText( s );
01886     if ( !anEvent->description().isEmpty() ) {
01887       anEvent->setDescription(
01888         anEvent->description() + '\n' + QString::fromUtf8( s ), isRich );
01889     } else {
01890       anEvent->setDescription( QString::fromUtf8( s ), isRich );
01891     }
01892     deleteStr( s );
01893   }
01894 
01895   // location
01896   if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) {
01897     s = fakeCString( vObjectUStringZValue( vo ) );
01898     anEvent->setLocation( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
01899     deleteStr( s );
01900   }
01901 
01902   // some stupid vCal exporters ignore the standard and use Description
01903   // instead of Summary for the default field.  Correct for this.
01904   if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) {
01905     QString tmpStr = anEvent->description().simplified();
01906     anEvent->setDescription( "" );
01907     anEvent->setSummary( tmpStr );
01908   }
01909 
01910 #if 0
01911   // status
01912   if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) {
01913     QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) );
01914     deleteStr( s );
01915 // TODO: Define Event status
01916 //    anEvent->setStatus( tmpStr );
01917   } else {
01918 //    anEvent->setStatus( "NEEDS ACTION" );
01919   }
01920 #endif
01921 
01922   // secrecy
01923   Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
01924   if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) {
01925     s = fakeCString( vObjectUStringZValue( vo ) );
01926     if ( s && strcmp( s, "PRIVATE" ) == 0 ) {
01927       secrecy = Incidence::SecrecyPrivate;
01928     } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) {
01929       secrecy = Incidence::SecrecyConfidential;
01930     }
01931     deleteStr( s );
01932   }
01933   anEvent->setSecrecy( secrecy );
01934 
01935   // categories
01936   if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) {
01937     s = fakeCString( vObjectUStringZValue( vo ) );
01938     QString categories = QString::fromUtf8( s );
01939     deleteStr( s );
01940     QStringList tmpStrList = categories.split( ',' );
01941     anEvent->setCategories( tmpStrList );
01942   }
01943 
01944   // attachments
01945   initPropIterator( &voi, vevent );
01946   while ( moreIteration( &voi ) ) {
01947     vo = nextVObject( &voi );
01948     if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) {
01949       s = fakeCString( vObjectUStringZValue( vo ) );
01950       anEvent->addAttachment( Attachment::Ptr( new Attachment( QString( s ) ) ) );
01951       deleteStr( s );
01952     }
01953   }
01954 
01955   // resources
01956   if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) {
01957     QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01958     deleteStr( s );
01959     QStringList tmpStrList = resources.split( ';' );
01960     anEvent->setResources( tmpStrList );
01961   }
01962 
01963   // alarm stuff
01964   if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) {
01965     Alarm::Ptr alarm;
01966     VObject *a;
01967     VObject *b;
01968     a = isAPropertyOf( vo, VCRunTimeProp );
01969     b = isAPropertyOf( vo, VCDisplayStringProp );
01970 
01971     if ( a || b ) {
01972       alarm = anEvent->newAlarm();
01973       if ( a ) {
01974         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01975         deleteStr( s );
01976       }
01977       alarm->setEnabled( true );
01978 
01979       if ( b ) {
01980         s = fakeCString( vObjectUStringZValue( b ) );
01981         alarm->setDisplayAlarm( QString( s ) );
01982         deleteStr( s );
01983       } else {
01984         alarm->setDisplayAlarm( QString() );
01985       }
01986     }
01987   }
01988 
01989   if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) {
01990     Alarm::Ptr alarm;
01991     VObject *a;
01992     VObject *b;
01993     a = isAPropertyOf( vo, VCRunTimeProp );
01994     b = isAPropertyOf( vo, VCAudioContentProp );
01995 
01996     if ( a || b ) {
01997       alarm = anEvent->newAlarm();
01998       if ( a ) {
01999         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
02000         deleteStr( s );
02001       }
02002       alarm->setEnabled( true );
02003 
02004       if ( b ) {
02005         s = fakeCString( vObjectUStringZValue( b ) );
02006         alarm->setAudioAlarm( QFile::decodeName( s ) );
02007         deleteStr( s );
02008       } else {
02009         alarm->setAudioAlarm( QString() );
02010       }
02011     }
02012   }
02013 
02014   if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) {
02015     Alarm::Ptr alarm;
02016     VObject *a;
02017     VObject *b;
02018     a = isAPropertyOf( vo, VCRunTimeProp );
02019     b = isAPropertyOf( vo, VCProcedureNameProp );
02020 
02021     if ( a || b ) {
02022       alarm = anEvent->newAlarm();
02023       if ( a ) {
02024         alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
02025         deleteStr( s );
02026       }
02027       alarm->setEnabled( true );
02028 
02029       if ( b ) {
02030         s = fakeCString( vObjectUStringZValue( b ) );
02031         alarm->setProcedureAlarm( QFile::decodeName( s ) );
02032         deleteStr( s );
02033       } else {
02034         alarm->setProcedureAlarm( QString() );
02035       }
02036     }
02037   }
02038 
02039   // priority
02040   if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) {
02041     s = fakeCString( vObjectUStringZValue( vo ) );
02042     if ( s ) {
02043       anEvent->setPriority( atoi( s ) );
02044       deleteStr( s );
02045     }
02046   }
02047 
02048   // transparency
02049   if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) {
02050     s = fakeCString( vObjectUStringZValue( vo ) );
02051     if ( s ) {
02052       int i = atoi( s );
02053       anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
02054       deleteStr( s );
02055     }
02056   }
02057 
02058   // related event
02059   if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) {
02060     anEvent->setRelatedTo( s = fakeCString( vObjectUStringZValue( vo ) ) );
02061     deleteStr( s );
02062     d->mEventsRelate.append( anEvent );
02063   }
02064 
02065   /* PILOT SYNC STUFF */
02066   if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) {
02067     anEvent->setNonKDECustomProperty(
02068       KPilotIdProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
02069     deleteStr( s );
02070     if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) {
02071       anEvent->setNonKDECustomProperty(
02072         KPilotStatusProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
02073       deleteStr( s );
02074     } else {
02075       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( int( SYNCMOD ) ) );
02076     }
02077   }
02078 
02079   /* Rest of the custom properties */
02080   readCustomProperties( vevent, anEvent );
02081 
02082   return anEvent;
02083 }
02084 
02085 QString VCalFormat::parseTZ( const QByteArray &timezone ) const
02086 {
02087   qDebug() << timezone;
02088   QString pZone = timezone.mid( timezone.indexOf( "TZID:VCAL" ) + 9 );
02089   return pZone.mid( 0, pZone.indexOf( QChar( QLatin1Char( '\n' ) ) ) );
02090 }
02091 
02092 QString VCalFormat::parseDst( QByteArray &timezone ) const
02093 {
02094   if ( !timezone.contains( "BEGIN:DAYLIGHT" ) ) {
02095     return QString();
02096   }
02097 
02098   timezone = timezone.mid( timezone.indexOf( "BEGIN:DAYLIGHT" ) );
02099   timezone = timezone.mid( timezone.indexOf( "TZNAME:" ) + 7 );
02100   QString sStart = timezone.mid( 0, ( timezone.indexOf( "COMMENT:" ) ) );
02101   sStart.chop( 2 );
02102   timezone = timezone.mid( timezone.indexOf( "TZOFFSETTO:" ) + 11 );
02103   QString sOffset = timezone.mid( 0, ( timezone.indexOf( "DTSTART:" ) ) );
02104   sOffset.chop( 2 );
02105   sOffset.insert( 3, QString( ":" ) );
02106   timezone = timezone.mid( timezone.indexOf( "TZNAME:" ) + 7 );
02107   QString sEnd = timezone.mid( 0, ( timezone.indexOf( "COMMENT:" ) ) );
02108   sEnd.chop( 2 );
02109 
02110   return "TRUE;" + sOffset + ';' + sStart + ';' + sEnd + ";;";
02111 }
02112 
02113 QString VCalFormat::qDateToISO( const QDate &qd )
02114 {
02115   QString tmpStr;
02116 
02117   if ( !qd.isValid() ) {
02118     return QString();
02119   }
02120 
02121   tmpStr.sprintf( "%.2d%.2d%.2d", qd.year(), qd.month(), qd.day() );
02122   return tmpStr;
02123 
02124 }
02125 
02126 QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu )
02127 {
02128   QString tmpStr;
02129 
02130   if ( !dt.isValid() ) {
02131     return QString();
02132   }
02133 
02134   QDateTime tmpDT;
02135   if ( zulu ) {
02136     tmpDT = dt.toUtc().dateTime();
02137   } else {
02138 #if !defined(KCALCORE_FOR_MEEGO)
02139     tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime();
02140 #else
02141     tmpDT = dt.dateTime();
02142 #endif
02143   }
02144   tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
02145                   tmpDT.date().year(), tmpDT.date().month(),
02146                   tmpDT.date().day(), tmpDT.time().hour(),
02147                   tmpDT.time().minute(), tmpDT.time().second() );
02148   if ( zulu || dt.isUtc() ) {
02149     tmpStr += 'Z';
02150   }
02151   return tmpStr;
02152 }
02153 
02154 KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr )
02155 {
02156   QDate tmpDate;
02157   QTime tmpTime;
02158   QString tmpStr;
02159   int year, month, day, hour, minute, second;
02160 
02161   tmpStr = dtStr;
02162   year = tmpStr.left( 4 ).toInt();
02163   month = tmpStr.mid( 4, 2 ).toInt();
02164   day = tmpStr.mid( 6, 2 ).toInt();
02165   hour = tmpStr.mid( 9, 2 ).toInt();
02166   minute = tmpStr.mid( 11, 2 ).toInt();
02167   second = tmpStr.mid( 13, 2 ).toInt();
02168   tmpDate.setYMD( year, month, day );
02169   tmpTime.setHMS( hour, minute, second );
02170 
02171   if ( tmpDate.isValid() && tmpTime.isValid() ) {
02172     // correct for GMT if string is in Zulu format
02173     if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) {
02174       return KDateTime( tmpDate, tmpTime, KDateTime::UTC );
02175     } else {
02176       return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() );
02177     }
02178   } else {
02179     return KDateTime();
02180   }
02181 }
02182 
02183 QDate VCalFormat::ISOToQDate( const QString &dateStr )
02184 {
02185   int year, month, day;
02186 
02187   year = dateStr.left( 4 ).toInt();
02188   month = dateStr.mid( 4, 2 ).toInt();
02189   day = dateStr.mid( 6, 2 ).toInt();
02190 
02191   return QDate( year, month, day );
02192 }
02193 
02194 bool VCalFormat::parseTZOffsetISO8601( const QString &s, int &result )
02195 {
02196   // ISO8601 format(s):
02197   // +- hh : mm
02198   // +- hh mm
02199   // +- hh
02200 
02201   // We also accept broken one without +
02202   int mod = 1;
02203   int v = 0;
02204   QString str = s.trimmed();
02205   int ofs = 0;
02206   result = 0;
02207 
02208   // Check for end
02209   if ( str.size() <= ofs ) {
02210     return false;
02211   }
02212   if ( str[ofs] == '-' ) {
02213     mod = -1;
02214     ofs++;
02215   } else if ( str[ofs] == '+' ) {
02216     ofs++;
02217   }
02218   if ( str.size() <= ofs ) {
02219     return false;
02220   }
02221 
02222   // Make sure next two values are numbers
02223   bool ok;
02224 
02225   if ( str.size() < ( ofs + 2 ) ) {
02226     return false;
02227   }
02228 
02229   v = str.mid( ofs, 2 ).toInt( &ok ) * 60;
02230   if ( !ok ) {
02231     return false;
02232   }
02233   ofs += 2;
02234 
02235   if ( str.size() > ofs ) {
02236     if ( str[ofs] == ':' ) {
02237       ofs++;
02238     }
02239     if ( str.size() > ofs ) {
02240       if ( str.size() < ( ofs + 2 ) ) {
02241         return false;
02242       }
02243       v += str.mid( ofs, 2 ).toInt( &ok );
02244       if ( !ok ) {
02245         return false;
02246       }
02247     }
02248   }
02249   result = v * mod * 60;
02250   return true;
02251 }
02252 
02253 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
02254 // and break it down from it's tree-like format into the dictionary format
02255 // that is used internally in the VCalFormat.
02256 void VCalFormat::populate( VObject *vcal, bool deleted, const QString &notebook )
02257 {
02258   Q_UNUSED( notebook );
02259   // this function will populate the caldict dictionary and other event
02260   // lists. It turns vevents into Events and then inserts them.
02261 
02262   VObjectIterator i;
02263   VObject *curVO, *curVOProp;
02264   Event::Ptr anEvent;
02265   bool hasTimeZone = false; //The calendar came with a TZ and not UTC
02266   KDateTime::Spec previousSpec; //If we add a new TZ we should leave the spec as it was before
02267 
02268   if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) {
02269     char *methodType = 0;
02270     methodType = fakeCString( vObjectUStringZValue( curVO ) );
02271     kDebug() << "This calendar is an iTIP transaction of type '"
02272              << methodType << "'";
02273     deleteStr( methodType );
02274   }
02275 
02276   // warn the user that we might have trouble reading non-known calendar.
02277   if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) {
02278     char *s = fakeCString( vObjectUStringZValue( curVO ) );
02279     if ( !s || strcmp( productId().toUtf8(), s ) != 0 ) {
02280       kDebug() << "This vCalendar file was not created by KOrganizer or"
02281                << "any other product we support. Loading anyway...";
02282     }
02283     setLoadedProductId( s );
02284     deleteStr( s );
02285   }
02286 
02287   // warn the user we might have trouble reading this unknown version.
02288   if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) {
02289     char *s = fakeCString( vObjectUStringZValue( curVO ) );
02290     if ( !s || strcmp( _VCAL_VERSION, s ) != 0 ) {
02291       kDebug() << "This vCalendar file has version" << s
02292                << "We only support" << _VCAL_VERSION;
02293     }
02294     deleteStr( s );
02295   }
02296 
02297   // set the time zone (this is a property of the view, so just discard!)
02298   if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) {
02299     char *s = fakeCString( vObjectUStringZValue( curVO ) );
02300     QString ts( s );
02301     QString name = "VCAL" + ts;
02302     deleteStr( s );
02303 
02304     // TODO: While using the timezone-offset + vcal as timezone is is
02305     // most likely unique, we should REALLY actually create something
02306     // like vcal-tzoffset-daylightoffsets, or better yet,
02307     // vcal-hash<the former>
02308 
02309     QStringList tzList;
02310     QString tz;
02311     int utcOffset;
02312     int utcOffsetDst;
02313     if ( parseTZOffsetISO8601( ts, utcOffset ) ) {
02314       kDebug() << "got standard offset" << ts << utcOffset;
02315       // standard from tz
02316       // starting date for now 01011900
02317       KDateTime dt = KDateTime( QDateTime( QDate( 1900, 1, 1 ), QTime( 0, 0, 0 ) ) );
02318       tz = QString( "STD;%1;false;%2" ).arg( QString::number( utcOffset ) ).arg( dt.toString() );
02319       tzList.append( tz );
02320 
02321       // go through all the daylight tags
02322       initPropIterator( &i, vcal );
02323       while ( moreIteration( &i ) ) {
02324         curVO = nextVObject( &i );
02325         if ( strcmp( vObjectName( curVO ), VCDayLightProp ) == 0 ) {
02326           char *s = fakeCString( vObjectUStringZValue( curVO ) );
02327           QString dst = QString( s );
02328           QStringList argl = dst.split( ',' );
02329           deleteStr( s );
02330 
02331           // Too short -> not interesting
02332           if ( argl.size() < 4 ) {
02333             continue;
02334           }
02335 
02336           // We don't care about the non-DST periods
02337           if ( argl[0] != "TRUE" ) {
02338             continue;
02339           }
02340 
02341           if ( parseTZOffsetISO8601( argl[1], utcOffsetDst ) ) {
02342 
02343             kDebug() << "got DST offset" << argl[1] << utcOffsetDst;
02344             // standard
02345             QString strEndDate = argl[3];
02346             KDateTime endDate = ISOToKDateTime( strEndDate );
02347             // daylight
02348             QString strStartDate = argl[2];
02349             KDateTime startDate = ISOToKDateTime( strStartDate );
02350 
02351             QString strRealEndDate = strEndDate;
02352             QString strRealStartDate = strStartDate;
02353             KDateTime realEndDate = endDate;
02354             KDateTime realStartDate = startDate;
02355             // if we get dates for some reason in wrong order, earlier is used for dst
02356             if ( endDate < startDate ) {
02357               strRealEndDate = strStartDate;
02358               strRealStartDate = strEndDate;
02359               realEndDate = startDate;
02360               realStartDate = endDate;
02361             }
02362             tz = QString( "%1;%2;false;%3" ).
02363                  arg( strRealEndDate ).
02364                  arg( QString::number( utcOffset ) ).
02365                  arg( realEndDate.toString() );
02366             tzList.append( tz );
02367 
02368             tz = QString( "%1;%2;true;%3" ).
02369                  arg( strRealStartDate ).
02370                  arg( QString::number( utcOffsetDst ) ).
02371                  arg( realStartDate.toString() );
02372             tzList.append( tz );
02373           } else {
02374             kDebug() << "unable to parse dst" << argl[1];
02375           }
02376         }
02377       }
02378       ICalTimeZones *tzlist = d->mCalendar->timeZones();
02379       ICalTimeZoneSource tzs;
02380       ICalTimeZone zone = tzs.parse( name, tzList, *tzlist );
02381       if ( !zone.isValid() ) {
02382         kDebug() << "zone is not valid, parsing error" << tzList;
02383       } else {
02384         previousSpec = d->mCalendar->timeSpec();
02385         d->mCalendar->setTimeZoneId( name );
02386         hasTimeZone = true;
02387       }
02388     } else {
02389       kDebug() << "unable to parse tzoffset" << ts;
02390     }
02391   }
02392 
02393   // Store all events with a relatedTo property in a list for post-processing
02394   d->mEventsRelate.clear();
02395   d->mTodosRelate.clear();
02396 
02397   initPropIterator( &i, vcal );
02398 
02399   // go through all the vobjects in the vcal
02400   while ( moreIteration( &i ) ) {
02401     curVO = nextVObject( &i );
02402 
02403     /************************************************************************/
02404 
02405     // now, check to see that the object is an event or todo.
02406     if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) {
02407 
02408       if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) {
02409         char *s;
02410         s = fakeCString( vObjectUStringZValue( curVOProp ) );
02411         // check to see if event was deleted by the kpilot conduit
02412         if ( s ) {
02413           if ( atoi( s ) == SYNCDEL ) {
02414             deleteStr( s );
02415             kDebug() << "skipping pilot-deleted event";
02416             goto SKIP;
02417           }
02418           deleteStr( s );
02419         }
02420       }
02421 
02422       if ( !isAPropertyOf( curVO, VCDTstartProp ) &&
02423            !isAPropertyOf( curVO, VCDTendProp ) ) {
02424         kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping...";
02425         goto SKIP;
02426       }
02427 
02428       anEvent = VEventToEvent( curVO );
02429       if ( anEvent ) {
02430         if ( hasTimeZone && !anEvent->allDay() && anEvent->dtStart().isUtc() ) {
02431           //This sounds stupid but is how others are doing it, so here
02432           //we go. If there is a TZ in the VCALENDAR even if the dtStart
02433           //and dtend are in UTC, clients interpret it using also the TZ defined
02434           //in the Calendar. I know it sounds braindead but oh well
02435           int utcOffSet = anEvent->dtStart().utcOffset();
02436           KDateTime dtStart( anEvent->dtStart().dateTime().addSecs( utcOffSet ),
02437                              d->mCalendar->timeSpec() );
02438           KDateTime dtEnd( anEvent->dtEnd().dateTime().addSecs( utcOffSet ),
02439                              d->mCalendar->timeSpec() );
02440           anEvent->setDtStart( dtStart );
02441           anEvent->setDtEnd( dtEnd );
02442         }
02443         Event::Ptr old = !anEvent->hasRecurrenceId() ?
02444       d->mCalendar->event( anEvent->uid() ) :
02445         d->mCalendar->event( anEvent->uid(), anEvent->recurrenceId() );
02446 
02447         if ( old ) {
02448           if ( deleted ) {
02449             d->mCalendar->deleteEvent( old ); // move old to deleted
02450             removeAllVCal( d->mEventsRelate, old );
02451           } else if ( anEvent->revision() > old->revision() ) {
02452             d->mCalendar->deleteEvent( old ); // move old to deleted
02453             removeAllVCal( d->mEventsRelate, old );
02454             d->mCalendar->addEvent( anEvent ); // and replace it with this one
02455           }
02456         } else if ( deleted ) {
02457           old = !anEvent->hasRecurrenceId() ?
02458           d->mCalendar->deletedEvent( anEvent->uid() ) :
02459             d->mCalendar->deletedEvent( anEvent->uid(), anEvent->recurrenceId() );
02460           if ( !old ) {
02461             d->mCalendar->addEvent( anEvent ); // add this one
02462             d->mCalendar->deleteEvent( anEvent ); // and move it to deleted
02463           }
02464         } else {
02465           d->mCalendar->addEvent( anEvent ); // just add this one
02466         }
02467       }
02468     } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) {
02469       Todo::Ptr aTodo = VTodoToEvent( curVO );
02470       if ( aTodo ) {
02471         if ( hasTimeZone && !aTodo->allDay()  && aTodo->dtStart().isUtc() ) {
02472           //This sounds stupid but is how others are doing it, so here
02473           //we go. If there is a TZ in the VCALENDAR even if the dtStart
02474           //and dtend are in UTC, clients interpret it usint alse the TZ defined
02475           //in the Calendar. I know it sounds braindead but oh well
02476           int utcOffSet = aTodo->dtStart().utcOffset();
02477           KDateTime dtStart( aTodo->dtStart().dateTime().addSecs( utcOffSet ),
02478                             d->mCalendar->timeSpec() );
02479           aTodo->setDtStart( dtStart );
02480           if ( aTodo->hasDueDate() ) {
02481             KDateTime dtDue( aTodo->dtDue().dateTime().addSecs( utcOffSet ),
02482                             d->mCalendar->timeSpec() );
02483             aTodo->setDtDue( dtDue );
02484           }
02485         }
02486         Todo::Ptr old = !aTodo->hasRecurrenceId() ?
02487       d->mCalendar->todo( aTodo->uid() ) :
02488         d->mCalendar->todo( aTodo->uid(), aTodo->recurrenceId() );
02489         if ( old ) {
02490           if ( deleted ) {
02491             d->mCalendar->deleteTodo( old ); // move old to deleted
02492             removeAllVCal( d->mTodosRelate, old );
02493           } else if ( aTodo->revision() > old->revision() ) {
02494             d->mCalendar->deleteTodo( old ); // move old to deleted
02495             removeAllVCal( d->mTodosRelate, old );
02496             d->mCalendar->addTodo( aTodo ); // and replace it with this one
02497           }
02498         } else if ( deleted ) {
02499           old = d->mCalendar->deletedTodo( aTodo->uid(), aTodo->recurrenceId() );
02500           if ( !old ) {
02501             d->mCalendar->addTodo( aTodo ); // add this one
02502             d->mCalendar->deleteTodo( aTodo ); // and move it to deleted
02503           }
02504         } else {
02505           d->mCalendar->addTodo( aTodo ); // just add this one
02506         }
02507       }
02508     } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) ||
02509                 ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) ||
02510                 ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) {
02511       // do nothing, we know these properties and we want to skip them.
02512       // we have either already processed them or are ignoring them.
02513       ;
02514     } else if ( strcmp( vObjectName( curVO ), VCDayLightProp ) == 0 ) {
02515       // do nothing daylights are already processed
02516       ;
02517     } else {
02518       kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
02519     }
02520     SKIP:
02521       ;
02522   } // while
02523 
02524   // Post-Process list of events with relations, put Event objects in relation
02525   Event::List::ConstIterator eIt;
02526   for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) {
02527     (*eIt)->setRelatedTo( (*eIt)->relatedTo() );
02528   }
02529   Todo::List::ConstIterator tIt;
02530   for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) {
02531     (*tIt)->setRelatedTo( (*tIt)->relatedTo() );
02532    }
02533 
02534    //Now lets put the TZ back as it was if we have changed it.
02535   if ( hasTimeZone ) {
02536     d->mCalendar->setTimeSpec(previousSpec);
02537   }
02538 
02539 }
02540 
02541 const char *VCalFormat::dayFromNum( int day )
02542 {
02543   const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
02544 
02545   return days[day];
02546 }
02547 
02548 int VCalFormat::numFromDay( const QString &day )
02549 {
02550   if ( day == "MO " ) {
02551     return 0;
02552   }
02553   if ( day == "TU " ) {
02554     return 1;
02555   }
02556   if ( day == "WE " ) {
02557     return 2;
02558   }
02559   if ( day == "TH " ) {
02560     return 3;
02561   }
02562   if ( day == "FR " ) {
02563     return 4;
02564   }
02565   if ( day == "SA " ) {
02566     return 5;
02567   }
02568   if ( day == "SU " ) {
02569     return 6;
02570   }
02571 
02572   return -1; // something bad happened. :)
02573 }
02574 
02575 Attendee::PartStat VCalFormat::readStatus( const char *s ) const
02576 {
02577   QString statStr = s;
02578   statStr = statStr.toUpper();
02579   Attendee::PartStat status;
02580 
02581   if ( statStr == "X-ACTION" ) {
02582     status = Attendee::NeedsAction;
02583   } else if ( statStr == "NEEDS ACTION" ) {
02584     status = Attendee::NeedsAction;
02585   } else if ( statStr == "ACCEPTED" ) {
02586     status = Attendee::Accepted;
02587   } else if ( statStr == "SENT" ) {
02588     status = Attendee::NeedsAction;
02589   } else if ( statStr == "TENTATIVE" ) {
02590     status = Attendee::Tentative;
02591   } else if ( statStr == "CONFIRMED" ) {
02592     status = Attendee::Accepted;
02593   } else if ( statStr == "DECLINED" ) {
02594     status = Attendee::Declined;
02595   } else if ( statStr == "COMPLETED" ) {
02596     status = Attendee::Completed;
02597   } else if ( statStr == "DELEGATED" ) {
02598     status = Attendee::Delegated;
02599   } else {
02600     kDebug() << "error setting attendee mStatus, unknown mStatus!";
02601     status = Attendee::NeedsAction;
02602   }
02603 
02604   return status;
02605 }
02606 
02607 QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const
02608 {
02609   switch( status ) {
02610   default:
02611   case Attendee::NeedsAction:
02612     return "NEEDS ACTION";
02613     break;
02614   case Attendee::Accepted:
02615     return "ACCEPTED";
02616     break;
02617   case Attendee::Declined:
02618     return "DECLINED";
02619     break;
02620   case Attendee::Tentative:
02621     return "TENTATIVE";
02622     break;
02623   case Attendee::Delegated:
02624     return "DELEGATED";
02625     break;
02626   case Attendee::Completed:
02627     return "COMPLETED";
02628     break;
02629   case Attendee::InProcess:
02630     return "NEEDS ACTION";
02631     break;
02632   }
02633 }
02634 
02635 void VCalFormat::readCustomProperties( VObject *o, const Incidence::Ptr &i )
02636 {
02637   VObjectIterator iter;
02638   VObject *cur;
02639   const char *curname;
02640   char *s;
02641 
02642   initPropIterator( &iter, o );
02643   while ( moreIteration( &iter ) ) {
02644     cur = nextVObject( &iter );
02645     curname = vObjectName( cur );
02646     Q_ASSERT( curname );
02647     if ( ( curname[0] == 'X' && curname[1] == '-' ) &&
02648          strcmp( curname, ICOrganizerProp ) != 0 ) {
02649       // TODO - for the time being, we ignore the parameters part
02650       // and just do the value handling here
02651       i->setNonKDECustomProperty(
02652         curname, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( cur ) ) ) );
02653       deleteStr( s );
02654     }
02655   }
02656 }
02657 
02658 void VCalFormat::writeCustomProperties( VObject *o, const Incidence::Ptr &i )
02659 {
02660   const QMap<QByteArray, QString> custom = i->customProperties();
02661   for ( QMap<QByteArray, QString>::ConstIterator c = custom.begin();
02662         c != custom.end();  ++c ) {
02663     if ( d->mManuallyWrittenExtensionFields.contains( c.key() ) ) {
02664       continue;
02665     }
02666 
02667     addPropValue( o, c.key(), c.value().toUtf8() );
02668   }
02669 }
02670 
02671 void VCalFormat::virtual_hook( int id, void *data )
02672 {
02673   Q_UNUSED( id );
02674   Q_UNUSED( data );
02675   Q_ASSERT( false );
02676 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:16:57 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

Skip menu "KCalCore Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal