File indexing completed on 2024-11-24 04:44:11
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Christian Mollekopf <mollekopf@kolabsys.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-3.0-or-later 0005 */ 0006 0007 #include "kabcconversion.h" 0008 0009 #include "commonconversion.h" 0010 #include "pimkolab_debug.h" 0011 #include <QBuffer> 0012 #include <QImageReader> 0013 namespace Kolab 0014 { 0015 namespace Conversion 0016 { 0017 // The following was copied from kdepim/libkleo/kleo/enum.h,.cpp 0018 enum CryptoMessageFormat { 0019 InlineOpenPGPFormat = 1, 0020 OpenPGPMIMEFormat = 2, 0021 SMIMEFormat = 4, 0022 SMIMEOpaqueFormat = 8, 0023 AnyOpenPGP = InlineOpenPGPFormat | OpenPGPMIMEFormat, 0024 AnySMIME = SMIMEOpaqueFormat | SMIMEFormat, 0025 AutoFormat = AnyOpenPGP | AnySMIME 0026 }; 0027 0028 enum EncryptionPreference { 0029 UnknownPreference = 0, 0030 NeverEncrypt = 1, 0031 AlwaysEncrypt = 2, 0032 AlwaysEncryptIfPossible = 3, 0033 AlwaysAskForEncryption = 4, 0034 AskWheneverPossible = 5, 0035 MaxEncryptionPreference = AskWheneverPossible 0036 }; 0037 0038 enum SigningPreference { 0039 UnknownSigningPreference = 0, 0040 NeverSign = 1, 0041 AlwaysSign = 2, 0042 AlwaysSignIfPossible = 3, 0043 AlwaysAskForSigning = 4, 0044 AskSigningWheneverPossible = 5, 0045 MaxSigningPreference = AskSigningWheneverPossible 0046 }; 0047 0048 static const struct { 0049 CryptoMessageFormat format; 0050 const char *displayName; 0051 const char *configName; 0052 } cryptoMessageFormats[] = { 0053 {InlineOpenPGPFormat, ("Inline OpenPGP (deprecated)"), "inline openpgp"}, 0054 {OpenPGPMIMEFormat, ("OpenPGP/MIME"), "openpgp/mime"}, 0055 {SMIMEFormat, ("S/MIME"), "s/mime"}, 0056 {SMIMEOpaqueFormat, ("S/MIME Opaque"), "s/mime opaque"}, 0057 }; 0058 static const unsigned int numCryptoMessageFormats = sizeof cryptoMessageFormats / sizeof *cryptoMessageFormats; 0059 0060 const char *cryptoMessageFormatToString(CryptoMessageFormat f) 0061 { 0062 if (f == AutoFormat) { 0063 return "auto"; 0064 } 0065 for (unsigned int i = 0; i < numCryptoMessageFormats; ++i) { 0066 if (f == cryptoMessageFormats[i].format) { 0067 return cryptoMessageFormats[i].configName; 0068 } 0069 } 0070 return nullptr; 0071 } 0072 0073 QStringList cryptoMessageFormatsToStringList(unsigned int f) 0074 { 0075 QStringList result; 0076 for (unsigned int i = 0; i < numCryptoMessageFormats; ++i) { 0077 if (f & cryptoMessageFormats[i].format) { 0078 result.push_back(QLatin1StringView(cryptoMessageFormats[i].configName)); 0079 } 0080 } 0081 return result; 0082 } 0083 0084 CryptoMessageFormat stringToCryptoMessageFormat(const QString &s) 0085 { 0086 const QString t = s.toLower(); 0087 for (unsigned int i = 0; i < numCryptoMessageFormats; ++i) { 0088 if (t == QLatin1StringView(cryptoMessageFormats[i].configName)) { 0089 return cryptoMessageFormats[i].format; 0090 } 0091 } 0092 return AutoFormat; 0093 } 0094 0095 unsigned int stringListToCryptoMessageFormats(const QStringList &sl) 0096 { 0097 unsigned int result = 0; 0098 const QStringList::const_iterator end(sl.end()); 0099 for (QStringList::const_iterator it = sl.begin(); it != end; ++it) { 0100 result |= stringToCryptoMessageFormat(*it); 0101 } 0102 return result; 0103 } 0104 0105 // For the config values used below, see also kaddressbook/editors/cryptowidget.cpp 0106 0107 const char *encryptionPreferenceToString(EncryptionPreference pref) 0108 { 0109 switch (pref) { 0110 case UnknownPreference: 0111 return nullptr; 0112 case NeverEncrypt: 0113 return "never"; 0114 case AlwaysEncrypt: 0115 return "always"; 0116 case AlwaysEncryptIfPossible: 0117 return "alwaysIfPossible"; 0118 case AlwaysAskForEncryption: 0119 return "askAlways"; 0120 case AskWheneverPossible: 0121 return "askWhenPossible"; 0122 } 0123 return nullptr; // keep the compiler happy 0124 } 0125 0126 EncryptionPreference stringToEncryptionPreference(const QString &str) 0127 { 0128 if (str == QLatin1StringView("never")) { 0129 return NeverEncrypt; 0130 } else if (str == QLatin1StringView("always")) { 0131 return AlwaysEncrypt; 0132 } else if (str == QLatin1StringView("alwaysIfPossible")) { 0133 return AlwaysEncryptIfPossible; 0134 } else if (str == QLatin1StringView("askAlways")) { 0135 return AlwaysAskForEncryption; 0136 } else if (str == QLatin1StringView("askWhenPossible")) { 0137 return AskWheneverPossible; 0138 } 0139 return UnknownPreference; 0140 } 0141 0142 const char *signingPreferenceToString(SigningPreference pref) 0143 { 0144 switch (pref) { 0145 case UnknownSigningPreference: 0146 return nullptr; 0147 case NeverSign: 0148 return "never"; 0149 case AlwaysSign: 0150 return "always"; 0151 case AlwaysSignIfPossible: 0152 return "alwaysIfPossible"; 0153 case AlwaysAskForSigning: 0154 return "askAlways"; 0155 case AskSigningWheneverPossible: 0156 return "askWhenPossible"; 0157 } 0158 return nullptr; // keep the compiler happy 0159 } 0160 0161 SigningPreference stringToSigningPreference(const QString &str) 0162 { 0163 if (str == QLatin1StringView("never")) { 0164 return NeverSign; 0165 } 0166 if (str == QLatin1StringView("always")) { 0167 return AlwaysSign; 0168 } 0169 if (str == QLatin1StringView("alwaysIfPossible")) { 0170 return AlwaysSignIfPossible; 0171 } 0172 if (str == QLatin1StringView("askAlways")) { 0173 return AlwaysAskForSigning; 0174 } 0175 if (str == QLatin1StringView("askWhenPossible")) { 0176 return AskSigningWheneverPossible; 0177 } 0178 return UnknownSigningPreference; 0179 } 0180 0181 int fromAddressType(int kabcType, bool &pref) 0182 { 0183 int type = 0; 0184 if (kabcType & KContacts::Address::Dom) { 0185 qCWarning(PIMKOLAB_LOG) << "domestic address is not supported"; 0186 } 0187 if (kabcType & KContacts::Address::Intl) { 0188 qCWarning(PIMKOLAB_LOG) << "international address is not supported"; 0189 } 0190 if (kabcType & KContacts::Address::Pref) { 0191 pref = true; 0192 } 0193 if (kabcType & KContacts::Address::Postal) { 0194 qCWarning(PIMKOLAB_LOG) << "postal address is not supported"; 0195 } 0196 if (kabcType & KContacts::Address::Parcel) { 0197 qCWarning(PIMKOLAB_LOG) << "parcel is not supported"; 0198 } 0199 if (kabcType & KContacts::Address::Home) { 0200 type |= Kolab::Address::Home; 0201 } 0202 if (kabcType & KContacts::Address::Work) { 0203 type |= Kolab::Address::Work; 0204 } 0205 return type; 0206 } 0207 0208 KContacts::Address::Type toAddressType(int types, bool pref) 0209 { 0210 KContacts::Address::Type type = {}; 0211 if (pref) { 0212 type |= KContacts::Address::Pref; 0213 } 0214 if (types & Kolab::Address::Home) { 0215 type |= KContacts::Address::Home; 0216 } 0217 if (types & Kolab::Address::Work) { 0218 type |= KContacts::Address::Work; 0219 } 0220 return type; 0221 } 0222 0223 int fromPhoneType(int kabcType, bool &pref) 0224 { 0225 int type = 0; 0226 if (kabcType & KContacts::PhoneNumber::Home) { 0227 type |= Kolab::Telephone::Home; 0228 } 0229 if (kabcType & KContacts::PhoneNumber::Work) { 0230 type |= Kolab::Telephone::Work; 0231 } 0232 if (kabcType & KContacts::PhoneNumber::Msg) { 0233 type |= Kolab::Telephone::Text; 0234 } 0235 if (kabcType & KContacts::PhoneNumber::Pref) { 0236 pref = true; 0237 } 0238 if (kabcType & KContacts::PhoneNumber::Voice) { 0239 type |= Kolab::Telephone::Voice; 0240 } 0241 if (kabcType & KContacts::PhoneNumber::Fax) { 0242 type |= Kolab::Telephone::Fax; 0243 } 0244 if (kabcType & KContacts::PhoneNumber::Cell) { 0245 type |= Kolab::Telephone::Cell; 0246 } 0247 if (kabcType & KContacts::PhoneNumber::Video) { 0248 type |= Kolab::Telephone::Video; 0249 } 0250 if (kabcType & KContacts::PhoneNumber::Bbs) { 0251 qCWarning(PIMKOLAB_LOG) << "mailbox number is not supported"; 0252 } 0253 if (kabcType & KContacts::PhoneNumber::Modem) { 0254 qCWarning(PIMKOLAB_LOG) << "modem is not supported"; 0255 } 0256 if (kabcType & KContacts::PhoneNumber::Car) { 0257 type |= Kolab::Telephone::Car; 0258 } 0259 if (kabcType & KContacts::PhoneNumber::Isdn) { 0260 qCWarning(PIMKOLAB_LOG) << "isdn number is not supported"; 0261 } 0262 if (kabcType & KContacts::PhoneNumber::Pcs) { 0263 type |= Kolab::Telephone::Text; 0264 } 0265 if (kabcType & KContacts::PhoneNumber::Pager) { 0266 type |= Kolab::Telephone::Pager; 0267 } 0268 return type; 0269 } 0270 0271 KContacts::PhoneNumber::Type toPhoneType(int types, bool pref) 0272 { 0273 KContacts::PhoneNumber::Type type = {}; 0274 if (types & Kolab::Telephone::Home) { 0275 type |= KContacts::PhoneNumber::Home; 0276 } 0277 if (types & Kolab::Telephone::Work) { 0278 type |= KContacts::PhoneNumber::Work; 0279 } 0280 if (types & Kolab::Telephone::Text) { 0281 type |= KContacts::PhoneNumber::Msg; 0282 } 0283 if (pref) { 0284 type |= KContacts::PhoneNumber::Pref; 0285 } 0286 if (types & Kolab::Telephone::Voice) { 0287 type |= KContacts::PhoneNumber::Voice; 0288 } 0289 if (types & Kolab::Telephone::Fax) { 0290 type |= KContacts::PhoneNumber::Fax; 0291 } 0292 if (types & Kolab::Telephone::Cell) { 0293 type |= KContacts::PhoneNumber::Cell; 0294 } 0295 if (types & Kolab::Telephone::Video) { 0296 type |= KContacts::PhoneNumber::Video; 0297 } 0298 if (types & Kolab::Telephone::Car) { 0299 type |= KContacts::PhoneNumber::Car; 0300 } 0301 if (types & Kolab::Telephone::Text) { 0302 type |= KContacts::PhoneNumber::Pcs; 0303 } 0304 if (types & Kolab::Telephone::Pager) { 0305 type |= KContacts::PhoneNumber::Pager; 0306 } 0307 return type; 0308 } 0309 0310 std::string fromPicture(const KContacts::Picture &pic, std::string &mimetype) 0311 { 0312 QByteArray input; 0313 QBuffer buffer(&input); 0314 buffer.open(QIODevice::WriteOnly); 0315 QImage img; 0316 0317 if (pic.isIntern()) { 0318 if (!pic.data().isNull()) { 0319 img = pic.data(); 0320 } 0321 } else if (!pic.url().isEmpty()) { 0322 qCWarning(PIMKOLAB_LOG) << "external pictures are currently not supported"; 0323 // FIXME add kio support to libcalendaring or use libcurl 0324 // if ( KIO::NetAccess::download( pic.url(), tmpFile, 0 /*no widget known*/ ) ) { 0325 // img.load( tmpFile ); 0326 // KIO::NetAccess::removeTempFile( tmpFile ); 0327 // } 0328 } 0329 if (img.isNull()) { 0330 qCCritical(PIMKOLAB_LOG) << "invalid picture"; 0331 return {}; 0332 } 0333 if (!img.hasAlphaChannel()) { 0334 if (!img.save(&buffer, "JPEG")) { 0335 qCCritical(PIMKOLAB_LOG) << "error on jpeg save"; 0336 return {}; 0337 } 0338 mimetype = "image/jpeg"; 0339 } else { 0340 if (!img.save(&buffer, "PNG")) { 0341 qCCritical(PIMKOLAB_LOG) << "error on png save"; 0342 return {}; 0343 } 0344 mimetype = "image/png"; 0345 } 0346 return std::string(input.data(), input.size()); 0347 } 0348 0349 KContacts::Picture toPicture(const std::string &data, const std::string &mimetype) 0350 { 0351 QImage img; 0352 bool ret = false; 0353 QByteArray type(mimetype.data(), mimetype.size()); 0354 type = type.split('/').last(); // extract "jpeg" from "image/jpeg" 0355 if (QImageReader::supportedImageFormats().contains(type)) { 0356 ret = img.loadFromData(QByteArray::fromRawData(data.data(), data.size()), type.constData()); 0357 } else { 0358 ret = img.loadFromData(QByteArray::fromRawData(data.data(), data.size())); 0359 } 0360 if (!ret) { 0361 qCWarning(PIMKOLAB_LOG) << "failed to load picture"; 0362 return {}; 0363 } 0364 0365 KContacts::Picture logo(img); 0366 if (logo.isEmpty()) { 0367 qCWarning(PIMKOLAB_LOG) << "failed to read picture"; 0368 return {}; 0369 } 0370 return logo; 0371 } 0372 0373 template<typename T> 0374 void setCustom(const std::string &value, const std::string &id, T &object) 0375 { 0376 std::vector<Kolab::CustomProperty> properties = object.customProperties(); 0377 properties.emplace_back(id, value); 0378 object.setCustomProperties(properties); 0379 } 0380 0381 template<typename T> 0382 std::string getCustom(const std::string &id, T &object) 0383 { 0384 const std::vector<Kolab::CustomProperty> &properties = object.customProperties(); 0385 for (const Kolab::CustomProperty &prop : properties) { 0386 if (prop.identifier == id) { 0387 return prop.value; 0388 } 0389 } 0390 return {}; 0391 } 0392 0393 static QString emailTypesToStringList(int emailTypes) 0394 { 0395 QStringList types; 0396 if (emailTypes & Kolab::Email::Home) { 0397 types << QStringLiteral("home"); 0398 } 0399 if (emailTypes & Kolab::Email::Work) { 0400 types << QStringLiteral("work"); 0401 } 0402 return types.join(QLatin1Char(',')); 0403 } 0404 0405 static int emailTypesFromStringlist(const QString &types) 0406 { 0407 int emailTypes = Kolab::Email::NoType; 0408 if (types.contains(QLatin1StringView("home"))) { 0409 emailTypes |= Kolab::Email::Home; 0410 } 0411 if (types.contains(QLatin1StringView("work"))) { 0412 emailTypes |= Kolab::Email::Work; 0413 } 0414 return emailTypes; 0415 } 0416 0417 KContacts::Addressee toKABC(const Kolab::Contact &contact) 0418 { 0419 KContacts::Addressee addressee; 0420 addressee.setUid(fromStdString(contact.uid())); 0421 addressee.setCategories(toStringList(contact.categories())); 0422 // addressee.setName(fromStdString(contact.name()));//This one is only for compatibility (and results in a non-existing name property) 0423 addressee.setFormattedName(fromStdString(contact.name())); // This on corresponds to fn 0424 0425 const Kolab::NameComponents &nc = contact.nameComponents(); 0426 if (!nc.surnames().empty()) { 0427 addressee.setFamilyName(fromStdString(nc.surnames().front())); 0428 } 0429 if (!nc.given().empty()) { 0430 addressee.setGivenName(fromStdString(nc.given().front())); 0431 } 0432 if (!nc.additional().empty()) { 0433 addressee.setAdditionalName(fromStdString(nc.additional().front())); 0434 } 0435 if (!nc.prefixes().empty()) { 0436 addressee.setPrefix(fromStdString(nc.prefixes().front())); 0437 } 0438 if (!nc.suffixes().empty()) { 0439 addressee.setSuffix(fromStdString(nc.suffixes().front())); 0440 } 0441 0442 addressee.setNote(fromStdString(contact.note())); 0443 0444 addressee.setSecrecy(KContacts::Secrecy::Public); // We don't have any privacy setting in xCard 0445 0446 QString preferredEmail; 0447 0448 if (!contact.emailAddresses().empty()) { 0449 QStringList emails; 0450 const auto contactEmailAddresses{contact.emailAddresses()}; 0451 emails.reserve(contactEmailAddresses.size()); 0452 for (const Kolab::Email &email : contactEmailAddresses) { 0453 emails << fromStdString(email.address()); 0454 const QString types = emailTypesToStringList(email.types()); 0455 if (!types.isEmpty()) { 0456 addressee.insertCustom(QStringLiteral("KOLAB"), QStringLiteral("EmailTypes%1").arg(fromStdString(email.address())), types); 0457 } 0458 } 0459 addressee.setEmails(emails); 0460 if ((contact.emailAddressPreferredIndex() >= 0) && (contact.emailAddressPreferredIndex() < static_cast<int>(contact.emailAddresses().size()))) { 0461 preferredEmail = fromStdString(contact.emailAddresses().at(contact.emailAddressPreferredIndex()).address()); 0462 } else { 0463 preferredEmail = fromStdString(contact.emailAddresses().at(0).address()); 0464 } 0465 KContacts::Email email(preferredEmail); 0466 email.setPreferred(true); 0467 addressee.addEmail(email); 0468 } 0469 0470 if (!contact.freeBusyUrl().empty()) { 0471 if (preferredEmail.isEmpty()) { 0472 qCCritical(PIMKOLAB_LOG) << "f/b url is set but no email address available, skipping"; 0473 } else { 0474 addressee.insertCustom(QStringLiteral("KOLAB"), QStringLiteral("FreebusyUrl"), fromStdString(contact.freeBusyUrl())); 0475 } 0476 } 0477 0478 if (!contact.nickNames().empty()) { 0479 addressee.setNickName(fromStdString(contact.nickNames().at(0))); // TODO support multiple 0480 } 0481 0482 if (contact.bDay().isValid()) { 0483 addressee.setBirthday(toDate(contact.bDay())); 0484 } 0485 if (!contact.titles().empty()) { 0486 addressee.setTitle(fromStdString(contact.titles().at(0))); // TODO support multiple 0487 } 0488 if (!contact.urls().empty()) { 0489 KContacts::ResourceLocatorUrl url; 0490 url.setUrl(QUrl(fromStdString(contact.urls().at(0).url()))); // TODO support multiple 0491 addressee.setUrl(url); 0492 const auto urls{contact.urls()}; 0493 for (const Kolab::Url &u : urls) { 0494 if (u.type() == Kolab::Url::Blog) { 0495 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("BlogFeed"), fromStdString(u.url())); 0496 } 0497 } 0498 } 0499 0500 if (!contact.affiliations().empty()) { 0501 // Storing only a const reference leads to segfaults. No idea why. 0502 const Kolab::Affiliation aff = contact.affiliations().at(0); // TODO support multiple 0503 if (!aff.organisation().empty()) { 0504 addressee.setOrganization(fromStdString(aff.organisation())); 0505 } 0506 if (!aff.organisationalUnits().empty()) { 0507 addressee.setDepartment(fromStdString(aff.organisationalUnits().at(0))); // TODO support multiple 0508 } 0509 if (!aff.roles().empty()) { 0510 addressee.setRole(fromStdString(aff.roles().at(0))); // TODO support multiple 0511 } 0512 if (!aff.logo().empty()) { 0513 addressee.setLogo(toPicture(aff.logo(), aff.logoMimetype())); 0514 } 0515 const auto affRelateds{aff.relateds()}; 0516 for (const Kolab::Related &related : affRelateds) { 0517 if (related.type() != Kolab::Related::Text) { 0518 qCCritical(PIMKOLAB_LOG) << "invalid relation type"; 0519 continue; 0520 } 0521 if (related.relationTypes() & Kolab::Related::Assistant) { 0522 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-AssistantsName"), fromStdString(related.text())); 0523 } 0524 if (related.relationTypes() & Kolab::Related::Manager) { 0525 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-ManagersName"), fromStdString(related.text())); 0526 } 0527 } 0528 const auto addresses{aff.addresses()}; 0529 for (const Kolab::Address &address : addresses) { 0530 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Office"), fromStdString(address.label())); // TODO support proper addresses 0531 } 0532 } 0533 const std::string &prof = getCustom("X-Profession", contact); 0534 if (!prof.empty()) { 0535 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Profession"), fromStdString(prof)); 0536 } 0537 0538 const std::string &adrBook = getCustom("X-AddressBook", contact); 0539 if (!adrBook.empty()) { 0540 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-AddressBook"), fromStdString(prof)); 0541 } 0542 0543 if (!contact.photo().empty()) { 0544 addressee.setPhoto(toPicture(contact.photo(), contact.photoMimetype())); 0545 } 0546 0547 if (!contact.telephones().empty()) { 0548 int index = 0; 0549 const auto contactTelephones{contact.telephones()}; 0550 for (const Kolab::Telephone &tel : contactTelephones) { 0551 bool pref = false; 0552 if (index == contact.telephonesPreferredIndex()) { 0553 pref = true; 0554 } 0555 KContacts::PhoneNumber number(fromStdString(tel.number()), toPhoneType(tel.types(), pref)); 0556 index++; 0557 addressee.insertPhoneNumber(number); 0558 } 0559 } 0560 0561 if (!contact.addresses().empty()) { 0562 int index = 0; 0563 const auto contactAddresses{contact.addresses()}; 0564 for (const Kolab::Address &a : contactAddresses) { 0565 bool pref = false; 0566 if (index == contact.addressPreferredIndex()) { 0567 pref = true; 0568 } 0569 KContacts::Address adr(toAddressType(a.types(), pref)); 0570 adr.setLabel(fromStdString(a.label())); 0571 adr.setStreet(fromStdString(a.street())); 0572 adr.setLocality(fromStdString(a.locality())); 0573 adr.setRegion(fromStdString(a.region())); 0574 adr.setPostalCode(fromStdString(a.code())); 0575 adr.setCountry(fromStdString(a.country())); 0576 0577 index++; 0578 addressee.insertAddress(adr); 0579 } 0580 } 0581 0582 if (contact.anniversary().isValid()) { 0583 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Anniversary"), toDate(contact.anniversary()).toString(Qt::ISODate)); 0584 } 0585 0586 if (!contact.imAddresses().empty()) { 0587 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-IMAddress"), fromStdString(contact.imAddresses()[0])); // TODO support multiple 0588 } 0589 0590 if (!contact.relateds().empty()) { 0591 const auto relateds{contact.relateds()}; 0592 for (const Kolab::Related &rel : relateds) { 0593 if (rel.type() != Kolab::Related::Text) { 0594 qCCritical(PIMKOLAB_LOG) << "relation type not supported"; 0595 continue; 0596 } 0597 if (rel.relationTypes() & Kolab::Related::Spouse) { 0598 addressee.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-SpousesName"), fromStdString(rel.text())); // TODO support multiple 0599 } else { 0600 qCWarning(PIMKOLAB_LOG) << "relation not supported"; 0601 continue; 0602 } 0603 } 0604 } 0605 0606 return addressee; 0607 } 0608 0609 Kolab::Contact fromKABC(const KContacts::Addressee &addressee) 0610 { 0611 int prefNum = -1; 0612 int prefCounter = -1; 0613 Kolab::Contact c; 0614 c.setUid(toStdString(addressee.uid())); 0615 c.setCategories(fromStringList(addressee.categories())); 0616 c.setName(toStdString(addressee.formattedName())); 0617 Kolab::NameComponents nc; 0618 nc.setSurnames(std::vector<std::string>() << toStdString(addressee.familyName())); 0619 nc.setGiven(std::vector<std::string>() << toStdString(addressee.givenName())); 0620 nc.setAdditional(std::vector<std::string>() << toStdString(addressee.additionalName())); 0621 nc.setPrefixes(std::vector<std::string>() << toStdString(addressee.prefix())); 0622 nc.setSuffixes(std::vector<std::string>() << toStdString(addressee.suffix())); 0623 c.setNameComponents(nc); 0624 0625 c.setNote(toStdString(addressee.note())); 0626 c.setFreeBusyUrl(toStdString(addressee.custom(QStringLiteral("KOLAB"), QStringLiteral("FreebusyUrl")))); 0627 0628 if (!addressee.title().isEmpty()) { 0629 c.setTitles(std::vector<std::string>() << toStdString(addressee.title())); 0630 } 0631 0632 Kolab::Affiliation businessAff; 0633 businessAff.setOrganisation(toStdString(addressee.organization())); 0634 if (!addressee.department().isEmpty()) { 0635 qCDebug(PIMKOLAB_LOG) << addressee.department() << addressee.department().toLatin1() << addressee.department().toUtf8(); 0636 businessAff.setOrganisationalUnits(std::vector<std::string>() << toStdString(addressee.department())); 0637 } 0638 0639 if (!addressee.logo().isEmpty()) { 0640 std::string logoMimetype; 0641 const std::string &logo = fromPicture(addressee.logo(), logoMimetype); 0642 businessAff.setLogo(logo, logoMimetype); 0643 } 0644 if (!addressee.role().isEmpty()) { 0645 businessAff.setRoles(std::vector<std::string>() << toStdString(addressee.role())); 0646 } 0647 const QString &office = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Office")); 0648 if (!office.isEmpty()) { 0649 Kolab::Address a; 0650 a.setTypes(Kolab::Address::Work); 0651 a.setLabel(toStdString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Office")))); 0652 businessAff.setAddresses(std::vector<Kolab::Address>() << a); 0653 } 0654 0655 std::vector<Kolab::Related> relateds; 0656 const QString &manager = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-ManagersName")); 0657 if (!manager.isEmpty()) { 0658 relateds.emplace_back(Kolab::Related::Text, toStdString(manager), Kolab::Related::Manager); 0659 } 0660 const QString &assistant = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-AssistantsName")); 0661 if (!assistant.isEmpty()) { 0662 relateds.emplace_back(Kolab::Related::Text, toStdString(assistant), Kolab::Related::Assistant); 0663 } 0664 if (!relateds.empty()) { 0665 businessAff.setRelateds(relateds); 0666 } 0667 if (!(businessAff == Kolab::Affiliation())) { 0668 c.setAffiliations(std::vector<Kolab::Affiliation>() << businessAff); 0669 } 0670 0671 std::vector<Kolab::Url> urls; 0672 const QUrl url{addressee.url().url()}; 0673 if (!url.isEmpty()) { 0674 urls.emplace_back(toStdString(url.url())); 0675 } 0676 const QString &blogUrl = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("BlogFeed")); 0677 if (!blogUrl.isEmpty()) { 0678 urls.emplace_back(toStdString(blogUrl), Kolab::Url::Blog); 0679 } 0680 c.setUrls(urls); 0681 0682 std::vector<Kolab::Address> addresses; 0683 prefNum = -1; 0684 prefCounter = -1; 0685 const auto contactAddresses{addressee.addresses()}; 0686 for (const KContacts::Address &a : contactAddresses) { 0687 Kolab::Address adr; 0688 bool pref = false; 0689 adr.setTypes(fromAddressType(a.type(), pref)); 0690 prefCounter++; 0691 if (pref) { 0692 prefNum = prefCounter; 0693 } 0694 adr.setLabel(toStdString(a.label())); 0695 adr.setStreet(toStdString(a.street())); 0696 adr.setLocality(toStdString(a.locality())); 0697 adr.setRegion(toStdString(a.region())); 0698 adr.setCode(toStdString(a.postalCode())); 0699 adr.setCountry(toStdString(a.country())); 0700 addresses.push_back(adr); 0701 } 0702 c.setAddresses(addresses, prefNum); 0703 0704 if (!addressee.nickName().isEmpty()) { 0705 c.setNickNames(std::vector<std::string>() << toStdString(addressee.nickName())); 0706 } 0707 0708 const QString &spouse = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-SpousesName")); 0709 if (!spouse.isEmpty()) { 0710 c.setRelateds(std::vector<Kolab::Related>() << Kolab::Related(Kolab::Related::Text, toStdString(spouse), Kolab::Related::Spouse)); 0711 } 0712 c.setBDay(fromDate(addressee.birthday(), true)); 0713 c.setAnniversary( 0714 fromDate(QDateTime(QDate::fromString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Anniversary")), Qt::ISODate), {}), true)); 0715 if (!addressee.photo().isEmpty()) { 0716 std::string mimetype; 0717 const std::string &photo = fromPicture(addressee.photo(), mimetype); 0718 c.setPhoto(photo, mimetype); 0719 } 0720 // TODO 0721 // c.setGender(addressee.gender()); 0722 std::vector<std::string> languages; 0723 const auto langs{addressee.langs()}; 0724 for (const KContacts::Lang &n : langs) { 0725 languages.push_back(toStdString(n.language())); 0726 } 0727 c.setLanguages(languages); 0728 0729 std::vector<Kolab::Telephone> phones; 0730 prefNum = -1; 0731 prefCounter = -1; 0732 const auto phoneNumbers{addressee.phoneNumbers()}; 0733 for (const KContacts::PhoneNumber &n : phoneNumbers) { 0734 Kolab::Telephone p; 0735 p.setNumber(toStdString(n.number())); 0736 bool pref = false; 0737 p.setTypes(fromPhoneType(n.type(), pref)); 0738 prefCounter++; 0739 if (pref) { 0740 prefNum = prefCounter; 0741 } 0742 phones.push_back(p); 0743 } 0744 c.setTelephones(phones, prefNum); 0745 0746 const QString &imAddress = addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-IMAddress")); 0747 if (!imAddress.isEmpty()) { 0748 c.setIMaddresses(std::vector<std::string>() << toStdString(imAddress), 0); 0749 } 0750 0751 int prefEmail = -1; 0752 int count = 0; 0753 std::vector<Kolab::Email> emails; 0754 const auto addressesEmails{addressee.emails()}; 0755 emails.reserve(addressesEmails.count()); 0756 for (const QString &e : addressesEmails) { 0757 if ((prefEmail == -1) && (e == addressee.preferredEmail())) { 0758 prefEmail = count; 0759 } 0760 count++; 0761 emails.emplace_back(toStdString(e), emailTypesFromStringlist(addressee.custom(QStringLiteral("KOLAB"), QStringLiteral("EmailTypes%1").arg(e)))); 0762 } 0763 c.setEmailAddresses(emails, prefEmail); 0764 0765 if (addressee.geo().isValid()) { 0766 c.setGPSpos(std::vector<Kolab::Geo>() << Kolab::Geo(addressee.geo().latitude(), addressee.geo().longitude())); 0767 } 0768 0769 Kolab::Crypto crypto; 0770 0771 const QStringList protocolPrefs = 0772 addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("CRYPTOPROTOPREF")).split(QLatin1Char(','), Qt::SkipEmptyParts); 0773 const uint cryptoFormats = stringListToCryptoMessageFormats(protocolPrefs); 0774 int formats = 0; 0775 if (cryptoFormats & InlineOpenPGPFormat) { 0776 formats |= Kolab::Crypto::PGPinline; 0777 } 0778 if (cryptoFormats & OpenPGPMIMEFormat) { 0779 formats |= Kolab::Crypto::PGPmime; 0780 } 0781 if (cryptoFormats & SMIMEFormat) { 0782 formats |= Kolab::Crypto::SMIME; 0783 } 0784 if (cryptoFormats & SMIMEOpaqueFormat) { 0785 formats |= Kolab::Crypto::SMIMEopaque; 0786 } 0787 crypto.setAllowed(formats); 0788 0789 Kolab::Crypto::CryptoPref signPref = Kolab::Crypto::Ask; 0790 switch (stringToSigningPreference(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("CRYPTOSIGNPREF")))) { 0791 case NeverSign: 0792 signPref = Kolab::Crypto::Never; 0793 break; 0794 case AlwaysSign: 0795 signPref = Kolab::Crypto::Always; 0796 break; 0797 case AlwaysSignIfPossible: 0798 signPref = Kolab::Crypto::IfPossible; 0799 break; 0800 case AlwaysAskForSigning: 0801 case AskSigningWheneverPossible: 0802 signPref = Kolab::Crypto::Ask; 0803 break; 0804 default: 0805 signPref = Kolab::Crypto::Ask; 0806 } 0807 crypto.setSignPref(signPref); 0808 0809 Kolab::Crypto::CryptoPref encryptPref = Kolab::Crypto::Ask; 0810 switch (stringToSigningPreference(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("CRYPTOENCRYPTPREF")))) { 0811 case NeverEncrypt: 0812 encryptPref = Kolab::Crypto::Never; 0813 break; 0814 case AlwaysEncrypt: 0815 encryptPref = Kolab::Crypto::Always; 0816 break; 0817 case AlwaysEncryptIfPossible: 0818 encryptPref = Kolab::Crypto::IfPossible; 0819 break; 0820 case AlwaysAskForEncryption: 0821 case AskWheneverPossible: 0822 encryptPref = Kolab::Crypto::Ask; 0823 break; 0824 default: 0825 encryptPref = Kolab::Crypto::Ask; 0826 } 0827 crypto.setEncryptPref(encryptPref); 0828 0829 c.setCrypto(crypto); 0830 0831 // FIXME the keys are most certainly wrong, look at cryptopageplugin.cpp 0832 std::vector<Kolab::Key> keys; 0833 const std::string &pgpkey = toStdString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("OPENPGPFP"))); 0834 if (!pgpkey.empty()) { 0835 keys.emplace_back(pgpkey, Kolab::Key::PGP); 0836 } 0837 const std::string &smimekey = toStdString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("SMIMEFP"))); 0838 if (!smimekey.empty()) { 0839 keys.emplace_back(smimekey, Kolab::Key::PKCS7_MIME); 0840 } 0841 c.setKeys(keys); 0842 0843 if (!addressee.sound().isEmpty()) { 0844 qCWarning(PIMKOLAB_LOG) << "sound is not supported"; 0845 } 0846 0847 const std::string &profession = toStdString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Profession"))); 0848 if (!profession.empty()) { 0849 setCustom(profession, "X-Profession", c); 0850 } 0851 0852 const std::string &adrBook = toStdString(addressee.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-AddressBook"))); 0853 if (!adrBook.empty()) { 0854 setCustom(adrBook, "X-AddressBook", c); 0855 } 0856 0857 // TODO preserve all custom properties (also such which are unknown to us) 0858 0859 return c; 0860 } 0861 0862 DistList fromKABC(const KContacts::ContactGroup &cg) 0863 { 0864 DistList dl; 0865 dl.setName(toStdString(cg.name())); 0866 dl.setUid(toStdString(cg.id())); 0867 0868 std::vector<Kolab::ContactReference> members; 0869 for (int i = 0; i < cg.dataCount(); i++) { 0870 const KContacts::ContactGroup::Data &data = cg.data(i); 0871 members.emplace_back(Kolab::ContactReference::EmailReference, toStdString(data.email()), toStdString(data.name())); 0872 } 0873 for (int i = 0; i < cg.contactReferenceCount(); i++) { 0874 const KContacts::ContactGroup::ContactReference &ref = cg.contactReference(i); 0875 members.emplace_back(Kolab::ContactReference::UidReference, toStdString(ref.uid())); 0876 } 0877 0878 if (cg.contactGroupReferenceCount() > 0) { 0879 qCWarning(PIMKOLAB_LOG) << "Tried to save contact group references, which should have been resolved already"; 0880 } 0881 0882 dl.setMembers(members); 0883 0884 return dl; 0885 } 0886 0887 KContacts::ContactGroup toKABC(const DistList &dl) 0888 { 0889 KContacts::ContactGroup cg(fromStdString(dl.name())); 0890 cg.setId(fromStdString(dl.uid())); 0891 const auto members{dl.members()}; 0892 for (const Kolab::ContactReference &m : members) { 0893 switch (m.type()) { 0894 case Kolab::ContactReference::EmailReference: 0895 cg.append(KContacts::ContactGroup::Data(fromStdString(m.name()), fromStdString(m.email()))); 0896 break; 0897 case Kolab::ContactReference::UidReference: 0898 cg.append(KContacts::ContactGroup::ContactReference(fromStdString(m.uid()))); 0899 break; 0900 default: 0901 qCCritical(PIMKOLAB_LOG) << "invalid contact reference"; 0902 } 0903 } 0904 0905 return cg; 0906 } 0907 } // Namespace 0908 } // Namespace