File indexing completed on 2024-12-01 04:47:51
0001 /* 0002 This file is part of the kolab resource - the implementation of the 0003 Kolab storage format. See www.kolab.org for documentation on this. 0004 0005 SPDX-FileCopyrightText: 2004 Bo Thorsen <bo@sonofthor.dk> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "kolabbase.h" 0011 #include "pimkolab_debug.h" 0012 0013 #include <KContacts/Addressee> 0014 #include <KContacts/ContactGroup> 0015 0016 using namespace KolabV2; 0017 0018 KolabBase::KolabBase(const QString &tz) 0019 : mCreationDate(QDateTime::currentDateTime()) 0020 , mLastModified(QDateTime::currentDateTimeUtc()) 0021 , mSensitivity(Public) 0022 { 0023 // unlike the previously used KTimeZone here, QTimeZone defaults to local time zone if tz.isEmpty() 0024 // we therefore force it to invalid, which however is unsafe to use (unlike KTimeZone) 0025 // therefore all usage of mTimeZone must be preceded by a validity check 0026 mTimeZone = tz.isEmpty() ? QTimeZone() : QTimeZone(tz.toUtf8()); 0027 } 0028 0029 KolabBase::~KolabBase() = default; 0030 0031 void KolabBase::setFields(const KCalendarCore::Incidence::Ptr &incidence) 0032 { 0033 // So far unhandled KCalendarCore::IncidenceBase fields: 0034 // mPilotID, mSyncStatus, mFloats 0035 0036 setUid(incidence->uid()); 0037 setBody(incidence->description()); 0038 setCategories(incidence->categoriesStr()); 0039 setCreationDate(localToUTC(incidence->created())); 0040 setLastModified(incidence->lastModified()); 0041 setSensitivity(static_cast<Sensitivity>(incidence->secrecy())); 0042 // TODO: Attachments 0043 } 0044 0045 void KolabBase::saveTo(const KCalendarCore::Incidence::Ptr &incidence) const 0046 { 0047 incidence->setUid(uid()); 0048 incidence->setDescription(body()); 0049 incidence->setCategories(categories()); 0050 incidence->setCreated(utcToLocal(creationDate())); 0051 incidence->setLastModified(lastModified()); 0052 switch (sensitivity()) { 0053 case 1: 0054 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPrivate); 0055 break; 0056 case 2: 0057 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyConfidential); 0058 break; 0059 default: 0060 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPublic); 0061 break; 0062 } 0063 0064 // TODO: Attachments 0065 } 0066 0067 void KolabBase::setFields(const KContacts::Addressee *addressee) 0068 { 0069 // An addressee does not have a creation date, so somehow we should 0070 // make one, if this is a new entry 0071 0072 setUid(addressee->uid()); 0073 setBody(addressee->note()); 0074 setCategories(addressee->categories().join(QLatin1Char(','))); 0075 0076 // Set creation-time and last-modification-time 0077 const QString creationString = addressee->custom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate")); 0078 qCDebug(PIMKOLAB_LOG) << "Creation time string:" << creationString; 0079 QDateTime creationDate; 0080 if (creationString.isEmpty() && mTimeZone.isValid()) { 0081 creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone); 0082 qCDebug(PIMKOLAB_LOG) << "Creation date set to current time" << mTimeZone; 0083 } else { 0084 creationDate = stringToDateTime(creationString); 0085 qCDebug(PIMKOLAB_LOG) << "Creation date loaded"; 0086 } 0087 QDateTime modified; 0088 if (mTimeZone.isValid()) { 0089 modified = addressee->revision().toTimeZone(mTimeZone); 0090 } 0091 if (!modified.isValid()) { 0092 modified = QDateTime::currentDateTimeUtc(); 0093 } 0094 setLastModified(modified); 0095 if (modified < creationDate) { 0096 // It's not possible that the modification date is earlier than creation 0097 creationDate = modified; 0098 qCDebug(PIMKOLAB_LOG) << "Creation date set to modification date"; 0099 } 0100 setCreationDate(creationDate); 0101 const QString newCreationDate = dateTimeToString(creationDate); 0102 if (creationString != newCreationDate) { 0103 // We modified the creation date, so store it for future reference 0104 const_cast<KContacts::Addressee *>(addressee)->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), newCreationDate); 0105 qCDebug(PIMKOLAB_LOG) << "Creation date modified. New one:" << newCreationDate; 0106 } 0107 0108 switch (addressee->secrecy().type()) { 0109 case KContacts::Secrecy::Private: 0110 setSensitivity(Private); 0111 break; 0112 case KContacts::Secrecy::Confidential: 0113 setSensitivity(Confidential); 0114 break; 0115 default: 0116 setSensitivity(Public); 0117 } 0118 0119 // TODO: Attachments 0120 } 0121 0122 void KolabBase::saveTo(KContacts::Addressee *addressee) const 0123 { 0124 addressee->setUid(uid()); 0125 addressee->setNote(body()); 0126 addressee->setCategories(categories().split(QLatin1Char(','), Qt::SkipEmptyParts)); 0127 if (mTimeZone.isValid()) { 0128 addressee->setRevision(lastModified().toTimeZone(mTimeZone)); 0129 } 0130 addressee->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), dateTimeToString(creationDate())); 0131 0132 switch (sensitivity()) { 0133 case Private: 0134 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Private)); 0135 break; 0136 case Confidential: 0137 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Confidential)); 0138 break; 0139 default: 0140 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public)); 0141 break; 0142 } 0143 // TODO: Attachments 0144 } 0145 0146 void KolabBase::setFields(const KContacts::ContactGroup *contactGroup) 0147 { 0148 // A contactgroup does not have a creation date, so somehow we should 0149 // make one, if this is a new entry 0150 0151 setUid(contactGroup->id()); 0152 0153 // Set creation-time and last-modification-time 0154 QDateTime creationDate; 0155 if (mTimeZone.isValid()) { 0156 creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone); 0157 } 0158 qCDebug(PIMKOLAB_LOG) << "Creation date set to current time"; 0159 0160 QDateTime modified = QDateTime::currentDateTimeUtc(); 0161 setLastModified(modified); 0162 if (modified < creationDate) { 0163 // It's not possible that the modification date is earlier than creation 0164 creationDate = modified; 0165 qCDebug(PIMKOLAB_LOG) << "Creation date set to modification date"; 0166 } 0167 setCreationDate(creationDate); 0168 } 0169 0170 void KolabBase::saveTo(KContacts::ContactGroup *contactGroup) const 0171 { 0172 contactGroup->setId(uid()); 0173 } 0174 0175 void KolabBase::setUid(const QString &uid) 0176 { 0177 mUid = uid; 0178 } 0179 0180 QString KolabBase::uid() const 0181 { 0182 return mUid; 0183 } 0184 0185 void KolabBase::setBody(const QString &body) 0186 { 0187 mBody = body; 0188 } 0189 0190 QString KolabBase::body() const 0191 { 0192 return mBody; 0193 } 0194 0195 void KolabBase::setCategories(const QString &categories) 0196 { 0197 mCategories = categories; 0198 } 0199 0200 QString KolabBase::categories() const 0201 { 0202 return mCategories; 0203 } 0204 0205 void KolabBase::setCreationDate(const QDateTime &date) 0206 { 0207 mCreationDate = date; 0208 } 0209 0210 QDateTime KolabBase::creationDate() const 0211 { 0212 return mCreationDate; 0213 } 0214 0215 void KolabBase::setLastModified(const QDateTime &date) 0216 { 0217 mLastModified = date; 0218 } 0219 0220 QDateTime KolabBase::lastModified() const 0221 { 0222 return mLastModified; 0223 } 0224 0225 void KolabBase::setSensitivity(Sensitivity sensitivity) 0226 { 0227 mSensitivity = sensitivity; 0228 } 0229 0230 KolabBase::Sensitivity KolabBase::sensitivity() const 0231 { 0232 return mSensitivity; 0233 } 0234 0235 void KolabBase::setPilotSyncId(unsigned long id) 0236 { 0237 mHasPilotSyncId = true; 0238 mPilotSyncId = id; 0239 } 0240 0241 bool KolabBase::hasPilotSyncId() const 0242 { 0243 return mHasPilotSyncId; 0244 } 0245 0246 unsigned long KolabBase::pilotSyncId() const 0247 { 0248 return mPilotSyncId; 0249 } 0250 0251 void KolabBase::setPilotSyncStatus(int status) 0252 { 0253 mHasPilotSyncStatus = true; 0254 mPilotSyncStatus = status; 0255 } 0256 0257 bool KolabBase::hasPilotSyncStatus() const 0258 { 0259 return mHasPilotSyncStatus; 0260 } 0261 0262 int KolabBase::pilotSyncStatus() const 0263 { 0264 return mPilotSyncStatus; 0265 } 0266 0267 bool KolabBase::loadEmailAttribute(QDomElement &element, Email &email) 0268 { 0269 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { 0270 if (n.isComment()) { 0271 continue; 0272 } 0273 if (n.isElement()) { 0274 QDomElement e = n.toElement(); 0275 const QString tagName = e.tagName(); 0276 0277 if (tagName == QLatin1StringView("display-name")) { 0278 email.displayName = e.text(); 0279 } else if (tagName == QLatin1StringView("smtp-address")) { 0280 email.smtpAddress = e.text(); 0281 } else { 0282 // TODO: Unhandled tag - save for later storage 0283 qCDebug(PIMKOLAB_LOG) << "Warning: Unhandled tag" << e.tagName(); 0284 } 0285 } else { 0286 qCDebug(PIMKOLAB_LOG) << "Node is not a comment or an element???"; 0287 } 0288 } 0289 0290 return true; 0291 } 0292 0293 void KolabBase::saveEmailAttribute(QDomElement &element, const Email &email, const QString &tagName) const 0294 { 0295 QDomElement e = element.ownerDocument().createElement(tagName); 0296 element.appendChild(e); 0297 writeString(e, QStringLiteral("display-name"), email.displayName); 0298 writeString(e, QStringLiteral("smtp-address"), email.smtpAddress); 0299 } 0300 0301 bool KolabBase::loadAttribute(QDomElement &element) 0302 { 0303 const QString tagName = element.tagName(); 0304 switch (tagName[0].toLatin1()) { 0305 case 'u': 0306 if (tagName == QLatin1StringView("uid")) { 0307 setUid(element.text()); 0308 return true; 0309 } 0310 break; 0311 case 'b': 0312 if (tagName == QLatin1StringView("body")) { 0313 setBody(element.text()); 0314 return true; 0315 } 0316 break; 0317 case 'c': 0318 if (tagName == QLatin1StringView("categories")) { 0319 setCategories(element.text()); 0320 return true; 0321 } 0322 if (tagName == QLatin1StringView("creation-date")) { 0323 setCreationDate(stringToDateTime(element.text())); 0324 return true; 0325 } 0326 break; 0327 case 'l': 0328 if (tagName == QLatin1StringView("last-modification-date")) { 0329 setLastModified(stringToDateTime(element.text())); 0330 return true; 0331 } 0332 break; 0333 case 's': 0334 if (tagName == QLatin1StringView("sensitivity")) { 0335 setSensitivity(stringToSensitivity(element.text())); 0336 return true; 0337 } 0338 break; 0339 case 'p': 0340 if (tagName == QLatin1StringView("product-id")) { 0341 return true; // ignore this field 0342 } 0343 if (tagName == QLatin1StringView("pilot-sync-id")) { 0344 setPilotSyncId(element.text().toULong()); 0345 return true; 0346 } 0347 if (tagName == QLatin1StringView("pilot-sync-status")) { 0348 setPilotSyncStatus(element.text().toInt()); 0349 return true; 0350 } 0351 break; 0352 default: 0353 break; 0354 } 0355 return false; 0356 } 0357 0358 bool KolabBase::saveAttributes(QDomElement &element) const 0359 { 0360 writeString(element, QStringLiteral("product-id"), productID()); 0361 writeString(element, QStringLiteral("uid"), uid()); 0362 writeString(element, QStringLiteral("body"), body()); 0363 writeString(element, QStringLiteral("categories"), categories()); 0364 writeString(element, QStringLiteral("creation-date"), dateTimeToString(creationDate().toUTC())); 0365 writeString(element, QStringLiteral("last-modification-date"), dateTimeToString(lastModified().toUTC())); 0366 writeString(element, QStringLiteral("sensitivity"), sensitivityToString(sensitivity())); 0367 if (hasPilotSyncId()) { 0368 writeString(element, QStringLiteral("pilot-sync-id"), QString::number(pilotSyncId())); 0369 } 0370 if (hasPilotSyncStatus()) { 0371 writeString(element, QStringLiteral("pilot-sync-status"), QString::number(pilotSyncStatus())); 0372 } 0373 return true; 0374 } 0375 0376 bool KolabBase::load(const QString &xml) 0377 { 0378 const QDomDocument document = loadDocument(xml); 0379 if (document.isNull()) { 0380 return false; 0381 } 0382 // XML file loaded into tree. Now parse it 0383 return loadXML(document); 0384 } 0385 0386 QDomDocument KolabBase::loadDocument(const QString &xmlData) 0387 { 0388 QString errorMsg; 0389 int errorLine, errorColumn; 0390 QDomDocument document; 0391 bool ok = document.setContent(xmlData, &errorMsg, &errorLine, &errorColumn); 0392 0393 if (!ok) { 0394 qWarning("Error loading document: %s, line %d, column %d", qPrintable(errorMsg), errorLine, errorColumn); 0395 return {}; 0396 } 0397 0398 return document; 0399 } 0400 0401 QDomDocument KolabBase::domTree() 0402 { 0403 QDomDocument document; 0404 0405 const QString p = QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""); 0406 document.appendChild(document.createProcessingInstruction(QStringLiteral("xml"), p)); 0407 0408 return document; 0409 } 0410 0411 QString KolabBase::dateTimeToString(const QDateTime &time) 0412 { 0413 return time.toString(Qt::ISODate); 0414 } 0415 0416 QString KolabBase::dateToString(QDate date) 0417 { 0418 return date.toString(Qt::ISODate); 0419 } 0420 0421 QDateTime KolabBase::stringToDateTime(const QString &time) 0422 { 0423 return QDateTime::fromString(time, Qt::ISODate); 0424 } 0425 0426 QDate KolabBase::stringToDate(const QString &date) 0427 { 0428 return QDate::fromString(date, Qt::ISODate); 0429 } 0430 0431 QString KolabBase::sensitivityToString(Sensitivity s) 0432 { 0433 switch (s) { 0434 case Private: 0435 return QStringLiteral("private"); 0436 case Confidential: 0437 return QStringLiteral("confidential"); 0438 case Public: 0439 return QStringLiteral("public"); 0440 } 0441 0442 return QStringLiteral("What what what???"); 0443 } 0444 0445 KolabBase::Sensitivity KolabBase::stringToSensitivity(const QString &s) 0446 { 0447 if (s == QLatin1StringView("private")) { 0448 return Private; 0449 } 0450 if (s == QLatin1StringView("confidential")) { 0451 return Confidential; 0452 } 0453 return Public; 0454 } 0455 0456 QString KolabBase::colorToString(const QColor &color) 0457 { 0458 // Color is in the format "#RRGGBB" 0459 return color.name(); 0460 } 0461 0462 QColor KolabBase::stringToColor(const QString &s) 0463 { 0464 return QColor(s); 0465 } 0466 0467 void KolabBase::writeString(QDomElement &element, const QString &tag, const QString &tagString) 0468 { 0469 if (!tagString.isEmpty()) { 0470 QDomElement e = element.ownerDocument().createElement(tag); 0471 QDomText t = element.ownerDocument().createTextNode(tagString); 0472 e.appendChild(t); 0473 element.appendChild(e); 0474 } 0475 } 0476 0477 QDateTime KolabBase::localToUTC(const QDateTime &time) const 0478 { 0479 return time.toUTC(); 0480 } 0481 0482 QDateTime KolabBase::utcToLocal(const QDateTime &time) const 0483 { 0484 QDateTime dt = time; 0485 dt.setTimeZone(QTimeZone::utc()); 0486 return dt; 0487 }