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