1 /*
2 This file is part of the kolab resource - the implementation of the
3 Kolab storage format. See www.kolab.org for documentation on this.
4
5 SPDX-FileCopyrightText: 2004 Bo Thorsen <bo@sonofthor.dk>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10 #include "kolabbase.h"
11 #include "pimkolab_debug.h"
12
13 #include <KCalendarCore/Journal>
14 #include <KContacts/Addressee>
15 #include <KContacts/ContactGroup>
16
17 using namespace KolabV2;
18
KolabBase(const QString & tz)19 KolabBase::KolabBase(const QString &tz)
20 : mCreationDate(QDateTime::currentDateTime())
21 , mLastModified(QDateTime::currentDateTimeUtc())
22 , mSensitivity(Public)
23 , mHasPilotSyncId(false)
24 , mHasPilotSyncStatus(false)
25 {
26 // unlike the previously used KTimeZone here, QTimeZone defaults to local time zone if tz.isEmpty()
27 // we therefore force it to invalid, which however is unsafe to use (unlike KTimeZone)
28 // therefore all usage of mTimeZone must be preceded by a validity check
29 mTimeZone = tz.isEmpty() ? QTimeZone() : QTimeZone(tz.toUtf8());
30 }
31
~KolabBase()32 KolabBase::~KolabBase()
33 {
34 }
35
setFields(const KCalendarCore::Incidence::Ptr & incidence)36 void KolabBase::setFields(const KCalendarCore::Incidence::Ptr &incidence)
37 {
38 // So far unhandled KCalendarCore::IncidenceBase fields:
39 // mPilotID, mSyncStatus, mFloats
40
41 setUid(incidence->uid());
42 setBody(incidence->description());
43 setCategories(incidence->categoriesStr());
44 setCreationDate(localToUTC(incidence->created()));
45 setLastModified(incidence->lastModified());
46 setSensitivity(static_cast<Sensitivity>(incidence->secrecy()));
47 // TODO: Attachments
48 }
49
saveTo(const KCalendarCore::Incidence::Ptr & incidence) const50 void KolabBase::saveTo(const KCalendarCore::Incidence::Ptr &incidence) const
51 {
52 incidence->setUid(uid());
53 incidence->setDescription(body());
54 incidence->setCategories(categories());
55 incidence->setCreated(utcToLocal(creationDate()));
56 incidence->setLastModified(lastModified());
57 switch (sensitivity()) {
58 case 1:
59 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPrivate);
60 break;
61 case 2:
62 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyConfidential);
63 break;
64 default:
65 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPublic);
66 break;
67 }
68
69 // TODO: Attachments
70 }
71
setFields(const KContacts::Addressee * addressee)72 void KolabBase::setFields(const KContacts::Addressee *addressee)
73 {
74 // An addressee does not have a creation date, so somehow we should
75 // make one, if this is a new entry
76
77 setUid(addressee->uid());
78 setBody(addressee->note());
79 setCategories(addressee->categories().join(QLatin1Char(',')));
80
81 // Set creation-time and last-modification-time
82 const QString creationString = addressee->custom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"));
83 qCDebug(PIMKOLAB_LOG) << "Creation time string:" << creationString;
84 QDateTime creationDate;
85 if (creationString.isEmpty() && mTimeZone.isValid()) {
86 creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone);
87 qCDebug(PIMKOLAB_LOG) << "Creation date set to current time" << mTimeZone;
88 } else {
89 creationDate = stringToDateTime(creationString);
90 qCDebug(PIMKOLAB_LOG) << "Creation date loaded";
91 }
92 QDateTime modified;
93 if (mTimeZone.isValid()) {
94 modified = addressee->revision().toTimeZone(mTimeZone);
95 }
96 if (!modified.isValid()) {
97 modified = QDateTime::currentDateTimeUtc();
98 }
99 setLastModified(modified);
100 if (modified < creationDate) {
101 // It's not possible that the modification date is earlier than creation
102 creationDate = modified;
103 qCDebug(PIMKOLAB_LOG) << "Creation date set to modification date";
104 }
105 setCreationDate(creationDate);
106 const QString newCreationDate = dateTimeToString(creationDate);
107 if (creationString != newCreationDate) {
108 // We modified the creation date, so store it for future reference
109 const_cast<KContacts::Addressee *>(addressee)->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), newCreationDate);
110 qCDebug(PIMKOLAB_LOG) << "Creation date modified. New one:" << newCreationDate;
111 }
112
113 switch (addressee->secrecy().type()) {
114 case KContacts::Secrecy::Private:
115 setSensitivity(Private);
116 break;
117 case KContacts::Secrecy::Confidential:
118 setSensitivity(Confidential);
119 break;
120 default:
121 setSensitivity(Public);
122 }
123
124 // TODO: Attachments
125 }
126
saveTo(KContacts::Addressee * addressee) const127 void KolabBase::saveTo(KContacts::Addressee *addressee) const
128 {
129 addressee->setUid(uid());
130 addressee->setNote(body());
131 addressee->setCategories(categories().split(QLatin1Char(','), Qt::SkipEmptyParts));
132 if (mTimeZone.isValid()) {
133 addressee->setRevision(lastModified().toTimeZone(mTimeZone));
134 }
135 addressee->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), dateTimeToString(creationDate()));
136
137 switch (sensitivity()) {
138 case Private:
139 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Private));
140 break;
141 case Confidential:
142 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Confidential));
143 break;
144 default:
145 addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
146 break;
147 }
148 // TODO: Attachments
149 }
150
setFields(const KContacts::ContactGroup * contactGroup)151 void KolabBase::setFields(const KContacts::ContactGroup *contactGroup)
152 {
153 // A contactgroup does not have a creation date, so somehow we should
154 // make one, if this is a new entry
155
156 setUid(contactGroup->id());
157
158 // Set creation-time and last-modification-time
159 QDateTime creationDate;
160 if (mTimeZone.isValid()) {
161 creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone);
162 }
163 qCDebug(PIMKOLAB_LOG) << "Creation date set to current time";
164
165 QDateTime modified = QDateTime::currentDateTimeUtc();
166 setLastModified(modified);
167 if (modified < creationDate) {
168 // It's not possible that the modification date is earlier than creation
169 creationDate = modified;
170 qCDebug(PIMKOLAB_LOG) << "Creation date set to modification date";
171 }
172 setCreationDate(creationDate);
173 }
174
saveTo(KContacts::ContactGroup * contactGroup) const175 void KolabBase::saveTo(KContacts::ContactGroup *contactGroup) const
176 {
177 contactGroup->setId(uid());
178 }
179
setUid(const QString & uid)180 void KolabBase::setUid(const QString &uid)
181 {
182 mUid = uid;
183 }
184
uid() const185 QString KolabBase::uid() const
186 {
187 return mUid;
188 }
189
setBody(const QString & body)190 void KolabBase::setBody(const QString &body)
191 {
192 mBody = body;
193 }
194
body() const195 QString KolabBase::body() const
196 {
197 return mBody;
198 }
199
setCategories(const QString & categories)200 void KolabBase::setCategories(const QString &categories)
201 {
202 mCategories = categories;
203 }
204
categories() const205 QString KolabBase::categories() const
206 {
207 return mCategories;
208 }
209
setCreationDate(const QDateTime & date)210 void KolabBase::setCreationDate(const QDateTime &date)
211 {
212 mCreationDate = date;
213 }
214
creationDate() const215 QDateTime KolabBase::creationDate() const
216 {
217 return mCreationDate;
218 }
219
setLastModified(const QDateTime & date)220 void KolabBase::setLastModified(const QDateTime &date)
221 {
222 mLastModified = date;
223 }
224
lastModified() const225 QDateTime KolabBase::lastModified() const
226 {
227 return mLastModified;
228 }
229
setSensitivity(Sensitivity sensitivity)230 void KolabBase::setSensitivity(Sensitivity sensitivity)
231 {
232 mSensitivity = sensitivity;
233 }
234
sensitivity() const235 KolabBase::Sensitivity KolabBase::sensitivity() const
236 {
237 return mSensitivity;
238 }
239
setPilotSyncId(unsigned long id)240 void KolabBase::setPilotSyncId(unsigned long id)
241 {
242 mHasPilotSyncId = true;
243 mPilotSyncId = id;
244 }
245
hasPilotSyncId() const246 bool KolabBase::hasPilotSyncId() const
247 {
248 return mHasPilotSyncId;
249 }
250
pilotSyncId() const251 unsigned long KolabBase::pilotSyncId() const
252 {
253 return mPilotSyncId;
254 }
255
setPilotSyncStatus(int status)256 void KolabBase::setPilotSyncStatus(int status)
257 {
258 mHasPilotSyncStatus = true;
259 mPilotSyncStatus = status;
260 }
261
hasPilotSyncStatus() const262 bool KolabBase::hasPilotSyncStatus() const
263 {
264 return mHasPilotSyncStatus;
265 }
266
pilotSyncStatus() const267 int KolabBase::pilotSyncStatus() const
268 {
269 return mPilotSyncStatus;
270 }
271
loadEmailAttribute(QDomElement & element,Email & email)272 bool KolabBase::loadEmailAttribute(QDomElement &element, Email &email)
273 {
274 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
275 if (n.isComment()) {
276 continue;
277 }
278 if (n.isElement()) {
279 QDomElement e = n.toElement();
280 const QString tagName = e.tagName();
281
282 if (tagName == QLatin1String("display-name")) {
283 email.displayName = e.text();
284 } else if (tagName == QLatin1String("smtp-address")) {
285 email.smtpAddress = e.text();
286 } else {
287 // TODO: Unhandled tag - save for later storage
288 qCDebug(PIMKOLAB_LOG) << "Warning: Unhandled tag" << e.tagName();
289 }
290 } else {
291 qCDebug(PIMKOLAB_LOG) << "Node is not a comment or an element???";
292 }
293 }
294
295 return true;
296 }
297
saveEmailAttribute(QDomElement & element,const Email & email,const QString & tagName) const298 void KolabBase::saveEmailAttribute(QDomElement &element, const Email &email, const QString &tagName) const
299 {
300 QDomElement e = element.ownerDocument().createElement(tagName);
301 element.appendChild(e);
302 writeString(e, QStringLiteral("display-name"), email.displayName);
303 writeString(e, QStringLiteral("smtp-address"), email.smtpAddress);
304 }
305
loadAttribute(QDomElement & element)306 bool KolabBase::loadAttribute(QDomElement &element)
307 {
308 const QString tagName = element.tagName();
309 switch (tagName[0].toLatin1()) {
310 case 'u':
311 if (tagName == QLatin1String("uid")) {
312 setUid(element.text());
313 return true;
314 }
315 break;
316 case 'b':
317 if (tagName == QLatin1String("body")) {
318 setBody(element.text());
319 return true;
320 }
321 break;
322 case 'c':
323 if (tagName == QLatin1String("categories")) {
324 setCategories(element.text());
325 return true;
326 }
327 if (tagName == QLatin1String("creation-date")) {
328 setCreationDate(stringToDateTime(element.text()));
329 return true;
330 }
331 break;
332 case 'l':
333 if (tagName == QLatin1String("last-modification-date")) {
334 setLastModified(stringToDateTime(element.text()));
335 return true;
336 }
337 break;
338 case 's':
339 if (tagName == QLatin1String("sensitivity")) {
340 setSensitivity(stringToSensitivity(element.text()));
341 return true;
342 }
343 break;
344 case 'p':
345 if (tagName == QLatin1String("product-id")) {
346 return true; // ignore this field
347 }
348 if (tagName == QLatin1String("pilot-sync-id")) {
349 setPilotSyncId(element.text().toULong());
350 return true;
351 }
352 if (tagName == QLatin1String("pilot-sync-status")) {
353 setPilotSyncStatus(element.text().toInt());
354 return true;
355 }
356 break;
357 default:
358 break;
359 }
360 return false;
361 }
362
saveAttributes(QDomElement & element) const363 bool KolabBase::saveAttributes(QDomElement &element) const
364 {
365 writeString(element, QStringLiteral("product-id"), productID());
366 writeString(element, QStringLiteral("uid"), uid());
367 writeString(element, QStringLiteral("body"), body());
368 writeString(element, QStringLiteral("categories"), categories());
369 writeString(element, QStringLiteral("creation-date"), dateTimeToString(creationDate().toUTC()));
370 writeString(element, QStringLiteral("last-modification-date"), dateTimeToString(lastModified().toUTC()));
371 writeString(element, QStringLiteral("sensitivity"), sensitivityToString(sensitivity()));
372 if (hasPilotSyncId()) {
373 writeString(element, QStringLiteral("pilot-sync-id"), QString::number(pilotSyncId()));
374 }
375 if (hasPilotSyncStatus()) {
376 writeString(element, QStringLiteral("pilot-sync-status"), QString::number(pilotSyncStatus()));
377 }
378 return true;
379 }
380
load(const QString & xml)381 bool KolabBase::load(const QString &xml)
382 {
383 const QDomDocument document = loadDocument(xml);
384 if (document.isNull()) {
385 return false;
386 }
387 // XML file loaded into tree. Now parse it
388 return loadXML(document);
389 }
390
loadDocument(const QString & xmlData)391 QDomDocument KolabBase::loadDocument(const QString &xmlData)
392 {
393 QString errorMsg;
394 int errorLine, errorColumn;
395 QDomDocument document;
396 bool ok = document.setContent(xmlData, &errorMsg, &errorLine, &errorColumn);
397
398 if (!ok) {
399 qWarning("Error loading document: %s, line %d, column %d", qPrintable(errorMsg), errorLine, errorColumn);
400 return QDomDocument();
401 }
402
403 return document;
404 }
405
domTree()406 QDomDocument KolabBase::domTree()
407 {
408 QDomDocument document;
409
410 const QString p = QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"");
411 document.appendChild(document.createProcessingInstruction(QStringLiteral("xml"), p));
412
413 return document;
414 }
415
dateTimeToString(const QDateTime & time)416 QString KolabBase::dateTimeToString(const QDateTime &time)
417 {
418 return time.toString(Qt::ISODate);
419 }
420
dateToString(QDate date)421 QString KolabBase::dateToString(QDate date)
422 {
423 return date.toString(Qt::ISODate);
424 }
425
stringToDateTime(const QString & time)426 QDateTime KolabBase::stringToDateTime(const QString &time)
427 {
428 return QDateTime::fromString(time, Qt::ISODate);
429 }
430
stringToDate(const QString & date)431 QDate KolabBase::stringToDate(const QString &date)
432 {
433 return QDate::fromString(date, Qt::ISODate);
434 }
435
sensitivityToString(Sensitivity s)436 QString KolabBase::sensitivityToString(Sensitivity s)
437 {
438 switch (s) {
439 case Private:
440 return QStringLiteral("private");
441 case Confidential:
442 return QStringLiteral("confidential");
443 case Public:
444 return QStringLiteral("public");
445 }
446
447 return QStringLiteral("What what what???");
448 }
449
stringToSensitivity(const QString & s)450 KolabBase::Sensitivity KolabBase::stringToSensitivity(const QString &s)
451 {
452 if (s == QLatin1String("private")) {
453 return Private;
454 }
455 if (s == QLatin1String("confidential")) {
456 return Confidential;
457 }
458 return Public;
459 }
460
colorToString(const QColor & color)461 QString KolabBase::colorToString(const QColor &color)
462 {
463 // Color is in the format "#RRGGBB"
464 return color.name();
465 }
466
stringToColor(const QString & s)467 QColor KolabBase::stringToColor(const QString &s)
468 {
469 return QColor(s);
470 }
471
writeString(QDomElement & element,const QString & tag,const QString & tagString)472 void KolabBase::writeString(QDomElement &element, const QString &tag, const QString &tagString)
473 {
474 if (!tagString.isEmpty()) {
475 QDomElement e = element.ownerDocument().createElement(tag);
476 QDomText t = element.ownerDocument().createTextNode(tagString);
477 e.appendChild(t);
478 element.appendChild(e);
479 }
480 }
481
localToUTC(const QDateTime & time) const482 QDateTime KolabBase::localToUTC(const QDateTime &time) const
483 {
484 return time.toUTC();
485 }
486
utcToLocal(const QDateTime & time) const487 QDateTime KolabBase::utcToLocal(const QDateTime &time) const
488 {
489 QDateTime dt = time;
490 dt.setTimeSpec(Qt::UTC);
491 return dt;
492 }
493