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(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 = style().pixelMetric( QStyle::PM_DefaultFrameWidth ) - 1;
01543   if ( frameWidth < 0 )
01544     frameWidth = 0;
01545   if ( frameWidth != mBox->lineWidth() )
01546     mBox->setLineWidth( frameWidth );
01547 }
01548 
01549 //-----------------------------------------------------------------------------
01550 void KMReaderWin::styleChange( QStyle& oldStyle )
01551 {
01552   setStyleDependantFrameWidth();
01553   QWidget::styleChange( oldStyle );
01554 }
01555 
01556 //-----------------------------------------------------------------------------
01557 void KMReaderWin::slotAtmLoadPart( int choice )
01558 {
01559   mChoice = choice;
01560 
01561   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01562   if ( node && !node->msgPart().isComplete() )
01563   {
01564     // load the part
01565     mAtmUpdate = true;
01566     KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() );
01567     connect( command, SIGNAL( partsRetrieved() ),
01568         this, SLOT( slotAtmDistributeClick() ) );
01569     command->start();
01570   } else
01571     slotAtmDistributeClick();
01572 }
01573 
01574 //-----------------------------------------------------------------------------
01575 void KMReaderWin::slotAtmDistributeClick()
01576 {
01577   switch ( mChoice )
01578   {
01579     case 1:
01580       slotAtmOpen();
01581       break;
01582     case 2:
01583       slotAtmOpenWith();
01584       break;
01585     case 3:
01586       slotAtmView();
01587       break;
01588     case 4:
01589       slotAtmSave();
01590       break;
01591     case 5:
01592       slotAtmProperties();
01593       break;
01594     default: kdWarning(5006) << "unknown menu item " << mChoice << endl;
01595   }
01596 }
01597 
01598 //-----------------------------------------------------------------------------
01599 void KMReaderWin::slotFind()
01600 {
01601   //dnaber:
01602   KAction *act = mViewer->actionCollection()->action("find");
01603   if( act )
01604     act->activate();
01605 }
01606 
01607 //-----------------------------------------------------------------------------
01608 void KMReaderWin::slotToggleFixedFont()
01609 {
01610   mUseFixedFont = !mUseFixedFont;
01611   update(true);
01612 }
01613 
01614 
01615 //-----------------------------------------------------------------------------
01616 void KMReaderWin::slotCopySelectedText()
01617 {
01618   kapp->clipboard()->setText( mViewer->selectedText() );
01619 }
01620 
01621 
01622 //-----------------------------------------------------------------------------
01623 void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart)
01624 {
01625   assert(aMsgPart!=0);
01626   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01627   KMMessage* msg;
01628   if (node && node->dwPart()->Body().Message()) {
01629     // make a deep copy
01630     msg = new KMMessage( new DwMessage(*node->dwPart()->Body().Message()) );
01631   } else {
01632     msg = new KMMessage;
01633     msg->fromString(aMsgPart->bodyDecoded());
01634   }
01635   assert(msg != 0);
01636   // some information that is needed for imap messages with LOD
01637   msg->setParent( message()->parent() );
01638   if ( !message()->headerField("X-UID").isEmpty() )
01639     msg->setHeaderField("X-UID", message()->headerField("X-UID"));
01640   msg->setReadyToShow(true);
01641   KMReaderMainWin *win = new KMReaderMainWin();
01642   win->showMsg( overrideCodec(), msg );
01643   win->resize(550,600);
01644   win->show();
01645 }
01646 
01647 
01648 void KMReaderWin::setMsgPart( partNode * node ) {
01649   // ### extracted from parseObjectTree...
01650   if ( kmkernel->groupware().isEnabled() )
01651     emit signalGroupwareShow( false );
01652   htmlWriter()->reset();
01653   mColorBar->hide();
01654   htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01655   htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
01656   // end ###
01657   if ( node ) {
01658     ObjectTreeParser otp( this, 0, true );
01659     otp.parseObjectTree( node );
01660   }
01661   // ### this, too
01662   htmlWriter()->queue( "</body></html>" );
01663   htmlWriter()->flush();
01664 }
01665 
01666 //-----------------------------------------------------------------------------
01667 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML,
01668                   const QString& aFileName, const QString& pname )
01669 {
01670   KCursorSaver busy(KBusyPtr::busy());
01671   if (qstricmp(aMsgPart->typeStr(), "message")==0) {
01672       // if called from compose win
01673       KMMessage* msg = new KMMessage;
01674       assert(aMsgPart!=0);
01675       msg->fromString(aMsgPart->bodyDecoded());
01676       mMainWindow->setCaption(msg->subject());
01677       setMsg(msg, true);
01678       setAutoDelete(true);
01679   } else if (qstricmp(aMsgPart->typeStr(), "text")==0) {
01680       if (qstricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
01681         showVCard( aMsgPart );
01682     return;
01683       }
01684       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01685       htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
01686 
01687       if (aHTML && (qstricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML
01688     // ### this is broken. It doesn't stip off the HTML header and footer!
01689     htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) );
01690     mColorBar->setHtmlMode();
01691       } else { // plain text
01692     const QCString str = aMsgPart->bodyDecoded();
01693     ObjectTreeParser otp( this );
01694     otp.writeBodyStr( str,
01695               overrideCodec() ? overrideCodec() : aMsgPart->codec(),
01696               message() ? message()->from() : QString::null );
01697       }
01698       htmlWriter()->queue("</body></html>");
01699       htmlWriter()->flush();
01700       mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
01701   } else if (qstricmp(aMsgPart->typeStr(), "image")==0 ||
01702              (qstricmp(aMsgPart->typeStr(), "application")==0 &&
01703               qstricmp(aMsgPart->subtypeStr(), "postscript")==0))
01704   {
01705       if (aFileName.isEmpty()) return;  // prevent crash
01706       // Open the window with a size so the image fits in (if possible):
01707       QImageIO *iio = new QImageIO();
01708       iio->setFileName(aFileName);
01709       if( iio->read() ) {
01710           QImage img = iio->image();
01711 #if KDE_IS_VERSION( 3, 1, 90 )
01712           QRect desk = KGlobalSettings::desktopGeometry(mMainWindow);
01713 #else
01714           QRect desk = QApplication::desktop()->screen(QApplication::desktop()->screenNumber(mMainWindow))->rect();
01715 #endif
01716           // determine a reasonable window size
01717           int width, height;
01718           if( img.width() < 50 )
01719               width = 70;
01720           else if( img.width()+20 < desk.width() )
01721               width = img.width()+20;
01722           else
01723               width = desk.width();
01724           if( img.height() < 50 )
01725               height = 70;
01726           else if( img.height()+20 < desk.height() )
01727               height = img.height()+20;
01728           else
01729               height = desk.height();
01730           mMainWindow->resize( width, height );
01731       }
01732       // Just write the img tag to HTML:
01733       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01734       htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
01735       htmlWriter()->write( "<img src=\"file:" +
01736                KURL::encode_string( aFileName ) +
01737                "\" border=\"0\">\n"
01738                "</body></html>\n" );
01739       htmlWriter()->end();
01740       setCaption( i18n("View Attachment: %1").arg( pname ) );
01741       show();
01742   } else {
01743       MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself
01744       QString str = aMsgPart->bodyDecoded();
01745       // A QString cannot handle binary data. So if it's shorter than the
01746       // attachment, we assume the attachment is binary:
01747       if( str.length() < (unsigned) aMsgPart->decodedSize() ) {
01748         str += QString::fromLatin1("\n") + i18n("[KMail: Attachment contains binary data. Trying to show first character.]",
01749                     "[KMail: Attachment contains binary data. Trying to show first %n characters.]",
01750                     str.length());
01751       }
01752       viewer->setText(str);
01753       viewer->resize(500, 550);
01754       viewer->show();
01755   }
01756   // ---Sven's view text, html and image attachments in html widget end ---
01757 }
01758 
01759 
01760 //-----------------------------------------------------------------------------
01761 void KMReaderWin::slotAtmView()
01762 {
01763   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01764   if( node ) {
01765     KMMessagePart& msgPart = node->msgPart();
01766     QString pname = msgPart.fileName();
01767     if (pname.isEmpty()) pname=msgPart.name();
01768     if (pname.isEmpty()) pname=msgPart.contentDescription();
01769     if (pname.isEmpty()) pname="unnamed";
01770     // image Attachment is saved already
01771     if (qstricmp(msgPart.typeStr(), "message")==0) {
01772       atmViewMsg(&msgPart);
01773     } else if ((qstricmp(msgPart.typeStr(), "text")==0) &&
01774            (qstricmp(msgPart.subtypeStr(), "x-vcard")==0)) {
01775       setMsgPart( &msgPart, htmlMail(), mAtmCurrentName, pname );
01776     } else {
01777       KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(),
01778     mAtmCurrentName, pname, overrideCodec() );
01779       win->show();
01780     }
01781   }
01782 }
01783 
01784 
01785 //-----------------------------------------------------------------------------
01786 void KMReaderWin::slotAtmOpen()
01787 {
01788   openAttachment( mAtmCurrent, mAtmCurrentName );
01789 }
01790 
01791 void KMReaderWin::openAttachment( int id, const QString & name ) {
01792   mAtmCurrentName = name;
01793   mAtmCurrent = id;
01794 
01795   QString str, pname, cmd, fileName;
01796 
01797   partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
01798   if( !node ) {
01799     kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl;
01800     return;
01801   }
01802 
01803   KMMessagePart& msgPart = node->msgPart();
01804   if (qstricmp(msgPart.typeStr(), "message")==0)
01805   {
01806     atmViewMsg(&msgPart);
01807     return;
01808   }
01809 
01810   if (qstricmp(msgPart.typeStr(), "text") == 0)
01811   {
01812     if (qstricmp(msgPart.subtypeStr(), "x-vcard") == 0) {
01813      showVCard( &msgPart );
01814      return;
01815     }
01816   }
01817 
01818   // What to do when user clicks on an attachment --dnaber, 2000-06-01
01819   // TODO: show full path for Service, not only name
01820   QString mimetype = KMimeType::findByURL(KURL(KURL::encode_string(name)))->name();
01821   KService::Ptr offer = KServiceTypeProfile::preferredService(mimetype, "Application");
01822   // remember for slotDoAtmOpen
01823   mOffer = offer;
01824   QString question;
01825   QString open_text;
01826   QString filenameText = msgPart.fileName();
01827   if (filenameText.isEmpty()) filenameText = msgPart.name();
01828   if ( offer ) {
01829     open_text = i18n("&Open with '%1'").arg(offer->name());
01830   } else {
01831     open_text = i18n("&Open with...");
01832   }
01833   question = i18n("Open attachment '%1'?\n"
01834                   "Note that opening an attachment may compromise your "
01835                   "system's security!").arg(filenameText);
01836   int choice = KMessageBox::questionYesNoCancel(this, question,
01837       i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text,
01838       QString::fromLatin1("askSave")+ mimetype ); // dontAskAgainName
01839   if( choice == KMessageBox::Yes ) {        // Save
01840     slotAtmLoadPart( 4 );
01841   } else if( choice == KMessageBox::No ) {  // Open
01842 
01843     // this load-part is duplicated from slotAtmLoadPart but is needed here
01844     // to first display the choice before the attachment is actually downloaded
01845     if ( node && !node->msgPart().isComplete() )
01846     {
01847       // load the part
01848       mAtmUpdate = true;
01849       KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() );
01850       connect( command, SIGNAL( partsRetrieved() ),
01851           this, SLOT( slotDoAtmOpen() ) );
01852       command->start();
01853     } else {
01854       slotDoAtmOpen();
01855     }
01856 
01857   } else {                  // Cancel
01858     kdDebug(5006) << "Canceled opening attachment" << endl;
01859   }
01860 
01861 }
01862 
01863 //-----------------------------------------------------------------------------
01864 void KMReaderWin::slotDoAtmOpen()
01865 {
01866   if ( mOffer ) {
01867     // There's a default service for this kind of file - use it
01868     KURL::List lst;
01869     KURL url;
01870     url.setPath(mAtmCurrentName);
01871     lst.append(url);
01872     KRun::run(*mOffer, lst);
01873   } else {
01874     slotAtmOpenWith();
01875   }
01876 }
01877 
01878 //-----------------------------------------------------------------------------
01879 void KMReaderWin::slotAtmOpenWith()
01880 {
01881   // It makes sense to have an extra "Open with..." entry in the menu
01882   // so the user can change filetype associations.
01883 
01884     KURL::List lst;
01885     KURL url;
01886     url.setPath(mAtmCurrentName);
01887     lst.append(url);
01888     KRun::displayOpenWithDialog(lst);
01889 }
01890 
01891 
01892 //-----------------------------------------------------------------------------
01893 void KMReaderWin::slotAtmSave()
01894 {
01895   if ( !mRootNode )
01896     return;
01897 
01898   partNode * node = mRootNode->findId( mAtmCurrent );
01899   if ( !node ) {
01900     kdWarning(5006) << "KMReaderWin::slotAtmSave - could not find node " << mAtmCurrent << endl;
01901     return;
01902   }
01903 
01904   QPtrList<partNode> parts;
01905   parts.append( node );
01906   // save, do not leave encoded
01907   KMSaveAttachmentsCommand *command = new KMSaveAttachmentsCommand( this, parts,
01908       message(), false );
01909   command->start();
01910 }
01911 
01912 
01913 //-----------------------------------------------------------------------------
01914 void KMReaderWin::slotAtmProperties()
01915 {
01916     KMMsgPartDialogCompat dlg(0,TRUE);
01917 
01918     KCursorSaver busy(KBusyPtr::busy());
01919     partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
01920     if( node ) {
01921         KMMessagePart& msgPart = node->msgPart();
01922 
01923         dlg.setMsgPart(&msgPart);
01924         dlg.exec();
01925     }
01926 }
01927 
01928 
01929 //-----------------------------------------------------------------------------
01930 void KMReaderWin::slotScrollUp()
01931 {
01932   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -10);
01933 }
01934 
01935 
01936 //-----------------------------------------------------------------------------
01937 void KMReaderWin::slotScrollDown()
01938 {
01939   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, 10);
01940 }
01941 
01942 bool KMReaderWin::atBottom() const
01943 {
01944     const QScrollView *view = static_cast<const QScrollView *>(mViewer->widget());
01945     return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
01946 }
01947 
01948 //-----------------------------------------------------------------------------
01949 void KMReaderWin::slotJumpDown()
01950 {
01951     QScrollView *view = static_cast<QScrollView *>(mViewer->widget());
01952     int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30;
01953     view->scrollBy( 0, view->clipper()->height() - offs );
01954 }
01955 
01956 //-----------------------------------------------------------------------------
01957 void KMReaderWin::slotScrollPrior()
01958 {
01959   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8));
01960 }
01961 
01962 
01963 //-----------------------------------------------------------------------------
01964 void KMReaderWin::slotScrollNext()
01965 {
01966   static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8));
01967 }
01968 
01969 //-----------------------------------------------------------------------------
01970 void KMReaderWin::slotDocumentChanged()
01971 {
01972 
01973 }
01974 
01975 
01976 //-----------------------------------------------------------------------------
01977 void KMReaderWin::slotTextSelected(bool)
01978 {
01979   QString temp = mViewer->selectedText();
01980   kapp->clipboard()->setText(temp);
01981 }
01982 
01983 //-----------------------------------------------------------------------------
01984 void KMReaderWin::selectAll()
01985 {
01986   mViewer->selectAll();
01987 }
01988 
01989 //-----------------------------------------------------------------------------
01990 QString KMReaderWin::copyText()
01991 {
01992   QString temp = mViewer->selectedText();
01993   return temp;
01994 }
01995 
01996 
01997 //-----------------------------------------------------------------------------
01998 void KMReaderWin::slotDocumentDone()
01999 {
02000   // mSbVert->setValue(0);
02001 }
02002 
02003 
02004 //-----------------------------------------------------------------------------
02005 void KMReaderWin::setHtmlOverride(bool override)
02006 {
02007   mHtmlOverride = override;
02008   if (message())
02009       message()->setDecodeHTML(htmlMail());
02010 }
02011 
02012 
02013 //-----------------------------------------------------------------------------
02014 bool KMReaderWin::htmlMail()
02015 {
02016   return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride));
02017 }
02018 
02019 
02020 //-----------------------------------------------------------------------------
02021 void KMReaderWin::update( bool force )
02022 {
02023   KMMessage* msg = message();
02024   if ( msg )
02025     setMsg( msg, force );
02026 }
02027 
02028 
02029 //-----------------------------------------------------------------------------
02030 KMMessage* KMReaderWin::message( KMFolder** aFolder ) const
02031 {
02032   KMFolder*  tmpFolder;
02033   KMFolder*& folder = aFolder ? *aFolder : tmpFolder;
02034   folder = 0;
02035   if (mMessage)
02036       return mMessage;
02037   if (mLastSerNum) {
02038     KMMessage *message = 0;
02039     int index;
02040     kmkernel->msgDict()->getLocation( mLastSerNum, &folder, &index );
02041     if (folder )
02042       message = folder->getMsg( index );
02043     if (!message)
02044       kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl;
02045     return message;
02046   }
02047   return 0;
02048 }
02049 
02050 
02051 
02052 //-----------------------------------------------------------------------------
02053 void KMReaderWin::slotUrlClicked()
02054 {
02055   KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow);
02056   uint identity = 0;
02057   if ( message() && message()->parent() ) {
02058     identity = message()->parent()->identity();
02059   }
02060 
02061   KMCommand *command = new KMUrlClickedCommand( mUrlClicked, identity, this,
02062                         false, mainWidget );
02063   command->start();
02064 }
02065 
02066 //-----------------------------------------------------------------------------
02067 void KMReaderWin::slotMailtoCompose()
02068 {
02069   KMCommand *command = new KMMailtoComposeCommand( mUrlClicked, message() );
02070   command->start();
02071 }
02072 
02073 //-----------------------------------------------------------------------------
02074 void KMReaderWin::slotMailtoForward()
02075 {
02076   KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mUrlClicked,
02077                            message() );
02078   command->start();
02079 }
02080 
02081 //-----------------------------------------------------------------------------
02082 void KMReaderWin::slotMailtoAddAddrBook()
02083 {
02084   KMCommand *command = new KMMailtoAddAddrBookCommand( mUrlClicked,
02085                                mMainWindow);
02086   command->start();
02087 }
02088 
02089 //-----------------------------------------------------------------------------
02090 void KMReaderWin::slotMailtoOpenAddrBook()
02091 {
02092   KMCommand *command = new KMMailtoOpenAddrBookCommand( mUrlClicked,
02093                             mMainWindow );
02094   command->start();
02095 }
02096 
02097 //-----------------------------------------------------------------------------
02098 void KMReaderWin::slotUrlCopy()
02099 {
02100   // we don't necessarily need a mainWidget for KMUrlCopyCommand so
02101   // it doesn't matter if the dynamic_cast fails.
02102   KMCommand *command =
02103     new KMUrlCopyCommand( mUrlClicked,
02104                           dynamic_cast<KMMainWidget*>( mMainWindow ) );
02105   command->start();
02106 }
02107 
02108 //-----------------------------------------------------------------------------
02109 void KMReaderWin::slotUrlOpen( const KURL &url )
02110 {
02111   if ( !url.isEmpty() )
02112     mUrlClicked = url;
02113   KMCommand *command = new KMUrlOpenCommand( mUrlClicked, this );
02114   command->start();
02115 }
02116 
02117 //-----------------------------------------------------------------------------
02118 void KMReaderWin::slotAddBookmarks()
02119 {
02120     KMCommand *command = new KMAddBookmarksCommand( mUrlClicked, this );
02121     command->start();
02122 }
02123 
02124 //-----------------------------------------------------------------------------
02125 void KMReaderWin::slotUrlSave()
02126 {
02127   KMCommand *command = new KMUrlSaveCommand( mUrlClicked, mMainWindow );
02128   command->start();
02129 }
02130 
02131 //-----------------------------------------------------------------------------
02132 void KMReaderWin::slotMailtoReply()
02133 {
02134   KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mUrlClicked,
02135     message(), copyText() );
02136   command->start();
02137 }
02138 
02139 //-----------------------------------------------------------------------------
02140 void KMReaderWin::slotShowMsgSrc()
02141 {
02142   KMMessage *msg = message();
02143   if ( !msg )
02144     return;
02145   bool oldStatus = msg->isComplete();
02146   msg->setComplete( true ); // otherwise imap messages are completely downloaded
02147   KMCommand *command = new KMShowMsgSrcCommand( mMainWindow, msg,
02148                                                 isFixedFont() );
02149   command->start();
02150   msg->setComplete( oldStatus );
02151 }
02152 
02153 //-----------------------------------------------------------------------------
02154 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) {
02155   return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ;
02156 }
02157 
02158 //-----------------------------------------------------------------------------
02159 void KMReaderWin::slotSaveAttachments()
02160 {
02161   mAtmUpdate = true;
02162   KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow,
02163                                                                         message() );
02164   saveCommand->start();
02165 }
02166 
02167 //-----------------------------------------------------------------------------
02168 void KMReaderWin::slotSaveMsg()
02169 {
02170   KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() );
02171 
02172   if (saveCommand->url().isEmpty())
02173     delete saveCommand;
02174   else
02175     saveCommand->start();
02176 }
02177 
02178 //-----------------------------------------------------------------------------
02179 #include "kmreaderwin.moc"
02180 
02181 
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Mar 6 17:18:22 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003