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

KCalCore Library

  • sources
  • kde-4.12
  • kdepimlibs
  • 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) == QLatin1String("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) == QLatin1String("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) == QLatin1String("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) == QLatin1String("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) + QLatin1Char(';');
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) + QLatin1Char(';');
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] == QLatin1Char(' ')) {
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 += QLatin1Char(';');
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 = QLatin1String("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 = QLatin1String("MAILTO:") + curAttendee->name() + QLatin1String(" <") + curAttendee->email() + QLatin1Char('>');
711  } else if (curAttendee->name().isEmpty() && curAttendee->email().isEmpty()) {
712  tmpStr = QLatin1String("MAILTO: ");
713  kDebug() << "warning! this Event has an attendee w/o name or email!";
714  } else if (curAttendee->name().isEmpty()) {
715  tmpStr = QLatin1String("MAILTO: ") + curAttendee->email();
716  } else {
717  tmpStr = QLatin1String("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) + QLatin1Char(';');
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) + QLatin1Char(';');
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() != QLatin1String("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(QLatin1Char('<'))) > 0) {
1084  // both email address and name
1085  emailPos2 = tmpStr.lastIndexOf(QLatin1Char('>'));
1086  a = Attendee::Ptr(new Attendee(tmpStr.left(emailPos1 - 1),
1087  tmpStr.mid(emailPos1 + 1,
1088  emailPos2 - (emailPos1 + 1))));
1089  } else if (tmpStr.indexOf(QLatin1Char('@')) > 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  if (anEvent->dtDue().time().hour() == 0 &&
1169  anEvent->dtDue().time().minute() == 0 &&
1170  anEvent->dtDue().time().second() == 0) {
1171 #if defined(KCALCORE_FOR_MEEGO)
1172  QDate dueDate = anEvent->dtDue().date();
1173  anEvent->setDtDue(KDateTime(dueDate, KDateTime::ClockTime));
1174 #endif
1175  anEvent->setAllDay(true);
1176  }
1177  } else {
1178  anEvent->setDtDue(KDateTime());
1179  }
1180 
1181  // start time
1182  if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != 0) {
1183  anEvent->setDtStart(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1184  deleteStr(s);
1185  if (anEvent->dtStart().time().hour() == 0 &&
1186  anEvent->dtStart().time().minute() == 0 &&
1187  anEvent->dtStart().time().second() == 0) {
1188 #if defined(KCALCORE_FOR_MEEGO)
1189  QDate startDate = anEvent->dtStart().date();
1190  anEvent->setDtStart(KDateTime(startDate, KDateTime::ClockTime));
1191 #endif
1192  anEvent->setAllDay(true);
1193  }
1194  } else {
1195  anEvent->setDtStart(KDateTime());
1196  }
1197 
1198  // repeat stuff
1199  if ((vo = isAPropertyOf(vtodo, VCRRuleProp)) != 0) {
1200  QString tmpStr = (s = fakeCString(vObjectUStringZValue(vo)));
1201  deleteStr(s);
1202  tmpStr = tmpStr.simplified();
1203  tmpStr = tmpStr.toUpper();
1204  // first, read the type of the recurrence
1205  int typelen = 1;
1206  uint type = Recurrence::rNone;
1207  if (tmpStr.left(1) == "D") {
1208  type = Recurrence::rDaily;
1209  } else if (tmpStr.left(1) == "W") {
1210  type = Recurrence::rWeekly;
1211  } else {
1212  typelen = 2;
1213  if (tmpStr.left(2) == "MP") {
1214  type = Recurrence::rMonthlyPos;
1215  } else if (tmpStr.left(2) == "MD") {
1216  type = Recurrence::rMonthlyDay;
1217  } else if (tmpStr.left(2) == "YM") {
1218  type = Recurrence::rYearlyMonth;
1219  } else if (tmpStr.left(2) == "YD") {
1220  type = Recurrence::rYearlyDay;
1221  }
1222  }
1223 
1224  if (type != Recurrence::rNone) {
1225 
1226  // Immediately after the type is the frequency
1227  int index = tmpStr.indexOf(' ');
1228  int last = tmpStr.lastIndexOf(' ') + 1; // find last entry
1229  int rFreq = tmpStr.mid(typelen, (index - 1)).toInt();
1230  ++index; // advance to beginning of stuff after freq
1231 
1232  // Read the type-specific settings
1233  switch (type) {
1234  case Recurrence::rDaily:
1235  anEvent->recurrence()->setDaily(rFreq);
1236  break;
1237 
1238  case Recurrence::rWeekly:
1239  {
1240  QBitArray qba(7);
1241  QString dayStr;
1242  if (index == last) {
1243  // e.g. W1 #0
1244  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
1245  } else {
1246  // e.g. W1 SU #0
1247  while (index < last) {
1248  dayStr = tmpStr.mid(index, 3);
1249  int dayNum = numFromDay(dayStr);
1250  if (dayNum >= 0) {
1251  qba.setBit(dayNum);
1252  }
1253  index += 3; // advance to next day, or possibly "#"
1254  }
1255  }
1256  anEvent->recurrence()->setWeekly(rFreq, qba);
1257  break;
1258  }
1259 
1260  case Recurrence::rMonthlyPos:
1261  {
1262  anEvent->recurrence()->setMonthly(rFreq);
1263 
1264  QBitArray qba(7);
1265  short tmpPos;
1266  if (index == last) {
1267  // e.g. MP1 #0
1268  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
1269  if (tmpPos == 5) {
1270  tmpPos = -1;
1271  }
1272  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
1273  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
1274  } else {
1275  // e.g. MP1 1+ SU #0
1276  while (index < last) {
1277  tmpPos = tmpStr.mid(index, 1).toShort();
1278  index += 1;
1279  if (tmpStr.mid(index, 1) == "-") {
1280  // convert tmpPos to negative
1281  tmpPos = 0 - tmpPos;
1282  }
1283  index += 2; // advance to day(s)
1284  while (numFromDay(tmpStr.mid(index, 3)) >= 0) {
1285  int dayNum = numFromDay(tmpStr.mid(index, 3));
1286  qba.setBit(dayNum);
1287  index += 3; // advance to next day, or possibly pos or "#"
1288  }
1289  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
1290  qba.detach();
1291  qba.fill(false); // clear out
1292  } // while != "#"
1293  }
1294  break;
1295  }
1296 
1297  case Recurrence::rMonthlyDay:
1298  anEvent->recurrence()->setMonthly(rFreq);
1299  if (index == last) {
1300  // e.g. MD1 #0
1301  short tmpDay = anEvent->dtStart().date().day();
1302  anEvent->recurrence()->addMonthlyDate(tmpDay);
1303  } else {
1304  // e.g. MD1 3 #0
1305  while (index < last) {
1306  int index2 = tmpStr.indexOf(' ', index);
1307  if ((tmpStr.mid((index2 - 1), 1) == "-") ||
1308  (tmpStr.mid((index2 - 1), 1) == "+")) {
1309  index2 = index2 - 1;
1310  }
1311  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
1312  index = index2;
1313  if (tmpStr.mid(index, 1) == "-") {
1314  tmpDay = 0 - tmpDay;
1315  }
1316  index += 2; // advance the index;
1317  anEvent->recurrence()->addMonthlyDate(tmpDay);
1318  } // while != #
1319  }
1320  break;
1321 
1322  case Recurrence::rYearlyMonth:
1323  anEvent->recurrence()->setYearly(rFreq);
1324 
1325  if (index == last) {
1326  // e.g. YM1 #0
1327  short tmpMonth = anEvent->dtStart().date().month();
1328  anEvent->recurrence()->addYearlyMonth(tmpMonth);
1329  } else {
1330  // e.g. YM1 3 #0
1331  while (index < last) {
1332  int index2 = tmpStr.indexOf(' ', index);
1333  short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort();
1334  index = index2 + 1;
1335  anEvent->recurrence()->addYearlyMonth(tmpMonth);
1336  } // while != #
1337  }
1338  break;
1339 
1340  case Recurrence::rYearlyDay:
1341  anEvent->recurrence()->setYearly(rFreq);
1342 
1343  if (index == last) {
1344  // e.g. YD1 #0
1345  short tmpDay = anEvent->dtStart().date().dayOfYear();
1346  anEvent->recurrence()->addYearlyDay(tmpDay);
1347  } else {
1348  // e.g. YD1 123 #0
1349  while (index < last) {
1350  int index2 = tmpStr.indexOf(' ', index);
1351  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
1352  index = index2 + 1;
1353  anEvent->recurrence()->addYearlyDay(tmpDay);
1354  } // while != #
1355  }
1356  break;
1357 
1358  default:
1359  break;
1360  }
1361 
1362  // find the last field, which is either the duration or the end date
1363  index = last;
1364  if (tmpStr.mid(index, 1) == "#") {
1365  // Nr of occurrences
1366  index++;
1367  int rDuration = tmpStr.mid(index, tmpStr.length() - index).toInt();
1368  if (rDuration > 0) {
1369  anEvent->recurrence()->setDuration(rDuration);
1370  }
1371  } else if (tmpStr.indexOf('T', index) != -1) {
1372  KDateTime rEndDate = ISOToKDateTime(tmpStr.mid(index, tmpStr.length() - index));
1373  anEvent->recurrence()->setEndDateTime(rEndDate);
1374  }
1375  } else {
1376  kDebug() << "we don't understand this type of recurrence!";
1377  } // if known recurrence type
1378  } // repeats
1379 
1380  // recurrence exceptions
1381  if ((vo = isAPropertyOf(vtodo, VCExpDateProp)) != 0) {
1382  s = fakeCString(vObjectUStringZValue(vo));
1383  QStringList exDates = QString::fromUtf8(s).split(',');
1384  QStringList::ConstIterator it;
1385  for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) {
1386  KDateTime exDate = ISOToKDateTime(*it);
1387  if (exDate.time().hour() == 0 &&
1388  exDate.time().minute() == 0 &&
1389  exDate.time().second() == 0) {
1390  anEvent->recurrence()->addExDate(ISOToQDate(*it));
1391  } else {
1392  anEvent->recurrence()->addExDateTime(exDate);
1393  }
1394  }
1395  deleteStr(s);
1396  }
1397 
1398  // alarm stuff
1399  if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) {
1400  Alarm::Ptr alarm;
1401  VObject *a;
1402  VObject *b;
1403  a = isAPropertyOf(vo, VCRunTimeProp);
1404  b = isAPropertyOf(vo, VCDisplayStringProp);
1405 
1406  if (a || b) {
1407  alarm = anEvent->newAlarm();
1408  if (a) {
1409  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
1410  deleteStr(s);
1411  }
1412  alarm->setEnabled(true);
1413  if (b) {
1414  s = fakeCString(vObjectUStringZValue(b));
1415  alarm->setDisplayAlarm(QString(s));
1416  deleteStr(s);
1417  } else {
1418  alarm->setDisplayAlarm(QString());
1419  }
1420  }
1421  }
1422 
1423  if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) {
1424  Alarm::Ptr alarm;
1425  VObject *a;
1426  VObject *b;
1427  a = isAPropertyOf(vo, VCRunTimeProp);
1428  b = isAPropertyOf(vo, VCAudioContentProp);
1429 
1430  if (a || b) {
1431  alarm = anEvent->newAlarm();
1432  if (a) {
1433  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
1434  deleteStr(s);
1435  }
1436  alarm->setEnabled(true);
1437  if (b) {
1438  s = fakeCString(vObjectUStringZValue(b));
1439  alarm->setAudioAlarm(QFile::decodeName(s));
1440  deleteStr(s);
1441  } else {
1442  alarm->setAudioAlarm(QString());
1443  }
1444  }
1445  }
1446 
1447  if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) {
1448  Alarm::Ptr alarm;
1449  VObject *a;
1450  VObject *b;
1451  a = isAPropertyOf(vo, VCRunTimeProp);
1452  b = isAPropertyOf(vo, VCProcedureNameProp);
1453 
1454  if (a || b) {
1455  alarm = anEvent->newAlarm();
1456  if (a) {
1457  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
1458  deleteStr(s);
1459  }
1460  alarm->setEnabled(true);
1461 
1462  if (b) {
1463  s = fakeCString(vObjectUStringZValue(b));
1464  alarm->setProcedureAlarm(QFile::decodeName(s));
1465  deleteStr(s);
1466  } else {
1467  alarm->setProcedureAlarm(QString());
1468  }
1469  }
1470  }
1471 
1472  // related todo
1473  if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != 0) {
1474  anEvent->setRelatedTo(s = fakeCString(vObjectUStringZValue(vo)));
1475  deleteStr(s);
1476  d->mTodosRelate.append(anEvent);
1477  }
1478 
1479  // secrecy
1480  Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
1481  if ((vo = isAPropertyOf(vtodo, VCClassProp)) != 0) {
1482  s = fakeCString(vObjectUStringZValue(vo));
1483  if (s && strcmp(s, "PRIVATE") == 0) {
1484  secrecy = Incidence::SecrecyPrivate;
1485  } else if (s && strcmp(s, "CONFIDENTIAL") == 0) {
1486  secrecy = Incidence::SecrecyConfidential;
1487  }
1488  deleteStr(s);
1489  }
1490  anEvent->setSecrecy(secrecy);
1491 
1492  // categories
1493  if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != 0) {
1494  s = fakeCString(vObjectUStringZValue(vo));
1495  QString categories = QString::fromUtf8(s);
1496  deleteStr(s);
1497  QStringList tmpStrList = categories.split(';');
1498  anEvent->setCategories(tmpStrList);
1499  }
1500 
1501  /* PILOT SYNC STUFF */
1502  if ((vo = isAPropertyOf(vtodo, KPilotIdProp))) {
1503  anEvent->setNonKDECustomProperty(
1504  KPilotIdProp, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
1505  deleteStr(s);
1506  if ((vo = isAPropertyOf(vtodo, KPilotStatusProp))) {
1507  anEvent->setNonKDECustomProperty(
1508  KPilotStatusProp, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
1509  deleteStr(s);
1510  } else {
1511  anEvent->setNonKDECustomProperty(KPilotStatusProp, QString::number(int(SYNCMOD)));
1512  }
1513  }
1514 
1515  return anEvent;
1516 }
1517 
1518 Event::Ptr VCalFormat::VEventToEvent(VObject *vevent)
1519 {
1520  VObject *vo;
1521  VObjectIterator voi;
1522  char *s;
1523 
1524  Event::Ptr anEvent(new Event);
1525 
1526  // creation date
1527  if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != 0) {
1528  anEvent->setCreated(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1529  deleteStr(s);
1530  }
1531 
1532  // unique id
1533  vo = isAPropertyOf(vevent, VCUniqueStringProp);
1534  // while the UID property is preferred, it is not required. We'll use the
1535  // default Event UID if none is given.
1536  if (vo) {
1537  anEvent->setUid(s = fakeCString(vObjectUStringZValue(vo)));
1538  deleteStr(s);
1539  }
1540 
1541 #if defined(KCALCORE_FOR_SYMBIAN)
1542  // recurrence id
1543  vo = isAPropertyOf(vevent, VCRecurrenceIdProp);
1544  if (vo) {
1545  anEvent->setRecurrenceId(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1546  deleteStr(s);
1547  }
1548 #endif
1549 
1550  // revision
1551  // again NSCAL doesn't give us much to work with, so we improvise...
1552  anEvent->setRevision(0);
1553  if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != 0) {
1554  s = fakeCString(vObjectUStringZValue(vo));
1555  if (s) {
1556  anEvent->setRevision(atoi(s));
1557  deleteStr(s);
1558  }
1559  }
1560 
1561  // last modification date
1562  if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != 0) {
1563  anEvent->setLastModified(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1564  deleteStr(s);
1565  } else {
1566  anEvent->setLastModified(KDateTime::currentUtcDateTime());
1567  }
1568 
1569  // organizer
1570  // if our extension property for the event's ORGANIZER exists, add it.
1571  if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != 0) {
1572  // FIXME: Also use the full name, not just the email address
1573  anEvent->setOrganizer(s = fakeCString(vObjectUStringZValue(vo)));
1574  deleteStr(s);
1575  } else {
1576  if (d->mCalendar->owner()->name() != "Unknown Name") {
1577  anEvent->setOrganizer(d->mCalendar->owner());
1578  }
1579  }
1580 
1581  // deal with attendees.
1582  initPropIterator(&voi, vevent);
1583  while (moreIteration(&voi)) {
1584  vo = nextVObject(&voi);
1585  if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
1586  Attendee::Ptr a;
1587  VObject *vp;
1588  s = fakeCString(vObjectUStringZValue(vo));
1589  QString tmpStr = QString::fromUtf8(s);
1590  deleteStr(s);
1591  tmpStr = tmpStr.simplified();
1592  int emailPos1, emailPos2;
1593  if ((emailPos1 = tmpStr.indexOf('<')) > 0) {
1594  // both email address and name
1595  emailPos2 = tmpStr.lastIndexOf('>');
1596  a = Attendee::Ptr(new Attendee(tmpStr.left(emailPos1 - 1),
1597  tmpStr.mid(emailPos1 + 1,
1598  emailPos2 - (emailPos1 + 1))));
1599  } else if (tmpStr.indexOf('@') > 0) {
1600  // just an email address
1601  a = Attendee::Ptr(new Attendee(0, tmpStr));
1602  } else {
1603  // just a name
1604  QString email = tmpStr.replace(' ', '.');
1605  a = Attendee::Ptr(new Attendee(tmpStr, email));
1606  }
1607 
1608  // is there an RSVP property?
1609  if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0) {
1610  a->setRSVP(vObjectStringZValue(vp));
1611  }
1612  // is there a status property?
1613  if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0) {
1614  a->setStatus(readStatus(vObjectStringZValue(vp)));
1615  }
1616  // add the attendee
1617  anEvent->addAttendee(a);
1618  }
1619  }
1620 
1621  // This isn't strictly true. An event that doesn't have a start time
1622  // or an end time isn't all-day, it has an anchor in time but it doesn't
1623  // "take up" any time.
1624  /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
1625  (isAPropertyOf(vevent, VCDTendProp) == 0)) {
1626  anEvent->setAllDay(true);
1627  } else {
1628  }*/
1629 
1630  anEvent->setAllDay(false);
1631 
1632  // start time
1633  if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != 0) {
1634  anEvent->setDtStart(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1635  deleteStr(s);
1636 
1637  if (anEvent->dtStart().time().hour() == 0 &&
1638  anEvent->dtStart().time().minute() == 0 &&
1639  anEvent->dtStart().time().second() == 0) {
1640 #if defined(KCALCORE_FOR_MEEGO)
1641  QDate startDate = anEvent->dtStart().date();
1642  anEvent->setDtStart(KDateTime(startDate, KDateTime::ClockTime));
1643 #endif
1644  anEvent->setAllDay(true);
1645  }
1646  }
1647 
1648  // stop time
1649  if ((vo = isAPropertyOf(vevent, VCDTendProp)) != 0) {
1650  anEvent->setDtEnd(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(vo))));
1651  deleteStr(s);
1652 
1653  if (anEvent->dtEnd().time().hour() == 0 &&
1654  anEvent->dtEnd().time().minute() == 0 &&
1655  anEvent->dtEnd().time().second() == 0) {
1656 #if defined(KCALCORE_FOR_MEEGO)
1657  QDate endDate = anEvent->dtEnd().date();
1658  anEvent->setDtEnd(KDateTime(endDate, KDateTime::ClockTime));
1659 #endif
1660  anEvent->setAllDay(true);
1661  }
1662  }
1663 #if defined(KCALCORE_FOR_MEEGO)
1664  if (anEvent->allDay()) {
1665  if (anEvent->dtEnd() == anEvent->dtStart()) {
1666  anEvent->setDtEnd(anEvent->dtEnd().addDays(1));
1667  }
1668  }
1669 #endif
1670 
1671  // at this point, there should be at least a start or end time.
1672  // fix up for events that take up no time but have a time associated
1673  if (!isAPropertyOf(vevent, VCDTstartProp)) {
1674  anEvent->setDtStart(anEvent->dtEnd());
1675  }
1676  if (! isAPropertyOf(vevent, VCDTendProp)) {
1677  anEvent->setDtEnd(anEvent->dtStart());
1678  }
1679 
1681 
1682  // repeat stuff
1683  if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != 0) {
1684  QString tmpStr = (s = fakeCString(vObjectUStringZValue(vo)));
1685  deleteStr(s);
1686  tmpStr = tmpStr.simplified();
1687  tmpStr = tmpStr.toUpper();
1688  // first, read the type of the recurrence
1689  int typelen = 1;
1690  uint type = Recurrence::rNone;
1691  if (tmpStr.left(1) == "D") {
1692  type = Recurrence::rDaily;
1693  } else if (tmpStr.left(1) == "W") {
1694  type = Recurrence::rWeekly;
1695  } else {
1696  typelen = 2;
1697  if (tmpStr.left(2) == "MP") {
1698  type = Recurrence::rMonthlyPos;
1699  } else if (tmpStr.left(2) == "MD") {
1700  type = Recurrence::rMonthlyDay;
1701  } else if (tmpStr.left(2) == "YM") {
1702  type = Recurrence::rYearlyMonth;
1703  } else if (tmpStr.left(2) == "YD") {
1704  type = Recurrence::rYearlyDay;
1705  }
1706  }
1707 
1708  if (type != Recurrence::rNone) {
1709 
1710  // Immediately after the type is the frequency
1711  int index = tmpStr.indexOf(' ');
1712  int last = tmpStr.lastIndexOf(' ') + 1; // find last entry
1713  int rFreq = tmpStr.mid(typelen, (index - 1)).toInt();
1714  ++index; // advance to beginning of stuff after freq
1715 
1716  // Read the type-specific settings
1717  switch (type) {
1718  case Recurrence::rDaily:
1719  anEvent->recurrence()->setDaily(rFreq);
1720  break;
1721 
1722  case Recurrence::rWeekly:
1723  {
1724  QBitArray qba(7);
1725  QString dayStr;
1726  if (index == last) {
1727  // e.g. W1 #0
1728  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
1729  } else {
1730  // e.g. W1 SU #0
1731  while (index < last) {
1732  dayStr = tmpStr.mid(index, 3);
1733  int dayNum = numFromDay(dayStr);
1734  if (dayNum >= 0) {
1735  qba.setBit(dayNum);
1736  }
1737  index += 3; // advance to next day, or possibly "#"
1738  }
1739  }
1740  anEvent->recurrence()->setWeekly(rFreq, qba);
1741  break;
1742  }
1743 
1744  case Recurrence::rMonthlyPos:
1745  {
1746  anEvent->recurrence()->setMonthly(rFreq);
1747 
1748  QBitArray qba(7);
1749  short tmpPos;
1750  if (index == last) {
1751  // e.g. MP1 #0
1752  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
1753  if (tmpPos == 5) {
1754  tmpPos = -1;
1755  }
1756  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
1757  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
1758  } else {
1759  // e.g. MP1 1+ SU #0
1760  while (index < last) {
1761  tmpPos = tmpStr.mid(index, 1).toShort();
1762  index += 1;
1763  if (tmpStr.mid(index, 1) == "-") {
1764  // convert tmpPos to negative
1765  tmpPos = 0 - tmpPos;
1766  }
1767  index += 2; // advance to day(s)
1768  while (numFromDay(tmpStr.mid(index, 3)) >= 0) {
1769  int dayNum = numFromDay(tmpStr.mid(index, 3));
1770  qba.setBit(dayNum);
1771  index += 3; // advance to next day, or possibly pos or "#"
1772  }
1773  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
1774  qba.detach();
1775  qba.fill(false); // clear out
1776  } // while != "#"
1777  }
1778  break;
1779  }
1780 
1781  case Recurrence::rMonthlyDay:
1782  anEvent->recurrence()->setMonthly(rFreq);
1783  if (index == last) {
1784  // e.g. MD1 #0
1785  short tmpDay = anEvent->dtStart().date().day();
1786  anEvent->recurrence()->addMonthlyDate(tmpDay);
1787  } else {
1788  // e.g. MD1 3 #0
1789  while (index < last) {
1790  int index2 = tmpStr.indexOf(' ', index);
1791  if ((tmpStr.mid((index2 - 1), 1) == "-") ||
1792  (tmpStr.mid((index2 - 1), 1) == "+")) {
1793  index2 = index2 - 1;
1794  }
1795  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
1796  index = index2;
1797  if (tmpStr.mid(index, 1) == "-") {
1798  tmpDay = 0 - tmpDay;
1799  }
1800  index += 2; // advance the index;
1801  anEvent->recurrence()->addMonthlyDate(tmpDay);
1802  } // while != #
1803  }
1804  break;
1805 
1806  case Recurrence::rYearlyMonth:
1807  anEvent->recurrence()->setYearly(rFreq);
1808 
1809  if (index == last) {
1810  // e.g. YM1 #0
1811  short tmpMonth = anEvent->dtStart().date().month();
1812  anEvent->recurrence()->addYearlyMonth(tmpMonth);
1813  } else {
1814  // e.g. YM1 3 #0
1815  while (index < last) {
1816  int index2 = tmpStr.indexOf(' ', index);
1817  short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort();
1818  index = index2 + 1;
1819  anEvent->recurrence()->addYearlyMonth(tmpMonth);
1820  } // while != #
1821  }
1822  break;
1823 
1824  case Recurrence::rYearlyDay:
1825  anEvent->recurrence()->setYearly(rFreq);
1826 
1827  if (index == last) {
1828  // e.g. YD1 #0
1829  short tmpDay = anEvent->dtStart().date().dayOfYear();
1830  anEvent->recurrence()->addYearlyDay(tmpDay);
1831  } else {
1832  // e.g. YD1 123 #0
1833  while (index < last) {
1834  int index2 = tmpStr.indexOf(' ', index);
1835  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
1836  index = index2 + 1;
1837  anEvent->recurrence()->addYearlyDay(tmpDay);
1838  } // while != #
1839  }
1840  break;
1841 
1842  default:
1843  break;
1844  }
1845 
1846  // find the last field, which is either the duration or the end date
1847  index = last;
1848  if (tmpStr.mid(index, 1) == "#") {
1849  // Nr of occurrences
1850  index++;
1851  int rDuration = tmpStr.mid(index, tmpStr.length() - index).toInt();
1852  if (rDuration > 0) {
1853  anEvent->recurrence()->setDuration(rDuration);
1854  }
1855  } else if (tmpStr.indexOf('T', index) != -1) {
1856  KDateTime rEndDate = ISOToKDateTime(tmpStr.mid(index, tmpStr.length() - index));
1857  anEvent->recurrence()->setEndDateTime(rEndDate);
1858  }
1859 // anEvent->recurrence()->dump();
1860 
1861  } else {
1862  kDebug() << "we don't understand this type of recurrence!";
1863  } // if known recurrence type
1864  } // repeats
1865 
1866  // recurrence exceptions
1867  if ((vo = isAPropertyOf(vevent, VCExpDateProp)) != 0) {
1868  s = fakeCString(vObjectUStringZValue(vo));
1869  QStringList exDates = QString::fromUtf8(s).split(',');
1870  QStringList::ConstIterator it;
1871  for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) {
1872  KDateTime exDate = ISOToKDateTime(*it);
1873  if (exDate.time().hour() == 0 &&
1874  exDate.time().minute() == 0 &&
1875  exDate.time().second() == 0) {
1876  anEvent->recurrence()->addExDate(ISOToQDate(*it));
1877  } else {
1878  anEvent->recurrence()->addExDateTime(exDate);
1879  }
1880  }
1881  deleteStr(s);
1882  }
1883 
1884  // summary
1885  if ((vo = isAPropertyOf(vevent, VCSummaryProp))) {
1886  s = fakeCString(vObjectUStringZValue(vo));
1887  anEvent->setSummary(QString::fromUtf8(s), Qt::mightBeRichText(s));
1888  deleteStr(s);
1889  }
1890 
1891  // description
1892  if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != 0) {
1893  s = fakeCString(vObjectUStringZValue(vo));
1894  bool isRich = Qt::mightBeRichText(s);
1895  if (!anEvent->description().isEmpty()) {
1896  anEvent->setDescription(
1897  anEvent->description() + '\n' + QString::fromUtf8(s), isRich);
1898  } else {
1899  anEvent->setDescription(QString::fromUtf8(s), isRich);
1900  }
1901  deleteStr(s);
1902  }
1903 
1904  // location
1905  if ((vo = isAPropertyOf(vevent, VCLocationProp)) != 0) {
1906  s = fakeCString(vObjectUStringZValue(vo));
1907  anEvent->setLocation(QString::fromUtf8(s), Qt::mightBeRichText(s));
1908  deleteStr(s);
1909  }
1910 
1911  // some stupid vCal exporters ignore the standard and use Description
1912  // instead of Summary for the default field. Correct for this.
1913  if (anEvent->summary().isEmpty() && !(anEvent->description().isEmpty())) {
1914  QString tmpStr = anEvent->description().simplified();
1915  anEvent->setDescription("");
1916  anEvent->setSummary(tmpStr);
1917  }
1918 
1919 #if 0
1920  // status
1921  if ((vo = isAPropertyOf(vevent, VCStatusProp)) != 0) {
1922  QString tmpStr(s = fakeCString(vObjectUStringZValue(vo)));
1923  deleteStr(s);
1924 // TODO: Define Event status
1925 // anEvent->setStatus( tmpStr );
1926  } else {
1927 // anEvent->setStatus( "NEEDS ACTION" );
1928  }
1929 #endif
1930 
1931  // secrecy
1932  Incidence::Secrecy secrecy = Incidence::SecrecyPublic;
1933  if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) {
1934  s = fakeCString(vObjectUStringZValue(vo));
1935  if (s && strcmp(s, "PRIVATE") == 0) {
1936  secrecy = Incidence::SecrecyPrivate;
1937  } else if (s && strcmp(s, "CONFIDENTIAL") == 0) {
1938  secrecy = Incidence::SecrecyConfidential;
1939  }
1940  deleteStr(s);
1941  }
1942  anEvent->setSecrecy(secrecy);
1943 
1944  // categories
1945  if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != 0) {
1946  s = fakeCString(vObjectUStringZValue(vo));
1947  QString categories = QString::fromUtf8(s);
1948  deleteStr(s);
1949  QStringList tmpStrList = categories.split(',');
1950  anEvent->setCategories(tmpStrList);
1951  }
1952 
1953  // attachments
1954  initPropIterator(&voi, vevent);
1955  while (moreIteration(&voi)) {
1956  vo = nextVObject(&voi);
1957  if (strcmp(vObjectName(vo), VCAttachProp) == 0) {
1958  s = fakeCString(vObjectUStringZValue(vo));
1959  anEvent->addAttachment(Attachment::Ptr(new Attachment(QString(s))));
1960  deleteStr(s);
1961  }
1962  }
1963 
1964  // resources
1965  if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) {
1966  QString resources = (s = fakeCString(vObjectUStringZValue(vo)));
1967  deleteStr(s);
1968  QStringList tmpStrList = resources.split(';');
1969  anEvent->setResources(tmpStrList);
1970  }
1971 
1972  // alarm stuff
1973  if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) {
1974  Alarm::Ptr alarm;
1975  VObject *a;
1976  VObject *b;
1977  a = isAPropertyOf(vo, VCRunTimeProp);
1978  b = isAPropertyOf(vo, VCDisplayStringProp);
1979 
1980  if (a || b) {
1981  alarm = anEvent->newAlarm();
1982  if (a) {
1983  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
1984  deleteStr(s);
1985  }
1986  alarm->setEnabled(true);
1987 
1988  if (b) {
1989  s = fakeCString(vObjectUStringZValue(b));
1990  alarm->setDisplayAlarm(QString(s));
1991  deleteStr(s);
1992  } else {
1993  alarm->setDisplayAlarm(QString());
1994  }
1995  }
1996  }
1997 
1998  if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) {
1999  Alarm::Ptr alarm;
2000  VObject *a;
2001  VObject *b;
2002  a = isAPropertyOf(vo, VCRunTimeProp);
2003  b = isAPropertyOf(vo, VCAudioContentProp);
2004 
2005  if (a || b) {
2006  alarm = anEvent->newAlarm();
2007  if (a) {
2008  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
2009  deleteStr(s);
2010  }
2011  alarm->setEnabled(true);
2012 
2013  if (b) {
2014  s = fakeCString(vObjectUStringZValue(b));
2015  alarm->setAudioAlarm(QFile::decodeName(s));
2016  deleteStr(s);
2017  } else {
2018  alarm->setAudioAlarm(QString());
2019  }
2020  }
2021  }
2022 
2023  if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) {
2024  Alarm::Ptr alarm;
2025  VObject *a;
2026  VObject *b;
2027  a = isAPropertyOf(vo, VCRunTimeProp);
2028  b = isAPropertyOf(vo, VCProcedureNameProp);
2029 
2030  if (a || b) {
2031  alarm = anEvent->newAlarm();
2032  if (a) {
2033  alarm->setTime(ISOToKDateTime(s = fakeCString(vObjectUStringZValue(a))));
2034  deleteStr(s);
2035  }
2036  alarm->setEnabled(true);
2037 
2038  if (b) {
2039  s = fakeCString(vObjectUStringZValue(b));
2040  alarm->setProcedureAlarm(QFile::decodeName(s));
2041  deleteStr(s);
2042  } else {
2043  alarm->setProcedureAlarm(QString());
2044  }
2045  }
2046  }
2047 
2048  // priority
2049  if ((vo = isAPropertyOf(vevent, VCPriorityProp))) {
2050  s = fakeCString(vObjectUStringZValue(vo));
2051  if (s) {
2052  anEvent->setPriority(atoi(s));
2053  deleteStr(s);
2054  }
2055  }
2056 
2057  // transparency
2058  if ((vo = isAPropertyOf(vevent, VCTranspProp)) != 0) {
2059  s = fakeCString(vObjectUStringZValue(vo));
2060  if (s) {
2061  int i = atoi(s);
2062  anEvent->setTransparency(i == 1 ? Event::Transparent : Event::Opaque);
2063  deleteStr(s);
2064  }
2065  }
2066 
2067  // related event
2068  if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != 0) {
2069  anEvent->setRelatedTo(s = fakeCString(vObjectUStringZValue(vo)));
2070  deleteStr(s);
2071  d->mEventsRelate.append(anEvent);
2072  }
2073 
2074  /* PILOT SYNC STUFF */
2075  if ((vo = isAPropertyOf(vevent, KPilotIdProp))) {
2076  anEvent->setNonKDECustomProperty(
2077  KPilotIdProp, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
2078  deleteStr(s);
2079  if ((vo = isAPropertyOf(vevent, KPilotStatusProp))) {
2080  anEvent->setNonKDECustomProperty(
2081  KPilotStatusProp, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
2082  deleteStr(s);
2083  } else {
2084  anEvent->setNonKDECustomProperty(KPilotStatusProp, QString::number(int(SYNCMOD)));
2085  }
2086  }
2087 
2088  /* Rest of the custom properties */
2089  readCustomProperties(vevent, anEvent);
2090 
2091  return anEvent;
2092 }
2093 
2094 QString VCalFormat::parseTZ(const QByteArray &timezone) const
2095 {
2096  // kDebug() << timezone;
2097  QString pZone = timezone.mid(timezone.indexOf("TZID:VCAL") + 9);
2098  return pZone.mid(0, pZone.indexOf(QChar(QLatin1Char('\n'))));
2099 }
2100 
2101 QString VCalFormat::parseDst(QByteArray &timezone) const
2102 {
2103  if (!timezone.contains("BEGIN:DAYLIGHT")) {
2104  return QString();
2105  }
2106 
2107  timezone = timezone.mid(timezone.indexOf("BEGIN:DAYLIGHT"));
2108  timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7);
2109  QString sStart = timezone.mid(0, (timezone.indexOf("COMMENT:")));
2110  sStart.chop(2);
2111  timezone = timezone.mid(timezone.indexOf("TZOFFSETTO:") + 11);
2112  QString sOffset = timezone.mid(0, (timezone.indexOf("DTSTART:")));
2113  sOffset.chop(2);
2114  sOffset.insert(3, QString(":"));
2115  timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7);
2116  QString sEnd = timezone.mid(0, (timezone.indexOf("COMMENT:")));
2117  sEnd.chop(2);
2118 
2119  return "TRUE;" + sOffset + ';' + sStart + ';' + sEnd + ";;";
2120 }
2121 
2122 QString VCalFormat::qDateToISO(const QDate &qd)
2123 {
2124  QString tmpStr;
2125 
2126  if (!qd.isValid()) {
2127  return QString();
2128  }
2129 
2130  tmpStr.sprintf("%.2d%.2d%.2d", qd.year(), qd.month(), qd.day());
2131  return tmpStr;
2132 
2133 }
2134 
2135 QString VCalFormat::kDateTimeToISO(const KDateTime &dt, bool zulu)
2136 {
2137  QString tmpStr;
2138 
2139  if (!dt.isValid()) {
2140  return QString();
2141  }
2142 
2143  QDateTime tmpDT;
2144  if (zulu) {
2145  tmpDT = dt.toUtc().dateTime();
2146  } else {
2147 #if !defined(KCALCORE_FOR_MEEGO)
2148  tmpDT = dt.toTimeSpec(d->mCalendar->timeSpec()).dateTime();
2149 #else
2150  tmpDT = dt.dateTime();
2151 #endif
2152  }
2153  tmpStr.sprintf("%.2d%.2d%.2dT%.2d%.2d%.2d",
2154  tmpDT.date().year(), tmpDT.date().month(),
2155  tmpDT.date().day(), tmpDT.time().hour(),
2156  tmpDT.time().minute(), tmpDT.time().second());
2157  if (zulu || dt.isUtc()) {
2158  tmpStr += 'Z';
2159  }
2160  return tmpStr;
2161 }
2162 
2163 KDateTime VCalFormat::ISOToKDateTime(const QString &dtStr)
2164 {
2165  QDate tmpDate;
2166  QTime tmpTime;
2167  QString tmpStr;
2168  int year, month, day, hour, minute, second;
2169 
2170  tmpStr = dtStr;
2171  year = tmpStr.left(4).toInt();
2172  month = tmpStr.mid(4, 2).toInt();
2173  day = tmpStr.mid(6, 2).toInt();
2174  hour = tmpStr.mid(9, 2).toInt();
2175  minute = tmpStr.mid(11, 2).toInt();
2176  second = tmpStr.mid(13, 2).toInt();
2177  tmpDate.setYMD(year, month, day);
2178  tmpTime.setHMS(hour, minute, second);
2179 
2180  if (tmpDate.isValid() && tmpTime.isValid()) {
2181  // correct for GMT if string is in Zulu format
2182  if (dtStr.at(dtStr.length() - 1) == 'Z') {
2183  return KDateTime(tmpDate, tmpTime, KDateTime::UTC);
2184  } else {
2185  return KDateTime(tmpDate, tmpTime, d->mCalendar->timeSpec());
2186  }
2187  } else {
2188  return KDateTime();
2189  }
2190 }
2191 
2192 QDate VCalFormat::ISOToQDate(const QString &dateStr)
2193 {
2194  int year, month, day;
2195 
2196  year = dateStr.left(4).toInt();
2197  month = dateStr.mid(4, 2).toInt();
2198  day = dateStr.mid(6, 2).toInt();
2199 
2200  return QDate(year, month, day);
2201 }
2202 
2203 bool VCalFormat::parseTZOffsetISO8601(const QString &s, int &result)
2204 {
2205  // ISO8601 format(s):
2206  // +- hh : mm
2207  // +- hh mm
2208  // +- hh
2209 
2210  // We also accept broken one without +
2211  int mod = 1;
2212  int v = 0;
2213  QString str = s.trimmed();
2214  int ofs = 0;
2215  result = 0;
2216 
2217  // Check for end
2218  if (str.size() <= ofs) {
2219  return false;
2220  }
2221  if (str[ofs] == '-') {
2222  mod = -1;
2223  ofs++;
2224  } else if (str[ofs] == '+') {
2225  ofs++;
2226  }
2227  if (str.size() <= ofs) {
2228  return false;
2229  }
2230 
2231  // Make sure next two values are numbers
2232  bool ok;
2233 
2234  if (str.size() < (ofs + 2)) {
2235  return false;
2236  }
2237 
2238  v = str.mid(ofs, 2).toInt(&ok) * 60;
2239  if (!ok) {
2240  return false;
2241  }
2242  ofs += 2;
2243 
2244  if (str.size() > ofs) {
2245  if (str[ofs] == ':') {
2246  ofs++;
2247  }
2248  if (str.size() > ofs) {
2249  if (str.size() < (ofs + 2)) {
2250  return false;
2251  }
2252  v += str.mid(ofs, 2).toInt(&ok);
2253  if (!ok) {
2254  return false;
2255  }
2256  }
2257  }
2258  result = v * mod * 60;
2259  return true;
2260 }
2261 
2262 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
2263 // and break it down from it's tree-like format into the dictionary format
2264 // that is used internally in the VCalFormat.
2265 void VCalFormat::populate(VObject *vcal, bool deleted, const QString &notebook)
2266 {
2267  Q_UNUSED(notebook);
2268  // this function will populate the caldict dictionary and other event
2269  // lists. It turns vevents into Events and then inserts them.
2270 
2271  VObjectIterator i;
2272  VObject *curVO, *curVOProp;
2273  Event::Ptr anEvent;
2274  bool hasTimeZone = false; //The calendar came with a TZ and not UTC
2275  KDateTime::Spec previousSpec; //If we add a new TZ we should leave the spec as it was before
2276 
2277  if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) {
2278  char *methodType = 0;
2279  methodType = fakeCString(vObjectUStringZValue(curVO));
2280  // kDebug() << "This calendar is an iTIP transaction of type '" << methodType << "'";
2281  deleteStr(methodType);
2282  }
2283 
2284  // warn the user that we might have trouble reading non-known calendar.
2285  if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != 0) {
2286  char *s = fakeCString(vObjectUStringZValue(curVO));
2287  if (!s || strcmp(productId().toUtf8(), s) != 0) {
2288  kDebug() << "This vCalendar file was not created by KOrganizer or"
2289  << "any other product we support. Loading anyway...";
2290  }
2291  setLoadedProductId(s);
2292  deleteStr(s);
2293  }
2294 
2295  // warn the user we might have trouble reading this unknown version.
2296  if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) {
2297  char *s = fakeCString(vObjectUStringZValue(curVO));
2298  if (!s || strcmp(_VCAL_VERSION, s) != 0) {
2299  kDebug() << "This vCalendar file has version" << s
2300  << "We only support" << _VCAL_VERSION;
2301  }
2302  deleteStr(s);
2303  }
2304 
2305  // set the time zone (this is a property of the view, so just discard!)
2306  if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) {
2307  char *s = fakeCString(vObjectUStringZValue(curVO));
2308  QString ts(s);
2309  QString name = QLatin1String("VCAL") + ts;
2310  deleteStr(s);
2311 
2312  // TODO: While using the timezone-offset + vcal as timezone is is
2313  // most likely unique, we should REALLY actually create something
2314  // like vcal-tzoffset-daylightoffsets, or better yet,
2315  // vcal-hash<the former>
2316 
2317  QStringList tzList;
2318  QString tz;
2319  int utcOffset;
2320  int utcOffsetDst;
2321  if (parseTZOffsetISO8601(ts, utcOffset)) {
2322  // kDebug() << "got standard offset" << ts << utcOffset;
2323  // standard from tz
2324  // starting date for now 01011900
2325  KDateTime dt = KDateTime(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)));
2326  tz = QString("STD;%1;false;%2").arg(QString::number(utcOffset)).arg(dt.toString());
2327  tzList.append(tz);
2328 
2329  // go through all the daylight tags
2330  initPropIterator(&i, vcal);
2331  while (moreIteration(&i)) {
2332  curVO = nextVObject(&i);
2333  if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) {
2334  char *s = fakeCString(vObjectUStringZValue(curVO));
2335  QString dst = QLatin1String(s);
2336  QStringList argl = dst.split(QLatin1Char(','));
2337  deleteStr(s);
2338 
2339  // Too short -> not interesting
2340  if (argl.size() < 4) {
2341  continue;
2342  }
2343 
2344  // We don't care about the non-DST periods
2345  if (argl[0] != QLatin1String("TRUE")) {
2346  continue;
2347  }
2348 
2349  if (parseTZOffsetISO8601(argl[1], utcOffsetDst)) {
2350 
2351  // kDebug() << "got DST offset" << argl[1] << utcOffsetDst;
2352  // standard
2353  QString strEndDate = argl[3];
2354  KDateTime endDate = ISOToKDateTime(strEndDate);
2355  // daylight
2356  QString strStartDate = argl[2];
2357  KDateTime startDate = ISOToKDateTime(strStartDate);
2358 
2359  QString strRealEndDate = strEndDate;
2360  QString strRealStartDate = strStartDate;
2361  KDateTime realEndDate = endDate;
2362  KDateTime realStartDate = startDate;
2363  // if we get dates for some reason in wrong order, earlier is used for dst
2364  if (endDate < startDate) {
2365  strRealEndDate = strStartDate;
2366  strRealStartDate = strEndDate;
2367  realEndDate = startDate;
2368  realStartDate = endDate;
2369  }
2370  tz = QString::fromLatin1("%1;%2;false;%3").
2371  arg(strRealEndDate).
2372  arg(QString::number(utcOffset)).
2373  arg(realEndDate.toString());
2374  tzList.append(tz);
2375 
2376  tz = QString::fromLatin1("%1;%2;true;%3").
2377  arg(strRealStartDate).
2378  arg(QString::number(utcOffsetDst)).
2379  arg(realStartDate.toString());
2380  tzList.append(tz);
2381  } else {
2382  kDebug() << "unable to parse dst" << argl[1];
2383  }
2384  }
2385  }
2386  ICalTimeZones *tzlist = d->mCalendar->timeZones();
2387  ICalTimeZoneSource tzs;
2388  ICalTimeZone zone = tzs.parse(name, tzList, *tzlist);
2389  if (!zone.isValid()) {
2390  kDebug() << "zone is not valid, parsing error" << tzList;
2391  } else {
2392  previousSpec = d->mCalendar->timeSpec();
2393  d->mCalendar->setTimeZoneId(name);
2394  hasTimeZone = true;
2395  }
2396  } else {
2397  kDebug() << "unable to parse tzoffset" << ts;
2398  }
2399  }
2400 
2401  // Store all events with a relatedTo property in a list for post-processing
2402  d->mEventsRelate.clear();
2403  d->mTodosRelate.clear();
2404 
2405  initPropIterator(&i, vcal);
2406 
2407  // go through all the vobjects in the vcal
2408  while (moreIteration(&i)) {
2409  curVO = nextVObject(&i);
2410 
2411  /************************************************************************/
2412 
2413  // now, check to see that the object is an event or todo.
2414  if (strcmp(vObjectName(curVO), VCEventProp) == 0) {
2415 
2416  if ((curVOProp = isAPropertyOf(curVO, KPilotStatusProp)) != 0) {
2417  char *s;
2418  s = fakeCString(vObjectUStringZValue(curVOProp));
2419  // check to see if event was deleted by the kpilot conduit
2420  if (s) {
2421  if (atoi(s) == SYNCDEL) {
2422  deleteStr(s);
2423  kDebug() << "skipping pilot-deleted event";
2424  goto SKIP;
2425  }
2426  deleteStr(s);
2427  }
2428  }
2429 
2430  if (!isAPropertyOf(curVO, VCDTstartProp) &&
2431  !isAPropertyOf(curVO, VCDTendProp)) {
2432  kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping...";
2433  goto SKIP;
2434  }
2435 
2436  anEvent = VEventToEvent(curVO);
2437  if (anEvent) {
2438  if (hasTimeZone && !anEvent->allDay() && anEvent->dtStart().isUtc()) {
2439  //This sounds stupid but is how others are doing it, so here
2440  //we go. If there is a TZ in the VCALENDAR even if the dtStart
2441  //and dtend are in UTC, clients interpret it using also the TZ defined
2442  //in the Calendar. I know it sounds braindead but oh well
2443  int utcOffSet = anEvent->dtStart().utcOffset();
2444  KDateTime dtStart(anEvent->dtStart().dateTime().addSecs(utcOffSet),
2445  d->mCalendar->timeSpec());
2446  KDateTime dtEnd(anEvent->dtEnd().dateTime().addSecs(utcOffSet),
2447  d->mCalendar->timeSpec());
2448  anEvent->setDtStart(dtStart);
2449  anEvent->setDtEnd(dtEnd);
2450  }
2451  Event::Ptr old = !anEvent->hasRecurrenceId() ?
2452  d->mCalendar->event(anEvent->uid()) :
2453  d->mCalendar->event(anEvent->uid(), anEvent->recurrenceId());
2454 
2455  if (old) {
2456  if (deleted) {
2457  d->mCalendar->deleteEvent(old); // move old to deleted
2458  removeAllVCal(d->mEventsRelate, old);
2459  } else if (anEvent->revision() > old->revision()) {
2460  d->mCalendar->deleteEvent(old); // move old to deleted
2461  removeAllVCal(d->mEventsRelate, old);
2462  d->mCalendar->addEvent(anEvent); // and replace it with this one
2463  }
2464  } else if (deleted) {
2465  old = !anEvent->hasRecurrenceId() ?
2466  d->mCalendar->deletedEvent(anEvent->uid()) :
2467  d->mCalendar->deletedEvent(anEvent->uid(), anEvent->recurrenceId());
2468  if (!old) {
2469  d->mCalendar->addEvent(anEvent); // add this one
2470  d->mCalendar->deleteEvent(anEvent); // and move it to deleted
2471  }
2472  } else {
2473  d->mCalendar->addEvent(anEvent); // just add this one
2474  }
2475  }
2476  } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) {
2477  Todo::Ptr aTodo = VTodoToEvent(curVO);
2478  if (aTodo) {
2479  if (hasTimeZone && !aTodo->allDay() && aTodo->dtStart().isUtc()) {
2480  //This sounds stupid but is how others are doing it, so here
2481  //we go. If there is a TZ in the VCALENDAR even if the dtStart
2482  //and dtend are in UTC, clients interpret it usint alse the TZ defined
2483  //in the Calendar. I know it sounds braindead but oh well
2484  int utcOffSet = aTodo->dtStart().utcOffset();
2485  KDateTime dtStart(aTodo->dtStart().dateTime().addSecs(utcOffSet),
2486  d->mCalendar->timeSpec());
2487  aTodo->setDtStart(dtStart);
2488  if (aTodo->hasDueDate()) {
2489  KDateTime dtDue(aTodo->dtDue().dateTime().addSecs(utcOffSet),
2490  d->mCalendar->timeSpec());
2491  aTodo->setDtDue(dtDue);
2492  }
2493  }
2494  Todo::Ptr old = !aTodo->hasRecurrenceId() ?
2495  d->mCalendar->todo(aTodo->uid()) :
2496  d->mCalendar->todo(aTodo->uid(), aTodo->recurrenceId());
2497  if (old) {
2498  if (deleted) {
2499  d->mCalendar->deleteTodo(old); // move old to deleted
2500  removeAllVCal(d->mTodosRelate, old);
2501  } else if (aTodo->revision() > old->revision()) {
2502  d->mCalendar->deleteTodo(old); // move old to deleted
2503  removeAllVCal(d->mTodosRelate, old);
2504  d->mCalendar->addTodo(aTodo); // and replace it with this one
2505  }
2506  } else if (deleted) {
2507  old = d->mCalendar->deletedTodo(aTodo->uid(), aTodo->recurrenceId());
2508  if (!old) {
2509  d->mCalendar->addTodo(aTodo); // add this one
2510  d->mCalendar->deleteTodo(aTodo); // and move it to deleted
2511  }
2512  } else {
2513  d->mCalendar->addTodo(aTodo); // just add this one
2514  }
2515  }
2516  } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) ||
2517  (strcmp(vObjectName(curVO), VCProdIdProp) == 0) ||
2518  (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) {
2519  // do nothing, we know these properties and we want to skip them.
2520  // we have either already processed them or are ignoring them.
2521  ;
2522  } else if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) {
2523  // do nothing daylights are already processed
2524  ;
2525  } else {
2526  kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
2527  }
2528 SKIP:
2529  ;
2530  } // while
2531 
2532  // Post-Process list of events with relations, put Event objects in relation
2533  Event::List::ConstIterator eIt;
2534  for (eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt) {
2535  (*eIt)->setRelatedTo((*eIt)->relatedTo());
2536  }
2537  Todo::List::ConstIterator tIt;
2538  for (tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt) {
2539  (*tIt)->setRelatedTo((*tIt)->relatedTo());
2540  }
2541 
2542  //Now lets put the TZ back as it was if we have changed it.
2543  if (hasTimeZone) {
2544  d->mCalendar->setTimeSpec(previousSpec);
2545  }
2546 
2547 }
2548 
2549 const char *VCalFormat::dayFromNum(int day)
2550 {
2551  const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " };
2552 
2553  return days[day];
2554 }
2555 
2556 int VCalFormat::numFromDay(const QString &day)
2557 {
2558  if (day == QLatin1String("MO ")) {
2559  return 0;
2560  }
2561  if (day == QLatin1String("TU ")) {
2562  return 1;
2563  }
2564  if (day == QLatin1String("WE ")) {
2565  return 2;
2566  }
2567  if (day == QLatin1String("TH ")) {
2568  return 3;
2569  }
2570  if (day == QLatin1String("FR ")) {
2571  return 4;
2572  }
2573  if (day == QLatin1String("SA ")) {
2574  return 5;
2575  }
2576  if (day == QLatin1String("SU ")) {
2577  return 6;
2578  }
2579 
2580  return -1; // something bad happened. :)
2581 }
2582 
2583 Attendee::PartStat VCalFormat::readStatus(const char *s) const
2584 {
2585  QString statStr = s;
2586  statStr = statStr.toUpper();
2587  Attendee::PartStat status;
2588 
2589  if (statStr == QLatin1String("X-ACTION")) {
2590  status = Attendee::NeedsAction;
2591  } else if (statStr == QLatin1String("NEEDS ACTION")) {
2592  status = Attendee::NeedsAction;
2593  } else if (statStr == QLatin1String("ACCEPTED")) {
2594  status = Attendee::Accepted;
2595  } else if (statStr == QLatin1String("SENT")) {
2596  status = Attendee::NeedsAction;
2597  } else if (statStr == QLatin1String("TENTATIVE")) {
2598  status = Attendee::Tentative;
2599  } else if (statStr == QLatin1String("CONFIRMED")) {
2600  status = Attendee::Accepted;
2601  } else if (statStr == QLatin1String("DECLINED")) {
2602  status = Attendee::Declined;
2603  } else if (statStr == QLatin1String("COMPLETED")) {
2604  status = Attendee::Completed;
2605  } else if (statStr == QLatin1String("DELEGATED")) {
2606  status = Attendee::Delegated;
2607  } else {
2608  kDebug() << "error setting attendee mStatus, unknown mStatus!";
2609  status = Attendee::NeedsAction;
2610  }
2611 
2612  return status;
2613 }
2614 
2615 QByteArray VCalFormat::writeStatus(Attendee::PartStat status) const
2616 {
2617  switch (status) {
2618  default:
2619  case Attendee::NeedsAction:
2620  return "NEEDS ACTION";
2621  break;
2622  case Attendee::Accepted:
2623  return "ACCEPTED";
2624  break;
2625  case Attendee::Declined:
2626  return "DECLINED";
2627  break;
2628  case Attendee::Tentative:
2629  return "TENTATIVE";
2630  break;
2631  case Attendee::Delegated:
2632  return "DELEGATED";
2633  break;
2634  case Attendee::Completed:
2635  return "COMPLETED";
2636  break;
2637  case Attendee::InProcess:
2638  return "NEEDS ACTION";
2639  break;
2640  }
2641 }
2642 
2643 void VCalFormat::readCustomProperties(VObject *o, const Incidence::Ptr &i)
2644 {
2645  VObjectIterator iter;
2646  VObject *cur;
2647  const char *curname;
2648  char *s;
2649 
2650  initPropIterator(&iter, o);
2651  while (moreIteration(&iter)) {
2652  cur = nextVObject(&iter);
2653  curname = vObjectName(cur);
2654  Q_ASSERT(curname);
2655  if ((curname[0] == 'X' && curname[1] == '-') &&
2656  strcmp(curname, ICOrganizerProp) != 0) {
2657  // TODO - for the time being, we ignore the parameters part
2658  // and just do the value handling here
2659  i->setNonKDECustomProperty(
2660  curname, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(cur))));
2661  deleteStr(s);
2662  }
2663  }
2664 }
2665 
2666 void VCalFormat::writeCustomProperties(VObject *o, const Incidence::Ptr &i)
2667 {
2668  const QMap<QByteArray, QString> custom = i->customProperties();
2669  for (QMap<QByteArray, QString>::ConstIterator c = custom.begin();
2670  c != custom.end(); ++c) {
2671  if (d->mManuallyWrittenExtensionFields.contains(c.key()) ||
2672  c.key().startsWith("X-KDE-VOLATILE")) {
2673  continue;
2674  }
2675 
2676  addPropValue(o, c.key(), c.value().toUtf8());
2677  }
2678 }
2679 
2680 void VCalFormat::virtual_hook(int id, void *data)
2681 {
2682  Q_UNUSED(id);
2683  Q_UNUSED(data);
2684  Q_ASSERT(false);
2685 }
KCalCore::Exception
Exception base class, currently used as a fancy kind of error code and not as an C++ exception...
Definition: exceptions.h:50
KCalCore::VCalFormat::numFromDay
int numFromDay(const QString &day)
Converts a two letter representation of the day (i.e.
Definition: vcalformat.cpp:2556
KCalCore::Attachment::Ptr
QSharedPointer< Attachment > Ptr
A shared pointer to an Attachment object.
Definition: attachment.h:65
KCalCore::VCalFormat::save
bool save(const Calendar::Ptr &calendar, const QString &fileName)
Definition: vcalformat.cpp:134
KCalCore::CalFormat::productId
static const QString & productId()
Returns the our library's PRODID string to write into calendar files.
Definition: calformat.cpp:108
KCalCore::CalFormat::setException
void setException(Exception *error)
Sets an exception that is to be used by the functions of this class to report errors.
Definition: calformat.cpp:85
KCalCore::CalFormat::clearException
void clearException()
Clears the exception status.
Definition: calformat.cpp:79
KCalCore::Alarm::Ptr
QSharedPointer< Alarm > Ptr
A shared pointer to an Alarm object.
Definition: alarm.h:78
KCalCore::Incidence::SecrecyConfidential
Secret to the owner and some others.
Definition: incidence.h:96
KCalCore::VCalFormat::VCalFormat
VCalFormat()
Constructor a new vCalendar Format object.
Definition: vcalformat.cpp:88
KCalCore::Event::Ptr
QSharedPointer< Event > Ptr
A shared pointer to an Event object.
Definition: event.h:55
KCalCore::VCalFormat::parseDst
QString parseDst(QByteArray &timezone) const
Parse DAYLIGHT tag from vtimezone.
Definition: vcalformat.cpp:2101
KCalCore::VCalFormat::VEventToEvent
Event::Ptr VEventToEvent(VObject *vevent)
Translates a VObject into a Event and returns a pointer to it.
Definition: vcalformat.cpp:1518
KCalCore::ICalTimeZoneSource
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data...
Definition: icaltimezones.h:405
KCalCore::Recurrence::yearDays
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Definition: recurrence.cpp:667
KCalCore::VCalFormat::toString
QString toString(const Calendar::Ptr &calendar, const QString &notebook=QString(), bool deleted=false)
Definition: vcalformat.cpp:235
KCalCore::Attachment::List
QVector< Ptr > List
List of attachments.
Definition: attachment.h:70
KCalCore::Incidence::Ptr
QSharedPointer< Incidence > Ptr
A shared pointer to an Incidence.
Definition: incidence.h:112
KCalCore::Recurrence::frequency
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:599
KCalCore::Event::List
QVector< Ptr > List
List of events.
Definition: event.h:60
KCalCore::VCalFormat::writeStatus
QByteArray writeStatus(Attendee::PartStat status) const
Converts an Attendee::PartStat into a QByteArray string.
Definition: vcalformat.cpp:2615
KCalCore::Event::Opaque
Event appears in free/busy time.
Definition: event.h:48
KCalCore::Attendee::Delegated
Event or to-do delegated.
Definition: attendee.h:75
KCalCore::VCalFormat::parseTZOffsetISO8601
bool parseTZOffsetISO8601(const QString &s, int &result)
Parse one of the myriad of ISO8601 timezone offset formats, e.g.
Definition: vcalformat.cpp:2203
KCalCore::Event::Transparent
Event does not appear in free/busy time.
Definition: event.h:49
KCalCore::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:87
todo.h
This file is part of the API for handling calendar data and defines the Todo class.
KCalCore::Exception::CalVersionUnknown
Unknown calendar format detected.
Definition: exceptions.h:66
KCalCore::SortableList
A QList which can be sorted.
Definition: sortablelist.h:86
calendar.h
This file is part of the API for handling calendar data and defines the Calendar class.
KCalCore::VCalFormat::VTodoToEvent
Todo::Ptr VTodoToEvent(VObject *vtodo)
Translates a VObject of the TODO type into an Event.
Definition: vcalformat.cpp:1029
KCalCore::VCalFormat::eventToVTodo
VObject * eventToVTodo(const Todo::Ptr &todo)
Translates a Todo into a VTodo-type VObject and return pointer.
Definition: vcalformat.cpp:314
KCalCore::Attendee::PartStat
PartStat
The different types of participant status.
Definition: attendee.h:70
KCalCore::Recurrence::monthDays
QList< int > monthDays() const
Returns list of day numbers of a month.
Definition: recurrence.cpp:648
KCalCore::ICalTimeZoneSource::parse
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from an iCalendar VTIMEZO...
Definition: icaltimezones.cpp:913
KCalCore::Incidence::SecrecyPrivate
Secret to the owner.
Definition: incidence.h:95
exceptions.h
This file is part of the API for handling calendar data and defines the Exception class...
KCalCore::ICalTimeZone::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:321
KCalCore::VCalFormat::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: vcalformat.cpp:2680
KCalCore::VCalFormat::ISOToQDate
QDate ISOToQDate(const QString &dtStr)
Takes a string in the YYYYMMDD format and returns a valid QDate.
Definition: vcalformat.cpp:2192
KCalCore::VCalFormat::readStatus
Attendee::PartStat readStatus(const char *s) const
Converts a status string into an Attendee::PartStat.
Definition: vcalformat.cpp:2583
KCalCore::Alarm::Display
Display a dialog box.
Definition: alarm.h:69
KCalCore::Recurrence::duration
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrence.cpp:481
KCalCore::Recurrence::yearMonths
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
Definition: recurrence.cpp:678
KCalCore::CalFormat::setLoadedProductId
void setLoadedProductId(const QString &id)
Sets the PRODID string loaded from calendar file.
Definition: calformat.cpp:118
KCalCore::VCalFormat::load
bool load(const Calendar::Ptr &calendar, const QString &fileName)
Definition: vcalformat.cpp:103
KCalCore::VCalFormat::qDateToISO
QString qDateToISO(const QDate &date)
Takes a QDate and returns a string in the format YYYYMMDDTHHMMSS.
Definition: vcalformat.cpp:2122
KCalCore::Attendee::InProcess
To-do in process of being completed.
Definition: attendee.h:77
KCalCore::Calendar::Ptr
QSharedPointer< Calendar > Ptr
A shared pointer to a Calendar.
Definition: calendar.h:138
KCalCore::Todo::List
QVector< Ptr > List
List of to-dos.
Definition: todo.h:55
KCalCore::Attendee::NeedsAction
Event, to-do or journal needs action (default)
Definition: attendee.h:71
KCalCore::VCalFormat::fromRawString
bool fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted=false, const QString &notebook=QString())
Definition: vcalformat.cpp:206
KCalCore::VCalFormat::eventToVEvent
VObject * eventToVEvent(const Event::Ptr &event)
Translates an Event into a VEvent-type VObject and returns a pointer to it.
Definition: vcalformat.cpp:656
KCalCore::Attendee::Ptr
QSharedPointer< Attendee > Ptr
A shared pointer to an Attendee object.
Definition: attendee.h:94
KCalCore::Attendee::Completed
To-do completed.
Definition: attendee.h:76
KCalCore::VCalFormat::populate
void populate(VObject *vcal, bool deleted=false, const QString &notebook=QString())
Takes a vCalendar tree of VObjects, and puts all of them that have the "event" property into the dict...
Definition: vcalformat.cpp:2265
KCalCore::Alarm::Procedure
Call a script.
Definition: alarm.h:70
event.h
This file is part of the API for handling calendar data and defines the Event class.
KCalCore::Todo
Provides a To-do in the sense of RFC2445.
Definition: todo.h:44
KCalCore::Event
This class provides an Event in the sense of RFC2445.
Definition: event.h:41
KCalCore::Attachment
Represents information related to an attachment for a Calendar Incidence.
Definition: attachment.h:59
KCalCore::Incidence::Secrecy
Secrecy
The different types of incidence access classifications.
Definition: incidence.h:93
KCalCore::Alarm::Audio
Play an audio file.
Definition: alarm.h:72
KCalCore::ICalTimeZones
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
Definition: icaltimezones.h:65
KCalCore::Attendee::Tentative
Event or to-do tentatively accepted.
Definition: attendee.h:74
KCalCore::VCalFormat::~VCalFormat
virtual ~VCalFormat()
Destructor.
Definition: vcalformat.cpp:98
KCalCore::ICalTimeZone
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
Definition: icaltimezones.h:176
KCalCore::Recurrence::recurrenceType
ushort recurrenceType() const
Returns the event's recurrence status.
Definition: recurrence.cpp:233
vcalformat.h
This file is part of the API for handling calendar data and defines the VCalFormat base class...
KCalCore::VCalFormat::parseTZ
QString parseTZ(const QByteArray &timezone) const
Parse TZ tag from vtimezone.
Definition: vcalformat.cpp:2094
KCalCore::VCalFormat
vCalendar format implementation.
Definition: vcalformat.h:69
KCalCore::Attendee
Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (...
Definition: attendee.h:57
KCalCore::Todo::Ptr
QSharedPointer< Todo > Ptr
A shared pointer to a Todo object.
Definition: todo.h:50
KCalCore::Recurrence::days
QBitArray days() const
Returns week day mask (bit 0 = Monday).
Definition: recurrence.cpp:629
KCalCore::Recurrence::endDateTime
KDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:428
KCalCore::VCalFormat::ISOToKDateTime
KDateTime ISOToKDateTime(const QString &dtStr)
Takes a string in YYYYMMDDTHHMMSS format and returns a valid KDateTime.
Definition: vcalformat.cpp:2163
KCalCore::Attendee::Accepted
Event, to-do or journal accepted.
Definition: attendee.h:72
KCalCore::Recurrence::recurs
bool recurs() const
Returns whether the event recurs at all.
Definition: recurrence.cpp:228
KCalCore::Incidence::SecrecyPublic
Not secret (default)
Definition: incidence.h:94
KCalCore::Attendee::Declined
Event, to-do or journal declined.
Definition: attendee.h:73
KCalCore::VCalFormat::fromString
bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted=false, const QString &notebook=QString())
Definition: vcalformat.cpp:200
KCalCore::ICalTimeZones::zone
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Definition: icaltimezones.cpp:184
KCalCore::Recurrence::monthPositions
QList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
Definition: recurrence.cpp:659
KCalCore::VCalFormat::dayFromNum
const char * dayFromNum(int day)
Takes a number 0 - 6 and returns the two letter string of that day, i.e.
Definition: vcalformat.cpp:2549
KCalCore::VCalFormat::kDateTimeToISO
QString kDateTimeToISO(const KDateTime &date, bool zulu=true)
Takes a KDateTime and returns a string in format YYYYMMDDTHHMMSS.
Definition: vcalformat.cpp:2135
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:57 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



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