30 #include <kalarmcal/identities.h>
32 #include <kpimidentities/identitymanager.h>
33 #include <kpimidentities/identity.h>
34 #include <kpimutils/email.h>
35 #include <mailtransport/transportmanager.h>
36 #include <mailtransport/transport.h>
37 #include <mailtransport/messagequeuejob.h>
39 #include <kcalcore/person.h>
41 #include <kcal/person.h>
43 #include <kmime/kmime_header_parsing.h>
44 #include <kmime/kmime_headers.h>
45 #include <kmime/kmime_message.h>
47 #include <kstandarddirs.h>
49 #include <kaboutdata.h>
50 #include <kfileitem.h>
51 #include <kio/netaccess.h>
52 #include <ktemporaryfile.h>
53 #include <kemailsettings.h>
55 #include <kcharsets.h>
64 #include <QtDBus/QtDBus>
68 #ifdef KMAIL_SUPPORTED
69 #include "kmailinterface.h"
71 static const QLatin1String KMAIL_DBUS_SERVICE(
"org.kde.kmail");
75 namespace HeaderParsing
77 bool parseAddress(
const char* & scursor,
const char *
const send,
78 KMime::Types::Address & result,
bool isCRLF=
false );
89 {
return i18nc(
"@info/plain",
"A 'From' email address must be configured in order to execute email alarms."); }
92 {
return i18nc(
"@info/plain KMail folder name: this should be translated the same as in kmail",
"sent-mail"); }
94 KAMail* KAMail::mInstance = 0;
114 KPIMIdentities::Identity identity;
116 if (jobdata.
event.emailFromId()
119 identity = Identities::identityManager()->identityForUoid(jobdata.
event.emailFromId());
120 if (identity.isNull())
122 kError() <<
"Identity" << jobdata.
event.emailFromId() <<
"not found";
123 errmsgs = errors(i18nc(
"@info",
"Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> not found", jobdata.
event.emailFromId()));
126 if (identity.primaryEmailAddress().isEmpty())
128 kError() <<
"Identity" << identity.identityName() <<
"uoid" << identity.uoid() <<
": no email address";
129 errmsgs = errors(i18nc(
"@info",
"Invalid 'From' email address.<nl/>Email identity <resource>%1</resource> has no email address", identity.identityName()));
132 jobdata.
from = identity.fullEmailAddr();
139 errmsgs = errors(i18nc(
"@info",
"<para>No 'From' email address is configured (no default email identity found)</para>"
140 "<para>Please set it in <application>KMail</application> or in the <application>KAlarm</application> Configuration dialog.</para>"));
143 errmsgs = errors(i18nc(
"@info",
"<para>No 'From' email address is configured.</para>"
144 "<para>Please set it in the KDE System Settings or in the <application>KAlarm</application> Configuration dialog.</para>"));
148 errmsgs = errors(i18nc(
"@info",
"<para>No 'From' email address is configured.</para>"
149 "<para>Please set it in the <application>KAlarm</application> Configuration dialog.</para>"));
156 << endl <<
"Subject:" << jobdata.
event.emailSubject();
158 MailTransport::TransportManager* manager = MailTransport::TransportManager::self();
159 MailTransport::Transport* transport = 0;
160 if (Preferences::emailClient() == Preferences::sendmail)
162 kDebug() <<
"Sending via sendmail";
163 if (KStandardDirs::findExe(
QLatin1String(
"akonadi_mailfilter_agent")).isEmpty())
165 kError() <<
"Sendmail requires akonadi_mailfilter_agent to be installed";
166 errmsgs = errors(i18nc(
"@info",
"Sendmail option requires <filename>%1</filename> to be installed",
QLatin1String(
"akonadi_mailfilter_agent")));
170 for (
int i = 0, count = transports.
count(); i < count; ++i)
172 if (transports[i]->type() == MailTransport::Transport::EnumType::Sendmail)
175 transport = transports[i];
183 transport = manager->createTransport();
185 transport->setType(MailTransport::Transport::EnumType::Sendmail);
186 transport->setHost(command);
187 transport->setRequiresAuthentication(
false);
188 transport->setStorePassword(
false);
189 manager->addTransport(transport);
190 transport->writeConfig();
191 kDebug() <<
"Creating sendmail transport, id=" << transport->id();
196 kDebug() <<
"Sending via KDE";
197 const int transportId = identity.transport().isEmpty() ? -1 : identity.transport().toInt();
198 transport = manager->transportById(transportId,
true);
201 kError() <<
"No mail transport found for identity" << identity.identityName() <<
"uoid" << identity.uoid();
202 errmsgs = errors(i18nc(
"@info",
"No mail transport configured for email identity <resource>%1</resource>", identity.identityName()));
206 kDebug() <<
"Using transport" << transport->name() <<
", id=" << transport->id();
208 KMime::Message::Ptr message = KMime::Message::Ptr(
new KMime::Message);
210 err = appendBodyAttachments(*message, jobdata);
213 kError() <<
"Error compiling message:" << err;
214 errmsgs = errors(err);
218 MailTransport::MessageQueueJob* mailjob =
new MailTransport::MessageQueueJob(kapp);
219 mailjob->setMessage(message);
220 mailjob->transportAttribute().setTransportId(transport->id());
227 MailTransport::SentBehaviourAttribute::SentBehaviour sentAction =
229 ? MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection : MailTransport::SentBehaviourAttribute::Delete;
230 mailjob->sentBehaviourAttribute().setSentBehaviour(sentAction);
232 mJobData.enqueue(jobdata);
233 if (mJobs.count() == 1)
236 connect(mailjob, SIGNAL(result(
KJob*)), instance(), SLOT(slotEmailSent(
KJob*)));
245 void KAMail::slotEmailSent(
KJob* job)
247 bool copyerr =
false;
251 kError() <<
"Failed:" << job->errorString();
252 errmsgs = errors(job->errorString(), SEND_ERROR);
255 if (mJobs.isEmpty() || mJobData.isEmpty() || job != mJobs.
head())
258 kError() <<
"Wrong job at head of queue: wiping queue";
264 errmsgs += i18nc(
"@info",
"Emails may not have been sent");
265 errmsgs += i18nc(
"@info",
"Program error");
270 jobdata = mJobData.dequeue();
271 if (jobdata.allowNotify)
272 notifyQueued(jobdata.event);
274 if (!mJobs.isEmpty())
278 mJobs.
head()->start();
287 KMime::Headers::Date*
date =
new KMime::Headers::Date;
289 message.setHeader(date);
291 KMime::Headers::From*
from =
new KMime::Headers::From;
293 message.setHeader(from);
295 KMime::Headers::To*
to =
new KMime::Headers::To;
297 KCalCore::Person::List toList = data.
event.emailAddressees();
301 for (
int i = 0, count = toList.
count(); i < count; ++i)
303 to->addAddress(toList[i]->email().toLatin1(), toList[i]->name());
305 to->addAddress(toList[i].email().toLatin1(), toList[i].name());
307 message.setHeader(to);
311 KMime::Headers::Bcc* bcc =
new KMime::Headers::Bcc;
313 message.setHeader(bcc);
316 KMime::Headers::Subject*
subject =
new KMime::Headers::Subject;
319 message.setHeader(subject);
321 KMime::Headers::UserAgent* agent =
new KMime::Headers::UserAgent;
323 message.setHeader(agent);
325 KMime::Headers::MessageID*
id =
new KMime::Headers::MessageID;
327 message.setHeader(
id);
335 QString KAMail::appendBodyAttachments(KMime::Message& message, JobData& data)
337 QStringList attachments = data.event.emailAttachments();
338 if (!attachments.
count())
341 message.contentType()->setMimeType(
"text/plain");
342 message.contentType()->setCharset(
"utf-8");
343 message.fromUnicodeString(data.event.message());
345 encodings.
removeAll(KMime::Headers::CE8Bit);
346 message.contentTransferEncoding()->setEncoding(encodings[0]);
352 message.contentType()->setMimeType(
"multipart/mixed");
353 message.contentType()->setBoundary(KMime::multiPartBoundary());
355 if (!data.event.message().isEmpty())
358 KMime::Content* content =
new KMime::Content();
359 content->contentType()->setMimeType(
"text/plain");
360 content->contentType()->setCharset(
"utf-8");
361 content->fromUnicodeString(data.event.message());
363 encodings.
removeAll(KMime::Headers::CE8Bit);
364 content->contentTransferEncoding()->setEncoding(encodings[0]);
366 message.addContent(content);
373 KUrl url(attachment);
374 QString attachError = i18nc(
"@info",
"Error attaching file: <filename>%1</filename>", attachment);
378 kError() <<
"Not found:" << attachment;
379 return i18nc(
"@info",
"Attachment not found: <filename>%1</filename>", attachment);
381 KFileItem fi(uds, url);
382 if (fi.isDir() || !fi.isReadable()) {
383 kError() <<
"Not file/not readable:" << attachment;
390 kError() <<
"Load failure:" << attachment;
394 if (!file.open(QIODevice::ReadOnly)) {
395 kDebug() <<
"tmp load error:" << attachment;
398 qint64 size = file.
size();
401 bool atterror =
false;
402 if (contents.
size() < size) {
403 kDebug() <<
"Read error:" << attachment;
407 QByteArray coded = KCodecs::base64Encode(contents,
true);
408 KMime::Content* content =
new KMime::Content();
409 content->setBody(coded +
"\n\n");
412 KMimeType::Ptr type = KMimeType::findByUrl(url);
413 KMime::Headers::ContentType* ctype =
new KMime::Headers::ContentType(content);
415 ctype->setName(attachment,
"local");
416 content->setHeader(ctype);
419 KMime::Headers::ContentTransferEncoding* cte =
new KMime::Headers::ContentTransferEncoding(content);
420 cte->setEncoding(KMime::Headers::CEbase64);
421 cte->setDecoded(
false);
422 content->setHeader(cte);
424 message.addContent(content);
437 void KAMail::notifyQueued(
const KAEvent& event)
439 KMime::Types::Address addr;
443 KCalCore::Person::List addresses =
event.emailAddressees();
447 for (
int i = 0, end = addresses.
count(); i < end; ++i)
450 QByteArray email = addresses[i]->email().toLocal8Bit();
452 QByteArray email = addresses[i].email().toLocal8Bit();
454 const char* em = email;
458 QString domain = addr.mailboxList.first().addrSpec().domain;
459 if (!domain.
isEmpty() && domain != localhost && domain != hostname)
474 return e.getSetting(KEMailSettings::EmailAddress);
490 const KMime::Types::Mailbox::List mailboxes =
parseAddresses(items, invalidItem);
493 for (
int i = 0, count = mailboxes.count(); i < count; ++i)
496 KCalCore::Person::Ptr person(
new KCalCore::Person(mailboxes[i].name(), mailboxes[i].addrSpec().asString()));
499 list += KCal::Person(mailboxes[i].name(), mailboxes[i].addrSpec().asString());
533 if (i == start || i == end)
564 int length = items.
length();
565 for (
int next = 0; next < length; )
579 case 1: list += item;
break;
582 default:
return item;
598 attachment = attachment.
trimmed();
621 KFileItem fi(uds, url);
622 if (fi.isDir() || !fi.isReadable())
635 case SEND_FAIL: error1 = i18nc(
"@info",
"Failed to send email");
break;
636 case SEND_ERROR: error1 = i18nc(
"@info",
"Error sending email");
break;
645 #ifdef KMAIL_SUPPORTED
652 args << serialNumber << (int)0;
654 #warning Set correct DBus interface/object for kmail
660 kError() <<
"D-Bus call failed:" << reply.
error().
message();
663 return reply.
value();
672 return KPIMUtils::extractEmailAddress(KPIMUtils::normalizeAddressesAndEncodeIdn(emailAddress));
677 const QStringList splitEmails(KPIMUtils::splitAddressList(emailAddresses));
679 Q_FOREACH(
const QString& email, splitEmails)
681 normalizedEmail << KPIMUtils::extractEmailAddress(KPIMUtils::normalizeAddressesAndEncodeIdn(email));
683 return normalizedEmail;
692 charsets <<
"us-ascii" <<
"iso-8859-1" <<
"locale" <<
"utf-8";
694 for (
int i = 0, count = charsets.
count(); i < count; ++i)
697 if (encoding ==
"locale")
700 kAsciiToLower(encoding.
data());
704 if (encoding ==
"us-ascii")
706 if (KMime::isUsAscii(text))
713 kDebug() <<
"Auto-Charset: Something is wrong and I cannot get a codec. [" << encoding <<
"]";
731 kAsciiToLower(codec.
data());
732 return KGlobal::charsets()->codecForName(
QLatin1String(codec));
743 KMime::Types::Mailbox::List list;
751 for (
int i = 0, count = text.
length(); i <= count; ++i)
761 if (ch ==
' ' || ch ==
'\t')
764 state = (ch ==
'"') ? 10 : 1;
792 if (ch ==
',' || ch ==
';')
796 invalidItem = text.
mid(start);
797 return KMime::Types::Mailbox::List();
801 if (ch ==
'"' && lastch !=
'\\')
827 QString addr = text.
mid(startAddr, endAddr - startAddr);
828 KMime::Types::Mailbox mbox;
829 mbox.fromUnicodeString(addr);
830 if (mbox.address().isEmpty())
832 invalidItem = text.
mid(start, endAddr - start);
833 return KMime::Types::Mailbox::List();
837 int len = endName - start;
838 QString name = text.
mid(start, endName - start);
840 name = name.
mid(1, len - 2);
845 endName = startAddr = endAddr = 0;
860 namespace HeaderParsing
863 using namespace KMime;
864 using namespace KMime::Types;
865 using namespace KMime::HeaderParsing;
872 QString & result,
bool isCRLF ) {
877 if ( scursor != send ) {
879 eatCFWS( scursor, send, isCRLF );
881 char ch = *scursor++;
891 if ( parseAtom( scursor, send, result,
false ) ) {
907 Address & result,
bool isCRLF ) {
910 eatCFWS( scursor, send, isCRLF );
911 if ( scursor == send )
915 Mailbox maybeMailbox;
916 const char * oldscursor = scursor;
917 if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
919 result.displayName.clear();
920 result.mailboxList.append( maybeMailbox );
923 scursor = oldscursor;
928 if (
parseUserName( scursor, send, maybeUserName, isCRLF ) ) {
930 maybeMailbox.setName(
QString() );
932 addrSpec.localPart = maybeUserName;
933 addrSpec.domain.
clear();
934 maybeMailbox.setAddress( addrSpec );
935 result.displayName.clear();
936 result.mailboxList.append( maybeMailbox );
939 scursor = oldscursor;
941 Address maybeAddress;
944 if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) )
946 scursor = oldscursor;
950 result = maybeAddress;
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QString & append(QChar ch)
static QString controlCentreAddress()
virtual QByteArray name() const =0
static void initHeaders(KMime::Message &, KAMail::JobData &)
static QStringList extractEmailsAndNormalize(const QString &emailAddresses)
void emailSent(KAMail::JobData &, const QStringList &errmsgs, bool copyerr=false)
virtual QByteArray text(quint32 serialNumber) const =0
static int checkAddress(QString &address)
static QString i18n_sent_mail()
the KAlarm application object
static QString convertAddresses(const QString &addresses, QList< KCal::Person > &)
int count(const T &value) const
static int checkAttachment(QString &attachment, KUrl *=0)
static KTimeZone timeZone(bool reload=false)
int removeAll(const T &value)
static QString i18n_NeedFromEmailAddress()
static QString convertAttachments(const QString &attachments, QStringList &list)
static const QLatin1String EMAIL_QUEUED_NOTIFY
static bool emailCopyToKMail()
static QByteArray autoDetectCharset(const QString &text)
QByteArray toLocal8Bit() const
QByteArray toLatin1() const
QString mid(int position, int n) const
static QString emailBccAddress()
static KMime::Types::Mailbox::List parseAddresses(const QString &text, QString &invalidItem)
static QString emailAddress()
QTextCodec * codecForName(const QByteArray &name)
bool canEncode(QChar ch) const
static QString extractEmailAndNormalize(const QString &emailAddress)
QString fromLatin1(const char *str, int size)
static int send(JobData &, QStringList &errmsgs)
quint32 serialNumber() const
static MainWindow * mainMainWindow()
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const QDBusError & error()
static const QTextCodec * codecForName(const QByteArray &str)
static MailFrom emailFrom()
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Options(Notify|WindowModal))