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