File indexing completed on 2024-04-14 14:19:00

0001 /*
0002     This file is part of the KContacts framework.
0003     SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
0004     SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
0005     SPDX-FileCopyrightText: 2005 Ingo Kloecker <kloecker@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include <QDate>
0011 #include <QRegularExpression>
0012 #include <QSharedData>
0013 #include <QUuid>
0014 
0015 #include "kcontacts_debug.h"
0016 #include <KLocalizedString>
0017 
0018 #include "addressee.h"
0019 #include "addresseehelper.h"
0020 #include "field.h"
0021 #include "parametermap_p.h"
0022 
0023 using namespace KContacts;
0024 
0025 static bool matchBinaryPattern(int value, int pattern);
0026 
0027 template<class L>
0028 static bool listEquals(const QVector<L> &list, const QVector<L> &pattern);
0029 static bool listEquals(const QStringList &list, const QStringList &pattern);
0030 
0031 struct CustomData {
0032     QString name;
0033     QString value;
0034 };
0035 
0036 inline bool operator==(const CustomData &a, const CustomData &b)
0037 {
0038     return std::tie(a.name, a.value) == std::tie(b.name, b.value);
0039 }
0040 
0041 inline bool operator!=(const CustomData &a, const CustomData &b)
0042 {
0043     return std::tie(a.name, a.value) != std::tie(b.name, b.value);
0044 }
0045 
0046 inline bool operator<(const CustomData &a, const CustomData &b)
0047 {
0048     return a.name < b.name;
0049 }
0050 
0051 class Q_DECL_HIDDEN Addressee::Private : public QSharedData
0052 {
0053 public:
0054     Private()
0055         : mUid(QUuid::createUuid().toString().mid(1, 36))
0056         , mEmpty(true)
0057         , mChanged(false)
0058         , mBirthdayWithTime(false)
0059     {
0060         // We avoid the curly braces so the string is RFC4122 compliant and can be used as urn
0061     }
0062 
0063     Private(const Private &other)
0064         : QSharedData(other)
0065     {
0066         mUid = other.mUid;
0067         mName = other.mName;
0068         mFormattedName = other.mFormattedName;
0069         mFamilyName = other.mFamilyName;
0070         mGivenName = other.mGivenName;
0071         mAdditionalName = other.mAdditionalName;
0072         mPrefix = other.mPrefix;
0073         mSuffix = other.mSuffix;
0074         mBirthday = other.mBirthday;
0075         mBirthdayWithTime = other.mBirthdayWithTime;
0076         mMailer = other.mMailer;
0077         mTimeZone = other.mTimeZone;
0078         mGeo = other.mGeo;
0079         mDepartment = other.mDepartment;
0080         mNote = other.mNote;
0081         mProductId = other.mProductId;
0082         mRevision = other.mRevision;
0083         mSortString = other.mSortString;
0084         mSecrecy = other.mSecrecy;
0085         mLogo = other.mLogo;
0086         mPhoto = other.mPhoto;
0087         mSound = other.mSound;
0088 
0089         mPhoneNumbers = other.mPhoneNumbers;
0090         mAddresses = other.mAddresses;
0091         mKeys = other.mKeys;
0092         mLangs = other.mLangs;
0093         mGender = other.mGender;
0094         mEmails = other.mEmails;
0095         mCategories = other.mCategories;
0096         mCustomFields = other.mCustomFields;
0097         mCalendarUrl = other.mCalendarUrl;
0098         mSoundListExtra = other.mSoundListExtra;
0099         mPhotoExtraList = other.mPhotoExtraList;
0100         mLogoExtraList = other.mLogoExtraList;
0101         mUrlExtraList = other.mUrlExtraList;
0102         mMembers = other.mMembers;
0103         mRelationships = other.mRelationships;
0104         mSources = other.mSources;
0105         mEmpty = other.mEmpty;
0106         mImpps = other.mImpps;
0107         mChanged = other.mChanged;
0108         mTitleExtraList = other.mTitleExtraList;
0109         mRoleExtraList = other.mRoleExtraList;
0110         mOrgExtraList = other.mOrgExtraList;
0111     }
0112 
0113     ~Private()
0114     {
0115     }
0116 
0117     std::vector<CustomData>::iterator findByName(const QString &qualifiedName)
0118     {
0119         return std::find_if(mCustomFields.begin(), mCustomFields.end(), [&qualifiedName](const CustomData &info) {
0120             return info.name == qualifiedName;
0121         });
0122     }
0123 
0124     std::vector<CustomData>::const_iterator findByName(const QString &qualifiedName) const
0125     {
0126         return std::find_if(mCustomFields.cbegin(), mCustomFields.cend(), [&qualifiedName](const CustomData &info) {
0127             return info.name == qualifiedName;
0128         });
0129     }
0130 
0131     QString mUid;
0132     QString mName;
0133     QString mFormattedName;
0134     QString mFamilyName;
0135     QString mGivenName;
0136     QString mAdditionalName;
0137     QString mPrefix;
0138     QString mSuffix;
0139     QDateTime mBirthday;
0140     QString mMailer;
0141     TimeZone mTimeZone;
0142     Geo mGeo;
0143     QString mDepartment;
0144     QString mNote;
0145     QString mProductId;
0146     QDateTime mRevision;
0147     QString mSortString;
0148     Secrecy mSecrecy;
0149     Picture mLogo;
0150     Picture mPhoto;
0151     Sound mSound;
0152 
0153     PhoneNumber::List mPhoneNumbers;
0154     Address::List mAddresses;
0155     Key::List mKeys;
0156     Email::List mEmails;
0157     Lang::List mLangs;
0158     Impp::List mImpps;
0159     Gender mGender;
0160     QString mKind;
0161     QStringList mCategories;
0162     std::vector<CustomData> mCustomFields;
0163     CalendarUrl::List mCalendarUrl;
0164     Sound::List mSoundListExtra;
0165     Picture::List mPhotoExtraList;
0166     Picture::List mLogoExtraList;
0167     ResourceLocatorUrl::List mUrlExtraList;
0168     QVector<QUrl> mSources;
0169     QStringList mMembers;
0170     Related::List mRelationships;
0171     FieldGroup::List mFieldGroupList;
0172     Title::List mTitleExtraList;
0173     Role::List mRoleExtraList;
0174     Org::List mOrgExtraList;
0175     NickName::List mNickNameExtraList;
0176     ClientPidMap::List mClientPidMapList;
0177     bool mEmpty : 1;
0178     bool mChanged : 1;
0179     bool mBirthdayWithTime;
0180 };
0181 
0182 Addressee::Addressee()
0183     : d(new Private)
0184 {
0185 }
0186 
0187 Addressee::~Addressee()
0188 {
0189 }
0190 
0191 Addressee::Addressee(const Addressee &other)
0192     : d(other.d)
0193 {
0194 }
0195 
0196 Addressee &Addressee::operator=(const Addressee &other)
0197 {
0198     if (this != &other) {
0199         d = other.d;
0200     }
0201 
0202     return *this;
0203 }
0204 
0205 bool Addressee::operator==(const Addressee &addressee) const
0206 {
0207     if (d->mUid != addressee.d->mUid) {
0208         qCDebug(KCONTACTS_LOG) << "uid differs";
0209         return false;
0210     }
0211 
0212     if (d->mName != addressee.d->mName //
0213         && !(d->mName.isEmpty() && addressee.d->mName.isEmpty())) {
0214         qCDebug(KCONTACTS_LOG) << "name differs";
0215         return false;
0216     }
0217 
0218     if (d->mFormattedName != addressee.d->mFormattedName //
0219         && !(d->mFormattedName.isEmpty() && addressee.d->mFormattedName.isEmpty())) {
0220         qCDebug(KCONTACTS_LOG) << "formattedName differs";
0221         return false;
0222     }
0223 
0224     if (d->mFamilyName != addressee.d->mFamilyName //
0225         && !(d->mFamilyName.isEmpty() && addressee.d->mFamilyName.isEmpty())) {
0226         qCDebug(KCONTACTS_LOG) << "familyName differs";
0227         return false;
0228     }
0229 
0230     if (d->mGivenName != addressee.d->mGivenName //
0231         && !(d->mGivenName.isEmpty() && addressee.d->mGivenName.isEmpty())) {
0232         qCDebug(KCONTACTS_LOG) << "givenName differs";
0233         return false;
0234     }
0235 
0236     if (d->mAdditionalName != addressee.d->mAdditionalName //
0237         && !(d->mAdditionalName.isEmpty() && addressee.d->mAdditionalName.isEmpty())) {
0238         qCDebug(KCONTACTS_LOG) << "additionalName differs";
0239         return false;
0240     }
0241 
0242     if (d->mPrefix != addressee.d->mPrefix //
0243         && !(d->mPrefix.isEmpty() && addressee.d->mPrefix.isEmpty())) {
0244         qCDebug(KCONTACTS_LOG) << "prefix differs";
0245         return false;
0246     }
0247 
0248     if (d->mSuffix != addressee.d->mSuffix //
0249         && !(d->mSuffix.isEmpty() && addressee.d->mSuffix.isEmpty())) {
0250         qCDebug(KCONTACTS_LOG) << "suffix differs";
0251         return false;
0252     }
0253 
0254     if (d->mBirthday != addressee.d->mBirthday //
0255         || d->mBirthdayWithTime != addressee.d->mBirthdayWithTime) {
0256         qCDebug(KCONTACTS_LOG) << "birthday differs";
0257         return false;
0258     }
0259 
0260     if (d->mMailer != addressee.d->mMailer //
0261         && !(d->mMailer.isEmpty() && addressee.d->mMailer.isEmpty())) {
0262         qCDebug(KCONTACTS_LOG) << "mailer differs";
0263         return false;
0264     }
0265 
0266     if (d->mTimeZone != addressee.d->mTimeZone) {
0267         qCDebug(KCONTACTS_LOG) << "timeZone differs";
0268         return false;
0269     }
0270 
0271     if (d->mGeo != addressee.d->mGeo) {
0272         qCDebug(KCONTACTS_LOG) << "geo differs";
0273         return false;
0274     }
0275 
0276     if (d->mDepartment != addressee.d->mDepartment //
0277         && !(d->mDepartment.isEmpty() && addressee.d->mDepartment.isEmpty())) {
0278         qCDebug(KCONTACTS_LOG) << "department differs";
0279         return false;
0280     }
0281 
0282     if (d->mNote != addressee.d->mNote //
0283         && !(d->mNote.isEmpty() && addressee.d->mNote.isEmpty())) {
0284         qCDebug(KCONTACTS_LOG) << "note differs";
0285         return false;
0286     }
0287 
0288     if (d->mProductId != addressee.d->mProductId //
0289         && !(d->mProductId.isEmpty() && addressee.d->mProductId.isEmpty())) {
0290         qCDebug(KCONTACTS_LOG) << "productId differs";
0291         return false;
0292     }
0293 
0294     if (d->mSortString != addressee.d->mSortString //
0295         && !(d->mSortString.isEmpty() && addressee.d->mSortString.isEmpty())) {
0296         qCDebug(KCONTACTS_LOG) << "sortString differs";
0297         return false;
0298     }
0299 
0300     if (d->mSecrecy != addressee.d->mSecrecy) {
0301         qCDebug(KCONTACTS_LOG) << "secrecy differs";
0302         return false;
0303     }
0304 
0305     if (d->mLogo != addressee.d->mLogo) {
0306         qCDebug(KCONTACTS_LOG) << "logo differs";
0307         return false;
0308     }
0309 
0310     if (d->mPhoto != addressee.d->mPhoto) {
0311         qCDebug(KCONTACTS_LOG) << "photo differs";
0312         return false;
0313     }
0314 
0315     if (d->mSound != addressee.d->mSound) {
0316         qCDebug(KCONTACTS_LOG) << "sound differs";
0317         return false;
0318     }
0319 
0320     if (!listEquals(d->mPhoneNumbers, addressee.d->mPhoneNumbers)) {
0321         qCDebug(KCONTACTS_LOG) << "phoneNumbers differs";
0322         return false;
0323     }
0324 
0325     if (!listEquals(d->mAddresses, addressee.d->mAddresses)) {
0326         qCDebug(KCONTACTS_LOG) << "addresses differs";
0327         return false;
0328     }
0329 
0330     if (!listEquals(d->mKeys, addressee.d->mKeys)) {
0331         qCDebug(KCONTACTS_LOG) << "keys differs";
0332         return false;
0333     }
0334     if (!listEquals(d->mEmails, addressee.d->mEmails)) {
0335         qCDebug(KCONTACTS_LOG) << "emails differs";
0336         return false;
0337     }
0338 
0339     if (!listEquals(d->mCategories, addressee.d->mCategories)) {
0340         qCDebug(KCONTACTS_LOG) << "categories differs";
0341         return false;
0342     }
0343 
0344     if (d->mCustomFields != addressee.d->mCustomFields) {
0345         qCDebug(KCONTACTS_LOG) << "custom differs";
0346         return false;
0347     }
0348     if (d->mLangs != addressee.d->mLangs) {
0349         qCDebug(KCONTACTS_LOG) << "langs differs";
0350         return false;
0351     }
0352     if (d->mImpps != addressee.d->mImpps) {
0353         qCDebug(KCONTACTS_LOG) << "impps differs";
0354         return false;
0355     }
0356     if (d->mGender != addressee.d->mGender) {
0357         qCDebug(KCONTACTS_LOG) << "gender differs";
0358         return false;
0359     }
0360     if (d->mKind != addressee.d->mKind) {
0361         qCDebug(KCONTACTS_LOG) << "kind differs";
0362         return false;
0363     }
0364     if (!listEquals(d->mCalendarUrl, addressee.d->mCalendarUrl)) {
0365         qCDebug(KCONTACTS_LOG) << "calendarUrl differs";
0366         return false;
0367     }
0368     if (!listEquals(d->mSoundListExtra, addressee.d->mSoundListExtra)) {
0369         qCDebug(KCONTACTS_LOG) << "Extra sound differs";
0370         return false;
0371     }
0372     if (!listEquals(d->mPhotoExtraList, addressee.d->mPhotoExtraList)) {
0373         qCDebug(KCONTACTS_LOG) << "Extra photo differs";
0374         return false;
0375     }
0376     if (!listEquals(d->mLogoExtraList, addressee.d->mLogoExtraList)) {
0377         qCDebug(KCONTACTS_LOG) << "Extra logo differs";
0378         return false;
0379     }
0380     if (!listEquals(d->mUrlExtraList, addressee.d->mUrlExtraList)) {
0381         qCDebug(KCONTACTS_LOG) << "Extra url differs";
0382         return false;
0383     }
0384     if (!listEquals(d->mMembers, addressee.d->mMembers)) {
0385         qCDebug(KCONTACTS_LOG) << "Extra url differs";
0386         return false;
0387     }
0388     if (!listEquals(d->mRelationships, addressee.d->mRelationships)) {
0389         qCDebug(KCONTACTS_LOG) << "Relationships differs";
0390         return false;
0391     }
0392     if (!listEquals(d->mSources, addressee.d->mSources)) {
0393         qCDebug(KCONTACTS_LOG) << "Sources differs";
0394         return false;
0395     }
0396 
0397     if (!listEquals(d->mFieldGroupList, addressee.d->mFieldGroupList)) {
0398         qCDebug(KCONTACTS_LOG) << "Field Groups differs";
0399         return false;
0400     }
0401 
0402     if (!listEquals(d->mTitleExtraList, addressee.d->mTitleExtraList)) {
0403         qCDebug(KCONTACTS_LOG) << "Extra TitleList differs";
0404         return false;
0405     }
0406 
0407     if (!listEquals(d->mRoleExtraList, addressee.d->mRoleExtraList)) {
0408         qCDebug(KCONTACTS_LOG) << "Extra RoleList differs";
0409         return false;
0410     }
0411 
0412     if (!listEquals(d->mOrgExtraList, addressee.d->mOrgExtraList)) {
0413         qCDebug(KCONTACTS_LOG) << "Extra Organization List differs";
0414         return false;
0415     }
0416 
0417     if (!listEquals(d->mNickNameExtraList, addressee.d->mNickNameExtraList)) {
0418         qCDebug(KCONTACTS_LOG) << "Extra NickName List differs";
0419         return false;
0420     }
0421 
0422     if (!listEquals(d->mClientPidMapList, addressee.d->mClientPidMapList)) {
0423         qCDebug(KCONTACTS_LOG) << "ClientPidMap List differs";
0424         return false;
0425     }
0426     return true;
0427 }
0428 
0429 bool Addressee::operator!=(const Addressee &a) const
0430 {
0431     return !(a == *this);
0432 }
0433 
0434 bool Addressee::isEmpty() const
0435 {
0436     return d->mEmpty;
0437 }
0438 
0439 void Addressee::setUid(const QString &id)
0440 {
0441     if (id == d->mUid) {
0442         return;
0443     }
0444 
0445     d->mEmpty = false;
0446     d->mUid = id;
0447 }
0448 
0449 QString Addressee::uid() const
0450 {
0451     return d->mUid;
0452 }
0453 
0454 QString Addressee::uidLabel()
0455 {
0456     return i18n("Unique Identifier");
0457 }
0458 
0459 void Addressee::setName(const QString &name)
0460 {
0461     if (name == d->mName) {
0462         return;
0463     }
0464 
0465     d->mEmpty = false;
0466     d->mName = name;
0467 }
0468 
0469 QString Addressee::name() const
0470 {
0471     return d->mName;
0472 }
0473 
0474 QString Addressee::nameLabel()
0475 {
0476     return i18n("Name");
0477 }
0478 
0479 void Addressee::setKind(const QString &kind)
0480 {
0481     if (kind == d->mKind) {
0482         return;
0483     }
0484 
0485     d->mEmpty = false;
0486     d->mKind = kind;
0487 }
0488 
0489 QString Addressee::kind() const
0490 {
0491     return d->mKind;
0492 }
0493 
0494 void Addressee::insertExtraSound(const Sound &sound)
0495 {
0496     d->mEmpty = false;
0497     d->mSoundListExtra.append(sound);
0498 }
0499 
0500 Sound::List Addressee::extraSoundList() const
0501 {
0502     return d->mSoundListExtra;
0503 }
0504 
0505 void Addressee::insertExtraPhoto(const Picture &picture)
0506 {
0507     d->mEmpty = false;
0508     d->mPhotoExtraList.append(picture);
0509 }
0510 
0511 Picture::List Addressee::extraPhotoList() const
0512 {
0513     return d->mPhotoExtraList;
0514 }
0515 
0516 void Addressee::insertExtraLogo(const Picture &logo)
0517 {
0518     d->mEmpty = false;
0519     d->mLogoExtraList.append(logo);
0520 }
0521 
0522 Picture::List Addressee::extraLogoList() const
0523 {
0524     return d->mLogoExtraList;
0525 }
0526 
0527 void Addressee::setExtraSoundList(const Sound::List &soundList)
0528 {
0529     d->mEmpty = false;
0530     d->mSoundListExtra = soundList;
0531 }
0532 
0533 void Addressee::setExtraPhotoList(const Picture::List &pictureList)
0534 {
0535     d->mEmpty = false;
0536     d->mPhotoExtraList = pictureList;
0537 }
0538 
0539 void Addressee::setExtraLogoList(const Picture::List &logoList)
0540 {
0541     d->mEmpty = false;
0542     d->mLogoExtraList = logoList;
0543 }
0544 
0545 void Addressee::insertExtraUrl(const ResourceLocatorUrl &url)
0546 {
0547     if (url.isValid()) {
0548         d->mEmpty = false;
0549         d->mUrlExtraList.append(url);
0550     }
0551 }
0552 
0553 void Addressee::setExtraUrlList(const ResourceLocatorUrl::List &urlList)
0554 {
0555     d->mEmpty = false;
0556     d->mUrlExtraList = urlList;
0557 }
0558 
0559 ResourceLocatorUrl::List Addressee::extraUrlList() const
0560 {
0561     return d->mUrlExtraList;
0562 }
0563 
0564 void Addressee::insertSourceUrl(const QUrl &url)
0565 {
0566     d->mEmpty = false;
0567     d->mSources.append(url);
0568 }
0569 
0570 void Addressee::setSourcesUrlList(const QVector<QUrl> &urlList)
0571 {
0572     d->mEmpty = false;
0573     d->mSources = urlList;
0574 }
0575 
0576 QVector<QUrl> Addressee::sourcesUrlList() const
0577 {
0578     return d->mSources;
0579 }
0580 
0581 FieldGroup::List Addressee::fieldGroupList() const
0582 {
0583     return d->mFieldGroupList;
0584 }
0585 
0586 void Addressee::setFieldGroupList(const FieldGroup::List &fieldGroupList)
0587 {
0588     d->mEmpty = false;
0589     d->mFieldGroupList = fieldGroupList;
0590 }
0591 
0592 void Addressee::insertFieldGroup(const FieldGroup &fieldGroup)
0593 {
0594     if (fieldGroup.isValid()) {
0595         d->mEmpty = false;
0596         // TODO don't duplicate ?
0597         d->mFieldGroupList.append(fieldGroup);
0598     }
0599 }
0600 
0601 ClientPidMap::List Addressee::clientPidMapList() const
0602 {
0603     return d->mClientPidMapList;
0604 }
0605 
0606 void Addressee::setClientPidMapList(const ClientPidMap::List &clientpidmaplist)
0607 {
0608     d->mEmpty = false;
0609     d->mClientPidMapList = clientpidmaplist;
0610 }
0611 
0612 void Addressee::insertClientPidMap(const ClientPidMap &clientpidmap)
0613 {
0614     if (clientpidmap.isValid()) {
0615         d->mEmpty = false;
0616         // TODO don't duplicate ?
0617         d->mClientPidMapList.append(clientpidmap);
0618     }
0619 }
0620 
0621 void Addressee::insertImpp(const Impp &impp)
0622 {
0623     if (impp.isValid()) {
0624         d->mEmpty = false;
0625         // Don't duplicate ?
0626         d->mImpps.append(impp);
0627     }
0628 }
0629 
0630 void Addressee::setImppList(const Impp::List &imppList)
0631 {
0632     d->mEmpty = false;
0633     d->mImpps = imppList;
0634 }
0635 
0636 Impp::List Addressee::imppList() const
0637 {
0638     return d->mImpps;
0639 }
0640 
0641 void Addressee::insertCalendarUrl(const CalendarUrl &calendarUrl)
0642 {
0643     d->mEmpty = false;
0644     // TODO verify that there is not same calendarurl
0645     if (calendarUrl.isValid()) {
0646         d->mCalendarUrl.append(calendarUrl);
0647     }
0648 }
0649 
0650 CalendarUrl::List Addressee::calendarUrlList() const
0651 {
0652     return d->mCalendarUrl;
0653 }
0654 
0655 void Addressee::setFormattedName(const QString &formattedName)
0656 {
0657     if (formattedName == d->mFormattedName) {
0658         return;
0659     }
0660 
0661     d->mEmpty = false;
0662     d->mFormattedName = formattedName;
0663 }
0664 
0665 QString Addressee::formattedName() const
0666 {
0667     return d->mFormattedName;
0668 }
0669 
0670 QString Addressee::formattedNameLabel()
0671 {
0672     return i18n("Formatted Name");
0673 }
0674 
0675 void Addressee::setFamilyName(const QString &familyName)
0676 {
0677     if (familyName == d->mFamilyName) {
0678         return;
0679     }
0680 
0681     d->mEmpty = false;
0682     d->mFamilyName = familyName;
0683 }
0684 
0685 QString Addressee::familyName() const
0686 {
0687     return d->mFamilyName;
0688 }
0689 
0690 QString Addressee::familyNameLabel()
0691 {
0692     return i18n("Family Name");
0693 }
0694 
0695 void Addressee::setGivenName(const QString &givenName)
0696 {
0697     if (givenName == d->mGivenName) {
0698         return;
0699     }
0700 
0701     d->mEmpty = false;
0702     d->mGivenName = givenName;
0703 }
0704 
0705 QString Addressee::givenName() const
0706 {
0707     return d->mGivenName;
0708 }
0709 
0710 QString Addressee::givenNameLabel()
0711 {
0712     return i18n("Given Name");
0713 }
0714 
0715 void Addressee::setAdditionalName(const QString &additionalName)
0716 {
0717     if (additionalName == d->mAdditionalName) {
0718         return;
0719     }
0720 
0721     d->mEmpty = false;
0722     d->mAdditionalName = additionalName;
0723 }
0724 
0725 QString Addressee::additionalName() const
0726 {
0727     return d->mAdditionalName;
0728 }
0729 
0730 QString Addressee::additionalNameLabel()
0731 {
0732     return i18n("Additional Names");
0733 }
0734 
0735 void Addressee::setPrefix(const QString &prefix)
0736 {
0737     if (prefix == d->mPrefix) {
0738         return;
0739     }
0740 
0741     d->mEmpty = false;
0742     d->mPrefix = prefix;
0743 }
0744 
0745 QString Addressee::prefix() const
0746 {
0747     return d->mPrefix;
0748 }
0749 
0750 QString Addressee::prefixLabel()
0751 {
0752     return i18n("Honorific Prefixes");
0753 }
0754 
0755 void Addressee::setSuffix(const QString &suffix)
0756 {
0757     if (suffix == d->mSuffix) {
0758         return;
0759     }
0760 
0761     d->mEmpty = false;
0762     d->mSuffix = suffix;
0763 }
0764 
0765 QString Addressee::suffix() const
0766 {
0767     return d->mSuffix;
0768 }
0769 
0770 QString Addressee::suffixLabel()
0771 {
0772     return i18n("Honorific Suffixes");
0773 }
0774 
0775 void Addressee::setNickName(const QString &nickName)
0776 {
0777     NickName t(nickName);
0778     if (!d->mNickNameExtraList.isEmpty()) {
0779         t = d->mNickNameExtraList.takeFirst();
0780         t.setNickName(nickName);
0781         d->mNickNameExtraList.prepend(t);
0782         d->mEmpty = false;
0783     } else {
0784         insertExtraNickName(t);
0785     }
0786 }
0787 
0788 void Addressee::setNickName(const NickName &nickName)
0789 {
0790     insertExtraNickName(nickName);
0791 }
0792 
0793 void Addressee::insertExtraNickName(const NickName &nickName)
0794 {
0795     if (nickName.isValid()) {
0796         d->mEmpty = false;
0797         d->mNickNameExtraList.append(nickName);
0798     }
0799 }
0800 
0801 void Addressee::setExtraNickNameList(const NickName::List &nickNameList)
0802 {
0803     d->mEmpty = false;
0804     d->mNickNameExtraList = nickNameList;
0805 }
0806 
0807 NickName::List Addressee::extraNickNameList() const
0808 {
0809     return d->mNickNameExtraList;
0810 }
0811 
0812 QString Addressee::nickName() const
0813 {
0814     if (d->mNickNameExtraList.isEmpty()) {
0815         return {};
0816     } else {
0817         return d->mNickNameExtraList.at(0).nickname();
0818     }
0819 }
0820 
0821 QString Addressee::nickNameLabel()
0822 {
0823     return i18n("Nick Name");
0824 }
0825 
0826 void Addressee::setBirthday(const QDateTime &birthday, bool withTime)
0827 {
0828     if (birthday == d->mBirthday && d->mBirthdayWithTime == withTime) {
0829         return;
0830     }
0831 
0832     d->mEmpty = false;
0833     d->mBirthday = birthday;
0834     if (!withTime) {
0835         d->mBirthday.setTime(QTime());
0836     }
0837     d->mBirthdayWithTime = withTime;
0838 }
0839 
0840 void Addressee::setBirthday(const QDate &birthday)
0841 {
0842     if (birthday == d->mBirthday.date() && !d->mBirthdayWithTime) {
0843         return;
0844     }
0845 
0846     d->mEmpty = false;
0847     d->mBirthday = QDateTime(birthday, QTime());
0848     d->mBirthdayWithTime = false;
0849 }
0850 
0851 QDateTime Addressee::birthday() const
0852 {
0853     return d->mBirthday;
0854 }
0855 
0856 bool Addressee::birthdayHasTime() const
0857 {
0858     return d->mBirthdayWithTime;
0859 }
0860 
0861 QString Addressee::birthdayLabel()
0862 {
0863     return i18n("Birthday");
0864 }
0865 
0866 QString Addressee::homeAddressStreetLabel()
0867 {
0868     return i18n("Home Address Street");
0869 }
0870 
0871 QString Addressee::homeAddressPostOfficeBoxLabel()
0872 {
0873     return i18n("Home Address Post Office Box");
0874 }
0875 
0876 QString Addressee::homeAddressLocalityLabel()
0877 {
0878     return i18n("Home Address City");
0879 }
0880 
0881 QString Addressee::homeAddressRegionLabel()
0882 {
0883     return i18n("Home Address State");
0884 }
0885 
0886 QString Addressee::homeAddressPostalCodeLabel()
0887 {
0888     return i18n("Home Address Zip Code");
0889 }
0890 
0891 QString Addressee::homeAddressCountryLabel()
0892 {
0893     return i18n("Home Address Country");
0894 }
0895 
0896 QString Addressee::homeAddressLabelLabel()
0897 {
0898     return i18n("Home Address Label");
0899 }
0900 
0901 QString Addressee::businessAddressStreetLabel()
0902 {
0903     return i18n("Business Address Street");
0904 }
0905 
0906 QString Addressee::businessAddressPostOfficeBoxLabel()
0907 {
0908     return i18n("Business Address Post Office Box");
0909 }
0910 
0911 QString Addressee::businessAddressLocalityLabel()
0912 {
0913     return i18n("Business Address City");
0914 }
0915 
0916 QString Addressee::businessAddressRegionLabel()
0917 {
0918     return i18n("Business Address State");
0919 }
0920 
0921 QString Addressee::businessAddressPostalCodeLabel()
0922 {
0923     return i18n("Business Address Zip Code");
0924 }
0925 
0926 QString Addressee::businessAddressCountryLabel()
0927 {
0928     return i18n("Business Address Country");
0929 }
0930 
0931 QString Addressee::businessAddressLabelLabel()
0932 {
0933     return i18n("Business Address Label");
0934 }
0935 
0936 QString Addressee::homePhoneLabel()
0937 {
0938     return i18n("Home Phone");
0939 }
0940 
0941 QString Addressee::businessPhoneLabel()
0942 {
0943     return i18n("Business Phone");
0944 }
0945 
0946 QString Addressee::mobilePhoneLabel()
0947 {
0948     return i18n("Mobile Phone");
0949 }
0950 
0951 QString Addressee::homeFaxLabel()
0952 {
0953     return i18n("Home Fax");
0954 }
0955 
0956 QString Addressee::businessFaxLabel()
0957 {
0958     return i18n("Business Fax");
0959 }
0960 
0961 QString Addressee::carPhoneLabel()
0962 {
0963     return i18n("Car Phone");
0964 }
0965 
0966 QString Addressee::isdnLabel()
0967 {
0968     return i18n("ISDN");
0969 }
0970 
0971 QString Addressee::pagerLabel()
0972 {
0973     return i18n("Pager");
0974 }
0975 
0976 QString Addressee::emailLabel()
0977 {
0978     return i18n("Email Address");
0979 }
0980 
0981 void Addressee::setMailer(const QString &mailer)
0982 {
0983     if (mailer == d->mMailer) {
0984         return;
0985     }
0986 
0987     d->mEmpty = false;
0988     d->mMailer = mailer;
0989 }
0990 
0991 QString Addressee::mailer() const
0992 {
0993     return d->mMailer;
0994 }
0995 
0996 QString Addressee::mailerLabel()
0997 {
0998     return i18n("Mail Client");
0999 }
1000 
1001 void Addressee::setTimeZone(const TimeZone &timeZone)
1002 {
1003     if (timeZone == d->mTimeZone) {
1004         return;
1005     }
1006 
1007     d->mEmpty = false;
1008     d->mTimeZone = timeZone;
1009 }
1010 
1011 TimeZone Addressee::timeZone() const
1012 {
1013     return d->mTimeZone;
1014 }
1015 
1016 QString Addressee::timeZoneLabel()
1017 {
1018     return i18n("Time Zone");
1019 }
1020 
1021 void Addressee::setGeo(const Geo &geo)
1022 {
1023     if (geo == d->mGeo) {
1024         return;
1025     }
1026 
1027     d->mEmpty = false;
1028     d->mGeo = geo;
1029 }
1030 
1031 Geo Addressee::geo() const
1032 {
1033     return d->mGeo;
1034 }
1035 
1036 QString Addressee::geoLabel()
1037 {
1038     return i18n("Geographic Position");
1039 }
1040 
1041 void Addressee::setTitle(const QString &title)
1042 {
1043     Title t(title);
1044     if (!d->mTitleExtraList.isEmpty()) {
1045         t = d->mTitleExtraList.takeFirst();
1046         t.setTitle(title);
1047         d->mTitleExtraList.prepend(t);
1048         d->mEmpty = false;
1049     } else {
1050         insertExtraTitle(title);
1051     }
1052 }
1053 
1054 void Addressee::setTitle(const Title &title)
1055 {
1056     insertExtraTitle(title);
1057 }
1058 
1059 void Addressee::insertExtraTitle(const Title &title)
1060 {
1061     if (title.isValid()) {
1062         d->mEmpty = false;
1063         d->mTitleExtraList.append(title);
1064     }
1065 }
1066 
1067 QString Addressee::title() const
1068 {
1069     if (d->mTitleExtraList.isEmpty()) {
1070         return {};
1071     } else {
1072         return d->mTitleExtraList.at(0).title();
1073     }
1074 }
1075 
1076 Title::List Addressee::extraTitleList() const
1077 {
1078     return d->mTitleExtraList;
1079 }
1080 
1081 void Addressee::setExtraTitleList(const Title::List &urltitle)
1082 {
1083     d->mEmpty = false;
1084     d->mTitleExtraList = urltitle;
1085 }
1086 
1087 QString Addressee::titleLabel()
1088 {
1089     return i18nc("a person's title", "Title");
1090 }
1091 
1092 void Addressee::setRole(const QString &role)
1093 {
1094     Role t(role);
1095     if (!d->mRoleExtraList.isEmpty()) {
1096         t = d->mRoleExtraList.takeFirst();
1097         t.setRole(role);
1098         d->mRoleExtraList.prepend(t);
1099         d->mEmpty = false;
1100     } else {
1101         insertExtraRole(t);
1102     }
1103 }
1104 
1105 void Addressee::setRole(const Role &role)
1106 {
1107     insertExtraRole(role);
1108 }
1109 
1110 void Addressee::insertExtraRole(const Role &role)
1111 {
1112     if (role.isValid()) {
1113         d->mEmpty = false;
1114         d->mRoleExtraList.append(role);
1115     }
1116 }
1117 
1118 void Addressee::setExtraRoleList(const Role::List &roleList)
1119 {
1120     d->mEmpty = false;
1121     d->mRoleExtraList = roleList;
1122 }
1123 
1124 Role::List Addressee::extraRoleList() const
1125 {
1126     return d->mRoleExtraList;
1127 }
1128 
1129 QString Addressee::role() const
1130 {
1131     if (d->mRoleExtraList.isEmpty()) {
1132         return {};
1133     } else {
1134         return d->mRoleExtraList.at(0).role();
1135     }
1136 }
1137 
1138 QString Addressee::roleLabel()
1139 {
1140     return i18nc("of a person in an organization", "Role");
1141 }
1142 
1143 void Addressee::setOrganization(const QString &organization)
1144 {
1145     Org t(organization);
1146     if (!d->mOrgExtraList.isEmpty()) {
1147         t = d->mOrgExtraList.takeFirst();
1148         t.setOrganization(organization);
1149         d->mOrgExtraList.prepend(t);
1150         d->mEmpty = false;
1151     } else {
1152         insertExtraOrganization(t);
1153     }
1154 }
1155 
1156 void Addressee::setOrganization(const Org &organization)
1157 {
1158     insertExtraOrganization(organization);
1159 }
1160 
1161 void Addressee::insertExtraOrganization(const Org &organization)
1162 {
1163     if (organization.isValid()) {
1164         d->mEmpty = false;
1165         d->mOrgExtraList.append(organization);
1166     }
1167 }
1168 
1169 void Addressee::setExtraOrganizationList(const Org::List &orgList)
1170 {
1171     d->mEmpty = false;
1172     d->mOrgExtraList = orgList;
1173 }
1174 
1175 Org::List Addressee::extraOrganizationList() const
1176 {
1177     return d->mOrgExtraList;
1178 }
1179 
1180 QString Addressee::organization() const
1181 {
1182     if (d->mOrgExtraList.isEmpty()) {
1183         return {};
1184     } else {
1185         return d->mOrgExtraList.at(0).organization();
1186     }
1187 }
1188 
1189 QString Addressee::organizationLabel()
1190 {
1191     return i18n("Organization");
1192 }
1193 
1194 void Addressee::setDepartment(const QString &department)
1195 {
1196     if (department == d->mDepartment) {
1197         return;
1198     }
1199 
1200     d->mEmpty = false;
1201     d->mDepartment = department;
1202 }
1203 
1204 QString Addressee::department() const
1205 {
1206     return d->mDepartment;
1207 }
1208 
1209 QString Addressee::departmentLabel()
1210 {
1211     return i18n("Department");
1212 }
1213 
1214 void Addressee::setNote(const QString &note)
1215 {
1216     if (note == d->mNote) {
1217         return;
1218     }
1219 
1220     d->mEmpty = false;
1221     d->mNote = note;
1222 }
1223 
1224 QString Addressee::note() const
1225 {
1226     return d->mNote;
1227 }
1228 
1229 QString Addressee::noteLabel()
1230 {
1231     return i18n("Note");
1232 }
1233 
1234 void Addressee::setProductId(const QString &productId)
1235 {
1236     if (productId == d->mProductId) {
1237         return;
1238     }
1239 
1240     d->mEmpty = false;
1241     d->mProductId = productId;
1242 }
1243 
1244 QString Addressee::productId() const
1245 {
1246     return d->mProductId;
1247 }
1248 
1249 QString Addressee::productIdLabel()
1250 {
1251     return i18n("Product Identifier");
1252 }
1253 
1254 void Addressee::setRevision(const QDateTime &revision)
1255 {
1256     if (revision == d->mRevision) {
1257         return;
1258     }
1259 
1260     d->mEmpty = false;
1261     d->mRevision = revision;
1262 }
1263 
1264 QDateTime Addressee::revision() const
1265 {
1266     return d->mRevision;
1267 }
1268 
1269 QString Addressee::revisionLabel()
1270 {
1271     return i18n("Revision Date");
1272 }
1273 
1274 void Addressee::setSortString(const QString &sortString)
1275 {
1276     if (sortString == d->mSortString) {
1277         return;
1278     }
1279 
1280     d->mEmpty = false;
1281     d->mSortString = sortString;
1282 }
1283 
1284 QString Addressee::sortString() const
1285 {
1286     return d->mSortString;
1287 }
1288 
1289 QString Addressee::sortStringLabel()
1290 {
1291     return i18n("Sort String");
1292 }
1293 
1294 void Addressee::setUrl(const QUrl &url)
1295 {
1296     KContacts::ResourceLocatorUrl resourceLocator;
1297     resourceLocator.setUrl(url);
1298     insertExtraUrl(resourceLocator);
1299 }
1300 
1301 void Addressee::setUrl(const ResourceLocatorUrl &url)
1302 {
1303     insertExtraUrl(url);
1304 }
1305 
1306 ResourceLocatorUrl Addressee::url() const
1307 {
1308     if (d->mUrlExtraList.isEmpty()) {
1309         return ResourceLocatorUrl();
1310     } else {
1311         return d->mUrlExtraList.at(0);
1312     }
1313 }
1314 
1315 QString Addressee::urlLabel()
1316 {
1317     return i18n("Homepage");
1318 }
1319 
1320 void Addressee::setSecrecy(const Secrecy &secrecy)
1321 {
1322     if (secrecy == d->mSecrecy) {
1323         return;
1324     }
1325 
1326     d->mEmpty = false;
1327     d->mSecrecy = secrecy;
1328 }
1329 
1330 Secrecy Addressee::secrecy() const
1331 {
1332     return d->mSecrecy;
1333 }
1334 
1335 QString Addressee::secrecyLabel()
1336 {
1337     return i18n("Security Class");
1338 }
1339 
1340 void Addressee::setLogo(const Picture &logo)
1341 {
1342     if (logo == d->mLogo) {
1343         return;
1344     }
1345 
1346     d->mEmpty = false;
1347     d->mLogo = logo;
1348 }
1349 
1350 Picture Addressee::logo() const
1351 {
1352     return d->mLogo;
1353 }
1354 
1355 QString Addressee::logoLabel()
1356 {
1357     return i18n("Logo");
1358 }
1359 
1360 void Addressee::setPhoto(const Picture &photo)
1361 {
1362     if (photo == d->mPhoto) {
1363         return;
1364     }
1365 
1366     d->mEmpty = false;
1367     d->mPhoto = photo;
1368 }
1369 
1370 Picture Addressee::photo() const
1371 {
1372     return d->mPhoto;
1373 }
1374 
1375 QString Addressee::photoLabel()
1376 {
1377     return i18n("Photo");
1378 }
1379 
1380 void Addressee::setSound(const Sound &sound)
1381 {
1382     if (sound == d->mSound) {
1383         return;
1384     }
1385 
1386     d->mEmpty = false;
1387     d->mSound = sound;
1388 }
1389 
1390 Sound Addressee::sound() const
1391 {
1392     return d->mSound;
1393 }
1394 
1395 QString Addressee::soundLabel()
1396 {
1397     return i18n("Sound");
1398 }
1399 
1400 void Addressee::setNameFromString(const QString &s)
1401 {
1402     QString str = s;
1403     // remove enclosing quotes from string
1404     if (str.length() > 1 && s[0] == QLatin1Char('"') && s[s.length() - 1] == QLatin1Char('"')) {
1405         str = s.mid(1, s.length() - 2);
1406     }
1407 
1408     setFormattedName(str);
1409     setName(str);
1410 
1411     // clear all name parts
1412     setPrefix(QString());
1413     setGivenName(QString());
1414     setAdditionalName(QString());
1415     setFamilyName(QString());
1416     setSuffix(QString());
1417 
1418     if (str.isEmpty()) {
1419         return;
1420     }
1421 
1422     static QString spaceStr = QStringLiteral(" ");
1423     static QString emptyStr = QStringLiteral("");
1424     AddresseeHelper *helper = AddresseeHelper::self();
1425 
1426     int i = str.indexOf(QLatin1Char(','));
1427     if (i < 0) {
1428         QStringList parts = str.split(spaceStr);
1429         int leftOffset = 0;
1430         int rightOffset = parts.count() - 1;
1431 
1432         QString suffix;
1433         while (rightOffset >= 0) {
1434             if (helper->containsSuffix(parts[rightOffset])) {
1435                 suffix.prepend(parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
1436                 rightOffset--;
1437             } else {
1438                 break;
1439             }
1440         }
1441         setSuffix(suffix);
1442 
1443         if (rightOffset < 0) {
1444             return;
1445         }
1446 
1447         if (rightOffset - 1 >= 0 && helper->containsPrefix(parts[rightOffset - 1].toLower())) {
1448             setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
1449             rightOffset--;
1450         } else {
1451             if (helper->treatAsFamilyName()) {
1452                 setFamilyName(parts[rightOffset]);
1453             } else {
1454                 setGivenName(parts[rightOffset]);
1455             }
1456         }
1457 
1458         QString prefix;
1459         while (leftOffset < rightOffset) {
1460             if (helper->containsTitle(parts[leftOffset])) {
1461                 prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1462                 leftOffset++;
1463             } else {
1464                 break;
1465             }
1466         }
1467         setPrefix(prefix);
1468 
1469         if (leftOffset < rightOffset) {
1470             setGivenName(parts[leftOffset]);
1471             leftOffset++;
1472         }
1473 
1474         QString additionalName;
1475         while (leftOffset < rightOffset) {
1476             additionalName.append((additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1477             leftOffset++;
1478         }
1479         setAdditionalName(additionalName);
1480     } else {
1481         QString part1 = str.left(i);
1482         QString part2 = str.mid(i + 1);
1483 
1484         QStringList parts = part1.split(spaceStr);
1485         int leftOffset = 0;
1486         int rightOffset = parts.count() - 1;
1487 
1488         if (!parts.isEmpty()) {
1489             QString suffix;
1490             while (rightOffset >= 0) {
1491                 if (helper->containsSuffix(parts[rightOffset])) {
1492                     suffix.prepend(parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
1493                     rightOffset--;
1494                 } else {
1495                     break;
1496                 }
1497             }
1498             setSuffix(suffix);
1499 
1500             if (rightOffset - 1 >= 0 && helper->containsPrefix(parts[rightOffset - 1].toLower())) {
1501                 setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
1502                 rightOffset--;
1503             } else {
1504                 setFamilyName(parts[rightOffset]);
1505             }
1506 
1507             QString prefix;
1508             while (leftOffset < rightOffset) {
1509                 if (helper->containsTitle(parts[leftOffset])) {
1510                     prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1511                     leftOffset++;
1512                 } else {
1513                     break;
1514                 }
1515             }
1516         } else {
1517             setPrefix(QString());
1518             setFamilyName(QString());
1519             setSuffix(QString());
1520         }
1521 
1522         parts = part2.split(spaceStr);
1523 
1524         leftOffset = 0;
1525         rightOffset = parts.count();
1526 
1527         if (!parts.isEmpty()) {
1528             QString prefix;
1529             while (leftOffset < rightOffset) {
1530                 if (helper->containsTitle(parts[leftOffset])) {
1531                     prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1532                     leftOffset++;
1533                 } else {
1534                     break;
1535                 }
1536             }
1537             setPrefix(prefix);
1538 
1539             if (leftOffset < rightOffset) {
1540                 setGivenName(parts[leftOffset]);
1541                 leftOffset++;
1542             }
1543 
1544             QString additionalName;
1545             while (leftOffset < rightOffset) {
1546                 additionalName.append((additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1547                 leftOffset++;
1548             }
1549             setAdditionalName(additionalName);
1550         } else {
1551             setGivenName(QString());
1552             setAdditionalName(QString());
1553         }
1554     }
1555 }
1556 
1557 QString Addressee::realName() const
1558 {
1559     QString n(formattedName());
1560     if (!n.isEmpty()) {
1561         return n;
1562     }
1563 
1564     n = assembledName();
1565     if (!n.isEmpty()) {
1566         return n;
1567     }
1568 
1569     n = name();
1570     if (!n.isEmpty()) {
1571         return n;
1572     }
1573 
1574     return organization();
1575 }
1576 
1577 QString Addressee::assembledName() const
1578 {
1579     // clang-format off
1580     const QString name = prefix() + QLatin1Char(' ')
1581                          + givenName() + QLatin1Char(' ')
1582                          + additionalName() + QLatin1Char(' ')
1583                          + familyName() + QLatin1Char(' ')
1584                          + suffix();
1585     // clang-format on
1586 
1587     return name.simplified();
1588 }
1589 
1590 QString Addressee::fullEmail(const QString &email) const
1591 {
1592     QString e;
1593     if (email.isNull()) {
1594         e = preferredEmail();
1595     } else {
1596         e = email;
1597     }
1598     if (e.isEmpty()) {
1599         return QString();
1600     }
1601 
1602     QString text;
1603     if (realName().isEmpty()) {
1604         text = e;
1605     } else {
1606         QRegularExpression needQuotes(QStringLiteral("[^ 0-9A-Za-z\\x{0080}-\\x{FFFF}]"));
1607         if (realName().indexOf(needQuotes) != -1) {
1608             QString name = realName();
1609             name.replace(QLatin1String("\""), QLatin1String("\\\""));
1610             text = QLatin1String("\"") + name + QLatin1String("\" <") + e + QLatin1Char('>');
1611         } else {
1612             text = realName() + QLatin1String(" <") + e + QLatin1Char('>');
1613         }
1614     }
1615 
1616     return text;
1617 }
1618 
1619 void Addressee::addEmail(const Email &email)
1620 {
1621     const QString mailAddr = email.mail();
1622     auto it = std::find_if(d->mEmails.begin(), d->mEmails.end(), [&mailAddr](const Email &e) {
1623         return e.mail() == mailAddr;
1624     });
1625     if (it != d->mEmails.end()) { // Already exists, modify it
1626         *it = email;
1627         if (email.isPreferred()) {
1628             // Move it to the beginning of mEmails
1629             std::rotate(d->mEmails.begin(), it, it + 1);
1630         }
1631         return;
1632     }
1633 
1634     // Add it to the list
1635     d->mEmpty = false;
1636     if (email.isPreferred()) {
1637         d->mEmails.prepend(email);
1638     } else {
1639         d->mEmails.append(email);
1640     }
1641 }
1642 
1643 #if KCONTACTS_BUILD_DEPRECATED_SINCE(5, 88)
1644 void Addressee::insertEmail(const QString &email, bool preferred, const QMap<QString, QStringList> &param)
1645 {
1646     if (email.simplified().isEmpty()) {
1647         return;
1648     }
1649 
1650     for (int i = 0; i < d->mEmails.size(); ++i) {
1651         if (d->mEmails.at(i).mail() == email) {
1652             if (!preferred || i == 0) {
1653                 return;
1654             }
1655             Email tempMail = d->mEmails.takeAt(i);
1656             d->mEmails.prepend(tempMail);
1657             return;
1658         }
1659     }
1660 
1661     Email mail(email);
1662     mail.setParameters(param);
1663     d->mEmpty = false;
1664     if (preferred) {
1665         d->mEmails.prepend(mail);
1666     } else {
1667         d->mEmails.append(mail);
1668     }
1669 }
1670 #endif
1671 
1672 void Addressee::removeEmail(const QString &email)
1673 {
1674     for (int i = 0; i < d->mEmails.size(); ++i) {
1675         if (d->mEmails.at(i).mail() == email) {
1676             d->mEmails.removeAt(i);
1677         }
1678     }
1679 }
1680 
1681 QString Addressee::preferredEmail() const
1682 {
1683     if (d->mEmails.isEmpty()) {
1684         return QString();
1685     } else {
1686         return d->mEmails.first().mail();
1687     }
1688 }
1689 
1690 QStringList Addressee::emails() const
1691 {
1692     QStringList list;
1693     const int numberOfEmail = d->mEmails.size();
1694     list.reserve(numberOfEmail);
1695     for (int i = 0; i < numberOfEmail; ++i) {
1696         list << d->mEmails.at(i).mail();
1697     }
1698 
1699     return list;
1700 }
1701 
1702 Email::List Addressee::emailList() const
1703 {
1704     return d->mEmails;
1705 }
1706 
1707 void Addressee::setEmails(const QStringList &emails)
1708 {
1709     d->mEmails.clear();
1710     const int numEmails = emails.size();
1711     d->mEmails.reserve(numEmails);
1712     for (int i = 0; i < numEmails; ++i) {
1713         d->mEmails.append(Email(emails.at(i)));
1714     }
1715     d->mEmpty = false;
1716 }
1717 
1718 void Addressee::setEmailList(const Email::List &list)
1719 {
1720     d->mEmails = list;
1721     d->mEmpty = false;
1722 }
1723 
1724 void Addressee::removeLang(const QString &language)
1725 {
1726     for (int i = 0; i < d->mLangs.size(); ++i) {
1727         if (d->mLangs.at(i).language() == language) {
1728             d->mLangs.removeAt(i);
1729         }
1730     }
1731 }
1732 
1733 void Addressee::setLangs(const Lang::List &langs)
1734 {
1735     d->mLangs = langs;
1736     d->mEmpty = false;
1737 }
1738 
1739 void Addressee::insertLang(const Lang &language)
1740 {
1741     const QString languageStr = language.language();
1742     if (languageStr.simplified().isEmpty()) {
1743         return;
1744     }
1745     d->mEmpty = false;
1746 
1747     auto it = std::find_if(d->mLangs.begin(), d->mLangs.end(), [&languageStr](const Lang &lang) {
1748         return lang.language() == languageStr;
1749     });
1750     if (it != d->mLangs.end()) {
1751         (*it).setParams(language.params());
1752         return;
1753     }
1754 
1755     d->mLangs.append(language);
1756 }
1757 
1758 Lang::List Addressee::langs() const
1759 {
1760     return d->mLangs;
1761 }
1762 
1763 void Addressee::setGender(const Gender &gender)
1764 {
1765     if (gender == d->mGender) {
1766         return;
1767     }
1768 
1769     d->mEmpty = false;
1770     d->mGender = gender;
1771 }
1772 
1773 Gender Addressee::gender() const
1774 {
1775     return d->mGender;
1776 }
1777 
1778 void Addressee::insertPhoneNumber(const PhoneNumber &phoneNumber)
1779 {
1780     d->mEmpty = false;
1781 
1782     auto it = std::find_if(d->mPhoneNumbers.begin(), d->mPhoneNumbers.end(), [&phoneNumber](const PhoneNumber &pNumber) {
1783         return pNumber.id() == phoneNumber.id();
1784     });
1785     if (it != d->mPhoneNumbers.end()) {
1786         *it = phoneNumber;
1787         return;
1788     }
1789 
1790     if (!phoneNumber.number().simplified().isEmpty()) {
1791         d->mPhoneNumbers.append(phoneNumber);
1792     }
1793 }
1794 
1795 void Addressee::removePhoneNumber(const PhoneNumber &phoneNumber)
1796 {
1797     auto it = std::find_if(d->mPhoneNumbers.begin(), d->mPhoneNumbers.end(), [&phoneNumber](const PhoneNumber &p) {
1798         return p.id() == phoneNumber.id();
1799     });
1800 
1801     if (it != d->mPhoneNumbers.end()) {
1802         d->mPhoneNumbers.erase(it);
1803     }
1804 }
1805 
1806 PhoneNumber Addressee::phoneNumber(PhoneNumber::Type type) const
1807 {
1808     PhoneNumber phoneNumber(QString(), type);
1809 
1810     for (const PhoneNumber &phone : d->mPhoneNumbers) {
1811         if (matchBinaryPattern(phone.type(), type)) {
1812             if (phone.type() & PhoneNumber::Pref) {
1813                 return phone;
1814             } else if (phoneNumber.number().isEmpty()) {
1815                 phoneNumber = phone;
1816             }
1817         }
1818     }
1819 
1820     return phoneNumber;
1821 }
1822 
1823 PhoneNumber::List Addressee::phoneNumbers() const
1824 {
1825     return d->mPhoneNumbers;
1826 }
1827 
1828 void Addressee::setPhoneNumbers(const PhoneNumber::List &phoneNumbers)
1829 {
1830     d->mEmpty = false;
1831     d->mPhoneNumbers.clear();
1832     d->mPhoneNumbers = phoneNumbers;
1833 }
1834 
1835 PhoneNumber::List Addressee::phoneNumbers(PhoneNumber::Type type) const
1836 {
1837     PhoneNumber::List list;
1838 
1839     std::copy_if(d->mPhoneNumbers.cbegin(), d->mPhoneNumbers.cend(), std::back_inserter(list), [type](const auto &phone) {
1840         return matchBinaryPattern(phone.type(), type);
1841     });
1842     return list;
1843 }
1844 
1845 PhoneNumber Addressee::findPhoneNumber(const QString &id) const
1846 {
1847     auto it = std::find_if(d->mPhoneNumbers.cbegin(), d->mPhoneNumbers.cend(), [&id](const PhoneNumber &phone) {
1848         return phone.id() == id;
1849     });
1850 
1851     return it != d->mPhoneNumbers.cend() ? *it : PhoneNumber{};
1852 }
1853 
1854 void Addressee::insertKey(const Key &key)
1855 {
1856     d->mEmpty = false;
1857 
1858     auto it = std::find_if(d->mKeys.begin(), d->mKeys.end(), [&key](Key &existing) {
1859         return existing.id() == key.id();
1860     });
1861     if (it != d->mKeys.end()) {
1862         *it = key;
1863     } else {
1864         d->mKeys.append(key);
1865     }
1866 }
1867 
1868 void Addressee::removeKey(const Key &key)
1869 {
1870     auto it = std::remove_if(d->mKeys.begin(), d->mKeys.end(), [&key](const Key &k) {
1871         return k.id() == key.id();
1872     });
1873     d->mKeys.erase(it, d->mKeys.end());
1874 }
1875 
1876 Key Addressee::key(Key::Type type, const QString &customTypeString) const
1877 {
1878     for (const auto &key : d->mKeys) {
1879         if (key.type() == type) {
1880             if (type == Key::Custom) {
1881                 if (customTypeString.isEmpty()) {
1882                     return key;
1883                 } else {
1884                     if (key.customTypeString() == customTypeString) {
1885                         return key;
1886                     }
1887                 }
1888             } else {
1889                 return key;
1890             }
1891         }
1892     }
1893     return Key(QString(), type);
1894 }
1895 
1896 void Addressee::setKeys(const Key::List &list)
1897 {
1898     d->mKeys = list;
1899     d->mEmpty = false;
1900 }
1901 
1902 Key::List Addressee::keys() const
1903 {
1904     return d->mKeys;
1905 }
1906 
1907 Key::List Addressee::keys(Key::Type type, const QString &customTypeString) const
1908 {
1909     Key::List list;
1910     auto matchFunc = [type, &customTypeString](const Key &key) {
1911         if (key.type() == type) {
1912             if (type == Key::Custom) {
1913                 if (customTypeString.isEmpty()) {
1914                     return true;
1915                 } else {
1916                     if (key.customTypeString() == customTypeString) {
1917                         return true;
1918                     }
1919                 }
1920             } else {
1921                 return true;
1922             }
1923         }
1924         return false;
1925     };
1926 
1927     std::copy_if(d->mKeys.cbegin(), d->mKeys.cend(), std::back_inserter(list), matchFunc);
1928 
1929     return list;
1930 }
1931 
1932 Key Addressee::findKey(const QString &id) const
1933 {
1934     auto it = std::find_if(d->mKeys.cbegin(), d->mKeys.cend(), [&id](const Key &key) {
1935         return key.id() == id;
1936     });
1937 
1938     return it != d->mKeys.cend() ? *it : Key{};
1939 }
1940 
1941 QString Addressee::toString() const
1942 {
1943     QString str = QLatin1String("Addressee {\n");
1944     str += QStringLiteral("  Uid: %1\n").arg(uid());
1945 
1946     str += QStringLiteral("  Name: %1\n").arg(name());
1947     str += QStringLiteral("  FormattedName: %1\n").arg(formattedName());
1948     str += QStringLiteral("  FamilyName: %1\n").arg(familyName());
1949     str += QStringLiteral("  GivenName: %1\n").arg(givenName());
1950     str += QStringLiteral("  AdditionalName: %1\n").arg(additionalName());
1951     str += QStringLiteral("  Prefix: %1\n").arg(prefix());
1952     str += QStringLiteral("  Suffix: %1\n").arg(suffix());
1953     str += QStringLiteral("  NickName: %1\n").arg(nickName());
1954     str += QStringLiteral("  Birthday: %1\n").arg(birthday().toString());
1955     str += QStringLiteral("  Mailer: %1\n").arg(mailer());
1956     str += QStringLiteral("  TimeZone: %1\n").arg(timeZone().toString());
1957     str += QStringLiteral("  Geo: %1\n").arg(geo().toString());
1958     str += QStringLiteral("  Title: %1\n").arg(title());
1959     str += QStringLiteral("  Role: %1\n").arg(role());
1960     str += QStringLiteral("  Organization: %1\n").arg(organization());
1961     str += QStringLiteral("  Department: %1\n").arg(department());
1962     str += QStringLiteral("  Note: %1\n").arg(note());
1963     str += QStringLiteral("  ProductId: %1\n").arg(productId());
1964     str += QStringLiteral("  Revision: %1\n").arg(revision().toString());
1965     str += QStringLiteral("  SortString: %1\n").arg(sortString());
1966     str += QStringLiteral("  Url: %1\n").arg(url().url().url());
1967     str += QStringLiteral("  Secrecy: %1\n").arg(secrecy().toString());
1968     str += QStringLiteral("  Logo: %1\n").arg(logo().toString());
1969     str += QStringLiteral("  Photo: %1\n").arg(photo().toString());
1970     str += QStringLiteral("  Sound: %1\n").arg(sound().toString());
1971     str += QStringLiteral("  Gender: %1\n").arg(gender().toString());
1972     str += QStringLiteral("  Kind: %1\n").arg(kind());
1973 
1974     str += QLatin1String("  Emails {\n");
1975     const Email::List listEmail = d->mEmails;
1976     for (const Email &email : listEmail) {
1977         str += email.toString();
1978     }
1979     str += QLatin1String("  }\n");
1980 
1981     str += QLatin1String("  Langs {\n");
1982     const Lang::List listLang = d->mLangs;
1983     for (const Lang &lang : listLang) {
1984         str += lang.toString();
1985     }
1986     str += QLatin1String("  }\n");
1987 
1988     str += QLatin1String("  PhoneNumbers {\n");
1989     const PhoneNumber::List phones = phoneNumbers();
1990     for (const auto &p : phones) {
1991         str += p.toString();
1992     }
1993     str += QLatin1String("  }\n");
1994 
1995     str += QLatin1String("  Addresses {\n");
1996     const Address::List addrList = addresses();
1997     for (const auto &addr : addrList) {
1998         str += addr.toString();
1999     }
2000     str += QLatin1String("  }\n");
2001 
2002     str += QLatin1String("  Keys {\n");
2003     const Key::List keyList = keys();
2004     for (const auto &k : keyList) {
2005         str += k.toString();
2006     }
2007     str += QLatin1String("  }\n");
2008 
2009     str += QLatin1String("}\n");
2010 
2011     return str;
2012 }
2013 
2014 void Addressee::insertAddress(const Address &address)
2015 {
2016     if (address.isEmpty()) {
2017         return;
2018     }
2019 
2020     d->mEmpty = false;
2021 
2022     auto it = std::find_if(d->mAddresses.begin(), d->mAddresses.end(), [&address](const Address &addr) {
2023         return addr.id() == address.id();
2024     });
2025     if (it != d->mAddresses.end()) {
2026         *it = address;
2027         return;
2028     }
2029 
2030     d->mAddresses.append(address);
2031 }
2032 
2033 void Addressee::removeAddress(const Address &address)
2034 {
2035     auto it = std::find_if(d->mAddresses.begin(), d->mAddresses.end(), [&address](const Address &addr) {
2036         return addr.id() == address.id();
2037     });
2038     if (it != d->mAddresses.end()) {
2039         d->mAddresses.erase(it);
2040     }
2041 }
2042 
2043 void Addressee::setAddresses(const Address::List &addresses)
2044 {
2045     d->mEmpty = false;
2046     d->mAddresses = addresses;
2047 }
2048 
2049 Address Addressee::address(Address::Type type) const
2050 {
2051     Address address(type);
2052     for (const Address &addr : d->mAddresses) {
2053         if (matchBinaryPattern(addr.type(), type)) {
2054             if (addr.type() & Address::Pref) {
2055                 return addr;
2056             } else if (address.isEmpty()) {
2057                 address = addr;
2058             }
2059         }
2060     }
2061 
2062     return address;
2063 }
2064 
2065 Address::List Addressee::addresses() const
2066 {
2067     return d->mAddresses;
2068 }
2069 
2070 Address::List Addressee::addresses(Address::Type type) const
2071 {
2072     Address::List list;
2073 
2074     std::copy_if(d->mAddresses.cbegin(), d->mAddresses.cend(), std::back_inserter(list), [type](const Address &addr) {
2075         return matchBinaryPattern(addr.type(), type);
2076     });
2077 
2078     return list;
2079 }
2080 
2081 Address Addressee::findAddress(const QString &id) const
2082 {
2083     auto it = std::find_if(d->mAddresses.cbegin(), d->mAddresses.cend(), [&id](const Address &addr) {
2084         return addr.id() == id;
2085     });
2086     return it != d->mAddresses.cend() ? *it : Address{};
2087 }
2088 
2089 void Addressee::insertCategory(const QString &c)
2090 {
2091     d->mEmpty = false;
2092 
2093     if (d->mCategories.contains(c)) {
2094         return;
2095     }
2096 
2097     d->mCategories.append(c);
2098 }
2099 
2100 void Addressee::removeCategory(const QString &category)
2101 {
2102     if (d->mCategories.contains(category)) {
2103         d->mCategories.removeAll(category);
2104     }
2105 }
2106 
2107 bool Addressee::hasCategory(const QString &category) const
2108 {
2109     return d->mCategories.contains(category);
2110 }
2111 
2112 void Addressee::setCategories(const QStringList &c)
2113 {
2114     d->mEmpty = false;
2115 
2116     d->mCategories = c;
2117 }
2118 
2119 QStringList Addressee::categories() const
2120 {
2121     return d->mCategories;
2122 }
2123 
2124 void Addressee::insertMember(const QString &member)
2125 {
2126     d->mEmpty = false;
2127 
2128     if (d->mMembers.contains(member)) {
2129         return;
2130     }
2131 
2132     d->mMembers.append(member);
2133 }
2134 
2135 void Addressee::setMembers(const QStringList &m)
2136 {
2137     d->mEmpty = false;
2138     d->mMembers = m;
2139 }
2140 
2141 QStringList Addressee::members() const
2142 {
2143     return d->mMembers;
2144 }
2145 
2146 void Addressee::insertRelationship(const Related &relation)
2147 {
2148     d->mEmpty = false;
2149 
2150     if (d->mRelationships.contains(relation)) {
2151         return;
2152     }
2153 
2154     d->mRelationships.append(relation);
2155 }
2156 
2157 void Addressee::setRelationships(const Related::List &c)
2158 {
2159     d->mEmpty = false;
2160     d->mRelationships = c;
2161 }
2162 
2163 Related::List Addressee::relationships() const
2164 {
2165     return d->mRelationships;
2166 }
2167 
2168 static const auto VENDOR_ID = QStringLiteral("KADDRESSBOOK");
2169 static const auto X_ANNIVERSARY = QStringLiteral("X-Anniversary");
2170 static const auto X_ASSISTANTSNAME = QStringLiteral("X-AssistantsName");
2171 static const auto BLOGFEED = QStringLiteral("BlogFeed");
2172 static const auto X_MANAGERSNAME = QStringLiteral("X-ManagersName");
2173 static const auto X_OFFICE = QStringLiteral("X-Office");
2174 static const auto X_PROFESSION = QStringLiteral("X-Profession");
2175 static const auto X_SPOUSESNAME = QStringLiteral("X-SpousesName");
2176 
2177 QDate Addressee::anniversary() const
2178 {
2179     return QDate::fromString(custom(VENDOR_ID, X_ANNIVERSARY), Qt::ISODate);
2180 }
2181 
2182 void Addressee::setAnniversary(const QDate &anniversary)
2183 {
2184     if (anniversary.isValid()) {
2185         insertCustom(VENDOR_ID, X_ANNIVERSARY, anniversary.toString(Qt::ISODate));
2186     } else {
2187         removeCustom(VENDOR_ID, X_ANNIVERSARY);
2188     }
2189 }
2190 
2191 QString Addressee::assistantsName() const
2192 {
2193     return custom(VENDOR_ID, X_ASSISTANTSNAME);
2194 }
2195 
2196 void Addressee::setAssistantsName(const QString &assistantsName)
2197 {
2198     if (!assistantsName.isEmpty()) {
2199         insertCustom(VENDOR_ID, X_ASSISTANTSNAME, assistantsName);
2200     } else {
2201         removeCustom(VENDOR_ID, X_ASSISTANTSNAME);
2202     }
2203 }
2204 
2205 QUrl Addressee::blogFeed() const
2206 {
2207     return QUrl(custom(VENDOR_ID, BLOGFEED));
2208 }
2209 
2210 void Addressee::setBlogFeed(const QUrl &blogFeed)
2211 {
2212     if (!blogFeed.isEmpty()) {
2213         insertCustom(VENDOR_ID, BLOGFEED, blogFeed.url());
2214     } else {
2215         removeCustom(VENDOR_ID, BLOGFEED);
2216     }
2217 }
2218 
2219 QString Addressee::managersName() const
2220 {
2221     return custom(VENDOR_ID, X_MANAGERSNAME);
2222 }
2223 
2224 void Addressee::setManagersName(const QString &managersName)
2225 {
2226     if (!managersName.isEmpty()) {
2227         insertCustom(VENDOR_ID, X_MANAGERSNAME, managersName);
2228     } else {
2229         removeCustom(VENDOR_ID, X_MANAGERSNAME);
2230     }
2231 }
2232 
2233 QString Addressee::office() const
2234 {
2235     return custom(VENDOR_ID, X_OFFICE);
2236 }
2237 
2238 void Addressee::setOffice(const QString &office)
2239 {
2240     if (!office.isEmpty()) {
2241         insertCustom(VENDOR_ID, X_OFFICE, office);
2242     } else {
2243         removeCustom(VENDOR_ID, X_OFFICE);
2244     }
2245 }
2246 
2247 QString Addressee::profession() const
2248 {
2249     return custom(VENDOR_ID, X_PROFESSION);
2250 }
2251 
2252 void Addressee::setProfession(const QString &profession)
2253 {
2254     if (!profession.isEmpty()) {
2255         insertCustom(VENDOR_ID, X_PROFESSION, profession);
2256     } else {
2257         removeCustom(VENDOR_ID, X_PROFESSION);
2258     }
2259 }
2260 
2261 QString Addressee::spousesName() const
2262 {
2263     return custom(VENDOR_ID, X_SPOUSESNAME);
2264 }
2265 
2266 void Addressee::setSpousesName(const QString &spousesName)
2267 {
2268     if (!spousesName.isEmpty()) {
2269         insertCustom(VENDOR_ID, X_SPOUSESNAME, spousesName);
2270     } else {
2271         removeCustom(VENDOR_ID, X_SPOUSESNAME);
2272     }
2273 }
2274 
2275 void Addressee::insertCustom(const QString &app, const QString &name, const QString &value)
2276 {
2277     if (value.isEmpty() || name.isEmpty() || app.isEmpty()) {
2278         return;
2279     }
2280 
2281     d->mEmpty = false;
2282 
2283     const QString qualifiedName = app + QLatin1Char('-') + name;
2284 
2285     auto it = d->findByName(qualifiedName);
2286     if (it != d->mCustomFields.end()) {
2287         it->value = value;
2288     } else {
2289         const CustomData newdata{qualifiedName, value};
2290         auto beforeIt = std::lower_bound(d->mCustomFields.begin(), d->mCustomFields.end(), newdata);
2291         d->mCustomFields.insert(beforeIt, newdata);
2292     }
2293 }
2294 
2295 void Addressee::removeCustom(const QString &app, const QString &name)
2296 {
2297     const QString qualifiedName = app + QLatin1Char('-') + name;
2298     auto it = d->findByName(qualifiedName);
2299     if (it != d->mCustomFields.end()) {
2300         d->mCustomFields.erase(it);
2301     }
2302 }
2303 
2304 QString Addressee::custom(const QString &app, const QString &name) const
2305 {
2306     const QString qualifiedName = app + QLatin1Char('-') + name;
2307     auto it = d->findByName(qualifiedName);
2308     return it != d->mCustomFields.cend() ? it->value : QString{};
2309 }
2310 
2311 void Addressee::setCustoms(const QStringList &customs)
2312 {
2313     d->mEmpty = false;
2314 
2315     d->mCustomFields.clear();
2316 
2317     // Less than 10 elements in "customs", we needn't use a set
2318     QStringList seen;
2319     for (const QString &custom : customs) {
2320         const int index = custom.indexOf(QLatin1Char(':'));
2321         if (index == -1) {
2322             continue;
2323         }
2324 
2325         const QString qualifiedName = custom.left(index);
2326         const QString value = custom.mid(index + 1);
2327 
2328         if (!seen.contains(qualifiedName)) {
2329             d->mCustomFields.push_back({qualifiedName, value});
2330             seen.push_back(qualifiedName);
2331         }
2332     }
2333     std::sort(d->mCustomFields.begin(), d->mCustomFields.end());
2334 }
2335 
2336 QStringList Addressee::customs() const
2337 {
2338     QStringList result;
2339     result.reserve(d->mCustomFields.size());
2340 
2341     static const QLatin1Char sep(':');
2342     for (const auto &[name, value] : d->mCustomFields) {
2343         result << name + sep + value;
2344     }
2345 
2346     return result;
2347 }
2348 
2349 void Addressee::parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
2350 {
2351     // This is a simplified version of KPIM::splitAddress().
2352 
2353     fullName.clear();
2354     email.clear();
2355     if (rawEmail.isEmpty()) {
2356         return; // KPIM::AddressEmpty;
2357     }
2358 
2359     // The code works on 8-bit strings, so convert the input to UTF-8.
2360     QByteArray address = rawEmail.toUtf8();
2361 
2362     QByteArray displayName;
2363     QByteArray addrSpec;
2364     QByteArray comment;
2365 
2366     // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
2367     // The purpose is to extract a displayable string from the mailboxes.
2368     // Comments in the addr-spec are not handled. No error checking is done.
2369 
2370     enum sourceLevel {
2371         TopLevel,
2372         InComment,
2373         InAngleAddress,
2374     };
2375     sourceLevel context = TopLevel;
2376     bool inQuotedString = false;
2377     int commentLevel = 0;
2378     bool stop = false;
2379 
2380     for (char *p = address.data(); *p && !stop; ++p) {
2381         switch (context) {
2382         case TopLevel:
2383             switch (*p) {
2384             case '"':
2385                 inQuotedString = !inQuotedString;
2386                 displayName += *p;
2387                 break;
2388             case '(':
2389                 if (!inQuotedString) {
2390                     context = InComment;
2391                     commentLevel = 1;
2392                 } else {
2393                     displayName += *p;
2394                 }
2395                 break;
2396             case '<':
2397                 if (!inQuotedString) {
2398                     context = InAngleAddress;
2399                 } else {
2400                     displayName += *p;
2401                 }
2402                 break;
2403             case '\\': // quoted character
2404                 displayName += *p;
2405                 ++p; // skip the '\'
2406                 if (*p) {
2407                     displayName += *p;
2408                 } else {
2409                     // return KPIM::UnexpectedEnd;
2410                     goto ABORT_PARSING;
2411                 }
2412                 break;
2413             case ',':
2414                 if (!inQuotedString) {
2415                     // if ( allowMultipleAddresses )
2416                     //  stop = true;
2417                     // else
2418                     //  return KPIM::UnexpectedComma;
2419                     goto ABORT_PARSING;
2420                 } else {
2421                     displayName += *p;
2422                 }
2423                 break;
2424             default:
2425                 displayName += *p;
2426             }
2427             break;
2428         case InComment:
2429             switch (*p) {
2430             case '(':
2431                 ++commentLevel;
2432                 comment += *p;
2433                 break;
2434             case ')':
2435                 --commentLevel;
2436                 if (commentLevel == 0) {
2437                     context = TopLevel;
2438                     comment += ' '; // separate the text of several comments
2439                 } else {
2440                     comment += *p;
2441                 }
2442                 break;
2443             case '\\': // quoted character
2444                 comment += *p;
2445                 ++p; // skip the '\'
2446                 if (*p) {
2447                     comment += *p;
2448                 } else {
2449                     // return KPIM::UnexpectedEnd;
2450                     goto ABORT_PARSING;
2451                 }
2452                 break;
2453             default:
2454                 comment += *p;
2455             }
2456             break;
2457         case InAngleAddress:
2458             switch (*p) {
2459             case '"':
2460                 inQuotedString = !inQuotedString;
2461                 addrSpec += *p;
2462                 break;
2463             case '>':
2464                 if (!inQuotedString) {
2465                     context = TopLevel;
2466                 } else {
2467                     addrSpec += *p;
2468                 }
2469                 break;
2470             case '\\': // quoted character
2471                 addrSpec += *p;
2472                 ++p; // skip the '\'
2473                 if (*p) {
2474                     addrSpec += *p;
2475                 } else {
2476                     // return KPIM::UnexpectedEnd;
2477                     goto ABORT_PARSING;
2478                 }
2479                 break;
2480             default:
2481                 addrSpec += *p;
2482             }
2483             break;
2484         } // switch ( context )
2485     }
2486 
2487 ABORT_PARSING:
2488     displayName = displayName.trimmed();
2489     comment = comment.trimmed();
2490     addrSpec = addrSpec.trimmed();
2491     fullName = QString::fromUtf8(displayName);
2492     email = QString::fromUtf8(addrSpec); // check for errors
2493     if (inQuotedString) {
2494         return; // KPIM::UnbalancedQuote;
2495     }
2496     if (context == InComment) {
2497         return; // KPIM::UnbalancedParens;
2498     }
2499     if (context == InAngleAddress) {
2500         return; // KPIM::UnclosedAngleAddr;
2501     }
2502 
2503     if (addrSpec.isEmpty()) {
2504         if (displayName.isEmpty()) {
2505             return; // KPIM::NoAddressSpec;
2506         } else {
2507             // addrSpec = displayName;
2508             // displayName.truncate( 0 );
2509             // Address of the form "foo@bar" or "foo@bar (Name)".
2510             email = fullName;
2511             fullName = QString::fromUtf8(comment);
2512         }
2513     }
2514 
2515     email = email.toLower();
2516 
2517     // Check that the full name is not enclosed in balanced double quotes.
2518     // If it is then remove them.
2519     const unsigned int len = fullName.length();
2520     if (len < 3) { // not long enough to be
2521         return;
2522     }
2523     if (fullName.startsWith(QLatin1Char('"')) && fullName.endsWith(QLatin1Char('"'))) {
2524         fullName = fullName.mid(1, len - 2);
2525     }
2526 }
2527 
2528 void Addressee::setChanged(bool value)
2529 {
2530     d->mChanged = value;
2531 }
2532 
2533 bool Addressee::changed() const
2534 {
2535     return d->mChanged;
2536 }
2537 
2538 QString Addressee::mimeType()
2539 {
2540     return QStringLiteral("text/directory");
2541 }
2542 
2543 QDataStream &KContacts::operator<<(QDataStream &s, const Addressee &a)
2544 {
2545     s << a.d->mUid;
2546 
2547     s << a.d->mName;
2548     s << a.d->mFormattedName;
2549     s << a.d->mFamilyName;
2550     s << a.d->mGivenName;
2551     s << a.d->mAdditionalName;
2552     s << a.d->mPrefix;
2553     s << a.d->mSuffix;
2554     s << a.d->mBirthday;
2555     s << a.d->mBirthdayWithTime;
2556     s << a.d->mMailer;
2557     s << a.d->mTimeZone;
2558     s << a.d->mGeo;
2559     s << a.d->mDepartment;
2560     s << a.d->mNote;
2561     s << a.d->mProductId;
2562     s << a.d->mRevision;
2563     s << a.d->mSortString;
2564     s << a.d->mSecrecy;
2565     s << a.d->mLogo;
2566     s << a.d->mPhoto;
2567     s << a.d->mSound;
2568     s << a.d->mPhoneNumbers;
2569     s << a.d->mAddresses;
2570     s << a.d->mEmails;
2571     s << a.d->mCategories;
2572     s << a.customs();
2573     s << a.d->mKeys;
2574     s << a.d->mLangs;
2575     s << a.d->mGender;
2576     s << a.d->mKind;
2577     s << a.d->mCalendarUrl;
2578     s << a.d->mSoundListExtra;
2579     s << a.d->mPhotoExtraList;
2580     s << a.d->mLogoExtraList;
2581     s << a.d->mUrlExtraList;
2582     s << a.d->mMembers;
2583     s << a.d->mRelationships;
2584     s << a.d->mSources;
2585     s << a.d->mImpps;
2586     s << a.d->mFieldGroupList;
2587     s << a.d->mTitleExtraList;
2588     s << a.d->mRoleExtraList;
2589     s << a.d->mOrgExtraList;
2590     s << a.d->mNickNameExtraList;
2591     s << a.d->mClientPidMapList;
2592 
2593     return s;
2594 }
2595 
2596 QDataStream &KContacts::operator>>(QDataStream &s, Addressee &a)
2597 {
2598     s >> a.d->mUid;
2599 
2600     s >> a.d->mName;
2601     s >> a.d->mFormattedName;
2602     s >> a.d->mFamilyName;
2603     s >> a.d->mGivenName;
2604     s >> a.d->mAdditionalName;
2605     s >> a.d->mPrefix;
2606     s >> a.d->mSuffix;
2607     s >> a.d->mBirthday;
2608     s >> a.d->mBirthdayWithTime;
2609     s >> a.d->mMailer;
2610     s >> a.d->mTimeZone;
2611     s >> a.d->mGeo;
2612     s >> a.d->mDepartment;
2613     s >> a.d->mNote;
2614     s >> a.d->mProductId;
2615     s >> a.d->mRevision;
2616     s >> a.d->mSortString;
2617     s >> a.d->mSecrecy;
2618     s >> a.d->mLogo;
2619     s >> a.d->mPhoto;
2620     s >> a.d->mSound;
2621     s >> a.d->mPhoneNumbers;
2622     s >> a.d->mAddresses;
2623     s >> a.d->mEmails;
2624     s >> a.d->mCategories;
2625     QStringList customFields;
2626     s >> customFields;
2627     a.setCustoms(customFields);
2628     s >> a.d->mKeys;
2629     s >> a.d->mLangs;
2630     s >> a.d->mGender;
2631     s >> a.d->mKind;
2632     s >> a.d->mCalendarUrl;
2633     s >> a.d->mSoundListExtra;
2634     s >> a.d->mPhotoExtraList;
2635     s >> a.d->mLogoExtraList;
2636     s >> a.d->mUrlExtraList;
2637     s >> a.d->mMembers;
2638     s >> a.d->mRelationships;
2639     s >> a.d->mSources;
2640     s >> a.d->mImpps;
2641     s >> a.d->mFieldGroupList;
2642     s >> a.d->mTitleExtraList;
2643     s >> a.d->mRoleExtraList;
2644     s >> a.d->mOrgExtraList;
2645     s >> a.d->mNickNameExtraList;
2646     s >> a.d->mClientPidMapList;
2647     a.d->mEmpty = false;
2648 
2649     return s;
2650 }
2651 
2652 bool matchBinaryPattern(int value, int pattern)
2653 {
2654     /**
2655       We want to match all telephonnumbers/addresses which have the bits in the
2656       pattern set. More are allowed.
2657       if pattern == 0 we have a special handling, then we want only those with
2658       exactly no bit set.
2659      */
2660     if (pattern == 0) {
2661         return value == 0;
2662     } else {
2663         return pattern == (pattern & value);
2664     }
2665 }
2666 
2667 template<class L>
2668 bool listEquals(const QVector<L> &list, const QVector<L> &pattern)
2669 {
2670     if (list.count() != pattern.count()) {
2671         return false;
2672     }
2673     const int numberOfElement(list.count());
2674     for (int i = 0; i < numberOfElement; ++i) {
2675         if (!pattern.contains(list[i])) {
2676             return false;
2677         }
2678     }
2679 
2680     return true;
2681 }
2682 
2683 bool listEquals(const QStringList &list, const QStringList &pattern)
2684 {
2685     if (list.count() != pattern.count()) {
2686         return false;
2687     }
2688 
2689     const int numberOfElement(list.count());
2690     for (int i = 0; i < numberOfElement; ++i) {
2691         if (!pattern.contains(list[i])) {
2692             return false;
2693         }
2694     }
2695 
2696     return true;
2697 }
2698 
2699 template<typename T>
2700 static QVariantList toVariantList(const QVector<T> &v)
2701 {
2702     QVariantList l;
2703     l.reserve(v.size());
2704     std::transform(v.begin(), v.end(), std::back_inserter(l), [](const T &elem) {
2705         return QVariant::fromValue(elem);
2706     });
2707     return l;
2708 }
2709 
2710 template<typename T>
2711 static QVector<T> fromVariantList(const QVariantList &v)
2712 {
2713     QVector<T> l;
2714     l.reserve(v.size());
2715     std::transform(v.begin(), v.end(), std::back_inserter(l), [](const QVariant &elem) {
2716         return elem.value<T>();
2717     });
2718     return l;
2719 }
2720 
2721 void Addressee::setBirthdayProperty(const QDateTime &birthday) {
2722     // The property setter cannot pass withTime, so we have to guess.
2723     setBirthday(birthday, birthday.time().msecsSinceStartOfDay() != 0);
2724 }
2725 
2726 QVariantList Addressee::emailsVariant() const
2727 {
2728     return toVariantList(d->mEmails);
2729 }
2730 
2731 void Addressee::setEmailsVariant(const QVariantList &emails)
2732 {
2733     setEmailList(fromVariantList<Email>(emails));
2734 }
2735 
2736 void Addressee::setPhoneNumbersVariant(const QVariantList &emails)
2737 {
2738     setPhoneNumbers(fromVariantList<PhoneNumber>(emails));
2739 }
2740 
2741 QVariantList Addressee::phoneNumbersVariant() const
2742 {
2743     return toVariantList(d->mPhoneNumbers);
2744 }
2745 
2746 QVariantList Addressee::addressesVariant() const
2747 {
2748     return toVariantList(d->mAddresses);
2749 }
2750 
2751 QVariantList Addressee::urlsVariant() const
2752 {
2753     return toVariantList(d->mUrlExtraList);
2754 }
2755 
2756 QVariantList Addressee::imppsVariant() const
2757 {
2758     return toVariantList(d->mImpps);
2759 }
2760 
2761 void Addressee::setImppsVariant(const QVariantList &impps)
2762 {
2763     setImppList(fromVariantList<Impp>(impps));
2764 }
2765 
2766 #include "moc_addressee.cpp"