1 /*
2     This file is part of the KContacts framework.
3     SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
4     SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
5     SPDX-FileCopyrightText: 2005 Ingo Kloecker <kloecker@kde.org>
6 
7     SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include <QDate>
11 #include <QRegularExpression>
12 #include <QSharedData>
13 #include <QUuid>
14 
15 #include "kcontacts_debug.h"
16 #include <KLocalizedString>
17 
18 #include "addressee.h"
19 #include "addresseehelper.h"
20 #include "field.h"
21 #include "parametermap_p.h"
22 
23 using namespace KContacts;
24 
25 static bool matchBinaryPattern(int value, int pattern);
26 
27 template<class L>
28 static bool listEquals(const QVector<L> &list, const QVector<L> &pattern);
29 static bool listEquals(const QStringList &list, const QStringList &pattern);
30 
31 struct CustomData {
32     QString name;
33     QString value;
34 };
35 
operator ==(const CustomData & a,const CustomData & b)36 inline bool operator==(const CustomData &a, const CustomData &b)
37 {
38     return std::tie(a.name, a.value) == std::tie(b.name, b.value);
39 }
40 
operator !=(const CustomData & a,const CustomData & b)41 inline bool operator!=(const CustomData &a, const CustomData &b)
42 {
43     return std::tie(a.name, a.value) != std::tie(b.name, b.value);
44 }
45 
operator <(const CustomData & a,const CustomData & b)46 inline bool operator<(const CustomData &a, const CustomData &b)
47 {
48     return a.name < b.name;
49 }
50 
51 class Q_DECL_HIDDEN Addressee::Private : public QSharedData
52 {
53 public:
Private()54     Private()
55         : mUid(QUuid::createUuid().toString().mid(1, 36))
56         , mEmpty(true)
57         , mChanged(false)
58         , mBirthdayWithTime(false)
59     {
60         // We avoid the curly braces so the string is RFC4122 compliant and can be used as urn
61     }
62 
Private(const Private & other)63     Private(const Private &other)
64         : QSharedData(other)
65     {
66         mUid = other.mUid;
67         mName = other.mName;
68         mFormattedName = other.mFormattedName;
69         mFamilyName = other.mFamilyName;
70         mGivenName = other.mGivenName;
71         mAdditionalName = other.mAdditionalName;
72         mPrefix = other.mPrefix;
73         mSuffix = other.mSuffix;
74         mBirthday = other.mBirthday;
75         mBirthdayWithTime = other.mBirthdayWithTime;
76         mMailer = other.mMailer;
77         mTimeZone = other.mTimeZone;
78         mGeo = other.mGeo;
79         mDepartment = other.mDepartment;
80         mNote = other.mNote;
81         mProductId = other.mProductId;
82         mRevision = other.mRevision;
83         mSortString = other.mSortString;
84         mSecrecy = other.mSecrecy;
85         mLogo = other.mLogo;
86         mPhoto = other.mPhoto;
87         mSound = other.mSound;
88 
89         mPhoneNumbers = other.mPhoneNumbers;
90         mAddresses = other.mAddresses;
91         mKeys = other.mKeys;
92         mLangs = other.mLangs;
93         mGender = other.mGender;
94         mEmails = other.mEmails;
95         mCategories = other.mCategories;
96         mCustomFields = other.mCustomFields;
97         mCalendarUrl = other.mCalendarUrl;
98         mSoundListExtra = other.mSoundListExtra;
99         mPhotoExtraList = other.mPhotoExtraList;
100         mLogoExtraList = other.mLogoExtraList;
101         mUrlExtraList = other.mUrlExtraList;
102         mMembers = other.mMembers;
103         mRelationships = other.mRelationships;
104         mSources = other.mSources;
105         mEmpty = other.mEmpty;
106         mImpps = other.mImpps;
107         mChanged = other.mChanged;
108         mTitleExtraList = other.mTitleExtraList;
109         mRoleExtraList = other.mRoleExtraList;
110         mOrgExtraList = other.mOrgExtraList;
111     }
112 
~Private()113     ~Private()
114     {
115     }
116 
findByName(const QString & qualifiedName)117     std::vector<CustomData>::iterator findByName(const QString &qualifiedName)
118     {
119         return std::find_if(mCustomFields.begin(), mCustomFields.end(), [&qualifiedName](const CustomData &info) {
120             return info.name == qualifiedName;
121         });
122     }
123 
findByName(const QString & qualifiedName) const124     std::vector<CustomData>::const_iterator findByName(const QString &qualifiedName) const
125     {
126         return std::find_if(mCustomFields.cbegin(), mCustomFields.cend(), [&qualifiedName](const CustomData &info) {
127             return info.name == qualifiedName;
128         });
129     }
130 
131     QString mUid;
132     QString mName;
133     QString mFormattedName;
134     QString mFamilyName;
135     QString mGivenName;
136     QString mAdditionalName;
137     QString mPrefix;
138     QString mSuffix;
139     QDateTime mBirthday;
140     QString mMailer;
141     TimeZone mTimeZone;
142     Geo mGeo;
143     QString mDepartment;
144     QString mNote;
145     QString mProductId;
146     QDateTime mRevision;
147     QString mSortString;
148     Secrecy mSecrecy;
149     Picture mLogo;
150     Picture mPhoto;
151     Sound mSound;
152 
153     PhoneNumber::List mPhoneNumbers;
154     Address::List mAddresses;
155     Key::List mKeys;
156     Email::List mEmails;
157     Lang::List mLangs;
158     Impp::List mImpps;
159     Gender mGender;
160     QString mKind;
161     QStringList mCategories;
162     std::vector<CustomData> mCustomFields;
163     CalendarUrl::List mCalendarUrl;
164     Sound::List mSoundListExtra;
165     Picture::List mPhotoExtraList;
166     Picture::List mLogoExtraList;
167     ResourceLocatorUrl::List mUrlExtraList;
168     QVector<QUrl> mSources;
169     QStringList mMembers;
170     Related::List mRelationships;
171     FieldGroup::List mFieldGroupList;
172     Title::List mTitleExtraList;
173     Role::List mRoleExtraList;
174     Org::List mOrgExtraList;
175     NickName::List mNickNameExtraList;
176     ClientPidMap::List mClientPidMapList;
177     bool mEmpty : 1;
178     bool mChanged : 1;
179     bool mBirthdayWithTime;
180 };
181 
Addressee()182 Addressee::Addressee()
183     : d(new Private)
184 {
185 }
186 
~Addressee()187 Addressee::~Addressee()
188 {
189 }
190 
Addressee(const Addressee & other)191 Addressee::Addressee(const Addressee &other)
192     : d(other.d)
193 {
194 }
195 
operator =(const Addressee & other)196 Addressee &Addressee::operator=(const Addressee &other)
197 {
198     if (this != &other) {
199         d = other.d;
200     }
201 
202     return *this;
203 }
204 
operator ==(const Addressee & addressee) const205 bool Addressee::operator==(const Addressee &addressee) const
206 {
207     if (d->mUid != addressee.d->mUid) {
208         qCDebug(KCONTACTS_LOG) << "uid differs";
209         return false;
210     }
211 
212     if (d->mName != addressee.d->mName //
213         && !(d->mName.isEmpty() && addressee.d->mName.isEmpty())) {
214         qCDebug(KCONTACTS_LOG) << "name differs";
215         return false;
216     }
217 
218     if (d->mFormattedName != addressee.d->mFormattedName //
219         && !(d->mFormattedName.isEmpty() && addressee.d->mFormattedName.isEmpty())) {
220         qCDebug(KCONTACTS_LOG) << "formattedName differs";
221         return false;
222     }
223 
224     if (d->mFamilyName != addressee.d->mFamilyName //
225         && !(d->mFamilyName.isEmpty() && addressee.d->mFamilyName.isEmpty())) {
226         qCDebug(KCONTACTS_LOG) << "familyName differs";
227         return false;
228     }
229 
230     if (d->mGivenName != addressee.d->mGivenName //
231         && !(d->mGivenName.isEmpty() && addressee.d->mGivenName.isEmpty())) {
232         qCDebug(KCONTACTS_LOG) << "givenName differs";
233         return false;
234     }
235 
236     if (d->mAdditionalName != addressee.d->mAdditionalName //
237         && !(d->mAdditionalName.isEmpty() && addressee.d->mAdditionalName.isEmpty())) {
238         qCDebug(KCONTACTS_LOG) << "additionalName differs";
239         return false;
240     }
241 
242     if (d->mPrefix != addressee.d->mPrefix //
243         && !(d->mPrefix.isEmpty() && addressee.d->mPrefix.isEmpty())) {
244         qCDebug(KCONTACTS_LOG) << "prefix differs";
245         return false;
246     }
247 
248     if (d->mSuffix != addressee.d->mSuffix //
249         && !(d->mSuffix.isEmpty() && addressee.d->mSuffix.isEmpty())) {
250         qCDebug(KCONTACTS_LOG) << "suffix differs";
251         return false;
252     }
253 
254     if (d->mBirthday != addressee.d->mBirthday //
255         || d->mBirthdayWithTime != addressee.d->mBirthdayWithTime) {
256         qCDebug(KCONTACTS_LOG) << "birthday differs";
257         return false;
258     }
259 
260     if (d->mMailer != addressee.d->mMailer //
261         && !(d->mMailer.isEmpty() && addressee.d->mMailer.isEmpty())) {
262         qCDebug(KCONTACTS_LOG) << "mailer differs";
263         return false;
264     }
265 
266     if (d->mTimeZone != addressee.d->mTimeZone) {
267         qCDebug(KCONTACTS_LOG) << "timeZone differs";
268         return false;
269     }
270 
271     if (d->mGeo != addressee.d->mGeo) {
272         qCDebug(KCONTACTS_LOG) << "geo differs";
273         return false;
274     }
275 
276     if (d->mDepartment != addressee.d->mDepartment //
277         && !(d->mDepartment.isEmpty() && addressee.d->mDepartment.isEmpty())) {
278         qCDebug(KCONTACTS_LOG) << "department differs";
279         return false;
280     }
281 
282     if (d->mNote != addressee.d->mNote //
283         && !(d->mNote.isEmpty() && addressee.d->mNote.isEmpty())) {
284         qCDebug(KCONTACTS_LOG) << "note differs";
285         return false;
286     }
287 
288     if (d->mProductId != addressee.d->mProductId //
289         && !(d->mProductId.isEmpty() && addressee.d->mProductId.isEmpty())) {
290         qCDebug(KCONTACTS_LOG) << "productId differs";
291         return false;
292     }
293 
294     if (d->mSortString != addressee.d->mSortString //
295         && !(d->mSortString.isEmpty() && addressee.d->mSortString.isEmpty())) {
296         qCDebug(KCONTACTS_LOG) << "sortString differs";
297         return false;
298     }
299 
300     if (d->mSecrecy != addressee.d->mSecrecy) {
301         qCDebug(KCONTACTS_LOG) << "secrecy differs";
302         return false;
303     }
304 
305     if (d->mLogo != addressee.d->mLogo) {
306         qCDebug(KCONTACTS_LOG) << "logo differs";
307         return false;
308     }
309 
310     if (d->mPhoto != addressee.d->mPhoto) {
311         qCDebug(KCONTACTS_LOG) << "photo differs";
312         return false;
313     }
314 
315     if (d->mSound != addressee.d->mSound) {
316         qCDebug(KCONTACTS_LOG) << "sound differs";
317         return false;
318     }
319 
320     if (!listEquals(d->mPhoneNumbers, addressee.d->mPhoneNumbers)) {
321         qCDebug(KCONTACTS_LOG) << "phoneNumbers differs";
322         return false;
323     }
324 
325     if (!listEquals(d->mAddresses, addressee.d->mAddresses)) {
326         qCDebug(KCONTACTS_LOG) << "addresses differs";
327         return false;
328     }
329 
330     if (!listEquals(d->mKeys, addressee.d->mKeys)) {
331         qCDebug(KCONTACTS_LOG) << "keys differs";
332         return false;
333     }
334     if (!listEquals(d->mEmails, addressee.d->mEmails)) {
335         qCDebug(KCONTACTS_LOG) << "emails differs";
336         return false;
337     }
338 
339     if (!listEquals(d->mCategories, addressee.d->mCategories)) {
340         qCDebug(KCONTACTS_LOG) << "categories differs";
341         return false;
342     }
343 
344     if (d->mCustomFields != addressee.d->mCustomFields) {
345         qCDebug(KCONTACTS_LOG) << "custom differs";
346         return false;
347     }
348     if (d->mLangs != addressee.d->mLangs) {
349         qCDebug(KCONTACTS_LOG) << "langs differs";
350         return false;
351     }
352     if (d->mImpps != addressee.d->mImpps) {
353         qCDebug(KCONTACTS_LOG) << "impps differs";
354         return false;
355     }
356     if (d->mGender != addressee.d->mGender) {
357         qCDebug(KCONTACTS_LOG) << "gender differs";
358         return false;
359     }
360     if (d->mKind != addressee.d->mKind) {
361         qCDebug(KCONTACTS_LOG) << "kind differs";
362         return false;
363     }
364     if (!listEquals(d->mCalendarUrl, addressee.d->mCalendarUrl)) {
365         qCDebug(KCONTACTS_LOG) << "calendarUrl differs";
366         return false;
367     }
368     if (!listEquals(d->mSoundListExtra, addressee.d->mSoundListExtra)) {
369         qCDebug(KCONTACTS_LOG) << "Extra sound differs";
370         return false;
371     }
372     if (!listEquals(d->mPhotoExtraList, addressee.d->mPhotoExtraList)) {
373         qCDebug(KCONTACTS_LOG) << "Extra photo differs";
374         return false;
375     }
376     if (!listEquals(d->mLogoExtraList, addressee.d->mLogoExtraList)) {
377         qCDebug(KCONTACTS_LOG) << "Extra logo differs";
378         return false;
379     }
380     if (!listEquals(d->mUrlExtraList, addressee.d->mUrlExtraList)) {
381         qCDebug(KCONTACTS_LOG) << "Extra url differs";
382         return false;
383     }
384     if (!listEquals(d->mMembers, addressee.d->mMembers)) {
385         qCDebug(KCONTACTS_LOG) << "Extra url differs";
386         return false;
387     }
388     if (!listEquals(d->mRelationships, addressee.d->mRelationships)) {
389         qCDebug(KCONTACTS_LOG) << "Relationships differs";
390         return false;
391     }
392     if (!listEquals(d->mSources, addressee.d->mSources)) {
393         qCDebug(KCONTACTS_LOG) << "Sources differs";
394         return false;
395     }
396 
397     if (!listEquals(d->mFieldGroupList, addressee.d->mFieldGroupList)) {
398         qCDebug(KCONTACTS_LOG) << "Field Groups differs";
399         return false;
400     }
401 
402     if (!listEquals(d->mTitleExtraList, addressee.d->mTitleExtraList)) {
403         qCDebug(KCONTACTS_LOG) << "Extra TitleList differs";
404         return false;
405     }
406 
407     if (!listEquals(d->mRoleExtraList, addressee.d->mRoleExtraList)) {
408         qCDebug(KCONTACTS_LOG) << "Extra RoleList differs";
409         return false;
410     }
411 
412     if (!listEquals(d->mOrgExtraList, addressee.d->mOrgExtraList)) {
413         qCDebug(KCONTACTS_LOG) << "Extra Organization List differs";
414         return false;
415     }
416 
417     if (!listEquals(d->mNickNameExtraList, addressee.d->mNickNameExtraList)) {
418         qCDebug(KCONTACTS_LOG) << "Extra NickName List differs";
419         return false;
420     }
421 
422     if (!listEquals(d->mClientPidMapList, addressee.d->mClientPidMapList)) {
423         qCDebug(KCONTACTS_LOG) << "ClientPidMap List differs";
424         return false;
425     }
426     return true;
427 }
428 
operator !=(const Addressee & a) const429 bool Addressee::operator!=(const Addressee &a) const
430 {
431     return !(a == *this);
432 }
433 
isEmpty() const434 bool Addressee::isEmpty() const
435 {
436     return d->mEmpty;
437 }
438 
setUid(const QString & id)439 void Addressee::setUid(const QString &id)
440 {
441     if (id == d->mUid) {
442         return;
443     }
444 
445     d->mEmpty = false;
446     d->mUid = id;
447 }
448 
uid() const449 QString Addressee::uid() const
450 {
451     return d->mUid;
452 }
453 
uidLabel()454 QString Addressee::uidLabel()
455 {
456     return i18n("Unique Identifier");
457 }
458 
setName(const QString & name)459 void Addressee::setName(const QString &name)
460 {
461     if (name == d->mName) {
462         return;
463     }
464 
465     d->mEmpty = false;
466     d->mName = name;
467 }
468 
name() const469 QString Addressee::name() const
470 {
471     return d->mName;
472 }
473 
nameLabel()474 QString Addressee::nameLabel()
475 {
476     return i18n("Name");
477 }
478 
setKind(const QString & kind)479 void Addressee::setKind(const QString &kind)
480 {
481     if (kind == d->mKind) {
482         return;
483     }
484 
485     d->mEmpty = false;
486     d->mKind = kind;
487 }
488 
kind() const489 QString Addressee::kind() const
490 {
491     return d->mKind;
492 }
493 
insertExtraSound(const Sound & sound)494 void Addressee::insertExtraSound(const Sound &sound)
495 {
496     d->mEmpty = false;
497     d->mSoundListExtra.append(sound);
498 }
499 
extraSoundList() const500 Sound::List Addressee::extraSoundList() const
501 {
502     return d->mSoundListExtra;
503 }
504 
insertExtraPhoto(const Picture & picture)505 void Addressee::insertExtraPhoto(const Picture &picture)
506 {
507     d->mEmpty = false;
508     d->mPhotoExtraList.append(picture);
509 }
510 
extraPhotoList() const511 Picture::List Addressee::extraPhotoList() const
512 {
513     return d->mPhotoExtraList;
514 }
515 
insertExtraLogo(const Picture & logo)516 void Addressee::insertExtraLogo(const Picture &logo)
517 {
518     d->mEmpty = false;
519     d->mLogoExtraList.append(logo);
520 }
521 
extraLogoList() const522 Picture::List Addressee::extraLogoList() const
523 {
524     return d->mLogoExtraList;
525 }
526 
setExtraSoundList(const Sound::List & soundList)527 void Addressee::setExtraSoundList(const Sound::List &soundList)
528 {
529     d->mEmpty = false;
530     d->mSoundListExtra = soundList;
531 }
532 
setExtraPhotoList(const Picture::List & pictureList)533 void Addressee::setExtraPhotoList(const Picture::List &pictureList)
534 {
535     d->mEmpty = false;
536     d->mPhotoExtraList = pictureList;
537 }
538 
setExtraLogoList(const Picture::List & logoList)539 void Addressee::setExtraLogoList(const Picture::List &logoList)
540 {
541     d->mEmpty = false;
542     d->mLogoExtraList = logoList;
543 }
544 
insertExtraUrl(const ResourceLocatorUrl & url)545 void Addressee::insertExtraUrl(const ResourceLocatorUrl &url)
546 {
547     if (url.isValid()) {
548         d->mEmpty = false;
549         d->mUrlExtraList.append(url);
550     }
551 }
552 
setExtraUrlList(const ResourceLocatorUrl::List & urlList)553 void Addressee::setExtraUrlList(const ResourceLocatorUrl::List &urlList)
554 {
555     d->mEmpty = false;
556     d->mUrlExtraList = urlList;
557 }
558 
extraUrlList() const559 ResourceLocatorUrl::List Addressee::extraUrlList() const
560 {
561     return d->mUrlExtraList;
562 }
563 
insertSourceUrl(const QUrl & url)564 void Addressee::insertSourceUrl(const QUrl &url)
565 {
566     d->mEmpty = false;
567     d->mSources.append(url);
568 }
569 
setSourcesUrlList(const QVector<QUrl> & urlList)570 void Addressee::setSourcesUrlList(const QVector<QUrl> &urlList)
571 {
572     d->mEmpty = false;
573     d->mSources = urlList;
574 }
575 
sourcesUrlList() const576 QVector<QUrl> Addressee::sourcesUrlList() const
577 {
578     return d->mSources;
579 }
580 
fieldGroupList() const581 FieldGroup::List Addressee::fieldGroupList() const
582 {
583     return d->mFieldGroupList;
584 }
585 
setFieldGroupList(const FieldGroup::List & fieldGroupList)586 void Addressee::setFieldGroupList(const FieldGroup::List &fieldGroupList)
587 {
588     d->mEmpty = false;
589     d->mFieldGroupList = fieldGroupList;
590 }
591 
insertFieldGroup(const FieldGroup & fieldGroup)592 void Addressee::insertFieldGroup(const FieldGroup &fieldGroup)
593 {
594     if (fieldGroup.isValid()) {
595         d->mEmpty = false;
596         // TODO don't duplicate ?
597         d->mFieldGroupList.append(fieldGroup);
598     }
599 }
600 
clientPidMapList() const601 ClientPidMap::List Addressee::clientPidMapList() const
602 {
603     return d->mClientPidMapList;
604 }
605 
setClientPidMapList(const ClientPidMap::List & clientpidmaplist)606 void Addressee::setClientPidMapList(const ClientPidMap::List &clientpidmaplist)
607 {
608     d->mEmpty = false;
609     d->mClientPidMapList = clientpidmaplist;
610 }
611 
insertClientPidMap(const ClientPidMap & clientpidmap)612 void Addressee::insertClientPidMap(const ClientPidMap &clientpidmap)
613 {
614     if (clientpidmap.isValid()) {
615         d->mEmpty = false;
616         // TODO don't duplicate ?
617         d->mClientPidMapList.append(clientpidmap);
618     }
619 }
620 
insertImpp(const Impp & impp)621 void Addressee::insertImpp(const Impp &impp)
622 {
623     if (impp.isValid()) {
624         d->mEmpty = false;
625         // Don't duplicate ?
626         d->mImpps.append(impp);
627     }
628 }
629 
setImppList(const Impp::List & imppList)630 void Addressee::setImppList(const Impp::List &imppList)
631 {
632     d->mEmpty = false;
633     d->mImpps = imppList;
634 }
635 
imppList() const636 Impp::List Addressee::imppList() const
637 {
638     return d->mImpps;
639 }
640 
insertCalendarUrl(const CalendarUrl & calendarUrl)641 void Addressee::insertCalendarUrl(const CalendarUrl &calendarUrl)
642 {
643     d->mEmpty = false;
644     // TODO verify that there is not same calendarurl
645     if (calendarUrl.isValid()) {
646         d->mCalendarUrl.append(calendarUrl);
647     }
648 }
649 
calendarUrlList() const650 CalendarUrl::List Addressee::calendarUrlList() const
651 {
652     return d->mCalendarUrl;
653 }
654 
setFormattedName(const QString & formattedName)655 void Addressee::setFormattedName(const QString &formattedName)
656 {
657     if (formattedName == d->mFormattedName) {
658         return;
659     }
660 
661     d->mEmpty = false;
662     d->mFormattedName = formattedName;
663 }
664 
formattedName() const665 QString Addressee::formattedName() const
666 {
667     return d->mFormattedName;
668 }
669 
formattedNameLabel()670 QString Addressee::formattedNameLabel()
671 {
672     return i18n("Formatted Name");
673 }
674 
setFamilyName(const QString & familyName)675 void Addressee::setFamilyName(const QString &familyName)
676 {
677     if (familyName == d->mFamilyName) {
678         return;
679     }
680 
681     d->mEmpty = false;
682     d->mFamilyName = familyName;
683 }
684 
familyName() const685 QString Addressee::familyName() const
686 {
687     return d->mFamilyName;
688 }
689 
familyNameLabel()690 QString Addressee::familyNameLabel()
691 {
692     return i18n("Family Name");
693 }
694 
setGivenName(const QString & givenName)695 void Addressee::setGivenName(const QString &givenName)
696 {
697     if (givenName == d->mGivenName) {
698         return;
699     }
700 
701     d->mEmpty = false;
702     d->mGivenName = givenName;
703 }
704 
givenName() const705 QString Addressee::givenName() const
706 {
707     return d->mGivenName;
708 }
709 
givenNameLabel()710 QString Addressee::givenNameLabel()
711 {
712     return i18n("Given Name");
713 }
714 
setAdditionalName(const QString & additionalName)715 void Addressee::setAdditionalName(const QString &additionalName)
716 {
717     if (additionalName == d->mAdditionalName) {
718         return;
719     }
720 
721     d->mEmpty = false;
722     d->mAdditionalName = additionalName;
723 }
724 
additionalName() const725 QString Addressee::additionalName() const
726 {
727     return d->mAdditionalName;
728 }
729 
additionalNameLabel()730 QString Addressee::additionalNameLabel()
731 {
732     return i18n("Additional Names");
733 }
734 
setPrefix(const QString & prefix)735 void Addressee::setPrefix(const QString &prefix)
736 {
737     if (prefix == d->mPrefix) {
738         return;
739     }
740 
741     d->mEmpty = false;
742     d->mPrefix = prefix;
743 }
744 
prefix() const745 QString Addressee::prefix() const
746 {
747     return d->mPrefix;
748 }
749 
prefixLabel()750 QString Addressee::prefixLabel()
751 {
752     return i18n("Honorific Prefixes");
753 }
754 
setSuffix(const QString & suffix)755 void Addressee::setSuffix(const QString &suffix)
756 {
757     if (suffix == d->mSuffix) {
758         return;
759     }
760 
761     d->mEmpty = false;
762     d->mSuffix = suffix;
763 }
764 
suffix() const765 QString Addressee::suffix() const
766 {
767     return d->mSuffix;
768 }
769 
suffixLabel()770 QString Addressee::suffixLabel()
771 {
772     return i18n("Honorific Suffixes");
773 }
774 
setNickName(const QString & nickName)775 void Addressee::setNickName(const QString &nickName)
776 {
777     NickName t(nickName);
778     if (!d->mNickNameExtraList.isEmpty()) {
779         t = d->mNickNameExtraList.takeFirst();
780         t.setNickName(nickName);
781         d->mNickNameExtraList.prepend(t);
782         d->mEmpty = false;
783     } else {
784         insertExtraNickName(t);
785     }
786 }
787 
setNickName(const NickName & nickName)788 void Addressee::setNickName(const NickName &nickName)
789 {
790     insertExtraNickName(nickName);
791 }
792 
insertExtraNickName(const NickName & nickName)793 void Addressee::insertExtraNickName(const NickName &nickName)
794 {
795     if (nickName.isValid()) {
796         d->mEmpty = false;
797         d->mNickNameExtraList.append(nickName);
798     }
799 }
800 
setExtraNickNameList(const NickName::List & nickNameList)801 void Addressee::setExtraNickNameList(const NickName::List &nickNameList)
802 {
803     d->mEmpty = false;
804     d->mNickNameExtraList = nickNameList;
805 }
806 
extraNickNameList() const807 NickName::List Addressee::extraNickNameList() const
808 {
809     return d->mNickNameExtraList;
810 }
811 
nickName() const812 QString Addressee::nickName() const
813 {
814     if (d->mNickNameExtraList.isEmpty()) {
815         return {};
816     } else {
817         return d->mNickNameExtraList.at(0).nickname();
818     }
819 }
820 
nickNameLabel()821 QString Addressee::nickNameLabel()
822 {
823     return i18n("Nick Name");
824 }
825 
setBirthday(const QDateTime & birthday,bool withTime)826 void Addressee::setBirthday(const QDateTime &birthday, bool withTime)
827 {
828     if (birthday == d->mBirthday && d->mBirthdayWithTime == withTime) {
829         return;
830     }
831 
832     d->mEmpty = false;
833     d->mBirthday = birthday;
834     if (!withTime) {
835         d->mBirthday.setTime(QTime());
836     }
837     d->mBirthdayWithTime = withTime;
838 }
839 
setBirthday(const QDate & birthday)840 void Addressee::setBirthday(const QDate &birthday)
841 {
842     if (birthday == d->mBirthday.date() && !d->mBirthdayWithTime) {
843         return;
844     }
845 
846     d->mEmpty = false;
847     d->mBirthday = QDateTime(birthday, QTime());
848     d->mBirthdayWithTime = false;
849 }
850 
birthday() const851 QDateTime Addressee::birthday() const
852 {
853     return d->mBirthday;
854 }
855 
birthdayHasTime() const856 bool Addressee::birthdayHasTime() const
857 {
858     return d->mBirthdayWithTime;
859 }
860 
birthdayLabel()861 QString Addressee::birthdayLabel()
862 {
863     return i18n("Birthday");
864 }
865 
homeAddressStreetLabel()866 QString Addressee::homeAddressStreetLabel()
867 {
868     return i18n("Home Address Street");
869 }
870 
homeAddressPostOfficeBoxLabel()871 QString Addressee::homeAddressPostOfficeBoxLabel()
872 {
873     return i18n("Home Address Post Office Box");
874 }
875 
homeAddressLocalityLabel()876 QString Addressee::homeAddressLocalityLabel()
877 {
878     return i18n("Home Address City");
879 }
880 
homeAddressRegionLabel()881 QString Addressee::homeAddressRegionLabel()
882 {
883     return i18n("Home Address State");
884 }
885 
homeAddressPostalCodeLabel()886 QString Addressee::homeAddressPostalCodeLabel()
887 {
888     return i18n("Home Address Zip Code");
889 }
890 
homeAddressCountryLabel()891 QString Addressee::homeAddressCountryLabel()
892 {
893     return i18n("Home Address Country");
894 }
895 
homeAddressLabelLabel()896 QString Addressee::homeAddressLabelLabel()
897 {
898     return i18n("Home Address Label");
899 }
900 
businessAddressStreetLabel()901 QString Addressee::businessAddressStreetLabel()
902 {
903     return i18n("Business Address Street");
904 }
905 
businessAddressPostOfficeBoxLabel()906 QString Addressee::businessAddressPostOfficeBoxLabel()
907 {
908     return i18n("Business Address Post Office Box");
909 }
910 
businessAddressLocalityLabel()911 QString Addressee::businessAddressLocalityLabel()
912 {
913     return i18n("Business Address City");
914 }
915 
businessAddressRegionLabel()916 QString Addressee::businessAddressRegionLabel()
917 {
918     return i18n("Business Address State");
919 }
920 
businessAddressPostalCodeLabel()921 QString Addressee::businessAddressPostalCodeLabel()
922 {
923     return i18n("Business Address Zip Code");
924 }
925 
businessAddressCountryLabel()926 QString Addressee::businessAddressCountryLabel()
927 {
928     return i18n("Business Address Country");
929 }
930 
businessAddressLabelLabel()931 QString Addressee::businessAddressLabelLabel()
932 {
933     return i18n("Business Address Label");
934 }
935 
homePhoneLabel()936 QString Addressee::homePhoneLabel()
937 {
938     return i18n("Home Phone");
939 }
940 
businessPhoneLabel()941 QString Addressee::businessPhoneLabel()
942 {
943     return i18n("Business Phone");
944 }
945 
mobilePhoneLabel()946 QString Addressee::mobilePhoneLabel()
947 {
948     return i18n("Mobile Phone");
949 }
950 
homeFaxLabel()951 QString Addressee::homeFaxLabel()
952 {
953     return i18n("Home Fax");
954 }
955 
businessFaxLabel()956 QString Addressee::businessFaxLabel()
957 {
958     return i18n("Business Fax");
959 }
960 
carPhoneLabel()961 QString Addressee::carPhoneLabel()
962 {
963     return i18n("Car Phone");
964 }
965 
isdnLabel()966 QString Addressee::isdnLabel()
967 {
968     return i18n("ISDN");
969 }
970 
pagerLabel()971 QString Addressee::pagerLabel()
972 {
973     return i18n("Pager");
974 }
975 
emailLabel()976 QString Addressee::emailLabel()
977 {
978     return i18n("Email Address");
979 }
980 
setMailer(const QString & mailer)981 void Addressee::setMailer(const QString &mailer)
982 {
983     if (mailer == d->mMailer) {
984         return;
985     }
986 
987     d->mEmpty = false;
988     d->mMailer = mailer;
989 }
990 
mailer() const991 QString Addressee::mailer() const
992 {
993     return d->mMailer;
994 }
995 
mailerLabel()996 QString Addressee::mailerLabel()
997 {
998     return i18n("Mail Client");
999 }
1000 
setTimeZone(const TimeZone & timeZone)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 
timeZone() const1011 TimeZone Addressee::timeZone() const
1012 {
1013     return d->mTimeZone;
1014 }
1015 
timeZoneLabel()1016 QString Addressee::timeZoneLabel()
1017 {
1018     return i18n("Time Zone");
1019 }
1020 
setGeo(const Geo & geo)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 
geo() const1031 Geo Addressee::geo() const
1032 {
1033     return d->mGeo;
1034 }
1035 
geoLabel()1036 QString Addressee::geoLabel()
1037 {
1038     return i18n("Geographic Position");
1039 }
1040 
setTitle(const QString & title)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 
setTitle(const Title & title)1054 void Addressee::setTitle(const Title &title)
1055 {
1056     insertExtraTitle(title);
1057 }
1058 
insertExtraTitle(const Title & title)1059 void Addressee::insertExtraTitle(const Title &title)
1060 {
1061     if (title.isValid()) {
1062         d->mEmpty = false;
1063         d->mTitleExtraList.append(title);
1064     }
1065 }
1066 
title() const1067 QString Addressee::title() const
1068 {
1069     if (d->mTitleExtraList.isEmpty()) {
1070         return {};
1071     } else {
1072         return d->mTitleExtraList.at(0).title();
1073     }
1074 }
1075 
extraTitleList() const1076 Title::List Addressee::extraTitleList() const
1077 {
1078     return d->mTitleExtraList;
1079 }
1080 
setExtraTitleList(const Title::List & urltitle)1081 void Addressee::setExtraTitleList(const Title::List &urltitle)
1082 {
1083     d->mEmpty = false;
1084     d->mTitleExtraList = urltitle;
1085 }
1086 
titleLabel()1087 QString Addressee::titleLabel()
1088 {
1089     return i18nc("a person's title", "Title");
1090 }
1091 
setRole(const QString & role)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 
setRole(const Role & role)1105 void Addressee::setRole(const Role &role)
1106 {
1107     insertExtraRole(role);
1108 }
1109 
insertExtraRole(const Role & role)1110 void Addressee::insertExtraRole(const Role &role)
1111 {
1112     if (role.isValid()) {
1113         d->mEmpty = false;
1114         d->mRoleExtraList.append(role);
1115     }
1116 }
1117 
setExtraRoleList(const Role::List & roleList)1118 void Addressee::setExtraRoleList(const Role::List &roleList)
1119 {
1120     d->mEmpty = false;
1121     d->mRoleExtraList = roleList;
1122 }
1123 
extraRoleList() const1124 Role::List Addressee::extraRoleList() const
1125 {
1126     return d->mRoleExtraList;
1127 }
1128 
role() const1129 QString Addressee::role() const
1130 {
1131     if (d->mRoleExtraList.isEmpty()) {
1132         return {};
1133     } else {
1134         return d->mRoleExtraList.at(0).role();
1135     }
1136 }
1137 
roleLabel()1138 QString Addressee::roleLabel()
1139 {
1140     return i18nc("of a person in an organization", "Role");
1141 }
1142 
setOrganization(const QString & organization)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 
setOrganization(const Org & organization)1156 void Addressee::setOrganization(const Org &organization)
1157 {
1158     insertExtraOrganization(organization);
1159 }
1160 
insertExtraOrganization(const Org & organization)1161 void Addressee::insertExtraOrganization(const Org &organization)
1162 {
1163     if (organization.isValid()) {
1164         d->mEmpty = false;
1165         d->mOrgExtraList.append(organization);
1166     }
1167 }
1168 
setExtraOrganizationList(const Org::List & orgList)1169 void Addressee::setExtraOrganizationList(const Org::List &orgList)
1170 {
1171     d->mEmpty = false;
1172     d->mOrgExtraList = orgList;
1173 }
1174 
extraOrganizationList() const1175 Org::List Addressee::extraOrganizationList() const
1176 {
1177     return d->mOrgExtraList;
1178 }
1179 
organization() const1180 QString Addressee::organization() const
1181 {
1182     if (d->mOrgExtraList.isEmpty()) {
1183         return {};
1184     } else {
1185         return d->mOrgExtraList.at(0).organization();
1186     }
1187 }
1188 
organizationLabel()1189 QString Addressee::organizationLabel()
1190 {
1191     return i18n("Organization");
1192 }
1193 
setDepartment(const QString & department)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 
department() const1204 QString Addressee::department() const
1205 {
1206     return d->mDepartment;
1207 }
1208 
departmentLabel()1209 QString Addressee::departmentLabel()
1210 {
1211     return i18n("Department");
1212 }
1213 
setNote(const QString & note)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 
note() const1224 QString Addressee::note() const
1225 {
1226     return d->mNote;
1227 }
1228 
noteLabel()1229 QString Addressee::noteLabel()
1230 {
1231     return i18n("Note");
1232 }
1233 
setProductId(const QString & productId)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 
productId() const1244 QString Addressee::productId() const
1245 {
1246     return d->mProductId;
1247 }
1248 
productIdLabel()1249 QString Addressee::productIdLabel()
1250 {
1251     return i18n("Product Identifier");
1252 }
1253 
setRevision(const QDateTime & revision)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 
revision() const1264 QDateTime Addressee::revision() const
1265 {
1266     return d->mRevision;
1267 }
1268 
revisionLabel()1269 QString Addressee::revisionLabel()
1270 {
1271     return i18n("Revision Date");
1272 }
1273 
setSortString(const QString & sortString)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 
sortString() const1284 QString Addressee::sortString() const
1285 {
1286     return d->mSortString;
1287 }
1288 
sortStringLabel()1289 QString Addressee::sortStringLabel()
1290 {
1291     return i18n("Sort String");
1292 }
1293 
setUrl(const QUrl & url)1294 void Addressee::setUrl(const QUrl &url)
1295 {
1296     KContacts::ResourceLocatorUrl resourceLocator;
1297     resourceLocator.setUrl(url);
1298     insertExtraUrl(resourceLocator);
1299 }
1300 
setUrl(const ResourceLocatorUrl & url)1301 void Addressee::setUrl(const ResourceLocatorUrl &url)
1302 {
1303     insertExtraUrl(url);
1304 }
1305 
url() const1306 ResourceLocatorUrl Addressee::url() const
1307 {
1308     if (d->mUrlExtraList.isEmpty()) {
1309         return ResourceLocatorUrl();
1310     } else {
1311         return d->mUrlExtraList.at(0);
1312     }
1313 }
1314 
urlLabel()1315 QString Addressee::urlLabel()
1316 {
1317     return i18n("Homepage");
1318 }
1319 
setSecrecy(const Secrecy & secrecy)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 
secrecy() const1330 Secrecy Addressee::secrecy() const
1331 {
1332     return d->mSecrecy;
1333 }
1334 
secrecyLabel()1335 QString Addressee::secrecyLabel()
1336 {
1337     return i18n("Security Class");
1338 }
1339 
setLogo(const Picture & logo)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 
logo() const1350 Picture Addressee::logo() const
1351 {
1352     return d->mLogo;
1353 }
1354 
logoLabel()1355 QString Addressee::logoLabel()
1356 {
1357     return i18n("Logo");
1358 }
1359 
setPhoto(const Picture & photo)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 
photo() const1370 Picture Addressee::photo() const
1371 {
1372     return d->mPhoto;
1373 }
1374 
photoLabel()1375 QString Addressee::photoLabel()
1376 {
1377     return i18n("Photo");
1378 }
1379 
setSound(const Sound & sound)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 
sound() const1390 Sound Addressee::sound() const
1391 {
1392     return d->mSound;
1393 }
1394 
soundLabel()1395 QString Addressee::soundLabel()
1396 {
1397     return i18n("Sound");
1398 }
1399 
setNameFromString(const QString & s)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 
realName() const1557 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 
assembledName() const1577 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 
fullEmail(const QString & email) const1590 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 
addEmail(const Email & email)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)
insertEmail(const QString & email,bool preferred,const QMap<QString,QStringList> & param)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 
removeEmail(const QString & email)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 
preferredEmail() const1681 QString Addressee::preferredEmail() const
1682 {
1683     if (d->mEmails.isEmpty()) {
1684         return QString();
1685     } else {
1686         return d->mEmails.first().mail();
1687     }
1688 }
1689 
emails() const1690 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 
emailList() const1702 Email::List Addressee::emailList() const
1703 {
1704     return d->mEmails;
1705 }
1706 
setEmails(const QStringList & emails)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 
setEmailList(const Email::List & list)1718 void Addressee::setEmailList(const Email::List &list)
1719 {
1720     d->mEmails = list;
1721     d->mEmpty = false;
1722 }
1723 
removeLang(const QString & language)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 
setLangs(const Lang::List & langs)1733 void Addressee::setLangs(const Lang::List &langs)
1734 {
1735     d->mLangs = langs;
1736     d->mEmpty = false;
1737 }
1738 
insertLang(const Lang & language)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 
langs() const1758 Lang::List Addressee::langs() const
1759 {
1760     return d->mLangs;
1761 }
1762 
setGender(const Gender & gender)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 
gender() const1773 Gender Addressee::gender() const
1774 {
1775     return d->mGender;
1776 }
1777 
insertPhoneNumber(const PhoneNumber & phoneNumber)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 
removePhoneNumber(const PhoneNumber & phoneNumber)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 
phoneNumber(PhoneNumber::Type type) const1806 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 
phoneNumbers() const1823 PhoneNumber::List Addressee::phoneNumbers() const
1824 {
1825     return d->mPhoneNumbers;
1826 }
1827 
setPhoneNumbers(const PhoneNumber::List & phoneNumbers)1828 void Addressee::setPhoneNumbers(const PhoneNumber::List &phoneNumbers)
1829 {
1830     d->mEmpty = false;
1831     d->mPhoneNumbers.clear();
1832     d->mPhoneNumbers = phoneNumbers;
1833 }
1834 
phoneNumbers(PhoneNumber::Type type) const1835 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 
findPhoneNumber(const QString & id) const1845 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 
insertKey(const Key & key)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 
removeKey(const Key & key)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 
key(Key::Type type,const QString & customTypeString) const1876 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 
setKeys(const Key::List & list)1896 void Addressee::setKeys(const Key::List &list)
1897 {
1898     d->mKeys = list;
1899     d->mEmpty = false;
1900 }
1901 
keys() const1902 Key::List Addressee::keys() const
1903 {
1904     return d->mKeys;
1905 }
1906 
keys(Key::Type type,const QString & customTypeString) const1907 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 
findKey(const QString & id) const1932 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 
toString() const1941 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 
insertAddress(const Address & address)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 
removeAddress(const Address & address)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 
address(Address::Type type) const2043 Address Addressee::address(Address::Type type) const
2044 {
2045     Address address(type);
2046     for (const Address &addr : d->mAddresses) {
2047         if (matchBinaryPattern(addr.type(), type)) {
2048             if (addr.type() & Address::Pref) {
2049                 return addr;
2050             } else if (address.isEmpty()) {
2051                 address = addr;
2052             }
2053         }
2054     }
2055 
2056     return address;
2057 }
2058 
addresses() const2059 Address::List Addressee::addresses() const
2060 {
2061     return d->mAddresses;
2062 }
2063 
addresses(Address::Type type) const2064 Address::List Addressee::addresses(Address::Type type) const
2065 {
2066     Address::List list;
2067 
2068     std::copy_if(d->mAddresses.cbegin(), d->mAddresses.cend(), std::back_inserter(list), [type](const Address &addr) {
2069         return matchBinaryPattern(addr.type(), type);
2070     });
2071 
2072     return list;
2073 }
2074 
findAddress(const QString & id) const2075 Address Addressee::findAddress(const QString &id) const
2076 {
2077     auto it = std::find_if(d->mAddresses.cbegin(), d->mAddresses.cend(), [&id](const Address &addr) {
2078         return addr.id() == id;
2079     });
2080     return it != d->mAddresses.cend() ? *it : Address{};
2081 }
2082 
insertCategory(const QString & c)2083 void Addressee::insertCategory(const QString &c)
2084 {
2085     d->mEmpty = false;
2086 
2087     if (d->mCategories.contains(c)) {
2088         return;
2089     }
2090 
2091     d->mCategories.append(c);
2092 }
2093 
removeCategory(const QString & category)2094 void Addressee::removeCategory(const QString &category)
2095 {
2096     if (d->mCategories.contains(category)) {
2097         d->mCategories.removeAll(category);
2098     }
2099 }
2100 
hasCategory(const QString & category) const2101 bool Addressee::hasCategory(const QString &category) const
2102 {
2103     return d->mCategories.contains(category);
2104 }
2105 
setCategories(const QStringList & c)2106 void Addressee::setCategories(const QStringList &c)
2107 {
2108     d->mEmpty = false;
2109 
2110     d->mCategories = c;
2111 }
2112 
categories() const2113 QStringList Addressee::categories() const
2114 {
2115     return d->mCategories;
2116 }
2117 
insertMember(const QString & member)2118 void Addressee::insertMember(const QString &member)
2119 {
2120     d->mEmpty = false;
2121 
2122     if (d->mMembers.contains(member)) {
2123         return;
2124     }
2125 
2126     d->mMembers.append(member);
2127 }
2128 
setMembers(const QStringList & m)2129 void Addressee::setMembers(const QStringList &m)
2130 {
2131     d->mEmpty = false;
2132     d->mMembers = m;
2133 }
2134 
members() const2135 QStringList Addressee::members() const
2136 {
2137     return d->mMembers;
2138 }
2139 
insertRelationship(const Related & relation)2140 void Addressee::insertRelationship(const Related &relation)
2141 {
2142     d->mEmpty = false;
2143 
2144     if (d->mRelationships.contains(relation)) {
2145         return;
2146     }
2147 
2148     d->mRelationships.append(relation);
2149 }
2150 
setRelationships(const Related::List & c)2151 void Addressee::setRelationships(const Related::List &c)
2152 {
2153     d->mEmpty = false;
2154     d->mRelationships = c;
2155 }
2156 
relationships() const2157 Related::List Addressee::relationships() const
2158 {
2159     return d->mRelationships;
2160 }
2161 
2162 static const auto VENDOR_ID = QStringLiteral("KADDRESSBOOK");
2163 static const auto X_ANNIVERSARY = QStringLiteral("X-Anniversary");
2164 static const auto X_ASSISTANTSNAME = QStringLiteral("X-AssistantsName");
2165 static const auto BLOGFEED = QStringLiteral("BlogFeed");
2166 static const auto X_MANAGERSNAME = QStringLiteral("X-ManagersName");
2167 static const auto X_OFFICE = QStringLiteral("X-Office");
2168 static const auto X_PROFESSION = QStringLiteral("X-Profession");
2169 static const auto X_SPOUSESNAME = QStringLiteral("X-SpousesName");
2170 
anniversary() const2171 QDate Addressee::anniversary() const
2172 {
2173     return QDate::fromString(custom(VENDOR_ID, X_ANNIVERSARY), Qt::ISODate);
2174 }
2175 
setAnniversary(const QDate & anniversary)2176 void Addressee::setAnniversary(const QDate &anniversary)
2177 {
2178     if (anniversary.isValid()) {
2179         insertCustom(VENDOR_ID, X_ANNIVERSARY, anniversary.toString(Qt::ISODate));
2180     } else {
2181         removeCustom(VENDOR_ID, X_ANNIVERSARY);
2182     }
2183 }
2184 
assistantsName() const2185 QString Addressee::assistantsName() const
2186 {
2187     return custom(VENDOR_ID, X_ASSISTANTSNAME);
2188 }
2189 
setAssistantsName(const QString & assistantsName)2190 void Addressee::setAssistantsName(const QString &assistantsName)
2191 {
2192     if (!assistantsName.isEmpty()) {
2193         insertCustom(VENDOR_ID, X_ASSISTANTSNAME, assistantsName);
2194     } else {
2195         removeCustom(VENDOR_ID, X_ASSISTANTSNAME);
2196     }
2197 }
2198 
blogFeed() const2199 QUrl Addressee::blogFeed() const
2200 {
2201     return QUrl(custom(VENDOR_ID, BLOGFEED));
2202 }
2203 
setBlogFeed(const QUrl & blogFeed)2204 void Addressee::setBlogFeed(const QUrl &blogFeed)
2205 {
2206     if (!blogFeed.isEmpty()) {
2207         insertCustom(VENDOR_ID, BLOGFEED, blogFeed.url());
2208     } else {
2209         removeCustom(VENDOR_ID, BLOGFEED);
2210     }
2211 }
2212 
managersName() const2213 QString Addressee::managersName() const
2214 {
2215     return custom(VENDOR_ID, X_MANAGERSNAME);
2216 }
2217 
setManagersName(const QString & managersName)2218 void Addressee::setManagersName(const QString &managersName)
2219 {
2220     if (!managersName.isEmpty()) {
2221         insertCustom(VENDOR_ID, X_MANAGERSNAME, managersName);
2222     } else {
2223         removeCustom(VENDOR_ID, X_MANAGERSNAME);
2224     }
2225 }
2226 
office() const2227 QString Addressee::office() const
2228 {
2229     return custom(VENDOR_ID, X_OFFICE);
2230 }
2231 
setOffice(const QString & office)2232 void Addressee::setOffice(const QString &office)
2233 {
2234     if (!office.isEmpty()) {
2235         insertCustom(VENDOR_ID, X_OFFICE, office);
2236     } else {
2237         removeCustom(VENDOR_ID, X_OFFICE);
2238     }
2239 }
2240 
profession() const2241 QString Addressee::profession() const
2242 {
2243     return custom(VENDOR_ID, X_PROFESSION);
2244 }
2245 
setProfession(const QString & profession)2246 void Addressee::setProfession(const QString &profession)
2247 {
2248     if (!profession.isEmpty()) {
2249         insertCustom(VENDOR_ID, X_PROFESSION, profession);
2250     } else {
2251         removeCustom(VENDOR_ID, X_PROFESSION);
2252     }
2253 }
2254 
spousesName() const2255 QString Addressee::spousesName() const
2256 {
2257     return custom(VENDOR_ID, X_SPOUSESNAME);
2258 }
2259 
setSpousesName(const QString & spousesName)2260 void Addressee::setSpousesName(const QString &spousesName)
2261 {
2262     if (!spousesName.isEmpty()) {
2263         insertCustom(VENDOR_ID, X_SPOUSESNAME, spousesName);
2264     } else {
2265         removeCustom(VENDOR_ID, X_SPOUSESNAME);
2266     }
2267 }
2268 
insertCustom(const QString & app,const QString & name,const QString & value)2269 void Addressee::insertCustom(const QString &app, const QString &name, const QString &value)
2270 {
2271     if (value.isEmpty() || name.isEmpty() || app.isEmpty()) {
2272         return;
2273     }
2274 
2275     d->mEmpty = false;
2276 
2277     const QString qualifiedName = app + QLatin1Char('-') + name;
2278 
2279     auto it = d->findByName(qualifiedName);
2280     if (it != d->mCustomFields.end()) {
2281         it->value = value;
2282     } else {
2283         const CustomData newdata{qualifiedName, value};
2284         auto beforeIt = std::lower_bound(d->mCustomFields.begin(), d->mCustomFields.end(), newdata);
2285         d->mCustomFields.insert(beforeIt, newdata);
2286     }
2287 }
2288 
removeCustom(const QString & app,const QString & name)2289 void Addressee::removeCustom(const QString &app, const QString &name)
2290 {
2291     const QString qualifiedName = app + QLatin1Char('-') + name;
2292     auto it = d->findByName(qualifiedName);
2293     if (it != d->mCustomFields.end()) {
2294         d->mCustomFields.erase(it);
2295     }
2296 }
2297 
custom(const QString & app,const QString & name) const2298 QString Addressee::custom(const QString &app, const QString &name) const
2299 {
2300     const QString qualifiedName = app + QLatin1Char('-') + name;
2301     auto it = d->findByName(qualifiedName);
2302     return it != d->mCustomFields.cend() ? it->value : QString{};
2303 }
2304 
setCustoms(const QStringList & customs)2305 void Addressee::setCustoms(const QStringList &customs)
2306 {
2307     d->mEmpty = false;
2308 
2309     d->mCustomFields.clear();
2310 
2311     // Less than 10 elements in "customs", we needn't use a set
2312     QStringList seen;
2313     for (const QString &custom : customs) {
2314         const int index = custom.indexOf(QLatin1Char(':'));
2315         if (index == -1) {
2316             continue;
2317         }
2318 
2319         const QString qualifiedName = custom.left(index);
2320         const QString value = custom.mid(index + 1);
2321 
2322         if (!seen.contains(qualifiedName)) {
2323             d->mCustomFields.push_back({qualifiedName, value});
2324             seen.push_back(qualifiedName);
2325         }
2326     }
2327     std::sort(d->mCustomFields.begin(), d->mCustomFields.end());
2328 }
2329 
customs() const2330 QStringList Addressee::customs() const
2331 {
2332     QStringList result;
2333     result.reserve(d->mCustomFields.size());
2334 
2335     static const QLatin1Char sep(':');
2336     for (const auto &[name, value] : d->mCustomFields) {
2337         result << name + sep + value;
2338     }
2339 
2340     return result;
2341 }
2342 
parseEmailAddress(const QString & rawEmail,QString & fullName,QString & email)2343 void Addressee::parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
2344 {
2345     // This is a simplified version of KPIM::splitAddress().
2346 
2347     fullName.clear();
2348     email.clear();
2349     if (rawEmail.isEmpty()) {
2350         return; // KPIM::AddressEmpty;
2351     }
2352 
2353     // The code works on 8-bit strings, so convert the input to UTF-8.
2354     QByteArray address = rawEmail.toUtf8();
2355 
2356     QByteArray displayName;
2357     QByteArray addrSpec;
2358     QByteArray comment;
2359 
2360     // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
2361     // The purpose is to extract a displayable string from the mailboxes.
2362     // Comments in the addr-spec are not handled. No error checking is done.
2363 
2364     enum sourceLevel {
2365         TopLevel,
2366         InComment,
2367         InAngleAddress,
2368     };
2369     sourceLevel context = TopLevel;
2370     bool inQuotedString = false;
2371     int commentLevel = 0;
2372     bool stop = false;
2373 
2374     for (char *p = address.data(); *p && !stop; ++p) {
2375         switch (context) {
2376         case TopLevel:
2377             switch (*p) {
2378             case '"':
2379                 inQuotedString = !inQuotedString;
2380                 displayName += *p;
2381                 break;
2382             case '(':
2383                 if (!inQuotedString) {
2384                     context = InComment;
2385                     commentLevel = 1;
2386                 } else {
2387                     displayName += *p;
2388                 }
2389                 break;
2390             case '<':
2391                 if (!inQuotedString) {
2392                     context = InAngleAddress;
2393                 } else {
2394                     displayName += *p;
2395                 }
2396                 break;
2397             case '\\': // quoted character
2398                 displayName += *p;
2399                 ++p; // skip the '\'
2400                 if (*p) {
2401                     displayName += *p;
2402                 } else {
2403                     // return KPIM::UnexpectedEnd;
2404                     goto ABORT_PARSING;
2405                 }
2406                 break;
2407             case ',':
2408                 if (!inQuotedString) {
2409                     // if ( allowMultipleAddresses )
2410                     //  stop = true;
2411                     // else
2412                     //  return KPIM::UnexpectedComma;
2413                     goto ABORT_PARSING;
2414                 } else {
2415                     displayName += *p;
2416                 }
2417                 break;
2418             default:
2419                 displayName += *p;
2420             }
2421             break;
2422         case InComment:
2423             switch (*p) {
2424             case '(':
2425                 ++commentLevel;
2426                 comment += *p;
2427                 break;
2428             case ')':
2429                 --commentLevel;
2430                 if (commentLevel == 0) {
2431                     context = TopLevel;
2432                     comment += ' '; // separate the text of several comments
2433                 } else {
2434                     comment += *p;
2435                 }
2436                 break;
2437             case '\\': // quoted character
2438                 comment += *p;
2439                 ++p; // skip the '\'
2440                 if (*p) {
2441                     comment += *p;
2442                 } else {
2443                     // return KPIM::UnexpectedEnd;
2444                     goto ABORT_PARSING;
2445                 }
2446                 break;
2447             default:
2448                 comment += *p;
2449             }
2450             break;
2451         case InAngleAddress:
2452             switch (*p) {
2453             case '"':
2454                 inQuotedString = !inQuotedString;
2455                 addrSpec += *p;
2456                 break;
2457             case '>':
2458                 if (!inQuotedString) {
2459                     context = TopLevel;
2460                 } else {
2461                     addrSpec += *p;
2462                 }
2463                 break;
2464             case '\\': // quoted character
2465                 addrSpec += *p;
2466                 ++p; // skip the '\'
2467                 if (*p) {
2468                     addrSpec += *p;
2469                 } else {
2470                     // return KPIM::UnexpectedEnd;
2471                     goto ABORT_PARSING;
2472                 }
2473                 break;
2474             default:
2475                 addrSpec += *p;
2476             }
2477             break;
2478         } // switch ( context )
2479     }
2480 
2481 ABORT_PARSING:
2482     displayName = displayName.trimmed();
2483     comment = comment.trimmed();
2484     addrSpec = addrSpec.trimmed();
2485     fullName = QString::fromUtf8(displayName);
2486     email = QString::fromUtf8(addrSpec); // check for errors
2487     if (inQuotedString) {
2488         return; // KPIM::UnbalancedQuote;
2489     }
2490     if (context == InComment) {
2491         return; // KPIM::UnbalancedParens;
2492     }
2493     if (context == InAngleAddress) {
2494         return; // KPIM::UnclosedAngleAddr;
2495     }
2496 
2497     if (addrSpec.isEmpty()) {
2498         if (displayName.isEmpty()) {
2499             return; // KPIM::NoAddressSpec;
2500         } else {
2501             // addrSpec = displayName;
2502             // displayName.truncate( 0 );
2503             // Address of the form "foo@bar" or "foo@bar (Name)".
2504             email = fullName;
2505             fullName = QString::fromUtf8(comment);
2506         }
2507     }
2508 
2509     email = email.toLower();
2510 
2511     // Check that the full name is not enclosed in balanced double quotes.
2512     // If it is then remove them.
2513     const unsigned int len = fullName.length();
2514     if (len < 3) { // not long enough to be
2515         return;
2516     }
2517     if (fullName.startsWith(QLatin1Char('"')) && fullName.endsWith(QLatin1Char('"'))) {
2518         fullName = fullName.mid(1, len - 2);
2519     }
2520 }
2521 
setChanged(bool value)2522 void Addressee::setChanged(bool value)
2523 {
2524     d->mChanged = value;
2525 }
2526 
changed() const2527 bool Addressee::changed() const
2528 {
2529     return d->mChanged;
2530 }
2531 
mimeType()2532 QString Addressee::mimeType()
2533 {
2534     return QStringLiteral("text/directory");
2535 }
2536 
operator <<(QDataStream & s,const Addressee & a)2537 QDataStream &KContacts::operator<<(QDataStream &s, const Addressee &a)
2538 {
2539     s << a.d->mUid;
2540 
2541     s << a.d->mName;
2542     s << a.d->mFormattedName;
2543     s << a.d->mFamilyName;
2544     s << a.d->mGivenName;
2545     s << a.d->mAdditionalName;
2546     s << a.d->mPrefix;
2547     s << a.d->mSuffix;
2548     s << a.d->mBirthday;
2549     s << a.d->mBirthdayWithTime;
2550     s << a.d->mMailer;
2551     s << a.d->mTimeZone;
2552     s << a.d->mGeo;
2553     s << a.d->mDepartment;
2554     s << a.d->mNote;
2555     s << a.d->mProductId;
2556     s << a.d->mRevision;
2557     s << a.d->mSortString;
2558     s << a.d->mSecrecy;
2559     s << a.d->mLogo;
2560     s << a.d->mPhoto;
2561     s << a.d->mSound;
2562     s << a.d->mPhoneNumbers;
2563     s << a.d->mAddresses;
2564     s << a.d->mEmails;
2565     s << a.d->mCategories;
2566     s << a.customs();
2567     s << a.d->mKeys;
2568     s << a.d->mLangs;
2569     s << a.d->mGender;
2570     s << a.d->mKind;
2571     s << a.d->mCalendarUrl;
2572     s << a.d->mSoundListExtra;
2573     s << a.d->mPhotoExtraList;
2574     s << a.d->mLogoExtraList;
2575     s << a.d->mUrlExtraList;
2576     s << a.d->mMembers;
2577     s << a.d->mRelationships;
2578     s << a.d->mSources;
2579     s << a.d->mImpps;
2580     s << a.d->mFieldGroupList;
2581     s << a.d->mTitleExtraList;
2582     s << a.d->mRoleExtraList;
2583     s << a.d->mOrgExtraList;
2584     s << a.d->mNickNameExtraList;
2585     s << a.d->mClientPidMapList;
2586 
2587     return s;
2588 }
2589 
operator >>(QDataStream & s,Addressee & a)2590 QDataStream &KContacts::operator>>(QDataStream &s, Addressee &a)
2591 {
2592     s >> a.d->mUid;
2593 
2594     s >> a.d->mName;
2595     s >> a.d->mFormattedName;
2596     s >> a.d->mFamilyName;
2597     s >> a.d->mGivenName;
2598     s >> a.d->mAdditionalName;
2599     s >> a.d->mPrefix;
2600     s >> a.d->mSuffix;
2601     s >> a.d->mBirthday;
2602     s >> a.d->mBirthdayWithTime;
2603     s >> a.d->mMailer;
2604     s >> a.d->mTimeZone;
2605     s >> a.d->mGeo;
2606     s >> a.d->mDepartment;
2607     s >> a.d->mNote;
2608     s >> a.d->mProductId;
2609     s >> a.d->mRevision;
2610     s >> a.d->mSortString;
2611     s >> a.d->mSecrecy;
2612     s >> a.d->mLogo;
2613     s >> a.d->mPhoto;
2614     s >> a.d->mSound;
2615     s >> a.d->mPhoneNumbers;
2616     s >> a.d->mAddresses;
2617     s >> a.d->mEmails;
2618     s >> a.d->mCategories;
2619     QStringList customFields;
2620     s >> customFields;
2621     a.setCustoms(customFields);
2622     s >> a.d->mKeys;
2623     s >> a.d->mLangs;
2624     s >> a.d->mGender;
2625     s >> a.d->mKind;
2626     s >> a.d->mCalendarUrl;
2627     s >> a.d->mSoundListExtra;
2628     s >> a.d->mPhotoExtraList;
2629     s >> a.d->mLogoExtraList;
2630     s >> a.d->mUrlExtraList;
2631     s >> a.d->mMembers;
2632     s >> a.d->mRelationships;
2633     s >> a.d->mSources;
2634     s >> a.d->mImpps;
2635     s >> a.d->mFieldGroupList;
2636     s >> a.d->mTitleExtraList;
2637     s >> a.d->mRoleExtraList;
2638     s >> a.d->mOrgExtraList;
2639     s >> a.d->mNickNameExtraList;
2640     s >> a.d->mClientPidMapList;
2641     a.d->mEmpty = false;
2642 
2643     return s;
2644 }
2645 
matchBinaryPattern(int value,int pattern)2646 bool matchBinaryPattern(int value, int pattern)
2647 {
2648     /**
2649       We want to match all telephonnumbers/addresses which have the bits in the
2650       pattern set. More are allowed.
2651       if pattern == 0 we have a special handling, then we want only those with
2652       exactly no bit set.
2653      */
2654     if (pattern == 0) {
2655         return value == 0;
2656     } else {
2657         return pattern == (pattern & value);
2658     }
2659 }
2660 
2661 template<class L>
listEquals(const QVector<L> & list,const QVector<L> & pattern)2662 bool listEquals(const QVector<L> &list, const QVector<L> &pattern)
2663 {
2664     if (list.count() != pattern.count()) {
2665         return false;
2666     }
2667     const int numberOfElement(list.count());
2668     for (int i = 0; i < numberOfElement; ++i) {
2669         if (!pattern.contains(list[i])) {
2670             return false;
2671         }
2672     }
2673 
2674     return true;
2675 }
2676 
listEquals(const QStringList & list,const QStringList & pattern)2677 bool listEquals(const QStringList &list, const QStringList &pattern)
2678 {
2679     if (list.count() != pattern.count()) {
2680         return false;
2681     }
2682 
2683     const int numberOfElement(list.count());
2684     for (int i = 0; i < numberOfElement; ++i) {
2685         if (!pattern.contains(list[i])) {
2686             return false;
2687         }
2688     }
2689 
2690     return true;
2691 }
2692 
2693 template<typename T>
toVariantList(const QVector<T> & v)2694 static QVariantList toVariantList(const QVector<T> &v)
2695 {
2696     QVariantList l;
2697     l.reserve(v.size());
2698     std::transform(v.begin(), v.end(), std::back_inserter(l), [](const T &elem) {
2699         return QVariant::fromValue(elem);
2700     });
2701     return l;
2702 }
2703 
2704 template<typename T>
fromVariantList(const QVariantList & v)2705 static QVector<T> fromVariantList(const QVariantList &v)
2706 {
2707     QVector<T> l;
2708     l.reserve(v.size());
2709     std::transform(v.begin(), v.end(), std::back_inserter(l), [](const QVariant &elem) {
2710         return elem.value<T>();
2711     });
2712     return l;
2713 }
2714 
emailsVariant() const2715 QVariantList Addressee::emailsVariant() const
2716 {
2717     return toVariantList(d->mEmails);
2718 }
2719 
setEmailsVariant(const QVariantList & emails)2720 void Addressee::setEmailsVariant(const QVariantList &emails)
2721 {
2722     setEmailList(fromVariantList<Email>(emails));
2723 }
2724 
setPhoneNumbersVariant(const QVariantList & emails)2725 void Addressee::setPhoneNumbersVariant(const QVariantList &emails)
2726 {
2727     setPhoneNumbers(fromVariantList<PhoneNumber>(emails));
2728 }
2729 
phoneNumbersVariant() const2730 QVariantList Addressee::phoneNumbersVariant() const
2731 {
2732     return toVariantList(d->mPhoneNumbers);
2733 }
2734 
addressesVariant() const2735 QVariantList Addressee::addressesVariant() const
2736 {
2737     return toVariantList(d->mAddresses);
2738 }
2739 
urlsVariant() const2740 QVariantList Addressee::urlsVariant() const
2741 {
2742     return toVariantList(d->mUrlExtraList);
2743 }
2744 
imppsVariant() const2745 QVariantList Addressee::imppsVariant() const
2746 {
2747     return toVariantList(d->mImpps);
2748 }
2749 
setImppsVariant(const QVariantList & impps)2750 void Addressee::setImppsVariant(const QVariantList &impps)
2751 {
2752     setImppList(fromVariantList<Impp>(impps));
2753 }
2754 
2755 #include "moc_addressee.cpp"
2756