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