KCalendarCore

vcalformat.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  SPDX-FileCopyrightText: 1998 Preston Brown <[email protected]>
5  SPDX-FileCopyrightText: 2001 Cornelius Schumacher <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 /**
10  @file
11  This file is part of the API for handling calendar data and
12  defines the VCalFormat base class.
13 
14  This class implements the vCalendar format. It provides methods for
15  loading/saving/converting vCalendar format data into the internal
16  representation as Calendar and Incidences.
17 
18  @brief
19  vCalendar format implementation.
20 
21  @author Preston Brown <[email protected]>
22  @author Cornelius Schumacher <[email protected]>
23 */
24 #include "vcalformat.h"
25 #include "calendar.h"
26 #include "event.h"
27 #include "exceptions.h"
28 #include "todo.h"
29 
30 #include "kcalendarcore_debug.h"
31 
32 extern "C" {
33 #include <libical/vcc.h>
34 #include <libical/vobject.h>
35 }
36 
37 #include <QBitArray>
38 #include <QFile>
39 #include <QTextDocument> // for .toHtmlEscaped() and Qt::mightBeRichText()
40 #include <QTimeZone>
41 
42 using namespace KCalendarCore;
43 
44 /**
45  Private class that helps to provide binary compatibility between releases.
46  @internal
47 */
48 //@cond PRIVATE
49 template <typename K>
50 void removeAllVCal(QVector< QSharedPointer<K> > &c, const QSharedPointer<K> &x)
51 {
52  if (c.count() < 1) {
53  return;
54  }
55 
56  int cnt = c.count(x);
57  if (cnt != 1) {
58  qCritical() << "There number of relatedTos for this incidence is "
59  << cnt << " (there must be 1 relatedTo only)";
60  Q_ASSERT_X(false, "removeAllVCal", "Count is not 1.");
61  return;
62  }
63 
64  c.remove(c.indexOf(x));
65 }
66 
67 class Q_DECL_HIDDEN KCalendarCore::VCalFormat::Private
68 {
69 public:
70  Calendar::Ptr mCalendar;
71  Event::List mEventsRelate; // Events with relations
72  Todo::List mTodosRelate; // To-dos with relations
73  QSet<QByteArray> mManuallyWrittenExtensionFields; // X- fields that are manually dumped
74 };
75 //@endcond
76 
78 {
79 }
80 
82 {
83  delete d;
84 }
85 
86 bool VCalFormat::load(const Calendar::Ptr &calendar, const QString &fileName)
87 {
88  d->mCalendar = calendar;
89 
91 
92  // this is not necessarily only 1 vcal. Could be many vcals, or include
93  // a vcard...
94  VObject *vcal = Parse_MIME_FromFileName(const_cast<char *>(QFile::encodeName(fileName).data()));
95 
96  if (!vcal) {
98  return false;
99  }
100 
101  // any other top-level calendar stuff should be added/initialized here
102 
103  // put all vobjects into their proper places
104  auto savedTimeZoneId = d->mCalendar->timeZoneId();
105  populate(vcal, false, fileName);
106  d->mCalendar->setTimeZoneId(savedTimeZoneId);
107 
108  // clean up from vcal API stuff
109  cleanVObjects(vcal);
110  cleanStrTbl();
111 
112  return true;
113 }
114 
115 bool VCalFormat::save(const Calendar::Ptr &calendar, const QString &fileName)
116 {
117  Q_UNUSED(calendar);
118  Q_UNUSED(fileName);
119  qCWarning(KCALCORE_LOG) << "Saving VCAL is not supported";
120  return false;
121 }
122 
123 bool VCalFormat::fromString(const Calendar::Ptr &calendar, const QString &string,
124  bool deleted, const QString &notebook)
125 {
126  return fromRawString(calendar, string.toUtf8(), deleted, notebook);
127 }
128 
129 bool VCalFormat::fromRawString(const Calendar::Ptr &calendar, const QByteArray &string,
130  bool deleted, const QString &notebook)
131 {
132  d->mCalendar = calendar;
133 
134  if (!string.size()) {
135  return false;
136  }
137 
138  VObject *vcal = Parse_MIME(string.data(), string.size());
139  if (!vcal) {
140  return false;
141  }
142 
143  VObjectIterator i;
144  initPropIterator(&i, vcal);
145 
146  // put all vobjects into their proper places
147  auto savedTimeZoneId = d->mCalendar->timeZoneId();
148  populate(vcal, deleted, notebook);
149  d->mCalendar->setTimeZoneId(savedTimeZoneId);
150 
151  // clean up from vcal API stuff
152  cleanVObjects(vcal);
153  cleanStrTbl();
154 
155  return true;
156 }
157 
159  const QString &notebook, bool deleted)
160 {
161  Q_UNUSED(calendar);
162  Q_UNUSED(notebook);
163  Q_UNUSED(deleted);
164 
165  qCWarning(KCALCORE_LOG) << "Exporting into VCAL is not supported";
166  return {};
167 }
168 
170 {
171  VObject *vo = nullptr;
172  VObjectIterator voi;
173  char *s = nullptr;
174 
175  Todo::Ptr anEvent(new Todo);
176 
177  // creation date
178  if ((vo = isAPropertyOf(vtodo, VCDCreatedProp)) != nullptr) {
179  anEvent->setCreated(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
180  deleteStr(s);
181  }
182 
183  // unique id
184  vo = isAPropertyOf(vtodo, VCUniqueStringProp);
185  // while the UID property is preferred, it is not required. We'll use the
186  // default Event UID if none is given.
187  if (vo) {
188  anEvent->setUid(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
189  deleteStr(s);
190  }
191 
192  // last modification date
193  if ((vo = isAPropertyOf(vtodo, VCLastModifiedProp)) != nullptr) {
194  anEvent->setLastModified(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
195  deleteStr(s);
196  } else {
197  anEvent->setLastModified(QDateTime::currentDateTimeUtc());
198  }
199 
200  // organizer
201  // if our extension property for the event's ORGANIZER exists, add it.
202  if ((vo = isAPropertyOf(vtodo, ICOrganizerProp)) != nullptr) {
203  anEvent->setOrganizer(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
204  deleteStr(s);
205  } else {
206  if (d->mCalendar->owner().name() != QLatin1String("Unknown Name")) {
207  anEvent->setOrganizer(d->mCalendar->owner());
208  }
209  }
210 
211  // attendees.
212  initPropIterator(&voi, vtodo);
213  while (moreIteration(&voi)) {
214  vo = nextVObject(&voi);
215  if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
216  Attendee a;
217  VObject *vp;
218  s = fakeCString(vObjectUStringZValue(vo));
219  QString tmpStr = QString::fromUtf8(s);
220  deleteStr(s);
221  tmpStr = tmpStr.simplified();
222  int emailPos1;
223  if ((emailPos1 = tmpStr.indexOf(QLatin1Char('<'))) > 0) {
224  // both email address and name
225  int emailPos2 = tmpStr.lastIndexOf(QLatin1Char('>'));
226  a = Attendee(tmpStr.left(emailPos1 - 1), tmpStr.mid(emailPos1 + 1, emailPos2 - (emailPos1 + 1)));
227  } else if (tmpStr.indexOf(QLatin1Char('@')) > 0) {
228  // just an email address
229  a = Attendee(QString(), tmpStr);
230  } else {
231  // just a name
232  // WTF??? Replacing the spaces of a name and using this as email?
233  QString email = tmpStr.replace(QLatin1Char(' '), QLatin1Char('.'));
234  a = Attendee(tmpStr, email);
235  }
236 
237  // is there an RSVP property?
238  if ((vp = isAPropertyOf(vo, VCRSVPProp)) != nullptr) {
239  a.setRSVP(vObjectStringZValue(vp));
240  }
241  // is there a status property?
242  if ((vp = isAPropertyOf(vo, VCStatusProp)) != nullptr) {
243  a.setStatus(readStatus(vObjectStringZValue(vp)));
244  }
245  // add the attendee
246  anEvent->addAttendee(a);
247  }
248  }
249 
250  // description for todo
251  if ((vo = isAPropertyOf(vtodo, VCDescriptionProp)) != nullptr) {
252  s = fakeCString(vObjectUStringZValue(vo));
253  anEvent->setDescription(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s)));
254  deleteStr(s);
255  }
256 
257  // summary
258  if ((vo = isAPropertyOf(vtodo, VCSummaryProp))) {
259  s = fakeCString(vObjectUStringZValue(vo));
260  anEvent->setSummary(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s)));
261  deleteStr(s);
262  }
263 
264  // location
265  if ((vo = isAPropertyOf(vtodo, VCLocationProp)) != nullptr) {
266  s = fakeCString(vObjectUStringZValue(vo));
267  anEvent->setLocation(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s)));
268  deleteStr(s);
269  }
270 
271  // completed
272  // was: status
273  if ((vo = isAPropertyOf(vtodo, VCStatusProp)) != nullptr) {
274  s = fakeCString(vObjectUStringZValue(vo));
275  if (s && strcmp(s, "COMPLETED") == 0) {
276  anEvent->setCompleted(true);
277  } else {
278  anEvent->setCompleted(false);
279  }
280  deleteStr(s);
281  } else {
282  anEvent->setCompleted(false);
283  }
284 
285  // completion date
286  if ((vo = isAPropertyOf(vtodo, VCCompletedProp)) != nullptr) {
287  anEvent->setCompleted(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
288  deleteStr(s);
289  }
290 
291  // priority
292  if ((vo = isAPropertyOf(vtodo, VCPriorityProp))) {
293  s = fakeCString(vObjectUStringZValue(vo));
294  if (s) {
295  anEvent->setPriority(atoi(s));
296  deleteStr(s);
297  }
298  }
299 
300  anEvent->setAllDay(false);
301 
302  // due date
303  if ((vo = isAPropertyOf(vtodo, VCDueProp)) != nullptr) {
304  anEvent->setDtDue(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
305  deleteStr(s);
306  if (anEvent->dtDue().time().hour() == 0 &&
307  anEvent->dtDue().time().minute() == 0 &&
308  anEvent->dtDue().time().second() == 0) {
309  anEvent->setAllDay(true);
310  }
311  } else {
312  anEvent->setDtDue(QDateTime());
313  }
314 
315  // start time
316  if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != nullptr) {
317  anEvent->setDtStart(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
318  deleteStr(s);
319  if (anEvent->dtStart().time().hour() == 0 &&
320  anEvent->dtStart().time().minute() == 0 &&
321  anEvent->dtStart().time().second() == 0) {
322  anEvent->setAllDay(true);
323  }
324  } else {
325  anEvent->setDtStart(QDateTime());
326  }
327 
328  // recurrence stuff
329  if ((vo = isAPropertyOf(vtodo, VCRRuleProp)) != nullptr) {
330  uint recurrenceType = Recurrence::rNone;
331  int recurrenceTypeAbbrLen = 0;
332 
333  QString tmpStr = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
334  deleteStr(s);
335  tmpStr = tmpStr.simplified();
336  const int tmpStrLen = tmpStr.length();
337  if (tmpStrLen > 0) {
338  tmpStr = tmpStr.toUpper();
339  // first, read the type of the recurrence
340  recurrenceTypeAbbrLen = 1;
341  if (tmpStr.at(0) == QLatin1Char('D')) {
342  recurrenceType = Recurrence::rDaily;
343  } else if (tmpStr.at(0) == QLatin1Char('W')) {
344  recurrenceType = Recurrence::rWeekly;
345  } else if (tmpStrLen > 1) {
346  recurrenceTypeAbbrLen = 2;
347  if (tmpStr.leftRef(2) == QLatin1String("MP")) {
348  recurrenceType = Recurrence::rMonthlyPos;
349  } else if (tmpStr.leftRef(2) == QLatin1String("MD")) {
350  recurrenceType = Recurrence::rMonthlyDay;
351  } else if (tmpStr.leftRef(2) == QLatin1String("YM")) {
352  recurrenceType = Recurrence::rYearlyMonth;
353  } else if (tmpStr.leftRef(2) == QLatin1String("YD")) {
354  recurrenceType = Recurrence::rYearlyDay;
355  }
356  }
357  }
358 
359  if (recurrenceType != Recurrence::rNone) {
360  // Immediately after the type is the frequency
361  int index = tmpStr.indexOf(QLatin1Char(' '));
362  int last = tmpStr.lastIndexOf(QLatin1Char(' ')) + 1; // find last entry
363  int rFreq = tmpStr.midRef(recurrenceTypeAbbrLen, (index - 1)).toInt();
364  ++index; // advance to beginning of stuff after freq
365 
366  // Read the type-specific settings
367  switch (recurrenceType) {
368  case Recurrence::rDaily:
369  anEvent->recurrence()->setDaily(rFreq);
370  break;
371 
372  case Recurrence::rWeekly: {
373  QBitArray qba(7);
374  QString dayStr;
375  if (index == last) {
376  // e.g. W1 #0
377  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
378  } else {
379  // e.g. W1 SU #0
380  while (index < last) {
381  dayStr = tmpStr.mid(index, 3);
382  int dayNum = numFromDay(dayStr);
383  if (dayNum >= 0) {
384  qba.setBit(dayNum);
385  }
386  index += 3; // advance to next day, or possibly "#"
387  }
388  }
389  anEvent->recurrence()->setWeekly(rFreq, qba);
390  break;
391  }
392 
393  case Recurrence::rMonthlyPos: {
394  anEvent->recurrence()->setMonthly(rFreq);
395 
396  QBitArray qba(7);
397  short tmpPos;
398  if (index == last) {
399  // e.g. MP1 #0
400  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
401  if (tmpPos == 5) {
402  tmpPos = -1;
403  }
404  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
405  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
406  } else {
407  // e.g. MP1 1+ SU #0
408  while (index < last) {
409  tmpPos = tmpStr.mid(index, 1).toShort();
410  index += 1;
411  if (tmpStr.mid(index, 1) == QLatin1String("-")) {
412  // convert tmpPos to negative
413  tmpPos = 0 - tmpPos;
414  }
415  index += 2; // advance to day(s)
416  while (numFromDay(tmpStr.mid(index, 3)) >= 0) {
417  int dayNum = numFromDay(tmpStr.mid(index, 3));
418  qba.setBit(dayNum);
419  index += 3; // advance to next day, or possibly pos or "#"
420  }
421  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
422  qba.detach();
423  qba.fill(false); // clear out
424  } // while != "#"
425  }
426  break;
427  }
428 
429  case Recurrence::rMonthlyDay:
430  anEvent->recurrence()->setMonthly(rFreq);
431  if (index == last) {
432  // e.g. MD1 #0
433  short tmpDay = anEvent->dtStart().date().day();
434  anEvent->recurrence()->addMonthlyDate(tmpDay);
435  } else {
436  // e.g. MD1 3 #0
437  while (index < last) {
438  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
439  if ((tmpStr.mid((index2 - 1), 1) == QLatin1String("-")) ||
440  (tmpStr.mid((index2 - 1), 1) == QLatin1String("+"))) {
441  index2 = index2 - 1;
442  }
443  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
444  index = index2;
445  if (tmpStr.mid(index, 1) == QLatin1String("-")) {
446  tmpDay = 0 - tmpDay;
447  }
448  index += 2; // advance the index;
449  anEvent->recurrence()->addMonthlyDate(tmpDay);
450  } // while != #
451  }
452  break;
453 
454  case Recurrence::rYearlyMonth:
455  anEvent->recurrence()->setYearly(rFreq);
456 
457  if (index == last) {
458  // e.g. YM1 #0
459  short tmpMonth = anEvent->dtStart().date().month();
460  anEvent->recurrence()->addYearlyMonth(tmpMonth);
461  } else {
462  // e.g. YM1 3 #0
463  while (index < last) {
464  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
465  short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort();
466  index = index2 + 1;
467  anEvent->recurrence()->addYearlyMonth(tmpMonth);
468  } // while != #
469  }
470  break;
471 
472  case Recurrence::rYearlyDay:
473  anEvent->recurrence()->setYearly(rFreq);
474 
475  if (index == last) {
476  // e.g. YD1 #0
477  short tmpDay = anEvent->dtStart().date().dayOfYear();
478  anEvent->recurrence()->addYearlyDay(tmpDay);
479  } else {
480  // e.g. YD1 123 #0
481  while (index < last) {
482  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
483  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
484  index = index2 + 1;
485  anEvent->recurrence()->addYearlyDay(tmpDay);
486  } // while != #
487  }
488  break;
489 
490  default:
491  break;
492  }
493 
494  // find the last field, which is either the duration or the end date
495  index = last;
496  if (tmpStr.mid(index, 1) == QLatin1String("#")) {
497  // Nr of occurrences
498  index++;
499  int rDuration = tmpStr.midRef(index, tmpStr.length() - index).toInt();
500  if (rDuration > 0) {
501  anEvent->recurrence()->setDuration(rDuration);
502  }
503  } else if (tmpStr.indexOf(QLatin1Char('T'), index) != -1) {
504  QDateTime rEndDate = ISOToQDateTime(tmpStr.mid(index, tmpStr.length() - index));
505  anEvent->recurrence()->setEndDateTime(rEndDate);
506  }
507  } else {
508  qCDebug(KCALCORE_LOG) << "we don't understand this type of recurrence!";
509  } // if known recurrence type
510  } // repeats
511 
512  // recurrence exceptions
513  if ((vo = isAPropertyOf(vtodo, VCExpDateProp)) != nullptr) {
514  s = fakeCString(vObjectUStringZValue(vo));
515  QStringList exDates = QString::fromUtf8(s).split(QLatin1Char(','));
517  for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) {
518  QDateTime exDate = ISOToQDateTime(*it);
519  if (exDate.time().hour() == 0 &&
520  exDate.time().minute() == 0 &&
521  exDate.time().second() == 0) {
522  anEvent->recurrence()->addExDate(ISOToQDate(*it));
523  } else {
524  anEvent->recurrence()->addExDateTime(exDate);
525  }
526  }
527  deleteStr(s);
528  }
529 
530  // alarm stuff
531  if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) {
532  Alarm::Ptr alarm;
533  VObject *a = isAPropertyOf(vo, VCRunTimeProp);
534  VObject *b = isAPropertyOf(vo, VCDisplayStringProp);
535 
536  if (a || b) {
537  alarm = anEvent->newAlarm();
538  if (a) {
539  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
540  deleteStr(s);
541  }
542  alarm->setEnabled(true);
543  if (b) {
544  s = fakeCString(vObjectUStringZValue(b));
545  alarm->setDisplayAlarm(QString::fromUtf8(s));
546  deleteStr(s);
547  } else {
548  alarm->setDisplayAlarm(QString());
549  }
550  }
551  }
552 
553  if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) {
554  Alarm::Ptr alarm;
555  VObject *a;
556  VObject *b;
557  a = isAPropertyOf(vo, VCRunTimeProp);
558  b = isAPropertyOf(vo, VCAudioContentProp);
559 
560  if (a || b) {
561  alarm = anEvent->newAlarm();
562  if (a) {
563  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
564  deleteStr(s);
565  }
566  alarm->setEnabled(true);
567  if (b) {
568  s = fakeCString(vObjectUStringZValue(b));
569  alarm->setAudioAlarm(QFile::decodeName(s));
570  deleteStr(s);
571  } else {
572  alarm->setAudioAlarm(QString());
573  }
574  }
575  }
576 
577  if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) {
578  Alarm::Ptr alarm;
579  VObject *a = isAPropertyOf(vo, VCRunTimeProp);
580  VObject *b = isAPropertyOf(vo, VCProcedureNameProp);
581 
582  if (a || b) {
583  alarm = anEvent->newAlarm();
584  if (a) {
585  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
586  deleteStr(s);
587  }
588  alarm->setEnabled(true);
589 
590  if (b) {
591  s = fakeCString(vObjectUStringZValue(b));
592  alarm->setProcedureAlarm(QFile::decodeName(s));
593  deleteStr(s);
594  } else {
595  alarm->setProcedureAlarm(QString());
596  }
597  }
598  }
599 
600  // related todo
601  if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != nullptr) {
602  anEvent->setRelatedTo(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
603  deleteStr(s);
604  d->mTodosRelate.append(anEvent);
605  }
606 
607  // secrecy
609  if ((vo = isAPropertyOf(vtodo, VCClassProp)) != nullptr) {
610  s = fakeCString(vObjectUStringZValue(vo));
611  if (s && strcmp(s, "PRIVATE") == 0) {
612  secrecy = Incidence::SecrecyPrivate;
613  } else if (s && strcmp(s, "CONFIDENTIAL") == 0) {
615  }
616  deleteStr(s);
617  }
618  anEvent->setSecrecy(secrecy);
619 
620  // categories
621  if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != nullptr) {
622  s = fakeCString(vObjectUStringZValue(vo));
623  QString categories = QString::fromUtf8(s);
624  deleteStr(s);
625  QStringList tmpStrList = categories.split(QLatin1Char(';'));
626  anEvent->setCategories(tmpStrList);
627  }
628 
629  return anEvent;
630 }
631 
633 {
634  VObject *vo = nullptr;
635  VObjectIterator voi;
636  char *s = nullptr;
637 
638  Event::Ptr anEvent(new Event);
639 
640  // creation date
641  if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != nullptr) {
642  anEvent->setCreated(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
643  deleteStr(s);
644  }
645 
646  // unique id
647  vo = isAPropertyOf(vevent, VCUniqueStringProp);
648  // while the UID property is preferred, it is not required. We'll use the
649  // default Event UID if none is given.
650  if (vo) {
651  anEvent->setUid(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
652  deleteStr(s);
653  }
654 
655  // revision
656  // again NSCAL doesn't give us much to work with, so we improvise...
657  anEvent->setRevision(0);
658  if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != nullptr) {
659  s = fakeCString(vObjectUStringZValue(vo));
660  if (s) {
661  anEvent->setRevision(atoi(s));
662  deleteStr(s);
663  }
664  }
665 
666  // last modification date
667  if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != nullptr) {
668  anEvent->setLastModified(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
669  deleteStr(s);
670  } else {
671  anEvent->setLastModified(QDateTime::currentDateTimeUtc());
672  }
673 
674  // organizer
675  // if our extension property for the event's ORGANIZER exists, add it.
676  if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != nullptr) {
677  // FIXME: Also use the full name, not just the email address
678  anEvent->setOrganizer(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
679  deleteStr(s);
680  } else {
681  if (d->mCalendar->owner().name() != QLatin1String("Unknown Name")) {
682  anEvent->setOrganizer(d->mCalendar->owner());
683  }
684  }
685 
686  // deal with attendees.
687  initPropIterator(&voi, vevent);
688  while (moreIteration(&voi)) {
689  vo = nextVObject(&voi);
690  if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) {
691  Attendee a;
692  VObject *vp = nullptr;
693  s = fakeCString(vObjectUStringZValue(vo));
694  QString tmpStr = QString::fromUtf8(s);
695  deleteStr(s);
696  tmpStr = tmpStr.simplified();
697  int emailPos1;
698  if ((emailPos1 = tmpStr.indexOf(QLatin1Char('<'))) > 0) {
699  // both email address and name
700  int emailPos2 = tmpStr.lastIndexOf(QLatin1Char('>'));
701  a = Attendee(tmpStr.left(emailPos1 - 1), tmpStr.mid(emailPos1 + 1, emailPos2 - (emailPos1 + 1)));
702  } else if (tmpStr.indexOf(QLatin1Char('@')) > 0) {
703  // just an email address
704  a = Attendee(QString(), tmpStr);
705  } else {
706  // just a name
707  QString email = tmpStr.replace(QLatin1Char(' '), QLatin1Char('.'));
708  a = Attendee(tmpStr, email);
709  }
710 
711  // is there an RSVP property?
712  if ((vp = isAPropertyOf(vo, VCRSVPProp)) != nullptr) {
713  a.setRSVP(vObjectStringZValue(vp));
714  }
715  // is there a status property?
716  if ((vp = isAPropertyOf(vo, VCStatusProp)) != nullptr) {
717  a.setStatus(readStatus(vObjectStringZValue(vp)));
718  }
719  // add the attendee
720  anEvent->addAttendee(a);
721  }
722  }
723 
724  // This isn't strictly true. An event that doesn't have a start time
725  // or an end time isn't all-day, it has an anchor in time but it doesn't
726  // "take up" any time.
727  /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) ||
728  (isAPropertyOf(vevent, VCDTendProp) == 0)) {
729  anEvent->setAllDay(true);
730  } else {
731  }*/
732 
733  anEvent->setAllDay(false);
734 
735  // start time
736  if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != nullptr) {
737  anEvent->setDtStart(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
738  deleteStr(s);
739 
740  if (anEvent->dtStart().time().hour() == 0 &&
741  anEvent->dtStart().time().minute() == 0 &&
742  anEvent->dtStart().time().second() == 0) {
743  anEvent->setAllDay(true);
744  }
745  }
746 
747  // stop time
748  if ((vo = isAPropertyOf(vevent, VCDTendProp)) != nullptr) {
749  anEvent->setDtEnd(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))));
750  deleteStr(s);
751 
752  if (anEvent->dtEnd().time().hour() == 0 &&
753  anEvent->dtEnd().time().minute() == 0 &&
754  anEvent->dtEnd().time().second() == 0) {
755  anEvent->setAllDay(true);
756  }
757  }
758 
759  // at this point, there should be at least a start or end time.
760  // fix up for events that take up no time but have a time associated
761  if (!isAPropertyOf(vevent, VCDTstartProp)) {
762  anEvent->setDtStart(anEvent->dtEnd());
763  }
764  if (! isAPropertyOf(vevent, VCDTendProp)) {
765  anEvent->setDtEnd(anEvent->dtStart());
766  }
767 
768  ///////////////////////////////////////////////////////////////////////////
769 
770  // recurrence stuff
771  if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != nullptr) {
772  uint recurrenceType = Recurrence::rNone;
773  int recurrenceTypeAbbrLen = 0;
774 
775  QString tmpStr = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
776  deleteStr(s);
777  tmpStr = tmpStr.simplified();
778  const int tmpStrLen = tmpStr.length();
779  if (tmpStrLen > 0) {
780  tmpStr = tmpStr.toUpper();
781  // first, read the type of the recurrence
782  recurrenceTypeAbbrLen = 1;
783  if (tmpStr.at(0) == QLatin1Char('D')) {
784  recurrenceType = Recurrence::rDaily;
785  } else if (tmpStr.at(0) == QLatin1Char('W')) {
786  recurrenceType = Recurrence::rWeekly;
787  } else if (tmpStrLen > 1){
788  recurrenceTypeAbbrLen = 2;
789  if (tmpStr.leftRef(2) == QLatin1String("MP")) {
790  recurrenceType = Recurrence::rMonthlyPos;
791  } else if (tmpStr.leftRef(2) == QLatin1String("MD")) {
792  recurrenceType = Recurrence::rMonthlyDay;
793  } else if (tmpStr.leftRef(2) == QLatin1String("YM")) {
794  recurrenceType = Recurrence::rYearlyMonth;
795  } else if (tmpStr.leftRef(2) == QLatin1String("YD")) {
796  recurrenceType = Recurrence::rYearlyDay;
797  }
798  }
799  }
800 
801  if (recurrenceType != Recurrence::rNone) {
802  // Immediately after the type is the frequency
803  int index = tmpStr.indexOf(QLatin1Char(' '));
804  int last = tmpStr.lastIndexOf(QLatin1Char(' ')) + 1; // find last entry
805  int rFreq = tmpStr.midRef(recurrenceTypeAbbrLen, (index - 1)).toInt();
806  ++index; // advance to beginning of stuff after freq
807 
808  // Read the type-specific settings
809  switch (recurrenceType) {
810  case Recurrence::rDaily:
811  anEvent->recurrence()->setDaily(rFreq);
812  break;
813 
814  case Recurrence::rWeekly: {
815  QBitArray qba(7);
816  QString dayStr;
817  if (index == last) {
818  // e.g. W1 #0
819  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
820  } else {
821  // e.g. W1 SU #0
822  while (index < last) {
823  dayStr = tmpStr.mid(index, 3);
824  int dayNum = numFromDay(dayStr);
825  if (dayNum >= 0) {
826  qba.setBit(dayNum);
827  }
828  index += 3; // advance to next day, or possibly "#"
829  }
830  }
831  anEvent->recurrence()->setWeekly(rFreq, qba);
832  break;
833  }
834 
835  case Recurrence::rMonthlyPos: {
836  anEvent->recurrence()->setMonthly(rFreq);
837 
838  QBitArray qba(7);
839  short tmpPos;
840  if (index == last) {
841  // e.g. MP1 #0
842  tmpPos = anEvent->dtStart().date().day() / 7 + 1;
843  if (tmpPos == 5) {
844  tmpPos = -1;
845  }
846  qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1);
847  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
848  } else {
849  // e.g. MP1 1+ SU #0
850  while (index < last) {
851  tmpPos = tmpStr.mid(index, 1).toShort();
852  index += 1;
853  if (tmpStr.mid(index, 1) == QLatin1String("-")) {
854  // convert tmpPos to negative
855  tmpPos = 0 - tmpPos;
856  }
857  index += 2; // advance to day(s)
858  while (numFromDay(tmpStr.mid(index, 3)) >= 0) {
859  int dayNum = numFromDay(tmpStr.mid(index, 3));
860  qba.setBit(dayNum);
861  index += 3; // advance to next day, or possibly pos or "#"
862  }
863  anEvent->recurrence()->addMonthlyPos(tmpPos, qba);
864  qba.detach();
865  qba.fill(false); // clear out
866  } // while != "#"
867  }
868  break;
869  }
870 
871  case Recurrence::rMonthlyDay:
872  anEvent->recurrence()->setMonthly(rFreq);
873  if (index == last) {
874  // e.g. MD1 #0
875  short tmpDay = anEvent->dtStart().date().day();
876  anEvent->recurrence()->addMonthlyDate(tmpDay);
877  } else {
878  // e.g. MD1 3 #0
879  while (index < last) {
880  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
881  if ((tmpStr.mid((index2 - 1), 1) == QLatin1String("-")) ||
882  (tmpStr.mid((index2 - 1), 1) == QLatin1String("+"))) {
883  index2 = index2 - 1;
884  }
885  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
886  index = index2;
887  if (tmpStr.mid(index, 1) == QLatin1String("-")) {
888  tmpDay = 0 - tmpDay;
889  }
890  index += 2; // advance the index;
891  anEvent->recurrence()->addMonthlyDate(tmpDay);
892  } // while != #
893  }
894  break;
895 
896  case Recurrence::rYearlyMonth:
897  anEvent->recurrence()->setYearly(rFreq);
898 
899  if (index == last) {
900  // e.g. YM1 #0
901  short tmpMonth = anEvent->dtStart().date().month();
902  anEvent->recurrence()->addYearlyMonth(tmpMonth);
903  } else {
904  // e.g. YM1 3 #0
905  while (index < last) {
906  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
907  short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort();
908  index = index2 + 1;
909  anEvent->recurrence()->addYearlyMonth(tmpMonth);
910  } // while != #
911  }
912  break;
913 
914  case Recurrence::rYearlyDay:
915  anEvent->recurrence()->setYearly(rFreq);
916 
917  if (index == last) {
918  // e.g. YD1 #0
919  const int tmpDay = anEvent->dtStart().date().dayOfYear();
920  anEvent->recurrence()->addYearlyDay(tmpDay);
921  } else {
922  // e.g. YD1 123 #0
923  while (index < last) {
924  int index2 = tmpStr.indexOf(QLatin1Char(' '), index);
925  short tmpDay = tmpStr.mid(index, (index2 - index)).toShort();
926  index = index2 + 1;
927  anEvent->recurrence()->addYearlyDay(tmpDay);
928  } // while != #
929  }
930  break;
931 
932  default:
933  break;
934  }
935 
936  // find the last field, which is either the duration or the end date
937  index = last;
938  if (tmpStr.mid(index, 1) == QLatin1String("#")) {
939  // Nr of occurrences
940  index++;
941  int rDuration = tmpStr.midRef(index, tmpStr.length() - index).toInt();
942  if (rDuration > 0) {
943  anEvent->recurrence()->setDuration(rDuration);
944  }
945  } else if (tmpStr.indexOf(QLatin1Char('T'), index) != -1) {
946  QDateTime rEndDate = ISOToQDateTime(tmpStr.mid(index, tmpStr.length() - index));
947  anEvent->recurrence()->setEndDateTime(rEndDate);
948  }
949 // anEvent->recurrence()->dump();
950 
951  } else {
952  qCDebug(KCALCORE_LOG) << "we don't understand this type of recurrence!";
953  } // if known recurrence type
954  } // repeats
955 
956  // recurrence exceptions
957  if ((vo = isAPropertyOf(vevent, VCExpDateProp)) != nullptr) {
958  s = fakeCString(vObjectUStringZValue(vo));
959  QStringList exDates = QString::fromUtf8(s).split(QLatin1Char(','));
961  for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) {
962  QDateTime exDate = ISOToQDateTime(*it);
963  if (exDate.time().hour() == 0 &&
964  exDate.time().minute() == 0 &&
965  exDate.time().second() == 0) {
966  anEvent->recurrence()->addExDate(ISOToQDate(*it));
967  } else {
968  anEvent->recurrence()->addExDateTime(exDate);
969  }
970  }
971  deleteStr(s);
972  }
973 
974  // summary
975  if ((vo = isAPropertyOf(vevent, VCSummaryProp))) {
976  s = fakeCString(vObjectUStringZValue(vo));
977  anEvent->setSummary(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s)));
978  deleteStr(s);
979  }
980 
981  // description
982  if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != nullptr) {
983  s = fakeCString(vObjectUStringZValue(vo));
984  bool isRich = Qt::mightBeRichText(QString::fromUtf8(s));
985  if (!anEvent->description().isEmpty()) {
986  anEvent->setDescription(
987  anEvent->description() + QLatin1Char('\n') + QString::fromUtf8(s), isRich);
988  } else {
989  anEvent->setDescription(QString::fromUtf8(s), isRich);
990  }
991  deleteStr(s);
992  }
993 
994  // location
995  if ((vo = isAPropertyOf(vevent, VCLocationProp)) != nullptr) {
996  s = fakeCString(vObjectUStringZValue(vo));
997  anEvent->setLocation(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s)));
998  deleteStr(s);
999  }
1000 
1001  // some stupid vCal exporters ignore the standard and use Description
1002  // instead of Summary for the default field. Correct for this.
1003  if (anEvent->summary().isEmpty() && !(anEvent->description().isEmpty())) {
1004  QString tmpStr = anEvent->description().simplified();
1005  anEvent->setDescription(QString());
1006  anEvent->setSummary(tmpStr);
1007  }
1008 
1009 #if 0
1010  // status
1011  if ((vo = isAPropertyOf(vevent, VCStatusProp)) != 0) {
1012  QString tmpStr(s = fakeCString(vObjectUStringZValue(vo)));
1013  deleteStr(s);
1014 // TODO: Define Event status
1015 // anEvent->setStatus( tmpStr );
1016  } else {
1017 // anEvent->setStatus( "NEEDS ACTION" );
1018  }
1019 #endif
1020 
1021  // secrecy
1023  if ((vo = isAPropertyOf(vevent, VCClassProp)) != nullptr) {
1024  s = fakeCString(vObjectUStringZValue(vo));
1025  if (s && strcmp(s, "PRIVATE") == 0) {
1026  secrecy = Incidence::SecrecyPrivate;
1027  } else if (s && strcmp(s, "CONFIDENTIAL") == 0) {
1029  }
1030  deleteStr(s);
1031  }
1032  anEvent->setSecrecy(secrecy);
1033 
1034  // categories
1035  if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != nullptr) {
1036  s = fakeCString(vObjectUStringZValue(vo));
1037  QString categories = QString::fromUtf8(s);
1038  deleteStr(s);
1039  QStringList tmpStrList = categories.split(QLatin1Char(','));
1040  anEvent->setCategories(tmpStrList);
1041  }
1042 
1043  // attachments
1044  initPropIterator(&voi, vevent);
1045  while (moreIteration(&voi)) {
1046  vo = nextVObject(&voi);
1047  if (strcmp(vObjectName(vo), VCAttachProp) == 0) {
1048  s = fakeCString(vObjectUStringZValue(vo));
1049  anEvent->addAttachment(Attachment(QString::fromUtf8(s)));
1050  deleteStr(s);
1051  }
1052  }
1053 
1054  // resources
1055  if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != nullptr) {
1056  QString resources = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
1057  deleteStr(s);
1058  QStringList tmpStrList = resources.split(QLatin1Char(';'));
1059  anEvent->setResources(tmpStrList);
1060  }
1061 
1062  // alarm stuff
1063  if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) {
1064  Alarm::Ptr alarm;
1065  VObject *a = isAPropertyOf(vo, VCRunTimeProp);
1066  VObject *b = isAPropertyOf(vo, VCDisplayStringProp);
1067 
1068  if (a || b) {
1069  alarm = anEvent->newAlarm();
1070  if (a) {
1071  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
1072  deleteStr(s);
1073  }
1074  alarm->setEnabled(true);
1075 
1076  if (b) {
1077  s = fakeCString(vObjectUStringZValue(b));
1078  alarm->setDisplayAlarm(QString::fromUtf8(s));
1079  deleteStr(s);
1080  } else {
1081  alarm->setDisplayAlarm(QString());
1082  }
1083  }
1084  }
1085 
1086  if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) {
1087  Alarm::Ptr alarm;
1088  VObject *a;
1089  VObject *b;
1090  a = isAPropertyOf(vo, VCRunTimeProp);
1091  b = isAPropertyOf(vo, VCAudioContentProp);
1092 
1093  if (a || b) {
1094  alarm = anEvent->newAlarm();
1095  if (a) {
1096  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
1097  deleteStr(s);
1098  }
1099  alarm->setEnabled(true);
1100 
1101  if (b) {
1102  s = fakeCString(vObjectUStringZValue(b));
1103  alarm->setAudioAlarm(QFile::decodeName(s));
1104  deleteStr(s);
1105  } else {
1106  alarm->setAudioAlarm(QString());
1107  }
1108  }
1109  }
1110 
1111  if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) {
1112  Alarm::Ptr alarm;
1113  VObject *a;
1114  VObject *b;
1115  a = isAPropertyOf(vo, VCRunTimeProp);
1116  b = isAPropertyOf(vo, VCProcedureNameProp);
1117 
1118  if (a || b) {
1119  alarm = anEvent->newAlarm();
1120  if (a) {
1121  alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a)))));
1122  deleteStr(s);
1123  }
1124  alarm->setEnabled(true);
1125 
1126  if (b) {
1127  s = fakeCString(vObjectUStringZValue(b));
1128  alarm->setProcedureAlarm(QFile::decodeName(s));
1129  deleteStr(s);
1130  } else {
1131  alarm->setProcedureAlarm(QString());
1132  }
1133  }
1134  }
1135 
1136  // priority
1137  if ((vo = isAPropertyOf(vevent, VCPriorityProp))) {
1138  s = fakeCString(vObjectUStringZValue(vo));
1139  if (s) {
1140  anEvent->setPriority(atoi(s));
1141  deleteStr(s);
1142  }
1143  }
1144 
1145  // transparency
1146  if ((vo = isAPropertyOf(vevent, VCTranspProp)) != nullptr) {
1147  s = fakeCString(vObjectUStringZValue(vo));
1148  if (s) {
1149  int i = atoi(s);
1150  anEvent->setTransparency(i == 1 ? Event::Transparent : Event::Opaque);
1151  deleteStr(s);
1152  }
1153  }
1154 
1155  // related event
1156  if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != nullptr) {
1157  anEvent->setRelatedTo(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))));
1158  deleteStr(s);
1159  d->mEventsRelate.append(anEvent);
1160  }
1161 
1162  /* Rest of the custom properties */
1163  readCustomProperties(vevent, anEvent);
1164 
1165  return anEvent;
1166 }
1167 
1169 {
1170  // qCDebug(KCALCORE_LOG) << timezone;
1171  QString pZone = QString::fromUtf8(timezone.mid(timezone.indexOf("TZID:VCAL") + 9));
1172  return pZone.mid(0, pZone.indexOf(QLatin1Char('\n')));
1173 }
1174 
1176 {
1177  if (!timezone.contains("BEGIN:DAYLIGHT")) {
1178  return QString();
1179  }
1180 
1181  timezone = timezone.mid(timezone.indexOf("BEGIN:DAYLIGHT"));
1182  timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7);
1183  QString sStart = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("COMMENT:"))));
1184  sStart.chop(2);
1185  timezone = timezone.mid(timezone.indexOf("TZOFFSETTO:") + 11);
1186  QString sOffset = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("DTSTART:"))));
1187  sOffset.chop(2);
1188  sOffset.insert(3, QLatin1Char(':'));
1189  timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7);
1190  QString sEnd = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("COMMENT:"))));
1191  sEnd.chop(2);
1192 
1193  return
1194  QStringLiteral("TRUE;") +
1195  sOffset + QLatin1Char(';') +
1196  sStart + QLatin1Char(';') +
1197  sEnd + QLatin1String(";;");
1198 }
1199 
1201 {
1202  if (!qd.isValid()) {
1203  return QString();
1204  }
1205 
1206  return QString::asprintf("%.2d%.2d%.2d", qd.year(), qd.month(), qd.day());
1207 }
1208 
1210 {
1211  if (!dt.isValid()) {
1212  return QString();
1213  }
1214 
1215  QDateTime tmpDT;
1216  if (zulu) {
1217  tmpDT = dt.toUTC();
1218  } else {
1219  tmpDT = dt.toTimeZone(d->mCalendar->timeZone());
1220  }
1221  QString tmpStr = QString::asprintf("%.2d%.2d%.2dT%.2d%.2d%.2d",
1222  tmpDT.date().year(), tmpDT.date().month(),
1223  tmpDT.date().day(), tmpDT.time().hour(),
1224  tmpDT.time().minute(), tmpDT.time().second());
1225  if (zulu || dt.timeZone() == QTimeZone::utc()) {
1226  tmpStr += QLatin1Char('Z');
1227  }
1228  return tmpStr;
1229 }
1230 
1232 {
1233  QDate tmpDate;
1234  QTime tmpTime;
1235  QString tmpStr;
1236  int year, month, day, hour, minute, second;
1237 
1238  tmpStr = dtStr;
1239  year = tmpStr.leftRef(4).toInt();
1240  month = tmpStr.midRef(4, 2).toInt();
1241  day = tmpStr.midRef(6, 2).toInt();
1242  hour = tmpStr.midRef(9, 2).toInt();
1243  minute = tmpStr.midRef(11, 2).toInt();
1244  second = tmpStr.midRef(13, 2).toInt();
1245  tmpDate.setDate(year, month, day);
1246  tmpTime.setHMS(hour, minute, second);
1247 
1248  if (tmpDate.isValid() && tmpTime.isValid()) {
1249  // correct for GMT if string is in Zulu format
1250  if (dtStr.at(dtStr.length() - 1) == QLatin1Char('Z')) {
1251  return QDateTime(tmpDate, tmpTime, Qt::UTC);
1252  } else {
1253  return QDateTime(tmpDate, tmpTime, d->mCalendar->timeZone());
1254  }
1255  } else {
1256  return QDateTime();
1257  }
1258 }
1259 
1261 {
1262  int year, month, day;
1263 
1264  year = dateStr.leftRef(4).toInt();
1265  month = dateStr.midRef(4, 2).toInt();
1266  day = dateStr.midRef(6, 2).toInt();
1267 
1268  return QDate(year, month, day);
1269 }
1270 
1271 bool VCalFormat::parseTZOffsetISO8601(const QString &s, int &result)
1272 {
1273  // ISO8601 format(s):
1274  // +- hh : mm
1275  // +- hh mm
1276  // +- hh
1277 
1278  // We also accept broken one without +
1279  int mod = 1;
1280  int v = 0;
1281  QString str = s.trimmed();
1282  int ofs = 0;
1283  result = 0;
1284 
1285  // Check for end
1286  if (str.size() <= ofs) {
1287  return false;
1288  }
1289  if (str[ofs] == QLatin1Char('-')) {
1290  mod = -1;
1291  ofs++;
1292  } else if (str[ofs] == QLatin1Char('+')) {
1293  ofs++;
1294  }
1295  if (str.size() <= ofs) {
1296  return false;
1297  }
1298 
1299  // Make sure next two values are numbers
1300  bool ok;
1301 
1302  if (str.size() < (ofs + 2)) {
1303  return false;
1304  }
1305 
1306  v = str.midRef(ofs, 2).toInt(&ok) * 60;
1307  if (!ok) {
1308  return false;
1309  }
1310  ofs += 2;
1311 
1312  if (str.size() > ofs) {
1313  if (str[ofs] == QLatin1Char(':')) {
1314  ofs++;
1315  }
1316  if (str.size() > ofs) {
1317  if (str.size() < (ofs + 2)) {
1318  return false;
1319  }
1320  v += str.midRef(ofs, 2).toInt(&ok);
1321  if (!ok) {
1322  return false;
1323  }
1324  }
1325  }
1326  result = v * mod * 60;
1327  return true;
1328 }
1329 
1330 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc.
1331 // and break it down from it's tree-like format into the dictionary format
1332 // that is used internally in the VCalFormat.
1333 void VCalFormat::populate(VObject *vcal, bool deleted, const QString &notebook)
1334 {
1335  Q_UNUSED(notebook);
1336  // this function will populate the caldict dictionary and other event
1337  // lists. It turns vevents into Events and then inserts them.
1338 
1339  VObjectIterator i;
1340  VObject *curVO;
1341  Event::Ptr anEvent;
1342  bool hasTimeZone = false; //The calendar came with a TZ and not UTC
1343  QTimeZone previousZone; //If we add a new TZ we should leave the spec as it was before
1344 
1345  if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != nullptr) {
1346  char *methodType = fakeCString(vObjectUStringZValue(curVO));
1347  // qCDebug(KCALCORE_LOG) << "This calendar is an iTIP transaction of type '" << methodType << "'";
1348  deleteStr(methodType);
1349  }
1350 
1351  // warn the user that we might have trouble reading non-known calendar.
1352  if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != nullptr) {
1353  char *s = fakeCString(vObjectUStringZValue(curVO));
1354  if (!s || strcmp(productId().toUtf8().constData(), s) != 0) {
1355  qCDebug(KCALCORE_LOG) << "This vCalendar file was not created by KOrganizer or"
1356  << "any other product we support. Loading anyway...";
1357  }
1359  deleteStr(s);
1360  }
1361 
1362  // warn the user we might have trouble reading this unknown version.
1363  if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != nullptr) {
1364  char *s = fakeCString(vObjectUStringZValue(curVO));
1365  if (!s || strcmp(_VCAL_VERSION, s) != 0) {
1366  qCDebug(KCALCORE_LOG) << "This vCalendar file has version" << s
1367  << "We only support" << _VCAL_VERSION;
1368  }
1369  deleteStr(s);
1370  }
1371 
1372  // set the time zone (this is a property of the view, so just discard!)
1373  if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != nullptr) {
1374  char *s = fakeCString(vObjectUStringZValue(curVO));
1375  QString ts = QString::fromUtf8(s);
1376  QString name = QLatin1String("VCAL") + ts;
1377  deleteStr(s);
1378 
1379  // TODO: While using the timezone-offset + vcal as timezone is is
1380  // most likely unique, we should REALLY actually create something
1381  // like vcal-tzoffset-daylightoffsets, or better yet,
1382  // vcal-hash<the former>
1383 
1384  QStringList tzList;
1385  QString tz;
1386  int utcOffset;
1387  int utcOffsetDst;
1388  if (parseTZOffsetISO8601(ts, utcOffset)) {
1389  // qCDebug(KCALCORE_LOG) << "got standard offset" << ts << utcOffset;
1390  // standard from tz
1391  // starting date for now 01011900
1392  QDateTime dt = QDateTime(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)));
1393  tz = QStringLiteral("STD;%1;false;%2").arg(QString::number(utcOffset), dt.toString());
1394  tzList.append(tz);
1395 
1396  // go through all the daylight tags
1397  initPropIterator(&i, vcal);
1398  while (moreIteration(&i)) {
1399  curVO = nextVObject(&i);
1400  if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) {
1401  char *s = fakeCString(vObjectUStringZValue(curVO));
1402  QString dst = QLatin1String(s);
1403  QStringList argl = dst.split(QLatin1Char(','));
1404  deleteStr(s);
1405 
1406  // Too short -> not interesting
1407  if (argl.size() < 4) {
1408  continue;
1409  }
1410 
1411  // We don't care about the non-DST periods
1412  if (argl[0] != QLatin1String("TRUE")) {
1413  continue;
1414  }
1415 
1416  if (parseTZOffsetISO8601(argl[1], utcOffsetDst)) {
1417 
1418  // qCDebug(KCALCORE_LOG) << "got DST offset" << argl[1] << utcOffsetDst;
1419  // standard
1420  QString strEndDate = argl[3];
1421  QDateTime endDate = ISOToQDateTime(strEndDate);
1422  // daylight
1423  QString strStartDate = argl[2];
1424  QDateTime startDate = ISOToQDateTime(strStartDate);
1425 
1426  QString strRealEndDate = strEndDate;
1427  QString strRealStartDate = strStartDate;
1428  QDateTime realEndDate = endDate;
1429  QDateTime realStartDate = startDate;
1430  // if we get dates for some reason in wrong order, earlier is used for dst
1431  if (endDate < startDate) {
1432  strRealEndDate = strStartDate;
1433  strRealStartDate = strEndDate;
1434  realEndDate = startDate;
1435  realStartDate = endDate;
1436  }
1437  tz = QStringLiteral("%1;%2;false;%3").
1438  arg(strRealEndDate, QString::number(utcOffset), realEndDate.toString());
1439  tzList.append(tz);
1440 
1441  tz = QStringLiteral("%1;%2;true;%3").
1442  arg(strRealStartDate, QString::number(utcOffsetDst), realStartDate.toString());
1443  tzList.append(tz);
1444  } else {
1445  qCDebug(KCALCORE_LOG) << "unable to parse dst" << argl[1];
1446  }
1447  }
1448  }
1450  qCDebug(KCALCORE_LOG) << "zone is not valid, parsing error" << tzList;
1451  } else {
1452  previousZone = d->mCalendar->timeZone();
1453  d->mCalendar->setTimeZoneId(name.toUtf8());
1454  hasTimeZone = true;
1455  }
1456  } else {
1457  qCDebug(KCALCORE_LOG) << "unable to parse tzoffset" << ts;
1458  }
1459  }
1460 
1461  // Store all events with a relatedTo property in a list for post-processing
1462  d->mEventsRelate.clear();
1463  d->mTodosRelate.clear();
1464 
1465  initPropIterator(&i, vcal);
1466 
1467  // go through all the vobjects in the vcal
1468  while (moreIteration(&i)) {
1469  curVO = nextVObject(&i);
1470 
1471  /************************************************************************/
1472 
1473  // now, check to see that the object is an event or todo.
1474  if (strcmp(vObjectName(curVO), VCEventProp) == 0) {
1475 
1476  if (!isAPropertyOf(curVO, VCDTstartProp) &&
1477  !isAPropertyOf(curVO, VCDTendProp)) {
1478  qCDebug(KCALCORE_LOG) << "found a VEvent with no DTSTART and no DTEND! Skipping...";
1479  goto SKIP;
1480  }
1481 
1482  anEvent = VEventToEvent(curVO);
1483  if (anEvent) {
1484  if (hasTimeZone && !anEvent->allDay() && anEvent->dtStart().timeZone() == QTimeZone::utc()) {
1485  //This sounds stupid but is how others are doing it, so here
1486  //we go. If there is a TZ in the VCALENDAR even if the dtStart
1487  //and dtend are in UTC, clients interpret it using also the TZ defined
1488  //in the Calendar. I know it sounds braindead but oh well
1489  int utcOffSet = anEvent->dtStart().offsetFromUtc();
1490  QDateTime dtStart(anEvent->dtStart().addSecs(utcOffSet));
1491  dtStart.setTimeZone(d->mCalendar->timeZone());
1492  QDateTime dtEnd(anEvent->dtEnd().addSecs(utcOffSet));
1493  dtEnd.setTimeZone(d->mCalendar->timeZone());
1494  anEvent->setDtStart(dtStart);
1495  anEvent->setDtEnd(dtEnd);
1496  }
1497  Event::Ptr old = !anEvent->hasRecurrenceId() ?
1498  d->mCalendar->event(anEvent->uid()) :
1499  d->mCalendar->event(anEvent->uid(), anEvent->recurrenceId());
1500 
1501  if (old) {
1502  if (deleted) {
1503  d->mCalendar->deleteEvent(old); // move old to deleted
1504  removeAllVCal(d->mEventsRelate, old);
1505  } else if (anEvent->revision() > old->revision()) {
1506  d->mCalendar->deleteEvent(old); // move old to deleted
1507  removeAllVCal(d->mEventsRelate, old);
1508  d->mCalendar->addEvent(anEvent); // and replace it with this one
1509  }
1510  } else if (deleted) {
1511  old = !anEvent->hasRecurrenceId() ?
1512  d->mCalendar->deletedEvent(anEvent->uid()) :
1513  d->mCalendar->deletedEvent(anEvent->uid(), anEvent->recurrenceId());
1514  if (!old) {
1515  d->mCalendar->addEvent(anEvent); // add this one
1516  d->mCalendar->deleteEvent(anEvent); // and move it to deleted
1517  }
1518  } else {
1519  d->mCalendar->addEvent(anEvent); // just add this one
1520  }
1521  }
1522  } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) {
1523  Todo::Ptr aTodo = VTodoToEvent(curVO);
1524  if (aTodo) {
1525  if (hasTimeZone && !aTodo->allDay() && aTodo->dtStart().timeZone() == QTimeZone::utc()) {
1526  //This sounds stupid but is how others are doing it, so here
1527  //we go. If there is a TZ in the VCALENDAR even if the dtStart
1528  //and dtend are in UTC, clients interpret it usint alse the TZ defined
1529  //in the Calendar. I know it sounds braindead but oh well
1530  int utcOffSet = aTodo->dtStart().offsetFromUtc();
1531  QDateTime dtStart(aTodo->dtStart().addSecs(utcOffSet));
1532  dtStart.setTimeZone(d->mCalendar->timeZone());
1533  aTodo->setDtStart(dtStart);
1534  if (aTodo->hasDueDate()) {
1535  QDateTime dtDue(aTodo->dtDue().addSecs(utcOffSet));
1536  dtDue.setTimeZone(d->mCalendar->timeZone());
1537  aTodo->setDtDue(dtDue);
1538  }
1539  }
1540  Todo::Ptr old = !aTodo->hasRecurrenceId() ?
1541  d->mCalendar->todo(aTodo->uid()) :
1542  d->mCalendar->todo(aTodo->uid(), aTodo->recurrenceId());
1543  if (old) {
1544  if (deleted) {
1545  d->mCalendar->deleteTodo(old); // move old to deleted
1546  removeAllVCal(d->mTodosRelate, old);
1547  } else if (aTodo->revision() > old->revision()) {
1548  d->mCalendar->deleteTodo(old); // move old to deleted
1549  removeAllVCal(d->mTodosRelate, old);
1550  d->mCalendar->addTodo(aTodo); // and replace it with this one
1551  }
1552  } else if (deleted) {
1553  old = d->mCalendar->deletedTodo(aTodo->uid(), aTodo->recurrenceId());
1554  if (!old) {
1555  d->mCalendar->addTodo(aTodo); // add this one
1556  d->mCalendar->deleteTodo(aTodo); // and move it to deleted
1557  }
1558  } else {
1559  d->mCalendar->addTodo(aTodo); // just add this one
1560  }
1561  }
1562  } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) ||
1563  (strcmp(vObjectName(curVO), VCProdIdProp) == 0) ||
1564  (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) {
1565  // do nothing, we know these properties and we want to skip them.
1566  // we have either already processed them or are ignoring them.
1567  ;
1568  } else if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) {
1569  // do nothing daylights are already processed
1570  ;
1571  } else {
1572  qCDebug(KCALCORE_LOG) << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"";
1573  }
1574  SKIP:
1575  ;
1576  } // while
1577 
1578  // Post-Process list of events with relations, put Event objects in relation
1580  for (eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt) {
1581  (*eIt)->setRelatedTo((*eIt)->relatedTo());
1582  }
1584  for (tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt) {
1585  (*tIt)->setRelatedTo((*tIt)->relatedTo());
1586  }
1587 
1588  //Now lets put the TZ back as it was if we have changed it.
1589  if (hasTimeZone) {
1590  d->mCalendar->setTimeZone(previousZone);
1591  }
1592 }
1593 
1595 {
1596  if (day == QLatin1String("MO ")) {
1597  return 0;
1598  }
1599  if (day == QLatin1String("TU ")) {
1600  return 1;
1601  }
1602  if (day == QLatin1String("WE ")) {
1603  return 2;
1604  }
1605  if (day == QLatin1String("TH ")) {
1606  return 3;
1607  }
1608  if (day == QLatin1String("FR ")) {
1609  return 4;
1610  }
1611  if (day == QLatin1String("SA ")) {
1612  return 5;
1613  }
1614  if (day == QLatin1String("SU ")) {
1615  return 6;
1616  }
1617 
1618  return -1; // something bad happened. :)
1619 }
1620 
1622 {
1623  QString statStr = QString::fromUtf8(s);
1624  statStr = statStr.toUpper();
1625  Attendee::PartStat status;
1626 
1627  if (statStr == QLatin1String("X-ACTION")) {
1628  status = Attendee::NeedsAction;
1629  } else if (statStr == QLatin1String("NEEDS ACTION")) {
1630  status = Attendee::NeedsAction;
1631  } else if (statStr == QLatin1String("ACCEPTED")) {
1632  status = Attendee::Accepted;
1633  } else if (statStr == QLatin1String("SENT")) {
1634  status = Attendee::NeedsAction;
1635  } else if (statStr == QLatin1String("TENTATIVE")) {
1636  status = Attendee::Tentative;
1637  } else if (statStr == QLatin1String("CONFIRMED")) {
1638  status = Attendee::Accepted;
1639  } else if (statStr == QLatin1String("DECLINED")) {
1640  status = Attendee::Declined;
1641  } else if (statStr == QLatin1String("COMPLETED")) {
1642  status = Attendee::Completed;
1643  } else if (statStr == QLatin1String("DELEGATED")) {
1644  status = Attendee::Delegated;
1645  } else {
1646  qCDebug(KCALCORE_LOG) << "error setting attendee mStatus, unknown mStatus!";
1647  status = Attendee::NeedsAction;
1648  }
1649 
1650  return status;
1651 }
1652 
1654 {
1655  switch (status) {
1656  default:
1657  case Attendee::NeedsAction:
1658  return "NEEDS ACTION";
1659  case Attendee::Accepted:
1660  return "ACCEPTED";
1661  case Attendee::Declined:
1662  return "DECLINED";
1663  case Attendee::Tentative:
1664  return "TENTATIVE";
1665  case Attendee::Delegated:
1666  return "DELEGATED";
1667  case Attendee::Completed:
1668  return "COMPLETED";
1669  case Attendee::InProcess:
1670  return "NEEDS ACTION";
1671  }
1672 }
1673 
1674 void VCalFormat::readCustomProperties(VObject *o, const Incidence::Ptr &i)
1675 {
1676  VObjectIterator iter;
1677  char *s;
1678 
1679  initPropIterator(&iter, o);
1680  while (moreIteration(&iter)) {
1681  VObject *cur = nextVObject(&iter);
1682  const char *curname = vObjectName(cur);
1683  Q_ASSERT(curname);
1684  if ((curname[0] == 'X' && curname[1] == '-') &&
1685  strcmp(curname, ICOrganizerProp) != 0) {
1686  // TODO - for the time being, we ignore the parameters part
1687  // and just do the value handling here
1688  i->setNonKDECustomProperty(
1689  curname, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(cur))));
1690  deleteStr(s);
1691  }
1692  }
1693 }
1694 
1695 void VCalFormat::writeCustomProperties(VObject *o, const Incidence::Ptr &i)
1696 {
1697  const QMap<QByteArray, QString> custom = i->customProperties();
1699  c != custom.end(); ++c) {
1700  if (d->mManuallyWrittenExtensionFields.contains(c.key()) ||
1701  c.key().startsWith("X-KDE-VOLATILE")) { //krazy:exclude=strings
1702  continue;
1703  }
1704 
1705  addPropValue(o, c.key().constData(), c.value().toUtf8().constData());
1706  }
1707 }
1708 
1709 void VCalFormat::virtual_hook(int id, void *data)
1710 {
1711  Q_UNUSED(id);
1712  Q_UNUSED(data);
1713  Q_ASSERT(false);
1714 }
bool load(const Calendar::Ptr &calendar, const QString &fileName) override
Definition: vcalformat.cpp:86
bool parseTZOffsetISO8601(const QString &s, int &result)
Parse one of the myriad of ISO8601 timezone offset formats, e.g.
Event or to-do tentatively accepted.
Definition: attendee.h:69
Secrecy
The different types of incidence access classifications.
Definition: incidence.h:97
QString toString(Qt::DateFormat format) const const
int toInt(bool *ok, int base) const const
int minute() const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString toUpper() const const
QDateTime toUTC() const const
short toShort(bool *ok, int base) const const
QString toString(const Calendar::Ptr &calendar, const QString &notebook=QString(), bool deleted=false) override
Definition: vcalformat.cpp:158
QString asprintf(const char *cformat,...)
Event does not appear in free/busy time.
Definition: event.h:41
Event, to-do or journal accepted.
Definition: attendee.h:67
Event, to-do or journal declined.
Definition: attendee.h:68
void setStatus(PartStat status)
Sets the PartStat of the attendee to status.
Definition: attendee.cpp:212
bool setHMS(int h, int m, int s, int ms)
Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (...
Definition: attendee.h:45
Todo::Ptr VTodoToEvent(VObject *vtodo)
Translates a VObject of the TODO type into an Event.
Definition: vcalformat.cpp:169
void setBit(int i)
bool fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted=false, const QString &notebook=QString()) override
Definition: vcalformat.cpp:129
int size() const const
void setLoadedProductId(const QString &id)
Sets the PRODID string loaded from calendar file.
Definition: calformat.cpp:100
QString simplified() const const
bool isValid() const const
QTime time() const const
QString qDateTimeToISO(const QDateTime &date, bool zulu=true)
Takes a QDateTime and returns a string in format YYYYMMDDTHHMMSS.
int day() const const
typedef ConstIterator
void chop(int n)
static const QString & productId()
Returns the our library&#39;s PRODID string to write into calendar files.
Definition: calformat.cpp:90
bool mightBeRichText(const QString &text)
int second() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
int size() const const
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
void clearException()
Clears the exception status.
Definition: calformat.cpp:61
void clear()
void setTimeZone(const QTimeZone &toZone)
int indexOf(char ch, int from) const const
QString number(int n, int base)
QString qDateToISO(const QDate &date)
Takes a QDate and returns a string in the format YYYYMMDDTHHMMSS.
void append(const T &value)
QString fromUtf8(const char *str, int size)
QString & insert(int position, QChar ch)
PartStat
The different types of participant status.
Definition: attendee.h:65
VCalFormat()
Constructor a new vCalendar Format object.
Definition: vcalformat.cpp:77
QStringRef leftRef(int n) const const
This class provides an Event in the sense of RFC2445.
Definition: event.h:30
QTimeZone utc()
Event::Ptr VEventToEvent(VObject *vevent)
Translates a VObject into a Event and returns a pointer to it.
Definition: vcalformat.cpp:632
This file is part of the API for handling calendar data and defines the Todo class.
QString trimmed() const const
This file is part of the API for handling calendar data and defines the Calendar class.
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool isValid() const const
void setRSVP(bool rsvp)
Sets the RSVP flag of the attendee to rsvp.
Definition: attendee.cpp:202
Represents information related to an attachment for a Calendar Incidence.
Definition: attachment.h:47
This file is part of the API for handling calendar data and defines the Exception class...
Event appears in free/busy time.
Definition: event.h:40
QDate ISOToQDate(const QString &dtStr)
Takes a string in the YYYYMMDD format and returns a valid QDate.
Provides a To-do in the sense of RFC2445.
Definition: todo.h:31
QMap::iterator end()
QByteArray mid(int pos, int len) const const
QMap::iterator begin()
~VCalFormat() override
Destructor.
Definition: vcalformat.cpp:81
int hour() const const
QStringRef midRef(int position, int n) const const
Attendee::PartStat readStatus(const char *s) const
Converts a status string into an Attendee::PartStat.
bool isValid() const const
bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted=false, const QString &notebook=QString()) override
Definition: vcalformat.cpp:123
bool setDate(int year, int month, int day)
QString & replace(int position, int n, QChar after)
bool save(const Calendar::Ptr &calendar, const QString &fileName) override
Definition: vcalformat.cpp:115
QByteArray toLatin1() const const
QString mid(int position, int n) const const
QDate date() const const
Event or to-do delegated.
Definition: attendee.h:70
Exception base class, currently used as a fancy kind of error code and not as an C++ exception...
Definition: exceptions.h:42
This file is part of the API for handling calendar data and defines the Event class.
QString parseTZ(const QByteArray &timezone) const
Parse TZ tag from vtimezone.
QString parseDst(QByteArray &timezone) const
Parse DAYLIGHT tag from vtimezone.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
typedef ConstIterator
bool contains(char ch) const const
int length() const const
void virtual_hook(int id, void *data) override
Secret to the owner and some others.
Definition: incidence.h:100
void setException(Exception *error)
Sets an exception that is to be used by the functions of this class to report errors.
Definition: calformat.cpp:67
QString left(int n) const const
Unknown calendar format detected.
Definition: exceptions.h:57
vCalendar format implementation.
Definition: vcalformat.h:60
QDateTime ISOToQDateTime(const QString &dtStr)
Takes a string in YYYYMMDDTHHMMSS format and returns a valid QDateTime.
This file is part of the API for handling calendar data and defines the VCalFormat base class...
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...
bool fill(bool value, int size)
int numFromDay(const QString &day)
Converts a two letter representation of the day (i.e.
QList::const_iterator constEnd() const const
To-do in process of being completed.
Definition: attendee.h:72
QList::const_iterator constBegin() const const
Event, to-do or journal needs action (default)
Definition: attendee.h:66
bool isTimeZoneIdAvailable(const QByteArray &ianaId)
QByteArray writeStatus(Attendee::PartStat status) const
Converts an Attendee::PartStat into a QByteArray string.
int year() const const
int month() const const
QDateTime currentDateTimeUtc()
QByteArray encodeName(const QString &fileName)
QString decodeName(const QByteArray &localFileName)
Namespace for all KCalendarCore types.
Definition: alarm.h:36
QTimeZone timeZone() const const
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Mar 2 2021 01:22:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.