File indexing completed on 2024-04-14 03:51:24

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