File indexing completed on 2024-04-28 11:34:55

0001 /*
0002     This file is part of the KContacts framework.
0003     SPDX-FileCopyrightText: 2003 Helge Deller <deller@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 /*
0009     Useful links:
0010         - http://tldp.org/HOWTO/LDAP-Implementation-HOWTO/schemas.html
0011         - http://www.faqs.org/rfcs/rfc2849.html
0012 
0013     Not yet handled items:
0014         - objectclass microsoftaddressbook
0015                 - info,
0016                 - initials,
0017                 - otherfacsimiletelephonenumber,
0018                 - otherpager,
0019                 - physicaldeliveryofficename,
0020 */
0021 
0022 #include "ldifconverter.h"
0023 #include "address.h"
0024 #include "kcontacts_debug.h"
0025 #include "vcardconverter.h"
0026 
0027 #include "ldif_p.h"
0028 
0029 #include <KCountry>
0030 #include <KLocalizedString>
0031 
0032 #include <QIODevice>
0033 #include <QStringList>
0034 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0035 #include <QTextCodec>
0036 #endif
0037 #include <QTextStream>
0038 
0039 using namespace KContacts;
0040 
0041 /* internal functions - do not use !! */
0042 
0043 namespace KContacts
0044 {
0045 /**
0046   @internal
0047 
0048   Evaluates @p fieldname and sets the @p value at the addressee or the address
0049   objects when appropriate.
0050 
0051   @param a The addressee to store information into
0052   @param homeAddr The home address to store respective information into
0053   @param workAddr The work address to store respective information into
0054   @param fieldname LDIF field name to evaluate
0055   @param value The value of the field addressed by @p fieldname
0056 */
0057 void evaluatePair(Addressee &a,
0058                   Address &homeAddr,
0059                   Address &workAddr,
0060                   QString &fieldname,
0061                   QString &value,
0062                   int &birthday,
0063                   int &birthmonth,
0064                   int &birthyear,
0065                   ContactGroup &contactGroup);
0066 }
0067 
0068 /* generate LDIF stream */
0069 
0070 static void ldif_out(QTextStream &t, const QString &formatStr, const QString &value)
0071 {
0072     if (value.isEmpty()) {
0073         return;
0074     }
0075 
0076     const QByteArray txt = Ldif::assembleLine(formatStr, value, 72);
0077 
0078     // write the string
0079     t << QString::fromUtf8(txt) << "\n";
0080 }
0081 
0082 bool LDIFConverter::addresseeAndContactGroupToLDIF(const AddresseeList &addrList, const ContactGroup::List &contactGroupList, QString &str)
0083 {
0084     bool result = addresseeToLDIF(addrList, str);
0085     if (!contactGroupList.isEmpty()) {
0086         result = (contactGroupToLDIF(contactGroupList, str) || result); // order matters
0087     }
0088     return result;
0089 }
0090 
0091 bool LDIFConverter::contactGroupToLDIF(const ContactGroup &contactGroup, QString &str)
0092 {
0093     if (contactGroup.dataCount() <= 0) {
0094         return false;
0095     }
0096     QTextStream t(&str, QIODevice::WriteOnly | QIODevice::Append);
0097 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0098     t.setCodec(QTextCodec::codecForName("UTF-8"));
0099 #endif
0100 
0101     t << "objectclass: top\n";
0102     t << "objectclass: groupOfNames\n";
0103 
0104     for (int i = 0; i < contactGroup.dataCount(); ++i) {
0105         const ContactGroup::Data &data = contactGroup.data(i);
0106         const QString value = QStringLiteral("cn=%1,mail=%2").arg(data.name(), data.email());
0107         ldif_out(t, QStringLiteral("member"), value);
0108     }
0109 
0110     t << "\n";
0111     return true;
0112 }
0113 
0114 bool LDIFConverter::contactGroupToLDIF(const ContactGroup::List &contactGroupList, QString &str)
0115 {
0116     if (contactGroupList.isEmpty()) {
0117         return false;
0118     }
0119 
0120     bool result = true;
0121     for (const ContactGroup &group : contactGroupList) {
0122         result = (contactGroupToLDIF(group, str) || result); // order matters
0123     }
0124     return result;
0125 }
0126 
0127 bool LDIFConverter::addresseeToLDIF(const AddresseeList &addrList, QString &str)
0128 {
0129     if (addrList.isEmpty()) {
0130         return false;
0131     }
0132 
0133     bool result = true;
0134     for (const Addressee &addr : addrList) {
0135         result = (addresseeToLDIF(addr, str) || result); // order matters
0136     }
0137     return result;
0138 }
0139 
0140 static QString countryName(const QString &isoCodeOrName)
0141 {
0142     const auto c = KCountry::fromAlpha2(isoCodeOrName);
0143     return c.isValid() ? c.name() : isoCodeOrName;
0144 }
0145 
0146 bool LDIFConverter::addresseeToLDIF(const Addressee &addr, QString &str)
0147 {
0148     if (addr.isEmpty()) {
0149         return false;
0150     }
0151 
0152     QTextStream t(&str, QIODevice::WriteOnly | QIODevice::Append);
0153 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0154     t.setCodec(QTextCodec::codecForName("UTF-8"));
0155 #endif
0156 
0157     const Address homeAddr = addr.address(Address::Home);
0158     const Address workAddr = addr.address(Address::Work);
0159 
0160     ldif_out(t, QStringLiteral("dn"), QStringLiteral("cn=%1,mail=%2").arg(addr.formattedName().simplified(), addr.preferredEmail()));
0161     t << "objectclass: top\n";
0162     t << "objectclass: person\n";
0163     t << "objectclass: organizationalPerson\n";
0164 
0165     ldif_out(t, QStringLiteral("givenname"), addr.givenName());
0166     ldif_out(t, QStringLiteral("sn"), addr.familyName());
0167     ldif_out(t, QStringLiteral("cn"), addr.formattedName().simplified());
0168     ldif_out(t, QStringLiteral("uid"), addr.uid());
0169     ldif_out(t, QStringLiteral("nickname"), addr.nickName());
0170     ldif_out(t, QStringLiteral("xmozillanickname"), addr.nickName());
0171     ldif_out(t, QStringLiteral("mozillanickname"), addr.nickName());
0172 
0173     ldif_out(t, QStringLiteral("mail"), addr.preferredEmail());
0174     const QStringList emails = addr.emails();
0175     const int numEmails = emails.count();
0176     for (int i = 1; i < numEmails; ++i) {
0177         if (i == 0) {
0178             // nothing
0179         } else if (i == 1) {
0180             ldif_out(t, QStringLiteral("mozillasecondemail"), emails[1]);
0181         } else {
0182             ldif_out(t, QStringLiteral("othermailbox"), emails[i]);
0183         }
0184     }
0185     // ldif_out( t, "mozilla_AIMScreenName: %1\n", "screen_name" );
0186 
0187     ldif_out(t, QStringLiteral("telephonenumber"), addr.phoneNumber(PhoneNumber::Work).number());
0188     ldif_out(t, QStringLiteral("facsimiletelephonenumber"), addr.phoneNumber(PhoneNumber::Fax).number());
0189     ldif_out(t, QStringLiteral("homephone"), addr.phoneNumber(PhoneNumber::Home).number());
0190     ldif_out(t, QStringLiteral("mobile"),
0191              addr.phoneNumber(PhoneNumber::Cell).number()); // Netscape 7
0192     ldif_out(t, QStringLiteral("cellphone"),
0193              addr.phoneNumber(PhoneNumber::Cell).number()); // Netscape 4.x
0194     ldif_out(t, QStringLiteral("pager"), addr.phoneNumber(PhoneNumber::Pager).number());
0195     ldif_out(t, QStringLiteral("pagerphone"), addr.phoneNumber(PhoneNumber::Pager).number());
0196 
0197     ldif_out(t, QStringLiteral("streethomeaddress"), homeAddr.street());
0198     ldif_out(t, QStringLiteral("postalcode"), workAddr.postalCode());
0199     ldif_out(t, QStringLiteral("postofficebox"), workAddr.postOfficeBox());
0200 
0201     QStringList streets = homeAddr.street().split(QLatin1Char('\n'));
0202     const int numberOfStreets(streets.count());
0203     if (numberOfStreets > 0) {
0204         ldif_out(t, QStringLiteral("homepostaladdress"), streets.at(0)); // Netscape 7
0205     }
0206     if (numberOfStreets > 1) {
0207         ldif_out(t, QStringLiteral("mozillahomepostaladdress2"), streets.at(1)); // Netscape 7
0208     }
0209     ldif_out(t, QStringLiteral("mozillahomelocalityname"), homeAddr.locality()); // Netscape 7
0210     ldif_out(t, QStringLiteral("mozillahomestate"), homeAddr.region());
0211     ldif_out(t, QStringLiteral("mozillahomepostalcode"), homeAddr.postalCode());
0212     ldif_out(t, QStringLiteral("mozillahomecountryname"), countryName(homeAddr.country()));
0213     ldif_out(t, QStringLiteral("locality"), workAddr.locality());
0214     ldif_out(t, QStringLiteral("streetaddress"), workAddr.street()); // Netscape 4.x
0215 
0216     streets = workAddr.street().split(QLatin1Char('\n'));
0217     const int streetsCount = streets.count();
0218     if (streetsCount > 0) {
0219         ldif_out(t, QStringLiteral("street"), streets.at(0));
0220     }
0221     if (streetsCount > 1) {
0222         ldif_out(t, QStringLiteral("mozillaworkstreet2"), streets.at(1));
0223     }
0224     ldif_out(t, QStringLiteral("countryname"), countryName(workAddr.country()));
0225     ldif_out(t, QStringLiteral("l"), workAddr.locality());
0226     ldif_out(t, QStringLiteral("c"), countryName(workAddr.country()));
0227     ldif_out(t, QStringLiteral("st"), workAddr.region());
0228 
0229     ldif_out(t, QStringLiteral("title"), addr.title());
0230     ldif_out(t, QStringLiteral("vocation"), addr.prefix());
0231     ldif_out(t, QStringLiteral("ou"), addr.role());
0232     ldif_out(t, QStringLiteral("o"), addr.organization());
0233     ldif_out(t, QStringLiteral("organization"), addr.organization());
0234     ldif_out(t, QStringLiteral("organizationname"), addr.organization());
0235 
0236     // Compatibility with older kabc versions.
0237     if (!addr.department().isEmpty()) {
0238         ldif_out(t, QStringLiteral("department"), addr.department());
0239     } else {
0240         ldif_out(t, QStringLiteral("department"), addr.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Department")));
0241     }
0242 
0243     ldif_out(t, QStringLiteral("workurl"), addr.url().url().toDisplayString());
0244     ldif_out(t, QStringLiteral("homeurl"), addr.url().url().toDisplayString());
0245     ldif_out(t, QStringLiteral("mozillahomeurl"), addr.url().url().toDisplayString());
0246 
0247     ldif_out(t, QStringLiteral("description"), addr.note());
0248     if (addr.revision().isValid()) {
0249         ldif_out(t, QStringLiteral("modifytimestamp"), dateToVCardString(addr.revision()));
0250     }
0251 
0252     const QDate birthday = addr.birthday().date();
0253     if (birthday.isValid()) {
0254         const int year = birthday.year();
0255         if (year > 0) {
0256             ldif_out(t, QStringLiteral("birthyear"), QString::number(year));
0257         }
0258         ldif_out(t, QStringLiteral("birthmonth"), QString::number(birthday.month()));
0259         ldif_out(t, QStringLiteral("birthday"), QString::number(birthday.day()));
0260     }
0261 
0262     t << "\n";
0263 
0264     return true;
0265 }
0266 
0267 /* convert from LDIF stream */
0268 bool LDIFConverter::LDIFToAddressee(const QString &str, AddresseeList &addrList, ContactGroup::List &contactGroupList, const QDateTime &dt)
0269 {
0270     if (str.isEmpty()) {
0271         return true;
0272     }
0273 
0274     bool endldif = false;
0275     bool end = false;
0276     Ldif ldif;
0277     Ldif::ParseValue ret;
0278     Addressee a;
0279     Address homeAddr;
0280     Address workAddr;
0281     int birthday = -1;
0282     int birthmonth = -1;
0283     int birthyear = -1;
0284     ContactGroup contactGroup;
0285     ldif.setLdif(str.toLatin1());
0286     QDateTime qdt = dt;
0287     if (!qdt.isValid()) {
0288         qdt = QDateTime::currentDateTime();
0289     }
0290     a.setRevision(qdt);
0291     homeAddr = Address(Address::Home);
0292     workAddr = Address(Address::Work);
0293 
0294     do {
0295         ret = ldif.nextItem();
0296         switch (ret) {
0297         case Ldif::Item: {
0298             QString fieldname = ldif.attr().toLower();
0299             QString value = QString::fromUtf8(ldif.value());
0300             evaluatePair(a, homeAddr, workAddr, fieldname, value, birthday, birthmonth, birthyear, contactGroup);
0301             break;
0302         }
0303         case Ldif::EndEntry:
0304             if (contactGroup.count() == 0) {
0305                 // if the new address is not empty, append it
0306                 QDate birthDate(birthyear, birthmonth, birthday);
0307                 if (birthDate.isValid()) {
0308                     a.setBirthday(birthDate);
0309                 }
0310 
0311                 if (!a.formattedName().isEmpty() || !a.name().isEmpty() || !a.familyName().isEmpty()) {
0312                     if (!homeAddr.isEmpty()) {
0313                         a.insertAddress(homeAddr);
0314                     }
0315                     if (!workAddr.isEmpty()) {
0316                         a.insertAddress(workAddr);
0317                     }
0318                     addrList.append(a);
0319                 }
0320             } else {
0321                 contactGroupList.append(contactGroup);
0322             }
0323             a = Addressee();
0324             contactGroup = ContactGroup();
0325             a.setRevision(qdt);
0326             homeAddr = Address(Address::Home);
0327             workAddr = Address(Address::Work);
0328             break;
0329         case Ldif::MoreData:
0330             if (endldif) {
0331                 end = true;
0332             } else {
0333                 ldif.endLdif();
0334                 endldif = true;
0335                 break;
0336             }
0337         default:
0338             break;
0339         }
0340     } while (!end);
0341 
0342     return true;
0343 }
0344 
0345 void KContacts::evaluatePair(Addressee &a,
0346                              Address &homeAddr,
0347                              Address &workAddr,
0348                              QString &fieldname,
0349                              QString &value,
0350                              int &birthday,
0351                              int &birthmonth,
0352                              int &birthyear,
0353                              ContactGroup &contactGroup)
0354 {
0355     if (fieldname == QLatin1String("dn")) { // ignore
0356         return;
0357     }
0358 
0359     if (fieldname.startsWith(QLatin1Char('#'))) {
0360         return;
0361     }
0362 
0363     if (fieldname.isEmpty() && !a.note().isEmpty()) {
0364         // some LDIF export filters are broken and add additional
0365         // comments on stand-alone lines. Just add them to the notes for now.
0366         a.setNote(a.note() + QLatin1Char('\n') + value);
0367         return;
0368     }
0369 
0370     if (fieldname == QLatin1String("givenname")) {
0371         a.setGivenName(value);
0372         return;
0373     }
0374 
0375     if (fieldname == QLatin1String("xmozillanickname") //
0376         || fieldname == QLatin1String("nickname") //
0377         || fieldname == QLatin1String("mozillanickname")) {
0378         a.setNickName(value);
0379         return;
0380     }
0381 
0382     if (fieldname == QLatin1String("sn")) {
0383         a.setFamilyName(value);
0384         return;
0385     }
0386 
0387     if (fieldname == QLatin1String("uid")) {
0388         a.setUid(value);
0389         return;
0390     }
0391     if (fieldname == QLatin1String("mail") //
0392         || fieldname == QLatin1String("mozillasecondemail") /* mozilla */
0393         || fieldname == QLatin1String("othermailbox") /*TheBat!*/) {
0394         if (a.emails().indexOf(value) == -1) {
0395             a.addEmail(value);
0396         }
0397         return;
0398     }
0399 
0400     if (fieldname == QLatin1String("title")) {
0401         a.setTitle(value);
0402         return;
0403     }
0404 
0405     if (fieldname == QLatin1String("vocation")) {
0406         a.setPrefix(value);
0407         return;
0408     }
0409 
0410     if (fieldname == QLatin1String("cn")) {
0411         a.setFormattedName(value);
0412         return;
0413     }
0414 
0415     if (fieldname == QLatin1Char('o') || fieldname == QLatin1String("organization") // Exchange
0416         || fieldname == QLatin1String("organizationname")) { // Exchange
0417         a.setOrganization(value);
0418         return;
0419     }
0420 
0421     // clang-format off
0422     if (fieldname == QLatin1String("description")
0423         || fieldname == QLatin1String("mozillacustom1")
0424         || fieldname == QLatin1String("mozillacustom2")
0425         || fieldname == QLatin1String("mozillacustom3")
0426         || fieldname == QLatin1String("mozillacustom4")
0427         || fieldname == QLatin1String("custom1")
0428         || fieldname == QLatin1String("custom2")
0429         || fieldname == QLatin1String("custom3")
0430         || fieldname == QLatin1String("custom4")) {
0431         if (!a.note().isEmpty()) {
0432             a.setNote(a.note() + QLatin1Char('\n'));
0433         }
0434         a.setNote(a.note() + value);
0435         return;
0436     }
0437     // clang-format on
0438 
0439     if (fieldname == QLatin1String("homeurl") //
0440         || fieldname == QLatin1String("workurl") //
0441         || fieldname == QLatin1String("mozillahomeurl")) {
0442         if (a.url().url().isEmpty()) {
0443             ResourceLocatorUrl url;
0444             url.setUrl(QUrl(value));
0445             a.setUrl(url);
0446             return;
0447         }
0448         if (a.url().url().toDisplayString() == QUrl(value).toDisplayString()) {
0449             return;
0450         }
0451         // TODO: current version of kabc only supports one URL.
0452         // TODO: change this with KDE 4
0453     }
0454 
0455     if (fieldname == QLatin1String("homephone")) {
0456         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Home));
0457         return;
0458     }
0459 
0460     if (fieldname == QLatin1String("telephonenumber")) {
0461         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Work));
0462         return;
0463     }
0464     if (fieldname == QLatin1String("mobile") /* mozilla/Netscape 7 */
0465         || fieldname == QLatin1String("cellphone")) {
0466         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Cell));
0467         return;
0468     }
0469 
0470     if (fieldname == QLatin1String("pager") // mozilla
0471         || fieldname == QLatin1String("pagerphone")) { // mozilla
0472         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Pager));
0473         return;
0474     }
0475 
0476     if (fieldname == QLatin1String("facsimiletelephonenumber")) {
0477         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Fax));
0478         return;
0479     }
0480 
0481     if (fieldname == QLatin1String("xmozillaanyphone")) { // mozilla
0482         a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Work));
0483         return;
0484     }
0485 
0486     if (fieldname == QLatin1String("streethomeaddress") //
0487         || fieldname == QLatin1String("mozillahomestreet")) { // thunderbird
0488         homeAddr.setStreet(value);
0489         return;
0490     }
0491 
0492     if (fieldname == QLatin1String("street") //
0493         || fieldname == QLatin1String("postaladdress")) { // mozilla
0494         workAddr.setStreet(value);
0495         return;
0496     }
0497     if (fieldname == QLatin1String("mozillapostaladdress2") //
0498         || fieldname == QLatin1String("mozillaworkstreet2")) { // mozilla
0499         workAddr.setStreet(workAddr.street() + QLatin1Char('\n') + value);
0500         return;
0501     }
0502 
0503     if (fieldname == QLatin1String("postalcode")) {
0504         workAddr.setPostalCode(value);
0505         return;
0506     }
0507 
0508     if (fieldname == QLatin1String("postofficebox")) {
0509         workAddr.setPostOfficeBox(value);
0510         return;
0511     }
0512 
0513     if (fieldname == QLatin1String("homepostaladdress")) { // Netscape 7
0514         homeAddr.setStreet(value);
0515         return;
0516     }
0517 
0518     if (fieldname == QLatin1String("mozillahomepostaladdress2")) { // mozilla
0519         homeAddr.setStreet(homeAddr.street() + QLatin1Char('\n') + value);
0520         return;
0521     }
0522 
0523     if (fieldname == QLatin1String("mozillahomelocalityname")) { // mozilla
0524         homeAddr.setLocality(value);
0525         return;
0526     }
0527 
0528     if (fieldname == QLatin1String("mozillahomestate")) { // mozilla
0529         homeAddr.setRegion(value);
0530         return;
0531     }
0532 
0533     if (fieldname == QLatin1String("mozillahomepostalcode")) { // mozilla
0534         homeAddr.setPostalCode(value);
0535         return;
0536     }
0537 
0538     if (fieldname == QLatin1String("mozillahomecountryname")) { // mozilla
0539         if (value.length() <= 2) {
0540             value = countryName(value);
0541         }
0542         homeAddr.setCountry(value);
0543         return;
0544     }
0545 
0546     if (fieldname == QLatin1String("locality")) {
0547         workAddr.setLocality(value);
0548         return;
0549     }
0550 
0551     if (fieldname == QLatin1String("streetaddress")) { // Netscape 4.x
0552         workAddr.setStreet(value);
0553         return;
0554     }
0555 
0556     if (fieldname == QLatin1String("countryname") //
0557         || fieldname == QLatin1Char('c')) { // mozilla
0558         if (value.length() <= 2) {
0559             value = countryName(value);
0560         }
0561         workAddr.setCountry(value);
0562         return;
0563     }
0564 
0565     if (fieldname == QLatin1Char('l')) { // mozilla
0566         workAddr.setLocality(value);
0567         return;
0568     }
0569 
0570     if (fieldname == QLatin1String("st")) {
0571         workAddr.setRegion(value);
0572         return;
0573     }
0574 
0575     if (fieldname == QLatin1String("ou")) {
0576         a.setRole(value);
0577         return;
0578     }
0579 
0580     if (fieldname == QLatin1String("department")) {
0581         a.setDepartment(value);
0582         return;
0583     }
0584 
0585     if (fieldname == QLatin1String("member")) {
0586         // this is a mozilla list member (cn=xxx, mail=yyy)
0587         const QStringList list = value.split(QLatin1Char(','));
0588         QString name;
0589         QString email;
0590 
0591         const QLatin1String cnTag("cn=");
0592         const QLatin1String mailTag("mail=");
0593         for (const auto &str : list) {
0594             if (str.startsWith(cnTag)) {
0595                 name = QStringView(str).mid(cnTag.size()).trimmed().toString();
0596             } else if (str.startsWith(mailTag)) {
0597                 email = QStringView(str).mid(mailTag.size()).trimmed().toString();
0598             }
0599         }
0600 
0601         if (!name.isEmpty() && !email.isEmpty()) {
0602             email = QLatin1String(" <") + email + QLatin1Char('>');
0603         }
0604         ContactGroup::Data data;
0605         data.setEmail(email);
0606         data.setName(name);
0607         contactGroup.append(data);
0608         return;
0609     }
0610 
0611     if (fieldname == QLatin1String("modifytimestamp")) {
0612         if (value == QLatin1String("0Z")) { // ignore
0613             return;
0614         }
0615         QDateTime dt = VCardStringToDate(value);
0616         if (dt.isValid()) {
0617             a.setRevision(dt);
0618             return;
0619         }
0620     }
0621 
0622     if (fieldname == QLatin1String("display-name")) {
0623         contactGroup.setName(value);
0624         return;
0625     }
0626 
0627     if (fieldname == QLatin1String("objectclass")) { // ignore
0628         return;
0629     }
0630 
0631     if (fieldname == QLatin1String("birthyear")) {
0632         bool ok;
0633         birthyear = value.toInt(&ok);
0634         if (!ok) {
0635             birthyear = -1;
0636         }
0637         return;
0638     }
0639     if (fieldname == QLatin1String("birthmonth")) {
0640         birthmonth = value.toInt();
0641         return;
0642     }
0643     if (fieldname == QLatin1String("birthday")) {
0644         birthday = value.toInt();
0645         return;
0646     }
0647     if (fieldname == QLatin1String("xbatbirthday")) {
0648 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0649         const QStringView str{value};
0650 #else
0651         const QStringRef str{&value};
0652 #endif
0653         QDate dt(str.mid(0, 4).toInt(), str.mid(4, 2).toInt(), str.mid(6, 2).toInt());
0654         if (dt.isValid()) {
0655             a.setBirthday(dt);
0656         }
0657         return;
0658     }
0659     qCWarning(KCONTACTS_LOG) << QStringLiteral("LDIFConverter: Unknown field for '%1': '%2=%3'\n").arg(a.formattedName(), fieldname, value);
0660 }