You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1137 lines
42 KiB
1137 lines
42 KiB
/* -*- mode: C++; c-file-style: "gnu" -*- |
|
Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net |
|
Copyright (c) 2009 Andras Mantia <andras@kdab.net> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program 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 General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License along |
|
with this program; if not, write to the Free Software Foundation, Inc., |
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
*/ |
|
|
|
#include "kmkernel.h" |
|
#include "messagehelper.h" |
|
#include "stringutil.h" |
|
#include "messageviewer/stringutil.h" |
|
#include "version-kmail.h" |
|
#include "kmversion.h" |
|
#include "templateparser.h" |
|
#include "messageinfo.h" |
|
#include "mdnadvicedialog.h" |
|
#include "mailinglist-magic.h" |
|
#include "foldercollection.h" |
|
#include "util.h" |
|
|
|
#include <messageviewer/objecttreeparser.h> |
|
#include <messageviewer/kcursorsaver.h> |
|
|
|
#include <KDateTime> |
|
#include <KProtocolManager> |
|
#include <KMime/Message> |
|
#include <kmime/kmime_mdn.h> |
|
#include <kmime/kmime_dateformatter.h> |
|
#include <kmime/kmime_headers.h> |
|
#include <kpimidentities/identitymanager.h> |
|
#include <kpimidentities/identity.h> |
|
#include <KPIMUtils/Email> |
|
|
|
namespace KMail { |
|
|
|
namespace MessageHelper { |
|
|
|
//TODO init them somewhere, now the init code is in KMMsgBase::readConfig() |
|
static QStringList sReplySubjPrefixes, sForwardSubjPrefixes; |
|
static bool sReplaceSubjPrefix, sReplaceForwSubjPrefix; |
|
|
|
static const struct { |
|
const char * dontAskAgainID; |
|
bool canDeny; |
|
const char * text; |
|
} mdnMessageBoxes[] = { |
|
{ "mdnNormalAsk", true, |
|
I18N_NOOP("This message contains a request to return a notification " |
|
"about your reception of the message.\n" |
|
"You can either ignore the request or let KMail send a " |
|
"\"denied\" or normal response.") }, |
|
{ "mdnUnknownOption", false, |
|
I18N_NOOP("This message contains a request to send a notification " |
|
"about your reception of the message.\n" |
|
"It contains a processing instruction that is marked as " |
|
"\"required\", but which is unknown to KMail.\n" |
|
"You can either ignore the request or let KMail send a " |
|
"\"failed\" response.") }, |
|
{ "mdnMultipleAddressesInReceiptTo", true, |
|
I18N_NOOP("This message contains a request to send a notification " |
|
"about your reception of the message,\n" |
|
"but it is requested to send the notification to more " |
|
"than one address.\n" |
|
"You can either ignore the request or let KMail send a " |
|
"\"denied\" or normal response.") }, |
|
{ "mdnReturnPathEmpty", true, |
|
I18N_NOOP("This message contains a request to send a notification " |
|
"about your reception of the message,\n" |
|
"but there is no return-path set.\n" |
|
"You can either ignore the request or let KMail send a " |
|
"\"denied\" or normal response.") }, |
|
{ "mdnReturnPathNotInReceiptTo", true, |
|
I18N_NOOP("This message contains a request to send a notification " |
|
"about your reception of the message,\n" |
|
"but the return-path address differs from the address " |
|
"the notification was requested to be sent to.\n" |
|
"You can either ignore the request or let KMail send a " |
|
"\"denied\" or normal response.") }, |
|
}; |
|
|
|
static const int numMdnMessageBoxes |
|
= sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes; |
|
|
|
static int requestAdviceOnMDN( const char * what ) { |
|
for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) |
|
if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) { |
|
const KCursorSaver saver( Qt::ArrowCursor ); |
|
MDNAdviceDialog::MDNAdvice answer; |
|
answer = MDNAdviceDialog::questionIgnoreSend( mdnMessageBoxes[i].text, |
|
mdnMessageBoxes[i].canDeny ); |
|
switch ( answer ) { |
|
case MDNAdviceDialog::MDNSend: |
|
return 3; |
|
|
|
case MDNAdviceDialog::MDNSendDenied: |
|
return 2; |
|
|
|
default: |
|
case MDNAdviceDialog::MDNIgnore: |
|
return 0; |
|
} |
|
} |
|
kWarning() <<"didn't find data for message box \"" |
|
<< what << "\""; |
|
return 0; |
|
} |
|
|
|
QString replaceHeadersInString( const KMime::Message::Ptr &msg, const QString & s ) |
|
{ |
|
QString result = s; |
|
QRegExp rx( "\\$\\{([a-z0-9-]+)\\}", Qt::CaseInsensitive ); |
|
Q_ASSERT( rx.isValid() ); |
|
|
|
QRegExp rxDate( "\\$\\{date\\}" ); |
|
Q_ASSERT( rxDate.isValid() ); |
|
|
|
QString sDate = KMime::DateFormatter::formatDate( |
|
KMime::DateFormatter::Localized, msg->date()->dateTime().dateTime().toTime_t() ); |
|
|
|
int idx = 0; |
|
if( ( idx = rxDate.indexIn( result, idx ) ) != -1 ) { |
|
result.replace( idx, rxDate.matchedLength(), sDate ); |
|
} |
|
|
|
idx = 0; |
|
while ( ( idx = rx.indexIn( result, idx ) ) != -1 ) { |
|
QString replacement = msg->headerByType( rx.cap(1).toLatin1() ) ? msg->headerByType( rx.cap(1).toLatin1() )->asUnicodeString() : ""; |
|
result.replace( idx, rx.matchedLength(), replacement ); |
|
idx += replacement.length(); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
void initHeader( const KMime::Message::Ptr &message, uint id ) |
|
{ |
|
applyIdentity( message, id ); |
|
message->to()->clear(); |
|
message->subject()->clear(); |
|
message->date()->setDateTime( KDateTime::currentLocalDateTime() ); |
|
|
|
// user agent, e.g. KMail/1.9.50 (Windows/5.0; KDE/3.97.1; i686; svn-762186; 2008-01-15) |
|
QStringList extraInfo; |
|
# if defined KMAIL_SVN_REVISION_STRING && defined KMAIL_SVN_LAST_CHANGE |
|
extraInfo << KMAIL_SVN_REVISION_STRING << KMAIL_SVN_LAST_CHANGE; |
|
#else |
|
#error forgot to include version-kmail.h |
|
# endif |
|
|
|
message->userAgent()->fromUnicodeString( KProtocolManager::userAgentForApplication( "KMail", KMAIL_VERSION, extraInfo ), "utf-8" ); |
|
|
|
// This will allow to change Content-Type: |
|
message->contentType()->setMimeType( "text/plain" ); |
|
} |
|
|
|
void applyIdentity( const KMime::Message::Ptr &message, uint id ) |
|
{ |
|
const KPIMIdentities::Identity & ident = |
|
kmkernel->identityManager()->identityForUoidOrDefault( id ); |
|
|
|
if(ident.fullEmailAddr().isEmpty()) |
|
message->from()->clear(); |
|
else |
|
message->from()->addAddress(ident.fullEmailAddr().toUtf8()); |
|
|
|
if(ident.replyToAddr().isEmpty()) |
|
message->replyTo()->clear(); |
|
else |
|
message->replyTo()->addAddress(ident.replyToAddr().toUtf8()); |
|
|
|
if(ident.bcc().isEmpty()) |
|
message->bcc()->clear(); |
|
else |
|
message->bcc()->addAddress(ident.bcc().toUtf8()); |
|
|
|
if (ident.organization().isEmpty()) |
|
message->removeHeader("Organization"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "Organization", message.get(), ident.organization(), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
|
|
if (ident.isDefault()) |
|
message->removeHeader("X-KMail-Identity"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Identity", message.get(), QString::number( ident.uoid() ), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
|
|
if (ident.transport().isEmpty()) |
|
message->removeHeader("X-KMail-Transport"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Transport", message.get(), ident.transport(), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
|
|
if (ident.fcc().isEmpty()) |
|
message->removeHeader("X-KMail-Fcc"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Fcc", message.get(), ident.fcc(), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
|
|
if (ident.drafts().isEmpty()) |
|
message->removeHeader("X-KMail-Drafts"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Drafts", message.get(), ident.drafts(), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
|
|
if (ident.templates().isEmpty()) |
|
message->removeHeader("X-KMail-Templates"); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Templates", message.get(), ident.templates(), "utf-8" ); |
|
message->setHeader( header ); |
|
} |
|
} |
|
|
|
MessageReply createReply( const Akonadi::Item &item, |
|
const KMime::Message::Ptr &origMsg, |
|
KMail::ReplyStrategy replyStrategy, |
|
const QString &selection /*.clear() */, |
|
bool noQuote /* = false */, |
|
bool allowDecryption /* = true */, |
|
bool selectionIsBody /* = false */, |
|
const QString &tmpl /* = QString() */ ) |
|
{ |
|
KMime::Message::Ptr msg( new KMime::Message ); |
|
QString str, mailingListStr, replyToStr, toStr; |
|
QStringList mailingListAddresses; |
|
QByteArray refStr, headerName; |
|
bool replyAll = true; |
|
|
|
initFromMessage( item, msg, origMsg); |
|
MailingList::name( origMsg, headerName, mailingListStr ); |
|
replyToStr = origMsg->replyTo()->asUnicodeString(); |
|
|
|
msg->contentType()->setCharset("utf-8"); |
|
// determine the mailing list posting address |
|
Akonadi::Collection parentCollection = item.parentCollection(); |
|
QSharedPointer<FolderCollection> fd; |
|
if ( parentCollection.isValid() ) { |
|
fd = FolderCollection::forCollection( parentCollection ); |
|
if ( fd->isMailingListEnabled() && !fd->mailingListPostAddress().isEmpty() ) { |
|
mailingListAddresses << fd->mailingListPostAddress(); |
|
} |
|
} |
|
|
|
if ( origMsg->headerByType("List-Post") && origMsg->headerByType("List-Post")->asUnicodeString().contains( "mailto:", Qt::CaseInsensitive ) ) { |
|
QString listPost = origMsg->headerByType("List-Post")->asUnicodeString(); |
|
QRegExp rx( "<mailto:([^@>]+)@([^>]+)>", Qt::CaseInsensitive ); |
|
if ( rx.indexIn( listPost, 0 ) != -1 ) // matched |
|
mailingListAddresses << rx.cap(1) + '@' + rx.cap(2); |
|
} |
|
|
|
switch( replyStrategy ) { |
|
case ReplySmart : { |
|
if ( origMsg->headerByType( "Mail-Followup-To" ) ) { |
|
toStr = origMsg->headerByType( "Mail-Followup-To" )->asUnicodeString(); |
|
} |
|
else if ( !replyToStr.isEmpty() ) { |
|
toStr = replyToStr; |
|
// use the ReplyAll template only when it's a reply to a mailing list |
|
if ( mailingListAddresses.isEmpty() ) |
|
replyAll = false; |
|
} |
|
else if ( !mailingListAddresses.isEmpty() ) { |
|
toStr = mailingListAddresses[0]; |
|
} |
|
else { |
|
|
|
// doesn't seem to be a mailing list, reply to From: address |
|
toStr = origMsg->from()->asUnicodeString(); |
|
|
|
if( kmkernel->identityManager()->thatIsMe( KPIMUtils::extractEmailAddress( toStr ) ) ) { |
|
// sender seems to be one of our own identities, so we assume that this |
|
// is a reply to a "sent" mail where the users wants to add additional |
|
// information for the recipient. |
|
toStr = origMsg->to()->asUnicodeString(); |
|
} |
|
|
|
replyAll = false; |
|
} |
|
// strip all my addresses from the list of recipients |
|
QStringList recipients = KPIMUtils::splitAddressList( toStr ); |
|
toStr = StringUtil::stripMyAddressesFromAddressList( recipients ).join(", "); |
|
// ... unless the list contains only my addresses (reply to self) |
|
if ( toStr.isEmpty() && !recipients.isEmpty() ) |
|
toStr = recipients[0]; |
|
|
|
break; |
|
} |
|
case ReplyList : { |
|
if ( origMsg->headerByType( "Mail-Followup-To" ) ) { |
|
toStr = origMsg->headerByType( "Mail-Followup-To" )->asUnicodeString(); |
|
} |
|
else if ( !mailingListAddresses.isEmpty() ) { |
|
toStr = mailingListAddresses[0]; |
|
} |
|
else if ( !replyToStr.isEmpty() ) { |
|
// assume a Reply-To header mangling mailing list |
|
toStr = replyToStr; |
|
} |
|
// strip all my addresses from the list of recipients |
|
QStringList recipients = KPIMUtils::splitAddressList( toStr ); |
|
toStr = StringUtil::stripMyAddressesFromAddressList( recipients ).join(", "); |
|
|
|
break; |
|
} |
|
case ReplyAll : { |
|
QStringList recipients; |
|
QStringList ccRecipients; |
|
|
|
// add addresses from the Reply-To header to the list of recipients |
|
if( !replyToStr.isEmpty() ) { |
|
recipients += KPIMUtils::splitAddressList( replyToStr ); |
|
// strip all possible mailing list addresses from the list of Reply-To |
|
// addresses |
|
for ( QStringList::const_iterator it = mailingListAddresses.constBegin(); |
|
it != mailingListAddresses.constEnd(); |
|
++it ) { |
|
recipients = MessageViewer::StringUtil::stripAddressFromAddressList( *it, recipients ); |
|
} |
|
} |
|
|
|
if ( !mailingListAddresses.isEmpty() ) { |
|
// this is a mailing list message |
|
if ( recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty() ) { |
|
// The sender didn't set a Reply-to address, so we add the From |
|
// address to the list of CC recipients. |
|
ccRecipients += origMsg->from()->asUnicodeString(); |
|
kDebug() << "Added" << origMsg->from()->asUnicodeString() <<"to the list of CC recipients"; |
|
} |
|
// if it is a mailing list, add the posting address |
|
recipients.prepend( mailingListAddresses[0] ); |
|
} |
|
else { |
|
// this is a normal message |
|
if ( recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty() ) { |
|
// in case of replying to a normal message only then add the From |
|
// address to the list of recipients if there was no Reply-to address |
|
recipients += origMsg->from()->asUnicodeString(); |
|
kDebug() << "Added" << origMsg->from()->asUnicodeString() <<"to the list of recipients"; |
|
} |
|
} |
|
|
|
// strip all my addresses from the list of recipients |
|
toStr = StringUtil::stripMyAddressesFromAddressList( recipients ).join(", "); |
|
|
|
// merge To header and CC header into a list of CC recipients |
|
if( !origMsg->cc()->asUnicodeString().isEmpty() || !origMsg->to()->asUnicodeString().isEmpty() ) { |
|
QStringList list; |
|
if (!origMsg->to()->asUnicodeString().isEmpty()) |
|
list += KPIMUtils::splitAddressList(origMsg->to()->asUnicodeString()); |
|
if (!origMsg->cc()->asUnicodeString().isEmpty()) |
|
list += KPIMUtils::splitAddressList(origMsg->cc()->asUnicodeString()); |
|
for( QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) { |
|
if( !MessageViewer::StringUtil::addressIsInAddressList( *it, recipients ) |
|
&& !MessageViewer::StringUtil::addressIsInAddressList( *it, ccRecipients ) ) { |
|
ccRecipients += *it; |
|
kDebug() << "Added" << *it <<"to the list of CC recipients"; |
|
} |
|
} |
|
} |
|
|
|
if ( !ccRecipients.isEmpty() ) { |
|
// strip all my addresses from the list of CC recipients |
|
ccRecipients = StringUtil::stripMyAddressesFromAddressList( ccRecipients ); |
|
|
|
// in case of a reply to self toStr might be empty. if that's the case |
|
// then propagate a cc recipient to To: (if there is any). |
|
if ( toStr.isEmpty() && !ccRecipients.isEmpty() ) { |
|
toStr = ccRecipients[0]; |
|
ccRecipients.pop_front(); |
|
} |
|
|
|
msg->cc()->fromUnicodeString( ccRecipients.join(", "), "utf-8" ); |
|
} |
|
|
|
if ( toStr.isEmpty() && !recipients.isEmpty() ) { |
|
// reply to self without other recipients |
|
toStr = recipients[0]; |
|
} |
|
break; |
|
} |
|
case ReplyAuthor : { |
|
if ( !replyToStr.isEmpty() ) { |
|
QStringList recipients = KPIMUtils::splitAddressList( replyToStr ); |
|
// strip the mailing list post address from the list of Reply-To |
|
// addresses since we want to reply in private |
|
for ( QStringList::const_iterator it = mailingListAddresses.constBegin(); |
|
it != mailingListAddresses.constEnd(); |
|
++it ) { |
|
recipients = MessageViewer::StringUtil::stripAddressFromAddressList( *it, recipients ); |
|
} |
|
if ( !recipients.isEmpty() ) { |
|
toStr = recipients.join(", "); |
|
} |
|
else { |
|
// there was only the mailing list post address in the Reply-To header, |
|
// so use the From address instead |
|
toStr = origMsg->from()->asUnicodeString(); |
|
} |
|
} |
|
else if ( ! origMsg->from()->asUnicodeString().isEmpty() ) { |
|
toStr = origMsg->from()->asUnicodeString(); |
|
} |
|
replyAll = false; |
|
break; |
|
} |
|
case ReplyNone : { |
|
// the addressees will be set by the caller |
|
} |
|
} |
|
|
|
msg->to()->fromUnicodeString(toStr, "utf-8"); |
|
|
|
refStr = getRefStr( origMsg ); |
|
if (!refStr.isEmpty()) |
|
msg->references()->fromUnicodeString (refStr, "utf-8" ); |
|
//In-Reply-To = original msg-id |
|
msg->inReplyTo()->fromUnicodeString( origMsg->messageID()->asUnicodeString(), "utf-8" ); |
|
|
|
msg->subject()->fromUnicodeString( replySubject( origMsg ), "utf-8" ); |
|
|
|
// If the reply shouldn't be blank, apply the template to the message |
|
if ( !noQuote ) { |
|
TemplateParser parser( msg, (replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply), |
|
selection,kmkernel->smartQuote(), allowDecryption, selectionIsBody ); |
|
if ( !tmpl.isEmpty() ) |
|
parser.process( tmpl, origMsg ); |
|
else |
|
parser.process( origMsg ); |
|
} |
|
link( msg, item, KPIM::MessageStatus::statusReplied() ); |
|
if ( parentCollection.isValid() && fd->putRepliesInSameFolder() ) { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Fcc", msg.get(), QString::number( parentCollection.id() ), "utf-8" ); |
|
msg->setHeader( header ); |
|
} |
|
#if 0 |
|
// replies to an encrypted message should be encrypted as well |
|
if ( encryptionState() == KMMsgPartiallyEncrypted || |
|
encryptionState() == KMMsgFullyEncrypted ) { |
|
msg->setEncryptionState( KMMsgFullyEncrypted ); |
|
} |
|
|
|
#else //TODO port to akonadi |
|
kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO; |
|
#endif |
|
|
|
MessageReply reply; |
|
reply.msg = msg; |
|
reply.replyAll = replyAll; |
|
return reply; |
|
} |
|
|
|
void link( const KMime::Message::Ptr &msg, const Akonadi::Item & item,const KPIM::MessageStatus& aStatus ) |
|
{ |
|
Q_ASSERT( aStatus.isReplied() || aStatus.isForwarded() || aStatus.isDeleted() ); |
|
|
|
QString message = msg->headerByType( "X-KMail-Link-Message" ) ? msg->headerByType( "X-KMail-Link-Message" )->asUnicodeString() : QString(); |
|
if ( !message.isEmpty() ) |
|
message += ','; |
|
QString type = msg->headerByType( "X-KMail-Link-Type" ) ? msg->headerByType( "X-KMail-Link-Type" )->asUnicodeString(): QString(); |
|
if ( !type.isEmpty() ) |
|
type += ','; |
|
|
|
message += QString::number( item.id() ); |
|
if ( aStatus.isReplied() ) |
|
type += "reply"; |
|
else if ( aStatus.isForwarded() ) |
|
type += "forward"; |
|
else if ( aStatus.isDeleted() ) |
|
type += "deleted"; |
|
|
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Link-Message", msg.get(), message, "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
header = new KMime::Headers::Generic( "X-KMail-Link-Type", msg.get(), type, "utf-8" ); |
|
msg->setHeader( header ); |
|
} |
|
|
|
|
|
|
|
KMime::Message::Ptr createForward( const Akonadi::Item &item, const KMime::Message::Ptr &origMsg, const QString &tmpl /* = QString() */ ) |
|
{ |
|
KMime::Message::Ptr msg( new KMime::Message ); |
|
|
|
// This is a non-multipart, non-text mail (e.g. text/calendar). Construct |
|
// a multipart/mixed mail and add the original body as an attachment. |
|
if ( !origMsg->contentType()->isMultipart() && |
|
( !origMsg->contentType()->isText() || |
|
( origMsg->contentType()->isText() && origMsg->contentType()->subType() != "html" |
|
&& origMsg->contentType()->subType() != "plain" ) ) ) { |
|
initFromMessage( item, msg, origMsg ); |
|
msg->removeHeader("Content-Type"); |
|
msg->removeHeader("Content-Transfer-Encoding"); |
|
|
|
msg->contentType()->setMimeType( "multipart/mixed" ); |
|
|
|
//TODO: Andras: somebody should check if this is correct. :) |
|
// empty text part |
|
KMime::Content *msgPart = new KMime::Content; |
|
msgPart->contentType()->setMimeType("text/plain"); |
|
msg->addContent( msgPart ); |
|
|
|
// the old contents of the mail |
|
KMime::Content *secondPart = new KMime::Content; |
|
secondPart->contentType()->setMimeType( origMsg->contentType()->mimeType() ); |
|
secondPart->setBody( origMsg->body() ); |
|
// use the headers of the original mail |
|
secondPart->setHead( origMsg->head() ); |
|
msg->addContent( secondPart ); |
|
msg->assemble(); |
|
//TODO Port it msg->cleanupHeader(); |
|
} |
|
|
|
// Normal message (multipart or text/plain|html) |
|
// Just copy the message, the template parser will do the hard work of |
|
// replacing the body text in TemplateParser::addProcessedBodyToMessage() |
|
else { |
|
//TODO Check if this is ok |
|
msg->setHead( origMsg->head() ); |
|
msg->setBody( origMsg->body() ); |
|
QString oldContentType = msg->contentType()->asUnicodeString(); |
|
initFromMessage(item, msg, origMsg ); |
|
|
|
// restore the content type, initFromMessage() sets the contents type to |
|
// text/plain, via initHeader(), for unclear reasons |
|
msg->contentType()->fromUnicodeString( oldContentType, "utf-8" ); |
|
msg->assemble(); |
|
} |
|
|
|
msg->subject()->fromUnicodeString( forwardSubject( origMsg ), "utf-8" ); |
|
|
|
TemplateParser parser( msg, TemplateParser::Forward, |
|
QString(), |
|
false, false, false); |
|
if ( !tmpl.isEmpty() ) |
|
parser.process( tmpl, origMsg ); |
|
else |
|
parser.process( origMsg ); |
|
|
|
link( msg, item, KPIM::MessageStatus::statusForwarded() ); |
|
return msg; |
|
} |
|
|
|
|
|
KMime::Message::Ptr createResend( const Akonadi::Item & item, const KMime::Message::Ptr &origMsg ) |
|
{ |
|
KMime::Message::Ptr msg( new KMime::Message ); |
|
initFromMessage( item, msg, origMsg); |
|
msg->setContent( origMsg->encodedContent() ); |
|
msg->removeHeader( "Message-Id" ); |
|
uint originalIdentity = identityUoid( item, origMsg); |
|
|
|
// Remove all unnecessary headers |
|
msg->removeHeader("Bcc"); |
|
msg->removeHeader( "Cc" ); |
|
msg->removeHeader( "To" ); |
|
msg->removeHeader( "Subject" ); |
|
// Set the identity from above |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Identity", msg.get(), QString::number( originalIdentity ), "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
// Restore the original bcc field as this is overwritten in applyIdentity |
|
msg->bcc( origMsg->bcc() ); |
|
return msg; |
|
} |
|
|
|
KMime::Message::Ptr createRedirect( const Akonadi::Item & item, const QString &toStr ) |
|
{ |
|
const KMime::Message::Ptr origMsg = KMail::Util::message( item ); |
|
if ( !origMsg ) |
|
return KMime::Message::Ptr(); |
|
|
|
// copy the message 1:1 |
|
KMime::Message::Ptr msg( new KMime::Message ); |
|
msg->setContent( origMsg->encodedContent() ); |
|
|
|
uint id = 0; |
|
QString strId = msg->headerByType( "X-KMail-Identity" ) ? msg->headerByType( "X-KMail-Identity" )->asUnicodeString().trimmed() : ""; |
|
if ( !strId.isEmpty()) |
|
id = strId.toUInt(); |
|
const KPIMIdentities::Identity & ident = |
|
kmkernel->identityManager()->identityForUoidOrDefault( id ); |
|
|
|
// X-KMail-Redirect-From: content |
|
QString strByWayOf = QString("%1 (by way of %2 <%3>)") |
|
.arg( origMsg->from()->asUnicodeString() ) |
|
.arg( ident.fullName() ) |
|
.arg( ident.emailAddr() ); |
|
|
|
// Resent-From: content |
|
QString strFrom = QString("%1 <%2>") |
|
.arg( ident.fullName() ) |
|
.arg( ident.emailAddr() ); |
|
|
|
// format the current date to be used in Resent-Date: |
|
QString origDate = msg->date()->asUnicodeString(); |
|
msg->date()->setDateTime( KDateTime::currentLocalDateTime() ); |
|
QString newDate = msg->date()->asUnicodeString(); |
|
|
|
// prepend Resent-*: headers (c.f. RFC2822 3.6.6) |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "Resent-Message-ID", msg.get(), MessageViewer::StringUtil::generateMessageId( msg->sender()->asUnicodeString() ), "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
header = new KMime::Headers::Generic( "Resent-Date", msg.get(), newDate, "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
header = new KMime::Headers::Generic( "Resent-To", msg.get(), toStr, "utf-8" ); |
|
msg->setHeader( header ); |
|
header = new KMime::Headers::Generic( "Resent-To", msg.get(), strFrom, "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
header = new KMime::Headers::Generic( "X-KMail-Redirect-From", msg.get(), strByWayOf, "utf-8" ); |
|
msg->setHeader( header ); |
|
header = new KMime::Headers::Generic( "X-KMail-Recipients", msg.get(), toStr, "utf-8" ); |
|
msg->setHeader( header ); |
|
|
|
link( msg, item, KPIM::MessageStatus::statusForwarded() ); |
|
return msg; |
|
} |
|
|
|
|
|
void initFromMessage(const Akonadi::Item & item, const KMime::Message::Ptr &msg, const KMime::Message::Ptr &origMsg, bool idHeaders) |
|
{ |
|
uint id = identityUoid( item, origMsg ); |
|
|
|
if ( idHeaders ) |
|
initHeader( msg, id ); |
|
else { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Identity", msg.get(), QString::number(id), "utf-8" ); |
|
msg->setHeader( header ); |
|
} |
|
if (origMsg->headerByType("X-KMail-Transport")) { |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Identity", msg.get(), origMsg->headerByType("X-KMail-Transport")->asUnicodeString(), "utf-8" ); |
|
msg->setHeader( header ); |
|
} |
|
} |
|
|
|
KMime::Types::AddrSpecList extractAddrSpecs( const KMime::Message::Ptr &msg, const QByteArray & header ) |
|
{ |
|
KMime::Types::AddrSpecList result; |
|
if ( !msg->headerByType( header ) ) |
|
return result; |
|
|
|
KMime::Types::AddressList al = MessageViewer::StringUtil::splitAddrField( msg->headerByType( header )->asUnicodeString().toUtf8() ); |
|
for ( KMime::Types::AddressList::const_iterator ait = al.constBegin() ; ait != al.constEnd() ; ++ait ) |
|
for ( KMime::Types::MailboxList::const_iterator mit = (*ait).mailboxList.constBegin() ; mit != (*ait).mailboxList.constEnd() ; ++mit ) |
|
result.push_back( (*mit).addrSpec() ); |
|
return result; |
|
} |
|
|
|
QString cleanSubject( const KMime::Message::Ptr &msg ) |
|
{ |
|
return cleanSubject( msg, sReplySubjPrefixes + sForwardSubjPrefixes, |
|
true, QString() ).trimmed(); |
|
} |
|
|
|
QString cleanSubject( const KMime::Message::Ptr &msg, const QStringList & prefixRegExps, |
|
bool replace, const QString & newPrefix ) |
|
{ |
|
return replacePrefixes( msg->subject()->asUnicodeString(), prefixRegExps, replace, |
|
newPrefix ); |
|
} |
|
|
|
QString forwardSubject( const KMime::Message::Ptr &msg ) |
|
{ |
|
return cleanSubject( msg, sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:" ); |
|
} |
|
|
|
QString replySubject( const KMime::Message::Ptr &msg ) |
|
{ |
|
return cleanSubject( msg, sReplySubjPrefixes, sReplaceSubjPrefix, "Re:" ); |
|
} |
|
|
|
QString replacePrefixes( const QString& str, const QStringList &prefixRegExps, |
|
bool replace, const QString &newPrefix ) |
|
{ |
|
bool recognized = false; |
|
// construct a big regexp that |
|
// 1. is anchored to the beginning of str (sans whitespace) |
|
// 2. matches at least one of the part regexps in prefixRegExps |
|
QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*") |
|
.arg( prefixRegExps.join(")|(?:") ); |
|
QRegExp rx( bigRegExp, Qt::CaseInsensitive ); |
|
if ( !rx.isValid() ) { |
|
kWarning() << "bigRegExp = \"" |
|
<< bigRegExp << "\"\n" |
|
<< "prefix regexp is invalid!"; |
|
// try good ole Re/Fwd: |
|
recognized = str.startsWith( newPrefix ); |
|
} else { // valid rx |
|
QString tmp = str; |
|
if ( rx.indexIn( tmp ) == 0 ) { |
|
recognized = true; |
|
if ( replace ) |
|
return tmp.replace( 0, rx.matchedLength(), newPrefix + ' ' ); |
|
} |
|
} |
|
if ( !recognized ) |
|
return newPrefix + ' ' + str; |
|
else |
|
return str; |
|
} |
|
|
|
uint identityUoid( const Akonadi::Item & item , const KMime::Message::Ptr &msg ) |
|
{ |
|
QString idString; |
|
if ( msg->headerByType("X-KMail-Identity") ) |
|
idString = msg->headerByType("X-KMail-Identity")->asUnicodeString().trimmed(); |
|
bool ok = false; |
|
int id = idString.toUInt( &ok ); |
|
|
|
if ( !ok || id == 0 ) |
|
id = kmkernel->identityManager()->identityForAddress( msg->to()->asUnicodeString() + ", " + msg->cc()->asUnicodeString() ).uoid(); |
|
|
|
if ( id == 0 && item.isValid() ) { |
|
if ( item.parentCollection().isValid() ) { |
|
QSharedPointer<FolderCollection> fd = FolderCollection::forCollection( item.parentCollection() ); |
|
id = fd->identity(); |
|
} |
|
} |
|
return id; |
|
} |
|
|
|
KMime::Message::Ptr createDeliveryReceipt( const Akonadi::Item & item, const KMime::Message::Ptr &msg ) |
|
{ |
|
QString str, receiptTo; |
|
KMime::Message::Ptr receipt; |
|
|
|
receiptTo = msg->headerByType("Disposition-Notification-To") ? msg->headerByType("Disposition-Notification-To")->asUnicodeString() : ""; |
|
if ( receiptTo.trimmed().isEmpty() ) |
|
return KMime::Message::Ptr(); |
|
receiptTo.remove( '\n' ); |
|
|
|
receipt = KMime::Message::Ptr( new KMime::Message ); |
|
initFromMessage( item, receipt, msg ); |
|
receipt->to()->fromUnicodeString( receiptTo, "utf-8" ); |
|
receipt->subject()->fromUnicodeString( i18n("Receipt: ") + msg->subject()->asUnicodeString(), "utf-8"); |
|
|
|
str = "Your message was successfully delivered."; |
|
str += "\n\n---------- Message header follows ----------\n"; |
|
str += msg->head(); |
|
str += "--------------------------------------------\n"; |
|
// Conversion to toLatin1 is correct here as Mail headers should contain |
|
// ascii only |
|
receipt->setBody(str.toLatin1()); |
|
setAutomaticFields( receipt ); |
|
receipt->assemble(); |
|
|
|
return receipt; |
|
} |
|
|
|
KMime::Message::Ptr createMDN( const Akonadi::Item & item, |
|
const KMime::Message::Ptr &msg, |
|
KMime::MDN::ActionMode a, |
|
KMime::MDN::DispositionType d, |
|
bool allowGUI, |
|
QList<KMime::MDN::DispositionModifier> m ) |
|
{ |
|
// RFC 2298: At most one MDN may be issued on behalf of each |
|
// particular recipient by their user agent. That is, once an MDN |
|
// has been issued on behalf of a recipient, no further MDNs may be |
|
// issued on behalf of that recipient, even if another disposition |
|
// is performed on the message. |
|
//#define MDN_DEBUG 1 |
|
#ifndef MDN_DEBUG |
|
if ( MessageInfo::instance()->mdnSentState( msg.get() ) != KMMsgMDNStateUnknown && |
|
MessageInfo::instance()->mdnSentState( msg.get() ) != KMMsgMDNNone ) |
|
return KMime::Message::Ptr(); |
|
#else |
|
char st[2]; st[0] = (char)MessageInfo::instance()->mdnSentState( msg ); st[1] = 0; |
|
kDebug() << "mdnSentState() == '" << st <<"'"; |
|
#endif |
|
|
|
// RFC 2298: An MDN MUST NOT be generated in response to an MDN. |
|
if ( MessageViewer::ObjectTreeParser::findType( msg.get(), "message", |
|
"disposition-notification", true, true ) ) { |
|
MessageInfo::instance()->setMDNSentState( msg.get(), KMMsgMDNIgnore ); |
|
return KMime::Message::Ptr(); |
|
} |
|
|
|
// extract where to send to: |
|
QString receiptTo = msg->headerByType("Disposition-Notification-To") ? msg->headerByType("Disposition-Notification-To")->asUnicodeString() : ""; |
|
if ( receiptTo.trimmed().isEmpty() ) return KMime::Message::Ptr(); |
|
receiptTo.remove( '\n' ); |
|
|
|
|
|
KMime::MDN::SendingMode s = KMime::MDN::SentAutomatically; // set to manual if asked user |
|
QString special; // fill in case of error, warning or failure |
|
KConfigGroup mdnConfig( KMKernel::config(), "MDN" ); |
|
|
|
// default: |
|
int mode = mdnConfig.readEntry( "default-policy", 0 ); |
|
if ( !mode || mode < 0 || mode > 3 ) { |
|
// early out for ignore: |
|
MessageInfo::instance()->setMDNSentState( msg.get(), KMMsgMDNIgnore ); |
|
return KMime::Message::Ptr(); |
|
} |
|
if ( mode == 1 /* ask */ && !allowGUI ) |
|
return KMime::Message::Ptr(); // don't setMDNSentState here! |
|
|
|
// RFC 2298: An importance of "required" indicates that |
|
// interpretation of the parameter is necessary for proper |
|
// generation of an MDN in response to this request. If a UA does |
|
// not understand the meaning of the parameter, it MUST NOT generate |
|
// an MDN with any disposition type other than "failed" in response |
|
// to the request. |
|
QString notificationOptions = msg->headerByType("Disposition-Notification-Options") ? msg->headerByType("Disposition-Notification-Options")->asUnicodeString() : ""; |
|
if ( notificationOptions.contains( "required", Qt::CaseSensitive ) ) { |
|
// ### hacky; should parse... |
|
// There is a required option that we don't understand. We need to |
|
// ask the user what we should do: |
|
if ( !allowGUI ) return KMime::Message::Ptr(); // don't setMDNSentState here! |
|
mode = requestAdviceOnMDN( "mdnUnknownOption" ); |
|
s = KMime::MDN::SentManually; |
|
|
|
special = i18n("Header \"Disposition-Notification-Options\" contained " |
|
"required, but unknown parameter"); |
|
d = KMime::MDN::Failed; |
|
m.clear(); // clear modifiers |
|
} |
|
|
|
// RFC 2298: [ Confirmation from the user SHOULD be obtained (or no |
|
// MDN sent) ] if there is more than one distinct address in the |
|
// Disposition-Notification-To header. |
|
kDebug() << "KPIMUtils::splitAddressList(receiptTo):" // krazy:exclude=kdebug |
|
<< KPIMUtils::splitAddressList(receiptTo).join("\n"); |
|
if ( KPIMUtils::splitAddressList(receiptTo).count() > 1 ) { |
|
if ( !allowGUI ) return KMime::Message::Ptr(); // don't setMDNSentState here! |
|
mode = requestAdviceOnMDN( "mdnMultipleAddressesInReceiptTo" ); |
|
s = KMime::MDN::SentManually; |
|
} |
|
|
|
// RFC 2298: MDNs SHOULD NOT be sent automatically if the address in |
|
// the Disposition-Notification-To header differs from the address |
|
// in the Return-Path header. [...] Confirmation from the user |
|
// SHOULD be obtained (or no MDN sent) if there is no Return-Path |
|
// header in the message [...] |
|
KMime::Types::AddrSpecList returnPathList = extractAddrSpecs( msg, "Return-Path"); |
|
QString returnPath = returnPathList.isEmpty() ? QString() |
|
: returnPathList.front().localPart + '@' + returnPathList.front().domain ; |
|
kDebug() << "clean return path:" << returnPath; |
|
if ( returnPath.isEmpty() || !receiptTo.contains( returnPath, Qt::CaseSensitive ) ) { |
|
if ( !allowGUI ) return KMime::Message::Ptr(); // don't setMDNSentState here! |
|
mode = requestAdviceOnMDN( returnPath.isEmpty() ? |
|
"mdnReturnPathEmpty" : |
|
"mdnReturnPathNotInReceiptTo" ); |
|
s = KMime::MDN::SentManually; |
|
} |
|
|
|
if ( true ) { |
|
if ( mode == 1 ) { // ask |
|
assert( allowGUI ); |
|
mode = requestAdviceOnMDN( "mdnNormalAsk" ); |
|
s = KMime::MDN::SentManually; // asked user |
|
} |
|
|
|
switch ( mode ) { |
|
case 0: // ignore: |
|
MessageInfo::instance()->setMDNSentState( msg.get(), KMMsgMDNIgnore ); |
|
return KMime::Message::Ptr(); |
|
default: |
|
case 1: |
|
kFatal() << "The \"ask\" mode should never appear here!"; |
|
break; |
|
case 2: // deny |
|
d = KMime::MDN::Denied; |
|
m.clear(); |
|
break; |
|
case 3: |
|
break; |
|
} |
|
} |
|
|
|
|
|
// extract where to send from: |
|
QString finalRecipient = kmkernel->identityManager() |
|
->identityForUoidOrDefault( identityUoid( item, msg ) ).fullEmailAddr(); |
|
|
|
// |
|
// Generate message: |
|
// |
|
|
|
KMime::Message::Ptr receipt( new KMime::Message() ); |
|
initFromMessage( item, receipt, msg); |
|
receipt->contentType()->from7BitString( "multipart/report" ); |
|
receipt->removeHeader("Content-Transfer-Encoding"); |
|
// Modify the ContentType directly (replaces setAutomaticFields(true)) |
|
receipt->contentType()->setParameter( "report-type", "disposition-notification" ); |
|
|
|
|
|
QString description = replaceHeadersInString( msg, KMime::MDN::descriptionFor( d, m ) ); |
|
|
|
// text/plain part: |
|
KMime::Content* firstMsgPart = new KMime::Content( msg.get() ); |
|
firstMsgPart->contentType()->setMimeType( "text/plain" ); |
|
firstMsgPart->setBody( description.toUtf8() ); |
|
receipt->addContent( firstMsgPart ); |
|
|
|
// message/disposition-notification part: |
|
KMime::Content* secondMsgPart = new KMime::Content( msg.get() ); |
|
secondMsgPart->contentType()->setMimeType( "message/disposition-notification" ); |
|
//secondMsgPart.setCharset( "us-ascii" ); |
|
//secondMsgPart.setCteStr( "7bit" ); |
|
secondMsgPart->setBody( KMime::MDN::dispositionNotificationBodyContent( |
|
finalRecipient, |
|
msg->headerByType("Original-Recipient") ? msg->headerByType("Original-Recipient")->as7BitString() : "", |
|
msg->messageID()->as7BitString(), /* Message-ID */ |
|
d, a, s, m, special ) ); |
|
receipt->addContent( secondMsgPart ); |
|
|
|
// message/rfc822 or text/rfc822-headers body part: |
|
int num = mdnConfig.readEntry( "quote-message", 0 ); |
|
if ( num < 0 || num > 2 ) num = 0; |
|
/* 0=> Nothing, 1=>Full Message, 2=>HeadersOnly*/ |
|
|
|
KMime::Content* thirdMsgPart = new KMime::Content( msg.get() ); |
|
switch ( num ) { |
|
case 1: |
|
thirdMsgPart->contentType()->setMimeType( "message/rfc822" ); |
|
thirdMsgPart->setBody( asSendableString( msg ) ); |
|
receipt->addContent( thirdMsgPart ); |
|
break; |
|
case 2: |
|
thirdMsgPart->contentType()->setMimeType( "text/rfc822-headers" ); |
|
thirdMsgPart->setBody( headerAsSendableString( msg ) ); |
|
receipt->addContent( thirdMsgPart ); |
|
break; |
|
case 0: |
|
default: |
|
break; |
|
}; |
|
|
|
receipt->to()->fromUnicodeString( receiptTo, "utf-8" ); |
|
receipt->subject()->from7BitString( "Message Disposition Notification" ); |
|
KMime::Headers::Generic *header = new KMime::Headers::Generic( "In-Reply-To", receipt.get(), msg->messageID()->as7BitString() ); |
|
receipt->setHeader( header ); |
|
|
|
receipt->references()->from7BitString( getRefStr( msg ) ); |
|
|
|
receipt->assemble(); |
|
|
|
kDebug() << "final message:" + receipt->body();//TODO port asString(); |
|
|
|
// |
|
// Set "MDN sent" status: |
|
// |
|
KMMsgMDNSentState state = KMMsgMDNStateUnknown; |
|
switch ( d ) { |
|
case KMime::MDN::Displayed: state = KMMsgMDNDisplayed; break; |
|
case KMime::MDN::Deleted: state = KMMsgMDNDeleted; break; |
|
case KMime::MDN::Dispatched: state = KMMsgMDNDispatched; break; |
|
case KMime::MDN::Processed: state = KMMsgMDNProcessed; break; |
|
case KMime::MDN::Denied: state = KMMsgMDNDenied; break; |
|
case KMime::MDN::Failed: state = KMMsgMDNFailed; break; |
|
}; |
|
MessageInfo::instance()->setMDNSentState( msg.get(), state ); |
|
|
|
receipt->assemble(); |
|
return receipt; |
|
} |
|
|
|
|
|
void setAutomaticFields(const KMime::Message::Ptr &msg, bool aIsMulti) |
|
{ |
|
//TODO review and port header.MimeVersion().FromString("1.0"); |
|
|
|
if (aIsMulti || msg->contents().size() > 1) |
|
{ |
|
// Set the type to 'Multipart' and the subtype to 'Mixed' |
|
msg->contentType()->setMimeType( "multipart/mixed" ); |
|
// Create a random printable string and set it as the boundary parameter |
|
//TODO review and port contentType.CreateBoundary(0); |
|
} |
|
} |
|
|
|
QByteArray asSendableString( const KMime::Message::Ptr &msg ) |
|
{ |
|
KMime::Message message; |
|
message.setContent( msg->encodedContent() ); |
|
removePrivateHeaderFields( KMime::Message::Ptr( &message ) ); |
|
message.removeHeader("Bcc"); |
|
return message.encodedContent(); |
|
} |
|
|
|
QByteArray headerAsSendableString( const KMime::Message::Ptr &msg ) |
|
{ |
|
KMime::Message message; |
|
message.setContent( msg->encodedContent() ); |
|
removePrivateHeaderFields( KMime::Message::Ptr( &message ) ); |
|
message.removeHeader("Bcc"); |
|
return message.head(); |
|
} |
|
|
|
void removePrivateHeaderFields( const KMime::Message::Ptr &msg ) { |
|
msg->removeHeader("Status"); |
|
msg->removeHeader("X-Status"); |
|
msg->removeHeader("X-KMail-EncryptionState"); |
|
msg->removeHeader("X-KMail-SignatureState"); |
|
msg->removeHeader("X-KMail-MDN-Sent"); |
|
msg->removeHeader("X-KMail-Transport"); |
|
msg->removeHeader("X-KMail-Identity"); |
|
msg->removeHeader("X-KMail-Fcc"); |
|
msg->removeHeader("X-KMail-Redirect-From"); |
|
msg->removeHeader("X-KMail-Link-Message"); |
|
msg->removeHeader("X-KMail-Link-Type"); |
|
msg->removeHeader("X-KMail-QuotePrefix"); |
|
msg->removeHeader("X-KMail-CursorPos"); |
|
msg->removeHeader( "X-KMail-Templates" ); |
|
msg->removeHeader( "X-KMail-Drafts" ); |
|
msg->removeHeader( "X-KMail-Tag" ); |
|
} |
|
|
|
QByteArray getRefStr( const KMime::Message::Ptr &msg ) |
|
{ |
|
QByteArray firstRef, lastRef, refStr, retRefStr; |
|
int i, j; |
|
|
|
refStr = msg->headerByType("References") ? msg->headerByType("References")->as7BitString().trimmed() : ""; |
|
|
|
if (refStr.isEmpty()) |
|
return msg->messageID()->as7BitString(); |
|
|
|
i = refStr.indexOf('<'); |
|
j = refStr.indexOf('>'); |
|
firstRef = refStr.mid(i, j-i+1); |
|
if (!firstRef.isEmpty()) |
|
retRefStr = firstRef + ' '; |
|
|
|
i = refStr.lastIndexOf('<'); |
|
j = refStr.lastIndexOf('>'); |
|
|
|
lastRef = refStr.mid(i, j-i+1); |
|
if (!lastRef.isEmpty() && lastRef != firstRef) |
|
retRefStr += lastRef + ' '; |
|
|
|
retRefStr += msg->messageID()->as7BitString(); |
|
return retRefStr; |
|
} |
|
|
|
|
|
QList<Nepomuk::Tag> tagList(const Akonadi::Item &msg) |
|
{ |
|
const Nepomuk::Resource res( msg.url() ); |
|
return res.tags(); |
|
} |
|
|
|
void setTagList( const Akonadi::Item& msg, const QList<Nepomuk::Tag> &tags ) |
|
{ |
|
Nepomuk::Resource res( msg.url() ); |
|
res.setTags( tags ); |
|
} |
|
|
|
QString msgId( const KMime::Message::Ptr &msg ) |
|
{ |
|
if ( !msg->headerByType("Message-Id") ) |
|
return QString(); |
|
QString msgId = msg->headerByType("Message-Id")->asUnicodeString(); |
|
|
|
// search the end of the message id |
|
const int rightAngle = msgId.indexOf( '>' ); |
|
if (rightAngle != -1) |
|
msgId.truncate( rightAngle + 1 ); |
|
// now search the start of the message id |
|
const int leftAngle = msgId.lastIndexOf( '<' ); |
|
if (leftAngle != -1) |
|
msgId = msgId.mid( leftAngle ); |
|
return msgId; |
|
} |
|
|
|
QString ccStrip( const KMime::Message::Ptr &msg ) |
|
{ |
|
return MessageViewer::StringUtil::stripEmailAddr( msg->cc()->asUnicodeString() ); |
|
} |
|
|
|
QString toStrip( const KMime::Message::Ptr &msg ) |
|
{ |
|
return MessageViewer::StringUtil::stripEmailAddr( msg->to()->asUnicodeString() ); |
|
} |
|
|
|
QString fromStrip( const KMime::Message::Ptr &msg ) |
|
{ |
|
return MessageViewer::StringUtil::stripEmailAddr( msg->from()->asUnicodeString() ); |
|
} |
|
|
|
|
|
QString stripOffPrefixes( const QString& str ) |
|
{ |
|
return replacePrefixes( str, sReplySubjPrefixes + sForwardSubjPrefixes, |
|
true, QString() ).trimmed(); |
|
} |
|
|
|
QString skipKeyword( const QString& aStr, QChar sepChar, |
|
bool* hasKeyword) |
|
{ |
|
QString str = aStr; |
|
|
|
while (str[0] == ' ') str.remove(0,1); |
|
if (hasKeyword) *hasKeyword=false; |
|
|
|
unsigned int i = 0, maxChars = 3; |
|
unsigned int strLength(str.length()); |
|
for (i=0; i < strLength && i < maxChars; i++) |
|
{ |
|
if (str[i] < 'A' || str[i] == sepChar) break; |
|
} |
|
|
|
if (str[i] == sepChar) // skip following spaces too |
|
{ |
|
do { |
|
i++; |
|
} while (str[i] == ' '); |
|
if (hasKeyword) *hasKeyword=true; |
|
return str.mid(i); |
|
} |
|
return str; |
|
} |
|
|
|
|
|
} |
|
|
|
}
|
|
|