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 }