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