kmail

kmreaderwin.cpp
1 // -*- mode: C++; c-file-style: "gnu" -*-
2 // kmreaderwin.cpp
3 // Author: Markus Wuebben <markus.wuebben@kde.org>
4 
5 // define this to copy all html that is written to the readerwindow to
6 // filehtmlwriter.out in the current working directory
7 // #define KMAIL_READER_HTML_DEBUG 1
8 
9 #include <config.h>
10 
11 #include "kmreaderwin.h"
12 
13 #include "globalsettings.h"
14 #include "kmversion.h"
15 #include "kmmainwidget.h"
16 #include "kmreadermainwin.h"
17 #include <libtdepim/tdefileio.h>
18 #include "kmfolderindex.h"
19 #include "kmcommands.h"
20 #include "kmmsgpartdlg.h"
21 #include "mailsourceviewer.h"
22 using KMail::MailSourceViewer;
23 #include "partNode.h"
24 #include "kmmsgdict.h"
25 #include "messagesender.h"
26 #include "kcursorsaver.h"
27 #include "kmfolder.h"
28 #include "vcardviewer.h"
29 using KMail::VCardViewer;
30 #include "objecttreeparser.h"
31 using KMail::ObjectTreeParser;
32 #include "partmetadata.h"
33 using KMail::PartMetaData;
34 #include "attachmentstrategy.h"
35 using KMail::AttachmentStrategy;
36 #include "headerstrategy.h"
37 using KMail::HeaderStrategy;
38 #include "headerstyle.h"
39 using KMail::HeaderStyle;
40 #include "tdehtmlparthtmlwriter.h"
41 using KMail::HtmlWriter;
42 using KMail::KHtmlPartHtmlWriter;
43 #include "htmlstatusbar.h"
45 #include "folderjob.h"
46 using KMail::FolderJob;
47 #include "csshelper.h"
48 using KMail::CSSHelper;
49 #include "isubject.h"
50 using KMail::ISubject;
51 #include "urlhandlermanager.h"
53 #include "interfaces/observable.h"
54 #include "util.h"
55 #include "kmheaders.h"
56 
57 #include "broadcaststatus.h"
58 
59 #include <kmime_mdn.h>
60 using namespace KMime;
61 #ifdef KMAIL_READER_HTML_DEBUG
62 #include "filehtmlwriter.h"
63 using KMail::FileHtmlWriter;
64 #include "teehtmlwriter.h"
66 #endif
67 
68 #include <kasciistringtools.h>
69 #include <kstringhandler.h>
70 
71 #include <mimelib/mimepp.h>
72 #include <mimelib/body.h>
73 #include <mimelib/utility.h>
74 
75 #include <kleo/specialjob.h>
76 #include <kleo/cryptobackend.h>
77 #include <kleo/cryptobackendfactory.h>
78 
79 // KABC includes
80 #include <tdeabc/addressee.h>
81 #include <tdeabc/vcardconverter.h>
82 
83 // tdehtml headers
84 #include <tdehtml_part.h>
85 #include <tdehtmlview.h> // So that we can get rid of the frames
86 #include <dom/html_element.h>
87 #include <dom/html_block.h>
88 #include <dom/html_document.h>
89 #include <dom/dom_string.h>
90 #include <dom/dom_exception.h>
91 
92 #include <tdeapplication.h>
93 // for the click on attachment stuff (dnaber):
94 #include <kuserprofile.h>
95 #include <kcharsets.h>
96 #include <tdepopupmenu.h>
97 #include <kstandarddirs.h> // Sven's : for access and getpid
98 #include <kcursor.h>
99 #include <kdebug.h>
100 #include <tdefiledialog.h>
101 #include <tdelocale.h>
102 #include <tdemessagebox.h>
103 #include <tdeglobalsettings.h>
104 #include <krun.h>
105 #include <tdetempfile.h>
106 #include <kprocess.h>
107 #include <kdialog.h>
108 #include <tdeaction.h>
109 #include <kiconloader.h>
110 #include <kmdcodec.h>
111 #include <kasciistricmp.h>
112 #include <kurldrag.h>
113 
114 #include <tqclipboard.h>
115 #include <tqhbox.h>
116 #include <tqtextcodec.h>
117 #include <tqpaintdevicemetrics.h>
118 #include <tqlayout.h>
119 #include <tqlabel.h>
120 #include <tqsplitter.h>
121 #include <tqstyle.h>
122 
123 // X headers...
124 #undef Never
125 #undef Always
126 
127 #include <unistd.h>
128 #include <stdlib.h>
129 #include <sys/stat.h>
130 #include <errno.h>
131 #include <stdio.h>
132 #include <ctype.h>
133 #include <string.h>
134 
135 #ifdef HAVE_PATHS_H
136 #include <paths.h>
137 #endif
138 
139 class NewByteArray : public TQByteArray
140 {
141 public:
142  NewByteArray &appendNULL();
143  NewByteArray &operator+=( const char * );
144  NewByteArray &operator+=( const TQByteArray & );
145  NewByteArray &operator+=( const TQCString & );
146  TQByteArray& qByteArray();
147 };
148 
149 NewByteArray& NewByteArray::appendNULL()
150 {
151  TQByteArray::detach();
152  uint len1 = size();
153  if ( !TQByteArray::resize( len1 + 1 ) )
154  return *this;
155  *(data() + len1) = '\0';
156  return *this;
157 }
158 NewByteArray& NewByteArray::operator+=( const char * newData )
159 {
160  if ( !newData )
161  return *this;
162  TQByteArray::detach();
163  uint len1 = size();
164  uint len2 = tqstrlen( newData );
165  if ( !TQByteArray::resize( len1 + len2 ) )
166  return *this;
167  memcpy( data() + len1, newData, len2 );
168  return *this;
169 }
170 NewByteArray& NewByteArray::operator+=( const TQByteArray & newData )
171 {
172  if ( newData.isNull() )
173  return *this;
174  TQByteArray::detach();
175  uint len1 = size();
176  uint len2 = newData.size();
177  if ( !TQByteArray::resize( len1 + len2 ) )
178  return *this;
179  memcpy( data() + len1, newData.data(), len2 );
180  return *this;
181 }
182 NewByteArray& NewByteArray::operator+=( const TQCString & newData )
183 {
184  if ( newData.isEmpty() )
185  return *this;
186  TQByteArray::detach();
187  uint len1 = size();
188  uint len2 = newData.length(); // forget about the trailing 0x00 !
189  if ( !TQByteArray::resize( len1 + len2 ) )
190  return *this;
191  memcpy( data() + len1, newData.data(), len2 );
192  return *this;
193 }
194 TQByteArray& NewByteArray::qByteArray()
195 {
196  return *((TQByteArray*)this);
197 }
198 
199 // This function returns the complete data that were in this
200 // message parts - *after* all encryption has been removed that
201 // could be removed.
202 // - This is used to store the message in decrypted form.
203 void KMReaderWin::objectTreeToDecryptedMsg( partNode* node,
204  NewByteArray& resultingData,
205  KMMessage& theMessage,
206  bool weAreReplacingTheRootNode,
207  int recCount )
208 {
209  kdDebug(5006) << TQString("-------------------------------------------------" ) << endl;
210  kdDebug(5006) << TQString("KMReaderWin::objectTreeToDecryptedMsg( %1 ) START").arg( recCount ) << endl;
211  if( node ) {
212 
213  kdDebug(5006) << node->typeString() << '/' << node->subTypeString() << endl;
214 
215  partNode* curNode = node;
216  partNode* dataNode = curNode;
217  partNode * child = node->firstChild();
218  const bool bIsMultipart = node->type() == DwMime::kTypeMultipart ;
219  bool bKeepPartAsIs = false;
220 
221  switch( curNode->type() ){
222  case DwMime::kTypeMultipart: {
223  switch( curNode->subType() ){
224  case DwMime::kSubtypeSigned: {
225  bKeepPartAsIs = true;
226  }
227  break;
228  case DwMime::kSubtypeEncrypted: {
229  if ( child )
230  dataNode = child;
231  }
232  break;
233  }
234  }
235  break;
236  case DwMime::kTypeMessage: {
237  switch( curNode->subType() ){
238  case DwMime::kSubtypeRfc822: {
239  if ( child )
240  dataNode = child;
241  }
242  break;
243  }
244  }
245  break;
246  case DwMime::kTypeApplication: {
247  switch( curNode->subType() ){
248  case DwMime::kSubtypeOctetStream: {
249  if ( child )
250  dataNode = child;
251  }
252  break;
253  case DwMime::kSubtypePkcs7Signature: {
254  // note: subtype Pkcs7Signature specifies a signature part
255  // which we do NOT want to remove!
256  bKeepPartAsIs = true;
257  }
258  break;
259  case DwMime::kSubtypePkcs7Mime: {
260  // note: subtype Pkcs7Mime can also be signed
261  // and we do NOT want to remove the signature!
262  if ( child && curNode->encryptionState() != KMMsgNotEncrypted )
263  dataNode = child;
264  }
265  break;
266  }
267  }
268  break;
269  }
270 
271 
272  DwHeaders& rootHeaders( theMessage.headers() );
273  DwBodyPart * part = dataNode->dwPart() ? dataNode->dwPart() : 0;
274  DwHeaders * headers(
275  (part && part->hasHeaders())
276  ? &part->Headers()
277  : ( (weAreReplacingTheRootNode || !dataNode->parentNode())
278  ? &rootHeaders
279  : 0 ) );
280  if( dataNode == curNode ) {
281 kdDebug(5006) << "dataNode == curNode: Save curNode without replacing it." << endl;
282 
283  // A) Store the headers of this part IF curNode is not the root node
284  // AND we are not replacing a node that already *has* replaced
285  // the root node in previous recursion steps of this function...
286  if( headers ) {
287  if( dataNode->parentNode() && !weAreReplacingTheRootNode ) {
288 kdDebug(5006) << "dataNode is NOT replacing the root node: Store the headers." << endl;
289  resultingData += headers->AsString().c_str();
290  } else if( weAreReplacingTheRootNode && part && part->hasHeaders() ){
291 kdDebug(5006) << "dataNode replace the root node: Do NOT store the headers but change" << endl;
292 kdDebug(5006) << " the Message's headers accordingly." << endl;
293 kdDebug(5006) << " old Content-Type = " << rootHeaders.ContentType().AsString().c_str() << endl;
294 kdDebug(5006) << " new Content-Type = " << headers->ContentType( ).AsString().c_str() << endl;
295  rootHeaders.ContentType() = headers->ContentType();
296  theMessage.setContentTransferEncodingStr(
297  headers->HasContentTransferEncoding()
298  ? headers->ContentTransferEncoding().AsString().c_str()
299  : "" );
300  rootHeaders.ContentDescription() = headers->ContentDescription();
301  rootHeaders.ContentDisposition() = headers->ContentDisposition();
302  theMessage.setNeedsAssembly();
303  }
304  }
305 
306  if ( bKeepPartAsIs ) {
307  resultingData += dataNode->encodedBody();
308  } else {
309 
310  // B) Store the body of this part.
311  if( headers && bIsMultipart && dataNode->firstChild() ) {
312 kdDebug(5006) << "is valid Multipart, processing children:" << endl;
313  TQCString boundary = headers->ContentType().Boundary().c_str();
314  curNode = dataNode->firstChild();
315  // store children of multipart
316  while( curNode ) {
317 kdDebug(5006) << "--boundary" << endl;
318  if( resultingData.size() &&
319  ( '\n' != resultingData.at( resultingData.size()-1 ) ) )
320  resultingData += TQCString( "\n" );
321  resultingData += TQCString( "\n" );
322  resultingData += "--";
323  resultingData += boundary;
324  resultingData += "\n";
325  // note: We are processing a harmless multipart that is *not*
326  // to be replaced by one of it's children, therefor
327  // we set their doStoreHeaders to true.
328  objectTreeToDecryptedMsg( curNode,
329  resultingData,
330  theMessage,
331  false,
332  recCount + 1 );
333  curNode = curNode->nextSibling();
334  }
335 kdDebug(5006) << "--boundary--" << endl;
336  resultingData += "\n--";
337  resultingData += boundary;
338  resultingData += "--\n\n";
339 kdDebug(5006) << "Multipart processing children - DONE" << endl;
340  } else if( part ){
341  // store simple part
342 kdDebug(5006) << "is Simple part or invalid Multipart, storing body data .. DONE" << endl;
343  resultingData += part->Body().AsString().c_str();
344  }
345  }
346  } else {
347 kdDebug(5006) << "dataNode != curNode: Replace curNode by dataNode." << endl;
348  bool rootNodeReplaceFlag = weAreReplacingTheRootNode || !curNode->parentNode();
349  if( rootNodeReplaceFlag ) {
350 kdDebug(5006) << " Root node will be replaced." << endl;
351  } else {
352 kdDebug(5006) << " Root node will NOT be replaced." << endl;
353  }
354  // store special data to replace the current part
355  // (e.g. decrypted data or embedded RfC 822 data)
356  objectTreeToDecryptedMsg( dataNode,
357  resultingData,
358  theMessage,
359  rootNodeReplaceFlag,
360  recCount + 1 );
361  }
362  }
363  kdDebug(5006) << TQString("\nKMReaderWin::objectTreeToDecryptedMsg( %1 ) END").arg( recCount ) << endl;
364 }
365 
366 
367 /*
368  ===========================================================================
369 
370 
371  E N D O F T E M P O R A R Y M I M E C O D E
372 
373 
374  ===========================================================================
375 */
376 
377 
378 
379 
380 
381 
382 
383 
384 
385 
386 
387 void KMReaderWin::createWidgets() {
388  TQVBoxLayout * vlay = new TQVBoxLayout( this );
389  mSplitter = new TQSplitter( Qt::Vertical, this, "mSplitter" );
390  vlay->addWidget( mSplitter );
391  mMimePartTree = new KMMimePartTree( this, mSplitter, "mMimePartTree" );
392  mBox = new TQHBox( mSplitter, "mBox" );
393  setStyleDependantFrameWidth();
394  mBox->setFrameStyle( mMimePartTree->frameStyle() );
395  mColorBar = new HtmlStatusBar( mBox, "mColorBar" );
396  mViewer = new TDEHTMLPart( mBox, "mViewer" );
397  mSplitter->setOpaqueResize( TDEGlobalSettings::opaqueResize() );
398  mSplitter->setResizeMode( mMimePartTree, TQSplitter::KeepSize );
399 }
400 
401 const int KMReaderWin::delay = 150;
402 
403 //-----------------------------------------------------------------------------
404 KMReaderWin::KMReaderWin(TQWidget *aParent,
405  TQWidget *mainWindow,
406  TDEActionCollection* actionCollection,
407  const char *aName,
408  int aFlags )
409  : TQWidget(aParent, aName, aFlags | TQt::WDestructiveClose),
410  mSerNumOfOriginalMessage( 0 ),
411  mNodeIdOffset( -1 ),
412  mAttachmentStrategy( 0 ),
413  mHeaderStrategy( 0 ),
414  mHeaderStyle( 0 ),
415  mUpdateReaderWinTimer( 0, "mUpdateReaderWinTimer" ),
416  mResizeTimer( 0, "mResizeTimer" ),
417  mDelayedMarkTimer( 0, "mDelayedMarkTimer" ),
418  mHeaderRefreshTimer( 0, "mHeaderRefreshTimer" ),
419  mOldGlobalOverrideEncoding( "---" ), // init with dummy value
420  mCSSHelper( 0 ),
421  mRootNode( 0 ),
422  mMainWindow( mainWindow ),
423  mActionCollection( actionCollection ),
424  mMailToComposeAction( 0 ),
425  mMailToReplyAction( 0 ),
426  mMailToForwardAction( 0 ),
427  mAddAddrBookAction( 0 ),
428  mOpenAddrBookAction( 0 ),
429  mCopyAction( 0 ),
430  mCopyURLAction( 0 ),
431  mUrlOpenAction( 0 ),
432  mUrlSaveAsAction( 0 ),
433  mAddBookmarksAction( 0 ),
434  mStartIMChatAction( 0 ),
435  mSelectAllAction( 0 ),
436  mHeaderOnlyAttachmentsAction( 0 ),
437  mSelectEncodingAction( 0 ),
438  mToggleFixFontAction( 0 ),
439  mCanStartDrag( false ),
440  mHtmlWriter( 0 ),
441  mSavedRelativePosition( 0 ),
442  mDecrytMessageOverwrite( false ),
443  mShowSignatureDetails( false ),
444  mShowAttachmentQuicklist( true ),
445  mShowRawToltecMail( false )
446 {
447  mExternalWindow = (aParent == mainWindow );
448  mSplitterSizes << 180 << 100;
449  mMimeTreeMode = 1;
450  mMimeTreeAtBottom = true;
451  mAutoDelete = false;
452  mLastSerNum = 0;
453  mWaitingForSerNum = 0;
454  mMessage = 0;
455  mMsgDisplay = true;
456  mPrinting = false;
457  mShowColorbar = false;
458  mAtmUpdate = false;
459 
460  createWidgets();
461  createActions( actionCollection );
462  initHtmlWidget();
463  readConfig();
464 
465  mHtmlOverride = false;
466  mHtmlLoadExtOverride = false;
467 
468  mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin() - 1;
469 
470  connect( &mUpdateReaderWinTimer, TQT_SIGNAL(timeout()),
471  TQT_TQOBJECT(this), TQT_SLOT(updateReaderWin()) );
472  connect( &mResizeTimer, TQT_SIGNAL(timeout()),
473  TQT_TQOBJECT(this), TQT_SLOT(slotDelayedResize()) );
474  connect( &mDelayedMarkTimer, TQT_SIGNAL(timeout()),
475  TQT_TQOBJECT(this), TQT_SLOT(slotTouchMessage()) );
476  connect( &mHeaderRefreshTimer, TQT_SIGNAL(timeout()),
477  TQT_TQOBJECT(this), TQT_SLOT(updateHeader()) );
478 
479 }
480 
481 void KMReaderWin::createActions( TDEActionCollection * ac ) {
482  if ( !ac )
483  return;
484 
485  TDERadioAction *raction = 0;
486 
487  // header style
488  TDEActionMenu *headerMenu =
489  new TDEActionMenu( i18n("View->", "&Headers"), ac, "view_headers" );
490  headerMenu->setToolTip( i18n("Choose display style of message headers") );
491 
492  connect( headerMenu, TQT_SIGNAL(activated()),
493  TQT_TQOBJECT(this), TQT_SLOT(slotCycleHeaderStyles()) );
494 
495  raction = new TDERadioAction( i18n("View->headers->", "&Enterprise Headers"), 0,
496  TQT_TQOBJECT(this), TQT_SLOT(slotEnterpriseHeaders()),
497  ac, "view_headers_enterprise" );
498  raction->setToolTip( i18n("Show the list of headers in Enterprise style") );
499  raction->setExclusiveGroup( "view_headers_group" );
500  headerMenu->insert(raction);
501 
502  raction = new TDERadioAction( i18n("View->headers->", "&Fancy Headers"), 0,
503  TQT_TQOBJECT(this), TQT_SLOT(slotFancyHeaders()),
504  ac, "view_headers_fancy" );
505  raction->setToolTip( i18n("Show the list of headers in a fancy format") );
506  raction->setExclusiveGroup( "view_headers_group" );
507  headerMenu->insert( raction );
508 
509  raction = new TDERadioAction( i18n("View->headers->", "&Brief Headers"), 0,
510  TQT_TQOBJECT(this), TQT_SLOT(slotBriefHeaders()),
511  ac, "view_headers_brief" );
512  raction->setToolTip( i18n("Show brief list of message headers") );
513  raction->setExclusiveGroup( "view_headers_group" );
514  headerMenu->insert( raction );
515 
516  raction = new TDERadioAction( i18n("View->headers->", "&Standard Headers"), 0,
517  TQT_TQOBJECT(this), TQT_SLOT(slotStandardHeaders()),
518  ac, "view_headers_standard" );
519  raction->setToolTip( i18n("Show standard list of message headers") );
520  raction->setExclusiveGroup( "view_headers_group" );
521  headerMenu->insert( raction );
522 
523  raction = new TDERadioAction( i18n("View->headers->", "&Long Headers"), 0,
524  TQT_TQOBJECT(this), TQT_SLOT(slotLongHeaders()),
525  ac, "view_headers_long" );
526  raction->setToolTip( i18n("Show long list of message headers") );
527  raction->setExclusiveGroup( "view_headers_group" );
528  headerMenu->insert( raction );
529 
530  raction = new TDERadioAction( i18n("View->headers->", "&All Headers"), 0,
531  TQT_TQOBJECT(this), TQT_SLOT(slotAllHeaders()),
532  ac, "view_headers_all" );
533  raction->setToolTip( i18n("Show all message headers") );
534  raction->setExclusiveGroup( "view_headers_group" );
535  headerMenu->insert( raction );
536 
537  // attachment style
538  TDEActionMenu *attachmentMenu =
539  new TDEActionMenu( i18n("View->", "&Attachments"), ac, "view_attachments" );
540  attachmentMenu->setToolTip( i18n("Choose display style of attachments") );
541  connect( attachmentMenu, TQT_SIGNAL(activated()),
542  TQT_TQOBJECT(this), TQT_SLOT(slotCycleAttachmentStrategy()) );
543 
544  raction = new TDERadioAction( i18n("View->attachments->", "&As Icons"), 0,
545  TQT_TQOBJECT(this), TQT_SLOT(slotIconicAttachments()),
546  ac, "view_attachments_as_icons" );
547  raction->setToolTip( i18n("Show all attachments as icons. Click to see them.") );
548  raction->setExclusiveGroup( "view_attachments_group" );
549  attachmentMenu->insert( raction );
550 
551  raction = new TDERadioAction( i18n("View->attachments->", "&Smart"), 0,
552  TQT_TQOBJECT(this), TQT_SLOT(slotSmartAttachments()),
553  ac, "view_attachments_smart" );
554  raction->setToolTip( i18n("Show attachments as suggested by sender.") );
555  raction->setExclusiveGroup( "view_attachments_group" );
556  attachmentMenu->insert( raction );
557 
558  raction = new TDERadioAction( i18n("View->attachments->", "&Inline"), 0,
559  TQT_TQOBJECT(this), TQT_SLOT(slotInlineAttachments()),
560  ac, "view_attachments_inline" );
561  raction->setToolTip( i18n("Show all attachments inline (if possible)") );
562  raction->setExclusiveGroup( "view_attachments_group" );
563  attachmentMenu->insert( raction );
564 
565  raction = new TDERadioAction( i18n("View->attachments->", "&Hide"), 0,
566  TQT_TQOBJECT(this), TQT_SLOT(slotHideAttachments()),
567  ac, "view_attachments_hide" );
568  raction->setToolTip( i18n("Do not show attachments in the message viewer") );
569  raction->setExclusiveGroup( "view_attachments_group" );
570  attachmentMenu->insert( raction );
571 
572  mHeaderOnlyAttachmentsAction = new TDERadioAction( i18n( "View->attachments->", "In Header &Only" ), 0,
573  TQT_TQOBJECT(this), TQT_SLOT( slotHeaderOnlyAttachments() ),
574  ac, "view_attachments_headeronly" );
575  mHeaderOnlyAttachmentsAction->setToolTip( i18n( "Show Attachments only in the header of the mail" ) );
576  mHeaderOnlyAttachmentsAction->setExclusiveGroup( "view_attachments_group" );
577  attachmentMenu->insert( mHeaderOnlyAttachmentsAction );
578 
579  // Set Encoding submenu
580  mSelectEncodingAction = new TDESelectAction( i18n( "&Set Encoding" ), "charset", 0,
581  TQT_TQOBJECT(this), TQT_SLOT( slotSetEncoding() ),
582  ac, "encoding" );
583  TQStringList encodings = KMMsgBase::supportedEncodings( false );
584  encodings.prepend( i18n( "Auto" ) );
585  mSelectEncodingAction->setItems( encodings );
586  mSelectEncodingAction->setCurrentItem( 0 );
587 
588  mMailToComposeAction = new TDEAction( i18n("New Message To..."), "mail-message-new",
589  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoCompose()), ac,
590  "mailto_compose" );
591  mMailToReplyAction = new TDEAction( i18n("Reply To..."), "mail-reply-sender",
592  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoReply()), ac,
593  "mailto_reply" );
594  mMailToForwardAction = new TDEAction( i18n("Forward To..."), "mail-forward",
595  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoForward()), ac,
596  "mailto_forward" );
597  mAddAddrBookAction = new TDEAction( i18n("Add to Address Book"),
598  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoAddAddrBook()),
599  ac, "add_addr_book" );
600  mOpenAddrBookAction = new TDEAction( i18n("Open in Address Book"),
601  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoOpenAddrBook()),
602  ac, "openin_addr_book" );
603  mCopyAction = KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(slotCopySelectedText()), ac, "kmail_copy");
604  mSelectAllAction = new TDEAction( i18n("Select All Text"), CTRL+SHIFT+Key_A, TQT_TQOBJECT(this),
605  TQT_SLOT(selectAll()), ac, "mark_all_text" );
606  mCopyURLAction = new TDEAction( i18n("Copy Link Address"), 0, TQT_TQOBJECT(this),
607  TQT_SLOT(slotUrlCopy()), ac, "copy_url" );
608  mUrlOpenAction = new TDEAction( i18n("Open URL"), 0, TQT_TQOBJECT(this),
609  TQT_SLOT(slotUrlOpen()), ac, "open_url" );
610  mAddBookmarksAction = new TDEAction( i18n("Bookmark This Link"),
611  "bookmark_add",
612  0, TQT_TQOBJECT(this), TQT_SLOT(slotAddBookmarks()),
613  ac, "add_bookmarks" );
614  mUrlSaveAsAction = new TDEAction( i18n("Save Link As..."), 0, TQT_TQOBJECT(this),
615  TQT_SLOT(slotUrlSave()), ac, "saveas_url" );
616 
617  mToggleFixFontAction = new TDEToggleAction( i18n("Use Fi&xed Font"),
618  Key_X, TQT_TQOBJECT(this), TQT_SLOT(slotToggleFixedFont()),
619  ac, "toggle_fixedfont" );
620 
621  mStartIMChatAction = new TDEAction( i18n("Chat &With..."), 0, TQT_TQOBJECT(this),
622  TQT_SLOT(slotIMChat()), ac, "start_im_chat" );
623 }
624 
625 // little helper function
626 TDERadioAction *KMReaderWin::actionForHeaderStyle( const HeaderStyle * style, const HeaderStrategy * strategy ) {
627  if ( !mActionCollection )
628  return 0;
629  const char * actionName = 0;
630  if ( style == HeaderStyle::enterprise() )
631  actionName = "view_headers_enterprise";
632  if ( style == HeaderStyle::fancy() )
633  actionName = "view_headers_fancy";
634  else if ( style == HeaderStyle::brief() )
635  actionName = "view_headers_brief";
636  else if ( style == HeaderStyle::plain() ) {
637  if ( strategy == HeaderStrategy::standard() )
638  actionName = "view_headers_standard";
639  else if ( strategy == HeaderStrategy::rich() )
640  actionName = "view_headers_long";
641  else if ( strategy == HeaderStrategy::all() )
642  actionName = "view_headers_all";
643  }
644  if ( actionName )
645  return static_cast<TDERadioAction*>(mActionCollection->action(actionName));
646  else
647  return 0;
648 }
649 
650 TDERadioAction *KMReaderWin::actionForAttachmentStrategy( const AttachmentStrategy * as ) {
651  if ( !mActionCollection )
652  return 0;
653  const char * actionName = 0;
654  if ( as == AttachmentStrategy::iconic() )
655  actionName = "view_attachments_as_icons";
656  else if ( as == AttachmentStrategy::smart() )
657  actionName = "view_attachments_smart";
658  else if ( as == AttachmentStrategy::inlined() )
659  actionName = "view_attachments_inline";
660  else if ( as == AttachmentStrategy::hidden() )
661  actionName = "view_attachments_hide";
662  else if ( as == AttachmentStrategy::headerOnly() )
663  actionName = "view_attachments_headeronly";
664 
665  if ( actionName )
666  return static_cast<TDERadioAction*>(mActionCollection->action(actionName));
667  else
668  return 0;
669 }
670 
671 void KMReaderWin::slotEnterpriseHeaders() {
672  setHeaderStyleAndStrategy( HeaderStyle::enterprise(),
673  HeaderStrategy::rich() );
674  if( !mExternalWindow )
675  writeConfig();
676 }
677 
678 void KMReaderWin::slotFancyHeaders() {
679  setHeaderStyleAndStrategy( HeaderStyle::fancy(),
680  HeaderStrategy::rich() );
681  if( !mExternalWindow )
682  writeConfig();
683 }
684 
685 void KMReaderWin::slotBriefHeaders() {
686  setHeaderStyleAndStrategy( HeaderStyle::brief(),
687  HeaderStrategy::brief() );
688  if( !mExternalWindow )
689  writeConfig();
690 }
691 
692 void KMReaderWin::slotStandardHeaders() {
693  setHeaderStyleAndStrategy( HeaderStyle::plain(),
694  HeaderStrategy::standard());
695  writeConfig();
696 }
697 
698 void KMReaderWin::slotLongHeaders() {
699  setHeaderStyleAndStrategy( HeaderStyle::plain(),
700  HeaderStrategy::rich() );
701  if( !mExternalWindow )
702  writeConfig();
703 }
704 
705 void KMReaderWin::slotAllHeaders() {
706  setHeaderStyleAndStrategy( HeaderStyle::plain(),
707  HeaderStrategy::all() );
708  if( !mExternalWindow )
709  writeConfig();
710 }
711 
712 void KMReaderWin::slotLevelQuote( int l )
713 {
714  mLevelQuote = l;
716  update(true);
717 }
718 
719 void KMReaderWin::slotCycleHeaderStyles() {
720  const HeaderStrategy * strategy = headerStrategy();
721  const HeaderStyle * style = headerStyle();
722 
723  const char * actionName = 0;
724  if ( style == HeaderStyle::enterprise() ) {
725  slotFancyHeaders();
726  actionName = "view_headers_fancy";
727  }
728  if ( style == HeaderStyle::fancy() ) {
729  slotBriefHeaders();
730  actionName = "view_headers_brief";
731  } else if ( style == HeaderStyle::brief() ) {
732  slotStandardHeaders();
733  actionName = "view_headers_standard";
734  } else if ( style == HeaderStyle::plain() ) {
735  if ( strategy == HeaderStrategy::standard() ) {
736  slotLongHeaders();
737  actionName = "view_headers_long";
738  } else if ( strategy == HeaderStrategy::rich() ) {
739  slotAllHeaders();
740  actionName = "view_headers_all";
741  } else if ( strategy == HeaderStrategy::all() ) {
742  slotEnterpriseHeaders();
743  actionName = "view_headers_enterprise";
744  }
745  }
746 
747  if ( actionName )
748  static_cast<TDERadioAction*>( mActionCollection->action( actionName ) )->setChecked( true );
749 }
750 
751 
752 void KMReaderWin::slotIconicAttachments() {
753  setAttachmentStrategy( AttachmentStrategy::iconic() );
754 }
755 
756 void KMReaderWin::slotSmartAttachments() {
757  setAttachmentStrategy( AttachmentStrategy::smart() );
758 }
759 
760 void KMReaderWin::slotInlineAttachments() {
761  setAttachmentStrategy( AttachmentStrategy::inlined() );
762 }
763 
764 void KMReaderWin::slotHideAttachments() {
765  setAttachmentStrategy( AttachmentStrategy::hidden() );
766 }
767 
768 void KMReaderWin::slotHeaderOnlyAttachments() {
769  setAttachmentStrategy( AttachmentStrategy::headerOnly() );
770 }
771 
772 void KMReaderWin::slotCycleAttachmentStrategy() {
773  setAttachmentStrategy( attachmentStrategy()->next() );
774  TDERadioAction * action = actionForAttachmentStrategy( attachmentStrategy() );
775  assert( action );
776  action->setChecked( true );
777 }
778 
779 
780 //-----------------------------------------------------------------------------
781 KMReaderWin::~KMReaderWin()
782 {
783  clearBodyPartMementos();
784  delete mHtmlWriter; mHtmlWriter = 0;
785  delete mCSSHelper;
786  if (mAutoDelete) delete message();
787  delete mRootNode; mRootNode = 0;
788  removeTempFiles();
789 }
790 
791 
792 //-----------------------------------------------------------------------------
793 void KMReaderWin::slotMessageArrived( KMMessage *msg )
794 {
795  if (msg && ((KMMsgBase*)msg)->isMessage()) {
796  if ( msg->getMsgSerNum() == mWaitingForSerNum ) {
797  setMsg( msg, true );
798  } else {
799  //kdDebug( 5006 ) << "KMReaderWin::slotMessageArrived - ignoring update" << endl;
800  }
801  }
802 }
803 
804 //-----------------------------------------------------------------------------
806 {
807  if ( !mAtmUpdate ) {
808  // reparse the msg
809  //kdDebug(5006) << "KMReaderWin::update - message" << endl;
810  updateReaderWin();
811  return;
812  }
813 
814  if ( !mRootNode )
815  return;
816 
817  KMMessage* msg = static_cast<KMMessage*>( observable );
818  assert( msg != 0 );
819 
820  // find our partNode and update it
821  if ( !msg->lastUpdatedPart() ) {
822  kdDebug(5006) << "KMReaderWin::update - no updated part" << endl;
823  return;
824  }
825  partNode* node = mRootNode->findNodeForDwPart( msg->lastUpdatedPart() );
826  if ( !node ) {
827  kdDebug(5006) << "KMReaderWin::update - can't find node for part" << endl;
828  return;
829  }
830  node->setDwPart( msg->lastUpdatedPart() );
831 
832  // update the tmp file
833  // we have to set it writeable temporarily
834  ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRWXU );
835  TQByteArray data = node->msgPart().bodyDecodedBinary();
836  size_t size = data.size();
837  if ( node->msgPart().type() == DwMime::kTypeText && size) {
838  size = KMail::Util::crlf2lf( data.data(), size );
839  }
840  KPIM::kBytesToFile( data.data(), size, mAtmCurrentName, false, false, false );
841  ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRUSR );
842 
843  mAtmUpdate = false;
844 }
845 
846 //-----------------------------------------------------------------------------
848 {
849  for (TQStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end();
850  it++)
851  {
852  TQFile::remove(*it);
853  }
854  mTempFiles.clear();
855  for (TQStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end();
856  it++)
857  {
858  TQDir(*it).rmdir(*it);
859  }
860  mTempDirs.clear();
861 }
862 
863 
864 //-----------------------------------------------------------------------------
865 bool KMReaderWin::event(TQEvent *e)
866 {
867  if (e->type() == TQEvent::ApplicationPaletteChange)
868  {
869  delete mCSSHelper;
870  mCSSHelper = new KMail::CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) );
871  if (message())
872  message()->readConfig();
873  update( true ); // Force update
874  return true;
875  }
876  return TQWidget::event(e);
877 }
878 
879 
880 //-----------------------------------------------------------------------------
882 {
883  const TDEConfigGroup mdnGroup( KMKernel::config(), "MDN" );
884  /*should be: const*/ TDEConfigGroup reader( KMKernel::config(), "Reader" );
885 
886  delete mCSSHelper;
887  mCSSHelper = new KMail::CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) );
888 
889  mNoMDNsWhenEncrypted = mdnGroup.readBoolEntry( "not-send-when-encrypted", true );
890 
891  mUseFixedFont = reader.readBoolEntry( "useFixedFont", false );
892  if ( mToggleFixFontAction )
893  mToggleFixFontAction->setChecked( mUseFixedFont );
894 
895  mHtmlMail = reader.readBoolEntry( "htmlMail", false );
896  mHtmlLoadExternal = reader.readBoolEntry( "htmlLoadExternal", false );
897 
898  setHeaderStyleAndStrategy( HeaderStyle::create( reader.readEntry( "header-style", "fancy" ) ),
899  HeaderStrategy::create( reader.readEntry( "header-set-displayed", "rich" ) ) );
900  TDERadioAction *raction = actionForHeaderStyle( headerStyle(), headerStrategy() );
901  if ( raction )
902  raction->setChecked( true );
903 
904  setAttachmentStrategy( AttachmentStrategy::create( reader.readEntry( "attachment-strategy", "smart" ) ) );
905  raction = actionForAttachmentStrategy( attachmentStrategy() );
906  if ( raction )
907  raction->setChecked( true );
908 
909  // if the user uses OpenPGP then the color bar defaults to enabled
910  // else it defaults to disabled
911  mShowColorbar = reader.readBoolEntry( "showColorbar", Kpgp::Module::getKpgp()->usePGP() );
912  // if the value defaults to enabled and KMail (with color bar) is used for
913  // the first time the config dialog doesn't know this if we don't save the
914  // value now
915  reader.writeEntry( "showColorbar", mShowColorbar );
916 
917  mMimeTreeAtBottom = reader.readEntry( "MimeTreeLocation", "bottom" ) != "top";
918  const TQString s = reader.readEntry( "MimeTreeMode", "smart" );
919  if ( s == "never" )
920  mMimeTreeMode = 0;
921  else if ( s == "always" )
922  mMimeTreeMode = 2;
923  else
924  mMimeTreeMode = 1;
925 
926  const int mimeH = reader.readNumEntry( "MimePaneHeight", 100 );
927  const int messageH = reader.readNumEntry( "MessagePaneHeight", 180 );
928  mSplitterSizes.clear();
929  if ( mMimeTreeAtBottom )
930  mSplitterSizes << messageH << mimeH;
931  else
932  mSplitterSizes << mimeH << messageH;
933 
934  adjustLayout();
935 
936  readGlobalOverrideCodec();
937 
938  if (message())
939  update();
941 }
942 
943 
944 void KMReaderWin::adjustLayout() {
945  if ( mMimeTreeAtBottom )
946  mSplitter->moveToLast( mMimePartTree );
947  else
948  mSplitter->moveToFirst( mMimePartTree );
949  mSplitter->setSizes( mSplitterSizes );
950 
951  if ( mMimeTreeMode == 2 && mMsgDisplay )
952  mMimePartTree->show();
953  else
954  mMimePartTree->hide();
955 
956  if ( mShowColorbar && mMsgDisplay )
957  mColorBar->show();
958  else
959  mColorBar->hide();
960 }
961 
962 
963 void KMReaderWin::saveSplitterSizes( TDEConfigBase & c ) const {
964  if ( !mSplitter || !mMimePartTree )
965  return;
966  if ( mMimePartTree->isHidden() )
967  return; // don't rely on TQSplitter maintaining sizes for hidden widgets.
968 
969  c.writeEntry( "MimePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 1 : 0 ] );
970  c.writeEntry( "MessagePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 0 : 1 ] );
971 }
972 
973 //-----------------------------------------------------------------------------
974 void KMReaderWin::writeConfig( bool sync ) const {
975  TDEConfigGroup reader( KMKernel::config(), "Reader" );
976 
977  reader.writeEntry( "useFixedFont", mUseFixedFont );
978  if ( headerStyle() )
979  reader.writeEntry( "header-style", headerStyle()->name() );
980  if ( headerStrategy() )
981  reader.writeEntry( "header-set-displayed", headerStrategy()->name() );
982  if ( attachmentStrategy() )
983  reader.writeEntry( "attachment-strategy", attachmentStrategy()->name() );
984 
985  saveSplitterSizes( reader );
986 
987  if ( sync )
988  kmkernel->slotRequestConfigSync();
989 }
990 
991 //-----------------------------------------------------------------------------
993 {
994  mViewer->widget()->setFocusPolicy(TQ_WheelFocus);
995  // Let's better be paranoid and disable plugins (it defaults to enabled):
996  mViewer->setPluginsEnabled(false);
997  mViewer->setJScriptEnabled(false); // just make this explicit
998  mViewer->setJavaEnabled(false); // just make this explicit
999  mViewer->setMetaRefreshEnabled(false);
1000  mViewer->setURLCursor(KCursor::handCursor());
1001  // Espen 2000-05-14: Getting rid of thick ugly frames
1002  mViewer->view()->setLineWidth(0);
1003  // register our own event filter for shift-click
1004  mViewer->view()->viewport()->installEventFilter( this );
1005 
1006  if ( !htmlWriter() )
1007 #ifdef KMAIL_READER_HTML_DEBUG
1008  mHtmlWriter = new TeeHtmlWriter( new FileHtmlWriter( TQString() ),
1009  new KHtmlPartHtmlWriter( mViewer, 0 ) );
1010 #else
1011  mHtmlWriter = new KHtmlPartHtmlWriter( mViewer, 0 );
1012 #endif
1013 
1014  connect(mViewer->browserExtension(),
1015  TQT_SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),this,
1016  TQT_SLOT(slotUrlOpen(const KURL &)));
1017  connect(mViewer->browserExtension(),
1018  TQT_SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this,
1019  TQT_SLOT(slotUrlOpen(const KURL &)));
1020  connect(mViewer,TQT_SIGNAL(popupMenu(const TQString &, const TQPoint &)),
1021  TQT_SLOT(slotUrlPopup(const TQString &, const TQPoint &)));
1022  connect( kmkernel->imProxy(), TQT_SIGNAL( sigContactPresenceChanged( const TQString & ) ),
1023  TQT_TQOBJECT(this), TQT_SLOT( contactStatusChanged( const TQString & ) ) );
1024  connect( kmkernel->imProxy(), TQT_SIGNAL( sigPresenceInfoExpired() ),
1025  TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
1026 }
1027 
1028 void KMReaderWin::contactStatusChanged( const TQString &uid)
1029 {
1030 // kdDebug( 5006 ) << k_funcinfo << " got a presence change for " << uid << endl;
1031  // get the list of nodes for this contact from the htmlView
1032  DOM::NodeList presenceNodes = mViewer->htmlDocument()
1033  .getElementsByName( DOM::DOMString( TQString::fromLatin1("presence-") + uid ) );
1034  for ( unsigned int i = 0; i < presenceNodes.length(); ++i ) {
1035  DOM::Node n = presenceNodes.item( i );
1036  kdDebug( 5006 ) << "name is " << n.nodeName().string() << endl;
1037  kdDebug( 5006 ) << "value of content was " << n.firstChild().nodeValue().string() << endl;
1038  TQString newPresence = kmkernel->imProxy()->presenceString( uid );
1039  if ( newPresence.isNull() ) // TDEHTML crashes if you setNodeValue( TQString() )
1040  newPresence = TQString::fromLatin1( "ENOIMRUNNING" );
1041  n.firstChild().setNodeValue( newPresence );
1042 // kdDebug( 5006 ) << "value of content is now " << n.firstChild().nodeValue().string() << endl;
1043  }
1044 // kdDebug( 5006 ) << "and we updated the above presence nodes" << uid << endl;
1045 }
1046 
1047 void KMReaderWin::setAttachmentStrategy( const AttachmentStrategy * strategy ) {
1048  mAttachmentStrategy = strategy ? strategy : AttachmentStrategy::smart();
1049  update( true );
1050 }
1051 
1053  const HeaderStrategy * strategy ) {
1054  mHeaderStyle = style ? style : HeaderStyle::fancy();
1055  mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich();
1056  if ( mHeaderOnlyAttachmentsAction ) {
1057  const bool styleHasAttachmentQuickList = mHeaderStyle == HeaderStyle::fancy() ||
1058  mHeaderStyle == HeaderStyle::enterprise();
1059  mHeaderOnlyAttachmentsAction->setEnabled( styleHasAttachmentQuickList );
1060  if ( !styleHasAttachmentQuickList && mAttachmentStrategy == AttachmentStrategy::headerOnly() ) {
1061  // Style changed to something without an attachment quick list, need to change attachment
1062  // strategy
1063  setAttachmentStrategy( AttachmentStrategy::smart() );
1064  }
1065  }
1066  update( true );
1067 }
1068 
1069 //-----------------------------------------------------------------------------
1070 void KMReaderWin::setOverrideEncoding( const TQString & encoding )
1071 {
1072  if ( encoding == mOverrideEncoding )
1073  return;
1074 
1075  mOverrideEncoding = encoding;
1076  if ( mSelectEncodingAction ) {
1077  if ( encoding.isEmpty() ) {
1078  mSelectEncodingAction->setCurrentItem( 0 );
1079  }
1080  else {
1081  TQStringList encodings = mSelectEncodingAction->items();
1082  uint i = 0;
1083  for ( TQStringList::const_iterator it = encodings.begin(), end = encodings.end(); it != end; ++it, ++i ) {
1084  if ( TDEGlobal::charsets()->encodingForName( *it ) == encoding ) {
1085  mSelectEncodingAction->setCurrentItem( i );
1086  break;
1087  }
1088  }
1089  if ( i == encodings.size() ) {
1090  // the value of encoding is unknown => use Auto
1091  kdWarning(5006) << "Unknown override character encoding \"" << encoding
1092  << "\". Using Auto instead." << endl;
1093  mSelectEncodingAction->setCurrentItem( 0 );
1094  mOverrideEncoding = TQString();
1095  }
1096  }
1097  }
1098  update( true );
1099 }
1100 
1101 
1102 void KMReaderWin::setPrintFont( const TQFont& font )
1103 {
1104 
1105  mCSSHelper->setPrintFont( font );
1106 }
1107 
1108 //-----------------------------------------------------------------------------
1109 const TQTextCodec * KMReaderWin::overrideCodec() const
1110 {
1111  if ( mOverrideEncoding.isEmpty() || mOverrideEncoding == "Auto" ) // Auto
1112  return 0;
1113  else
1114  return KMMsgBase::codecForName( mOverrideEncoding.latin1() );
1115 }
1116 
1117 //-----------------------------------------------------------------------------
1118 void KMReaderWin::slotSetEncoding()
1119 {
1120  if ( mSelectEncodingAction->currentItem() == 0 ) // Auto
1121  mOverrideEncoding = TQString();
1122  else
1123  mOverrideEncoding = TDEGlobal::charsets()->encodingForName( mSelectEncodingAction->currentText() );
1124  update( true );
1125 }
1126 
1127 //-----------------------------------------------------------------------------
1128 void KMReaderWin::readGlobalOverrideCodec()
1129 {
1130  // if the global character encoding wasn't changed then there's nothing to do
1131  if ( GlobalSettings::self()->overrideCharacterEncoding() == mOldGlobalOverrideEncoding )
1132  return;
1133 
1134  setOverrideEncoding( GlobalSettings::self()->overrideCharacterEncoding() );
1135  mOldGlobalOverrideEncoding = GlobalSettings::self()->overrideCharacterEncoding();
1136 }
1137 
1138 //-----------------------------------------------------------------------------
1139 void KMReaderWin::setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset )
1140 {
1141  mSerNumOfOriginalMessage = serNumOfOriginalMessage;
1142  mNodeIdOffset = nodeIdOffset;
1143 }
1144 
1145 //-----------------------------------------------------------------------------
1146 void KMReaderWin::setMsg( KMMessage* aMsg, bool force, bool updateOnly )
1147 {
1148  if ( aMsg ) {
1149  kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " "
1150  << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl;
1151  }
1152 
1153  // Reset message-transient state
1154  if ( aMsg && aMsg->getMsgSerNum() != mLastSerNum && !updateOnly ){
1155  mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin()-1;
1156  mShowRawToltecMail = !GlobalSettings::self()->showToltecReplacementText();
1157  clearBodyPartMementos();
1158  }
1159  if ( mPrinting )
1160  mLevelQuote = -1;
1161 
1162  bool complete = true;
1163  if ( aMsg &&
1164  !aMsg->readyToShow() &&
1165  (aMsg->getMsgSerNum() != mLastSerNum) &&
1166  !aMsg->isComplete() )
1167  complete = false;
1168 
1169  // If not forced and there is aMsg and aMsg is same as mMsg then return
1170  if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum)
1171  return;
1172 
1173  // (de)register as observer
1174  if (aMsg && message())
1175  message()->detach( this );
1176  if (aMsg)
1177  aMsg->attach( this );
1178  mAtmUpdate = false;
1179 
1180  mDelayedMarkTimer.stop();
1181 
1182  mMessage = 0;
1183  if ( !aMsg ) {
1184  mWaitingForSerNum = 0; // otherwise it has been set
1185  mLastSerNum = 0;
1186  } else {
1187  mLastSerNum = aMsg->getMsgSerNum();
1188  // Check if the serial number can be used to find the assoc KMMessage
1189  // If so, keep only the serial number (and not mMessage), to avoid a dangling mMessage
1190  // when going to another message in the mainwindow.
1191  // Otherwise, keep only mMessage, this is fine for standalone KMReaderMainWins since
1192  // we're working on a copy of the KMMessage, which we own.
1193  if (message() != aMsg) {
1194  mMessage = aMsg;
1195  mLastSerNum = 0;
1196  }
1197  }
1198 
1199  if (aMsg) {
1200  aMsg->setOverrideCodec( overrideCodec() );
1201  aMsg->setDecodeHTML( htmlMail() );
1202  // FIXME: workaround to disable DND for IMAP load-on-demand
1203  if ( !aMsg->isComplete() )
1204  mViewer->setDNDEnabled( false );
1205  else
1206  mViewer->setDNDEnabled( true );
1207  }
1208 
1209  // only display the msg if it is complete
1210  // otherwise we'll get flickering with progressively loaded messages
1211  if ( complete )
1212  {
1213  // Avoid flicker, somewhat of a cludge
1214  if (force) {
1215  // stop the timer to avoid calling updateReaderWin twice
1216  mUpdateReaderWinTimer.stop();
1217  updateReaderWin();
1218  }
1219  else if (mUpdateReaderWinTimer.isActive())
1220  mUpdateReaderWinTimer.changeInterval( delay );
1221  else
1222  mUpdateReaderWinTimer.start( 0, true );
1223  }
1224 
1225  if ( aMsg && (aMsg->isUnread() || aMsg->isNew()) && GlobalSettings::self()->delayedMarkAsRead() ) {
1226  if ( GlobalSettings::self()->delayedMarkTime() != 0 )
1227  mDelayedMarkTimer.start( GlobalSettings::self()->delayedMarkTime() * 1000, true );
1228  else
1229  slotTouchMessage();
1230  }
1231 
1232  mHeaderRefreshTimer.start( 1000, false );
1233 }
1234 
1235 //-----------------------------------------------------------------------------
1237 {
1238  mUpdateReaderWinTimer.stop();
1239  clear();
1240  mDelayedMarkTimer.stop();
1241  mLastSerNum = 0;
1242  mWaitingForSerNum = 0;
1243  mMessage = 0;
1244 }
1245 
1246 // enter items for the "Important changes" list here:
1247 static const char * const kmailChanges[] = {
1248  ""
1249 };
1250 static const int numKMailChanges =
1251  sizeof kmailChanges / sizeof *kmailChanges;
1252 
1253 // enter items for the "new features" list here, so the main body of
1254 // the welcome page can be left untouched (probably much easier for
1255 // the translators). Note that the <li>...</li> tags are added
1256 // automatically below:
1257 static const char * const kmailNewFeatures[] = {
1258  I18N_NOOP("Full namespace support for IMAP"),
1259  I18N_NOOP("Offline mode"),
1260  I18N_NOOP("Sieve script management and editing"),
1261  I18N_NOOP("Account specific filtering"),
1262  I18N_NOOP("Filtering of incoming mail for online IMAP accounts"),
1263  I18N_NOOP("Online IMAP folders can be used when filtering into folders"),
1264  I18N_NOOP("Automatically delete older mails on POP servers")
1265 };
1266 static const int numKMailNewFeatures =
1267  sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
1268 
1269 
1270 //-----------------------------------------------------------------------------
1271 //static
1273 {
1274  TQCString str;
1275  for ( int i = 0 ; i < numKMailChanges ; ++i )
1276  str += kmailChanges[i];
1277  for ( int i = 0 ; i < numKMailNewFeatures ; ++i )
1278  str += kmailNewFeatures[i];
1279  KMD5 md5( str );
1280  return md5.base64Digest();
1281 }
1282 
1283 //-----------------------------------------------------------------------------
1284 void KMReaderWin::displaySplashPage( const TQString &info )
1285 {
1286  mMsgDisplay = false;
1287  adjustLayout();
1288 
1289  TQString location = locate("data", "kmail/about/main.html");
1290  TQString content = KPIM::kFileToString(location);
1291  content = content.arg( locate( "data", "libtdepim/about/kde_infopage.css" ) );
1292  if ( kapp->reverseLayout() )
1293  content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libtdepim/about/kde_infopage_rtl.css" ) );
1294  else
1295  content = content.arg( "" );
1296 
1297  mViewer->begin(KURL( location ));
1298 
1299  TQString fontSize = TQString::number( pointsToPixel( mCSSHelper->bodyFont().pointSize() ) );
1300  TQString appTitle = i18n("KMail");
1301  TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
1302  TQString quickDescription = i18n("The email client for the Trinity Desktop Environment.");
1303  mViewer->write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
1304  mViewer->end();
1305 }
1306 
1308 {
1309  TQString info =
1310  i18n( "<h2 style='margin-top: 0px;'>Retrieving Folder Contents</h2><p>Please wait . . .</p>&nbsp;" );
1311 
1312  displaySplashPage( info );
1313 }
1314 
1316 {
1317  TQString info =
1318  i18n( "<h2 style='margin-top: 0px;'>Offline</h2><p>KMail is currently in offline mode. "
1319  "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>&nbsp;" );
1320 
1321  displaySplashPage( info );
1322 }
1323 
1324 
1325 //-----------------------------------------------------------------------------
1327 {
1328  TQString info =
1329  i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; "
1330  "%4: prior KMail version; %5: prior TDE version; "
1331  "%6: generated list of new features; "
1332  "%7: First-time user text (only shown on first start); "
1333  "%8: generated list of important changes; "
1334  "--- end of comment ---",
1335  "<h2 style='margin-top: 0px;'>Welcome to KMail %1</h2><p>KMail is the email client for the Trinity "
1336  "Desktop Environment. It is designed to be fully compatible with "
1337  "Internet mailing standards including MIME, SMTP, POP3 and IMAP."
1338  "</p>\n"
1339  "<ul><li>KMail has many powerful features which are described in the "
1340  "<a href=\"%2\">documentation</a></li>\n"
1341  "<li>The <a href=\"%3\">KMail (TDE) homepage</A> offers information about "
1342  "new versions of KMail</li></ul>\n"
1343  "%8\n" // important changes
1344  "<p>Some of the new features in this release of KMail include "
1345  "(compared to KMail %4, which is part of TDE %5):</p>\n"
1346  "<ul>\n%6</ul>\n"
1347  "%7\n"
1348  "<p>We hope that you will enjoy KMail.</p>\n"
1349  "<p>Thank you,</p>\n"
1350  "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The KMail Team</p>")
1351  .arg(KMAIL_VERSION) // KMail version
1352  .arg("help:/kmail/index.html") // KMail help:// URL
1353  .arg("http://www.trinitydesktop.org") // homepage URL
1354  .arg("1.8").arg("3.4"); // prior KMail and TDE version
1355 
1356  TQString featureItems;
1357  for ( int i = 0 ; i < numKMailNewFeatures ; i++ )
1358  featureItems += i18n("<li>%1</li>\n").arg( i18n( kmailNewFeatures[i] ) );
1359 
1360  info = info.arg( featureItems );
1361 
1362  if( kmkernel->firstStart() ) {
1363  info = info.arg( i18n("<p>Please take a moment to fill in the KMail "
1364  "configuration panel at Settings-&gt;Configure "
1365  "KMail.\n"
1366  "You need to create at least a default identity and "
1367  "an incoming as well as outgoing mail account."
1368  "</p>\n") );
1369  } else {
1370  info = info.arg( TQString() );
1371  }
1372 
1373  if ( ( numKMailChanges > 1 ) || ( numKMailChanges == 1 && strlen(kmailChanges[0]) > 0 ) ) {
1374  TQString changesText =
1375  i18n("<p><span style='font-size:125%; font-weight:bold;'>"
1376  "Important changes</span> (compared to KMail %1):</p>\n")
1377  .arg("1.8");
1378  changesText += "<ul>\n";
1379  for ( int i = 0 ; i < numKMailChanges ; i++ )
1380  changesText += i18n("<li>%1</li>\n").arg( i18n( kmailChanges[i] ) );
1381  changesText += "</ul>\n";
1382  info = info.arg( changesText );
1383  }
1384  else
1385  info = info.arg(""); // remove the %8
1386 
1387  displaySplashPage( info );
1388 }
1389 
1391  mMsgDisplay = true;
1392  adjustLayout();
1393 }
1394 
1395 
1396 //-----------------------------------------------------------------------------
1397 
1399 {
1400  if (!mMsgDisplay) return;
1401 
1402  mViewer->setOnlyLocalReferences(!htmlLoadExternal());
1403 
1404  htmlWriter()->reset();
1405 
1406  KMFolder* folder = 0;
1407  if (message(&folder))
1408  {
1409  if ( mShowColorbar )
1410  mColorBar->show();
1411  else
1412  mColorBar->hide();
1413  displayMessage();
1414  }
1415  else
1416  {
1417  mColorBar->hide();
1418  mMimePartTree->hide();
1419  mMimePartTree->clear();
1420  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
1421  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) + "</body></html>" );
1422  htmlWriter()->end();
1423  }
1424 
1425  if (mSavedRelativePosition)
1426  {
1427  TQScrollView * scrollview = static_cast<TQScrollView *>(mViewer->widget());
1428  scrollview->setContentsPos( 0,
1429  tqRound( scrollview->contentsHeight() * mSavedRelativePosition ) );
1430  mSavedRelativePosition = 0;
1431  }
1432 }
1433 
1434 //-----------------------------------------------------------------------------
1435 int KMReaderWin::pointsToPixel(int pointSize) const
1436 {
1437  const TQPaintDeviceMetrics pdm(mViewer->view());
1438 
1439  return (pointSize * pdm.logicalDpiY() + 36) / 72;
1440 }
1441 
1442 //-----------------------------------------------------------------------------
1443 void KMReaderWin::showHideMimeTree( bool isPlainTextTopLevel ) {
1444  if ( mMimeTreeMode == 2 ||
1445  ( mMimeTreeMode == 1 && !isPlainTextTopLevel ) )
1446  mMimePartTree->show();
1447  else {
1448  // don't rely on TQSplitter maintaining sizes for hidden widgets:
1449  TDEConfigGroup reader( KMKernel::config(), "Reader" );
1450  saveSplitterSizes( reader );
1451  mMimePartTree->hide();
1452  }
1453 }
1454 
1456  KMMessage * msg = message();
1457 
1458  mMimePartTree->clear();
1459  showHideMimeTree( !msg || // treat no message as "text/plain"
1460  ( msg->type() == DwMime::kTypeText
1461  && msg->subtype() == DwMime::kSubtypePlain ) );
1462 
1463  if ( !msg )
1464  return;
1465 
1466  msg->setOverrideCodec( overrideCodec() );
1467 
1468  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
1469  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
1470 
1471  if (!parent())
1472  setCaption(msg->subject());
1473 
1474  removeTempFiles();
1475 
1476  mColorBar->setNeutralMode();
1477 
1478  parseMsg(msg);
1479 
1480  if( mColorBar->isNeutral() )
1481  mColorBar->setNormalMode();
1482 
1483  htmlWriter()->queue("</body></html>");
1484  htmlWriter()->flush();
1485 
1486  TQTimer::singleShot( 1, TQT_TQOBJECT(this), TQT_SLOT(injectAttachments()) );
1487 }
1488 
1489 static bool message_was_saved_decrypted_before( const KMMessage * msg ) {
1490  if ( !msg )
1491  return false;
1492  //kdDebug(5006) << "msgId = " << msg->msgId() << endl;
1493  return msg->msgId().stripWhiteSpace().startsWith( "<DecryptedMsg." );
1494 }
1495 
1496 //-----------------------------------------------------------------------------
1498 {
1499  KMMessagePart msgPart;
1500  TQCString subtype, contDisp;
1501  TQByteArray str;
1502 
1503  assert(aMsg!=0);
1504 
1505  aMsg->setIsBeingParsed( true );
1506 
1507  if ( mRootNode && !mRootNode->processed() )
1508  {
1509  kdWarning() << "The root node is not yet processed! Danger!\n";
1510  return;
1511  } else
1512  delete mRootNode;
1513  mRootNode = partNode::fromMessage( aMsg, this );
1514  const TQCString mainCntTypeStr = mRootNode->typeString() + '/' + mRootNode->subTypeString();
1515 
1516  TQString cntDesc = aMsg->subject();
1517  if( cntDesc.isEmpty() )
1518  cntDesc = i18n("( body part )");
1519  TDEIO::filesize_t cntSize = aMsg->msgSize();
1520  TQString cntEnc;
1521  if( aMsg->contentTransferEncodingStr().isEmpty() )
1522  cntEnc = "7bit";
1523  else
1524  cntEnc = aMsg->contentTransferEncodingStr();
1525 
1526  // fill the MIME part tree viewer
1527  mRootNode->fillMimePartTree( 0,
1528  mMimePartTree,
1529  cntDesc,
1530  mainCntTypeStr,
1531  cntEnc,
1532  cntSize );
1533 
1534  partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard );
1535  bool hasVCard = false;
1536  if( vCardNode ) {
1537  // ### FIXME: We should only do this if the vCard belongs to the sender,
1538  // ### i.e. if the sender's email address is contained in the vCard.
1539  TDEABC::VCardConverter t;
1540 #if defined(KABC_VCARD_ENCODING_FIX)
1541  const TQByteArray vcard = vCardNode->msgPart().bodyDecodedBinary();
1542  if ( !t.parseVCardsRaw( vcard.data() ).empty() ) {
1543 #else
1544  const TQString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() );
1545  if ( !t.parseVCards( vcard ).empty() ) {
1546 #endif
1547  hasVCard = true;
1548  writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() );
1549  }
1550  }
1551 
1552  if ( !mRootNode || !mRootNode->isToltecMessage() || mShowRawToltecMail ) {
1553  htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard ? vCardNode : 0, true ) );
1554  }
1555 
1556  // show message content
1557  ObjectTreeParser otp( this );
1558  otp.setAllowAsync( true );
1559  otp.setShowRawToltecMail( mShowRawToltecMail );
1560  otp.parseObjectTree( mRootNode );
1561 
1562  // store encrypted/signed status information in the KMMessage
1563  // - this can only be done *after* calling parseObjectTree()
1564  KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState();
1565  KMMsgSignatureState signatureState = mRootNode->overallSignatureState();
1566  // Don't crash when switching message while GPG passphrase entry dialog is shown #53185
1567  if (aMsg != message()) {
1568  displayMessage();
1569  return;
1570  }
1571  aMsg->setEncryptionState( encryptionState );
1572  // Don't reset the signature state to "not signed" (e.g. if one canceled the
1573  // decryption of a signed messages which has already been decrypted before).
1574  if ( signatureState != KMMsgNotSigned ||
1575  aMsg->signatureState() == KMMsgSignatureStateUnknown ) {
1576  aMsg->setSignatureState( signatureState );
1577  }
1578 
1579  bool emitReplaceMsgByUnencryptedVersion = false;
1580  const TDEConfigGroup reader( KMKernel::config(), "Reader" );
1581  if ( reader.readBoolEntry( "store-displayed-messages-unencrypted", false ) ) {
1582 
1583  // Hack to make sure the S/MIME CryptPlugs follows the strict requirement
1584  // of german government:
1585  // --> All received encrypted messages *must* be stored in unencrypted form
1586  // after they have been decrypted once the user has read them.
1587  // ( "Aufhebung der Verschluesselung nach dem Lesen" )
1588  //
1589  // note: Since there is no configuration option for this, we do that for
1590  // all kinds of encryption now - *not* just for S/MIME.
1591  // This could be changed in the objectTreeToDecryptedMsg() function
1592  // by deciding when (or when not, resp.) to set the 'dataNode' to
1593  // something different than 'curNode'.
1594 
1595 
1596 kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg() - special post-encryption handling:\n1." << endl;
1597 kdDebug(5006) << "(aMsg == msg) = " << (aMsg == message()) << endl;
1598 kdDebug(5006) << "aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() = " << (aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder()) << endl;
1599 kdDebug(5006) << "message_was_saved_decrypted_before( aMsg ) = " << message_was_saved_decrypted_before( aMsg ) << endl;
1600 kdDebug(5006) << "this->decryptMessage() = " << decryptMessage() << endl;
1601 kdDebug(5006) << "otp.hasPendingAsyncJobs() = " << otp.hasPendingAsyncJobs() << endl;
1602 kdDebug(5006) << " (KMMsgFullyEncrypted == encryptionState) = " << (KMMsgFullyEncrypted == encryptionState) << endl;
1603 kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl;
1604  // only proceed if we were called the normal way - not by
1605  // double click on the message (==not running in a separate window)
1606  if( (aMsg == message())
1607  // don't remove encryption in the outbox folder :)
1608  && ( aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() )
1609  // only proceed if this message was not saved encryptedly before
1610  && !message_was_saved_decrypted_before( aMsg )
1611  // only proceed if the message has actually been decrypted
1612  && decryptMessage()
1613  // only proceed if no pending async jobs are running:
1614  && !otp.hasPendingAsyncJobs()
1615  // only proceed if this message is (at least partially) encrypted
1616  && ( (KMMsgFullyEncrypted == encryptionState)
1617  || (KMMsgPartiallyEncrypted == encryptionState) ) ) {
1618 
1619 kdDebug(5006) << "KMReaderWin - calling objectTreeToDecryptedMsg()" << endl;
1620 
1621  NewByteArray decryptedData;
1622  // note: The following call may change the message's headers.
1623  objectTreeToDecryptedMsg( mRootNode, decryptedData, *aMsg );
1624  // add a \0 to the data
1625  decryptedData.appendNULL();
1626  TQCString resultString( decryptedData.data() );
1627 kdDebug(5006) << "KMReaderWin - resulting data:" << resultString << endl;
1628 
1629  if( !resultString.isEmpty() ) {
1630 kdDebug(5006) << "KMReaderWin - composing unencrypted message" << endl;
1631  // try this:
1632  aMsg->setBody( resultString );
1633  KMMessage* unencryptedMessage = new KMMessage( *aMsg );
1634  unencryptedMessage->setParent( 0 );
1635  // because this did not work:
1636  /*
1637  DwMessage dwMsg( aMsg->asDwString() );
1638  dwMsg.Body() = DwBody( DwString( resultString.data() ) );
1639  dwMsg.Body().Parse();
1640  KMMessage* unencryptedMessage = new KMMessage( &dwMsg );
1641  */
1642  //kdDebug(5006) << "KMReaderWin - resulting message:" << unencryptedMessage->asString() << endl;
1643  kdDebug(5006) << "KMReaderWin - attach unencrypted message to aMsg" << endl;
1644  aMsg->setUnencryptedMsg( unencryptedMessage );
1645  emitReplaceMsgByUnencryptedVersion = true;
1646  }
1647  }
1648  }
1649 
1650  // save current main Content-Type before deleting mRootNode
1651  const int rootNodeCntType = mRootNode ? mRootNode->type() : DwMime::kTypeText;
1652  const int rootNodeCntSubtype = mRootNode ? mRootNode->subType() : DwMime::kSubtypePlain;
1653 
1654  // store message id to avoid endless recursions
1655  setIdOfLastViewedMessage( aMsg->msgId() );
1656 
1657  if( emitReplaceMsgByUnencryptedVersion ) {
1658  kdDebug(5006) << "KMReaderWin - invoce saving in decrypted form:" << endl;
1660  } else {
1661  kdDebug(5006) << "KMReaderWin - finished parsing and displaying of message." << endl;
1662  showHideMimeTree( rootNodeCntType == DwMime::kTypeText &&
1663  rootNodeCntSubtype == DwMime::kSubtypePlain );
1664  }
1665 
1666  aMsg->setIsBeingParsed( false );
1667 }
1668 
1669 
1670 //-----------------------------------------------------------------------------
1671 void KMReaderWin::updateHeader()
1672 {
1673  /*
1674  * TODO: mess around with TDEHTML DOM some more and figure out how to
1675  * replace the entire header div w/out flickering to hell and back
1676  *
1677  * DOM::NodeList divs(mViewer->document().documentElement().getElementsByTagName("div"));
1678  * static_cast<DOM::HTMLDivElement>(divs.item(0)).setInnerHTML(writeMsgHeader());
1679  */
1680 
1681  KMMessage* currentMessage = message();
1682 
1683  if (currentMessage &&
1684  mHeaderStyle == HeaderStyle::fancy() &&
1685  currentMessage->parent())
1686  {
1687  int i;
1688  int divNumber = -1;
1689  DOM::NodeList divs(mViewer->document().documentElement().getElementsByTagName("div"));
1690  DOM::NodeList headerDivs(static_cast<DOM::HTMLDivElement>(divs.item(0)).getElementsByTagName("div"));
1691  for (i=0; i<((int)headerDivs.length()); i++) {
1692  if (static_cast<DOM::HTMLDivElement>(headerDivs.item(i)).id().string() == "sendersCurrentTime") {
1693  divNumber = i;
1694  break;
1695  }
1696  }
1697 
1698  if (divNumber >= 0) {
1699  DOM::HTMLDivElement elem = static_cast<DOM::HTMLDivElement>(headerDivs.item(i));
1700 
1701  // HACK
1702  // Get updated time information
1703  TQString latestHeader = headerStyle()->format( currentMessage, headerStrategy(), "", mPrinting, false );
1704  int startPos = latestHeader.find("<div id=\"sendersCurrentTime\" style=\"");
1705  if (startPos >= 0) {
1706  latestHeader = latestHeader.mid(startPos);
1707  int endPos = latestHeader.find("</div>");
1708  if (endPos >= 0) {
1709  endPos = endPos + 6;
1710  latestHeader.truncate(endPos);
1711 
1712  TQString divText = latestHeader;
1713  TQString divStyle = latestHeader;
1714 
1715  divText = divText.mid(divText.find(">")+1);
1716  divText.truncate(divText.find("</div>"));
1717 
1718  divStyle = divStyle.mid(TQString("<div id=\"sendersCurrentTime\" style=\"").length());
1719  divStyle.truncate(divStyle.find("\""));
1720 
1721  elem.setInnerHTML(divText);
1722  elem.setAttribute("style", divStyle);
1723  elem.applyChanges();
1724  }
1725  }
1726  }
1727  }
1728 }
1729 
1730 //-----------------------------------------------------------------------------
1731 TQString KMReaderWin::writeMsgHeader( KMMessage* aMsg, partNode *vCardNode, bool topLevel )
1732 {
1733  kdFatal( !headerStyle(), 5006 )
1734  << "trying to writeMsgHeader() without a header style set!" << endl;
1735  kdFatal( !headerStrategy(), 5006 )
1736  << "trying to writeMsgHeader() without a header strategy set!" << endl;
1737  TQString href;
1738  if ( vCardNode )
1739  href = vCardNode->asHREF( "body" );
1740 
1741  return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting, topLevel );
1742 }
1743 
1744 
1745 
1746 //-----------------------------------------------------------------------------
1747 TQString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart,
1748  int aPartNum )
1749 {
1750  TQString fileName = aMsgPart->fileName();
1751  if( fileName.isEmpty() )
1752  fileName = aMsgPart->name();
1753 
1754  //--- Sven's save attachments to /tmp start ---
1755  TQString fname = createTempDir( TQString::number( aPartNum ) );
1756  if ( fname.isEmpty() )
1757  return TQString();
1758 
1759  // strip off a leading path
1760  int slashPos = fileName.findRev( '/' );
1761  if( -1 != slashPos )
1762  fileName = fileName.mid( slashPos + 1 );
1763  if( fileName.isEmpty() )
1764  fileName = "unnamed";
1765  fname += "/" + fileName;
1766 
1767  TQByteArray data = aMsgPart->bodyDecodedBinary();
1768  size_t size = data.size();
1769  if ( aMsgPart->type() == DwMime::kTypeText && size) {
1770  // convert CRLF to LF before writing text attachments to disk
1771  size = KMail::Util::crlf2lf( data.data(), size );
1772  }
1773  if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) )
1774  return TQString();
1775 
1776  mTempFiles.append( fname );
1777  // make file read-only so that nobody gets the impression that he might
1778  // edit attached files (cf. bug #52813)
1779  ::chmod( TQFile::encodeName( fname ), S_IRUSR );
1780 
1781  return fname;
1782 }
1783 
1784 TQString KMReaderWin::createTempDir( const TQString &param )
1785 {
1786  KTempFile *tempFile = new KTempFile( TQString(), "." + param );
1787  tempFile->setAutoDelete( true );
1788  TQString fname = tempFile->name();
1789  delete tempFile;
1790 
1791  if( ::access( TQFile::encodeName( fname ), W_OK ) != 0 )
1792  // Not there or not writable
1793  if( ::mkdir( TQFile::encodeName( fname ), 0 ) != 0
1794  || ::chmod( TQFile::encodeName( fname ), S_IRWXU ) != 0 )
1795  return TQString(); //failed create
1796 
1797  assert( !fname.isNull() );
1798 
1799  mTempDirs.append( fname );
1800  return fname;
1801 }
1802 
1803 //-----------------------------------------------------------------------------
1804 void KMReaderWin::showVCard( KMMessagePart *msgPart )
1805 {
1806 #if defined(KABC_VCARD_ENCODING_FIX)
1807  const TQByteArray vCard = msgPart->bodyDecodedBinary();
1808 #else
1809  const TQString vCard = msgPart->bodyToUnicode( overrideCodec() );
1810 #endif
1811  VCardViewer *vcv = new VCardViewer( this, vCard, "vCardDialog" );
1812  vcv->show();
1813 }
1814 
1815 //-----------------------------------------------------------------------------
1817 {
1818  if (!message()) return;
1819  mViewer->view()->print();
1820 }
1821 
1822 
1823 //-----------------------------------------------------------------------------
1824 int KMReaderWin::msgPartFromUrl(const KURL &aUrl)
1825 {
1826  if (aUrl.isEmpty()) return -1;
1827  if (!aUrl.isLocalFile()) return -1;
1828 
1829  TQString path = aUrl.path();
1830  uint right = path.findRev('/');
1831  uint left = path.findRev('.', right);
1832 
1833  bool ok;
1834  int res = path.mid(left + 1, right - left - 1).toInt(&ok);
1835  return (ok) ? res : -1;
1836 }
1837 
1838 
1839 //-----------------------------------------------------------------------------
1840 void KMReaderWin::resizeEvent(TQResizeEvent *)
1841 {
1842  if( !mResizeTimer.isActive() )
1843  {
1844  //
1845  // Combine all resize operations that are requested as long a
1846  // the timer runs.
1847  //
1848  mResizeTimer.start( 100, true );
1849  }
1850 }
1851 
1852 
1853 //-----------------------------------------------------------------------------
1854 void KMReaderWin::slotDelayedResize()
1855 {
1856  mSplitter->setGeometry(0, 0, width(), height());
1857 }
1858 
1859 
1860 //-----------------------------------------------------------------------------
1861 void KMReaderWin::slotTouchMessage()
1862 {
1863  if ( !message() )
1864  return;
1865 
1866  if ( !message()->isNew() && !message()->isUnread() )
1867  return;
1868 
1869  SerNumList serNums;
1870  serNums.append( message()->getMsgSerNum() );
1871  KMCommand *command = new KMSeStatusCommand( KMMsgStatusRead, serNums );
1872  command->start();
1873 
1874  // should we send an MDN?
1875  if ( mNoMDNsWhenEncrypted &&
1876  message()->encryptionState() != KMMsgNotEncrypted &&
1877  message()->encryptionState() != KMMsgEncryptionStateUnknown )
1878  return;
1879 
1880  KMFolder *folder = message()->parent();
1881  if (folder &&
1882  (folder->isOutbox() || folder->isSent() || folder->isTrash() ||
1883  folder->isDrafts() || folder->isTemplates() ) )
1884  return;
1885 
1886  if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction,
1887  MDN::Displayed,
1888  true /* allow GUI */ ) )
1889  if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue
1890  KMessageBox::error( this, i18n("Could not send MDN.") );
1891 }
1892 
1893 
1894 //-----------------------------------------------------------------------------
1895 void KMReaderWin::closeEvent(TQCloseEvent *e)
1896 {
1898  writeConfig();
1899 }
1900 
1901 
1902 bool foundSMIMEData( const TQString aUrl,
1903  TQString& displayName,
1904  TQString& libName,
1905  TQString& keyId )
1906 {
1907  static TQString showCertMan("showCertificate#");
1908  displayName = "";
1909  libName = "";
1910  keyId = "";
1911  int i1 = aUrl.find( showCertMan );
1912  if( -1 < i1 ) {
1913  i1 += showCertMan.length();
1914  int i2 = aUrl.find(" ### ", i1);
1915  if( i1 < i2 )
1916  {
1917  displayName = aUrl.mid( i1, i2-i1 );
1918  i1 = i2+5;
1919  i2 = aUrl.find(" ### ", i1);
1920  if( i1 < i2 )
1921  {
1922  libName = aUrl.mid( i1, i2-i1 );
1923  i2 += 5;
1924 
1925  keyId = aUrl.mid( i2 );
1926  /*
1927  int len = aUrl.length();
1928  if( len > i2+1 ) {
1929  keyId = aUrl.mid( i2, 2 );
1930  i2 += 2;
1931  while( len > i2+1 ) {
1932  keyId += ':';
1933  keyId += aUrl.mid( i2, 2 );
1934  i2 += 2;
1935  }
1936  }
1937  */
1938  }
1939  }
1940  }
1941  return !keyId.isEmpty();
1942 }
1943 
1944 
1945 //-----------------------------------------------------------------------------
1946 void KMReaderWin::slotUrlOn(const TQString &aUrl)
1947 {
1948  const KURL url(aUrl);
1949 
1950  if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" || url.protocol() == "attachment"
1951  || (url.protocol().isEmpty() && url.path().isEmpty()) ) {
1952  mViewer->setDNDEnabled( false );
1953  } else {
1954  mViewer->setDNDEnabled( true );
1955  }
1956 
1957  if ( aUrl.stripWhiteSpace().isEmpty() ) {
1958  KPIM::BroadcastStatus::instance()->reset();
1959  mHoveredUrl = KURL();
1960  mLastClickImagePath = TQString();
1961  return;
1962  }
1963 
1964  mHoveredUrl = url;
1965 
1966  const TQString msg = URLHandlerManager::instance()->statusBarMessage( url, this );
1967 
1968  kdWarning( msg.isEmpty(), 5006 ) << "KMReaderWin::slotUrlOn(): Unhandled URL hover!" << endl;
1969  KPIM::BroadcastStatus::instance()->setTransienStatusMsg( msg );
1970 }
1971 
1972 
1973 //-----------------------------------------------------------------------------
1974 void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &)
1975 {
1976  mClickedUrl = aUrl;
1977 
1978  if ( URLHandlerManager::instance()->handleClick( aUrl, this ) )
1979  return;
1980 
1981  kdWarning( 5006 ) << "KMReaderWin::slotOpenUrl(): Unhandled URL click!" << endl;
1982  emit urlClicked( aUrl, Qt::LeftButton );
1983 }
1984 
1985 //-----------------------------------------------------------------------------
1986 void KMReaderWin::slotUrlPopup(const TQString &aUrl, const TQPoint& aPos)
1987 {
1988  const KURL url( aUrl );
1989  mClickedUrl = url;
1990 
1991  if ( url.protocol() == "mailto" ) {
1992  mCopyURLAction->setText( i18n( "Copy Email Address" ) );
1993  } else {
1994  mCopyURLAction->setText( i18n( "Copy Link Address" ) );
1995  }
1996 
1997  if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) )
1998  return;
1999 
2000  if ( message() ) {
2001  kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl;
2002  emitPopupMenu( url, aPos );
2003  }
2004 }
2005 
2006 // Checks if the given node has a parent node that is a DIV which has an ID attribute
2007 // with the value specified here
2008 static bool hasParentDivWithId( const DOM::Node &start, const TQString &id )
2009 {
2010  if ( start.isNull() )
2011  return false;
2012 
2013  if ( start.nodeName().string() == "div" ) {
2014  for ( unsigned int i = 0; i < start.attributes().length(); i++ ) {
2015  if ( start.attributes().item( i ).nodeName().string() == "id" &&
2016  start.attributes().item( i ).nodeValue().string() == id )
2017  return true;
2018  }
2019  }
2020 
2021  if ( !start.parentNode().isNull() )
2022  return hasParentDivWithId( start.parentNode(), id );
2023  else return false;
2024 }
2025 
2026 //-----------------------------------------------------------------------------
2027 void KMReaderWin::showAttachmentPopup( int id, const TQString & name, const TQPoint & p )
2028 {
2029  mAtmCurrent = id;
2030  mAtmCurrentName = name;
2031  TDEPopupMenu *menu = new TDEPopupMenu();
2032  menu->insertItem(SmallIcon("document-open"),i18n("to open", "Open"), 1);
2033  menu->insertItem(i18n("Open With..."), 2);
2034  menu->insertItem(i18n("to view something", "View"), 3);
2035  menu->insertItem(SmallIcon("document-save-as"),i18n("Save As..."), 4);
2036  menu->insertItem(SmallIcon("edit-copy"), i18n("Copy"), 9 );
2037  const bool canChange = message()->parent() ? !message()->parent()->isReadOnly() : false;
2038  if ( GlobalSettings::self()->allowAttachmentEditing() && canChange )
2039  menu->insertItem(SmallIcon("edit"), i18n("Edit Attachment"), 8 );
2040  if ( GlobalSettings::self()->allowAttachmentDeletion() && canChange )
2041  menu->insertItem(SmallIcon("edit-delete"), i18n("Delete Attachment"), 7 );
2042  if ( name.endsWith( ".xia", false ) &&
2043  Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) )
2044  menu->insertItem( i18n( "Decrypt With Chiasmus..." ), 6 );
2045  menu->insertItem(i18n("Properties"), 5);
2046 
2047  const bool attachmentInHeader = hasParentDivWithId( mViewer->nodeUnderMouse(), "attachmentInjectionPoint" );
2048  const bool hasScrollbar = mViewer->view()->verticalScrollBar()->isVisible();
2049  if ( attachmentInHeader && hasScrollbar ) {
2050  menu->insertItem( i18n("Scroll To"), 10 );
2051  }
2052 
2053  connect(menu, TQT_SIGNAL(activated(int)), TQT_TQOBJECT(this), TQT_SLOT(slotHandleAttachment(int)));
2054  menu->exec( p ,0 );
2055  delete menu;
2056 }
2057 
2058 //-----------------------------------------------------------------------------
2060 {
2061  if ( !mBox )
2062  return;
2063  // set the width of the frame to a reasonable value for the current GUI style
2064  int frameWidth;
2065  if( style().isA("KeramikStyle") )
2066  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
2067  else
2068  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
2069  if ( frameWidth < 0 )
2070  frameWidth = 0;
2071  if ( frameWidth != mBox->lineWidth() )
2072  mBox->setLineWidth( frameWidth );
2073 }
2074 
2075 //-----------------------------------------------------------------------------
2076 void KMReaderWin::styleChange( TQStyle& oldStyle )
2077 {
2079  TQWidget::styleChange( oldStyle );
2080 }
2081 
2082 //-----------------------------------------------------------------------------
2083 void KMReaderWin::slotHandleAttachment( int choice )
2084 {
2085  mAtmUpdate = true;
2086  partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
2087  if ( mAtmCurrentName.isEmpty() && node )
2088  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2089  if ( choice < 7 ) {
2090  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand(
2091  node, message(), mAtmCurrent, mAtmCurrentName,
2092  KMHandleAttachmentCommand::AttachmentAction( choice ), 0, this );
2093  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2094  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2095  command->start();
2096  } else if ( choice == 7 ) {
2097  slotDeleteAttachment( node );
2098  } else if ( choice == 8 ) {
2099  slotEditAttachment( node );
2100  } else if ( choice == 9 ) {
2101  if ( !node ) return;
2102  KURL::List urls;
2103  KURL url = tempFileUrlFromPartNode( node );
2104  if (!url.isValid() ) return;
2105  urls.append( url );
2106  KURLDrag* drag = new KURLDrag( urls, this );
2107  TQApplication::clipboard()->setData( drag, TQClipboard::Clipboard );
2108  } else if ( choice == 10 ) { // Scroll To
2109  scrollToAttachment( node );
2110  }
2111 }
2112 
2113 //-----------------------------------------------------------------------------
2115 {
2116  mViewer->findText();
2117 }
2118 
2119 //-----------------------------------------------------------------------------
2121 {
2122  mViewer->findTextNext();
2123 }
2124 
2125 //-----------------------------------------------------------------------------
2127 {
2128  mUseFixedFont = !mUseFixedFont;
2130  update(true);
2131 }
2132 
2133 
2134 //-----------------------------------------------------------------------------
2136 {
2137  kapp->clipboard()->setText( mViewer->selectedText() );
2138 }
2139 
2140 
2141 //-----------------------------------------------------------------------------
2142 void KMReaderWin::atmViewMsg( KMMessagePart* aMsgPart, int nodeId )
2143 {
2144  assert(aMsgPart!=0);
2145  KMMessage* msg = new KMMessage;
2146  msg->fromString(aMsgPart->bodyDecoded());
2147  assert(msg != 0);
2148  msg->setMsgSerNum( 0 ); // because lookups will fail
2149  // some information that is needed for imap messages with LOD
2150  msg->setParent( message()->parent() );
2151  msg->setUID(message()->UID());
2152  msg->setReadyToShow(true);
2153  KMReaderMainWin *win = new KMReaderMainWin();
2154  win->showMsg( overrideEncoding(), msg, message()->getMsgSerNum(), nodeId );
2155  win->show();
2156 }
2157 
2158 
2159 void KMReaderWin::setMsgPart( partNode * node ) {
2160  htmlWriter()->reset();
2161  mColorBar->hide();
2162  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2163  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
2164  // end ###
2165  if ( node ) {
2166  ObjectTreeParser otp( this, 0, true );
2167  otp.parseObjectTree( node );
2168  }
2169  // ### this, too
2170  htmlWriter()->queue( "</body></html>" );
2171  htmlWriter()->flush();
2172 }
2173 
2174 //-----------------------------------------------------------------------------
2175 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML,
2176  const TQString& aFileName, const TQString& pname )
2177 {
2178  KCursorSaver busy(KBusyPtr::busy());
2179  if (kasciistricmp(aMsgPart->typeStr(), "message")==0) {
2180  // if called from compose win
2181  KMMessage* msg = new KMMessage;
2182  assert(aMsgPart!=0);
2183  msg->fromString(aMsgPart->bodyDecoded());
2184  mMainWindow->setCaption(msg->subject());
2185  setMsg(msg, true);
2186  setAutoDelete(true);
2187  } else if (kasciistricmp(aMsgPart->typeStr(), "text")==0) {
2188  if (kasciistricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
2189  showVCard( aMsgPart );
2190  return;
2191  }
2192  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2193  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
2194 
2195  if (aHTML && (kasciistricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML
2196  // ### this is broken. It doesn't stip off the HTML header and footer!
2197  htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) );
2198  mColorBar->setHtmlMode();
2199  } else { // plain text
2200  const TQCString str = aMsgPart->bodyDecoded();
2201  ObjectTreeParser otp( this );
2202  otp.writeBodyStr( str,
2203  overrideCodec() ? overrideCodec() : aMsgPart->codec(),
2204  message() ? message()->from() : TQString() );
2205  }
2206  htmlWriter()->queue("</body></html>");
2207  htmlWriter()->flush();
2208  mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
2209  } else if (kasciistricmp(aMsgPart->typeStr(), "image")==0 ||
2210  (kasciistricmp(aMsgPart->typeStr(), "application")==0 &&
2211  kasciistricmp(aMsgPart->subtypeStr(), "postscript")==0))
2212  {
2213  if (aFileName.isEmpty()) return; // prevent crash
2214  // Open the window with a size so the image fits in (if possible):
2215  TQImageIO *iio = new TQImageIO();
2216  iio->setFileName(aFileName);
2217  if( iio->read() ) {
2218  TQImage img = iio->image();
2219  TQRect desk = TDEGlobalSettings::desktopGeometry(mMainWindow);
2220  // determine a reasonable window size
2221  int width, height;
2222  if( img.width() < 50 )
2223  width = 70;
2224  else if( img.width()+20 < desk.width() )
2225  width = img.width()+20;
2226  else
2227  width = desk.width();
2228  if( img.height() < 50 )
2229  height = 70;
2230  else if( img.height()+20 < desk.height() )
2231  height = img.height()+20;
2232  else
2233  height = desk.height();
2234  mMainWindow->resize( width, height );
2235  }
2236  // Just write the img tag to HTML:
2237  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2238  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
2239  htmlWriter()->write( "<img src=\"file:" +
2240  KURL::encode_string( aFileName ) +
2241  "\" border=\"0\">\n"
2242  "</body></html>\n" );
2243  htmlWriter()->end();
2244  setCaption( i18n("View Attachment: %1").arg( pname ) );
2245  show();
2246  delete iio;
2247  } else {
2248  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2249  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
2250  htmlWriter()->queue( "<pre>" );
2251 
2252  TQString str = aMsgPart->bodyDecoded();
2253  // A TQString cannot handle binary data. So if it's shorter than the
2254  // attachment, we assume the attachment is binary:
2255  if( str.length() < (unsigned) aMsgPart->decodedSize() ) {
2256  str.prepend( i18n("[KMail: Attachment contains binary data. Trying to show first character.]",
2257  "[KMail: Attachment contains binary data. Trying to show first %n characters.]",
2258  str.length()) + TQChar('\n') );
2259  }
2260  htmlWriter()->queue( TQStyleSheet::escape( str ) );
2261  htmlWriter()->queue( "</pre>" );
2262  htmlWriter()->queue("</body></html>");
2263  htmlWriter()->flush();
2264  mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
2265  }
2266  // ---Sven's view text, html and image attachments in html widget end ---
2267 }
2268 
2269 
2270 //-----------------------------------------------------------------------------
2271 void KMReaderWin::slotAtmView( int id, const TQString& name )
2272 {
2273  partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
2274  if( node ) {
2275  mAtmCurrent = id;
2276  mAtmCurrentName = name;
2277  if ( mAtmCurrentName.isEmpty() )
2278  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2279 
2280  KMMessagePart& msgPart = node->msgPart();
2281  TQString pname = msgPart.fileName();
2282  if (pname.isEmpty()) pname=msgPart.name();
2283  if (pname.isEmpty()) pname=msgPart.contentDescription();
2284  if (pname.isEmpty()) pname="unnamed";
2285  // image Attachment is saved already
2286  if (kasciistricmp(msgPart.typeStr(), "message")==0) {
2287  atmViewMsg( &msgPart,id );
2288  } else if ((kasciistricmp(msgPart.typeStr(), "text")==0) &&
2289  (kasciistricmp(msgPart.subtypeStr(), "x-vcard")==0)) {
2290  setMsgPart( &msgPart, htmlMail(), name, pname );
2291  } else {
2292  KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(),
2293  name, pname, overrideEncoding() );
2294  win->show();
2295  }
2296  }
2297 }
2298 
2299 //-----------------------------------------------------------------------------
2300 void KMReaderWin::openAttachment( int id, const TQString & name )
2301 {
2302  mAtmCurrentName = name;
2303  mAtmCurrent = id;
2304 
2305  TQString str, pname, cmd, fileName;
2306 
2307  partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
2308  if( !node ) {
2309  kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl;
2310  return;
2311  }
2312  if ( mAtmCurrentName.isEmpty() )
2313  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2314 
2315  KMMessagePart& msgPart = node->msgPart();
2316  if (kasciistricmp(msgPart.typeStr(), "message")==0)
2317  {
2318  atmViewMsg( &msgPart, id );
2319  return;
2320  }
2321 
2322  TQCString contentTypeStr( msgPart.typeStr() + '/' + msgPart.subtypeStr() );
2323  KPIM::kAsciiToLower( contentTypeStr.data() );
2324 
2325  if ( qstrcmp( contentTypeStr, "text/x-vcard" ) == 0 ) {
2326  showVCard( &msgPart );
2327  return;
2328  }
2329 
2330  // determine the MIME type of the attachment
2331  KMimeType::Ptr mimetype;
2332  // prefer the value of the Content-Type header
2333  mimetype = KMimeType::mimeType( TQString::fromLatin1( contentTypeStr ) );
2334  if ( mimetype->name() == "application/octet-stream" ) {
2335  // consider the filename if Content-Type is application/octet-stream
2336  mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ );
2337  }
2338  if ( ( mimetype->name() == "application/octet-stream" )
2339  && msgPart.isComplete() ) {
2340  // consider the attachment's contents if neither the Content-Type header
2341  // nor the filename give us a clue
2342  mimetype = KMimeType::findByFileContent( name );
2343  }
2344 
2345  KService::Ptr offer =
2346  KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
2347 
2348  TQString open_text;
2349  TQString filenameText = msgPart.fileName();
2350  if ( filenameText.isEmpty() )
2351  filenameText = msgPart.name();
2352  if ( offer ) {
2353  open_text = i18n("&Open with '%1'").arg( offer->name() );
2354  } else {
2355  open_text = i18n("&Open With...");
2356  }
2357  const TQString text = i18n("Open attachment '%1'?\n"
2358  "Note that opening an attachment may compromise "
2359  "your system's security.")
2360  .arg( filenameText );
2361  const int choice = KMessageBox::questionYesNoCancel( this, text,
2362  i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text,
2363  TQString::fromLatin1("askSave") + mimetype->name() ); // dontAskAgainName
2364 
2365  if( choice == KMessageBox::Yes ) { // Save
2366  mAtmUpdate = true;
2367  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
2368  message(), mAtmCurrent, mAtmCurrentName, KMHandleAttachmentCommand::Save,
2369  offer, this );
2370  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2371  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2372  command->start();
2373  }
2374  else if( choice == KMessageBox::No ) { // Open
2375  KMHandleAttachmentCommand::AttachmentAction action = ( offer ?
2376  KMHandleAttachmentCommand::Open : KMHandleAttachmentCommand::OpenWith );
2377  mAtmUpdate = true;
2378  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
2379  message(), mAtmCurrent, mAtmCurrentName, action, offer, this );
2380  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2381  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2382  command->start();
2383  } else { // Cancel
2384  kdDebug(5006) << "Canceled opening attachment" << endl;
2385  }
2386 }
2387 
2388 //-----------------------------------------------------------------------------
2390 {
2391  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -10);
2392 }
2393 
2394 
2395 //-----------------------------------------------------------------------------
2396 void KMReaderWin::slotScrollDown()
2397 {
2398  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, 10);
2399 }
2400 
2401 bool KMReaderWin::atBottom() const
2402 {
2403  const TQScrollView *view = static_cast<const TQScrollView *>(mViewer->widget());
2404  return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
2405 }
2406 
2407 //-----------------------------------------------------------------------------
2408 void KMReaderWin::slotJumpDown()
2409 {
2410  TQScrollView *view = static_cast<TQScrollView *>(mViewer->widget());
2411  int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30;
2412  view->scrollBy( 0, view->clipper()->height() - offs );
2413 }
2414 
2415 //-----------------------------------------------------------------------------
2416 void KMReaderWin::slotScrollPrior()
2417 {
2418  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8));
2419 }
2420 
2421 
2422 //-----------------------------------------------------------------------------
2423 void KMReaderWin::slotScrollNext()
2424 {
2425  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8));
2426 }
2427 
2428 //-----------------------------------------------------------------------------
2429 void KMReaderWin::slotDocumentChanged()
2430 {
2431 
2432 }
2433 
2434 
2435 //-----------------------------------------------------------------------------
2436 void KMReaderWin::slotTextSelected(bool)
2437 {
2438  TQString temp = mViewer->selectedText();
2439  kapp->clipboard()->setText(temp);
2440 }
2441 
2442 //-----------------------------------------------------------------------------
2444 {
2445  mViewer->selectAll();
2446 }
2447 
2448 //-----------------------------------------------------------------------------
2450 {
2451  TQString temp = mViewer->selectedText();
2452  return temp;
2453 }
2454 
2455 
2456 //-----------------------------------------------------------------------------
2457 void KMReaderWin::slotDocumentDone()
2458 {
2459  // mSbVert->setValue(0);
2460 }
2461 
2462 
2463 //-----------------------------------------------------------------------------
2464 void KMReaderWin::setHtmlOverride(bool override)
2465 {
2466  mHtmlOverride = override;
2467  if (message())
2469 }
2470 
2471 
2472 //-----------------------------------------------------------------------------
2473 void KMReaderWin::setHtmlLoadExtOverride(bool override)
2474 {
2475  mHtmlLoadExtOverride = override;
2476  //if (message())
2477  // message()->setDecodeHTML(htmlMail());
2478 }
2479 
2480 
2481 //-----------------------------------------------------------------------------
2483 {
2484  return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride));
2485 }
2486 
2487 
2488 //-----------------------------------------------------------------------------
2490 {
2491  return ((mHtmlLoadExternal && !mHtmlLoadExtOverride) ||
2492  (!mHtmlLoadExternal && mHtmlLoadExtOverride));
2493 }
2494 
2495 
2496 //-----------------------------------------------------------------------------
2498 {
2499  const TQScrollView * scrollview = static_cast<TQScrollView *>( mViewer->widget() );
2500  mSavedRelativePosition =
2501  static_cast<float>( scrollview->contentsY() ) / scrollview->contentsHeight();
2502 }
2503 
2504 
2505 //-----------------------------------------------------------------------------
2506 void KMReaderWin::update( bool force )
2507 {
2508  KMMessage* msg = message();
2509  if ( msg )
2510  setMsg( msg, force, true /* updateOnly */ );
2511 }
2512 
2513 
2514 //-----------------------------------------------------------------------------
2516 {
2517  KMFolder* tmpFolder;
2518  KMFolder*& folder = aFolder ? *aFolder : tmpFolder;
2519  folder = 0;
2520  if (mMessage)
2521  return mMessage;
2522  if (mLastSerNum) {
2523  KMMessage *message = 0;
2524  int index;
2525  KMMsgDict::instance()->getLocation( mLastSerNum, &folder, &index );
2526  if (folder )
2527  message = folder->getMsg( index );
2528  if (!message)
2529  kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl;
2530  return message;
2531  }
2532  return 0;
2533 }
2534 
2535 
2536 
2537 //-----------------------------------------------------------------------------
2538 void KMReaderWin::slotUrlClicked()
2539 {
2540  KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow);
2541  uint identity = 0;
2542  if ( message() && message()->parent() ) {
2543  identity = message()->parent()->identity();
2544  }
2545 
2546  KMCommand *command = new KMUrlClickedCommand( mClickedUrl, identity, this,
2547  false, mainWidget );
2548  command->start();
2549 }
2550 
2551 //-----------------------------------------------------------------------------
2552 void KMReaderWin::slotMailtoCompose()
2553 {
2554  KMCommand *command = new KMMailtoComposeCommand( mClickedUrl, message() );
2555  command->start();
2556 }
2557 
2558 //-----------------------------------------------------------------------------
2559 void KMReaderWin::slotMailtoForward()
2560 {
2561  KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mClickedUrl,
2562  message() );
2563  command->start();
2564 }
2565 
2566 //-----------------------------------------------------------------------------
2567 void KMReaderWin::slotMailtoAddAddrBook()
2568 {
2569  KMCommand *command = new KMMailtoAddAddrBookCommand( mClickedUrl,
2570  mMainWindow);
2571  command->start();
2572 }
2573 
2574 //-----------------------------------------------------------------------------
2575 void KMReaderWin::slotMailtoOpenAddrBook()
2576 {
2577  KMCommand *command = new KMMailtoOpenAddrBookCommand( mClickedUrl,
2578  mMainWindow );
2579  command->start();
2580 }
2581 
2582 //-----------------------------------------------------------------------------
2584 {
2585  // we don't necessarily need a mainWidget for KMUrlCopyCommand so
2586  // it doesn't matter if the dynamic_cast fails.
2587  KMCommand *command =
2588  new KMUrlCopyCommand( mClickedUrl,
2589  dynamic_cast<KMMainWidget*>( mMainWindow ) );
2590  command->start();
2591 }
2592 
2593 //-----------------------------------------------------------------------------
2594 void KMReaderWin::slotUrlOpen( const KURL &url )
2595 {
2596  if ( !url.isEmpty() )
2597  mClickedUrl = url;
2598  KMCommand *command = new KMUrlOpenCommand( mClickedUrl, this );
2599  command->start();
2600 }
2601 
2602 //-----------------------------------------------------------------------------
2603 void KMReaderWin::slotAddBookmarks()
2604 {
2605  KMCommand *command = new KMAddBookmarksCommand( mClickedUrl, this );
2606  command->start();
2607 }
2608 
2609 //-----------------------------------------------------------------------------
2611 {
2612  KMCommand *command = new KMUrlSaveCommand( mClickedUrl, mMainWindow );
2613  command->start();
2614 }
2615 
2616 //-----------------------------------------------------------------------------
2618 {
2619  KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mClickedUrl,
2620  message(), copyText() );
2621  command->start();
2622 }
2623 
2624 //-----------------------------------------------------------------------------
2625 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) {
2626  return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ;
2627 }
2628 
2629 partNode * KMReaderWin::partNodeForId( int id ) {
2630  return mRootNode ? mRootNode->findId( id ) : 0 ;
2631 }
2632 
2633 
2634 KURL KMReaderWin::tempFileUrlFromPartNode( const partNode * node )
2635 {
2636  if (!node) return KURL();
2637  TQStringList::const_iterator it = mTempFiles.begin();
2638  TQStringList::const_iterator end = mTempFiles.end();
2639 
2640  while ( it != end ) {
2641  TQString path = *it;
2642  it++;
2643  uint right = path.findRev('/');
2644  uint left = path.findRev('.', right);
2645 
2646  bool ok;
2647  int res = path.mid(left + 1, right - left - 1).toInt(&ok);
2648  if ( res == node->nodeId() )
2649  return KURL( path );
2650  }
2651  return KURL();
2652 }
2653 
2654 //-----------------------------------------------------------------------------
2655 void KMReaderWin::slotSaveAttachments()
2656 {
2657  mAtmUpdate = true;
2658  KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow,
2659  message() );
2660  saveCommand->start();
2661 }
2662 
2663 //-----------------------------------------------------------------------------
2664 void KMReaderWin::saveAttachment( const KURL &tempFileName )
2665 {
2666  mAtmCurrent = msgPartFromUrl( tempFileName );
2667  mAtmCurrentName = mClickedUrl.path();
2668  slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save
2669 }
2670 
2671 //-----------------------------------------------------------------------------
2672 void KMReaderWin::slotSaveMsg()
2673 {
2674  KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() );
2675 
2676  if (saveCommand->url().isEmpty())
2677  delete saveCommand;
2678  else
2679  saveCommand->start();
2680 }
2681 //-----------------------------------------------------------------------------
2683 {
2684  KMCommand *command = new KMIMChatCommand( mClickedUrl, message() );
2685  command->start();
2686 }
2687 
2688 //-----------------------------------------------------------------------------
2689 static TQString linkForNode( const DOM::Node &node )
2690 {
2691  try {
2692  if ( node.isNull() )
2693  return TQString();
2694 
2695  const DOM::NamedNodeMap attributes = node.attributes();
2696  if ( !attributes.isNull() ) {
2697  const DOM::Node href = attributes.getNamedItem( DOM::DOMString( "href" ) );
2698  if ( !href.isNull() ) {
2699  return href.nodeValue().string();
2700  }
2701  }
2702  if ( !node.parentNode().isNull() ) {
2703  return linkForNode( node.parentNode() );
2704  } else {
2705  return TQString();
2706  }
2707  } catch ( DOM::DOMException &e ) {
2708  kdWarning(5006) << "Got an exception when trying to determine link under cursor!" << endl;
2709  return TQString();
2710  }
2711 }
2712 
2713 //-----------------------------------------------------------------------------
2714 bool KMReaderWin::eventFilter( TQObject *, TQEvent *e )
2715 {
2716  if ( e->type() == TQEvent::MouseButtonPress ) {
2717  TQMouseEvent* me = TQT_TQMOUSEEVENT(e);
2718  if ( me->button() == Qt::LeftButton && ( me->state() & ShiftButton ) ) {
2719  // special processing for shift+click
2720  URLHandlerManager::instance()->handleShiftClick( mHoveredUrl, this );
2721  return true;
2722  }
2723 
2724  if ( me->button() == Qt::LeftButton ) {
2725 
2726  TQString imagePath;
2727  const DOM::Node nodeUnderMouse = mViewer->nodeUnderMouse();
2728  if ( !nodeUnderMouse.isNull() ) {
2729  const DOM::NamedNodeMap attributes = nodeUnderMouse.attributes();
2730  if ( !attributes.isNull() ) {
2731  const DOM::Node src = attributes.getNamedItem( DOM::DOMString( "src" ) );
2732  if ( !src.isNull() ) {
2733  imagePath = src.nodeValue().string();
2734  }
2735  }
2736  }
2737 
2738  mCanStartDrag = URLHandlerManager::instance()->willHandleDrag( mHoveredUrl, imagePath, this );
2739  mLastClickPosition = me->pos();
2740  mLastClickImagePath = imagePath;
2741  }
2742  }
2743 
2744  if ( e->type() == TQEvent::MouseButtonRelease ) {
2745  mCanStartDrag = false;
2746  }
2747 
2748  if ( e->type() == TQEvent::MouseMove ) {
2749  TQMouseEvent* me = TQT_TQMOUSEEVENT( e );
2750 
2751  // Handle this ourselves instead of connecting to mViewer::onURL(), since TDEHTML misses some
2752  // notifications in case we started a drag ourselves
2753  slotUrlOn( linkForNode( mViewer->nodeUnderMouse() ) );
2754 
2755  if ( ( mLastClickPosition - me->pos() ).manhattanLength() > TDEGlobalSettings::dndEventDelay() ) {
2756  if ( mCanStartDrag && ( !( mHoveredUrl.isEmpty() && mLastClickImagePath.isEmpty() ) ) ) {
2757  if ( URLHandlerManager::instance()->handleDrag( mHoveredUrl, mLastClickImagePath, this ) ) {
2758  mCanStartDrag = false;
2759  slotUrlOn( TQString() );
2760 
2761  // HACK: Send a mouse release event to the TDEHTMLView, as otherwise that will be missed in
2762  // case we started a drag. If the event is missed, the HTML view gets into a wrong
2763  // state, in which funny things like unsolicited drags start to happen.
2764  TQMouseEvent mouseEvent( TQEvent::MouseButtonRelease, me->pos(), Qt::NoButton, Qt::NoButton );
2765  TQT_TQOBJECT( mViewer->view() )->eventFilter( mViewer->view()->viewport(),
2766  &mouseEvent );
2767  return true;
2768  }
2769  }
2770  }
2771  }
2772 
2773  // standard event processing
2774  return false;
2775 }
2776 
2777 void KMReaderWin::fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId )
2778 {
2779  Q_ASSERT( msg && nodeId );
2780 
2781  if ( mSerNumOfOriginalMessage != 0 ) {
2782  KMFolder *folder = 0;
2783  int index = -1;
2784  KMMsgDict::instance()->getLocation( mSerNumOfOriginalMessage, &folder, &index );
2785  if ( folder && index != -1 )
2786  *msg = folder->getMsg( index );
2787 
2788  if ( !( *msg ) ) {
2789  kdWarning( 5006 ) << "Unable to find the original message, aborting attachment deletion!" << endl;
2790  return;
2791  }
2792 
2793  *nodeId = node->nodeId() + mNodeIdOffset;
2794  }
2795  else {
2796  *nodeId = node->nodeId();
2797  *msg = message();
2798  }
2799 }
2800 
2801 void KMReaderWin::slotDeleteAttachment(partNode * node)
2802 {
2803  if ( KMessageBox::warningContinueCancel( this,
2804  i18n("Deleting an attachment might invalidate any digital signature on this message."),
2805  i18n("Delete Attachment"), KStdGuiItem::del(), "DeleteAttachmentSignatureWarning" )
2806  != KMessageBox::Continue ) {
2807  return;
2808  }
2809 
2810  int nodeId = -1;
2811  KMMessage *msg = 0;
2812  fillCommandInfo( node, &msg, &nodeId );
2813  if ( msg && nodeId != -1 ) {
2814  KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( nodeId, msg, this );
2815  command->start();
2816  connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
2817  TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
2818  connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
2819  TQT_TQOBJECT(this), TQT_SLOT( disconnectMsgAdded() ) );
2820 
2821  // ### HACK: Since the command will do delete + add, a new message will arrive. However, we don't
2822  // want the selection to change. Therefore, as soon as a new message arrives, select it, and then
2823  // disconnect.
2824  // Of course the are races, another message can arrive before ours, but we take the risk.
2825  // And it won't work properly with multiple main windows
2826  const KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
2827  connect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
2828  TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
2829  }
2830 
2831  // If we are operating on a copy of parts of the message, make sure to update the copy as well.
2832  if ( mSerNumOfOriginalMessage != 0 && message() ) {
2833  message()->deleteBodyPart( node->nodeId() );
2834  update( true );
2835  }
2836 }
2837 
2838 void KMReaderWin::msgAdded( TQListViewItem *item )
2839 {
2840  // A new message was added to the message list view. Select it.
2841  // This is only connected right after we started a attachment delete command, so we expect a new
2842  // message. Disconnect right afterwards, we only want this particular message to be selected.
2844  KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
2845  headers->setCurrentItem( item );
2846  headers->clearSelection();
2847  headers->setSelected( item, true );
2848 }
2849 
2851 {
2852  const KMHeaders *const headers = KMKernel::self()->getKMMainWidget()->headers();
2853  disconnect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
2854  TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
2855 }
2856 
2857 void KMReaderWin::slotEditAttachment(partNode * node)
2858 {
2859  if ( KMessageBox::warningContinueCancel( this,
2860  i18n("Modifying an attachment might invalidate any digital signature on this message."),
2861  i18n("Edit Attachment"), KGuiItem( i18n("Edit"), "edit" ), "EditAttachmentSignatureWarning" )
2862  != KMessageBox::Continue ) {
2863  return;
2864  }
2865 
2866  int nodeId = -1;
2867  KMMessage *msg = 0;
2868  fillCommandInfo( node, &msg, &nodeId );
2869  if ( msg && nodeId != -1 ) {
2870  KMEditAttachmentCommand* command = new KMEditAttachmentCommand( nodeId, msg, this );
2871  command->start();
2872  }
2873 
2874  // FIXME: If we are operating on a copy of parts of the message, make sure to update the copy as well.
2875 }
2876 
2877 KMail::CSSHelper* KMReaderWin::cssHelper()
2878 {
2879  return mCSSHelper;
2880 }
2881 
2883 {
2884  if ( !GlobalSettings::self()->alwaysDecrypt() )
2885  return mDecrytMessageOverwrite;
2886  return true;
2887 }
2888 
2889 void KMReaderWin::scrollToAttachment( const partNode *node )
2890 {
2891  DOM::Document doc = mViewer->htmlDocument();
2892 
2893  // The anchors for this are created in ObjectTreeParser::parseObjectTree()
2894  mViewer->gotoAnchor( TQString::fromLatin1( "att%1" ).arg( node->nodeId() ) );
2895 
2896  // Remove any old color markings which might be there
2897  const partNode *root = node->topLevelParent();
2898  for ( int i = 0; i <= root->totalChildCount() + 1; i++ ) {
2899  DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( i + 1 ) );
2900  if ( !attachmentDiv.isNull() )
2901  attachmentDiv.removeAttribute( "style" );
2902  }
2903 
2904  // Don't mark hidden nodes, that would just produce a strange yellow line
2905  if ( node->isDisplayedHidden() )
2906  return;
2907 
2908  // Now, color the div of the attachment in yellow, so that the user sees what happened.
2909  // We created a special marked div for this in writeAttachmentMarkHeader() in ObjectTreeParser,
2910  // find and modify that now.
2911  DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( node->nodeId() ) );
2912  if ( attachmentDiv.isNull() ) {
2913  kdWarning( 5006 ) << "Could not find attachment div for attachment " << node->nodeId() << endl;
2914  return;
2915  }
2916 
2917  attachmentDiv.setAttribute( "style", TQString( "border:2px solid %1" )
2918  .arg( cssHelper()->pgpWarnColor().name() ) );
2919 
2920  // Update rendering, otherwise the rendering is not updated when the user clicks on an attachment
2921  // that causes scrolling and the open attachment dialog
2922  doc.updateRendering();
2923 }
2924 
2925 void KMReaderWin::injectAttachments()
2926 {
2927  // inject attachments in header view
2928  // we have to do that after the otp has run so we also see encrypted parts
2929  DOM::Document doc = mViewer->htmlDocument();
2930  DOM::Element injectionPoint = doc.getElementById( "attachmentInjectionPoint" );
2931  if ( injectionPoint.isNull() )
2932  return;
2933 
2934  TQString imgpath( locate("data","kmail/pics/") );
2935  TQString visibility;
2936  TQString urlHandle;
2937  TQString imgSrc;
2938  if( !showAttachmentQuicklist() ) {
2939  urlHandle.append( "kmail:showAttachmentQuicklist" );
2940  imgSrc.append( "attachmentQuicklistClosed.png" );
2941  } else {
2942  urlHandle.append( "kmail:hideAttachmentQuicklist" );
2943  imgSrc.append( "attachmentQuicklistOpened.png" );
2944  }
2945 
2946  TQString html = renderAttachments( mRootNode, TQApplication::palette().active().background() );
2947  if ( html.isEmpty() )
2948  return;
2949 
2950  TQString link("");
2951  if ( headerStyle() == HeaderStyle::fancy() ) {
2952  link += "<div style=\"text-align: left;\"><a href=\"" + urlHandle + "\"><img src=\"" +
2953  imgpath + imgSrc + "\"/></a></div>";
2954  html.prepend( link );
2955  html.prepend( TQString::fromLatin1( "<div style=\"float:left;\">%1&nbsp;</div>" ).
2956  arg( i18n( "Attachments:" ) ) );
2957  } else {
2958  link += "<div style=\"text-align: right;\"><a href=\"" + urlHandle + "\"><img src=\"" +
2959  imgpath + imgSrc + "\"/></a></div>";
2960  html.prepend( link );
2961  }
2962 
2963  assert( injectionPoint.tagName() == "div" );
2964  static_cast<DOM::HTMLElement>( injectionPoint ).setInnerHTML( html );
2965 }
2966 
2967 static TQColor nextColor( const TQColor & c )
2968 {
2969  int h, s, v;
2970  c.hsv( &h, &s, &v );
2971  return TQColor( (h + 50) % 360, TQMAX(s, 64), v, TQColor::Hsv );
2972 }
2973 
2974 TQString KMReaderWin::renderAttachments(partNode * node, const TQColor &bgColor )
2975 {
2976  if ( !node )
2977  return TQString();
2978 
2979  TQString html;
2980  if ( node->firstChild() ) {
2981  TQString subHtml = renderAttachments( node->firstChild(), nextColor( bgColor ) );
2982  if ( !subHtml.isEmpty() ) {
2983 
2984  TQString visibility;
2985  if ( !showAttachmentQuicklist() ) {
2986  visibility.append( "display:none;" );
2987  }
2988 
2989  TQString margin;
2990  if ( node != mRootNode || headerStyle() != HeaderStyle::enterprise() )
2991  margin = "padding:2px; margin:2px; ";
2992  TQString align = "left";
2993  if ( headerStyle() == HeaderStyle::enterprise() )
2994  align = "right";
2995  if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
2996  html += TQString::fromLatin1("<div style=\"background:%1; %2"
2997  "vertical-align:middle; float:%3; %4\">").arg( bgColor.name() ).arg( margin )
2998  .arg( align ).arg( visibility );
2999  html += subHtml;
3000  if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
3001  html += "</div>";
3002  }
3003  } else {
3004  partNode::AttachmentDisplayInfo info = node->attachmentDisplayInfo();
3005  if ( info.displayInHeader ) {
3006  html += "<div style=\"float:left;\">";
3007  html += TQString::fromLatin1( "<span style=\"white-space:nowrap; border-width: 0px; border-left-width: 5px; border-color: %1; 2px; border-left-style: solid;\">" ).arg( bgColor.name() );
3008  TQString fileName = writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
3009  TQString href = node->asHREF( "header" );
3010  html += TQString::fromLatin1( "<a href=\"" ) + href +
3011  TQString::fromLatin1( "\">" );
3012  html += "<img style=\"vertical-align:middle;\" src=\"" + info.icon + "\"/>&nbsp;";
3013  if ( headerStyle() == HeaderStyle::enterprise() ) {
3014  TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
3015  TQFontMetrics fm( bodyFont );
3016  html += KStringHandler::rPixelSqueeze( info.label, fm, 140 );
3017  } else if ( headerStyle() == HeaderStyle::fancy() ) {
3018  TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
3019  TQFontMetrics fm( bodyFont );
3020  html += KStringHandler::rPixelSqueeze( info.label, fm, 640 );
3021  } else {
3022  html += info.label;
3023  }
3024  html += "</a></span></div> ";
3025  }
3026  }
3027 
3028  html += renderAttachments( node->nextSibling(), nextColor ( bgColor ) );
3029  return html;
3030 }
3031 
3032 using namespace KMail::Interface;
3033 
3034 void KMReaderWin::setBodyPartMemento( const partNode * node, const TQCString & which, BodyPartMemento * memento )
3035 {
3036  const TQCString index = node->path() + ':' + which.lower();
3037 
3038  const std::map<TQCString,BodyPartMemento*>::iterator it = mBodyPartMementoMap.lower_bound( index );
3039  if ( it != mBodyPartMementoMap.end() && it->first == index ) {
3040 
3041  if ( memento && memento == it->second )
3042  return;
3043 
3044  delete it->second;
3045 
3046  if ( memento ) {
3047  it->second = memento;
3048  }
3049  else {
3050  mBodyPartMementoMap.erase( it );
3051  }
3052 
3053  } else {
3054  if ( memento ) {
3055  mBodyPartMementoMap.insert( it, std::make_pair( index, memento ) );
3056  }
3057  }
3058 
3059  if ( Observable * o = memento ? memento->asObservable() : 0 )
3060  o->attach( this );
3061 }
3062 
3063 BodyPartMemento * KMReaderWin::bodyPartMemento( const partNode * node, const TQCString & which ) const
3064 {
3065  const TQCString index = node->path() + ':' + which.lower();
3066  const std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.find( index );
3067  if ( it == mBodyPartMementoMap.end() ) {
3068  return 0;
3069  }
3070  else {
3071  return it->second;
3072  }
3073 }
3074 
3075 static void detach_and_delete( BodyPartMemento * memento, KMReaderWin * obs ) {
3076  if ( Observable * const o = memento ? memento->asObservable() : 0 )
3077  o->detach( obs );
3078  delete memento;
3079 }
3080 
3081 void KMReaderWin::clearBodyPartMementos()
3082 {
3083  for ( std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it )
3084  // Detach the memento from the reader. When cancelling it, it might trigger an update of the
3085  // reader, which we are not interested in, and which is dangerous, since half the mementos are
3086  // already deleted.
3087  // https://issues.kolab.org/issue4187
3088  detach_and_delete( it->second, this );
3089 
3090  mBodyPartMementoMap.clear();
3091 }
3092 
3093 #include "kmreaderwin.moc"
3094 
3095