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

KCalCore Library

  • kcalcore
vcalformat.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  Copyright (c) 1998 Preston Brown <pbrown@kde.org>
5  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
37 #include "vcalformat.h"
38 #include "calendar.h"
39 #include "event.h"
40 #include "exceptions.h"
41 #include "icaltimezones.h"
42 #include "todo.h"
43 #include "versit/vcc.h"
44 #include "versit/vobject.h"
45 
46 #include <KCodecs>
47 #include <KDebug>
48 
49 #include <QtCore/QBitArray>
50 #include <QtCore/QFile>
51 #include <QTextDocument> // for Qt::escape() and Qt::mightBeRichText()
52 
53 using namespace KCalCore;
54 
59 //@cond PRIVATE
60 template <typename K>
61 void removeAllVCal(QVector< QSharedPointer<K> > &c, const QSharedPointer<K> &x)
62 {
63  if (c.count() < 1) {
64  return;
65  }
66 
67  int cnt = c.count(x);
68  if (cnt != 1) {
69  qCritical() << "There number of relatedTos for this incidence is "
70  << cnt << " (there must be 1 relatedTo only)";
71  Q_ASSERT_X(false, "removeAllVCal", "Count is not 1.");
72  return;
73  }
74 
75  c.remove(c.indexOf(x));
76 }
77 
78 class KCalCore::VCalFormat::Private
79 {
80 public:
81  Calendar::Ptr mCalendar;
82  Event::List mEventsRelate; // Events with relations
83  Todo::List mTodosRelate; // To-dos with relations
84  QSet<QByteArray> mManuallyWrittenExtensionFields; // X- fields that are manually dumped
85 };
86 //@endcond
87 
88 VCalFormat::VCalFormat() : d(new KCalCore::VCalFormat::Private)
89 {
90 #if defined(KCALCORE_FOR_SYMBIAN)
91  d->mManuallyWrittenExtensionFields << VCRecurrenceIdProp;
92  d->mManuallyWrittenExtensionFields << EPOCAgendaEntryTypeProp;
93 #endif
94  d->mManuallyWrittenExtensionFields << KPilotIdProp;
95  d->mManuallyWrittenExtensionFields << KPilotStatusProp;
96 }
97 
98 VCalFormat::~VCalFormat()
99 {
100  delete d;
101 }
102 
103 bool VCalFormat::load(const Calendar::Ptr &calendar, const QString &fileName)
104 {
105  d->mCalendar = calendar;
106 
107  clearException();
108 
109  VObject *vcal = 0;
110 
111  // this is not necessarily only 1 vcal. Could be many vcals, or include
112  // a vcard...
113  vcal = Parse_MIME_FromFileName(const_cast<char *>(QFile::encodeName(fileName).data()));
114 
115  if (!vcal) {
116  setException(new Exception(Exception::CalVersionUnknown));
117  return false;
118  }
119 
120  // any other top-level calendar stuff should be added/initialized here
121 
122  // put all vobjects into their proper places
123  QString savedTimeZoneId = d->mCalendar->timeZoneId();
124  populate(vcal, false, fileName);
125  d->mCalendar->setTimeZoneId(savedTimeZoneId);
126 
127  // clean up from vcal API stuff
128  cleanVObjects(vcal);
129  cleanStrTbl();
130 
131  return true;
132 }
133 
134 bool VCalFormat::save(const Calendar::Ptr &calendar, const QString &fileName)
135 {
136  d->mCalendar = calendar;
137 
138  ICalTimeZones *tzlist = d->mCalendar->timeZones();
139 
140  QString tmpStr;
141  VObject *vcal, *vo;
142 
143  vcal = newVObject(VCCalProp);
144 
145  // addPropValue(vcal,VCLocationProp, "0.0");
146  addPropValue(vcal, VCProdIdProp, productId().toLatin1());
147  addPropValue(vcal, VCVersionProp, _VCAL_VERSION);
148 
149  // TODO STUFF
150  Todo::List todoList = d->mCalendar->rawTodos();
151  Todo::List::ConstIterator it;
152  for (it = todoList.constBegin(); it != todoList.constEnd(); ++it) {
153  if ((*it)->dtStart().timeZone().name().mid(0, 4) == 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")) { //krazy:exclude=strings
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 }
calendar.h
This file is part of the API for handling calendar data and defines the Calendar class.
KCalCore::Alarm::Ptr
QSharedPointer< Alarm > Ptr
A shared pointer to an Alarm object.
Definition: alarm.h:78
KCalCore::Alarm::Display
@ Display
Display a dialog box.
Definition: alarm.h:69
KCalCore::Alarm::Audio
@ Audio
Play an audio file.
Definition: alarm.h:72
KCalCore::Alarm::Procedure
@ Procedure
Call a script.
Definition: alarm.h:70
KCalCore::Attachment
Represents information related to an attachment for a Calendar Incidence.
Definition: attachment.h:60
KCalCore::Attachment::List
QVector< Ptr > List
List of attachments.
Definition: attachment.h:70
KCalCore::Attachment::Ptr
QSharedPointer< Attachment > Ptr
A shared pointer to an Attachment object.
Definition: attachment.h:65
KCalCore::Attendee
Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (...
Definition: attendee.h:58
KCalCore::Attendee::PartStat
PartStat
The different types of participant status.
Definition: attendee.h:70
KCalCore::Attendee::InProcess
@ InProcess
To-do in process of being completed.
Definition: attendee.h:77
KCalCore::Attendee::Tentative
@ Tentative
Event or to-do tentatively accepted.
Definition: attendee.h:74
KCalCore::Attendee::Accepted
@ Accepted
Event, to-do or journal accepted.
Definition: attendee.h:72
KCalCore::Attendee::Delegated
@ Delegated
Event or to-do delegated.
Definition: attendee.h:75
KCalCore::Attendee::NeedsAction
@ NeedsAction
Event, to-do or journal needs action (default)
Definition: attendee.h:71
KCalCore::Attendee::Declined
@ Declined
Event, to-do or journal declined.
Definition: attendee.h:73
KCalCore::Attendee::Completed
@ Completed
To-do completed.
Definition: attendee.h:76
KCalCore::Attendee::Ptr
QSharedPointer< Attendee > Ptr
A shared pointer to an Attendee object.
Definition: attendee.h:113
KCalCore::CalFormat::setLoadedProductId
void setLoadedProductId(const QString &id)
Sets the PRODID string loaded from calendar file.
Definition: calformat.cpp:118
KCalCore::CalFormat::clearException
void clearException()
Clears the exception status.
Definition: calformat.cpp:79
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::Calendar::Ptr
QSharedPointer< Calendar > Ptr
A shared pointer to a Calendar.
Definition: calendar.h:138
KCalCore::Event
This class provides an Event in the sense of RFC2445.
Definition: event.h:42
KCalCore::Event::Transparent
@ Transparent
Event does not appear in free/busy time.
Definition: event.h:49
KCalCore::Event::Opaque
@ Opaque
Event appears in free/busy time.
Definition: event.h:48
KCalCore::Event::Ptr
QSharedPointer< Event > Ptr
A shared pointer to an Event object.
Definition: event.h:55
KCalCore::Event::List
QVector< Ptr > List
List of events.
Definition: event.h:60
KCalCore::Exception
Exception base class, currently used as a fancy kind of error code and not as an C++ exception.
Definition: exceptions.h:51
KCalCore::Exception::CalVersionUnknown
@ CalVersionUnknown
Unknown calendar format detected.
Definition: exceptions.h:66
KCalCore::ICalTimeZoneSource
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data.
Definition: icaltimezones.h:406
KCalCore::ICalTimeZoneSource::parse
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from an iCalendar VTIMEZO...
Definition: icaltimezones.cpp:852
KCalCore::ICalTimeZone
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
Definition: icaltimezones.h:178
KCalCore::ICalTimeZone::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:317
KCalCore::ICalTimeZones
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
Definition: icaltimezones.h:66
KCalCore::ICalTimeZones::zone
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Definition: icaltimezones.cpp:180
KCalCore::Incidence::Secrecy
Secrecy
The different types of incidence access classifications.
Definition: incidence.h:93
KCalCore::Incidence::SecrecyPrivate
@ SecrecyPrivate
Secret to the owner.
Definition: incidence.h:95
KCalCore::Incidence::SecrecyConfidential
@ SecrecyConfidential
Secret to the owner and some others.
Definition: incidence.h:96
KCalCore::Incidence::SecrecyPublic
@ SecrecyPublic
Not secret (default)
Definition: incidence.h:94
KCalCore::Incidence::Ptr
QSharedPointer< Incidence > Ptr
A shared pointer to an Incidence.
Definition: incidence.h:112
KCalCore::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:88
KCalCore::Recurrence::recurrenceType
ushort recurrenceType() const
Returns the event's recurrence status.
Definition: recurrence.cpp:233
KCalCore::Recurrence::frequency
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:599
KCalCore::Recurrence::recurs
bool recurs() const
Returns whether the event recurs at all.
Definition: recurrence.cpp:228
KCalCore::Recurrence::monthDays
QList< int > monthDays() const
Returns list of day numbers of a month.
Definition: recurrence.cpp:648
KCalCore::Recurrence::endDateTime
KDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:428
KCalCore::Recurrence::days
QBitArray days() const
Returns week day mask (bit 0 = Monday).
Definition: recurrence.cpp:629
KCalCore::Recurrence::yearMonths
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
Definition: recurrence.cpp:678
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::yearDays
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Definition: recurrence.cpp:667
KCalCore::Recurrence::monthPositions
QList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
Definition: recurrence.cpp:659
KCalCore::SortableList
A QList which can be sorted.
Definition: sortablelist.h:87
KCalCore::Todo
Provides a To-do in the sense of RFC2445.
Definition: todo.h:45
KCalCore::Todo::List
QVector< Ptr > List
List of to-dos.
Definition: todo.h:55
KCalCore::Todo::Ptr
QSharedPointer< Todo > Ptr
A shared pointer to a Todo object.
Definition: todo.h:50
KCalCore::VCalFormat
vCalendar format implementation.
Definition: vcalformat.h:70
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::VCalFormat::load
bool load(const Calendar::Ptr &calendar, const QString &fileName)
Definition: vcalformat.cpp:103
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::VCalFormat::writeStatus
QByteArray writeStatus(Attendee::PartStat status) const
Converts an Attendee::PartStat into a QByteArray string.
Definition: vcalformat.cpp:2615
KCalCore::VCalFormat::qDateToISO
QString qDateToISO(const QDate &date)
Takes a QDate and returns a string in the format YYYYMMDDTHHMMSS.
Definition: vcalformat.cpp:2122
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
KCalCore::VCalFormat::VTodoToEvent
Todo::Ptr VTodoToEvent(VObject *vtodo)
Translates a VObject of the TODO type into an Event.
Definition: vcalformat.cpp:1029
KCalCore::VCalFormat::fromString
bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted=false, const QString &notebook=QString())
Definition: vcalformat.cpp:200
KCalCore::VCalFormat::VCalFormat
VCalFormat()
Constructor a new vCalendar Format object.
Definition: vcalformat.cpp:88
KCalCore::VCalFormat::parseDst
QString parseDst(QByteArray &timezone) const
Parse DAYLIGHT tag from vtimezone.
Definition: vcalformat.cpp:2101
KCalCore::VCalFormat::readStatus
Attendee::PartStat readStatus(const char *s) const
Converts a status string into an Attendee::PartStat.
Definition: vcalformat.cpp:2583
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::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::VCalFormat::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: vcalformat.cpp:2680
KCalCore::VCalFormat::parseTZ
QString parseTZ(const QByteArray &timezone) const
Parse TZ tag from vtimezone.
Definition: vcalformat.cpp:2094
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::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::VCalFormat::VEventToEvent
Event::Ptr VEventToEvent(VObject *vevent)
Translates a VObject into a Event and returns a pointer to it.
Definition: vcalformat.cpp:1518
KCalCore::VCalFormat::~VCalFormat
virtual ~VCalFormat()
Destructor.
Definition: vcalformat.cpp:98
KCalCore::VCalFormat::ISOToKDateTime
KDateTime ISOToKDateTime(const QString &dtStr)
Takes a string in YYYYMMDDTHHMMSS format and returns a valid KDateTime.
Definition: vcalformat.cpp:2163
KCalCore::VCalFormat::toString
QString toString(const Calendar::Ptr &calendar, const QString &notebook=QString(), bool deleted=false)
Definition: vcalformat.cpp:235
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::numFromDay
int numFromDay(const QString &day)
Converts a two letter representation of the day (i.e.
Definition: vcalformat.cpp:2556
KCalCore::VCalFormat::save
bool save(const Calendar::Ptr &calendar, const QString &fileName)
Definition: vcalformat.cpp:134
event.h
This file is part of the API for handling calendar data and defines the Event class.
exceptions.h
This file is part of the API for handling calendar data and defines the Exception class.
KCalCore
TODO: KDE5:
Definition: alarm.h:47
todo.h
This file is part of the API for handling calendar data and defines the Todo class.
vcalformat.h
This file is part of the API for handling calendar data and defines the VCalFormat base class.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Wed May 25 2022 00:00:00 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

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

kdepimlibs-4.14.10 API Reference

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

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