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