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 ¬ebook ) 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 ¬ebook ) 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 ¬ebook, 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 ¬ebook ) 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 Mon May 14 2012 04:35:41 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:35:41 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.