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

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • 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