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.
1717 lines
53 KiB
1717 lines
53 KiB
/* -*- mode: C++; c-file-style: "gnu" -*- |
|
This file is part of KMail, the KDE mail client. |
|
Copyright (c) 1997 Markus Wuebben <markus.wuebben@kde.org> |
|
Copyright (c) 2009 Laurent Montel <montel@kde.org> |
|
|
|
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. |
|
*/ |
|
|
|
// define this to copy all html that is written to the readerwindow to |
|
// filehtmlwriter.out in the current working directory |
|
//#define KMAIL_READER_HTML_DEBUG 1 |
|
#include "kmreaderwin.h" |
|
|
|
#include <config-kmail.h> |
|
|
|
#include "globalsettings.h" |
|
#include "kmversion.h" |
|
#include "kmmainwidget.h" |
|
#include "kmreadermainwin.h" |
|
#include <kpimutils/kfileio.h> |
|
#include "kmfolderindex.h" |
|
#include "kmcommands.h" |
|
#include <QByteArray> |
|
#include <QImageReader> |
|
#include <QCloseEvent> |
|
#include <QEvent> |
|
#include <QVBoxLayout> |
|
#include <QResizeEvent> |
|
#include <QMouseEvent> |
|
#include <QScrollArea> |
|
#include <QScrollBar> |
|
#include <QSignalMapper> |
|
#include "partNode.h" |
|
#include "kmmsgdict.h" |
|
#include "messagesender.h" |
|
#include "messageviewer/kcursorsaver.h" |
|
#include "kmfolder.h" |
|
#ifndef USE_AKONADI_VIEWER |
|
#include "messageviewer/vcardviewer.h" |
|
#include "objecttreeparser.h" |
|
using KMail::ObjectTreeParser; |
|
#endif |
|
#include "messageviewer/headerstrategy.h" |
|
#include "messageviewer/headerstyle.h" |
|
#include "messageviewer/khtmlparthtmlwriter.h" |
|
using MessageViewer::HtmlWriter; |
|
#include "messageviewer/htmlstatusbar.h" |
|
#include "folderjob.h" |
|
using KMail::FolderJob; |
|
|
|
#include "messageviewer/csshelper.h" |
|
using MessageViewer::CSSHelper; |
|
#include "isubject.h" |
|
using KMail::ISubject; |
|
#include "urlhandlermanager.h" |
|
using KMail::URLHandlerManager; |
|
#include "interfaces/observable.h" |
|
#include "util.h" |
|
#include <kicon.h> |
|
#include "broadcaststatus.h" |
|
#include "messageviewer/attachmentdialog.h" |
|
#include "stringutil.h" |
|
|
|
#include <kmime/kmime_mdn.h> |
|
using namespace KMime; |
|
|
|
#include "messageviewer/viewer.h" |
|
using namespace MessageViewer; |
|
#include "messageviewer/attachmentstrategy.h" |
|
|
|
#include <mimelib/mimepp.h> |
|
#include <mimelib/body.h> |
|
#include <mimelib/utility.h> |
|
|
|
#include "kleo/specialjob.h" |
|
#include "kleo/cryptobackend.h" |
|
#include "kleo/cryptobackendfactory.h" |
|
|
|
// KABC includes |
|
#include <kabc/addressee.h> |
|
#include <kabc/vcardconverter.h> |
|
|
|
// khtml headers |
|
#include <khtml_part.h> |
|
#include <khtmlview.h> // So that we can get rid of the frames |
|
#include <dom/html_element.h> |
|
#include <dom/html_block.h> |
|
#include <dom/html_document.h> |
|
#include <dom/dom_string.h> |
|
|
|
#include <kde_file.h> |
|
#include <kactionmenu.h> |
|
// for the click on attachment stuff (dnaber): |
|
#include <kcharsets.h> |
|
#include <kmenu.h> |
|
#include <kstandarddirs.h> // Sven's : for access and getpid |
|
#include <kdebug.h> |
|
#include <kfiledialog.h> |
|
#include <klocale.h> |
|
#include <kmessagebox.h> |
|
#include <kmimetypetrader.h> |
|
#include <kglobalsettings.h> |
|
#include <krun.h> |
|
#include <ktemporaryfile.h> |
|
#include <kdialog.h> |
|
#include <kaction.h> |
|
#include <kfontaction.h> |
|
#include <kiconloader.h> |
|
#include <kcodecs.h> |
|
#include <kascii.h> |
|
#include <kselectaction.h> |
|
#include <kstandardaction.h> |
|
#include <ktoggleaction.h> |
|
#include <kconfiggroup.h> |
|
|
|
#include <QClipboard> |
|
#include <QCursor> |
|
#include <QTextCodec> |
|
#include <QLayout> |
|
#include <QLabel> |
|
#include <QSplitter> |
|
#include <QStyle> |
|
|
|
// X headers... |
|
#undef Never |
|
#undef Always |
|
|
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <sys/stat.h> |
|
#include <errno.h> |
|
#include <stdio.h> |
|
#include <ctype.h> |
|
#include <string.h> |
|
|
|
#ifdef HAVE_PATHS_H |
|
#include <paths.h> |
|
#include <kvbox.h> |
|
#include <QTextDocument> |
|
#endif |
|
|
|
using namespace KMail; |
|
|
|
//----------------------------------------------------------------------------- |
|
KMReaderWin::KMReaderWin(QWidget *aParent, |
|
QWidget *mainWindow, |
|
KActionCollection* actionCollection, |
|
Qt::WindowFlags aFlags ) |
|
: QWidget(aParent, aFlags ), |
|
mSerNumOfOriginalMessage( 0 ), |
|
mNodeIdOffset( -1 ), |
|
mDelayedMarkTimer( 0 ), |
|
mRootNode( 0 ), |
|
mMainWindow( mainWindow ), |
|
mActionCollection( actionCollection ), |
|
mMailToComposeAction( 0 ), |
|
mMailToReplyAction( 0 ), |
|
mMailToForwardAction( 0 ), |
|
mAddAddrBookAction( 0 ), |
|
mOpenAddrBookAction( 0 ), |
|
mUrlSaveAsAction( 0 ), |
|
mAddBookmarksAction( 0 ), |
|
mShowFullToAddressList( false ), |
|
mShowFullCcAddressList( false ) |
|
{ |
|
mDelayedMarkTimer.setObjectName( "mDelayedMarkTimer" ); |
|
mLastSerNum = 0; |
|
mWaitingForSerNum = 0; |
|
mMessage = 0; |
|
mAtmUpdate = false; |
|
createActions(); |
|
QVBoxLayout * vlay = new QVBoxLayout( this ); |
|
vlay->setMargin( 0 ); |
|
mViewer = new Viewer( this/*TODO*/,KGlobal::config(),mMainWindow,mActionCollection ); |
|
vlay->addWidget( mViewer ); |
|
readConfig(); |
|
|
|
mDelayedMarkTimer.setSingleShot( true ); |
|
connect( &mDelayedMarkTimer, SIGNAL(timeout()), |
|
this, SLOT(slotTouchMessage()) ); |
|
setMsg( 0, false ); |
|
} |
|
|
|
void KMReaderWin::createActions() |
|
{ |
|
KActionCollection *ac = mActionCollection; |
|
if ( !ac ) { |
|
return; |
|
} |
|
// |
|
// Message Menu |
|
// |
|
|
|
// new message to |
|
mMailToComposeAction = new KAction( KIcon( "mail-message-new" ), |
|
i18n( "New Message To..." ), this ); |
|
ac->addAction("mail_new", mMailToComposeAction ); |
|
connect( mMailToComposeAction, SIGNAL(triggered(bool)), |
|
SLOT(slotMailtoCompose()) ); |
|
|
|
// reply to |
|
mMailToReplyAction = new KAction( KIcon( "mail-reply-sender" ), |
|
i18n( "Reply To..." ), this ); |
|
ac->addAction( "mailto_reply", mMailToReplyAction ); |
|
connect( mMailToReplyAction, SIGNAL(triggered(bool)), |
|
SLOT(slotMailtoReply()) ); |
|
|
|
// forward to |
|
mMailToForwardAction = new KAction( KIcon( "mail-forward" ), |
|
i18n( "Forward To..." ), this ); |
|
ac->addAction( "mailto_forward", mMailToForwardAction ); |
|
connect( mMailToForwardAction, SIGNAL(triggered(bool)), |
|
SLOT(slotMailtoForward()) ); |
|
|
|
// add to addressbook |
|
mAddAddrBookAction = new KAction( KIcon( "contact-new" ), |
|
i18n( "Add to Address Book" ), this ); |
|
ac->addAction( "add_addr_book", mAddAddrBookAction ); |
|
connect( mAddAddrBookAction, SIGNAL(triggered(bool)), |
|
SLOT(slotMailtoAddAddrBook()) ); |
|
|
|
// open in addressbook |
|
mOpenAddrBookAction = new KAction( KIcon( "view-pim-contacts" ), |
|
i18n( "Open in Address Book" ), this ); |
|
ac->addAction( "openin_addr_book", mOpenAddrBookAction ); |
|
connect( mOpenAddrBookAction, SIGNAL(triggered(bool)), |
|
SLOT(slotMailtoOpenAddrBook()) ); |
|
#ifndef USE_AKONADI_VIEWER |
|
// copy selected text to clipboard |
|
mCopyAction = ac->addAction( KStandardAction::Copy, "kmail_copy", this, |
|
SLOT(slotCopySelectedText()) ); |
|
// copy all text to clipboard |
|
mSelectAllAction = new KAction(i18n("Select All Text"), this); |
|
ac->addAction("mark_all_text", mSelectAllAction ); |
|
connect(mSelectAllAction, SIGNAL(triggered(bool) ), SLOT(selectAll())); |
|
mSelectAllAction->setShortcut( KStandardShortcut::selectAll() ); |
|
// copy Email address to clipboard |
|
mCopyURLAction = new KAction( KIcon( "edit-copy" ), |
|
i18n( "Copy Link Address" ), this ); |
|
ac->addAction( "copy_url", mCopyURLAction ); |
|
connect( mCopyURLAction, SIGNAL(triggered(bool)), SLOT(slotUrlCopy()) ); |
|
// find text |
|
KAction *action = new KAction(KIcon("edit-find"), i18n("&Find in Message..."), this); |
|
ac->addAction("find_in_messages", action ); |
|
connect(action, SIGNAL(triggered(bool)), SLOT(slotFind())); |
|
// open URL |
|
mUrlOpenAction = new KAction( KIcon( "document-open" ), i18n( "Open URL" ), this ); |
|
ac->addAction( "open_url", mUrlOpenAction ); |
|
connect( mUrlOpenAction, SIGNAL(triggered(bool)), SLOT(slotUrlOpen()) ); |
|
#endif |
|
// bookmark message |
|
mAddBookmarksAction = new KAction( KIcon( "bookmark-new" ), i18n( "Bookmark This Link" ), this ); |
|
ac->addAction( "add_bookmarks", mAddBookmarksAction ); |
|
connect( mAddBookmarksAction, SIGNAL(triggered(bool)), |
|
SLOT(slotAddBookmarks()) ); |
|
|
|
// save URL as |
|
mUrlSaveAsAction = new KAction( i18n( "Save Link As..." ), this ); |
|
ac->addAction( "saveas_url", mUrlSaveAsAction ); |
|
connect( mUrlSaveAsAction, SIGNAL(triggered(bool)), SLOT(slotUrlSave()) ); |
|
} |
|
|
|
void KMReaderWin::setUseFixedFont( bool useFixedFont ) |
|
{ |
|
mViewer->setUseFixedFont( useFixedFont ); |
|
} |
|
|
|
bool KMReaderWin::isFixedFont() const |
|
{ |
|
return mViewer->isFixedFont(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
KMReaderWin::~KMReaderWin() |
|
{ |
|
clearBodyPartMementos(); |
|
#if 0 |
|
if (mAutoDelete) delete message(); |
|
#endif |
|
delete mRootNode; mRootNode = 0; |
|
removeTempFiles(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMessageArrived( KMMessage *msg ) |
|
{ |
|
if (msg && ((KMMsgBase*)msg)->isMessage()) { |
|
if ( msg->getMsgSerNum() == mWaitingForSerNum ) { |
|
setMsg( msg, true ); |
|
} else { |
|
kDebug() << "Ignoring update"; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::update( KMail::Interface::Observable * observable ) |
|
{ |
|
if ( !mAtmUpdate ) { |
|
// reparse the msg |
|
saveRelativePosition(); |
|
updateReaderWin(); |
|
return; |
|
} |
|
|
|
if ( !mRootNode ) |
|
return; |
|
|
|
KMMessage* msg = static_cast<KMMessage*>( observable ); |
|
assert( msg != 0 ); |
|
|
|
// find our partNode and update it |
|
if ( !msg->lastUpdatedPart() ) { |
|
kDebug() << "No updated part"; |
|
return; |
|
} |
|
partNode* node = mRootNode->findNodeForDwPart( msg->lastUpdatedPart() ); |
|
if ( !node ) { |
|
kDebug() << "Can't find node for part"; |
|
return; |
|
} |
|
node->setDwPart( msg->lastUpdatedPart() ); |
|
|
|
// update the tmp file |
|
// we have to set it writeable temporarily |
|
::chmod( QFile::encodeName( mAtmCurrentName ), S_IRWXU ); |
|
QByteArray data = node->msgPart().bodyDecodedBinary(); |
|
if ( node->msgPart().type() == DwMime::kTypeText && data.size() > 0 ) { |
|
// convert CRLF to LF before writing text attachments to disk |
|
const size_t newsize = KMail::Util::crlf2lf( data.data(), data.size() ); |
|
data.truncate( newsize ); |
|
} |
|
KPIMUtils::kByteArrayToFile( data, mAtmCurrentName, false, false, false ); |
|
::chmod( QFile::encodeName( mAtmCurrentName ), S_IRUSR ); |
|
|
|
mAtmUpdate = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::removeTempFiles() |
|
{ |
|
for (QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); |
|
++it) |
|
{ |
|
QFile::remove(*it); |
|
} |
|
mTempFiles.clear(); |
|
for (QStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end(); |
|
it++) |
|
{ |
|
QDir(*it).rmdir(*it); |
|
} |
|
mTempDirs.clear(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::readConfig(void) |
|
{ |
|
//TODO remove it |
|
} |
|
|
|
void KMReaderWin::setAttachmentStrategy( const AttachmentStrategy * strategy ) { |
|
mViewer->setAttachmentStrategy( strategy ); |
|
} |
|
|
|
void KMReaderWin::setHeaderStyleAndStrategy( const HeaderStyle * style, |
|
const HeaderStrategy * strategy ) { |
|
mViewer->setHeaderStyleAndStrategy( style, strategy ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setOverrideEncoding( const QString & encoding ) |
|
{ |
|
mViewer->setOverrideEncoding( encoding ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset ) |
|
{ |
|
mSerNumOfOriginalMessage = serNumOfOriginalMessage; |
|
mNodeIdOffset = nodeIdOffset; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setMsg( KMMessage* aMsg, bool force ) |
|
{ |
|
if ( aMsg ) { |
|
kDebug() << "(" << aMsg->getMsgSerNum() <<", last" << mLastSerNum <<")" << aMsg->subject() |
|
<< aMsg->fromStrip() << ", readyToShow" << (aMsg->readyToShow()); |
|
} |
|
#ifdef USE_AKONADI_VIEWER |
|
//TEMPORARY |
|
if ( aMsg ) { |
|
KMime::Message *message = new KMime::Message; |
|
message->setContent( aMsg->asString() ); |
|
message->parse(); |
|
mViewer->setMessage( message , force ? Viewer::Force : Viewer::Delayed); |
|
//return; |
|
} |
|
#endif |
|
|
|
#ifndef USE_AKONADI_VIEWER |
|
// Reset message-transient state |
|
if (aMsg && aMsg->getMsgSerNum() != mLastSerNum ){ |
|
mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin()-1; |
|
clearBodyPartMementos(); |
|
} |
|
if ( mPrinting ) |
|
mLevelQuote = -1; |
|
#endif |
|
bool complete = true; |
|
if ( aMsg && |
|
!aMsg->readyToShow() && |
|
(aMsg->getMsgSerNum() != mLastSerNum) && |
|
!aMsg->isComplete() ) |
|
complete = false; |
|
|
|
// If not forced and there is aMsg and aMsg is same as mMsg then return |
|
if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum) |
|
return; |
|
|
|
// (de)register as observer |
|
if (aMsg && message()) |
|
message()->detach( this ); |
|
if (aMsg) |
|
aMsg->attach( this ); |
|
mAtmUpdate = false; |
|
|
|
// connect to the updates if we have hancy headers |
|
|
|
mDelayedMarkTimer.stop(); |
|
|
|
mMessage = 0; |
|
if ( !aMsg ) { |
|
mWaitingForSerNum = 0; // otherwise it has been set |
|
mLastSerNum = 0; |
|
} else { |
|
mLastSerNum = aMsg->getMsgSerNum(); |
|
// Check if the serial number can be used to find the assoc KMMessage |
|
// If so, keep only the serial number (and not mMessage), to avoid a dangling mMessage |
|
// when going to another message in the mainwindow. |
|
// Otherwise, keep only mMessage, this is fine for standalone KMReaderMainWins since |
|
// we're working on a copy of the KMMessage, which we own. |
|
if (message() != aMsg) { |
|
mMessage = aMsg; |
|
mLastSerNum = 0; |
|
} |
|
} |
|
|
|
if (aMsg) { |
|
#ifndef USE_AKONADI_VIEWER |
|
aMsg->setOverrideCodec( overrideCodec() ); |
|
#endif |
|
aMsg->setDecodeHTML( htmlMail() ); |
|
// FIXME: workaround to disable DND for IMAP load-on-demand |
|
#ifndef USE_AKONADI_VIEWER |
|
if ( !aMsg->isComplete() ) |
|
mViewer->setDNDEnabled( false ); |
|
else |
|
mViewer->setDNDEnabled( true ); |
|
#endif |
|
} |
|
|
|
// only display the msg if it is complete |
|
// otherwise we'll get flickering with progressively loaded messages |
|
if ( complete ) |
|
{ |
|
// Avoid flicker, somewhat of a cludge |
|
if (force) { |
|
// stop the timer to avoid calling updateReaderWin twice |
|
updateReaderWin(); |
|
} |
|
} |
|
|
|
if ( aMsg && (aMsg->status().isUnread() || aMsg->status().isNew()) |
|
&& GlobalSettings::self()->delayedMarkAsRead() ) { |
|
if ( GlobalSettings::self()->delayedMarkTime() != 0 ) |
|
mDelayedMarkTimer.start( GlobalSettings::self()->delayedMarkTime() * 1000 ); |
|
else |
|
slotTouchMessage(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::clearCache() |
|
{ |
|
clear(); |
|
mDelayedMarkTimer.stop(); |
|
mLastSerNum = 0; |
|
mWaitingForSerNum = 0; |
|
mMessage = 0; |
|
} |
|
|
|
// enter items for the "Important changes" list here: |
|
static const char * const kmailChanges[] = { |
|
"" |
|
}; |
|
static const int numKMailChanges = |
|
sizeof kmailChanges / sizeof *kmailChanges; |
|
|
|
// enter items for the "new features" list here, so the main body of |
|
// the welcome page can be left untouched (probably much easier for |
|
// the translators). Note that the <li>...</li> tags are added |
|
// automatically below: |
|
static const char * const kmailNewFeatures[] = { |
|
"" |
|
}; |
|
static const int numKMailNewFeatures = |
|
sizeof kmailNewFeatures / sizeof *kmailNewFeatures; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//static |
|
QString KMReaderWin::newFeaturesMD5() |
|
{ |
|
QByteArray str; |
|
for ( int i = 0 ; i < numKMailChanges ; ++i ) |
|
str += kmailChanges[i]; |
|
for ( int i = 0 ; i < numKMailNewFeatures ; ++i ) |
|
str += kmailNewFeatures[i]; |
|
KMD5 md5( str ); |
|
return md5.base64Digest(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::displaySplashPage( const QString &info ) |
|
{ |
|
mViewer->displaySplashPage( info ); |
|
} |
|
|
|
void KMReaderWin::displayBusyPage() |
|
{ |
|
QString info = |
|
i18n( "<h2 style='margin-top: 0px;'>Retrieving Folder Contents</h2><p>Please wait . . .</p> " ); |
|
|
|
displaySplashPage( info ); |
|
} |
|
|
|
void KMReaderWin::displayOfflinePage() |
|
{ |
|
QString info = |
|
i18n( "<h2 style='margin-top: 0px;'>Offline</h2><p>KMail is currently in offline mode. " |
|
"Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p> " ); |
|
|
|
displaySplashPage( info ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::displayAboutPage() |
|
{ |
|
KLocalizedString info = |
|
ki18nc("%1: KMail version; %2: help:// URL; %3: homepage URL; " |
|
"%4: generated list of new features; " |
|
"%5: First-time user text (only shown on first start); " |
|
"%6: generated list of important changes; " |
|
"--- end of comment ---", |
|
"<h2 style='margin-top: 0px;'>Welcome to KMail %1</h2><p>KMail is the email client for the K " |
|
"Desktop Environment. It is designed to be fully compatible with " |
|
"Internet mailing standards including MIME, SMTP, POP3 and IMAP." |
|
"</p>\n" |
|
"<ul><li>KMail has many powerful features which are described in the " |
|
"<a href=\"%2\">documentation</a></li>\n" |
|
"<li>The <a href=\"%3\">KMail homepage</A> offers information about " |
|
"new versions of KMail</li></ul>\n" |
|
"%6\n" // important changes |
|
"%4\n" // new features |
|
"%5\n" // first start info |
|
"<p>We hope that you will enjoy KMail.</p>\n" |
|
"<p>Thank you,</p>\n" |
|
"<p style='margin-bottom: 0px'> The KMail Team</p>") |
|
.subs( KMAIL_VERSION ) // KMail version |
|
.subs( "help:/kmail/index.html" ) // KMail help:// URL |
|
.subs( "http://kontact.kde.org/kmail/" ); // KMail homepage URL |
|
|
|
if ( ( numKMailNewFeatures > 1 ) || ( numKMailNewFeatures == 1 && strlen(kmailNewFeatures[0]) > 0 ) ) { |
|
QString featuresText = |
|
i18n("<p>Some of the new features in this release of KMail include " |
|
"(compared to KMail %1, which is part of KDE %2):</p>\n", |
|
QString("1.9"), QString("3.5")); // prior KMail and KDE version |
|
featuresText += "<ul>\n"; |
|
for ( int i = 0 ; i < numKMailChanges ; i++ ) |
|
featuresText += "<li>" + i18n( kmailNewFeatures[i] ) + "</li>\n"; |
|
featuresText += "</ul>\n"; |
|
info = info.subs( featuresText ); |
|
} |
|
else |
|
info = info.subs( QString() ); // remove the place holder |
|
|
|
if( kmkernel->firstStart() ) { |
|
info = info.subs( i18n("<p>Please take a moment to fill in the KMail " |
|
"configuration panel at Settings->Configure " |
|
"KMail.\n" |
|
"You need to create at least a default identity and " |
|
"an incoming as well as outgoing mail account." |
|
"</p>\n") ); |
|
} else { |
|
info = info.subs( QString() ); // remove the place holder |
|
} |
|
|
|
if ( ( numKMailChanges > 1 ) || ( numKMailChanges == 1 && strlen(kmailChanges[0]) > 0 ) ) { |
|
QString changesText = |
|
i18n("<p><span style='font-size:125%; font-weight:bold;'>" |
|
"Important changes</span> (compared to KMail %1):</p>\n", |
|
QString("1.9")); |
|
changesText += "<ul>\n"; |
|
for ( int i = 0 ; i < numKMailChanges ; i++ ) |
|
changesText += i18n("<li>%1</li>\n", i18n( kmailChanges[i] ) ); |
|
changesText += "</ul>\n"; |
|
info = info.subs( changesText ); |
|
} |
|
else |
|
info = info.subs( QString() ); // remove the place holder |
|
|
|
displaySplashPage( info.toString() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void KMReaderWin::updateReaderWin() |
|
{ |
|
//TODO remove this function |
|
#ifndef USE_AKONADI_VIEWER |
|
if ( !mMsgDisplay ) { |
|
return; |
|
} |
|
mViewer->setOnlyLocalReferences( !htmlLoadExternal() ); |
|
htmlWriter()->reset(); |
|
KMFolder* folder = 0; |
|
if (message(&folder)) |
|
{ |
|
if ( GlobalSettings::self()->showColorbar() ) { |
|
mColorBar->show(); |
|
} else { |
|
mColorBar->hide(); |
|
} |
|
displayMessage(); |
|
} else { |
|
mColorBar->hide(); |
|
mMimePartTree->hide(); |
|
mMimePartTree->clearAndResetSortOrder(); |
|
htmlWriter()->begin( cssHelper()->cssDefinitions( isFixedFont() ) ); |
|
htmlWriter()->write( cssHelper()->htmlHead( isFixedFont() ) + "</body></html>" ); |
|
htmlWriter()->end(); |
|
} |
|
if ( mSavedRelativePosition ) { |
|
QScrollBar *scrollBar = mViewer->view()->verticalScrollBar(); |
|
scrollBar->setValue( scrollBar->maximum() * mSavedRelativePosition ); |
|
mSavedRelativePosition = 0; |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart, |
|
int aPartNum ) |
|
{ |
|
// If the message part is already written to a file, no point in doing it again. |
|
// This function is called twice actually, once from the rendering of the attachment |
|
// in the body and once for the header. |
|
const partNode * node = mRootNode->findId( aPartNum ); |
|
KUrl existingFileName = tempFileUrlFromPartNode( node ); |
|
if ( !existingFileName.isEmpty() ) { |
|
return existingFileName.toLocalFile(); |
|
} |
|
|
|
QString fileName = aMsgPart->fileName(); |
|
if( fileName.isEmpty() ) |
|
fileName = aMsgPart->name(); |
|
|
|
QString fname = createTempDir( QString::number( aPartNum ) ); |
|
if ( fname.isEmpty() ) |
|
return QString(); |
|
|
|
// strip off a leading path |
|
int slashPos = fileName.lastIndexOf( '/' ); |
|
if( -1 != slashPos ) |
|
fileName = fileName.mid( slashPos + 1 ); |
|
if( fileName.isEmpty() ) |
|
fileName = "unnamed"; |
|
fname += '/' + fileName; |
|
|
|
QByteArray data = aMsgPart->bodyDecodedBinary(); |
|
if ( aMsgPart->type() == DwMime::kTypeText && data.size() > 0 ) { |
|
// convert CRLF to LF before writing text attachments to disk |
|
const size_t newsize = KMail::Util::crlf2lf( data.data(), data.size() ); |
|
data.truncate( newsize ); |
|
} |
|
if( !KPIMUtils::kByteArrayToFile( data, fname, false, false, false ) ) |
|
return QString(); |
|
|
|
mTempFiles.append( fname ); |
|
// make file read-only so that nobody gets the impression that he might |
|
// edit attached files (cf. bug #52813) |
|
::chmod( QFile::encodeName( fname ), S_IRUSR ); |
|
|
|
return fname; |
|
} |
|
|
|
QString KMReaderWin::createTempDir( const QString ¶m ) |
|
{ |
|
KTemporaryFile *tempFile = new KTemporaryFile(); |
|
tempFile->setSuffix( '.' + param ); |
|
tempFile->open(); |
|
QString fname = tempFile->fileName(); |
|
delete tempFile; |
|
|
|
if ( ::access( QFile::encodeName( fname ), W_OK ) != 0 ) { |
|
// Not there or not writable |
|
if( KDE_mkdir( QFile::encodeName( fname ), 0 ) != 0 || |
|
::chmod( QFile::encodeName( fname ), S_IRWXU ) != 0 ) { |
|
return QString(); //failed create |
|
} |
|
} |
|
|
|
assert( !fname.isNull() ); |
|
|
|
mTempDirs.append( fname ); |
|
return fname; |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::showVCard( KMMessagePart * msgPart ) { |
|
const QByteArray vCard = msgPart->bodyDecodedBinary(); |
|
|
|
VCardViewer *vcv = new VCardViewer(this, vCard ); |
|
vcv->setObjectName( "vCardDialog" ); |
|
vcv->show(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::printMsg( KMMessage* aMsg ) |
|
{ |
|
disconnect( mPartHtmlWriter, SIGNAL( finished() ), this, SLOT( slotPrintMsg() ) ); |
|
connect( mPartHtmlWriter, SIGNAL( finished() ), this, SLOT( slotPrintMsg() ) ); |
|
setMsg( aMsg, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotPrintMsg() |
|
{ |
|
disconnect( mPartHtmlWriter, SIGNAL( finished() ), this, SLOT( slotPrintMsg() ) ); |
|
if (!message()) return; |
|
mViewer->view()->print(); |
|
deleteLater(); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
int KMReaderWin::msgPartFromUrl( const KUrl &aUrl ) |
|
{ |
|
if ( aUrl.isEmpty() ) return -1; |
|
if ( !aUrl.isLocalFile() ) return -1; |
|
|
|
QString path = aUrl.toLocalFile(); |
|
uint right = path.lastIndexOf( '/' ); |
|
uint left = path.lastIndexOf( '.', right ); |
|
|
|
bool ok; |
|
int res = path.mid( left + 1, right - left - 1 ).toInt( &ok ); |
|
return ( ok ) ? res : -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotTouchMessage() |
|
{ |
|
if ( !message() ) |
|
return; |
|
|
|
if ( !message()->status().isNew() && !message()->status().isUnread() ) |
|
return; |
|
|
|
SerNumList serNums; |
|
serNums.append( message()->getMsgSerNum() ); |
|
KMCommand *command = new KMSetStatusCommand( MessageStatus::statusRead(), serNums ); |
|
command->start(); |
|
|
|
// should we send an MDN? |
|
if ( mNoMDNsWhenEncrypted && |
|
message()->encryptionState() != KMMsgNotEncrypted && |
|
message()->encryptionState() != KMMsgEncryptionStateUnknown ) |
|
return; |
|
|
|
KMFolder *folder = message()->parent(); |
|
if ( folder && |
|
( folder->isOutbox() || folder->isSent() || folder->isTrash() || |
|
folder->isDrafts() || folder->isTemplates() ) ) |
|
return; |
|
|
|
if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction, |
|
MDN::Displayed, |
|
true /* allow GUI */ ) ) |
|
if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue |
|
KMessageBox::error( this, i18n("Could not send MDN.") ); |
|
} |
|
|
|
|
|
bool foundSMIMEData( const QString aUrl, |
|
QString& displayName, |
|
QString& libName, |
|
QString& keyId ) |
|
{ |
|
static QString showCertMan("showCertificate#"); |
|
displayName = ""; |
|
libName = ""; |
|
keyId = ""; |
|
int i1 = aUrl.indexOf( showCertMan ); |
|
if( -1 < i1 ) { |
|
i1 += showCertMan.length(); |
|
int i2 = aUrl.indexOf(" ### ", i1); |
|
if( i1 < i2 ) |
|
{ |
|
displayName = aUrl.mid( i1, i2-i1 ); |
|
i1 = i2+5; |
|
i2 = aUrl.indexOf(" ### ", i1); |
|
if( i1 < i2 ) |
|
{ |
|
libName = aUrl.mid( i1, i2-i1 ); |
|
i2 += 5; |
|
|
|
keyId = aUrl.mid( i2 ); |
|
/* |
|
int len = aUrl.length(); |
|
if( len > i2+1 ) { |
|
keyId = aUrl.mid( i2, 2 ); |
|
i2 += 2; |
|
while( len > i2+1 ) { |
|
keyId += ':'; |
|
keyId += aUrl.mid( i2, 2 ); |
|
i2 += 2; |
|
} |
|
} |
|
*/ |
|
} |
|
} |
|
} |
|
return !keyId.isEmpty(); |
|
} |
|
|
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlOn(const QString &aUrl) |
|
{ |
|
const KUrl url(aUrl); |
|
if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" || url.protocol() == "attachment" |
|
|| (url.protocol().isEmpty() && url.path().isEmpty()) ) { |
|
mViewer->setDNDEnabled( false ); |
|
} else { |
|
mViewer->setDNDEnabled( true ); |
|
} |
|
if ( aUrl.trimmed().isEmpty() ) { |
|
KPIM::BroadcastStatus::instance()->reset(); |
|
mHoveredUrl = KUrl(); |
|
return; |
|
} |
|
|
|
mHoveredUrl = url; |
|
|
|
const QString msg = URLHandlerManager::instance()->statusBarMessage( url, this ); |
|
|
|
if ( msg.isEmpty() ) { |
|
kWarning() << "Unhandled URL hover!"; |
|
} |
|
KPIM::BroadcastStatus::instance()->setTransientStatusMsg( msg ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlPopup(const QString &aUrl, const QPoint& aPos) |
|
{ |
|
const KUrl url( aUrl ); |
|
mClickedUrl = url; |
|
|
|
if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) ) |
|
return; |
|
|
|
if ( message() ) { |
|
kWarning() << "Unhandled URL right-click!"; |
|
emit popupMenu( *message(), url, aPos ); |
|
} |
|
} |
|
// Checks if the given node has a parent node that is a DIV which has an ID attribute |
|
// with the value specified here |
|
static bool hasParentDivWithId( const DOM::Node &start, const QString &id ) |
|
{ |
|
if ( start.isNull() ) |
|
return false; |
|
|
|
if ( start.nodeName().string() == "div" ) { |
|
for ( unsigned int i = 0; i < start.attributes().length(); i++ ) { |
|
if ( start.attributes().item( i ).nodeName().string() == "id" && |
|
start.attributes().item( i ).nodeValue().string() == id ) |
|
return true; |
|
} |
|
} |
|
|
|
if ( !start.parentNode().isNull() ) |
|
return hasParentDivWithId( start.parentNode(), id ); |
|
else return false; |
|
} |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::prepareHandleAttachment( int id, const QString& fileName ) |
|
{ |
|
mAtmCurrent = id; |
|
mAtmCurrentName = fileName; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotHandleAttachment( int choice ) |
|
{ |
|
mAtmUpdate = true; |
|
partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; |
|
if ( choice == KMHandleAttachmentCommand::Delete ) { |
|
slotDeleteAttachment( node ); |
|
} else if ( choice == KMHandleAttachmentCommand::Edit ) { |
|
slotEditAttachment( node ); |
|
} else if ( choice == KMHandleAttachmentCommand::Copy ) { |
|
if ( !node ) |
|
return; |
|
QList<QUrl> urls; |
|
KUrl kUrl = tempFileUrlFromPartNode( node ); |
|
QUrl url = QUrl::fromPercentEncoding( kUrl.toEncoded() ); |
|
|
|
if ( !url.isValid() ) |
|
return; |
|
urls.append( url ); |
|
|
|
QMimeData *mimeData = new QMimeData; |
|
mimeData->setUrls( urls ); |
|
QApplication::clipboard()->setMimeData( mimeData, QClipboard::Clipboard ); |
|
} else if ( choice == KMHandleAttachmentCommand::ScrollTo ) { |
|
scrollToAttachment( node ); |
|
} |
|
else { |
|
KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( |
|
node, message(), mAtmCurrent, mAtmCurrentName, |
|
KMHandleAttachmentCommand::AttachmentAction( choice ), KService::Ptr( 0 ), this ); |
|
connect( command, SIGNAL( showAttachment( int, const QString& ) ), |
|
this, SLOT( slotAtmView( int, const QString& ) ) ); |
|
command->start(); |
|
} |
|
} |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotFind() |
|
{ |
|
mViewer->slotFind(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotCopySelectedText() |
|
{ |
|
QString selection = mViewer->selectedText(); |
|
selection.replace( QChar::Nbsp, ' ' ); |
|
QApplication::clipboard()->setText( selection ); |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::atmViewMsg( KMMessagePart* aMsgPart, int nodeId ) |
|
{ |
|
assert(aMsgPart!=0); |
|
KMMessage* msg = new KMMessage; |
|
msg->fromString(aMsgPart->bodyDecoded()); |
|
assert(msg != 0); |
|
msg->setMsgSerNum( 0 ); // because lookups will fail |
|
// some information that is needed for imap messages with LOD |
|
msg->setParent( message()->parent() ); |
|
msg->setUID(message()->UID()); |
|
msg->setReadyToShow(true); |
|
KMReaderMainWin *win = new KMReaderMainWin(); |
|
QString encodeStr; |
|
#ifndef USE_AKONADI_VIEWER |
|
encodeStr = overrideEncoding(); |
|
#else |
|
encodeStr = mViewer->overrideEncoding(); |
|
#endif |
|
win->showMsg( encodeStr, msg, message()->getMsgSerNum(), nodeId ); |
|
win->show(); |
|
} |
|
#endif |
|
|
|
void KMReaderWin::setMsgPart( partNode * node ) { |
|
#ifndef USE_AKONADI_VIEWER |
|
htmlWriter()->reset(); |
|
mColorBar->hide(); |
|
htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); |
|
htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) ); |
|
// end ### |
|
if ( node ) { |
|
ObjectTreeParser otp( this, 0, true ); |
|
otp.parseObjectTree( node ); |
|
} |
|
// ### this, too |
|
htmlWriter()->queue( "</body></html>" ); |
|
htmlWriter()->flush(); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML, |
|
const QString& aFileName, const QString& pname ) |
|
{ |
|
#ifndef USE_AKONADI_VIEWER |
|
// Cancel scheduled updates of the reader window, as that would stop the |
|
// timer of the HTML writer, which would make viewing attachment not work |
|
// anymore as not all HTML is written to the HTML part. |
|
// We're updating the reader window here ourselves anyway. |
|
mUpdateReaderWinTimer.stop(); |
|
|
|
KCursorSaver busy(KBusyPtr::busy()); |
|
if (kasciistricmp(aMsgPart->typeStr(), "message")==0) { |
|
// if called from compose win |
|
KMMessage* msg = new KMMessage; |
|
assert(aMsgPart!=0); |
|
msg->fromString(aMsgPart->bodyDecoded()); |
|
mMainWindow->setWindowTitle(msg->subject()); |
|
setMsg(msg, true); |
|
setAutoDelete(true); |
|
} else if (kasciistricmp(aMsgPart->typeStr(), "text")==0) { |
|
if (kasciistricmp(aMsgPart->subtypeStr(), "x-vcard") == 0 || |
|
kasciistricmp(aMsgPart->subtypeStr(), "directory") == 0) { |
|
showVCard( aMsgPart ); |
|
return; |
|
} |
|
htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); |
|
htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) ); |
|
|
|
if (aHTML && (kasciistricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML |
|
// ### this is broken. It doesn't stip off the HTML header and footer! |
|
htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) ); |
|
mColorBar->setHtmlMode(); |
|
} else { // plain text |
|
const QByteArray str = aMsgPart->bodyDecoded(); |
|
ObjectTreeParser otp( this ); |
|
otp.writeBodyStr( str, |
|
overrideCodec() ? overrideCodec() : aMsgPart->codec(), |
|
message() ? message()->from() : QString() ); |
|
} |
|
htmlWriter()->queue("</body></html>"); |
|
htmlWriter()->flush(); |
|
mMainWindow->setWindowTitle(i18n("View Attachment: %1", pname)); |
|
} else if (kasciistricmp(aMsgPart->typeStr(), "image")==0 || |
|
(kasciistricmp(aMsgPart->typeStr(), "application")==0 && |
|
kasciistricmp(aMsgPart->subtypeStr(), "postscript")==0)) |
|
{ |
|
if (aFileName.isEmpty()) return; // prevent crash |
|
// Open the window with a size so the image fits in (if possible): |
|
QImageReader *iio = new QImageReader(); |
|
iio->setFileName(aFileName); |
|
if( iio->canRead() ) { |
|
QImage img = iio->read(); |
|
QRect desk = KGlobalSettings::desktopGeometry(mMainWindow); |
|
// determine a reasonable window size |
|
int width, height; |
|
if( img.width() < 50 ) |
|
width = 70; |
|
else if( img.width()+20 < desk.width() ) |
|
width = img.width()+20; |
|
else |
|
width = desk.width(); |
|
if( img.height() < 50 ) |
|
height = 70; |
|
else if( img.height()+20 < desk.height() ) |
|
height = img.height()+20; |
|
else |
|
height = desk.height(); |
|
mMainWindow->resize( width, height ); |
|
} |
|
// Just write the img tag to HTML: |
|
htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); |
|
htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) ); |
|
htmlWriter()->write( "<img src=\"file:" + |
|
KUrl::toPercentEncoding( aFileName ) + |
|
"\" border=\"0\">\n" |
|
"</body></html>\n" ); |
|
htmlWriter()->end(); |
|
setWindowTitle( i18n("View Attachment: %1", pname ) ); |
|
show(); |
|
delete iio; |
|
} else { |
|
htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); |
|
htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) ); |
|
htmlWriter()->queue( "<pre>" ); |
|
|
|
QString str = aMsgPart->bodyDecoded(); |
|
// A QString cannot handle binary data. So if it's shorter than the |
|
// attachment, we assume the attachment is binary: |
|
if( str.length() < aMsgPart->decodedSize() ) { |
|
str.prepend( i18np("[KMail: Attachment contains binary data. Trying to show first character.]", |
|
"[KMail: Attachment contains binary data. Trying to show first %1 characters.]", |
|
str.length()) + QChar::fromLatin1('\n') ); |
|
} |
|
htmlWriter()->queue( Qt::escape( str ) ); |
|
htmlWriter()->queue( "</pre>" ); |
|
htmlWriter()->queue("</body></html>"); |
|
htmlWriter()->flush(); |
|
mMainWindow->setWindowTitle(i18n("View Attachment: %1", pname)); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotAtmView( int id, const QString& name ) |
|
{ |
|
#ifndef USE_AKONADI_VIEWER |
|
partNode* node = mRootNode ? mRootNode->findId( id ) : 0; |
|
if( node ) { |
|
mAtmCurrent = id; |
|
mAtmCurrentName = name; |
|
if ( mAtmCurrentName.isEmpty() ) |
|
mAtmCurrentName = tempFileUrlFromPartNode( node ).toLocalFile(); |
|
|
|
KMMessagePart& msgPart = node->msgPart(); |
|
QString pname = msgPart.fileName(); |
|
if (pname.isEmpty()) pname=msgPart.name(); |
|
if (pname.isEmpty()) pname=msgPart.contentDescription(); |
|
if (pname.isEmpty()) pname="unnamed"; |
|
// image Attachment is saved already |
|
if (kasciistricmp(msgPart.typeStr(), "message")==0) { |
|
atmViewMsg( &msgPart,id ); |
|
} else if ((kasciistricmp(msgPart.typeStr(), "text")==0) && |
|
( (kasciistricmp(msgPart.subtypeStr(), "x-vcard")==0) || |
|
(kasciistricmp(msgPart.subtypeStr(), "directory")==0) )) { |
|
setMsgPart( &msgPart, htmlMail(), name, pname ); |
|
} else { |
|
KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(), |
|
name, pname, overrideEncoding() ); |
|
win->show(); |
|
} |
|
} |
|
#endif |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::openAttachment( int id, const QString & name ) |
|
{ |
|
mAtmCurrentName = name; |
|
mAtmCurrent = id; |
|
|
|
QString str, pname, cmd, fileName; |
|
|
|
partNode* node = mRootNode ? mRootNode->findId( id ) : 0; |
|
if( !node ) { |
|
kWarning() << "Could not find node" << id; |
|
return; |
|
} |
|
if ( mAtmCurrentName.isEmpty() ) |
|
mAtmCurrentName = tempFileUrlFromPartNode( node ).toLocalFile(); |
|
|
|
KMMessagePart& msgPart = node->msgPart(); |
|
if (kasciistricmp(msgPart.typeStr(), "message")==0) |
|
{ |
|
atmViewMsg( &msgPart, id ); |
|
return; |
|
} |
|
|
|
QByteArray contentTypeStr( msgPart.typeStr() + '/' + msgPart.subtypeStr() ); |
|
kAsciiToLower( contentTypeStr.data() ); |
|
|
|
// determine the MIME type of the attachment |
|
KMimeType::Ptr mimetype; |
|
// prefer the value of the Content-Type header |
|
mimetype = KMimeType::mimeType( QString::fromLatin1( contentTypeStr ), KMimeType::ResolveAliases ); |
|
if ( !mimetype.isNull() && mimetype->is( KABC::Addressee::mimeType() ) ) { |
|
showVCard( &msgPart ); |
|
return; |
|
} |
|
|
|
// special case treatment on mac |
|
if ( KMail::Util::handleUrlOnMac( mAtmCurrentName ) ) |
|
return; |
|
|
|
if ( mimetype.isNull() || mimetype->name() == "application/octet-stream" ) { |
|
// consider the filename if mimetype can not be found by content-type |
|
mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ ); |
|
} |
|
if ( ( mimetype->name() == "application/octet-stream" ) |
|
&& msgPart.isComplete() ) { |
|
// consider the attachment's contents if neither the Content-Type header |
|
// nor the filename give us a clue |
|
mimetype = KMimeType::findByFileContent( name ); |
|
} |
|
|
|
KService::Ptr offer = |
|
KMimeTypeTrader::self()->preferredService( mimetype->name(), "Application" ); |
|
|
|
QString filenameText = msgPart.fileName(); |
|
if ( filenameText.isEmpty() ) |
|
filenameText = msgPart.name(); |
|
|
|
AttachmentDialog dialog( this, filenameText, offer ? offer->name() : QString(), |
|
QString::fromLatin1( "askSave_" ) + mimetype->name() ); |
|
const int choice = dialog.exec(); |
|
|
|
if ( choice == AttachmentDialog::Save ) { |
|
mAtmUpdate = true; |
|
KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node, |
|
message(), mAtmCurrent, mAtmCurrentName, KMHandleAttachmentCommand::Save, |
|
offer, this ); |
|
connect( command, SIGNAL( showAttachment( int, const QString& ) ), |
|
this, SLOT( slotAtmView( int, const QString& ) ) ); |
|
command->start(); |
|
} |
|
else if ( ( choice == AttachmentDialog::Open ) || |
|
( choice == AttachmentDialog::OpenWith ) ) { |
|
KMHandleAttachmentCommand::AttachmentAction action; |
|
if ( choice == AttachmentDialog::Open ) |
|
action = KMHandleAttachmentCommand::Open; |
|
else |
|
action = KMHandleAttachmentCommand::OpenWith; |
|
mAtmUpdate = true; |
|
KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node, |
|
message(), mAtmCurrent, mAtmCurrentName, action, offer, this ); |
|
connect( command, SIGNAL( showAttachment( int, const QString& ) ), |
|
this, SLOT( slotAtmView( int, const QString& ) ) ); |
|
command->start(); |
|
} else { // Cancel |
|
kDebug() << "Canceled opening attachment"; |
|
} |
|
} |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
QString KMReaderWin::copyText() |
|
{ |
|
QString temp = mViewer->selectedText(); |
|
return temp; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setHtmlOverride( bool override ) |
|
{ |
|
mViewer->setHtmlOverride( override ); |
|
} |
|
|
|
bool KMReaderWin::htmlOverride() const |
|
{ |
|
return mViewer->htmlOverride(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::setHtmlLoadExtOverride( bool override ) |
|
{ |
|
mViewer->setHtmlLoadExtOverride( override ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMReaderWin::htmlMail() |
|
{ |
|
return mViewer->htmlMail(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMReaderWin::htmlLoadExternal() |
|
{ |
|
return mViewer->htmlLoadExternal(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::saveRelativePosition() |
|
{ |
|
mViewer->saveRelativePosition(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::update( bool force ) |
|
{ |
|
#ifndef USE_AKONADI_VIEWER |
|
KMMessage *msg = message(); |
|
if ( msg ) { |
|
saveRelativePosition(); |
|
setMsg( msg, force ); |
|
} |
|
#else |
|
//TODO |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
KMMessage *KMReaderWin::message( KMFolder **aFolder ) const |
|
{ |
|
KMFolder* tmpFolder; |
|
KMFolder*& folder = aFolder ? *aFolder : tmpFolder; |
|
folder = 0; |
|
if (mMessage) |
|
return mMessage; |
|
if (mLastSerNum) { |
|
KMMessage *message = 0; |
|
int index; |
|
KMMsgDict::instance()->getLocation( mLastSerNum, &folder, &index ); |
|
if (folder ) |
|
message = folder->getMsg( index ); |
|
if ( !message ) { |
|
kWarning() << "Attempt to reference invalid serial number" << mLastSerNum; |
|
} |
|
return message; |
|
} |
|
return 0; |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlClicked() |
|
{ |
|
KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow); |
|
uint identity = 0; |
|
if ( message() && message()->parent() ) { |
|
identity = message()->parent()->identity(); |
|
} |
|
|
|
KMCommand *command = new KMUrlClickedCommand( mClickedUrl, identity, this, |
|
false, mainWidget ); |
|
command->start(); |
|
} |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMailtoCompose() |
|
{ |
|
KMCommand *command = new KMMailtoComposeCommand( urlClicked(), message() ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMailtoForward() |
|
{ |
|
KMCommand *command = new KMMailtoForwardCommand( mMainWindow, urlClicked(), |
|
message() ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMailtoAddAddrBook() |
|
{ |
|
KMCommand *command = new KMMailtoAddAddrBookCommand( urlClicked(), |
|
mMainWindow ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMailtoOpenAddrBook() |
|
{ |
|
KMCommand *command = new KMMailtoOpenAddrBookCommand( urlClicked(), |
|
mMainWindow ); |
|
command->start(); |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlCopy() |
|
{ |
|
// we don't necessarily need a mainWidget for KMUrlCopyCommand so |
|
// it doesn't matter if the dynamic_cast fails. |
|
KMCommand *command = |
|
new KMUrlCopyCommand( urlClicked(), |
|
dynamic_cast<KMMainWidget*>( mMainWindow ) ); |
|
command->start(); |
|
} |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlOpen( const KUrl &url ) |
|
{ |
|
#if 0 //port it |
|
if ( !url.isEmpty() ) |
|
mClickedUrl = url; |
|
KMCommand *command = new KMUrlOpenCommand( mClickedUrl, this ); |
|
command->start(); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotAddBookmarks() |
|
{ |
|
KMCommand *command = new KMAddBookmarksCommand( urlClicked(), this ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotUrlSave() |
|
{ |
|
KMCommand *command = new KMUrlSaveCommand( urlClicked(), mMainWindow ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotMailtoReply() |
|
{ |
|
KMCommand *command = new KMMailtoReplyCommand( mMainWindow, urlClicked(), |
|
message(), copyText() ); |
|
command->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
partNode * KMReaderWin::partNodeFromUrl( const KUrl & url ) { |
|
return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ; |
|
} |
|
|
|
partNode * KMReaderWin::partNodeForId( int id ) { |
|
return mRootNode ? mRootNode->findId( id ) : 0 ; |
|
} |
|
|
|
|
|
KUrl KMReaderWin::tempFileUrlFromPartNode( const partNode *node ) |
|
{ |
|
if (!node) |
|
return KUrl(); |
|
|
|
foreach ( const QString &path, mTempFiles ) { |
|
int right = path.lastIndexOf( '/' ); |
|
int left = path.lastIndexOf( '.', right ); |
|
|
|
bool ok = false; |
|
int res = path.mid( left + 1, right - left - 1 ).toInt( &ok ); |
|
if ( ok && res == node->nodeId() ) |
|
return KUrl( path ); |
|
} |
|
return KUrl(); |
|
} |
|
#ifndef USE_AKONADI_VIEWER |
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::slotSaveAttachments() |
|
{ |
|
mAtmUpdate = true; |
|
KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow, |
|
message() ); |
|
saveCommand->start(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMReaderWin::saveAttachment( const KUrl &tempFileName ) |
|
{ |
|
mAtmCurrent = msgPartFromUrl( tempFileName ); |
|
mAtmCurrentName = mClickedUrl.toLocalFile(); |
|
slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save |
|
} |
|
#endif |
|
|
|
void KMReaderWin::fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId ) |
|
{ |
|
Q_ASSERT( msg && nodeId ); |
|
|
|
if ( mSerNumOfOriginalMessage != 0 ) { |
|
KMFolder *folder = 0; |
|
int index = -1; |
|
KMMsgDict::instance()->getLocation( mSerNumOfOriginalMessage, &folder, &index ); |
|
if ( folder && index != -1 ) |
|
*msg = folder->getMsg( index ); |
|
|
|
if ( !( *msg ) ) { |
|
kWarning() << "Unable to find the original message, aborting attachment deletion!"; |
|
return; |
|
} |
|
|
|
*nodeId = node->nodeId() + mNodeIdOffset; |
|
} |
|
else { |
|
*nodeId = node->nodeId(); |
|
*msg = message(); |
|
} |
|
} |
|
|
|
void KMReaderWin::slotDeleteAttachment(partNode * node) |
|
{ |
|
if ( KMessageBox::warningContinueCancel( this, |
|
i18n("Deleting an attachment might invalidate any digital signature on this message."), |
|
i18n("Delete Attachment"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), |
|
"DeleteAttachmentSignatureWarning" ) |
|
!= KMessageBox::Continue ) { |
|
return; |
|
} |
|
|
|
int nodeId = -1; |
|
KMMessage *msg = 0; |
|
fillCommandInfo( node, &msg, &nodeId ); |
|
if ( msg && nodeId != -1 ) { |
|
KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( nodeId, msg, this ); |
|
command->start(); |
|
} |
|
|
|
// If we are operating on a copy of parts of the message, make sure to update the copy as well. |
|
if ( mSerNumOfOriginalMessage != 0 && message() ) { |
|
message()->deleteBodyPart( node->nodeId() ); |
|
update( true ); |
|
} |
|
} |
|
|
|
void KMReaderWin::slotEditAttachment(partNode * node) |
|
{ |
|
if ( KMessageBox::warningContinueCancel( this, |
|
i18n("Modifying an attachment might invalidate any digital signature on this message."), |
|
i18n("Edit Attachment"), KGuiItem( i18n("Edit"), "document-properties" ), KStandardGuiItem::cancel(), |
|
"EditAttachmentSignatureWarning" ) |
|
!= KMessageBox::Continue ) { |
|
return; |
|
} |
|
|
|
int nodeId = -1; |
|
KMMessage *msg = 0; |
|
fillCommandInfo( node, &msg, &nodeId ); |
|
if ( msg && nodeId != -1 ) { |
|
KMEditAttachmentCommand* command = new KMEditAttachmentCommand( nodeId, msg, this ); |
|
command->start(); |
|
} |
|
|
|
// FIXME: If we are operating on a copy of parts of the message, make sure to update the copy as well. |
|
} |
|
|
|
CSSHelper* KMReaderWin::cssHelper() const |
|
{ |
|
return mViewer->cssHelper(); |
|
} |
|
|
|
bool KMReaderWin::decryptMessage() const |
|
{ |
|
return mViewer->decryptMessage(); |
|
} |
|
using namespace KMail::Interface; |
|
|
|
void KMReaderWin::setBodyPartMemento( const partNode *node, |
|
const QByteArray &which, |
|
BodyPartMemento *memento ) |
|
{ |
|
const QByteArray index = node->path() + ':' + which.toLower(); |
|
|
|
const std::map<QByteArray,BodyPartMemento*>::iterator it = |
|
mBodyPartMementoMap.lower_bound( index ); |
|
|
|
if ( it != mBodyPartMementoMap.end() && it->first == index ) { |
|
if ( memento && memento == it->second ) { |
|
return; |
|
} |
|
|
|
delete it->second; |
|
|
|
if ( memento ) { |
|
it->second = memento; |
|
} else { |
|
mBodyPartMementoMap.erase( it ); |
|
} |
|
} else { |
|
if ( memento ) { |
|
mBodyPartMementoMap.insert( it, std::make_pair( index, memento ) ); |
|
} |
|
} |
|
|
|
if ( Observable * o = memento ? memento->asObservable() : 0 ) { |
|
o->attach( this ); |
|
} |
|
} |
|
|
|
BodyPartMemento *KMReaderWin::bodyPartMemento( const partNode *node, |
|
const QByteArray &which ) const |
|
{ |
|
const QByteArray index = node->path() + ':' + which.toLower(); |
|
const std::map<QByteArray,BodyPartMemento*>::const_iterator it = |
|
mBodyPartMementoMap.find( index ); |
|
|
|
if ( it == mBodyPartMementoMap.end() ) { |
|
return 0; |
|
} else { |
|
return it->second; |
|
} |
|
} |
|
|
|
void KMReaderWin::clearBodyPartMementos() |
|
{ |
|
for ( std::map<QByteArray,BodyPartMemento*>::const_iterator |
|
it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end(); |
|
it != end; ++it ) { |
|
delete it->second; |
|
} |
|
mBodyPartMementoMap.clear(); |
|
} |
|
|
|
bool KMReaderWin::showFullToAddressList() const |
|
{ |
|
return mShowFullToAddressList; |
|
} |
|
|
|
void KMReaderWin::setShowFullToAddressList( bool showFullToAddressList ) |
|
{ |
|
mShowFullToAddressList = showFullToAddressList; |
|
} |
|
|
|
bool KMReaderWin::showFullCcAddressList() const |
|
{ |
|
return mShowFullCcAddressList; |
|
} |
|
|
|
void KMReaderWin::setShowFullCcAddressList( bool showFullCcAddressList ) |
|
{ |
|
mShowFullCcAddressList = showFullCcAddressList; |
|
} |
|
|
|
bool KMReaderWin::showSignatureDetails() const |
|
{ |
|
return mViewer->showSignatureDetails(); |
|
} |
|
|
|
void KMReaderWin::setShowSignatureDetails( bool showDetails ) |
|
{ |
|
mViewer->setShowSignatureDetails( showDetails ); |
|
} |
|
bool KMReaderWin::showAttachmentQuicklist() const |
|
{ |
|
return mViewer->showAttachmentQuicklist(); |
|
} |
|
|
|
void KMReaderWin::setShowAttachmentQuicklist( bool showAttachmentQuicklist ) |
|
{ |
|
mViewer->setShowAttachmentQuicklist( showAttachmentQuicklist ); |
|
} |
|
|
|
bool KMReaderWin::htmlLoadExtOverride() const |
|
{ |
|
return mViewer->htmlLoadExtOverride(); |
|
} |
|
void KMReaderWin::setDecryptMessageOverwrite( bool overwrite ) |
|
{ |
|
mViewer->setDecryptMessageOverwrite( overwrite ); |
|
} |
|
const AttachmentStrategy * KMReaderWin::attachmentStrategy() const |
|
{ |
|
return mViewer->attachmentStrategy(); |
|
} |
|
|
|
QString KMReaderWin::overrideEncoding() const |
|
{ |
|
return mViewer->overrideEncoding(); |
|
} |
|
|
|
KToggleAction *KMReaderWin::toggleFixFontAction() |
|
{ |
|
return mViewer->toggleFixFontAction(); |
|
} |
|
|
|
KAction *KMReaderWin::toggleMimePartTreeAction() |
|
{ |
|
return mViewer->toggleMimePartTreeAction(); |
|
} |
|
|
|
KAction *KMReaderWin::selectAllAction() |
|
{ |
|
return mViewer->selectAllAction(); |
|
} |
|
|
|
const HeaderStrategy * KMReaderWin::headerStrategy() const { |
|
return mViewer->headerStrategy(); |
|
} |
|
const HeaderStyle * KMReaderWin::headerStyle() const { |
|
return mViewer->headerStyle(); |
|
} |
|
|
|
|
|
KHTMLPart * KMReaderWin::htmlPart() const |
|
{ |
|
return mViewer->htmlPart(); |
|
} |
|
|
|
KAction *KMReaderWin::copyURLAction() |
|
{ |
|
return mViewer->copyURLAction(); |
|
} |
|
|
|
KAction *KMReaderWin::copyAction() |
|
{ |
|
return mViewer->copyAction(); |
|
} |
|
KAction *KMReaderWin::urlOpenAction() |
|
{ |
|
return mViewer->urlOpenAction(); |
|
} |
|
void KMReaderWin::setPrinting(bool enable) |
|
{ |
|
mViewer->setPrinting( enable ); |
|
} |
|
|
|
void KMReaderWin::clear(bool force ) |
|
{ |
|
mViewer->clear( force ? Viewer::Force : Viewer::Delayed ); |
|
} |
|
|
|
void KMReaderWin::setMessage( Akonadi::Item item, Viewer::UpdateMode updateMode) |
|
{ |
|
kDebug()<<" item.storageCollectionId :"<<item.storageCollectionId(); |
|
mViewer->setMessageItem( item, updateMode ); |
|
} |
|
|
|
KUrl KMReaderWin::urlClicked() const |
|
{ |
|
return mViewer->urlClicked(); |
|
} |
|
|
|
bool KMReaderWin::autoDelete(void) const |
|
{ |
|
return mViewer->autoDelete(); |
|
} |
|
|
|
void KMReaderWin::setAutoDelete(bool f) |
|
{ |
|
mViewer->setAutoDelete( f ); |
|
} |
|
|
|
|
|
#include "kmreaderwin.moc" |
|
|
|
|
|
|