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