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