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 updateReaderWinTimer.stop(); 00919 clear(); 00920 mDelayedMarkTimer.stop(); 00921 mLastSerNum = 0; 00922 mWaitingForSerNum = 0; 00923 mMessage = 0; 00924 } 00925 00926 // enter items for the "Important changes" list here: 00927 static const char * const kmailChanges[] = { 00928 I18N_NOOP("Support for 3rd-party CryptPlugs has been discontinued. " 00929 "Support for the GnuPG cryptographic backend is now included " 00930 "directly in KMail.") 00931 }; 00932 static const int numKMailChanges = 00933 sizeof kmailChanges / sizeof *kmailChanges; 00934 00935 // enter items for the "new features" list here, so the main body of 00936 // the welcome page can be left untouched (probably much easier for 00937 // the translators). Note that the <li>...</li> tags are added 00938 // automatically below: 00939 static const char * const kmailNewFeatures[] = { 00940 I18N_NOOP( "Antispam wizard" ), 00941 I18N_NOOP( "Filter log" ), 00942 I18N_NOOP( "Quick search" ), 00943 I18N_NOOP( "Automatic mailing-list detection" ), 00944 I18N_NOOP( "View/open message files" ), 00945 I18N_NOOP( "HTML message composing" ), 00946 I18N_NOOP( "New filter criteria: in address book, in category, has attachment" ), 00947 I18N_NOOP("Cryptographic backend auto-configuration"), 00948 I18N_NOOP("Sign/encrypt key separation"), 00949 I18N_NOOP("Per-identity S/MIME key preselection"), 00950 I18N_NOOP("Per-identity cryptographic message format preselection"), 00951 I18N_NOOP("Per-contact crypto preferences"), 00952 I18N_NOOP("List only opened IMAP folders"), 00953 }; 00954 static const int numKMailNewFeatures = 00955 sizeof kmailNewFeatures / sizeof *kmailNewFeatures; 00956 00957 00958 //----------------------------------------------------------------------------- 00959 //static 00960 QString KMReaderWin::newFeaturesMD5() 00961 { 00962 QCString str; 00963 for ( int i = 0 ; i < numKMailChanges ; ++i ) 00964 str += kmailChanges[i]; 00965 for ( int i = 0 ; i < numKMailNewFeatures ; ++i ) 00966 str += kmailNewFeatures[i]; 00967 KMD5 md5( str ); 00968 return md5.base64Digest(); 00969 } 00970 00971 //----------------------------------------------------------------------------- 00972 void KMReaderWin::displayAboutPage() 00973 { 00974 mMsgDisplay = false; 00975 adjustLayout(); 00976 00977 QString location = locate("data", "kmail/about/main.html"); 00978 QString content = KPIM::kFileToString(location); 00979 mViewer->begin(KURL( location )); 00980 QString info = 00981 i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; " 00982 "%4: prior KMail version; %5: prior KDE version; " 00983 "%6: generated list of new features; " 00984 "%7: First-time user text (only shown on first start); " 00985 "%8: prior KMail version; " 00986 "%9: generated list of important changes; " 00987 "--- end of comment ---", 00988 "<h2>Welcome to KMail %1</h2><p>KMail is the email client for the K " 00989 "Desktop Environment. It is designed to be fully compatible with " 00990 "Internet mailing standards including MIME, SMTP, POP3 and IMAP." 00991 "</p>\n" 00992 "<ul><li>KMail has many powerful features which are described in the " 00993 "<a href=\"%2\">documentation</a></li>\n" 00994 "<li>The <a href=\"%3\">KMail homepage</A> offers information about " 00995 "new versions of KMail</li></ul>\n" 00996 "<p><span style='font-size:125%; font-weight:bold;'>" 00997 "Important changes</span> (compared to KMail %8):</p>\n" 00998 "<ul>\n%9</ul>\n" 00999 "<p>Some of the new features in this release of KMail include " 01000 "(compared to KMail %4, which is part of KDE %5):</p>\n" 01001 "<ul>\n%6</ul>\n" 01002 "%7\n" 01003 "<p>We hope that you will enjoy KMail.</p>\n" 01004 "<p>Thank you,</p>\n" 01005 "<p>&nbsp; &nbsp; The KMail Team</p>") 01006 .arg(KMAIL_VERSION) // KMail version 01007 .arg("help:/kmail/index.html") // KMail help:// URL 01008 .arg("http://kmail.kde.org/") // KMail homepage URL 01009 .arg("1.6").arg("3.2"); // prior KMail and KDE version 01010 01011 QString featureItems; 01012 for ( int i = 0 ; i < numKMailNewFeatures ; i++ ) 01013 featureItems += i18n("<li>%1</li>\n").arg( i18n( kmailNewFeatures[i] ) ); 01014 01015 info = info.arg( featureItems ); 01016 01017 if( kmkernel->firstStart() ) { 01018 info = info.arg( i18n("<p>Please take a moment to fill in the KMail " 01019 "configuration panel at Settings-&gt;Configure " 01020 "KMail.\n" 01021 "You need to create at least a default identity and " 01022 "an incoming as well as outgoing mail account." 01023 "</p>\n") ); 01024 } else { 01025 info = info.arg( QString::null ); 01026 } 01027 01028 QString changesItems; 01029 for ( int i = 0 ; i < numKMailChanges ; i++ ) 01030 changesItems += i18n("<li>%1</li>\n").arg( i18n( kmailChanges[i] ) ); 01031 01032 info = info.arg("1.6").arg( changesItems ); 01033 01034 mViewer->write(content.arg(pointsToPixel( mCSSHelper->bodyFont().pointSize() )).arg(info)); 01035 mViewer->end(); 01036 } 01037 01038 void KMReaderWin::enableMsgDisplay() { 01039 mMsgDisplay = true; 01040 adjustLayout(); 01041 } 01042 01043 01044 //----------------------------------------------------------------------------- 01045 01046 void KMReaderWin::updateReaderWin() 01047 { 01048 if (!mMsgDisplay) return; 01049 01050 htmlWriter()->reset(); 01051 01052 KMFolder* folder; 01053 if (message(&folder)) 01054 { 01055 if( !kmkernel->iCalIface().isResourceImapFolder( folder ) ){ 01056 if ( mShowColorbar ) 01057 mColorBar->show(); 01058 else 01059 mColorBar->hide(); 01060 displayMessage(); 01061 } 01062 } 01063 else 01064 { 01065 mColorBar->hide(); 01066 mMimePartTree->hide(); 01067 mMimePartTree->clear(); 01068 htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); 01069 htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) + "</body></html>" ); 01070 htmlWriter()->end(); 01071 } 01072 } 01073 01074 //----------------------------------------------------------------------------- 01075 int KMReaderWin::pointsToPixel(int pointSize) const 01076 { 01077 const QPaintDeviceMetrics pdm(mViewer->view()); 01078 01079 return (pointSize * pdm.logicalDpiY() + 36) / 72; 01080 } 01081 01082 //----------------------------------------------------------------------------- 01083 void KMReaderWin::showHideMimeTree( bool isPlainTextTopLevel ) { 01084 if ( mMimeTreeMode == 2 || 01085 ( mMimeTreeMode == 1 && !isPlainTextTopLevel ) ) 01086 mMimePartTree->show(); 01087 else { 01088 // don't rely on QSplitter maintaining sizes for hidden widgets: 01089 KConfigGroup reader( KMKernel::config(), "Reader" ); 01090 saveSplitterSizes( reader ); 01091 mMimePartTree->hide(); 01092 } 01093 } 01094 01095 void KMReaderWin::displayMessage() { 01096 KMMessage * msg = message(); 01097 01098 mMimePartTree->clear(); 01099 showHideMimeTree( !msg || // treat no message as "text/plain" 01100 ( msg->type() == DwMime::kTypeText 01101 && msg->subtype() == DwMime::kSubtypePlain ) ); 01102 01103 if ( !msg ) 01104 return; 01105 01106 msg->setOverrideCodec( overrideCodec() ); 01107 01108 htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); 01109 htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) ); 01110 01111 if (!parent()) 01112 setCaption(msg->subject()); 01113 01114 removeTempFiles(); 01115 01116 mColorBar->setNeutralMode(); 01117 01118 parseMsg(msg); 01119 01120 if( mColorBar->isNeutral() ) 01121 mColorBar->setNormalMode(); 01122 01123 htmlWriter()->queue("</body></html>"); 01124 htmlWriter()->flush(); 01125 } 01126 01127 01128 //----------------------------------------------------------------------------- 01129 void KMReaderWin::parseMsg(KMMessage* aMsg) 01130 { 01131 #ifndef NDEBUG 01132 kdDebug( 5006 ) 01133 << "parseMsg(KMMessage* aMsg " 01134 << ( aMsg == message() ? "==" : "!=" ) 01135 << " aMsg )" << endl; 01136 #endif 01137 01138 KMMessagePart msgPart; 01139 QCString subtype, contDisp; 01140 QByteArray str; 01141 01142 assert(aMsg!=0); 01143 01144 delete mRootNode; 01145 mRootNode = partNode::fromMessage( aMsg ); 01146 const QCString mainCntTypeStr = mRootNode->typeString() + '/' + mRootNode->subTypeString(); 01147 01148 QString cntDesc = aMsg->subject(); 01149 if( cntDesc.isEmpty() ) 01150 cntDesc = i18n("( body part )"); 01151 KIO::filesize_t cntSize = aMsg->msgSize(); 01152 QString cntEnc; 01153 if( aMsg->contentTransferEncodingStr().isEmpty() ) 01154 cntEnc = "7bit"; 01155 else 01156 cntEnc = aMsg->contentTransferEncodingStr(); 01157 01158 // fill the MIME part tree viewer 01159 mRootNode->fillMimePartTree( 0, 01160 mMimePartTree, 01161 cntDesc, 01162 mainCntTypeStr, 01163 cntEnc, 01164 cntSize ); 01165 01166 partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard ); 01167 bool hasVCard = false; 01168 if( vCardNode ) { 01169 // ### FIXME: We should only do this if the vCard belongs to the sender, 01170 // ### i.e. if the sender's email address is contained in the vCard. 01171 const QString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() ); 01172 KABC::VCardConverter t; 01173 if ( !t.parseVCards( vcard ).empty() ) { 01174 hasVCard = true; 01175 kdDebug(5006) << "FOUND A VALID VCARD" << endl; 01176 writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() ); 01177 } 01178 } 01179 htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard) ); 01180 01181 // show message content 01182 ObjectTreeParser otp( this ); 01183 otp.parseObjectTree( mRootNode ); 01184 01185 // store encrypted/signed status information in the KMMessage 01186 // - this can only be done *after* calling parseObjectTree() 01187 KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState(); 01188 KMMsgSignatureState signatureState = mRootNode->overallSignatureState(); 01189 aMsg->setEncryptionState( encryptionState ); 01190 aMsg->setSignatureState( signatureState ); 01191 01192 bool emitReplaceMsgByUnencryptedVersion = false; 01193 const KConfigGroup reader( KMKernel::config(), "Reader" ); 01194 if ( reader.readBoolEntry( "store-displayed-messages-unencrypted", false ) ) { 01195 01196 // Hack to make sure the S/MIME CryptPlugs follows the strict requirement 01197 // of german government: 01198 // --> All received encrypted messages *must* be stored in unencrypted form 01199 // after they have been decrypted once the user has read them. 01200 // ( "Aufhebung der Verschluesselung nach dem Lesen" ) 01201 // 01202 // note: Since there is no configuration option for this, we do that for 01203 // all kinds of encryption now - *not* just for S/MIME. 01204 // This could be changed in the objectTreeToDecryptedMsg() function 01205 // by deciding when (or when not, resp.) to set the 'dataNode' to 01206 // something different than 'curNode'. 01207 01208 01209 kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg() - special post-encryption handling:\n1." << endl; 01210 kdDebug(5006) << "(aMsg == msg) = " << (aMsg == message()) << endl; 01211 kdDebug(5006) << " (KMMsgStatusUnknown == mLastStatus) = " << (KMMsgStatusUnknown == mLastStatus) << endl; 01212 kdDebug(5006) << "|| (KMMsgStatusNew == mLastStatus) = " << (KMMsgStatusNew == mLastStatus) << endl; 01213 kdDebug(5006) << "|| (KMMsgStatusUnread == mLastStatus) = " << (KMMsgStatusUnread == mLastStatus) << endl; 01214 kdDebug(5006) << "(mIdOfLastViewedMessage != aMsg->msgId()) = " << (mIdOfLastViewedMessage != aMsg->msgId()) << endl; 01215 kdDebug(5006) << " (KMMsgFullyEncrypted == encryptionState) = " << (KMMsgFullyEncrypted == encryptionState) << endl; 01216 kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl; 01217 // only proceed if we were called the normal way - not by 01218 // double click on the message (==not running in a separate window) 01219 if( (aMsg == message()) 01220 // only proceed if this message was not saved encryptedly before 01221 // to make sure only *new* messages are saved in decrypted form 01222 && ( (KMMsgStatusUnknown == mLastStatus) 01223 || (KMMsgStatusNew == mLastStatus) 01224 || (KMMsgStatusUnread == mLastStatus) ) 01225 // avoid endless recursions 01226 && (mIdOfLastViewedMessage != aMsg->msgId()) 01227 // only proceed if this message is (at least partially) encrypted 01228 && ( (KMMsgFullyEncrypted == encryptionState) 01229 || (KMMsgPartiallyEncrypted == encryptionState) ) ) { 01230 01231 kdDebug(5006) << "KMReaderWin - calling objectTreeToDecryptedMsg()" << endl; 01232 01233 NewByteArray decryptedData; 01234 // note: The following call may change the message's headers. 01235 objectTreeToDecryptedMsg( mRootNode, decryptedData, *aMsg ); 01236 // add a \0 to the data 01237 decryptedData.appendNULL(); 01238 QCString resultString( decryptedData.data() ); 01239 kdDebug(5006) << "KMReaderWin - resulting data:" << resultString << endl; 01240 01241 if( !resultString.isEmpty() ) { 01242 kdDebug(5006) << "KMReaderWin - composing unencrypted message" << endl; 01243 // try this: 01244 aMsg->setBody( resultString ); 01245 KMMessage* unencryptedMessage = new KMMessage( *aMsg ); 01246 // because this did not work: 01247 /* 01248 DwMessage dwMsg( DwString( aMsg->asString() ) ); 01249 dwMsg.Body() = DwBody( DwString( resultString.data() ) ); 01250 dwMsg.Body().Parse(); 01251 KMMessage* unencryptedMessage = new KMMessage( &dwMsg ); 01252 */ 01253 kdDebug(5006) << "KMReaderWin - resulting message:" << unencryptedMessage->asString() << endl; 01254 kdDebug(5006) << "KMReaderWin - attach unencrypted message to aMsg" << endl; 01255 aMsg->setUnencryptedMsg( unencryptedMessage ); 01256 emitReplaceMsgByUnencryptedVersion = true; 01257 } 01258 } 01259 } 01260 01261 // save current main Content-Type before deleting mRootNode 01262 const int rootNodeCntType = mRootNode ? mRootNode->type() : DwMime::kTypeText; 01263 const int rootNodeCntSubtype = mRootNode ? mRootNode->subType() : DwMime::kSubtypePlain; 01264 01265 // store message id to avoid endless recursions 01266 setIdOfLastViewedMessage( aMsg->msgId() ); 01267 01268 if( emitReplaceMsgByUnencryptedVersion ) { 01269 kdDebug(5006) << "KMReaderWin - invoce saving in decrypted form:" << endl; 01270 emit replaceMsgByUnencryptedVersion(); 01271 } else { 01272 kdDebug(5006) << "KMReaderWin - finished parsing and displaying of message." << endl; 01273 showHideMimeTree( rootNodeCntType == DwMime::kTypeText && 01274 rootNodeCntSubtype == DwMime::kSubtypePlain ); 01275 } 01276 } 01277 01278 01279 //----------------------------------------------------------------------------- 01280 QString KMReaderWin::writeMsgHeader(KMMessage* aMsg, bool hasVCard) 01281 { 01282 kdFatal( !headerStyle(), 5006 ) 01283 << "trying to writeMsgHeader() without a header style set!" << endl; 01284 kdFatal( !headerStrategy(), 5006 ) 01285 << "trying to writeMsgHeader() without a header strategy set!" << endl; 01286 QString href; 01287 if (hasVCard) 01288 href = QString("file:") + KURL::encode_string( mTempFiles.last() ); 01289 01290 return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting ); 01291 } 01292 01293 01294 01295 //----------------------------------------------------------------------------- 01296 QString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart, 01297 int aPartNum ) 01298 { 01299 QString fileName = aMsgPart->fileName(); 01300 if( fileName.isEmpty() ) 01301 fileName = aMsgPart->name(); 01302 01303 //--- Sven's save attachments to /tmp start --- 01304 KTempFile *tempFile = new KTempFile( QString::null, 01305 "." + QString::number( aPartNum ) ); 01306 tempFile->setAutoDelete( true ); 01307 QString fname = tempFile->name(); 01308 delete tempFile; 01309 01310 if( ::access( QFile::encodeName( fname ), W_OK ) != 0 ) 01311 // Not there or not writable 01312 if( ::mkdir( QFile::encodeName( fname ), 0 ) != 0 01313 || ::chmod( QFile::encodeName( fname ), S_IRWXU ) != 0 ) 01314 return QString::null; //failed create 01315 01316 assert( !fname.isNull() ); 01317 01318 mTempDirs.append( fname ); 01319 // strip off a leading path 01320 int slashPos = fileName.findRev( '/' ); 01321 if( -1 != slashPos ) 01322 fileName = fileName.mid( slashPos + 1 ); 01323 if( fileName.isEmpty() ) 01324 fileName = "unnamed"; 01325 fname += "/" + fileName; 01326 01327 QByteArray data = aMsgPart->bodyDecodedBinary(); 01328 size_t size = data.size(); 01329 if ( aMsgPart->type() == DwMime::kTypeText && size) { 01330 // convert CRLF to LF before writing text attachments to disk 01331 size = KMFolder::crlf2lf( data.data(), size ); 01332 } 01333 if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) ) 01334 return QString::null; 01335 01336 mTempFiles.append( fname ); 01337 // make file read-only so that nobody gets the impression that he might 01338 // edit attached files (cf. bug #52813) 01339 ::chmod( QFile::encodeName( fname ), S_IRUSR ); 01340 01341 return fname; 01342 } 01343 01344 01345 //----------------------------------------------------------------------------- 01346 void KMReaderWin::showVCard( KMMessagePart * msgPart ) { 01347 const QString vCard = msgPart->bodyToUnicode( overrideCodec() ); 01348 01349 VCardViewer *vcv = new VCardViewer(this, vCard, "vCardDialog"); 01350 vcv->show(); 01351 } 01352 01353 //----------------------------------------------------------------------------- 01354 void KMReaderWin::printMsg() 01355 { 01356 if (!message()) return; 01357 mViewer->view()->print(); 01358 } 01359 01360 01361 //----------------------------------------------------------------------------- 01362 int KMReaderWin::msgPartFromUrl(const KURL &aUrl) 01363 { 01364 if (aUrl.isEmpty()) return -1; 01365 01366 if (!aUrl.isLocalFile()) return -1; 01367 01368 QString path = aUrl.path(); 01369 uint right = path.findRev('/'); 01370 uint left = path.findRev('.', right); 01371 01372 bool ok; 01373 int res = path.mid(left + 1, right - left - 1).toInt(&ok); 01374 return (ok) ? res : -1; 01375 } 01376 01377 01378 //----------------------------------------------------------------------------- 01379 void KMReaderWin::resizeEvent(QResizeEvent *) 01380 { 01381 if( !mResizeTimer.isActive() ) 01382 { 01383 // 01384 // Combine all resize operations that are requested as long a 01385 // the timer runs. 01386 // 01387 mResizeTimer.start( 100, true ); 01388 } 01389 } 01390 01391 01392 //----------------------------------------------------------------------------- 01393 void KMReaderWin::slotDelayedResize() 01394 { 01395 mSplitter->setGeometry(0, 0, width(), height()); 01396 } 01397 01398 01399 //----------------------------------------------------------------------------- 01400 void KMReaderWin::slotTouchMessage() 01401 { 01402 if ( !message() ) 01403 return; 01404 01405 if ( !message()->isNew() && !message()->isUnread() ) 01406 return; 01407 01408 SerNumList serNums; 01409 serNums.append( message()->getMsgSerNum() ); 01410 KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums ); 01411 command->start(); 01412 if ( mNoMDNsWhenEncrypted && 01413 message()->encryptionState() != KMMsgNotEncrypted && 01414 message()->encryptionState() != KMMsgEncryptionStateUnknown ) 01415 return; 01416 if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction, 01417 MDN::Displayed, 01418 true /* allow GUI */ ) ) 01419 if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue 01420 KMessageBox::error( this, i18n("Could not send MDN.") ); 01421 } 01422 01423 01424 //----------------------------------------------------------------------------- 01425 void KMReaderWin::closeEvent(QCloseEvent *e) 01426 { 01427 QWidget::closeEvent(e); 01428 writeConfig(); 01429 } 01430 01431 01432 bool foundSMIMEData( const QString aUrl, 01433 QString& displayName, 01434 QString& libName, 01435 QString& keyId ) 01436 { 01437 static QString showCertMan("showCertificate#"); 01438 displayName = ""; 01439 libName = ""; 01440 keyId = ""; 01441 int i1 = aUrl.find( showCertMan ); 01442 if( -1 < i1 ) { 01443 i1 += showCertMan.length(); 01444 int i2 = aUrl.find(" ### ", i1); 01445 if( i1 < i2 ) 01446 { 01447 displayName = aUrl.mid( i1, i2-i1 ); 01448 i1 = i2+5; 01449 i2 = aUrl.find(" ### ", i1); 01450 if( i1 < i2 ) 01451 { 01452 libName = aUrl.mid( i1, i2-i1 ); 01453 i2 += 5; 01454 01455 keyId = aUrl.mid( i2 ); 01456 /* 01457 int len = aUrl.length(); 01458 if( len > i2+1 ) { 01459 keyId = aUrl.mid( i2, 2 ); 01460 i2 += 2; 01461 while( len > i2+1 ) { 01462 keyId += ':'; 01463 keyId += aUrl.mid( i2, 2 ); 01464 i2 += 2; 01465 } 01466 } 01467 */ 01468 } 01469 } 01470 } 01471 return !keyId.isEmpty(); 01472 } 01473 01474 01475 //----------------------------------------------------------------------------- 01476 void KMReaderWin::slotUrlOn(const QString &aUrl) 01477 { 01478 if ( aUrl.stripWhiteSpace().isEmpty() ) { 01479 KPIM::BroadcastStatus::instance()->reset(); 01480 return; 01481 } 01482 01483 const KURL url(aUrl); 01484 mUrlClicked = url; 01485 01486 const QString msg = URLHandlerManager::instance()->statusBarMessage( url, this ); 01487 01488 kdWarning( msg.isEmpty(), 5006 ) << "KMReaderWin::slotUrlOn(): Unhandled URL hover!" << endl; 01489 KPIM::BroadcastStatus::instance()->setTransientStatusMsg( msg ); 01490 } 01491 01492 01493 //----------------------------------------------------------------------------- 01494 void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &) 01495 { 01496 mUrlClicked = aUrl; 01497 01498 if ( URLHandlerManager::instance()->handleClick( aUrl, this ) ) 01499 return; 01500 01501 kdWarning( 5006 ) << "KMReaderWin::slotOpenUrl(): Unhandled URL click!" << endl; 01502 emit urlClicked( aUrl, Qt::LeftButton ); 01503 } 01504 01505 //----------------------------------------------------------------------------- 01506 void KMReaderWin::slotUrlPopup(const QString &aUrl, const QPoint& aPos) 01507 { 01508 const KURL url( aUrl ); 01509 mUrlClicked = url; 01510 01511 if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) ) 01512 return; 01513 01514 if ( message() ) { 01515 kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl; 01516 emit popupMenu( *message(), url, aPos ); 01517 } 01518 } 01519 01520 void KMReaderWin::showAttachmentPopup( int id, const QString & name, const QPoint & p ) { 01521 mAtmCurrent = id; 01522 mAtmCurrentName = name; 01523 KPopupMenu *menu = new KPopupMenu(); 01524 menu->insertItem(SmallIcon("fileopen"),i18n("Open"), 1); 01525 menu->insertItem(i18n("Open With..."), 2); 01526 menu->insertItem(i18n("to view something", "View"), 3); 01527 menu->insertItem(SmallIcon("filesaveas"),i18n("Save As..."), 4); 01528 menu->insertItem(i18n("Properties"), 5); 01529 connect(menu, SIGNAL(activated(int)), this, SLOT(slotAtmLoadPart(int))); 01530 menu->exec( p ,0 ); 01531 delete menu; 01532 } 01533 01534 //----------------------------------------------------------------------------- 01535 void KMReaderWin::setStyleDependantFrameWidth() 01536 { 01537 if ( !mBox ) 01538 return; 01539 // set the width of the frame to a reasonable value for the current GUI style 01540 int frameWidth; 01541 if( style().isA("KeramikStyle") ) 01542 frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ) - 1; 01543 else 01544 frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ); 01545 if ( frameWidth < 0 ) 01546 frameWidth = 0; 01547 if ( frameWidth != mBox->lineWidth() ) 01548 mBox->setLineWidth( frameWidth ); 01549 } 01550 01551 //----------------------------------------------------------------------------- 01552 void KMReaderWin::styleChange( QStyle& oldStyle ) 01553 { 01554 setStyleDependantFrameWidth(); 01555 QWidget::styleChange( oldStyle ); 01556 } 01557 01558 //----------------------------------------------------------------------------- 01559 void KMReaderWin::slotAtmLoadPart( int choice ) 01560 { 01561 mChoice = choice; 01562 01563 partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; 01564 if ( node && !node->msgPart().isComplete() ) 01565 { 01566 // load the part 01567 mAtmUpdate = true; 01568 KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() ); 01569 connect( command, SIGNAL( partsRetrieved() ), 01570 this, SLOT( slotAtmDistributeClick() ) ); 01571 command->start(); 01572 } else 01573 slotAtmDistributeClick(); 01574 } 01575 01576 //----------------------------------------------------------------------------- 01577 void KMReaderWin::slotAtmDistributeClick() 01578 { 01579 switch ( mChoice ) 01580 { 01581 case 1: 01582 slotAtmOpen(); 01583 break; 01584 case 2: 01585 slotAtmOpenWith(); 01586 break; 01587 case 3: 01588 slotAtmView(); 01589 break; 01590 case 4: 01591 slotAtmSave(); 01592 break; 01593 case 5: 01594 slotAtmProperties(); 01595 break; 01596 default: kdWarning(5006) << "unknown menu item " << mChoice << endl; 01597 } 01598 } 01599 01600 //----------------------------------------------------------------------------- 01601 void KMReaderWin::slotFind() 01602 { 01603 //dnaber: 01604 KAction *act = mViewer->actionCollection()->action("find"); 01605 if( act ) 01606 act->activate(); 01607 } 01608 01609 //----------------------------------------------------------------------------- 01610 void KMReaderWin::slotToggleFixedFont() 01611 { 01612 mUseFixedFont = !mUseFixedFont; 01613 update(true); 01614 } 01615 01616 01617 //----------------------------------------------------------------------------- 01618 void KMReaderWin::slotCopySelectedText() 01619 { 01620 kapp->clipboard()->setText( mViewer->selectedText() ); 01621 } 01622 01623 01624 //----------------------------------------------------------------------------- 01625 void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart) 01626 { 01627 assert(aMsgPart!=0); 01628 partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; 01629 KMMessage* msg; 01630 if (node && node->dwPart()->Body().Message()) { 01631 // make a deep copy 01632 msg = new KMMessage( new DwMessage(*node->dwPart()->Body().Message()) ); 01633 } else { 01634 msg = new KMMessage; 01635 msg->fromString(aMsgPart->bodyDecoded()); 01636 } 01637 assert(msg != 0); 01638 // some information that is needed for imap messages with LOD 01639 msg->setParent( message()->parent() ); 01640 msg->setUID(message()->UID()); 01641 msg->setReadyToShow(true); 01642 KMReaderMainWin *win = new KMReaderMainWin(); 01643 win->showMsg( overrideCodec(), msg ); 01644 win->resize(550,600); 01645 win->show(); 01646 } 01647 01648 01649 void KMReaderWin::setMsgPart( partNode * node ) { 01650 htmlWriter()->reset(); 01651 mColorBar->hide(); 01652 htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); 01653 htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) ); 01654 // end ### 01655 if ( node ) { 01656 ObjectTreeParser otp( this, 0, true ); 01657 otp.parseObjectTree( node ); 01658 } 01659 // ### this, too 01660 htmlWriter()->queue( "</body></html>" ); 01661 htmlWriter()->flush(); 01662 } 01663 01664 //----------------------------------------------------------------------------- 01665 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML, 01666 const QString& aFileName, const QString& pname ) 01667 { 01668 KCursorSaver busy(KBusyPtr::busy()); 01669 if (qstricmp(aMsgPart->typeStr(), "message")==0) { 01670 // if called from compose win 01671 KMMessage* msg = new KMMessage; 01672 assert(aMsgPart!=0); 01673 msg->fromString(aMsgPart->bodyDecoded()); 01674 mMainWindow->setCaption(msg->subject()); 01675 setMsg(msg, true); 01676 setAutoDelete(true); 01677 } else if (qstricmp(aMsgPart->typeStr(), "text")==0) { 01678 if (qstricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) { 01679 showVCard( aMsgPart ); 01680 return; 01681 } 01682 htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); 01683 htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) ); 01684 01685 if (aHTML && (qstricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML 01686 // ### this is broken. It doesn't stip off the HTML header and footer! 01687 htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) ); 01688 mColorBar->setHtmlMode(); 01689 } else { // plain text 01690 const QCString str = aMsgPart->bodyDecoded(); 01691 ObjectTreeParser otp( this ); 01692 otp.writeBodyStr( str, 01693 overrideCodec() ? overrideCodec() : aMsgPart->codec(), 01694 message() ? message()->from() : QString::null ); 01695 } 01696 htmlWriter()->queue("</body></html>"); 01697 htmlWriter()->flush(); 01698 mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname)); 01699 } else if (qstricmp(aMsgPart->typeStr(), "image")==0 || 01700 (qstricmp(aMsgPart->typeStr(), "application")==0 && 01701 qstricmp(aMsgPart->subtypeStr(), "postscript")==0)) 01702 { 01703 if (aFileName.isEmpty()) return; // prevent crash 01704 // Open the window with a size so the image fits in (if possible): 01705 QImageIO *iio = new QImageIO(); 01706 iio->setFileName(aFileName); 01707 if( iio->read() ) { 01708 QImage img = iio->image(); 01709 QRect desk = KGlobalSettings::desktopGeometry(mMainWindow); 01710 // determine a reasonable window size 01711 int width, height; 01712 if( img.width() < 50 ) 01713 width = 70; 01714 else if( img.width()+20 < desk.width() ) 01715 width = img.width()+20; 01716 else 01717 width = desk.width(); 01718 if( img.height() < 50 ) 01719 height = 70; 01720 else if( img.height()+20 < desk.height() ) 01721 height = img.height()+20; 01722 else 01723 height = desk.height(); 01724 mMainWindow->resize( width, height ); 01725 } 01726 // Just write the img tag to HTML: 01727 htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) ); 01728 htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) ); 01729 htmlWriter()->write( "<img src=\"file:" + 01730 KURL::encode_string( aFileName ) + 01731 "\" border=\"0\">\n" 01732 "</body></html>\n" ); 01733 htmlWriter()->end(); 01734 setCaption( i18n("View Attachment: %1").arg( pname ) ); 01735 show(); 01736 } else { 01737 MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself 01738 QString str = aMsgPart->bodyDecoded(); 01739 // A QString cannot handle binary data. So if it's shorter than the 01740 // attachment, we assume the attachment is binary: 01741 if( str.length() < (unsigned) aMsgPart->decodedSize() ) { 01742 str += QString::fromLatin1("\n") + i18n("[KMail: Attachment contains binary data. Trying to show first character.]", 01743 "[KMail: Attachment contains binary data. Trying to show first %n characters.]", 01744 str.length()); 01745 } 01746 viewer->setText(str); 01747 viewer->resize(500, 550); 01748 viewer->show(); 01749 } 01750 // ---Sven's view text, html and image attachments in html widget end --- 01751 } 01752 01753 01754 //----------------------------------------------------------------------------- 01755 void KMReaderWin::slotAtmView() 01756 { 01757 partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; 01758 if( node ) { 01759 KMMessagePart& msgPart = node->msgPart(); 01760 QString pname = msgPart.fileName(); 01761 if (pname.isEmpty()) pname=msgPart.name(); 01762 if (pname.isEmpty()) pname=msgPart.contentDescription(); 01763 if (pname.isEmpty()) pname="unnamed"; 01764 // image Attachment is saved already 01765 if (qstricmp(msgPart.typeStr(), "message")==0) { 01766 atmViewMsg(&msgPart); 01767 } else if ((qstricmp(msgPart.typeStr(), "text")==0) && 01768 (qstricmp(msgPart.subtypeStr(), "x-vcard")==0)) { 01769 setMsgPart( &msgPart, htmlMail(), mAtmCurrentName, pname ); 01770 } else { 01771 KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(), 01772 mAtmCurrentName, pname, overrideCodec() ); 01773 win->show(); 01774 } 01775 } 01776 } 01777 01778 01779 //----------------------------------------------------------------------------- 01780 void KMReaderWin::slotAtmOpen() 01781 { 01782 openAttachment( mAtmCurrent, mAtmCurrentName ); 01783 } 01784 01785 void KMReaderWin::openAttachment( int id, const QString & name ) { 01786 mAtmCurrentName = name; 01787 mAtmCurrent = id; 01788 01789 QString str, pname, cmd, fileName; 01790 01791 partNode* node = mRootNode ? mRootNode->findId( id ) : 0; 01792 if( !node ) { 01793 kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl; 01794 return; 01795 } 01796 01797 KMMessagePart& msgPart = node->msgPart(); 01798 if (qstricmp(msgPart.typeStr(), "message")==0) 01799 { 01800 atmViewMsg(&msgPart); 01801 return; 01802 } 01803 01804 const QString contentTypeStr = 01805 ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower(); 01806 01807 if ( contentTypeStr == "text/x-vcard" ) { 01808 showVCard( &msgPart ); 01809 return; 01810 } 01811 01812 // determine the MIME type of the attachment 01813 KMimeType::Ptr mimetype; 01814 // prefer the value of the Content-Type header 01815 mimetype = KMimeType::mimeType( contentTypeStr ); 01816 if ( mimetype->name() == "application/octet-stream" ) { 01817 // consider the filename if Content-Type is application/octet-stream 01818 mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ ); 01819 } 01820 if ( ( mimetype->name() == "application/octet-stream" ) 01821 && msgPart.isComplete() ) { 01822 // consider the attachment's contents if neither the Content-Type header 01823 // nor the filename give us a clue 01824 mimetype = KMimeType::findByFileContent( name ); 01825 } 01826 01827 KService::Ptr offer = 01828 KServiceTypeProfile::preferredService( mimetype->name(), "Application" ); 01829 01830 // remember for slotDoAtmOpen; FIXME, this is ugly 01831 mOffer = offer; 01832 QString open_text; 01833 QString filenameText = msgPart.fileName(); 01834 if ( filenameText.isEmpty() ) 01835 filenameText = msgPart.name(); 01836 if ( offer ) { 01837 open_text = i18n("&Open with '%1'").arg( offer->name() ); 01838 } else { 01839 open_text = i18n("&Open With..."); 01840 } 01841 const QString text = i18n("Open attachment '%1'?\n" 01842 "Note that opening an attachment may compromise " 01843 "your system's security.") 01844 .arg( filenameText ); 01845 const int choice = KMessageBox::questionYesNoCancel( this, text, 01846 i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text, 01847 QString::fromLatin1("askSave") + mimetype->name() ); // dontAskAgainName 01848 01849 if( choice == KMessageBox::Yes ) { // Save 01850 slotAtmLoadPart( 4 ); 01851 } 01852 else if( choice == KMessageBox::No ) { // Open 01853 // this load-part is duplicated from slotAtmLoadPart but is needed here 01854 // to first display the choice before the attachment is actually downloaded 01855 if ( !msgPart.isComplete() ) { 01856 // load the part 01857 mAtmUpdate = true; 01858 KMLoadPartsCommand *command = new KMLoadPartsCommand( node, message() ); 01859 connect( command, SIGNAL( partsRetrieved() ), 01860 this, SLOT( slotDoAtmOpen() ) ); 01861 command->start(); 01862 } else { 01863 slotDoAtmOpen(); 01864 } 01865 } else { // Cancel 01866 kdDebug(5006) << "Canceled opening attachment" << endl; 01867 } 01868 } 01869 01870 //----------------------------------------------------------------------------- 01871 void KMReaderWin::slotDoAtmOpen() 01872 { 01873 if ( !mOffer ) { 01874 slotAtmOpenWith(); 01875 return; 01876 } 01877 01878 KURL::List lst; 01879 KURL url; 01880 bool autoDelete = true; 01881 QString fname = createAtmFileLink(); 01882 01883 if ( fname == QString::null ) { 01884 autoDelete = false; 01885 fname = mAtmCurrentName; 01886 } 01887 01888 url.setPath( fname ); 01889 lst.append( url ); 01890 if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) { 01891 QFile::remove(url.path()); 01892 } 01893 } 01894 01895 //----------------------------------------------------------------------------- 01896 void KMReaderWin::slotAtmOpenWith() 01897 { 01898 // It makes sense to have an extra "Open with..." entry in the menu 01899 // so the user can change filetype associations. 01900 01901 KURL::List lst; 01902 KURL url; 01903 bool autoDelete = true; 01904 QString fname = createAtmFileLink(); 01905 01906 if ( fname == QString::null ) { 01907 autoDelete = false; 01908 fname = mAtmCurrentName; 01909 } 01910 01911 url.setPath( fname ); 01912 lst.append(url); 01913 if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) { 01914 QFile::remove(url.path()); 01915 } 01916 } 01917 01918 01919 //----------------------------------------------------------------------------- 01920 void KMReaderWin::slotAtmSave() 01921 { 01922 if ( !mRootNode ) 01923 return; 01924 01925 partNode * node = mRootNode->findId( mAtmCurrent ); 01926 if ( !node ) { 01927 kdWarning(5006) << "KMReaderWin::slotAtmSave - could not find node " << mAtmCurrent << endl; 01928 return; 01929 } 01930 01931 QPtrList<partNode> parts; 01932 parts.append( node ); 01933 // save, do not leave encoded 01934 KMSaveAttachmentsCommand *command = 01935 new KMSaveAttachmentsCommand( this, parts, message(), false ); 01936 command->start(); 01937 } 01938 01939 01940 //----------------------------------------------------------------------------- 01941 void KMReaderWin::slotAtmProperties() 01942 { 01943 KMMsgPartDialogCompat dlg(0,TRUE); 01944 01945 partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; 01946 if( node ) { 01947 KMMessagePart& msgPart = node->msgPart(); 01948 01949 dlg.setMsgPart(&msgPart); 01950 dlg.exec(); 01951 } 01952 } 01953 01954 01955 //----------------------------------------------------------------------------- 01956 void KMReaderWin::slotScrollUp() 01957 { 01958 static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -10); 01959 } 01960 01961 01962 //----------------------------------------------------------------------------- 01963 void KMReaderWin::slotScrollDown() 01964 { 01965 static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, 10); 01966 } 01967 01968 bool KMReaderWin::atBottom() const 01969 { 01970 const QScrollView *view = static_cast<const QScrollView *>(mViewer->widget()); 01971 return view->contentsY() + view->visibleHeight() >= view->contentsHeight(); 01972 } 01973 01974 //----------------------------------------------------------------------------- 01975 void KMReaderWin::slotJumpDown() 01976 { 01977 QScrollView *view = static_cast<QScrollView *>(mViewer->widget()); 01978 int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30; 01979 view->scrollBy( 0, view->clipper()->height() - offs ); 01980 } 01981 01982 //----------------------------------------------------------------------------- 01983 void KMReaderWin::slotScrollPrior() 01984 { 01985 static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8)); 01986 } 01987 01988 01989 //----------------------------------------------------------------------------- 01990 void KMReaderWin::slotScrollNext() 01991 { 01992 static_cast<QScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8)); 01993 } 01994 01995 //----------------------------------------------------------------------------- 01996 void KMReaderWin::slotDocumentChanged() 01997 { 01998 01999 } 02000 02001 02002 //----------------------------------------------------------------------------- 02003 void KMReaderWin::slotTextSelected(bool) 02004 { 02005 QString temp = mViewer->selectedText(); 02006 kapp->clipboard()->setText(temp); 02007 } 02008 02009 //----------------------------------------------------------------------------- 02010 void KMReaderWin::selectAll() 02011 { 02012 mViewer->selectAll(); 02013 } 02014 02015 //----------------------------------------------------------------------------- 02016 QString KMReaderWin::copyText() 02017 { 02018 QString temp = mViewer->selectedText(); 02019 return temp; 02020 } 02021 02022 02023 //----------------------------------------------------------------------------- 02024 void KMReaderWin::slotDocumentDone() 02025 { 02026 // mSbVert->setValue(0); 02027 } 02028 02029 02030 //----------------------------------------------------------------------------- 02031 void KMReaderWin::setHtmlOverride(bool override) 02032 { 02033 mHtmlOverride = override; 02034 if (message()) 02035 message()->setDecodeHTML(htmlMail()); 02036 } 02037 02038 02039 //----------------------------------------------------------------------------- 02040 bool KMReaderWin::htmlMail() 02041 { 02042 return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride)); 02043 } 02044 02045 02046 //----------------------------------------------------------------------------- 02047 void KMReaderWin::update( bool force ) 02048 { 02049 KMMessage* msg = message(); 02050 if ( msg ) 02051 setMsg( msg, force ); 02052 } 02053 02054 02055 //----------------------------------------------------------------------------- 02056 KMMessage* KMReaderWin::message( KMFolder** aFolder ) const 02057 { 02058 KMFolder* tmpFolder; 02059 KMFolder*& folder = aFolder ? *aFolder : tmpFolder; 02060 folder = 0; 02061 if (mMessage) 02062 return mMessage; 02063 if (mLastSerNum) { 02064 KMMessage *message = 0; 02065 int index; 02066 kmkernel->msgDict()->getLocation( mLastSerNum, &folder, &index ); 02067 if (folder ) 02068 message = folder->getMsg( index ); 02069 if (!message) 02070 kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl; 02071 return message; 02072 } 02073 return 0; 02074 } 02075 02076 02077 02078 //----------------------------------------------------------------------------- 02079 void KMReaderWin::slotUrlClicked() 02080 { 02081 KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow); 02082 uint identity = 0; 02083 if ( message() && message()->parent() ) { 02084 identity = message()->parent()->identity(); 02085 } 02086 02087 KMCommand *command = new KMUrlClickedCommand( mUrlClicked, identity, this, 02088 false, mainWidget ); 02089 command->start(); 02090 } 02091 02092 //----------------------------------------------------------------------------- 02093 void KMReaderWin::slotMailtoCompose() 02094 { 02095 KMCommand *command = new KMMailtoComposeCommand( mUrlClicked, message() ); 02096 command->start(); 02097 } 02098 02099 //----------------------------------------------------------------------------- 02100 void KMReaderWin::slotMailtoForward() 02101 { 02102 KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mUrlClicked, 02103 message() ); 02104 command->start(); 02105 } 02106 02107 //----------------------------------------------------------------------------- 02108 void KMReaderWin::slotMailtoAddAddrBook() 02109 { 02110 KMCommand *command = new KMMailtoAddAddrBookCommand( mUrlClicked, 02111 mMainWindow); 02112 command->start(); 02113 } 02114 02115 //----------------------------------------------------------------------------- 02116 void KMReaderWin::slotMailtoOpenAddrBook() 02117 { 02118 KMCommand *command = new KMMailtoOpenAddrBookCommand( mUrlClicked, 02119 mMainWindow ); 02120 command->start(); 02121 } 02122 02123 //----------------------------------------------------------------------------- 02124 void KMReaderWin::slotUrlCopy() 02125 { 02126 // we don't necessarily need a mainWidget for KMUrlCopyCommand so 02127 // it doesn't matter if the dynamic_cast fails. 02128 KMCommand *command = 02129 new KMUrlCopyCommand( mUrlClicked, 02130 dynamic_cast<KMMainWidget*>( mMainWindow ) ); 02131 command->start(); 02132 } 02133 02134 //----------------------------------------------------------------------------- 02135 void KMReaderWin::slotUrlOpen( const KURL &url ) 02136 { 02137 if ( !url.isEmpty() ) 02138 mUrlClicked = url; 02139 KMCommand *command = new KMUrlOpenCommand( mUrlClicked, this ); 02140 command->start(); 02141 } 02142 02143 //----------------------------------------------------------------------------- 02144 void KMReaderWin::slotAddBookmarks() 02145 { 02146 KMCommand *command = new KMAddBookmarksCommand( mUrlClicked, this ); 02147 command->start(); 02148 } 02149 02150 //----------------------------------------------------------------------------- 02151 void KMReaderWin::slotUrlSave() 02152 { 02153 KMCommand *command = new KMUrlSaveCommand( mUrlClicked, mMainWindow ); 02154 command->start(); 02155 } 02156 02157 //----------------------------------------------------------------------------- 02158 void KMReaderWin::slotMailtoReply() 02159 { 02160 KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mUrlClicked, 02161 message(), copyText() ); 02162 command->start(); 02163 } 02164 02165 //----------------------------------------------------------------------------- 02166 void KMReaderWin::slotShowMsgSrc() 02167 { 02168 KMMessage *msg = message(); 02169 if ( !msg ) 02170 return; 02171 KMShowMsgSrcCommand *command = new KMShowMsgSrcCommand( msg, isFixedFont() ); 02172 command->start(); 02173 } 02174 02175 //----------------------------------------------------------------------------- 02176 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) { 02177 return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ; 02178 } 02179 02180 partNode * KMReaderWin::partNodeForId( int id ) { 02181 return mRootNode ? mRootNode->findId( id ) : 0 ; 02182 } 02183 02184 //----------------------------------------------------------------------------- 02185 void KMReaderWin::slotSaveAttachments() 02186 { 02187 mAtmUpdate = true; 02188 KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow, 02189 message() ); 02190 saveCommand->start(); 02191 } 02192 02193 //----------------------------------------------------------------------------- 02194 void KMReaderWin::slotSaveMsg() 02195 { 02196 KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() ); 02197 02198 if (saveCommand->url().isEmpty()) 02199 delete saveCommand; 02200 else 02201 saveCommand->start(); 02202 } 02203 //----------------------------------------------------------------------------- 02204 void KMReaderWin::slotIMChat() 02205 { 02206 KMCommand *command = new KMIMChatCommand( mUrlClicked, message() ); 02207 command->start(); 02208 } 02209 02210 //----------------------------------------------------------------------------- 02211 QString KMReaderWin::createAtmFileLink() const 02212 { 02213 QFileInfo atmFileInfo(mAtmCurrentName); 02214 02215 KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["), 02216 "]."+ atmFileInfo.extension() ); 02217 02218 linkFile->setAutoDelete(true); 02219 QString linkName = linkFile->name(); 02220 delete linkFile; 02221 02222 if ( link(QFile::encodeName(mAtmCurrentName), QFile::encodeName(linkName)) == 0 ) { 02223 return linkName; // success 02224 } 02225 kdWarning() << "Couldn't link to " << mAtmCurrentName << endl; 02226 return QString::null; 02227 } 02228 02229 #include "kmreaderwin.moc" 02230 02231
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 Oct 1 15:19:24 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003