/* Copyright (c) 1998 Barry D Benowitz Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2009 Allen Winter 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; iemail(); 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(); 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(); 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() ; 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()); } }