• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • 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 &string )
00154 {
00155   return fromRawString( calendar, string.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 *dObject = addPropValue( vtodo, VCDescriptionProp,
00297                                anEvent->description().toLocal8Bit() );
00298     if ( anEvent->description().indexOf( '\n' ) != -1 ) {
00299       addPropValue( dObject, 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 *dObject = addPropValue( vevent, VCDescriptionProp,
00566                                anEvent->description().toLocal8Bit() );
00567     if ( anEvent->description().indexOf( '\n' ) != -1 ) {
00568       addPropValue( dObject, 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 ( s && 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     s = fakeCString( vObjectUStringZValue( vo ) );
00814     if ( s ) {
00815       anEvent->setPriority( atoi( s ) );
00816       deleteStr( s );
00817     }
00818   }
00819 
00820   // due date
00821   if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) {
00822     anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00823     deleteStr( s );
00824     anEvent->setHasDueDate( true );
00825   } else {
00826     anEvent->setHasDueDate( false );
00827   }
00828 
00829   // start time
00830   if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) {
00831     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00832     deleteStr( s );
00833     anEvent->setHasStartDate( true );
00834   } else {
00835     anEvent->setHasStartDate( false );
00836   }
00837 
00838   // alarm stuff
00839   if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) {
00840     Alarm *alarm = anEvent->newAlarm();
00841     VObject *a;
00842     if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) {
00843       alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
00844       deleteStr( s );
00845     }
00846     alarm->setEnabled( true );
00847     if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) {
00848       if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) {
00849         s = fakeCString( vObjectUStringZValue( a ) );
00850         alarm->setProcedureAlarm( QFile::decodeName( s ) );
00851         deleteStr( s );
00852       }
00853     }
00854     if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) {
00855       if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) {
00856         s = fakeCString( vObjectUStringZValue( a ) );
00857         alarm->setAudioAlarm( QFile::decodeName( s ) );
00858         deleteStr( s );
00859       }
00860     }
00861   }
00862 
00863   // related todo
00864   if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) {
00865     anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
00866     deleteStr( s );
00867     d->mTodosRelate.append( anEvent );
00868   }
00869 
00870   // categories
00871   if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) {
00872     s = fakeCString( vObjectUStringZValue( vo ) );
00873     QString categories = QString::fromLocal8Bit( s );
00874     deleteStr( s );
00875     QStringList tmpStrList = categories.split( ';' );
00876     anEvent->setCategories( tmpStrList );
00877   }
00878 
00879   /* PILOT SYNC STUFF */
00880   if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) {
00881     anEvent->setNonKDECustomProperty(
00882       KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00883     deleteStr( s );
00884     if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) {
00885       anEvent->setNonKDECustomProperty(
00886         KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00887       deleteStr( s );
00888     } else {
00889       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) );
00890     }
00891   }
00892 
00893   return anEvent;
00894 }
00895 
00896 Event *VCalFormat::VEventToEvent( VObject *vevent )
00897 {
00898   VObject *vo;
00899   VObjectIterator voi;
00900   char *s;
00901 
00902   Event *anEvent = new Event;
00903 
00904   // creation date
00905   if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) {
00906       anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00907       deleteStr( s );
00908   }
00909 
00910   // unique id
00911   vo = isAPropertyOf( vevent, VCUniqueStringProp );
00912   // while the UID property is preferred, it is not required.  We'll use the
00913   // default Event UID if none is given.
00914   if ( vo ) {
00915     anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
00916     deleteStr( s );
00917   }
00918 
00919   // revision
00920   // again NSCAL doesn't give us much to work with, so we improvise...
00921   anEvent->setRevision( 0 );
00922   if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) {
00923     s = fakeCString( vObjectUStringZValue( vo ) );
00924     if ( s ) {
00925       anEvent->setRevision( atoi( s ) );
00926       deleteStr( s );
00927     }
00928   }
00929 
00930   // last modification date
00931   if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) {
00932     anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
00933     deleteStr( s );
00934   } else {
00935     anEvent->setLastModified( KDateTime::currentUtcDateTime() );
00936   }
00937 
00938   // organizer
00939   // if our extension property for the event's ORGANIZER exists, add it.
00940   if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) {
00941     // FIXME:  Also use the full name, not just the email address
00942     anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
00943     deleteStr( s );
00944   } else {
00945     anEvent->setOrganizer( d->mCalendar->owner() );
00946   }
00947 
00948   // deal with attendees.
00949   initPropIterator( &voi, vevent );
00950   while ( moreIteration( &voi ) ) {
00951     vo = nextVObject( &voi );
00952     if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
00953       Attendee *a;
00954       VObject *vp;
00955       s = fakeCString( vObjectUStringZValue( vo ) );
00956       QString tmpStr = QString::fromLocal8Bit( s );
00957       deleteStr( s );
00958       tmpStr = tmpStr.simplified();
00959       int emailPos1, emailPos2;
00960       if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
00961         // both email address and name
00962         emailPos2 = tmpStr.lastIndexOf( '>' );
00963         a = new Attendee( tmpStr.left( emailPos1 - 1 ),
00964                           tmpStr.mid( emailPos1 + 1,
00965                                       emailPos2 - ( emailPos1 + 1 ) ) );
00966       } else if ( tmpStr.indexOf( '@' ) > 0 ) {
00967         // just an email address
00968         a = new Attendee( 0, tmpStr );
00969       } else {
00970         // just a name
00971         QString email = tmpStr.replace( ' ', '.' );
00972         a = new Attendee( tmpStr, email );
00973       }
00974 
00975       // is there an RSVP property?
00976       if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
00977         a->setRSVP( vObjectStringZValue( vp ) );
00978       }
00979       // is there a status property?
00980       if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
00981         a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
00982       }
00983       // add the attendee
00984       anEvent->addAttendee( a );
00985     }
00986   }
00987 
00988   // This isn't strictly true.  An event that doesn't have a start time
00989   // or an end time isn't all-day, it has an anchor in time but it doesn't
00990   // "take up" any time.
00991   /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
00992       (isAPropertyOf(vevent, VCDTendProp) == 0)) {
00993     anEvent->setAllDay(true);
00994     } else {
00995     }*/
00996 
00997   anEvent->setAllDay( false );
00998 
00999   // start time
01000   if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) {
01001     anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01002     deleteStr( s );
01003     if ( anEvent->dtStart().time().isNull() ) {
01004       anEvent->setAllDay( true );
01005     }
01006   }
01007 
01008   // stop time
01009   if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) {
01010     anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01011     deleteStr( s );
01012     if ( anEvent->dtEnd().time().isNull() ) {
01013       anEvent->setAllDay( true );
01014     }
01015   }
01016 
01017   // at this point, there should be at least a start or end time.
01018   // fix up for events that take up no time but have a time associated
01019   if ( !( vo = isAPropertyOf( vevent, VCDTstartProp ) ) ) {
01020     anEvent->setDtStart( anEvent->dtEnd() );
01021   }
01022   if ( !( vo = isAPropertyOf( vevent, VCDTendProp ) ) ) {
01023     anEvent->setDtEnd( anEvent->dtStart() );
01024   }
01025 
01027 
01028   // repeat stuff
01029   if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) {
01030     QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01031     deleteStr( s );
01032     tmpStr.simplified();
01033     tmpStr = tmpStr.toUpper();
01034 
01035     // first, read the type of the recurrence
01036     int typelen = 1;
01037     uint type = Recurrence::rNone;
01038     if ( tmpStr.left(1) == "D" ) {
01039       type = Recurrence::rDaily;
01040     } else if ( tmpStr.left(1) == "W" ) {
01041       type = Recurrence::rWeekly;
01042     } else {
01043       typelen = 2;
01044       if ( tmpStr.left(2) == "MP" ) {
01045         type = Recurrence::rMonthlyPos;
01046       } else if ( tmpStr.left(2) == "MD" ) {
01047         type = Recurrence::rMonthlyDay;
01048       } else if ( tmpStr.left(2) == "YM" ) {
01049         type = Recurrence::rYearlyMonth;
01050       } else if ( tmpStr.left(2) == "YD" ) {
01051         type = Recurrence::rYearlyDay;
01052       }
01053     }
01054 
01055     if ( type != Recurrence::rNone ) {
01056 
01057       // Immediately after the type is the frequency
01058       int index = tmpStr.indexOf( ' ' );
01059       int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
01060       int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
01061       ++index; // advance to beginning of stuff after freq
01062 
01063       // Read the type-specific settings
01064       switch ( type ) {
01065       case Recurrence::rDaily:
01066         anEvent->recurrence()->setDaily(rFreq);
01067         break;
01068 
01069       case Recurrence::rWeekly:
01070       {
01071         QBitArray qba(7);
01072         QString dayStr;
01073         if ( index == last ) {
01074           // e.g. W1 #0
01075           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01076         } else {
01077           // e.g. W1 SU #0
01078           while ( index < last ) {
01079             dayStr = tmpStr.mid( index, 3 );
01080             int dayNum = numFromDay( dayStr );
01081             if ( dayNum >= 0 ) {
01082               qba.setBit( dayNum );
01083             }
01084             index += 3; // advance to next day, or possibly "#"
01085           }
01086         }
01087         anEvent->recurrence()->setWeekly( rFreq, qba );
01088         break;
01089       }
01090 
01091       case Recurrence::rMonthlyPos:
01092       {
01093         anEvent->recurrence()->setMonthly( rFreq );
01094 
01095         QBitArray qba(7);
01096         short tmpPos;
01097         if ( index == last ) {
01098           // e.g. MP1 #0
01099           tmpPos = anEvent->dtStart().date().day() / 7 + 1;
01100           if ( tmpPos == 5 ) {
01101             tmpPos = -1;
01102           }
01103           qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
01104           anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01105         } else {
01106           // e.g. MP1 1+ SU #0
01107           while ( index < last ) {
01108             tmpPos = tmpStr.mid( index, 1 ).toShort();
01109             index += 1;
01110             if ( tmpStr.mid( index, 1 ) == "-" ) {
01111               // convert tmpPos to negative
01112               tmpPos = 0 - tmpPos;
01113             }
01114             index += 2; // advance to day(s)
01115             while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
01116               int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
01117               qba.setBit( dayNum );
01118               index += 3; // advance to next day, or possibly pos or "#"
01119             }
01120             anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
01121             qba.detach();
01122             qba.fill( false ); // clear out
01123           } // while != "#"
01124         }
01125         break;
01126       }
01127 
01128       case Recurrence::rMonthlyDay:
01129         anEvent->recurrence()->setMonthly( rFreq );
01130         if( index == last ) {
01131           // e.g. MD1 #0
01132           short tmpDay = anEvent->dtStart().date().day();
01133           anEvent->recurrence()->addMonthlyDate( tmpDay );
01134         } else {
01135           // e.g. MD1 3 #0
01136           while ( index < last ) {
01137             int index2 = tmpStr.indexOf( ' ', index );
01138             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01139             index = index2 - 1;
01140             if ( tmpStr.mid( index, 1 ) == "-" ) {
01141               tmpDay = 0 - tmpDay;
01142             }
01143             index += 2; // advance the index;
01144             anEvent->recurrence()->addMonthlyDate( tmpDay );
01145           } // while != #
01146         }
01147         break;
01148 
01149       case Recurrence::rYearlyMonth:
01150         anEvent->recurrence()->setYearly( rFreq );
01151 
01152         if ( index == last ) {
01153           // e.g. YM1 #0
01154           short tmpMonth = anEvent->dtStart().date().month();
01155           anEvent->recurrence()->addYearlyMonth( tmpMonth );
01156         } else {
01157           // e.g. YM1 3 #0
01158           while ( index < last ) {
01159             int index2 = tmpStr.indexOf( ' ', index );
01160             short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
01161             index = index2 + 1;
01162             anEvent->recurrence()->addYearlyMonth( tmpMonth );
01163           } // while != #
01164         }
01165         break;
01166 
01167       case Recurrence::rYearlyDay:
01168         anEvent->recurrence()->setYearly( rFreq );
01169 
01170         if ( index == last ) {
01171           // e.g. YD1 #0
01172           short tmpDay = anEvent->dtStart().date().dayOfYear();
01173           anEvent->recurrence()->addYearlyDay( tmpDay );
01174         } else {
01175           // e.g. YD1 123 #0
01176           while ( index < last ) {
01177             int index2 = tmpStr.indexOf( ' ', index );
01178             short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
01179             index = index2 + 1;
01180             anEvent->recurrence()->addYearlyDay( tmpDay );
01181           } // while != #
01182         }
01183         break;
01184 
01185       default:
01186         break;
01187       }
01188 
01189       // find the last field, which is either the duration or the end date
01190       index = last;
01191       if ( tmpStr.mid( index, 1 ) == "#" ) {
01192         // Nr of occurrences
01193         index++;
01194         int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
01195         if ( rDuration > 0 ) {
01196           anEvent->recurrence()->setDuration( rDuration );
01197         }
01198       } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
01199         KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
01200         rEndDate.setDateOnly( true );
01201         anEvent->recurrence()->setEndDateTime( rEndDate );
01202       }
01203 // anEvent->recurrence()->dump();
01204 
01205     } else {
01206       kDebug() << "we don't understand this type of recurrence!";
01207     } // if known recurrence type
01208   } // repeats
01209 
01210   // recurrence exceptions
01211   if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) {
01212     s = fakeCString( vObjectUStringZValue( vo ) );
01213     QStringList exDates = QString::fromLocal8Bit( s ).split( ',' );
01214     QStringList::ConstIterator it;
01215     for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
01216       anEvent->recurrence()->addExDate( ISOToQDate(*it) );
01217     }
01218     deleteStr( s );
01219   }
01220 
01221   // summary
01222   if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) {
01223     s = fakeCString( vObjectUStringZValue( vo ) );
01224     anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
01225     deleteStr( s );
01226   }
01227 
01228   // description
01229   if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) {
01230     s = fakeCString( vObjectUStringZValue( vo ) );
01231     bool isRich = Qt::mightBeRichText( s );
01232     if ( !anEvent->description().isEmpty() ) {
01233       anEvent->setDescription(
01234         anEvent->description() + '\n' + QString::fromLocal8Bit( s ), isRich );
01235     } else {
01236       anEvent->setDescription( QString::fromLocal8Bit( s ), isRich );
01237     }
01238     deleteStr( s );
01239   }
01240 
01241   // location
01242   if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) {
01243     s = fakeCString( vObjectUStringZValue( vo ) );
01244     anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) );
01245     deleteStr( s );
01246   }
01247 
01248   // some stupid vCal exporters ignore the standard and use Description
01249   // instead of Summary for the default field.  Correct for this.
01250   if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) {
01251     QString tmpStr = anEvent->description().simplified();
01252     anEvent->setDescription( "" );
01253     anEvent->setSummary( tmpStr );
01254   }
01255 
01256 #if 0
01257   // status
01258   if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) {
01259     QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) );
01260     deleteStr( s );
01261 // TODO: Define Event status
01262 //    anEvent->setStatus( tmpStr );
01263   } else {
01264 //    anEvent->setStatus( "NEEDS ACTION" );
01265   }
01266 #endif
01267 
01268   // secrecy
01269   Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
01270   if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) {
01271     s = fakeCString( vObjectUStringZValue( vo ) );
01272     if ( s && strcmp( s, "PRIVATE" ) == 0 ) {
01273       secrecy = Incidence::SecrecyPrivate;
01274     } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) {
01275       secrecy = Incidence::SecrecyConfidential;
01276     }
01277     deleteStr( s );
01278   }
01279   anEvent->setSecrecy( secrecy );
01280 
01281   // categories
01282   if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) {
01283     s = fakeCString( vObjectUStringZValue( vo ) );
01284     QString categories = QString::fromLocal8Bit( s );
01285     deleteStr( s );
01286     QStringList tmpStrList = categories.split( ',' );
01287     anEvent->setCategories( tmpStrList );
01288   }
01289 
01290   // attachments
01291   initPropIterator( &voi, vevent );
01292   while ( moreIteration( &voi ) ) {
01293     vo = nextVObject( &voi );
01294     if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) {
01295       s = fakeCString( vObjectUStringZValue( vo ) );
01296       anEvent->addAttachment( new Attachment( QString( s ) ) );
01297       deleteStr( s );
01298     }
01299   }
01300 
01301   // resources
01302   if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) {
01303     QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
01304     deleteStr( s );
01305     QStringList tmpStrList = resources.split( ';' );
01306     anEvent->setResources( tmpStrList );
01307   }
01308 
01309   // alarm stuff
01310   if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) {
01311     Alarm *alarm = anEvent->newAlarm();
01312     VObject *a;
01313     if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) {
01314       alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
01315       deleteStr( s );
01316     }
01317     alarm->setEnabled( true );
01318     if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) {
01319       if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) {
01320         s = fakeCString( vObjectUStringZValue( a ) );
01321         alarm->setProcedureAlarm( QFile::decodeName( s ) );
01322         deleteStr( s );
01323       }
01324     }
01325     if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) {
01326       if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) {
01327         s = fakeCString( vObjectUStringZValue( a ) );
01328         alarm->setAudioAlarm( QFile::decodeName( s ) );
01329         deleteStr( s );
01330       }
01331     }
01332   }
01333 
01334   // priority
01335   if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) {
01336     s = fakeCString( vObjectUStringZValue( vo ) );
01337     if ( s ) {
01338       anEvent->setPriority( atoi( s ) );
01339       deleteStr( s );
01340     }
01341   }
01342 
01343   // transparency
01344   if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) {
01345     s = fakeCString( vObjectUStringZValue( vo ) );
01346     if ( s ) {
01347       int i = atoi( s );
01348       anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
01349       deleteStr( s );
01350     }
01351   }
01352 
01353   // related event
01354   if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) {
01355     anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
01356     deleteStr( s );
01357     d->mEventsRelate.append( anEvent );
01358   }
01359 
01360   /* PILOT SYNC STUFF */
01361   if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) {
01362     anEvent->setNonKDECustomProperty(
01363       KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01364     deleteStr( s );
01365     if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) {
01366       anEvent->setNonKDECustomProperty(
01367         KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
01368       deleteStr( s );
01369     } else {
01370       anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) );
01371     }
01372   }
01373 
01374   return anEvent;
01375 }
01376 
01377 QString VCalFormat::qDateToISO( const QDate &qd )
01378 {
01379   QString tmpStr;
01380 
01381   if ( !qd.isValid() ) {
01382     return QString();
01383   }
01384 
01385   tmpStr.sprintf( "%.2d%.2d%.2d", qd.year(), qd.month(), qd.day() );
01386   return tmpStr;
01387 
01388 }
01389 
01390 QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu )
01391 {
01392   QString tmpStr;
01393 
01394   if ( !dt.isValid() ) {
01395     return QString();
01396   }
01397 
01398   QDateTime tmpDT;
01399   if ( zulu ) {
01400     tmpDT = dt.toUtc().dateTime();
01401   } else {
01402     tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime();
01403   }
01404   tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
01405                   tmpDT.date().year(), tmpDT.date().month(),
01406                   tmpDT.date().day(), tmpDT.time().hour(),
01407                   tmpDT.time().minute(), tmpDT.time().second() );
01408   if ( zulu ) {
01409     tmpStr += 'Z';
01410   }
01411   return tmpStr;
01412 }
01413 
01414 KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr )
01415 {
01416   QDate tmpDate;
01417   QTime tmpTime;
01418   QString tmpStr;
01419   int year, month, day, hour, minute, second;
01420 
01421   tmpStr = dtStr;
01422   year = tmpStr.left( 4 ).toInt();
01423   month = tmpStr.mid( 4, 2 ).toInt();
01424   day = tmpStr.mid( 6, 2 ).toInt();
01425   hour = tmpStr.mid( 9, 2 ).toInt();
01426   minute = tmpStr.mid( 11, 2 ).toInt();
01427   second = tmpStr.mid( 13, 2 ).toInt();
01428   tmpDate.setYMD( year, month, day );
01429   tmpTime.setHMS( hour, minute, second );
01430 
01431   if ( tmpDate.isValid() && tmpTime.isValid() ) {
01432     // correct for GMT if string is in Zulu format
01433     if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) {
01434       return KDateTime( tmpDate, tmpTime, KDateTime::UTC );
01435     } else {
01436       return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() );
01437     }
01438   } else {
01439     return KDateTime();
01440   }
01441 }
01442 
01443 QDate VCalFormat::ISOToQDate( const QString &dateStr )
01444 {
01445   int year, month, day;
01446 
01447   year = dateStr.left( 4 ).toInt();
01448   month = dateStr.mid( 4, 2 ).toInt();
01449   day = dateStr.mid( 6, 2 ).toInt();
01450 
01451   return QDate( year, month, day );
01452 }
01453 
01454 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
01455 // and break it down from it's tree-like format into the dictionary format
01456 // that is used internally in the VCalFormat.
01457 void VCalFormat::populate( VObject *vcal )
01458 {
01459   // this function will populate the caldict dictionary and other event
01460   // lists. It turns vevents into Events and then inserts them.
01461 
01462   VObjectIterator i;
01463   VObject *curVO, *curVOProp;
01464   Event *anEvent;
01465 
01466   if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) {
01467     char *methodType = 0;
01468     methodType = fakeCString( vObjectUStringZValue( curVO ) );
01469     kDebug() << "This calendar is an iTIP transaction of type '"
01470              << methodType << "'";
01471     deleteStr( methodType );
01472   }
01473 
01474   // warn the user that we might have trouble reading non-known calendar.
01475   if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) {
01476     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01477     if ( !s || strcmp( productId().toLocal8Bit(), s ) != 0 ) {
01478       kDebug() << "This vCalendar file was not created by KOrganizer or"
01479                << "any other product we support. Loading anyway...";
01480     }
01481     setLoadedProductId( s );
01482     deleteStr( s );
01483   }
01484 
01485   // warn the user we might have trouble reading this unknown version.
01486   if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) {
01487     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01488     if ( !s || strcmp( _VCAL_VERSION, s ) != 0 ) {
01489       kDebug() << "This vCalendar file has version" << s
01490                << "We only support" << _VCAL_VERSION;
01491     }
01492     deleteStr( s );
01493   }
01494 
01495 #if 0
01496   // set the time zone (this is a property of the view, so just discard!)
01497   if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) {
01498     char *s = fakeCString( vObjectUStringZValue( curVO ) );
01499     d->mCalendar->setTimeZone( s );
01500     deleteStr( s );
01501   }
01502 #endif
01503 
01504   // Store all events with a relatedTo property in a list for post-processing
01505   d->mEventsRelate.clear();
01506   d->mTodosRelate.clear();
01507 
01508   initPropIterator( &i, vcal );
01509 
01510   // go through all the vobjects in the vcal
01511   while ( moreIteration( &i ) ) {
01512     curVO = nextVObject( &i );
01513 
01514     /************************************************************************/
01515 
01516     // now, check to see that the object is an event or todo.
01517     if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) {
01518 
01519       if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) {
01520         char *s;
01521         s = fakeCString( vObjectUStringZValue( curVOProp ) );
01522         // check to see if event was deleted by the kpilot conduit
01523         if ( s ) {
01524           if ( atoi( s ) == SYNCDEL ) {
01525             deleteStr( s );
01526             kDebug() << "skipping pilot-deleted event";
01527             goto SKIP;
01528           }
01529           deleteStr( s );
01530         }
01531       }
01532 
01533       // this code checks to see if we are trying to read in an event
01534       // that we already find to be in the calendar.  If we find this
01535       // to be the case, we skip the event.
01536       if ( ( curVOProp = isAPropertyOf( curVO, VCUniqueStringProp ) ) != 0 ) {
01537         char *s = fakeCString( vObjectUStringZValue( curVOProp ) );
01538         QString tmpStr( s );
01539         deleteStr( s );
01540 
01541         if ( d->mCalendar->incidence( tmpStr ) ) {
01542           goto SKIP;
01543         }
01544       }
01545 
01546       if ( ( !( curVOProp = isAPropertyOf( curVO, VCDTstartProp ) ) ) &&
01547            ( !( curVOProp = isAPropertyOf( curVO, VCDTendProp ) ) ) ) {
01548         kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping...";
01549         goto SKIP;
01550       }
01551 
01552       anEvent = VEventToEvent( curVO );
01553       // we now use addEvent instead of insertEvent so that the
01554       // signal/slot get connected.
01555       if ( anEvent ) {
01556         if ( anEvent->dtStart().isValid() && anEvent->dtEnd().isValid() ) {
01557           d->mCalendar->addEvent( anEvent );
01558         }
01559       } else {
01560         // some sort of error must have occurred while in translation.
01561         goto SKIP;
01562       }
01563     } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) {
01564       Todo *aTodo = VTodoToEvent( curVO );
01565 
01566       Todo *old = d->mCalendar->todo( aTodo->uid() );
01567       if ( old ) {
01568         d->mCalendar->deleteTodo( old );
01569         d->mTodosRelate.removeAll( old );
01570       }
01571 
01572       d->mCalendar->addTodo( aTodo );
01573     } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) ||
01574                 ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) ||
01575                 ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) {
01576       // do nothing, we know these properties and we want to skip them.
01577       // we have either already processed them or are ignoring them.
01578       ;
01579     } else {
01580       kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
01581     }
01582   SKIP:
01583     ;
01584   } // while
01585 
01586   // Post-Process list of events with relations, put Event objects in relation
01587   Event::List::ConstIterator eIt;
01588   for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) {
01589     (*eIt)->setRelatedTo( d->mCalendar->incidence( (*eIt)->relatedToUid() ) );
01590   }
01591   Todo::List::ConstIterator tIt;
01592   for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) {
01593     (*tIt)->setRelatedTo( d->mCalendar->incidence( (*tIt)->relatedToUid() ) );
01594    }
01595 }
01596 
01597 const char *VCalFormat::dayFromNum( int day )
01598 {
01599   const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
01600 
01601   return days[day];
01602 }
01603 
01604 int VCalFormat::numFromDay( const QString &day )
01605 {
01606   if ( day == "MO " ) {
01607     return 0;
01608   }
01609   if ( day == "TU " ) {
01610     return 1;
01611   }
01612   if ( day == "WE " ) {
01613     return 2;
01614   }
01615   if ( day == "TH " ) {
01616     return 3;
01617   }
01618   if ( day == "FR " ) {
01619     return 4;
01620   }
01621   if ( day == "SA " ) {
01622     return 5;
01623   }
01624   if ( day == "SU " ) {
01625     return 6;
01626   }
01627 
01628   return -1; // something bad happened. :)
01629 }
01630 
01631 Attendee::PartStat VCalFormat::readStatus( const char *s ) const
01632 {
01633   QString statStr = s;
01634   statStr = statStr.toUpper();
01635   Attendee::PartStat status;
01636 
01637   if ( statStr == "X-ACTION" ) {
01638     status = Attendee::NeedsAction;
01639   } else if ( statStr == "NEEDS ACTION" ) {
01640     status = Attendee::NeedsAction;
01641   } else if ( statStr == "ACCEPTED" ) {
01642     status = Attendee::Accepted;
01643   } else if ( statStr == "SENT" ) {
01644     status = Attendee::NeedsAction;
01645   } else if ( statStr == "TENTATIVE" ) {
01646     status = Attendee::Tentative;
01647   } else if ( statStr == "CONFIRMED" ) {
01648     status = Attendee::Accepted;
01649   } else if ( statStr == "DECLINED" ) {
01650     status = Attendee::Declined;
01651   } else if ( statStr == "COMPLETED" ) {
01652     status = Attendee::Completed;
01653   } else if ( statStr == "DELEGATED" ) {
01654     status = Attendee::Delegated;
01655   } else {
01656     kDebug() << "error setting attendee mStatus, unknown mStatus!";
01657     status = Attendee::NeedsAction;
01658   }
01659 
01660   return status;
01661 }
01662 
01663 QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const
01664 {
01665   switch( status ) {
01666   default:
01667   case Attendee::NeedsAction:
01668     return "NEEDS ACTION";
01669     break;
01670   case Attendee::Accepted:
01671     return "ACCEPTED";
01672     break;
01673   case Attendee::Declined:
01674     return "DECLINED";
01675     break;
01676   case Attendee::Tentative:
01677     return "TENTATIVE";
01678     break;
01679   case Attendee::Delegated:
01680     return "DELEGATED";
01681     break;
01682   case Attendee::Completed:
01683     return "COMPLETED";
01684     break;
01685   case Attendee::InProcess:
01686     return "NEEDS ACTION";
01687     break;
01688   }
01689 }

KCal Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.5
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