SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / korgac / mailclient.cpp
blobcb53f4485eb86fb63333e9143bfc3c0ee34c6ffd
1 /*
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>
53 using namespace KOrg;
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";
71 return false;
74 const QString from = incidence->organizer()->fullName();
75 const QString organizerEmail = incidence->organizer()->email();
77 QStringList toList;
78 QStringList ccList;
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()) {
85 continue;
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) {
92 continue;
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) {
106 ccList << tname;
107 } else {
108 toList << tname;
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";
114 return false;
116 QString to;
117 if (!toList.isEmpty()) {
118 to = toList.join(QStringLiteral(", "));
120 QString cc;
121 if (!ccList.isEmpty()) {
122 cc = ccList.join(QStringLiteral(", "));
125 QString subject;
126 if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) {
127 KCalCore::Incidence::Ptr inc = incidence.staticCast<KCalCore::Incidence>();
128 subject = inc->summary();
129 } else {
130 subject = i18n("Free Busy Object");
133 const QString body =
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();
154 } else {
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)
170 QString subject;
172 if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) {
173 KCalCore::Incidence::Ptr inc = incidence.staticCast<KCalCore::Incidence>();
174 subject = inc->summary();
175 } else {
176 subject = i18n("Free Busy Message");
178 const QString body =
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)
203 Q_UNUSED(hidden);
205 if (!MailTransport::TransportManager::self()->showTransportCreationDialog(
206 0, MailTransport::TransportManager::IfNoTransportExists)) {
207 return false;
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.
212 QString to = _to;
213 if (to.isEmpty()) {
214 to = from;
216 qCDebug(KOALARMCLIENT_LOG) << "\nFrom:" << from
217 << "\nTo:" << to
218 << "\nCC:" << cc
219 << "\nSubject:" << subject << "\nBody: \n" << body
220 << "\nAttachment:\n" << attachment
221 << "\nmailTransport: " << mailTransport;
223 QTime timer;
224 timer.start();
226 MailTransport::Transport *transport =
227 MailTransport::TransportManager::self()->transportByName(mailTransport);
229 if (!transport) {
230 transport = MailTransport::TransportManager::self()->transportById(MailTransport::TransportManager::self()->defaultTransportId(), false);
233 if (!transport) {
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();
238 return false;
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
249 true
250 #else
251 false
252 #endif
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()->setEncoding(KMime::Headers::CE7Bit);
260 message->contentTransferEncoding()->setDecoded(true);
262 // Set the headers
263 message->userAgent()->fromUnicodeString(
264 KProtocolManager::userAgentForApplication(
265 QStringLiteral("KOrganizer"), QStringLiteral(KDEPIM_VERSION)), "utf-8");
266 message->from()->fromUnicodeString(from, "utf-8");
267 message->to()->fromUnicodeString(to, "utf-8");
268 message->cc()->fromUnicodeString(cc, "utf-8");
269 if (bccMe) {
270 message->bcc()->fromUnicodeString(from, "utf-8"); //from==me, right?
272 message->date()->setDateTime(QDateTime::currentDateTime());
273 message->subject()->fromUnicodeString(subject, "utf-8");
275 if (outlookConformInvitation) {
276 message->contentType()->setMimeType("text/calendar");
277 message->contentType()->setCharset("utf-8");
278 message->contentType()->setName(QStringLiteral("cal.ics"), "utf-8");
279 message->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request"));
281 if (!attachment.isEmpty()) {
282 KMime::Headers::ContentDisposition *disposition = new KMime::Headers::ContentDisposition;
283 disposition->setDisposition(KMime::Headers::CDinline);
284 message->setHeader(disposition);
285 message->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
286 message->setBody(KMime::CRLFtoLF(attachment.toUtf8()));
288 } else {
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 = new KMime::Headers::ContentDisposition;
301 bodyDisposition->setDisposition(KMime::Headers::CDinline);
302 bodyMessage->contentType()->setMimeType("text/plain");
303 bodyMessage->contentType()->setCharset("utf-8");
304 bodyMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
305 bodyMessage->setBody(KMime::CRLFtoLF(body.toUtf8()));
306 message->addContent(bodyMessage);
308 // Set the sedcond multipart, the attachment.
309 if (!attachment.isEmpty()) {
310 KMime::Content *attachMessage = new KMime::Content;
311 KMime::Headers::ContentDisposition *attachDisposition = new KMime::Headers::ContentDisposition;
312 attachDisposition->setDisposition(KMime::Headers::CDattachment);
313 attachMessage->contentType()->setMimeType("text/calendar");
314 attachMessage->contentType()->setCharset("utf-8");
315 attachMessage->contentType()->setName(QStringLiteral("cal.ics"), "utf-8");
316 attachMessage->contentType()->setParameter(QStringLiteral("method"),
317 QStringLiteral("request"));
318 attachMessage->setHeader(attachDisposition);
319 attachMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
320 attachMessage->setBody(KMime::CRLFtoLF(attachment.toUtf8()));
321 message->addContent(attachMessage);
325 // Job done, attach the both multiparts and assemble the message.
326 message->assemble();
328 // Put the newly created item in the MessageQueueJob.
329 MailTransport::MessageQueueJob *qjob = new MailTransport::MessageQueueJob(this);
330 qjob->transportAttribute().setTransportId(transportId);
332 if (identity.disabledFcc()) {
333 qjob->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::Delete);
334 } else {
335 const Akonadi::Collection sentCollection(identity.fcc().toLongLong());
336 if (sentCollection.isValid()) {
337 qjob->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::MoveToCollection);
338 qjob->sentBehaviourAttribute().setMoveToCollection(sentCollection);
339 } else {
340 qjob->sentBehaviourAttribute().setSentBehaviour(
341 MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection);
345 if (transport && transport->specifySenderOverwriteAddress()) {
346 qjob->addressAttribute().setFrom(
347 KEmailAddress::extractEmailAddress(
348 KEmailAddress::normalizeAddressesAndEncodeIdn(transport->senderOverwriteAddress())));
349 } else {
350 qjob->addressAttribute().setFrom(
351 KEmailAddress::extractEmailAddress(
352 KEmailAddress::normalizeAddressesAndEncodeIdn(from)));
355 if (!to.isEmpty()) {
356 qjob->addressAttribute().setTo(extractEmailAndNormalize(to));
358 if (!cc.isEmpty()) {
359 qjob->addressAttribute().setCc(extractEmailAndNormalize(cc));
361 if (bccMe) {
362 qjob->addressAttribute().setBcc(extractEmailAndNormalize(from));
364 qjob->setMessage(message);
365 if (!qjob->exec()) {
366 qCDebug(KOALARMCLIENT_LOG) << "Error queuing message in outbox:" << qjob->errorText();
367 return false;
370 // Everything done successful now.
371 qCDebug(KOALARMCLIENT_LOG) << "Send mail finished. Time elapsed in ms:" << timer.elapsed();
372 return true;