kmail

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