2 Copyright (c) 1998 Barry D Benowitz <b.benowitz@telesciences.com>
3 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
4 Copyright (c) 2009 Allen Winter <winter@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 As a special exception, permission is given to link this program
21 with any edition of Qt, and distribute the resulting executable,
22 without including the source code for Qt in the source distribution.
25 #include <config-enterprise.h>
27 #include "mailclient.h"
28 #include "kdepim-version.h"
29 #include "koalarmclient_debug.h"
31 #include <AkonadiCore/Collection>
33 #include <KCalCore/Attendee>
34 #include <KCalCore/Incidence>
35 #include <KCalCore/IncidenceBase>
37 #include <KCalUtils/IncidenceFormatter>
39 #include <KMime/Message>
41 #include <KIdentityManagement/Identity>
43 #include <KEmailAddress>
45 #include <MailTransport/MessageQueueJob>
46 #include <MailTransport/Transport>
47 #include <MailTransport/TransportManager>
49 #include <KLocalizedString>
50 #include <KProtocolManager>
51 #include <KSystemTimeZone>
55 MailClient::MailClient() : QObject()
59 MailClient::~MailClient()
63 bool MailClient::mailAttendees(const KCalCore::IncidenceBase::Ptr
&incidence
,
64 const KIdentityManagement::Identity
&identity
,
65 bool bccMe
, const QString
&attachment
,
66 const QString
&mailTransport
)
68 KCalCore::Attendee::List attendees
= incidence
->attendees();
69 if (attendees
.isEmpty()) {
70 qCDebug(KOALARMCLIENT_LOG
) << "There are no attendees to e-mail";
74 const QString from
= incidence
->organizer()->fullName();
75 const QString organizerEmail
= incidence
->organizer()->email();
79 const int numberOfAttendees(attendees
.count());
80 for (int i
= 0; i
< numberOfAttendees
; ++i
) {
81 KCalCore::Attendee::Ptr a
= attendees
.at(i
);
83 const QString email
= a
->email();
84 if (email
.isEmpty()) {
88 // In case we (as one of our identities) are the organizer we are sending
89 // this mail. We could also have added ourselves as an attendee, in which
90 // case we don't want to send ourselves a notification mail.
91 if (organizerEmail
== email
) {
95 // Build a nice address for this attendee including the CN.
96 QString tname
, temail
;
97 const QString username
= KEmailAddress::quoteNameIfNecessary(a
->name());
98 // ignore the return value from extractEmailAddressAndName() because
99 // it will always be false since tusername does not contain "@domain".
100 KEmailAddress::extractEmailAddressAndName(username
, temail
/*byref*/, tname
/*byref*/);
101 tname
+= QStringLiteral(" <") + email
+ QLatin1Char('>');
103 // Optional Participants and Non-Participants are copied on the email
104 if (a
->role() == KCalCore::Attendee::OptParticipant
||
105 a
->role() == KCalCore::Attendee::NonParticipant
) {
111 if (toList
.isEmpty() && ccList
.isEmpty()) {
112 // Not really to be called a groupware meeting, eh
113 qCDebug(KOALARMCLIENT_LOG
) << "There are really no attendees to e-mail";
117 if (!toList
.isEmpty()) {
118 to
= toList
.join(QStringLiteral(", "));
121 if (!ccList
.isEmpty()) {
122 cc
= ccList
.join(QStringLiteral(", "));
126 if (incidence
->type() != KCalCore::Incidence::TypeFreeBusy
) {
127 KCalCore::Incidence::Ptr inc
= incidence
.staticCast
<KCalCore::Incidence
>();
128 subject
= inc
->summary();
130 subject
= i18n("Free Busy Object");
134 KCalUtils::IncidenceFormatter::mailBodyStr(incidence
, KSystemTimeZones::local());
136 return send(identity
, from
, to
, cc
, subject
, body
, false,
137 bccMe
, attachment
, mailTransport
);
140 bool MailClient::mailOrganizer(const KCalCore::IncidenceBase::Ptr
&incidence
,
141 const KIdentityManagement::Identity
&identity
,
142 const QString
&from
, bool bccMe
,
143 const QString
&attachment
,
144 const QString
&sub
, const QString
&mailTransport
)
146 const QString to
= incidence
->organizer()->fullName();
147 QString subject
= sub
;
149 if (incidence
->type() != KCalCore::Incidence::TypeFreeBusy
) {
150 KCalCore::Incidence::Ptr inc
= incidence
.staticCast
<KCalCore::Incidence
>();
151 if (subject
.isEmpty()) {
152 subject
= inc
->summary();
155 subject
= i18n("Free Busy Message");
158 const QString body
= KCalUtils::IncidenceFormatter::mailBodyStr(incidence
, KSystemTimeZones::local());
160 return send(identity
, from
, to
, QString(), subject
, body
, false,
161 bccMe
, attachment
, mailTransport
);
164 bool MailClient::mailTo(const KCalCore::IncidenceBase::Ptr
&incidence
,
165 const KIdentityManagement::Identity
&identity
,
166 const QString
&from
, bool bccMe
,
167 const QString
&recipients
, const QString
&attachment
,
168 const QString
&mailTransport
)
172 if (incidence
->type() != KCalCore::Incidence::TypeFreeBusy
) {
173 KCalCore::Incidence::Ptr inc
= incidence
.staticCast
<KCalCore::Incidence
>() ;
174 subject
= inc
->summary();
176 subject
= i18n("Free Busy Message");
179 KCalUtils::IncidenceFormatter::mailBodyStr(incidence
, KSystemTimeZones::local());
181 return send(identity
, from
, recipients
, QString(), subject
, body
, false,
182 bccMe
, attachment
, mailTransport
);
185 QStringList
extractEmailAndNormalize(const QString
&email
)
187 const QStringList splittedEmail
= KEmailAddress::splitAddressList(email
);
188 QStringList normalizedEmail
;
189 normalizedEmail
.reserve(splittedEmail
.count());
190 Q_FOREACH (const QString
&email
, splittedEmail
) {
191 const QString str
= KEmailAddress::extractEmailAddress(KEmailAddress::normalizeAddressesAndEncodeIdn(email
));
192 normalizedEmail
<< str
;
194 return normalizedEmail
;
197 bool MailClient::send(const KIdentityManagement::Identity
&identity
,
198 const QString
&from
, const QString
&_to
,
199 const QString
&cc
, const QString
&subject
,
200 const QString
&body
, bool hidden
, bool bccMe
,
201 const QString
&attachment
, const QString
&mailTransport
)
205 if (!MailTransport::TransportManager::self()->showTransportCreationDialog(
206 0, MailTransport::TransportManager::IfNoTransportExists
)) {
210 // We must have a recipients list for most MUAs. Thus, if the 'to' list
211 // is empty simply use the 'from' address as the recipient.
216 qCDebug(KOALARMCLIENT_LOG
) << "\nFrom:" << from
219 << "\nSubject:" << subject
<< "\nBody: \n" << body
220 << "\nAttachment:\n" << attachment
221 << "\nmailTransport: " << mailTransport
;
226 MailTransport::Transport
*transport
=
227 MailTransport::TransportManager::self()->transportByName(mailTransport
);
230 transport
= MailTransport::TransportManager::self()->transportById(MailTransport::TransportManager::self()->defaultTransportId(), false);
234 // TODO: we need better error handling. Currently korganizer says "Error sending invitation".
235 // Using a boolean for errors isn't granular enough.
236 qCCritical(KOALARMCLIENT_LOG
) << "Error fetching transport; mailTransport"
237 << mailTransport
<< MailTransport::TransportManager::self()->defaultTransportName();
241 const int transportId
= transport
->id();
243 // gather config values
244 KConfig
config(QStringLiteral("kmail2rc"));
246 KConfigGroup
configGroup(&config
, QStringLiteral("Invitations"));
247 const bool outlookConformInvitation
= configGroup
.readEntry("LegacyBodyInvites",
248 #ifdef KDEPIM_ENTERPRISE_BUILD
255 // Now build the message we like to send. The message KMime::Message::Ptr instance
256 // will be the root message that has 2 additional message. The body itself and
257 // the attached cal.ics calendar file.
258 KMime::Message::Ptr message
= KMime::Message::Ptr(new KMime::Message
);
259 message
->contentTransferEncoding()->clear(); // 7Bit, decoded.
262 message
->userAgent()->fromUnicodeString(
263 KProtocolManager::userAgentForApplication(
264 QStringLiteral("KOrganizer"), QStringLiteral(KDEPIM_VERSION
)), "utf-8");
265 message
->from()->fromUnicodeString(from
, "utf-8");
266 message
->to()->fromUnicodeString(to
, "utf-8");
267 message
->cc()->fromUnicodeString(cc
, "utf-8");
269 message
->bcc()->fromUnicodeString(from
, "utf-8"); //from==me, right?
271 message
->date()->setDateTime(QDateTime::currentDateTime());
272 message
->subject()->fromUnicodeString(subject
, "utf-8");
274 if (outlookConformInvitation
) {
275 message
->contentType()->setMimeType("text/calendar");
276 message
->contentType()->setCharset("utf-8");
277 message
->contentType()->setName(QStringLiteral("cal.ics"), "utf-8");
278 message
->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request"));
280 if (!attachment
.isEmpty()) {
281 KMime::Headers::ContentDisposition
*disposition
=
282 new KMime::Headers::ContentDisposition(message
.get());
283 disposition
->setDisposition(KMime::Headers::CDinline
);
284 message
->setHeader(disposition
);
285 message
->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr
);
286 message
->setBody(KMime::CRLFtoLF(attachment
.toUtf8()));
289 // We need to set following 4 lines by hand else KMime::Content::addContent
290 // will create a new Content instance for us to attach the main message
291 // what we don't need cause we already have the main message instance where
292 // 2 additional messages are attached.
293 KMime::Headers::ContentType
*ct
= message
->contentType();
294 ct
->setMimeType("multipart/mixed");
295 ct
->setBoundary(KMime::multiPartBoundary());
296 ct
->setCategory(KMime::Headers::CCcontainer
);
298 // Set the first multipart, the body message.
299 KMime::Content
*bodyMessage
= new KMime::Content
;
300 KMime::Headers::ContentDisposition
*bodyDisposition
=
301 new KMime::Headers::ContentDisposition(bodyMessage
);
302 bodyDisposition
->setDisposition(KMime::Headers::CDinline
);
303 bodyMessage
->contentType()->setMimeType("text/plain");
304 bodyMessage
->contentType()->setCharset("utf-8");
305 bodyMessage
->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr
);
306 bodyMessage
->setBody(KMime::CRLFtoLF(body
.toUtf8()));
307 message
->addContent(bodyMessage
);
309 // Set the sedcond multipart, the attachment.
310 if (!attachment
.isEmpty()) {
311 KMime::Content
*attachMessage
= new KMime::Content
;
312 KMime::Headers::ContentDisposition
*attachDisposition
=
313 new KMime::Headers::ContentDisposition(attachMessage
);
314 attachDisposition
->setDisposition(KMime::Headers::CDattachment
);
315 attachMessage
->contentType()->setMimeType("text/calendar");
316 attachMessage
->contentType()->setCharset("utf-8");
317 attachMessage
->contentType()->setName(QStringLiteral("cal.ics"), "utf-8");
318 attachMessage
->contentType()->setParameter(QStringLiteral("method"),
319 QStringLiteral("request"));
320 attachMessage
->setHeader(attachDisposition
);
321 attachMessage
->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr
);
322 attachMessage
->setBody(KMime::CRLFtoLF(attachment
.toUtf8()));
323 message
->addContent(attachMessage
);
327 // Job done, attach the both multiparts and assemble the message.
330 // Put the newly created item in the MessageQueueJob.
331 MailTransport::MessageQueueJob
*qjob
= new MailTransport::MessageQueueJob(this);
332 qjob
->transportAttribute().setTransportId(transportId
);
334 if (identity
.disabledFcc()) {
335 qjob
->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::Delete
);
337 const Akonadi::Collection
sentCollection(identity
.fcc().toLongLong());
338 if (sentCollection
.isValid()) {
339 qjob
->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::MoveToCollection
);
340 qjob
->sentBehaviourAttribute().setMoveToCollection(sentCollection
);
342 qjob
->sentBehaviourAttribute().setSentBehaviour(
343 MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection
);
347 if (transport
&& transport
->specifySenderOverwriteAddress()) {
348 qjob
->addressAttribute().setFrom(
349 KEmailAddress::extractEmailAddress(
350 KEmailAddress::normalizeAddressesAndEncodeIdn(transport
->senderOverwriteAddress())));
352 qjob
->addressAttribute().setFrom(
353 KEmailAddress::extractEmailAddress(
354 KEmailAddress::normalizeAddressesAndEncodeIdn(from
)));
358 qjob
->addressAttribute().setTo(extractEmailAndNormalize(to
));
361 qjob
->addressAttribute().setCc(extractEmailAndNormalize(cc
));
364 qjob
->addressAttribute().setBcc(extractEmailAndNormalize(from
));
366 qjob
->setMessage(message
);
368 qCDebug(KOALARMCLIENT_LOG
) << "Error queuing message in outbox:" << qjob
->errorText();
372 // Everything done successful now.
373 qCDebug(KOALARMCLIENT_LOG
) << "Send mail finished. Time elapsed in ms:" << timer
.elapsed();