data/ICalEventCodec.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "ICalEventCodec.h" | ||
| 2 | |||
| 3 | #include <QLoggingCategory> | ||
| 4 | #include <QSet> | ||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 |
3/7✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 8 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
2 | Q_LOGGING_CATEGORY(lcEventCodec, "mailjd.calendar.codec") |
| 8 | |||
| 9 | namespace ICalEventCodec { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | 694 | bool sameInstant(const QDateTime &a, const QDateTime &b, bool dateOnly) { | |
| 14 |
2/2✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 692 times.
|
694 | if (a.isValid() != b.isValid()) |
| 15 | 2 | return false; | |
| 16 |
2/2✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 686 times.
|
692 | if (!a.isValid()) |
| 17 | 6 | return true; | |
| 18 |
11/15✓ Branch 9 → 10 taken 49 times.
✓ Branch 9 → 14 taken 637 times.
✓ Branch 10 → 11 taken 49 times.
✓ Branch 11 → 12 taken 49 times.
✓ Branch 14 → 15 taken 637 times.
✓ Branch 15 → 16 taken 637 times.
✓ Branch 16 → 17 taken 637 times.
✓ Branch 19 → 20 taken 637 times.
✓ Branch 19 → 21 taken 49 times.
✓ Branch 21 → 22 taken 637 times.
✓ Branch 21 → 23 taken 49 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
|
686 | return dateOnly ? a.date() == b.date() : a.toUTC() == b.toUTC(); |
| 19 | } | ||
| 20 | |||
| 21 | 676 | bool sameStamp(const QDateTime &a, bool aDate, const QDateTime &b, bool bDate) { | |
| 22 |
5/6✓ Branch 2 → 3 taken 676 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 123 times.
✓ Branch 4 → 6 taken 553 times.
✓ Branch 7 → 8 taken 123 times.
✓ Branch 7 → 9 taken 553 times.
|
676 | if (aDate != bDate || !sameInstant(a, b, aDate)) |
| 23 | 123 | return false; | |
| 24 |
9/14✓ Branch 9 → 10 taken 501 times.
✓ Branch 9 → 14 taken 52 times.
✓ Branch 10 → 11 taken 501 times.
✓ Branch 11 → 12 taken 501 times.
✓ Branch 13 → 14 taken 501 times.
✗ Branch 13 → 15 not taken.
✓ Branch 17 → 18 taken 501 times.
✓ Branch 17 → 19 taken 52 times.
✓ Branch 19 → 20 taken 501 times.
✓ Branch 19 → 21 taken 52 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
|
553 | return aDate || ICalTime::zoneId(a, false) == ICalTime::zoneId(b, false); |
| 25 | } | ||
| 26 | |||
| 27 | 1352 | QSet<qint64> instantSet(const QList<QDateTime> &values, bool dateOnly) { | |
| 28 | 1352 | QSet<qint64> set; | |
| 29 |
2/2✓ Branch 17 → 5 taken 38 times.
✓ Branch 17 → 18 taken 1352 times.
|
1390 | for (const QDateTime &value : values) |
| 30 |
2/3✓ Branch 6 → 7 taken 38 times.
✓ Branch 7 → 8 taken 38 times.
✗ Branch 7 → 15 not taken.
|
38 | if (value.isValid()) |
| 31 |
3/5✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 38 times.
✗ Branch 9 → 10 not taken.
✓ Branch 11 → 12 taken 38 times.
✓ Branch 13 → 14 taken 38 times.
|
38 | set.insert(dateOnly ? value.date().toJulianDay() : value.toSecsSinceEpoch()); |
| 32 | 1352 | return set; | |
| 33 | ✗ | } | |
| 34 | |||
| 35 | 862 | QString nowStamp() { | |
| 36 |
2/2✓ Branch 2 → 3 taken 862 times.
✓ Branch 6 → 7 taken 862 times.
|
1724 | return QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd'T'HHmmss'Z'")); |
| 37 | } | ||
| 38 | |||
| 39 | 802 | void setText(ICal::Component *component, const QString &name, const QString &value) { | |
| 40 |
2/2✓ Branch 3 → 4 taken 260 times.
✓ Branch 3 → 6 taken 542 times.
|
802 | if (value.isEmpty()) { |
| 41 | 260 | component->removeProperties(name); | |
| 42 | 260 | return; | |
| 43 | } | ||
| 44 | 542 | ICal::Property *existing = component->property(name); | |
| 45 |
2/2✓ Branch 7 → 8 taken 162 times.
✓ Branch 7 → 12 taken 380 times.
|
542 | if (existing) { |
| 46 | // Language and other parameters stay with the value they describe. | ||
| 47 |
2/2✓ Branch 8 → 9 taken 162 times.
✓ Branch 9 → 10 taken 162 times.
|
162 | existing->setValue(ICal::escapeText(value)); |
| 48 | 162 | return; | |
| 49 | } | ||
| 50 |
2/2✓ Branch 12 → 13 taken 380 times.
✓ Branch 13 → 14 taken 380 times.
|
380 | component->setPropertyValue(name, ICal::escapeText(value)); |
| 51 | } | ||
| 52 | |||
| 53 | // The TZID parameter to write for `value`: the original text when it still | ||
| 54 | // names the same zone (keeps Outlook's "W. Europe Standard Time" and its | ||
| 55 | // VTIMEZONE), else the IANA id. | ||
| 56 | 547 | QString tzidFor(const QDateTime &value, const ICal::Property *original, | |
| 57 | const ICalTime::ZoneResolver &resolver) { | ||
| 58 |
2/2✓ Branch 3 → 4 taken 34 times.
✓ Branch 3 → 5 taken 513 times.
|
547 | if (value.timeSpec() != Qt::TimeZone) |
| 59 | 34 | return {}; | |
| 60 |
2/2✓ Branch 5 → 6 taken 124 times.
✓ Branch 5 → 33 taken 389 times.
|
513 | if (original) { |
| 61 |
1/1✓ Branch 9 → 10 taken 124 times.
|
248 | const QString tzid = original->parameter(QStringLiteral("TZID")); |
| 62 |
11/16✓ Branch 13 → 14 taken 122 times.
✓ Branch 13 → 19 taken 2 times.
✓ Branch 14 → 15 taken 122 times.
✓ Branch 15 → 16 taken 122 times.
✓ Branch 17 → 18 taken 122 times.
✗ Branch 17 → 19 not taken.
✓ Branch 20 → 21 taken 122 times.
✓ Branch 20 → 22 taken 2 times.
✓ Branch 22 → 23 taken 122 times.
✓ Branch 22 → 24 taken 2 times.
✓ Branch 24 → 25 taken 122 times.
✓ Branch 24 → 27 taken 2 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
|
124 | if (!tzid.isEmpty() && resolver.resolve(tzid) == value.timeZone()) |
| 63 | 122 | return tzid; | |
| 64 | 124 | } | |
| 65 |
3/3✓ Branch 33 → 34 taken 391 times.
✓ Branch 34 → 35 taken 391 times.
✓ Branch 35 → 36 taken 391 times.
|
391 | return QString::fromUtf8(value.timeZone().id()); |
| 66 | } | ||
| 67 | |||
| 68 | 544 | void writeDate(ICal::Component *component, const QString &name, const QDateTime &value, | |
| 69 | bool dateOnly, const ICalTime::ZoneResolver &resolver) { | ||
| 70 |
1/1✓ Branch 2 → 3 taken 544 times.
|
544 | const ICal::Property *original = component->property(name); |
| 71 | 544 | ICal::Property property; | |
| 72 |
2/2✓ Branch 3 → 4 taken 121 times.
✓ Branch 3 → 5 taken 423 times.
|
544 | if (original) { |
| 73 | 121 | property = *original; | |
| 74 | } else { | ||
| 75 | 423 | property.name = name; | |
| 76 | } | ||
| 77 |
2/2✓ Branch 6 → 7 taken 544 times.
✓ Branch 7 → 8 taken 544 times.
|
544 | ICalTime::writeProperty(&property, value, dateOnly, tzidFor(value, original, resolver)); |
| 78 |
1/1✓ Branch 9 → 10 taken 544 times.
|
544 | component->setProperty(property); |
| 79 | 544 | } | |
| 80 | |||
| 81 | 424 | void writeDateList(ICal::Component *component, const QString &name, const QList<QDateTime> &values, | |
| 82 | bool dateOnly, const QDateTime &zoneSource, | ||
| 83 | const ICalTime::ZoneResolver &resolver) { | ||
| 84 |
1/1✓ Branch 5 → 6 taken 424 times.
|
848 | const ICal::Property *anchor = component->property(QStringLiteral("DTSTART")); |
| 85 | 424 | component->removeProperties(name); | |
| 86 |
2/2✓ Branch 48 → 11 taken 3 times.
✓ Branch 48 → 49 taken 424 times.
|
427 | for (const QDateTime &value : values) { |
| 87 |
2/3✓ Branch 12 → 13 taken 3 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 3 times.
|
3 | if (!value.isValid()) |
| 88 | ✗ | continue; | |
| 89 | // Exclusions/additions follow DTSTART's zone so they match occurrences. | ||
| 90 | 3 | QDateTime zoned = value; | |
| 91 |
4/7✓ Branch 16 → 17 taken 3 times.
✗ Branch 16 → 20 not taken.
✓ Branch 17 → 18 taken 3 times.
✓ Branch 18 → 19 taken 3 times.
✗ Branch 18 → 20 not taken.
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 28 not taken.
|
3 | if (!dateOnly && zoneSource.timeSpec() == Qt::TimeZone) |
| 92 |
2/2✓ Branch 22 → 23 taken 3 times.
✓ Branch 23 → 24 taken 3 times.
|
3 | zoned = value.toTimeZone(zoneSource.timeZone()); |
| 93 | ✗ | else if (!dateOnly && zoneSource.timeSpec() == Qt::LocalTime) | |
| 94 | ✗ | zoned = value.toLocalTime(); | |
| 95 | 3 | ICal::Property property; | |
| 96 | 3 | property.name = name; | |
| 97 |
2/2✓ Branch 39 → 40 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
|
3 | ICalTime::writeProperty(&property, zoned, dateOnly, tzidFor(zoned, anchor, resolver)); |
| 98 |
1/1✓ Branch 42 → 43 taken 3 times.
|
3 | component->appendProperty(property); |
| 99 | 3 | } | |
| 100 | 424 | } | |
| 101 | |||
| 102 | 736 | ICal::Property makeProperty(const QString &name, const QString &value) { | |
| 103 | 736 | ICal::Property property; | |
| 104 | 736 | property.name = name; | |
| 105 | 736 | property.value = value; | |
| 106 | 736 | return property; | |
| 107 | } | ||
| 108 | |||
| 109 | // Every TZID referenced by the VEVENTs of `calendar`. | ||
| 110 | 508 | QSet<QString> referencedTzids(const ICal::Component &calendar) { | |
| 111 | 508 | QSet<QString> tzids; | |
| 112 |
2/2✓ Branch 30 → 5 taken 795 times.
✓ Branch 30 → 31 taken 508 times.
|
1303 | for (const ICal::Component &component : calendar.components) { |
| 113 |
2/2✓ Branch 8 → 9 taken 269 times.
✓ Branch 8 → 10 taken 526 times.
|
795 | if (component.name != QLatin1String("VEVENT")) |
| 114 | 269 | continue; | |
| 115 |
2/2✓ Branch 26 → 12 taken 4457 times.
✓ Branch 26 → 27 taken 526 times.
|
4983 | for (const ICal::Property &property : component.properties) { |
| 116 |
1/1✓ Branch 16 → 17 taken 4457 times.
|
4457 | const QString tzid = property.parameter(QStringLiteral("TZID")); |
| 117 |
2/2✓ Branch 20 → 21 taken 817 times.
✓ Branch 20 → 23 taken 3640 times.
|
4457 | if (!tzid.isEmpty()) |
| 118 |
1/1✓ Branch 21 → 22 taken 817 times.
|
817 | tzids.insert(tzid); |
| 119 | 4457 | } | |
| 120 | } | ||
| 121 | 508 | return tzids; | |
| 122 | ✗ | } | |
| 123 | |||
| 124 | // S106-838: a DATE EXDATE on a timed series excludes that day's occurrence | ||
| 125 | // (libical semantics). Exclusions match occurrences by instant, so the date | ||
| 126 | // takes DTSTART's time and zone; UTC midnight would exclude nothing. | ||
| 127 | 47 | QDateTime exclusionInstant(const ICalTime::Value &value, const CalendarEvent &event) { | |
| 128 |
9/12✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 8 taken 41 times.
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 6 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 6 times.
✓ Branch 10 → 11 taken 41 times.
✓ Branch 10 → 12 taken 6 times.
|
47 | if (!value.dateOnly || event.allDay || !event.dtStart.isValid() || !value.dateTime.isValid()) |
| 129 | 41 | return value.dateTime; | |
| 130 | 6 | QDateTime instant = event.dtStart; | |
| 131 |
2/2✓ Branch 13 → 14 taken 6 times.
✓ Branch 14 → 15 taken 6 times.
|
6 | instant.setDate(value.dateTime.date()); |
| 132 | 6 | return instant; | |
| 133 | 6 | } | |
| 134 | |||
| 135 | } // namespace | ||
| 136 | |||
| 137 | 1810 | QString componentText(const ICal::Component &component) { | |
| 138 | 1810 | return ICal::serialize(component); | |
| 139 | } | ||
| 140 | |||
| 141 | 1692 | CalendarEvent eventFromComponent(const ICal::Component &vevent, | |
| 142 | const ICalTime::ZoneResolver &resolver) { | ||
| 143 | 1692 | CalendarEvent event; | |
| 144 |
3/3✓ Branch 6 → 7 taken 1692 times.
✓ Branch 7 → 8 taken 1692 times.
✓ Branch 8 → 9 taken 1692 times.
|
1692 | event.uid = ICal::unescapeText(vevent.value(QStringLiteral("UID"))).trimmed(); |
| 145 |
3/3✓ Branch 18 → 19 taken 1692 times.
✓ Branch 19 → 20 taken 1692 times.
✓ Branch 20 → 21 taken 1692 times.
|
1692 | event.summary = ICal::unescapeText(vevent.value(QStringLiteral("SUMMARY"))).trimmed(); |
| 146 |
3/3✓ Branch 30 → 31 taken 1692 times.
✓ Branch 31 → 32 taken 1692 times.
✓ Branch 32 → 33 taken 1692 times.
|
1692 | event.description = ICal::unescapeText(vevent.value(QStringLiteral("DESCRIPTION"))).trimmed(); |
| 147 |
3/3✓ Branch 42 → 43 taken 1692 times.
✓ Branch 43 → 44 taken 1692 times.
✓ Branch 44 → 45 taken 1692 times.
|
1692 | event.location = ICal::unescapeText(vevent.value(QStringLiteral("LOCATION"))).trimmed(); |
| 148 |
2/2✓ Branch 54 → 55 taken 1692 times.
✓ Branch 55 → 56 taken 1692 times.
|
1692 | event.rrule = vevent.value(QStringLiteral("RRULE")).trimmed(); |
| 149 |
3/3✓ Branch 64 → 65 taken 1692 times.
✓ Branch 67 → 68 taken 1691 times.
✓ Branch 67 → 72 taken 1 time.
|
1692 | if (const ICal::Property *start = vevent.property(QStringLiteral("DTSTART"))) { |
| 150 |
1/1✓ Branch 68 → 69 taken 1691 times.
|
1691 | const ICalTime::Value value = ICalTime::parseProperty(*start, resolver); |
| 151 | 1691 | event.dtStart = value.dateTime; | |
| 152 | 1691 | event.allDay = value.dateOnly; | |
| 153 | 1691 | } | |
| 154 |
3/3✓ Branch 75 → 76 taken 1692 times.
✓ Branch 78 → 79 taken 1299 times.
✓ Branch 78 → 83 taken 393 times.
|
1692 | if (const ICal::Property *end = vevent.property(QStringLiteral("DTEND"))) { |
| 155 |
1/1✓ Branch 79 → 80 taken 1299 times.
|
1299 | event.dtEnd = ICalTime::parseProperty(*end, resolver).dateTime; |
| 156 |
10/16✓ Branch 86 → 87 taken 393 times.
✓ Branch 87 → 88 taken 57 times.
✓ Branch 87 → 91 taken 336 times.
✓ Branch 88 → 89 taken 57 times.
✓ Branch 89 → 90 taken 56 times.
✓ Branch 89 → 91 taken 1 time.
✓ Branch 92 → 93 taken 393 times.
✗ Branch 92 → 94 not taken.
✓ Branch 94 → 95 taken 393 times.
✗ Branch 94 → 96 not taken.
✓ Branch 96 → 97 taken 56 times.
✓ Branch 96 → 122 taken 337 times.
✗ Branch 288 → 289 not taken.
✗ Branch 288 → 290 not taken.
✗ Branch 292 → 293 not taken.
✗ Branch 292 → 294 not taken.
|
786 | } else if (vevent.hasProperty(QStringLiteral("DURATION")) && event.dtStart.isValid()) { |
| 157 | 56 | qint64 seconds = 0; | |
| 158 |
8/20✓ Branch 100 → 101 taken 56 times.
✓ Branch 101 → 102 taken 56 times.
✓ Branch 102 → 103 taken 56 times.
✗ Branch 102 → 105 not taken.
✓ Branch 103 → 104 taken 56 times.
✗ Branch 103 → 105 not taken.
✓ Branch 106 → 107 taken 56 times.
✗ Branch 106 → 108 not taken.
✓ Branch 108 → 109 taken 56 times.
✗ Branch 108 → 110 not taken.
✓ Branch 110 → 111 taken 56 times.
✗ Branch 110 → 112 not taken.
✓ Branch 112 → 113 taken 56 times.
✗ Branch 112 → 121 not taken.
✗ Branch 296 → 297 not taken.
✗ Branch 296 → 298 not taken.
✗ Branch 300 → 301 not taken.
✗ Branch 300 → 302 not taken.
✗ Branch 304 → 305 not taken.
✗ Branch 304 → 306 not taken.
|
112 | if (parseDuration(vevent.value(QStringLiteral("DURATION")), &seconds) && seconds > 0) |
| 159 |
2/6✗ Branch 113 → 114 not taken.
✓ Branch 113 → 117 taken 56 times.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 117 not taken.
✗ Branch 115 → 116 not taken.
✓ Branch 117 → 118 taken 56 times.
|
112 | event.dtEnd = event.allDay && seconds % 86400 == 0 ? event.dtStart.addDays(seconds / 86400) |
| 160 | 56 | : event.dtStart.addSecs(seconds); | |
| 161 | } | ||
| 162 | // A DATE DTSTART without end lasts one day (RFC 5545 §3.6.1); the end | ||
| 163 | // stays unset here so the stored record matches the resource. | ||
| 164 |
3/3✓ Branch 125 → 126 taken 1692 times.
✓ Branch 128 → 129 taken 46 times.
✓ Branch 128 → 133 taken 1646 times.
|
1692 | if (const ICal::Property *recurrence = vevent.property(QStringLiteral("RECURRENCE-ID"))) |
| 165 |
1/1✓ Branch 129 → 130 taken 46 times.
|
46 | event.recurrenceId = ICalTime::parseProperty(*recurrence, resolver).dateTime; |
| 166 |
5/5✓ Branch 136 → 137 taken 1692 times.
✓ Branch 139 → 140 taken 1692 times.
✓ Branch 140 → 141 taken 1692 times.
✓ Branch 157 → 142 taken 37 times.
✓ Branch 157 → 158 taken 1692 times.
|
1729 | for (const ICal::Property &exdate : vevent.propertiesNamed(QStringLiteral("EXDATE"))) |
| 167 |
5/5✓ Branch 143 → 144 taken 37 times.
✓ Branch 144 → 145 taken 37 times.
✓ Branch 145 → 146 taken 37 times.
✓ Branch 153 → 147 taken 47 times.
✓ Branch 153 → 154 taken 37 times.
|
84 | for (const ICalTime::Value &value : ICalTime::parseList(exdate, resolver)) |
| 168 |
2/2✓ Branch 148 → 149 taken 47 times.
✓ Branch 149 → 150 taken 47 times.
|
1776 | event.exdates.append(exclusionInstant(value, event)); |
| 169 |
5/5✓ Branch 162 → 163 taken 1692 times.
✓ Branch 165 → 166 taken 1692 times.
✓ Branch 166 → 167 taken 1692 times.
✓ Branch 181 → 168 taken 14 times.
✓ Branch 181 → 182 taken 1692 times.
|
1706 | for (const ICal::Property &rdate : vevent.propertiesNamed(QStringLiteral("RDATE"))) |
| 170 |
5/5✓ Branch 169 → 170 taken 14 times.
✓ Branch 170 → 171 taken 14 times.
✓ Branch 171 → 172 taken 14 times.
✓ Branch 177 → 173 taken 21 times.
✓ Branch 177 → 178 taken 14 times.
|
35 | for (const ICalTime::Value &value : ICalTime::parseList(rdate, resolver)) |
| 171 |
1/1✓ Branch 174 → 175 taken 21 times.
|
1727 | event.rdates.append(value.dateTime); |
| 172 |
1/1✓ Branch 183 → 184 taken 1692 times.
|
1692 | event.reminders = reminderMinutes(vevent, event.dtStart, event.dtEnd, resolver); |
| 173 |
1/1✓ Branch 186 → 187 taken 1692 times.
|
1692 | event.people = peopleOf(vevent); |
| 174 | 1692 | event.peopleSet = true; | |
| 175 |
1/1✓ Branch 189 → 190 taken 1692 times.
|
1692 | event.attendees = attendeesOf(vevent); |
| 176 |
1/1✓ Branch 195 → 196 taken 1692 times.
|
1692 | const QString lastModified = vevent.value(QStringLiteral("LAST-MODIFIED")); |
| 177 |
2/2✓ Branch 199 → 200 taken 353 times.
✓ Branch 199 → 206 taken 1339 times.
|
1692 | if (!lastModified.isEmpty()) |
| 178 |
1/1✓ Branch 201 → 202 taken 353 times.
|
353 | event.lastModified = ICalTime::parseValue(lastModified, {}, false, resolver).dateTime; |
| 179 |
1/1✓ Branch 206 → 207 taken 1692 times.
|
1692 | event.icalRaw = componentText(vevent); |
| 180 | 1692 | return event; | |
| 181 | 1692 | } | |
| 182 | |||
| 183 | 1078 | bool parseResource(const QString &text, Resource *resource, QString *error) { | |
| 184 | 1078 | QString parseError; | |
| 185 |
1/1✓ Branch 5 → 6 taken 1078 times.
|
1078 | const ICal::Component calendar = ICal::parseFirst(text, QStringLiteral("VCALENDAR"), &parseError); |
| 186 |
2/2✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 13 taken 1073 times.
|
1078 | if (calendar.name.isEmpty()) { |
| 187 |
2/2✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 4 times.
|
5 | if (error) |
| 188 | 1 | *error = parseError; | |
| 189 | 5 | return false; | |
| 190 | } | ||
| 191 | 1073 | resource->calendar = calendar; | |
| 192 |
3/3✓ Branch 17 → 18 taken 1073 times.
✓ Branch 18 → 19 taken 1073 times.
✓ Branch 19 → 20 taken 1073 times.
|
1073 | resource->method = calendar.value(QStringLiteral("METHOD")).trimmed().toUpper(); |
| 193 |
1/1✓ Branch 26 → 27 taken 1073 times.
|
1073 | resource->events.clear(); |
| 194 |
1/1✓ Branch 27 → 28 taken 1073 times.
|
1073 | const ICalTime::ZoneResolver resolver(calendar); |
| 195 |
2/2✓ Branch 59 → 30 taken 1340 times.
✓ Branch 59 → 60 taken 1073 times.
|
2413 | for (const ICal::Component &component : calendar.components) { |
| 196 |
2/2✓ Branch 33 → 34 taken 251 times.
✓ Branch 33 → 35 taken 1089 times.
|
1340 | if (component.name != QLatin1String("VEVENT")) |
| 197 | 253 | continue; | |
| 198 |
1/1✓ Branch 35 → 36 taken 1089 times.
|
1089 | CalendarEvent event = eventFromComponent(component, resolver); |
| 199 |
2/2✓ Branch 37 → 38 taken 2 times.
✓ Branch 37 → 49 taken 1087 times.
|
1089 | if (event.uid.isEmpty()) { |
| 200 |
5/5✓ Branch 38 → 39 taken 2 times.
✓ Branch 42 → 43 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 47 → 40 taken 2 times.
✓ Branch 47 → 48 taken 2 times.
|
4 | qCWarning(lcEventCodec) << "Skipping VEVENT without UID"; |
| 201 | 2 | continue; | |
| 202 | 2 | } | |
| 203 | 1087 | event.resourceIcal = text; | |
| 204 |
1/1✓ Branch 50 → 51 taken 1087 times.
|
1087 | resource->events.append(event); |
| 205 | 1089 | } | |
| 206 | 1073 | return true; | |
| 207 | 1078 | } | |
| 208 | |||
| 209 | 550 | ICal::Component eventComponent(const CalendarEvent &event, const ICalTime::ZoneResolver &resolver) { | |
| 210 | 550 | ICal::Component component; | |
| 211 |
2/2✓ Branch 3 → 4 taken 338 times.
✓ Branch 3 → 13 taken 212 times.
|
550 | if (!event.icalRaw.isEmpty()) |
| 212 |
1/1✓ Branch 7 → 8 taken 338 times.
|
338 | component = ICal::parseFirst(event.icalRaw, QStringLiteral("VEVENT")); |
| 213 | 550 | const bool fresh = component.name.isEmpty(); | |
| 214 |
2/2✓ Branch 14 → 15 taken 212 times.
✓ Branch 14 → 42 taken 338 times.
|
550 | if (fresh) { |
| 215 | 212 | component.name = QStringLiteral("VEVENT"); | |
| 216 |
2/2✓ Branch 21 → 22 taken 212 times.
✓ Branch 26 → 27 taken 212 times.
|
424 | component.appendProperty(makeProperty(QStringLiteral("UID"), ICal::escapeText(event.uid))); |
| 217 |
2/2✓ Branch 31 → 32 taken 212 times.
✓ Branch 36 → 37 taken 212 times.
|
424 | component.appendProperty(makeProperty(QStringLiteral("DTSTAMP"), nowStamp())); |
| 218 | } | ||
| 219 |
3/63✓ Branch 42 → 43 taken 212 times.
✓ Branch 42 → 48 taken 338 times.
✓ Branch 48 → 49 taken 338 times.
✗ Branch 372 → 373 not taken.
✗ Branch 372 → 375 not taken.
✗ Branch 373 → 374 not taken.
✗ Branch 373 → 375 not taken.
✗ Branch 376 → 377 not taken.
✗ Branch 376 → 379 not taken.
✗ Branch 377 → 378 not taken.
✗ Branch 377 → 379 not taken.
✗ Branch 380 → 381 not taken.
✗ Branch 380 → 383 not taken.
✗ Branch 381 → 382 not taken.
✗ Branch 381 → 383 not taken.
✗ Branch 384 → 385 not taken.
✗ Branch 384 → 387 not taken.
✗ Branch 385 → 386 not taken.
✗ Branch 385 → 387 not taken.
✗ Branch 388 → 389 not taken.
✗ Branch 388 → 391 not taken.
✗ Branch 389 → 390 not taken.
✗ Branch 389 → 391 not taken.
✗ Branch 392 → 393 not taken.
✗ Branch 392 → 395 not taken.
✗ Branch 393 → 394 not taken.
✗ Branch 393 → 395 not taken.
✗ Branch 396 → 397 not taken.
✗ Branch 396 → 399 not taken.
✗ Branch 397 → 398 not taken.
✗ Branch 397 → 399 not taken.
✗ Branch 400 → 401 not taken.
✗ Branch 400 → 403 not taken.
✗ Branch 401 → 402 not taken.
✗ Branch 401 → 403 not taken.
✗ Branch 404 → 405 not taken.
✗ Branch 404 → 407 not taken.
✗ Branch 405 → 406 not taken.
✗ Branch 405 → 407 not taken.
✗ Branch 408 → 409 not taken.
✗ Branch 408 → 411 not taken.
✗ Branch 409 → 410 not taken.
✗ Branch 409 → 411 not taken.
✗ Branch 412 → 413 not taken.
✗ Branch 412 → 415 not taken.
✗ Branch 413 → 414 not taken.
✗ Branch 413 → 415 not taken.
✗ Branch 416 → 417 not taken.
✗ Branch 416 → 419 not taken.
✗ Branch 417 → 418 not taken.
✗ Branch 417 → 419 not taken.
✗ Branch 420 → 421 not taken.
✗ Branch 420 → 423 not taken.
✗ Branch 421 → 422 not taken.
✗ Branch 421 → 423 not taken.
✗ Branch 424 → 425 not taken.
✗ Branch 424 → 427 not taken.
✗ Branch 425 → 426 not taken.
✗ Branch 425 → 427 not taken.
✗ Branch 428 → 429 not taken.
✗ Branch 428 → 431 not taken.
✗ Branch 429 → 430 not taken.
✗ Branch 429 → 431 not taken.
|
550 | const CalendarEvent before = fresh ? CalendarEvent{} : eventFromComponent(component, resolver); |
| 220 | 550 | bool changed = fresh; | |
| 221 | 550 | bool significant = false; | |
| 222 |
6/6✓ Branch 49 → 50 taken 338 times.
✓ Branch 49 → 53 taken 212 times.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 53 taken 337 times.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 54 → 64 taken 549 times.
|
550 | if (!fresh && before.uid != event.uid) { |
| 223 | // A copy (duplicate, "keep both") is a new resource with its own UID. | ||
| 224 |
2/2✓ Branch 55 → 56 taken 1 time.
✓ Branch 59 → 60 taken 1 time.
|
2 | component.setPropertyValue(QStringLiteral("UID"), ICal::escapeText(event.uid)); |
| 225 | 1 | changed = true; | |
| 226 | } | ||
| 227 | 1650 | const auto text = [&](const QString &name, const QString &oldValue, const QString &newValue) { | |
| 228 |
6/6✓ Branch 2 → 3 taken 1014 times.
✓ Branch 2 → 6 taken 636 times.
✓ Branch 4 → 5 taken 848 times.
✓ Branch 4 → 6 taken 166 times.
✓ Branch 7 → 8 taken 848 times.
✓ Branch 7 → 9 taken 802 times.
|
1650 | if (!fresh && oldValue == newValue) |
| 229 | 848 | return; | |
| 230 | 802 | setText(&component, name, newValue); | |
| 231 | 802 | changed = true; | |
| 232 | 550 | }; | |
| 233 |
4/5✓ Branch 64 → 65 taken 550 times.
✓ Branch 65 → 66 taken 18 times.
✓ Branch 65 → 70 taken 532 times.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 93 taken 550 times.
|
568 | if (event.recurrenceId.isValid() && |
| 234 |
3/5✓ Branch 66 → 67 taken 18 times.
✗ Branch 66 → 69 not taken.
✓ Branch 67 → 68 taken 18 times.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 70 taken 18 times.
|
18 | (fresh || !sameInstant(before.recurrenceId, event.recurrenceId, event.allDay))) { |
| 235 | ✗ | QDateTime anchor = event.recurrenceId; | |
| 236 | ✗ | if (!event.allDay && event.dtStart.timeSpec() == Qt::TimeZone) | |
| 237 | ✗ | anchor = anchor.toTimeZone(event.dtStart.timeZone()); | |
| 238 | ✗ | writeDate(&component, QStringLiteral("RECURRENCE-ID"), anchor, event.allDay, resolver); | |
| 239 | ✗ | changed = true; | |
| 240 | ✗ | } | |
| 241 |
7/7✓ Branch 93 → 94 taken 338 times.
✓ Branch 93 → 96 taken 212 times.
✓ Branch 94 → 95 taken 338 times.
✓ Branch 95 → 96 taken 62 times.
✓ Branch 95 → 97 taken 276 times.
✓ Branch 98 → 99 taken 274 times.
✓ Branch 98 → 136 taken 276 times.
|
550 | if (fresh || !sameStamp(before.dtStart, before.allDay, event.dtStart, event.allDay)) { |
| 242 |
3/3✓ Branch 99 → 100 taken 274 times.
✓ Branch 100 → 101 taken 272 times.
✓ Branch 100 → 108 taken 2 times.
|
274 | if (event.dtStart.isValid()) |
| 243 |
1/1✓ Branch 104 → 105 taken 272 times.
|
544 | writeDate(&component, QStringLiteral("DTSTART"), event.dtStart, event.allDay, resolver); |
| 244 | // F-10 (SPRINT-104): a stored DURATION would recompute the end from | ||
| 245 | // the NEW start on the server (10:00+PT1H edited to 10:30 read back | ||
| 246 | // as 10:30–11:30). Write the intended end explicitly instead. | ||
| 247 |
12/16✓ Branch 108 → 109 taken 274 times.
✓ Branch 109 → 110 taken 272 times.
✓ Branch 109 → 116 taken 2 times.
✓ Branch 113 → 114 taken 272 times.
✓ Branch 114 → 115 taken 1 time.
✓ Branch 114 → 116 taken 271 times.
✓ Branch 117 → 118 taken 272 times.
✓ Branch 117 → 119 taken 2 times.
✓ Branch 119 → 120 taken 272 times.
✓ Branch 119 → 121 taken 2 times.
✓ Branch 121 → 122 taken 1 time.
✓ Branch 121 → 135 taken 273 times.
✗ Branch 460 → 461 not taken.
✗ Branch 460 → 462 not taken.
✗ Branch 464 → 465 not taken.
✗ Branch 464 → 466 not taken.
|
546 | if (event.dtEnd.isValid() && component.hasProperty(QStringLiteral("DURATION"))) { |
| 248 |
1/1✓ Branch 125 → 126 taken 1 time.
|
1 | component.removeProperties(QStringLiteral("DURATION")); |
| 249 |
1/1✓ Branch 131 → 132 taken 1 time.
|
2 | writeDate(&component, QStringLiteral("DTEND"), event.dtEnd, event.allDay, resolver); |
| 250 | } | ||
| 251 | 274 | changed = significant = true; | |
| 252 | } | ||
| 253 |
5/6✓ Branch 136 → 137 taken 338 times.
✓ Branch 136 → 140 taken 212 times.
✓ Branch 137 → 138 taken 338 times.
✗ Branch 137 → 140 not taken.
✓ Branch 142 → 143 taken 273 times.
✓ Branch 142 → 166 taken 277 times.
|
888 | if (fresh || before.allDay != event.allDay || |
| 254 |
3/3✓ Branch 138 → 139 taken 338 times.
✓ Branch 139 → 140 taken 61 times.
✓ Branch 139 → 141 taken 277 times.
|
338 | !sameStamp(before.dtEnd, before.allDay, event.dtEnd, event.allDay)) { |
| 255 |
1/1✓ Branch 146 → 147 taken 273 times.
|
273 | component.removeProperties(QStringLiteral("DURATION")); |
| 256 |
3/3✓ Branch 149 → 150 taken 273 times.
✓ Branch 150 → 151 taken 271 times.
✓ Branch 150 → 158 taken 2 times.
|
273 | if (event.dtEnd.isValid()) |
| 257 |
1/1✓ Branch 154 → 155 taken 271 times.
|
542 | writeDate(&component, QStringLiteral("DTEND"), event.dtEnd, event.allDay, resolver); |
| 258 | else | ||
| 259 |
1/1✓ Branch 161 → 162 taken 2 times.
|
2 | component.removeProperties(QStringLiteral("DTEND")); |
| 260 | 273 | changed = significant = true; | |
| 261 | } | ||
| 262 |
1/1✓ Branch 169 → 170 taken 550 times.
|
1100 | text(QStringLiteral("SUMMARY"), before.summary, event.summary); |
| 263 |
1/1✓ Branch 175 → 176 taken 550 times.
|
1100 | text(QStringLiteral("LOCATION"), before.location, event.location); |
| 264 |
1/1✓ Branch 181 → 182 taken 550 times.
|
1100 | text(QStringLiteral("DESCRIPTION"), before.description, event.description); |
| 265 |
11/16✓ Branch 184 → 185 taken 338 times.
✓ Branch 184 → 189 taken 212 times.
✓ Branch 185 → 186 taken 338 times.
✓ Branch 186 → 187 taken 338 times.
✗ Branch 188 → 189 not taken.
✓ Branch 188 → 190 taken 338 times.
✓ Branch 191 → 192 taken 338 times.
✓ Branch 191 → 193 taken 212 times.
✓ Branch 193 → 194 taken 338 times.
✓ Branch 193 → 195 taken 212 times.
✓ Branch 195 → 196 taken 212 times.
✓ Branch 195 → 221 taken 338 times.
✗ Branch 516 → 517 not taken.
✗ Branch 516 → 518 not taken.
✗ Branch 520 → 521 not taken.
✗ Branch 520 → 522 not taken.
|
550 | if (fresh || before.rrule.trimmed() != event.rrule.trimmed()) { |
| 266 |
1/1✓ Branch 196 → 197 taken 212 times.
|
212 | QString rule = event.rrule.trimmed(); |
| 267 | // T-610/SEC-09: RRULE delimiters stay unescaped; strip CR/LF injection. | ||
| 268 |
1/1✓ Branch 199 → 200 taken 212 times.
|
212 | rule.remove(QLatin1Char('\r')); |
| 269 |
1/1✓ Branch 202 → 203 taken 212 times.
|
212 | rule.remove(QLatin1Char('\n')); |
| 270 |
2/2✓ Branch 204 → 205 taken 204 times.
✓ Branch 204 → 212 taken 8 times.
|
212 | if (rule.isEmpty()) |
| 271 |
1/1✓ Branch 208 → 209 taken 204 times.
|
204 | component.removeProperties(QStringLiteral("RRULE")); |
| 272 | else | ||
| 273 |
1/1✓ Branch 215 → 216 taken 8 times.
|
8 | component.setPropertyValue(QStringLiteral("RRULE"), rule); |
| 274 | 212 | changed = true; | |
| 275 | 212 | significant = true; | |
| 276 | 212 | } | |
| 277 |
11/16✓ Branch 221 → 222 taken 338 times.
✓ Branch 221 → 227 taken 212 times.
✓ Branch 222 → 223 taken 338 times.
✓ Branch 223 → 224 taken 338 times.
✓ Branch 225 → 226 taken 338 times.
✗ Branch 225 → 227 not taken.
✓ Branch 229 → 230 taken 338 times.
✓ Branch 229 → 231 taken 212 times.
✓ Branch 231 → 232 taken 338 times.
✓ Branch 231 → 233 taken 212 times.
✓ Branch 233 → 234 taken 212 times.
✓ Branch 233 → 241 taken 338 times.
✗ Branch 543 → 544 not taken.
✗ Branch 543 → 545 not taken.
✗ Branch 547 → 548 not taken.
✗ Branch 547 → 549 not taken.
|
888 | if (fresh || instantSet(before.exdates, before.allDay) != instantSet(event.exdates, event.allDay) || |
| 278 |
1/2✗ Branch 226 → 227 not taken.
✓ Branch 226 → 228 taken 338 times.
|
338 | before.allDay != event.allDay) { |
| 279 |
1/1✓ Branch 237 → 238 taken 212 times.
|
424 | writeDateList(&component, QStringLiteral("EXDATE"), event.exdates, event.allDay, event.dtStart, |
| 280 | resolver); | ||
| 281 | 212 | changed = significant = true; | |
| 282 | } | ||
| 283 |
11/16✓ Branch 241 → 242 taken 338 times.
✓ Branch 241 → 247 taken 212 times.
✓ Branch 242 → 243 taken 338 times.
✓ Branch 243 → 244 taken 338 times.
✓ Branch 245 → 246 taken 338 times.
✗ Branch 245 → 247 not taken.
✓ Branch 249 → 250 taken 338 times.
✓ Branch 249 → 251 taken 212 times.
✓ Branch 251 → 252 taken 338 times.
✓ Branch 251 → 253 taken 212 times.
✓ Branch 253 → 254 taken 212 times.
✓ Branch 253 → 261 taken 338 times.
✗ Branch 557 → 558 not taken.
✗ Branch 557 → 559 not taken.
✗ Branch 561 → 562 not taken.
✗ Branch 561 → 563 not taken.
|
888 | if (fresh || instantSet(before.rdates, before.allDay) != instantSet(event.rdates, event.allDay) || |
| 284 |
1/2✗ Branch 246 → 247 not taken.
✓ Branch 246 → 248 taken 338 times.
|
338 | before.allDay != event.allDay) { |
| 285 |
1/1✓ Branch 257 → 258 taken 212 times.
|
424 | writeDateList(&component, QStringLiteral("RDATE"), event.rdates, event.allDay, event.dtStart, |
| 286 | resolver); | ||
| 287 | 212 | changed = significant = true; | |
| 288 | } | ||
| 289 | // D-14: people are CONTACT lines, never ATTENDEE; naming people is no | ||
| 290 | // significant change (no SEQUENCE bump, nobody to re-invite). | ||
| 291 |
11/11✓ Branch 261 → 262 taken 158 times.
✓ Branch 261 → 268 taken 392 times.
✓ Branch 262 → 263 taken 15 times.
✓ Branch 262 → 265 taken 143 times.
✓ Branch 264 → 267 taken 12 times.
✓ Branch 264 → 268 taken 3 times.
✓ Branch 265 → 266 taken 143 times.
✓ Branch 266 → 267 taken 2 times.
✓ Branch 266 → 268 taken 141 times.
✓ Branch 269 → 270 taken 14 times.
✓ Branch 269 → 272 taken 536 times.
|
550 | if (event.peopleSet && (fresh ? !event.people.isEmpty() : before.people != event.people)) { |
| 292 |
1/1✓ Branch 270 → 271 taken 14 times.
|
14 | writePeople(&component, event.people); |
| 293 | 14 | changed = true; | |
| 294 | } | ||
| 295 |
5/5✓ Branch 272 → 273 taken 212 times.
✓ Branch 272 → 275 taken 338 times.
✓ Branch 275 → 276 taken 338 times.
✓ Branch 277 → 278 taken 4 times.
✓ Branch 277 → 280 taken 546 times.
|
550 | if (fresh ? !event.reminders.isEmpty() : before.reminders != event.reminders) { |
| 296 |
1/1✓ Branch 278 → 279 taken 4 times.
|
4 | writeReminders(&component, event.reminders, event.summary); |
| 297 | 4 | changed = true; // not significant: attendees need no new invitation | |
| 298 | } | ||
| 299 |
2/2✓ Branch 280 → 281 taken 212 times.
✓ Branch 280 → 301 taken 338 times.
|
550 | if (fresh) { |
| 300 |
1/1✓ Branch 287 → 288 taken 212 times.
|
424 | component.setPropertyValue(QStringLiteral("SEQUENCE"), QStringLiteral("0")); |
| 301 |
2/2✓ Branch 292 → 293 taken 212 times.
✓ Branch 296 → 297 taken 212 times.
|
424 | component.setPropertyValue(QStringLiteral("LAST-MODIFIED"), nowStamp()); |
| 302 | 212 | return component; | |
| 303 | } | ||
| 304 |
2/2✓ Branch 301 → 302 taken 119 times.
✓ Branch 301 → 303 taken 219 times.
|
338 | if (!changed) |
| 305 | 119 | return component; | |
| 306 |
2/2✓ Branch 303 → 304 taken 62 times.
✓ Branch 303 → 321 taken 157 times.
|
219 | if (significant) { |
| 307 |
2/2✓ Branch 307 → 308 taken 62 times.
✓ Branch 308 → 309 taken 62 times.
|
62 | const int sequence = component.value(QStringLiteral("SEQUENCE")).toInt(); |
| 308 |
2/2✓ Branch 312 → 313 taken 62 times.
✓ Branch 316 → 317 taken 62 times.
|
124 | component.setPropertyValue(QStringLiteral("SEQUENCE"), QString::number(sequence + 1)); |
| 309 | } | ||
| 310 |
2/2✓ Branch 321 → 322 taken 219 times.
✓ Branch 325 → 326 taken 219 times.
|
438 | component.setPropertyValue(QStringLiteral("DTSTAMP"), nowStamp()); |
| 311 |
2/2✓ Branch 329 → 330 taken 219 times.
✓ Branch 333 → 334 taken 219 times.
|
438 | component.setPropertyValue(QStringLiteral("LAST-MODIFIED"), nowStamp()); |
| 312 | 219 | return component; | |
| 313 | 550 | } | |
| 314 | |||
| 315 | 95 | void adoptPushedResource(CalendarEvent *event, const QByteArray &body) { | |
| 316 |
1/1✓ Branch 2 → 3 taken 95 times.
|
95 | const QString text = QString::fromUtf8(body); |
| 317 |
1/1✓ Branch 6 → 7 taken 95 times.
|
95 | const ICal::Component calendar = ICal::parseFirst(text, QStringLiteral("VCALENDAR")); |
| 318 |
1/2✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 95 times.
|
95 | if (calendar.name.isEmpty()) |
| 319 | ✗ | return; | |
| 320 |
1/1✓ Branch 12 → 13 taken 95 times.
|
95 | const ICalTime::ZoneResolver resolver(calendar); |
| 321 |
4/5✓ Branch 16 → 17 taken 95 times.
✓ Branch 19 → 20 taken 95 times.
✓ Branch 20 → 21 taken 95 times.
✓ Branch 76 → 22 taken 96 times.
✗ Branch 76 → 77 not taken.
|
96 | for (const ICal::Component &component : calendar.componentsNamed(QStringLiteral("VEVENT"))) { |
| 322 |
4/5✓ Branch 26 → 27 taken 96 times.
✓ Branch 27 → 28 taken 96 times.
✓ Branch 28 → 29 taken 96 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 96 times.
|
192 | if (ICal::unescapeText(component.value(QStringLiteral("UID"))).trimmed() != event->uid) |
| 323 | 1 | continue; | |
| 324 |
1/1✓ Branch 40 → 41 taken 96 times.
|
96 | const ICal::Property *recurrence = component.property(QStringLiteral("RECURRENCE-ID")); |
| 325 | const QDateTime anchor = | ||
| 326 |
5/7✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 46 taken 95 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 49 taken 95 times.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
|
96 | recurrence ? ICalTime::parseProperty(*recurrence, resolver).dateTime : QDateTime(); |
| 327 |
4/4✓ Branch 49 → 50 taken 96 times.
✓ Branch 50 → 51 taken 96 times.
✓ Branch 51 → 52 taken 95 times.
✓ Branch 51 → 58 taken 1 time.
|
191 | if (anchor.isValid() != event->recurrenceId.isValid() || |
| 328 |
13/18✓ Branch 52 → 53 taken 95 times.
✓ Branch 53 → 54 taken 1 time.
✓ Branch 53 → 59 taken 94 times.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 55 → 56 taken 1 time.
✓ Branch 56 → 57 taken 1 time.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 1 time.
✓ Branch 60 → 61 taken 1 time.
✓ Branch 60 → 62 taken 95 times.
✓ Branch 62 → 63 taken 1 time.
✓ Branch 62 → 64 taken 95 times.
✓ Branch 64 → 65 taken 1 time.
✓ Branch 64 → 66 taken 95 times.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
|
191 | (anchor.isValid() && anchor.toUTC() != event->recurrenceId.toUTC())) |
| 329 | 1 | continue; | |
| 330 |
1/1✓ Branch 66 → 67 taken 95 times.
|
95 | event->icalRaw = componentText(component); |
| 331 | 95 | event->resourceIcal = text; | |
| 332 | 95 | return; | |
| 333 | 191 | } | |
| 334 | 285 | } | |
| 335 | |||
| 336 | 508 | QByteArray buildResource(const CalendarEvent &master, const QList<CalendarEvent> &exceptions, | |
| 337 | const QString &storedResource) { | ||
| 338 | 508 | ICal::Component calendar; | |
| 339 |
2/2✓ Branch 3 → 4 taken 352 times.
✓ Branch 3 → 13 taken 156 times.
|
508 | if (!storedResource.isEmpty()) |
| 340 |
1/1✓ Branch 7 → 8 taken 352 times.
|
352 | calendar = ICal::parseFirst(storedResource, QStringLiteral("VCALENDAR")); |
| 341 |
2/2✓ Branch 14 → 15 taken 156 times.
✓ Branch 14 → 50 taken 352 times.
|
508 | if (calendar.name.isEmpty()) { |
| 342 | 156 | calendar = {}; | |
| 343 | 156 | calendar.name = QStringLiteral("VCALENDAR"); | |
| 344 |
1/1✓ Branch 30 → 31 taken 156 times.
|
312 | calendar.appendProperty(makeProperty(QStringLiteral("VERSION"), QStringLiteral("2.0"))); |
| 345 |
1/1✓ Branch 43 → 44 taken 156 times.
|
156 | calendar.appendProperty(makeProperty(QStringLiteral("PRODID"), |
| 346 | 312 | QStringLiteral("-//MailJD//CalDAV Client//EN"))); | |
| 347 | } | ||
| 348 |
1/1✓ Branch 53 → 54 taken 508 times.
|
508 | calendar.removeProperties(QStringLiteral("METHOD")); |
| 349 |
1/1✓ Branch 56 → 57 taken 508 times.
|
508 | const ICalTime::ZoneResolver resolver(calendar); |
| 350 |
1/1✓ Branch 60 → 61 taken 508 times.
|
508 | calendar.removeComponents(QStringLiteral("VEVENT")); |
| 351 |
2/2✓ Branch 63 → 64 taken 508 times.
✓ Branch 64 → 65 taken 508 times.
|
508 | calendar.components.append(eventComponent(master, resolver)); |
| 352 |
2/2✓ Branch 74 → 68 taken 18 times.
✓ Branch 74 → 75 taken 508 times.
|
526 | for (const CalendarEvent &exception : exceptions) |
| 353 |
2/2✓ Branch 69 → 70 taken 18 times.
✓ Branch 70 → 71 taken 18 times.
|
18 | calendar.components.append(eventComponent(exception, resolver)); |
| 354 | // A TZID needs its VTIMEZONE (RFC 5545 §3.2.19). Keep the resource's own | ||
| 355 | // definitions; generate the missing ones before the first VEVENT. | ||
| 356 | 508 | QSet<QString> defined; | |
| 357 |
5/5✓ Branch 79 → 80 taken 508 times.
✓ Branch 82 → 83 taken 508 times.
✓ Branch 83 → 84 taken 508 times.
✓ Branch 100 → 85 taken 267 times.
✓ Branch 100 → 101 taken 508 times.
|
775 | for (const ICal::Component &component : calendar.componentsNamed(QStringLiteral("VTIMEZONE"))) |
| 358 |
4/4✓ Branch 89 → 90 taken 267 times.
✓ Branch 90 → 91 taken 267 times.
✓ Branch 91 → 92 taken 267 times.
✓ Branch 92 → 93 taken 267 times.
|
775 | defined.insert(ICal::unescapeText(component.value(QStringLiteral("TZID"))).trimmed()); |
| 359 | 508 | int insertAt = 0; | |
| 360 |
1/2✓ Branch 105 → 106 taken 777 times.
✗ Branch 105 → 111 not taken.
|
1554 | while (insertAt < calendar.components.size() && |
| 361 |
4/4✓ Branch 109 → 110 taken 269 times.
✓ Branch 109 → 111 taken 508 times.
✓ Branch 112 → 103 taken 269 times.
✓ Branch 112 → 113 taken 508 times.
|
1554 | calendar.components.at(insertAt).name != QLatin1String("VEVENT")) |
| 362 | 269 | ++insertAt; | |
| 363 |
6/6✓ Branch 113 → 114 taken 508 times.
✓ Branch 114 → 115 taken 506 times.
✓ Branch 114 → 116 taken 2 times.
✓ Branch 115 → 117 taken 506 times.
✓ Branch 116 → 117 taken 2 times.
✓ Branch 117 → 118 taken 508 times.
|
508 | const int year = (master.dtStart.isValid() ? master.dtStart.date() : QDate::currentDate()).year(); |
| 364 |
3/3✓ Branch 118 → 119 taken 508 times.
✓ Branch 119 → 120 taken 508 times.
✓ Branch 120 → 121 taken 508 times.
|
508 | QStringList missing = referencedTzids(calendar).subtract(defined).values(); |
| 365 |
1/1✓ Branch 122 → 123 taken 508 times.
|
508 | missing.sort(); |
| 366 |
4/4✓ Branch 123 → 124 taken 508 times.
✓ Branch 124 → 125 taken 508 times.
✓ Branch 161 → 126 taken 127 times.
✓ Branch 161 → 162 taken 508 times.
|
635 | for (const QString &tzid : missing) { |
| 367 |
1/1✓ Branch 127 → 128 taken 127 times.
|
127 | const QTimeZone zone = resolver.resolve(tzid); |
| 368 |
2/3✓ Branch 128 → 129 taken 127 times.
✗ Branch 129 → 130 not taken.
✓ Branch 129 → 142 taken 127 times.
|
127 | if (!zone.isValid()) { |
| 369 | ✗ | qCWarning(lcEventCodec) << "Cannot generate VTIMEZONE for" << tzid; | |
| 370 | ✗ | continue; | |
| 371 | ✗ | } | |
| 372 |
1/1✓ Branch 142 → 143 taken 127 times.
|
127 | ICal::Component timezone = ICalTime::vtimezoneFor(zone, year); |
| 373 |
2/2✓ Branch 143 → 144 taken 127 times.
✓ Branch 147 → 148 taken 127 times.
|
254 | timezone.setPropertyValue(QStringLiteral("TZID"), ICal::escapeText(tzid)); |
| 374 |
1/1✓ Branch 151 → 152 taken 127 times.
|
127 | calendar.components.insert(insertAt++, timezone); |
| 375 | 127 | } | |
| 376 |
2/2✓ Branch 162 → 163 taken 508 times.
✓ Branch 163 → 164 taken 508 times.
|
1016 | return ICal::serialize(calendar).toUtf8(); |
| 377 | 508 | } | |
| 378 | |||
| 379 | } // namespace ICalEventCodec | ||
| 380 |