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