• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KCal Library

vcalformat.cpp

Go to the documentation of this file.
00001 /*
00002   This file is part of the kcal 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 */
00038 #include "vcalformat.h"
00039 #include "calendar.h"
00040 #include "versit/vcc.h"
00041 #include "versit/vobject.h"
00042 
00043 #include <kdebug.h>
00044 #include <kdatetime.h>
00045 #include <klocale.h>
00046 
00047 #include <QtCore/QString>
00048 #include <QtCore/QRegExp>
00049 #include <QtCore/QFile>
00050 #include <QtCore/QByteArray>
00051 #include <QtGui/QTextDocument>
00052 
00053 using namespace KCal;
00054 
00059 //@cond PRIVATE
00060 class KCal::VCalFormat::Private
00061 {
00062   public:
00063     Calendar *mCalendar;
00064     Event::List mEventsRelate;  // Events with relations
00065     Todo::List mTodosRelate;    // To-dos with relations
00066 };
00067 //@endcond
00068 
00069 VCalFormat::VCalFormat() : d( new KCal::VCalFormat::Private )
00070 {
00071 }
00072 
00073 VCalFormat::~VCalFormat()
00074 {
00075   delete d;
00076 }
00077 
00078 bool VCalFormat::load( Calendar *calendar, const QString &fileName )
00079 {
00080   d->mCalendar = calendar;
00081 
00082   clearException();
00083 
00084   kDebug() << fileName;
00085 
00086   VObject *vcal = 0;
00087 
00088   // this is not necessarily only 1 vcal.  Could be many vcals, or include
00089   // a vcard...
00090   vcal = Parse_MIME_FromFileName( const_cast<char *>( QFile::encodeName( fileName ).data() ) );
00091 
00092   if ( !vcal ) {
00093     setException( new ErrorFormat( ErrorFormat::CalVersionUnknown ) );
00094     return false;
00095   }
00096 
00097   // any other top-level calendar stuff should be added/initialized here
00098 
00099   // put all vobjects into their proper places
00100   populate( vcal );
00101 
00102   // clean up from vcal API stuff
00103   cleanVObjects( vcal );
00104   cleanStrTbl();
00105 
00106   return true;
00107 }
00108 
00109 bool VCalFormat::save( Calendar *calendar, const QString &fileName )
00110 {
00111   d->mCalendar = calendar;
00112 
00113   QString tmpStr;
00114   VObject *vcal, *vo;
00115 
00116   kDebug() << fileName;
00117 
00118   vcal = newVObject( VCCalProp );
00119 
00120   //  addPropValue(vcal,VCLocationProp, "0.0");
00121   addPropValue( vcal, VCProdIdProp, productId().toLatin1() );
00122   addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
00123 
00124   // TODO STUFF
00125   Todo::List todoList = d->mCalendar->rawTodos();
00126   Todo::List::ConstIterator it;
00127   for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
00128     vo = eventToVTodo( *it );
00129     addVObjectProp( vcal, vo );
00130   }
00131 
00132   // EVENT STUFF
00133   Event::List events = d->mCalendar->rawEvents();
00134   Event::List::ConstIterator it2;
00135   for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) {
00136     vo = eventToVEvent( *it2 );
00137     addVObjectProp( vcal, vo );
00138   }
00139 
00140   writeVObjectToFile( QFile::encodeName( fileName ).data(), vcal );
00141   cleanVObjects( vcal );
00142   cleanStrTbl();
00143 
00144   if ( QFile::exists( fileName ) ) {
00145     return true;
00146   } else {
00147     return false; // error
00148   }
00149 
00150   return false;
00151 }
00152 
00153 bool VCalFormat::fromString( Calendar *calendar, const QString &text )
00154 {
00155   return fromRawString( calendar, text.toUtf8() );
00156 }
00157 
00158 bool VCalFormat::fromRawString( Calendar *calendar, const QByteArray &string )
00159 {
00160   d->mCalendar = calendar;
00161 
00162   if ( !string.size() ) {
00163     return false;
00164   }
00165 
00166   VObject *vcal = Parse_MIME( string.data(), string.size() );
00167   if ( !vcal ) {
00168     return false;
00169   }
00170 
00171   VObjectIterator i;
00172   VObject *curvo;
00173   initPropIterator( &i, vcal );
00174 
00175   // we only take the first object. TODO: parse all incidences.
00176   do  {
00177     curvo = nextVObject( &i );
00178   } while ( strcmp( vObjectName( curvo ), VCEventProp ) &&
00179             strcmp( vObjectName( curvo ), VCTodoProp ) );
00180 
00181   if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) {
00182     Event *event = VEventToEvent( curvo );
00183     calendar->addEvent( event );
00184   } else {
00185     kDebug() << "Unknown object type.";
00186     deleteVObject( vcal );
00187     return false;
00188   }
00189 
00190   deleteVObject( vcal );
00191 
00192   return true;
00193 }
00194 
00195 QString VCalFormat::toString( Calendar *calendar )
00196 {
00197   // TODO: Factor out VCalFormat::asString()
00198   d->mCalendar = calendar;
00199 
00200   VObject *vcal = newVObject( VCCalProp );
00201 
00202   addPropValue( vcal, VCProdIdProp, CalFormat::productId().toLatin1() );
00203   addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
00204 
00205   // TODO: Use all data.
00206   Event::List events = calendar->events();
00207   if( events.isEmpty() ) {
00208      cleanVObject ( vcal );
00209      return QString();
00210   }
00211   Event *event = events.first();
00212   if ( !event ) {
00213     cleanVObject ( vcal );
00214     return QString();
00215   }
00216   VObject *vevent = eventToVEvent( event );
00217 
00218   addVObjectProp( vcal, vevent );
00219 
00220   char *buf = writeMemVObject( 0, 0, vcal );
00221 
00222   QString result( buf );
00223 
00224   cleanVObject( vcal );
00225 
00226   return result;
00227 }
00228 
00229 VObject *VCalFormat::eventToVTodo( const Todo *anEvent )
00230 {
00231   VObject *vtodo;
00232   QString tmpStr;
00233 
00234   vtodo = newVObject( VCTodoProp );
00235 
00236   // due date
00237   if ( anEvent->hasDueDate() ) {
00238     tmpStr = kDateTimeToISO( anEvent->dtDue(), !anEvent->allDay() );
00239     addPropValue( vtodo, VCDueProp, tmpStr.toLocal8Bit() );
00240   }
00241 
00242   // start date
00243   if ( anEvent->hasStartDate() ) {
00244     tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
00245     addPropValue( vtodo, VCDTstartProp, tmpStr.toLocal8Bit() );
00246   }
00247 
00248   // creation date
00249   tmpStr = kDateTimeToISO( anEvent->created() );
00250   addPropValue( vtodo, VCDCreatedProp, tmpStr.toLocal8Bit() );
00251 
00252   // unique id
00253   addPropValue( vtodo, VCUniqueStringProp,
00254                 anEvent->uid().toLocal8Bit() );
00255 
00256   // revision
00257   tmpStr.sprintf( "%i", anEvent->revision() );
00258   addPropValue( vtodo, VCSequenceProp, tmpStr.toLocal8Bit() );
00259 
00260   // last modification date
00261   tmpStr = kDateTimeToISO( anEvent->lastModified() );
00262   addPropValue( vtodo, VCLastModifiedProp, tmpStr.toLocal8Bit() );
00263 
00264   // organizer stuff
00265   // @TODO: How about the common name?
00266   if ( !anEvent->organizer().email().isEmpty() ) {
00267     tmpStr = "MAILTO:" + anEvent->organizer().email();
00268     addPropValue( vtodo, ICOrganizerProp, tmpStr.toLocal8Bit() );
00269   }
00270 
00271   // attendees
00272   if ( anEvent->attendeeCount() > 0 ) {
00273     Attendee::List::ConstIterator it;
00274     Attendee *curAttendee;
00275     for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end();
00276           ++it ) {
00277       curAttendee = *it;
00278       if ( !curAttendee->email().isEmpty() &&
00279            !curAttendee->name().isEmpty() ) {
00280         tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
00281       } else if ( curAttendee->name().isEmpty() ) {
00282         tmpStr = "MAILTO: " + curAttendee->email();
00283       } else if ( curAttendee->email().isEmpty() ) {
00284         tmpStr = "MAILTO: " + curAttendee->name();
00285       } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
00286         kDebug() << "warning! this Event has an attendee w/o name or email!";
00287       }
00288       VObject *aProp = addPropValue( vtodo, VCAttendeeProp, tmpStr.toLocal8Bit() );
00289       addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
00290       addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
00291     }
00292   }
00293 
00294   // description BL:
00295   if ( !anEvent->description().isEmpty() ) {
00296     VObject *d = addPropValue( vtodo, VCDescriptionProp,
00297                                anEvent->description().toLocal8Bit() );
00298     if ( anEvent->description().indexOf( '\n' ) != -1 ) {
00299       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00300     }
00301   }
00302 
00303   // summary
00304   if ( !anEvent->summary().isEmpty() ) {
00305     addPropValue( vtodo, VCSummaryProp, anEvent->summary().toLocal8Bit() );
00306   }
00307 
00308   // location
00309   if ( !anEvent->location().isEmpty() ) {
00310     addPropValue( vtodo, VCLocationProp, anEvent->location().toLocal8Bit() );
00311   }
00312 
00313   // completed status
00314   // backward compatibility, KOrganizer used to interpret only these two values
00315   addPropValue( vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : "NEEDS_ACTION" );
00316 
00317   // completion date
00318   if ( anEvent->hasCompletedDate() ) {
00319     tmpStr = kDateTimeToISO( anEvent->completed() );
00320     addPropValue( vtodo, VCCompletedProp, tmpStr.toLocal8Bit() );
00321   }
00322 
00323   // priority
00324   tmpStr.sprintf( "%i", anEvent->priority() );
00325   addPropValue( vtodo, VCPriorityProp, tmpStr.toLocal8Bit() );
00326 
00327   // related event
00328   if ( anEvent->relatedTo() ) {
00329     addPropValue( vtodo, VCRelatedToProp,
00330                   anEvent->relatedTo()->uid().toLocal8Bit() );
00331   }
00332 
00333   // categories
00334   const QStringList tmpStrList = anEvent->categories();
00335   tmpStr = "";
00336   QString catStr;
00337   QStringList::const_iterator its;
00338   for ( its = tmpStrList.constBegin(); its != tmpStrList.constEnd(); ++its ) {
00339     catStr = *its;
00340     if ( catStr[0] == ' ' ) {
00341       tmpStr += catStr.mid( 1 );
00342     } else {
00343       tmpStr += catStr;
00344     }
00345     // this must be a ';' character as the vCalendar specification requires!
00346     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00347     // read in.
00348     tmpStr += ';';
00349   }
00350   if ( !tmpStr.isEmpty() ) {
00351     tmpStr.truncate( tmpStr.length() - 1 );
00352     addPropValue( vtodo, VCCategoriesProp, tmpStr.toLocal8Bit() );
00353   }
00354 
00355   // alarm stuff
00356   Alarm::List::ConstIterator it;
00357   for ( it = anEvent->alarms().begin(); it != anEvent->alarms().end(); ++it ) {
00358     Alarm *alarm = *it;
00359     if ( alarm->enabled() ) {
00360       VObject *a = addProp( vtodo, VCDAlarmProp );
00361       tmpStr = kDateTimeToISO( alarm->time() );
00362       addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00363       addPropValue( a, VCRepeatCountProp, "1" );
00364       addPropValue( a, VCDisplayStringProp, "beep!" );
00365       if ( alarm->type() == Alarm::Audio ) {
00366         a = addProp( vtodo, VCAAlarmProp );
00367         addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00368         addPropValue( a, VCRepeatCountProp, "1" );
00369         addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
00370       } else if ( alarm->type() == Alarm::Procedure ) {
00371         a = addProp( vtodo, VCPAlarmProp );
00372         addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00373         addPropValue( a, VCRepeatCountProp, "1" );
00374         addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
00375       }
00376     }
00377   }
00378 
00379   QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
00380   if ( !pilotId.isEmpty() ) {
00381     // pilot sync stuff
00382     addPropValue( vtodo, KPilotIdProp, pilotId.toLocal8Bit() );
00383     addPropValue( vtodo, KPilotStatusProp,
00384                   anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() );
00385   }
00386 
00387   return vtodo;
00388 }
00389 
00390 VObject *VCalFormat::eventToVEvent( const Event *anEvent )
00391 {
00392   VObject *vevent;
00393   QString tmpStr;
00394 
00395   vevent = newVObject( VCEventProp );
00396 
00397   // start and end time
00398   tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
00399   addPropValue( vevent, VCDTstartProp, tmpStr.toLocal8Bit() );
00400 
00401   // events that have time associated but take up no time should
00402   // not have both DTSTART and DTEND.
00403   if ( anEvent->dtStart() != anEvent->dtEnd() ) {
00404     tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() );
00405     addPropValue( vevent, VCDTendProp, tmpStr.toLocal8Bit() );
00406   }
00407 
00408   // creation date
00409   tmpStr = kDateTimeToISO( anEvent->created() );
00410   addPropValue( vevent, VCDCreatedProp, tmpStr.toLocal8Bit() );
00411 
00412   // unique id
00413   addPropValue( vevent, VCUniqueStringProp,
00414                 anEvent->uid().toLocal8Bit() );
00415 
00416   // revision
00417   tmpStr.sprintf( "%i", anEvent->revision() );
00418   addPropValue( vevent, VCSequenceProp, tmpStr.toLocal8Bit() );
00419 
00420   // last modification date
00421   tmpStr = kDateTimeToISO( anEvent->lastModified() );
00422   addPropValue( vevent, VCLastModifiedProp, tmpStr.toLocal8Bit() );
00423 
00424   // attendee and organizer stuff
00425   // TODO: What to do with the common name?
00426   if ( !anEvent->organizer().email().isEmpty() ) {
00427     tmpStr = "MAILTO:" + anEvent->organizer().email();
00428     addPropValue( vevent, ICOrganizerProp, tmpStr.toLocal8Bit() );
00429   }
00430 
00431   // TODO: Put this functionality into Attendee class
00432   if ( anEvent->attendeeCount() > 0 ) {
00433     Attendee::List::ConstIterator it;
00434     for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd();
00435           ++it ) {
00436       Attendee *curAttendee = *it;
00437       if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) {
00438         tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
00439       } else if ( curAttendee->name().isEmpty() ) {
00440         tmpStr = "MAILTO: " + curAttendee->email();
00441       } else if ( curAttendee->email().isEmpty() ) {
00442         tmpStr = "MAILTO: " + curAttendee->name();
00443       } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
00444         kDebug() << "warning! this Event has an attendee w/o name or email!";
00445       }
00446       VObject *aProp = addPropValue( vevent, VCAttendeeProp, tmpStr.toLocal8Bit() );
00447       addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
00448       addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
00449     }
00450   }
00451 
00452   // recurrence rule stuff
00453   const Recurrence *recur = anEvent->recurrence();
00454   if ( recur->recurs() ) {
00455     bool validRecur = true;
00456     QString tmpStr2;
00457     switch ( recur->recurrenceType() ) {
00458     case Recurrence::rDaily:
00459       tmpStr.sprintf( "D%i ", recur->frequency() );
00460       break;
00461     case Recurrence::rWeekly:
00462       tmpStr.sprintf( "W%i ", recur->frequency() );
00463       for ( int i = 0; i < 7; ++i ) {
00464         QBitArray days ( recur->days() );
00465         if ( days.testBit(i) ) {
00466           tmpStr += dayFromNum( i );
00467         }
00468       }
00469       break;
00470     case Recurrence::rMonthlyPos:
00471     {
00472       tmpStr.sprintf( "MP%i ", recur->frequency() );
00473       // write out all rMonthPos's
00474       QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
00475       for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin();
00476             posit != tmpPositions.constEnd(); ++posit ) {
00477         int pos = (*posit).pos();
00478         tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) );
00479         if ( pos < 0 ) {
00480           tmpStr2 += "- ";
00481         } else {
00482           tmpStr2 += "+ ";
00483         }
00484         tmpStr += tmpStr2;
00485         tmpStr += dayFromNum( (*posit).day() - 1 );
00486       }
00487       break;
00488     }
00489     case Recurrence::rMonthlyDay:
00490     {
00491       tmpStr.sprintf( "MD%i ", recur->frequency() );
00492       // write out all rMonthDays;
00493       const QList<int> tmpDays = recur->monthDays();
00494       for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin();
00495             tmpDay != tmpDays.constEnd(); ++tmpDay ) {
00496         tmpStr2.sprintf( "%i ", *tmpDay );
00497         tmpStr += tmpStr2;
00498       }
00499       break;
00500     }
00501     case Recurrence::rYearlyMonth:
00502     {
00503       tmpStr.sprintf( "YM%i ", recur->frequency() );
00504       // write out all the months;'
00505       // TODO: Any way to write out the day within the month???
00506       const QList<int> months = recur->yearMonths();
00507       for ( QList<int>::ConstIterator mit = months.constBegin();
00508             mit != months.constEnd(); ++mit ) {
00509         tmpStr2.sprintf( "%i ", *mit );
00510         tmpStr += tmpStr2;
00511       }
00512       break;
00513     }
00514     case Recurrence::rYearlyDay:
00515     {
00516       tmpStr.sprintf( "YD%i ", recur->frequency() );
00517       // write out all the rYearNums;
00518       const QList<int> tmpDays = recur->yearDays();
00519       for ( QList<int>::ConstIterator tmpDay = tmpDays.begin();
00520             tmpDay != tmpDays.end(); ++tmpDay ) {
00521         tmpStr2.sprintf( "%i ", *tmpDay );
00522         tmpStr += tmpStr2;
00523       }
00524       break;
00525     }
00526     default:
00527       // TODO: Write rYearlyPos and arbitrary rules!
00528       kDebug() << "ERROR, it should never get here in eventToVEvent!";
00529       validRecur = false;
00530       break;
00531     } // switch
00532 
00533     if ( recur->duration() > 0 ) {
00534       tmpStr2.sprintf( "#%i", recur->duration() );
00535       tmpStr += tmpStr2;
00536     } else if ( recur->duration() == -1 ) {
00537       tmpStr += "#0"; // defined as repeat forever
00538     } else {
00539       tmpStr += kDateTimeToISO( recur->endDateTime(), false );
00540     }
00541     // Only write out the rrule if we have a valid recurrence (i.e. a known
00542     // type in thee switch above)
00543     if ( validRecur ) {
00544       addPropValue( vevent, VCRRuleProp, tmpStr.toLocal8Bit() );
00545     }
00546 
00547   } // event repeats
00548 
00549   // exceptions to recurrence
00550   DateList dateList = recur->exDates();
00551   DateList::ConstIterator it;
00552   QString tmpStr2;
00553 
00554   for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) {
00555     tmpStr = qDateToISO(*it) + ';';
00556     tmpStr2 += tmpStr;
00557   }
00558   if ( !tmpStr2.isEmpty() ) {
00559     tmpStr2.truncate( tmpStr2.length() - 1 );
00560     addPropValue( vevent, VCExpDateProp, tmpStr2.toLocal8Bit() );
00561   }
00562 
00563   // description
00564   if ( !anEvent->description().isEmpty() ) {
00565     VObject *d = addPropValue( vevent, VCDescriptionProp,
00566                                anEvent->description().toLocal8Bit() );
00567     if ( anEvent->description().indexOf( '\n' ) != -1 ) {
00568       addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
00569     }
00570   }
00571 
00572   // summary
00573   if ( !anEvent->summary().isEmpty() ) {
00574     addPropValue( vevent, VCSummaryProp, anEvent->summary().toLocal8Bit() );
00575   }
00576 
00577   // location
00578   if ( !anEvent->location().isEmpty() ) {
00579     addPropValue( vevent, VCLocationProp, anEvent->location().toLocal8Bit() );
00580   }
00581 
00582   // status
00583 // TODO: define Event status
00584 //  addPropValue( vevent, VCStatusProp, anEvent->statusStr().toLocal8Bit() );
00585 
00586   // secrecy
00587   const char *text = 0;
00588   switch ( anEvent->secrecy() ) {
00589   case Incidence::SecrecyPublic:
00590     text = "PUBLIC";
00591     break;
00592   case Incidence::SecrecyPrivate:
00593     text = "PRIVATE";
00594     break;
00595   case Incidence::SecrecyConfidential:
00596     text = "CONFIDENTIAL";
00597     break;
00598   }
00599   if ( text ) {
00600     addPropValue( vevent, VCClassProp, text );
00601   }
00602 
00603   // categories
00604   QStringList tmpStrList = anEvent->categories();
00605   tmpStr = "";
00606   QString catStr;
00607   for ( QStringList::const_iterator it = tmpStrList.constBegin(); it != tmpStrList.constEnd();
00608         ++it ) {
00609     catStr = *it;
00610     if ( catStr[0] == ' ' ) {
00611       tmpStr += catStr.mid( 1 );
00612     } else {
00613       tmpStr += catStr;
00614     }
00615     // this must be a ';' character as the vCalendar specification requires!
00616     // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
00617     // read in.
00618     tmpStr += ';';
00619   }
00620   if ( !tmpStr.isEmpty() ) {
00621     tmpStr.truncate( tmpStr.length() - 1 );
00622     addPropValue( vevent, VCCategoriesProp, tmpStr.toLocal8Bit() );
00623   }
00624 
00625   // attachments
00626   // TODO: handle binary attachments!
00627   Attachment::List attachments = anEvent->attachments();
00628   Attachment::List::ConstIterator atIt;
00629   for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) {
00630     addPropValue( vevent, VCAttachProp, (*atIt)->uri().toLocal8Bit() );
00631   }
00632 
00633   // resources
00634   tmpStrList = anEvent->resources();
00635   tmpStr = tmpStrList.join( ";" );
00636   if ( !tmpStr.isEmpty() ) {
00637     addPropValue( vevent, VCResourcesProp, tmpStr.toLocal8Bit() );
00638   }
00639 
00640   // alarm stuff
00641   Alarm::List::ConstIterator it2;
00642   for ( it2 = anEvent->alarms().constBegin(); it2 != anEvent->alarms().constEnd(); ++it2 ) {
00643     Alarm *alarm = *it2;
00644     if ( alarm->enabled() ) {
00645       VObject *a = addProp( vevent, VCDAlarmProp );
00646       tmpStr = kDateTimeToISO( alarm->time() );
00647       addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00648       addPropValue( a, VCRepeatCountProp, "1" );
00649       addPropValue( a, VCDisplayStringProp, "beep!" );
00650       if ( alarm->type() == Alarm::Audio ) {
00651         a = addProp( vevent, VCAAlarmProp );
00652         addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00653         addPropValue( a, VCRepeatCountProp, "1" );
00654         addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
00655       }
00656       if ( alarm->type() == Alarm::Procedure ) {
00657         a = addProp( vevent, VCPAlarmProp );
00658         addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() );
00659         addPropValue( a, VCRepeatCountProp, "1" );
00660         addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
00661       }
00662     }
00663   }
00664 
00665   // priority
00666   tmpStr.sprintf( "%i", anEvent->priority() );
00667   addPropValue( vevent, VCPriorityProp, tmpStr.toLocal8Bit() );
00668 
00669   // transparency
00670   tmpStr.sprintf( "%i", anEvent->transparency() );
00671   addPropValue( vevent, VCTranspProp, tmpStr.toLocal8Bit() );
00672 
00673   // related event
00674   if ( anEvent->relatedTo() ) {
00675     addPropValue( vevent, VCRelatedToProp, anEvent->relatedTo()->uid().toLocal8Bit() );
00676   }
00677 
00678   QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
00679   if ( !pilotId.isEmpty() ) {
00680     // pilot sync stuff
00681     addPropValue( vevent, KPilotIdProp, pilotId.toLocal8Bit() );
00682     addPropValue( vevent, KPilotStatusProp,
00683                   anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() );
00684   }
00685 
00686   return vevent;
00687 }
00688 
00689 Todo *VCalFormat::VTodoToEvent( VObject *vtodo )
00690 {
00691   VObject *vo;
00692   VObjectIterator voi;
00693   char *s;
00694 
00695   Todo *anEvent = new Todo;
00696 
00697   // creation date
00698   if ( ( vo = isAPropertyOf( vtodo, VCDCreatedProp ) ) != 0 ) {
00699       anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00700       deleteStr( s );
00701   }
00702 
00703   // unique id
00704   vo = isAPropertyOf( vtodo, VCUniqueStringProp );
00705   // while the UID property is preferred, it is not required.  We'll use the
00706   // default Event UID if none is given.
00707   if ( vo ) {
00708     anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
00709     deleteStr( s );
00710   }
00711 
00712   // last modification date
00713   if ( ( vo = isAPropertyOf( vtodo, VCLastModifiedProp ) ) != 0 ) {
00714     anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00715     deleteStr( s );
00716   } else {
00717     anEvent->setLastModified( KDateTime::currentUtcDateTime() );
00718   }
00719 
00720   // organizer
00721   // if our extension property for the event's ORGANIZER exists, add it.
00722   if ( ( vo = isAPropertyOf( vtodo, ICOrganizerProp ) ) != 0 ) {
00723     anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
00724     deleteStr( s );
00725   } else {
00726     anEvent->setOrganizer( d->mCalendar->owner() );
00727   }
00728 
00729   // attendees.
00730   initPropIterator( &voi, vtodo );
00731   while ( moreIteration( &voi ) ) {
00732     vo = nextVObject( &voi );
00733     if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
00734       Attendee *a;
00735       VObject *vp;
00736       s = fakeCString( vObjectUStringZValue( vo ) );
00737       QString tmpStr = QString::fromLocal8Bit( s );
00738       deleteStr( s );
00739       tmpStr = tmpStr.simplified();
00740       int emailPos1, emailPos2;
00741       if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
00742         // both email address and name
00743         emailPos2 = tmpStr.lastIndexOf( '>' );
00744         a = new Attendee( tmpStr.left( emailPos1 - 1 ),
00745                           tmpStr.mid( emailPos1 + 1,
00746                                       emailPos2 - ( emailPos1 + 1 ) ) );
00747       } else if ( tmpStr.indexOf( '@' ) > 0 ) {
00748         // just an email address
00749         a = new Attendee( 0, tmpStr );
00750       } else {
00751         // just a name
00752         // WTF??? Replacing the spaces of a name and using this as email?
00753         QString email = tmpStr.replace( ' ', '.' );
00754         a = new Attendee( tmpStr, email );
00755       }
00756 
00757       // is there an RSVP property?
00758       if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
00759         a->setRSVP( vObjectStringZValue( vp ) );
00760       }
00761       // is there a status property?
00762       if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
00763         a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
00764       }
00765       // add the attendee
00766       anEvent->addAttendee( a );
00767     }
00768   }
00769 
00770   // description for todo
00771   if ( ( vo = isAPropertyOf( vtodo, VCDescriptionProp ) ) != 0 ) {
00772     s = fakeCString( vObjectUStringZValue( vo ) );
00773     anEvent->setDescription( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
00774     deleteStr( s );
00775   }
00776 
00777   // summary
00778   if ( ( vo = isAPropertyOf( vtodo, VCSummaryProp ) ) ) {
00779     s = fakeCString( vObjectUStringZValue( vo ) );
00780     anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
00781     deleteStr( s );
00782   }
00783 
00784   // location
00785   if ( ( vo = isAPropertyOf( vtodo, VCLocationProp ) ) != 0 ) {
00786     s = fakeCString( vObjectUStringZValue( vo ) );
00787     anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
00788     deleteStr( s );
00789   }
00790 
00791   // completed
00792   // was: status
00793   if ( ( vo = isAPropertyOf( vtodo, VCStatusProp ) ) != 0 ) {
00794     s = fakeCString( vObjectUStringZValue( vo ) );
00795     if ( strcmp( s, "COMPLETED" ) == 0 ) {
00796       anEvent->setCompleted( true );
00797     } else {
00798       anEvent->setCompleted( false );
00799     }
00800     deleteStr( s );
00801   } else {
00802     anEvent->setCompleted( false );
00803   }
00804 
00805   // completion date
00806   if ( ( vo = isAPropertyOf( vtodo, VCCompletedProp ) ) != 0 ) {
00807     anEvent->setCompleted( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00808     deleteStr( s );
00809   }
00810 
00811   // priority
00812   if ( ( vo = isAPropertyOf( vtodo, VCPriorityProp ) ) ) {
00813     anEvent->setPriority( atoi( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00814     deleteStr( s );
00815   }
00816 
00817   // due date
00818   if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) {
00819     anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00820     deleteStr( s );
00821     anEvent->setHasDueDate( true );
00822   } else {
00823     anEvent->setHasDueDate( false );
00824   }
00825 
00826   // start time
00827   if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) {
00828     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00829     deleteStr( s );
00830     anEvent->setHasStartDate( true );
00831   } else {
00832     anEvent->setHasStartDate( false );
00833   }
00834 
00835   // alarm stuff
00836   if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) {
00837     Alarm *alarm = anEvent->newAlarm();
00838     VObject *a;
00839     if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) {
00840       alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
00841       deleteStr( s );
00842     }
00843     alarm->setEnabled( true );
00844     if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) {
00845       if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) {
00846         s = fakeCString( vObjectUStringZValue( a ) );
00847         alarm->setProcedureAlarm( QFile::decodeName( s ) );
00848         deleteStr( s );
00849       }
00850     }
00851     if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) {
00852       if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) {
00853         s = fakeCString( vObjectUStringZValue( a ) );
00854         alarm->setAudioAlarm( QFile::decodeName( s ) );
00855         deleteStr( s );
00856       }
00857     }
00858   }
00859 
00860   // related todo
00861   if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) {
00862     anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
00863     deleteStr( s );
00864     d->mTodosRelate.append( anEvent );
00865   }
00866 
00867   // categories
00868   if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) {
00869     s = fakeCString( vObjectUStringZValue( vo ) );
00870     QString categories = QString::fromLocal8Bit( s );
00871     deleteStr( s );
00872     QStringList tmpStrList = categories.split( ';' );
00873     anEvent->setCategories( tmpStrList );
00874   }
00875 
00876   /* PILOT SYNC STUFF */
00877   if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) {
00878     anEvent->setNonKDECustomProperty(
00879       KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00880     deleteStr( s );
00881     if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) {
00882       anEvent->setNonKDECustomProperty(
00883         KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00884       deleteStr( s );
00885     } else {
00886       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) );
00887     }
00888   }
00889 
00890   return anEvent;
00891 }
00892 
00893 Event *VCalFormat::VEventToEvent( VObject *vevent )
00894 {
00895   VObject *vo;
00896   VObjectIterator voi;
00897   char *s;
00898 
00899   Event *anEvent = new Event;
00900 
00901   // creation date
00902   if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) {
00903       anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00904       deleteStr( s );
00905   }
00906 
00907   // unique id
00908   vo = isAPropertyOf( vevent, VCUniqueStringProp );
00909   // while the UID property is preferred, it is not required.  We'll use the
00910   // default Event UID if none is given.
00911   if ( vo ) {
00912     anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
00913     deleteStr( s );
00914   }
00915 
00916   // revision
00917   // again NSCAL doesn't give us much to work with, so we improvise...
00918   if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) {
00919     anEvent->setRevision( atoi( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00920     deleteStr( s );
00921   } else {
00922     anEvent->setRevision( 0 );
00923   }
00924 
00925   // last modification date
00926   if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) {
00927     anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00928     deleteStr( s );
00929   } else {
00930     anEvent->setLastModified( KDateTime::currentUtcDateTime() );
00931   }
00932 
00933   // organizer
00934   // if our extension property for the event's ORGANIZER exists, add it.
00935   if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) {
00936     // FIXME:  Also use the full name, not just the email address
00937     anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
00938     deleteStr( s );
00939   } else {
00940     anEvent->setOrganizer( d->mCalendar->owner() );
00941   }
00942 
00943   // deal with attendees.
00944   initPropIterator( &voi, vevent );
00945   while ( moreIteration( &voi ) ) {
00946     vo = nextVObject( &voi );
00947     if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
00948       Attendee *a;
00949       VObject *vp;
00950       s = fakeCString( vObjectUStringZValue( vo ) );
00951       QString tmpStr = QString::fromLocal8Bit( s );
00952       deleteStr( s );
00953       tmpStr = tmpStr.simplified();
00954       int emailPos1, emailPos2;
00955       if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
00956         // both email address and name
00957         emailPos2 = tmpStr.lastIndexOf( '>' );
00958         a = new Attendee( tmpStr.left( emailPos1 - 1 ),
00959                           tmpStr.mid( emailPos1 + 1,
00960                                       emailPos2 - ( emailPos1 + 1 ) ) );
00961       } else if ( tmpStr.indexOf( '@' ) > 0 ) {
00962         // just an email address
00963         a = new Attendee( 0, tmpStr );
00964       } else {
00965         // just a name
00966         QString email = tmpStr.replace( ' ', '.' );
00967         a = new Attendee( tmpStr, email );
00968       }
00969 
00970       // is there an RSVP property?
00971       if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
00972         a->setRSVP( vObjectStringZValue( vp ) );
00973       }
00974       // is there a status property?
00975       if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
00976         a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
00977       }
00978       // add the attendee
00979       anEvent->addAttendee( a );
00980     }
00981   }
00982 
00983   // This isn't strictly true.  An event that doesn't have a start time
00984   // or an end time isn't all-day, it has an anchor in time but it doesn't
00985   // "take up" any time.
00986   /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
00987       (isAPropertyOf(vevent, VCDTendProp) == 0)) {
00988     anEvent->setAllDay(true);
00989     } else {
00990     }*/
00991 
00992   anEvent->setAllDay( false );
00993 
00994   // start time
00995   if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) {
00996     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00997     deleteStr( s );
00998     if ( anEvent->dtStart().time().isNull() ) {
00999       anEvent->setAllDay( true );
01000     }
01001   }
01002 
01003   // stop time
01004   if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) {
01005     anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01006     deleteStr( s );
01007     if ( anEvent->dtEnd().time().isNull() ) {
01008       anEvent->setAllDay( true );
01009     }
01010   }
01011 
01012   // at this point, there should be at least a start or end time.
01013   // fix up for events that take up no time but have a time associated
01014   if ( !( vo = isAPropertyOf( vevent, VCDTstartProp ) ) ) {
01015     anEvent->setDtStart( anEvent->dtEnd() );
01016   }
01017   if ( !( vo = isAPropertyOf( vevent, VCDTendProp ) ) ) {
01018     anEvent->setDtEnd( anEvent->dtStart() );
01019   }
01020 
01022 
01023   // repeat stuff
01024   if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) {
01025     QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01026     deleteStr( s );
01027     tmpStr.simplified();
01028     tmpStr = tmpStr.toUpper();
01029 
01030     // first, read the type of the recurrence
01031     int typelen = 1;
01032     uint type = Recurrence::rNone;
01033     if ( tmpStr.left(1) == "D" ) {
01034       type = Recurrence::rDaily;
01035     } else if ( tmpStr.left(1) == "W" ) {
01036       type = Recurrence::rWeekly;
01037     } else {
01038       typelen = 2;
01039       if ( tmpStr.left(2) == "MP" ) {
01040         type = Recurrence::rMonthlyPos;
01041       } else if ( tmpStr.left(2) == "MD" ) {
01042         type = Recurrence::rMonthlyDay;
01043       } else if ( tmpStr.left(2) == "YM" ) {
01044         type = Recurrence::rYearlyMonth;
01045       } else if ( tmpStr.left(2) == "YD" ) {
01046         type = Recurrence::rYearlyDay;
01047       }
01048     }
01049 
01050     if ( type != Recurrence::rNone ) {
01051 
01052       // Immediately after the type is the frequency
01053       int index = tmpStr.indexOf( ' ' );
01054       int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
01055       int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
01056       ++index; // advance to beginning of stuff after freq
01057 
01058       // Read the type-specific settings
01059       switch ( type ) {
01060       case Recurrence::rDaily:
01061         anEvent->recurrence()->setDaily(rFreq);
01062         break;
01063 
01064       case Recurrence::rWeekly:
01065       {
01066         QBitArray qba(7);
01067         QString dayStr;
01068         if ( index == last ) {
01069           // e.g. W1 #0
01070           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01071         } else {
01072           // e.g. W1 SU #0
01073           while ( index < last ) {
01074             dayStr = tmpStr.mid( index, 3 );
01075             int dayNum = numFromDay( dayStr );
01076             qba.setBit( dayNum );
01077             index += 3; // advance to next day, or possibly "#"
01078           }
01079         }
01080         anEvent->recurrence()->setWeekly( rFreq, qba );
01081         break;
01082       }
01083 
01084       case Recurrence::rMonthlyPos:
01085       {
01086         anEvent->recurrence()->setMonthly( rFreq );
01087 
01088         QBitArray qba(7);
01089         short tmpPos;
01090         if ( index == last ) {
01091           // e.g. MP1 #0
01092           tmpPos = anEvent->dtStart().date().day() / 7 + 1;
01093           if ( tmpPos == 5 ) {
01094             tmpPos = -1;
01095           }
01096           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01097           anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01098         } else {
01099           // e.g. MP1 1+ SU #0
01100           while ( index < last ) {
01101             tmpPos = tmpStr.mid( index, 1 ).toShort();
01102             index += 1;
01103             if ( tmpStr.mid( index, 1 ) == "-" ) {
01104               // convert tmpPos to negative
01105               tmpPos = 0 - tmpPos;
01106             }
01107             index += 2; // advance to day(s)
01108             while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
01109               int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
01110               qba.setBit( dayNum );
01111               index += 3; // advance to next day, or possibly pos or "#"
01112             }
01113             anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01114             qba.detach();
01115             qba.fill( false ); // clear out
01116           } // while != "#"
01117         }
01118         break;
01119       }
01120 
01121       case Recurrence::rMonthlyDay:
01122         anEvent->recurrence()->setMonthly( rFreq );
01123         if( index == last ) {
01124           // e.g. MD1 #0
01125           short tmpDay = anEvent->dtStart().date().day();
01126           anEvent->recurrence()->addMonthlyDate( tmpDay );
01127         } else {
01128           // e.g. MD1 3 #0
01129           while ( index < last ) {
01130             int index2 = tmpStr.indexOf( ' ', index );
01131             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01132             index = index2 - 1;
01133             if ( tmpStr.mid( index, 1 ) == "-" ) {
01134               tmpDay = 0 - tmpDay;
01135             }
01136             index += 2; // advance the index;
01137             anEvent->recurrence()->addMonthlyDate( tmpDay );
01138           } // while != #
01139         }
01140         break;
01141 
01142       case Recurrence::rYearlyMonth:
01143         anEvent->recurrence()->setYearly( rFreq );
01144 
01145         if ( index == last ) {
01146           // e.g. YM1 #0
01147           short tmpMonth = anEvent->dtStart().date().month();
01148           anEvent->recurrence()->addYearlyMonth( tmpMonth );
01149         } else {
01150           // e.g. YM1 3 #0
01151           while ( index < last ) {
01152             int index2 = tmpStr.indexOf( ' ', index );
01153             short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
01154             index = index2 + 1;
01155             anEvent->recurrence()->addYearlyMonth( tmpMonth );
01156           } // while != #
01157         }
01158         break;
01159 
01160       case Recurrence::rYearlyDay:
01161         anEvent->recurrence()->setYearly( rFreq );
01162 
01163         if ( index == last ) {
01164           // e.g. YD1 #0
01165           short tmpDay = anEvent->dtStart().date().dayOfYear();
01166           anEvent->recurrence()->addYearlyDay( tmpDay );
01167         } else {
01168           // e.g. YD1 123 #0
01169           while ( index < last ) {
01170             int index2 = tmpStr.indexOf( ' ', index );
01171             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01172             index = index2 + 1;
01173             anEvent->recurrence()->addYearlyDay( tmpDay );
01174           } // while != #
01175         }
01176         break;
01177 
01178       default:
01179         break;
01180       }
01181 
01182       // find the last field, which is either the duration or the end date
01183       index = last;
01184       if ( tmpStr.mid( index, 1 ) == "#" ) {
01185         // Nr of occurrences
01186         index++;
01187         int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
01188         if ( rDuration > 0 ) {
01189           anEvent->recurrence()->setDuration( rDuration );
01190         }
01191       } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
01192         KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
01193         rEndDate.setDateOnly( true );
01194         anEvent->recurrence()->setEndDateTime( rEndDate );
01195       }
01196 // anEvent->recurrence()->dump();
01197 
01198     } else {
01199       kDebug() << "we don't understand this type of recurrence!";
01200     } // if known recurrence type
01201   } // repeats
01202 
01203   // recurrence exceptions
01204   if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) {
01205     s = fakeCString( vObjectUStringZValue( vo ) );
01206     QStringList exDates = QString::fromLocal8Bit( s ).split( ',' );
01207     QStringList::ConstIterator it;
01208     for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
01209       anEvent->recurrence()->addExDate( ISOToQDate(*it) );
01210     }
01211     deleteStr( s );
01212   }
01213 
01214   // summary
01215   if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) {
01216     s = fakeCString( vObjectUStringZValue( vo ) );
01217     anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
01218     deleteStr( s );
01219   }
01220 
01221   // description
01222   if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) {
01223     s = fakeCString( vObjectUStringZValue( vo ) );
01224     bool isRich = Qt::mightBeRichText( s );
01225     if ( !anEvent->description().isEmpty() ) {
01226       anEvent->setDescription(
01227         anEvent->description() + '\n' + QString::fromLocal8Bit( s ), isRich );
01228     } else {
01229       anEvent->setDescription( QString::fromLocal8Bit( s ), isRich );
01230     }
01231     deleteStr( s );
01232   }
01233 
01234   // location
01235   if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) {
01236     s = fakeCString( vObjectUStringZValue( vo ) );
01237     anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
01238     deleteStr( s );
01239   }
01240 
01241   // some stupid vCal exporters ignore the standard and use Description
01242   // instead of Summary for the default field.  Correct for this.
01243   if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) {
01244     QString tmpStr = anEvent->description().simplified();
01245     anEvent->setDescription( "" );
01246     anEvent->setSummary( tmpStr );
01247   }
01248 
01249 #if 0
01250   // status
01251   if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) {
01252     QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) );
01253     deleteStr( s );
01254 // TODO: Define Event status
01255 //    anEvent->setStatus( tmpStr );
01256   } else {
01257 //    anEvent->setStatus( "NEEDS ACTION" );
01258   }
01259 #endif
01260 
01261   // secrecy
01262   Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
01263   if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) {
01264     s = fakeCString( vObjectUStringZValue( vo ) );
01265     if ( strcmp( s, "PRIVATE" ) == 0 ) {
01266       secrecy = Incidence::SecrecyPrivate;
01267     } else if ( strcmp( s, "CONFIDENTIAL" ) == 0 ) {
01268       secrecy = Incidence::SecrecyConfidential;
01269     }
01270     deleteStr( s );
01271   }
01272   anEvent->setSecrecy( secrecy );
01273 
01274   // categories
01275   if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) {
01276     s = fakeCString( vObjectUStringZValue( vo ) );
01277     QString categories = QString::fromLocal8Bit( s );
01278     deleteStr( s );
01279     QStringList tmpStrList = categories.split( ',' );
01280     anEvent->setCategories( tmpStrList );
01281   }
01282 
01283   // attachments
01284   initPropIterator( &voi, vevent );
01285   while ( moreIteration( &voi ) ) {
01286     vo = nextVObject( &voi );
01287     if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) {
01288       s = fakeCString( vObjectUStringZValue( vo ) );
01289       anEvent->addAttachment( new Attachment( QString( s ) ) );
01290       deleteStr( s );
01291     }
01292   }
01293 
01294   // resources
01295   if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) {
01296     QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01297     deleteStr( s );
01298     QStringList tmpStrList = resources.split( ';' );
01299     anEvent->setResources( tmpStrList );
01300   }
01301 
01302   // alarm stuff
01303   if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) {
01304     Alarm *alarm = anEvent->newAlarm();
01305     VObject *a;
01306     if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) {
01307       alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01308       deleteStr( s );
01309     }
01310     alarm->setEnabled( true );
01311     if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) {
01312       if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) {
01313         s = fakeCString( vObjectUStringZValue( a ) );
01314         alarm->setProcedureAlarm( QFile::decodeName( s ) );
01315         deleteStr( s );
01316       }
01317     }
01318     if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) {
01319       if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) {
01320         s = fakeCString( vObjectUStringZValue( a ) );
01321         alarm->setAudioAlarm( QFile::decodeName( s ) );
01322         deleteStr( s );
01323       }
01324     }
01325   }
01326 
01327   // priority
01328   if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) {
01329     anEvent->setPriority( atoi( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01330     deleteStr( s );
01331   }
01332 
01333   // transparency
01334   if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) {
01335     int i = atoi( s = fakeCString( vObjectUStringZValue( vo ) ) );
01336     anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
01337     deleteStr( s );
01338   }
01339 
01340   // related event
01341   if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) {
01342     anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
01343     deleteStr( s );
01344     d->mEventsRelate.append( anEvent );
01345   }
01346 
01347   /* PILOT SYNC STUFF */
01348   if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) {
01349     anEvent->setNonKDECustomProperty(
01350       KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01351     deleteStr( s );
01352     if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) {
01353       anEvent->setNonKDECustomProperty(
01354         KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01355       deleteStr( s );
01356     } else {
01357       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) );
01358     }
01359   }
01360 
01361   return anEvent;
01362 }
01363 
01364 QString VCalFormat::qDateToISO( const QDate &qd )
01365 {
01366   QString tmpStr;
01367 
01368   if ( !qd.isValid() ) {
01369     return QString();
01370   }
01371 
01372   tmpStr.sprintf( "%.2d%.2d%.2d", qd.year(), qd.month(), qd.day() );
01373   return tmpStr;
01374 
01375 }
01376 
01377 QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu )
01378 {
01379   QString tmpStr;
01380 
01381   if ( !dt.isValid() ) {
01382     return QString();
01383   }
01384 
01385   QDateTime tmpDT;
01386   if ( zulu ) {
01387     tmpDT = dt.toUtc().dateTime();
01388   } else {
01389     tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime();
01390   }
01391   tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
01392                   tmpDT.date().year(), tmpDT.date().month(),
01393                   tmpDT.date().day(), tmpDT.time().hour(),
01394                   tmpDT.time().minute(), tmpDT.time().second() );
01395   if ( zulu ) {
01396     tmpStr += 'Z';
01397   }
01398   return tmpStr;
01399 }
01400 
01401 KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr )
01402 {
01403   QDate tmpDate;
01404   QTime tmpTime;
01405   QString tmpStr;
01406   int year, month, day, hour, minute, second;
01407 
01408   tmpStr = dtStr;
01409   year = tmpStr.left( 4 ).toInt();
01410   month = tmpStr.mid( 4, 2 ).toInt();
01411   day = tmpStr.mid( 6, 2 ).toInt();
01412   hour = tmpStr.mid( 9, 2 ).toInt();
01413   minute = tmpStr.mid( 11, 2 ).toInt();
01414   second = tmpStr.mid( 13, 2 ).toInt();
01415   tmpDate.setYMD( year, month, day );
01416   tmpTime.setHMS( hour, minute, second );
01417 
01418   if ( tmpDate.isValid() && tmpTime.isValid() ) {
01419     // correct for GMT if string is in Zulu format
01420     if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) {
01421       return KDateTime( tmpDate, tmpTime, KDateTime::UTC );
01422     } else {
01423       return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() );
01424     }
01425   } else {
01426     return KDateTime();
01427   }
01428 }
01429 
01430 QDate VCalFormat::ISOToQDate( const QString &dateStr )
01431 {
01432   int year, month, day;
01433 
01434   year = dateStr.left( 4 ).toInt();
01435   month = dateStr.mid( 4, 2 ).toInt();
01436   day = dateStr.mid( 6, 2 ).toInt();
01437 
01438   return QDate( year, month, day );
01439 }
01440 
01441 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
01442 // and break it down from it's tree-like format into the dictionary format
01443 // that is used internally in the VCalFormat.
01444 void VCalFormat::populate( VObject *vcal )
01445 {
01446   // this function will populate the caldict dictionary and other event
01447   // lists. It turns vevents into Events and then inserts them.
01448 
01449   VObjectIterator i;
01450   VObject *curVO, *curVOProp;
01451   Event *anEvent;
01452 
01453   if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) {
01454     char *methodType = 0;
01455     methodType = fakeCString( vObjectUStringZValue( curVO ) );
01456     kDebug() << "This calendar is an iTIP transaction of type '"
01457              << methodType << "'";
01458     deleteStr( methodType );
01459   }
01460 
01461   // warn the user that we might have trouble reading non-known calendar.
01462   if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) {
01463     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01464     if ( strcmp( productId().toLocal8Bit(), s ) != 0 ) {
01465       kDebug() << "This vCalendar file was not created by KOrganizer or"
01466                << "any other product we support. Loading anyway...";
01467     }
01468     setLoadedProductId( s );
01469     deleteStr( s );
01470   }
01471 
01472   // warn the user we might have trouble reading this unknown version.
01473   if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) {
01474     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01475     if ( strcmp( _VCAL_VERSION, s ) != 0 ) {
01476       kDebug() << "This vCalendar file has version" << s
01477                << "We only support" << _VCAL_VERSION;
01478     }
01479     deleteStr( s );
01480   }
01481 
01482 #if 0
01483   // set the time zone (this is a property of the view, so just discard!)
01484   if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) {
01485     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01486     d->mCalendar->setTimeZone( s );
01487     deleteStr( s );
01488   }
01489 #endif
01490 
01491   // Store all events with a relatedTo property in a list for post-processing
01492   d->mEventsRelate.clear();
01493   d->mTodosRelate.clear();
01494 
01495   initPropIterator( &i, vcal );
01496 
01497   // go through all the vobjects in the vcal
01498   while ( moreIteration( &i ) ) {
01499     curVO = nextVObject( &i );
01500 
01501     /************************************************************************/
01502 
01503     // now, check to see that the object is an event or todo.
01504     if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) {
01505 
01506       if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) {
01507         char *s;
01508         s = fakeCString( vObjectUStringZValue( curVOProp ) );
01509         // check to see if event was deleted by the kpilot conduit
01510         if ( atoi( s ) == SYNCDEL ) {
01511           deleteStr( s );
01512           kDebug() << "skipping pilot-deleted event";
01513           goto SKIP;
01514         }
01515         deleteStr( s );
01516       }
01517 
01518       // this code checks to see if we are trying to read in an event
01519       // that we already find to be in the calendar.  If we find this
01520       // to be the case, we skip the event.
01521       if ( ( curVOProp = isAPropertyOf( curVO, VCUniqueStringProp ) ) != 0 ) {
01522         char *s = fakeCString( vObjectUStringZValue( curVOProp ) );
01523         QString tmpStr( s );
01524         deleteStr( s );
01525 
01526         if ( d->mCalendar->incidence( tmpStr ) ) {
01527           goto SKIP;
01528         }
01529       }
01530 
01531       if ( ( !( curVOProp = isAPropertyOf( curVO, VCDTstartProp ) ) ) &&
01532            ( !( curVOProp = isAPropertyOf( curVO, VCDTendProp ) ) ) ) {
01533         kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping...";
01534         goto SKIP;
01535       }
01536 
01537       anEvent = VEventToEvent( curVO );
01538       // we now use addEvent instead of insertEvent so that the
01539       // signal/slot get connected.
01540       if ( anEvent ) {
01541         if ( anEvent->dtStart().isValid() && anEvent->dtEnd().isValid() ) {
01542           d->mCalendar->addEvent( anEvent );
01543         }
01544       } else {
01545         // some sort of error must have occurred while in translation.
01546         goto SKIP;
01547       }
01548     } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) {
01549       Todo *aTodo = VTodoToEvent( curVO );
01550 
01551       Todo *old = d->mCalendar->todo( aTodo->uid() );
01552       if ( old ) {
01553         d->mCalendar->deleteTodo( old );
01554         d->mTodosRelate.removeAll( old );
01555       }
01556 
01557       d->mCalendar->addTodo( aTodo );
01558     } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) ||
01559                 ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) ||
01560                 ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) {
01561       // do nothing, we know these properties and we want to skip them.
01562       // we have either already processed them or are ignoring them.
01563       ;
01564     } else {
01565       kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
01566     }
01567   SKIP:
01568     ;
01569   } // while
01570 
01571   // Post-Process list of events with relations, put Event objects in relation
01572   Event::List::ConstIterator eIt;
01573   for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) {
01574     (*eIt)->setRelatedTo( d->mCalendar->incidence( (*eIt)->relatedToUid() ) );
01575   }
01576   Todo::List::ConstIterator tIt;
01577   for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) {
01578     (*tIt)->setRelatedTo( d->mCalendar->incidence( (*tIt)->relatedToUid() ) );
01579    }
01580 }
01581 
01582 const char *VCalFormat::dayFromNum( int day )
01583 {
01584   const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
01585 
01586   return days[day];
01587 }
01588 
01589 int VCalFormat::numFromDay( const QString &day )
01590 {
01591   if ( day == "MO " ) {
01592     return 0;
01593   }
01594   if ( day == "TU " ) {
01595     return 1;
01596   }
01597   if ( day == "WE " ) {
01598     return 2;
01599   }
01600   if ( day == "TH " ) {
01601     return 3;
01602   }
01603   if ( day == "FR " ) {
01604     return 4;
01605   }
01606   if ( day == "SA " ) {
01607     return 5;
01608   }
01609   if ( day == "SU " ) {
01610     return 6;
01611   }
01612 
01613   return -1; // something bad happened. :)
01614 }
01615 
01616 Attendee::PartStat VCalFormat::readStatus( const char *s ) const
01617 {
01618   QString statStr = s;
01619   statStr = statStr.toUpper();
01620   Attendee::PartStat status;
01621 
01622   if ( statStr == "X-ACTION" ) {
01623     status = Attendee::NeedsAction;
01624   } else if ( statStr == "NEEDS ACTION" ) {
01625     status = Attendee::NeedsAction;
01626   } else if ( statStr == "ACCEPTED" ) {
01627     status = Attendee::Accepted;
01628   } else if ( statStr == "SENT" ) {
01629     status = Attendee::NeedsAction;
01630   } else if ( statStr == "TENTATIVE" ) {
01631     status = Attendee::Tentative;
01632   } else if ( statStr == "CONFIRMED" ) {
01633     status = Attendee::Accepted;
01634   } else if ( statStr == "DECLINED" ) {
01635     status = Attendee::Declined;
01636   } else if ( statStr == "COMPLETED" ) {
01637     status = Attendee::Completed;
01638   } else if ( statStr == "DELEGATED" ) {
01639     status = Attendee::Delegated;
01640   } else {
01641     kDebug() << "error setting attendee mStatus, unknown mStatus!";
01642     status = Attendee::NeedsAction;
01643   }
01644 
01645   return status;
01646 }
01647 
01648 QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const
01649 {
01650   switch( status ) {
01651   default:
01652   case Attendee::NeedsAction:
01653     return "NEEDS ACTION";
01654     break;
01655   case Attendee::Accepted:
01656     return "ACCEPTED";
01657     break;
01658   case Attendee::Declined:
01659     return "DECLINED";
01660     break;
01661   case Attendee::Tentative:
01662     return "TENTATIVE";
01663     break;
01664   case Attendee::Delegated:
01665     return "DELEGATED";
01666     break;
01667   case Attendee::Completed:
01668     return "COMPLETED";
01669     break;
01670   case Attendee::InProcess:
01671     return "NEEDS ACTION";
01672     break;
01673   }
01674 }

KCal Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal