kmail Library API Documentation

kmreaderwin.cpp

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