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

source code of kcontacts/src/addressee.cpp