File indexing completed on 2024-12-22 04:57:03
0001 /* 0002 SPDX-FileCopyrightText: 2015-2017 Krzysztof Nowicki <krissn@op.pl> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ewsrecurrence.h" 0008 0009 #include <QBitArray> 0010 #include <QDate> 0011 #include <QXmlStreamReader> 0012 0013 #include "ewsclient_debug.h" 0014 #include "ewstypes.h" 0015 0016 using namespace KCalendarCore; 0017 0018 static const QString dayOfWeekNames[] = { 0019 QStringLiteral("Monday"), 0020 QStringLiteral("Tuesday"), 0021 QStringLiteral("Wednesday"), 0022 QStringLiteral("Thursday"), 0023 QStringLiteral("Friday"), 0024 QStringLiteral("Saturday"), 0025 QStringLiteral("Sunday"), 0026 QStringLiteral("Day"), 0027 QStringLiteral("Weekday"), 0028 QStringLiteral("WeekendDay"), 0029 }; 0030 constexpr unsigned dayOfWeekNameCount = sizeof(dayOfWeekNames) / sizeof(dayOfWeekNames[0]); 0031 0032 static const QString dayOfWeekIndexNames[] = { 0033 QStringLiteral("First"), 0034 QStringLiteral("Second"), 0035 QStringLiteral("Third"), 0036 QStringLiteral("Fourth"), 0037 QStringLiteral("Last"), 0038 }; 0039 constexpr unsigned dayOfWeekIndexNameCount = sizeof(dayOfWeekIndexNames) / sizeof(dayOfWeekIndexNames[0]); 0040 0041 static const QString monthNames[] = { 0042 QStringLiteral("January"), 0043 QStringLiteral("February"), 0044 QStringLiteral("March"), 0045 QStringLiteral("April"), 0046 QStringLiteral("May"), 0047 QStringLiteral("June"), 0048 QStringLiteral("July"), 0049 QStringLiteral("August"), 0050 QStringLiteral("September"), 0051 QStringLiteral("October"), 0052 QStringLiteral("November"), 0053 QStringLiteral("December"), 0054 }; 0055 constexpr unsigned monthNameCount = sizeof(monthNames) / sizeof(monthNames[0]); 0056 0057 EwsRecurrence::EwsRecurrence() 0058 : Recurrence() 0059 { 0060 } 0061 0062 EwsRecurrence::EwsRecurrence(QXmlStreamReader &reader) 0063 { 0064 while (reader.readNextStartElement()) { 0065 QString elmName = reader.name().toString(); 0066 if (reader.namespaceUri() != ewsTypeNsUri) { 0067 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0068 return; 0069 } 0070 0071 if (elmName == QLatin1StringView("RelativeYearlyRecurrence")) { 0072 if (!readRelativeYearlyRecurrence(reader)) { 0073 return; 0074 } 0075 } else if (elmName == QLatin1StringView("AbsoluteYearlyRecurrence")) { 0076 if (!readAbsoluteYearlyRecurrence(reader)) { 0077 return; 0078 } 0079 } else if (elmName == QLatin1StringView("RelativeMonthlyRecurrence")) { 0080 if (!readRelativeMonthlyRecurrence(reader)) { 0081 return; 0082 } 0083 } else if (elmName == QLatin1StringView("AbsoluteMonthlyRecurrence")) { 0084 if (!readAbsoluteMonthlyRecurrence(reader)) { 0085 return; 0086 } 0087 } else if (elmName == QLatin1StringView("WeeklyRecurrence")) { 0088 if (!readWeeklyRecurrence(reader)) { 0089 return; 0090 } 0091 } else if (elmName == QLatin1StringView("DailyRecurrence")) { 0092 if (!readWeeklyRecurrence(reader)) { 0093 return; 0094 } 0095 } else if (elmName == QLatin1StringView("NoEndRecurrence")) { 0096 // Ignore - this is the default 0097 reader.skipCurrentElement(); 0098 } else if (elmName == QLatin1StringView("EndDateRecurrence")) { 0099 if (!readEndDateRecurrence(reader)) { 0100 return; 0101 } 0102 } else if (elmName == QLatin1StringView("NumberedRecurrence")) { 0103 if (!readNumberedRecurrence(reader)) { 0104 return; 0105 } 0106 } else { 0107 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.").arg(QStringLiteral("Recurrence"), elmName); 0108 return; 0109 } 0110 } 0111 } 0112 0113 EwsRecurrence::EwsRecurrence(const EwsRecurrence &other) 0114 : Recurrence(other) 0115 { 0116 } 0117 0118 bool EwsRecurrence::readRelativeYearlyRecurrence(QXmlStreamReader &reader) 0119 { 0120 QBitArray dow(7); 0121 short dowWeekIndex = 0; 0122 short month = 0; 0123 bool hasDow = false; 0124 bool hasDowWeekIndex = false; 0125 bool hasMonth = false; 0126 0127 while (reader.readNextStartElement()) { 0128 QString elmName = reader.name().toString(); 0129 if (reader.namespaceUri() != ewsTypeNsUri) { 0130 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0131 return false; 0132 } 0133 0134 if (elmName == QLatin1StringView("DaysOfWeek")) { 0135 if (!readDow(reader, dow)) { 0136 return false; 0137 } 0138 hasDow = true; 0139 } else if (elmName == QLatin1StringView("DayOfWeekIndex")) { 0140 bool ok; 0141 QString text = reader.readElementText(); 0142 dowWeekIndex = decodeEnumString<short>(text, dayOfWeekIndexNames, dayOfWeekIndexNameCount, &ok); 0143 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0144 qCWarning(EWSCLI_LOG) 0145 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("DayOfWeekIndex").arg(text)); 0146 return false; 0147 } 0148 if (dowWeekIndex == 4) { // "Last" 0149 dowWeekIndex = -1; 0150 } 0151 hasDowWeekIndex = true; 0152 } else if (elmName == QLatin1StringView("Month")) { 0153 bool ok; 0154 QString text = reader.readElementText(); 0155 month = decodeEnumString<short>(text, monthNames, monthNameCount, &ok); 0156 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0157 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Month"), text); 0158 return false; 0159 } 0160 hasMonth = true; 0161 } else { 0162 qCWarning(EWSCLI_LOG) 0163 << QStringLiteral("Failed to read %1 element - unknown element: %2.").arg(QStringLiteral("RelativeYearlyRecurrence"), elmName); 0164 return false; 0165 } 0166 } 0167 0168 if (!hasMonth || !hasDow || !hasDowWeekIndex) { 0169 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Month, DaysOfWeek and DayOfWeekIndex elements."); 0170 return false; 0171 } 0172 0173 addYearlyMonth(month); 0174 addYearlyPos(dowWeekIndex, dow); 0175 0176 return true; 0177 } 0178 0179 bool EwsRecurrence::readAbsoluteYearlyRecurrence(QXmlStreamReader &reader) 0180 { 0181 short dom = 0; 0182 short month = 0; 0183 bool hasDom = false; 0184 bool hasMonth = false; 0185 0186 while (reader.readNextStartElement()) { 0187 QString elmName = reader.name().toString(); 0188 if (reader.namespaceUri() != ewsTypeNsUri) { 0189 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0190 return false; 0191 } 0192 0193 if (elmName == QLatin1StringView("DayOfMonth")) { 0194 bool ok; 0195 QString text = reader.readElementText(); 0196 dom = text.toShort(&ok); 0197 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0198 qCWarning(EWSCLI_LOG) 0199 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("DayOfMonth").arg(text)); 0200 return false; 0201 } 0202 hasDom = true; 0203 } else if (elmName == QLatin1StringView("Month")) { 0204 bool ok; 0205 QString text = reader.readElementText(); 0206 month = decodeEnumString<short>(text, monthNames, monthNameCount, &ok); 0207 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0208 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Month").arg(text)); 0209 return false; 0210 } 0211 hasMonth = true; 0212 } else { 0213 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.").arg(elmName); 0214 return false; 0215 } 0216 } 0217 0218 if (!hasDom || !hasMonth) { 0219 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - need both month and dom values."); 0220 return false; 0221 } 0222 0223 // "If for a particular month this value is larger than the number of days in the month, 0224 // the last day of the month is assumed for this property." 0225 QDate date(2000, month, 1); 0226 if (dom > date.daysInMonth()) { 0227 dom = -1; 0228 } 0229 0230 addYearlyMonth(month); 0231 addYearlyDay(dom); 0232 0233 return true; 0234 } 0235 0236 bool EwsRecurrence::readRelativeMonthlyRecurrence(QXmlStreamReader &reader) 0237 { 0238 QBitArray dow(7); 0239 short dowWeekIndex = 0; 0240 int interval = 0; 0241 bool hasDow = false; 0242 bool hasDowWeekIndex = false; 0243 bool hasInterval = false; 0244 0245 while (reader.readNextStartElement()) { 0246 QString elmName = reader.name().toString(); 0247 if (reader.namespaceUri() != ewsTypeNsUri) { 0248 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0249 return false; 0250 } 0251 0252 if (elmName == QLatin1StringView("Interval")) { 0253 bool ok; 0254 QString text = reader.readElementText(); 0255 interval = text.toInt(&ok); 0256 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0257 qCWarning(EWSCLI_LOG) 0258 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Interval").arg(text)); 0259 return false; 0260 } 0261 hasInterval = true; 0262 } else if (elmName == QLatin1StringView("DaysOfWeek")) { 0263 if (!readDow(reader, dow)) { 0264 return false; 0265 } 0266 hasDow = true; 0267 } else if (elmName == QLatin1StringView("DayOfWeekIndex")) { 0268 bool ok; 0269 QString text = reader.readElementText(); 0270 dowWeekIndex = decodeEnumString<short>(text, dayOfWeekIndexNames, dayOfWeekIndexNameCount, &ok); 0271 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0272 qCWarning(EWSCLI_LOG) 0273 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("DayOfWeekIndex").arg(text)); 0274 return false; 0275 } 0276 if (dowWeekIndex == 4) { // "Last" 0277 dowWeekIndex = -1; 0278 } 0279 hasDowWeekIndex = true; 0280 } else { 0281 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - unknown element: %1.").arg(elmName); 0282 return false; 0283 } 0284 } 0285 0286 if (!hasInterval || !hasDow || !hasDowWeekIndex) { 0287 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Interval, DaysOfWeek and DayOfWeekIndex elements."); 0288 return false; 0289 } 0290 0291 addMonthlyPos(dowWeekIndex, dow); 0292 setFrequency(interval); 0293 0294 return true; 0295 } 0296 0297 bool EwsRecurrence::readAbsoluteMonthlyRecurrence(QXmlStreamReader &reader) 0298 { 0299 short dom = 0; 0300 int interval = 0; 0301 bool hasInterval = false; 0302 bool hasDom = false; 0303 0304 while (reader.readNextStartElement()) { 0305 QString elmName = reader.name().toString(); 0306 if (reader.namespaceUri() != ewsTypeNsUri) { 0307 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0308 return false; 0309 } 0310 0311 if (elmName == QLatin1StringView("Interval")) { 0312 bool ok; 0313 QString text = reader.readElementText(); 0314 interval = text.toInt(&ok); 0315 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0316 qCWarningNC(EWSCLI_LOG) 0317 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Interval").arg(text)); 0318 return false; 0319 } 0320 hasInterval = true; 0321 } else if (elmName == QLatin1StringView("DayOfMonth")) { 0322 bool ok; 0323 QString text = reader.readElementText(); 0324 dom = text.toShort(&ok); 0325 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0326 qCWarningNC(EWSCLI_LOG) 0327 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("DayOfMonth").arg(text)); 0328 return false; 0329 } 0330 hasDom = true; 0331 } else { 0332 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - unknown element: %1.").arg(elmName); 0333 return false; 0334 } 0335 } 0336 0337 if (!hasInterval || !hasDom) { 0338 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected both Interval and DayOfMonth."); 0339 return false; 0340 } 0341 0342 addMonthlyDate(dom); 0343 setFrequency(interval); 0344 0345 return true; 0346 } 0347 0348 bool EwsRecurrence::readWeeklyRecurrence(QXmlStreamReader &reader) 0349 { 0350 int interval = 1; 0351 QBitArray dow(7); 0352 int weekStart = 0; 0353 bool hasInterval = false; 0354 bool hasDow = false; 0355 bool hasWeekStart = false; 0356 0357 while (reader.readNextStartElement()) { 0358 QString elmName = reader.name().toString(); 0359 if (reader.namespaceUri() != ewsTypeNsUri) { 0360 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0361 return false; 0362 } 0363 0364 if (elmName == QLatin1StringView("Interval")) { 0365 bool ok; 0366 QString text = reader.readElementText(); 0367 interval = text.toInt(&ok); 0368 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0369 qCWarningNC(EWSCLI_LOG) 0370 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Interval").arg(text)); 0371 return false; 0372 } 0373 hasInterval = true; 0374 } else if (elmName == QLatin1StringView("DaysOfWeek")) { 0375 if (!readDow(reader, dow)) { 0376 return false; 0377 } 0378 hasDow = true; 0379 } else if (elmName == QLatin1StringView("FirstDayOfWeek")) { 0380 bool ok; 0381 QString text = reader.readElementText(); 0382 weekStart = decodeEnumString<int>(text, dayOfWeekNames, dayOfWeekNameCount, &ok) + 1; 0383 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0384 qCWarningNC(EWSCLI_LOG) 0385 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("FirstDayOfWeek").arg(text)); 0386 return false; 0387 } 0388 hasWeekStart = true; 0389 } else { 0390 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.").arg(QStringLiteral("WeeklyRecurrence"), elmName); 0391 return false; 0392 } 0393 } 0394 0395 if (!hasInterval || !hasDow || !hasWeekStart) { 0396 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Interval, DaysOfWeek and FirstDatOfWeek elements."); 0397 return false; 0398 } 0399 0400 setWeekly(interval, dow, weekStart); 0401 0402 return true; 0403 } 0404 0405 bool EwsRecurrence::readDailyRecurrence(QXmlStreamReader &reader) 0406 { 0407 int interval = 1; 0408 bool hasInterval = false; 0409 0410 while (reader.readNextStartElement()) { 0411 QString elmName = reader.name().toString(); 0412 if (reader.namespaceUri() != ewsTypeNsUri) { 0413 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0414 return false; 0415 } 0416 0417 if (elmName == QLatin1StringView("Interval")) { 0418 bool ok; 0419 QString text = reader.readElementText(); 0420 interval = text.toInt(&ok); 0421 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0422 qCWarning(EWSCLI_LOG) 0423 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("Interval").arg(text)); 0424 return false; 0425 } 0426 hasInterval = true; 0427 } else { 0428 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.").arg(elmName); 0429 return false; 0430 } 0431 } 0432 0433 if (!hasInterval) { 0434 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected an Interval element."); 0435 return false; 0436 } 0437 0438 setDaily(interval); 0439 0440 return true; 0441 } 0442 0443 bool EwsRecurrence::readEndDateRecurrence(QXmlStreamReader &reader) 0444 { 0445 QDate dateEnd; 0446 0447 while (reader.readNextStartElement()) { 0448 QString elmName = reader.name().toString(); 0449 if (reader.namespaceUri() != ewsTypeNsUri) { 0450 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0451 return false; 0452 } 0453 0454 if (elmName == QLatin1StringView("EndDate")) { 0455 QString text = reader.readElementText(); 0456 dateEnd = QDate::fromString(text, Qt::ISODate); 0457 if (reader.error() != QXmlStreamReader::NoError || !dateEnd.isValid()) { 0458 qCWarning(EWSCLI_LOG) 0459 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("EndDate").arg(text)); 0460 return false; 0461 } 0462 } else if (elmName == QLatin1StringView("StartDate")) { 0463 // Don't care 0464 reader.skipCurrentElement(); 0465 } else { 0466 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.").arg(QStringLiteral("EndDateRecurrence"), elmName); 0467 return false; 0468 } 0469 } 0470 0471 setEndDate(dateEnd); 0472 0473 return true; 0474 } 0475 0476 bool EwsRecurrence::readNumberedRecurrence(QXmlStreamReader &reader) 0477 { 0478 int numOccurrences = 0; 0479 0480 while (reader.readNextStartElement()) { 0481 QString elmName = reader.name().toString(); 0482 if (reader.namespaceUri() != ewsTypeNsUri) { 0483 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); 0484 return false; 0485 } 0486 0487 if (elmName == QLatin1StringView("NumberOfOccurrences")) { 0488 bool ok; 0489 QString text = reader.readElementText(); 0490 numOccurrences = text.toInt(&ok); 0491 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0492 qCWarning(EWSCLI_LOG) 0493 << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("NumberOfOccurrences").arg(text)); 0494 return false; 0495 } 0496 } else if (elmName == QLatin1StringView("StartDate")) { 0497 // Don't care 0498 reader.skipCurrentElement(); 0499 } else { 0500 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.").arg(elmName); 0501 return false; 0502 } 0503 } 0504 0505 setDuration(numOccurrences); 0506 0507 return true; 0508 } 0509 0510 bool EwsRecurrence::readDow(QXmlStreamReader &reader, QBitArray &dow) 0511 { 0512 bool ok; 0513 QString text = reader.readElementText(); 0514 const QStringList days = text.split(QLatin1Char(' ')); 0515 for (const QString &day : days) { 0516 auto dowIndex = decodeEnumString<short>(day, dayOfWeekNames, dayOfWeekNameCount, &ok); 0517 if (reader.error() != QXmlStreamReader::NoError || !ok) { 0518 qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).").arg(QStringLiteral("DaysOfWeek").arg(day)); 0519 return false; 0520 } 0521 if (dowIndex == 7) { // "Day" 0522 dow.fill(true, 0, 7); 0523 } else if (dowIndex == 8) { // "Weekday" 0524 dow.fill(true, 0, 5); 0525 } else if (dowIndex == 9) { // "WeekendDay" 0526 dow.fill(true, 5, 7); 0527 } else { 0528 dow.setBit(dowIndex); 0529 } 0530 } 0531 return true; 0532 }