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