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

KDE's Doxygen guidelines are available online.