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_p.h"
20#include "parametermap_p.h"
21
22using namespace KContacts;
23
24static bool matchBinaryPattern(int value, int pattern);
25
26template<class L>
27static bool listEquals(const QList<L> &list, const QList<L> &pattern);
28static bool listEquals(const QStringList &list, const QStringList &pattern);
29
30struct CustomData {
31 QString name;
32 QString value;
33};
34
35inline bool operator==(const CustomData &a, const CustomData &b)
36{
37 return std::tie(args: a.name, args: a.value) == std::tie(args: b.name, args: b.value);
38}
39
40inline bool operator!=(const CustomData &a, const CustomData &b)
41{
42 return std::tie(args: a.name, args: a.value) != std::tie(args: b.name, args: b.value);
43}
44
45inline bool operator<(const CustomData &a, const CustomData &b)
46{
47 return a.name < b.name;
48}
49
50class Q_DECL_HIDDEN Addressee::Private : public QSharedData
51{
52public:
53 Private()
54 : mUid(QUuid::createUuid().toString().mid(position: 1, n: 36))
55 , mEmpty(true)
56 , mChanged(false)
57 , mBirthdayWithTime(false)
58 {
59 // We avoid the curly braces so the string is RFC4122 compliant and can be used as urn
60 }
61
62 Private(const Private &other)
63 : QSharedData(other)
64 {
65 mUid = other.mUid;
66 mName = other.mName;
67 mFormattedName = other.mFormattedName;
68 mFamilyName = other.mFamilyName;
69 mGivenName = other.mGivenName;
70 mAdditionalName = other.mAdditionalName;
71 mPrefix = other.mPrefix;
72 mSuffix = other.mSuffix;
73 mBirthday = other.mBirthday;
74 mBirthdayWithTime = other.mBirthdayWithTime;
75 mMailer = other.mMailer;
76 mTimeZone = other.mTimeZone;
77 mGeo = other.mGeo;
78 mDepartment = other.mDepartment;
79 mNote = other.mNote;
80 mProductId = other.mProductId;
81 mRevision = other.mRevision;
82 mSortString = other.mSortString;
83 mSecrecy = other.mSecrecy;
84 mLogo = other.mLogo;
85 mPhoto = other.mPhoto;
86 mSound = other.mSound;
87
88 mPhoneNumbers = other.mPhoneNumbers;
89 mAddresses = other.mAddresses;
90 mKeys = other.mKeys;
91 mLangs = other.mLangs;
92 mGender = other.mGender;
93 mEmails = other.mEmails;
94 mCategories = other.mCategories;
95 mCustomFields = other.mCustomFields;
96 mCalendarUrl = other.mCalendarUrl;
97 mSoundListExtra = other.mSoundListExtra;
98 mPhotoExtraList = other.mPhotoExtraList;
99 mLogoExtraList = other.mLogoExtraList;
100 mUrlExtraList = other.mUrlExtraList;
101 mMembers = other.mMembers;
102 mRelationships = other.mRelationships;
103 mSources = other.mSources;
104 mEmpty = other.mEmpty;
105 mImpps = other.mImpps;
106 mChanged = other.mChanged;
107 mTitleExtraList = other.mTitleExtraList;
108 mRoleExtraList = other.mRoleExtraList;
109 mOrgExtraList = other.mOrgExtraList;
110 mDeathDate = other.mDeathDate;
111 }
112
113 ~Private()
114 {
115 }
116
117 std::vector<CustomData>::iterator findByName(const QString &qualifiedName)
118 {
119 return std::find_if(first: mCustomFields.begin(), last: mCustomFields.end(), pred: [&qualifiedName](const CustomData &info) {
120 return info.name == qualifiedName;
121 });
122 }
123
124 std::vector<CustomData>::const_iterator findByName(const QString &qualifiedName) const
125 {
126 return std::find_if(first: mCustomFields.cbegin(), last: mCustomFields.cend(), pred: [&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 ;
150 Picture mPhoto;
151 Sound mSound;
152 QDateTime mDeathDate;
153
154 PhoneNumber::List mPhoneNumbers;
155 Address::List mAddresses;
156 Key::List mKeys;
157 Email::List mEmails;
158 Lang::List mLangs;
159 Impp::List mImpps;
160 Gender mGender;
161 QString mKind;
162 QStringList mCategories;
163 std::vector<CustomData> mCustomFields;
164 CalendarUrl::List mCalendarUrl;
165 Sound::List mSoundListExtra;
166 Picture::List mPhotoExtraList;
167 Picture::List mLogoExtraList;
168 ResourceLocatorUrl::List mUrlExtraList;
169 QList<QUrl> mSources;
170 QStringList mMembers;
171 Related::List mRelationships;
172 FieldGroup::List mFieldGroupList;
173 Title::List mTitleExtraList;
174 Role::List mRoleExtraList;
175 Org::List mOrgExtraList;
176 NickName::List mNickNameExtraList;
177 ClientPidMap::List mClientPidMapList;
178 bool mEmpty : 1;
179 bool mChanged : 1;
180 bool mBirthdayWithTime;
181};
182
183Addressee::Addressee()
184 : d(new Private)
185{
186}
187
188Addressee::~Addressee()
189{
190}
191
192Addressee::Addressee(const Addressee &other)
193 : d(other.d)
194{
195}
196
197Addressee &Addressee::operator=(const Addressee &other)
198{
199 if (this != &other) {
200 d = other.d;
201 }
202
203 return *this;
204}
205
206bool Addressee::operator==(const Addressee &addressee) const
207{
208 if (d->mUid != addressee.d->mUid) {
209 qCDebug(KCONTACTS_LOG) << "uid differs";
210 return false;
211 }
212
213 if (d->mName != addressee.d->mName //
214 && !(d->mName.isEmpty() && addressee.d->mName.isEmpty())) {
215 qCDebug(KCONTACTS_LOG) << "name differs";
216 return false;
217 }
218
219 if (d->mFormattedName != addressee.d->mFormattedName //
220 && !(d->mFormattedName.isEmpty() && addressee.d->mFormattedName.isEmpty())) {
221 qCDebug(KCONTACTS_LOG) << "formattedName differs";
222 return false;
223 }
224
225 if (d->mFamilyName != addressee.d->mFamilyName //
226 && !(d->mFamilyName.isEmpty() && addressee.d->mFamilyName.isEmpty())) {
227 qCDebug(KCONTACTS_LOG) << "familyName differs";
228 return false;
229 }
230
231 if (d->mGivenName != addressee.d->mGivenName //
232 && !(d->mGivenName.isEmpty() && addressee.d->mGivenName.isEmpty())) {
233 qCDebug(KCONTACTS_LOG) << "givenName differs";
234 return false;
235 }
236
237 if (d->mAdditionalName != addressee.d->mAdditionalName //
238 && !(d->mAdditionalName.isEmpty() && addressee.d->mAdditionalName.isEmpty())) {
239 qCDebug(KCONTACTS_LOG) << "additionalName differs";
240 return false;
241 }
242
243 if (d->mPrefix != addressee.d->mPrefix //
244 && !(d->mPrefix.isEmpty() && addressee.d->mPrefix.isEmpty())) {
245 qCDebug(KCONTACTS_LOG) << "prefix differs";
246 return false;
247 }
248
249 if (d->mSuffix != addressee.d->mSuffix //
250 && !(d->mSuffix.isEmpty() && addressee.d->mSuffix.isEmpty())) {
251 qCDebug(KCONTACTS_LOG) << "suffix differs";
252 return false;
253 }
254
255 if (d->mBirthday != addressee.d->mBirthday //
256 || d->mBirthdayWithTime != addressee.d->mBirthdayWithTime) {
257 qCDebug(KCONTACTS_LOG) << "birthday differs";
258 return false;
259 }
260
261 if (d->mMailer != addressee.d->mMailer //
262 && !(d->mMailer.isEmpty() && addressee.d->mMailer.isEmpty())) {
263 qCDebug(KCONTACTS_LOG) << "mailer differs";
264 return false;
265 }
266
267 if (d->mTimeZone != addressee.d->mTimeZone) {
268 qCDebug(KCONTACTS_LOG) << "timeZone differs";
269 return false;
270 }
271
272 if (d->mGeo != addressee.d->mGeo) {
273 qCDebug(KCONTACTS_LOG) << "geo differs";
274 return false;
275 }
276
277 if (d->mDepartment != addressee.d->mDepartment //
278 && !(d->mDepartment.isEmpty() && addressee.d->mDepartment.isEmpty())) {
279 qCDebug(KCONTACTS_LOG) << "department differs";
280 return false;
281 }
282
283 if (d->mNote != addressee.d->mNote //
284 && !(d->mNote.isEmpty() && addressee.d->mNote.isEmpty())) {
285 qCDebug(KCONTACTS_LOG) << "note differs";
286 return false;
287 }
288
289 if (d->mProductId != addressee.d->mProductId //
290 && !(d->mProductId.isEmpty() && addressee.d->mProductId.isEmpty())) {
291 qCDebug(KCONTACTS_LOG) << "productId differs";
292 return false;
293 }
294
295 if (d->mSortString != addressee.d->mSortString //
296 && !(d->mSortString.isEmpty() && addressee.d->mSortString.isEmpty())) {
297 qCDebug(KCONTACTS_LOG) << "sortString differs";
298 return false;
299 }
300
301 if (d->mSecrecy != addressee.d->mSecrecy) {
302 qCDebug(KCONTACTS_LOG) << "secrecy differs";
303 return false;
304 }
305
306 if (d->mLogo != addressee.d->mLogo) {
307 qCDebug(KCONTACTS_LOG) << "logo differs";
308 return false;
309 }
310
311 if (d->mPhoto != addressee.d->mPhoto) {
312 qCDebug(KCONTACTS_LOG) << "photo differs";
313 return false;
314 }
315
316 if (d->mSound != addressee.d->mSound) {
317 qCDebug(KCONTACTS_LOG) << "sound differs";
318 return false;
319 }
320
321 if (!listEquals(list: d->mPhoneNumbers, pattern: addressee.d->mPhoneNumbers)) {
322 qCDebug(KCONTACTS_LOG) << "phoneNumbers differs";
323 return false;
324 }
325
326 if (!listEquals(list: d->mAddresses, pattern: addressee.d->mAddresses)) {
327 qCDebug(KCONTACTS_LOG) << "addresses differs";
328 return false;
329 }
330
331 if (!listEquals(list: d->mKeys, pattern: addressee.d->mKeys)) {
332 qCDebug(KCONTACTS_LOG) << "keys differs";
333 return false;
334 }
335 if (!listEquals(list: d->mEmails, pattern: addressee.d->mEmails)) {
336 qCDebug(KCONTACTS_LOG) << "emails differs";
337 return false;
338 }
339
340 if (!listEquals(list: d->mCategories, pattern: addressee.d->mCategories)) {
341 qCDebug(KCONTACTS_LOG) << "categories differs";
342 return false;
343 }
344
345 if (d->mCustomFields != addressee.d->mCustomFields) {
346 qCDebug(KCONTACTS_LOG) << "custom differs";
347 return false;
348 }
349 if (d->mLangs != addressee.d->mLangs) {
350 qCDebug(KCONTACTS_LOG) << "langs differs";
351 return false;
352 }
353 if (d->mImpps != addressee.d->mImpps) {
354 qCDebug(KCONTACTS_LOG) << "impps differs";
355 return false;
356 }
357 if (d->mGender != addressee.d->mGender) {
358 qCDebug(KCONTACTS_LOG) << "gender differs";
359 return false;
360 }
361 if (d->mKind != addressee.d->mKind) {
362 qCDebug(KCONTACTS_LOG) << "kind differs";
363 return false;
364 }
365 if (!listEquals(list: d->mCalendarUrl, pattern: addressee.d->mCalendarUrl)) {
366 qCDebug(KCONTACTS_LOG) << "calendarUrl differs";
367 return false;
368 }
369 if (!listEquals(list: d->mSoundListExtra, pattern: addressee.d->mSoundListExtra)) {
370 qCDebug(KCONTACTS_LOG) << "Extra sound differs";
371 return false;
372 }
373 if (!listEquals(list: d->mPhotoExtraList, pattern: addressee.d->mPhotoExtraList)) {
374 qCDebug(KCONTACTS_LOG) << "Extra photo differs";
375 return false;
376 }
377 if (!listEquals(list: d->mLogoExtraList, pattern: addressee.d->mLogoExtraList)) {
378 qCDebug(KCONTACTS_LOG) << "Extra logo differs";
379 return false;
380 }
381 if (!listEquals(list: d->mUrlExtraList, pattern: addressee.d->mUrlExtraList)) {
382 qCDebug(KCONTACTS_LOG) << "Extra url differs";
383 return false;
384 }
385 if (!listEquals(list: d->mMembers, pattern: addressee.d->mMembers)) {
386 qCDebug(KCONTACTS_LOG) << "Extra url differs";
387 return false;
388 }
389 if (!listEquals(list: d->mRelationships, pattern: addressee.d->mRelationships)) {
390 qCDebug(KCONTACTS_LOG) << "Relationships differs";
391 return false;
392 }
393 if (!listEquals(list: d->mSources, pattern: addressee.d->mSources)) {
394 qCDebug(KCONTACTS_LOG) << "Sources differs";
395 return false;
396 }
397
398 if (!listEquals(list: d->mFieldGroupList, pattern: addressee.d->mFieldGroupList)) {
399 qCDebug(KCONTACTS_LOG) << "Field Groups differs";
400 return false;
401 }
402
403 if (!listEquals(list: d->mTitleExtraList, pattern: addressee.d->mTitleExtraList)) {
404 qCDebug(KCONTACTS_LOG) << "Extra TitleList differs";
405 return false;
406 }
407
408 if (!listEquals(list: d->mRoleExtraList, pattern: addressee.d->mRoleExtraList)) {
409 qCDebug(KCONTACTS_LOG) << "Extra RoleList differs";
410 return false;
411 }
412
413 if (!listEquals(list: d->mOrgExtraList, pattern: addressee.d->mOrgExtraList)) {
414 qCDebug(KCONTACTS_LOG) << "Extra Organization List differs";
415 return false;
416 }
417
418 if (!listEquals(list: d->mNickNameExtraList, pattern: addressee.d->mNickNameExtraList)) {
419 qCDebug(KCONTACTS_LOG) << "Extra NickName List differs";
420 return false;
421 }
422
423 if (!listEquals(list: d->mClientPidMapList, pattern: addressee.d->mClientPidMapList)) {
424 qCDebug(KCONTACTS_LOG) << "ClientPidMap List differs";
425 return false;
426 }
427
428 if (d->mDeathDate != addressee.d->mDeathDate) {
429 qCDebug(KCONTACTS_LOG) << "death date differs";
430 return false;
431 }
432 return true;
433}
434
435bool Addressee::operator!=(const Addressee &a) const
436{
437 return !(a == *this);
438}
439
440bool Addressee::isEmpty() const
441{
442 return d->mEmpty;
443}
444
445void Addressee::setUid(const QString &id)
446{
447 if (id == d->mUid) {
448 return;
449 }
450
451 d->mEmpty = false;
452 d->mUid = id;
453}
454
455QString Addressee::uid() const
456{
457 return d->mUid;
458}
459
460QString Addressee::uidLabel()
461{
462 return i18n("Unique Identifier");
463}
464
465void Addressee::setName(const QString &name)
466{
467 if (name == d->mName) {
468 return;
469 }
470
471 d->mEmpty = false;
472 d->mName = name;
473}
474
475QString Addressee::name() const
476{
477 return d->mName;
478}
479
480QString Addressee::nameLabel()
481{
482 return i18n("Name");
483}
484
485void Addressee::setKind(const QString &kind)
486{
487 if (kind == d->mKind) {
488 return;
489 }
490
491 d->mEmpty = false;
492 d->mKind = kind;
493}
494
495QString Addressee::kind() const
496{
497 return d->mKind;
498}
499
500void Addressee::insertExtraSound(const Sound &sound)
501{
502 d->mEmpty = false;
503 d->mSoundListExtra.append(t: sound);
504}
505
506Sound::List Addressee::extraSoundList() const
507{
508 return d->mSoundListExtra;
509}
510
511void Addressee::insertExtraPhoto(const Picture &picture)
512{
513 d->mEmpty = false;
514 d->mPhotoExtraList.append(t: picture);
515}
516
517Picture::List Addressee::extraPhotoList() const
518{
519 return d->mPhotoExtraList;
520}
521
522void Addressee::insertExtraLogo(const Picture &)
523{
524 d->mEmpty = false;
525 d->mLogoExtraList.append(t: logo);
526}
527
528Picture::List Addressee::extraLogoList() const
529{
530 return d->mLogoExtraList;
531}
532
533void Addressee::setExtraSoundList(const Sound::List &soundList)
534{
535 d->mEmpty = false;
536 d->mSoundListExtra = soundList;
537}
538
539void Addressee::setExtraPhotoList(const Picture::List &pictureList)
540{
541 d->mEmpty = false;
542 d->mPhotoExtraList = pictureList;
543}
544
545void Addressee::setExtraLogoList(const Picture::List &logoList)
546{
547 d->mEmpty = false;
548 d->mLogoExtraList = logoList;
549}
550
551void Addressee::insertExtraUrl(const ResourceLocatorUrl &url)
552{
553 if (url.isValid()) {
554 d->mEmpty = false;
555 d->mUrlExtraList.append(t: url);
556 }
557}
558
559void Addressee::setExtraUrlList(const ResourceLocatorUrl::List &urlList)
560{
561 d->mEmpty = false;
562 d->mUrlExtraList = urlList;
563}
564
565ResourceLocatorUrl::List Addressee::extraUrlList() const
566{
567 return d->mUrlExtraList;
568}
569
570void Addressee::insertSourceUrl(const QUrl &url)
571{
572 d->mEmpty = false;
573 d->mSources.append(t: url);
574}
575
576void Addressee::setSourcesUrlList(const QList<QUrl> &urlList)
577{
578 d->mEmpty = false;
579 d->mSources = urlList;
580}
581
582QList<QUrl> Addressee::sourcesUrlList() const
583{
584 return d->mSources;
585}
586
587FieldGroup::List Addressee::fieldGroupList() const
588{
589 return d->mFieldGroupList;
590}
591
592void Addressee::setFieldGroupList(const FieldGroup::List &fieldGroupList)
593{
594 d->mEmpty = false;
595 d->mFieldGroupList = fieldGroupList;
596}
597
598void Addressee::insertFieldGroup(const FieldGroup &fieldGroup)
599{
600 if (fieldGroup.isValid()) {
601 d->mEmpty = false;
602 // TODO don't duplicate ?
603 d->mFieldGroupList.append(t: fieldGroup);
604 }
605}
606
607ClientPidMap::List Addressee::clientPidMapList() const
608{
609 return d->mClientPidMapList;
610}
611
612void Addressee::setClientPidMapList(const ClientPidMap::List &clientpidmaplist)
613{
614 d->mEmpty = false;
615 d->mClientPidMapList = clientpidmaplist;
616}
617
618void Addressee::insertClientPidMap(const ClientPidMap &clientpidmap)
619{
620 if (clientpidmap.isValid()) {
621 d->mEmpty = false;
622 // TODO don't duplicate ?
623 d->mClientPidMapList.append(t: clientpidmap);
624 }
625}
626
627void Addressee::insertImpp(const Impp &impp)
628{
629 if (impp.isValid()) {
630 d->mEmpty = false;
631 // Don't duplicate ?
632 d->mImpps.append(t: impp);
633 }
634}
635
636void Addressee::setImppList(const Impp::List &imppList)
637{
638 d->mEmpty = false;
639 d->mImpps = imppList;
640}
641
642Impp::List Addressee::imppList() const
643{
644 return d->mImpps;
645}
646
647void Addressee::insertCalendarUrl(const CalendarUrl &calendarUrl)
648{
649 d->mEmpty = false;
650 // TODO verify that there is not same calendarurl
651 if (calendarUrl.isValid()) {
652 d->mCalendarUrl.append(t: calendarUrl);
653 }
654}
655
656CalendarUrl::List Addressee::calendarUrlList() const
657{
658 return d->mCalendarUrl;
659}
660
661void Addressee::setFormattedName(const QString &formattedName)
662{
663 if (formattedName == d->mFormattedName) {
664 return;
665 }
666
667 d->mEmpty = false;
668 d->mFormattedName = formattedName;
669}
670
671QString Addressee::formattedName() const
672{
673 return d->mFormattedName;
674}
675
676QString Addressee::formattedNameLabel()
677{
678 return i18n("Formatted Name");
679}
680
681void Addressee::setFamilyName(const QString &familyName)
682{
683 if (familyName == d->mFamilyName) {
684 return;
685 }
686
687 d->mEmpty = false;
688 d->mFamilyName = familyName;
689}
690
691QString Addressee::familyName() const
692{
693 return d->mFamilyName;
694}
695
696QString Addressee::familyNameLabel()
697{
698 return i18n("Family Name");
699}
700
701void Addressee::setGivenName(const QString &givenName)
702{
703 if (givenName == d->mGivenName) {
704 return;
705 }
706
707 d->mEmpty = false;
708 d->mGivenName = givenName;
709}
710
711QString Addressee::givenName() const
712{
713 return d->mGivenName;
714}
715
716QString Addressee::givenNameLabel()
717{
718 return i18n("Given Name");
719}
720
721void Addressee::setAdditionalName(const QString &additionalName)
722{
723 if (additionalName == d->mAdditionalName) {
724 return;
725 }
726
727 d->mEmpty = false;
728 d->mAdditionalName = additionalName;
729}
730
731QString Addressee::additionalName() const
732{
733 return d->mAdditionalName;
734}
735
736QString Addressee::additionalNameLabel()
737{
738 return i18n("Additional Names");
739}
740
741void Addressee::setPrefix(const QString &prefix)
742{
743 if (prefix == d->mPrefix) {
744 return;
745 }
746
747 d->mEmpty = false;
748 d->mPrefix = prefix;
749}
750
751QString Addressee::prefix() const
752{
753 return d->mPrefix;
754}
755
756QString Addressee::prefixLabel()
757{
758 return i18n("Honorific Prefixes");
759}
760
761void Addressee::setSuffix(const QString &suffix)
762{
763 if (suffix == d->mSuffix) {
764 return;
765 }
766
767 d->mEmpty = false;
768 d->mSuffix = suffix;
769}
770
771QString Addressee::suffix() const
772{
773 return d->mSuffix;
774}
775
776QString Addressee::suffixLabel()
777{
778 return i18n("Honorific Suffixes");
779}
780
781void Addressee::setNickName(const QString &nickName)
782{
783 NickName t(nickName);
784 if (!d->mNickNameExtraList.isEmpty()) {
785 t = d->mNickNameExtraList.takeFirst();
786 t.setNickName(nickName);
787 d->mNickNameExtraList.prepend(t);
788 d->mEmpty = false;
789 } else {
790 insertExtraNickName(nickName: t);
791 }
792}
793
794void Addressee::setNickName(const NickName &nickName)
795{
796 insertExtraNickName(nickName);
797}
798
799void Addressee::insertExtraNickName(const NickName &nickName)
800{
801 if (nickName.isValid()) {
802 d->mEmpty = false;
803 d->mNickNameExtraList.append(t: nickName);
804 }
805}
806
807void Addressee::setExtraNickNameList(const NickName::List &nickNameList)
808{
809 d->mEmpty = false;
810 d->mNickNameExtraList = nickNameList;
811}
812
813NickName::List Addressee::extraNickNameList() const
814{
815 return d->mNickNameExtraList;
816}
817
818QString Addressee::nickName() const
819{
820 if (d->mNickNameExtraList.isEmpty()) {
821 return {};
822 } else {
823 return d->mNickNameExtraList.at(i: 0).nickname();
824 }
825}
826
827QString Addressee::nickNameLabel()
828{
829 return i18n("Nick Name");
830}
831
832void Addressee::setBirthday(const QDateTime &birthday, bool withTime)
833{
834 if (birthday == d->mBirthday && d->mBirthdayWithTime == withTime) {
835 return;
836 }
837
838 d->mEmpty = false;
839 d->mBirthday = birthday;
840 if (!withTime) {
841 d->mBirthday.setTime(time: QTime());
842 }
843 d->mBirthdayWithTime = withTime;
844}
845
846void Addressee::setBirthday(const QDate &birthday)
847{
848 if (birthday == d->mBirthday.date() && !d->mBirthdayWithTime) {
849 return;
850 }
851
852 d->mEmpty = false;
853 d->mBirthday = QDateTime(birthday, QTime());
854 d->mBirthdayWithTime = false;
855}
856
857QDateTime Addressee::birthday() const
858{
859 return d->mBirthday;
860}
861
862void Addressee::setDeathDate(const QDateTime &deathDate)
863{
864 if (deathDate == d->mDeathDate) {
865 return;
866 }
867
868 d->mEmpty = false;
869 d->mDeathDate = deathDate;
870}
871
872QDateTime Addressee::deathDate() const
873{
874 return d->mDeathDate;
875}
876
877bool Addressee::birthdayHasTime() const
878{
879 return d->mBirthdayWithTime;
880}
881
882QString Addressee::birthdayLabel()
883{
884 return i18n("Birthday");
885}
886
887QString Addressee::homeAddressStreetLabel()
888{
889 return i18n("Home Address Street");
890}
891
892QString Addressee::homeAddressPostOfficeBoxLabel()
893{
894 return i18n("Home Address Post Office Box");
895}
896
897QString Addressee::homeAddressLocalityLabel()
898{
899 return i18n("Home Address City");
900}
901
902QString Addressee::homeAddressRegionLabel()
903{
904 return i18n("Home Address State");
905}
906
907QString Addressee::homeAddressPostalCodeLabel()
908{
909 return i18n("Home Address Zip Code");
910}
911
912QString Addressee::homeAddressCountryLabel()
913{
914 return i18n("Home Address Country");
915}
916
917QString Addressee::homeAddressLabelLabel()
918{
919 return i18n("Home Address Label");
920}
921
922QString Addressee::businessAddressStreetLabel()
923{
924 return i18n("Business Address Street");
925}
926
927QString Addressee::businessAddressPostOfficeBoxLabel()
928{
929 return i18n("Business Address Post Office Box");
930}
931
932QString Addressee::businessAddressLocalityLabel()
933{
934 return i18n("Business Address City");
935}
936
937QString Addressee::businessAddressRegionLabel()
938{
939 return i18n("Business Address State");
940}
941
942QString Addressee::businessAddressPostalCodeLabel()
943{
944 return i18n("Business Address Zip Code");
945}
946
947QString Addressee::businessAddressCountryLabel()
948{
949 return i18n("Business Address Country");
950}
951
952QString Addressee::businessAddressLabelLabel()
953{
954 return i18n("Business Address Label");
955}
956
957QString Addressee::homePhoneLabel()
958{
959 return i18n("Home Phone");
960}
961
962QString Addressee::businessPhoneLabel()
963{
964 return i18n("Business Phone");
965}
966
967QString Addressee::mobilePhoneLabel()
968{
969 return i18n("Mobile Phone");
970}
971
972QString Addressee::homeFaxLabel()
973{
974 return i18n("Home Fax");
975}
976
977QString Addressee::businessFaxLabel()
978{
979 return i18n("Business Fax");
980}
981
982QString Addressee::carPhoneLabel()
983{
984 return i18n("Car Phone");
985}
986
987QString Addressee::isdnLabel()
988{
989 return i18n("ISDN");
990}
991
992QString Addressee::pagerLabel()
993{
994 return i18n("Pager");
995}
996
997QString Addressee::emailLabel()
998{
999 return i18n("Email Address");
1000}
1001
1002void Addressee::setMailer(const QString &mailer)
1003{
1004 if (mailer == d->mMailer) {
1005 return;
1006 }
1007
1008 d->mEmpty = false;
1009 d->mMailer = mailer;
1010}
1011
1012QString Addressee::mailer() const
1013{
1014 return d->mMailer;
1015}
1016
1017QString Addressee::mailerLabel()
1018{
1019 return i18n("Mail Client");
1020}
1021
1022void Addressee::setTimeZone(const TimeZone &timeZone)
1023{
1024 if (timeZone == d->mTimeZone) {
1025 return;
1026 }
1027
1028 d->mEmpty = false;
1029 d->mTimeZone = timeZone;
1030}
1031
1032TimeZone Addressee::timeZone() const
1033{
1034 return d->mTimeZone;
1035}
1036
1037QString Addressee::timeZoneLabel()
1038{
1039 return i18n("Time Zone");
1040}
1041
1042void Addressee::setGeo(const Geo &geo)
1043{
1044 if (geo == d->mGeo) {
1045 return;
1046 }
1047
1048 d->mEmpty = false;
1049 d->mGeo = geo;
1050}
1051
1052Geo Addressee::geo() const
1053{
1054 return d->mGeo;
1055}
1056
1057QString Addressee::geoLabel()
1058{
1059 return i18n("Geographic Position");
1060}
1061
1062void Addressee::setTitle(const QString &title)
1063{
1064 Title t(title);
1065 if (!d->mTitleExtraList.isEmpty()) {
1066 t = d->mTitleExtraList.takeFirst();
1067 t.setTitle(title);
1068 d->mTitleExtraList.prepend(t);
1069 d->mEmpty = false;
1070 } else {
1071 insertExtraTitle(title);
1072 }
1073}
1074
1075void Addressee::setTitle(const Title &title)
1076{
1077 insertExtraTitle(title);
1078}
1079
1080void Addressee::insertExtraTitle(const Title &title)
1081{
1082 if (title.isValid()) {
1083 d->mEmpty = false;
1084 d->mTitleExtraList.append(t: title);
1085 }
1086}
1087
1088QString Addressee::title() const
1089{
1090 if (d->mTitleExtraList.isEmpty()) {
1091 return {};
1092 } else {
1093 return d->mTitleExtraList.at(i: 0).title();
1094 }
1095}
1096
1097Title::List Addressee::extraTitleList() const
1098{
1099 return d->mTitleExtraList;
1100}
1101
1102void Addressee::setExtraTitleList(const Title::List &urltitle)
1103{
1104 d->mEmpty = false;
1105 d->mTitleExtraList = urltitle;
1106}
1107
1108QString Addressee::titleLabel()
1109{
1110 return i18nc("a person's title", "Title");
1111}
1112
1113void Addressee::setRole(const QString &role)
1114{
1115 Role t(role);
1116 if (!d->mRoleExtraList.isEmpty()) {
1117 t = d->mRoleExtraList.takeFirst();
1118 t.setRole(role);
1119 d->mRoleExtraList.prepend(t);
1120 d->mEmpty = false;
1121 } else {
1122 insertExtraRole(role: t);
1123 }
1124}
1125
1126void Addressee::setRole(const Role &role)
1127{
1128 insertExtraRole(role);
1129}
1130
1131void Addressee::insertExtraRole(const Role &role)
1132{
1133 if (role.isValid()) {
1134 d->mEmpty = false;
1135 d->mRoleExtraList.append(t: role);
1136 }
1137}
1138
1139void Addressee::setExtraRoleList(const Role::List &roleList)
1140{
1141 d->mEmpty = false;
1142 d->mRoleExtraList = roleList;
1143}
1144
1145Role::List Addressee::extraRoleList() const
1146{
1147 return d->mRoleExtraList;
1148}
1149
1150QString Addressee::role() const
1151{
1152 if (d->mRoleExtraList.isEmpty()) {
1153 return {};
1154 } else {
1155 return d->mRoleExtraList.at(i: 0).role();
1156 }
1157}
1158
1159QString Addressee::roleLabel()
1160{
1161 return i18nc("of a person in an organization", "Role");
1162}
1163
1164void Addressee::setOrganization(const QString &organization)
1165{
1166 Org t(organization);
1167 if (!d->mOrgExtraList.isEmpty()) {
1168 t = d->mOrgExtraList.takeFirst();
1169 t.setOrganization(organization);
1170 d->mOrgExtraList.prepend(t);
1171 d->mEmpty = false;
1172 } else {
1173 insertExtraOrganization(organization: t);
1174 }
1175}
1176
1177void Addressee::setOrganization(const Org &organization)
1178{
1179 insertExtraOrganization(organization);
1180}
1181
1182void Addressee::insertExtraOrganization(const Org &organization)
1183{
1184 if (organization.isValid()) {
1185 d->mEmpty = false;
1186 d->mOrgExtraList.append(t: organization);
1187 }
1188}
1189
1190void Addressee::setExtraOrganizationList(const Org::List &orgList)
1191{
1192 d->mEmpty = false;
1193 d->mOrgExtraList = orgList;
1194}
1195
1196Org::List Addressee::extraOrganizationList() const
1197{
1198 return d->mOrgExtraList;
1199}
1200
1201QString Addressee::organization() const
1202{
1203 if (d->mOrgExtraList.isEmpty()) {
1204 return {};
1205 } else {
1206 return d->mOrgExtraList.at(i: 0).organization();
1207 }
1208}
1209
1210QString Addressee::organizationLabel()
1211{
1212 return i18n("Organization");
1213}
1214
1215void Addressee::setDepartment(const QString &department)
1216{
1217 if (department == d->mDepartment) {
1218 return;
1219 }
1220
1221 d->mEmpty = false;
1222 d->mDepartment = department;
1223}
1224
1225QString Addressee::department() const
1226{
1227 return d->mDepartment;
1228}
1229
1230QString Addressee::departmentLabel()
1231{
1232 return i18n("Department");
1233}
1234
1235void Addressee::setNote(const QString &note)
1236{
1237 if (note == d->mNote) {
1238 return;
1239 }
1240
1241 d->mEmpty = false;
1242 d->mNote = note;
1243}
1244
1245QString Addressee::note() const
1246{
1247 return d->mNote;
1248}
1249
1250QString Addressee::noteLabel()
1251{
1252 return i18n("Note");
1253}
1254
1255void Addressee::setProductId(const QString &productId)
1256{
1257 if (productId == d->mProductId) {
1258 return;
1259 }
1260
1261 d->mEmpty = false;
1262 d->mProductId = productId;
1263}
1264
1265QString Addressee::productId() const
1266{
1267 return d->mProductId;
1268}
1269
1270QString Addressee::productIdLabel()
1271{
1272 return i18n("Product Identifier");
1273}
1274
1275void Addressee::setRevision(const QDateTime &revision)
1276{
1277 if (revision == d->mRevision) {
1278 return;
1279 }
1280
1281 d->mEmpty = false;
1282 d->mRevision = revision;
1283}
1284
1285QDateTime Addressee::revision() const
1286{
1287 return d->mRevision;
1288}
1289
1290QString Addressee::revisionLabel()
1291{
1292 return i18n("Revision Date");
1293}
1294
1295void Addressee::setSortString(const QString &sortString)
1296{
1297 if (sortString == d->mSortString) {
1298 return;
1299 }
1300
1301 d->mEmpty = false;
1302 d->mSortString = sortString;
1303}
1304
1305QString Addressee::sortString() const
1306{
1307 return d->mSortString;
1308}
1309
1310QString Addressee::sortStringLabel()
1311{
1312 return i18n("Sort String");
1313}
1314
1315void Addressee::setUrl(const QUrl &url)
1316{
1317 KContacts::ResourceLocatorUrl resourceLocator;
1318 resourceLocator.setUrl(url);
1319 insertExtraUrl(url: resourceLocator);
1320}
1321
1322void Addressee::setUrl(const ResourceLocatorUrl &url)
1323{
1324 insertExtraUrl(url);
1325}
1326
1327ResourceLocatorUrl Addressee::url() const
1328{
1329 if (d->mUrlExtraList.isEmpty()) {
1330 return ResourceLocatorUrl();
1331 } else {
1332 return d->mUrlExtraList.at(i: 0);
1333 }
1334}
1335
1336QString Addressee::urlLabel()
1337{
1338 return i18n("Homepage");
1339}
1340
1341void Addressee::setSecrecy(const Secrecy &secrecy)
1342{
1343 if (secrecy == d->mSecrecy) {
1344 return;
1345 }
1346
1347 d->mEmpty = false;
1348 d->mSecrecy = secrecy;
1349}
1350
1351Secrecy Addressee::secrecy() const
1352{
1353 return d->mSecrecy;
1354}
1355
1356QString Addressee::secrecyLabel()
1357{
1358 return i18n("Security Class");
1359}
1360
1361void Addressee::setLogo(const Picture &)
1362{
1363 if (logo == d->mLogo) {
1364 return;
1365 }
1366
1367 d->mEmpty = false;
1368 d->mLogo = logo;
1369}
1370
1371Picture Addressee::logo() const
1372{
1373 return d->mLogo;
1374}
1375
1376QString Addressee::logoLabel()
1377{
1378 return i18n("Logo");
1379}
1380
1381void Addressee::setPhoto(const Picture &photo)
1382{
1383 if (photo == d->mPhoto) {
1384 return;
1385 }
1386
1387 d->mEmpty = false;
1388 d->mPhoto = photo;
1389}
1390
1391Picture Addressee::photo() const
1392{
1393 return d->mPhoto;
1394}
1395
1396QString Addressee::photoLabel()
1397{
1398 return i18n("Photo");
1399}
1400
1401void Addressee::setSound(const Sound &sound)
1402{
1403 if (sound == d->mSound) {
1404 return;
1405 }
1406
1407 d->mEmpty = false;
1408 d->mSound = sound;
1409}
1410
1411Sound Addressee::sound() const
1412{
1413 return d->mSound;
1414}
1415
1416QString Addressee::soundLabel()
1417{
1418 return i18n("Sound");
1419}
1420
1421void Addressee::setNameFromString(const QString &s)
1422{
1423 QString str = s;
1424 // remove enclosing quotes from string
1425 if (str.length() > 1 && s[0] == QLatin1Char('"') && s[s.length() - 1] == QLatin1Char('"')) {
1426 str = s.mid(position: 1, n: s.length() - 2);
1427 }
1428
1429 setFormattedName(str);
1430 setName(str);
1431
1432 // clear all name parts
1433 setPrefix(QString());
1434 setGivenName(QString());
1435 setAdditionalName(QString());
1436 setFamilyName(QString());
1437 setSuffix(QString());
1438
1439 if (str.isEmpty()) {
1440 return;
1441 }
1442
1443 static QString spaceStr = QStringLiteral(" ");
1444 static QString emptyStr = QStringLiteral("");
1445 AddresseeHelper *helper = AddresseeHelper::self();
1446
1447 int i = str.indexOf(ch: QLatin1Char(','));
1448 if (i < 0) {
1449 QStringList parts = str.split(sep: spaceStr);
1450 int leftOffset = 0;
1451 int rightOffset = parts.count() - 1;
1452
1453 QString suffix;
1454 while (rightOffset >= 0) {
1455 if (helper->containsSuffix(suffix: parts[rightOffset])) {
1456 suffix.prepend(s: parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
1457 rightOffset--;
1458 } else {
1459 break;
1460 }
1461 }
1462 setSuffix(suffix);
1463
1464 if (rightOffset < 0) {
1465 return;
1466 }
1467
1468 if (rightOffset - 1 >= 0 && helper->containsPrefix(prefix: parts[rightOffset - 1].toLower())) {
1469 setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
1470 rightOffset--;
1471 } else {
1472 if (helper->treatAsFamilyName()) {
1473 setFamilyName(parts[rightOffset]);
1474 } else {
1475 setGivenName(parts[rightOffset]);
1476 }
1477 }
1478
1479 QString prefix;
1480 while (leftOffset < rightOffset) {
1481 if (helper->containsTitle(title: parts[leftOffset])) {
1482 prefix.append(s: (prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1483 leftOffset++;
1484 } else {
1485 break;
1486 }
1487 }
1488 setPrefix(prefix);
1489
1490 if (leftOffset < rightOffset) {
1491 setGivenName(parts[leftOffset]);
1492 leftOffset++;
1493 }
1494
1495 QString additionalName;
1496 while (leftOffset < rightOffset) {
1497 additionalName.append(s: (additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1498 leftOffset++;
1499 }
1500 setAdditionalName(additionalName);
1501 } else {
1502 QString part1 = str.left(n: i);
1503 QString part2 = str.mid(position: i + 1);
1504
1505 QStringList parts = part1.split(sep: spaceStr);
1506 int leftOffset = 0;
1507 int rightOffset = parts.count() - 1;
1508
1509 if (!parts.isEmpty()) {
1510 QString suffix;
1511 while (rightOffset >= 0) {
1512 if (helper->containsSuffix(suffix: parts[rightOffset])) {
1513 suffix.prepend(s: parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
1514 rightOffset--;
1515 } else {
1516 break;
1517 }
1518 }
1519 setSuffix(suffix);
1520
1521 if (rightOffset - 1 >= 0 && helper->containsPrefix(prefix: parts[rightOffset - 1].toLower())) {
1522 setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
1523 rightOffset--;
1524 } else {
1525 setFamilyName(parts[rightOffset]);
1526 }
1527
1528 QString prefix;
1529 while (leftOffset < rightOffset) {
1530 if (helper->containsTitle(title: parts[leftOffset])) {
1531 prefix.append(s: (prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1532 leftOffset++;
1533 } else {
1534 break;
1535 }
1536 }
1537 } else {
1538 setPrefix(QString());
1539 setFamilyName(QString());
1540 setSuffix(QString());
1541 }
1542
1543 parts = part2.split(sep: spaceStr);
1544
1545 leftOffset = 0;
1546 rightOffset = parts.count();
1547
1548 if (!parts.isEmpty()) {
1549 QString prefix;
1550 while (leftOffset < rightOffset) {
1551 if (helper->containsTitle(title: parts[leftOffset])) {
1552 prefix.append(s: (prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1553 leftOffset++;
1554 } else {
1555 break;
1556 }
1557 }
1558 setPrefix(prefix);
1559
1560 if (leftOffset < rightOffset) {
1561 setGivenName(parts[leftOffset]);
1562 leftOffset++;
1563 }
1564
1565 QString additionalName;
1566 while (leftOffset < rightOffset) {
1567 additionalName.append(s: (additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
1568 leftOffset++;
1569 }
1570 setAdditionalName(additionalName);
1571 } else {
1572 setGivenName(QString());
1573 setAdditionalName(QString());
1574 }
1575 }
1576}
1577
1578QString Addressee::realName() const
1579{
1580 QString n(formattedName());
1581 if (!n.isEmpty()) {
1582 return n;
1583 }
1584
1585 n = assembledName();
1586 if (!n.isEmpty()) {
1587 return n;
1588 }
1589
1590 n = name();
1591 if (!n.isEmpty()) {
1592 return n;
1593 }
1594
1595 return organization();
1596}
1597
1598QString Addressee::assembledName() const
1599{
1600 // clang-format off
1601 const QString name = prefix() + QLatin1Char(' ')
1602 + givenName() + QLatin1Char(' ')
1603 + additionalName() + QLatin1Char(' ')
1604 + familyName() + QLatin1Char(' ')
1605 + suffix();
1606 // clang-format on
1607
1608 return name.simplified();
1609}
1610
1611QString Addressee::fullEmail(const QString &email) const
1612{
1613 QString e;
1614 if (email.isNull()) {
1615 e = preferredEmail();
1616 } else {
1617 e = email;
1618 }
1619 if (e.isEmpty()) {
1620 return QString();
1621 }
1622
1623 QString text;
1624 if (realName().isEmpty()) {
1625 text = e;
1626 } else {
1627 QRegularExpression needQuotes(QStringLiteral("[^ 0-9A-Za-z\\x{0080}-\\x{FFFF}]"));
1628 if (realName().indexOf(re: needQuotes) != -1) {
1629 QString name = realName();
1630 name.replace(before: QLatin1String("\""), after: QLatin1String("\\\""));
1631 text = QLatin1String("\"") + name + QLatin1String("\" <") + e + QLatin1Char('>');
1632 } else {
1633 text = realName() + QLatin1String(" <") + e + QLatin1Char('>');
1634 }
1635 }
1636
1637 return text;
1638}
1639
1640void Addressee::addEmail(const Email &email)
1641{
1642 const QString mailAddr = email.mail();
1643 auto it = std::find_if(first: d->mEmails.begin(), last: d->mEmails.end(), pred: [&mailAddr](const Email &e) {
1644 return e.mail() == mailAddr;
1645 });
1646 if (it != d->mEmails.end()) { // Already exists, modify it
1647 *it = email;
1648 if (email.isPreferred()) {
1649 // Move it to the beginning of mEmails
1650 std::rotate(first: d->mEmails.begin(), middle: it, last: it + 1);
1651 }
1652 return;
1653 }
1654
1655 // Add it to the list
1656 d->mEmpty = false;
1657 if (email.isPreferred()) {
1658 d->mEmails.prepend(t: email);
1659 } else {
1660 d->mEmails.append(t: email);
1661 }
1662}
1663
1664void Addressee::removeEmail(const QString &email)
1665{
1666 for (int i = 0; i < d->mEmails.size(); ++i) {
1667 if (d->mEmails.at(i).mail() == email) {
1668 d->mEmails.removeAt(i);
1669 }
1670 }
1671}
1672
1673QString Addressee::preferredEmail() const
1674{
1675 if (d->mEmails.isEmpty()) {
1676 return QString();
1677 } else {
1678 return d->mEmails.first().mail();
1679 }
1680}
1681
1682QStringList Addressee::emails() const
1683{
1684 QStringList list;
1685 const int numberOfEmail = d->mEmails.size();
1686 list.reserve(asize: numberOfEmail);
1687 for (int i = 0; i < numberOfEmail; ++i) {
1688 list << d->mEmails.at(i).mail();
1689 }
1690
1691 return list;
1692}
1693
1694Email::List Addressee::emailList() const
1695{
1696 return d->mEmails;
1697}
1698
1699void Addressee::setEmails(const QStringList &emails)
1700{
1701 d->mEmails.clear();
1702 const int numEmails = emails.size();
1703 d->mEmails.reserve(asize: numEmails);
1704 for (int i = 0; i < numEmails; ++i) {
1705 d->mEmails.append(t: Email(emails.at(i)));
1706 }
1707 d->mEmpty = false;
1708}
1709
1710void Addressee::setEmailList(const Email::List &list)
1711{
1712 d->mEmails = list;
1713 d->mEmpty = false;
1714}
1715
1716void Addressee::removeLang(const QString &language)
1717{
1718 for (int i = 0; i < d->mLangs.size(); ++i) {
1719 if (d->mLangs.at(i).language() == language) {
1720 d->mLangs.removeAt(i);
1721 }
1722 }
1723}
1724
1725void Addressee::setLangs(const Lang::List &langs)
1726{
1727 d->mLangs = langs;
1728 d->mEmpty = false;
1729}
1730
1731void Addressee::insertLang(const Lang &language)
1732{
1733 const QString languageStr = language.language();
1734 if (languageStr.simplified().isEmpty()) {
1735 return;
1736 }
1737 d->mEmpty = false;
1738
1739 auto it = std::find_if(first: d->mLangs.begin(), last: d->mLangs.end(), pred: [&languageStr](const Lang &lang) {
1740 return lang.language() == languageStr;
1741 });
1742 if (it != d->mLangs.end()) {
1743 (*it).setParams(language.params());
1744 return;
1745 }
1746
1747 d->mLangs.append(t: language);
1748}
1749
1750Lang::List Addressee::langs() const
1751{
1752 return d->mLangs;
1753}
1754
1755void Addressee::setGender(const Gender &gender)
1756{
1757 if (gender == d->mGender) {
1758 return;
1759 }
1760
1761 d->mEmpty = false;
1762 d->mGender = gender;
1763}
1764
1765Gender Addressee::gender() const
1766{
1767 return d->mGender;
1768}
1769
1770void Addressee::insertPhoneNumber(const PhoneNumber &phoneNumber)
1771{
1772 d->mEmpty = false;
1773
1774 auto it = std::find_if(first: d->mPhoneNumbers.begin(), last: d->mPhoneNumbers.end(), pred: [&phoneNumber](const PhoneNumber &pNumber) {
1775 return pNumber.id() == phoneNumber.id();
1776 });
1777 if (it != d->mPhoneNumbers.end()) {
1778 *it = phoneNumber;
1779 return;
1780 }
1781
1782 if (!phoneNumber.number().simplified().isEmpty()) {
1783 d->mPhoneNumbers.append(t: phoneNumber);
1784 }
1785}
1786
1787void Addressee::removePhoneNumber(const PhoneNumber &phoneNumber)
1788{
1789 auto it = std::find_if(first: d->mPhoneNumbers.begin(), last: d->mPhoneNumbers.end(), pred: [&phoneNumber](const PhoneNumber &p) {
1790 return p.id() == phoneNumber.id();
1791 });
1792
1793 if (it != d->mPhoneNumbers.end()) {
1794 d->mPhoneNumbers.erase(pos: it);
1795 }
1796}
1797
1798PhoneNumber Addressee::phoneNumber(PhoneNumber::Type type) const
1799{
1800 PhoneNumber phoneNumber(QString(), type);
1801
1802 for (const PhoneNumber &phone : d->mPhoneNumbers) {
1803 if (matchBinaryPattern(value: phone.type(), pattern: type)) {
1804 if (phone.type() & PhoneNumber::Pref) {
1805 return phone;
1806 } else if (phoneNumber.number().isEmpty()) {
1807 phoneNumber = phone;
1808 }
1809 }
1810 }
1811
1812 return phoneNumber;
1813}
1814
1815PhoneNumber::List Addressee::phoneNumbers() const
1816{
1817 return d->mPhoneNumbers;
1818}
1819
1820void Addressee::setPhoneNumbers(const PhoneNumber::List &phoneNumbers)
1821{
1822 d->mEmpty = false;
1823 d->mPhoneNumbers.clear();
1824 d->mPhoneNumbers = phoneNumbers;
1825}
1826
1827PhoneNumber::List Addressee::phoneNumbers(PhoneNumber::Type type) const
1828{
1829 PhoneNumber::List list;
1830
1831 std::copy_if(first: d->mPhoneNumbers.cbegin(), last: d->mPhoneNumbers.cend(), result: std::back_inserter(x&: list), pred: [type](const auto &phone) {
1832 return matchBinaryPattern(phone.type(), type);
1833 });
1834 return list;
1835}
1836
1837PhoneNumber Addressee::findPhoneNumber(const QString &id) const
1838{
1839 auto it = std::find_if(first: d->mPhoneNumbers.cbegin(), last: d->mPhoneNumbers.cend(), pred: [&id](const PhoneNumber &phone) {
1840 return phone.id() == id;
1841 });
1842
1843 return it != d->mPhoneNumbers.cend() ? *it : PhoneNumber{};
1844}
1845
1846void Addressee::insertKey(const Key &key)
1847{
1848 d->mEmpty = false;
1849
1850 auto it = std::find_if(first: d->mKeys.begin(), last: d->mKeys.end(), pred: [&key](Key &existing) {
1851 return existing.id() == key.id();
1852 });
1853 if (it != d->mKeys.end()) {
1854 *it = key;
1855 } else {
1856 d->mKeys.append(t: key);
1857 }
1858}
1859
1860void Addressee::removeKey(const Key &key)
1861{
1862 auto it = std::remove_if(first: d->mKeys.begin(), last: d->mKeys.end(), pred: [&key](const Key &k) {
1863 return k.id() == key.id();
1864 });
1865 d->mKeys.erase(abegin: it, aend: d->mKeys.end());
1866}
1867
1868Key Addressee::key(Key::Type type, const QString &customTypeString) const
1869{
1870 for (const auto &key : d->mKeys) {
1871 if (key.type() == type) {
1872 if (type == Key::Custom) {
1873 if (customTypeString.isEmpty()) {
1874 return key;
1875 } else {
1876 if (key.customTypeString() == customTypeString) {
1877 return key;
1878 }
1879 }
1880 } else {
1881 return key;
1882 }
1883 }
1884 }
1885 return Key(QString(), type);
1886}
1887
1888void Addressee::setKeys(const Key::List &list)
1889{
1890 d->mKeys = list;
1891 d->mEmpty = false;
1892}
1893
1894Key::List Addressee::keys() const
1895{
1896 return d->mKeys;
1897}
1898
1899Key::List Addressee::keys(Key::Type type, const QString &customTypeString) const
1900{
1901 Key::List list;
1902 auto matchFunc = [type, &customTypeString](const Key &key) {
1903 if (key.type() == type) {
1904 if (type == Key::Custom) {
1905 if (customTypeString.isEmpty()) {
1906 return true;
1907 } else {
1908 if (key.customTypeString() == customTypeString) {
1909 return true;
1910 }
1911 }
1912 } else {
1913 return true;
1914 }
1915 }
1916 return false;
1917 };
1918
1919 std::copy_if(first: d->mKeys.cbegin(), last: d->mKeys.cend(), result: std::back_inserter(x&: list), pred: matchFunc);
1920
1921 return list;
1922}
1923
1924Key Addressee::findKey(const QString &id) const
1925{
1926 auto it = std::find_if(first: d->mKeys.cbegin(), last: d->mKeys.cend(), pred: [&id](const Key &key) {
1927 return key.id() == id;
1928 });
1929
1930 return it != d->mKeys.cend() ? *it : Key{};
1931}
1932
1933QString Addressee::toString() const
1934{
1935 QString str = QLatin1String("Addressee {\n");
1936 str += QStringLiteral(" Uid: %1\n").arg(a: uid());
1937
1938 str += QStringLiteral(" Name: %1\n").arg(a: name());
1939 str += QStringLiteral(" FormattedName: %1\n").arg(a: formattedName());
1940 str += QStringLiteral(" FamilyName: %1\n").arg(a: familyName());
1941 str += QStringLiteral(" GivenName: %1\n").arg(a: givenName());
1942 str += QStringLiteral(" AdditionalName: %1\n").arg(a: additionalName());
1943 str += QStringLiteral(" Prefix: %1\n").arg(a: prefix());
1944 str += QStringLiteral(" Suffix: %1\n").arg(a: suffix());
1945 str += QStringLiteral(" NickName: %1\n").arg(a: nickName());
1946 str += QStringLiteral(" Birthday: %1\n").arg(a: birthday().toString());
1947 str += QStringLiteral(" Mailer: %1\n").arg(a: mailer());
1948 str += QStringLiteral(" TimeZone: %1\n").arg(a: timeZone().toString());
1949 str += QStringLiteral(" Geo: %1\n").arg(a: geo().toString());
1950 str += QStringLiteral(" Title: %1\n").arg(a: title());
1951 str += QStringLiteral(" Role: %1\n").arg(a: role());
1952 str += QStringLiteral(" Organization: %1\n").arg(a: organization());
1953 str += QStringLiteral(" Department: %1\n").arg(a: department());
1954 str += QStringLiteral(" Note: %1\n").arg(a: note());
1955 str += QStringLiteral(" ProductId: %1\n").arg(a: productId());
1956 str += QStringLiteral(" Revision: %1\n").arg(a: revision().toString());
1957 str += QStringLiteral(" SortString: %1\n").arg(a: sortString());
1958 str += QStringLiteral(" Url: %1\n").arg(a: url().url().url());
1959 str += QStringLiteral(" Secrecy: %1\n").arg(a: secrecy().toString());
1960 str += QStringLiteral(" Logo: %1\n").arg(a: logo().toString());
1961 str += QStringLiteral(" Photo: %1\n").arg(a: photo().toString());
1962 str += QStringLiteral(" Sound: %1\n").arg(a: sound().toString());
1963 str += QStringLiteral(" Gender: %1\n").arg(a: gender().toString());
1964 str += QStringLiteral(" Kind: %1\n").arg(a: kind());
1965
1966 str += QLatin1String(" Emails {\n");
1967 const Email::List listEmail = d->mEmails;
1968 for (const Email &email : listEmail) {
1969 str += email.toString();
1970 }
1971 str += QLatin1String(" }\n");
1972
1973 str += QLatin1String(" Langs {\n");
1974 const Lang::List listLang = d->mLangs;
1975 for (const Lang &lang : listLang) {
1976 str += lang.toString();
1977 }
1978 str += QLatin1String(" }\n");
1979
1980 str += QLatin1String(" PhoneNumbers {\n");
1981 const PhoneNumber::List phones = phoneNumbers();
1982 for (const auto &p : phones) {
1983 str += p.toString();
1984 }
1985 str += QLatin1String(" }\n");
1986
1987 str += QLatin1String(" Addresses {\n");
1988 const Address::List addrList = addresses();
1989 for (const auto &addr : addrList) {
1990 str += addr.toString();
1991 }
1992 str += QLatin1String(" }\n");
1993
1994 str += QLatin1String(" Keys {\n");
1995 const Key::List keyList = keys();
1996 for (const auto &k : keyList) {
1997 str += k.toString();
1998 }
1999 str += QLatin1String(" }\n");
2000
2001 str += QLatin1String("}\n");
2002
2003 return str;
2004}
2005
2006void Addressee::insertAddress(const Address &address)
2007{
2008 if (address.isEmpty()) {
2009 return;
2010 }
2011
2012 d->mEmpty = false;
2013
2014 auto it = std::find_if(first: d->mAddresses.begin(), last: d->mAddresses.end(), pred: [&address](const Address &addr) {
2015 return addr.id() == address.id();
2016 });
2017 if (it != d->mAddresses.end()) {
2018 *it = address;
2019 return;
2020 }
2021
2022 d->mAddresses.append(t: address);
2023}
2024
2025void Addressee::removeAddress(const Address &address)
2026{
2027 auto it = std::find_if(first: d->mAddresses.begin(), last: d->mAddresses.end(), pred: [&address](const Address &addr) {
2028 return addr.id() == address.id();
2029 });
2030 if (it != d->mAddresses.end()) {
2031 d->mAddresses.erase(pos: it);
2032 }
2033}
2034
2035void Addressee::setAddresses(const Address::List &addresses)
2036{
2037 d->mEmpty = false;
2038 d->mAddresses = addresses;
2039}
2040
2041Address Addressee::address(Address::Type type) const
2042{
2043 Address address(type);
2044 for (const Address &addr : d->mAddresses) {
2045 if (matchBinaryPattern(value: addr.type(), pattern: type)) {
2046 if (addr.type() & Address::Pref) {
2047 return addr;
2048 } else if (address.isEmpty()) {
2049 address = addr;
2050 }
2051 }
2052 }
2053
2054 return address;
2055}
2056
2057Address::List Addressee::addresses() const
2058{
2059 return d->mAddresses;
2060}
2061
2062Address::List Addressee::addresses(Address::Type type) const
2063{
2064 Address::List list;
2065
2066 std::copy_if(first: d->mAddresses.cbegin(), last: d->mAddresses.cend(), result: std::back_inserter(x&: list), pred: [type](const Address &addr) {
2067 return matchBinaryPattern(value: addr.type(), pattern: type);
2068 });
2069
2070 return list;
2071}
2072
2073Address Addressee::findAddress(const QString &id) const
2074{
2075 auto it = std::find_if(first: d->mAddresses.cbegin(), last: d->mAddresses.cend(), pred: [&id](const Address &addr) {
2076 return addr.id() == id;
2077 });
2078 return it != d->mAddresses.cend() ? *it : Address{};
2079}
2080
2081void Addressee::insertCategory(const QString &c)
2082{
2083 d->mEmpty = false;
2084
2085 if (d->mCategories.contains(str: c)) {
2086 return;
2087 }
2088
2089 d->mCategories.append(t: c);
2090}
2091
2092void Addressee::removeCategory(const QString &category)
2093{
2094 if (d->mCategories.contains(str: category)) {
2095 d->mCategories.removeAll(t: category);
2096 }
2097}
2098
2099bool Addressee::hasCategory(const QString &category) const
2100{
2101 return d->mCategories.contains(str: category);
2102}
2103
2104void Addressee::setCategories(const QStringList &c)
2105{
2106 d->mEmpty = false;
2107
2108 d->mCategories = c;
2109}
2110
2111QStringList Addressee::categories() const
2112{
2113 return d->mCategories;
2114}
2115
2116void Addressee::insertMember(const QString &member)
2117{
2118 d->mEmpty = false;
2119
2120 if (d->mMembers.contains(str: member)) {
2121 return;
2122 }
2123
2124 d->mMembers.append(t: member);
2125}
2126
2127void Addressee::setMembers(const QStringList &m)
2128{
2129 d->mEmpty = false;
2130 d->mMembers = m;
2131}
2132
2133QStringList Addressee::members() const
2134{
2135 return d->mMembers;
2136}
2137
2138void Addressee::insertRelationship(const Related &relation)
2139{
2140 d->mEmpty = false;
2141
2142 if (d->mRelationships.contains(t: relation)) {
2143 return;
2144 }
2145
2146 d->mRelationships.append(t: relation);
2147}
2148
2149void Addressee::setRelationships(const Related::List &c)
2150{
2151 d->mEmpty = false;
2152 d->mRelationships = c;
2153}
2154
2155Related::List Addressee::relationships() const
2156{
2157 return d->mRelationships;
2158}
2159
2160static const auto VENDOR_ID = QStringLiteral("KADDRESSBOOK");
2161static const auto X_ANNIVERSARY = QStringLiteral("X-Anniversary");
2162static const auto X_ASSISTANTSNAME = QStringLiteral("X-AssistantsName");
2163static const auto BLOGFEED = QStringLiteral("BlogFeed");
2164static const auto X_MANAGERSNAME = QStringLiteral("X-ManagersName");
2165static const auto X_OFFICE = QStringLiteral("X-Office");
2166static const auto X_PROFESSION = QStringLiteral("X-Profession");
2167static const auto X_SPOUSESNAME = QStringLiteral("X-SpousesName");
2168
2169QDate Addressee::anniversary() const
2170{
2171 return QDate::fromString(string: custom(app: VENDOR_ID, name: X_ANNIVERSARY), format: Qt::ISODate);
2172}
2173
2174void Addressee::setAnniversary(const QDate &anniversary)
2175{
2176 if (anniversary.isValid()) {
2177 insertCustom(app: VENDOR_ID, name: X_ANNIVERSARY, value: anniversary.toString(format: Qt::ISODate));
2178 } else {
2179 removeCustom(app: VENDOR_ID, name: X_ANNIVERSARY);
2180 }
2181}
2182
2183QString Addressee::assistantsName() const
2184{
2185 return custom(app: VENDOR_ID, name: X_ASSISTANTSNAME);
2186}
2187
2188void Addressee::setAssistantsName(const QString &assistantsName)
2189{
2190 if (!assistantsName.isEmpty()) {
2191 insertCustom(app: VENDOR_ID, name: X_ASSISTANTSNAME, value: assistantsName);
2192 } else {
2193 removeCustom(app: VENDOR_ID, name: X_ASSISTANTSNAME);
2194 }
2195}
2196
2197QUrl Addressee::blogFeed() const
2198{
2199 return QUrl(custom(app: VENDOR_ID, name: BLOGFEED));
2200}
2201
2202void Addressee::setBlogFeed(const QUrl &blogFeed)
2203{
2204 if (!blogFeed.isEmpty()) {
2205 insertCustom(app: VENDOR_ID, name: BLOGFEED, value: blogFeed.url());
2206 } else {
2207 removeCustom(app: VENDOR_ID, name: BLOGFEED);
2208 }
2209}
2210
2211QString Addressee::managersName() const
2212{
2213 return custom(app: VENDOR_ID, name: X_MANAGERSNAME);
2214}
2215
2216void Addressee::setManagersName(const QString &managersName)
2217{
2218 if (!managersName.isEmpty()) {
2219 insertCustom(app: VENDOR_ID, name: X_MANAGERSNAME, value: managersName);
2220 } else {
2221 removeCustom(app: VENDOR_ID, name: X_MANAGERSNAME);
2222 }
2223}
2224
2225QString Addressee::office() const
2226{
2227 return custom(app: VENDOR_ID, name: X_OFFICE);
2228}
2229
2230void Addressee::setOffice(const QString &office)
2231{
2232 if (!office.isEmpty()) {
2233 insertCustom(app: VENDOR_ID, name: X_OFFICE, value: office);
2234 } else {
2235 removeCustom(app: VENDOR_ID, name: X_OFFICE);
2236 }
2237}
2238
2239QString Addressee::profession() const
2240{
2241 return custom(app: VENDOR_ID, name: X_PROFESSION);
2242}
2243
2244void Addressee::setProfession(const QString &profession)
2245{
2246 if (!profession.isEmpty()) {
2247 insertCustom(app: VENDOR_ID, name: X_PROFESSION, value: profession);
2248 } else {
2249 removeCustom(app: VENDOR_ID, name: X_PROFESSION);
2250 }
2251}
2252
2253QString Addressee::spousesName() const
2254{
2255 return custom(app: VENDOR_ID, name: X_SPOUSESNAME);
2256}
2257
2258void Addressee::setSpousesName(const QString &spousesName)
2259{
2260 if (!spousesName.isEmpty()) {
2261 insertCustom(app: VENDOR_ID, name: X_SPOUSESNAME, value: spousesName);
2262 } else {
2263 removeCustom(app: VENDOR_ID, name: X_SPOUSESNAME);
2264 }
2265}
2266
2267void Addressee::insertCustom(const QString &app, const QString &name, const QString &value)
2268{
2269 if (value.isEmpty() || name.isEmpty() || app.isEmpty()) {
2270 return;
2271 }
2272
2273 d->mEmpty = false;
2274
2275 const QString qualifiedName = app + QLatin1Char('-') + name;
2276
2277 auto it = d->findByName(qualifiedName);
2278 if (it != d->mCustomFields.end()) {
2279 it->value = value;
2280 } else {
2281 const CustomData newdata{.name: qualifiedName, .value: value};
2282 auto beforeIt = std::lower_bound(first: d->mCustomFields.begin(), last: d->mCustomFields.end(), val: newdata);
2283 d->mCustomFields.insert(position: beforeIt, x: newdata);
2284 }
2285}
2286
2287void Addressee::removeCustom(const QString &app, const QString &name)
2288{
2289 const QString qualifiedName = app + QLatin1Char('-') + name;
2290 auto it = d->findByName(qualifiedName);
2291 if (it != d->mCustomFields.end()) {
2292 d->mCustomFields.erase(position: it);
2293 }
2294}
2295
2296QString Addressee::custom(const QString &app, const QString &name) const
2297{
2298 const QString qualifiedName = app + QLatin1Char('-') + name;
2299 auto it = d->findByName(qualifiedName);
2300 return it != d->mCustomFields.cend() ? it->value : QString{};
2301}
2302
2303void Addressee::setCustoms(const QStringList &customs)
2304{
2305 d->mEmpty = false;
2306
2307 d->mCustomFields.clear();
2308
2309 // Less than 10 elements in "customs", we needn't use a set
2310 QStringList seen;
2311 for (const QString &custom : customs) {
2312 const int index = custom.indexOf(ch: QLatin1Char(':'));
2313 if (index == -1) {
2314 continue;
2315 }
2316
2317 const QString qualifiedName = custom.left(n: index);
2318 const QString value = custom.mid(position: index + 1);
2319
2320 if (!seen.contains(str: qualifiedName)) {
2321 d->mCustomFields.push_back(x: {.name: qualifiedName, .value: value});
2322 seen.push_back(t: qualifiedName);
2323 }
2324 }
2325 std::sort(first: d->mCustomFields.begin(), last: d->mCustomFields.end());
2326}
2327
2328QStringList Addressee::customs() const
2329{
2330 QStringList result;
2331 result.reserve(asize: d->mCustomFields.size());
2332
2333 static const QLatin1Char sep(':');
2334 for (const auto &[name, value] : d->mCustomFields) {
2335 result << name + sep + value;
2336 }
2337
2338 return result;
2339}
2340
2341void Addressee::parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
2342{
2343 // This is a simplified version of KPIM::splitAddress().
2344
2345 fullName.clear();
2346 email.clear();
2347 if (rawEmail.isEmpty()) {
2348 return; // KPIM::AddressEmpty;
2349 }
2350
2351 // The code works on 8-bit strings, so convert the input to UTF-8.
2352 QByteArray address = rawEmail.toUtf8();
2353
2354 QByteArray displayName;
2355 QByteArray addrSpec;
2356 QByteArray comment;
2357
2358 // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
2359 // The purpose is to extract a displayable string from the mailboxes.
2360 // Comments in the addr-spec are not handled. No error checking is done.
2361
2362 enum sourceLevel {
2363 TopLevel,
2364 InComment,
2365 InAngleAddress,
2366 };
2367 sourceLevel context = TopLevel;
2368 bool inQuotedString = false;
2369 int commentLevel = 0;
2370 bool stop = false;
2371
2372 for (char *p = address.data(); *p && !stop; ++p) {
2373 switch (context) {
2374 case TopLevel:
2375 switch (*p) {
2376 case '"':
2377 inQuotedString = !inQuotedString;
2378 displayName += *p;
2379 break;
2380 case '(':
2381 if (!inQuotedString) {
2382 context = InComment;
2383 commentLevel = 1;
2384 } else {
2385 displayName += *p;
2386 }
2387 break;
2388 case '<':
2389 if (!inQuotedString) {
2390 context = InAngleAddress;
2391 } else {
2392 displayName += *p;
2393 }
2394 break;
2395 case '\\': // quoted character
2396 displayName += *p;
2397 ++p; // skip the '\'
2398 if (*p) {
2399 displayName += *p;
2400 } else {
2401 // return KPIM::UnexpectedEnd;
2402 goto ABORT_PARSING;
2403 }
2404 break;
2405 case ',':
2406 if (!inQuotedString) {
2407 // if ( allowMultipleAddresses )
2408 // stop = true;
2409 // else
2410 // return KPIM::UnexpectedComma;
2411 goto ABORT_PARSING;
2412 } else {
2413 displayName += *p;
2414 }
2415 break;
2416 default:
2417 displayName += *p;
2418 }
2419 break;
2420 case InComment:
2421 switch (*p) {
2422 case '(':
2423 ++commentLevel;
2424 comment += *p;
2425 break;
2426 case ')':
2427 --commentLevel;
2428 if (commentLevel == 0) {
2429 context = TopLevel;
2430 comment += ' '; // separate the text of several comments
2431 } else {
2432 comment += *p;
2433 }
2434 break;
2435 case '\\': // quoted character
2436 comment += *p;
2437 ++p; // skip the '\'
2438 if (*p) {
2439 comment += *p;
2440 } else {
2441 // return KPIM::UnexpectedEnd;
2442 goto ABORT_PARSING;
2443 }
2444 break;
2445 default:
2446 comment += *p;
2447 }
2448 break;
2449 case InAngleAddress:
2450 switch (*p) {
2451 case '"':
2452 inQuotedString = !inQuotedString;
2453 addrSpec += *p;
2454 break;
2455 case '>':
2456 if (!inQuotedString) {
2457 context = TopLevel;
2458 } else {
2459 addrSpec += *p;
2460 }
2461 break;
2462 case '\\': // quoted character
2463 addrSpec += *p;
2464 ++p; // skip the '\'
2465 if (*p) {
2466 addrSpec += *p;
2467 } else {
2468 // return KPIM::UnexpectedEnd;
2469 goto ABORT_PARSING;
2470 }
2471 break;
2472 default:
2473 addrSpec += *p;
2474 }
2475 break;
2476 } // switch ( context )
2477 }
2478
2479ABORT_PARSING:
2480 displayName = displayName.trimmed();
2481 comment = comment.trimmed();
2482 addrSpec = addrSpec.trimmed();
2483 fullName = QString::fromUtf8(ba: displayName);
2484 email = QString::fromUtf8(ba: addrSpec); // check for errors
2485 if (inQuotedString) {
2486 return; // KPIM::UnbalancedQuote;
2487 }
2488 if (context == InComment) {
2489 return; // KPIM::UnbalancedParens;
2490 }
2491 if (context == InAngleAddress) {
2492 return; // KPIM::UnclosedAngleAddr;
2493 }
2494
2495 if (addrSpec.isEmpty()) {
2496 if (displayName.isEmpty()) {
2497 return; // KPIM::NoAddressSpec;
2498 } else {
2499 // addrSpec = displayName;
2500 // displayName.truncate( 0 );
2501 // Address of the form "foo@bar" or "foo@bar (Name)".
2502 email = fullName;
2503 fullName = QString::fromUtf8(ba: comment);
2504 }
2505 }
2506
2507 email = email.toLower();
2508
2509 // Check that the full name is not enclosed in balanced double quotes.
2510 // If it is then remove them.
2511 const unsigned int len = fullName.length();
2512 if (len < 3) { // not long enough to be
2513 return;
2514 }
2515 if (fullName.startsWith(c: QLatin1Char('"')) && fullName.endsWith(c: QLatin1Char('"'))) {
2516 fullName = fullName.mid(position: 1, n: len - 2);
2517 }
2518}
2519
2520void Addressee::setChanged(bool value)
2521{
2522 d->mChanged = value;
2523}
2524
2525bool Addressee::changed() const
2526{
2527 return d->mChanged;
2528}
2529
2530QString Addressee::mimeType()
2531{
2532 return QStringLiteral("text/directory");
2533}
2534
2535QDataStream &KContacts::operator<<(QDataStream &s, const Addressee &a)
2536{
2537 s << a.d->mUid;
2538
2539 s << a.d->mName;
2540 s << a.d->mFormattedName;
2541 s << a.d->mFamilyName;
2542 s << a.d->mGivenName;
2543 s << a.d->mAdditionalName;
2544 s << a.d->mPrefix;
2545 s << a.d->mSuffix;
2546 s << a.d->mBirthday;
2547 s << a.d->mBirthdayWithTime;
2548 s << a.d->mMailer;
2549 s << a.d->mTimeZone;
2550 s << a.d->mGeo;
2551 s << a.d->mDepartment;
2552 s << a.d->mNote;
2553 s << a.d->mProductId;
2554 s << a.d->mRevision;
2555 s << a.d->mSortString;
2556 s << a.d->mSecrecy;
2557 s << a.d->mLogo;
2558 s << a.d->mPhoto;
2559 s << a.d->mSound;
2560 s << a.d->mPhoneNumbers;
2561 s << a.d->mAddresses;
2562 s << a.d->mEmails;
2563 s << a.d->mCategories;
2564 s << a.customs();
2565 s << a.d->mKeys;
2566 s << a.d->mLangs;
2567 s << a.d->mGender;
2568 s << a.d->mKind;
2569 s << a.d->mCalendarUrl;
2570 s << a.d->mSoundListExtra;
2571 s << a.d->mPhotoExtraList;
2572 s << a.d->mLogoExtraList;
2573 s << a.d->mUrlExtraList;
2574 s << a.d->mMembers;
2575 s << a.d->mRelationships;
2576 s << a.d->mSources;
2577 s << a.d->mImpps;
2578 s << a.d->mFieldGroupList;
2579 s << a.d->mTitleExtraList;
2580 s << a.d->mRoleExtraList;
2581 s << a.d->mOrgExtraList;
2582 s << a.d->mNickNameExtraList;
2583 s << a.d->mClientPidMapList;
2584 s << a.d->mDeathDate;
2585
2586 return s;
2587}
2588
2589QDataStream &KContacts::operator>>(QDataStream &s, Addressee &a)
2590{
2591 s >> a.d->mUid;
2592
2593 s >> a.d->mName;
2594 s >> a.d->mFormattedName;
2595 s >> a.d->mFamilyName;
2596 s >> a.d->mGivenName;
2597 s >> a.d->mAdditionalName;
2598 s >> a.d->mPrefix;
2599 s >> a.d->mSuffix;
2600 s >> a.d->mBirthday;
2601 s >> a.d->mBirthdayWithTime;
2602 s >> a.d->mMailer;
2603 s >> a.d->mTimeZone;
2604 s >> a.d->mGeo;
2605 s >> a.d->mDepartment;
2606 s >> a.d->mNote;
2607 s >> a.d->mProductId;
2608 s >> a.d->mRevision;
2609 s >> a.d->mSortString;
2610 s >> a.d->mSecrecy;
2611 s >> a.d->mLogo;
2612 s >> a.d->mPhoto;
2613 s >> a.d->mSound;
2614 s >> a.d->mPhoneNumbers;
2615 s >> a.d->mAddresses;
2616 s >> a.d->mEmails;
2617 s >> a.d->mCategories;
2618 QStringList customFields;
2619 s >> customFields;
2620 a.setCustoms(customFields);
2621 s >> a.d->mKeys;
2622 s >> a.d->mLangs;
2623 s >> a.d->mGender;
2624 s >> a.d->mKind;
2625 s >> a.d->mCalendarUrl;
2626 s >> a.d->mSoundListExtra;
2627 s >> a.d->mPhotoExtraList;
2628 s >> a.d->mLogoExtraList;
2629 s >> a.d->mUrlExtraList;
2630 s >> a.d->mMembers;
2631 s >> a.d->mRelationships;
2632 s >> a.d->mSources;
2633 s >> a.d->mImpps;
2634 s >> a.d->mFieldGroupList;
2635 s >> a.d->mTitleExtraList;
2636 s >> a.d->mRoleExtraList;
2637 s >> a.d->mOrgExtraList;
2638 s >> a.d->mNickNameExtraList;
2639 s >> a.d->mClientPidMapList;
2640 s >> a.d->mDeathDate;
2641 a.d->mEmpty = false;
2642
2643 return s;
2644}
2645
2646bool 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
2661template<class L>
2662bool listEquals(const QList<L> &list, const QList<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
2677bool 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(str: list[i])) {
2686 return false;
2687 }
2688 }
2689
2690 return true;
2691}
2692
2693void Addressee::setBirthdayProperty(const QDateTime &birthday) {
2694 // The property setter cannot pass withTime, so we have to guess.
2695 setBirthday(birthday, withTime: birthday.time().msecsSinceStartOfDay() != 0);
2696}
2697
2698#include "moc_addressee.cpp"
2699

source code of kcontacts/src/addressee.cpp