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