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