kmail Library API Documentation

kmreaderwin.cpp

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