kde-playground/kdepimlibs/akonadi/calendar/mailclient_p.cpp
2015-04-14 21:49:29 +00:00

401 lines
16 KiB
C++

/*
Copyright (c) 1998 Barry D Benowitz <b.benowitz@telesciences.com>
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
Copyright (c) 2009 Allen Winter <winter@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "mailclient_p.h"
#include "akonadi/kdepimlibs-version.h"
#include <akonadi/collection.h>
#include <kcalcore/attendee.h>
#include <kcalcore/incidence.h>
#include <kcalcore/incidencebase.h>
#include <kcalutils/incidenceformatter.h>
#include <kmime/kmime_message.h>
#include <kpimidentities/identity.h>
#include <kpimutils/email.h>
#include <mailtransport/messagequeuejob.h>
#include <mailtransport/transport.h>
#include <mailtransport/transportmanager.h>
#include <KDebug>
#include <KLocalizedString>
#include <KJob>
#include <KProtocolManager>
#include <KSystemTimeZone>
using namespace Akonadi;
bool Akonadi::MailClient::sRunningUnitTests = false;
UnitTestResult::List MailClient::sUnitTestResults;
MailClient::MailClient(QObject *parent) : QObject(parent)
{
}
MailClient::~MailClient()
{
}
void MailClient::mailAttendees(const KCalCore::IncidenceBase::Ptr &incidence,
const KPIMIdentities::Identity &identity,
bool bccMe, const QString &attachment,
const QString &mailTransport)
{
Q_ASSERT(incidence);
KCalCore::Attendee::List attendees = incidence->attendees();
if (attendees.isEmpty()) {
kWarning() << "There are no attendees to e-mail";
emit finished(ResultNoAttendees, i18n("There are no attendees to e-mail"));
return;
}
const QString from = incidence->organizer()->fullName();
const QString organizerEmail = incidence->organizer()->email();
QStringList toList;
QStringList ccList;
const int numberOfAttendees = attendees.count();
for (int i=0; i<numberOfAttendees; ++i) {
KCalCore::Attendee::Ptr a = attendees.at(i);
const QString email = a->email();
if (email.isEmpty()) {
continue;
}
// In case we (as one of our identities) are the organizer we are sending
// this mail. We could also have added ourselves as an attendee, in which
// case we don't want to send ourselves a notification mail.
if (organizerEmail == email) {
continue;
}
// Build a nice address for this attendee including the CN.
QString tname, temail;
const QString username = KPIMUtils::quoteNameIfNecessary(a->name());
// ignore the return value from extractEmailAddressAndName() because
// it will always be false since tusername does not contain "@domain".
KPIMUtils::extractEmailAddressAndName(username, temail/*byref*/, tname/*byref*/);
tname += QLatin1String(" <") + email + QLatin1Char('>');
// Optional Participants and Non-Participants are copied on the email
if (a->role() == KCalCore::Attendee::OptParticipant ||
a->role() == KCalCore::Attendee::NonParticipant) {
ccList << tname;
} else {
toList << tname;
}
}
if (toList.isEmpty() && ccList.isEmpty()) {
// Not really to be called a groupware meeting, eh
kWarning() << "There are really no attendees to e-mail";
emit finished(ResultReallyNoAttendees, i18n("There are no attendees to e-mail"));
return;
}
QString to;
if (!toList.isEmpty()) {
to = toList.join(QLatin1String(", "));
}
QString cc;
if (!ccList.isEmpty()) {
cc = ccList.join(QLatin1String(", "));
}
QString subject;
if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) {
KCalCore::Incidence::Ptr inc = incidence.staticCast<KCalCore::Incidence>();
subject = inc->summary();
} else {
subject = i18n("Free Busy Object");
}
const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
KSystemTimeZones::local());
send(identity, from, to, cc, subject, body, false, bccMe, attachment, mailTransport);
}
void MailClient::mailOrganizer(const KCalCore::IncidenceBase::Ptr &incidence,
const KPIMIdentities::Identity &identity,
const QString &from, bool bccMe,
const QString &attachment,
const QString &sub, const QString &mailTransport)
{
const QString to = incidence->organizer()->fullName();
QString subject = sub;
if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) {
KCalCore::Incidence::Ptr inc = incidence.staticCast<KCalCore::Incidence>();
if (subject.isEmpty()) {
subject = inc->summary();
}
} else {
subject = i18n("Free Busy Message");
}
const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
KSystemTimeZones::local());
send(identity, from, to, QString(), subject, body, false, bccMe, attachment, mailTransport);
}
void MailClient::mailTo(const KCalCore::IncidenceBase::Ptr &incidence,
const KPIMIdentities::Identity &identity,
const QString &from, bool bccMe,
const QString &recipients, const QString &attachment,
const QString &mailTransport)
{
QString subject;
if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) {
KCalCore::Incidence::Ptr inc = incidence.staticCast<KCalCore::Incidence>() ;
subject = inc->summary();
} else {
subject = i18n("Free Busy Message");
}
const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
KSystemTimeZones::local());
send(identity, from, recipients, QString(), subject, body, false,
bccMe, attachment, mailTransport);
}
static QStringList extractEmailAndNormalize(const QString &email)
{
const QStringList splittedEmail = KPIMUtils::splitAddressList(email);
QStringList normalizedEmail;
foreach(const QString &email, splittedEmail) {
const QString str = KPIMUtils::extractEmailAddress(KPIMUtils::normalizeAddressesAndEncodeIdn(email));
normalizedEmail << str;
}
return normalizedEmail;
}
void MailClient::send(const KPIMIdentities::Identity &identity,
const QString &from, const QString &_to,
const QString &cc, const QString &subject,
const QString &body, bool hidden, bool bccMe,
const QString &attachment, const QString &mailTransport)
{
Q_UNUSED(identity);
Q_UNUSED(hidden);
UnitTestResult unitTestResult;
if (!MailTransport::TransportManager::self()->showTransportCreationDialog(
0, MailTransport::TransportManager::IfNoTransportExists)) {
kError() << "Error while creating transport";
emit finished(ResultErrorCreatingTransport, i18n("Error while creating transport"));
return;
}
// We must have a recipients list for most MUAs. Thus, if the 'to' list
// is empty simply use the 'from' address as the recipient.
QString to = _to;
if (to.isEmpty()) {
to = from;
}
kDebug() << "\nFrom:" << from
<< "\nTo:" << to
<< "\nCC:" << cc
<< "\nSubject:" << subject << "\nBody: \n" << body
<< "\nAttachment:\n" << attachment
<< "\nmailTransport: " << mailTransport;
QTime timer;
timer.start();
MailTransport::Transport *transport =
MailTransport::TransportManager::self()->transportByName(mailTransport);
if (!transport) {
transport =
MailTransport::TransportManager::self()->transportByName(
MailTransport::TransportManager::self()->defaultTransportName());
}
if (!transport) {
kError() << "Error fetching transport; mailTransport"
<< mailTransport << MailTransport::TransportManager::self()->defaultTransportName();
emit finished(ResultErrorFetchingTransport,
i18n("Error fetching transport. Unable to send invitations"));
return;
}
const int transportId = transport->id();
// gather config values
KConfig config(QLatin1String("mailviewerrc"));
KConfigGroup configGroup(&config, QLatin1String("Invitations"));
const bool outlookConformInvitation = configGroup.readEntry("LegacyBodyInvites",
#ifdef KDEPIM_ENTERPRISE_BUILD
true
#else
false
#endif
);
// Now build the message we like to send. The message KMime::Message::Ptr instance
// will be the root message that has 2 additional message. The body itself and
// the attached cal.ics calendar file.
KMime::Message::Ptr message = KMime::Message::Ptr(new KMime::Message);
message->contentTransferEncoding()->clear(); // 7Bit, decoded.
// Set the headers
message->userAgent()->fromUnicodeString(
KProtocolManager::userAgentForApplication(
QLatin1String("KOrganizer"), QLatin1String(KDEPIMLIBS_VERSION)), "utf-8");
message->from()->fromUnicodeString(from, "utf-8");
message->to()->fromUnicodeString(to, "utf-8");
message->cc()->fromUnicodeString(cc, "utf-8");
if (bccMe) {
message->bcc()->fromUnicodeString(from, "utf-8"); //from==me, right?
}
message->date()->setDateTime(KDateTime::currentLocalDateTime());
message->subject()->fromUnicodeString(subject, "utf-8");
if (outlookConformInvitation) {
message->contentType()->setMimeType("text/calendar");
message->contentType()->setCharset("utf-8");
message->contentType()->setName(QLatin1String("cal.ics"), "utf-8");
message->contentType()->setParameter(QLatin1String("method"), QLatin1String("request"));
if (!attachment.isEmpty()) {
KMime::Headers::ContentDisposition *disposition =
new KMime::Headers::ContentDisposition(message.get());
disposition->setDisposition(KMime::Headers::CDinline);
message->setHeader(disposition);
message->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
message->setBody(KMime::CRLFtoLF(attachment.toUtf8()));
}
} else {
// We need to set following 4 lines by hand else KMime::Content::addContent
// will create a new Content instance for us to attach the main message
// what we don't need cause we already have the main message instance where
// 2 additional messages are attached.
KMime::Headers::ContentType *ct = message->contentType();
ct->setMimeType("multipart/mixed");
ct->setBoundary(KMime::multiPartBoundary());
ct->setCategory(KMime::Headers::CCcontainer);
// Set the first multipart, the body message.
KMime::Content *bodyMessage = new KMime::Content;
KMime::Headers::ContentDisposition *bodyDisposition =
new KMime::Headers::ContentDisposition(bodyMessage);
bodyDisposition->setDisposition(KMime::Headers::CDinline);
bodyMessage->contentType()->setMimeType("text/plain");
bodyMessage->contentType()->setCharset("utf-8");
bodyMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
bodyMessage->setBody(KMime::CRLFtoLF(body.toUtf8()));
message->addContent(bodyMessage);
// Set the sedcond multipart, the attachment.
if (!attachment.isEmpty()) {
KMime::Content *attachMessage = new KMime::Content;
KMime::Headers::ContentDisposition *attachDisposition =
new KMime::Headers::ContentDisposition(attachMessage);
attachDisposition->setDisposition(KMime::Headers::CDattachment);
attachMessage->contentType()->setMimeType("text/calendar");
attachMessage->contentType()->setCharset("utf-8");
attachMessage->contentType()->setName(QLatin1String("cal.ics"), "utf-8");
attachMessage->contentType()->setParameter(QLatin1String("method"),
QLatin1String("request"));
attachMessage->setHeader(attachDisposition);
attachMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
attachMessage->setBody(KMime::CRLFtoLF(attachment.toUtf8()));
message->addContent(attachMessage);
}
}
// Job done, attach the both multiparts and assemble the message.
message->assemble();
// Put the newly created item in the MessageQueueJob.
MailTransport::MessageQueueJob *qjob = new MailTransport::MessageQueueJob(this);
qjob->transportAttribute().setTransportId(transportId);
qjob->sentBehaviourAttribute().setSentBehaviour(
MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection);
const QString unormalizedFrom = (transport && transport->specifySenderOverwriteAddress()) ?
transport->senderOverwriteAddress() : from;
const QString normalizedFrom = KPIMUtils::extractEmailAddress(
KPIMUtils::normalizeAddressesAndEncodeIdn(unormalizedFrom));
const QString finalFrom = KPIMUtils::extractEmailAddress(normalizedFrom);
qjob->addressAttribute().setFrom(finalFrom);
QStringList toStringList;
if (!to.isEmpty()) {
toStringList = extractEmailAndNormalize(to);
qjob->addressAttribute().setTo(toStringList);
}
QStringList ccStringList;
if (!cc.isEmpty()) {
ccStringList = extractEmailAndNormalize(cc);
qjob->addressAttribute().setCc(ccStringList);
}
QStringList bccStringList;
if (bccMe) {
bccStringList = extractEmailAndNormalize(from);
qjob->addressAttribute().setBcc(bccStringList);
}
if (sRunningUnitTests) {
unitTestResult.message = message;
unitTestResult.from = finalFrom;
unitTestResult.to = toStringList;
unitTestResult.cc = ccStringList;
unitTestResult.bcc = bccStringList;
unitTestResult.transportId = transportId;
sUnitTestResults << unitTestResult;
qjob->deleteLater();
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection,
Q_ARG(Akonadi::MailClient::Result, ResultSuccess),
Q_ARG(QString, QString()));
} else {
qjob->setMessage(message);
connect(qjob, SIGNAL(finished(KJob*)), SLOT(handleQueueJobFinished(KJob*)));
qjob->start();
}
}
void MailClient::handleQueueJobFinished(KJob *job)
{
if (job->error()) {
kError() << "Error queueing message:" << job->errorText();
emit finished(ResultQueueJobError, i18n("Error queuing message in outbox: %1",
job->errorText()));
} else {
kDebug() << "Mail queued";
emit finished(ResultSuccess, QString());
}
}