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

KCalCore Library

  • kcalcore
vcalformat.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  Copyright (c) 1998 Preston Brown <pbrown@kde.org>
5  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
37 #include "vcalformat.h"
38 #include "calendar.h"
39 #include "event.h"
40 #include "exceptions.h"
41 #include "icaltimezones.h"
42 #include "todo.h"
43 #include "versit/vcc.h"
44 #include "versit/vobject.h"
45 
46 #include <KCodecs>
47 #include <KDebug>
48 
49 #include <QtCore/QBitArray>
50 #include <QtCore/QFile>
51 #include <QtGui/QTextDocument> // for Qt::escape() and Qt::mightBeRichText()
52 
53 using namespace KCalCore;
54 
59 //@cond PRIVATE
60 template <typename K>
61 void removeAllVCal( QVector< QSharedPointer<K> > &c, const QSharedPointer<K> &x )
62 {
63  Q_ASSERT( c.count( x ) == 1 );
64  c.remove( c.indexOf( x ) );
65 }
66 
67 class KCalCore::VCalFormat::Private
68 {
69  public:
70  Calendar::Ptr mCalendar;
71  Event::List mEventsRelate; // Events with relations
72  Todo::List mTodosRelate; // To-dos with relations
73  QSet<QByteArray> mManuallyWrittenExtensionFields; // X- fields that are manually dumped
74 };
75 //@endcond
76 
77 VCalFormat::VCalFormat() : d( new KCalCore::VCalFormat::Private )
78 {
79 #if defined(KCALCORE_FOR_SYMBIAN)
80  d->mManuallyWrittenExtensionFields << VCRecurrenceIdProp;
81  d->mManuallyWrittenExtensionFields << EPOCAgendaEntryTypeProp;
82 #endif
83  d->mManuallyWrittenExtensionFields << KPilotIdProp;
84  d->mManuallyWrittenExtensionFields << KPilotStatusProp;
85 }
86 
87 VCalFormat::~VCalFormat()
88 {
89  delete d;
90 }
91 
92 bool VCalFormat::load( const Calendar::Ptr &calendar, const QString &fileName )
93 {
94  d->mCalendar = calendar;
95 
96  clearException();
97 
98  VObject *vcal = 0;
99 
100  // this is not necessarily only 1 vcal. Could be many vcals, or include
101  // a vcard...
102  vcal = Parse_MIME_FromFileName( const_cast<char *>( QFile::encodeName( fileName ).data() ) );
103 
104  if ( !vcal ) {
105  setException( new Exception( Exception::CalVersionUnknown ) );
106  return false;
107  }
108 
109  // any other top-level calendar stuff should be added/initialized here
110 
111  // put all vobjects into their proper places
112  QString savedTimeZoneId = d->mCalendar->timeZoneId();
113  populate( vcal, false, fileName );
114  d->mCalendar->setTimeZoneId(savedTimeZoneId);
115 
116  // clean up from vcal API stuff
117  cleanVObjects( vcal );
118  cleanStrTbl();
119 
120  return true;
121 }
122 
123 bool VCalFormat::save( const Calendar::Ptr &calendar, const QString &fileName )
124 {
125  d->mCalendar = calendar;
126 
127  ICalTimeZones *tzlist = d->mCalendar->timeZones();
128 
129  QString tmpStr;
130  VObject *vcal, *vo;
131 
132  vcal = newVObject( VCCalProp );
133 
134  // addPropValue(vcal,VCLocationProp, "0.0");
135  addPropValue( vcal, VCProdIdProp, productId().toLatin1() );
136  addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
137 
138  // TODO STUFF
139  Todo::List todoList = d->mCalendar->rawTodos();
140  Todo::List::ConstIterator it;
141  for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
142  if ( (*it)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
143  ICalTimeZone zone = tzlist->zone( (*it)->dtStart().timeZone().name() );
144  if ( zone.isValid() ) {
145  QByteArray timezone = zone.vtimezone();
146  addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toLocal8Bit() );
147  QString dst = parseDst( timezone );
148  while ( !dst.isEmpty() ) {
149  addPropValue( vcal, VCDayLightProp, dst.toLocal8Bit() );
150  dst = parseDst( timezone );
151  }
152  }
153  }
154  vo = eventToVTodo( *it );
155  addVObjectProp( vcal, vo );
156  }
157  // EVENT STUFF
158  Event::List events = d->mCalendar->rawEvents();
159  Event::List::ConstIterator it2;
160  for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) {
161  if ( (*it2)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
162  ICalTimeZone zone = tzlist->zone( (*it2)->dtStart().timeZone().name() );
163  if ( zone.isValid() ) {
164  QByteArray timezone = zone.vtimezone();
165  addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toLocal8Bit() );
166  QString dst = parseDst( timezone );
167  while ( !dst.isEmpty() ) {
168  addPropValue( vcal, VCDayLightProp, dst.toLocal8Bit() );
169  dst = parseDst( timezone );
170  }
171  }
172  }
173  vo = eventToVEvent( *it2 );
174  addVObjectProp( vcal, vo );
175  }
176  writeVObjectToFile( QFile::encodeName( fileName ).data(), vcal );
177  cleanVObjects( vcal );
178  cleanStrTbl();
179 
180  if ( QFile::exists( fileName ) ) {
181  return true;
182  } else {
183  return false; // error
184  }
185 
186  return false;
187 }
188 
189 bool VCalFormat::fromString( const Calendar::Ptr &calendar, const QString &string,
190  bool deleted, const QString &notebook )
191 {
192  return fromRawString( calendar, string.toUtf8(), deleted, notebook );
193 }
194 
195 bool VCalFormat::fromRawString( const Calendar::Ptr &calendar, const QByteArray &string,
196  bool deleted, const QString &notebook )
197 {
198  d->mCalendar = calendar;
199 
200  if ( !string.size() ) {
201  return false;
202  }
203 
204  VObject *vcal = Parse_MIME( string.data(), string.size() );
205  if ( !vcal ) {
206  return false;
207  }
208 
209  VObjectIterator i;
210  initPropIterator( &i, vcal );
211 
212  // put all vobjects into their proper places
213  QString savedTimeZoneId = d->mCalendar->timeZoneId();
214  populate( vcal, deleted, notebook );
215  d->mCalendar->setTimeZoneId(savedTimeZoneId);
216 
217  // clean up from vcal API stuff
218  cleanVObjects( vcal );
219  cleanStrTbl();
220 
221  return true;
222 }
223 
224 QString VCalFormat::toString( const Calendar::Ptr &calendar,
225  const QString &notebook, bool deleted )
226 {
227  // TODO: Factor out VCalFormat::asString()
228  d->mCalendar = calendar;
229 
230  ICalTimeZones *tzlist = d->mCalendar->timeZones();
231 
232  VObject *vo;
233  VObject *vcal = newVObject( VCCalProp );
234 
235  addPropValue( vcal, VCProdIdProp, CalFormat::productId().toLatin1() );
236  addPropValue( vcal, VCVersionProp, _VCAL_VERSION );
237 
238  // TODO STUFF
239  Todo::List todoList = deleted ? d->mCalendar->deletedTodos() : d->mCalendar->rawTodos();
240  Todo::List::ConstIterator it;
241  for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
242  if ( !deleted || !d->mCalendar->todo( (*it)->uid(), (*it)->recurrenceId() ) ) {
243  // use existing ones, or really deleted ones
244  if ( notebook.isEmpty() ||
245  ( !calendar->notebook(*it).isEmpty() &&
246  notebook.endsWith( calendar->notebook( *it ) ) ) ) {
247  if ( (*it)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
248  ICalTimeZone zone = tzlist->zone( (*it)->dtStart().timeZone().name() );
249  if ( zone.isValid() ) {
250  QByteArray timezone = zone.vtimezone();
251  addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toUtf8() );
252  QString dst = parseDst( timezone );
253  while ( !dst.isEmpty() ) {
254  addPropValue( vcal, VCDayLightProp, dst.toUtf8() );
255  dst = parseDst( timezone );
256  }
257  }
258  }
259  vo = eventToVTodo( *it );
260  addVObjectProp( vcal, vo );
261  }
262  }
263  }
264 
265  // EVENT STUFF
266  Event::List events = deleted ? d->mCalendar->deletedEvents() : d->mCalendar->rawEvents();
267  Event::List::ConstIterator it2;
268  for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) {
269  if ( !deleted || !d->mCalendar->event( (*it2)->uid(), (*it2)->recurrenceId() ) ) {
270  // use existing ones, or really deleted ones
271  if ( notebook.isEmpty() ||
272  ( !calendar->notebook( *it2 ).isEmpty() &&
273  notebook.endsWith( calendar->notebook( *it2 ) ) ) ) {
274  if ( (*it2)->dtStart().timeZone().name().mid( 0, 4 ) == "VCAL" ) {
275  ICalTimeZone zone = tzlist->zone( (*it2)->dtStart().timeZone().name() );
276  if ( zone.isValid() ) {
277  QByteArray timezone = zone.vtimezone();
278  addPropValue( vcal, VCTimeZoneProp, parseTZ( timezone ).toUtf8() );
279  QString dst = parseDst( timezone );
280  while ( !dst.isEmpty() ) {
281  addPropValue( vcal, VCDayLightProp, dst.toUtf8() );
282  dst = parseDst( timezone );
283  }
284  }
285  }
286  vo = eventToVEvent( *it2 );
287  addVObjectProp( vcal, vo );
288  }
289  }
290  }
291 
292  char *buf = writeMemVObject( 0, 0, vcal );
293 
294  QString result( QString::fromUtf8(buf) );
295 
296  deleteStr( buf );
297 
298  cleanVObject( vcal );
299 
300  return result;
301 }
302 
303 VObject *VCalFormat::eventToVTodo( const Todo::Ptr &anEvent )
304 {
305  VObject *vtodo;
306  QString tmpStr;
307 
308  vtodo = newVObject( VCTodoProp );
309 
310  // due date
311  if ( anEvent->hasDueDate() ) {
312  tmpStr = kDateTimeToISO( anEvent->dtDue(), !anEvent->allDay() );
313  addPropValue( vtodo, VCDueProp, tmpStr.toUtf8() );
314  }
315 
316  // start date
317  if ( anEvent->hasStartDate() ) {
318  tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
319  addPropValue( vtodo, VCDTstartProp, tmpStr.toUtf8() );
320  }
321 
322  // creation date
323  tmpStr = kDateTimeToISO( anEvent->created() );
324  addPropValue( vtodo, VCDCreatedProp, tmpStr.toUtf8() );
325 
326  // unique id
327  addPropValue( vtodo, VCUniqueStringProp,
328  anEvent->uid().toUtf8() );
329 
330  // revision
331  tmpStr.sprintf( "%i", anEvent->revision() );
332  addPropValue( vtodo, VCSequenceProp, tmpStr.toUtf8() );
333 
334  // last modification date
335  tmpStr = kDateTimeToISO( anEvent->lastModified() );
336  addPropValue( vtodo, VCLastModifiedProp, tmpStr.toUtf8() );
337 
338  // organizer stuff
339  // @TODO: How about the common name?
340  if ( !anEvent->organizer()->email().isEmpty() ) {
341  tmpStr = "MAILTO:" + anEvent->organizer()->email();
342  addPropValue( vtodo, ICOrganizerProp, tmpStr.toUtf8() );
343  }
344 
345  // attendees
346  if ( anEvent->attendeeCount() > 0 ) {
347  Attendee::List::ConstIterator it;
348  Attendee::Ptr curAttendee;
349  for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd();
350  ++it ) {
351  curAttendee = *it;
352  if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) {
353  tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
354  } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
355  tmpStr = "MAILTO: ";
356  kDebug() << "warning! this Event has an attendee w/o name or email!";
357  } else if ( curAttendee->name().isEmpty() ) {
358  tmpStr = "MAILTO: " + curAttendee->email();
359  } else {
360  tmpStr = "MAILTO: " + curAttendee->name();
361  }
362  VObject *aProp = addPropValue( vtodo, VCAttendeeProp, tmpStr.toUtf8() );
363  addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
364  addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
365  }
366  }
367 
368  // recurrence rule stuff
369  const Recurrence *recur = anEvent->recurrence();
370  if ( recur->recurs() ) {
371  bool validRecur = true;
372  QString tmpStr2;
373  switch ( recur->recurrenceType() ) {
374  case Recurrence::rDaily:
375  tmpStr.sprintf( "D%i ", recur->frequency() );
376  break;
377  case Recurrence::rWeekly:
378  tmpStr.sprintf( "W%i ", recur->frequency() );
379  for ( int i = 0; i < 7; ++i ) {
380  QBitArray days ( recur->days() );
381  if ( days.testBit(i) ) {
382  tmpStr += dayFromNum( i );
383  }
384  }
385  break;
386  case Recurrence::rMonthlyPos:
387  {
388  tmpStr.sprintf( "MP%i ", recur->frequency() );
389  // write out all rMonthPos's
390  QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
391  for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin();
392  posit != tmpPositions.constEnd(); ++posit ) {
393  int pos = (*posit).pos();
394  tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) );
395  if ( pos < 0 ) {
396  tmpStr2 += "- ";
397  } else {
398  tmpStr2 += "+ ";
399  }
400  tmpStr += tmpStr2;
401  tmpStr += dayFromNum( (*posit).day() - 1 );
402  }
403  break;
404  }
405  case Recurrence::rMonthlyDay:
406  {
407  tmpStr.sprintf( "MD%i ", recur->frequency() );
408  // write out all rMonthDays;
409  const QList<int> tmpDays = recur->monthDays();
410  for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin();
411  tmpDay != tmpDays.constEnd(); ++tmpDay ) {
412  tmpStr2.sprintf( "%i ", *tmpDay );
413  tmpStr += tmpStr2;
414  }
415  break;
416  }
417  case Recurrence::rYearlyMonth:
418  {
419  tmpStr.sprintf( "YM%i ", recur->frequency() );
420  // write out all the months;'
421  // TODO: Any way to write out the day within the month???
422  const QList<int> months = recur->yearMonths();
423  for ( QList<int>::ConstIterator mit = months.constBegin();
424  mit != months.constEnd(); ++mit ) {
425  tmpStr2.sprintf( "%i ", *mit );
426  tmpStr += tmpStr2;
427  }
428  break;
429  }
430  case Recurrence::rYearlyDay:
431  {
432  tmpStr.sprintf( "YD%i ", recur->frequency() );
433  // write out all the rYearNums;
434  const QList<int> tmpDays = recur->yearDays();
435  for ( QList<int>::ConstIterator tmpDay = tmpDays.begin();
436  tmpDay != tmpDays.end(); ++tmpDay ) {
437  tmpStr2.sprintf( "%i ", *tmpDay );
438  tmpStr += tmpStr2;
439  }
440  break;
441  }
442  default:
443  // TODO: Write rYearlyPos and arbitrary rules!
444  kDebug() << "ERROR, it should never get here in eventToVTodo!";
445  validRecur = false;
446  break;
447  } // switch
448 
449  if ( recur->duration() > 0 ) {
450  tmpStr2.sprintf( "#%i", recur->duration() );
451  tmpStr += tmpStr2;
452  } else if ( recur->duration() == -1 ) {
453  tmpStr += "#0"; // defined as repeat forever
454  } else {
455  tmpStr += kDateTimeToISO( recur->endDateTime(), false );
456  }
457  // Only write out the rrule if we have a valid recurrence (i.e. a known
458  // type in thee switch above)
459  if ( validRecur ) {
460  addPropValue( vtodo, VCRRuleProp, tmpStr.toUtf8() );
461  }
462 
463  } // event repeats
464 
465  // exceptions dates to recurrence
466  DateList dateList = recur->exDates();
467  DateList::ConstIterator id;
468  QString tmpStr2;
469 
470  for ( id = dateList.constBegin(); id != dateList.constEnd(); ++id ) {
471  tmpStr = qDateToISO(*id) + ';';
472  tmpStr2 += tmpStr;
473  }
474  if ( !tmpStr2.isEmpty() ) {
475  tmpStr2.truncate( tmpStr2.length() - 1 );
476  addPropValue( vtodo, VCExpDateProp, tmpStr2.toUtf8() );
477  }
478  // exceptions datetimes to recurrence
479  DateTimeList dateTimeList = recur->exDateTimes();
480  DateTimeList::ConstIterator idt;
481  tmpStr2.clear();
482 
483  for ( idt = dateTimeList.constBegin(); idt != dateTimeList.constEnd(); ++idt ) {
484  tmpStr = kDateTimeToISO( *idt ) + ';';
485  tmpStr2 += tmpStr;
486  }
487  if ( !tmpStr2.isEmpty() ) {
488  tmpStr2.truncate( tmpStr2.length() - 1 );
489  addPropValue( vtodo, VCExpDateProp, tmpStr2.toUtf8() );
490  }
491 
492  // description BL:
493  if ( !anEvent->description().isEmpty() ) {
494  QByteArray in = anEvent->description().toUtf8();
495  QByteArray out;
496  KCodecs::quotedPrintableEncode( in, out, true );
497  if ( out != in ) {
498  VObject *d = addPropValue( vtodo, VCDescriptionProp, out );
499  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
500  addPropValue( d, VCCharSetProp, VCUtf8Prop );
501  } else {
502  addPropValue( vtodo, VCDescriptionProp, in );
503  }
504  }
505 
506  // summary
507  if ( !anEvent->summary().isEmpty() ) {
508  QByteArray in = anEvent->summary().toUtf8();
509  QByteArray out;
510  KCodecs::quotedPrintableEncode( in, out, true );
511  if ( out != in ) {
512  VObject *d = addPropValue( vtodo, VCSummaryProp, out );
513  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
514  addPropValue( d, VCCharSetProp, VCUtf8Prop );
515  } else {
516  addPropValue( vtodo, VCSummaryProp, in );
517  }
518  }
519 
520  // location
521  if ( !anEvent->location().isEmpty() ) {
522  QByteArray in = anEvent->location().toUtf8();
523  QByteArray out;
524  KCodecs::quotedPrintableEncode( in, out, true );
525  if ( out != in ) {
526  VObject *d = addPropValue( vtodo, VCLocationProp, out );
527  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
528  addPropValue( d, VCCharSetProp, VCUtf8Prop );
529  } else {
530  addPropValue( vtodo, VCLocationProp, in );
531  }
532  }
533 
534  // completed status
535  // backward compatibility, KOrganizer used to interpret only these two values
536  addPropValue( vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : "NEEDS ACTION" );
537 
538  // completion date
539  if ( anEvent->hasCompletedDate() ) {
540  tmpStr = kDateTimeToISO( anEvent->completed() );
541  addPropValue( vtodo, VCCompletedProp, tmpStr.toUtf8() );
542  }
543 
544  // priority
545  tmpStr.sprintf( "%i", anEvent->priority() );
546  addPropValue( vtodo, VCPriorityProp, tmpStr.toUtf8() );
547 
548  // related event
549  if ( !anEvent->relatedTo().isEmpty() ) {
550  addPropValue( vtodo, VCRelatedToProp,
551  anEvent->relatedTo().toUtf8() );
552  }
553 
554  // secrecy
555  const char *text = 0;
556  switch ( anEvent->secrecy() ) {
557  case Incidence::SecrecyPublic:
558  text = "PUBLIC";
559  break;
560  case Incidence::SecrecyPrivate:
561  text = "PRIVATE";
562  break;
563  case Incidence::SecrecyConfidential:
564  text = "CONFIDENTIAL";
565  break;
566  }
567  if ( text ) {
568  addPropValue( vtodo, VCClassProp, text );
569  }
570 
571  // categories
572  const QStringList tmpStrList = anEvent->categories();
573  tmpStr = "";
574  QString catStr;
575  QStringList::const_iterator its;
576  for ( its = tmpStrList.constBegin(); its != tmpStrList.constEnd(); ++its ) {
577  catStr = *its;
578  if ( catStr[0] == ' ' ) {
579  tmpStr += catStr.mid( 1 );
580  } else {
581  tmpStr += catStr;
582  }
583  // this must be a ';' character as the vCalendar specification requires!
584  // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
585  // read in.
586  tmpStr += ';';
587  }
588  if ( !tmpStr.isEmpty() ) {
589  tmpStr.truncate( tmpStr.length() - 1 );
590  addPropValue( vtodo, VCCategoriesProp, tmpStr.toUtf8() );
591  }
592 
593  // alarm stuff
594  Alarm::List::ConstIterator it;
595  for ( it = anEvent->alarms().constBegin(); it != anEvent->alarms().constEnd(); ++it ) {
596  Alarm::Ptr alarm = *it;
597  if ( alarm->enabled() ) {
598  VObject *a;
599  if ( alarm->type() == Alarm::Display ) {
600  a = addProp( vtodo, VCDAlarmProp );
601  tmpStr = kDateTimeToISO( alarm->time() );
602  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
603  addPropValue( a, VCRepeatCountProp, "1" );
604  if ( alarm->text().isNull() ) {
605  addPropValue( a, VCDisplayStringProp, "beep!" );
606  } else {
607  addPropValue( a, VCDisplayStringProp, alarm->text().toAscii().data() );
608  }
609  } else if ( alarm->type() == Alarm::Audio ) {
610  a = addProp( vtodo, VCAAlarmProp );
611  tmpStr = kDateTimeToISO( alarm->time() );
612  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
613  addPropValue( a, VCRepeatCountProp, "1" );
614  addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
615  } else if ( alarm->type() == Alarm::Procedure ) {
616  a = addProp( vtodo, VCPAlarmProp );
617  tmpStr = kDateTimeToISO( alarm->time() );
618  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
619  addPropValue( a, VCRepeatCountProp, "1" );
620  addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
621  }
622  }
623  }
624 
625  QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
626  if ( !pilotId.isEmpty() ) {
627  // pilot sync stuff
628  addPropValue( vtodo, KPilotIdProp, pilotId.toUtf8() );
629  addPropValue( vtodo, KPilotStatusProp,
630  anEvent->nonKDECustomProperty( KPilotStatusProp ).toUtf8() );
631  }
632 #if defined(KCALCORE_FOR_SYMBIAN)
633  if ( anEvent->nonKDECustomProperty( EPOCAgendaEntryTypeProp ).isEmpty() ) {
634  // Propagate braindeath by setting this property also so that
635  // S60 is happy
636  addPropValue( vtodo, EPOCAgendaEntryTypeProp, "TODO" );
637  }
638 
639  writeCustomProperties( vtodo, anEvent );
640 #endif
641 
642  return vtodo;
643 }
644 
645 VObject *VCalFormat::eventToVEvent( const Event::Ptr &anEvent )
646 {
647  VObject *vevent;
648  QString tmpStr;
649 
650  vevent = newVObject( VCEventProp );
651 
652  // start and end time
653  tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() );
654  addPropValue( vevent, VCDTstartProp, tmpStr.toUtf8() );
655 
656 #if !defined(KCALCORE_FOR_MEEGO)
657  // events that have time associated but take up no time should
658  // not have both DTSTART and DTEND.
659  if ( anEvent->dtStart() != anEvent->dtEnd() ) {
660  tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() );
661  addPropValue( vevent, VCDTendProp, tmpStr.toUtf8() );
662  }
663 #else
664  // N900 and s60-phones need enddate
665  tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() );
666  addPropValue( vevent, VCDTendProp, tmpStr.toUtf8() );
667 #endif
668 
669  // creation date
670  tmpStr = kDateTimeToISO( anEvent->created() );
671  addPropValue( vevent, VCDCreatedProp, tmpStr.toUtf8() );
672 
673  // unique id
674  addPropValue( vevent, VCUniqueStringProp,
675  anEvent->uid().toUtf8() );
676 
677  // revision
678  tmpStr.sprintf( "%i", anEvent->revision() );
679  addPropValue( vevent, VCSequenceProp, tmpStr.toUtf8() );
680 
681  // last modification date
682  tmpStr = kDateTimeToISO( anEvent->lastModified() );
683  addPropValue( vevent, VCLastModifiedProp, tmpStr.toUtf8() );
684 
685  // attendee and organizer stuff
686  // TODO: What to do with the common name?
687  if ( !anEvent->organizer()->email().isEmpty() ) {
688  tmpStr = "MAILTO:" + anEvent->organizer()->email();
689  addPropValue( vevent, ICOrganizerProp, tmpStr.toUtf8() );
690  }
691 
692  // TODO: Put this functionality into Attendee class
693  if ( anEvent->attendeeCount() > 0 ) {
694  Attendee::List::ConstIterator it;
695  for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd();
696  ++it ) {
697  Attendee::Ptr curAttendee = *it;
698  if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) {
699  tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>';
700  } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) {
701  tmpStr = "MAILTO: ";
702  kDebug() << "warning! this Event has an attendee w/o name or email!";
703  } else if ( curAttendee->name().isEmpty() ) {
704  tmpStr = "MAILTO: " + curAttendee->email();
705  } else {
706  tmpStr = "MAILTO: " + curAttendee->name();
707  }
708  VObject *aProp = addPropValue( vevent, VCAttendeeProp, tmpStr.toUtf8() );
709  addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" );
710  addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) );
711  }
712  }
713 
714  // recurrence rule stuff
715  const Recurrence *recur = anEvent->recurrence();
716  if ( recur->recurs() ) {
717  bool validRecur = true;
718  QString tmpStr2;
719  switch ( recur->recurrenceType() ) {
720  case Recurrence::rDaily:
721  tmpStr.sprintf( "D%i ", recur->frequency() );
722  break;
723  case Recurrence::rWeekly:
724  tmpStr.sprintf( "W%i ", recur->frequency() );
725  for ( int i = 0; i < 7; ++i ) {
726  QBitArray days ( recur->days() );
727  if ( days.testBit(i) ) {
728  tmpStr += dayFromNum( i );
729  }
730  }
731  break;
732  case Recurrence::rMonthlyPos:
733  {
734  tmpStr.sprintf( "MP%i ", recur->frequency() );
735  // write out all rMonthPos's
736  QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions();
737  for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin();
738  posit != tmpPositions.constEnd(); ++posit ) {
739  int pos = (*posit).pos();
740  tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) );
741  if ( pos < 0 ) {
742  tmpStr2 += "- ";
743  } else {
744  tmpStr2 += "+ ";
745  }
746  tmpStr += tmpStr2;
747  tmpStr += dayFromNum( (*posit).day() - 1 );
748  }
749  break;
750  }
751  case Recurrence::rMonthlyDay:
752  {
753  tmpStr.sprintf( "MD%i ", recur->frequency() );
754  // write out all rMonthDays;
755  const QList<int> tmpDays = recur->monthDays();
756  for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin();
757  tmpDay != tmpDays.constEnd(); ++tmpDay ) {
758  tmpStr2.sprintf( "%i ", *tmpDay );
759  tmpStr += tmpStr2;
760  }
761  break;
762  }
763  case Recurrence::rYearlyMonth:
764  {
765  tmpStr.sprintf( "YM%i ", recur->frequency() );
766  // write out all the months;'
767  // TODO: Any way to write out the day within the month???
768  const QList<int> months = recur->yearMonths();
769  for ( QList<int>::ConstIterator mit = months.constBegin();
770  mit != months.constEnd(); ++mit ) {
771  tmpStr2.sprintf( "%i ", *mit );
772  tmpStr += tmpStr2;
773  }
774  break;
775  }
776  case Recurrence::rYearlyDay:
777  {
778  tmpStr.sprintf( "YD%i ", recur->frequency() );
779  // write out all the rYearNums;
780  const QList<int> tmpDays = recur->yearDays();
781  for ( QList<int>::ConstIterator tmpDay = tmpDays.begin();
782  tmpDay != tmpDays.end(); ++tmpDay ) {
783  tmpStr2.sprintf( "%i ", *tmpDay );
784  tmpStr += tmpStr2;
785  }
786  break;
787  }
788  default:
789  // TODO: Write rYearlyPos and arbitrary rules!
790  kDebug() << "ERROR, it should never get here in eventToVEvent!";
791  validRecur = false;
792  break;
793  } // switch
794 
795  if ( recur->duration() > 0 ) {
796  tmpStr2.sprintf( "#%i", recur->duration() );
797  tmpStr += tmpStr2;
798  } else if ( recur->duration() == -1 ) {
799  tmpStr += "#0"; // defined as repeat forever
800  } else {
801 #if !defined(KCALCORE_FOR_MEEGO)
802  tmpStr += kDateTimeToISO( recur->endDateTime(), false );
803 #else
804  tmpStr +=
805  kDateTimeToISO( recur->endDateTime().toTimeSpec( d->mCalendar->timeSpec() ), false );
806 #endif
807  }
808  // Only write out the rrule if we have a valid recurrence (i.e. a known
809  // type in thee switch above)
810  if ( validRecur ) {
811  addPropValue( vevent, VCRRuleProp, tmpStr.toUtf8() );
812  }
813 
814  } // event repeats
815 
816  // exceptions dates to recurrence
817  DateList dateList = recur->exDates();
818  DateList::ConstIterator it;
819  QString tmpStr2;
820 
821  for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) {
822  tmpStr = qDateToISO(*it) + ';';
823  tmpStr2 += tmpStr;
824  }
825  if ( !tmpStr2.isEmpty() ) {
826  tmpStr2.truncate( tmpStr2.length() - 1 );
827  addPropValue( vevent, VCExpDateProp, tmpStr2.toUtf8() );
828  }
829  // exceptions datetimes to recurrence
830  DateTimeList dateTimeList = recur->exDateTimes();
831  DateTimeList::ConstIterator idt;
832  tmpStr2.clear();
833 
834  for ( idt = dateTimeList.constBegin(); idt != dateTimeList.constEnd(); ++idt ) {
835  tmpStr = kDateTimeToISO( *idt ) + ';';
836  tmpStr2 += tmpStr;
837  }
838  if ( !tmpStr2.isEmpty() ) {
839  tmpStr2.truncate( tmpStr2.length() - 1 );
840  addPropValue( vevent, VCExpDateProp, tmpStr2.toUtf8() );
841  }
842 
843  // description
844  if ( !anEvent->description().isEmpty() ) {
845  QByteArray in = anEvent->description().toUtf8();
846  QByteArray out;
847  KCodecs::quotedPrintableEncode( in, out, true );
848  if ( out != in ) {
849  VObject *d = addPropValue( vevent, VCDescriptionProp, out );
850  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
851  addPropValue( d, VCCharSetProp, VCUtf8Prop );
852  } else {
853  addPropValue( vevent, VCDescriptionProp, in );
854  }
855  }
856 
857  // summary
858  if ( !anEvent->summary().isEmpty() ) {
859  QByteArray in = anEvent->summary().toUtf8();
860  QByteArray out;
861  KCodecs::quotedPrintableEncode( in, out, true );
862  if ( out != in ) {
863  VObject *d = addPropValue( vevent, VCSummaryProp, out );
864  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
865  addPropValue( d, VCCharSetProp, VCUtf8Prop );
866  } else {
867  addPropValue( vevent, VCSummaryProp, in );
868  }
869  }
870 
871  // location
872  if ( !anEvent->location().isEmpty() ) {
873  QByteArray in = anEvent->location().toUtf8();
874  QByteArray out;
875  KCodecs::quotedPrintableEncode( in, out, true );
876  if ( out != in ) {
877  VObject *d = addPropValue( vevent, VCLocationProp, out );
878  addPropValue( d, VCEncodingProp, VCQuotedPrintableProp );
879  addPropValue( d, VCCharSetProp, VCUtf8Prop );
880  } else {
881  addPropValue( vevent, VCLocationProp, in );
882  }
883  }
884 
885  // status
886 // TODO: define Event status
887 // addPropValue( vevent, VCStatusProp, anEvent->statusStr().toUtf8() );
888 
889  // secrecy
890  const char *text = 0;
891  switch ( anEvent->secrecy() ) {
892  case Incidence::SecrecyPublic:
893  text = "PUBLIC";
894  break;
895  case Incidence::SecrecyPrivate:
896  text = "PRIVATE";
897  break;
898  case Incidence::SecrecyConfidential:
899  text = "CONFIDENTIAL";
900  break;
901  }
902  if ( text ) {
903  addPropValue( vevent, VCClassProp, text );
904  }
905 
906  // categories
907  QStringList tmpStrList = anEvent->categories();
908  tmpStr = "";
909  QString catStr;
910  for ( QStringList::const_iterator it = tmpStrList.constBegin(); it != tmpStrList.constEnd();
911  ++it ) {
912  catStr = *it;
913  if ( catStr[0] == ' ' ) {
914  tmpStr += catStr.mid( 1 );
915  } else {
916  tmpStr += catStr;
917  }
918  // this must be a ';' character as the vCalendar specification requires!
919  // vcc.y has been hacked to translate the ';' to a ',' when the vcal is
920  // read in.
921  tmpStr += ';';
922  }
923  if ( !tmpStr.isEmpty() ) {
924  tmpStr.truncate( tmpStr.length() - 1 );
925  addPropValue( vevent, VCCategoriesProp, tmpStr.toUtf8() );
926  }
927 
928  // attachments
929  // TODO: handle binary attachments!
930  Attachment::List attachments = anEvent->attachments();
931  Attachment::List::ConstIterator atIt;
932  for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) {
933  addPropValue( vevent, VCAttachProp, (*atIt)->uri().toUtf8() );
934  }
935 
936  // resources
937  tmpStrList = anEvent->resources();
938  tmpStr = tmpStrList.join( ";" );
939  if ( !tmpStr.isEmpty() ) {
940  addPropValue( vevent, VCResourcesProp, tmpStr.toUtf8() );
941  }
942 
943  // alarm stuff
944  Alarm::List::ConstIterator it2;
945  for ( it2 = anEvent->alarms().constBegin(); it2 != anEvent->alarms().constEnd(); ++it2 ) {
946  Alarm::Ptr alarm = *it2;
947  if ( alarm->enabled() ) {
948  VObject *a;
949  if ( alarm->type() == Alarm::Display ) {
950  a = addProp( vevent, VCDAlarmProp );
951  tmpStr = kDateTimeToISO( alarm->time() );
952  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
953  addPropValue( a, VCRepeatCountProp, "1" );
954  if ( alarm->text().isNull() ) {
955  addPropValue( a, VCDisplayStringProp, "beep!" );
956  } else {
957  addPropValue( a, VCDisplayStringProp, alarm->text().toAscii().data() );
958  }
959  } else if ( alarm->type() == Alarm::Audio ) {
960  a = addProp( vevent, VCAAlarmProp );
961  tmpStr = kDateTimeToISO( alarm->time() );
962  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
963  addPropValue( a, VCRepeatCountProp, "1" );
964  addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) );
965  }
966  if ( alarm->type() == Alarm::Procedure ) {
967  a = addProp( vevent, VCPAlarmProp );
968  tmpStr = kDateTimeToISO( alarm->time() );
969  addPropValue( a, VCRunTimeProp, tmpStr.toUtf8() );
970  addPropValue( a, VCRepeatCountProp, "1" );
971  addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) );
972  }
973  }
974  }
975 
976  // priority
977  tmpStr.sprintf( "%i", anEvent->priority() );
978  addPropValue( vevent, VCPriorityProp, tmpStr.toUtf8() );
979 
980  // transparency
981  tmpStr.sprintf( "%i", anEvent->transparency() );
982  addPropValue( vevent, VCTranspProp, tmpStr.toUtf8() );
983 
984  // related event
985  if ( !anEvent->relatedTo().isEmpty() ) {
986  addPropValue( vevent, VCRelatedToProp, anEvent->relatedTo().toUtf8() );
987  }
988 
989  QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp );
990  if ( !pilotId.isEmpty() ) {
991  // pilot sync stuff
992  addPropValue( vevent, KPilotIdProp, pilotId.toUtf8() );
993  addPropValue( vevent, KPilotStatusProp,
994  anEvent->nonKDECustomProperty( KPilotStatusProp ).toUtf8() );
995  }
996 
997 #if defined(KCALCORE_FOR_SYMBIAN)
998  if ( anEvent->nonKDECustomProperty( EPOCAgendaEntryTypeProp ).isEmpty() ) {
999  // Propagate braindeath by setting this property also so that
1000  // S60 is happy
1001  if ( anEvent->allDay() ) {
1002  addPropValue( vevent, EPOCAgendaEntryTypeProp, "EVENT" );
1003  } else {
1004  addPropValue( vevent, EPOCAgendaEntryTypeProp, "APPOINTMENT" );
1005  }
1006  }
1007 
1008  if ( anEvent->hasRecurrenceId() ) {
1009  tmpStr = kDateTimeToISO( anEvent->recurrenceId(), true );
1010  addPropValue( vevent, VCRecurrenceIdProp, tmpStr.toUtf8() );
1011  }
1012  writeCustomProperties( vevent, anEvent );
1013 #endif
1014 
1015  return vevent;
1016 }
1017 
1018 Todo::Ptr VCalFormat::VTodoToEvent( VObject *vtodo )
1019 {
1020  VObject *vo;
1021  VObjectIterator voi;
1022  char *s;
1023 
1024  Todo::Ptr anEvent( new Todo );
1025 
1026  // creation date
1027  if ( ( vo = isAPropertyOf( vtodo, VCDCreatedProp ) ) != 0 ) {
1028  anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1029  deleteStr( s );
1030  }
1031 
1032  // unique id
1033  vo = isAPropertyOf( vtodo, VCUniqueStringProp );
1034  // while the UID property is preferred, it is not required. We'll use the
1035  // default Event UID if none is given.
1036  if ( vo ) {
1037  anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
1038  deleteStr( s );
1039  }
1040 
1041  // last modification date
1042  if ( ( vo = isAPropertyOf( vtodo, VCLastModifiedProp ) ) != 0 ) {
1043  anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1044  deleteStr( s );
1045  } else {
1046  anEvent->setLastModified( KDateTime::currentUtcDateTime() );
1047  }
1048 
1049  // organizer
1050  // if our extension property for the event's ORGANIZER exists, add it.
1051  if ( ( vo = isAPropertyOf( vtodo, ICOrganizerProp ) ) != 0 ) {
1052  anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
1053  deleteStr( s );
1054  } else {
1055  if ( d->mCalendar->owner()->name() != "Unknown Name" ) {
1056  anEvent->setOrganizer( d->mCalendar->owner() );
1057  }
1058  }
1059 
1060  // attendees.
1061  initPropIterator( &voi, vtodo );
1062  while ( moreIteration( &voi ) ) {
1063  vo = nextVObject( &voi );
1064  if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
1065  Attendee::Ptr a;
1066  VObject *vp;
1067  s = fakeCString( vObjectUStringZValue( vo ) );
1068  QString tmpStr = QString::fromUtf8( s );
1069  deleteStr( s );
1070  tmpStr = tmpStr.simplified();
1071  int emailPos1, emailPos2;
1072  if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
1073  // both email address and name
1074  emailPos2 = tmpStr.lastIndexOf( '>' );
1075  a = Attendee::Ptr( new Attendee( tmpStr.left( emailPos1 - 1 ),
1076  tmpStr.mid( emailPos1 + 1,
1077  emailPos2 - ( emailPos1 + 1 ) ) ) );
1078  } else if ( tmpStr.indexOf( '@' ) > 0 ) {
1079  // just an email address
1080  a = Attendee::Ptr( new Attendee( 0, tmpStr ) );
1081  } else {
1082  // just a name
1083  // WTF??? Replacing the spaces of a name and using this as email?
1084  QString email = tmpStr.replace( ' ', '.' );
1085  a = Attendee::Ptr( new Attendee( tmpStr, email ) );
1086  }
1087 
1088  // is there an RSVP property?
1089  if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
1090  a->setRSVP( vObjectStringZValue( vp ) );
1091  }
1092  // is there a status property?
1093  if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
1094  a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
1095  }
1096  // add the attendee
1097  anEvent->addAttendee( a );
1098  }
1099  }
1100 
1101  // description for todo
1102  if ( ( vo = isAPropertyOf( vtodo, VCDescriptionProp ) ) != 0 ) {
1103  s = fakeCString( vObjectUStringZValue( vo ) );
1104  anEvent->setDescription( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
1105  deleteStr( s );
1106  }
1107 
1108  // summary
1109  if ( ( vo = isAPropertyOf( vtodo, VCSummaryProp ) ) ) {
1110  s = fakeCString( vObjectUStringZValue( vo ) );
1111  anEvent->setSummary( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
1112  deleteStr( s );
1113  }
1114 
1115  // location
1116  if ( ( vo = isAPropertyOf( vtodo, VCLocationProp ) ) != 0 ) {
1117  s = fakeCString( vObjectUStringZValue( vo ) );
1118  anEvent->setLocation( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
1119  deleteStr( s );
1120  }
1121 
1122  // completed
1123  // was: status
1124  if ( ( vo = isAPropertyOf( vtodo, VCStatusProp ) ) != 0 ) {
1125  s = fakeCString( vObjectUStringZValue( vo ) );
1126  if ( s && strcmp( s, "COMPLETED" ) == 0 ) {
1127  anEvent->setCompleted( true );
1128  } else {
1129  anEvent->setCompleted( false );
1130  }
1131  deleteStr( s );
1132  } else {
1133  anEvent->setCompleted( false );
1134  }
1135 
1136  // completion date
1137  if ( ( vo = isAPropertyOf( vtodo, VCCompletedProp ) ) != 0 ) {
1138  anEvent->setCompleted( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1139  deleteStr( s );
1140  }
1141 
1142  // priority
1143  if ( ( vo = isAPropertyOf( vtodo, VCPriorityProp ) ) ) {
1144  s = fakeCString( vObjectUStringZValue( vo ) );
1145  if ( s ) {
1146  anEvent->setPriority( atoi( s ) );
1147  deleteStr( s );
1148  }
1149  }
1150 
1151  anEvent->setAllDay( false );
1152 
1153  // due date
1154  if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) {
1155  anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1156  deleteStr( s );
1157  anEvent->setHasDueDate( true );
1158  if ( anEvent->dtDue().time().hour() == 0 &&
1159  anEvent->dtDue().time().minute() == 0 &&
1160  anEvent->dtDue().time().second() == 0 ) {
1161 #if defined(KCALCORE_FOR_MEEGO)
1162  QDate dueDate = anEvent->dtDue().date();
1163  anEvent->setDtDue( KDateTime( dueDate, KDateTime::ClockTime ) );
1164 #endif
1165  anEvent->setAllDay( true );
1166  }
1167  } else {
1168  anEvent->setHasDueDate( false );
1169  }
1170 
1171  // start time
1172  if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) {
1173  anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1174  deleteStr( s );
1175  anEvent->setHasStartDate( true );
1176  if ( anEvent->dtStart().time().hour() == 0 &&
1177  anEvent->dtStart().time().minute() == 0 &&
1178  anEvent->dtStart().time().second() == 0 ) {
1179 #if defined(KCALCORE_FOR_MEEGO)
1180  QDate startDate = anEvent->dtStart().date();
1181  anEvent->setDtStart( KDateTime( startDate, KDateTime::ClockTime ) );
1182 #endif
1183  anEvent->setAllDay( true );
1184  }
1185  } else {
1186  anEvent->setHasStartDate( false );
1187  }
1188 
1189  // repeat stuff
1190  if ( ( vo = isAPropertyOf( vtodo, VCRRuleProp ) ) != 0 ) {
1191  QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
1192  deleteStr( s );
1193  tmpStr.simplified();
1194  tmpStr = tmpStr.toUpper();
1195  // first, read the type of the recurrence
1196  int typelen = 1;
1197  uint type = Recurrence::rNone;
1198  if ( tmpStr.left(1) == "D" ) {
1199  type = Recurrence::rDaily;
1200  } else if ( tmpStr.left(1) == "W" ) {
1201  type = Recurrence::rWeekly;
1202  } else {
1203  typelen = 2;
1204  if ( tmpStr.left(2) == "MP" ) {
1205  type = Recurrence::rMonthlyPos;
1206  } else if ( tmpStr.left(2) == "MD" ) {
1207  type = Recurrence::rMonthlyDay;
1208  } else if ( tmpStr.left(2) == "YM" ) {
1209  type = Recurrence::rYearlyMonth;
1210  } else if ( tmpStr.left(2) == "YD" ) {
1211  type = Recurrence::rYearlyDay;
1212  }
1213  }
1214 
1215  if ( type != Recurrence::rNone ) {
1216 
1217  // Immediately after the type is the frequency
1218  int index = tmpStr.indexOf( ' ' );
1219  int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
1220  int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
1221  ++index; // advance to beginning of stuff after freq
1222 
1223  // Read the type-specific settings
1224  switch ( type ) {
1225  case Recurrence::rDaily:
1226  anEvent->recurrence()->setDaily(rFreq);
1227  break;
1228 
1229  case Recurrence::rWeekly:
1230  {
1231  QBitArray qba(7);
1232  QString dayStr;
1233  if ( index == last ) {
1234  // e.g. W1 #0
1235  qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
1236  } else {
1237  // e.g. W1 SU #0
1238  while ( index < last ) {
1239  dayStr = tmpStr.mid( index, 3 );
1240  int dayNum = numFromDay( dayStr );
1241  if ( dayNum >= 0 ) {
1242  qba.setBit( dayNum );
1243  }
1244  index += 3; // advance to next day, or possibly "#"
1245  }
1246  }
1247  anEvent->recurrence()->setWeekly( rFreq, qba );
1248  break;
1249  }
1250 
1251  case Recurrence::rMonthlyPos:
1252  {
1253  anEvent->recurrence()->setMonthly( rFreq );
1254 
1255  QBitArray qba(7);
1256  short tmpPos;
1257  if ( index == last ) {
1258  // e.g. MP1 #0
1259  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
1260  if ( tmpPos == 5 ) {
1261  tmpPos = -1;
1262  }
1263  qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
1264  anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
1265  } else {
1266  // e.g. MP1 1+ SU #0
1267  while ( index < last ) {
1268  tmpPos = tmpStr.mid( index, 1 ).toShort();
1269  index += 1;
1270  if ( tmpStr.mid( index, 1 ) == "-" ) {
1271  // convert tmpPos to negative
1272  tmpPos = 0 - tmpPos;
1273  }
1274  index += 2; // advance to day(s)
1275  while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
1276  int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
1277  qba.setBit( dayNum );
1278  index += 3; // advance to next day, or possibly pos or "#"
1279  }
1280  anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
1281  qba.detach();
1282  qba.fill( false ); // clear out
1283  } // while != "#"
1284  }
1285  break;
1286  }
1287 
1288  case Recurrence::rMonthlyDay:
1289  anEvent->recurrence()->setMonthly( rFreq );
1290  if( index == last ) {
1291  // e.g. MD1 #0
1292  short tmpDay = anEvent->dtStart().date().day();
1293  anEvent->recurrence()->addMonthlyDate( tmpDay );
1294  } else {
1295  // e.g. MD1 3 #0
1296  while ( index < last ) {
1297  int index2 = tmpStr.indexOf( ' ', index );
1298  if ( ( tmpStr.mid( ( index2 - 1 ), 1 ) == "-" ) ||
1299  ( tmpStr.mid( ( index2 - 1 ), 1 ) == "+" ) ) {
1300  index2 = index2 - 1;
1301  }
1302  short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
1303  index = index2;
1304  if ( tmpStr.mid( index, 1 ) == "-" ) {
1305  tmpDay = 0 - tmpDay;
1306  }
1307  index += 2; // advance the index;
1308  anEvent->recurrence()->addMonthlyDate( tmpDay );
1309  } // while != #
1310  }
1311  break;
1312 
1313  case Recurrence::rYearlyMonth:
1314  anEvent->recurrence()->setYearly( rFreq );
1315 
1316  if ( index == last ) {
1317  // e.g. YM1 #0
1318  short tmpMonth = anEvent->dtStart().date().month();
1319  anEvent->recurrence()->addYearlyMonth( tmpMonth );
1320  } else {
1321  // e.g. YM1 3 #0
1322  while ( index < last ) {
1323  int index2 = tmpStr.indexOf( ' ', index );
1324  short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
1325  index = index2 + 1;
1326  anEvent->recurrence()->addYearlyMonth( tmpMonth );
1327  } // while != #
1328  }
1329  break;
1330 
1331  case Recurrence::rYearlyDay:
1332  anEvent->recurrence()->setYearly( rFreq );
1333 
1334  if ( index == last ) {
1335  // e.g. YD1 #0
1336  short tmpDay = anEvent->dtStart().date().dayOfYear();
1337  anEvent->recurrence()->addYearlyDay( tmpDay );
1338  } else {
1339  // e.g. YD1 123 #0
1340  while ( index < last ) {
1341  int index2 = tmpStr.indexOf( ' ', index );
1342  short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
1343  index = index2 + 1;
1344  anEvent->recurrence()->addYearlyDay( tmpDay );
1345  } // while != #
1346  }
1347  break;
1348 
1349  default:
1350  break;
1351  }
1352 
1353  // find the last field, which is either the duration or the end date
1354  index = last;
1355  if ( tmpStr.mid( index, 1 ) == "#" ) {
1356  // Nr of occurrences
1357  index++;
1358  int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
1359  if ( rDuration > 0 ) {
1360  anEvent->recurrence()->setDuration( rDuration );
1361  }
1362  } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
1363  KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
1364  anEvent->recurrence()->setEndDateTime( rEndDate );
1365  }
1366  } else {
1367  kDebug() << "we don't understand this type of recurrence!";
1368  } // if known recurrence type
1369  } // repeats
1370 
1371  // recurrence exceptions
1372  if ( ( vo = isAPropertyOf( vtodo, VCExpDateProp ) ) != 0 ) {
1373  s = fakeCString( vObjectUStringZValue( vo ) );
1374  QStringList exDates = QString::fromUtf8( s ).split( ',' );
1375  QStringList::ConstIterator it;
1376  for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
1377  KDateTime exDate = ISOToKDateTime(*it);
1378  if ( exDate.time().hour() == 0 &&
1379  exDate.time().minute() == 0 &&
1380  exDate.time().second() == 0 ) {
1381  anEvent->recurrence()->addExDate( ISOToQDate( *it ) );
1382  } else {
1383  anEvent->recurrence()->addExDateTime( exDate );
1384  }
1385  }
1386  deleteStr( s );
1387  }
1388 
1389  // alarm stuff
1390  if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) {
1391  Alarm::Ptr alarm;
1392  VObject *a;
1393  VObject *b;
1394  a = isAPropertyOf( vo, VCRunTimeProp );
1395  b = isAPropertyOf( vo, VCDisplayStringProp );
1396 
1397  if ( a || b ) {
1398  alarm = anEvent->newAlarm();
1399  if ( a ) {
1400  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
1401  deleteStr( s );
1402  }
1403  alarm->setEnabled( true );
1404  if ( b ) {
1405  s = fakeCString( vObjectUStringZValue( b ) );
1406  alarm->setDisplayAlarm( QString( s ) );
1407  deleteStr( s );
1408  } else {
1409  alarm->setDisplayAlarm( QString() );
1410  }
1411  }
1412  }
1413 
1414  if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) {
1415  Alarm::Ptr alarm;
1416  VObject *a;
1417  VObject *b;
1418  a = isAPropertyOf( vo, VCRunTimeProp );
1419  b = isAPropertyOf( vo, VCAudioContentProp );
1420 
1421  if ( a || b ) {
1422  alarm = anEvent->newAlarm();
1423  if ( a ) {
1424  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
1425  deleteStr( s );
1426  }
1427  alarm->setEnabled( true );
1428  if ( b ) {
1429  s = fakeCString( vObjectUStringZValue( b ) );
1430  alarm->setAudioAlarm( QFile::decodeName( s ) );
1431  deleteStr( s );
1432  } else {
1433  alarm->setAudioAlarm( QString() );
1434  }
1435  }
1436  }
1437 
1438  if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) {
1439  Alarm::Ptr alarm;
1440  VObject *a;
1441  VObject *b;
1442  a = isAPropertyOf( vo, VCRunTimeProp );
1443  b = isAPropertyOf( vo, VCProcedureNameProp );
1444 
1445  if ( a || b ) {
1446  alarm = anEvent->newAlarm();
1447  if ( a ) {
1448  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
1449  deleteStr( s );
1450  }
1451  alarm->setEnabled( true );
1452 
1453  if ( b ) {
1454  s = fakeCString( vObjectUStringZValue( b ) );
1455  alarm->setProcedureAlarm( QFile::decodeName( s ) );
1456  deleteStr( s );
1457  } else {
1458  alarm->setProcedureAlarm( QString() );
1459  }
1460  }
1461  }
1462 
1463  // related todo
1464  if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) {
1465  anEvent->setRelatedTo( s = fakeCString( vObjectUStringZValue( vo ) ) );
1466  deleteStr( s );
1467  d->mTodosRelate.append( anEvent );
1468  }
1469 
1470  // secrecy
1471  Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
1472  if ( ( vo = isAPropertyOf( vtodo, VCClassProp ) ) != 0 ) {
1473  s = fakeCString( vObjectUStringZValue( vo ) );
1474  if ( s && strcmp( s, "PRIVATE" ) == 0 ) {
1475  secrecy = Incidence::SecrecyPrivate;
1476  } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) {
1477  secrecy = Incidence::SecrecyConfidential;
1478  }
1479  deleteStr( s );
1480  }
1481  anEvent->setSecrecy( secrecy );
1482 
1483  // categories
1484  if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) {
1485  s = fakeCString( vObjectUStringZValue( vo ) );
1486  QString categories = QString::fromUtf8( s );
1487  deleteStr( s );
1488  QStringList tmpStrList = categories.split( ';' );
1489  anEvent->setCategories( tmpStrList );
1490  }
1491 
1492  /* PILOT SYNC STUFF */
1493  if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) {
1494  anEvent->setNonKDECustomProperty(
1495  KPilotIdProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1496  deleteStr( s );
1497  if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) {
1498  anEvent->setNonKDECustomProperty(
1499  KPilotStatusProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1500  deleteStr( s );
1501  } else {
1502  anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( int( SYNCMOD ) ) );
1503  }
1504  }
1505 
1506  return anEvent;
1507 }
1508 
1509 Event::Ptr VCalFormat::VEventToEvent( VObject *vevent )
1510 {
1511  VObject *vo;
1512  VObjectIterator voi;
1513  char *s;
1514 
1515  Event::Ptr anEvent( new Event );
1516 
1517  // creation date
1518  if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) {
1519  anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1520  deleteStr( s );
1521  }
1522 
1523  // unique id
1524  vo = isAPropertyOf( vevent, VCUniqueStringProp );
1525  // while the UID property is preferred, it is not required. We'll use the
1526  // default Event UID if none is given.
1527  if ( vo ) {
1528  anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) );
1529  deleteStr( s );
1530  }
1531 
1532 #if defined(KCALCORE_FOR_SYMBIAN)
1533  // recurrence id
1534  vo = isAPropertyOf( vevent, VCRecurrenceIdProp );
1535  if ( vo ) {
1536  anEvent->setRecurrenceId( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1537  deleteStr( s );
1538  }
1539 #endif
1540 
1541  // revision
1542  // again NSCAL doesn't give us much to work with, so we improvise...
1543  anEvent->setRevision( 0 );
1544  if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) {
1545  s = fakeCString( vObjectUStringZValue( vo ) );
1546  if ( s ) {
1547  anEvent->setRevision( atoi( s ) );
1548  deleteStr( s );
1549  }
1550  }
1551 
1552  // last modification date
1553  if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) {
1554  anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1555  deleteStr( s );
1556  } else {
1557  anEvent->setLastModified( KDateTime::currentUtcDateTime() );
1558  }
1559 
1560  // organizer
1561  // if our extension property for the event's ORGANIZER exists, add it.
1562  if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) {
1563  // FIXME: Also use the full name, not just the email address
1564  anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) );
1565  deleteStr( s );
1566  } else {
1567  if ( d->mCalendar->owner()->name() != "Unknown Name" ) {
1568  anEvent->setOrganizer( d->mCalendar->owner() );
1569  }
1570  }
1571 
1572  // deal with attendees.
1573  initPropIterator( &voi, vevent );
1574  while ( moreIteration( &voi ) ) {
1575  vo = nextVObject( &voi );
1576  if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) {
1577  Attendee::Ptr a;
1578  VObject *vp;
1579  s = fakeCString( vObjectUStringZValue( vo ) );
1580  QString tmpStr = QString::fromUtf8( s );
1581  deleteStr( s );
1582  tmpStr = tmpStr.simplified();
1583  int emailPos1, emailPos2;
1584  if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) {
1585  // both email address and name
1586  emailPos2 = tmpStr.lastIndexOf( '>' );
1587  a = Attendee::Ptr( new Attendee( tmpStr.left( emailPos1 - 1 ),
1588  tmpStr.mid( emailPos1 + 1,
1589  emailPos2 - ( emailPos1 + 1 ) ) ) );
1590  } else if ( tmpStr.indexOf( '@' ) > 0 ) {
1591  // just an email address
1592  a = Attendee::Ptr( new Attendee( 0, tmpStr ) );
1593  } else {
1594  // just a name
1595  QString email = tmpStr.replace( ' ', '.' );
1596  a = Attendee::Ptr( new Attendee( tmpStr, email ) );
1597  }
1598 
1599  // is there an RSVP property?
1600  if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) {
1601  a->setRSVP( vObjectStringZValue( vp ) );
1602  }
1603  // is there a status property?
1604  if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) {
1605  a->setStatus( readStatus( vObjectStringZValue( vp ) ) );
1606  }
1607  // add the attendee
1608  anEvent->addAttendee( a );
1609  }
1610  }
1611 
1612  // This isn't strictly true. An event that doesn't have a start time
1613  // or an end time isn't all-day, it has an anchor in time but it doesn't
1614  // "take up" any time.
1615  /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
1616  (isAPropertyOf(vevent, VCDTendProp) == 0)) {
1617  anEvent->setAllDay(true);
1618  } else {
1619  }*/
1620 
1621  anEvent->setAllDay( false );
1622 
1623  // start time
1624  if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) {
1625  anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1626  deleteStr( s );
1627 
1628  if ( anEvent->dtStart().time().hour() == 0 &&
1629  anEvent->dtStart().time().minute() == 0 &&
1630  anEvent->dtStart().time().second() == 0 ) {
1631 #if defined(KCALCORE_FOR_MEEGO)
1632  QDate startDate = anEvent->dtStart().date();
1633  anEvent->setDtStart( KDateTime( startDate, KDateTime::ClockTime ) );
1634 #endif
1635  anEvent->setAllDay( true );
1636  }
1637  }
1638 
1639  // stop time
1640  if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) {
1641  anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
1642  deleteStr( s );
1643 
1644  if ( anEvent->dtEnd().time().hour() == 0 &&
1645  anEvent->dtEnd().time().minute() == 0 &&
1646  anEvent->dtEnd().time().second() == 0 ) {
1647 #if defined(KCALCORE_FOR_MEEGO)
1648  QDate endDate = anEvent->dtEnd().date();
1649  anEvent->setDtEnd( KDateTime( endDate, KDateTime::ClockTime ) );
1650 #endif
1651  anEvent->setAllDay( true );
1652  }
1653  }
1654 #if defined(KCALCORE_FOR_MEEGO)
1655  if ( anEvent->allDay() ) {
1656  if ( anEvent->dtEnd() == anEvent->dtStart() ) {
1657  anEvent->setDtEnd( anEvent->dtEnd().addDays( 1 ) );
1658  }
1659  }
1660 #endif
1661 
1662  // at this point, there should be at least a start or end time.
1663  // fix up for events that take up no time but have a time associated
1664  if ( !isAPropertyOf( vevent, VCDTstartProp ) ) {
1665  anEvent->setDtStart( anEvent->dtEnd() );
1666  }
1667  if ( ! isAPropertyOf( vevent, VCDTendProp ) ) {
1668  anEvent->setDtEnd( anEvent->dtStart() );
1669  }
1670 
1672 
1673  // repeat stuff
1674  if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) {
1675  QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
1676  deleteStr( s );
1677  tmpStr.simplified();
1678  tmpStr = tmpStr.toUpper();
1679  // first, read the type of the recurrence
1680  int typelen = 1;
1681  uint type = Recurrence::rNone;
1682  if ( tmpStr.left(1) == "D" ) {
1683  type = Recurrence::rDaily;
1684  } else if ( tmpStr.left(1) == "W" ) {
1685  type = Recurrence::rWeekly;
1686  } else {
1687  typelen = 2;
1688  if ( tmpStr.left(2) == "MP" ) {
1689  type = Recurrence::rMonthlyPos;
1690  } else if ( tmpStr.left(2) == "MD" ) {
1691  type = Recurrence::rMonthlyDay;
1692  } else if ( tmpStr.left(2) == "YM" ) {
1693  type = Recurrence::rYearlyMonth;
1694  } else if ( tmpStr.left(2) == "YD" ) {
1695  type = Recurrence::rYearlyDay;
1696  }
1697  }
1698 
1699  if ( type != Recurrence::rNone ) {
1700 
1701  // Immediately after the type is the frequency
1702  int index = tmpStr.indexOf( ' ' );
1703  int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry
1704  int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt();
1705  ++index; // advance to beginning of stuff after freq
1706 
1707  // Read the type-specific settings
1708  switch ( type ) {
1709  case Recurrence::rDaily:
1710  anEvent->recurrence()->setDaily(rFreq);
1711  break;
1712 
1713  case Recurrence::rWeekly:
1714  {
1715  QBitArray qba(7);
1716  QString dayStr;
1717  if ( index == last ) {
1718  // e.g. W1 #0
1719  qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
1720  } else {
1721  // e.g. W1 SU #0
1722  while ( index < last ) {
1723  dayStr = tmpStr.mid( index, 3 );
1724  int dayNum = numFromDay( dayStr );
1725  if ( dayNum >= 0 ) {
1726  qba.setBit( dayNum );
1727  }
1728  index += 3; // advance to next day, or possibly "#"
1729  }
1730  }
1731  anEvent->recurrence()->setWeekly( rFreq, qba );
1732  break;
1733  }
1734 
1735  case Recurrence::rMonthlyPos:
1736  {
1737  anEvent->recurrence()->setMonthly( rFreq );
1738 
1739  QBitArray qba(7);
1740  short tmpPos;
1741  if ( index == last ) {
1742  // e.g. MP1 #0
1743  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
1744  if ( tmpPos == 5 ) {
1745  tmpPos = -1;
1746  }
1747  qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 );
1748  anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
1749  } else {
1750  // e.g. MP1 1+ SU #0
1751  while ( index < last ) {
1752  tmpPos = tmpStr.mid( index, 1 ).toShort();
1753  index += 1;
1754  if ( tmpStr.mid( index, 1 ) == "-" ) {
1755  // convert tmpPos to negative
1756  tmpPos = 0 - tmpPos;
1757  }
1758  index += 2; // advance to day(s)
1759  while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) {
1760  int dayNum = numFromDay( tmpStr.mid( index, 3 ) );
1761  qba.setBit( dayNum );
1762  index += 3; // advance to next day, or possibly pos or "#"
1763  }
1764  anEvent->recurrence()->addMonthlyPos( tmpPos, qba );
1765  qba.detach();
1766  qba.fill( false ); // clear out
1767  } // while != "#"
1768  }
1769  break;
1770  }
1771 
1772  case Recurrence::rMonthlyDay:
1773  anEvent->recurrence()->setMonthly( rFreq );
1774  if( index == last ) {
1775  // e.g. MD1 #0
1776  short tmpDay = anEvent->dtStart().date().day();
1777  anEvent->recurrence()->addMonthlyDate( tmpDay );
1778  } else {
1779  // e.g. MD1 3 #0
1780  while ( index < last ) {
1781  int index2 = tmpStr.indexOf( ' ', index );
1782  if ( ( tmpStr.mid( ( index2 - 1 ), 1 ) == "-" ) ||
1783  ( tmpStr.mid( ( index2 - 1 ), 1 ) == "+" ) ) {
1784  index2 = index2 - 1;
1785  }
1786  short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
1787  index = index2;
1788  if ( tmpStr.mid( index, 1 ) == "-" ) {
1789  tmpDay = 0 - tmpDay;
1790  }
1791  index += 2; // advance the index;
1792  anEvent->recurrence()->addMonthlyDate( tmpDay );
1793  } // while != #
1794  }
1795  break;
1796 
1797  case Recurrence::rYearlyMonth:
1798  anEvent->recurrence()->setYearly( rFreq );
1799 
1800  if ( index == last ) {
1801  // e.g. YM1 #0
1802  short tmpMonth = anEvent->dtStart().date().month();
1803  anEvent->recurrence()->addYearlyMonth( tmpMonth );
1804  } else {
1805  // e.g. YM1 3 #0
1806  while ( index < last ) {
1807  int index2 = tmpStr.indexOf( ' ', index );
1808  short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort();
1809  index = index2 + 1;
1810  anEvent->recurrence()->addYearlyMonth( tmpMonth );
1811  } // while != #
1812  }
1813  break;
1814 
1815  case Recurrence::rYearlyDay:
1816  anEvent->recurrence()->setYearly( rFreq );
1817 
1818  if ( index == last ) {
1819  // e.g. YD1 #0
1820  short tmpDay = anEvent->dtStart().date().dayOfYear();
1821  anEvent->recurrence()->addYearlyDay( tmpDay );
1822  } else {
1823  // e.g. YD1 123 #0
1824  while ( index < last ) {
1825  int index2 = tmpStr.indexOf( ' ', index );
1826  short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort();
1827  index = index2 + 1;
1828  anEvent->recurrence()->addYearlyDay( tmpDay );
1829  } // while != #
1830  }
1831  break;
1832 
1833  default:
1834  break;
1835  }
1836 
1837  // find the last field, which is either the duration or the end date
1838  index = last;
1839  if ( tmpStr.mid( index, 1 ) == "#" ) {
1840  // Nr of occurrences
1841  index++;
1842  int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt();
1843  if ( rDuration > 0 ) {
1844  anEvent->recurrence()->setDuration( rDuration );
1845  }
1846  } else if ( tmpStr.indexOf( 'T', index ) != -1 ) {
1847  KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) );
1848  anEvent->recurrence()->setEndDateTime( rEndDate );
1849  }
1850 // anEvent->recurrence()->dump();
1851 
1852  } else {
1853  kDebug() << "we don't understand this type of recurrence!";
1854  } // if known recurrence type
1855  } // repeats
1856 
1857  // recurrence exceptions
1858  if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) {
1859  s = fakeCString( vObjectUStringZValue( vo ) );
1860  QStringList exDates = QString::fromUtf8( s ).split( ',' );
1861  QStringList::ConstIterator it;
1862  for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) {
1863  KDateTime exDate = ISOToKDateTime(*it);
1864  if ( exDate.time().hour() == 0 &&
1865  exDate.time().minute() == 0 &&
1866  exDate.time().second() == 0 ) {
1867  anEvent->recurrence()->addExDate( ISOToQDate( *it ) );
1868  } else {
1869  anEvent->recurrence()->addExDateTime( exDate );
1870  }
1871  }
1872  deleteStr( s );
1873  }
1874 
1875  // summary
1876  if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) {
1877  s = fakeCString( vObjectUStringZValue( vo ) );
1878  anEvent->setSummary( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
1879  deleteStr( s );
1880  }
1881 
1882  // description
1883  if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) {
1884  s = fakeCString( vObjectUStringZValue( vo ) );
1885  bool isRich = Qt::mightBeRichText( s );
1886  if ( !anEvent->description().isEmpty() ) {
1887  anEvent->setDescription(
1888  anEvent->description() + '\n' + QString::fromUtf8( s ), isRich );
1889  } else {
1890  anEvent->setDescription( QString::fromUtf8( s ), isRich );
1891  }
1892  deleteStr( s );
1893  }
1894 
1895  // location
1896  if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) {
1897  s = fakeCString( vObjectUStringZValue( vo ) );
1898  anEvent->setLocation( QString::fromUtf8( s ), Qt::mightBeRichText( s ) );
1899  deleteStr( s );
1900  }
1901 
1902  // some stupid vCal exporters ignore the standard and use Description
1903  // instead of Summary for the default field. Correct for this.
1904  if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) {
1905  QString tmpStr = anEvent->description().simplified();
1906  anEvent->setDescription( "" );
1907  anEvent->setSummary( tmpStr );
1908  }
1909 
1910 #if 0
1911  // status
1912  if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) {
1913  QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) );
1914  deleteStr( s );
1915 // TODO: Define Event status
1916 // anEvent->setStatus( tmpStr );
1917  } else {
1918 // anEvent->setStatus( "NEEDS ACTION" );
1919  }
1920 #endif
1921 
1922  // secrecy
1923  Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
1924  if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) {
1925  s = fakeCString( vObjectUStringZValue( vo ) );
1926  if ( s && strcmp( s, "PRIVATE" ) == 0 ) {
1927  secrecy = Incidence::SecrecyPrivate;
1928  } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) {
1929  secrecy = Incidence::SecrecyConfidential;
1930  }
1931  deleteStr( s );
1932  }
1933  anEvent->setSecrecy( secrecy );
1934 
1935  // categories
1936  if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) {
1937  s = fakeCString( vObjectUStringZValue( vo ) );
1938  QString categories = QString::fromUtf8( s );
1939  deleteStr( s );
1940  QStringList tmpStrList = categories.split( ',' );
1941  anEvent->setCategories( tmpStrList );
1942  }
1943 
1944  // attachments
1945  initPropIterator( &voi, vevent );
1946  while ( moreIteration( &voi ) ) {
1947  vo = nextVObject( &voi );
1948  if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) {
1949  s = fakeCString( vObjectUStringZValue( vo ) );
1950  anEvent->addAttachment( Attachment::Ptr( new Attachment( QString( s ) ) ) );
1951  deleteStr( s );
1952  }
1953  }
1954 
1955  // resources
1956  if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) {
1957  QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) );
1958  deleteStr( s );
1959  QStringList tmpStrList = resources.split( ';' );
1960  anEvent->setResources( tmpStrList );
1961  }
1962 
1963  // alarm stuff
1964  if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) {
1965  Alarm::Ptr alarm;
1966  VObject *a;
1967  VObject *b;
1968  a = isAPropertyOf( vo, VCRunTimeProp );
1969  b = isAPropertyOf( vo, VCDisplayStringProp );
1970 
1971  if ( a || b ) {
1972  alarm = anEvent->newAlarm();
1973  if ( a ) {
1974  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
1975  deleteStr( s );
1976  }
1977  alarm->setEnabled( true );
1978 
1979  if ( b ) {
1980  s = fakeCString( vObjectUStringZValue( b ) );
1981  alarm->setDisplayAlarm( QString( s ) );
1982  deleteStr( s );
1983  } else {
1984  alarm->setDisplayAlarm( QString() );
1985  }
1986  }
1987  }
1988 
1989  if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) {
1990  Alarm::Ptr alarm;
1991  VObject *a;
1992  VObject *b;
1993  a = isAPropertyOf( vo, VCRunTimeProp );
1994  b = isAPropertyOf( vo, VCAudioContentProp );
1995 
1996  if ( a || b ) {
1997  alarm = anEvent->newAlarm();
1998  if ( a ) {
1999  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
2000  deleteStr( s );
2001  }
2002  alarm->setEnabled( true );
2003 
2004  if ( b ) {
2005  s = fakeCString( vObjectUStringZValue( b ) );
2006  alarm->setAudioAlarm( QFile::decodeName( s ) );
2007  deleteStr( s );
2008  } else {
2009  alarm->setAudioAlarm( QString() );
2010  }
2011  }
2012  }
2013 
2014  if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) {
2015  Alarm::Ptr alarm;
2016  VObject *a;
2017  VObject *b;
2018  a = isAPropertyOf( vo, VCRunTimeProp );
2019  b = isAPropertyOf( vo, VCProcedureNameProp );
2020 
2021  if ( a || b ) {
2022  alarm = anEvent->newAlarm();
2023  if ( a ) {
2024  alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) );
2025  deleteStr( s );
2026  }
2027  alarm->setEnabled( true );
2028 
2029  if ( b ) {
2030  s = fakeCString( vObjectUStringZValue( b ) );
2031  alarm->setProcedureAlarm( QFile::decodeName( s ) );
2032  deleteStr( s );
2033  } else {
2034  alarm->setProcedureAlarm( QString() );
2035  }
2036  }
2037  }
2038 
2039  // priority
2040  if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) {
2041  s = fakeCString( vObjectUStringZValue( vo ) );
2042  if ( s ) {
2043  anEvent->setPriority( atoi( s ) );
2044  deleteStr( s );
2045  }
2046  }
2047 
2048  // transparency
2049  if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) {
2050  s = fakeCString( vObjectUStringZValue( vo ) );
2051  if ( s ) {
2052  int i = atoi( s );
2053  anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque );
2054  deleteStr( s );
2055  }
2056  }
2057 
2058  // related event
2059  if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) {
2060  anEvent->setRelatedTo( s = fakeCString( vObjectUStringZValue( vo ) ) );
2061  deleteStr( s );
2062  d->mEventsRelate.append( anEvent );
2063  }
2064 
2065  /* PILOT SYNC STUFF */
2066  if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) {
2067  anEvent->setNonKDECustomProperty(
2068  KPilotIdProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
2069  deleteStr( s );
2070  if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) {
2071  anEvent->setNonKDECustomProperty(
2072  KPilotStatusProp, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( vo ) ) ) );
2073  deleteStr( s );
2074  } else {
2075  anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( int( SYNCMOD ) ) );
2076  }
2077  }
2078 
2079  /* Rest of the custom properties */
2080  readCustomProperties( vevent, anEvent );
2081 
2082  return anEvent;
2083 }
2084 
2085 QString VCalFormat::parseTZ( const QByteArray &timezone ) const
2086 {
2087  qDebug() << timezone;
2088  QString pZone = timezone.mid( timezone.indexOf( "TZID:VCAL" ) + 9 );
2089  return pZone.mid( 0, pZone.indexOf( QChar( QLatin1Char( '\n' ) ) ) );
2090 }
2091 
2092 QString VCalFormat::parseDst( QByteArray &timezone ) const
2093 {
2094  if ( !timezone.contains( "BEGIN:DAYLIGHT" ) ) {
2095  return QString();
2096  }
2097 
2098  timezone = timezone.mid( timezone.indexOf( "BEGIN:DAYLIGHT" ) );
2099  timezone = timezone.mid( timezone.indexOf( "TZNAME:" ) + 7 );
2100  QString sStart = timezone.mid( 0, ( timezone.indexOf( "COMMENT:" ) ) );
2101  sStart.chop( 2 );
2102  timezone = timezone.mid( timezone.indexOf( "TZOFFSETTO:" ) + 11 );
2103  QString sOffset = timezone.mid( 0, ( timezone.indexOf( "DTSTART:" ) ) );
2104  sOffset.chop( 2 );
2105  sOffset.insert( 3, QString( ":" ) );
2106  timezone = timezone.mid( timezone.indexOf( "TZNAME:" ) + 7 );
2107  QString sEnd = timezone.mid( 0, ( timezone.indexOf( "COMMENT:" ) ) );
2108  sEnd.chop( 2 );
2109 
2110  return "TRUE;" + sOffset + ';' + sStart + ';' + sEnd + ";;";
2111 }
2112 
2113 QString VCalFormat::qDateToISO( const QDate &qd )
2114 {
2115  QString tmpStr;
2116 
2117  if ( !qd.isValid() ) {
2118  return QString();
2119  }
2120 
2121  tmpStr.sprintf( "%.2d%.2d%.2d", qd.year(), qd.month(), qd.day() );
2122  return tmpStr;
2123 
2124 }
2125 
2126 QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu )
2127 {
2128  QString tmpStr;
2129 
2130  if ( !dt.isValid() ) {
2131  return QString();
2132  }
2133 
2134  QDateTime tmpDT;
2135  if ( zulu ) {
2136  tmpDT = dt.toUtc().dateTime();
2137  } else {
2138 #if !defined(KCALCORE_FOR_MEEGO)
2139  tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime();
2140 #else
2141  tmpDT = dt.dateTime();
2142 #endif
2143  }
2144  tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d",
2145  tmpDT.date().year(), tmpDT.date().month(),
2146  tmpDT.date().day(), tmpDT.time().hour(),
2147  tmpDT.time().minute(), tmpDT.time().second() );
2148  if ( zulu || dt.isUtc() ) {
2149  tmpStr += 'Z';
2150  }
2151  return tmpStr;
2152 }
2153 
2154 KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr )
2155 {
2156  QDate tmpDate;
2157  QTime tmpTime;
2158  QString tmpStr;
2159  int year, month, day, hour, minute, second;
2160 
2161  tmpStr = dtStr;
2162  year = tmpStr.left( 4 ).toInt();
2163  month = tmpStr.mid( 4, 2 ).toInt();
2164  day = tmpStr.mid( 6, 2 ).toInt();
2165  hour = tmpStr.mid( 9, 2 ).toInt();
2166  minute = tmpStr.mid( 11, 2 ).toInt();
2167  second = tmpStr.mid( 13, 2 ).toInt();
2168  tmpDate.setYMD( year, month, day );
2169  tmpTime.setHMS( hour, minute, second );
2170 
2171  if ( tmpDate.isValid() && tmpTime.isValid() ) {
2172  // correct for GMT if string is in Zulu format
2173  if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) {
2174  return KDateTime( tmpDate, tmpTime, KDateTime::UTC );
2175  } else {
2176  return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() );
2177  }
2178  } else {
2179  return KDateTime();
2180  }
2181 }
2182 
2183 QDate VCalFormat::ISOToQDate( const QString &dateStr )
2184 {
2185  int year, month, day;
2186 
2187  year = dateStr.left( 4 ).toInt();
2188  month = dateStr.mid( 4, 2 ).toInt();
2189  day = dateStr.mid( 6, 2 ).toInt();
2190 
2191  return QDate( year, month, day );
2192 }
2193 
2194 bool VCalFormat::parseTZOffsetISO8601( const QString &s, int &result )
2195 {
2196  // ISO8601 format(s):
2197  // +- hh : mm
2198  // +- hh mm
2199  // +- hh
2200 
2201  // We also accept broken one without +
2202  int mod = 1;
2203  int v = 0;
2204  QString str = s.trimmed();
2205  int ofs = 0;
2206  result = 0;
2207 
2208  // Check for end
2209  if ( str.size() <= ofs ) {
2210  return false;
2211  }
2212  if ( str[ofs] == '-' ) {
2213  mod = -1;
2214  ofs++;
2215  } else if ( str[ofs] == '+' ) {
2216  ofs++;
2217  }
2218  if ( str.size() <= ofs ) {
2219  return false;
2220  }
2221 
2222  // Make sure next two values are numbers
2223  bool ok;
2224 
2225  if ( str.size() < ( ofs + 2 ) ) {
2226  return false;
2227  }
2228 
2229  v = str.mid( ofs, 2 ).toInt( &ok ) * 60;
2230  if ( !ok ) {
2231  return false;
2232  }
2233  ofs += 2;
2234 
2235  if ( str.size() > ofs ) {
2236  if ( str[ofs] == ':' ) {
2237  ofs++;
2238  }
2239  if ( str.size() > ofs ) {
2240  if ( str.size() < ( ofs + 2 ) ) {
2241  return false;
2242  }
2243  v += str.mid( ofs, 2 ).toInt( &ok );
2244  if ( !ok ) {
2245  return false;
2246  }
2247  }
2248  }
2249  result = v * mod * 60;
2250  return true;
2251 }
2252 
2253 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
2254 // and break it down from it's tree-like format into the dictionary format
2255 // that is used internally in the VCalFormat.
2256 void VCalFormat::populate( VObject *vcal, bool deleted, const QString &notebook )
2257 {
2258  Q_UNUSED( notebook );
2259  // this function will populate the caldict dictionary and other event
2260  // lists. It turns vevents into Events and then inserts them.
2261 
2262  VObjectIterator i;
2263  VObject *curVO, *curVOProp;
2264  Event::Ptr anEvent;
2265  bool hasTimeZone = false; //The calendar came with a TZ and not UTC
2266  KDateTime::Spec previousSpec; //If we add a new TZ we should leave the spec as it was before
2267 
2268  if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) {
2269  char *methodType = 0;
2270  methodType = fakeCString( vObjectUStringZValue( curVO ) );
2271  kDebug() << "This calendar is an iTIP transaction of type '"
2272  << methodType << "'";
2273  deleteStr( methodType );
2274  }
2275 
2276  // warn the user that we might have trouble reading non-known calendar.
2277  if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) {
2278  char *s = fakeCString( vObjectUStringZValue( curVO ) );
2279  if ( !s || strcmp( productId().toUtf8(), s ) != 0 ) {
2280  kDebug() << "This vCalendar file was not created by KOrganizer or"
2281  << "any other product we support. Loading anyway...";
2282  }
2283  setLoadedProductId( s );
2284  deleteStr( s );
2285  }
2286 
2287  // warn the user we might have trouble reading this unknown version.
2288  if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) {
2289  char *s = fakeCString( vObjectUStringZValue( curVO ) );
2290  if ( !s || strcmp( _VCAL_VERSION, s ) != 0 ) {
2291  kDebug() << "This vCalendar file has version" << s
2292  << "We only support" << _VCAL_VERSION;
2293  }
2294  deleteStr( s );
2295  }
2296 
2297  // set the time zone (this is a property of the view, so just discard!)
2298  if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) {
2299  char *s = fakeCString( vObjectUStringZValue( curVO ) );
2300  QString ts( s );
2301  QString name = "VCAL" + ts;
2302  deleteStr( s );
2303 
2304  // TODO: While using the timezone-offset + vcal as timezone is is
2305  // most likely unique, we should REALLY actually create something
2306  // like vcal-tzoffset-daylightoffsets, or better yet,
2307  // vcal-hash<the former>
2308 
2309  QStringList tzList;
2310  QString tz;
2311  int utcOffset;
2312  int utcOffsetDst;
2313  if ( parseTZOffsetISO8601( ts, utcOffset ) ) {
2314  kDebug() << "got standard offset" << ts << utcOffset;
2315  // standard from tz
2316  // starting date for now 01011900
2317  KDateTime dt = KDateTime( QDateTime( QDate( 1900, 1, 1 ), QTime( 0, 0, 0 ) ) );
2318  tz = QString( "STD;%1;false;%2" ).arg( QString::number( utcOffset ) ).arg( dt.toString() );
2319  tzList.append( tz );
2320 
2321  // go through all the daylight tags
2322  initPropIterator( &i, vcal );
2323  while ( moreIteration( &i ) ) {
2324  curVO = nextVObject( &i );
2325  if ( strcmp( vObjectName( curVO ), VCDayLightProp ) == 0 ) {
2326  char *s = fakeCString( vObjectUStringZValue( curVO ) );
2327  QString dst = QString( s );
2328  QStringList argl = dst.split( ',' );
2329  deleteStr( s );
2330 
2331  // Too short -> not interesting
2332  if ( argl.size() < 4 ) {
2333  continue;
2334  }
2335 
2336  // We don't care about the non-DST periods
2337  if ( argl[0] != "TRUE" ) {
2338  continue;
2339  }
2340 
2341  if ( parseTZOffsetISO8601( argl[1], utcOffsetDst ) ) {
2342 
2343  kDebug() << "got DST offset" << argl[1] << utcOffsetDst;
2344  // standard
2345  QString strEndDate = argl[3];
2346  KDateTime endDate = ISOToKDateTime( strEndDate );
2347  // daylight
2348  QString strStartDate = argl[2];
2349  KDateTime startDate = ISOToKDateTime( strStartDate );
2350 
2351  QString strRealEndDate = strEndDate;
2352  QString strRealStartDate = strStartDate;
2353  KDateTime realEndDate = endDate;
2354  KDateTime realStartDate = startDate;
2355  // if we get dates for some reason in wrong order, earlier is used for dst
2356  if ( endDate < startDate ) {
2357  strRealEndDate = strStartDate;
2358  strRealStartDate = strEndDate;
2359  realEndDate = startDate;
2360  realStartDate = endDate;
2361  }
2362  tz = QString( "%1;%2;false;%3" ).
2363  arg( strRealEndDate ).
2364  arg( QString::number( utcOffset ) ).
2365  arg( realEndDate.toString() );
2366  tzList.append( tz );
2367 
2368  tz = QString( "%1;%2;true;%3" ).
2369  arg( strRealStartDate ).
2370  arg( QString::number( utcOffsetDst ) ).
2371  arg( realStartDate.toString() );
2372  tzList.append( tz );
2373  } else {
2374  kDebug() << "unable to parse dst" << argl[1];
2375  }
2376  }
2377  }
2378  ICalTimeZones *tzlist = d->mCalendar->timeZones();
2379  ICalTimeZoneSource tzs;
2380  ICalTimeZone zone = tzs.parse( name, tzList, *tzlist );
2381  if ( !zone.isValid() ) {
2382  kDebug() << "zone is not valid, parsing error" << tzList;
2383  } else {
2384  previousSpec = d->mCalendar->timeSpec();
2385  d->mCalendar->setTimeZoneId( name );
2386  hasTimeZone = true;
2387  }
2388  } else {
2389  kDebug() << "unable to parse tzoffset" << ts;
2390  }
2391  }
2392 
2393  // Store all events with a relatedTo property in a list for post-processing
2394  d->mEventsRelate.clear();
2395  d->mTodosRelate.clear();
2396 
2397  initPropIterator( &i, vcal );
2398 
2399  // go through all the vobjects in the vcal
2400  while ( moreIteration( &i ) ) {
2401  curVO = nextVObject( &i );
2402 
2403  /************************************************************************/
2404 
2405  // now, check to see that the object is an event or todo.
2406  if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) {
2407 
2408  if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) {
2409  char *s;
2410  s = fakeCString( vObjectUStringZValue( curVOProp ) );
2411  // check to see if event was deleted by the kpilot conduit
2412  if ( s ) {
2413  if ( atoi( s ) == SYNCDEL ) {
2414  deleteStr( s );
2415  kDebug() << "skipping pilot-deleted event";
2416  goto SKIP;
2417  }
2418  deleteStr( s );
2419  }
2420  }
2421 
2422  if ( !isAPropertyOf( curVO, VCDTstartProp ) &&
2423  !isAPropertyOf( curVO, VCDTendProp ) ) {
2424  kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping...";
2425  goto SKIP;
2426  }
2427 
2428  anEvent = VEventToEvent( curVO );
2429  if ( anEvent ) {
2430  if ( hasTimeZone && !anEvent->allDay() && anEvent->dtStart().isUtc() ) {
2431  //This sounds stupid but is how others are doing it, so here
2432  //we go. If there is a TZ in the VCALENDAR even if the dtStart
2433  //and dtend are in UTC, clients interpret it using also the TZ defined
2434  //in the Calendar. I know it sounds braindead but oh well
2435  int utcOffSet = anEvent->dtStart().utcOffset();
2436  KDateTime dtStart( anEvent->dtStart().dateTime().addSecs( utcOffSet ),
2437  d->mCalendar->timeSpec() );
2438  KDateTime dtEnd( anEvent->dtEnd().dateTime().addSecs( utcOffSet ),
2439  d->mCalendar->timeSpec() );
2440  anEvent->setDtStart( dtStart );
2441  anEvent->setDtEnd( dtEnd );
2442  }
2443  Event::Ptr old = !anEvent->hasRecurrenceId() ?
2444  d->mCalendar->event( anEvent->uid() ) :
2445  d->mCalendar->event( anEvent->uid(), anEvent->recurrenceId() );
2446 
2447  if ( old ) {
2448  if ( deleted ) {
2449  d->mCalendar->deleteEvent( old ); // move old to deleted
2450  removeAllVCal( d->mEventsRelate, old );
2451  } else if ( anEvent->revision() > old->revision() ) {
2452  d->mCalendar->deleteEvent( old ); // move old to deleted
2453  removeAllVCal( d->mEventsRelate, old );
2454  d->mCalendar->addEvent( anEvent ); // and replace it with this one
2455  }
2456  } else if ( deleted ) {
2457  old = !anEvent->hasRecurrenceId() ?
2458  d->mCalendar->deletedEvent( anEvent->uid() ) :
2459  d->mCalendar->deletedEvent( anEvent->uid(), anEvent->recurrenceId() );
2460  if ( !old ) {
2461  d->mCalendar->addEvent( anEvent ); // add this one
2462  d->mCalendar->deleteEvent( anEvent ); // and move it to deleted
2463  }
2464  } else {
2465  d->mCalendar->addEvent( anEvent ); // just add this one
2466  }
2467  }
2468  } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) {
2469  Todo::Ptr aTodo = VTodoToEvent( curVO );
2470  if ( aTodo ) {
2471  if ( hasTimeZone && !aTodo->allDay() && aTodo->dtStart().isUtc() ) {
2472  //This sounds stupid but is how others are doing it, so here
2473  //we go. If there is a TZ in the VCALENDAR even if the dtStart
2474  //and dtend are in UTC, clients interpret it usint alse the TZ defined
2475  //in the Calendar. I know it sounds braindead but oh well
2476  int utcOffSet = aTodo->dtStart().utcOffset();
2477  KDateTime dtStart( aTodo->dtStart().dateTime().addSecs( utcOffSet ),
2478  d->mCalendar->timeSpec() );
2479  aTodo->setDtStart( dtStart );
2480  if ( aTodo->hasDueDate() ) {
2481  KDateTime dtDue( aTodo->dtDue().dateTime().addSecs( utcOffSet ),
2482  d->mCalendar->timeSpec() );
2483  aTodo->setDtDue( dtDue );
2484  }
2485  }
2486  Todo::Ptr old = !aTodo->hasRecurrenceId() ?
2487  d->mCalendar->todo( aTodo->uid() ) :
2488  d->mCalendar->todo( aTodo->uid(), aTodo->recurrenceId() );
2489  if ( old ) {
2490  if ( deleted ) {
2491  d->mCalendar->deleteTodo( old ); // move old to deleted
2492  removeAllVCal( d->mTodosRelate, old );
2493  } else if ( aTodo->revision() > old->revision() ) {
2494  d->mCalendar->deleteTodo( old ); // move old to deleted
2495  removeAllVCal( d->mTodosRelate, old );
2496  d->mCalendar->addTodo( aTodo ); // and replace it with this one
2497  }
2498  } else if ( deleted ) {
2499  old = d->mCalendar->deletedTodo( aTodo->uid(), aTodo->recurrenceId() );
2500  if ( !old ) {
2501  d->mCalendar->addTodo( aTodo ); // add this one
2502  d->mCalendar->deleteTodo( aTodo ); // and move it to deleted
2503  }
2504  } else {
2505  d->mCalendar->addTodo( aTodo ); // just add this one
2506  }
2507  }
2508  } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) ||
2509  ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) ||
2510  ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) {
2511  // do nothing, we know these properties and we want to skip them.
2512  // we have either already processed them or are ignoring them.
2513  ;
2514  } else if ( strcmp( vObjectName( curVO ), VCDayLightProp ) == 0 ) {
2515  // do nothing daylights are already processed
2516  ;
2517  } else {
2518  kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
2519  }
2520  SKIP:
2521  ;
2522  } // while
2523 
2524  // Post-Process list of events with relations, put Event objects in relation
2525  Event::List::ConstIterator eIt;
2526  for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) {
2527  (*eIt)->setRelatedTo( (*eIt)->relatedTo() );
2528  }
2529  Todo::List::ConstIterator tIt;
2530  for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) {
2531  (*tIt)->setRelatedTo( (*tIt)->relatedTo() );
2532  }
2533 
2534  //Now lets put the TZ back as it was if we have changed it.
2535  if ( hasTimeZone ) {
2536  d->mCalendar->setTimeSpec(previousSpec);
2537  }
2538 
2539 }
2540 
2541 const char *VCalFormat::dayFromNum( int day )
2542 {
2543  const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
2544 
2545  return days[day];
2546 }
2547 
2548 int VCalFormat::numFromDay( const QString &day )
2549 {
2550  if ( day == "MO " ) {
2551  return 0;
2552  }
2553  if ( day == "TU " ) {
2554  return 1;
2555  }
2556  if ( day == "WE " ) {
2557  return 2;
2558  }
2559  if ( day == "TH " ) {
2560  return 3;
2561  }
2562  if ( day == "FR " ) {
2563  return 4;
2564  }
2565  if ( day == "SA " ) {
2566  return 5;
2567  }
2568  if ( day == "SU " ) {
2569  return 6;
2570  }
2571 
2572  return -1; // something bad happened. :)
2573 }
2574 
2575 Attendee::PartStat VCalFormat::readStatus( const char *s ) const
2576 {
2577  QString statStr = s;
2578  statStr = statStr.toUpper();
2579  Attendee::PartStat status;
2580 
2581  if ( statStr == "X-ACTION" ) {
2582  status = Attendee::NeedsAction;
2583  } else if ( statStr == "NEEDS ACTION" ) {
2584  status = Attendee::NeedsAction;
2585  } else if ( statStr == "ACCEPTED" ) {
2586  status = Attendee::Accepted;
2587  } else if ( statStr == "SENT" ) {
2588  status = Attendee::NeedsAction;
2589  } else if ( statStr == "TENTATIVE" ) {
2590  status = Attendee::Tentative;
2591  } else if ( statStr == "CONFIRMED" ) {
2592  status = Attendee::Accepted;
2593  } else if ( statStr == "DECLINED" ) {
2594  status = Attendee::Declined;
2595  } else if ( statStr == "COMPLETED" ) {
2596  status = Attendee::Completed;
2597  } else if ( statStr == "DELEGATED" ) {
2598  status = Attendee::Delegated;
2599  } else {
2600  kDebug() << "error setting attendee mStatus, unknown mStatus!";
2601  status = Attendee::NeedsAction;
2602  }
2603 
2604  return status;
2605 }
2606 
2607 QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const
2608 {
2609  switch( status ) {
2610  default:
2611  case Attendee::NeedsAction:
2612  return "NEEDS ACTION";
2613  break;
2614  case Attendee::Accepted:
2615  return "ACCEPTED";
2616  break;
2617  case Attendee::Declined:
2618  return "DECLINED";
2619  break;
2620  case Attendee::Tentative:
2621  return "TENTATIVE";
2622  break;
2623  case Attendee::Delegated:
2624  return "DELEGATED";
2625  break;
2626  case Attendee::Completed:
2627  return "COMPLETED";
2628  break;
2629  case Attendee::InProcess:
2630  return "NEEDS ACTION";
2631  break;
2632  }
2633 }
2634 
2635 void VCalFormat::readCustomProperties( VObject *o, const Incidence::Ptr &i )
2636 {
2637  VObjectIterator iter;
2638  VObject *cur;
2639  const char *curname;
2640  char *s;
2641 
2642  initPropIterator( &iter, o );
2643  while ( moreIteration( &iter ) ) {
2644  cur = nextVObject( &iter );
2645  curname = vObjectName( cur );
2646  Q_ASSERT( curname );
2647  if ( ( curname[0] == 'X' && curname[1] == '-' ) &&
2648  strcmp( curname, ICOrganizerProp ) != 0 ) {
2649  // TODO - for the time being, we ignore the parameters part
2650  // and just do the value handling here
2651  i->setNonKDECustomProperty(
2652  curname, QString::fromUtf8( s = fakeCString( vObjectUStringZValue( cur ) ) ) );
2653  deleteStr( s );
2654  }
2655  }
2656 }
2657 
2658 void VCalFormat::writeCustomProperties( VObject *o, const Incidence::Ptr &i )
2659 {
2660  const QMap<QByteArray, QString> custom = i->customProperties();
2661  for ( QMap<QByteArray, QString>::ConstIterator c = custom.begin();
2662  c != custom.end(); ++c ) {
2663  if ( d->mManuallyWrittenExtensionFields.contains( c.key() ) ) {
2664  continue;
2665  }
2666 
2667  addPropValue( o, c.key(), c.value().toUtf8() );
2668  }
2669 }
2670 
2671 void VCalFormat::virtual_hook( int id, void *data )
2672 {
2673  Q_UNUSED( id );
2674  Q_UNUSED( data );
2675  Q_ASSERT( false );
2676 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Sep 24 2012 09:01:26 by doxygen 1.8.1.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

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

kdepimlibs-4.9.1 API Reference

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

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