kmail

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