mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 19:02:51 +00:00
1898 lines
77 KiB
C++
1898 lines
77 KiB
C++
/*
|
|
Copyright (C) 2010 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
|
Copyright (c) 2010 Leo Franchi <lfranchi@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 "composerviewbase.h"
|
|
|
|
#include "attachment/attachmentcontrollerbase.h"
|
|
#include "attachment/attachmentmodel.h"
|
|
#include "composer/signaturecontroller.h"
|
|
#include "composer/kmeditor.h"
|
|
#include "emailaddressresolvejob.h"
|
|
#include "keyresolver.h"
|
|
#include "part/globalpart.h"
|
|
#include "kleo_util.h"
|
|
#include "part/infopart.h"
|
|
#include "composer.h"
|
|
#include "utils/util.h"
|
|
#include "imagescaling/imagescalingutils.h"
|
|
|
|
#include "agents/sendlateragent/sendlaterinfo.h"
|
|
#include "agents/sendlateragent/sendlaterutil.h"
|
|
|
|
#include <addressline/recentaddresses.h>
|
|
#include "helper/messagehelper.h"
|
|
|
|
#include <messagecomposer/recipient/recipientseditor.h>
|
|
#include "settings/messagecomposersettings.h"
|
|
|
|
#include <messageviewer/viewer/objecttreeemptysource.h>
|
|
#include <messageviewer/viewer/objecttreeparser.h>
|
|
#ifndef QT_NO_CURSOR
|
|
#include <messageviewer/utils/kcursorsaver.h>
|
|
#endif
|
|
|
|
#include <messagecore/helpers/messagehelpers.h>
|
|
#include <messagecore/utils/stringutil.h>
|
|
#include <messagecore/attachment/attachmentcollector.h>
|
|
#include <messagecore/helpers/nodehelper.h>
|
|
|
|
#include <mailtransport/transportcombobox.h>
|
|
#include <mailtransport/messagequeuejob.h>
|
|
#include <mailtransport/transportmanager.h>
|
|
|
|
#include <akonadi/kmime/specialmailcollections.h>
|
|
#include <akonadi/itemcreatejob.h>
|
|
#include <akonadi/collectionfetchjob.h>
|
|
#include <akonadi/collectioncombobox.h>
|
|
|
|
#include <kpimidentities/identitycombo.h>
|
|
#include <kpimidentities/identitymanager.h>
|
|
#include <kpimutils/email.h>
|
|
|
|
#include <KSaveFile>
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
#include <krichtextwidget.h>
|
|
#include <KStandardDirs>
|
|
|
|
#include <QDir>
|
|
#include <QTimer>
|
|
#include <QUuid>
|
|
#include <QtCore/QTextCodec>
|
|
|
|
static QStringList encodeIdn( const QStringList &emails )
|
|
{
|
|
QStringList encoded;
|
|
foreach ( const QString &email, emails )
|
|
encoded << KPIMUtils::normalizeAddressesAndEncodeIdn( email );
|
|
|
|
return encoded;
|
|
}
|
|
|
|
MessageComposer::ComposerViewBase::ComposerViewBase ( QObject* parent, QWidget *parentGui)
|
|
: QObject ( parent )
|
|
, m_msg( KMime::Message::Ptr( new KMime::Message ) )
|
|
, m_attachmentController( 0 )
|
|
, m_attachmentModel( 0 )
|
|
, m_signatureController( 0 )
|
|
, m_recipientsEditor( 0 )
|
|
, m_identityCombo( 0 )
|
|
, m_identMan( 0 )
|
|
, m_editor( 0 )
|
|
, m_transport( 0 )
|
|
, m_fccCombo( 0 )
|
|
, m_parentWidget( parentGui )
|
|
, m_sign( false )
|
|
, m_encrypt( false )
|
|
, m_neverEncrypt( false )
|
|
, m_mdnRequested( false )
|
|
, m_urgent( false )
|
|
, m_cryptoMessageFormat( Kleo::AutoFormat )
|
|
, m_pendingQueueJobs( 0 )
|
|
, m_autoSaveTimer( 0 )
|
|
, m_autoSaveErrorShown( false )
|
|
, m_autoSaveInterval( 1 * 1000 * 60 ) // default of 1 min
|
|
, mSendLaterInfo (0)
|
|
{
|
|
m_charsets << "utf-8"; // default, so we have a backup in case client code forgot to set.
|
|
|
|
initAutoSave();
|
|
|
|
}
|
|
|
|
MessageComposer::ComposerViewBase::~ComposerViewBase()
|
|
{
|
|
delete mSendLaterInfo;
|
|
}
|
|
|
|
bool MessageComposer::ComposerViewBase::isComposing() const
|
|
{
|
|
return !m_composers.isEmpty();
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setMessage ( const KMime::Message::Ptr& msg )
|
|
{
|
|
|
|
foreach( MessageCore::AttachmentPart::Ptr attachment, m_attachmentModel->attachments() )
|
|
m_attachmentModel->removeAttachment( attachment );
|
|
|
|
m_msg = msg;
|
|
m_recipientsEditor->clear();
|
|
m_recipientsEditor->setRecipientString( m_msg->to()->mailboxes(), MessageComposer::Recipient::To );
|
|
m_recipientsEditor->setRecipientString( m_msg->cc()->mailboxes(), MessageComposer::Recipient::Cc );
|
|
m_recipientsEditor->setRecipientString( m_msg->bcc()->mailboxes(), MessageComposer::Recipient::Bcc );
|
|
m_recipientsEditor->setFocusBottom();
|
|
|
|
// If we are loading from a draft, load unexpanded aliases as well
|
|
if( m_msg->hasHeader( "X-KMail-UnExpanded-To" ) ) {
|
|
const QStringList spl = m_msg->headerByType( "X-KMail-UnExpanded-To" )->asUnicodeString().split( QLatin1String( "," ) );
|
|
foreach( const QString& addr, spl )
|
|
m_recipientsEditor->addRecipient( addr, MessageComposer::Recipient::To );
|
|
}
|
|
if( m_msg->hasHeader( "X-KMail-UnExpanded-CC" ) ) {
|
|
const QStringList spl = m_msg->headerByType( "X-KMail-UnExpanded-CC" )->asUnicodeString().split( QLatin1String( "," ) );
|
|
foreach( const QString& addr, spl )
|
|
m_recipientsEditor->addRecipient( addr, MessageComposer::Recipient::Cc );
|
|
}
|
|
if( m_msg->hasHeader( "X-KMail-UnExpanded-BCC" ) ) {
|
|
const QStringList spl = m_msg->headerByType( "X-KMail-UnExpanded-BCC" )->asUnicodeString().split( QLatin1String( "," ) );
|
|
foreach( const QString& addr, spl )
|
|
m_recipientsEditor->addRecipient( addr, MessageComposer::Recipient::Bcc );
|
|
}
|
|
|
|
// First, we copy the message and then parse it to the object tree parser.
|
|
// The otp gets the message text out of it, in textualContent(), and also decrypts
|
|
// the message if necessary.
|
|
KMime::Content *msgContent = new KMime::Content;
|
|
msgContent->setContent( m_msg->encodedContent() );
|
|
msgContent->parse();
|
|
MessageViewer::EmptySource emptySource;
|
|
MessageViewer::ObjectTreeParser otp( &emptySource );//All default are ok
|
|
otp.parseObjectTree( msgContent );
|
|
|
|
// Load the attachments
|
|
MessageCore::AttachmentCollector ac;
|
|
ac.collectAttachmentsFrom( msgContent );
|
|
std::vector<KMime::Content*>::const_iterator end( ac.attachments().end() );
|
|
for ( std::vector<KMime::Content*>::const_iterator it = ac.attachments().begin();
|
|
it != end ; ++it ) {
|
|
addAttachmentPart( *it );
|
|
}
|
|
|
|
int transportId = -1;
|
|
if ( m_msg->headerByType( "X-KMail-Transport" ) )
|
|
transportId = m_msg->headerByType( "X-KMail-Transport" )->asUnicodeString().toInt();
|
|
|
|
const MailTransport::Transport *transport = MailTransport::TransportManager::self()->transportById( transportId );
|
|
if ( transport )
|
|
m_transport->setCurrentTransport( transport->id() );
|
|
|
|
// Set the HTML text and collect HTML images
|
|
if ( !otp.htmlContent().isEmpty() ) {
|
|
m_editor->setHtml( otp.htmlContent() );
|
|
emit enableHtml();
|
|
collectImages( m_msg.get() );
|
|
} else {
|
|
m_editor->setPlainText( otp.plainTextContent() );
|
|
}
|
|
|
|
if ( m_msg->headerByType( "X-KMail-CursorPos" ) ) {
|
|
m_editor->setCursorPositionFromStart( m_msg->headerByType( "X-KMail-CursorPos" )->asUnicodeString().toInt() );
|
|
}
|
|
delete msgContent;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::updateTemplate ( const KMime::Message::Ptr& msg )
|
|
{
|
|
// First, we copy the message and then parse it to the object tree parser.
|
|
// The otp gets the message text out of it, in textualContent(), and also decrypts
|
|
// the message if necessary.
|
|
KMime::Content *msgContent = new KMime::Content;
|
|
msgContent->setContent( msg->encodedContent() );
|
|
msgContent->parse();
|
|
MessageViewer::EmptySource emptySource;
|
|
MessageViewer::ObjectTreeParser otp( &emptySource );//All default are ok
|
|
otp.parseObjectTree( msgContent );
|
|
// Set the HTML text and collect HTML images
|
|
if ( !otp.htmlContent().isEmpty() ) {
|
|
m_editor->setHtml( otp.htmlContent() );
|
|
emit enableHtml();
|
|
collectImages( msg.get() );
|
|
} else {
|
|
m_editor->setPlainText( otp.plainTextContent() );
|
|
}
|
|
|
|
if ( msg->headerByType( "X-KMail-CursorPos" ) ) {
|
|
m_editor->setCursorPositionFromStart( m_msg->headerByType( "X-KMail-CursorPos" )->asUnicodeString().toInt() );
|
|
}
|
|
delete msgContent;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::send ( MessageComposer::MessageSender::SendMethod method, MessageComposer::MessageSender::SaveIn saveIn, bool checkMailDispatcher )
|
|
{
|
|
mSendMethod = method;
|
|
mSaveIn = saveIn;
|
|
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
|
|
const KPIMIdentities::Identity identity = identityManager()->identityForUoid( m_identityCombo->currentIdentity() );
|
|
|
|
if(identity.attachVcard() && m_attachmentController->attachOwnVcard()) {
|
|
const QString vcardFileName = identity.vCardFile();
|
|
if(!vcardFileName.isEmpty()) {
|
|
m_attachmentController->addAttachmentUrlSync(KUrl(vcardFileName));
|
|
}
|
|
}
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-Transport", m_msg.get(), QString::number(m_transport->currentTransportId()), "utf-8" ) );
|
|
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-Fcc", m_msg.get(), QString::number( m_fccCollection.id() ) , "utf-8" ) );
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-Identity", m_msg.get(), QString::number( identity.uoid() ), "utf-8" ));
|
|
|
|
// Save the quote prefix which is used for this message. Each message can have
|
|
// a different quote prefix, for example depending on the original sender.
|
|
if ( m_editor->quotePrefixName().isEmpty() )
|
|
m_msg->removeHeader( "X-KMail-QuotePrefix" );
|
|
else
|
|
m_msg->setHeader( new KMime::Headers::Generic("X-KMail-QuotePrefix", m_msg.get(), m_editor->quotePrefixName(), "utf-8" ) );
|
|
|
|
if ( m_editor->isFormattingUsed() ) {
|
|
kDebug() << "Html mode";
|
|
m_msg->setHeader( new KMime::Headers::Generic("X-KMail-Markup", m_msg.get(), QLatin1String( "true" ), "utf-8" ) );
|
|
} else {
|
|
m_msg->removeHeader( "X-KMail-Markup" );
|
|
kDebug() << "Plain text";
|
|
}
|
|
|
|
if ( m_editor->isFormattingUsed() && inlineSigningEncryptionSelected() ) {
|
|
QString keepBtnText = m_encrypt ?
|
|
m_sign ? i18n( "&Keep markup, do not sign/encrypt" )
|
|
: i18n( "&Keep markup, do not encrypt" )
|
|
: i18n( "&Keep markup, do not sign" );
|
|
QString yesBtnText = m_encrypt ?
|
|
m_sign ? i18n("Sign/Encrypt (delete markup)")
|
|
: i18n( "Encrypt (delete markup)" )
|
|
: i18n( "Sign (delete markup)" );
|
|
int ret = KMessageBox::warningYesNoCancel( m_parentWidget,
|
|
i18n("<qt><p>Inline signing/encrypting of HTML messages is not possible;</p>"
|
|
"<p>do you want to delete your markup?</p></qt>"),
|
|
i18n("Sign/Encrypt Message?"),
|
|
KGuiItem( yesBtnText ),
|
|
KGuiItem( keepBtnText ) );
|
|
if ( KMessageBox::Cancel == ret ) {
|
|
return;
|
|
}
|
|
if ( KMessageBox::No == ret ) {
|
|
m_encrypt = false;
|
|
m_sign = false;
|
|
} else {
|
|
emit disableHtml( NoConfirmationNeeded );
|
|
}
|
|
}
|
|
|
|
if ( m_neverEncrypt && saveIn != MessageComposer::MessageSender::SaveInNone ) {
|
|
// we can't use the state of the mail itself, to remember the
|
|
// signing and encryption state, so let's add a header instead
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-SignatureActionEnabled", m_msg.get(),
|
|
m_sign ? QLatin1String( "true" ): QLatin1String( "false" ), "utf-8" ) );
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-EncryptActionEnabled", m_msg.get(),
|
|
m_encrypt ? QLatin1String( "true" ) : QLatin1String( "false" ), "utf-8" ) );
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-CryptoMessageFormat", m_msg.get(), QString::number( m_cryptoMessageFormat ), "utf-8" ) );
|
|
} else {
|
|
m_msg->removeHeader( "X-KMail-SignatureActionEnabled" );
|
|
m_msg->removeHeader( "X-KMail-EncryptActionEnabled" );
|
|
m_msg->removeHeader( "X-KMail-CryptoMessageFormat" );
|
|
}
|
|
|
|
if( mSendMethod == MessageComposer::MessageSender::SendImmediate && checkMailDispatcher)
|
|
MessageComposer::Util::sendMailDispatcherIsOnline( m_parentWidget );
|
|
|
|
readyForSending();
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setCustomHeader( const QMap<QByteArray, QString>&customHeader )
|
|
{
|
|
m_customHeader = customHeader;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::readyForSending()
|
|
{
|
|
kDebug() << "Entering readyForSending";
|
|
if( !m_msg ) {
|
|
kDebug() << "m_msg == 0!";
|
|
return;
|
|
}
|
|
|
|
if( !m_composers.isEmpty() ) {
|
|
// This may happen if e.g. the autosave timer calls applyChanges.
|
|
kDebug() << "Called while composer active; ignoring.";
|
|
return;
|
|
}
|
|
|
|
// first, expand all addresses
|
|
MessageComposer::EmailAddressResolveJob *job = new MessageComposer::EmailAddressResolveJob( this );
|
|
const KPIMIdentities::Identity identity = identityManager()->identityForUoid( m_identityCombo->currentIdentity() );
|
|
if (!identity.isNull()) {
|
|
job->setDefaultDomainName(identity.defaultDomainName());
|
|
}
|
|
job->setFrom( from() );
|
|
job->setTo( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::To ) );
|
|
job->setCc( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::Cc ) );
|
|
job->setBcc( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::Bcc ) );
|
|
connect( job, SIGNAL(result(KJob*)), SLOT(slotEmailAddressResolved(KJob*)) );
|
|
job->start();
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotEmailAddressResolved ( KJob* job )
|
|
{
|
|
if ( job->error() ) {
|
|
qWarning() << "An error occurred while resolving the email addresses:" << job->errorString();
|
|
// This error could be caused by a broken search infrastructure, so we ignore it for now
|
|
// to not block sending emails completely.
|
|
}
|
|
|
|
bool autoresizeImage = MessageComposer::MessageComposerSettings::self()->autoResizeImageEnabled();
|
|
|
|
const MessageComposer::EmailAddressResolveJob *resolveJob = qobject_cast<MessageComposer::EmailAddressResolveJob*>( job );
|
|
if( mSaveIn == MessageComposer::MessageSender::SaveInNone ) {
|
|
mExpandedFrom = resolveJob->expandedFrom();
|
|
mExpandedTo = resolveJob->expandedTo();
|
|
mExpandedCc = resolveJob->expandedCc();
|
|
mExpandedBcc = resolveJob->expandedBcc();
|
|
if (autoresizeImage) {
|
|
QStringList listEmails;
|
|
listEmails<< mExpandedFrom;
|
|
listEmails<< mExpandedTo;
|
|
listEmails<< mExpandedCc;
|
|
listEmails<< mExpandedBcc;
|
|
autoresizeImage = MessageComposer::Utils::filterRecipients(listEmails);
|
|
}
|
|
|
|
} else { // saved to draft, so keep the old values, not very nice.
|
|
mExpandedFrom = from();
|
|
foreach( const MessageComposer::Recipient::Ptr &r, m_recipientsEditor->recipients() ) {
|
|
switch( r->type() ) {
|
|
case MessageComposer::Recipient::To: mExpandedTo << r->email(); break;
|
|
case MessageComposer::Recipient::Cc: mExpandedCc << r->email(); break;
|
|
case MessageComposer::Recipient::Bcc: mExpandedBcc << r->email(); break;
|
|
case MessageComposer::Recipient::Undefined: Q_ASSERT( !"Unknown recpient type!" ); break;
|
|
}
|
|
}
|
|
QStringList unExpandedTo, unExpandedCc, unExpandedBcc;
|
|
foreach( const QString &exp, resolveJob->expandedTo() ) {
|
|
if( !mExpandedTo.contains( exp ) ) // this address was expanded, so save it explicitly
|
|
unExpandedTo << exp;
|
|
}
|
|
foreach( const QString& exp, resolveJob->expandedCc() ) {
|
|
if( !mExpandedCc.contains( exp ) )
|
|
unExpandedCc << exp;
|
|
}
|
|
foreach( const QString& exp, resolveJob->expandedBcc() ) {
|
|
if( !mExpandedBcc.contains( exp ) ) // this address was expanded, so save it explicitly
|
|
unExpandedBcc << exp;
|
|
}
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-UnExpanded-To", m_msg.get(), unExpandedTo.join( QLatin1String( ", " ) ).toLatin1() ) );
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-UnExpanded-CC", m_msg.get(), unExpandedCc.join( QLatin1String( ", " ) ).toLatin1() ) );
|
|
m_msg->setHeader( new KMime::Headers::Generic( "X-KMail-UnExpanded-BCC", m_msg.get(), unExpandedBcc.join( QLatin1String( ", " ) ).toLatin1() ) );
|
|
autoresizeImage = false;
|
|
}
|
|
|
|
Q_ASSERT(m_composers.isEmpty()); //composers should be empty. The caller of this function
|
|
//checks for emptyness before calling it
|
|
//so just ensure it actually is empty
|
|
//and document it
|
|
// we first figure out if we need to create multiple messages with different crypto formats
|
|
// if so, we create a composer per format
|
|
// if we aren't signing or encrypting, this just returns a single empty message
|
|
if( m_neverEncrypt && mSaveIn != MessageComposer::MessageSender::SaveInNone ) {
|
|
MessageComposer::Composer* composer = new MessageComposer::Composer;
|
|
composer->setNoCrypto( true );
|
|
m_composers.append( composer );
|
|
} else {
|
|
m_composers = generateCryptoMessages();
|
|
}
|
|
|
|
if( m_composers.isEmpty() ) {
|
|
emit failed( i18n( "It was not possible to create a message composer." ) );
|
|
return;
|
|
}
|
|
|
|
if (autoresizeImage) {
|
|
if (MessageComposer::MessageComposerSettings::self()->askBeforeResizing()) {
|
|
if (MessageComposer::Utils::containsImage(m_attachmentModel->attachments())) {
|
|
const int rc = KMessageBox::warningYesNo( m_parentWidget,i18n("Do you want to resize images?"),
|
|
i18n("Auto Resize Images"), KStandardGuiItem::yes(), KStandardGuiItem::no());
|
|
if (rc == KMessageBox::Yes) {
|
|
autoresizeImage = true;
|
|
} else {
|
|
autoresizeImage = false;
|
|
}
|
|
} else {
|
|
autoresizeImage = false;
|
|
}
|
|
}
|
|
}
|
|
// Compose each message and prepare it for queueing, sending, or storing
|
|
foreach( MessageComposer::Composer* composer, m_composers ) {
|
|
fillGlobalPart( composer->globalPart() );
|
|
m_editor->fillComposerTextPart( composer->textPart() );
|
|
fillInfoPart( composer->infoPart(), UseExpandedRecipients );
|
|
|
|
composer->addAttachmentParts( m_attachmentModel->attachments(), autoresizeImage );
|
|
|
|
connect( composer, SIGNAL(result(KJob*)), this, SLOT(slotSendComposeResult(KJob*)) );
|
|
composer->start();
|
|
kDebug() << "Started a composer for sending!";
|
|
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
// helper methods for reading encryption settings
|
|
|
|
inline int encryptKeyNearExpiryWarningThresholdInDays() {
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrKeyNearExpiryThresholdDays();
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline int signingKeyNearExpiryWarningThresholdInDays()
|
|
{
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnSignKeyNearExpiryThresholdDays();
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline int encryptRootCertNearExpiryWarningThresholdInDays()
|
|
{
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrRootNearExpiryThresholdDays();
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline int signingRootCertNearExpiryWarningThresholdInDays() {
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnSignRootNearExpiryThresholdDays();
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline int encryptChainCertNearExpiryWarningThresholdInDays()
|
|
{
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrChaincertNearExpiryThresholdDays();
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline int signingChainCertNearExpiryWarningThresholdInDays()
|
|
{
|
|
if ( ! MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire() ) {
|
|
return -1;
|
|
}
|
|
const int num =
|
|
MessageComposer::MessageComposerSettings::self()->cryptoWarnSignChaincertNearExpiryThresholdDays();;
|
|
return qMax( 1, num );
|
|
}
|
|
|
|
inline bool encryptToSelf()
|
|
{
|
|
// return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
|
|
return MessageComposer::MessageComposerSettings::self()->cryptoEncryptToSelf();
|
|
}
|
|
|
|
inline bool showKeyApprovalDialog()
|
|
{
|
|
return MessageComposer::MessageComposerSettings::self()->cryptoShowKeysForApproval();
|
|
}
|
|
|
|
} // nameless namespace
|
|
|
|
QList< MessageComposer::Composer* > MessageComposer::ComposerViewBase::generateCryptoMessages ()
|
|
{
|
|
|
|
kDebug() << "filling crypto info";
|
|
Kleo::KeyResolver* keyResolver = new Kleo::KeyResolver( encryptToSelf(),
|
|
showKeyApprovalDialog(),
|
|
MessageComposer::MessageComposerSettings::self()->pgpAutoEncrypt(),
|
|
m_cryptoMessageFormat,
|
|
encryptKeyNearExpiryWarningThresholdInDays(),
|
|
signingKeyNearExpiryWarningThresholdInDays(),
|
|
encryptRootCertNearExpiryWarningThresholdInDays(),
|
|
signingRootCertNearExpiryWarningThresholdInDays(),
|
|
encryptChainCertNearExpiryWarningThresholdInDays(),
|
|
signingChainCertNearExpiryWarningThresholdInDays() );
|
|
|
|
const KPIMIdentities::Identity &id = m_identMan->identityForUoidOrDefault( m_identityCombo->currentIdentity() );
|
|
|
|
QStringList encryptToSelfKeys;
|
|
QStringList signKeys;
|
|
|
|
bool signSomething = m_sign;
|
|
bool doSignCompletely = m_sign;
|
|
bool encryptSomething = m_encrypt;
|
|
bool doEncryptCompletely = m_encrypt;
|
|
|
|
//Add encryptionkeys from id to keyResolver
|
|
kDebug() << id.pgpEncryptionKey().isEmpty() << id.smimeEncryptionKey().isEmpty();
|
|
if ( !id.pgpEncryptionKey().isEmpty() )
|
|
encryptToSelfKeys.push_back( QLatin1String( id.pgpEncryptionKey() ) );
|
|
if ( !id.smimeEncryptionKey().isEmpty() )
|
|
encryptToSelfKeys.push_back( QLatin1String( id.smimeEncryptionKey() ) );
|
|
if ( keyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) {
|
|
kDebug() << "Failed to set encryptoToSelf keys!";
|
|
return QList< MessageComposer::Composer* >();
|
|
}
|
|
|
|
//Add signingkeys from id to keyResolver
|
|
if ( !id.pgpSigningKey().isEmpty() )
|
|
signKeys.push_back( QLatin1String( id.pgpSigningKey() ) );
|
|
if ( !id.smimeSigningKey().isEmpty() )
|
|
signKeys.push_back( QLatin1String( id.smimeSigningKey() ) );
|
|
if ( keyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) {
|
|
kDebug() << "Failed to set signing keys!";
|
|
return QList< MessageComposer::Composer* >();
|
|
}
|
|
|
|
foreach( MessageCore::AttachmentPart::Ptr attachment, m_attachmentModel->attachments() ) {
|
|
if( attachment->isSigned() ) {
|
|
signSomething = true;
|
|
} else {
|
|
doEncryptCompletely = false;
|
|
}
|
|
if( attachment->isEncrypted() ) {
|
|
encryptSomething = true;
|
|
} else {
|
|
doSignCompletely = false;
|
|
}
|
|
}
|
|
|
|
QStringList recipients( mExpandedTo ), bcc( mExpandedBcc );
|
|
recipients.append( mExpandedCc );
|
|
|
|
keyResolver->setPrimaryRecipients( recipients );
|
|
keyResolver->setSecondaryRecipients( bcc );
|
|
|
|
bool result = true;
|
|
signSomething = determineWhetherToSign( doSignCompletely, keyResolver,signSomething, result );
|
|
if(!result) {
|
|
/// TODO handle failure
|
|
kDebug() << "determineWhetherToSign: failed to resolve keys! oh noes";
|
|
emit failed( i18n( "Failed to resolve keys. Please report a bug." ) );
|
|
return QList< MessageComposer::Composer*>();
|
|
}
|
|
|
|
encryptSomething = determineWhetherToEncrypt( doEncryptCompletely,keyResolver,encryptSomething, signSomething, result );
|
|
if(!result) {
|
|
/// TODO handle failure
|
|
kDebug() << "determineWhetherToEncrypt: failed to resolve keys! oh noes";
|
|
emit failed( i18n( "Failed to resolve keys. Please report a bug." ) );
|
|
return QList< MessageComposer::Composer*>();
|
|
}
|
|
|
|
//No encryption or signing is needed
|
|
if( !signSomething && !encryptSomething ) {
|
|
return QList< MessageComposer::Composer* >() << new MessageComposer::Composer();
|
|
}
|
|
|
|
const Kpgp::Result kpgpResult = keyResolver->resolveAllKeys( signSomething, encryptSomething );
|
|
if ( kpgpResult == Kpgp::Canceled ) {
|
|
kDebug() << "resolveAllKeys: one key resolution canceled by user";
|
|
return QList< MessageComposer::Composer*>();
|
|
} else if ( kpgpResult != Kpgp::Ok ) {
|
|
// TODO handle failure
|
|
kDebug() << "resolveAllKeys: failed to resolve keys! oh noes";
|
|
emit failed( i18n( "Failed to resolve keys. Please report a bug." ) );
|
|
return QList< MessageComposer::Composer*>();
|
|
}
|
|
kDebug() << "done resolving keys:";
|
|
|
|
QList< MessageComposer::Composer* > composers;
|
|
|
|
if( encryptSomething || signSomething ) {
|
|
Kleo::CryptoMessageFormat concreteFormat = Kleo::AutoFormat;
|
|
for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
|
|
concreteFormat = concreteCryptoMessageFormats[i];
|
|
if ( keyResolver->encryptionItems( concreteFormat ).empty() )
|
|
continue;
|
|
|
|
if ( !(concreteFormat & m_cryptoMessageFormat) )
|
|
continue;
|
|
|
|
MessageComposer::Composer* composer = new MessageComposer::Composer;
|
|
|
|
if ( encryptSomething ) {
|
|
std::vector<Kleo::KeyResolver::SplitInfo> encData = keyResolver->encryptionItems( concreteFormat );
|
|
std::vector<Kleo::KeyResolver::SplitInfo>::iterator it;
|
|
std::vector<Kleo::KeyResolver::SplitInfo>::iterator end( encData.end() );
|
|
QList<QPair<QStringList, std::vector<GpgME::Key> > > data;
|
|
for( it = encData.begin(); it != end; ++it ) {
|
|
QPair<QStringList, std::vector<GpgME::Key> > p( it->recipients, it->keys );
|
|
data.append( p );
|
|
kDebug() << "got resolved keys for:" << it->recipients;
|
|
}
|
|
composer->setEncryptionKeys( data );
|
|
}
|
|
|
|
if( signSomething ) {
|
|
// find signing keys for this format
|
|
std::vector<GpgME::Key> signingKeys = keyResolver->signingKeys( concreteFormat );
|
|
composer->setSigningKeys( signingKeys );
|
|
}
|
|
|
|
|
|
composer->setMessageCryptoFormat( concreteFormat );
|
|
composer->setSignAndEncrypt( signSomething, encryptSomething );
|
|
|
|
composers.append( composer );
|
|
}
|
|
} else {
|
|
MessageComposer::Composer* composer = new MessageComposer::Composer;
|
|
composers.append( composer );
|
|
//If we canceled sign or encrypt be sure to change status in attachment.
|
|
markAllAttachmentsForSigning(false);
|
|
markAllAttachmentsForSigning(false);
|
|
}
|
|
|
|
if( composers.isEmpty() && ( signSomething || encryptSomething ) )
|
|
Q_ASSERT_X( false, "ComposerViewBase::fillCryptoInfo" , "No concrete sign or encrypt method selected");
|
|
|
|
|
|
return composers;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::fillGlobalPart ( MessageComposer::GlobalPart* globalPart )
|
|
{
|
|
globalPart->setParentWidgetForGui( m_parentWidget );
|
|
globalPart->setCharsets( m_charsets );
|
|
globalPart->setMDNRequested( m_mdnRequested );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::fillInfoPart ( MessageComposer::InfoPart* infoPart, MessageComposer::ComposerViewBase::RecipientExpansion expansion )
|
|
{
|
|
// TODO splitAddressList and expandAliases ugliness should be handled by a
|
|
// special AddressListEdit widget... (later: see RecipientsEditor)
|
|
|
|
if ( m_fccCombo ) {
|
|
infoPart->setFcc( QString::number( m_fccCombo->currentCollection().id() ) );
|
|
} else {
|
|
if ( m_fccCollection.isValid() ) {
|
|
infoPart->setFcc( QString::number( m_fccCollection.id() ) );
|
|
}
|
|
}
|
|
|
|
infoPart->setTransportId( m_transport->currentTransportId() );
|
|
infoPart->setReplyTo( replyTo() );
|
|
if ( expansion == UseExpandedRecipients ) {
|
|
infoPart->setFrom( mExpandedFrom );
|
|
infoPart->setTo( mExpandedTo );
|
|
infoPart->setCc( mExpandedCc );
|
|
infoPart->setBcc( mExpandedBcc );
|
|
} else {
|
|
infoPart->setFrom( from() );
|
|
infoPart->setTo( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::To ) );
|
|
infoPart->setCc( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::Cc ) );
|
|
infoPart->setBcc( m_recipientsEditor->recipientStringList( MessageComposer::Recipient::Bcc ) );
|
|
}
|
|
infoPart->setSubject( subject() );
|
|
infoPart->setUserAgent( QLatin1String( "KMail" ) );
|
|
infoPart->setUrgent( m_urgent );
|
|
|
|
if ( m_msg->inReplyTo() )
|
|
infoPart->setInReplyTo( m_msg->inReplyTo()->asUnicodeString() );
|
|
|
|
if ( m_msg->references() )
|
|
infoPart->setReferences( m_msg->references()->asUnicodeString() );
|
|
|
|
KMime::Headers::Base::List extras;
|
|
if( m_msg->headerByType( "X-KMail-SignatureActionEnabled" ) )
|
|
extras << m_msg->headerByType( "X-KMail-SignatureActionEnabled" );
|
|
if( m_msg->headerByType( "X-KMail-EncryptActionEnabled" ) )
|
|
extras << m_msg->headerByType( "X-KMail-EncryptActionEnabled" );
|
|
if( m_msg->headerByType( "X-KMail-CryptoMessageFormat" ) )
|
|
extras << m_msg->headerByType( "X-KMail-CryptoMessageFormat" );
|
|
if( m_msg->headerByType( "X-KMail-UnExpanded-To" ) )
|
|
extras << m_msg->headerByType( "X-KMail-UnExpanded-To" );
|
|
if( m_msg->headerByType( "X-KMail-UnExpanded-CC" ) )
|
|
extras << m_msg->headerByType( "X-KMail-UnExpanded-CC" );
|
|
if( m_msg->headerByType( "X-KMail-UnExpanded-BCC" ) )
|
|
extras << m_msg->headerByType( "X-KMail-UnExpanded-BCC" );
|
|
if( m_msg->headerByType( "Organization" ) )
|
|
extras << m_msg->headerByType( "Organization" );
|
|
if( m_msg->headerByType( "X-KMail-Identity" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Identity" );
|
|
if( m_msg->headerByType( "X-KMail-Transport" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Transport" );
|
|
if( m_msg->headerByType( "X-KMail-Fcc" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Fcc" );
|
|
if( m_msg->headerByType( "X-KMail-Drafts" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Drafts" );
|
|
if( m_msg->headerByType( "X-KMail-Templates" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Templates" );
|
|
if( m_msg->headerByType( "X-KMail-Link-Message" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Link-Message" );
|
|
if( m_msg->headerByType( "X-KMail-Link-Type" ) )
|
|
extras << m_msg->headerByType( "X-KMail-Link-Type" );
|
|
if( m_msg->headerByType( "X-Face" ) )
|
|
extras << m_msg->headerByType( "X-Face" );
|
|
if( m_msg->headerByType( "X-KMail-FccDisabled") )
|
|
extras << m_msg->headerByType( "X-KMail-FccDisabled");
|
|
|
|
infoPart->setExtraHeaders( extras );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotSendComposeResult( KJob* job )
|
|
{
|
|
kDebug() << "compose job might have error error" << job->error() << "errorString" << job->errorString();
|
|
Q_ASSERT( dynamic_cast< MessageComposer::Composer* >( job ) );
|
|
MessageComposer::Composer* composer = static_cast< MessageComposer::Composer* >( job );
|
|
|
|
if( composer->error() == MessageComposer::Composer::NoError ) {
|
|
Q_ASSERT( m_composers.contains( composer ) );
|
|
// The messages were composed successfully.
|
|
kDebug() << "NoError.";
|
|
const int numberOfMessage( composer->resultMessages().size() );
|
|
for( int i = 0; i < numberOfMessage; ++i ) {
|
|
if ( mSaveIn == MessageComposer::MessageSender::SaveInNone ) {
|
|
queueMessage( composer->resultMessages().at( i ), composer );
|
|
} else {
|
|
saveMessage( composer->resultMessages().at( i ), mSaveIn );
|
|
}
|
|
}
|
|
saveRecentAddresses( composer->resultMessages().at( 0 ) );
|
|
} else if( composer->error() == MessageComposer::Composer::UserCancelledError ) {
|
|
// The job warned the user about something, and the user chose to return
|
|
// to the message. Nothing to do.
|
|
kDebug() << "UserCancelledError.";
|
|
emit failed( i18n( "Job cancelled by the user" ) );
|
|
} else {
|
|
kDebug() << "other Error.";
|
|
QString msg;
|
|
if( composer->error() == MessageComposer::Composer::BugError ) {
|
|
msg = i18n( "Could not compose message: %1 \n Please report this bug.", job->errorString() );
|
|
} else {
|
|
msg = i18n( "Could not compose message: %1", job->errorString() );
|
|
}
|
|
emit failed( msg );
|
|
}
|
|
|
|
m_composers.removeAll( composer );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::saveRecentAddresses( KMime::Message::Ptr msg )
|
|
{
|
|
foreach( const QByteArray& address, msg->to()->addresses() )
|
|
KPIM::RecentAddresses::self( MessageComposer::MessageComposerSettings::self()->config() )->add( QLatin1String( address ) );
|
|
foreach( const QByteArray& address, msg->cc()->addresses() )
|
|
KPIM::RecentAddresses::self( MessageComposer::MessageComposerSettings::self()->config() )->add( QLatin1String( address ) );
|
|
foreach( const QByteArray& address, msg->bcc()->addresses() )
|
|
KPIM::RecentAddresses::self( MessageComposer::MessageComposerSettings::self()->config() )->add( QLatin1String( address ) );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::queueMessage( KMime::Message::Ptr message, MessageComposer::Composer* composer )
|
|
{
|
|
const MessageComposer::InfoPart *infoPart = composer->infoPart();
|
|
MailTransport::MessageQueueJob *qjob = new MailTransport::MessageQueueJob( this );
|
|
qjob->setMessage( message );
|
|
qjob->transportAttribute().setTransportId( infoPart->transportId() );
|
|
if( mSendMethod == MessageComposer::MessageSender::SendLater )
|
|
qjob->dispatchModeAttribute().setDispatchMode( MailTransport::DispatchModeAttribute::Manual );
|
|
|
|
|
|
if( message->hasHeader( "X-KMail-FccDisabled" ) ) {
|
|
qjob->sentBehaviourAttribute().setSentBehaviour( MailTransport::SentBehaviourAttribute::Delete );
|
|
} else if ( !infoPart->fcc().isEmpty() ) {
|
|
qjob->sentBehaviourAttribute().setSentBehaviour( MailTransport::SentBehaviourAttribute::MoveToCollection );
|
|
|
|
const Akonadi::Collection sentCollection( infoPart->fcc().toLongLong() );
|
|
qjob->sentBehaviourAttribute().setMoveToCollection( sentCollection );
|
|
} else {
|
|
qjob->sentBehaviourAttribute().setSentBehaviour(
|
|
MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection );
|
|
}
|
|
MessageComposer::Util::addSendReplyForwardAction(message, qjob);
|
|
|
|
fillQueueJobHeaders( qjob, message, infoPart );
|
|
|
|
MessageCore::StringUtil::removePrivateHeaderFields( message, false );
|
|
|
|
QMapIterator<QByteArray, QString> customHeader(m_customHeader);
|
|
while (customHeader.hasNext()) {
|
|
customHeader.next();
|
|
message->setHeader( new KMime::Headers::Generic( customHeader.key(), message.get(), customHeader.value(),"utf-8") );
|
|
}
|
|
message->assemble();
|
|
connect( qjob, SIGNAL(result(KJob*)), this, SLOT(slotQueueResult(KJob*)) );
|
|
m_pendingQueueJobs++;
|
|
qjob->start();
|
|
|
|
kDebug() << "Queued a message.";
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::slotQueueResult( KJob *job )
|
|
{
|
|
m_pendingQueueJobs--;
|
|
MailTransport::MessageQueueJob *qjob = static_cast<MailTransport::MessageQueueJob* >(job);
|
|
kDebug() << "mPendingQueueJobs" << m_pendingQueueJobs;
|
|
Q_ASSERT( m_pendingQueueJobs >= 0 );
|
|
|
|
if( job->error() ) {
|
|
kDebug() << "Failed to queue a message:" << job->errorString();
|
|
// There is not much we can do now, since all the MessageQueueJobs have been
|
|
// started. So just wait for them to finish.
|
|
// TODO show a message box or something
|
|
QString msg = i18n( "There were problems trying to queue the message for sending: %1",
|
|
job->errorString() );
|
|
|
|
if( m_pendingQueueJobs == 0 ) {
|
|
emit failed( msg );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( m_pendingQueueJobs == 0 ) {
|
|
emit sentSuccessfully(qjob->message()->messageID(false)->asUnicodeString());
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::fillQueueJobHeaders( MailTransport::MessageQueueJob* qjob, KMime::Message::Ptr message, const MessageComposer::InfoPart* infoPart )
|
|
{
|
|
MailTransport::Transport *transport = MailTransport::TransportManager::self()->transportById( infoPart->transportId() );
|
|
if ( transport && transport->specifySenderOverwriteAddress() )
|
|
qjob->addressAttribute().setFrom( KPIMUtils::extractEmailAddress( KPIMUtils::normalizeAddressesAndEncodeIdn( transport->senderOverwriteAddress() ) ) );
|
|
else
|
|
qjob->addressAttribute().setFrom( KPIMUtils::extractEmailAddress( KPIMUtils::normalizeAddressesAndEncodeIdn( infoPart->from() ) ) );
|
|
// if this header is not empty, it contains the real recipient of the message, either the primary or one of the
|
|
// secondary recipients. so we set that to the transport job, while leaving the message itself alone.
|
|
if( message->hasHeader( "X-KMail-EncBccRecipients" ) ) {
|
|
KMime::Headers::Base* realTo = message->headerByType( "X-KMail-EncBccRecipients" );
|
|
qjob->addressAttribute().setTo( cleanEmailList( encodeIdn( realTo->asUnicodeString().split( QLatin1Char( '%' ) ) ) ) );
|
|
message->removeHeader( "X-KMail-EncBccRecipients" );
|
|
message->assemble();
|
|
kDebug() << "sending with-bcc encr mail to a/n recipient:" << qjob->addressAttribute().to();
|
|
} else {
|
|
qjob->addressAttribute().setTo( cleanEmailList( encodeIdn( infoPart->to() ) ) );
|
|
qjob->addressAttribute().setCc( cleanEmailList( encodeIdn( infoPart->cc() ) ) );
|
|
qjob->addressAttribute().setBcc( cleanEmailList( encodeIdn( infoPart->bcc() ) ) );
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::initAutoSave()
|
|
{
|
|
kDebug() << "initalising autosave";
|
|
|
|
// Ensure that the autosave directory exists.
|
|
QDir dataDirectory( KStandardDirs::locateLocal( "data", QLatin1String( "kmail2/" ) ) );
|
|
if( !dataDirectory.exists( QLatin1String( "autosave" ) ) ) {
|
|
kDebug() << "Creating autosave directory.";
|
|
dataDirectory.mkdir( QLatin1String( "autosave" ) );
|
|
}
|
|
|
|
// Construct a file name
|
|
if ( m_autoSaveUUID.isEmpty() ) {
|
|
m_autoSaveUUID = QUuid::createUuid().toString();
|
|
}
|
|
|
|
updateAutoSave();
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::updateAutoSave()
|
|
{
|
|
if ( m_autoSaveInterval == 0 ) {
|
|
delete m_autoSaveTimer; m_autoSaveTimer = 0;
|
|
} else {
|
|
if ( !m_autoSaveTimer ) {
|
|
m_autoSaveTimer = new QTimer( this );
|
|
if ( m_parentWidget )
|
|
connect( m_autoSaveTimer, SIGNAL(timeout()),
|
|
m_parentWidget, SLOT(autoSaveMessage()) );
|
|
else
|
|
connect( m_autoSaveTimer, SIGNAL(timeout()),
|
|
this, SLOT(autoSaveMessage()) );
|
|
|
|
}
|
|
m_autoSaveTimer->start( m_autoSaveInterval );
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::cleanupAutoSave()
|
|
{
|
|
delete m_autoSaveTimer; m_autoSaveTimer = 0;
|
|
if ( !m_autoSaveUUID.isEmpty() ) {
|
|
|
|
kDebug() << "deleting autosave files" << m_autoSaveUUID;
|
|
|
|
// Delete the autosave files
|
|
QDir autoSaveDir( KStandardDirs::locateLocal( "data", QLatin1String( "kmail2/" ) ) + QLatin1String( "autosave" ) );
|
|
|
|
// Filter out only this composer window's autosave files
|
|
QStringList autoSaveFilter;
|
|
autoSaveFilter << m_autoSaveUUID + QLatin1String( "*" );
|
|
autoSaveDir.setNameFilters( autoSaveFilter );
|
|
|
|
// Return the files to be removed
|
|
QStringList autoSaveFiles = autoSaveDir.entryList();
|
|
kDebug() << "There are" << autoSaveFiles.count() << "to be deleted.";
|
|
|
|
// Delete each file
|
|
foreach( const QString &file, autoSaveFiles ) {
|
|
autoSaveDir.remove( file );
|
|
}
|
|
m_autoSaveUUID.clear();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void MessageComposer::ComposerViewBase::autoSaveMessage()
|
|
{
|
|
kDebug() << "Autosaving message";
|
|
|
|
if ( m_autoSaveTimer ) {
|
|
m_autoSaveTimer->stop();
|
|
}
|
|
|
|
if( !m_composers.isEmpty() ) {
|
|
// This may happen if e.g. the autosave timer calls applyChanges.
|
|
kDebug() << "Called while composer active; ignoring.";
|
|
return;
|
|
}
|
|
|
|
MessageComposer::Composer * const composer = createSimpleComposer();
|
|
composer->setAutoSave( true );
|
|
m_composers.append( composer );
|
|
connect( composer, SIGNAL(result(KJob*)), this, SLOT(slotAutoSaveComposeResult(KJob*)) );
|
|
composer->start();
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setAutoSaveFileName( const QString &fileName )
|
|
{
|
|
m_autoSaveUUID = fileName;
|
|
|
|
emit modified( true );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotAutoSaveComposeResult( KJob *job )
|
|
{
|
|
using MessageComposer::Composer;
|
|
|
|
Q_ASSERT( dynamic_cast< Composer* >( job ) );
|
|
Composer* composer = static_cast< Composer* >( job );
|
|
|
|
if( composer->error() == Composer::NoError ) {
|
|
Q_ASSERT( m_composers.contains( composer ) );
|
|
|
|
// The messages were composed successfully. Only save the first message, there should
|
|
// only be one anyway, since crypto is disabled.
|
|
kDebug() << "NoError.";
|
|
writeAutoSaveToDisk( composer->resultMessages().first() );
|
|
Q_ASSERT( composer->resultMessages().size() == 1 );
|
|
|
|
if( m_autoSaveInterval > 0 ) {
|
|
updateAutoSave();
|
|
}
|
|
} else if( composer->error() == MessageComposer::Composer::UserCancelledError ) {
|
|
// The job warned the user about something, and the user chose to return
|
|
// to the message. Nothing to do.
|
|
kDebug() << "UserCancelledError.";
|
|
emit failed( i18n( "Job cancelled by the user" ), AutoSave );
|
|
} else {
|
|
kDebug() << "other Error.";
|
|
emit failed( i18n( "Could not autosave message: %1", job->errorString() ), AutoSave );
|
|
}
|
|
|
|
m_composers.removeAll( composer );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::writeAutoSaveToDisk( const KMime::Message::Ptr& message )
|
|
{
|
|
const QString filename = KStandardDirs::locateLocal( "data", QLatin1String( "kmail2/" ) ) + QLatin1String( "autosave/" ) +
|
|
m_autoSaveUUID;
|
|
KSaveFile file( filename );
|
|
QString errorMessage;
|
|
kDebug() << "Writing message to disk as" << filename;
|
|
|
|
if( file.open() ) {
|
|
file.setPermissions( QFile::ReadUser | QFile::WriteUser );
|
|
|
|
if( file.write( message->encodedContent() ) !=
|
|
static_cast<qint64>( message->encodedContent().size() ) ) {
|
|
errorMessage = i18n( "Could not write all data to file." );
|
|
}
|
|
else {
|
|
if( !file.finalize() ) {
|
|
errorMessage = i18n( "Could not finalize the file." );
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
errorMessage = i18n( "Could not open file." );
|
|
}
|
|
|
|
if ( !errorMessage.isEmpty() ) {
|
|
kWarning() << "Auto saving failed:" << errorMessage << file.errorString();
|
|
if ( !m_autoSaveErrorShown ) {
|
|
KMessageBox::sorry( m_parentWidget, i18n( "Autosaving the message as %1 failed.\n"
|
|
"%2\n"
|
|
"Reason: %3",
|
|
filename,
|
|
errorMessage,
|
|
file.errorString() ),
|
|
i18n( "Autosaving Message Failed" ) );
|
|
|
|
// Error dialog shown, hide the errors the next time
|
|
m_autoSaveErrorShown = true;
|
|
}
|
|
}
|
|
else {
|
|
// No error occurred, the next error should be shown again
|
|
m_autoSaveErrorShown = false;
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::saveMessage( KMime::Message::Ptr message, MessageComposer::MessageSender::SaveIn saveIn )
|
|
{
|
|
Akonadi::Collection target;
|
|
const KPIMIdentities::Identity identity = identityManager()->identityForUoid( m_identityCombo->currentIdentity() );
|
|
message->date()->setDateTime( KDateTime::currentLocalDateTime() );
|
|
message->assemble();
|
|
|
|
Akonadi::Item item;
|
|
item.setMimeType( QLatin1String( "message/rfc822" ) );
|
|
item.setPayload( message );
|
|
if ( !identity.isNull() ) { // we have a valid identity
|
|
if ( saveIn == MessageComposer::MessageSender::SaveInTemplates ) {
|
|
if ( !identity.templates().isEmpty() ) { // the user has specified a custom templates collection
|
|
target = Akonadi::Collection( identity.templates().toLongLong() );
|
|
}
|
|
} else {
|
|
if ( !identity.drafts().isEmpty() ) { // the user has specified a custom drafts collection
|
|
target = Akonadi::Collection( identity.drafts().toLongLong() );
|
|
}
|
|
}
|
|
Akonadi::CollectionFetchJob *saveMessageJob = new Akonadi::CollectionFetchJob( target, Akonadi::CollectionFetchJob::Base );
|
|
saveMessageJob->setProperty( "Akonadi::Item" , QVariant::fromValue( item ) );
|
|
QObject::connect( saveMessageJob, SIGNAL(result(KJob*)), this, SLOT(slotSaveMessage(KJob*)) );
|
|
} else {
|
|
// preinitialize with the default collections
|
|
if ( saveIn == MessageComposer::MessageSender::SaveInTemplates ) {
|
|
target = Akonadi::SpecialMailCollections::self()->defaultCollection( Akonadi::SpecialMailCollections::Templates );
|
|
} else {
|
|
target = Akonadi::SpecialMailCollections::self()->defaultCollection( Akonadi::SpecialMailCollections::Drafts );
|
|
}
|
|
Akonadi::ItemCreateJob *create = new Akonadi::ItemCreateJob( item, target, this );
|
|
connect( create, SIGNAL(result(KJob*)), this, SLOT(slotCreateItemResult(KJob*)) );
|
|
m_pendingQueueJobs++;
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotSaveMessage( KJob* job )
|
|
{
|
|
Akonadi::Collection target;
|
|
Akonadi::Item item = job->property( "Akonadi::Item" ).value<Akonadi::Item>();
|
|
if( job->error() ) {
|
|
target = defaultSpecialTarget();
|
|
} else {
|
|
const Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob*>( job );
|
|
if(fetchJob->collections().isEmpty())
|
|
target = defaultSpecialTarget();
|
|
else
|
|
target = fetchJob->collections().first();
|
|
}
|
|
Akonadi::ItemCreateJob *create = new Akonadi::ItemCreateJob( item, target, this );
|
|
connect( create, SIGNAL(result(KJob*)), this, SLOT(slotCreateItemResult(KJob*)) );
|
|
m_pendingQueueJobs++;
|
|
}
|
|
|
|
Akonadi::Collection MessageComposer::ComposerViewBase::defaultSpecialTarget() const
|
|
{
|
|
Akonadi::Collection target;
|
|
if ( mSaveIn == MessageComposer::MessageSender::SaveInTemplates ) {
|
|
target = Akonadi::SpecialMailCollections::self()->defaultCollection( Akonadi::SpecialMailCollections::Templates );
|
|
} else {
|
|
target = Akonadi::SpecialMailCollections::self()->defaultCollection( Akonadi::SpecialMailCollections::Drafts );
|
|
}
|
|
return target;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotCreateItemResult( KJob *job )
|
|
{
|
|
m_pendingQueueJobs--;
|
|
kDebug() << "mPendingCreateItemJobs" << m_pendingQueueJobs;
|
|
Q_ASSERT( m_pendingQueueJobs >= 0 );
|
|
|
|
if( job->error() ) {
|
|
kWarning() << "Failed to save a message:" << job->errorString();
|
|
emit failed( i18n( "Failed to save the message: %1", job->errorString() ) );
|
|
return;
|
|
}
|
|
|
|
if (mSendLaterInfo) {
|
|
Akonadi::ItemCreateJob *createJob = static_cast<Akonadi::ItemCreateJob *>(job);
|
|
const Akonadi::Item item = createJob->item();
|
|
if (item.isValid()) {
|
|
mSendLaterInfo->setItemId(item.id());
|
|
SendLater::SendLaterUtil::writeSendLaterInfo(SendLater::SendLaterUtil::defaultConfig(), mSendLaterInfo);
|
|
delete mSendLaterInfo;
|
|
mSendLaterInfo = 0;
|
|
}
|
|
}
|
|
|
|
if( m_pendingQueueJobs == 0 ) {
|
|
emit sentSuccessfully();
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::addAttachment ( const KUrl& url, const QString& comment )
|
|
{
|
|
Q_UNUSED( comment );
|
|
kDebug() << "adding attachment with url:" << url;
|
|
m_attachmentController->addAttachment( url );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::addAttachmentUrlSync ( const KUrl& url, const QString& comment )
|
|
{
|
|
Q_UNUSED( comment );
|
|
kDebug() << "adding attachment with url:" << url;
|
|
m_attachmentController->addAttachmentUrlSync( url );
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::addAttachment ( const QString& name, const QString& filename, const QString& charset, const QByteArray& data, const QByteArray& mimeType )
|
|
{
|
|
MessageCore::AttachmentPart::Ptr attachment = MessageCore::AttachmentPart::Ptr( new MessageCore::AttachmentPart() );
|
|
if( !data.isEmpty() ) {
|
|
attachment->setName( name );
|
|
attachment->setFileName( filename );
|
|
attachment->setData( data );
|
|
attachment->setCharset( charset.toLatin1() );
|
|
attachment->setMimeType( mimeType );
|
|
// TODO what about the other fields?
|
|
|
|
m_attachmentController->addAttachment( attachment);
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::addAttachmentPart ( KMime::Content* partToAttach )
|
|
{
|
|
MessageCore::AttachmentPart::Ptr part( new MessageCore::AttachmentPart );
|
|
if( partToAttach->contentType()->mimeType() == "multipart/digest" ||
|
|
partToAttach->contentType()->mimeType() == "message/rfc822" ) {
|
|
// if it is a digest or a full message, use the encodedContent() of the attachment,
|
|
// which already has the proper headers
|
|
part->setData( partToAttach->encodedContent() );
|
|
} else {
|
|
part->setData( partToAttach->decodedContent() );
|
|
}
|
|
part->setMimeType( partToAttach->contentType()->mimeType() );
|
|
if ( partToAttach->contentDescription( false ) ) {
|
|
part->setDescription( partToAttach->contentDescription()->asUnicodeString() );
|
|
}
|
|
if ( partToAttach->contentType( false ) ) {
|
|
if ( partToAttach->contentType()->hasParameter( QLatin1String( "name" ) ) ) {
|
|
part->setName( partToAttach->contentType()->parameter( QLatin1String( "name" ) ) );
|
|
}
|
|
}
|
|
if ( partToAttach->contentDisposition( false ) ) {
|
|
part->setFileName( partToAttach->contentDisposition()->filename() );
|
|
part->setInline( partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline );
|
|
}
|
|
if ( part->name().isEmpty() && !part->fileName().isEmpty() ) {
|
|
part->setName( part->fileName() );
|
|
}
|
|
if ( part->fileName().isEmpty() && !part->name().isEmpty() ) {
|
|
part->setFileName( part->name() );
|
|
}
|
|
m_attachmentController->addAttachment( part );
|
|
}
|
|
|
|
MessageComposer::Composer* MessageComposer::ComposerViewBase::createSimpleComposer() {
|
|
MessageComposer::Composer* composer = new MessageComposer::Composer;
|
|
fillGlobalPart( composer->globalPart() );
|
|
m_editor->fillComposerTextPart( composer->textPart() );
|
|
fillInfoPart( composer->infoPart(), UseUnExpandedRecipients );
|
|
composer->addAttachmentParts( m_attachmentModel->attachments() );
|
|
return composer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
QString MessageComposer::ComposerViewBase::to() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_recipientsEditor->recipientString( MessageComposer::Recipient::To ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
QString MessageComposer::ComposerViewBase::cc() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_recipientsEditor->recipientString( MessageComposer::Recipient::Cc ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
QString MessageComposer::ComposerViewBase::bcc() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_recipientsEditor->recipientString( MessageComposer::Recipient::Bcc ) );
|
|
}
|
|
|
|
QString MessageComposer::ComposerViewBase::from() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_from );
|
|
}
|
|
|
|
QString MessageComposer::ComposerViewBase::replyTo() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_replyTo );
|
|
}
|
|
|
|
QString MessageComposer::ComposerViewBase::subject() const
|
|
{
|
|
return MessageComposer::Util::cleanedUpHeaderString( m_subject );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setParentWidgetForGui ( QWidget* w )
|
|
{
|
|
m_parentWidget = w;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setAttachmentController( MessageComposer::AttachmentControllerBase* controller )
|
|
{
|
|
m_attachmentController = controller;
|
|
}
|
|
|
|
MessageComposer::AttachmentControllerBase* MessageComposer::ComposerViewBase::attachmentController()
|
|
{
|
|
return m_attachmentController;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setAttachmentModel( MessageComposer::AttachmentModel* model )
|
|
{
|
|
m_attachmentModel = model;
|
|
}
|
|
|
|
MessageComposer::AttachmentModel* MessageComposer::ComposerViewBase::attachmentModel()
|
|
{
|
|
return m_attachmentModel;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setRecipientsEditor ( MessageComposer::RecipientsEditor* recEditor )
|
|
{
|
|
m_recipientsEditor = recEditor;
|
|
}
|
|
|
|
MessageComposer::RecipientsEditor* MessageComposer::ComposerViewBase::recipientsEditor()
|
|
{
|
|
return m_recipientsEditor;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setSignatureController(MessageComposer::SignatureController* sigController)
|
|
{
|
|
m_signatureController = sigController;
|
|
}
|
|
|
|
MessageComposer::SignatureController* MessageComposer::ComposerViewBase::signatureController()
|
|
{
|
|
return m_signatureController;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setIdentityCombo ( KPIMIdentities::IdentityCombo* identCombo )
|
|
{
|
|
m_identityCombo = identCombo;
|
|
}
|
|
|
|
KPIMIdentities::IdentityCombo* MessageComposer::ComposerViewBase::identityCombo()
|
|
{
|
|
return m_identityCombo;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::updateRecipients( const KPIMIdentities::Identity &ident, const KPIMIdentities::Identity &oldIdent, MessageComposer::Recipient::Type type )
|
|
{
|
|
QString oldIdentList;
|
|
QString newIdentList;
|
|
if ( type == MessageComposer::Recipient::Bcc ) {
|
|
oldIdentList = oldIdent.bcc();
|
|
newIdentList = ident.bcc();
|
|
|
|
} else if ( type == MessageComposer::Recipient::Cc ) {
|
|
oldIdentList = oldIdent.cc();
|
|
newIdentList = ident.cc();
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if ( oldIdentList != newIdentList ) {
|
|
const KMime::Types::Mailbox::List oldRecipients = MessageCore::StringUtil::mailboxListFromUnicodeString( oldIdentList );
|
|
foreach ( const KMime::Types::Mailbox &recipient, oldRecipients ) {
|
|
m_recipientsEditor->removeRecipient( MessageCore::StringUtil::mailboxListToUnicodeString( KMime::Types::Mailbox::List() << recipient ),
|
|
type );
|
|
}
|
|
|
|
const KMime::Types::Mailbox::List newRecipients = MessageCore::StringUtil::mailboxListFromUnicodeString( newIdentList );
|
|
foreach ( const KMime::Types::Mailbox &recipient, newRecipients ) {
|
|
m_recipientsEditor->addRecipient( MessageCore::StringUtil::mailboxListToUnicodeString( KMime::Types::Mailbox::List() << recipient ),
|
|
type );
|
|
}
|
|
m_recipientsEditor->setFocusBottom();
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::identityChanged ( const KPIMIdentities::Identity &ident, const KPIMIdentities::Identity &oldIdent, bool msgCleared )
|
|
{
|
|
updateRecipients( ident, oldIdent, MessageComposer::Recipient::Bcc );
|
|
updateRecipients( ident, oldIdent, MessageComposer::Recipient::Cc );
|
|
|
|
KPIMIdentities::Signature oldSig = const_cast<KPIMIdentities::Identity&>
|
|
( oldIdent ).signature();
|
|
KPIMIdentities::Signature newSig = const_cast<KPIMIdentities::Identity&>
|
|
( ident ).signature();
|
|
//replace existing signatures
|
|
const bool replaced = editor()->replaceSignature( oldSig, newSig );
|
|
// Just append the signature if there was no old signature
|
|
if ( !replaced && ( msgCleared || oldSig.rawText().isEmpty() ) ) {
|
|
signatureController()->applySignature( newSig );
|
|
}
|
|
const QString vcardFileName = ident.vCardFile();
|
|
attachmentController()->setIdentityHasOwnVcard(!vcardFileName.isEmpty());
|
|
attachmentController()->setAttachOwnVcard(ident.attachVcard());
|
|
|
|
m_editor->setAutocorrectionLanguage(ident.autocorrectionLanguage());
|
|
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setEditor ( MessageComposer::KMeditor* editor )
|
|
{
|
|
m_editor = editor;
|
|
|
|
m_editor->setRichTextSupport( KRichTextWidget::FullTextFormattingSupport |
|
|
KRichTextWidget::FullListSupport |
|
|
KRichTextWidget::SupportDirection |
|
|
KRichTextWidget::SupportAlignment |
|
|
KRichTextWidget::SupportRuleLine |
|
|
KRichTextWidget::SupportHyperlinks );
|
|
m_editor->enableImageActions();
|
|
m_editor->enableEmoticonActions();
|
|
m_editor->enableInsertHtmlActions();
|
|
m_editor->enableInsertTableActions();
|
|
|
|
m_editor->document()->setModified( false );
|
|
|
|
}
|
|
|
|
MessageComposer::KMeditor* MessageComposer::ComposerViewBase::editor()
|
|
{
|
|
return m_editor;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setTransportCombo ( MailTransport::TransportComboBox* transpCombo )
|
|
{
|
|
m_transport = transpCombo;
|
|
}
|
|
|
|
MailTransport::TransportComboBox* MessageComposer::ComposerViewBase::transportComboBox()
|
|
{
|
|
return m_transport;
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::setIdentityManager ( KPIMIdentities::IdentityManager* identMan )
|
|
{
|
|
m_identMan = identMan;
|
|
}
|
|
|
|
KPIMIdentities::IdentityManager* MessageComposer::ComposerViewBase::identityManager()
|
|
{
|
|
return m_identMan;
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::setFcc ( const Akonadi::Collection& fccCollection )
|
|
{
|
|
if ( m_fccCombo ) {
|
|
m_fccCombo->setDefaultCollection( fccCollection );
|
|
} else {
|
|
m_fccCollection = fccCollection;
|
|
}
|
|
Akonadi::CollectionFetchJob * const checkFccCollectionJob =
|
|
new Akonadi::CollectionFetchJob( fccCollection, Akonadi::CollectionFetchJob::Base );
|
|
connect( checkFccCollectionJob, SIGNAL(result(KJob*)),
|
|
SLOT(slotFccCollectionCheckResult(KJob*)) );
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::slotFccCollectionCheckResult( KJob* job )
|
|
{
|
|
if( job->error() ) {
|
|
const Akonadi::Collection sentMailCol =
|
|
Akonadi::SpecialMailCollections::self()->defaultCollection( Akonadi::SpecialMailCollections::SentMail );
|
|
if ( m_fccCombo ) {
|
|
m_fccCombo->setDefaultCollection( sentMailCol );
|
|
} else {
|
|
m_fccCollection = sentMailCol;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setFccCombo ( Akonadi::CollectionComboBox* fcc )
|
|
{
|
|
m_fccCombo = fcc;
|
|
}
|
|
|
|
Akonadi::CollectionComboBox* MessageComposer::ComposerViewBase::fccCombo()
|
|
{
|
|
return m_fccCombo;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setFrom(const QString& from)
|
|
{
|
|
m_from = from;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setReplyTo(const QString& replyTo)
|
|
{
|
|
m_replyTo = replyTo;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setSubject(const QString& subject)
|
|
{
|
|
m_subject = subject;
|
|
if (mSendLaterInfo) {
|
|
mSendLaterInfo->setSubject(m_subject);
|
|
mSendLaterInfo->setTo(to());
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setAutoSaveInterval( int interval )
|
|
{
|
|
m_autoSaveInterval = interval;
|
|
}
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::setCryptoOptions ( bool sign, bool encrypt, Kleo::CryptoMessageFormat format, bool neverEncryptDrafts )
|
|
{
|
|
m_sign = sign;
|
|
m_encrypt = encrypt;
|
|
m_cryptoMessageFormat = format;
|
|
m_neverEncrypt = neverEncryptDrafts;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setCharsets( const QList< QByteArray >& charsets )
|
|
{
|
|
m_charsets = charsets;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setMDNRequested( bool mdnRequested )
|
|
{
|
|
m_mdnRequested = mdnRequested;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setUrgent( bool urgent )
|
|
{
|
|
m_urgent = urgent;
|
|
}
|
|
|
|
QStringList MessageComposer::ComposerViewBase::cleanEmailList(const QStringList& emails)
|
|
{
|
|
QStringList clean;
|
|
foreach( const QString& email, emails )
|
|
clean << KPIMUtils::extractEmailAddress( email );
|
|
return clean;
|
|
}
|
|
|
|
int MessageComposer::ComposerViewBase::autoSaveInterval() const
|
|
{
|
|
return m_autoSaveInterval;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void MessageComposer::ComposerViewBase::collectImages( KMime::Content *root )
|
|
{
|
|
if ( KMime::Content * n = MessageViewer::ObjectTreeParser::findType( root, "multipart/alternative", true, true ) ) {
|
|
KMime::Content *parentnode = n->parent();
|
|
if ( parentnode &&
|
|
parentnode->contentType()->isMultipart() &&
|
|
parentnode->contentType()->subType() == "related" ) {
|
|
KMime::Content *node = MessageCore::NodeHelper::nextSibling( n );
|
|
while ( node ) {
|
|
if ( node->contentType()->isImage() ) {
|
|
kDebug() << "found image in multipart/related : " << node->contentType()->name();
|
|
QImage img;
|
|
img.loadFromData( node->decodedContent() );
|
|
m_editor->loadImage( img, QString::fromLatin1( QByteArray(QByteArray("cid:") + node->contentID()->identifier()) ),
|
|
node->contentType()->name() );
|
|
}
|
|
node = MessageCore::NodeHelper::nextSibling( node );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool MessageComposer::ComposerViewBase::inlineSigningEncryptionSelected()
|
|
{
|
|
if ( !m_sign && !m_encrypt ) {
|
|
return false;
|
|
}
|
|
return m_cryptoMessageFormat == Kleo::InlineOpenPGPFormat;
|
|
}
|
|
|
|
bool MessageComposer::ComposerViewBase::hasMissingAttachments( const QStringList& attachmentKeywords )
|
|
{
|
|
if ( attachmentKeywords.isEmpty() )
|
|
return false;
|
|
if ( m_attachmentModel->rowCount() > 0 ) {
|
|
return false;
|
|
}
|
|
|
|
QStringList attachWordsList = attachmentKeywords;
|
|
|
|
QRegExp rx ( QString::fromLatin1("\\b") +
|
|
attachWordsList.join( QString::fromLatin1("\\b|\\b") ) +
|
|
QString::fromLatin1("\\b") );
|
|
rx.setCaseSensitivity( Qt::CaseInsensitive );
|
|
|
|
// check whether the subject contains one of the attachment key words
|
|
// unless the message is a reply or a forwarded message
|
|
const QString subj = subject();
|
|
bool gotMatch = ( MessageHelper::stripOffPrefixes( subj ) == subj ) && ( rx.indexIn( subj ) >= 0 );
|
|
|
|
if ( !gotMatch ) {
|
|
// check whether the non-quoted text contains one of the attachment key
|
|
// words
|
|
QRegExp quotationRx( QString::fromLatin1("^([ \\t]*([|>:}#]|[A-Za-z]+>))+") );
|
|
QTextDocument *doc = m_editor->document();
|
|
QTextBlock end( doc->end() );
|
|
for ( QTextBlock it = doc->begin(); it != end; it = it.next() ) {
|
|
const QString line = it.text();
|
|
gotMatch = ( quotationRx.indexIn( line ) < 0 ) &&
|
|
( rx.indexIn( line ) >= 0 );
|
|
if ( gotMatch ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !gotMatch )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
MessageComposer::ComposerViewBase::MissingAttachment MessageComposer::ComposerViewBase::checkForMissingAttachments( const QStringList& attachmentKeywords )
|
|
{
|
|
if(!hasMissingAttachments( attachmentKeywords )) {
|
|
return NoMissingAttachmentFound;
|
|
}
|
|
int rc = KMessageBox::warningYesNoCancel( m_editor,
|
|
i18n("The message you have composed seems to refer to an "
|
|
"attached file but you have not attached anything.\n"
|
|
"Do you want to attach a file to your message?"),
|
|
i18n("File Attachment Reminder"),
|
|
KGuiItem(i18n("&Attach File...")),
|
|
KGuiItem(i18n("&Send as Is")) );
|
|
if ( rc == KMessageBox::Cancel )
|
|
return FoundMissingAttachmentAndCancel;
|
|
if ( rc == KMessageBox::Yes ) {
|
|
m_attachmentController->showAddAttachmentDialog();
|
|
return FoundMissingAttachmentAndAddedAttachment;
|
|
}
|
|
|
|
return FoundMissingAttachmentAndSending;
|
|
}
|
|
|
|
|
|
|
|
void MessageComposer::ComposerViewBase::markAllAttachmentsForSigning(bool sign)
|
|
{
|
|
foreach( MessageCore::AttachmentPart::Ptr attachment, m_attachmentModel->attachments() ) {
|
|
if( attachment->isSigned() ) {
|
|
attachment->setSigned(sign);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::markAllAttachmentsForEncryption(bool encrypt)
|
|
{
|
|
foreach( MessageCore::AttachmentPart::Ptr attachment, m_attachmentModel->attachments() ) {
|
|
if( attachment->isEncrypted() ) {
|
|
attachment->setEncrypted(encrypt);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MessageComposer::ComposerViewBase::determineWhetherToSign( bool doSignCompletely, Kleo::KeyResolver* keyResolver, bool signSomething, bool & result )
|
|
{
|
|
bool sign = false;
|
|
switch ( keyResolver->checkSigningPreferences( signSomething ) ) {
|
|
case Kleo::DoIt:
|
|
if ( !signSomething ) {
|
|
markAllAttachmentsForSigning( true );
|
|
return true;
|
|
}
|
|
sign = true;
|
|
break;
|
|
case Kleo::DontDoIt:
|
|
sign = false;
|
|
break;
|
|
case Kleo::AskOpportunistic:
|
|
assert( 0 );
|
|
case Kleo::Ask:
|
|
{
|
|
// the user wants to be asked or has to be asked
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = i18n("Examination of the recipient's signing preferences "
|
|
"yielded that you be asked whether or not to sign "
|
|
"this message.\n"
|
|
"Sign this message?");
|
|
switch ( KMessageBox::questionYesNoCancel( m_parentWidget, msg,
|
|
i18n("Sign Message?"),
|
|
KGuiItem( i18nc("to sign","&Sign") ),
|
|
KGuiItem( i18n("Do &Not Sign") ) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForSigning( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
markAllAttachmentsForSigning( false );
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case Kleo::Conflict:
|
|
{
|
|
// warn the user that there are conflicting signing preferences
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = i18n("There are conflicting signing preferences "
|
|
"for these recipients.\n"
|
|
"Sign this message?");
|
|
switch ( KMessageBox::warningYesNoCancel( m_parentWidget, msg,
|
|
i18n("Sign Message?"),
|
|
KGuiItem( i18nc("to sign","&Sign") ),
|
|
KGuiItem( i18n("Do &Not Sign") ) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForSigning( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
markAllAttachmentsForSigning( false );
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case Kleo::Impossible:
|
|
{
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = i18n("You have requested to sign this message, "
|
|
"but no valid signing keys have been configured "
|
|
"for this identity.");
|
|
if ( KMessageBox::warningContinueCancel( m_parentWidget, msg,
|
|
i18n("Send Unsigned?"),
|
|
KGuiItem( i18n("Send &Unsigned") ) )
|
|
== KMessageBox::Cancel ) {
|
|
result = false;
|
|
return false;
|
|
} else {
|
|
markAllAttachmentsForSigning( false );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !sign || !doSignCompletely ) {
|
|
if ( MessageComposer::MessageComposerSettings::self()->cryptoWarningUnsigned() ) {
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = sign && !doSignCompletely ?
|
|
i18n("Some parts of this message will not be signed.\n"
|
|
"Sending only partially signed messages might violate site policy.\n"
|
|
"Sign all parts instead?") // oh, I hate this...
|
|
: i18n("This message will not be signed.\n"
|
|
"Sending unsigned message might violate site policy.\n"
|
|
"Sign message instead?"); // oh, I hate this...
|
|
const QString buttonText = sign && !doSignCompletely
|
|
? i18n("&Sign All Parts") : i18n("&Sign");
|
|
switch ( KMessageBox::warningYesNoCancel( m_parentWidget, msg,
|
|
i18n("Unsigned-Message Warning"),
|
|
KGuiItem( buttonText ),
|
|
KGuiItem( i18n("Send &As Is") ) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForSigning( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
return sign || doSignCompletely;
|
|
}
|
|
}
|
|
}
|
|
return sign || doSignCompletely;
|
|
}
|
|
|
|
bool MessageComposer::ComposerViewBase::determineWhetherToEncrypt( bool doEncryptCompletely, Kleo::KeyResolver* keyResolver, bool encryptSomething, bool signSomething, bool & result )
|
|
{
|
|
bool encrypt = false;
|
|
bool opportunistic = false;
|
|
switch ( keyResolver->checkEncryptionPreferences( encryptSomething ) ) {
|
|
case Kleo::DoIt:
|
|
if ( !encryptSomething ) {
|
|
markAllAttachmentsForEncryption( true );
|
|
return true;
|
|
}
|
|
encrypt = true;
|
|
break;
|
|
case Kleo::DontDoIt:
|
|
encrypt = false;
|
|
break;
|
|
case Kleo::AskOpportunistic:
|
|
opportunistic = true;
|
|
// fall through...
|
|
case Kleo::Ask:
|
|
{
|
|
// the user wants to be asked or has to be asked
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = opportunistic
|
|
? i18n("Valid trusted encryption keys were found for all recipients.\n"
|
|
"Encrypt this message?")
|
|
: i18n("Examination of the recipient's encryption preferences "
|
|
"yielded that you be asked whether or not to encrypt "
|
|
"this message.\n"
|
|
"Encrypt this message?");
|
|
switch ( KMessageBox::questionYesNoCancel( m_parentWidget, msg,
|
|
i18n("Encrypt Message?"),
|
|
KGuiItem( signSomething
|
|
? i18n("Sign && &Encrypt")
|
|
: i18n("&Encrypt") ),
|
|
KGuiItem( signSomething
|
|
? i18n("&Sign Only")
|
|
: i18n("&Send As-Is") ) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForEncryption( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
markAllAttachmentsForEncryption( false );
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case Kleo::Conflict:
|
|
{
|
|
// warn the user that there are conflicting encryption preferences
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = i18n("There are conflicting encryption preferences "
|
|
"for these recipients.\n"
|
|
"Encrypt this message?");
|
|
switch ( KMessageBox::warningYesNoCancel( m_parentWidget, msg,
|
|
i18n("Encrypt Message?"),
|
|
KGuiItem( i18n("&Encrypt") ),
|
|
KGuiItem( i18n("Do &Not Encrypt")) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForEncryption( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
markAllAttachmentsForEncryption( false );
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case Kleo::Impossible:
|
|
{
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = i18n("You have requested to encrypt this message, "
|
|
"and to encrypt a copy to yourself, "
|
|
"but no valid trusted encryption keys have been "
|
|
"configured for this identity.");
|
|
if ( KMessageBox::warningContinueCancel( m_parentWidget, msg,
|
|
i18n("Send Unencrypted?"),
|
|
KGuiItem( i18n("Send &Unencrypted") ) )
|
|
== KMessageBox::Cancel ) {
|
|
result = false;
|
|
return false;
|
|
} else {
|
|
markAllAttachmentsForEncryption( false );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !encrypt || !doEncryptCompletely ) {
|
|
if ( MessageComposer::MessageComposerSettings::self()->cryptoWarningUnencrypted() ) {
|
|
#ifndef QT_NO_CURSOR
|
|
MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
|
|
#endif
|
|
const QString msg = !doEncryptCompletely ?
|
|
i18n("Some parts of this message will not be encrypted.\n"
|
|
"Sending only partially encrypted messages might violate "
|
|
"site policy and/or leak sensitive information.\n"
|
|
"Encrypt all parts instead?") // oh, I hate this...
|
|
: i18n("This message will not be encrypted.\n"
|
|
"Sending unencrypted messages might violate site policy and/or "
|
|
"leak sensitive information.\n"
|
|
"Encrypt messages instead?"); // oh, I hate this...
|
|
const QString buttonText = !doEncryptCompletely
|
|
? i18n("&Encrypt All Parts") : i18n("&Encrypt");
|
|
switch ( KMessageBox::warningYesNoCancel( m_parentWidget, msg,
|
|
i18n("Unencrypted Message Warning"),
|
|
KGuiItem( buttonText ),
|
|
KGuiItem( signSomething
|
|
? i18n("&Sign Only")
|
|
: i18n("&Send As-Is")) ) ) {
|
|
case KMessageBox::Cancel:
|
|
result = false;
|
|
return false;
|
|
case KMessageBox::Yes:
|
|
markAllAttachmentsForEncryption( true );
|
|
return true;
|
|
case KMessageBox::No:
|
|
return encrypt || doEncryptCompletely;
|
|
}
|
|
}
|
|
}
|
|
|
|
return encrypt || doEncryptCompletely;
|
|
}
|
|
|
|
void MessageComposer::ComposerViewBase::setSendLaterInfo( SendLater::SendLaterInfo *info)
|
|
{
|
|
delete mSendLaterInfo;
|
|
mSendLaterInfo = info;
|
|
}
|
|
|
|
SendLater::SendLaterInfo *MessageComposer::ComposerViewBase::sendLaterInfo() const
|
|
{
|
|
return mSendLaterInfo;
|
|
}
|
|
|