kmail

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