kmail

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