1 /*
2 * This file is part of KMail.
3 *
4 * SPDX-FileCopyrightText: 2010 KDAB
5 * SPDX-FileContributor: Tobias Koenig <tokoe@kde.org>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "aliasesexpandjob.h"
11
12 #include "distributionlistexpandjob.h"
13
14 #include <Akonadi/Contact/ContactGroupExpandJob>
15 #include <Akonadi/Contact/ContactSearchJob>
16 #include <KEmailAddress>
17
18 #include <MessageCore/StringUtil>
19
20 using namespace MessageComposer;
21
AliasesExpandJob(const QString & recipients,const QString & defaultDomain,QObject * parent)22 AliasesExpandJob::AliasesExpandJob(const QString &recipients, const QString &defaultDomain, QObject *parent)
23 : KJob(parent)
24 , mRecipients(KEmailAddress::splitAddressList(recipients))
25 , mDefaultDomain(defaultDomain)
26 {
27 }
28
~AliasesExpandJob()29 AliasesExpandJob::~AliasesExpandJob()
30 {
31 }
32
start()33 void AliasesExpandJob::start()
34 {
35 // At first we try to expand the recipient to a distribution list
36 // or nick name and save the results in a map for later lookup
37 for (const QString &recipient : std::as_const(mRecipients)) {
38 // speedup: assume aliases and list names don't contain '@'
39 if (recipient.isEmpty() || recipient.contains(QLatin1Char('@'))) {
40 continue;
41 }
42
43 // check for distribution list
44 auto expandJob = new DistributionListExpandJob(recipient, this);
45 expandJob->setProperty("recipient", recipient);
46 connect(expandJob, &Akonadi::ContactGroupExpandJob::result, this, &AliasesExpandJob::slotDistributionListExpansionDone);
47 mDistributionListExpansionJobs++;
48 expandJob->start();
49
50 // check for nick name
51 auto searchJob = new Akonadi::ContactSearchJob(this);
52 searchJob->setProperty("recipient", recipient);
53 searchJob->setQuery(Akonadi::ContactSearchJob::NickName, recipient.toLower());
54 connect(searchJob, &Akonadi::ContactSearchJob::result, this, &AliasesExpandJob::slotNicknameExpansionDone);
55 mNicknameExpansionJobs++;
56 searchJob->start();
57 }
58
59 if (mDistributionListExpansionJobs == 0 && mNicknameExpansionJobs == 0) {
60 emitResult();
61 }
62 }
63
addresses() const64 QString AliasesExpandJob::addresses() const
65 {
66 return mEmailAddresses;
67 }
68
emptyDistributionLists() const69 QStringList AliasesExpandJob::emptyDistributionLists() const
70 {
71 return mEmptyDistributionLists;
72 }
73
slotDistributionListExpansionDone(KJob * job)74 void AliasesExpandJob::slotDistributionListExpansionDone(KJob *job)
75 {
76 if (job->error()) {
77 setError(job->error());
78 setErrorText(job->errorText());
79 emitResult();
80 return;
81 }
82
83 const DistributionListExpandJob *expandJob = qobject_cast<DistributionListExpandJob *>(job);
84 const QString recipient = expandJob->property("recipient").toString();
85
86 DistributionListExpansionResult result;
87 result.addresses = expandJob->addresses();
88 result.isEmpty = expandJob->isEmpty();
89
90 mDistListExpansionResults.insert(recipient, result);
91
92 mDistributionListExpansionJobs--;
93 if (mDistributionListExpansionJobs == 0 && mNicknameExpansionJobs == 0) {
94 finishExpansion();
95 }
96 }
97
slotNicknameExpansionDone(KJob * job)98 void AliasesExpandJob::slotNicknameExpansionDone(KJob *job)
99 {
100 if (job->error()) {
101 setError(job->error());
102 setErrorText(job->errorText());
103 emitResult();
104 return;
105 }
106
107 const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
108 const KContacts::Addressee::List contacts = searchJob->contacts();
109 const QString recipient = searchJob->property("recipient").toString();
110
111 for (const KContacts::Addressee &contact : contacts) {
112 if (contact.nickName().toLower() == recipient.toLower()) {
113 mNicknameExpansionResults.insert(recipient, contact.fullEmail());
114 break;
115 }
116 }
117
118 mNicknameExpansionJobs--;
119 if (mDistributionListExpansionJobs == 0 && mNicknameExpansionJobs == 0) {
120 finishExpansion();
121 }
122 }
123
finishExpansion()124 void AliasesExpandJob::finishExpansion()
125 {
126 for (const QString &recipient : std::as_const(mRecipients)) {
127 if (recipient.isEmpty()) {
128 continue;
129 }
130 if (!mEmailAddresses.isEmpty()) {
131 mEmailAddresses += QLatin1String(", ");
132 }
133
134 const QString receiver = recipient.trimmed();
135
136 // take prefetched expand distribution list results
137 const DistributionListExpansionResult result = mDistListExpansionResults.value(recipient);
138 QString displayName;
139 QString addrSpec;
140 QString comment;
141
142 if (result.isEmpty) {
143 KEmailAddress::splitAddress(receiver, displayName, addrSpec, comment);
144 mEmailAddressOnly.append(addrSpec);
145 mEmailAddresses += receiver;
146 mEmptyDistributionLists << receiver;
147 continue;
148 }
149
150 if (!result.addresses.isEmpty()) {
151 KEmailAddress::splitAddress(result.addresses, displayName, addrSpec, comment);
152 mEmailAddressOnly.append(addrSpec);
153
154 mEmailAddresses += result.addresses;
155 continue;
156 }
157
158 // take prefetched expand nick name results
159 const QString recipientValue = mNicknameExpansionResults.value(recipient);
160 if (!recipientValue.isEmpty()) {
161 mEmailAddresses += recipientValue;
162 KEmailAddress::splitAddress(recipientValue, displayName, addrSpec, comment);
163 mEmailAddressOnly.append(addrSpec);
164
165 continue;
166 }
167
168 // check whether the address is missing the domain part
169 KEmailAddress::splitAddress(receiver, displayName, addrSpec, comment);
170 if (!addrSpec.contains(QLatin1Char('@'))) {
171 if (!mDefaultDomain.isEmpty()) {
172 mEmailAddresses += KEmailAddress::normalizedAddress(displayName, addrSpec + QLatin1Char('@') + mDefaultDomain, comment);
173 } else {
174 mEmailAddresses += MessageCore::StringUtil::guessEmailAddressFromLoginName(addrSpec);
175 }
176 } else {
177 mEmailAddresses += receiver;
178 }
179 mEmailAddressOnly.append(addrSpec);
180 }
181
182 emitResult();
183 }
184
emailAddressOnly() const185 QStringList AliasesExpandJob::emailAddressOnly() const
186 {
187 return mEmailAddressOnly;
188 }
189