kmail

objecttreeparser.cpp
1 /*
2  objecttreeparser.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
6  Copyright (c) 2003 Marc Mutz <mutz@kde.org>
7 
8  KMail is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License, version 2, as
10  published by the Free Software Foundation.
11 
12  KMail is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  In addition, as a special exception, the copyright holders give
22  permission to link the code of this program with any edition of
23  the TQt library by Trolltech AS, Norway (or with modified versions
24  of TQt that use the same license as TQt), and distribute linked
25  combinations including the two. You must obey the GNU General
26  Public License in all respects for all of the code used other than
27  TQt. If you modify this file, you may extend this exception to
28  your version of the file, but you are not obligated to do so. If
29  you do not wish to do so, delete this exception statement from
30  your version.
31 */
32 
33 #include <config.h>
34 
35 // my header file
36 #include "objecttreeparser.h"
37 #include "objecttreeparser_p.h"
38 
39 // other KMail headers
40 #include "kmkernel.h"
41 #include "kmreaderwin.h"
42 #include "partNode.h"
43 #include <libtdepim/tdefileio.h>
44 #include <libemailfunctions/email.h>
45 #include "partmetadata.h"
46 #include "attachmentstrategy.h"
47 #include "interfaces/htmlwriter.h"
48 #include "htmlstatusbar.h"
49 #include "csshelper.h"
50 #include "bodypartformatter.h"
51 #include "bodypartformatterfactory.h"
52 #include "partnodebodypart.h"
53 #include "interfaces/bodypartformatter.h"
54 #include "globalsettings.h"
55 #include "util.h"
56 #include "callback.h"
57 
58 // other module headers
59 #include <mimelib/enum.h>
60 #include <mimelib/bodypart.h>
61 #include <mimelib/string.h>
62 #include <mimelib/text.h>
63 
64 #include <kleo/specialjob.h>
65 #include <kleo/cryptobackendfactory.h>
66 #include <kleo/decryptverifyjob.h>
67 #include <kleo/verifydetachedjob.h>
68 #include <kleo/verifyopaquejob.h>
69 #include <kleo/keylistjob.h>
70 #include <kleo/importjob.h>
71 #include <kleo/dn.h>
72 
73 #include <gpgmepp/importresult.h>
74 #include <gpgmepp/decryptionresult.h>
75 #include <gpgmepp/key.h>
76 #include <gpgmepp/keylistresult.h>
77 #include <gpgme.h>
78 
79 #include <kpgpblock.h>
80 #include <kpgp.h>
81 #include <linklocator.h>
82 
83 #include <ktnef/ktnefparser.h>
84 #include <ktnef/ktnefmessage.h>
85 #include <ktnef/ktnefattach.h>
86 
87 // other KDE headers
88 #include <kdebug.h>
89 #include <tdelocale.h>
90 #include <kmimetype.h>
91 #include <tdeglobal.h>
92 #include <tdehtml_part.h>
93 #include <tdetempfile.h>
94 #include <kstandarddirs.h>
95 #include <tdeapplication.h>
96 #include <tdemessagebox.h>
97 #include <kiconloader.h>
98 #include <kmdcodec.h>
99 
100 // other TQt headers
101 #include <tqtextcodec.h>
102 #include <tqdir.h>
103 #include <tqfile.h>
104 #include <tqapplication.h>
105 #include <tdestyle.h>
106 #include <tqbuffer.h>
107 #include <tqpixmap.h>
108 #include <tqpainter.h>
109 #include <tqregexp.h>
110 
111 // other headers
112 #include <memory>
113 #include <sstream>
114 #include <sys/stat.h>
115 #include <sys/types.h>
116 #include <unistd.h>
117 #include <cassert>
118 #include "chiasmuskeyselector.h"
119 
120 namespace KMail {
121 
122  // A small class that eases temporary CryptPlugWrapper changes:
123  class ObjectTreeParser::CryptoProtocolSaver {
124  ObjectTreeParser * otp;
125  const Kleo::CryptoBackend::Protocol * protocol;
126  public:
127  CryptoProtocolSaver( ObjectTreeParser * _otp, const Kleo::CryptoBackend::Protocol* _w )
128  : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
129  {
130  if ( otp )
131  otp->setCryptoProtocol( _w );
132  }
133 
134  ~CryptoProtocolSaver() {
135  if ( otp )
136  otp->setCryptoProtocol( protocol );
137  }
138  };
139 
140 
141  ObjectTreeParser::ObjectTreeParser( KMReaderWin * reader, const Kleo::CryptoBackend::Protocol * protocol,
142  bool showOnlyOneMimePart, bool keepEncryptions,
143  bool includeSignatures,
144  const AttachmentStrategy * strategy,
145  HtmlWriter * htmlWriter,
146  CSSHelper * cssHelper )
147  : mReader( reader ),
148  mCryptoProtocol( protocol ),
149  mShowOnlyOneMimePart( showOnlyOneMimePart ),
150  mKeepEncryptions( keepEncryptions ),
151  mIncludeSignatures( includeSignatures ),
152  mHasPendingAsyncJobs( false ),
153  mAllowAsync( false ),
154  mShowRawToltecMail( false ),
155  mAttachmentStrategy( strategy ),
156  mHtmlWriter( htmlWriter ),
157  mCSSHelper( cssHelper )
158  {
159  if ( !attachmentStrategy() )
160  mAttachmentStrategy = reader ? reader->attachmentStrategy()
161  : AttachmentStrategy::smart();
162  if ( reader && !this->htmlWriter() )
163  mHtmlWriter = reader->htmlWriter();
164  if ( reader && !this->cssHelper() )
165  mCSSHelper = reader->mCSSHelper;
166  }
167 
168  ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser & other )
169  : mReader( other.mReader ),
170  mCryptoProtocol( other.cryptoProtocol() ),
171  mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
172  mKeepEncryptions( other.keepEncryptions() ),
173  mIncludeSignatures( other.includeSignatures() ),
174  mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ),
175  mAllowAsync( other.allowAsync() ),
176  mAttachmentStrategy( other.attachmentStrategy() ),
177  mHtmlWriter( other.htmlWriter() ),
178  mCSSHelper( other.cssHelper() )
179  {
180 
181  }
182 
183  ObjectTreeParser::~ObjectTreeParser() {}
184 
185  void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
186  const char* content,
187  const char* cntDesc,
188  bool append, bool addToTextualContent )
189  {
190  DwBodyPart* myBody = new DwBodyPart( DwString( content ), 0 );
191  myBody->Parse();
192 
193  if ( ( !myBody->Body().FirstBodyPart() ||
194  myBody->Body().AsString().length() == 0 ) &&
195  startNode.dwPart() &&
196  startNode.dwPart()->Body().Message() &&
197  startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
198  {
199  // if encapsulated imap messages are loaded the content-string is not complete
200  // so we need to keep the child dwparts
201  myBody = new DwBodyPart( *(startNode.dwPart()->Body().Message()) );
202  }
203 
204  if ( myBody->hasHeaders() ) {
205  DwText& desc = myBody->Headers().ContentDescription();
206  desc.FromString( cntDesc );
207  desc.SetModified();
208  myBody->Headers().Parse();
209  }
210 
211  partNode* parentNode = &startNode;
212  partNode* newNode = new partNode(false, myBody);
213 
214  // Build the object tree of the new node before setting the parent, as otherwise
215  // buildObjectTree() would erronously modify the parents as well
216  newNode->buildObjectTree( false );
217 
218  if ( append && parentNode->firstChild() ) {
219  parentNode = parentNode->firstChild();
220  while( parentNode->nextSibling() )
221  parentNode = parentNode->nextSibling();
222  parentNode->setNext( newNode );
223  } else
224  parentNode->setFirstChild( newNode );
225 
226  if ( startNode.mimePartTreeItem() ) {
227  newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
228  TQString(), TQString(), TQString(), 0,
229  append );
230  } else {
231  }
232  ObjectTreeParser otp( mReader, cryptoProtocol() );
233  otp.parseObjectTree( newNode );
234  if ( addToTextualContent ) {
235  mRawReplyString += otp.rawReplyString();
236  mTextualContent += otp.textualContent();
237  if ( !otp.textualContentCharset().isEmpty() )
238  mTextualContentCharset = otp.textualContentCharset();
239  }
240  }
241 
242 
243 //-----------------------------------------------------------------------------
244 
245  void ObjectTreeParser::parseObjectTree( partNode * node ) {
246  //kdDebug(5006) << "ObjectTreeParser::parseObjectTree( "
247  // << (node ? "node OK, " : "no node, ")
248  // << "showOnlyOneMimePart: " << (showOnlyOneMimePart() ? "TRUE" : "FALSE")
249  // << " )" << endl;
250 
251  if ( !node )
252  return;
253 
254  // reset pending async jobs state (we'll rediscover pending jobs as we go)
255  mHasPendingAsyncJobs = false;
256 
257  // reset "processed" flags for...
258  if ( showOnlyOneMimePart() ) {
259  // ... this node and all descendants
260  node->setProcessed( false, false );
261  if ( partNode * child = node->firstChild() )
262  child->setProcessed( false, true );
263  } else if ( mReader && !node->parentNode() ) {
264  // ...this node and all it's siblings and descendants
265  node->setProcessed( false, true );
266  }
267 
268  for ( ; node ; node = node->nextSibling() ) {
269  if ( node->processed() )
270  continue;
271 
272  ProcessResult processResult;
273 
274  if ( mReader ) {
275  htmlWriter()->queue( TQString::fromLatin1("<a name=\"att%1\"/>").arg( node->nodeId() ) );
276  }
277 
278  if ( const Interface::BodyPartFormatter * formatter
279  = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
280 
281  // Only use the external plugin if we have a reader. Otherwise, just do nothing for this
282  // node.
283  if ( mReader ) {
284  PartNodeBodyPart part( *node, codecFor( node ) );
285  // Set the default display strategy for this body part relying on the
286  // identity of KMail::Interface::BodyPart::Display and AttachmentStrategy::Display
287  part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
288 
289  writeAttachmentMarkHeader( node );
290  node->setDisplayedEmbedded( true );
291  Callback callback( mReader->message(), mReader );
292  const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), callback );
293  writeAttachmentMarkFooter();
294  switch ( result ) {
295  case Interface::BodyPartFormatter::AsIcon:
296  processResult.setNeverDisplayInline( true );
297  // fall through:
298  case Interface::BodyPartFormatter::Failed:
299  defaultHandling( node, processResult );
300  break;
301  case Interface::BodyPartFormatter::Ok:
302  case Interface::BodyPartFormatter::NeedContent:
303  // FIXME: incomplete content handling
304  ;
305  }
306  }
307  } else {
308  const BodyPartFormatter * bpf
309  = BodyPartFormatter::createFor( node->type(), node->subType() );
310  kdFatal( !bpf, 5006 ) << "THIS SHOULD NO LONGER HAPPEN ("
311  << node->typeString() << '/' << node->subTypeString()
312  << ')' << endl;
313 
314  writeAttachmentMarkHeader( node );
315  if ( bpf && !bpf->process( this, node, processResult ) ) {
316  defaultHandling( node, processResult );
317  }
318  writeAttachmentMarkFooter();
319  }
320 
321  node->setProcessed( true, false );
322 
323  // adjust signed/encrypted flags if inline PGP was found
324  processResult.adjustCryptoStatesOfNode( node );
325 
326  if ( showOnlyOneMimePart() )
327  break;
328  }
329  }
330 
331  void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
332  // ### (mmutz) default handling should go into the respective
333  // ### bodypartformatters.
334  if ( !mReader )
335  return;
336 
337 
338  const AttachmentStrategy * as = attachmentStrategy();
339  if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None &&
340  !showOnlyOneMimePart() &&
341  node->parentNode() /* message is not an attachment */ ) {
342  node->setDisplayedHidden( true );
343  return;
344  }
345 
346  bool asIcon = true;
347  if ( showOnlyOneMimePart() )
348  // ### (mmutz) this is wrong! If I click on an image part, I
349  // want the equivalent of "view...", except for the extra
350  // window!
351  asIcon = !node->hasContentDispositionInline();
352  else if ( !result.neverDisplayInline() )
353  if ( as )
354  asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
355  // neither image nor text -> show as icon
356  if ( !result.isImage() && node->type() != DwMime::kTypeText )
357  asIcon = true;
358  // if the image is not complete do not try to show it inline
359  if ( result.isImage() && !node->msgPart().isComplete() )
360  asIcon = true;
361  if ( asIcon ) {
362  if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) ||
363  showOnlyOneMimePart() ) {
364  writePartIcon( &node->msgPart(), node->nodeId() );
365  }
366  else {
367  node->setDisplayedHidden( true );
368  }
369  } else if ( result.isImage() ) {
370  node->setDisplayedEmbedded( true );
371  writePartIcon( &node->msgPart(), node->nodeId(), true );
372  }
373  else {
374  node->setDisplayedEmbedded( true );
375  writeBodyString( node->msgPart().bodyDecoded(),
376  node->trueFromAddress(),
377  codecFor( node ), result, false );
378  }
379  // end of ###
380  }
381 
382  void ProcessResult::adjustCryptoStatesOfNode( partNode * node ) const {
383  if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
384  ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
385  node->setSignatureState( inlineSignatureState() );
386  node->setEncryptionState( inlineEncryptionState() );
387  }
388  }
389 
393 
394  static int signatureToStatus( const GpgME::Signature &sig )
395  {
396  switch ( sig.status().code() ) {
397  case GPG_ERR_NO_ERROR:
398  return GPGME_SIG_STAT_GOOD;
399  case GPG_ERR_BAD_SIGNATURE:
400  return GPGME_SIG_STAT_BAD;
401  case GPG_ERR_NO_PUBKEY:
402  return GPGME_SIG_STAT_NOKEY;
403  case GPG_ERR_NO_DATA:
404  return GPGME_SIG_STAT_NOSIG;
405  case GPG_ERR_SIG_EXPIRED:
406  return GPGME_SIG_STAT_GOOD_EXP;
407  case GPG_ERR_KEY_EXPIRED:
408  return GPGME_SIG_STAT_GOOD_EXPKEY;
409  default:
410  return GPGME_SIG_STAT_ERROR;
411  }
412  }
413 
414  bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
415  partNode& sign,
416  const TQString& fromAddress,
417  bool doCheck,
418  bool hideErrors,
419  TQCString* cleartextData,
420  const std::vector<GpgME::Signature> & paramSignatures,
421  const GpgME::Key & paramKey)
422  {
423  bool bIsOpaqueSigned = false;
424  enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
425  cryptPlugError = NO_PLUGIN;
426 
427  const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
428 
429  TQString cryptPlugLibName;
430  TQString cryptPlugDisplayName;
431  if ( cryptProto ) {
432  cryptPlugLibName = cryptProto->name();
433  cryptPlugDisplayName = cryptProto->displayName();
434  }
435 
436 #ifndef NDEBUG
437  if ( !doCheck ) {
438  //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: showing OpenPGP (Encrypted+Signed) data" << endl;
439  }
440  else {
441  if ( data ) {
442  //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Multipart Signed data" << endl;
443  }
444  else {
445  //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl;
446  }
447  }
448 #endif
449 
450  if ( doCheck && cryptProto ) {
451  //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG "
452  // << cryptPlugLibName << endl;
453  }
454 
455  TQCString cleartext;
456  TQByteArray signaturetext;
457 
458  if ( doCheck && cryptProto ) {
459  if ( data ) {
460  cleartext = KMail::Util::CString( data->dwPart()->AsString() );
461 
462  dumpToFile( "dat_01_reader_signedtext_before_canonicalization",
463  cleartext.data(), cleartext.length() );
464 
465  // replace simple LFs by CRLSs
466  // according to RfC 2633, 3.1.1 Canonicalization
467  //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
468  cleartext = Util::lf2crlf( cleartext );
469  //kdDebug(5006) << " done." << endl;
470  }
471 
472  dumpToFile( "dat_02_reader_signedtext_after_canonicalization",
473  cleartext.data(), cleartext.length() );
474 
475  signaturetext = sign.msgPart().bodyDecodedBinary();
476  dumpToFile( "dat_03_reader.sig", signaturetext.data(),
477  signaturetext.size() );
478  }
479 
480  std::vector<GpgME::Signature> signatures;
481  GpgME::Key key;
482 
483  if ( !doCheck ) {
484  signatures = paramSignatures;
485  key = paramKey;
486  }
487 
488  PartMetaData messagePart;
489  messagePart.isSigned = true;
490  messagePart.technicalProblem = ( cryptProto == 0 );
491  messagePart.isGoodSignature = false;
492  messagePart.isEncrypted = false;
493  messagePart.isDecryptable = false;
494  messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
495  messagePart.status = i18n("Wrong Crypto Plug-In.");
496  messagePart.status_code = GPGME_SIG_STAT_NONE;
497 
498  if ( doCheck && cryptProto ) {
499  GpgME::VerificationResult result;
500  if ( data ) { // detached
501  const VerifyDetachedBodyPartMemento * m
502  = dynamic_cast<VerifyDetachedBodyPartMemento*>( sign.bodyPartMemento( "verifydetached" ) );
503  if ( !m ) {
504  Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
505  if ( !job ) {
506  cryptPlugError = CANT_VERIFY_SIGNATURES;
507  // PENDING(marc) cryptProto = 0 here?
508  } else {
509  TQByteArray plainData = cleartext;
510  plainData.resize( cleartext.size() - 1 );
511  VerifyDetachedBodyPartMemento * newM
512  = new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
513  if ( allowAsync() ) {
514  if ( newM->start() ) {
515  messagePart.inProgress = true;
516  mHasPendingAsyncJobs = true;
517  } else {
518  m = newM;
519  }
520  } else {
521  newM->exec();
522  m = newM;
523  }
524  sign.setBodyPartMemento( "verifydetached", newM );
525  }
526  } else if ( m->isRunning() ) {
527  messagePart.inProgress = true;
528  mHasPendingAsyncJobs = true;
529  m = 0;
530  }
531 
532  if ( m ) {
533  result = m->verifyResult();
534  messagePart.auditLogError = m->auditLogError();
535  messagePart.auditLog = m->auditLogAsHtml();
536  key = m->signingKey();
537  }
538  } else { // opaque
539  const VerifyOpaqueBodyPartMemento * m
540  = dynamic_cast<VerifyOpaqueBodyPartMemento*>( sign.bodyPartMemento( "verifyopaque" ) );
541  if ( !m ) {
542  Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
543  if ( !job ) {
544  cryptPlugError = CANT_VERIFY_SIGNATURES;
545  // PENDING(marc) cryptProto = 0 here?
546  } else {
547  VerifyOpaqueBodyPartMemento * newM
548  = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
549  if ( allowAsync() ) {
550  if ( newM->start() ) {
551  messagePart.inProgress = true;
552  mHasPendingAsyncJobs = true;
553  } else {
554  m = newM;
555  }
556  } else {
557  newM->exec();
558  m = newM;
559  }
560  sign.setBodyPartMemento( "verifyopaque", newM );
561  }
562  } else if ( m->isRunning() ) {
563  messagePart.inProgress = true;
564  mHasPendingAsyncJobs = true;
565  m = 0;
566  }
567 
568  if ( m ) {
569  result = m->verifyResult();
570  const TQByteArray & plainData = m->plainText();
571  cleartext = TQCString( plainData.data(), plainData.size() + 1 );
572  messagePart.auditLogError = m->auditLogError();
573  messagePart.auditLog = m->auditLogAsHtml();
574  key = m->signingKey();
575  }
576  }
577  std::stringstream ss;
578  ss << result;
579  //kdDebug(5006) << ss.str().c_str() << endl;
580  signatures = result.signatures();
581  }
582 
583  if ( doCheck ) {
584  //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl;
585  }
586 
587  // ### only one signature supported
588  if ( signatures.size() > 0 ) {
589  //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: found signature" << endl;
590  GpgME::Signature signature = signatures[0];
591 
592  messagePart.status_code = signatureToStatus( signature );
593  messagePart.status = TQString::fromUtf8( signature.status().asString() );
594  for ( uint i = 1; i < signatures.size(); ++i ) {
595  if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
596  messagePart.status_code = GPGME_SIG_STAT_DIFF;
597  messagePart.status = i18n("Different results for signatures");
598  }
599  }
600  if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
601  messagePart.isGoodSignature = true;
602 
603  // save extended signature status flags
604  messagePart.sigSummary = signature.summary();
605 
606  if ( key.keyID() )
607  messagePart.keyId = key.keyID();
608  if ( messagePart.keyId.isEmpty() )
609  messagePart.keyId = signature.fingerprint();
610  // ### Ugh. We depend on two enums being in sync:
611  messagePart.keyTrust = (Kpgp::Validity)signature.validity();
612  if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
613  messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
614  for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
615  // The following if /should/ always result in true but we
616  // won't trust implicitely the plugin that gave us these data.
617  if ( key.userID( iMail ).email() ) {
618  TQString email = TQString::fromUtf8( key.userID( iMail ).email() );
619  // ### work around gpgme 0.3.x / cryptplug bug where the
620  // ### email addresses are specified as angle-addr, not addr-spec:
621  if ( email.startsWith( "<" ) && email.endsWith( ">" ) )
622  email = email.mid( 1, email.length() - 2 );
623  if ( !email.isEmpty() )
624  messagePart.signerMailAddresses.append( email );
625  }
626  }
627 
628  if ( signature.creationTime() )
629  messagePart.creationTime.setTime_t( signature.creationTime() );
630  else
631  messagePart.creationTime = TQDateTime();
632  if ( messagePart.signer.isEmpty() ) {
633  if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
634  messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
635  if ( !messagePart.signerMailAddresses.empty() ) {
636  if ( messagePart.signer.isEmpty() )
637  messagePart.signer = messagePart.signerMailAddresses.front();
638  else
639  messagePart.signer += " <" + messagePart.signerMailAddresses.front() + '>';
640  }
641  }
642 
643  //kdDebug(5006) << "\n key id: " << messagePart.keyId
644  // << "\n key trust: " << messagePart.keyTrust
645  // << "\n signer: " << messagePart.signer << endl;
646 
647  } else {
648  messagePart.creationTime = TQDateTime();
649  }
650 
651  if ( !doCheck || !data ){
652  if ( cleartextData || !cleartext.isEmpty() ) {
653  if ( mReader )
654  htmlWriter()->queue( writeSigstatHeader( messagePart,
655  cryptProto,
656  fromAddress ) );
657  bIsOpaqueSigned = true;
658 
659  CryptoProtocolSaver cpws( this, cryptProto );
660  insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
661  "opaqued signed data" );
662 
663  if ( mReader )
664  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
665 
666  }
667  else if ( !hideErrors ) {
668  TQString txt;
669  txt = "<hr><b><h2>";
670  txt.append( i18n( "The crypto engine returned no cleartext data." ) );
671  txt.append( "</h2></b>" );
672  txt.append( "<br>&nbsp;<br>" );
673  txt.append( i18n( "Status: " ) );
674  if ( !messagePart.status.isEmpty() ) {
675  txt.append( "<i>" );
676  txt.append( messagePart.status );
677  txt.append( "</i>" );
678  }
679  else
680  txt.append( i18n("(unknown)") );
681  if ( mReader )
682  htmlWriter()->queue(txt);
683  }
684  }
685  else {
686  if ( mReader ) {
687  if ( !cryptProto ) {
688  TQString errorMsg;
689  switch ( cryptPlugError ) {
690  case NOT_INITIALIZED:
691  errorMsg = i18n( "Crypto plug-in \"%1\" is not initialized." )
692  .arg( cryptPlugLibName );
693  break;
694  case CANT_VERIFY_SIGNATURES:
695  errorMsg = i18n( "Crypto plug-in \"%1\" cannot verify signatures." )
696  .arg( cryptPlugLibName );
697  break;
698  case NO_PLUGIN:
699  if ( cryptPlugDisplayName.isEmpty() )
700  errorMsg = i18n( "No appropriate crypto plug-in was found." );
701  else
702  errorMsg = i18n( "%1 is either 'OpenPGP' or 'S/MIME'",
703  "No %1 plug-in was found." )
704  .arg( cryptPlugDisplayName );
705  break;
706  }
707  messagePart.errorText = i18n( "The message is signed, but the "
708  "validity of the signature cannot be "
709  "verified.<br />"
710  "Reason: %1" )
711  .arg( errorMsg );
712  }
713 
714  if ( mReader )
715  htmlWriter()->queue( writeSigstatHeader( messagePart,
716  cryptProto,
717  fromAddress ) );
718  }
719 
720  ObjectTreeParser otp( mReader, cryptProto, true );
721  otp.parseObjectTree( data );
722  mRawReplyString += otp.rawReplyString();
723  mTextualContent += otp.textualContent();
724  if ( !otp.textualContentCharset().isEmpty() )
725  mTextualContentCharset = otp.textualContentCharset();
726 
727  if ( mReader )
728  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
729  }
730 
731  //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: done, returning "
732  // << ( bIsOpaqueSigned ? "TRUE" : "FALSE" ) << endl;
733  return bIsOpaqueSigned;
734  }
735 
736 void ObjectTreeParser::writeDecryptionInProgressBlock() {
737  assert( mReader );
738  // PENDING(marc) find an animated icon here:
739  //const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( "decrypted", TDEIcon::Small );
740  const TQString decryptedData = i18n("Encrypted data not shown");
741  PartMetaData messagePart;
742  messagePart.isDecryptable = true;
743  messagePart.isEncrypted = true;
744  messagePart.isSigned = false;
745  messagePart.inProgress = true;
746  htmlWriter()->queue( writeSigstatHeader( messagePart,
747  cryptoProtocol(),
748  TQString() ) );
749  //htmlWriter()->queue( decryptedData );
750  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
751 }
752 
753 void ObjectTreeParser::writeDeferredDecryptionBlock() {
754  assert( mReader );
755  const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( "decrypted", TDEIcon::Small );
756  const TQString decryptedData =
757  "<div style=\"font-size:large; text-align:center;padding-top:20pt;\">" +
758  i18n("This message is encrypted.") +
759  "</div>"
760  "<div style=\"text-align:center; padding-bottom:20pt;\">"
761  "<a href=\"kmail:decryptMessage\">"
762  "<img src=\"" + iconName + "\"/>" +
763  i18n("Decrypt Message") +
764  "</a></div>";
765  PartMetaData messagePart;
766  messagePart.isDecryptable = true;
767  messagePart.isEncrypted = true;
768  messagePart.isSigned = false;
769  mRawReplyString += decryptedData.utf8();
770  htmlWriter()->queue( writeSigstatHeader( messagePart,
771  cryptoProtocol(),
772  TQString() ) );
773  htmlWriter()->queue( decryptedData );
774  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
775 }
776 
777 bool ObjectTreeParser::okDecryptMIME( partNode& data,
778  TQCString& decryptedData,
779  bool& signatureFound,
780  std::vector<GpgME::Signature> &signatures,
781  GpgME::Key &key,
782  bool showWarning,
783  bool& passphraseError,
784  bool& actuallyEncrypted,
785  bool& decryptionStarted,
786  TQString& aErrorText,
787  GpgME::Error & auditLogError,
788  TQString& auditLog )
789 {
790  passphraseError = false;
791  decryptionStarted = false;
792  aErrorText = TQString();
793  auditLogError = GpgME::Error();
794  auditLog = TQString();
795  bool bDecryptionOk = false;
796  enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
797  cryptPlugError = NO_PLUGIN;
798 
799  const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
800 
801  TQString cryptPlugLibName;
802  if ( cryptProto )
803  cryptPlugLibName = cryptProto->name();
804 
805  assert( !mReader || mReader->decryptMessage() );
806 
807  if ( cryptProto && !kmkernel->contextMenuShown() ) {
808  TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
809 #ifdef MARCS_DEBUG
810  TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
811  bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
812  (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
813  (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
814 
815  dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
816 
817  TQCString deb;
818  deb = "\n\nE N C R Y P T E D D A T A = ";
819  if ( cipherIsBinary )
820  deb += "[binary data]";
821  else {
822  deb += "\"";
823  deb += cipherStr;
824  deb += "\"";
825  }
826  deb += "\n\n";
827  kdDebug(5006) << deb << endl;
828 #endif
829 
830 
831  //kdDebug(5006) << "ObjectTreeParser::decryptMIME: going to call CRYPTPLUG "
832  // << cryptPlugLibName << endl;
833  if ( mReader )
834  emit mReader->noDrag(); // in case pineentry pops up, don't let kmheaders start a drag afterwards
835 
836  // Check whether the memento contains a result from last time:
837  const DecryptVerifyBodyPartMemento * m
838  = dynamic_cast<DecryptVerifyBodyPartMemento*>( data.bodyPartMemento( "decryptverify" ) );
839  if ( !m ) {
840  Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
841  if ( !job ) {
842  cryptPlugError = CANT_DECRYPT;
843  cryptProto = 0;
844  } else {
845  DecryptVerifyBodyPartMemento * newM
846  = new DecryptVerifyBodyPartMemento( job, cryptProto->keyListJob(), ciphertext );
847  if ( allowAsync() ) {
848  if ( newM->start() ) {
849  decryptionStarted = true;
850  mHasPendingAsyncJobs = true;
851  } else {
852  m = newM;
853  }
854  } else {
855  newM->exec();
856  m = newM;
857  }
858  data.setBodyPartMemento( "decryptverify", newM );
859  }
860  } else if ( m->isRunning() ) {
861  decryptionStarted = true;
862  mHasPendingAsyncJobs = true;
863  m = 0;
864  }
865 
866  if ( m ) {
867  const TQByteArray & plainText = m->plainText();
868  const GpgME::DecryptionResult & decryptResult = m->decryptResult();
869  const GpgME::VerificationResult & verifyResult = m->verifyResult();
870  std::stringstream ss;
871  ss << decryptResult << '\n' << verifyResult;
872  //kdDebug(5006) << ss.str().c_str() << endl;
873  signatureFound = verifyResult.signatures().size() > 0;
874  signatures = verifyResult.signatures();
875  key = m->signingKey();
876  bDecryptionOk = !decryptResult.error();
877  passphraseError = decryptResult.error().isCanceled()
878  || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
879  actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
880  aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() );
881  auditLogError = m->auditLogError();
882  auditLog = m->auditLogAsHtml();
883 
884  //kdDebug(5006) << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG"
885  // << endl;
886  if ( bDecryptionOk )
887  decryptedData = TQCString( plainText.data(), plainText.size() + 1 );
888  else if ( mReader && showWarning ) {
889  decryptedData = "<div style=\"font-size:x-large; text-align:center;"
890  "padding:20pt;\">"
891  + i18n("Encrypted data not shown.").utf8()
892  + "</div>";
893  if ( !passphraseError )
894  aErrorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.")
895  .arg( cryptPlugLibName )
896  + "<br />"
897  + i18n("Error: %1").arg( aErrorText );
898  }
899  }
900  }
901 
902  if ( !cryptProto ) {
903  decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
904  + i18n("Encrypted data not shown.").utf8()
905  + "</div>";
906  switch ( cryptPlugError ) {
907  case NOT_INITIALIZED:
908  aErrorText = i18n( "Crypto plug-in \"%1\" is not initialized." )
909  .arg( cryptPlugLibName );
910  break;
911  case CANT_DECRYPT:
912  aErrorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages." )
913  .arg( cryptPlugLibName );
914  break;
915  case NO_PLUGIN:
916  aErrorText = i18n( "No appropriate crypto plug-in was found." );
917  break;
918  }
919  } else if ( kmkernel->contextMenuShown() ) {
920  // ### Workaround for bug 56693 (kmail freeze with the complete desktop
921  // ### while pinentry-qt appears)
922  TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
923  TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
924  bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
925  (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
926  (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
927  if ( !cipherIsBinary ) {
928  decryptedData = cipherStr;
929  }
930  else {
931  decryptedData = "<div style=\"font-size:x-large; text-align:center;"
932  "padding:20pt;\">"
933  + i18n("Encrypted data not shown.").utf8()
934  + "</div>";
935  }
936  }
937 
938  dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
939 
940  return bDecryptionOk;
941 }
942 
943  //static
944  bool ObjectTreeParser::containsExternalReferences( const TQCString & str )
945  {
946  TQRegExp httpRegExp("(\\\"|\\\'|url\\s*\\(\\s*)http[s]?:");
947  int httpPos = str.find( httpRegExp, 0 );
948 
949  while ( httpPos >= 0 ) {
950  // look backwards for "href"
951  if ( httpPos > 5 ) {
952  int hrefPos = str.findRev( "href", httpPos - 5, true );
953  // if no 'href' is found or the distance between 'href' and '"http[s]:'
954  // is larger than 7 (7 is the distance in 'href = "http[s]:') then
955  // we assume that we have found an external reference
956  if ( ( hrefPos == -1 ) || ( httpPos - hrefPos > 7 ) )
957  return true;
958  }
959  // find next occurrence of "http: or "https:
960  httpPos = str.find( httpRegExp, httpPos + 6 );
961  }
962  return false;
963  }
964 
965  bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
966  TQCString cstr( curNode->msgPart().bodyDecoded() );
967 
968  mRawReplyString = cstr;
969  if ( curNode->isFirstTextPart() ) {
970  mTextualContent += curNode->msgPart().bodyToUnicode();
971  mTextualContentCharset = curNode->msgPart().charset();
972  }
973 
974  if ( !mReader )
975  return true;
976 
977  if ( curNode->isFirstTextPart() ||
978  attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
979  showOnlyOneMimePart() )
980  {
981  if ( mReader->htmlMail() ) {
982  curNode->setDisplayedEmbedded( true );
983  // ---Sven's strip </BODY> and </HTML> from end of attachment start-
984  // We must fo this, or else we will see only 1st inlined html
985  // attachment. It is IMHO enough to search only for </BODY> and
986  // put \0 there.
987  int i = cstr.findRev("</body>", -1, false); //case insensitive
988  if ( 0 <= i )
989  cstr.truncate(i);
990  else // just in case - search for </html>
991  {
992  i = cstr.findRev("</html>", -1, false); //case insensitive
993  if ( 0 <= i ) cstr.truncate(i);
994  }
995  // ---Sven's strip </BODY> and </HTML> from end of attachment end-
996  // Show the "external references" warning (with possibility to load
997  // external references only if loading external references is disabled
998  // and the HTML code contains obvious external references). For
999  // messages where the external references are obfuscated the user won't
1000  // have an easy way to load them but that shouldn't be a problem
1001  // because only spam contains obfuscated external references.
1002  if ( !mReader->htmlLoadExternal() &&
1003  containsExternalReferences( cstr ) ) {
1004  htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
1005  htmlWriter()->queue( i18n("<b>Note:</b> This HTML message may contain external "
1006  "references to images etc. For security/privacy reasons "
1007  "external references are not loaded. If you trust the "
1008  "sender of this message then you can load the external "
1009  "references for this message "
1010  "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
1011  htmlWriter()->queue( "</div><br><br>" );
1012  }
1013  } else {
1014  htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
1015  htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
1016  "security reasons, only the raw HTML code "
1017  "is shown. If you trust the sender of this "
1018  "message then you can activate formatted "
1019  "HTML display for this message "
1020  "<a href=\"kmail:showHTML\">by clicking here</a>.") );
1021  htmlWriter()->queue( "</div><br><br>" );
1022  }
1023  htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr : KMMessage::html2source( cstr )));
1024  mReader->mColorBar->setHtmlMode();
1025  return true;
1026  }
1027  return false;
1028  }
1029 } // namespace KMail
1030 
1031 static bool isMailmanMessage( partNode * curNode ) {
1032  if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
1033  return false;
1034  DwHeaders & headers = curNode->dwPart()->Headers();
1035  if ( headers.HasField("X-Mailman-Version") )
1036  return true;
1037  if ( headers.HasField("X-Mailer") &&
1038  0 == TQCString( headers.FieldBody("X-Mailer").AsString().c_str() )
1039  .find("MAILMAN", 0, false) )
1040  return true;
1041  return false;
1042 }
1043 
1044 namespace KMail {
1045 
1046  bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
1047  const TQCString cstr = curNode->msgPart().bodyDecoded();
1048 
1049  //###
1050  const TQCString delim1( "--__--__--\n\nMessage:");
1051  const TQCString delim2( "--__--__--\r\n\r\nMessage:");
1052  const TQCString delimZ2("--__--__--\n\n_____________");
1053  const TQCString delimZ1("--__--__--\r\n\r\n_____________");
1054  TQCString partStr, digestHeaderStr;
1055  int thisDelim = cstr.find(delim1.data(), 0, false);
1056  if ( thisDelim == -1 )
1057  thisDelim = cstr.find(delim2.data(), 0, false);
1058  if ( thisDelim == -1 ) {
1059  kdDebug(5006) << " Sorry: Old style Mailman message but no delimiter found." << endl;
1060  return false;
1061  }
1062 
1063  int nextDelim = cstr.find(delim1.data(), thisDelim+1, false);
1064  if ( -1 == nextDelim )
1065  nextDelim = cstr.find(delim2.data(), thisDelim+1, false);
1066  if ( -1 == nextDelim )
1067  nextDelim = cstr.find(delimZ1.data(), thisDelim+1, false);
1068  if ( -1 == nextDelim )
1069  nextDelim = cstr.find(delimZ2.data(), thisDelim+1, false);
1070  if ( nextDelim < 0)
1071  return false;
1072 
1073  //kdDebug(5006) << " processing old style Mailman digest" << endl;
1074  //if ( curNode->mRoot )
1075  // curNode = curNode->mRoot;
1076 
1077  // at least one message found: build a mime tree
1078  digestHeaderStr = "Content-Type=text/plain\nContent-Description=digest header\n\n";
1079  digestHeaderStr += cstr.mid( 0, thisDelim );
1080  insertAndParseNewChildNode( *curNode,
1081  &*digestHeaderStr,
1082  "Digest Header", true );
1083  //mReader->queueHtml("<br><hr><br>");
1084  // temporarily change curent node's Content-Type
1085  // to get our embedded RfC822 messages properly inserted
1086  curNode->setType( DwMime::kTypeMultipart );
1087  curNode->setSubType( DwMime::kSubtypeDigest );
1088  while( -1 < nextDelim ){
1089  int thisEoL = cstr.find("\nMessage:", thisDelim, false);
1090  if ( -1 < thisEoL )
1091  thisDelim = thisEoL+1;
1092  else{
1093  thisEoL = cstr.find("\n_____________", thisDelim, false);
1094  if ( -1 < thisEoL )
1095  thisDelim = thisEoL+1;
1096  }
1097  thisEoL = cstr.find('\n', thisDelim);
1098  if ( -1 < thisEoL )
1099  thisDelim = thisEoL+1;
1100  else
1101  thisDelim = thisDelim+1;
1102  //while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] )
1103  // ++thisDelim;
1104 
1105  partStr = "Content-Type=message/rfc822\nContent-Description=embedded message\n";
1106  partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
1107  TQCString subject("embedded message");
1108  TQCString subSearch("\nSubject:");
1109  int subPos = partStr.find(subSearch.data(), 0, false);
1110  if ( -1 < subPos ){
1111  subject = partStr.mid(subPos+subSearch.length());
1112  thisEoL = subject.find('\n');
1113  if ( -1 < thisEoL )
1114  subject.truncate( thisEoL );
1115  }
1116  //kdDebug(5006) << " embedded message found: \"" << subject << "\"" << endl;
1117  insertAndParseNewChildNode( *curNode,
1118  &*partStr,
1119  subject, true );
1120  //mReader->queueHtml("<br><hr><br>");
1121  thisDelim = nextDelim+1;
1122  nextDelim = cstr.find(delim1.data(), thisDelim, false);
1123  if ( -1 == nextDelim )
1124  nextDelim = cstr.find(delim2.data(), thisDelim, false);
1125  if ( -1 == nextDelim )
1126  nextDelim = cstr.find(delimZ1.data(), thisDelim, false);
1127  if ( -1 == nextDelim )
1128  nextDelim = cstr.find(delimZ2.data(), thisDelim, false);
1129  }
1130  // reset curent node's Content-Type
1131  curNode->setType( DwMime::kTypeText );
1132  curNode->setSubType( DwMime::kSubtypePlain );
1133  int thisEoL = cstr.find("_____________", thisDelim);
1134  if ( -1 < thisEoL ){
1135  thisDelim = thisEoL;
1136  thisEoL = cstr.find('\n', thisDelim);
1137  if ( -1 < thisEoL )
1138  thisDelim = thisEoL+1;
1139  }
1140  else
1141  thisDelim = thisDelim+1;
1142  partStr = "Content-Type=text/plain\nContent-Description=digest footer\n\n";
1143  partStr += cstr.mid( thisDelim );
1144  insertAndParseNewChildNode( *curNode,
1145  &*partStr,
1146  "Digest Footer", true );
1147  return true;
1148  }
1149 
1150  bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
1151  if ( !mReader ) {
1152  mRawReplyString = curNode->msgPart().bodyDecoded();
1153  if ( curNode->isFirstTextPart() ) {
1154  mTextualContent += curNode->msgPart().bodyToUnicode();
1155  mTextualContentCharset = curNode->msgPart().charset();
1156  }
1157  return true;
1158  }
1159 
1160  if ( !curNode->isFirstTextPart() &&
1161  attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
1162  !showOnlyOneMimePart() )
1163  return false;
1164 
1165  mRawReplyString = curNode->msgPart().bodyDecoded();
1166  if ( curNode->isFirstTextPart() ) {
1167  mTextualContent += curNode->msgPart().bodyToUnicode();
1168  mTextualContentCharset = curNode->msgPart().charset();
1169  }
1170 
1171  TQString label = curNode->msgPart().fileName().stripWhiteSpace();
1172  if ( label.isEmpty() )
1173  label = curNode->msgPart().name().stripWhiteSpace();
1174 
1175  const bool bDrawFrame = !curNode->isFirstTextPart()
1176  && !showOnlyOneMimePart()
1177  && !label.isEmpty();
1178  if ( bDrawFrame ) {
1179  label = KMMessage::quoteHtmlChars( label, true );
1180 
1181  const TQString comment =
1182  KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(), true );
1183 
1184  const TQString fileName =
1185  mReader->writeMessagePartToTempFile( &curNode->msgPart(),
1186  curNode->nodeId() );
1187 
1188  const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
1189 
1190  TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
1191  "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
1192  if ( !fileName.isEmpty() )
1193  htmlStr += "<a href=\"" + curNode->asHREF( "body" ) + "\">"
1194  + label + "</a>";
1195  else
1196  htmlStr += label;
1197  if ( !comment.isEmpty() )
1198  htmlStr += "<br>" + comment;
1199  htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
1200 
1201  htmlWriter()->queue( htmlStr );
1202  }
1203  // process old style not-multipart Mailman messages to
1204  // enable verification of the embedded messages' signatures
1205  if ( !isMailmanMessage( curNode ) ||
1206  !processMailmanMessage( curNode ) ) {
1207  writeBodyString( mRawReplyString, curNode->trueFromAddress(),
1208  codecFor( curNode ), result, !bDrawFrame );
1209  curNode->setDisplayedEmbedded( true );
1210  }
1211  if ( bDrawFrame )
1212  htmlWriter()->queue( "</td></tr></table>" );
1213 
1214  return true;
1215  }
1216 
1217  void ObjectTreeParser::stdChildHandling( partNode * child ) {
1218  if ( !child )
1219  return;
1220 
1221  ObjectTreeParser otp( *this );
1222  otp.setShowOnlyOneMimePart( false );
1223  otp.parseObjectTree( child );
1224  mRawReplyString += otp.rawReplyString();
1225  mTextualContent += otp.textualContent();
1226  if ( !otp.textualContentCharset().isEmpty() )
1227  mTextualContentCharset = otp.textualContentCharset();
1228  }
1229 
1230  TQString ObjectTreeParser::defaultToltecReplacementText()
1231  {
1232  return i18n( "This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
1233  "Microsoft Outlook in combination with the Toltec connector." );
1234  }
1235 
1236  bool ObjectTreeParser::processToltecMail( partNode *node )
1237  {
1238  if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
1239  !node->isToltecMessage() || mShowRawToltecMail )
1240  return false;
1241 
1242  htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
1243  htmlWriter()->queue( "<br><br><a href=\"kmail:showRawToltecMail\">" +
1244  i18n( "Show Raw Message" ) + "</a>" );
1245  return true;
1246  }
1247 
1248  bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
1249 
1250  if ( processToltecMail( node ) ) {
1251  return true;
1252  }
1253 
1254  partNode * child = node->firstChild();
1255  if ( !child )
1256  return false;
1257 
1258  // normal treatment of the parts in the mp/mixed container
1259  stdChildHandling( child );
1260  return true;
1261  }
1262 
1263  bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
1264  partNode * child = node->firstChild();
1265  if ( !child )
1266  return false;
1267 
1268  partNode * dataHtml = child->findType( DwMime::kTypeText,
1269  DwMime::kSubtypeHtml, false, true );
1270  partNode * dataPlain = child->findType( DwMime::kTypeText,
1271  DwMime::kSubtypePlain, false, true );
1272 
1273  if ( (mReader && mReader->htmlMail() && dataHtml) ||
1274  (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
1275  if ( dataPlain )
1276  dataPlain->setProcessed( true, false );
1277  stdChildHandling( dataHtml );
1278  return true;
1279  }
1280 
1281  if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
1282  if ( dataHtml )
1283  dataHtml->setProcessed( true, false );
1284  stdChildHandling( dataPlain );
1285  return true;
1286  }
1287 
1288  stdChildHandling( child );
1289  return true;
1290  }
1291 
1292  bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
1293  return processMultiPartMixedSubtype( node, result );
1294  }
1295 
1296  bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
1297  return processMultiPartMixedSubtype( node, result );
1298  }
1299 
1300  bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
1301  if ( node->childCount() != 2 ) {
1302  kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
1303  << "processing as multipart/mixed" << endl;
1304  if ( node->firstChild() )
1305  stdChildHandling( node->firstChild() );
1306  return node->firstChild();
1307  }
1308 
1309  partNode * signedData = node->firstChild();
1310  assert( signedData );
1311 
1312  partNode * signature = signedData->nextSibling();
1313  assert( signature );
1314 
1315  signature->setProcessed( true, true );
1316 
1317  if ( !includeSignatures() ) {
1318  stdChildHandling( signedData );
1319  return true;
1320  }
1321 
1322  // FIXME(marc) check here that the protocol parameter matches the
1323  // mimetype of "signature" (not required by the RFC, but practised
1324  // by all implementaions of security multiparts
1325 
1326  const TQString contentType = node->contentTypeParameter( "protocol" ).lower();
1327  const Kleo::CryptoBackend::Protocol *protocol = 0;
1328  if ( contentType == "application/pkcs7-signature" || contentType == "application/x-pkcs7-signature" )
1329  protocol = Kleo::CryptoBackendFactory::instance()->smime();
1330  else if ( contentType == "application/pgp-signature" || contentType == "application/x-pgp-signature" )
1331  protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
1332 
1333  if ( !protocol ) {
1334  signature->setProcessed( true, true );
1335  stdChildHandling( signedData );
1336  return true;
1337  }
1338 
1339  CryptoProtocolSaver saver( this, protocol );
1340 
1341  node->setSignatureState( KMMsgFullySigned );
1342  writeOpaqueOrMultipartSignedData( signedData, *signature,
1343  node->trueFromAddress() );
1344  return true;
1345  }
1346 
1347  bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
1348  partNode * child = node->firstChild();
1349  if ( !child )
1350  return false;
1351 
1352  if ( keepEncryptions() ) {
1353  node->setEncryptionState( KMMsgFullyEncrypted );
1354  const TQCString cstr = node->msgPart().bodyDecoded();
1355  if ( mReader )
1356  writeBodyString( cstr, node->trueFromAddress(),
1357  codecFor( node ), result, false );
1358  mRawReplyString += cstr;
1359  return true;
1360  }
1361 
1362  const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
1363 
1364  /*
1365  ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
1366  */
1367  partNode * data = child->findType( DwMime::kTypeApplication,
1368  DwMime::kSubtypeOctetStream, false, true );
1369  if ( data ) {
1370  useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
1371  }
1372  if ( !data ) {
1373  data = child->findType( DwMime::kTypeApplication,
1374  DwMime::kSubtypePkcs7Mime, false, true );
1375  if ( data ) {
1376  useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
1377  }
1378  }
1379  /*
1380  ---------------------------------------------------------------------------------------------------------------
1381  */
1382 
1383  if ( !data ) {
1384  stdChildHandling( child );
1385  return true;
1386  }
1387 
1388  CryptoProtocolSaver cpws( this, useThisCryptProto );
1389 
1390  if ( partNode * dataChild = data->firstChild() ) {
1391  //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
1392  stdChildHandling( dataChild );
1393  //kdDebug(5006) << "\n-----> Returning from parseObjectTree( curNode->mChild )\n" << endl;
1394  return true;
1395  }
1396 
1397  node->setEncryptionState( KMMsgFullyEncrypted );
1398 
1399  if ( mReader && !mReader->decryptMessage() ) {
1400  writeDeferredDecryptionBlock();
1401  data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
1402  return true;
1403  }
1404 
1405  //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl;
1406  PartMetaData messagePart;
1407  TQCString decryptedData;
1408  bool signatureFound;
1409  std::vector<GpgME::Signature> signatures;
1410  GpgME::Key signingKey;
1411  bool passphraseError;
1412  bool actuallyEncrypted = true;
1413  bool decryptionStarted;
1414 
1415  bool bOkDecrypt = okDecryptMIME( *data,
1416  decryptedData,
1417  signatureFound,
1418  signatures,
1419  signingKey,
1420  true,
1421  passphraseError,
1422  actuallyEncrypted,
1423  decryptionStarted,
1424  messagePart.errorText,
1425  messagePart.auditLogError,
1426  messagePart.auditLog );
1427 
1428  if ( decryptionStarted ) {
1429  writeDecryptionInProgressBlock();
1430  return true;
1431  }
1432 
1433  // paint the frame
1434  if ( mReader ) {
1435  messagePart.isDecryptable = bOkDecrypt;
1436  messagePart.isEncrypted = true;
1437  messagePart.isSigned = false;
1438  htmlWriter()->queue( writeSigstatHeader( messagePart,
1439  cryptoProtocol(),
1440  node->trueFromAddress() ) );
1441  }
1442 
1443  if ( bOkDecrypt ) {
1444  // Note: Multipart/Encrypted might also be signed
1445  // without encapsulating a nicely formatted
1446  // ~~~~~~~ Multipart/Signed part.
1447  // (see RFC 3156 --> 6.2)
1448  // In this case we paint a _2nd_ frame inside the
1449  // encryption frame, but we do _not_ show a respective
1450  // encapsulated MIME part in the Mime Tree Viewer
1451  // since we do want to show the _true_ structure of the
1452  // message there - not the structure that the sender's
1453  // MUA 'should' have sent. :-D (khz, 12.09.2002)
1454  //
1455  if ( signatureFound ) {
1456  writeOpaqueOrMultipartSignedData( 0,
1457  *node,
1458  node->trueFromAddress(),
1459  false,
1460  false,
1461  &decryptedData,
1462  signatures,
1463  signingKey );
1464  node->setSignatureState( KMMsgFullySigned );
1465  } else {
1466  insertAndParseNewChildNode( *node,
1467  &*decryptedData,
1468  "encrypted data" );
1469  }
1470  } else {
1471  mRawReplyString += decryptedData;
1472  if ( mReader ) {
1473  // print the error message that was returned in decryptedData
1474  // (utf8-encoded)
1475  htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1476  }
1477  }
1478 
1479  if ( mReader )
1480  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1481  data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
1482  return true;
1483  }
1484 
1485 
1486  bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
1487  if ( mReader
1488  && !attachmentStrategy()->inlineNestedMessages()
1489  && !showOnlyOneMimePart() )
1490  return false;
1491 
1492  if ( partNode * child = node->firstChild() ) {
1493  //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
1494  ObjectTreeParser otp( mReader, cryptoProtocol() );
1495  otp.parseObjectTree( child );
1496  mRawReplyString += otp.rawReplyString();
1497  mTextualContent += otp.textualContent();
1498  if ( !otp.textualContentCharset().isEmpty() )
1499  mTextualContentCharset = otp.textualContentCharset();
1500  //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
1501  return true;
1502  }
1503  //kdDebug(5006) << "\n-----> Initially processing data of embedded RfC 822 message\n" << endl;
1504  // paint the frame
1505  PartMetaData messagePart;
1506  if ( mReader ) {
1507  messagePart.isEncrypted = false;
1508  messagePart.isSigned = false;
1509  messagePart.isEncapsulatedRfc822Message = true;
1510  TQString filename =
1511  mReader->writeMessagePartToTempFile( &node->msgPart(),
1512  node->nodeId() );
1513  htmlWriter()->queue( writeSigstatHeader( messagePart,
1514  cryptoProtocol(),
1515  node->trueFromAddress(),
1516  node ) );
1517  }
1518  TQCString rfc822messageStr( node->msgPart().bodyDecoded() );
1519  // display the headers of the encapsulated message
1520  DwMessage* rfc822DwMessage = new DwMessage(); // will be deleted by c'tor of rfc822headers
1521  rfc822DwMessage->FromString( rfc822messageStr );
1522  rfc822DwMessage->Parse();
1523  KMMessage rfc822message( rfc822DwMessage );
1524  node->setFromAddress( rfc822message.from() );
1525  //kdDebug(5006) << "\n-----> Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl;
1526  if ( mReader )
1527  htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
1528  //mReader->parseMsgHeader( &rfc822message );
1529  // display the body of the encapsulated message
1530  insertAndParseNewChildNode( *node,
1531  &*rfc822messageStr,
1532  "encapsulated message", false /*append*/,
1533  false /*add to textual content*/ );
1534  node->setDisplayedEmbedded( true );
1535  if ( mReader )
1536  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1537  return true;
1538  }
1539 
1540 
1541  bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
1542  if ( partNode * child = node->firstChild() ) {
1543  //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
1544  ObjectTreeParser otp( mReader, cryptoProtocol() );
1545  otp.parseObjectTree( child );
1546  mRawReplyString += otp.rawReplyString();
1547  mTextualContent += otp.textualContent();
1548  if ( !otp.textualContentCharset().isEmpty() )
1549  mTextualContentCharset = otp.textualContentCharset();
1550  //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
1551  return true;
1552  }
1553 
1554  const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
1555  if ( node->parentNode()
1556  && DwMime::kTypeMultipart == node->parentNode()->type()
1557  && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
1558  //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl;
1559  node->setEncryptionState( KMMsgFullyEncrypted );
1560  if ( keepEncryptions() ) {
1561  const TQCString cstr = node->msgPart().bodyDecoded();
1562  if ( mReader )
1563  writeBodyString( cstr, node->trueFromAddress(),
1564  codecFor( node ), result, false );
1565  mRawReplyString += cstr;
1566  } else if ( mReader && !mReader->decryptMessage() ) {
1567  writeDeferredDecryptionBlock();
1568  } else {
1569  /*
1570  ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
1571  */
1572  PartMetaData messagePart;
1573  setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
1574  TQCString decryptedData;
1575  bool signatureFound;
1576  std::vector<GpgME::Signature> signatures;
1577  GpgME::Key signingKey;
1578  bool passphraseError;
1579  bool actuallyEncrypted = true;
1580  bool decryptionStarted;
1581 
1582  bool bOkDecrypt = okDecryptMIME( *node,
1583  decryptedData,
1584  signatureFound,
1585  signatures,
1586  signingKey,
1587  true,
1588  passphraseError,
1589  actuallyEncrypted,
1590  decryptionStarted,
1591  messagePart.errorText,
1592  messagePart.auditLogError,
1593  messagePart.auditLog );
1594 
1595  if ( decryptionStarted ) {
1596  writeDecryptionInProgressBlock();
1597  return true;
1598  }
1599 
1600  // paint the frame
1601  if ( mReader ) {
1602  messagePart.isDecryptable = bOkDecrypt;
1603  messagePart.isEncrypted = true;
1604  messagePart.isSigned = false;
1605  htmlWriter()->queue( writeSigstatHeader( messagePart,
1606  cryptoProtocol(),
1607  node->trueFromAddress() ) );
1608  }
1609 
1610  if ( bOkDecrypt ) {
1611  // fixing the missing attachments bug #1090-b
1612  insertAndParseNewChildNode( *node,
1613  &*decryptedData,
1614  "encrypted data" );
1615  } else {
1616  mRawReplyString += decryptedData;
1617  if ( mReader ) {
1618  // print the error message that was returned in decryptedData
1619  // (utf8-encoded)
1620  htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1621  }
1622  }
1623 
1624  if ( mReader )
1625  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1626  }
1627  return true;
1628  }
1629  setCryptoProtocol( oldUseThisCryptPlug );
1630  return false;
1631  }
1632 
1633  bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
1634  if ( partNode * child = node->firstChild() ) {
1635  //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
1636  ObjectTreeParser otp( mReader, cryptoProtocol() );
1637  otp.parseObjectTree( child );
1638  mRawReplyString += otp.rawReplyString();
1639  mTextualContent += otp.textualContent();
1640  if ( !otp.textualContentCharset().isEmpty() )
1641  mTextualContentCharset = otp.textualContentCharset();
1642  //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
1643  return true;
1644  }
1645 
1646  //kdDebug(5006) << "\n-----> Initially processing signed and/or encrypted data\n" << endl;
1647  if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
1648  return false;
1649 
1650  const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
1651 
1652  const TQString smimeType = node->contentTypeParameter("smime-type").lower();
1653 
1654  if ( smimeType == "certs-only" ) {
1655  result.setNeverDisplayInline( true );
1656  if ( !smimeCrypto || !mReader )
1657  return false;
1658 
1659  const TDEConfigGroup reader( KMKernel::config(), "Reader" );
1660  if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
1661  return false;
1662 
1663  const TQByteArray certData = node->msgPart().bodyDecodedBinary();
1664 
1665  const STD_NAMESPACE_PREFIX unique_ptr<Kleo::ImportJob> import( smimeCrypto->importJob() );
1666  const GpgME::ImportResult res = import->exec( certData );
1667  if ( res.error() ) {
1668  htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
1669  "Reason: %1").arg( TQString::fromLocal8Bit( res.error().asString() ) ) );
1670  return true;
1671  }
1672 
1673  const int nImp = res.numImported();
1674  const int nUnc = res.numUnchanged();
1675  const int nSKImp = res.numSecretKeysImported();
1676  const int nSKUnc = res.numSecretKeysUnchanged();
1677  if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
1678  htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
1679  return true;
1680  }
1681  TQString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br>&nbsp;<br>";
1682  if ( nImp )
1683  comment += i18n( "1 new certificate was imported.",
1684  "%n new certificates were imported.", nImp ) + "<br>";
1685  if ( nUnc )
1686  comment += i18n( "1 certificate was unchanged.",
1687  "%n certificates were unchanged.", nUnc ) + "<br>";
1688  if ( nSKImp )
1689  comment += i18n( "1 new secret key was imported.",
1690  "%n new secret keys were imported.", nSKImp ) + "<br>";
1691  if ( nSKUnc )
1692  comment += i18n( "1 secret key was unchanged.",
1693  "%n secret keys were unchanged.", nSKUnc ) + "<br>";
1694  comment += "&nbsp;<br>";
1695  htmlWriter()->queue( comment );
1696  if ( !nImp && !nSKImp ) {
1697  htmlWriter()->queue( "<hr>" );
1698  return true;
1699  }
1700  const std::vector<GpgME::Import> imports = res.imports();
1701  if ( imports.empty() ) {
1702  htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
1703  return true;
1704  }
1705  htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
1706  for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
1707  if ( (*it).error() )
1708  htmlWriter()->queue( i18n( "Failed: %1 (%2)" )
1709  .arg( (*it).fingerprint(),
1710  TQString::fromLocal8Bit( (*it).error().asString() ) ) );
1711  else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
1712  if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
1713  htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
1714  }
1715  else {
1716  htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
1717  }
1718  }
1719  htmlWriter()->queue( "<br>" );
1720  }
1721 
1722  htmlWriter()->queue( "<hr>" );
1723  return true;
1724  }
1725 
1726  if ( !smimeCrypto )
1727  return false;
1728  CryptoProtocolSaver cpws( this, smimeCrypto );
1729 
1730  bool isSigned = smimeType == "signed-data";
1731  bool isEncrypted = smimeType == "enveloped-data";
1732 
1733  // Analyze "signTestNode" node to find/verify a signature.
1734  // If zero this verification was successfully done after
1735  // decrypting via recursion by insertAndParseNewChildNode().
1736  partNode* signTestNode = isEncrypted ? 0 : node;
1737 
1738 
1739  // We try decrypting the content
1740  // if we either *know* that it is an encrypted message part
1741  // or there is neither signed nor encrypted parameter.
1742  if ( !isSigned ) {
1743  if ( isEncrypted ) {
1744  //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl;
1745  }
1746  else {
1747  //kdDebug(5006) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl;
1748  }
1749  TQCString decryptedData;
1750  PartMetaData messagePart;
1751  messagePart.isEncrypted = true;
1752  messagePart.isSigned = false;
1753  bool signatureFound;
1754  std::vector<GpgME::Signature> signatures;
1755  GpgME::Key signingKey;
1756  bool passphraseError;
1757  bool actuallyEncrypted = true;
1758  bool decryptionStarted;
1759 
1760  if ( mReader && !mReader->decryptMessage() ) {
1761  writeDeferredDecryptionBlock();
1762  isEncrypted = true;
1763  signTestNode = 0; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
1764  } else {
1765  const bool bOkDecrypt = okDecryptMIME( *node,
1766  decryptedData,
1767  signatureFound,
1768  signatures,
1769  signingKey,
1770  false,
1771  passphraseError,
1772  actuallyEncrypted,
1773  decryptionStarted,
1774  messagePart.errorText,
1775  messagePart.auditLogError,
1776  messagePart.auditLog );
1777  if ( decryptionStarted ) {
1778  writeDecryptionInProgressBlock();
1779  return true;
1780  }
1781  if ( bOkDecrypt ) {
1782  //kdDebug(5006) << "pkcs7 mime - encryption found - enveloped (encrypted) data !" << endl;
1783  isEncrypted = true;
1784  node->setEncryptionState( KMMsgFullyEncrypted );
1785  signTestNode = 0;
1786  // paint the frame
1787  messagePart.isDecryptable = true;
1788  if ( mReader )
1789  htmlWriter()->queue( writeSigstatHeader( messagePart,
1790  cryptoProtocol(),
1791  node->trueFromAddress() ) );
1792  insertAndParseNewChildNode( *node,
1793  &*decryptedData,
1794  "encrypted data" );
1795  if ( mReader )
1796  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1797  } else {
1798  // decryption failed, which could be because the part was encrypted but
1799  // decryption failed, or because we didn't know if it was encrypted, tried,
1800  // and failed. If the message was not actually encrypted, we continue
1801  // assuming it's signed
1802  if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
1803  isEncrypted = true;
1804  signTestNode = 0;
1805  }
1806 
1807  if ( isEncrypted ) {
1808  //kdDebug(5006) << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !" << endl;
1809  // paint the frame
1810  messagePart.isDecryptable = false;
1811  if ( mReader ) {
1812  htmlWriter()->queue( writeSigstatHeader( messagePart,
1813  cryptoProtocol(),
1814  node->trueFromAddress() ) );
1815  assert( mReader->decryptMessage() ); // handled above
1816  writePartIcon( &node->msgPart(), node->nodeId() );
1817  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1818  }
1819  } else {
1820  //kdDebug(5006) << "pkcs7 mime - NO encryption found" << endl;
1821  }
1822  }
1823  }
1824  if ( isEncrypted )
1825  node->setEncryptionState( KMMsgFullyEncrypted );
1826  }
1827 
1828  // We now try signature verification if necessarry.
1829  if ( signTestNode ) {
1830  if ( isSigned ) {
1831  //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: opaque signed data" << endl;
1832  }
1833  else {
1834  //kdDebug(5006) << "pkcs7 mime - type unknown - opaque signed data ?" << endl;
1835  }
1836 
1837  bool sigFound = writeOpaqueOrMultipartSignedData( 0,
1838  *signTestNode,
1839  node->trueFromAddress(),
1840  true,
1841  isEncrypted );
1842  if ( sigFound ) {
1843  if ( !isSigned ) {
1844  //kdDebug(5006) << "pkcs7 mime - signature found - opaque signed data !" << endl;
1845  isSigned = true;
1846  }
1847  signTestNode->setSignatureState( KMMsgFullySigned );
1848  if ( signTestNode != node )
1849  node->setSignatureState( KMMsgFullySigned );
1850  } else {
1851  //kdDebug(5006) << "pkcs7 mime - NO signature found :-(" << endl;
1852  }
1853  }
1854 
1855  return isSigned || isEncrypted;
1856 }
1857 
1858 bool ObjectTreeParser::decryptChiasmus( const TQByteArray& data, TQByteArray& bodyDecoded, TQString& errorText )
1859 {
1860  const Kleo::CryptoBackend::Protocol * chiasmus =
1861  Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
1862  Q_ASSERT( chiasmus );
1863  if ( !chiasmus )
1864  return false;
1865 
1866  const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQStringVariantMap() ) );
1867  if ( !listjob ) {
1868  errorText = i18n( "Chiasmus backend does not offer the "
1869  "\"x-obtain-keys\" function. Please report this bug." );
1870  return false;
1871  }
1872 
1873  if ( listjob->exec() ) {
1874  errorText = i18n( "Chiasmus Backend Error" );
1875  return false;
1876  }
1877 
1878  const TQVariant result = listjob->property( "result" );
1879  if ( result.type() != TQVariant::StringList ) {
1880  errorText = i18n( "Unexpected return value from Chiasmus backend: "
1881  "The \"x-obtain-keys\" function did not return a "
1882  "string list. Please report this bug." );
1883  return false;
1884  }
1885 
1886  const TQStringList keys = result.toStringList();
1887  if ( keys.empty() ) {
1888  errorText = i18n( "No keys have been found. Please check that a "
1889  "valid key path has been set in the Chiasmus "
1890  "configuration." );
1891  return false;
1892  }
1893 
1894  emit mReader->noDrag();
1895  ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
1896  keys, GlobalSettings::chiasmusDecryptionKey(),
1897  GlobalSettings::chiasmusDecryptionOptions() );
1898  if ( selectorDlg.exec() != TQDialog::Accepted )
1899  return false;
1900 
1901  GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
1902  GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
1903  assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
1904 
1905  const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-decrypt", TQStringVariantMap() ) );
1906  if ( !job ) {
1907  errorText = i18n( "Chiasmus backend does not offer the "
1908  "\"x-decrypt\" function. Please report this bug." );
1909  return false;
1910  }
1911 
1912  if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
1913  !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
1914  !job->setProperty( "input", data ) ) {
1915  errorText = i18n( "The \"x-decrypt\" function does not accept "
1916  "the expected parameters. Please report this bug." );
1917  return false;
1918  }
1919 
1920  if ( job->exec() ) {
1921  errorText = i18n( "Chiasmus Decryption Error" );
1922  return false;
1923  }
1924 
1925  const TQVariant resultData = job->property( "result" );
1926  if ( resultData.type() != TQVariant::ByteArray ) {
1927  errorText = i18n( "Unexpected return value from Chiasmus backend: "
1928  "The \"x-decrypt\" function did not return a "
1929  "byte array. Please report this bug." );
1930  return false;
1931  }
1932  bodyDecoded = resultData.toByteArray();
1933  return true;
1934 }
1935 
1936 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
1937 {
1938  if ( !mReader ) {
1939  mRawReplyString = curNode->msgPart().bodyDecoded();
1940  mTextualContent += curNode->msgPart().bodyToUnicode();
1941  mTextualContentCharset = curNode->msgPart().charset();
1942  return true;
1943  }
1944 
1945  TQByteArray decryptedBody;
1946  TQString errorText;
1947  const TQByteArray data = curNode->msgPart().bodyDecodedBinary();
1948  bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
1949  PartMetaData messagePart;
1950  messagePart.isDecryptable = bOkDecrypt;
1951  messagePart.isEncrypted = true;
1952  messagePart.isSigned = false;
1953  messagePart.errorText = errorText;
1954  if ( mReader )
1955  htmlWriter()->queue( writeSigstatHeader( messagePart,
1956  0, //cryptPlugWrapper(),
1957  curNode->trueFromAddress() ) );
1958  const TQByteArray body = bOkDecrypt ? decryptedBody : data;
1959  const TQString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
1960  const TQTextCodec* aCodec = chiasmusCharset.isEmpty()
1961  ? codecFor( curNode )
1962  : KMMsgBase::codecForName( chiasmusCharset.ascii() );
1963  htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false /*decorate*/ ) );
1964  result.setInlineEncryptionState( KMMsgFullyEncrypted );
1965  if ( mReader )
1966  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1967  return true;
1968 }
1969 
1970 bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
1971 {
1972  Q_UNUSED( result );
1973  if ( !mReader )
1974  return false;
1975 
1976  const TQString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
1977  KTNEFParser parser;
1978  if ( !parser.openFile( fileName ) || !parser.message()) {
1979  kdDebug() << k_funcinfo << "Could not parse " << fileName << endl;
1980  return false;
1981  }
1982 
1983  TQPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
1984  if ( tnefatts.isEmpty() ) {
1985  kdDebug() << k_funcinfo << "No attachments found in " << fileName << endl;
1986  return false;
1987  }
1988 
1989  if ( !showOnlyOneMimePart() ) {
1990  TQString label = node->msgPart().fileName().stripWhiteSpace();
1991  if ( label.isEmpty() )
1992  label = node->msgPart().name().stripWhiteSpace();
1993  label = KMMessage::quoteHtmlChars( label, true );
1994  const TQString comment = KMMessage::quoteHtmlChars( node->msgPart().contentDescription(), true );
1995  const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
1996 
1997  TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
1998  "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
1999  if ( !fileName.isEmpty() )
2000  htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
2001  + label + "</a>";
2002  else
2003  htmlStr += label;
2004  if ( !comment.isEmpty() )
2005  htmlStr += "<br>" + comment;
2006  htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
2007  htmlWriter()->queue( htmlStr );
2008  }
2009 
2010  for ( uint i = 0; i < tnefatts.count(); ++i ) {
2011  KTNEFAttach *att = tnefatts.at( i );
2012  TQString label = att->displayName();
2013  if( label.isEmpty() )
2014  label = att->name();
2015  label = KMMessage::quoteHtmlChars( label, true );
2016 
2017  TQString dir = mReader->createTempDir( "ktnef-" + TQString::number( i ) );
2018  parser.extractFileTo( att->name(), dir );
2019  mReader->mTempFiles.append( dir + TQDir::separator() + att->name() );
2020  TQString href = "file:" + KURL::encode_string( dir + TQDir::separator() + att->name() );
2021 
2022  KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
2023  TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( mimeType->icon( TQString(), false ), TDEIcon::Desktop );
2024 
2025  htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
2026  iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
2027  "</a></div><br>" );
2028  }
2029 
2030  if ( !showOnlyOneMimePart() )
2031  htmlWriter()->queue( "</td></tr></table>" );
2032 
2033  return true;
2034 }
2035 
2036  void ObjectTreeParser::writeBodyString( const TQCString & bodyString,
2037  const TQString & fromAddress,
2038  const TQTextCodec * codec,
2039  ProcessResult & result,
2040  bool decorate ) {
2041  assert( mReader ); assert( codec );
2042  KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
2043  KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
2044  writeBodyStr( bodyString, codec, fromAddress,
2045  inlineSignatureState, inlineEncryptionState, decorate );
2046  result.setInlineSignatureState( inlineSignatureState );
2047  result.setInlineEncryptionState( inlineEncryptionState );
2048  }
2049 
2050  void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
2051  if ( !mReader || !msgPart )
2052  return;
2053 
2054  TQString label = msgPart->fileName();
2055  if( label.isEmpty() )
2056  label = msgPart->name();
2057  if( label.isEmpty() )
2058  label = "unnamed";
2059  label = KMMessage::quoteHtmlChars( label, true );
2060 
2061  TQString comment = msgPart->contentDescription();
2062  comment = KMMessage::quoteHtmlChars( comment, true );
2063  if ( label == comment ) comment = TQString();
2064 
2065  TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
2066 
2067  TQString href = TQString( "attachment:%1?place=body" ).arg( partNum );
2068 
2069  TQString iconName;
2070  if( inlineImage )
2071  iconName = href;
2072  else {
2073  iconName = msgPart->iconName();
2074  if( iconName.right( 14 ) == "mime_empty.png" ) {
2075  msgPart->magicSetType();
2076  iconName = msgPart->iconName();
2077  }
2078  }
2079 
2080  TQCString contentId = msgPart->contentId();
2081  if ( !contentId.isEmpty() ) {
2082  htmlWriter()->embedPart( contentId, href );
2083  }
2084 
2085  if( inlineImage )
2086  // show the filename of the image below the embedded image
2087  htmlWriter()->queue( "<div><a href=\"" + href + "\">"
2088  "<img src=\"" + fileName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
2089  "</div>"
2090  "<div><a href=\"" + href + "\">" + label + "</a>"
2091  "</div>"
2092  "<div>" + comment + "</div><br>" );
2093  else
2094  // show the filename next to the icon
2095  htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
2096  iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
2097  "</a></div>"
2098  "<div>" + comment + "</div><br>" );
2099  }
2100 
2101 #define SIG_FRAME_COL_UNDEF 99
2102 #define SIG_FRAME_COL_RED -1
2103 #define SIG_FRAME_COL_YELLOW 0
2104 #define SIG_FRAME_COL_GREEN 1
2105 TQString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
2106  int status_code,
2107  GpgME::Signature::Summary summary,
2108  int& frameColor,
2109  bool& showKeyInfos )
2110 {
2111  // note: At the moment frameColor and showKeyInfos are
2112  // used for CMS only but not for PGP signatures
2113  // pending(khz): Implement usage of these for PGP sigs as well.
2114  showKeyInfos = true;
2115  TQString result;
2116  if( cryptProto ) {
2117  if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
2118  // process enum according to it's definition to be read in
2119  // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
2120  switch( status_code ) {
2121  case 0: // GPGME_SIG_STAT_NONE
2122  result = i18n("Error: Signature not verified");
2123  break;
2124  case 1: // GPGME_SIG_STAT_GOOD
2125  result = i18n("Good signature");
2126  break;
2127  case 2: // GPGME_SIG_STAT_BAD
2128  result = i18n("<b>Bad</b> signature");
2129  break;
2130  case 3: // GPGME_SIG_STAT_NOKEY
2131  result = i18n("No public key to verify the signature");
2132  break;
2133  case 4: // GPGME_SIG_STAT_NOSIG
2134  result = i18n("No signature found");
2135  break;
2136  case 5: // GPGME_SIG_STAT_ERROR
2137  result = i18n("Error verifying the signature");
2138  break;
2139  case 6: // GPGME_SIG_STAT_DIFF
2140  result = i18n("Different results for signatures");
2141  break;
2142  /* PENDING(khz) Verify exact meaning of the following values:
2143  case 7: // GPGME_SIG_STAT_GOOD_EXP
2144  return i18n("Signature certificate is expired");
2145  break;
2146  case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
2147  return i18n("One of the certificate's keys is expired");
2148  break;
2149  */
2150  default:
2151  result = ""; // do *not* return a default text here !
2152  break;
2153  }
2154  }
2155  else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
2156  // process status bits according to SigStatus_...
2157  // definitions in tdenetwork/libtdenetwork/cryptplug.h
2158 
2159  if( summary == GpgME::Signature::None ) {
2160  result = i18n("No status information available.");
2161  frameColor = SIG_FRAME_COL_YELLOW;
2162  showKeyInfos = false;
2163  return result;
2164  }
2165 
2166  if( summary & GpgME::Signature::Valid ) {
2167  result = i18n("Good signature.");
2168  // Note:
2169  // Here we are work differently than KMail did before!
2170  //
2171  // The GOOD case ( == sig matching and the complete
2172  // certificate chain was verified and is valid today )
2173  // by definition does *not* show any key
2174  // information but just states that things are OK.
2175  // (khz, according to LinuxTag 2002 meeting)
2176  frameColor = SIG_FRAME_COL_GREEN;
2177  showKeyInfos = false;
2178  return result;
2179  }
2180 
2181  // we are still there? OK, let's test the different cases:
2182 
2183  // we assume green, test for yellow or red (in this order!)
2184  frameColor = SIG_FRAME_COL_GREEN;
2185  TQString result2;
2186  if( summary & GpgME::Signature::KeyExpired ){
2187  // still is green!
2188  result2 += i18n("One key has expired.");
2189  }
2190  if( summary & GpgME::Signature::SigExpired ){
2191  // and still is green!
2192  result2 += i18n("The signature has expired.");
2193  }
2194 
2195  // test for yellow:
2196  if( summary & GpgME::Signature::KeyMissing ) {
2197  result2 += i18n("Unable to verify: key missing.");
2198  // if the signature certificate is missing
2199  // we cannot show infos on it
2200  showKeyInfos = false;
2201  frameColor = SIG_FRAME_COL_YELLOW;
2202  }
2203  if( summary & GpgME::Signature::CrlMissing ){
2204  result2 += i18n("CRL not available.");
2205  frameColor = SIG_FRAME_COL_YELLOW;
2206  }
2207  if( summary & GpgME::Signature::CrlTooOld ){
2208  result2 += i18n("Available CRL is too old.");
2209  frameColor = SIG_FRAME_COL_YELLOW;
2210  }
2211  if( summary & GpgME::Signature::BadPolicy ){
2212  result2 += i18n("A policy was not met.");
2213  frameColor = SIG_FRAME_COL_YELLOW;
2214  }
2215  if( summary & GpgME::Signature::SysError ){
2216  result2 += i18n("A system error occurred.");
2217  // if a system error occurred
2218  // we cannot trust any information
2219  // that was given back by the plug-in
2220  showKeyInfos = false;
2221  frameColor = SIG_FRAME_COL_YELLOW;
2222  }
2223 
2224  // test for red:
2225  if( summary & GpgME::Signature::KeyRevoked ){
2226  // this is red!
2227  result2 += i18n("One key has been revoked.");
2228  frameColor = SIG_FRAME_COL_RED;
2229  }
2230  if( summary & GpgME::Signature::Red ) {
2231  if( result2.isEmpty() )
2232  // Note:
2233  // Here we are work differently than KMail did before!
2234  //
2235  // The BAD case ( == sig *not* matching )
2236  // by definition does *not* show any key
2237  // information but just states that things are BAD.
2238  //
2239  // The reason for this: In this case ALL information
2240  // might be falsificated, we can NOT trust the data
2241  // in the body NOT the signature - so we don't show
2242  // any key/signature information at all!
2243  // (khz, according to LinuxTag 2002 meeting)
2244  showKeyInfos = false;
2245  frameColor = SIG_FRAME_COL_RED;
2246  }
2247  else
2248  result = "";
2249 
2250  if( SIG_FRAME_COL_GREEN == frameColor ) {
2251  result = i18n("Good signature.");
2252  } else if( SIG_FRAME_COL_RED == frameColor ) {
2253  result = i18n("<b>Bad</b> signature.");
2254  } else
2255  result = "";
2256 
2257  if( !result2.isEmpty() ) {
2258  if( !result.isEmpty() )
2259  result.append("<br />");
2260  result.append( result2 );
2261  }
2262  }
2263  /*
2264  // add i18n support for 3rd party plug-ins here:
2265  else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
2266 
2267  }
2268  */
2269  }
2270  return result;
2271 }
2272 
2273 
2274 static TQString writeSimpleSigstatHeader( const PartMetaData &block )
2275 {
2276  TQString html;
2277  html += "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
2278 
2279  if ( block.signClass == "signErr" ) {
2280  html += i18n( "Invalid signature." );
2281  } else if ( block.signClass == "signOkKeyBad" || block.signClass == "signWarn" ) {
2282  html += i18n( "Not enough information to check signature validity." );
2283  } else if ( block.signClass == "signOkKeyOk" ) {
2284  TQString addr;
2285  if ( !block.signerMailAddresses.isEmpty() )
2286  addr = block.signerMailAddresses.first();
2287  TQString name = addr;
2288  if ( name.isEmpty() )
2289  name = block.signer;
2290  if ( addr.isEmpty() ) {
2291  html += i18n( "Signature is valid." );
2292  } else {
2293  html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
2294  }
2295  } else {
2296  // should not happen
2297  html += i18n( "Unknown signature state" );
2298  }
2299  html += "</td><td align=\"right\">";
2300  html += "<a href=\"kmail:showSignatureDetails\">";
2301  html += i18n( "Show Details" );
2302  html += "</a></td></tr></table>";
2303  return html;
2304 }
2305 
2306 static TQString beginVerboseSigstatHeader()
2307 {
2308  return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
2309 }
2310 
2311 static TQString makeShowAuditLogLink( const GpgME::Error & err, const TQString & auditLog ) {
2312  if ( const unsigned int code = err.code() ) {
2313  if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
2314  //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not implemented)" << endl;
2315  return TQString();
2316  } else if ( code == GPG_ERR_NO_DATA ) {
2317  //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not available)" << endl;
2318  return i18n("No Audit Log available");
2319  } else {
2320  return i18n("Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) );
2321  }
2322  }
2323 
2324  if ( !auditLog.isEmpty() ) {
2325  KURL url;
2326  url.setProtocol( "kmail" );
2327  url.setPath( "showAuditLog" );
2328  url.addQueryItem( "log", auditLog );
2329 
2330  return "<a href=\"" + url.htmlURL() + "\">" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + "</a>";
2331  }
2332 
2333  return TQString();
2334 }
2335 
2336 static TQString endVerboseSigstatHeader( const PartMetaData & pmd )
2337 {
2338  TQString html;
2339  html += "</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
2340  html += "<a href=\"kmail:hideSignatureDetails\">";
2341  html += i18n( "Hide Details" );
2342  html += "</a></td></tr>";
2343  html += "<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
2344  html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
2345  html += "</td></tr></table>";
2346  return html;
2347 }
2348 
2349 TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
2350  const Kleo::CryptoBackend::Protocol * cryptProto,
2351  const TQString & fromAddress,
2352  partNode *node )
2353 {
2354  const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
2355  TQString signer = block.signer;
2356 
2357  TQString htmlStr, simpleHtmlStr;
2358  TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
2359  TQString cellPadding("cellpadding=\"1\"");
2360 
2361  if( block.isEncapsulatedRfc822Message )
2362  {
2363  htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
2364  "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
2365  if ( node )
2366  htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
2367  + i18n("Encapsulated message") + "</a>";
2368  else
2369  htmlStr += i18n("Encapsulated message");
2370  htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
2371  }
2372 
2373  if( block.isEncrypted )
2374  {
2375  htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
2376  "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
2377  if ( block.inProgress )
2378  htmlStr += i18n("Please wait while the message is being decrypted...");
2379  else if ( block.isDecryptable )
2380  htmlStr += i18n("Encrypted message");
2381  else {
2382  htmlStr += i18n("Encrypted message (decryption not possible)");
2383  if( !block.errorText.isEmpty() )
2384  htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
2385  }
2386  htmlStr += "</td></tr><tr class=\"encrB\"><td>";
2387  }
2388 
2389  if ( block.isSigned && block.inProgress )
2390  {
2391  block.signClass = "signInProgress";
2392  htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"signInProgress\">"
2393  "<tr class=\"signInProgressH\"><td dir=\"" + dir + "\">";
2394  htmlStr += i18n("Please wait while the signature is being verified...");
2395  htmlStr += "</td></tr><tr class=\"signInProgressB\"><td>";
2396  }
2397  simpleHtmlStr = htmlStr;
2398 
2399  if ( block.isSigned && !block.inProgress ) {
2400  TQStringList& blockAddrs( block.signerMailAddresses );
2401  // note: At the moment frameColor and showKeyInfos are
2402  // used for CMS only but not for PGP signatures
2403  // pending(khz): Implement usage of these for PGP sigs as well.
2404  int frameColor = SIG_FRAME_COL_UNDEF;
2405  bool showKeyInfos;
2406  bool onlyShowKeyURL = false;
2407  bool cannotCheckSignature = true;
2408  TQString statusStr = sigStatusToString( cryptProto,
2409  block.status_code,
2410  block.sigSummary,
2411  frameColor,
2412  showKeyInfos );
2413  // if needed fallback to english status text
2414  // that was reported by the plugin
2415  if( statusStr.isEmpty() )
2416  statusStr = block.status;
2417  if( block.technicalProblem )
2418  frameColor = SIG_FRAME_COL_YELLOW;
2419 
2420  switch( frameColor ){
2421  case SIG_FRAME_COL_RED:
2422  cannotCheckSignature = false;
2423  break;
2424  case SIG_FRAME_COL_YELLOW:
2425  cannotCheckSignature = true;
2426  break;
2427  case SIG_FRAME_COL_GREEN:
2428  cannotCheckSignature = false;
2429  break;
2430  }
2431 
2432  // compose the string for displaying the key ID
2433  // either as URL or not linked (for PGP)
2434  // note: Once we can start PGP key manager programs
2435  // from within KMail we could change this and
2436  // always show the URL. (khz, 2002/06/27)
2437  TQString startKeyHREF;
2438  if( isSMIME )
2439  startKeyHREF =
2440  TQString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
2441  .arg( cryptProto->displayName(),
2442  cryptProto->name(),
2443  block.keyId );
2444  TQString keyWithWithoutURL
2445  = isSMIME
2446  ? TQString("%1%2</a>")
2447  .arg( startKeyHREF,
2448  cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
2449  : "0x" + TQString::fromUtf8( block.keyId );
2450 
2451 
2452  // temporary hack: always show key infos!
2453  showKeyInfos = true;
2454 
2455  // Sorry for using 'black' as null color but .isValid()
2456  // checking with TQColor default c'tor did not work for
2457  // some reason.
2458  if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
2459 
2460  // new frame settings for CMS:
2461  // beautify the status string
2462  if( !statusStr.isEmpty() ) {
2463  statusStr.prepend("<i>");
2464  statusStr.append( "</i>");
2465  }
2466 
2467  // special color handling: S/MIME uses only green/yellow/red.
2468  switch( frameColor ) {
2469  case SIG_FRAME_COL_RED:
2470  block.signClass = "signErr";//"signCMSRed";
2471  onlyShowKeyURL = true;
2472  break;
2473  case SIG_FRAME_COL_YELLOW:
2474  if( block.technicalProblem )
2475  block.signClass = "signWarn";
2476  else
2477  block.signClass = "signOkKeyBad";//"signCMSYellow";
2478  break;
2479  case SIG_FRAME_COL_GREEN:
2480  block.signClass = "signOkKeyOk";//"signCMSGreen";
2481  // extra hint for green case
2482  // that email addresses in DN do not match fromAddress
2483  TQString greenCaseWarning;
2484  TQString msgFrom( KPIM::getEmailAddress(fromAddress) );
2485  TQString certificate;
2486  if( block.keyId.isEmpty() )
2487  certificate = i18n("certificate");
2488  else
2489  certificate = startKeyHREF + i18n("certificate") + "</a>";
2490  if( !blockAddrs.empty() ){
2491  if( blockAddrs.grep(
2492  msgFrom,
2493  false ).isEmpty() ) {
2494  greenCaseWarning =
2495  "<u>" +
2496  i18n("Warning:") +
2497  "</u> " +
2498  i18n("Sender's mail address is not stored "
2499  "in the %1 used for signing.").arg(certificate) +
2500  "<br />" +
2501  i18n("sender: ") +
2502  msgFrom +
2503  "<br />" +
2504  i18n("stored: ");
2505  // We cannot use TQt's join() function here but
2506  // have to join the addresses manually to
2507  // extract the mail addresses (without '<''>')
2508  // before including it into our string:
2509  bool bStart = true;
2510  for(TQStringList::ConstIterator it = blockAddrs.begin();
2511  it != blockAddrs.end(); ++it ){
2512  if( !bStart )
2513  greenCaseWarning.append(", <br />&nbsp; &nbsp;");
2514  bStart = false;
2515  greenCaseWarning.append( KPIM::getEmailAddress(*it) );
2516  }
2517  }
2518  } else {
2519  greenCaseWarning =
2520  "<u>" +
2521  i18n("Warning:") +
2522  "</u> " +
2523  i18n("No mail address is stored in the %1 used for signing, "
2524  "so we cannot compare it to the sender's address %2.")
2525  .arg(certificate,msgFrom);
2526  }
2527  if( !greenCaseWarning.isEmpty() ) {
2528  if( !statusStr.isEmpty() )
2529  statusStr.append("<br />&nbsp;<br />");
2530  statusStr.append( greenCaseWarning );
2531  }
2532  break;
2533  }
2534 
2535  TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
2536  "class=\"" + block.signClass + "\">"
2537  "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
2538  htmlStr += frame + beginVerboseSigstatHeader();
2539  simpleHtmlStr += frame;
2540  simpleHtmlStr += writeSimpleSigstatHeader( block );
2541  if( block.technicalProblem ) {
2542  htmlStr += block.errorText;
2543  }
2544  else if( showKeyInfos ) {
2545  if( cannotCheckSignature ) {
2546  htmlStr += i18n( "Not enough information to check "
2547  "signature. %1" )
2548  .arg( keyWithWithoutURL );
2549  }
2550  else {
2551 
2552  if (block.signer.isEmpty())
2553  signer = "";
2554  else {
2555  if( !blockAddrs.empty() ){
2556  TQString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
2557  signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
2558  }
2559  }
2560 
2561  if( block.keyId.isEmpty() ) {
2562  if( signer.isEmpty() || onlyShowKeyURL )
2563  htmlStr += i18n( "Message was signed with unknown key." );
2564  else
2565  htmlStr += i18n( "Message was signed by %1." )
2566  .arg( signer );
2567  } else {
2568  TQDateTime created = block.creationTime;
2569  if( created.isValid() ) {
2570  if( signer.isEmpty() ) {
2571  if( onlyShowKeyURL )
2572  htmlStr += i18n( "Message was signed with key %1." )
2573  .arg( keyWithWithoutURL );
2574  else
2575  htmlStr += i18n( "Message was signed on %1 with key %2." )
2576  .arg( TDEGlobal::locale()->formatDateTime( created ),
2577  keyWithWithoutURL );
2578  }
2579  else {
2580  if( onlyShowKeyURL )
2581  htmlStr += i18n( "Message was signed with key %1." )
2582  .arg( keyWithWithoutURL );
2583  else
2584  htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
2585  .arg( TDEGlobal::locale()->formatDateTime( created ),
2586  keyWithWithoutURL,
2587  signer );
2588  }
2589  }
2590  else {
2591  if( signer.isEmpty() || onlyShowKeyURL )
2592  htmlStr += i18n( "Message was signed with key %1." )
2593  .arg( keyWithWithoutURL );
2594  else
2595  htmlStr += i18n( "Message was signed by %2 with key %1." )
2596  .arg( keyWithWithoutURL,
2597  signer );
2598  }
2599  }
2600  }
2601  htmlStr += "<br />";
2602  if( !statusStr.isEmpty() ) {
2603  htmlStr += "&nbsp;<br />";
2604  htmlStr += i18n( "Status: " );
2605  htmlStr += statusStr;
2606  }
2607  } else {
2608  htmlStr += statusStr;
2609  }
2610  frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
2611  htmlStr += endVerboseSigstatHeader( block ) + frame;
2612  simpleHtmlStr += frame;
2613 
2614  } else {
2615 
2616  // old frame settings for PGP:
2617 
2618  if( block.signer.isEmpty() || block.technicalProblem ) {
2619  block.signClass = "signWarn";
2620  TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
2621  "class=\"" + block.signClass + "\">"
2622  "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
2623  htmlStr += frame + beginVerboseSigstatHeader();
2624  simpleHtmlStr += frame;
2625  simpleHtmlStr += writeSimpleSigstatHeader( block );
2626  if( block.technicalProblem ) {
2627  htmlStr += block.errorText;
2628  }
2629  else {
2630  if( !block.keyId.isEmpty() ) {
2631  TQDateTime created = block.creationTime;
2632  if ( created.isValid() )
2633  htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
2634  .arg( TDEGlobal::locale()->formatDateTime( created ),
2635  keyWithWithoutURL );
2636  else
2637  htmlStr += i18n( "Message was signed with unknown key %1." )
2638  .arg( keyWithWithoutURL );
2639  }
2640  else
2641  htmlStr += i18n( "Message was signed with unknown key." );
2642  htmlStr += "<br />";
2643  htmlStr += i18n( "The validity of the signature cannot be "
2644  "verified." );
2645  if( !statusStr.isEmpty() ) {
2646  htmlStr += "<br />";
2647  htmlStr += i18n( "Status: " );
2648  htmlStr += "<i>";
2649  htmlStr += statusStr;
2650  htmlStr += "</i>";
2651  }
2652  }
2653  frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
2654  htmlStr += endVerboseSigstatHeader( block ) + frame;
2655  simpleHtmlStr += frame;
2656  }
2657  else
2658  {
2659  // HTMLize the signer's user id and create mailto: link
2660  signer = KMMessage::quoteHtmlChars( signer, true );
2661  signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
2662 
2663  if (block.isGoodSignature) {
2664  if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
2665  block.signClass = "signOkKeyBad";
2666  else
2667  block.signClass = "signOkKeyOk";
2668  TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
2669  "class=\"" + block.signClass + "\">"
2670  "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
2671  htmlStr += frame + beginVerboseSigstatHeader();
2672  simpleHtmlStr += frame;
2673  simpleHtmlStr += writeSimpleSigstatHeader( block );
2674  if( !block.keyId.isEmpty() )
2675  htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
2676  .arg( keyWithWithoutURL,
2677  signer );
2678  else
2679  htmlStr += i18n( "Message was signed by %1." ).arg( signer );
2680  htmlStr += "<br />";
2681 
2682  switch( block.keyTrust )
2683  {
2684  case Kpgp::KPGP_VALIDITY_UNKNOWN:
2685  htmlStr += i18n( "The signature is valid, but the key's "
2686  "validity is unknown." );
2687  break;
2688  case Kpgp::KPGP_VALIDITY_MARGINAL:
2689  htmlStr += i18n( "The signature is valid and the key is "
2690  "marginally trusted." );
2691  break;
2692  case Kpgp::KPGP_VALIDITY_FULL:
2693  htmlStr += i18n( "The signature is valid and the key is "
2694  "fully trusted." );
2695  break;
2696  case Kpgp::KPGP_VALIDITY_ULTIMATE:
2697  htmlStr += i18n( "The signature is valid and the key is "
2698  "ultimately trusted." );
2699  break;
2700  default:
2701  htmlStr += i18n( "The signature is valid, but the key is "
2702  "untrusted." );
2703  }
2704  frame = "</td></tr>"
2705  "<tr class=\"" + block.signClass + "B\"><td>";
2706  htmlStr += endVerboseSigstatHeader( block ) + frame;
2707  simpleHtmlStr += frame;
2708  }
2709  else
2710  {
2711  block.signClass = "signErr";
2712  TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
2713  "class=\"" + block.signClass + "\">"
2714  "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
2715  htmlStr += frame + beginVerboseSigstatHeader();
2716  simpleHtmlStr += frame;
2717  simpleHtmlStr += writeSimpleSigstatHeader( block );
2718  if( !block.keyId.isEmpty() )
2719  htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
2720  .arg( keyWithWithoutURL,
2721  signer );
2722  else
2723  htmlStr += i18n( "Message was signed by %1." ).arg( signer );
2724  htmlStr += "<br />";
2725  htmlStr += i18n("Warning: The signature is bad.");
2726  frame = "</td></tr>"
2727  "<tr class=\"" + block.signClass + "B\"><td>";
2728  htmlStr += endVerboseSigstatHeader( block ) + frame;
2729  simpleHtmlStr += frame;
2730  }
2731  }
2732  }
2733  }
2734 
2735  if ( mReader->showSignatureDetails() )
2736  return htmlStr;
2737  return simpleHtmlStr;
2738 }
2739 
2740 TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
2741 {
2742  TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
2743 
2744  TQString htmlStr;
2745 
2746  if (block.isSigned) {
2747  htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
2748  htmlStr += "<td dir=\"" + dir + "\">" +
2749  i18n( "End of signed message" ) +
2750  "</td></tr></table>";
2751  }
2752 
2753  if (block.isEncrypted) {
2754  htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
2755  i18n( "End of encrypted message" ) +
2756  "</td></tr></table>";
2757  }
2758 
2759  if( block.isEncapsulatedRfc822Message )
2760  {
2761  htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
2762  i18n( "End of encapsulated message" ) +
2763  "</td></tr></table>";
2764  }
2765 
2766  return htmlStr;
2767 }
2768 
2769 //-----------------------------------------------------------------------------
2770 
2771 void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
2772 {
2773  if ( !mReader )
2774  return;
2775 
2776  htmlWriter()->queue( TQString( "<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
2777 }
2778 
2779 //-----------------------------------------------------------------------------
2780 
2781 void ObjectTreeParser::writeAttachmentMarkFooter()
2782 {
2783  if ( !mReader )
2784  return;
2785 
2786  htmlWriter()->queue( TQString( "</div>" ) );
2787 }
2788 
2789 //-----------------------------------------------------------------------------
2790 void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
2791  const TQString& fromAddress )
2792 {
2793  KMMsgSignatureState dummy1;
2794  KMMsgEncryptionState dummy2;
2795  writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
2796 }
2797 
2798 //-----------------------------------------------------------------------------
2799 void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
2800  const TQString& fromAddress,
2801  KMMsgSignatureState& inlineSignatureState,
2802  KMMsgEncryptionState& inlineEncryptionState,
2803  bool decorate )
2804 {
2805  bool goodSignature = false;
2806  Kpgp::Module* pgp = Kpgp::Module::getKpgp();
2807  assert(pgp != 0);
2808  bool isPgpMessage = false; // true if the message contains at least one
2809  // PGP MESSAGE or one PGP SIGNED MESSAGE block
2810  TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
2811  TQString headerStr = TQString("<div dir=\"%1\">").arg(dir);
2812 
2813  inlineSignatureState = KMMsgNotSigned;
2814  inlineEncryptionState = KMMsgNotEncrypted;
2815  TQPtrList<Kpgp::Block> pgpBlocks;
2816  TQStrList nonPgpBlocks;
2817  if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
2818  {
2819  bool isEncrypted = false, isSigned = false;
2820  bool fullySignedOrEncrypted = true;
2821  bool firstNonPgpBlock = true;
2822  bool couldDecrypt = false;
2823  TQString signer;
2824  TQCString keyId;
2825  TQString decryptionError;
2826  Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
2827 
2828  TQPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
2829 
2830  TQStrListIterator npbit( nonPgpBlocks );
2831 
2832  TQString htmlStr;
2833  for( ; *pbit != 0; ++pbit, ++npbit )
2834  {
2835  // insert the next Non-OpenPGP block
2836  TQCString str( *npbit );
2837  if( !str.isEmpty() ) {
2838  htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2839  //kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
2840  // << "'" << endl;
2841  // treat messages with empty lines before the first clearsigned
2842  // block as fully signed/encrypted
2843  if( firstNonPgpBlock ) {
2844  // check whether str only consists of \n
2845  for( TQCString::ConstIterator c = str.begin(); *c; ++c ) {
2846  if( *c != '\n' ) {
2847  fullySignedOrEncrypted = false;
2848  break;
2849  }
2850  }
2851  }
2852  else {
2853  fullySignedOrEncrypted = false;
2854  }
2855  }
2856  firstNonPgpBlock = false;
2857 
2858  //htmlStr += "<br>";
2859 
2860  Kpgp::Block* block = *pbit;
2861  if( ( block->type() == Kpgp::PgpMessageBlock &&
2862  // ### Workaround for bug 56693
2863  !kmkernel->contextMenuShown() ) ||
2864  ( block->type() == Kpgp::ClearsignedBlock ) )
2865  {
2866  isPgpMessage = true;
2867  if( block->type() == Kpgp::PgpMessageBlock )
2868  {
2869  if ( mReader )
2870  emit mReader->noDrag();
2871  // try to decrypt this OpenPGP block
2872  couldDecrypt = block->decrypt();
2873  isEncrypted = block->isEncrypted();
2874  if (!couldDecrypt) {
2875  decryptionError = pgp->lastErrorMsg();
2876  }
2877  }
2878  else
2879  {
2880  // try to verify this OpenPGP block
2881  block->verify();
2882  }
2883 
2884  isSigned = block->isSigned();
2885  if( isSigned )
2886  {
2887  keyId = block->signatureKeyId();
2888  signer = block->signatureUserId();
2889  if( !signer.isEmpty() )
2890  {
2891  goodSignature = block->goodSignature();
2892 
2893  if( !keyId.isEmpty() ) {
2894  keyTrust = pgp->keyTrust( keyId );
2895  Kpgp::Key* key = pgp->publicKey( keyId );
2896  if ( key ) {
2897  // Use the user ID from the key because this one
2898  // is charset safe.
2899  signer = key->primaryUserID();
2900  }
2901  }
2902  else
2903  // This is needed for the PGP 6 support because PGP 6 doesn't
2904  // print the key id of the signing key if the key is known.
2905  keyTrust = pgp->keyTrust( signer );
2906  }
2907  }
2908 
2909  if( isSigned )
2910  inlineSignatureState = KMMsgPartiallySigned;
2911  if( isEncrypted )
2912  inlineEncryptionState = KMMsgPartiallyEncrypted;
2913 
2914  PartMetaData messagePart;
2915 
2916  messagePart.isSigned = isSigned;
2917  messagePart.technicalProblem = false;
2918  messagePart.isGoodSignature = goodSignature;
2919  messagePart.isEncrypted = isEncrypted;
2920  messagePart.isDecryptable = couldDecrypt;
2921  messagePart.decryptionError = decryptionError;
2922  messagePart.signer = signer;
2923  messagePart.keyId = keyId;
2924  messagePart.keyTrust = keyTrust;
2925 
2926  htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
2927 
2928  htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
2929  htmlStr += writeSigstatFooter( messagePart );
2930  }
2931  else // block is neither message block nor clearsigned block
2932  htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
2933  decorate );
2934  }
2935 
2936  // add the last Non-OpenPGP block
2937  TQCString str( nonPgpBlocks.last() );
2938  if( !str.isEmpty() ) {
2939  htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2940  // Even if the trailing Non-OpenPGP block isn't empty we still
2941  // consider the message part fully signed/encrypted because else
2942  // all inline signed mailing list messages would only be partially
2943  // signed because of the footer which is often added by the mailing
2944  // list software. IK, 2003-02-15
2945  }
2946  if( fullySignedOrEncrypted ) {
2947  if( inlineSignatureState == KMMsgPartiallySigned )
2948  inlineSignatureState = KMMsgFullySigned;
2949  if( inlineEncryptionState == KMMsgPartiallyEncrypted )
2950  inlineEncryptionState = KMMsgFullyEncrypted;
2951  }
2952  htmlWriter()->queue( htmlStr );
2953  }
2954  else
2955  htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
2956 }
2957 
2958 
2959 TQString ObjectTreeParser::quotedHTML( const TQString& s, bool decorate )
2960 {
2961  assert( mReader );
2962  assert( cssHelper() );
2963 
2964  int convertFlags = LinkLocator::PreserveSpaces;
2965  if ( decorate && GlobalSettings::self()->showEmoticons() ) {
2966  convertFlags |= LinkLocator::ReplaceSmileys;
2967  }
2968  TQString htmlStr;
2969  const TQString normalStartTag = cssHelper()->nonQuotedFontTag();
2970  TQString quoteFontTag[3];
2971  TQString deepQuoteFontTag[3];
2972  for ( int i = 0 ; i < 3 ; ++i ) {
2973  quoteFontTag[i] = cssHelper()->quoteFontTag( i );
2974  deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
2975  }
2976  const TQString normalEndTag = "</div>";
2977  const TQString quoteEnd = "</div>";
2978 
2979  unsigned int pos, beg;
2980  const unsigned int length = s.length();
2981 
2982  // skip leading empty lines
2983  for ( pos = 0; pos < length && s.at(pos) <= TQChar(' '); pos++ ) { ; }
2984  while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
2985  beg = pos;
2986 
2987  int currQuoteLevel = -2; // -2 == no previous lines
2988  bool curHidden = false; // no hide any block
2989 
2990  while (beg<length)
2991  {
2992  TQString line;
2993 
2994  /* search next occurrence of '\n' */
2995  pos = s.find('\n', beg, false);
2996  if (pos == (unsigned int)(-1))
2997  pos = length;
2998 
2999  line = s.mid(beg,pos-beg);
3000  beg = pos+1;
3001 
3002  /* calculate line's current quoting depth */
3003  int actQuoteLevel = -1;
3004 
3005  if ( GlobalSettings::self()->showExpandQuotesMark() )
3006  {
3007  // Cache Icons
3008  if ( mCollapseIcon.isEmpty() ) {
3009  mCollapseIcon= LinkLocator::pngToDataUrl(
3010  TDEGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
3011  }
3012  if ( mExpandIcon.isEmpty() )
3013  mExpandIcon= LinkLocator::pngToDataUrl(
3014  TDEGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
3015  }
3016 
3017  for (unsigned int p=0; p<line.length(); p++) {
3018  switch (line[p].latin1()) {
3019  case '>':
3020  case '|':
3021  actQuoteLevel++;
3022  break;
3023  case ' ': // spaces and tabs are allowed between the quote markers
3024  case '\t':
3025  case '\r':
3026  break;
3027  default: // stop quoting depth calculation
3028  p = line.length();
3029  break;
3030  }
3031  } /* for() */
3032 
3033  bool actHidden = false;
3034  TQString textExpand;
3035 
3036  // This quoted line needs be hiden
3037  if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
3038  && mReader->mLevelQuote <= ( actQuoteLevel ) )
3039  actHidden = true;
3040 
3041  if ( actQuoteLevel != currQuoteLevel ) {
3042  /* finish last quotelevel */
3043  if (currQuoteLevel == -1)
3044  htmlStr.append( normalEndTag );
3045  else if ( currQuoteLevel >= 0 && !curHidden )
3046  htmlStr.append( quoteEnd );
3047 
3048  /* start new quotelevel */
3049  if (actQuoteLevel == -1)
3050  htmlStr += normalStartTag;
3051  else
3052  {
3053  if ( GlobalSettings::self()->showExpandQuotesMark() )
3054  {
3055  if ( actHidden )
3056  {
3057  //only show the QuoteMark when is the first line of the level hidden
3058  if ( !curHidden )
3059  {
3060  //Expand all quotes
3061  htmlStr += "<div class=\"quotelevelmark\" >" ;
3062  htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
3063  "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3064  .arg(-1)
3065  .arg( mExpandIcon );
3066  htmlStr += "</div><br/>";
3067  htmlStr += quoteEnd;
3068  }
3069  }else {
3070  htmlStr += "<div class=\"quotelevelmark\" >" ;
3071  htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
3072  "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3073  .arg(actQuoteLevel)
3074  .arg( mCollapseIcon);
3075  htmlStr += "</div>";
3076  if ( actQuoteLevel < 3 )
3077  htmlStr += quoteFontTag[actQuoteLevel];
3078  else
3079  htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3080  }
3081  } else
3082  if ( actQuoteLevel < 3 )
3083  htmlStr += quoteFontTag[actQuoteLevel];
3084  else
3085  htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3086  }
3087  currQuoteLevel = actQuoteLevel;
3088  }
3089  curHidden = actHidden;
3090 
3091 
3092  if ( !actHidden )
3093  {
3094  // don't write empty <div ...></div> blocks (they have zero height)
3095  // ignore ^M DOS linebreaks
3096  if( !line.replace('\015', "").isEmpty() )
3097  {
3098  htmlStr +=TQString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
3099  htmlStr += LinkLocator::convertToHtml( line, convertFlags );
3100  htmlStr += TQString( "</div>" );
3101  }
3102  else
3103  htmlStr += "<br>";
3104  }
3105  } /* while() */
3106 
3107  /* really finish the last quotelevel */
3108  if (currQuoteLevel == -1)
3109  htmlStr.append( normalEndTag );
3110  else
3111  htmlStr.append( quoteEnd );
3112 
3113  return htmlStr;
3114 }
3115 
3116 
3117 
3118  const TQTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
3119  assert( node );
3120  if ( mReader && mReader->overrideCodec() )
3121  return mReader->overrideCodec();
3122  return node->msgPart().codec();
3123  }
3124 
3125 #ifdef MARCS_DEBUG
3126  void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
3127  size_t len ) {
3128  assert( filename );
3129 
3130  TQFile f( filename );
3131  if ( f.open( IO_WriteOnly ) ) {
3132  if ( start ) {
3133  TQDataStream ds( &f );
3134  ds.writeRawBytes( start, len );
3135  }
3136  f.close(); // If data is 0 we just create a zero length file.
3137  }
3138  }
3139 #endif // !NDEBUG
3140 
3141 
3142 } // namespace KMail
This is a Mime Message.
Definition: kmmessage.h:68
static TQString quoteHtmlChars(const TQString &str, bool removeLineBreaks=false)
Quotes the following characters which have a special meaning in HTML: '<' '>' '&' '"'....
Definition: kmmessage.cpp:3804
static TQCString html2source(const TQCString &src)
Convert '<' into "<" resp.
Definition: kmmessage.cpp:3389
static TQString encodeMailtoUrl(const TQString &str)
Encodes an email address as mailto URL.
Definition: kmmessage.cpp:3462
This class implements a "reader window", that is a window used for reading or viewing messages.
Definition: kmreaderwin.h:75
KMail::HtmlWriter * htmlWriter()
Return the HtmlWriter connected to the TDEHTMLPart we use.
Definition: kmreaderwin.h:255
const KMail::AttachmentStrategy * attachmentStrategy() const
Get/set the message attachment strategy.
Definition: kmreaderwin.h:121
TQCString CString(const DwString &str)
Construct a TQCString from a DwString.
Definition: util.cpp:113
folderdiaquotatab.h
Definition: aboutdata.cpp:40