kmail Library API Documentation

kmreaderwin.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmreaderwin.cpp
00003 // Author: Markus Wuebben <markus.wuebben@kde.org>
00004 
00005 // define this to copy all html that is written to the readerwindow to
00006 // filehtmlwriter.out in the current working directory
00007 //#define KMAIL_READER_HTML_DEBUG 1
00008 
00009 #include <config.h>
00010 
00011 #include "kmreaderwin.h"
00012 
00013 #include "globalsettings.h"
00014 #include "kmversion.h"
00015 #include "kmmainwidget.h"
00016 #include "kmreadermainwin.h"
00017 #include "kmailicalifaceimpl.h"
00018 #include <kpgpblock.h>
00019 #include <libkdepim/kfileio.h>
00020 #include "kmfolderindex.h"
00021 #include "kmcommands.h"
00022 #include "kmmsgpartdlg.h"
00023 #include "mailsourceviewer.h"
00024 using KMail::MailSourceViewer;
00025 #include "partNode.h"
00026 #include "kmmsgdict.h"
00027 #include "kmsender.h"
00028 #include "kcursorsaver.h"
00029 #include "kmkernel.h"
00030 #include "kmfolder.h"
00031 #include "vcardviewer.h"
00032 using KMail::VCardViewer;
00033 #include "objecttreeparser.h"
00034 using KMail::ObjectTreeParser;
00035 #include "partmetadata.h"
00036 using KMail::PartMetaData;
00037 #include "attachmentstrategy.h"
00038 using KMail::AttachmentStrategy;
00039 #include "headerstrategy.h"
00040 using KMail::HeaderStrategy;
00041 #include "headerstyle.h"
00042 using KMail::HeaderStyle;
00043 #include "khtmlparthtmlwriter.h"
00044 using KMail::HtmlWriter;
00045 using KMail::KHtmlPartHtmlWriter;
00046 #include "htmlstatusbar.h"
00047 using KMail::HtmlStatusBar;
00048 #include "folderjob.h"
00049 using KMail::FolderJob;
00050 #include "csshelper.h"
00051 using KMail::CSSHelper;
00052 #include "isubject.h"
00053 using KMail::ISubject;
00054 #include "urlhandlermanager.h"
00055 using KMail::URLHandlerManager;
00056 #include "interfaces/observable.h"
00057 
00058 #include "broadcaststatus.h"
00059 
00060 #include <kmime_mdn.h>
00061 using namespace KMime;
00062 #ifdef KMAIL_READER_HTML_DEBUG
00063 #include "filehtmlwriter.h"
00064 using KMail::FileHtmlWriter;
00065 #include "teehtmlwriter.h"
00066 using KMail::TeeHtmlWriter;
00067 #endif
00068 
00069 #include <mimelib/mimepp.h>
00070 #include <mimelib/body.h>
00071 #include <mimelib/utility.h>
00072 
00073 // KABC includes
00074 #include <kabc/addressee.h>
00075 #include <kabc/vcardconverter.h>
00076 
00077 // khtml headers
00078 #include <khtml_part.h>
00079 #include <khtmlview.h> // So that we can get rid of the frames
00080 #include <dom/html_element.h>
00081 #include <dom/html_block.h>
00082 #include <dom/html_document.h>
00083 #include <dom/dom_string.h>
00084 
00085 #include <kapplication.h>
00086 // for the click on attachment stuff (dnaber):
00087 #include <kuserprofile.h>
00088 #include <kcharsets.h>
00089 #include <kpopupmenu.h>
00090 #include <kstandarddirs.h>  // Sven's : for access and getpid
00091 #include <kcursor.h>
00092 #include <kdebug.h>
00093 #include <kfiledialog.h>
00094 #include <klocale.h>
00095 #include <kmessagebox.h>
00096 #include <kglobalsettings.h>
00097 #include <krun.h>
00098 #include <ktempfile.h>
00099 #include <kprocess.h>
00100 #include <kdialog.h>
00101 #include <kaction.h>
00102 #include <kiconloader.h>
00103 #include <kmdcodec.h>
00104 
00105 #include <qclipboard.h>
00106 #include <qhbox.h>
00107 #include <qtextcodec.h>
00108 #include <qpaintdevicemetrics.h>
00109 #include <qlayout.h>
00110 #include <qlabel.h>
00111 #include <qsplitter.h>
00112 #include <qstyle.h>
00113 
00114 // X headers...
00115 #undef Never
00116 #undef Always
00117 
00118 #include <unistd.h>
00119 #include <stdlib.h>
00120 #include <sys/stat.h>
00121 #include <errno.h>
00122 #include <stdio.h>
00123 #include <ctype.h>
00124 #include <string.h>
00125 
00126 #ifdef HAVE_PATHS_H
00127 #include <paths.h>
00128 #endif
00129 
00130 class NewByteArray : public QByteArray
00131 {
00132 public:
00133     NewByteArray &appendNULL();
00134     NewByteArray &operator+=( const char * );
00135     NewByteArray &operator+=( const QByteArray & );
00136     NewByteArray &operator+=( const QCString & );
00137     QByteArray& qByteArray();
00138 };
00139 
00140 NewByteArray& NewByteArray::appendNULL()
00141 {
00142     QByteArray::detach();
00143     uint len1 = size();
00144     if ( !QByteArray::resize( len1 + 1 ) )
00145         return *this;
00146     *(data() + len1) = '\0';
00147     return *this;
00148 }
00149 NewByteArray& NewByteArray::operator+=( const char * newData )
00150 {
00151     if ( !newData )
00152         return *this;
00153     QByteArray::detach();
00154     uint len1 = size();
00155     uint len2 = qstrlen( newData );
00156     if ( !QByteArray::resize( len1 + len2 ) )
00157         return *this;
00158     memcpy( data() + len1, newData, len2 );
00159     return *this;
00160 }
00161 NewByteArray& NewByteArray::operator+=( const QByteArray & newData )
00162 {
00163     if ( newData.isNull() )
00164         return *this;
00165     QByteArray::detach();
00166     uint len1 = size();
00167     uint len2 = newData.size();
00168     if ( !QByteArray::resize( len1 + len2 ) )
00169         return *this;
00170     memcpy( data() + len1, newData.data(), len2 );
00171     return *this;
00172 }
00173 NewByteArray& NewByteArray::operator+=( const QCString & newData )
00174 {
00175     if ( newData.isEmpty() )
00176         return *this;
00177     QByteArray::detach();
00178     uint len1 = size();
00179     uint len2 = newData.length(); // forget about the trailing 0x00 !
00180     if ( !QByteArray::resize( len1 + len2 ) )
00181         return *this;
00182     memcpy( data() + len1, newData.data(), len2 );
00183     return *this;
00184 }
00185 QByteArray& NewByteArray::qByteArray()
00186 {
00187     return *((QByteArray*)this);
00188 }
00189 
00190 
00191 
00192 // This function returns the complete data that were in this
00193 // message parts - *after* all encryption has been removed that
00194 // could be removed.
00195 // - This is used to store the message in decrypted form.
00196 void KMReaderWin::objectTreeToDecryptedMsg( partNode* node,
00197                                             NewByteArray& resultingData,
00198                                             KMMessage& theMessage,
00199                                             bool weAreReplacingTheRootNode,
00200                                             int recCount )
00201 {
00202   kdDebug(5006) << QString("-------------------------------------------------" ) << endl;
00203   kdDebug(5006) << QString("KMReaderWin::objectTreeToDecryptedMsg( %1 )  START").arg( recCount ) << endl;
00204   if( node ) {
00205     partNode* curNode = node;
00206     partNode* dataNode = curNode;
00207     partNode * child = node->firstChild();
00208     bool bIsMultipart = false;
00209 
00210     switch( curNode->type() ){
00211       case DwMime::kTypeText: {
00212 kdDebug(5006) << "* text *" << endl;
00213           switch( curNode->subType() ){
00214           case DwMime::kSubtypeHtml:
00215 kdDebug(5006) << "html" << endl;
00216             break;
00217           case DwMime::kSubtypeXVCard:
00218 kdDebug(5006) << "v-card" << endl;
00219             break;
00220           case DwMime::kSubtypeRichtext:
00221 kdDebug(5006) << "rich text" << endl;
00222             break;
00223           case DwMime::kSubtypeEnriched:
00224 kdDebug(5006) << "enriched " << endl;
00225             break;
00226           case DwMime::kSubtypePlain:
00227 kdDebug(5006) << "plain " << endl;
00228             break;
00229           default:
00230 kdDebug(5006) << "default " << endl;
00231             break;
00232           }
00233         }
00234         break;
00235       case DwMime::kTypeMultipart: {
00236 kdDebug(5006) << "* multipart *" << endl;
00237           bIsMultipart = true;
00238           switch( curNode->subType() ){
00239           case DwMime::kSubtypeMixed:
00240 kdDebug(5006) << "mixed" << endl;
00241             break;
00242           case DwMime::kSubtypeAlternative:
00243 kdDebug(5006) << "alternative" << endl;
00244             break;
00245           case DwMime::kSubtypeDigest:
00246 kdDebug(5006) << "digest" << endl;
00247             break;
00248           case DwMime::kSubtypeParallel:
00249 kdDebug(5006) << "parallel" << endl;
00250             break;
00251           case DwMime::kSubtypeSigned:
00252 kdDebug(5006) << "signed" << endl;
00253             break;
00254           case DwMime::kSubtypeEncrypted: {
00255 kdDebug(5006) << "encrypted" << endl;
00256               if ( child ) {
00257                 /*
00258                     ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
00259                 */
00260                 partNode* data =
00261                   child->findType( DwMime::kTypeApplication, DwMime::kSubtypeOctetStream, false, true );
00262                 if ( !data )
00263                   data = child->findType( DwMime::kTypeApplication, DwMime::kSubtypePkcs7Mime, false, true );
00264                 if ( data && data->firstChild() )
00265                   dataNode = data;
00266               }
00267             }
00268             break;
00269           default :
00270 kdDebug(5006) << "(  unknown subtype  )" << endl;
00271             break;
00272           }
00273         }
00274         break;
00275       case DwMime::kTypeMessage: {
00276 kdDebug(5006) << "* message *" << endl;
00277           switch( curNode->subType() ){
00278           case DwMime::kSubtypeRfc822: {
00279 kdDebug(5006) << "RfC 822" << endl;
00280               if ( child )
00281                 dataNode = child;
00282             }
00283             break;
00284           }
00285         }
00286         break;
00287       case DwMime::kTypeApplication: {
00288 kdDebug(5006) << "* application *" << endl;
00289           switch( curNode->subType() ){
00290           case DwMime::kSubtypePostscript:
00291 kdDebug(5006) << "postscript" << endl;
00292             break;
00293           case DwMime::kSubtypeOctetStream: {
00294 kdDebug(5006) << "octet stream" << endl;
00295               if ( child )
00296                 dataNode = child;
00297             }
00298             break;
00299           case DwMime::kSubtypePgpEncrypted:
00300 kdDebug(5006) << "pgp encrypted" << endl;
00301             break;
00302           case DwMime::kSubtypePgpSignature:
00303 kdDebug(5006) << "pgp signed" << endl;
00304             break;
00305           case DwMime::kSubtypePkcs7Mime: {
00306 kdDebug(5006) << "pkcs7 mime" << endl;
00307               // note: subtype Pkcs7Mime can also be signed
00308               //       and we do NOT want to remove the signature!
00309               if ( child && curNode->encryptionState() != KMMsgNotEncrypted )
00310                 dataNode = child;
00311             }
00312             break;
00313           }
00314         }
00315         break;
00316       case DwMime::kTypeImage: {
00317 kdDebug(5006) << "* image *" << endl;
00318           switch( curNode->subType() ){
00319           case DwMime::kSubtypeJpeg:
00320 kdDebug(5006) << "JPEG" << endl;
00321             break;
00322           case DwMime::kSubtypeGif:
00323 kdDebug(5006) << "GIF" << endl;
00324             break;
00325           }
00326         }
00327         break;
00328       case DwMime::kTypeAudio: {
00329 kdDebug(5006) << "* audio *" << endl;
00330           switch( curNode->subType() ){
00331           case DwMime::kSubtypeBasic:
00332 kdDebug(5006) << "basic" << endl;
00333             break;
00334           }
00335         }
00336         break;
00337       case DwMime::kTypeVideo: {
00338 kdDebug(5006) << "* video *" << endl;
00339           switch( curNode->subType() ){
00340           case DwMime::kSubtypeMpeg:
00341 kdDebug(5006) << "mpeg" << endl;
00342             break;
00343           }
00344         }
00345         break;
00346       case DwMime::kTypeModel:
00347 kdDebug(5006) << "* model *" << endl;
00348         break;
00349     }
00350 
00351 
00352     DwHeaders& rootHeaders( theMessage.headers() );
00353     DwBodyPart * part = dataNode->dwPart() ? dataNode->dwPart() : 0;
00354     DwHeaders * headers(
00355         (part && part->hasHeaders())
00356         ? &part->Headers()
00357         : (  (weAreReplacingTheRootNode || !dataNode->parentNode())
00358             ? &rootHeaders
00359             : 0 ) );
00360     if( dataNode == curNode ) {
00361 kdDebug(5006) << "dataNode == curNode:  Save curNode without replacing it." << endl;
00362 
00363       // A) Store the headers of this part IF curNode is not the root node
00364       //    AND we are not replacing a node that already *has* replaced
00365       //    the root node in previous recursion steps of this function...
00366       if( headers ) {
00367         if( dataNode->parentNode() && !weAreReplacingTheRootNode ) {
00368 kdDebug(5006) << "dataNode is NOT replacing the root node:  Store the headers." << endl;
00369           resultingData += headers->AsString().c_str();
00370         } else if( weAreReplacingTheRootNode && part->hasHeaders() ){
00371 kdDebug(5006) << "dataNode replace the root node:  Do NOT store the headers but change" << endl;
00372 kdDebug(5006) << "                                 the Message's headers accordingly." << endl;
00373 kdDebug(5006) << "              old Content-Type = " << rootHeaders.ContentType().AsString().c_str() << endl;
00374 kdDebug(5006) << "              new Content-Type = " << headers->ContentType(   ).AsString().c_str() << endl;
00375           rootHeaders.ContentType()             = headers->ContentType();
00376           theMessage.setContentTransferEncodingStr(
00377               headers->HasContentTransferEncoding()
00378             ? headers->ContentTransferEncoding().AsString().c_str()
00379             : "" );
00380           rootHeaders.ContentDescription() = headers->ContentDescription();
00381           rootHeaders.ContentDisposition() = headers->ContentDisposition();
00382           theMessage.setNeedsAssembly();
00383         }
00384       }
00385 
00386       // B) Store the body of this part.
00387       if( headers && bIsMultipart && dataNode->firstChild() )  {
00388 kdDebug(5006) << "is valid Multipart, processing children:" << endl;
00389         QCString boundary = headers->ContentType().Boundary().c_str();
00390         curNode = dataNode->firstChild();
00391         // store children of multipart
00392         while( curNode ) {
00393 kdDebug(5006) << "--boundary" << endl;
00394           if( resultingData.size() &&
00395               ( '\n' != resultingData.at( resultingData.size()-1 ) ) )
00396             resultingData += QCString( "\n" );
00397           resultingData += QCString( "\n" );
00398           resultingData += "--";
00399           resultingData += boundary;
00400           resultingData += "\n";
00401           // note: We are processing a harmless multipart that is *not*
00402           //       to be replaced by one of it's children, therefor
00403           //       we set their doStoreHeaders to true.
00404           objectTreeToDecryptedMsg( curNode,
00405                                     resultingData,
00406                                     theMessage,
00407                                     false,
00408                                     recCount + 1 );
00409           curNode = curNode->nextSibling();
00410         }
00411 kdDebug(5006) << "--boundary--" << endl;
00412         resultingData += "\n--";
00413         resultingData += boundary;
00414         resultingData += "--\n\n";
00415 kdDebug(5006) << "Multipart processing children - DONE" << endl;
00416       } else if( part ){
00417         // decrypt and store simple part
00418 kdDebug(5006) << "is Simple part or invalid Multipart, processing single body (if inline encrypted):" << endl;
00419         // Problem: body text may be inline PGP encrypted, so we can not just dump it.
00420         // resultingData += part->Body().AsString().c_str();
00421 
00422         // Note: parseObjectTree() does no inline PGP decrypting anymore.
00423         ObjectTreeParser otp( 0, 0, false, false, true );
00424         dataNode->setProcessed( false, true );
00425         otp.setKeepEncryptions( false );
00426         otp.parseObjectTree( curNode );
00427         //resultingData += otp.rawReplyString();  // re-enable this, once ObjectTreeParser is updated.
00428 
00429 
00430         // Temporary solution, to be replaced by a Kleo::CryptoBackend job inside ObjectTreeParser:
00431         bool bDecryptedInlinePGP = false;
00432         QPtrList<Kpgp::Block> pgpBlocks;
00433         QStrList nonPgpBlocks;
00434         if ( Kpgp::Module::prepareMessageForDecryption( otp.rawReplyString(),
00435                                                         pgpBlocks,
00436                                                         nonPgpBlocks ) ) {
00437           if ( pgpBlocks.count() == 1 ) {
00438             Kpgp::Block * block = pgpBlocks.first();
00439             if ( block->type() == Kpgp::PgpMessageBlock ) {
00440               // try to decrypt this OpenPGP block
00441               block->decrypt();
00442               resultingData += nonPgpBlocks.first() + block->text() + nonPgpBlocks.last();
00443               bDecryptedInlinePGP = true;
00444             }
00445           }
00446         }
00447         if( !bDecryptedInlinePGP )
00448           resultingData += otp.rawReplyString();
00449         // end of temporary solution.
00450 
00451 
00452 kdDebug(5006) << "decrypting of single body - DONE" << endl;
00453       }
00454     } else {
00455 kdDebug(5006) << "dataNode != curNode:  Replace curNode by dataNode." << endl;
00456       bool rootNodeReplaceFlag = weAreReplacingTheRootNode || !curNode->parentNode();
00457       if( rootNodeReplaceFlag ) {
00458 kdDebug(5006) << "                      Root node will be replaced." << endl;
00459       } else {
00460 kdDebug(5006) << "                      Root node will NOT be replaced." << endl;
00461       }
00462       // store special data to replace the current part
00463       // (e.g. decrypted data or embedded RfC 822 data)
00464       objectTreeToDecryptedMsg( dataNode,
00465                                 resultingData,
00466                                 theMessage,
00467                                 rootNodeReplaceFlag,
00468                                 recCount + 1 );
00469     }
00470   }
00471   kdDebug(5006) << QString("\nKMReaderWin::objectTreeToDecryptedMsg( %1 )  END").arg( recCount ) << endl;
00472 }
00473 
00474 
00475 /*
00476  ===========================================================================
00477 
00478 
00479         E N D    O F     T E M P O R A R Y     M I M E     C O D E
00480 
00481 
00482  ===========================================================================
00483 */
00484 
00485 
00486 
00487 
00488 
00489 
00490 
00491 
00492 
00493 
00494 
00495 void KMReaderWin::createWidgets() {
00496   QVBoxLayout * vlay = new QVBoxLayout( this );
00497   mSplitter = new QSplitter( Qt::Vertical, this, "mSplitter" );
00498   vlay->addWidget( mSplitter );
00499   mMimePartTree = new KMMimePartTree( this, mSplitter, "mMimePartTree" );
00500   mBox = new QHBox( mSplitter, "mBox" );
00501   setStyleDependantFrameWidth();
00502   mBox->setFrameStyle( mMimePartTree->frameStyle() );
00503   mColorBar = new HtmlStatusBar( mBox, "mColorBar" );
00504   mViewer = new KHTMLPart( mBox, "mViewer" );
00505   mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
00506   mSplitter->setResizeMode( mMimePartTree, QSplitter::KeepSize );
00507 }
00508 
00509 const int KMReaderWin::delay = 150;
00510 
00511 //-----------------------------------------------------------------------------
00512 KMReaderWin::KMReaderWin(QWidget *aParent,
00513              QWidget *mainWindow,
00514              KActionCollection* actionCollection,
00515                          const char *aName,
00516                          int aFlags )
00517   : QWidget(aParent, aName, aFlags | Qt::WDestructiveClose),
00518     mAttachmentStrategy( 0 ),
00519     mHeaderStrategy( 0 ),
00520     mHeaderStyle( 0 ),
00521     mOverrideCodec( 0 ),
00522     mCSSHelper( 0 ),
00523     mRootNode( 0 ),
00524     mMainWindow( mainWindow ),
00525     mHtmlWriter( 0 )
00526 {
00527   mSplitterSizes << 180 << 100;
00528   mMimeTreeMode = 1;
00529   mMimeTreeAtBottom = true;
00530   mAutoDelete = false;
00531   mLastSerNum = 0;
00532   mWaitingForSerNum = 0;
00533   mMessage = 0;
00534   mLastStatus = KMMsgStatusUnknown;
00535   mMsgDisplay = true;
00536   mPrinting = false;
00537   mShowColorbar = false;
00538   mAtmUpdate = false;
00539 
00540   createWidgets();
00541   initHtmlWidget();
00542   readConfig();
00543 
00544   mHtmlOverride = false;
00545 
00546   connect( &updateReaderWinTimer, SIGNAL(timeout()),
00547        this, SLOT(updateReaderWin()) );
00548   connect( &mResizeTimer, SIGNAL(timeout()),
00549        this, SLOT(slotDelayedResize()) );
00550   connect( &mDelayedMarkTimer, SIGNAL(timeout()),
00551            this, SLOT(slotTouchMessage()) );
00552 
00553   createActions( actionCollection );
00554 }
00555 
00556 void KMReaderWin::createActions( KActionCollection * ac ) {
00557   if ( !ac )
00558       return;
00559 
00560   mMailToComposeAction = new KAction( i18n("New Message To..."), 0, this,
00561                     SLOT(slotMailtoCompose()), ac,
00562                     "mailto_compose" );
00563   mMailToReplyAction = new KAction( i18n("Reply To..."), 0, this,
00564                     SLOT(slotMailtoReply()), ac,
00565                     "mailto_reply" );
00566   mMailToForwardAction = new KAction( i18n("Forward To..."),
00567                     0, this, SLOT(slotMailtoForward()), ac,
00568                     "mailto_forward" );
00569   mAddAddrBookAction = new KAction( i18n("Add to Address Book"),
00570                     0, this, SLOT(slotMailtoAddAddrBook()),
00571                     ac, "add_addr_book" );
00572   mOpenAddrBookAction = new KAction( i18n("Open in Address Book"),
00573                     0, this, SLOT(slotMailtoOpenAddrBook()),
00574                     ac, "openin_addr_book" );
00575   mCopyAction = new KAction( i18n("Copy to Clipboard"), 0, this,
00576                  SLOT(slotUrlCopy()), ac, "copy_address" );
00577   mCopyURLAction = new KAction( i18n("Copy Link Address"), 0, this,
00578                 SLOT(slotUrlCopy()), ac, "copy_url" );
00579   mUrlOpenAction = new KAction( i18n("Open URL"), 0, this,
00580                  SLOT(slotUrlOpen()), ac, "open_url" );
00581   mAddBookmarksAction = new KAction( i18n("Bookmark This Link"),
00582                                      "bookmark_add",
00583                                      0, this, SLOT(slotAddBookmarks()),
00584                                      ac, "add_bookmarks" );
00585   mUrlSaveAsAction = new KAction( i18n("Save Link As..."), 0, this,
00586                  SLOT(slotUrlSave()), ac, "saveas_url" );
00587   mViewSourceAction = new KAction( i18n("&View Source"), Key_V, this,
00588               SLOT(slotShowMsgSrc()), ac, "view_source" );
00589 
00590   mToggleFixFontAction = new KToggleAction( i18n("Use Fi&xed Font"),
00591             Key_X, this, SLOT(slotToggleFixedFont()),
00592             ac, "toggle_fixedfont" );
00593 
00594   mStartIMChatAction = new KAction( i18n("Chat &With..."), 0, this,
00595                     SLOT(slotIMChat()), ac, "start_im_chat" );
00596 }
00597 
00598 
00599 //-----------------------------------------------------------------------------
00600 KMReaderWin::~KMReaderWin()
00601 {
00602   delete mHtmlWriter; mHtmlWriter = 0;
00603   if (mAutoDelete) delete message();
00604   delete mRootNode; mRootNode = 0;
00605   removeTempFiles();
00606 }
00607 
00608 
00609 //-----------------------------------------------------------------------------
00610 void KMReaderWin::slotMessageArrived( KMMessage *msg )
00611 {
00612   if (msg && ((KMMsgBase*)msg)->isMessage()) {
00613     if ( msg->getMsgSerNum() == mWaitingForSerNum ) {
00614       setMsg( msg, true );
00615     } else {
00616       kdDebug( 5006 ) <<  "KMReaderWin::slotMessageArrived - ignoring update" << endl;
00617     }
00618   }
00619 }
00620 
00621 //-----------------------------------------------------------------------------
00622 void KMReaderWin::update( KMail::Interface::Observable * observable ) {
00623   if ( !mAtmUpdate ) {
00624     kdDebug(5006) << "KMReaderWin::update - message" << endl;
00625     updateReaderWin();
00626     return;
00627   }
00628 
00629   if ( !mRootNode )
00630     return;
00631 
00632   kdDebug(5006) << "KMReaderWin::update - attachment " << mAtmCurrentName << endl;
00633   partNode * node = mRootNode->findId( mAtmCurrent );
00634   if ( !node ) {
00635     kdWarning(5006) << "KMReaderWin::update - Could not find node for attachment!" << endl;
00636     return;
00637   }
00638 
00639   assert( dynamic_cast<KMMessage*>( observable ) != 0 );
00640   // if the assert ever fails, this curious construction needs to
00641   // be rethought:
00642 
00643   // replace the dwpart of the node
00644   node->setDwPart( static_cast<KMMessage*>( observable )->lastUpdatedPart() );
00645   // update the tmp file
00646   // we have to set it writeable temporarily
00647   ::chmod( QFile::encodeName( mAtmCurrentName ), S_IRWXU );
00648   KPIM::kByteArrayToFile( node->msgPart().bodyDecodedBinary(), mAtmCurrentName,
00649             false, false, true );
00650   ::chmod( QFile::encodeName( mAtmCurrentName ), S_IRUSR );
00651 
00652   // no need to redisplay here as we only replaced the tmp file so that
00653   // the desired function (e.g. save) can work with it
00654 }
00655 
00656 //-----------------------------------------------------------------------------
00657 void KMReaderWin::removeTempFiles()
00658 {
00659   for (QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end();
00660     it++)
00661   {
00662     QFile::remove(*it);
00663   }
00664   mTempFiles.clear();
00665   for (QStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end();
00666     it++)
00667   {
00668     QDir(*it).rmdir(*it);
00669   }
00670   mTempDirs.clear();
00671 }
00672 
00673 
00674 //-----------------------------------------------------------------------------
00675 bool KMReaderWin::event(QEvent *e)
00676 {
00677   if (e->type() == QEvent::ApplicationPaletteChange)
00678   {
00679     delete mCSSHelper;
00680     mCSSHelper = new CSSHelper( QPaintDeviceMetrics( mViewer->view() ), this );
00681     if (message())
00682       message()->readConfig();
00683     update( true ); // Force update
00684     return true;
00685   }
00686   return QWidget::event(e);
00687 }
00688 
00689 
00690 //-----------------------------------------------------------------------------
00691 void KMReaderWin::readConfig(void)
00692 {
00693   const KConfigGroup mdnGroup( KMKernel::config(), "MDN" );
00694   /*should be: const*/ KConfigGroup reader( KMKernel::config(), "Reader" );
00695 
00696   delete mCSSHelper;
00697   mCSSHelper = new CSSHelper( QPaintDeviceMetrics( mViewer->view() ), this );
00698 
00699   mNoMDNsWhenEncrypted = mdnGroup.readBoolEntry( "not-send-when-encrypted", true );
00700 
00701   // initialize useFixedFont from the saved value; the corresponding toggle
00702   // action is initialized in the main window
00703   mUseFixedFont = reader.readBoolEntry( "useFixedFont", false );
00704   mHtmlMail = reader.readBoolEntry( "htmlMail", false );
00705   setHeaderStyleAndStrategy( HeaderStyle::create( reader.readEntry( "header-style", "fancy" ) ),
00706                  HeaderStrategy::create( reader.readEntry( "header-set-displayed", "rich" ) ) );
00707 
00708   mAttachmentStrategy =
00709     AttachmentStrategy::create( reader.readEntry( "attachment-strategy" ) );
00710 
00711   mViewer->setOnlyLocalReferences( !reader.readBoolEntry( "htmlLoadExternal", false ) );
00712 
00713   // if the user uses OpenPGP then the color bar defaults to enabled
00714   // else it defaults to disabled
00715   mShowColorbar = reader.readBoolEntry( "showColorbar", Kpgp::Module::getKpgp()->usePGP() );
00716   // if the value defaults to enabled and KMail (with color bar) is used for
00717   // the first time the config dialog doesn't know this if we don't save the
00718   // value now
00719   reader.writeEntry( "showColorbar", mShowColorbar );
00720 
00721   mMimeTreeAtBottom = reader.readEntry( "MimeTreeLocation", "bottom" ) != "top";
00722   const QString s = reader.readEntry( "MimeTreeMode", "smart" );
00723   if ( s == "never" )
00724     mMimeTreeMode = 0;
00725   else if ( s == "always" )
00726     mMimeTreeMode = 2;
00727   else
00728     mMimeTreeMode = 1;
00729 
00730   const int mimeH = reader.readNumEntry( "MimePaneHeight", 100 );
00731   const int messageH = reader.readNumEntry( "MessagePaneHeight", 180 );
00732   mSplitterSizes.clear();
00733   if ( mMimeTreeAtBottom )
00734     mSplitterSizes << messageH << mimeH;
00735   else
00736     mSplitterSizes << mimeH << messageH;
00737 
00738   adjustLayout();
00739 
00740   if (message())
00741     update();
00742   KMMessage::readConfig();
00743 }
00744 
00745 
00746 void KMReaderWin::adjustLayout() {
00747   if ( mMimeTreeAtBottom )
00748     mSplitter->moveToLast( mMimePartTree );
00749   else
00750     mSplitter->moveToFirst( mMimePartTree );
00751   mSplitter->setSizes( mSplitterSizes );
00752 
00753   if ( mMimeTreeMode == 2 && mMsgDisplay )
00754     mMimePartTree->show();
00755   else
00756     mMimePartTree->hide();
00757 
00758   if ( mShowColorbar && mMsgDisplay )
00759     mColorBar->show();
00760   else
00761     mColorBar->hide();
00762 }
00763 
00764 
00765 void KMReaderWin::saveSplitterSizes( KConfigBase & c ) const {
00766   if ( !mSplitter || !mMimePartTree )
00767     return;
00768   if ( mMimePartTree->isHidden() )
00769     return; // don't rely on QSplitter maintaining sizes for hidden widgets.
00770 
00771   c.writeEntry( "MimePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 1 : 0 ] );
00772   c.writeEntry( "MessagePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 0 : 1 ] );
00773 }
00774 
00775 //-----------------------------------------------------------------------------
00776 void KMReaderWin::writeConfig( bool sync ) const {
00777   KConfigGroup reader( KMKernel::config(), "Reader" );
00778 
00779   reader.writeEntry( "useFixedFont", mUseFixedFont );
00780   if ( headerStyle() )
00781     reader.writeEntry( "header-style", headerStyle()->name() );
00782   if ( headerStrategy() )
00783     reader.writeEntry( "header-set-displayed", headerStrategy()->name() );
00784   if ( attachmentStrategy() )
00785     reader.writeEntry( "attachment-strategy", attachmentStrategy()->name() );
00786 
00787   saveSplitterSizes( reader );
00788 
00789   if ( sync )
00790     kmkernel->slotRequestConfigSync();
00791 }
00792 
00793 //-----------------------------------------------------------------------------
00794 void KMReaderWin::initHtmlWidget(void)
00795 {
00796   mViewer->widget()->setFocusPolicy(WheelFocus);
00797   // Let's better be paranoid and disable plugins (it defaults to enabled):
00798   mViewer->setPluginsEnabled(false);
00799   mViewer->setJScriptEnabled(false); // just make this explicit
00800   mViewer->setJavaEnabled(false);    // just make this explicit
00801   mViewer->setMetaRefreshEnabled(false);
00802   mViewer->setURLCursor(KCursor::handCursor());
00803   // Espen 2000-05-14: Getting rid of thick ugly frames
00804   mViewer->view()->setLineWidth(0);
00805 
00806   if ( !htmlWriter() )
00807 #ifdef KMAIL_READER_HTML_DEBUG
00808     mHtmlWriter = new TeeHtmlWriter( new FileHtmlWriter( QString::null ),
00809                      new KHtmlPartHtmlWriter( mViewer, 0 ) );
00810 #else
00811     mHtmlWriter = new KHtmlPartHtmlWriter( mViewer, 0 );
00812 #endif
00813 
00814   connect(mViewer->browserExtension(),
00815           SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),this,
00816           SLOT(slotUrlOpen(const KURL &)));
00817   connect(mViewer->browserExtension(),
00818           SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this,
00819           SLOT(slotUrlOpen(const KURL &)));
00820   connect(mViewer,SIGNAL(onURL(const QString &)),this,
00821           SLOT(slotUrlOn(const QString &)));
00822   connect(mViewer,SIGNAL(popupMenu(const QString &, const QPoint &)),
00823           SLOT(slotUrlPopup(const QString &, const QPoint &)));
00824   connect( kmkernel->imProxy(), SIGNAL( sigContactPresenceChanged( const QString & ) ),
00825           this, SLOT( contactStatusChanged( const QString & ) ) );
00826   connect( kmkernel->imProxy(), SIGNAL( sigPresenceInfoExpired() ),
00827           this, SLOT( updateReaderWin() ) );
00828 }
00829 
00830 void KMReaderWin::contactStatusChanged( const QString &uid)
00831 {
00832     kdDebug( 5006 ) << k_funcinfo << " got a presence change for " << uid << endl;
00833     // get the list of nodes for this contact from the htmlView
00834     DOM::NodeList presenceNodes = mViewer->htmlDocument()
00835         .getElementsByName( DOM::DOMString( QString::fromLatin1("presence-") + uid ) );
00836     for ( unsigned int i = 0; i < presenceNodes.length(); ++i )
00837     {
00838         DOM::Node n =  presenceNodes.item( i );
00839         kdDebug( 5006 ) << "name is " << n.nodeName().string() << endl;
00840         kdDebug( 5006 ) << "value of content was " << n.firstChild().nodeValue().string() << endl;
00841         QString newPresence = kmkernel->imProxy()->presenceString( uid );
00842         if ( newPresence.isNull() ) // KHTML crashes if you setNodeValue( QString::null )
00843             newPresence = QString::fromLatin1( "ENOIMRUNNING" );
00844         n.firstChild().setNodeValue( newPresence );
00845         kdDebug( 5006 ) << "value of content is now " << n.firstChild().nodeValue().string() << endl;
00846     }
00847     kdDebug( 5006 ) << "and we updated the above presence nodes" << uid << endl;
00848 }
00849 
00850 void KMReaderWin::setAttachmentStrategy( const AttachmentStrategy * strategy ) {
00851   mAttachmentStrategy = strategy ? strategy : AttachmentStrategy::smart() ;
00852   update( true );
00853 }
00854 
00855 void KMReaderWin::setHeaderStyleAndStrategy( const HeaderStyle * style,
00856                          const HeaderStrategy * strategy ) {
00857   mHeaderStyle = style ? style : HeaderStyle::fancy() ;
00858   mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich() ;
00859   update( true );
00860 }
00861 
00862 void KMReaderWin::setOverrideCodec( const QTextCodec * codec ) {
00863   if ( mOverrideCodec == codec )
00864     return;
00865   mOverrideCodec = codec;
00866   update( true );
00867 }
00868 
00869 //-----------------------------------------------------------------------------
00870 void KMReaderWin::setMsg(KMMessage* aMsg, bool force)
00871 {
00872   if (aMsg)
00873       kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " "
00874         << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl;
00875 
00876   bool complete = true;
00877   if ( aMsg &&
00878        !aMsg->readyToShow() &&
00879        (aMsg->getMsgSerNum() != mLastSerNum) &&
00880        !aMsg->isComplete() )
00881     complete = false;
00882 
00883   // If not forced and there is aMsg and aMsg is same as mMsg then return
00884   if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum)
00885     return;
00886 
00887   // (de)register as observer
00888   if (aMsg && message())
00889     message()->detach( this );
00890   if (aMsg)
00891     aMsg->attach( this );
00892   mAtmUpdate = false;
00893 
00894   // connect to the updates if we have hancy headers
00895 
00896   mDelayedMarkTimer.stop();
00897 
00898   mLastSerNum = (aMsg) ? aMsg->getMsgSerNum() : 0;
00899   if ( !aMsg ) mWaitingForSerNum = 0; // otherwise it has been set
00900 
00901   // assume if a serial number exists it can be used to find the assoc KMMessage
00902   if (mLastSerNum <= 0)
00903     mMessage = aMsg;
00904   else
00905     mMessage = 0;
00906   if (message() != aMsg) {
00907     mMessage = aMsg;
00908     mLastSerNum = 0; // serial number was invalid
00909     Q_ASSERT(0);
00910   }
00911 
00912   if (aMsg) {
00913     aMsg->setOverrideCodec( overrideCodec() );
00914     aMsg->setDecodeHTML( htmlMail() );
00915     mLastStatus = aMsg->status();
00916     // FIXME: workaround to disable DND for IMAP load-on-demand
00917     if ( !aMsg->isComplete() )
00918       mViewer->setDNDEnabled( false );
00919     else
00920       mViewer->setDNDEnabled( true );
00921   } else {
00922     mLastStatus = KMMsgStatusUnknown;
00923   }
00924 
00925   // only display the msg if it is complete
00926   // otherwise we'll get flickering with progressively loaded messages
00927   if ( complete )
00928   {
00929     // Avoid flicker, somewhat of a cludge
00930     if (force) {
00931       // stop the timer to avoid calling updateReaderWin twice
00932       updateReaderWinTimer.stop();
00933       updateReaderWin();
00934     }
00935     else if (updateReaderWinTimer.isActive())
00936       updateReaderWinTimer.changeInterval( delay );
00937     else
00938       updateReaderWinTimer.start( 0, TRUE );
00939   }
00940 
00941   if ( GlobalSettings::delayedMarkAsRead() ) {
00942     if ( GlobalSettings::delayedMarkTime() != 0 )
00943       mDelayedMarkTimer.start( GlobalSettings::delayedMarkTime() * 1000, TRUE );
00944     else
00945       slotTouchMessage();
00946   }
00947 }
00948 
00949 //-----------------------------------------------------------------------------
00950 void KMReaderWin::clearCache()
00951 {
00952   updateReaderWinTimer.stop();
00953   clear();
00954   mDelayedMarkTimer.stop();
00955   mLastSerNum = 0;
00956   mWaitingForSerNum = 0;
00957   mMessage = 0;
00958 }
00959 
00960 // enter items for the "Important changes" list here:
00961 static const char * const kmailChanges[] = {
00962   I18N_NOOP("Support for 3rd-party CryptPlugs has been discontinued. "
00963         "Support for the GnuPG cryptographic backend is now included "
00964         "directly in KMail.")
00965 };
00966 static const int numKMailChanges =
00967   sizeof kmailChanges / sizeof *kmailChanges;
00968 
00969 // enter items for the "new features" list here, so the main body of
00970 // the welcome page can be left untouched (probably much easier for
00971 // the translators). Note that the <li>...</li> tags are added
00972 // automatically below:
00973 static const char * const kmailNewFeatures[] = {
00974   I18N_NOOP( "Antispam wizard" ),
00975   I18N_NOOP( "Filter log" ),
00976   I18N_NOOP( "Quick search" ),
00977   I18N_NOOP( "Automatic mailing-list detection" ),
00978   I18N_NOOP( "View/open message files" ),
00979   I18N_NOOP( "HTML message composing" ),
00980   I18N_NOOP( "New filter criteria: in address book, in category, has attachment" ),
00981   I18N_NOOP("Cryptographic backend auto-configuration"),
00982   I18N_NOOP("Sign/encrypt key separation"),
00983   I18N_NOOP("Per-identity S/MIME key preselection"),
00984   I18N_NOOP("Per-identity cryptographic message format preselection"),
00985   I18N_NOOP("Per-contact crypto preferences"),
00986   I18N_NOOP("List only opened IMAP folders"),
00987 };
00988 static const int numKMailNewFeatures =
00989   sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
00990 
00991 
00992 //-----------------------------------------------------------------------------
00993 //static
00994 QString KMReaderWin::newFeaturesMD5()
00995 {
00996   QCString str;
00997   for ( int i = 0 ; i < numKMailChanges ; ++i )
00998     str += kmailChanges[i];
00999   for ( int i = 0 ; i < numKMailNewFeatures ; ++i )
01000     str += kmailNewFeatures[i];
01001   KMD5 md5( str );
01002   return md5.base64Digest();
01003 }
01004 
01005 //-----------------------------------------------------------------------------
01006 void KMReaderWin::displayAboutPage()
01007 {
01008   mMsgDisplay = false;
01009   adjustLayout();
01010 
01011   QString location = locate("data", "kmail/about/main.html");
01012   QString content = KPIM::kFileToString(location);
01013   mViewer->begin(KURL( location ));
01014   QString info =
01015     i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; "
01016      "%4: prior KMail version; %5: prior KDE version; "
01017      "%6: generated list of new features; "
01018      "%7: First-time user text (only shown on first start); "
01019      "%8: prior KMail version; "
01020          "%9: generated list of important changes; "
01021      "--- end of comment ---",
01022      "<h2>Welcome to KMail %1</h2><p>KMail is the email client for the K "
01023      "Desktop Environment. It is designed to be fully compatible with "
01024      "Internet mailing standards including MIME, SMTP, POP3 and IMAP."
01025      "</p>\n"
01026      "<ul><li>KMail has many powerful features which are described in the "
01027      "<a href=\"%2\">documentation</a></li>\n"
01028      "<li>The <a href=\"%3\">KMail homepage</A> offers information about "
01029      "new versions of KMail</li></ul>\n"
01030          "<p><span style='font-size:125%; font-weight:bold;'>"
01031          "Important changes</span> (compared to KMail %8):</p>\n"
01032      "<ul>\n%9</ul>\n"
01033      "<p>Some of the new features in this release of KMail include "
01034      "(compared to KMail %4, which is part of KDE %5):</p>\n"
01035      "<ul>\n%6</ul>\n"
01036      "%7\n"
01037      "<p>We hope that you will enjoy KMail.</p>\n"
01038      "<p>Thank you,</p>\n"
01039      "<p>&nbsp; &nbsp; The KMail Team</p>")
01040     .arg(KMAIL_VERSION) // KMail version
01041     .arg("help:/kmail/index.html") // KMail help:// URL
01042     .arg("http://kmail.kde.org/") // KMail homepage URL
01043     .arg("1.6").arg("3.2"); // prior KMail and KDE version
01044 
01045   QString featureItems;
01046   for ( int i = 0 ; i < numKMailNewFeatures ; i++ )
01047     featureItems += i18n("<li>%1</li>\n").arg( i18n( kmailNewFeatures[i] ) );
01048 
01049   info = info.arg( featureItems );
01050 
01051   if( kmkernel->firstStart() ) {
01052     info = info.arg( i18n("<p>Please take a moment to fill in the KMail "
01053               "configuration panel at Settings-&gt;Configure "
01054               "KMail.\n"
01055               "You need to create at least a default identity and "
01056               "an incoming as well as outgoing mail account."
01057               "</p>\n") );
01058   } else {
01059     info = info.arg( QString::null );
01060   }
01061 
01062   QString changesItems;
01063   for ( int i = 0 ; i < numKMailChanges ; i++ )
01064     changesItems += i18n("<li>%1</li>\n").arg( i18n( kmailChanges[i] ) );
01065 
01066   info = info.arg("1.6").arg( changesItems );
01067 
01068   mViewer->write(content.arg(pointsToPixel( mCSSHelper->bodyFont().pointSize() )).arg(info));
01069   mViewer->end();
01070 }
01071 
01072 void KMReaderWin::enableMsgDisplay() {
01073   mMsgDisplay = true;
01074   adjustLayout();
01075 }
01076 
01077 
01078 //-----------------------------------------------------------------------------
01079 
01080 void KMReaderWin::updateReaderWin()
01081 {
01082   if (!mMsgDisplay) return;
01083 
01084   htmlWriter()->reset();
01085 
01086   KMFolder* folder;
01087   if (message(&folder))
01088   {
01089     if( !kmkernel->iCalIface().isResourceImapFolder( folder ) ){
01090       if ( mShowColorbar )
01091         mColorBar->show();
01092       else
01093         mColorBar->hide();
01094       displayMessage();
01095     }
01096   }
01097   else
01098   {
01099     mColorBar->hide();
01100     mMimePartTree->hide();
01101     mMimePartTree->clear();
01102     htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01103     htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) + "</body></html>" );
01104     htmlWriter()->end();
01105   }
01106 }
01107 
01108 //-----------------------------------------------------------------------------
01109 int KMReaderWin::pointsToPixel(int pointSize) const
01110 {
01111   const QPaintDeviceMetrics pdm(mViewer->view());
01112 
01113   return (pointSize * pdm.logicalDpiY() + 36) / 72;
01114 }
01115 
01116 //-----------------------------------------------------------------------------
01117 void KMReaderWin::showHideMimeTree( bool isPlainTextTopLevel ) {
01118   if ( mMimeTreeMode == 2 ||
01119        ( mMimeTreeMode == 1 && !isPlainTextTopLevel ) )
01120     mMimePartTree->show();
01121   else {
01122     // don't rely on QSplitter maintaining sizes for hidden widgets:
01123     KConfigGroup reader( KMKernel::config(), "Reader" );
01124     saveSplitterSizes( reader );
01125     mMimePartTree->hide();
01126   }
01127 }
01128 
01129 void KMReaderWin::displayMessage() {
01130   KMMessage * msg = message();
01131 
01132   mMimePartTree->clear();
01133   showHideMimeTree( !msg || // treat no message as "text/plain"
01134             ( msg->type() == DwMime::kTypeText
01135               && msg->subtype() == DwMime::kSubtypePlain ) );
01136 
01137   if ( !msg )
01138     return;
01139 
01140   msg->setOverrideCodec( overrideCodec() );
01141 
01142   htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01143   htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
01144 
01145   if (!parent())
01146     setCaption(msg->subject());
01147 
01148   removeTempFiles();
01149 
01150   mColorBar->setNeutralMode();
01151 
01152   parseMsg(msg);
01153 
01154   if( mColorBar->isNeutral() )
01155     mColorBar->setNormalMode();
01156 
01157   htmlWriter()->queue("</body></html>");
01158   htmlWriter()->flush();
01159 }
01160 
01161 
01162 //-----------------------------------------------------------------------------
01163 void KMReaderWin::parseMsg(KMMessage* aMsg)
01164 {
01165 #ifndef NDEBUG
01166   kdDebug( 5006 )
01167     << "parseMsg(KMMessage* aMsg "
01168     << ( aMsg == message() ? "==" : "!=" )
01169     << " aMsg )" << endl;
01170 #endif
01171 
01172   KMMessagePart msgPart;
01173   QCString subtype, contDisp;
01174   QByteArray str;
01175 
01176   assert(aMsg!=0);
01177 
01178   delete mRootNode;
01179   mRootNode = partNode::fromMessage( aMsg );
01180   const QCString mainCntTypeStr = mRootNode->typeString() + '/' + mRootNode->subTypeString();
01181 
01182   QString cntDesc = aMsg->subject();
01183   if( cntDesc.isEmpty() )
01184     cntDesc = i18n("( body part )");
01185   KIO::filesize_t cntSize = aMsg->msgSize();
01186   QString cntEnc;
01187   if( aMsg->contentTransferEncodingStr().isEmpty() )
01188     cntEnc = "7bit";
01189   else
01190     cntEnc = aMsg->contentTransferEncodingStr();
01191 
01192   // fill the MIME part tree viewer
01193   mRootNode->fillMimePartTree( 0,
01194                    mMimePartTree,
01195                    cntDesc,
01196                    mainCntTypeStr,
01197                    cntEnc,
01198                    cntSize );
01199 
01200   partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard );
01201   bool hasVCard = false;
01202   if( vCardNode ) {
01203     // ### FIXME: We should only do this if the vCard belongs to the sender,
01204     // ### i.e. if the sender's email address is contained in the vCard.
01205     const QString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() );
01206     KABC::VCardConverter t;
01207     if ( !t.parseVCards( vcard ).empty() ) {
01208       hasVCard = true;
01209       kdDebug(5006) << "FOUND A VALID VCARD" << endl;
01210       writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() );
01211     }
01212   }
01213   htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard) );
01214 
01215   // show message content
01216   ObjectTreeParser otp( this );
01217   otp.parseObjectTree( mRootNode );
01218 
01219   // store encrypted/signed status information in the KMMessage
01220   //  - this can only be done *after* calling parseObjectTree()
01221   KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState();
01222   KMMsgSignatureState  signatureState  = mRootNode->overallSignatureState();
01223   aMsg->setEncryptionState( encryptionState );
01224   aMsg->setSignatureState(  signatureState  );
01225 
01226   bool emitReplaceMsgByUnencryptedVersion = false;
01227   const KConfigGroup reader( KMKernel::config(), "Reader" );
01228   if ( reader.readBoolEntry( "store-displayed-messages-unencrypted", false ) ) {
01229 
01230     // Hack to make sure the S/MIME CryptPlugs follows the strict requirement
01231     // of german government:
01232     // --> All received encrypted messages *must* be stored in unencrypted form
01233     //     after they have been decrypted once the user has read them.
01234     //     ( "Aufhebung der Verschluesselung nach dem Lesen" )
01235     //
01236     // note: Since there is no configuration option for this, we do that for
01237     //       all kinds of encryption now - *not* just for S/MIME.
01238     //       This could be changed in the objectTreeToDecryptedMsg() function
01239     //       by deciding when (or when not, resp.) to set the 'dataNode' to
01240     //       something different than 'curNode'.
01241 
01242 kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg()  -  special post-encryption handling:\n1." << endl;
01243 /*
01244 kdDebug(5006) << "(aMsg == msg) = "                               << (aMsg == message()) << endl;
01245 kdDebug(5006) << "   (KMMsgStatusUnknown & mLastStatus) = "           << (KMMsgStatusUnknown & mLastStatus) << endl;
01246 kdDebug(5006) << "|| (KMMsgStatusNew     & mLastStatus) = "           << (KMMsgStatusNew     & mLastStatus) << endl;
01247 kdDebug(5006) << "|| (KMMsgStatusUnread  & mLastStatus) = "           << (KMMsgStatusUnread  & mLastStatus) << endl;
01248 kdDebug(5006) << "(mIdOfLastViewedMessage != aMsg->msgId()) = "    << (mIdOfLastViewedMessage != aMsg->msgId()) << endl;
01249 kdDebug(5006) << "   (KMMsgFullyEncrypted == encryptionState) = "     << (KMMsgFullyEncrypted == encryptionState) << endl;
01250 kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl;
01251 */
01252     // only proceed if we were called the normal way - not by
01253     // double click on the message (==not running in a separate window)
01254     if(    (aMsg == message())
01255           // only proceed if this message was not saved encryptedly before
01256           // to make sure only *new* messages are saved in decrypted form
01257         && ((KMMsgStatusUnknown | KMMsgStatusNew | KMMsgStatusUnread) & mLastStatus)
01258           // avoid endless recursions
01259         && (mIdOfLastViewedMessage != aMsg->msgId())
01260           // only proceed if this message is (at least partially) encrypted
01261         && (    (KMMsgFullyEncrypted == encryptionState)
01262              || (KMMsgPartiallyEncrypted == encryptionState) ) ) {
01263 
01264 kdDebug(5006) << "KMReaderWin  -  calling objectTreeToDecryptedMsg()" << endl;
01265 
01266       NewByteArray decryptedData;
01267       // note: The following call may change the message's headers.
01268       objectTreeToDecryptedMsg( mRootNode, decryptedData, *aMsg );
01269       // add a \0 to the data
01270       decryptedData.appendNULL();
01271       QCString resultString( decryptedData.data() );
01272 kdDebug(5006) << "KMReaderWin  -  resulting data:" << resultString << endl;
01273 
01274       if( !resultString.isEmpty() ) {
01275 kdDebug(5006) << "KMReaderWin  -  composing unencrypted message" << endl;
01276         // try this:
01277         aMsg->setBody( resultString );
01278         KMMessage* unencryptedMessage = new KMMessage( *aMsg );
01279         // because this did not work:
01280         /*
01281         DwMessage dwMsg( DwString( aMsg->asString() ) );
01282         dwMsg.Body() = DwBody( DwString( resultString.data() ) );
01283         dwMsg.Body().Parse();
01284         KMMessage* unencryptedMessage = new KMMessage( &dwMsg );
01285         */
01286 kdDebug(5006) << "KMReaderWin  -  resulting message:" << unencryptedMessage->asString() << endl;
01287 kdDebug(5006) << "KMReaderWin  -  attach unencrypted message to aMsg" << endl;
01288         aMsg->setUnencryptedMsg( unencryptedMessage );
01289         emitReplaceMsgByUnencryptedVersion = true;
01290       }
01291     }
01292   }
01293 
01294   // save current main Content-Type before deleting mRootNode
01295   const int rootNodeCntType = mRootNode ? mRootNode->type() : DwMime::kTypeText;
01296   const int rootNodeCntSubtype = mRootNode ? mRootNode->subType() : DwMime::kSubtypePlain;
01297 
01298   // store message id to avoid endless recursions
01299   setIdOfLastViewedMessage( aMsg->msgId() );
01300 
01301   if( emitReplaceMsgByUnencryptedVersion ) {
01302     kdDebug(5006) << "KMReaderWin  -  invoce saving in decrypted form:" << endl;
01303     emit replaceMsgByUnencryptedVersion();
01304   } else {
01305     kdDebug(5006) << "KMReaderWin  -  finished parsing and displaying of message." << endl;
01306     showHideMimeTree( rootNodeCntType == DwMime::kTypeText &&
01307               rootNodeCntSubtype == DwMime::kSubtypePlain );
01308   }
01309 }
01310 
01311 
01312 //-----------------------------------------------------------------------------
01313 QString KMReaderWin::writeMsgHeader(KMMessage* aMsg, bool hasVCard)
01314 {
01315   kdFatal( !headerStyle(), 5006 )
01316     << "trying to writeMsgHeader() without a header style set!" << endl;
01317   kdFatal( !headerStrategy(), 5006 )
01318     << "trying to writeMsgHeader() without a header strategy set!" << endl;
01319   QString href;
01320   if (hasVCard)
01321     href = QString("file:") + KURL::encode_string( mTempFiles.last() );
01322 
01323   return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting );
01324 }
01325 
01326 
01327 
01328 //-----------------------------------------------------------------------------
01329 QString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart,
01330                                                  int aPartNum )
01331 {
01332   QString fileName = aMsgPart->fileName();
01333   if( fileName.isEmpty() )
01334     fileName = aMsgPart->name();
01335 
01336   //--- Sven's save attachments to /tmp start ---
01337   KTempFile *tempFile = new KTempFile( QString::null,
01338                                        "." + QString::number( aPartNum ) );
01339   tempFile->setAutoDelete( true );
01340   QString fname = tempFile->name();
01341   delete tempFile;
01342 
01343   if( ::access( QFile::encodeName( fname ), W_OK ) != 0 )
01344     // Not there or not writable
01345     if( ::mkdir( QFile::encodeName( fname ), 0 ) != 0
01346         || ::chmod( QFile::encodeName( fname ), S_IRWXU ) != 0 )
01347       return QString::null; //failed create
01348 
01349   assert( !fname.isNull() );
01350 
01351   mTempDirs.append( fname );
01352   // strip off a leading path
01353   int slashPos = fileName.findRev( '/' );
01354   if( -1 != slashPos )
01355     fileName = fileName.mid( slashPos + 1 );
01356   if( fileName.isEmpty() )
01357     fileName = "unnamed";
01358   fname += "/" + fileName;
01359 
01360   QByteArray data = aMsgPart->bodyDecodedBinary();
01361   size_t size = data.size();
01362   if ( aMsgPart->type() == DwMime::kTypeText && size) {
01363     // convert CRLF to LF before writing text attachments to disk
01364     size = KMFolder::crlf2lf( data.data(), size );
01365   }
01366   if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) )
01367     return QString::null;
01368 
01369   mTempFiles.append( fname );
01370   // make file read-only so that nobody gets the impression that he might
01371   // edit attached files (cf. bug #52813)
01372   ::chmod( QFile::encodeName( fname ), S_IRUSR );
01373 
01374   return fname;
01375 }
01376 
01377 
01378 //-----------------------------------------------------------------------------
01379 void KMReaderWin::showVCard( KMMessagePart * msgPart ) {
01380   const QString vCard = msgPart->bodyToUnicode( overrideCodec() );
01381 
01382   VCardViewer *vcv = new VCardViewer(this, vCard, "vCardDialog");
01383   vcv->show();
01384 }
01385 
01386 //-----------------------------------------------------------------------------
01387 void KMReaderWin::printMsg()
01388 {
01389   if (!message()) return;
01390   mViewer->view()->print();
01391 }
01392 
01393 
01394 //-----------------------------------------------------------------------------
01395 int KMReaderWin::msgPartFromUrl(const KURL &aUrl)
01396 {
01397   if (aUrl.isEmpty()) return -1;
01398 
01399   if (!aUrl.isLocalFile()) return -1;
01400 
01401   QString path = aUrl.path();
01402   uint right = path.findRev('/');
01403   uint left = path.findRev('.', right);
01404 
01405   bool ok;
01406   int res = path.mid(left + 1, right - left - 1).toInt(&ok);
01407   return (ok) ? res : -1;
01408 }
01409 
01410 
01411 //-----------------------------------------------------------------------------
01412 void KMReaderWin::resizeEvent(QResizeEvent *)
01413 {
01414   if( !mResizeTimer.isActive() )
01415   {
01416     //
01417     // Combine all resize operations that are requested as long a
01418     // the timer runs.
01419     //
01420     mResizeTimer.start( 100, true );
01421   }
01422 }
01423 
01424 
01425 //-----------------------------------------------------------------------------
01426 void KMReaderWin::slotDelayedResize()
01427 {
01428   mSplitter->setGeometry(0, 0, width(), height());
01429 }
01430 
01431 
01432 //-----------------------------------------------------------------------------
01433 void KMReaderWin::slotTouchMessage()
01434 {
01435   if ( !message() )
01436     return;
01437 
01438   if ( !message()->isNew() && !message()->isUnread() )
01439     return;
01440 
01441   SerNumList serNums;
01442   serNums.append( message()->getMsgSerNum() );
01443   KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums );
01444   command->start();
01445   if ( mNoMDNsWhenEncrypted &&
01446        message()->encryptionState() != KMMsgNotEncrypted &&
01447        message()->encryptionState() != KMMsgEncryptionStateUnknown )
01448     return;
01449   if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction,
01450                            MDN::Displayed,
01451                            true /* allow GUI */ ) )
01452     if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue
01453       KMessageBox::error( this, i18n("Could not send MDN.") );
01454 }
01455 
01456 
01457 //-----------------------------------------------------------------------------
01458 void KMReaderWin::closeEvent(QCloseEvent *e)
01459 {
01460   QWidget::closeEvent(e);
01461   writeConfig();
01462 }
01463 
01464 
01465 bool foundSMIMEData( const QString aUrl,
01466                      QString& displayName,
01467                      QString& libName,
01468                      QString& keyId )
01469 {
01470   static QString showCertMan("showCertificate#");
01471   displayName = "";
01472   libName = "";
01473   keyId = "";
01474   int i1 = aUrl.find( showCertMan );
01475   if( -1 < i1 ) {
01476     i1 += showCertMan.length();
01477     int i2 = aUrl.find(" ### ", i1);
01478     if( i1 < i2 )
01479     {
01480       displayName = aUrl.mid( i1, i2-i1 );
01481       i1 = i2+5;
01482       i2 = aUrl.find(" ### ", i1);
01483       if( i1 < i2 )
01484       {
01485         libName = aUrl.mid( i1, i2-i1 );
01486         i2 += 5;
01487 
01488         keyId = aUrl.mid( i2 );
01489         /*
01490         int len = aUrl.length();
01491         if( len > i2+1 ) {
01492           keyId = aUrl.mid( i2, 2 );
01493           i2 += 2;
01494           while( len > i2+1 ) {
01495             keyId += ':';
01496             keyId += aUrl.mid( i2, 2 );
01497             i2 += 2;
01498           }
01499         }
01500         */
01501       }
01502     }
01503   }
01504   return !keyId.isEmpty();
01505 }
01506 
01507 
01508 //-----------------------------------------------------------------------------
01509 void KMReaderWin::slotUrlOn(const QString &aUrl)
01510 {
01511   if ( aUrl.stripWhiteSpace().isEmpty() ) {
01512     KPIM::BroadcastStatus::instance()->reset();
01513     return;
01514   }
01515 
01516   const KURL url(aUrl);
01517   mUrlClicked = url;
01518 
01519   const QString msg = URLHandlerManager::instance()->statusBarMessage( url, this );
01520 
01521   kdWarning( msg.isEmpty(), 5006 ) << "KMReaderWin::slotUrlOn(): Unhandled URL hover!" << endl;
01522   KPIM::BroadcastStatus::instance()->setTransientStatusMsg( msg );
01523 }
01524 
01525 
01526 //-----------------------------------------------------------------------------
01527 void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &)
01528 {
01529   mUrlClicked = aUrl;
01530 
01531   if ( URLHandlerManager::instance()->handleClick( aUrl, this ) )
01532     return;
01533 
01534   kdWarning( 5006 ) << "KMReaderWin::slotOpenUrl(): Unhandled URL click!" << endl;
01535   emit urlClicked( aUrl, Qt::LeftButton );
01536 }
01537 
01538 //-----------------------------------------------------------------------------
01539 void KMReaderWin::slotUrlPopup(const QString &aUrl, const QPoint& aPos)
01540 {
01541   const KURL url( aUrl );
01542   mUrlClicked = url;
01543 
01544   if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) )
01545     return;
01546 
01547   if ( message() ) {
01548     kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl;
01549     emit popupMenu( *message(), url, aPos );
01550   }
01551 }
01552 
01553 void KMReaderWin::showAttachmentPopup( int id, const QString & name, const QPoint & p ) {
01554   mAtmCurrent = id;
01555   mAtmCurrentName = name;
01556   KPopupMenu *menu = new KPopupMenu();
01557   menu->insertItem(SmallIcon("fileopen"),i18n("Open"), 1);
01558   menu->insertItem(i18n("Open With..."), 2);
01559   menu->insertItem(i18n("to view something", "View"), 3);
01560   menu->insertItem(SmallIcon("filesaveas"),i18n("Save As..."), 4);
01561   menu->insertItem(i18n("Properties"), 5);
01562   connect(menu, SIGNAL(activated(int)), this, SLOT(slotAtmLoadPart(int)));
01563   menu->exec( p ,0 );
01564   delete menu;
01565 }
01566 
01567 //-----------------------------------------------------------------------------
01568 void KMReaderWin::setStyleDependantFrameWidth()
01569 {
01570   if ( !mBox )
01571     return;
01572   // set the width of the frame to a reasonable value for the current GUI style
01573   int frameWidth;
01574   if( style().isA("KeramikStyle") )
01575     frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ) - 1;
01576   else
01577     frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth );
01578   if ( frameWidth < 0 )
01579     frameWidth = 0;
01580   if ( frameWidth != mBox->lineWidth() )
01581     mBox->setLineWidth( frameWidth );
01582 }
01583 
01584 //-----------------------------------------------------------------------------
01585 void KMReaderWin::styleChange( QStyle& oldStyle )
01586 {
01587   setStyleDependantFrameWidth();
01588   QWidget::styleChange( oldStyle );
01589 }
01590 
01591 //-----------------------------------------------------------------------------
01592 void KMReaderWin::slotAtmLoadPart( int choice )
01593 {
01594   mChoice = choice;
01595 
01596   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01597   if ( node && !node->msgPart().isComplete() )
01598   {
01599     // load the part
01600     mAtmUpdate = true;
01601     KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() );
01602     connect( command, SIGNAL( partsRetrieved() ),
01603         this, SLOT( slotAtmDistributeClick() ) );
01604     command->start();
01605   } else
01606     slotAtmDistributeClick();
01607 }
01608 
01609 //-----------------------------------------------------------------------------
01610 void KMReaderWin::slotAtmDistributeClick()
01611 {
01612   switch ( mChoice )
01613   {
01614     case 1:
01615       slotAtmOpen();
01616       break;
01617     case 2:
01618       slotAtmOpenWith();
01619       break;
01620     case 3:
01621       slotAtmView();
01622       break;
01623     case 4:
01624       slotAtmSave();
01625       break;
01626     case 5:
01627       slotAtmProperties();
01628       break;
01629     default: kdWarning(5006) << "unknown menu item " << mChoice << endl;
01630   }
01631 }
01632 
01633 //-----------------------------------------------------------------------------
01634 void KMReaderWin::slotFind()
01635 {
01636   //dnaber:
01637   KAction *act = mViewer->actionCollection()->action("find");
01638   if( act )
01639     act->activate();
01640 }
01641 
01642 //-----------------------------------------------------------------------------
01643 void KMReaderWin::slotToggleFixedFont()
01644 {
01645   mUseFixedFont = !mUseFixedFont;
01646   update(true);
01647 }
01648 
01649 
01650 //-----------------------------------------------------------------------------
01651 void KMReaderWin::slotCopySelectedText()
01652 {
01653   kapp->clipboard()->setText( mViewer->selectedText() );
01654 }
01655 
01656 
01657 //-----------------------------------------------------------------------------
01658 void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart)
01659 {
01660   assert(aMsgPart!=0);
01661   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01662   KMMessage* msg;
01663   if (node && node->dwPart()->Body().Message()) {
01664     // make a deep copy
01665     msg = new KMMessage( new DwMessage(*node->dwPart()->Body().Message()) );
01666   } else {
01667     msg = new KMMessage;
01668     msg->fromString(aMsgPart->bodyDecoded());
01669   }
01670   assert(msg != 0);
01671   // some information that is needed for imap messages with LOD
01672   msg->setParent( message()->parent() );
01673   msg->setUID(message()->UID());
01674   msg->setReadyToShow(true);
01675   KMReaderMainWin *win = new KMReaderMainWin();
01676   win->showMsg( overrideCodec(), msg );
01677   win->show();
01678 }
01679 
01680 
01681 void KMReaderWin::setMsgPart( partNode * node ) {
01682   htmlWriter()->reset();
01683   mColorBar->hide();
01684   htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01685   htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
01686   // end ###
01687   if ( node ) {
01688     ObjectTreeParser otp( this, 0, true );
01689     otp.parseObjectTree( node );
01690   }
01691   // ### this, too
01692   htmlWriter()->queue( "</body></html>" );
01693   htmlWriter()->flush();
01694 }
01695 
01696 //-----------------------------------------------------------------------------
01697 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML,
01698                   const QString& aFileName, const QString& pname )
01699 {
01700   KCursorSaver busy(KBusyPtr::busy());
01701   if (qstricmp(aMsgPart->typeStr(), "message")==0) {
01702       // if called from compose win
01703       KMMessage* msg = new KMMessage;
01704       assert(aMsgPart!=0);
01705       msg->fromString(aMsgPart->bodyDecoded());
01706       mMainWindow->setCaption(msg->subject());
01707       setMsg(msg, true);
01708       setAutoDelete(true);
01709   } else if (qstricmp(aMsgPart->typeStr(), "text")==0) {
01710       if (qstricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
01711         showVCard( aMsgPart );
01712     return;
01713       }
01714       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01715       htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
01716 
01717       if (aHTML && (qstricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML
01718     // ### this is broken. It doesn't stip off the HTML header and footer!
01719     htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) );
01720     mColorBar->setHtmlMode();
01721       } else { // plain text
01722     const QCString str = aMsgPart->bodyDecoded();
01723     ObjectTreeParser otp( this );
01724     otp.writeBodyStr( str,
01725               overrideCodec() ? overrideCodec() : aMsgPart->codec(),
01726               message() ? message()->from() : QString::null );
01727       }
01728       htmlWriter()->queue("</body></html>");
01729       htmlWriter()->flush();
01730       mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
01731   } else if (qstricmp(aMsgPart->typeStr(), "image")==0 ||
01732              (qstricmp(aMsgPart->typeStr(), "application")==0 &&
01733               qstricmp(aMsgPart->subtypeStr(), "postscript")==0))
01734   {
01735       if (aFileName.isEmpty()) return;  // prevent crash
01736       // Open the window with a size so the image fits in (if possible):
01737       QImageIO *iio = new QImageIO();
01738       iio->setFileName(aFileName);
01739       if( iio->read() ) {
01740           QImage img = iio->image();
01741           QRect desk = KGlobalSettings::desktopGeometry(mMainWindow);
01742           // determine a reasonable window size
01743           int width, height;
01744           if( img.width() < 50 )
01745               width = 70;
01746           else if( img.width()+20 < desk.width() )
01747               width = img.width()+20;
01748           else
01749               width = desk.width();
01750           if( img.height() < 50 )
01751               height = 70;
01752           else if( img.height()+20 < desk.height() )
01753               height = img.height()+20;
01754           else
01755               height = desk.height();
01756           mMainWindow->resize( width, height );
01757       }
01758       // Just write the img tag to HTML:
01759       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01760       htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
01761       htmlWriter()->write( "<img src=\"file:" +
01762                KURL::encode_string( aFileName ) +
01763                "\" border=\"0\">\n"
01764                "</body></html>\n" );
01765       htmlWriter()->end();
01766       setCaption( i18n("View Attachment: %1").arg( pname ) );
01767       show();
01768   } else {
01769       MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself
01770       QString str = aMsgPart->bodyDecoded();
01771       // A QString cannot handle binary data. So if it's shorter than the
01772       // attachment, we assume the attachment is binary:
01773       if( str.length() < (unsigned) aMsgPart->decodedSize() ) {
01774         str += QString::fromLatin1("\n") + i18n("[KMail: Attachment contains binary data. Trying to show first character.]",
01775                     "[KMail: Attachment contains binary data. Trying to show first %n characters.]",
01776                     str.length());
01777       }
01778       viewer->setText(str);
01779       viewer->resize(500, 550);
01780       viewer->show();
01781   }
01782   // ---Sven's view text, html and image attachments in html widget end ---
01783 }
01784 
01785 
01786 //-----------------------------------------------------------------------------
01787 void KMReaderWin::slotAtmView()
01788 {
01789   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01790   if( node ) {
01791     KMMessagePart& msgPart = node->msgPart();
01792     QString pname = msgPart.fileName();
01793     if (pname.isEmpty()) pname=msgPart.name();
01794     if (pname.isEmpty()) pname=msgPart.contentDescription();
01795     if (pname.isEmpty()) pname="unnamed";
01796     // image Attachment is saved already
01797     if (qstricmp(msgPart.typeStr(), "message")==0) {
01798       atmViewMsg(&msgPart);
01799     } else if ((qstricmp(msgPart.typeStr(), "text")==0) &&
01800            (qstricmp(msgPart.subtypeStr(), "x-vcard")==0)) {
01801       setMsgPart( &msgPart, htmlMail(), mAtmCurrentName, pname );
01802     } else {
01803       KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(),
01804     mAtmCurrentName, pname, overrideCodec() );
01805       win->show();
01806     }
01807   }
01808 }
01809 
01810 
01811 //-----------------------------------------------------------------------------
01812 void KMReaderWin::slotAtmOpen()
01813 {
01814   openAttachment( mAtmCurrent, mAtmCurrentName );
01815 }
01816 
01817 void KMReaderWin::openAttachment( int id, const QString & name ) {
01818   mAtmCurrentName = name;
01819   mAtmCurrent = id;
01820 
01821   QString str, pname, cmd, fileName;
01822 
01823   partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
01824   if( !node ) {
01825     kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl;
01826     return;
01827   }
01828 
01829   KMMessagePart& msgPart = node->msgPart();
01830   if (qstricmp(msgPart.typeStr(), "message")==0)
01831   {
01832     atmViewMsg(&msgPart);
01833     return;
01834   }
01835 
01836   const QString contentTypeStr =
01837     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
01838 
01839   if ( contentTypeStr == "text/x-vcard"  ) {
01840     showVCard( &msgPart );
01841     return;
01842   }
01843 
01844   // determine the MIME type of the attachment
01845   KMimeType::Ptr mimetype;
01846   // prefer the value of the Content-Type header
01847   mimetype = KMimeType::mimeType( contentTypeStr );
01848   if ( mimetype->name() == "application/octet-stream" ) {
01849     // consider the filename if Content-Type is application/octet-stream
01850     mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ );
01851   }
01852   if ( ( mimetype->name() == "application/octet-stream" )
01853        && msgPart.isComplete() ) {
01854     // consider the attachment's contents if neither the Content-Type header
01855     // nor the filename give us a clue
01856     mimetype = KMimeType::findByFileContent( name );
01857   }
01858 
01859   KService::Ptr offer =
01860     KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
01861 
01862   // remember for slotDoAtmOpen; FIXME, this is ugly
01863   mOffer = offer;
01864   QString open_text;
01865   QString filenameText = msgPart.fileName();
01866   if ( filenameText.isEmpty() )
01867     filenameText = msgPart.name();
01868   if ( offer ) {
01869     open_text = i18n("&Open with '%1'").arg( offer->name() );
01870   } else {
01871     open_text = i18n("&Open With...");
01872   }
01873   const QString text = i18n("Open attachment '%1'?\n"
01874                             "Note that opening an attachment may compromise "
01875                             "your system's security.")
01876                        .arg( filenameText );
01877   const int choice = KMessageBox::questionYesNoCancel( this, text,
01878       i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text,
01879       QString::fromLatin1("askSave") + mimetype->name() ); // dontAskAgainName
01880 
01881   if( choice == KMessageBox::Yes ) {        // Save
01882     slotAtmLoadPart( 4 );
01883   }
01884   else if( choice == KMessageBox::No ) {    // Open
01885     // this load-part is duplicated from slotAtmLoadPart but is needed here
01886     // to first display the choice before the attachment is actually downloaded
01887     if ( !msgPart.isComplete() ) {
01888       // load the part
01889       mAtmUpdate = true;
01890       KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() );
01891       connect( command, SIGNAL( partsRetrieved() ),
01892           this, SLOT( slotDoAtmOpen() ) );
01893       command->start();
01894     } else {
01895       slotDoAtmOpen();
01896     }
01897   } else {                  // Cancel
01898     kdDebug(5006) << "Canceled opening attachment" << endl;
01899   }
01900 }
01901 
01902 //-----------------------------------------------------------------------------
01903 void KMReaderWin::slotDoAtmOpen()
01904 {
01905   if ( !mOffer ) {
01906     slotAtmOpenWith();
01907     return;
01908   }
01909 
01910   KURL url;
01911   url.setPath( mAtmCurrentName );
01912   KURL::List lst;
01913   lst.append( url );
01914   KRun::run( *mOffer, lst );
01915 }
01916 
01917 //-----------------------------------------------------------------------------
01918 void KMReaderWin::slotAtmOpenWith()
01919 {
01920   // It makes sense to have an extra "Open with..." entry in the menu
01921   // so the user can change filetype associations.
01922 
01923     KURL::List lst;
01924     KURL url;
01925     url.setPath(mAtmCurrentName);
01926     lst.append(url);
01927     KRun::displayOpenWithDialog(lst);
01928 }
01929 
01930 
01931 //-----------------------------------------------------------------------------
01932 void KMReaderWin::slotAtmSave()
01933 {
01934   if ( !mRootNode )
01935     return;
01936 
01937   partNode * node = mRootNode->findId( mAtmCurrent );
01938   if ( !node ) {
01939     kdWarning(5006) << "KMReaderWin::slotAtmSave - could not find node " << mAtmCurrent << endl;
01940     return;
01941   }
01942 
01943   QPtrList<partNode> parts;
01944   parts.append( node );
01945   // save, do not leave encoded
01946   KMSaveAttachmentsCommand *command =
01947     new KMSaveAttachmentsCommand( this, parts, message(), false );
01948   command->start();
01949 }
01950 
01951 
01952 //-----------------------------------------------------------------------------
01953 void KMReaderWin::slotAtmProperties()
01954 {
01955     KMMsgPartDialogCompat dlg(0,TRUE);
01956 
01957     partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01958     if( node ) {
01959         KMMessagePart& msgPart = node->msgPart();
01960 
01961         dlg.setMsgPart(&msgPart);
01962         dlg.exec();
01963     }
01964 }
01965 
01966 
01967 //-----------------------------------------------------------------------------
01968 void KMReaderWin::slotScrollUp()
01969 {
01970   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -10);
01971 }
01972 
01973 
01974 //-----------------------------------------------------------------------------
01975 void KMReaderWin::slotScrollDown()
01976 {
01977   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, 10);
01978 }
01979 
01980 bool KMReaderWin::atBottom() const
01981 {
01982     const QScrollView *view = static_cast<const QScrollView *>(mViewer->widget());
01983     return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
01984 }
01985 
01986 //-----------------------------------------------------------------------------
01987 void KMReaderWin::slotJumpDown()
01988 {
01989     QScrollView *view = static_cast<QScrollView *>(mViewer->widget());
01990     int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30;
01991     view->scrollBy( 0, view->clipper()->height() - offs );
01992 }
01993 
01994 //-----------------------------------------------------------------------------
01995 void KMReaderWin::slotScrollPrior()
01996 {
01997   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8));
01998 }
01999 
02000 
02001 //-----------------------------------------------------------------------------
02002 void KMReaderWin::slotScrollNext()
02003 {
02004   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8));
02005 }
02006 
02007 //-----------------------------------------------------------------------------
02008 void KMReaderWin::slotDocumentChanged()
02009 {
02010 
02011 }
02012 
02013 
02014 //-----------------------------------------------------------------------------
02015 void KMReaderWin::slotTextSelected(bool)
02016 {
02017   QString temp = mViewer->selectedText();
02018   kapp->clipboard()->setText(temp);
02019 }
02020 
02021 //-----------------------------------------------------------------------------
02022 void KMReaderWin::selectAll()
02023 {
02024   mViewer->selectAll();
02025 }
02026 
02027 //-----------------------------------------------------------------------------
02028 QString KMReaderWin::copyText()
02029 {
02030   QString temp = mViewer->selectedText();
02031   return temp;
02032 }
02033 
02034 
02035 //-----------------------------------------------------------------------------
02036 void KMReaderWin::slotDocumentDone()
02037 {
02038   // mSbVert->setValue(0);
02039 }
02040 
02041 
02042 //-----------------------------------------------------------------------------
02043 void KMReaderWin::setHtmlOverride(bool override)
02044 {
02045   mHtmlOverride = override;
02046   if (message())
02047       message()->setDecodeHTML(htmlMail());
02048 }
02049 
02050 
02051 //-----------------------------------------------------------------------------
02052 bool KMReaderWin::htmlMail()
02053 {
02054   return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride));
02055 }
02056 
02057 
02058 //-----------------------------------------------------------------------------
02059 void KMReaderWin::update( bool force )
02060 {
02061   KMMessage* msg = message();
02062   if ( msg )
02063     setMsg( msg, force );
02064 }
02065 
02066 
02067 //-----------------------------------------------------------------------------
02068 KMMessage* KMReaderWin::message( KMFolder** aFolder ) const
02069 {
02070   KMFolder*  tmpFolder;
02071   KMFolder*& folder = aFolder ? *aFolder : tmpFolder;
02072   folder = 0;
02073   if (mMessage)
02074       return mMessage;
02075   if (mLastSerNum) {
02076     KMMessage *message = 0;
02077     int index;
02078     kmkernel->msgDict()->getLocation( mLastSerNum, &folder, &index );
02079     if (folder )
02080       message = folder->getMsg( index );
02081     if (!message)
02082       kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl;
02083     return message;
02084   }
02085   return 0;
02086 }
02087 
02088 
02089 
02090 //-----------------------------------------------------------------------------
02091 void KMReaderWin::slotUrlClicked()
02092 {
02093   KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow);
02094   uint identity = 0;
02095   if ( message() && message()->parent() ) {
02096     identity = message()->parent()->identity();
02097   }
02098 
02099   KMCommand *command = new KMUrlClickedCommand( mUrlClicked, identity, this,
02100                         false, mainWidget );
02101   command->start();
02102 }
02103 
02104 //-----------------------------------------------------------------------------
02105 void KMReaderWin::slotMailtoCompose()
02106 {
02107   KMCommand *command = new KMMailtoComposeCommand( mUrlClicked, message() );
02108   command->start();
02109 }
02110 
02111 //-----------------------------------------------------------------------------
02112 void KMReaderWin::slotMailtoForward()
02113 {
02114   KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mUrlClicked,
02115                            message() );
02116   command->start();
02117 }
02118 
02119 //-----------------------------------------------------------------------------
02120 void KMReaderWin::slotMailtoAddAddrBook()
02121 {
02122   KMCommand *command = new KMMailtoAddAddrBookCommand( mUrlClicked,
02123                                mMainWindow);
02124   command->start();
02125 }
02126 
02127 //-----------------------------------------------------------------------------
02128 void KMReaderWin::slotMailtoOpenAddrBook()
02129 {
02130   KMCommand *command = new KMMailtoOpenAddrBookCommand( mUrlClicked,
02131                             mMainWindow );
02132   command->start();
02133 }
02134 
02135 //-----------------------------------------------------------------------------
02136 void KMReaderWin::slotUrlCopy()
02137 {
02138   // we don't necessarily need a mainWidget for KMUrlCopyCommand so
02139   // it doesn't matter if the dynamic_cast fails.
02140   KMCommand *command =
02141     new KMUrlCopyCommand( mUrlClicked,
02142                           dynamic_cast<KMMainWidget*>( mMainWindow ) );
02143   command->start();
02144 }
02145 
02146 //-----------------------------------------------------------------------------
02147 void KMReaderWin::slotUrlOpen( const KURL &url )
02148 {
02149   if ( !url.isEmpty() )
02150     mUrlClicked = url;
02151   KMCommand *command = new KMUrlOpenCommand( mUrlClicked, this );
02152   command->start();
02153 }
02154 
02155 //-----------------------------------------------------------------------------
02156 void KMReaderWin::slotAddBookmarks()
02157 {
02158     KMCommand *command = new KMAddBookmarksCommand( mUrlClicked, this );
02159     command->start();
02160 }
02161 
02162 //-----------------------------------------------------------------------------
02163 void KMReaderWin::slotUrlSave()
02164 {
02165   KMCommand *command = new KMUrlSaveCommand( mUrlClicked, mMainWindow );
02166   command->start();
02167 }
02168 
02169 //-----------------------------------------------------------------------------
02170 void KMReaderWin::slotMailtoReply()
02171 {
02172   KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mUrlClicked,
02173     message(), copyText() );
02174   command->start();
02175 }
02176 
02177 //-----------------------------------------------------------------------------
02178 void KMReaderWin::slotShowMsgSrc()
02179 {
02180   KMMessage *msg = message();
02181   if ( !msg )
02182     return;
02183   KMShowMsgSrcCommand *command = new KMShowMsgSrcCommand( msg, isFixedFont() );
02184   command->start();
02185 }
02186 
02187 //-----------------------------------------------------------------------------
02188 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) {
02189   return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ;
02190 }
02191 
02192 partNode * KMReaderWin::partNodeForId( int id ) {
02193   return mRootNode ? mRootNode->findId( id ) : 0 ;
02194 }
02195 
02196 //-----------------------------------------------------------------------------
02197 void KMReaderWin::slotSaveAttachments()
02198 {
02199   mAtmUpdate = true;
02200   KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow,
02201                                                                         message() );
02202   saveCommand->start();
02203 }
02204 
02205 //-----------------------------------------------------------------------------
02206 void KMReaderWin::slotSaveMsg()
02207 {
02208   KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() );
02209 
02210   if (saveCommand->url().isEmpty())
02211     delete saveCommand;
02212   else
02213     saveCommand->start();
02214 }
02215 //-----------------------------------------------------------------------------
02216 void KMReaderWin::slotIMChat()
02217 {
02218   KMCommand *command = new KMIMChatCommand( mUrlClicked, message() );
02219   command->start();
02220 }
02221 
02222 #include "kmreaderwin.moc"
02223 
02224 
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Apr 4 04:48:29 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003