File indexing completed on 2024-04-28 15:19:03
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 /** 0009 @file 0010 This file is part of the API for handling calendar data and 0011 defines the ICalFormat class. 0012 0013 @brief 0014 iCalendar format implementation: a layer of abstraction for libical. 0015 0016 @author Cornelius Schumacher \<schumacher@kde.org\> 0017 */ 0018 #include "icalformat.h" 0019 #include "calendar_p.h" 0020 #include "calformat_p.h" 0021 #include "icalformat_p.h" 0022 #include "icaltimezones_p.h" 0023 #include "kcalendarcore_debug.h" 0024 #include "memorycalendar.h" 0025 0026 #include <QFile> 0027 #include <QSaveFile> 0028 #include <QTimeZone> 0029 0030 extern "C" { 0031 #include <libical/ical.h> 0032 #include <libical/icalmemory.h> 0033 #include <libical/icalparser.h> 0034 #include <libical/icalrestriction.h> 0035 #include <libical/icalss.h> 0036 } 0037 0038 using namespace KCalendarCore; 0039 0040 //@cond PRIVATE 0041 class KCalendarCore::ICalFormatPrivate : public KCalendarCore::CalFormatPrivate 0042 { 0043 public: 0044 ICalFormatPrivate(ICalFormat *parent) 0045 : mImpl(parent) 0046 , mTimeZone(QTimeZone::utc()) 0047 { 0048 } 0049 ICalFormatImpl mImpl; 0050 QTimeZone mTimeZone; 0051 }; 0052 //@endcond 0053 0054 ICalFormat::ICalFormat() 0055 : CalFormat(new ICalFormatPrivate(this)) 0056 { 0057 } 0058 0059 ICalFormat::~ICalFormat() 0060 { 0061 icalmemory_free_ring(); 0062 } 0063 0064 bool ICalFormat::load(const Calendar::Ptr &calendar, const QString &fileName) 0065 { 0066 qCDebug(KCALCORE_LOG) << fileName; 0067 0068 clearException(); 0069 0070 QFile file(fileName); 0071 if (!file.open(QIODevice::ReadOnly)) { 0072 qCritical() << "load error: unable to open " << fileName; 0073 setException(new Exception(Exception::LoadError)); 0074 return false; 0075 } 0076 const QByteArray text = file.readAll().trimmed(); 0077 file.close(); 0078 0079 if (!text.isEmpty()) { 0080 if (!calendar->hasValidNotebook(fileName) && !calendar->addNotebook(fileName, true)) { 0081 qCWarning(KCALCORE_LOG) << "Unable to add" << fileName << "as a notebook in calendar"; 0082 } 0083 if (!fromRawString(calendar, text, false, fileName)) { 0084 qCWarning(KCALCORE_LOG) << fileName << " is not a valid iCalendar file"; 0085 setException(new Exception(Exception::ParseErrorIcal)); 0086 return false; 0087 } 0088 } 0089 0090 // Note: we consider empty files to be valid 0091 0092 return true; 0093 } 0094 0095 bool ICalFormat::save(const Calendar::Ptr &calendar, const QString &fileName) 0096 { 0097 qCDebug(KCALCORE_LOG) << fileName; 0098 0099 clearException(); 0100 0101 QString text = toString(calendar); 0102 if (text.isEmpty()) { 0103 return false; 0104 } 0105 0106 // Write backup file 0107 const QString backupFile = fileName + QLatin1Char('~'); 0108 QFile::remove(backupFile); 0109 QFile::copy(fileName, backupFile); 0110 0111 QSaveFile file(fileName); 0112 if (!file.open(QIODevice::WriteOnly)) { 0113 qCritical() << "file open error: " << file.errorString() << ";filename=" << fileName; 0114 setException(new Exception(Exception::SaveErrorOpenFile, QStringList(fileName))); 0115 0116 return false; 0117 } 0118 0119 // Convert to UTF8 and save 0120 QByteArray textUtf8 = text.toUtf8(); 0121 file.write(textUtf8.data(), textUtf8.size()); 0122 // QSaveFile doesn't report a write error when the device is full (see Qt 0123 // bug 75077), so check that the data can actually be written. 0124 if (!file.flush()) { 0125 qCDebug(KCALCORE_LOG) << "file write error (flush failed)"; 0126 setException(new Exception(Exception::SaveErrorSaveFile, QStringList(fileName))); 0127 return false; 0128 } 0129 0130 if (!file.commit()) { 0131 qCDebug(KCALCORE_LOG) << "file finalize error:" << file.errorString(); 0132 setException(new Exception(Exception::SaveErrorSaveFile, QStringList(fileName))); 0133 0134 return false; 0135 } 0136 0137 return true; 0138 } 0139 0140 #if KCALENDARCORE_BUILD_DEPRECATED_SINCE(5, 97) 0141 bool ICalFormat::fromString(const Calendar::Ptr &cal, const QString &string, bool deleted, const QString ¬ebook) 0142 { 0143 return fromRawString(cal, string.toUtf8(), deleted, notebook); 0144 } 0145 #endif 0146 0147 Incidence::Ptr ICalFormat::readIncidence(const QByteArray &string) 0148 { 0149 Q_D(ICalFormat); 0150 0151 // Let's defend const correctness until the very gates of hell^Wlibical 0152 icalcomponent *calendar = icalcomponent_new_from_string(const_cast<char *>(string.constData())); 0153 if (!calendar) { 0154 qCritical() << "parse error from icalcomponent_new_from_string. string=" << QString::fromLatin1(string); 0155 setException(new Exception(Exception::ParseErrorIcal)); 0156 return Incidence::Ptr(); 0157 } 0158 0159 ICalTimeZoneCache tzCache; 0160 ICalTimeZoneParser parser(&tzCache); 0161 parser.parse(calendar); 0162 0163 Incidence::Ptr incidence; 0164 if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) { 0165 incidence = d->mImpl.readOneIncidence(calendar, &tzCache); 0166 } else if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) { 0167 icalcomponent *comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT); 0168 if (comp) { 0169 incidence = d->mImpl.readOneIncidence(comp, &tzCache); 0170 } 0171 } 0172 0173 if (!incidence) { 0174 qCDebug(KCALCORE_LOG) << "No VCALENDAR component found"; 0175 setException(new Exception(Exception::NoCalendar)); 0176 } 0177 0178 icalcomponent_free(calendar); 0179 icalmemory_free_ring(); 0180 0181 return incidence; 0182 } 0183 0184 bool ICalFormat::fromRawString(const Calendar::Ptr &cal, const QByteArray &string, bool deleted, const QString ¬ebook) 0185 { 0186 Q_D(ICalFormat); 0187 0188 // Get first VCALENDAR component. 0189 // TODO: Handle more than one VCALENDAR or non-VCALENDAR top components 0190 icalcomponent *calendar; 0191 0192 // Let's defend const correctness until the very gates of hell^Wlibical 0193 calendar = icalcomponent_new_from_string(const_cast<char *>(string.constData())); 0194 if (!calendar) { 0195 qCritical() << "parse error from icalcomponent_new_from_string. string=" << QString::fromLatin1(string); 0196 setException(new Exception(Exception::ParseErrorIcal)); 0197 return false; 0198 } 0199 0200 bool success = true; 0201 0202 if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) { 0203 icalcomponent *comp; 0204 for (comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT); comp; 0205 comp = icalcomponent_get_next_component(calendar, ICAL_VCALENDAR_COMPONENT)) { 0206 // put all objects into their proper places 0207 if (!d->mImpl.populate(cal, comp, deleted)) { 0208 qCritical() << "Could not populate calendar"; 0209 if (!exception()) { 0210 setException(new Exception(Exception::ParseErrorKcal)); 0211 } 0212 success = false; 0213 } else { 0214 setLoadedProductId(d->mImpl.loadedProductId()); 0215 } 0216 } 0217 } else if (icalcomponent_isa(calendar) != ICAL_VCALENDAR_COMPONENT) { 0218 qCDebug(KCALCORE_LOG) << "No VCALENDAR component found"; 0219 setException(new Exception(Exception::NoCalendar)); 0220 success = false; 0221 } else { 0222 // put all objects into their proper places 0223 if (!d->mImpl.populate(cal, calendar, deleted, notebook)) { 0224 qCDebug(KCALCORE_LOG) << "Could not populate calendar"; 0225 if (!exception()) { 0226 setException(new Exception(Exception::ParseErrorKcal)); 0227 } 0228 success = false; 0229 } else { 0230 setLoadedProductId(d->mImpl.loadedProductId()); 0231 } 0232 } 0233 0234 icalcomponent_free(calendar); 0235 icalmemory_free_ring(); 0236 0237 return success; 0238 } 0239 0240 Incidence::Ptr ICalFormat::fromString(const QString &string) 0241 { 0242 Q_D(ICalFormat); 0243 0244 MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeZone)); 0245 fromString(cal, string); 0246 0247 const Incidence::List list = cal->incidences(); 0248 return !list.isEmpty() ? list.first() : Incidence::Ptr(); 0249 } 0250 0251 QString ICalFormat::toString(const Calendar::Ptr &cal, const QString ¬ebook, bool deleted) 0252 { 0253 Q_D(ICalFormat); 0254 0255 icalcomponent *calendar = d->mImpl.createCalendarComponent(cal); 0256 icalcomponent *component; 0257 0258 QVector<QTimeZone> tzUsedList; 0259 TimeZoneEarliestDate earliestTz; 0260 0261 // todos 0262 Todo::List todoList = deleted ? cal->deletedTodos() : cal->rawTodos(); 0263 for (auto it = todoList.cbegin(), end = todoList.cend(); it != end; ++it) { 0264 if (!deleted || !cal->todo((*it)->uid(), (*it)->recurrenceId())) { 0265 // use existing ones, or really deleted ones 0266 if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { 0267 component = d->mImpl.writeTodo(*it, &tzUsedList); 0268 icalcomponent_add_component(calendar, component); 0269 ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); 0270 } 0271 } 0272 } 0273 // events 0274 Event::List events = deleted ? cal->deletedEvents() : cal->rawEvents(); 0275 for (auto it = events.cbegin(), end = events.cend(); it != end; ++it) { 0276 if (!deleted || !cal->event((*it)->uid(), (*it)->recurrenceId())) { 0277 // use existing ones, or really deleted ones 0278 if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { 0279 component = d->mImpl.writeEvent(*it, &tzUsedList); 0280 icalcomponent_add_component(calendar, component); 0281 ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); 0282 } 0283 } 0284 } 0285 0286 // journals 0287 Journal::List journals = deleted ? cal->deletedJournals() : cal->rawJournals(); 0288 for (auto it = journals.cbegin(), end = journals.cend(); it != end; ++it) { 0289 if (!deleted || !cal->journal((*it)->uid(), (*it)->recurrenceId())) { 0290 // use existing ones, or really deleted ones 0291 if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { 0292 component = d->mImpl.writeJournal(*it, &tzUsedList); 0293 icalcomponent_add_component(calendar, component); 0294 ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); 0295 } 0296 } 0297 } 0298 0299 // time zones 0300 if (todoList.isEmpty() && events.isEmpty() && journals.isEmpty()) { 0301 // no incidences means no used timezones, use all timezones 0302 // this will export a calendar having only timezone definitions 0303 tzUsedList = cal->d->mTimeZones; 0304 } 0305 for (const auto &qtz : std::as_const(tzUsedList)) { 0306 if (qtz != QTimeZone::utc()) { 0307 icaltimezone *tz = ICalTimeZoneParser::icaltimezoneFromQTimeZone(qtz, earliestTz[qtz]); 0308 if (!tz) { 0309 qCritical() << "bad time zone"; 0310 } else { 0311 component = icalcomponent_new_clone(icaltimezone_get_component(tz)); 0312 icalcomponent_add_component(calendar, component); 0313 icaltimezone_free(tz, 1); 0314 } 0315 } 0316 } 0317 0318 char *const componentString = icalcomponent_as_ical_string_r(calendar); 0319 const QString &text = QString::fromUtf8(componentString); 0320 free(componentString); 0321 0322 icalcomponent_free(calendar); 0323 icalmemory_free_ring(); 0324 0325 if (text.isEmpty()) { 0326 setException(new Exception(Exception::LibICalError)); 0327 } 0328 0329 return text; 0330 } 0331 0332 QString ICalFormat::toICalString(const Incidence::Ptr &incidence) 0333 { 0334 Q_D(ICalFormat); 0335 0336 MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeZone)); 0337 cal->addIncidence(Incidence::Ptr(incidence->clone())); 0338 return toString(cal.staticCast<Calendar>()); 0339 } 0340 0341 QString ICalFormat::toString(const Incidence::Ptr &incidence) 0342 { 0343 return QString::fromUtf8(toRawString(incidence)); 0344 } 0345 0346 QByteArray ICalFormat::toRawString(const Incidence::Ptr &incidence) 0347 { 0348 Q_D(ICalFormat); 0349 TimeZoneList tzUsedList; 0350 0351 icalcomponent *component = d->mImpl.writeIncidence(incidence, iTIPRequest, &tzUsedList); 0352 0353 QByteArray text = icalcomponent_as_ical_string(component); 0354 0355 TimeZoneEarliestDate earliestTzDt; 0356 ICalTimeZoneParser::updateTzEarliestDate(incidence, &earliestTzDt); 0357 0358 // time zones 0359 for (const auto &qtz : std::as_const(tzUsedList)) { 0360 if (qtz != QTimeZone::utc()) { 0361 icaltimezone *tz = ICalTimeZoneParser::icaltimezoneFromQTimeZone(qtz, earliestTzDt[qtz]); 0362 if (!tz) { 0363 qCritical() << "bad time zone"; 0364 } else { 0365 icalcomponent *tzcomponent = icaltimezone_get_component(tz); 0366 icalcomponent_add_component(component, component); 0367 text.append(icalcomponent_as_ical_string(tzcomponent)); 0368 icaltimezone_free(tz, 1); 0369 } 0370 } 0371 } 0372 0373 icalcomponent_free(component); 0374 0375 return text; 0376 } 0377 0378 QString ICalFormat::toString(RecurrenceRule *recurrence) 0379 { 0380 Q_D(ICalFormat); 0381 icalproperty *property = icalproperty_new_rrule(d->mImpl.writeRecurrenceRule(recurrence)); 0382 QString text = QString::fromUtf8(icalproperty_as_ical_string(property)); 0383 icalproperty_free(property); 0384 return text; 0385 } 0386 0387 QString KCalendarCore::ICalFormat::toString(const KCalendarCore::Duration &duration) const 0388 { 0389 Q_D(const ICalFormat); 0390 const auto icalDuration = d->mImpl.writeICalDuration(duration); 0391 // contrary to the libical API docs, the returned string is actually freed by icalmemory_free_ring, 0392 // freeing it here explicitly causes a double deletion failure 0393 return QString::fromUtf8(icaldurationtype_as_ical_string(icalDuration)); 0394 } 0395 0396 bool ICalFormat::fromString(RecurrenceRule *recurrence, const QString &rrule) 0397 { 0398 Q_D(ICalFormat); 0399 if (!recurrence) { 0400 return false; 0401 } 0402 bool success = true; 0403 icalerror_clear_errno(); 0404 struct icalrecurrencetype recur = icalrecurrencetype_from_string(rrule.toLatin1().constData()); 0405 if (icalerrno != ICAL_NO_ERROR) { 0406 qCDebug(KCALCORE_LOG) << "Recurrence parsing error:" << icalerror_strerror(icalerrno); 0407 success = false; 0408 } 0409 0410 if (success) { 0411 d->mImpl.readRecurrence(recur, recurrence); 0412 } 0413 0414 return success; 0415 } 0416 0417 Duration ICalFormat::durationFromString(const QString &duration) const 0418 { 0419 Q_D(const ICalFormat); 0420 icalerror_clear_errno(); 0421 const auto icalDuration = icaldurationtype_from_string(duration.toUtf8().constData()); 0422 if (icalerrno != ICAL_NO_ERROR) { 0423 qCDebug(KCALCORE_LOG) << "Duration parsing error:" << icalerror_strerror(icalerrno); 0424 return {}; 0425 } 0426 return d->mImpl.readICalDuration(icalDuration); 0427 } 0428 0429 QString ICalFormat::createScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method) 0430 { 0431 Q_D(ICalFormat); 0432 icalcomponent *message = nullptr; 0433 0434 if (incidence->type() == Incidence::TypeEvent || incidence->type() == Incidence::TypeTodo) { 0435 Incidence::Ptr i = incidence.staticCast<Incidence>(); 0436 0437 // Recurring events need timezone information to allow proper calculations 0438 // across timezones with different DST. 0439 const bool useUtcTimes = !i->recurs() && !i->allDay(); 0440 0441 const bool hasSchedulingId = (i->schedulingID() != i->uid()); 0442 0443 const bool incidenceNeedChanges = (useUtcTimes || hasSchedulingId); 0444 0445 if (incidenceNeedChanges) { 0446 // The incidence need changes, so clone it before we continue 0447 i = Incidence::Ptr(i->clone()); 0448 0449 // Handle conversion to UTC times 0450 if (useUtcTimes) { 0451 i->shiftTimes(QTimeZone::utc(), QTimeZone::utc()); 0452 } 0453 0454 // Handle scheduling ID being present 0455 if (hasSchedulingId) { 0456 // We have a separation of scheduling ID and UID 0457 i->setSchedulingID(QString(), i->schedulingID()); 0458 } 0459 0460 // Build the message with the cloned incidence 0461 message = d->mImpl.createScheduleComponent(i, method); 0462 } 0463 } 0464 0465 if (message == nullptr) { 0466 message = d->mImpl.createScheduleComponent(incidence, method); 0467 } 0468 0469 QString messageText = QString::fromUtf8(icalcomponent_as_ical_string(message)); 0470 0471 icalcomponent_free(message); 0472 return messageText; 0473 } 0474 0475 FreeBusy::Ptr ICalFormat::parseFreeBusy(const QString &str) 0476 { 0477 Q_D(ICalFormat); 0478 clearException(); 0479 0480 icalcomponent *message = icalparser_parse_string(str.toUtf8().constData()); 0481 0482 if (!message) { 0483 return FreeBusy::Ptr(); 0484 } 0485 0486 FreeBusy::Ptr freeBusy; 0487 0488 icalcomponent *c = nullptr; 0489 for (c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT); c != nullptr; 0490 c = icalcomponent_get_next_component(message, ICAL_VFREEBUSY_COMPONENT)) { 0491 FreeBusy::Ptr fb = d->mImpl.readFreeBusy(c); 0492 0493 if (freeBusy) { 0494 freeBusy->merge(fb); 0495 } else { 0496 freeBusy = fb; 0497 } 0498 } 0499 0500 if (!freeBusy) { 0501 qCDebug(KCALCORE_LOG) << "object is not a freebusy."; 0502 } 0503 0504 icalcomponent_free(message); 0505 0506 return freeBusy; 0507 } 0508 0509 ScheduleMessage::Ptr ICalFormat::parseScheduleMessage(const Calendar::Ptr &cal, const QString &messageText) 0510 { 0511 Q_D(ICalFormat); 0512 setTimeZone(cal->timeZone()); 0513 clearException(); 0514 0515 if (messageText.isEmpty()) { 0516 setException(new Exception(Exception::ParseErrorEmptyMessage)); 0517 return ScheduleMessage::Ptr(); 0518 } 0519 0520 icalcomponent *message = icalparser_parse_string(messageText.toUtf8().constData()); 0521 0522 if (!message) { 0523 setException(new Exception(Exception::ParseErrorUnableToParse)); 0524 0525 return ScheduleMessage::Ptr(); 0526 } 0527 0528 icalproperty *m = icalcomponent_get_first_property(message, ICAL_METHOD_PROPERTY); 0529 if (!m) { 0530 setException(new Exception(Exception::ParseErrorMethodProperty)); 0531 0532 return ScheduleMessage::Ptr(); 0533 } 0534 0535 // Populate the message's time zone collection with all VTIMEZONE components 0536 ICalTimeZoneCache tzlist; 0537 ICalTimeZoneParser parser(&tzlist); 0538 parser.parse(message); 0539 0540 IncidenceBase::Ptr incidence; 0541 icalcomponent *c = icalcomponent_get_first_component(message, ICAL_VEVENT_COMPONENT); 0542 if (c) { 0543 incidence = d->mImpl.readEvent(c, &tzlist).staticCast<IncidenceBase>(); 0544 } 0545 0546 if (!incidence) { 0547 c = icalcomponent_get_first_component(message, ICAL_VTODO_COMPONENT); 0548 if (c) { 0549 incidence = d->mImpl.readTodo(c, &tzlist).staticCast<IncidenceBase>(); 0550 } 0551 } 0552 0553 if (!incidence) { 0554 c = icalcomponent_get_first_component(message, ICAL_VJOURNAL_COMPONENT); 0555 if (c) { 0556 incidence = d->mImpl.readJournal(c, &tzlist).staticCast<IncidenceBase>(); 0557 } 0558 } 0559 0560 if (!incidence) { 0561 c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT); 0562 if (c) { 0563 incidence = d->mImpl.readFreeBusy(c).staticCast<IncidenceBase>(); 0564 } 0565 } 0566 0567 if (!incidence) { 0568 qCDebug(KCALCORE_LOG) << "object is not a freebusy, event, todo or journal"; 0569 setException(new Exception(Exception::ParseErrorNotIncidence)); 0570 0571 return ScheduleMessage::Ptr(); 0572 } 0573 0574 icalproperty_method icalmethod = icalproperty_get_method(m); 0575 iTIPMethod method; 0576 0577 switch (icalmethod) { 0578 case ICAL_METHOD_PUBLISH: 0579 method = iTIPPublish; 0580 break; 0581 case ICAL_METHOD_REQUEST: 0582 method = iTIPRequest; 0583 break; 0584 case ICAL_METHOD_REFRESH: 0585 method = iTIPRefresh; 0586 break; 0587 case ICAL_METHOD_CANCEL: 0588 method = iTIPCancel; 0589 break; 0590 case ICAL_METHOD_ADD: 0591 method = iTIPAdd; 0592 break; 0593 case ICAL_METHOD_REPLY: 0594 method = iTIPReply; 0595 break; 0596 case ICAL_METHOD_COUNTER: 0597 method = iTIPCounter; 0598 break; 0599 case ICAL_METHOD_DECLINECOUNTER: 0600 method = iTIPDeclineCounter; 0601 break; 0602 default: 0603 method = iTIPNoMethod; 0604 qCDebug(KCALCORE_LOG) << "Unknown method"; 0605 break; 0606 } 0607 0608 if (!icalrestriction_check(message)) { 0609 qCWarning(KCALCORE_LOG) << "\nkcalcore library reported a problem while parsing:"; 0610 qCWarning(KCALCORE_LOG) << ScheduleMessage::methodName(method) << ":" << d->mImpl.extractErrorProperty(c); 0611 } 0612 0613 Incidence::Ptr existingIncidence = cal->incidence(incidence->uid()); 0614 0615 icalcomponent *calendarComponent = nullptr; 0616 if (existingIncidence) { 0617 calendarComponent = d->mImpl.createCalendarComponent(cal); 0618 0619 // TODO: check, if cast is required, or if it can be done by virtual funcs. 0620 // TODO: Use a visitor for this! 0621 if (existingIncidence->type() == Incidence::TypeTodo) { 0622 Todo::Ptr todo = existingIncidence.staticCast<Todo>(); 0623 icalcomponent_add_component(calendarComponent, d->mImpl.writeTodo(todo)); 0624 } 0625 if (existingIncidence->type() == Incidence::TypeEvent) { 0626 Event::Ptr event = existingIncidence.staticCast<Event>(); 0627 icalcomponent_add_component(calendarComponent, d->mImpl.writeEvent(event)); 0628 } 0629 } else { 0630 icalcomponent_free(message); 0631 return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method, ScheduleMessage::Unknown)); 0632 } 0633 0634 icalproperty_xlicclass result = icalclassify(message, calendarComponent, static_cast<const char *>("")); 0635 0636 ScheduleMessage::Status status; 0637 0638 switch (result) { 0639 case ICAL_XLICCLASS_PUBLISHNEW: 0640 status = ScheduleMessage::PublishNew; 0641 break; 0642 case ICAL_XLICCLASS_PUBLISHUPDATE: 0643 status = ScheduleMessage::PublishUpdate; 0644 break; 0645 case ICAL_XLICCLASS_OBSOLETE: 0646 status = ScheduleMessage::Obsolete; 0647 break; 0648 case ICAL_XLICCLASS_REQUESTNEW: 0649 status = ScheduleMessage::RequestNew; 0650 break; 0651 case ICAL_XLICCLASS_REQUESTUPDATE: 0652 status = ScheduleMessage::RequestUpdate; 0653 break; 0654 case ICAL_XLICCLASS_UNKNOWN: 0655 default: 0656 status = ScheduleMessage::Unknown; 0657 break; 0658 } 0659 0660 icalcomponent_free(message); 0661 icalcomponent_free(calendarComponent); 0662 0663 return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method, status)); 0664 } 0665 0666 void ICalFormat::setTimeZone(const QTimeZone &timeZone) 0667 { 0668 Q_D(ICalFormat); 0669 d->mTimeZone = timeZone; 0670 } 0671 0672 QTimeZone ICalFormat::timeZone() const 0673 { 0674 Q_D(const ICalFormat); 0675 return d->mTimeZone; 0676 } 0677 0678 QByteArray ICalFormat::timeZoneId() const 0679 { 0680 Q_D(const ICalFormat); 0681 return d->mTimeZone.id(); 0682 } 0683 0684 #if KCALENDARCORE_BUILD_DEPRECATED_SINCE(5, 96) 0685 void ICalFormat::virtual_hook(int id, void *data) 0686 { 0687 Q_UNUSED(id); 0688 Q_UNUSED(data); 0689 Q_ASSERT(false); 0690 } 0691 #endif