• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

messageviewer

  • sources
  • kde-4.12
  • kdepim
  • messageviewer
  • viewer
objecttreeparser.cpp
Go to the documentation of this file.
1 /* -*- mode: C++; c-file-style: "gnu" -*-
2  objecttreeparser.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6  Copyright (C) 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7  Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8 
9  KMail is free software; you can redistribute it and/or modify it
10  under the terms of the GNU General Public License, version 2, as
11  published by the Free Software Foundation.
12 
13  KMail is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22  In addition, as a special exception, the copyright holders give
23  permission to link the code of this program with any edition of
24  the Qt library by Trolltech AS, Norway (or with modified versions
25  of Qt that use the same license as Qt), and distribute linked
26  combinations including the two. You must obey the GNU General
27  Public License in all respects for all of the code used other than
28  Qt. If you modify this file, you may extend this exception to
29  your version of the file, but you are not obligated to do so. If
30  you do not wish to do so, delete this exception statement from
31  your version.
32 */
33 
34 // MessageViewer includes
35 
36 
37 #include "objecttreeparser.h"
38 
39 #include "objecttreeparser_p.h"
40 #include "objecttreesourceif.h"
41 #include "utils/autoqpointer.h"
42 #include "viewer/viewer_p.h"
43 #include "partmetadata.h"
44 #include "attachmentstrategy.h"
45 #include "interfaces/htmlwriter.h"
46 #include "widgets/htmlstatusbar.h"
47 #include "csshelper.h"
48 #include "bodypartformatter.h"
49 #include "viewer/bodypartformatterfactory.h"
50 #include "viewer/partnodebodypart.h"
51 #include "interfaces/bodypartformatter.h"
52 #include "settings/globalsettings.h"
53 #include "utils/util.h"
54 #include "kleojobexecutor.h"
55 #include "viewer/nodehelper.h"
56 #include "utils/iconnamecache.h"
57 #include "viewer/htmlquotecolorer.h"
58 #include "chiasmuskeyselector.h"
59 
60 // KDEPIM includes
61 #include <messagecore/utils/stringutil.h>
62 #include <kleo/specialjob.h>
63 #include <kleo/cryptobackendfactory.h>
64 #include <kleo/decryptverifyjob.h>
65 #include <kleo/verifydetachedjob.h>
66 #include <kleo/verifyopaquejob.h>
67 #include <kleo/keylistjob.h>
68 #include <kleo/importjob.h>
69 #include <kleo/dn.h>
70 #include <libkpgp/kpgpblock.h>
71 #include <libkpgp/kpgp.h>
72 
73 // KDEPIMLIBS includes
74 #include <kpimutils/email.h>
75 #include <kpimutils/linklocator.h>
76 #include <gpgme++/importresult.h>
77 #include <gpgme++/decryptionresult.h>
78 #include <gpgme++/key.h>
79 #include <gpgme++/keylistresult.h>
80 #include <gpgme.h>
81 #include <kmime/kmime_message.h>
82 #include <kmime/kmime_util.h>
83 
84 // KDE includes
85 #include <kdebug.h>
86 #include <klocale.h>
87 #include <kmimetype.h>
88 #include <kglobal.h>
89 #include <ktemporaryfile.h>
90 #include <kstandarddirs.h>
91 #include <kiconloader.h>
92 #include <kcodecs.h>
93 #include <kconfiggroup.h>
94 #include <kstyle.h>
95 
96 // Qt includes
97 #include <QApplication>
98 #include <QTextDocument>
99 #include <QDir>
100 #include <QFile>
101 #include <QTextCodec>
102 #include <QByteArray>
103 #include <QBuffer>
104 #include <QPixmap>
105 #include <QPainter>
106 #include <QPointer>
107 #ifdef KDEPIM_NO_WEBKIT
108 # include <QTextBrowser>
109 #else
110 # include <QtWebKit/QWebPage>
111 # include <QtWebKit/QWebFrame>
112 #endif
113 
114 // other includes
115 #include <sstream>
116 #include <sys/stat.h>
117 #include <sys/types.h>
118 #include <unistd.h>
119 #include <memory>
120 #include <messagecore/helpers/nodehelper.h>
121 #include <qtextdocument.h>
122 
123 using KPIMUtils::LinkLocator;
124 using namespace MessageViewer;
125 using namespace MessageCore;
126 
127 // A small class that eases temporary CryptPlugWrapper changes:
128 class ObjectTreeParser::CryptoProtocolSaver {
129  ObjectTreeParser * otp;
130  const Kleo::CryptoBackend::Protocol * protocol;
131 public:
132  CryptoProtocolSaver( ObjectTreeParser * _otp, const Kleo::CryptoBackend::Protocol* _w )
133  : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
134  {
135  if ( otp )
136  otp->setCryptoProtocol( _w );
137  }
138 
139  ~CryptoProtocolSaver() {
140  if ( otp )
141  otp->setCryptoProtocol( protocol );
142  }
143 };
144 
145 ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser *topLevelParser,
146  bool showOnlyOneMimePart, bool keepEncryptions,
147  bool includeSignatures,
148  const AttachmentStrategy * strategy )
149  : mSource( topLevelParser->mSource ),
150  mNodeHelper( topLevelParser->mNodeHelper ),
151  mTopLevelContent( topLevelParser->mTopLevelContent ),
152  mCryptoProtocol( topLevelParser->mCryptoProtocol ),
153  mShowOnlyOneMimePart( showOnlyOneMimePart ),
154  mKeepEncryptions( keepEncryptions ),
155  mIncludeSignatures( includeSignatures ),
156  mHasPendingAsyncJobs( false ),
157  mAllowAsync( topLevelParser->mAllowAsync ),
158  mShowRawToltecMail( false ),
159  mAttachmentStrategy( strategy )
160 {
161  init();
162 }
163 
164 ObjectTreeParser::ObjectTreeParser( ObjectTreeSourceIf *source,
165  MessageViewer::NodeHelper* nodeHelper,
166  const Kleo::CryptoBackend::Protocol * protocol,
167  bool showOnlyOneMimePart, bool keepEncryptions,
168  bool includeSignatures,
169  const AttachmentStrategy * strategy )
170  : mSource( source ),
171  mNodeHelper( nodeHelper ),
172  mTopLevelContent( 0 ),
173  mCryptoProtocol( protocol ),
174  mShowOnlyOneMimePart( showOnlyOneMimePart ),
175  mKeepEncryptions( keepEncryptions ),
176  mIncludeSignatures( includeSignatures ),
177  mHasPendingAsyncJobs( false ),
178  mAllowAsync( false ),
179  mShowRawToltecMail( false ),
180  mAttachmentStrategy( strategy )
181 {
182  init();
183 }
184 
185 void ObjectTreeParser::init()
186 {
187  assert( mSource );
188  if ( !attachmentStrategy() )
189  mAttachmentStrategy = mSource->attachmentStrategy();
190 
191  if ( !mNodeHelper ) {
192  mNodeHelper = new NodeHelper();
193  mDeleteNodeHelper = true;
194  } else {
195  mDeleteNodeHelper = false;
196  }
197 }
198 
199 ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser & other )
200  : mSource( other.mSource ),
201  mNodeHelper( other.nodeHelper() ), //TODO(Andras) hm, review what happens if mDeleteNodeHelper was true in the source
202  mTopLevelContent( other.mTopLevelContent ),
203  mCryptoProtocol( other.cryptoProtocol() ),
204  mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
205  mKeepEncryptions( other.keepEncryptions() ),
206  mIncludeSignatures( other.includeSignatures() ),
207  mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ),
208  mAllowAsync( other.allowAsync() ),
209  mAttachmentStrategy( other.attachmentStrategy() ),
210  mDeleteNodeHelper( false ) // TODO see above
211 {
212 
213 }
214 
215 ObjectTreeParser::~ObjectTreeParser()
216 {
217  if ( mDeleteNodeHelper ) {
218  delete mNodeHelper;
219  mNodeHelper = 0;
220  }
221 }
222 
223 void ObjectTreeParser::copyContentFrom( const ObjectTreeParser *other )
224 {
225  mRawDecryptedBody += other->rawDecryptedBody();
226  mPlainTextContent += other->plainTextContent();
227  mHtmlContent += other->htmlContent();
228  if ( !other->plainTextContentCharset().isEmpty() ) {
229  mPlainTextContentCharset = other->plainTextContentCharset();
230  }
231  if ( !other->htmlContentCharset().isEmpty() ) {
232  mHtmlContentCharset = other->htmlContentCharset();
233  }
234 }
235 
236 void ObjectTreeParser::createAndParseTempNode( KMime::Content* parentNode, const char* content, const char* cntDesc )
237 {
238 // kDebug() << "CONTENT: " << QByteArray( content ).left( 100 ) << " CNTDESC: " << cntDesc;
239 
240  KMime::Content *newNode = new KMime::Content();
241  newNode->setContent( KMime::CRLFtoLF( content ) );
242  newNode->parse();
243 /*
244  kDebug() << "MEDIATYPE: " << newNode->contentType()->mediaType() << newNode->contentType()->mimeType() ;
245  kDebug() << "DECODEDCONTENT: " << newNode->decodedContent().left(400);
246  kDebug() << "ENCODEDCONTENT: " << newNode->encodedContent().left(400);
247  kDebug() << "BODY: " << newNode->body().left(400);
248  */
249 
250  if ( !newNode->head().isEmpty() ) {
251  newNode->contentDescription()->from7BitString( cntDesc );
252  }
253  mNodeHelper->attachExtraContent( parentNode, newNode );
254 
255  ObjectTreeParser otp( this );
256  otp.parseObjectTreeInternal( newNode );
257  copyContentFrom( &otp );
258 }
259 
260 
261 //-----------------------------------------------------------------------------
262 
263 void ObjectTreeParser::parseObjectTree( KMime::Content * node )
264 {
265  mTopLevelContent = node;
266  parseObjectTreeInternal( node );
267 }
268 
269 void ObjectTreeParser::parseObjectTreeInternal( KMime::Content * node )
270 {
271  if ( !node )
272  return;
273 
274  // reset pending async jobs state (we'll rediscover pending jobs as we go)
275  mHasPendingAsyncJobs = false;
276 
277  // reset "processed" flags for...
278  if ( showOnlyOneMimePart() ) {
279  // ... this node and all descendants
280  mNodeHelper->setNodeUnprocessed( node, false );
281  if ( MessageCore::NodeHelper::firstChild( node ) ) {
282  mNodeHelper->setNodeUnprocessed( node, true );
283  }
284  } else if ( !node->parent() ) {
285  // ...this node and all it's siblings and descendants
286  mNodeHelper->setNodeUnprocessed( node, true );
287  }
288 
289  // Make sure the whole content is relative, so that nothing is painted over the header
290  // if a malicious message uses absolute positioning.
291  // Also force word wrapping, which is useful for printing, see https://issues.kolab.org/issue3992.
292  bool isRoot = node->isTopLevel();
293  if ( isRoot && htmlWriter() )
294  htmlWriter()->queue( QLatin1String("<div style=\"position: relative; word-wrap: break-word\">\n") );
295 
296  for( ; node ; node = MessageCore::NodeHelper::nextSibling( node ) )
297  {
298  if ( mNodeHelper->nodeProcessed( node ) ) {
299  continue;
300  }
301 
302  ProcessResult processResult( mNodeHelper );
303 
304  KMime::ContentIndex contentIndex = node->index();
305  if ( htmlWriter() /*&& contentIndex.isValid()*/ )
306  htmlWriter()->queue( QString::fromLatin1("<a name=\"att%1\"></a>").arg( contentIndex.toString() ) );
307 
308  QByteArray mediaType( "text" );
309  QByteArray subType( "plain" );
310  if ( node->contentType( false ) && !node->contentType()->mediaType().isEmpty() &&
311  !node->contentType()->subType().isEmpty() ) {
312  mediaType = node->contentType()->mediaType();
313  subType = node->contentType()->subType();
314  }
315 
316  // First, try if an external plugin can handle this MIME part
317  if ( const Interface::BodyPartFormatter * formatter
318  = BodyPartFormatterFactory::instance()->createFor( mediaType, subType ) ) {
319  PartNodeBodyPart part( mTopLevelContent, node, mNodeHelper, codecFor( node ) );
320  // Set the default display strategy for this body part relying on the
321  // identity of Interface::BodyPart::Display and AttachmentStrategy::Display
322  part.setDefaultDisplay( (Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
323 
324  writeAttachmentMarkHeader( node );
325  mNodeHelper->setNodeDisplayedEmbedded( node, true );
326 
327  QObject * asyncResultObserver = allowAsync() ? mSource->sourceObject() : 0;
328  const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), asyncResultObserver );
329  switch ( result ) {
330  case Interface::BodyPartFormatter::AsIcon:
331  processResult.setNeverDisplayInline( true );
332  mNodeHelper->setNodeDisplayedEmbedded( node, false );
333  // fall through:
334  case Interface::BodyPartFormatter::Failed:
335  defaultHandling( node, processResult );
336  break;
337  case Interface::BodyPartFormatter::Ok:
338  case Interface::BodyPartFormatter::NeedContent:
339  // FIXME: incomplete content handling
340  ;
341  }
342 
343  writeAttachmentMarkFooter();
344 
345  // No external plugin can handle the MIME part, handle it internally
346  } else {
347  const BodyPartFormatter * bpf
348  = BodyPartFormatter::createFor( mediaType, subType );
349  if ( !bpf ) {
350  kFatal() << "THIS SHOULD NO LONGER HAPPEN:" << mediaType << '/' << subType;
351  }
352  writeAttachmentMarkHeader( node );
353  if ( bpf && !bpf->process( this, node, processResult ) ) {
354  defaultHandling( node, processResult );
355  }
356  writeAttachmentMarkFooter();
357  }
358  mNodeHelper->setNodeProcessed( node, false);
359 
360  // adjust signed/encrypted flags if inline PGP was found
361  processResult.adjustCryptoStatesOfNode( node );
362 
363  if ( showOnlyOneMimePart() )
364  break;
365  }
366 
367  if ( isRoot && htmlWriter() )
368  htmlWriter()->queue( QLatin1String("</div>\n") );
369 }
370 
371 void ObjectTreeParser::defaultHandling( KMime::Content * node, ProcessResult & result ) {
372  // ### (mmutz) default handling should go into the respective
373  // ### bodypartformatters.
374  if ( !htmlWriter() ) {
375  kWarning() << "no htmlWriter()";
376  return;
377  }
378 
379  // always show images in multipart/related when showing in html, not with an additional icon
380  if ( result.isImage() && node->parent() &&
381  node->parent()->contentType()->subType() == "related" && mSource->htmlMail() && !showOnlyOneMimePart() ) {
382  QString fileName = mNodeHelper->writeNodeToTempFile( node );
383  QString href = QLatin1String("file:///") + fileName;
384  QByteArray cid = node->contentID()->identifier();
385  htmlWriter()->embedPart( cid, href );
386  nodeHelper()->setNodeDisplayedEmbedded( node, true );
387  return;
388  }
389 
390  if ( node->contentType()->mimeType() == QByteArray( "application/octet-stream" ) &&
391  ( node->contentType()->name().endsWith( QString::fromLatin1( "p7m" ) ) ||
392  node->contentType()->name().endsWith( QString::fromLatin1( "p7s" ) ) ||
393  node->contentType()->name().endsWith( QString::fromLatin1( "p7c" ) )
394  ) &&
395  processApplicationPkcs7MimeSubtype( node, result ) ) {
396  return;
397  }
398 
399  const AttachmentStrategy *const as = attachmentStrategy();
400  if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None &&
401  !showOnlyOneMimePart() &&
402  node->parent() /* message is not an attachment */ ) {
403  mNodeHelper->setNodeDisplayedHidden( node, true );
404  return;
405  }
406 
407  bool asIcon = true;
408  if ( !result.neverDisplayInline() )
409  if ( as )
410  asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
411 
412  // Show it inline if showOnlyOneMimePart(), which means the user clicked the image
413  // in the message structure viewer manually, and therefore wants to see the full image
414  if ( result.isImage() && showOnlyOneMimePart() && !result.neverDisplayInline() )
415  asIcon = false;
416 
417  // neither image nor text -> show as icon
418  if ( !result.isImage()
419  && !node->contentType()->isText() )
420  asIcon = true;
421 
422 /*FIXME(Andras) port it
423  // if the image is not complete do not try to show it inline
424  if ( result.isImage() && !node->msgPart().isComplete() )
425  asIcon = true;
426  */
427 
428  if ( asIcon ) {
429  if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) ||
430  showOnlyOneMimePart() ) {
431  // Write the node as icon only
432  writePartIcon( node );
433  } else {
434  mNodeHelper->setNodeDisplayedHidden( node, true );
435  }
436  } else if ( result.isImage() ) {
437  // Embed the image
438  mNodeHelper->setNodeDisplayedEmbedded( node, true );
439  writePartIcon( node, true );
440  } else {
441  mNodeHelper->setNodeDisplayedEmbedded( node, true );
442  writeBodyString( node->decodedContent(),
443  NodeHelper::fromAsString( node ),
444  codecFor( node ), result, false );
445  }
446  // end of ###
447 }
448 
449 void ProcessResult::adjustCryptoStatesOfNode( KMime::Content * node ) const {
450  if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
451  ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
452  mNodeHelper->setSignatureState( node, inlineSignatureState() );
453  mNodeHelper->setEncryptionState( node, inlineEncryptionState() );
454  }
455 }
456 
460 
461 static int signatureToStatus( const GpgME::Signature &sig )
462 {
463  switch ( sig.status().code() ) {
464  case GPG_ERR_NO_ERROR:
465  return GPGME_SIG_STAT_GOOD;
466  case GPG_ERR_BAD_SIGNATURE:
467  return GPGME_SIG_STAT_BAD;
468  case GPG_ERR_NO_PUBKEY:
469  return GPGME_SIG_STAT_NOKEY;
470  case GPG_ERR_NO_DATA:
471  return GPGME_SIG_STAT_NOSIG;
472  case GPG_ERR_SIG_EXPIRED:
473  return GPGME_SIG_STAT_GOOD_EXP;
474  case GPG_ERR_KEY_EXPIRED:
475  return GPGME_SIG_STAT_GOOD_EXPKEY;
476  default:
477  return GPGME_SIG_STAT_ERROR;
478  }
479 }
480 
481 bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( KMime::Content* data,
482  KMime::Content& sign,
483  const QString& fromAddress,
484  bool doCheck,
485  QByteArray* cleartextData,
486  const std::vector<GpgME::Signature> & paramSignatures,
487  bool hideErrors )
488 {
489 // kDebug() << "DECRYPT" << data;
490  bool bIsOpaqueSigned = false;
491  enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
492  cryptPlugError = NO_PLUGIN;
493 
494  const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
495 
496  QString cryptPlugLibName;
497  QString cryptPlugDisplayName;
498  if ( cryptProto ) {
499  cryptPlugLibName = cryptProto->name();
500  cryptPlugDisplayName = cryptProto->displayName();
501  }
502 
503 #ifdef DEBUG_SIGNATURE
504 #ifndef NDEBUG
505  if ( !doCheck )
506  kDebug() << "showing OpenPGP (Encrypted+Signed) data";
507  else
508  if ( data )
509  kDebug() << "processing Multipart Signed data";
510  else
511  kDebug() << "processing Opaque Signed data";
512 #endif
513 
514  if ( doCheck && cryptProto ) {
515  //kDebug() << "going to call CRYPTPLUG" << cryptPlugLibName;
516  }
517 #endif
518 
519  QByteArray cleartext;
520  QByteArray signaturetext;
521 
522  if ( doCheck && cryptProto ) {
523  if ( data ) {
524  cleartext = data->encodedContent();
525 #ifdef DEBUG_SIGNATURE
526  kDebug() << "ClearText : " << cleartext;
527 
528  dumpToFile( "dat_01_reader_signedtext_before_canonicalization",
529  cleartext.data(), cleartext.length() );
530 
531  // replace simple LFs by CRLSs
532  // according to RfC 2633, 3.1.1 Canonicalization
533  kDebug() << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)";
534 #endif
535  cleartext = KMime::LFtoCRLF( cleartext );
536 #ifdef DEBUG_SIGNATURE
537  kDebug() << " done.";
538 #endif
539 }
540 
541  dumpToFile( "dat_02_reader_signedtext_after_canonicalization",
542  cleartext.data(), cleartext.length() );
543 
544  signaturetext = sign.decodedContent();
545  dumpToFile( "dat_03_reader.sig", signaturetext.data(),
546  signaturetext.size() );
547  }
548 
549  std::vector<GpgME::Signature> signatures;
550  if ( !doCheck )
551  signatures = paramSignatures;
552 
553  PartMetaData messagePart;
554  messagePart.isSigned = true;
555  messagePart.technicalProblem = ( cryptProto == 0 );
556  messagePart.isGoodSignature = false;
557  messagePart.isEncrypted = false;
558  messagePart.isDecryptable = false;
559  messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
560  messagePart.status = i18n("Wrong Crypto Plug-In.");
561  messagePart.status_code = GPGME_SIG_STAT_NONE;
562 
563  GpgME::Key key;
564 
565  if ( doCheck && cryptProto ) {
566 #ifdef DEBUG_SIGNATURE
567  kDebug() << "tokoe: doCheck and cryptProto";
568 #endif
569  GpgME::VerificationResult result;
570  if ( data ) { // detached
571 #ifdef DEBUG_SIGNATURE
572  kDebug() << "tokoe: is detached signature";
573 #endif
574  const VerifyDetachedBodyPartMemento * m
575  = dynamic_cast<VerifyDetachedBodyPartMemento*>( mNodeHelper->bodyPartMemento( &sign, "verifydetached" ) );
576  if ( !m ) {
577 #ifdef DEBUG_SIGNATURE
578  kDebug() << "tokoe: no memento available";
579 #endif
580  Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
581  if ( !job ) {
582  cryptPlugError = CANT_VERIFY_SIGNATURES;
583  // PENDING(marc) cryptProto = 0 here?
584  } else {
585  QByteArray plainData = cleartext;
586  VerifyDetachedBodyPartMemento * newM
587  = new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
588  if ( allowAsync() ) {
589 #ifdef DEBUG_SIGNATURE
590  kDebug() << "tokoe: allowAsync";
591 #endif
592  QObject::connect( newM, SIGNAL(update(MessageViewer::Viewer::UpdateMode)),
593  mSource->sourceObject(), SLOT(update(MessageViewer::Viewer::UpdateMode)) );
594  if ( newM->start() ) {
595 #ifdef DEBUG_SIGNATURE
596  kDebug() << "tokoe: new memento started";
597 #endif
598  messagePart.inProgress = true;
599  mHasPendingAsyncJobs = true;
600  } else {
601  m = newM;
602  }
603  } else {
604  newM->exec();
605  m = newM;
606  }
607  mNodeHelper->setBodyPartMemento( &sign, "verifydetached", newM );
608  }
609  } else if ( m->isRunning() ) {
610 #ifdef DEBUG_SIGNATURE
611  kDebug() << "tokoe: memento is running";
612 #endif
613  messagePart.inProgress = true;
614  mHasPendingAsyncJobs = true;
615  m = 0;
616  }
617 
618  if ( m ) {
619 #ifdef DEBUG_SIGNATURE
620  kDebug() << "tokoe: memento finished, assign result";
621 #endif
622  result = m->verifyResult();
623  messagePart.auditLogError = m->auditLogError();
624  messagePart.auditLog = m->auditLogAsHtml();
625  key = m->signingKey();
626  }
627  } else { // opaque
628 #ifdef DEBUG_SIGNATURE
629  kDebug() << "tokoe: is opaque signature";
630 #endif
631  const VerifyOpaqueBodyPartMemento * m
632  = dynamic_cast<VerifyOpaqueBodyPartMemento*>( mNodeHelper->bodyPartMemento( &sign, "verifyopaque" ) );
633  if ( !m ) {
634 #ifdef DEBUG_SIGNATURE
635  kDebug() << "tokoe: no memento available";
636 #endif
637  Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
638  if ( !job ) {
639  cryptPlugError = CANT_VERIFY_SIGNATURES;
640  // PENDING(marc) cryptProto = 0 here?
641  } else {
642  VerifyOpaqueBodyPartMemento * newM
643  = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
644  if ( allowAsync() ) {
645 #ifdef DEBUG_SIGNATURE
646  kDebug() << "tokoe: allowAsync";
647 #endif
648  QObject::connect( newM, SIGNAL(update(MessageViewer::Viewer::UpdateMode)), mSource->sourceObject(),
649  SLOT(update(MessageViewer::Viewer::UpdateMode)) );
650  if ( newM->start() ) {
651 #ifdef DEBUG_SIGNATURE
652  kDebug() << "tokoe: new memento started";
653 #endif
654  messagePart.inProgress = true;
655  mHasPendingAsyncJobs = true;
656  } else {
657  m = newM;
658  }
659  } else {
660  newM->exec();
661  m = newM;
662  }
663  mNodeHelper->setBodyPartMemento( &sign, "verifyopaque", newM );
664  }
665  } else if ( m->isRunning() ) {
666 #ifdef DEBUG_SIGNATURE
667  kDebug() << "tokoe: memento is running";
668 #endif
669  messagePart.inProgress = true;
670  mHasPendingAsyncJobs = true;
671  m = 0;
672  }
673 
674  if ( m ) {
675 #ifdef DEBUG_SIGNATURE
676  kDebug() << "tokoe: memento finished, assign result";
677 #endif
678  result = m->verifyResult();
679  cleartext = m->plainText();
680  messagePart.auditLogError = m->auditLogError();
681  messagePart.auditLog = m->auditLogAsHtml();
682  key = m->signingKey();
683  }
684  }
685  std::stringstream ss;
686  ss << result;
687 #ifdef DEBUG_SIGNATURE
688  kDebug() << ss.str().c_str();
689 #endif
690  signatures = result.signatures();
691  }
692  else
693  messagePart.auditLogError = GpgME::Error( GPG_ERR_NOT_IMPLEMENTED );
694 
695 #ifdef DEBUG_SIGNATURE
696  if ( doCheck )
697  kDebug() << "returned from CRYPTPLUG";
698 #endif
699 
700  // ### only one signature supported
701  if ( !signatures.empty() ) {
702 #ifdef DEBUG_SIGNATURE
703  kDebug() << "\nFound signature";
704 #endif
705  GpgME::Signature signature = signatures.front();
706 
707  messagePart.status_code = signatureToStatus( signature );
708  messagePart.status = QString::fromLocal8Bit( signature.status().asString() );
709  for ( uint i = 1; i < signatures.size(); ++i ) {
710  if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
711  messagePart.status_code = GPGME_SIG_STAT_DIFF;
712  messagePart.status = i18n("Different results for signatures");
713  }
714  }
715  if ( messagePart.status_code & GPGME_SIG_STAT_GOOD ) {
716  messagePart.isGoodSignature = true;
717  if ( !doCheck ) {
718  // We have a good signature but did not do a verify,
719  // this means the signature was already validated before by
720  // decryptverify for example.
721  Q_ASSERT( !key.keyID() ); // There should be no key set without doCheck
722 
723  // Search for the key by it's fingerprint so that we can check for
724  // trust etc.
725  Kleo::KeyListJob * job = cryptProto->keyListJob( false ); // local, no sigs
726  if ( !job ) {
727  kDebug() << "The Crypto backend does not support listing keys. ";
728  } else {
729  std::vector<GpgME::Key> found_keys;
730  // As we are local it is ok to make this synchronous
731  GpgME::KeyListResult res = job->exec( QStringList( QLatin1String(signature.fingerprint()) ), false, found_keys );
732  if ( res.error() ) {
733  kDebug() << "Error while searching key for Fingerprint: " << signature.fingerprint();
734  }
735  if ( found_keys.size() > 1 ) {
736  // Should not Happen
737  kDebug() << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint();
738  }
739  if ( found_keys.size() != 1 ) {
740  // Should not Happen at this point
741  kDebug() << "Oops: Found no Key for Fingerprint: " << signature.fingerprint();
742  } else {
743  key = found_keys[0];
744  }
745  }
746  }
747  }
748 
749  // save extended signature status flags
750  messagePart.sigSummary = signature.summary();
751 
752  if ( key.keyID() )
753  messagePart.keyId = key.keyID();
754  if ( messagePart.keyId.isEmpty() )
755  messagePart.keyId = signature.fingerprint();
756  // ### Ugh. We depend on two enums being in sync:
757  messagePart.keyTrust = (Kpgp::Validity)signature.validity();
758  if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
759  messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
760  for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
761  // The following if /should/ always result in TRUE but we
762  // won't trust implicitely the plugin that gave us these data.
763  if ( key.userID( iMail ).email() ) {
764  QString email = QString::fromUtf8( key.userID( iMail ).email() );
765  // ### work around gpgme 0.3.x / cryptplug bug where the
766  // ### email addresses are specified as angle-addr, not addr-spec:
767  if ( email.startsWith( QLatin1Char('<') ) && email.endsWith( QLatin1Char('>') ) )
768  email = email.mid( 1, email.length() - 2 );
769  if ( !email.isEmpty() )
770  messagePart.signerMailAddresses.append( email );
771  }
772  }
773 
774  if ( signature.creationTime() )
775  messagePart.creationTime.setTime_t( signature.creationTime() );
776  else
777  messagePart.creationTime = QDateTime();
778  if ( messagePart.signer.isEmpty() ) {
779  if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
780  messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
781  if ( !messagePart.signerMailAddresses.empty() ) {
782  if ( messagePart.signer.isEmpty() )
783  messagePart.signer = messagePart.signerMailAddresses.front();
784  else
785  messagePart.signer += QLatin1String(" <") + messagePart.signerMailAddresses.front() + QLatin1Char('>');
786  }
787  }
788 #ifdef DEBUG_SIGNATURE
789  kDebug() << "\n key id:" << messagePart.keyId
790  << "\n key trust:" << messagePart.keyTrust
791  << "\n signer:" << messagePart.signer;
792 #endif
793  } else {
794  messagePart.creationTime = QDateTime();
795  }
796 
797  if ( !doCheck || !data ){
798  if ( cleartextData || !cleartext.isEmpty() ) {
799  if ( htmlWriter() )
800  htmlWriter()->queue( writeSigstatHeader( messagePart,
801  cryptProto,
802  fromAddress ) );
803  bIsOpaqueSigned = true;
804 
805  CryptoProtocolSaver cpws( this, cryptProto );
806  createAndParseTempNode( &sign, doCheck ? cleartext.data() : cleartextData->data(),
807  "opaque signed data" );
808 
809  if ( htmlWriter() )
810  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
811 
812  }
813  else if ( !hideErrors ) {
814  QString txt;
815  txt = QLatin1String("<hr><b><h2>");
816  txt.append( i18n( "The crypto engine returned no cleartext data." ) );
817  txt.append( QLatin1String("</h2></b>" ));
818  txt.append( QLatin1String("<br/>&nbsp;<br/>") );
819  txt.append( i18n( "Status: " ) );
820  if ( !messagePart.status.isEmpty() ) {
821  txt.append( QLatin1String("<i>") );
822  txt.append( messagePart.status );
823  txt.append( QLatin1String("</i>") );
824  }
825  else
826  txt.append( i18nc("Status of message unknown.","(unknown)") );
827  if ( htmlWriter() )
828  htmlWriter()->queue(txt);
829  }
830  }
831  else {
832  if ( htmlWriter() ) {
833  if ( !cryptProto ) {
834  QString errorMsg;
835  switch ( cryptPlugError ) {
836  case NOT_INITIALIZED:
837  errorMsg = i18n( "Crypto plug-in \"%1\" is not initialized.",
838  cryptPlugLibName );
839  break;
840  case CANT_VERIFY_SIGNATURES:
841  errorMsg = i18n( "Crypto plug-in \"%1\" cannot verify signatures.",
842  cryptPlugLibName );
843  break;
844  case NO_PLUGIN:
845  if ( cryptPlugDisplayName.isEmpty() )
846  errorMsg = i18n( "No appropriate crypto plug-in was found." );
847  else
848  errorMsg = i18nc( "%1 is either 'OpenPGP' or 'S/MIME'",
849  "No %1 plug-in was found.",
850  cryptPlugDisplayName );
851  break;
852  }
853  messagePart.errorText = i18n( "The message is signed, but the "
854  "validity of the signature cannot be "
855  "verified.<br />"
856  "Reason: %1",
857  errorMsg );
858  }
859 
860  htmlWriter()->queue( writeSigstatHeader( messagePart,
861  cryptProto,
862  fromAddress ) );
863  }
864 
865  ObjectTreeParser otp( this, true );
866  otp.setAllowAsync( allowAsync() );
867  otp.parseObjectTreeInternal( data );
868  copyContentFrom( &otp );
869 
870  if ( htmlWriter() )
871  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
872  }
873 #ifdef DEBUG_SIGNATURE
874  kDebug() << "done, returning" << ( bIsOpaqueSigned ? "TRUE" : "FALSE" );
875 #endif
876  //kDebug() << "DECRYPTED" << data;
877  return bIsOpaqueSigned;
878 }
879 
880 void ObjectTreeParser::writeDeferredDecryptionBlock()
881 {
882  const QString iconName = QLatin1String("file:///") + KIconLoader::global()->iconPath( QLatin1String("document-decrypt"),
883  KIconLoader::Small );
884  const QString decryptedData = QLatin1String("<div style=\"font-size:large; text-align:center;"
885  "padding-top:20pt;\">")
886  + i18n("This message is encrypted.")
887  + QLatin1String("</div>"
888  "<div style=\"text-align:center; padding-bottom:20pt;\">"
889  "<a href=\"kmail:decryptMessage\">"
890  "<img src=\"") + iconName + QLatin1String("\"/>")
891  + i18n("Decrypt Message")
892  + QLatin1String("</a></div>");
893  PartMetaData messagePart;
894  messagePart.isDecryptable = true;
895  messagePart.isEncrypted = true;
896  messagePart.isSigned = false;
897  mRawDecryptedBody += decryptedData.toUtf8();
898 
899  if ( htmlWriter() ) { //TODO: check if this check should be here or at the beginning of the method
900  htmlWriter()->queue( writeSigstatHeader( messagePart,
901  cryptoProtocol(),
902  QString() ) );
903  htmlWriter()->queue( decryptedData );
904  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
905  }
906 }
907 
908 
909 void ObjectTreeParser::writeDecryptionInProgressBlock()
910 {
911  if ( !htmlWriter() )
912  return;
913  // PENDING(marc) find an animated icon here:
914  //const QString iconName = KGlobal::instance()->iconLoader()->iconPath( "decrypted", KIcon::Small );
915  const QString decryptedData = i18n("Encrypted data not shown");
916  PartMetaData messagePart;
917  messagePart.isDecryptable = true;
918  messagePart.isEncrypted = true;
919  messagePart.isSigned = false;
920  messagePart.inProgress = true;
921  htmlWriter()->queue( writeSigstatHeader( messagePart,
922  cryptoProtocol(),
923  QString() ) );
924  //htmlWriter()->queue( decryptedData );
925  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
926 }
927 
928 void ObjectTreeParser::writeCertificateImportResult( const GpgME::ImportResult & res )
929 {
930  if ( res.error() ) {
931  htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br />"
932  "Reason: %1", QString::fromLocal8Bit( res.error().asString() ) ) );
933  return;
934  }
935 
936  const int nImp = res.numImported();
937  const int nUnc = res.numUnchanged();
938  const int nSKImp = res.numSecretKeysImported();
939  const int nSKUnc = res.numSecretKeysUnchanged();
940  if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
941  htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
942  return;
943  }
944  QString comment = QLatin1String("<b>") + i18n( "Certificate import status:" ) + QLatin1String("</b><br/>&nbsp;<br/>");
945  if ( nImp )
946  comment += i18np( "1 new certificate was imported.",
947  "%1 new certificates were imported.", nImp ) + QLatin1String("<br/>");
948  if ( nUnc )
949  comment += i18np( "1 certificate was unchanged.",
950  "%1 certificates were unchanged.", nUnc ) + QLatin1String("<br/>");
951  if ( nSKImp )
952  comment += i18np( "1 new secret key was imported.",
953  "%1 new secret keys were imported.", nSKImp ) + QLatin1String("<br/>");
954  if ( nSKUnc )
955  comment += i18np( "1 secret key was unchanged.",
956  "%1 secret keys were unchanged.", nSKUnc ) + QLatin1String("<br/>");
957  comment += QLatin1String("&nbsp;<br/>");
958  htmlWriter()->queue( comment );
959  if ( !nImp && !nSKImp ) {
960  htmlWriter()->queue( QLatin1String("<hr>") );
961  return;
962  }
963  const std::vector<GpgME::Import> imports = res.imports();
964  if ( imports.empty() ) {
965  htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + QLatin1String("<hr>") );
966  return;
967  }
968  htmlWriter()->queue( QLatin1String("<b>") + i18n( "Certificate import details:" ) + QLatin1String("</b><br/>") );
969  std::vector<GpgME::Import>::const_iterator end( imports.end() );
970  for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != end ; ++it ) {
971  if ( (*it).error() ) {
972  htmlWriter()->queue( i18nc( "Certificate import failed.", "Failed: %1 (%2)", QLatin1String((*it).fingerprint()),
973  QString::fromLocal8Bit( (*it).error().asString() ) ) );
974  } else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
975  if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
976  htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)", QLatin1String((*it).fingerprint() )) );
977  } else {
978  htmlWriter()->queue( i18n( "New or changed: %1", QLatin1String((*it).fingerprint() )) );
979  }
980  }
981  htmlWriter()->queue( QLatin1String("<br/>") );
982  }
983 
984  htmlWriter()->queue( QLatin1String("<hr>") );
985 }
986 
987 
988 bool ObjectTreeParser::okDecryptMIME( KMime::Content& data,
989  QByteArray& decryptedData,
990  bool& signatureFound,
991  std::vector<GpgME::Signature> &signatures,
992  bool showWarning,
993  bool& passphraseError,
994  bool& actuallyEncrypted,
995  bool& decryptionStarted,
996  PartMetaData &partMetaData )
997 {
998  passphraseError = false;
999  decryptionStarted = false;
1000  partMetaData.errorText.clear();
1001  partMetaData.auditLogError = GpgME::Error();
1002  partMetaData.auditLog.clear();
1003  bool bDecryptionOk = false;
1004  enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
1005  cryptPlugError = NO_PLUGIN;
1006 
1007  const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
1008 
1009  QString cryptPlugLibName;
1010  if ( cryptProto )
1011  cryptPlugLibName = cryptProto->name();
1012 
1013  assert( mSource->decryptMessage() );
1014 
1015  const QString errorMsg = i18n( "Could not decrypt the data." );
1016  if ( cryptProto /*FIXME(Andras) port to akonadi
1017  && !kmkernel->contextMenuShown()*/ ) {
1018  QByteArray ciphertext = data.decodedContent();
1019  #ifdef MARCS_DEBUG
1020  QString cipherStr = QString::fromLatin1( ciphertext );
1021  bool cipherIsBinary = ( !cipherStr.contains("BEGIN ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
1022  ( !cipherStr.contains("BEGIN PGP ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
1023  ( !cipherStr.contains("BEGIN PGP MESSAGE", Qt::CaseInsensitive ) );
1024 
1025  dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
1026 
1027  QString deb;
1028  deb = "\n\nE N C R Y P T E D D A T A = ";
1029  if ( cipherIsBinary )
1030  deb += "[binary data]";
1031  else {
1032  deb += "\"";
1033  deb += cipherStr;
1034  deb += "\"";
1035  }
1036  deb += "\n\n";
1037  kDebug() << deb;
1038  #endif
1039 
1040 
1041  //kDebug() << "going to call CRYPTPLUG" << cryptPlugLibName;
1042 
1043  // Check whether the memento contains a result from last time:
1044  const DecryptVerifyBodyPartMemento * m
1045  = dynamic_cast<DecryptVerifyBodyPartMemento*>( mNodeHelper->bodyPartMemento( &data, "decryptverify" ) );
1046  if ( !m ) {
1047  Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
1048  if ( !job ) {
1049  cryptPlugError = CANT_DECRYPT;
1050  cryptProto = 0;
1051  } else {
1052  DecryptVerifyBodyPartMemento * newM
1053  = new DecryptVerifyBodyPartMemento( job, ciphertext );
1054  if ( allowAsync() ) {
1055  QObject::connect( newM, SIGNAL(update(MessageViewer::Viewer::UpdateMode)), mSource->sourceObject(),
1056  SLOT(update(MessageViewer::Viewer::UpdateMode)) );
1057  if ( newM->start() ) {
1058  decryptionStarted = true;
1059  mHasPendingAsyncJobs = true;
1060  } else {
1061  m = newM;
1062  }
1063  } else {
1064  newM->exec();
1065  m = newM;
1066  }
1067  mNodeHelper->setBodyPartMemento( &data, "decryptverify", newM );
1068  }
1069  } else if ( m->isRunning() ) {
1070  decryptionStarted = true;
1071  mHasPendingAsyncJobs = true;
1072  m = 0;
1073  }
1074 
1075  if ( m ) {
1076  const QByteArray & plainText = m->plainText();
1077  const GpgME::DecryptionResult & decryptResult = m->decryptResult();
1078  const GpgME::VerificationResult & verifyResult = m->verifyResult();
1079  std::stringstream ss;
1080  ss << decryptResult << '\n' << verifyResult;
1081  //kDebug() << ss.str().c_str();
1082  signatureFound = verifyResult.signatures().size() > 0;
1083  signatures = verifyResult.signatures();
1084  bDecryptionOk = !decryptResult.error();
1085  passphraseError = decryptResult.error().isCanceled()
1086  || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
1087  actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
1088  partMetaData.errorText = QString::fromLocal8Bit( decryptResult.error().asString() );
1089  partMetaData.auditLogError = m->auditLogError();
1090  partMetaData.auditLog = m->auditLogAsHtml();
1091  partMetaData.isEncrypted = actuallyEncrypted;
1092  if ( actuallyEncrypted && decryptResult.numRecipients() > 0 )
1093  partMetaData.keyId = decryptResult.recipient( 0 ).keyID();
1094 
1095  //kDebug() << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG";
1096  if ( bDecryptionOk )
1097  decryptedData = plainText;
1098  else if ( htmlWriter() && showWarning ) {
1099  decryptedData = "<div style=\"font-size:x-large; text-align:center;"
1100  "padding:20pt;\">"
1101  + errorMsg.toUtf8()
1102  + "</div>";
1103  if ( !passphraseError )
1104  partMetaData.errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName )
1105  + QLatin1String("<br />")
1106  + i18n("Error: %1", partMetaData.errorText );
1107  }
1108  }
1109 }
1110 
1111 if ( !cryptProto ) {
1112  decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
1113  + errorMsg.toUtf8()
1114  + "</div>";
1115  switch ( cryptPlugError ) {
1116  case NOT_INITIALIZED:
1117  partMetaData.errorText = i18n( "Crypto plug-in \"%1\" is not initialized.",
1118  cryptPlugLibName );
1119  break;
1120  case CANT_DECRYPT:
1121  partMetaData.errorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages.",
1122  cryptPlugLibName );
1123  break;
1124  case NO_PLUGIN:
1125  partMetaData.errorText = i18n( "No appropriate crypto plug-in was found." );
1126  break;
1127  }
1128 } else if (/*FIXME(Andras) port to akonadi
1129  kmkernel->contextMenuShown()*/ false ) {
1130  // ### Workaround for bug 56693 (kmail freeze with the complete desktop
1131  // ### while pinentry-qt appears)
1132  QByteArray ciphertext( data.decodedContent() );
1133  QString cipherStr = QString::fromLatin1( ciphertext );
1134  bool cipherIsBinary = ( !cipherStr.contains(QLatin1String("BEGIN ENCRYPTED MESSAGE"), Qt::CaseInsensitive ) ) &&
1135  ( !cipherStr.contains(QLatin1String("BEGIN PGP ENCRYPTED MESSAGE"), Qt::CaseInsensitive ) ) &&
1136  ( !cipherStr.contains(QLatin1String("BEGIN PGP MESSAGE"), Qt::CaseInsensitive ) );
1137  if ( !cipherIsBinary ) {
1138  decryptedData = ciphertext;
1139  }
1140  else {
1141  decryptedData = "<div style=\"font-size:x-large; text-align:center;"
1142  "padding:20pt;\">"
1143  + errorMsg.toUtf8()
1144  + "</div>";
1145  }
1146 }
1147 
1148 dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
1149 
1150 return bDecryptionOk;
1151 }
1152 
1153 //static
1154 bool ObjectTreeParser::containsExternalReferences( const QString & str, const QString&extraHead )
1155 {
1156  const bool hasBaseInHeader = extraHead.contains(QLatin1String("<base href=\""),Qt::CaseInsensitive);
1157  if(hasBaseInHeader && (str.contains(QLatin1String("href=\"/"),Qt::CaseInsensitive) ||
1158  str.contains(QLatin1String("<img src=\"/"),Qt::CaseInsensitive)) ) {
1159  return true;
1160  }
1161  /*
1162  //Laurent: workaround for local ref cid
1163  if(str.contains(QLatin1String("<img src=\"cid:"),Qt::CaseInsensitive)) {
1164  return true;
1165  }
1166  */
1167  int httpPos = str.indexOf( QLatin1String("\"http:"), Qt::CaseInsensitive );
1168  int httpsPos = str.indexOf( QLatin1String("\"https:"), Qt::CaseInsensitive );
1169 
1170  while ( httpPos >= 0 || httpsPos >= 0 ) {
1171  // pos = index of next occurrence of "http: or "https: whichever comes first
1172  int pos = ( httpPos < httpsPos )
1173  ? ( ( httpPos >= 0 ) ? httpPos : httpsPos )
1174  : ( ( httpsPos >= 0 ) ? httpsPos : httpPos );
1175  // look backwards for "href"
1176  if ( pos > 5 ) {
1177  int hrefPos = str.lastIndexOf( QLatin1String("href"), pos - 5, Qt::CaseInsensitive );
1178  // if no 'href' is found or the distance between 'href' and '"http[s]:'
1179  // is larger than 7 (7 is the distance in 'href = "http[s]:') then
1180  // we assume that we have found an external reference
1181  if ( ( hrefPos == -1 ) || ( pos - hrefPos > 7 ) ) {
1182 
1183  // HTML messages created by KMail itself for now contain the following:
1184  // <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1185  // Make sure not to show an external references warning for this string
1186  int dtdPos = str.indexOf( QLatin1String("http://www.w3.org/TR/html4/loose.dtd"), pos + 1 );
1187  if ( dtdPos != ( pos + 1 ) )
1188  return true;
1189  }
1190  }
1191  // find next occurrence of "http: or "https:
1192  if ( pos == httpPos ) {
1193  httpPos = str.indexOf( QLatin1String("\"http:"), httpPos + 6, Qt::CaseInsensitive );
1194  }
1195  else {
1196  httpsPos = str.indexOf( QLatin1String("\"https:"), httpsPos + 7, Qt::CaseInsensitive );
1197  }
1198  }
1199  return false;
1200 }
1201 
1202 bool ObjectTreeParser::processTextHtmlSubtype( KMime::Content * curNode, ProcessResult & ) {
1203  const QByteArray partBody( curNode->decodedContent() );
1204 
1205  const QString bodyHTML = codecFor( curNode )->toUnicode( partBody );
1206  mHtmlContent += bodyHTML;
1207  mHtmlContentCharset = NodeHelper::charset( curNode );
1208  mRawDecryptedBody = partBody;
1209 
1210  if ( !htmlWriter() )
1211  return true;
1212 
1213  QString bodyText;
1214  if ( mSource->htmlMail() ) {
1215  bodyText = bodyHTML;
1216  } else {
1217  bodyText = QLatin1String(StringUtil::convertAngleBracketsToHtml( partBody ));
1218  }
1219 
1220  if ( curNode->topLevel()->textContent() == curNode || attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
1221  showOnlyOneMimePart() )
1222  {
1223  if ( mSource->htmlMail() ) {
1224 
1225  HTMLQuoteColorer colorer;
1226  QString extraHead;
1227  for ( int i = 0; i < 3; ++i )
1228  colorer.setQuoteColor( i, cssHelper()->quoteColor( i ) );
1229  bodyText = colorer.process( bodyText, extraHead );
1230  mNodeHelper->setNodeDisplayedEmbedded( curNode, true );
1231  htmlWriter()->extraHead(extraHead);
1232 
1233  // Show the "external references" warning (with possibility to load
1234  // external references only if loading external references is disabled
1235  // and the HTML code contains obvious external references). For
1236  // messages where the external references are obfuscated the user won't
1237  // have an easy way to load them but that shouldn't be a problem
1238  // because only spam contains obfuscated external references.
1239  if ( !mSource->htmlLoadExternal() &&
1240  containsExternalReferences( bodyText,extraHead ) ) {
1241  htmlWriter()->queue( QLatin1String("<div class=\"htmlWarn\">\n") );
1242  htmlWriter()->queue( i18n("<b>Note:</b> This HTML message may contain external "
1243  "references to images etc. For security/privacy reasons "
1244  "external references are not loaded. If you trust the "
1245  "sender of this message then you can load the external "
1246  "references for this message "
1247  "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
1248  htmlWriter()->queue( QLatin1String("</div><br/><br/>") );
1249  }
1250  } else {
1251  htmlWriter()->queue( QLatin1String("<div class=\"htmlWarn\">\n") );
1252  htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
1253  "security reasons, only the raw HTML code "
1254  "is shown. If you trust the sender of this "
1255  "message then you can activate formatted "
1256  "HTML display for this message "
1257  "<a href=\"kmail:showHTML\">by clicking here</a>.") );
1258  htmlWriter()->queue( QLatin1String("</div><br/><br/>") );
1259  }
1260  // Make sure the body is relative, so that nothing is painted over above "Note: ..."
1261  // if a malicious message uses absolute positioning. #137643
1262  htmlWriter()->queue( QLatin1String("<div style=\"position: relative\">\n") );
1263  htmlWriter()->queue( bodyText );
1264  htmlWriter()->queue( QLatin1String("</div>\n" ));
1265  mSource->setHtmlMode( Util::Html );
1266  return true;
1267  }
1268  return false;
1269 }
1270 
1271 bool ObjectTreeParser::isMailmanMessage( KMime::Content * curNode )
1272 {
1273  if ( !curNode || curNode->head().isEmpty() )
1274  return false;
1275  if ( curNode->hasHeader("X-Mailman-Version") )
1276  return true;
1277  if ( curNode->hasHeader("X-Mailer") ) {
1278  KMime::Headers::Base *header = curNode->headerByType("X-Mailer");
1279  if ( header->asUnicodeString().contains(QLatin1String("MAILMAN"), Qt::CaseInsensitive ) )
1280  return true;
1281  }
1282  return false;
1283 }
1284 
1285 bool ObjectTreeParser::processMailmanMessage( KMime::Content* curNode ) {
1286  const QString str = QString::fromLatin1( curNode->decodedContent() );
1287 
1288  //###
1289  const QLatin1String delim1( "--__--__--\n\nMessage:" );
1290  const QLatin1String delim2( "--__--__--\r\n\r\nMessage:" );
1291  const QLatin1String delimZ2( "--__--__--\n\n_____________" );
1292  const QLatin1String delimZ1( "--__--__--\r\n\r\n_____________" );
1293  QString partStr, digestHeaderStr;
1294  int thisDelim = str.indexOf( delim1, Qt::CaseInsensitive );
1295  if ( thisDelim == -1 ) {
1296  thisDelim = str.indexOf( delim2, Qt::CaseInsensitive );
1297  }
1298  if ( thisDelim == -1 ) {
1299  return false;
1300  }
1301 
1302  int nextDelim = str.indexOf( delim1, thisDelim+1, Qt::CaseInsensitive );
1303  if ( -1 == nextDelim ) {
1304  nextDelim = str.indexOf( delim2, thisDelim+1, Qt::CaseInsensitive );
1305  }
1306  if ( -1 == nextDelim ) {
1307  nextDelim = str.indexOf( delimZ1, thisDelim+1, Qt::CaseInsensitive );
1308  }
1309  if ( -1 == nextDelim ) {
1310  nextDelim = str.indexOf( delimZ2, thisDelim+1, Qt::CaseInsensitive );
1311  }
1312  if ( nextDelim < 0) {
1313  return false;
1314  }
1315 
1316  //if ( curNode->mRoot )
1317  // curNode = curNode->mRoot;
1318 
1319  // at least one message found: build a mime tree
1320  digestHeaderStr = QLatin1String("Content-Type: text/plain\nContent-Description: digest header\n\n");
1321  digestHeaderStr += str.mid( 0, thisDelim );
1322  createAndParseTempNode( mTopLevelContent, digestHeaderStr.toLatin1(), "Digest Header" );
1323  //mReader->queueHtml("<br><hr><br>");
1324  // temporarily change curent node's Content-Type
1325  // to get our embedded RfC822 messages properly inserted
1326  curNode->contentType()->setMimeType( "multipart/digest" );
1327  while( -1 < nextDelim ){
1328  int thisEoL = str.indexOf(QLatin1String("\nMessage:"), thisDelim, Qt::CaseInsensitive );
1329  if ( -1 < thisEoL )
1330  thisDelim = thisEoL+1;
1331  else{
1332  thisEoL = str.indexOf(QLatin1String("\n_____________"), thisDelim, Qt::CaseInsensitive );
1333  if ( -1 < thisEoL )
1334  thisDelim = thisEoL+1;
1335  }
1336  thisEoL = str.indexOf( QLatin1Char('\n'), thisDelim );
1337  if ( -1 < thisEoL )
1338  thisDelim = thisEoL+1;
1339  else
1340  thisDelim = thisDelim+1;
1341  //while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] )
1342  // ++thisDelim;
1343 
1344  partStr = QLatin1String("Content-Type: message/rfc822\nContent-Description: embedded message\n\n");
1345  partStr += QLatin1String("Content-Type: text/plain\n");
1346  partStr += str.mid( thisDelim, nextDelim-thisDelim );
1347  QString subject = QString::fromLatin1("embedded message");
1348  QString subSearch = QString::fromLatin1("\nSubject:");
1349  int subPos = partStr.indexOf(subSearch, 0, Qt::CaseInsensitive );
1350  if ( -1 < subPos ){
1351  subject = partStr.mid(subPos+subSearch.length());
1352  thisEoL = subject.indexOf(QLatin1Char('\n'));
1353  if ( -1 < thisEoL )
1354  subject.truncate( thisEoL );
1355  }
1356  kDebug() << " embedded message found: \"" << subject;
1357  createAndParseTempNode( mTopLevelContent, partStr.toLatin1(), subject.toLatin1() );
1358  //mReader->queueHtml("<br><hr><br>");
1359  thisDelim = nextDelim+1;
1360  nextDelim = str.indexOf(delim1, thisDelim, Qt::CaseInsensitive );
1361  if ( -1 == nextDelim )
1362  nextDelim = str.indexOf(delim2, thisDelim, Qt::CaseInsensitive);
1363  if ( -1 == nextDelim )
1364  nextDelim = str.indexOf(delimZ1, thisDelim, Qt::CaseInsensitive);
1365  if ( -1 == nextDelim )
1366  nextDelim = str.indexOf(delimZ2, thisDelim, Qt::CaseInsensitive);
1367  }
1368  // reset curent node's Content-Type
1369  curNode->contentType()->setMimeType( "text/plain" );
1370  int thisEoL = str.indexOf( QLatin1String("_____________"), thisDelim );
1371  if ( -1 < thisEoL ){
1372  thisDelim = thisEoL;
1373  thisEoL = str.indexOf( QLatin1Char('\n'), thisDelim );
1374  if ( -1 < thisEoL )
1375  thisDelim = thisEoL+1;
1376  }
1377  else
1378  thisDelim = thisDelim+1;
1379  partStr = QLatin1String("Content-Type: text/plain\nContent-Description: digest footer\n\n");
1380  partStr += str.mid( thisDelim );
1381  createAndParseTempNode( mTopLevelContent, partStr.toLatin1(), "Digest Footer" );
1382  return true;
1383 }
1384 
1385 void ObjectTreeParser::extractNodeInfos( KMime::Content *curNode, bool isFirstTextPart )
1386 {
1387  mRawDecryptedBody = curNode->decodedContent();
1388  if ( isFirstTextPart ) {
1389  mPlainTextContent += curNode->decodedText();
1390  mPlainTextContentCharset += NodeHelper::charset( curNode );
1391  }
1392 }
1393 
1394 
1395 bool ObjectTreeParser::processTextPlainSubtype( KMime::Content *curNode, ProcessResult & result )
1396 {
1397  const bool isFirstTextPart = ( curNode->topLevel()->textContent() == curNode );
1398 
1399  if ( !isFirstTextPart && attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
1400  !showOnlyOneMimePart() )
1401  return false;
1402 
1403  extractNodeInfos( curNode, isFirstTextPart );
1404 
1405  QString label = NodeHelper::fileName( curNode );
1406 
1407  const bool bDrawFrame = !isFirstTextPart
1408  && !showOnlyOneMimePart()
1409  && !label.isEmpty();
1410  if ( bDrawFrame && htmlWriter()) {
1411  label = StringUtil::quoteHtmlChars( label, true );
1412 
1413  const QString comment =
1414  StringUtil::quoteHtmlChars( curNode->contentDescription()->asUnicodeString(), true );
1415 
1416  const QString fileName;
1417  mNodeHelper->writeNodeToTempFile( curNode );
1418  const QString dir = QApplication::isRightToLeft() ? QLatin1String("rtl") : QLatin1String("ltr") ;
1419 
1420  QString htmlStr = QLatin1String("<table cellspacing=\"1\" class=\"textAtm\">"
1421  "<tr class=\"textAtmH\"><td dir=\"") + dir + QLatin1String("\">");
1422  if ( !fileName.isEmpty() )
1423  htmlStr += QLatin1String("<a href=\"") + mNodeHelper->asHREF( curNode, QLatin1String("body") ) + QLatin1String("\">")
1424  + label + QLatin1String("</a>");
1425  else
1426  htmlStr += label;
1427  if ( !comment.isEmpty() )
1428  htmlStr += QLatin1String("<br/>") + comment;
1429  htmlStr += QLatin1String("</td></tr><tr class=\"textAtmB\"><td>");
1430 
1431  htmlWriter()->queue( htmlStr );
1432  }
1433  // process old style not-multipart Mailman messages to
1434  // enable verification of the embedded messages' signatures
1435  if ( !isMailmanMessage( curNode ) ||
1436  !processMailmanMessage( curNode ) ) {
1437  const QString oldPlainText = mPlainTextContent;
1438  writeBodyString( mRawDecryptedBody, NodeHelper::fromAsString( curNode ),
1439  codecFor( curNode ), result, !bDrawFrame );
1440 
1441  // Revert changes to mPlainTextContent made by writeBodyString if this is not the first
1442  // text part. The plain text content shall not contain any text/plain attachment, as it is content
1443  // of the main text node.
1444  if ( !isFirstTextPart ) {
1445  mPlainTextContent = oldPlainText;
1446  }
1447  mNodeHelper->setNodeDisplayedEmbedded( curNode, true );
1448  }
1449  if( bDrawFrame && htmlWriter() ) {
1450  htmlWriter()->queue( QLatin1String("</td></tr></table>") );
1451  }
1452 
1453  return true;
1454 }
1455 
1456 void ObjectTreeParser::stdChildHandling( KMime::Content * child ) {
1457  if ( !child )
1458  return;
1459 
1460  ObjectTreeParser otp( *this );
1461  otp.setShowOnlyOneMimePart( false );
1462  otp.parseObjectTreeInternal( child );
1463  copyContentFrom( &otp );
1464 }
1465 
1466 QString ObjectTreeParser::defaultToltecReplacementText()
1467 {
1468  return i18n( "This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
1469  "Microsoft Outlook in combination with the Toltec connector." );
1470 }
1471 
1472 bool ObjectTreeParser::processToltecMail( KMime::Content *node )
1473 {
1474  if ( !node || !htmlWriter() || !GlobalSettings::self()->showToltecReplacementText() ||
1475  !NodeHelper::isToltecMessage( node ) || mShowRawToltecMail )
1476  return false;
1477 
1478  htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
1479  htmlWriter()->queue( QLatin1String("<br/><br/><a href=\"kmail:showRawToltecMail\">") +
1480  i18n( "Show Raw Message" ) + QLatin1String("</a>") );
1481  return true;
1482 }
1483 
1484 bool ObjectTreeParser::processMultiPartMixedSubtype( KMime::Content * node, ProcessResult & )
1485 {
1486  if ( processToltecMail( node ) ) {
1487  return true;
1488  }
1489 
1490  KMime::Content * child = MessageCore::NodeHelper::firstChild( node );
1491  if ( !child )
1492  return false;
1493 
1494  // normal treatment of the parts in the mp/mixed container
1495  stdChildHandling( child );
1496  return true;
1497 }
1498 
1499 bool ObjectTreeParser::processMultiPartAlternativeSubtype( KMime::Content * node, ProcessResult & )
1500 {
1501  KMime::Content * child = MessageCore::NodeHelper::firstChild( node );
1502  if ( !child )
1503  return false;
1504 
1505  KMime::Content* dataHtml = findType( child, "text/html", false, true );
1506  KMime::Content* dataPlain = findType( child, "text/plain", false, true );
1507 
1508  if ( !dataHtml ) {
1509  // If we didn't find the HTML part as the first child of the multipart/alternative, it might
1510  // be that this is a HTML message with images, and text/plain and multipart/related are the
1511  // immediate children of this multipart/alternative node.
1512  // In this case, the HTML node is a child of multipart/related.
1513  dataHtml = findType( child, "multipart/related", false, true );
1514 
1515  // Still not found? Stupid apple mail actually puts the attachments inside of the
1516  // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed
1517  // here.
1518  // Do this only when prefering HTML mail, though, since otherwise the attachments are hidden
1519  // when displaying plain text.
1520  if ( !dataHtml && mSource->htmlMail() ) {
1521  dataHtml = findType( child, "multipart/mixed", false, true );
1522  }
1523  }
1524 
1525  // If there is no HTML writer, process both the HTML and the plain text nodes, as we're collecting
1526  // the plainTextContent and the htmlContent
1527  if ( !htmlWriter() ) {
1528  if ( dataPlain ) {
1529  stdChildHandling( dataPlain );
1530  }
1531  if ( dataHtml ) {
1532  stdChildHandling( dataHtml );
1533  }
1534  return true;
1535  }
1536 
1537  if ( ( mSource->htmlMail() && dataHtml) ||
1538  (dataHtml && dataPlain && dataPlain->body().isEmpty()) ) {
1539  if ( dataPlain )
1540  mNodeHelper->setNodeProcessed( dataPlain, false);
1541  stdChildHandling( dataHtml );
1542  mSource->setHtmlMode( Util::MultipartHtml );
1543  return true;
1544  }
1545 
1546  if ( !htmlWriter() || (!mSource->htmlMail() && dataPlain) ) {
1547  mNodeHelper->setNodeProcessed( dataHtml, false );
1548  stdChildHandling( dataPlain );
1549  mSource->setHtmlMode( Util::MultipartPlain );
1550  return true;
1551  }
1552 
1553  stdChildHandling( child );
1554  return true;
1555 }
1556 
1557 bool ObjectTreeParser::processMultiPartDigestSubtype( KMime::Content * node, ProcessResult & result ) {
1558  return processMultiPartMixedSubtype( node, result );
1559 }
1560 
1561 bool ObjectTreeParser::processMultiPartParallelSubtype( KMime::Content * node, ProcessResult & result ) {
1562  return processMultiPartMixedSubtype( node, result );
1563 }
1564 
1565 bool ObjectTreeParser::processMultiPartSignedSubtype( KMime::Content * node, ProcessResult & )
1566 {
1567  KMime::Content * child = MessageCore::NodeHelper::firstChild( node );
1568  if ( node->contents().size() != 2 ) {
1569  kDebug() << "mulitpart/signed must have exactly two child parts!" << endl
1570  << "processing as multipart/mixed";
1571  if ( child )
1572  stdChildHandling( child );
1573  return child;
1574  }
1575 
1576  KMime::Content * signedData = child;
1577  assert( signedData );
1578 
1579  KMime::Content * signature = node->contents().at(1);
1580  assert( signature );
1581 
1582  mNodeHelper->setNodeProcessed( signature, true);
1583 
1584  if ( !includeSignatures() ) {
1585  stdChildHandling( signedData );
1586  return true;
1587  }
1588 
1589  QString protocolContentType = node->contentType()->parameter( QLatin1String("protocol") ).toLower();
1590  const QString signatureContentType = QLatin1String(signature->contentType()->mimeType().toLower());
1591  if ( protocolContentType.isEmpty() ) {
1592  kWarning() << "Message doesn't set the protocol for the multipart/signed content-type, "
1593  "using content-type of the signature:" << signatureContentType;
1594  protocolContentType = signatureContentType;
1595  }
1596 
1597  const Kleo::CryptoBackend::Protocol *protocol = 0;
1598  if ( protocolContentType == QLatin1String( "application/pkcs7-signature" ) ||
1599  protocolContentType == QLatin1String( "application/x-pkcs7-signature" ) )
1600  protocol = Kleo::CryptoBackendFactory::instance()->smime();
1601  else if ( protocolContentType == QLatin1String( "application/pgp-signature" ) ||
1602  protocolContentType == QLatin1String( "application/x-pgp-signature" ) )
1603  protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
1604 
1605  if ( !protocol ) {
1606  mNodeHelper->setNodeProcessed( signature, true );
1607  stdChildHandling( signedData );
1608  return true;
1609  }
1610 
1611  CryptoProtocolSaver saver( this, protocol );
1612  mNodeHelper->setSignatureState( node, KMMsgFullySigned);
1613 
1614  writeOpaqueOrMultipartSignedData( signedData, *signature,
1615  NodeHelper::fromAsString( node ) );
1616  return true;
1617 }
1618 
1619 bool ObjectTreeParser::processMultiPartEncryptedSubtype( KMime::Content * node, ProcessResult & result )
1620 {
1621  KMime::Content * child = MessageCore::NodeHelper::firstChild( node );
1622  if ( !child )
1623  return false;
1624 
1625  if ( keepEncryptions() ) {
1626  mNodeHelper->setEncryptionState( node, KMMsgFullyEncrypted );
1627  const QByteArray cstr = node->decodedContent();
1628  if ( htmlWriter() ) {
1629  writeBodyString( cstr, NodeHelper::fromAsString( node ),
1630  codecFor( node ), result, false );
1631  }
1632  mRawDecryptedBody += cstr;
1633  return true;
1634  }
1635 
1636  const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
1637 
1638  /*
1639  ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
1640  */
1641  KMime::Content* data = findType( child, "application/octet-stream", false, true );
1642  if ( data ) {
1643  useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
1644  }
1645  if ( !data ) {
1646  data = findType( child, "application/pkcs7-mime", false, true );
1647  if ( data ) {
1648  useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
1649  }
1650  }
1651  /*
1652  ---------------------------------------------------------------------------------------------------------------
1653  */
1654 
1655  if ( !data ) {
1656  stdChildHandling( child );
1657  return true;
1658  }
1659 
1660  CryptoProtocolSaver cpws( this, useThisCryptProto );
1661 
1662  KMime::Content * dataChild = MessageCore::NodeHelper::firstChild( data );
1663  if ( dataChild ) {
1664  stdChildHandling( dataChild );
1665  return true;
1666  }
1667 
1668  mNodeHelper->setEncryptionState( node, KMMsgFullyEncrypted );
1669 
1670  if ( !mSource->decryptMessage() ) {
1671  writeDeferredDecryptionBlock();
1672  mNodeHelper->setNodeProcessed( data, false );// Set the data node to done to prevent it from being processed
1673  return true;
1674  }
1675 
1676  PartMetaData messagePart;
1677  // if we already have a decrypted node for this encrypted node, don't do the decryption again
1678  if ( KMime::Content * newNode = mNodeHelper->decryptedNodeForContent( data ) )
1679  {
1680 // if( NodeHelper::nodeProcessed( data ) )
1681  ObjectTreeParser otp( this );
1682  otp.parseObjectTreeInternal( newNode );
1683  copyContentFrom( &otp );
1684  messagePart = mNodeHelper->partMetaData( node );
1685  } else {
1686  QByteArray decryptedData;
1687  bool signatureFound;
1688  std::vector<GpgME::Signature> signatures;
1689  bool passphraseError;
1690  bool actuallyEncrypted = true;
1691  bool decryptionStarted;
1692 
1693  bool bOkDecrypt = okDecryptMIME( *data,
1694  decryptedData,
1695  signatureFound,
1696  signatures,
1697  true,
1698  passphraseError,
1699  actuallyEncrypted,
1700  decryptionStarted,
1701  messagePart );
1702  //kDebug() << "decrypted, signed?:" << signatureFound;
1703 
1704  if ( decryptionStarted ) {
1705  writeDecryptionInProgressBlock();
1706  return true;
1707  }
1708 
1709  mNodeHelper->setNodeProcessed( data, false ); // Set the data node to done to prevent it from being processed
1710 
1711  // paint the frame
1712  if ( htmlWriter() ) {
1713  messagePart.isDecryptable = bOkDecrypt;
1714  messagePart.isEncrypted = true;
1715  messagePart.isSigned = false;
1716  htmlWriter()->queue( writeSigstatHeader( messagePart,
1717  cryptoProtocol(),
1718  NodeHelper::fromAsString( node ) ) );
1719  }
1720 
1721  if ( bOkDecrypt ) {
1722  // Note: Multipart/Encrypted might also be signed
1723  // without encapsulating a nicely formatted
1724  // ~~~~~~~ Multipart/Signed part.
1725  // (see RFC 3156 --> 6.2)
1726  // In this case we paint a _2nd_ frame inside the
1727  // encryption frame, but we do _not_ show a respective
1728  // encapsulated MIME part in the Mime Tree Viewer
1729  // since we do want to show the _true_ structure of the
1730  // message there - not the structure that the sender's
1731  // MUA 'should' have sent. :-D (khz, 12.09.2002)
1732  //
1733  if ( signatureFound ) {
1734  writeOpaqueOrMultipartSignedData( 0,
1735  *node,
1736  NodeHelper::fromAsString( node ),
1737  false,
1738  &decryptedData,
1739  signatures,
1740  false );
1741  mNodeHelper->setSignatureState( node, KMMsgFullySigned);
1742  //kDebug() << "setting FULLY SIGNED to:" << node;
1743  } else {
1744  decryptedData = KMime::CRLFtoLF( decryptedData ); //KMime works with LF only inside insertAndParseNewChildNode
1745 
1746  createAndParseTempNode( node, decryptedData.constData(),"encrypted data" );
1747  }
1748  } else {
1749  mRawDecryptedBody += decryptedData;
1750  if ( htmlWriter() ) {
1751  // print the error message that was returned in decryptedData
1752  // (utf8-encoded)
1753  htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
1754  }
1755  }
1756 
1757 
1758  mNodeHelper->setPartMetaData( node, messagePart );
1759  }
1760 
1761  if ( htmlWriter() )
1762  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1763  return true;
1764 }
1765 
1766 
1767 bool ObjectTreeParser::processMessageRfc822Subtype( KMime::Content * node, ProcessResult & )
1768 {
1769  if ( htmlWriter() && !attachmentStrategy()->inlineNestedMessages() && !showOnlyOneMimePart() )
1770  return false;
1771 
1772  PartMetaData messagePart;
1773  messagePart.isEncrypted = false;
1774  messagePart.isSigned = false;
1775  messagePart.isEncapsulatedRfc822Message = true;
1776 
1777  KMime::Message::Ptr message = node->bodyAsMessage();
1778  if ( !message ) {
1779  kWarning() << "Node is of type message/rfc822 but doesn't have a message!";
1780  }
1781 
1782  if ( htmlWriter() && message ) {
1783 
1784  // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists,
1785  // since the user can click the link and expect to have normal attachment operations there.
1786  mNodeHelper->writeNodeToTempFile( message.get() );
1787 
1788  // Paint the frame header
1789  htmlWriter()->queue( writeSigstatHeader( messagePart,
1790  cryptoProtocol(),
1791  message->from()->asUnicodeString(),
1792  message.get() ) );
1793 
1794  // Paint the message header
1795  htmlWriter()->queue( mSource->createMessageHeader( message.get() ) );
1796 
1797  // Process the message, i.e. paint it by processing it with an OTP
1798  ObjectTreeParser otp( this );
1799  otp.parseObjectTreeInternal( message.get() );
1800 
1801  // Don't add the resulting textual content to our textual content here.
1802  // That is unwanted when inline forwarding a message, since the encapsulated message will
1803  // already be in the forward message as attachment, so don't duplicate the textual content
1804  // by adding it to the inline body as well
1805 
1806  // Paint the frame footer
1807  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1808  }
1809 
1810  mNodeHelper->setNodeDisplayedEmbedded( node, true );
1811  mNodeHelper->setPartMetaData( node, messagePart );
1812 
1813  return true;
1814 }
1815 
1816 
1817 bool ObjectTreeParser::processApplicationOctetStreamSubtype( KMime::Content * node, ProcessResult & result )
1818 {
1819  if ( KMime::Content * child = mNodeHelper->decryptedNodeForContent( node ) ) {
1820  ObjectTreeParser otp( this );
1821  otp.parseObjectTreeInternal( child );
1822  copyContentFrom( &otp );
1823  return true;
1824  }
1825 
1826  const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
1827  if ( node->parent()
1828  && node->parent()->contentType()->mimeType() == "multipart/encrypted" ) {
1829  mNodeHelper->setEncryptionState( node, KMMsgFullyEncrypted );
1830  if ( keepEncryptions() ) {
1831  const QByteArray cstr = node->decodedContent();
1832  if ( htmlWriter() ) {
1833  writeBodyString( cstr, NodeHelper::fromAsString( node ),
1834  codecFor( node ), result, false );
1835  }
1836  mRawDecryptedBody += cstr;
1837  } else if ( !mSource->decryptMessage() ) {
1838  writeDeferredDecryptionBlock();
1839  } else {
1840  /*
1841  ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
1842  */
1843  PartMetaData messagePart;
1844  setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
1845  QByteArray decryptedData;
1846  bool signatureFound;
1847  std::vector<GpgME::Signature> signatures;
1848  bool passphraseError;
1849  bool actuallyEncrypted = true;
1850  bool decryptionStarted;
1851 
1852  bool bOkDecrypt = okDecryptMIME( *node,
1853  decryptedData,
1854  signatureFound,
1855  signatures,
1856  true,
1857  passphraseError,
1858  actuallyEncrypted,
1859  decryptionStarted,
1860  messagePart );
1861 
1862  if ( decryptionStarted ) {
1863  writeDecryptionInProgressBlock();
1864  return true;
1865  }
1866 
1867  // paint the frame
1868  if ( htmlWriter() ) {
1869  messagePart.isDecryptable = bOkDecrypt;
1870  messagePart.isEncrypted = true;
1871  messagePart.isSigned = false;
1872  htmlWriter()->queue( writeSigstatHeader( messagePart,
1873  cryptoProtocol(),
1874  NodeHelper::fromAsString( node ) ) );
1875  }
1876 
1877  if ( bOkDecrypt ) {
1878  // fixing the missing attachments bug #1090-b
1879  createAndParseTempNode( node, decryptedData.constData(), "encrypted data" );
1880  } else {
1881  mRawDecryptedBody += decryptedData;
1882  if ( htmlWriter() ) {
1883  // print the error message that was returned in decryptedData
1884  // (utf8-encoded)
1885  htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
1886  }
1887  }
1888 
1889  if ( htmlWriter() )
1890  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1891  mNodeHelper->setPartMetaData( node, messagePart );
1892  }
1893  return true;
1894  }
1895  setCryptoProtocol( oldUseThisCryptPlug );
1896  return false;
1897 }
1898 
1899 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( KMime::Content * node, ProcessResult & result )
1900 {
1901  if ( KMime::Content * child = mNodeHelper->decryptedNodeForContent( node ) ) {
1902  ObjectTreeParser otp( this );
1903  otp.parseObjectTreeInternal( child );
1904  copyContentFrom( &otp );
1905  return true;
1906  }
1907 
1908  if ( node->head().isEmpty() )
1909  return false;
1910 
1911  const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
1912  if ( !smimeCrypto )
1913  return false;
1914 
1915  const QString smimeType = node->contentType()->parameter(QLatin1String("smime-type")).toLower();
1916 
1917  if ( smimeType == QLatin1String( "certs-only" ) ) {
1918  result.setNeverDisplayInline( true );
1919  if ( !htmlWriter() )
1920  return false;
1921 
1922  if ( !GlobalSettings::self()->autoImportKeys() )
1923  return false;
1924 
1925  const QByteArray certData = node->decodedContent();
1926 
1927  Kleo::ImportJob *import = smimeCrypto->importJob();
1928  KleoJobExecutor executor;
1929  const GpgME::ImportResult res = executor.exec( import, certData );
1930  writeCertificateImportResult( res );
1931  return true;
1932  }
1933 
1934  CryptoProtocolSaver cpws( this, smimeCrypto );
1935 
1936  bool isSigned = ( smimeType == QLatin1String( "signed-data" ) );
1937  bool isEncrypted = ( smimeType == QLatin1String( "enveloped-data" ) );
1938 
1939  // Analyze "signTestNode" node to find/verify a signature.
1940  // If zero this verification was successfully done after
1941  // decrypting via recursion by insertAndParseNewChildNode().
1942  KMime::Content* signTestNode = isEncrypted ? 0 : node;
1943 
1944 
1945  // We try decrypting the content
1946  // if we either *know* that it is an encrypted message part
1947  // or there is neither signed nor encrypted parameter.
1948  if ( !isSigned ) {
1949  if ( isEncrypted ) {
1950  ;//kDebug() << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data";
1951  } else {
1952  ;//kDebug() << "pkcs7 mime - type unknown - enveloped (encrypted) data ?";
1953  }
1954  QByteArray decryptedData;
1955  PartMetaData messagePart;
1956  messagePart.isEncrypted = true;
1957  messagePart.isSigned = false;
1958  bool signatureFound;
1959  std::vector<GpgME::Signature> signatures;
1960  bool passphraseError;
1961  bool actuallyEncrypted = true;
1962  bool decryptionStarted;
1963 
1964  if ( !mSource->decryptMessage() ) {
1965  writeDeferredDecryptionBlock();
1966  isEncrypted = true;
1967  signTestNode = 0; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
1968  } else {
1969  const bool bOkDecrypt = okDecryptMIME( *node, decryptedData, signatureFound, signatures,
1970  false, passphraseError, actuallyEncrypted,
1971  decryptionStarted, messagePart );
1972  //kDebug() << "PKCS7 found signature?" << signatureFound;
1973  if ( decryptionStarted ) {
1974  writeDecryptionInProgressBlock();
1975  return true;
1976  }
1977 
1978  if ( bOkDecrypt ) {
1979  //kDebug() << "pkcs7 mime - encryption found - enveloped (encrypted) data !";
1980  isEncrypted = true;
1981  mNodeHelper->setEncryptionState( node, KMMsgFullyEncrypted );
1982  if( signatureFound )
1983  mNodeHelper->setSignatureState( node, KMMsgFullySigned );
1984  signTestNode = 0;
1985  // paint the frame
1986  messagePart.isDecryptable = true;
1987  if ( htmlWriter() )
1988  htmlWriter()->queue( writeSigstatHeader( messagePart,
1989  cryptoProtocol(),
1990  NodeHelper::fromAsString( node ) ) );
1991  createAndParseTempNode( node, decryptedData.constData(), "encrypted data" );
1992  if ( htmlWriter() )
1993  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1994  } else {
1995  // decryption failed, which could be because the part was encrypted but
1996  // decryption failed, or because we didn't know if it was encrypted, tried,
1997  // and failed. If the message was not actually encrypted, we continue
1998  // assuming it's signed
1999  if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
2000  isEncrypted = true;
2001  signTestNode = 0;
2002  }
2003 
2004  if ( isEncrypted ) {
2005  //kDebug() << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !";
2006  // paint the frame
2007  messagePart.isDecryptable = false;
2008  if ( htmlWriter() ) {
2009  htmlWriter()->queue( writeSigstatHeader( messagePart,
2010  cryptoProtocol(),
2011  NodeHelper::fromAsString( node ) ) );
2012  assert( mSource->decryptMessage() ); // handled above
2013  writePartIcon( node );
2014  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
2015  }
2016  } else {
2017  //kDebug() << "pkcs7 mime - NO encryption found";
2018  }
2019  }
2020  }
2021  if ( isEncrypted )
2022  mNodeHelper->setEncryptionState( node, KMMsgFullyEncrypted );
2023  mNodeHelper->setPartMetaData( node, messagePart );
2024  }
2025 
2026  // We now try signature verification if necessarry.
2027  if ( signTestNode ) {
2028  if ( isSigned ) {
2029  ;//kDebug() << "pkcs7 mime == S/MIME TYPE: opaque signed data";
2030  } else {
2031  ;//kDebug() << "pkcs7 mime - type unknown - opaque signed data ?";
2032  }
2033 
2034  bool sigFound = writeOpaqueOrMultipartSignedData( 0,
2035  *signTestNode,
2036  NodeHelper::fromAsString( node ),
2037  true,
2038  0,
2039  std::vector<GpgME::Signature>(),
2040  isEncrypted );
2041  if ( sigFound ) {
2042  if ( !isSigned ) {
2043  //kDebug() << "pkcs7 mime - signature found - opaque signed data !";
2044  isSigned = true;
2045  }
2046 
2047  mNodeHelper->setSignatureState( signTestNode, KMMsgFullySigned );
2048  if ( signTestNode != node )
2049  mNodeHelper->setSignatureState( node, KMMsgFullySigned );
2050  } else {
2051  //kDebug() << "pkcs7 mime - NO signature found :-(";
2052  }
2053  }
2054 
2055  return isSigned || isEncrypted;
2056 }
2057 
2058 bool ObjectTreeParser::decryptChiasmus( const QByteArray& data, QByteArray& bodyDecoded, QString& errorText )
2059 {
2060  const Kleo::CryptoBackend::Protocol * chiasmus =
2061  Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
2062  if ( !chiasmus )
2063  return false;
2064 
2065  const std::auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
2066  if ( !listjob.get() ) {
2067  errorText = i18n( "Chiasmus backend does not offer the "
2068  "\"x-obtain-keys\" function. Please report this bug." );
2069  return false;
2070  }
2071 
2072  if ( listjob->exec() ) {
2073  errorText = i18n( "Chiasmus Backend Error" );
2074  return false;
2075  }
2076 
2077  const QVariant result = listjob->property( "result" );
2078  if ( result.type() != QVariant::StringList ) {
2079  errorText = i18n( "Unexpected return value from Chiasmus backend: "
2080  "The \"x-obtain-keys\" function did not return a "
2081  "string list. Please report this bug." );
2082  return false;
2083  }
2084 
2085  const QStringList keys = result.toStringList();
2086  if ( keys.empty() ) {
2087  errorText = i18n( "No keys have been found. Please check that a "
2088  "valid key path has been set in the Chiasmus "
2089  "configuration." );
2090  return false;
2091  }
2092 
2093  AutoQPointer<ChiasmusKeySelector> selectorDlg( new ChiasmusKeySelector( /*mReader*/0, i18n( "Chiasmus Decryption Key Selection" ),
2094  keys, GlobalSettings::chiasmusDecryptionKey(),
2095  GlobalSettings::chiasmusDecryptionOptions() ) );
2096 
2097  if ( selectorDlg->exec() != KDialog::Accepted || !selectorDlg ) {
2098  return false;
2099  }
2100  GlobalSettings::setChiasmusDecryptionOptions( selectorDlg->options() );
2101  GlobalSettings::setChiasmusDecryptionKey( selectorDlg->key() );
2102  assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
2103 
2104  Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
2105  if ( !job ) {
2106  errorText = i18n( "Chiasmus backend does not offer the "
2107  "\"x-decrypt\" function. Please report this bug." );
2108  return false;
2109  }
2110 
2111  if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
2112  !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
2113  !job->setProperty( "input", data ) ) {
2114  errorText = i18n( "The \"x-decrypt\" function does not accept "
2115  "the expected parameters. Please report this bug." );
2116  return false;
2117  }
2118 
2119  if ( job->exec() ) {
2120  errorText = i18n( "Chiasmus Decryption Error" );
2121  return false;
2122  }
2123 
2124  const QVariant resultData = job->property( "result" );
2125  if ( resultData.type() != QVariant::ByteArray ) {
2126  errorText = i18n( "Unexpected return value from Chiasmus backend: "
2127  "The \"x-decrypt\" function did not return a "
2128  "byte array. Please report this bug." );
2129  return false;
2130  }
2131  bodyDecoded = resultData.toByteArray();
2132  return true;
2133  }
2134 
2135  bool ObjectTreeParser::processApplicationChiasmusTextSubtype( KMime::Content * curNode, ProcessResult & result )
2136  {
2137  if ( !htmlWriter() ) {
2138  mRawDecryptedBody = curNode->decodedContent();
2139 
2140  // ### Surely this is totally wrong? The decoded text of this node is just garbage, since it is
2141  // encrypted. This whole if statement should be removed, and the decrypted body
2142  // should be added to mPlainTextContent. Needs testing with Chiasmus though, which I don't have.
2143  mPlainTextContent += curNode->decodedText();
2144  mPlainTextContentCharset = NodeHelper::charset( curNode );
2145  return true;
2146  }
2147 
2148  QByteArray decryptedBody;
2149  QString errorText;
2150  const QByteArray data = curNode->decodedContent();
2151  bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
2152  PartMetaData messagePart;
2153  messagePart.isDecryptable = bOkDecrypt;
2154  messagePart.isEncrypted = true;
2155  messagePart.isSigned = false;
2156  messagePart.errorText = errorText;
2157  if ( htmlWriter() )
2158  htmlWriter()->queue( writeSigstatHeader( messagePart,
2159  0, //cryptPlugWrapper(),
2160  NodeHelper::fromAsString( curNode ) ) );
2161  const QByteArray body = bOkDecrypt ? decryptedBody : data;
2162  const QString chiasmusCharset = curNode->contentType()->parameter(QLatin1String("chiasmus-charset"));
2163  const QTextCodec* aCodec = chiasmusCharset.isEmpty() ? codecFor( curNode )
2164  : NodeHelper::codecForName( chiasmusCharset.toLatin1() );
2165  htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false /*decorate*/ ) );
2166  result.setInlineEncryptionState( KMMsgFullyEncrypted );
2167  if ( htmlWriter() )
2168  htmlWriter()->queue( writeSigstatFooter( messagePart ) );
2169  mNodeHelper->setPartMetaData( curNode, messagePart );
2170  return true;
2171 }
2172 
2173 void ObjectTreeParser::writeBodyString( const QByteArray & bodyString,
2174  const QString & fromAddress,
2175  const QTextCodec * codec,
2176  ProcessResult & result,
2177  bool decorate )
2178 {
2179  assert( codec );
2180  KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
2181  KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
2182  writeBodyStr( bodyString, codec, fromAddress,
2183  inlineSignatureState, inlineEncryptionState, decorate );
2184  result.setInlineSignatureState( inlineSignatureState );
2185  result.setInlineEncryptionState( inlineEncryptionState );
2186 }
2187 
2188 void ObjectTreeParser::writePartIcon( KMime::Content * msgPart, bool inlineImage )
2189 {
2190  if ( !htmlWriter() || !msgPart )
2191  return;
2192 
2193  const QString name = msgPart->contentType()->name();
2194  QString label = name.isEmpty() ? NodeHelper::fileName( msgPart ) : name;
2195  if ( label.isEmpty() )
2196  label = i18nc( "display name for an unnamed attachment", "Unnamed" );
2197  label = StringUtil::quoteHtmlChars( label, true );
2198 
2199  QString comment = msgPart->contentDescription()->asUnicodeString();
2200  comment = StringUtil::quoteHtmlChars( comment, true );
2201  if ( label == comment )
2202  comment.clear();
2203 
2204  QString href = mNodeHelper->asHREF( msgPart, QLatin1String("body") );
2205 
2206  if ( inlineImage ) {
2207  const QString fileName = mNodeHelper->writeNodeToTempFile( msgPart );
2208  // show the filename of the image below the embedded image
2209  htmlWriter()->queue( QLatin1String("<div><a href=\"") + href + QLatin1String("\">"
2210  "<img src=\"file:///") + fileName + QLatin1String("\" border=\"0\" style=\"max-width: 100%\"/></a>"
2211  "</div>"
2212  "<div><a href=\"") + href + QLatin1String("\">") + label + QLatin1String("</a>"
2213  "</div>"
2214  "<div>") + comment + QLatin1String("</div><br/>") );
2215  } else {
2216  // show the filename next to the image
2217  const QString iconName = mNodeHelper->iconName( msgPart );
2218  if( iconName.right( 14 ) == QLatin1String( "mime_empty.png" ) ) {
2219  mNodeHelper->magicSetType( msgPart );
2220  //iconName = mNodeHelper->iconName( msgPart );
2221  }
2222  htmlWriter()->queue( QLatin1String("<div><a href=\"") + href + QLatin1String("\"><img src=\"file:///") +
2223  iconName + QLatin1String("\" border=\"0\" style=\"max-width: 100%\" alt=\"\"/>") + label +
2224  QLatin1String("</a></div>"
2225  "<div>") + comment +QLatin1String( "</div><br/>") );
2226  }
2227 }
2228 
2229 static const int SIG_FRAME_COL_UNDEF = 99;
2230 #define SIG_FRAME_COL_RED -1
2231 #define SIG_FRAME_COL_YELLOW 0
2232 #define SIG_FRAME_COL_GREEN 1
2233 QString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
2234  int status_code,
2235  GpgME::Signature::Summary summary,
2236  int& frameColor,
2237  bool& showKeyInfos )
2238 {
2239  // note: At the moment frameColor and showKeyInfos are
2240  // used for CMS only but not for PGP signatures
2241  // pending(khz): Implement usage of these for PGP sigs as well.
2242  showKeyInfos = true;
2243  QString result;
2244  if( cryptProto ) {
2245  if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
2246  // process enum according to it's definition to be read in
2247  // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
2248  switch( status_code ) {
2249  case 0: // GPGME_SIG_STAT_NONE
2250  result = i18n("Error: Signature not verified");
2251  break;
2252  case 1: // GPGME_SIG_STAT_GOOD
2253  result = i18n("Good signature");
2254  break;
2255  case 2: // GPGME_SIG_STAT_BAD
2256  result = i18n("<b>Bad</b> signature");
2257  break;
2258  case 3: // GPGME_SIG_STAT_NOKEY
2259  result = i18n("No public key to verify the signature");
2260  break;
2261  case 4: // GPGME_SIG_STAT_NOSIG
2262  result = i18n("No signature found");
2263  break;
2264  case 5: // GPGME_SIG_STAT_ERROR
2265  result = i18n("Error verifying the signature");
2266  break;
2267  case 6: // GPGME_SIG_STAT_DIFF
2268  result = i18n("Different results for signatures");
2269  break;
2270  /* PENDING(khz) Verify exact meaning of the following values:
2271  case 7: // GPGME_SIG_STAT_GOOD_EXP
2272  return i18n("Signature certificate is expired");
2273  break;
2274  case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
2275  return i18n("One of the certificate's keys is expired");
2276  break;
2277  */
2278  default:
2279  result.clear(); // do *not* return a default text here !
2280  break;
2281  }
2282  }
2283  else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
2284  // process status bits according to SigStatus_...
2285  // definitions in kdenetwork/libkdenetwork/cryptplug.h
2286 
2287  if( summary == GpgME::Signature::None ) {
2288  result = i18n("No status information available.");
2289  frameColor = SIG_FRAME_COL_YELLOW;
2290  showKeyInfos = false;
2291  return result;
2292  }
2293 
2294  if( summary & GpgME::Signature::Valid ) {
2295  result = i18n("Good signature.");
2296  // Note:
2297  // Here we are work differently than KMail did before!
2298  //
2299  // The GOOD case ( == sig matching and the complete
2300  // certificate chain was verified and is valid today )
2301  // by definition does *not* show any key
2302  // information but just states that things are OK.
2303  // (khz, according to LinuxTag 2002 meeting)
2304  frameColor = SIG_FRAME_COL_GREEN;
2305  showKeyInfos = false;
2306  return result;
2307  }
2308 
2309  // we are still there? OK, let's test the different cases:
2310 
2311  // we assume green, test for yellow or red (in this order!)
2312  frameColor = SIG_FRAME_COL_GREEN;
2313  QString result2;
2314  if( summary & GpgME::Signature::KeyExpired ){
2315  // still is green!
2316  result2 += i18n("One key has expired.");
2317  }
2318  if( summary & GpgME::Signature::SigExpired ){
2319  // and still is green!
2320  result2 += i18n("The signature has expired.");
2321  }
2322 
2323  // test for yellow:
2324  if( summary & GpgME::Signature::KeyMissing ) {
2325  result2 += i18n("Unable to verify: key missing.");
2326  // if the signature certificate is missing
2327  // we cannot show information on it
2328  showKeyInfos = false;
2329  frameColor = SIG_FRAME_COL_YELLOW;
2330  }
2331  if( summary & GpgME::Signature::CrlMissing ){
2332  result2 += i18n("CRL not available.");
2333  frameColor = SIG_FRAME_COL_YELLOW;
2334  }
2335  if( summary & GpgME::Signature::CrlTooOld ){
2336  result2 += i18n("Available CRL is too old.");
2337  frameColor = SIG_FRAME_COL_YELLOW;
2338  }
2339  if( summary & GpgME::Signature::BadPolicy ){
2340  result2 += i18n("A policy was not met.");
2341  frameColor = SIG_FRAME_COL_YELLOW;
2342  }
2343  if( summary & GpgME::Signature::SysError ){
2344  result2 += i18n("A system error occurred.");
2345  // if a system error occurred
2346  // we cannot trust any information
2347  // that was given back by the plug-in
2348  showKeyInfos = false;
2349  frameColor = SIG_FRAME_COL_YELLOW;
2350  }
2351 
2352  // test for red:
2353  if( summary & GpgME::Signature::KeyRevoked ){
2354  // this is red!
2355  result2 += i18n("One key has been revoked.");
2356  frameColor = SIG_FRAME_COL_RED;
2357  }
2358  if( summary & GpgME::Signature::Red ) {
2359  if( result2.isEmpty() )
2360  // Note:
2361  // Here we are work differently than KMail did before!
2362  //
2363  // The BAD case ( == sig *not* matching )
2364  // by definition does *not* show any key
2365  // information but just states that things are BAD.
2366  //
2367  // The reason for this: In this case ALL information
2368  // might be falsificated, we can NOT trust the data
2369  // in the body NOT the signature - so we don't show
2370  // any key/signature information at all!
2371  // (khz, according to LinuxTag 2002 meeting)
2372  showKeyInfos = false;
2373  frameColor = SIG_FRAME_COL_RED;
2374  }
2375  else
2376  result.clear();
2377 
2378  if( SIG_FRAME_COL_GREEN == frameColor ) {
2379  result = i18n("Good signature.");
2380  } else if( SIG_FRAME_COL_RED == frameColor ) {
2381  result = i18n("<b>Bad</b> signature.");
2382  } else
2383  result.clear();
2384 
2385  if( !result2.isEmpty() ) {
2386  if( !result.isEmpty() )
2387  result.append(QLatin1String("<br />"));
2388  result.append( result2 );
2389  }
2390  }
2391  /*
2392  // add i18n support for 3rd party plug-ins here:
2393  else if ( cryptPlug->libName().contains( "yetanotherpluginname", Qt::CaseInsensitive )) {
2394 
2395  }
2396  */
2397  }
2398  return result;
2399 }
2400 
2401 
2402 static QString writeSimpleSigstatHeader( const PartMetaData &block )
2403 {
2404  QString html;
2405  html += QLatin1String("<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>");
2406 
2407  if ( block.signClass == QLatin1String( "signErr" ) ) {
2408  html += i18n( "Invalid signature." );
2409  } else if ( block.signClass == QLatin1String( "signOkKeyBad" )
2410  || block.signClass == QLatin1String( "signWarn" ) ) {
2411  html += i18n( "Not enough information to check signature validity." );
2412  } else if ( block.signClass == QLatin1String( "signOkKeyOk" ) ) {
2413 
2414  QString addr;
2415  if ( !block.signerMailAddresses.isEmpty() )
2416  addr = block.signerMailAddresses.first();
2417 
2418  QString name = addr;
2419  if ( name.isEmpty() )
2420  name = block.signer;
2421 
2422  if ( addr.isEmpty() ) {
2423  html += i18n( "Signature is valid." );
2424  } else {
2425  html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>.", addr, name );
2426  }
2427 
2428  } else {
2429  // should not happen
2430  html += i18n( "Unknown signature state" );
2431  }
2432  html += QLatin1String("</td><td align=\"right\">");
2433  html += QLatin1String("<a href=\"kmail:showSignatureDetails\">");
2434  html += i18n( "Show Details" );
2435  html += QLatin1String("</a></td></tr></table>");
2436  return html;
2437 }
2438 
2439 static QString beginVerboseSigstatHeader()
2440 {
2441  return QLatin1String("<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">");
2442 }
2443 
2444 static QString makeShowAuditLogLink( const GpgME::Error & err, const QString & auditLog ) {
2445  // more or less the same as
2446  // kleopatra/utils/auditlog.cpp:formatLink(), so any bug fixed here
2447  // equally applies there:
2448  if ( const unsigned int code = err.code() ) {
2449  if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
2450  kDebug() << "not showing link (not implemented)";
2451  return QString();
2452  } else if ( code == GPG_ERR_NO_DATA ) {
2453  kDebug() << "not showing link (not available)";
2454  return i18n("No Audit Log available");
2455  } else {
2456  return i18n("Error Retrieving Audit Log: %1", QString::fromLocal8Bit( err.asString() ) );
2457  }
2458  }
2459 
2460  if ( !auditLog.isEmpty() ) {
2461  KUrl url;
2462  url.setScheme( QLatin1String("kmail") );
2463  url.setPath( QLatin1String("showAuditLog") );
2464  url.addQueryItem( QLatin1String("log"), auditLog );
2465 
2466  return QLatin1String("<a href=\"") + url.url() + QLatin1String("\">") + i18nc("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + QLatin1String("</a>");
2467  }
2468 
2469  return QString();
2470 }
2471 
2472 static QString endVerboseSigstatHeader( const PartMetaData & pmd )
2473 {
2474  QString html;
2475  html += QLatin1String("</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">");
2476  html += QLatin1String("<a href=\"kmail:hideSignatureDetails\">");
2477  html += i18n( "Hide Details" );
2478  html += QLatin1String("</a></td></tr>");
2479  html += QLatin1String("<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">");
2480  html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
2481  html += QLatin1String("</td></tr></table>");
2482  return html;
2483 }
2484 
2485 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
2486  const Kleo::CryptoBackend::Protocol * cryptProto,
2487  const QString & fromAddress,
2488  KMime::Content *node )
2489 {
2490  const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
2491  QString signer = block.signer;
2492 
2493  QString htmlStr, simpleHtmlStr;
2494  const QString dir = QApplication::isRightToLeft() ? QLatin1String("rtl") : QLatin1String("ltr");
2495  QString cellPadding(QLatin1String("cellpadding=\"1\""));
2496 
2497  if( block.isEncapsulatedRfc822Message )
2498  {
2499  htmlStr += QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" class=\"rfc822\">"
2500  "<tr class=\"rfc822H\"><td dir=\"") + dir + QLatin1String("\">");
2501  if( node ) {
2502  htmlStr += QLatin1String("<a href=\"") + mNodeHelper->asHREF( node, QLatin1String("body") ) + QLatin1String("\">")
2503  + i18n("Encapsulated message") + QLatin1String("</a>");
2504  } else {
2505  htmlStr += i18n("Encapsulated message");
2506  }
2507  htmlStr += QLatin1String("</td></tr><tr class=\"rfc822B\"><td>");
2508  }
2509 
2510  if( block.isEncrypted ) {
2511  htmlStr += QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" class=\"encr\">"
2512  "<tr class=\"encrH\"><td dir=\"") + dir + QLatin1String("\">");
2513  if ( block.inProgress ) {
2514  htmlStr += i18n("Please wait while the message is being decrypted...");
2515  } else if( block.isDecryptable ) {
2516  htmlStr += i18n("Encrypted message");
2517  } else {
2518  htmlStr += i18n("Encrypted message (decryption not possible)");
2519  if( !block.errorText.isEmpty() ) {
2520  htmlStr += QLatin1String("<br />") + i18n("Reason: %1", block.errorText );
2521  }
2522  }
2523  htmlStr += QLatin1String("</td></tr><tr class=\"encrB\"><td>");
2524  }
2525 
2526  if ( block.isSigned && block.inProgress ) {
2527  block.signClass =QLatin1String( "signInProgress");
2528  htmlStr += QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" class=\"signInProgress\">"
2529  "<tr class=\"signInProgressH\"><td dir=\"") + dir + QLatin1String("\">");
2530  htmlStr += i18n("Please wait while the signature is being verified...");
2531  htmlStr += QLatin1String("</td></tr><tr class=\"signInProgressB\"><td>");
2532  }
2533 
2534  simpleHtmlStr = htmlStr;
2535 
2536  if( block.isSigned && !block.inProgress ) {
2537  QStringList& blockAddrs( block.signerMailAddresses );
2538  // note: At the moment frameColor and showKeyInfos are
2539  // used for CMS only but not for PGP signatures
2540  // pending(khz): Implement usage of these for PGP sigs as well.
2541  int frameColor = SIG_FRAME_COL_UNDEF;
2542  bool showKeyInfos;
2543  bool onlyShowKeyURL = false;
2544  bool cannotCheckSignature = true;
2545  QString statusStr = sigStatusToString( cryptProto,
2546  block.status_code,
2547  block.sigSummary,
2548  frameColor,
2549  showKeyInfos );
2550  // if needed fallback to english status text
2551  // that was reported by the plugin
2552  if( statusStr.isEmpty() )
2553  statusStr = block.status;
2554  if( block.technicalProblem )
2555  frameColor = SIG_FRAME_COL_YELLOW;
2556 
2557  switch( frameColor ){
2558  case SIG_FRAME_COL_RED:
2559  cannotCheckSignature = false;
2560  break;
2561  case SIG_FRAME_COL_YELLOW:
2562  cannotCheckSignature = true;
2563  break;
2564  case SIG_FRAME_COL_GREEN:
2565  cannotCheckSignature = false;
2566  break;
2567  }
2568 
2569  // compose the string for displaying the key ID
2570  // either as URL or not linked (for unknown crypto)
2571  // note: Once we can start PGP key manager programs
2572  // from within KMail we could change this and
2573  // always show the URL. (khz, 2002/06/27)
2574  QString startKeyHREF;
2575  QString keyWithWithoutURL;
2576  if ( cryptProto ) {
2577  startKeyHREF =
2578  QString::fromLatin1("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
2579  .arg( cryptProto->displayName(),
2580  cryptProto->name(),
2581  QString::fromLatin1( block.keyId ) );
2582 
2583  keyWithWithoutURL =
2584  QString::fromLatin1("%1%2</a>").arg( startKeyHREF, QString::fromLatin1("0x" + block.keyId) );
2585  } else {
2586  keyWithWithoutURL = QLatin1String("0x") + QString::fromUtf8( block.keyId );
2587  }
2588 
2589 
2590 
2591  // temporary hack: always show key information!
2592  showKeyInfos = true;
2593 
2594  // Sorry for using 'black' as null color but .isValid()
2595  // checking with QColor default c'tor did not work for
2596  // some reason.
2597  if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
2598 
2599  // new frame settings for CMS:
2600  // beautify the status string
2601  if( !statusStr.isEmpty() ) {
2602  statusStr.prepend(QLatin1String("<i>"));
2603  statusStr.append( QLatin1String("</i>"));
2604  }
2605 
2606  // special color handling: S/MIME uses only green/yellow/red.
2607  switch( frameColor ) {
2608  case SIG_FRAME_COL_RED:
2609  block.signClass = QLatin1String("signErr");//"signCMSRed";
2610  onlyShowKeyURL = true;
2611  break;
2612  case SIG_FRAME_COL_YELLOW:
2613  if( block.technicalProblem )
2614  block.signClass = QLatin1String("signWarn");
2615  else
2616  block.signClass = QLatin1String("signOkKeyBad");//"signCMSYellow";
2617  break;
2618  case SIG_FRAME_COL_GREEN:
2619  block.signClass = QLatin1String("signOkKeyOk");//"signCMSGreen";
2620  // extra hint for green case
2621  // that email addresses in DN do not match fromAddress
2622  QString greenCaseWarning;
2623  QString msgFrom( KPIMUtils::extractEmailAddress(fromAddress) );
2624  QString certificate;
2625  if( block.keyId.isEmpty() )
2626  certificate = i18n("certificate");
2627  else
2628  certificate = startKeyHREF + i18n("certificate") + QLatin1String("</a>");
2629  if( !blockAddrs.empty() ){
2630  if( !blockAddrs.contains( msgFrom, Qt::CaseInsensitive ) ) {
2631  greenCaseWarning =
2632  QLatin1String("<u>") +
2633  i18nc("Start of warning message."
2634  ,"Warning:") +
2635  QLatin1String("</u> ") +
2636  i18n("Sender's mail address is not stored "
2637  "in the %1 used for signing.", certificate) +
2638  QLatin1String("<br />") +
2639  i18n("sender: ") +
2640  msgFrom +
2641  QLatin1String("<br />") +
2642  i18n("stored: ");
2643  // We cannot use Qt's join() function here but
2644  // have to join the addresses manually to
2645  // extract the mail addresses (without '<''>')
2646  // before including it into our string:
2647  bool bStart = true;
2648  for(QStringList::ConstIterator it = blockAddrs.constBegin();
2649  it != blockAddrs.constEnd(); ++it ){
2650  if( !bStart )
2651  greenCaseWarning.append(QLatin1String(", <br />&nbsp; &nbsp;"));
2652  bStart = false;
2653  greenCaseWarning.append( KPIMUtils::extractEmailAddress(*it) );
2654  }
2655  }
2656  } else {
2657  greenCaseWarning =
2658  QLatin1String("<u>") +
2659  i18nc("Start of warning message.","Warning:") +
2660  QLatin1String("</u> ") +
2661  i18n("No mail address is stored in the %1 used for signing, "
2662  "so we cannot compare it to the sender's address %2.",
2663  certificate,
2664  msgFrom);
2665  }
2666  if( !greenCaseWarning.isEmpty() ) {
2667  if( !statusStr.isEmpty() )
2668  statusStr.append(QLatin1String("<br />&nbsp;<br />"));
2669  statusStr.append( greenCaseWarning );
2670  }
2671  break;
2672  }
2673 
2674  QString frame = QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" "
2675  "class=\"") + block.signClass + QLatin1String("\">"
2676  "<tr class=\"") + block.signClass + QLatin1String("H\"><td dir=\"") + dir + QLatin1String("\">");
2677  htmlStr += frame + beginVerboseSigstatHeader();
2678  simpleHtmlStr += frame;
2679  simpleHtmlStr += writeSimpleSigstatHeader( block );
2680  if( block.technicalProblem ) {
2681  htmlStr += block.errorText;
2682  }
2683  else if( showKeyInfos ) {
2684  if( cannotCheckSignature ) {
2685  htmlStr += i18n( "Not enough information to check "
2686  "signature. %1",
2687  keyWithWithoutURL );
2688  }
2689  else {
2690 
2691  if (block.signer.isEmpty())
2692  signer.clear();
2693  else {
2694  if( !blockAddrs.empty() ){
2695  const KUrl address = KPIMUtils::encodeMailtoUrl( blockAddrs.first() );
2696  signer = QLatin1String("<a href=\"mailto:") + QLatin1String(KUrl::toPercentEncoding( address.path() )) +
2697  QLatin1String("\">") + signer + QLatin1String("</a>");
2698  }
2699  }
2700 
2701  if( block.keyId.isEmpty() ) {
2702  if( signer.isEmpty() || onlyShowKeyURL )
2703  htmlStr += i18n( "Message was signed with unknown key." );
2704  else
2705  htmlStr += i18n( "Message was signed by %1.",
2706  signer );
2707  } else {
2708  QDateTime created = block.creationTime;
2709  if( created.isValid() ) {
2710  if( signer.isEmpty() ) {
2711  if( onlyShowKeyURL )
2712  htmlStr += i18n( "Message was signed with key %1.",
2713  keyWithWithoutURL );
2714  else
2715  htmlStr += i18n( "Message was signed on %1 with key %2.",
2716  KGlobal::locale()->formatDateTime( created ),
2717  keyWithWithoutURL );
2718  }
2719  else {
2720  if( onlyShowKeyURL )
2721  htmlStr += i18n( "Message was signed with key %1.",
2722  keyWithWithoutURL );
2723  else
2724  htmlStr += i18n( "Message was signed by %3 on %1 with key %2",
2725  KGlobal::locale()->formatDateTime( created ),
2726  keyWithWithoutURL,
2727  signer );
2728  }
2729  }
2730  else {
2731  if( signer.isEmpty() || onlyShowKeyURL )
2732  htmlStr += i18n( "Message was signed with key %1.",
2733  keyWithWithoutURL );
2734  else
2735  htmlStr += i18n( "Message was signed by %2 with key %1.",
2736  keyWithWithoutURL,
2737  signer );
2738  }
2739  }
2740  }
2741  htmlStr += QLatin1String("<br />");
2742  if( !statusStr.isEmpty() ) {
2743  htmlStr += QLatin1String("&nbsp;<br />");
2744  htmlStr += i18n( "Status: " );
2745  htmlStr += statusStr;
2746  }
2747  } else {
2748  htmlStr += statusStr;
2749  }
2750  frame = QLatin1String("</td></tr><tr class=\"") + block.signClass + QLatin1String("B\"><td>");
2751  htmlStr += endVerboseSigstatHeader( block ) + frame;
2752  simpleHtmlStr += frame;
2753 
2754  } else {
2755 
2756  // old frame settings for PGP:
2757 
2758  if( block.signer.isEmpty() || block.technicalProblem ) {
2759  block.signClass = QLatin1String("signWarn");
2760  QString frame = QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" "
2761  "class=\"") + block.signClass + QLatin1String("\">"
2762  "<tr class=\"") + block.signClass + QLatin1String("H\"><td dir=\"" )+ dir + QLatin1String("\">");
2763  htmlStr += frame + beginVerboseSigstatHeader();
2764  simpleHtmlStr += frame;
2765  simpleHtmlStr += writeSimpleSigstatHeader( block );
2766  if( block.technicalProblem ) {
2767  htmlStr += block.errorText;
2768  }
2769  else {
2770  if( !block.keyId.isEmpty() ) {
2771  QDateTime created = block.creationTime;
2772  if ( created.isValid() )
2773  htmlStr += i18n( "Message was signed on %1 with unknown key %2.",
2774  KGlobal::locale()->formatDateTime( created ),
2775  keyWithWithoutURL );
2776  else
2777  htmlStr += i18n( "Message was signed with unknown key %1.",
2778  keyWithWithoutURL );
2779  }
2780  else
2781  htmlStr += i18n( "Message was signed with unknown key." );
2782  htmlStr += QLatin1String("<br />");
2783  htmlStr += i18n( "The validity of the signature cannot be "
2784  "verified." );
2785  if( !statusStr.isEmpty() ) {
2786  htmlStr += QLatin1String("<br />");
2787  htmlStr += i18n( "Status: " );
2788  htmlStr += QLatin1String("<i>");
2789  htmlStr += statusStr;
2790  htmlStr += QLatin1String("</i>");
2791  }
2792  }
2793  frame = QLatin1String("</td></tr><tr class=\"") + block.signClass + QLatin1String("B\"><td>");
2794  htmlStr += endVerboseSigstatHeader( block ) + frame;
2795  simpleHtmlStr += frame;
2796  }
2797  else
2798  {
2799  // HTMLize the signer's user id and create mailto: link
2800  signer = StringUtil::quoteHtmlChars( signer, true );
2801  signer = QLatin1String("<a href=\"mailto:") + signer + QLatin1String("\">") + signer + QLatin1String("</a>");
2802 
2803  if (block.isGoodSignature) {
2804  if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
2805  block.signClass = QLatin1String("signOkKeyBad");
2806  else
2807  block.signClass = QLatin1String("signOkKeyOk");
2808  QString frame = QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" "
2809  "class=\"") + block.signClass + QLatin1String("\">"
2810  "<tr class=\"") + block.signClass + QLatin1String("H\"><td dir=\"") + dir + QLatin1String("\">");
2811  htmlStr += frame + beginVerboseSigstatHeader();
2812  simpleHtmlStr += frame;
2813  simpleHtmlStr += writeSimpleSigstatHeader( block );
2814  if( !block.keyId.isEmpty() )
2815  htmlStr += i18n( "Message was signed by %2 (Key ID: %1).",
2816  keyWithWithoutURL,
2817  signer );
2818  else
2819  htmlStr += i18n( "Message was signed by %1.", signer );
2820  htmlStr += QLatin1String("<br />");
2821 
2822  switch( block.keyTrust )
2823  {
2824  case Kpgp::KPGP_VALIDITY_UNKNOWN:
2825  htmlStr += i18n( "The signature is valid, but the key's "
2826  "validity is unknown." );
2827  break;
2828  case Kpgp::KPGP_VALIDITY_MARGINAL:
2829  htmlStr += i18n( "The signature is valid and the key is "
2830  "marginally trusted." );
2831  break;
2832  case Kpgp::KPGP_VALIDITY_FULL:
2833  htmlStr += i18n( "The signature is valid and the key is "
2834  "fully trusted." );
2835  break;
2836  case Kpgp::KPGP_VALIDITY_ULTIMATE:
2837  htmlStr += i18n( "The signature is valid and the key is "
2838  "ultimately trusted." );
2839  break;
2840  default:
2841  htmlStr += i18n( "The signature is valid, but the key is "
2842  "untrusted." );
2843  }
2844  frame = QLatin1String("</td></tr>"
2845  "<tr class=\"") + block.signClass + QLatin1String("B\"><td>");
2846  htmlStr += endVerboseSigstatHeader( block ) + frame;
2847  simpleHtmlStr += frame;
2848  }
2849  else
2850  {
2851  block.signClass = QLatin1String("signErr");
2852  QString frame = QLatin1String("<table cellspacing=\"1\" ")+cellPadding+QLatin1String(" "
2853  "class=\"") + block.signClass + QLatin1String("\">"
2854  "<tr class=\"") + block.signClass + QLatin1String("H\"><td dir=\"") + dir + QLatin1String("\">");
2855  htmlStr += frame + beginVerboseSigstatHeader();
2856  simpleHtmlStr += frame;
2857  simpleHtmlStr += writeSimpleSigstatHeader( block );
2858  if( !block.keyId.isEmpty() )
2859  htmlStr += i18n( "Message was signed by %2 (Key ID: %1).",
2860  keyWithWithoutURL,
2861  signer );
2862  else
2863  htmlStr += i18n( "Message was signed by %1.", signer );
2864  htmlStr += QLatin1String("<br />");
2865  htmlStr += i18n("Warning: The signature is bad.");
2866  frame = QLatin1String("</td></tr>"
2867  "<tr class=\"") + block.signClass + QLatin1String("B\"><td>");
2868  htmlStr += endVerboseSigstatHeader( block ) + frame;
2869  simpleHtmlStr += frame;
2870  }
2871  }
2872  }
2873  }
2874 
2875  if ( mSource->showSignatureDetails() )
2876  return htmlStr;
2877  return simpleHtmlStr;
2878 }
2879 
2880 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
2881 {
2882  const QString dir = ( QApplication::isRightToLeft() ? QLatin1String("rtl") : QLatin1String("ltr") );
2883 
2884  QString htmlStr;
2885 
2886  if (block.isSigned) {
2887  htmlStr += QLatin1String("</td></tr><tr class=\"") + block.signClass + QLatin1String("H\">");
2888  htmlStr += QLatin1String("<td dir=\"") + dir + QLatin1String("\">") +
2889  i18n( "End of signed message" ) +
2890  QLatin1String("</td></tr></table>");
2891  }
2892 
2893  if (block.isEncrypted) {
2894  htmlStr += QLatin1String("</td></tr><tr class=\"encrH\"><td dir=\"") + dir + QLatin1String("\">") +
2895  i18n( "End of encrypted message" ) +
2896  QLatin1String("</td></tr></table>");
2897  }
2898 
2899  if( block.isEncapsulatedRfc822Message )
2900  {
2901  htmlStr += QLatin1String("</td></tr><tr class=\"rfc822H\"><td dir=\"") + dir + QLatin1String("\">") +
2902  i18n( "End of encapsulated message" ) +
2903  QLatin1String("</td></tr></table>");
2904  }
2905 
2906  return htmlStr;
2907 }
2908 
2909 
2910 //-----------------------------------------------------------------------------
2911 
2912 void ObjectTreeParser::writeAttachmentMarkHeader( KMime::Content *node )
2913 {
2914  if ( !htmlWriter() )
2915  return;
2916 
2917  htmlWriter()->queue( QString::fromLatin1( "<div id=\"attachmentDiv%1\">\n" ).arg( node->index().toString() ) );
2918 }
2919 
2920 //-----------------------------------------------------------------------------
2921 
2922 void ObjectTreeParser::writeAttachmentMarkFooter()
2923 {
2924  if ( !htmlWriter() )
2925  return;
2926 
2927  htmlWriter()->queue( QLatin1String( "</div>" ) );
2928 }
2929 
2930 
2931 
2932 //-----------------------------------------------------------------------------
2933 void ObjectTreeParser::writeBodyStr( const QByteArray& aStr, const QTextCodec *aCodec,
2934  const QString& fromAddress )
2935 {
2936  KMMsgSignatureState dummy1;
2937  KMMsgEncryptionState dummy2;
2938  writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
2939 }
2940 
2941 //-----------------------------------------------------------------------------
2942 void ObjectTreeParser::writeBodyStr( const QByteArray& aStr, const QTextCodec *aCodec,
2943  const QString& fromAddress,
2944  KMMsgSignatureState& inlineSignatureState,
2945  KMMsgEncryptionState& inlineEncryptionState,
2946  bool decorate )
2947 {
2948  const QString dir = ( QApplication::isRightToLeft() ? QLatin1String("rtl") : QLatin1String("ltr") );
2949  //QString headerStr = QString::fromLatin1("<div dir=\"%1\">").arg(dir);
2950 
2951  inlineSignatureState = KMMsgNotSigned;
2952  inlineEncryptionState = KMMsgNotEncrypted;
2953  QList<Kpgp::Block> pgpBlocks;
2954  QList<QByteArray> nonPgpBlocks;
2955  if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) ) {
2956 
2957  const Kleo::CryptoBackend::Protocol* cryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
2958  setCryptoProtocol( cryptProto );
2959 
2960  QString htmlStr;
2961  QString plainTextStr;
2962  bool fullySignedOrEncrypted = true;
2963 
2964  QList<Kpgp::Block>::iterator pbit = pgpBlocks.begin();
2965  QListIterator<QByteArray> npbit( nonPgpBlocks );
2966 
2967  for( ; pbit != pgpBlocks.end(); ++pbit )
2968  {
2969  // insert the next Non-OpenPGP block
2970  QByteArray str( npbit.next() );
2971  if( !str.trimmed().isEmpty() ) {
2972  const QString text = aCodec->toUnicode( str );
2973  plainTextStr += text;
2974  if ( htmlWriter() ) {
2975  htmlStr += quotedHTML( text, decorate );
2976  }
2977  kDebug() << "Non-empty Non-OpenPGP block found: '" << str << "'";
2978  fullySignedOrEncrypted = false;
2979  }
2980 
2981  Kpgp::Block &block = *pbit;
2982  KMime::Content* content = new KMime::Content;
2983  content->setBody( block.text() );
2984  content->parse();
2985 
2986  std::vector<GpgME::Signature> signatures;
2987  bool passphraseError;
2988  PartMetaData messagePart;
2989  messagePart.isEncrypted = false;
2990  messagePart.isSigned = false;
2991 
2992  QString text;
2993 
2994  if ( block.type() == Kpgp::PgpMessageBlock ) {
2995  QByteArray decryptedData;
2996  bool signatureFound;
2997  bool actuallyEncrypted = true;
2998  bool decryptionStarted;
2999  bool bOkDecrypt = okDecryptMIME( *content,
3000  decryptedData,
3001  signatureFound,
3002  signatures,
3003  true,
3004  passphraseError,
3005  actuallyEncrypted,
3006  decryptionStarted,
3007  messagePart );
3008 
3009  if ( decryptionStarted ) {
3010  writeDecryptionInProgressBlock();
3011  return;
3012  }
3013 
3014  messagePart.isDecryptable = bOkDecrypt;
3015  messagePart.isEncrypted = actuallyEncrypted;
3016  messagePart.isSigned = signatureFound;
3017 
3018  text = aCodec->toUnicode( decryptedData );
3019 
3020  } else if ( block.type() == Kpgp::ClearsignedBlock ) {
3021  //copied from ObjectTreeParser::writeOpaqueOrMultipartSignedData
3022  messagePart.isSigned = false;
3023  messagePart.technicalProblem = ( cryptProto == 0 );
3024  messagePart.isEncrypted = false;
3025  messagePart.isDecryptable = false;
3026  messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
3027  messagePart.status = i18n("Wrong Crypto Plug-In.");
3028  messagePart.status_code = GPGME_SIG_STAT_NONE;
3029 
3030  Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
3031  VerifyOpaqueBodyPartMemento * m
3032  = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), block.text() );
3033  m->exec();
3034  if ( m ) {
3035  text = aCodec->toUnicode( m->plainText() );
3036  messagePart.auditLogError = m->auditLogError();
3037  messagePart.auditLog = m->auditLogAsHtml();
3038  signatures = m->verifyResult().signatures();
3039  messagePart.isSigned = signatures.size() > 0;
3040  }
3041  }
3042 
3043  if (!messagePart.isEncrypted && !messagePart.isSigned ) {
3044  text = aCodec->toUnicode( block.text() );
3045  }
3046 
3047  if ( messagePart.isEncrypted )
3048  inlineEncryptionState = KMMsgPartiallyEncrypted;
3049 
3050  if ( messagePart.isSigned ) {
3051  inlineSignatureState = KMMsgPartiallySigned;
3052 
3053  //copied from ObjectTreeParser::writeOpaqueOrMultipartSignedData
3054  GpgME::Signature signature = signatures.front();
3055  GpgME::Key key;
3056  messagePart.status_code = signatureToStatus( signature );
3057  messagePart.isGoodSignature = messagePart.status_code & GPGME_SIG_STAT_GOOD;
3058  // save extended signature status flags
3059  messagePart.sigSummary = signature.summary();
3060 
3061  // Search for the key by it's fingerprint so that we can check for
3062  // trust etc.
3063  Kleo::KeyListJob * job = cryptProto->keyListJob( false ); // local, no sigs
3064  if ( !job ) {
3065  kDebug() << "The Crypto backend does not support listing keys. ";
3066  } else {
3067  std::vector<GpgME::Key> found_keys;
3068  // As we are local it is ok to make this synchronous
3069  GpgME::KeyListResult res = job->exec( QStringList( QLatin1String(signature.fingerprint()) ), false, found_keys );
3070  if ( res.error() ) {
3071  kDebug() << "Error while searching key for Fingerprint: " << signature.fingerprint();
3072  }
3073  if ( found_keys.size() > 1 ) {
3074  // Should not Happen
3075  kDebug() << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint();
3076  }
3077  if ( found_keys.size() != 1 ) {
3078  // Should not Happen at this point
3079  kDebug() << "Oops: Found no Key for Fingerprint: " << signature.fingerprint();
3080  } else {
3081  key = found_keys[0];
3082  }
3083  }
3084 
3085 
3086  if ( key.keyID() ) {
3087  messagePart.keyId = key.keyID();
3088  }
3089  if ( messagePart.keyId.isEmpty() )
3090  messagePart.keyId = signature.fingerprint();
3091  // ### Ugh. We depend on two enums being in sync:
3092  messagePart.keyTrust = (Kpgp::Validity)signature.validity();
3093  if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
3094  messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
3095  for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
3096  // The following if /should/ always result in TRUE but we
3097  // won't trust implicitely the plugin that gave us these data.
3098  if ( key.userID( iMail ).email() ) {
3099  QString email = QString::fromUtf8( key.userID( iMail ).email() );
3100  // ### work around gpgme 0.3.x / cryptplug bug where the
3101  // ### email addresses are specified as angle-addr, not addr-spec:
3102  if ( email.startsWith( QLatin1Char('<') ) && email.endsWith( QLatin1Char('>') ) )
3103  email = email.mid( 1, email.length() - 2 );
3104  if ( !email.isEmpty() )
3105  messagePart.signerMailAddresses.append( email );
3106  }
3107  }
3108 
3109  if ( signature.creationTime() )
3110  messagePart.creationTime.setTime_t( signature.creationTime() );
3111  else
3112  messagePart.creationTime = QDateTime();
3113  if ( messagePart.signer.isEmpty() ) {
3114  if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
3115  messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
3116  if ( !messagePart.signerMailAddresses.empty() ) {
3117  if ( messagePart.signer.isEmpty() )
3118  messagePart.signer = messagePart.signerMailAddresses.front();
3119  else
3120  messagePart.signer += QLatin1String(" <") + messagePart.signerMailAddresses.front() + QLatin1Char('>');
3121  }
3122  }
3123  }
3124 
3125  plainTextStr += text;
3126 
3127  if ( htmlWriter() ) {
3128  if (messagePart.isEncrypted || messagePart.isSigned)
3129  htmlStr += writeSigstatHeader( messagePart, cryptProto, fromAddress );
3130 
3131  if (messagePart.isEncrypted && !messagePart.isDecryptable)
3132  htmlStr += text; //Do not quote ErrorText
3133  else
3134  htmlStr += quotedHTML( text, decorate );
3135 
3136  if ( messagePart.isEncrypted || messagePart.isSigned )
3137  htmlStr += writeSigstatFooter( messagePart );
3138  }
3139 
3140 
3141  }
3142 
3143 
3144  // add the last Non-OpenPGP block
3145  QByteArray str( nonPgpBlocks.last() );
3146  if( !str.trimmed().isEmpty() ) {
3147  const QString text = aCodec->toUnicode( str );
3148  plainTextStr += text;
3149  if ( htmlWriter() ) {
3150  htmlStr += quotedHTML( text, decorate );
3151  }
3152  }
3153 
3154  //Do we have an fully Signed/Encrypted Message?
3155  if( fullySignedOrEncrypted ) {
3156  if( inlineSignatureState == KMMsgPartiallySigned )
3157  inlineSignatureState = KMMsgFullySigned;
3158  if( inlineEncryptionState == KMMsgPartiallyEncrypted )
3159  inlineEncryptionState = KMMsgFullyEncrypted;
3160  }
3161 
3162 
3163  if ( htmlWriter() ) {
3164  htmlWriter()->queue( htmlStr );
3165  }
3166  mPlainTextContent = plainTextStr;
3167  mPlainTextContentCharset = aCodec->name();
3168  }
3169  else { // No inline PGP encryption
3170 
3171  const QString plainText = aCodec->toUnicode( aStr );
3172 
3173  if ( mPlainTextContent.isEmpty() ) {
3174  mPlainTextContent = plainText;
3175  mPlainTextContentCharset = aCodec->name();
3176  }
3177 
3178  if ( htmlWriter() ) {
3179  htmlWriter()->queue( quotedHTML( plainText, decorate ) );
3180  }
3181  }
3182 }
3183 
3184 
3185 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
3186 {
3187  assert( cssHelper() );
3188 
3189  int convertFlags = LinkLocator::PreserveSpaces | LinkLocator::HighlightText;
3190  if ( decorate && GlobalSettings::self()->showEmoticons() ) {
3191  convertFlags |= LinkLocator::ReplaceSmileys;
3192  }
3193  QString htmlStr;
3194  const QString normalStartTag = cssHelper()->nonQuotedFontTag();
3195  QString quoteFontTag[3];
3196  QString deepQuoteFontTag[3];
3197  for ( int i = 0 ; i < 3 ; ++i ) {
3198  quoteFontTag[i] = cssHelper()->quoteFontTag( i );
3199  deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
3200  }
3201  const QString normalEndTag = QLatin1String("</div>");
3202  const QString quoteEnd = QLatin1String("</div>");
3203 
3204  const unsigned int length = s.length();
3205  bool paraIsRTL = false;
3206  bool startNewPara = true;
3207  unsigned int pos, beg;
3208 
3209  // skip leading empty lines
3210  for ( pos = 0; pos < length && s[pos] <= QLatin1Char(' '); pos++ )
3211  ;
3212  while (pos > 0 && (s[pos-1] == QLatin1Char(' ') || s[pos-1] == QLatin1Char('\t'))) pos--;
3213  beg = pos;
3214 
3215  int currQuoteLevel = -2; // -2 == no previous lines
3216  bool curHidden = false; // no hide any block
3217 
3218  if ( GlobalSettings::self()->showExpandQuotesMark() )
3219  {
3220  // Cache Icons
3221  if ( mCollapseIcon.isEmpty() ) {
3222  mCollapseIcon= LinkLocator::pngToDataUrl(
3223  IconNameCache::instance()->iconPath( QLatin1String("quotecollapse"), 0 ));
3224  }
3225  if ( mExpandIcon.isEmpty() )
3226  mExpandIcon= LinkLocator::pngToDataUrl(
3227  IconNameCache::instance()->iconPath( QLatin1String("quoteexpand"), 0 ));
3228  }
3229 
3230  while (beg<length)
3231  {
3232  /* search next occurrence of '\n' */
3233  pos = s.indexOf(QLatin1Char('\n'), beg, Qt::CaseInsensitive);
3234  if (pos == (unsigned int)(-1))
3235  pos = length;
3236 
3237  QString line( s.mid(beg,pos-beg) );
3238  beg = pos+1;
3239 
3240  /* calculate line's current quoting depth */
3241  int actQuoteLevel = -1;
3242  const int numberOfCaracters( line.length() );
3243  for (int p=0; p<numberOfCaracters; ++p) {
3244  switch (line[p].toLatin1()) {
3245  case '>':
3246  case '|':
3247  actQuoteLevel++;
3248  break;
3249  case ' ': // spaces and tabs are allowed between the quote markers
3250  case '\t':
3251  case '\r':
3252  break;
3253  default: // stop quoting depth calculation
3254  p = numberOfCaracters;
3255  break;
3256  }
3257  } /* for() */
3258 
3259  bool actHidden = false;
3260 
3261  // This quoted line needs be hidden
3262  if (GlobalSettings::self()->showExpandQuotesMark() && mSource->levelQuote() >= 0
3263  && mSource->levelQuote() <= ( actQuoteLevel ) )
3264  actHidden = true;
3265 
3266  if ( actQuoteLevel != currQuoteLevel ) {
3267  /* finish last quotelevel */
3268  if (currQuoteLevel == -1) {
3269  htmlStr.append( normalEndTag );
3270  } else if ( currQuoteLevel >= 0 && !curHidden ) {
3271  htmlStr.append( quoteEnd );
3272  }
3273 
3274  /* start new quotelevel */
3275  if (actQuoteLevel == -1) {
3276  htmlStr += normalStartTag;
3277  } else {
3278  if ( GlobalSettings::self()->showExpandQuotesMark() ) {
3279  if ( actHidden ) {
3280  //only show the QuoteMark when is the first line of the level hidden
3281  if ( !curHidden ) {
3282  //Expand all quotes
3283  htmlStr += QLatin1String("<div class=\"quotelevelmark\" >") ;
3284  htmlStr += QString::fromLatin1( "<a href=\"kmail:levelquote?%1 \">"
3285  "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3286  .arg(-1)
3287  .arg( mExpandIcon );
3288  htmlStr += QLatin1String("</div><br/>");
3289  htmlStr += quoteEnd;
3290  }
3291  } else {
3292  htmlStr += QLatin1String("<div class=\"quotelevelmark\" >" );
3293  htmlStr += QString::fromLatin1( "<a href=\"kmail:levelquote?%1 \">"
3294  "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3295  .arg(actQuoteLevel)
3296  .arg( mCollapseIcon);
3297  htmlStr += QLatin1String("</div>");
3298  if ( actQuoteLevel < 3 ) {
3299  htmlStr += quoteFontTag[actQuoteLevel];
3300  } else {
3301  htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3302  }
3303  }
3304  } else {
3305  if ( actQuoteLevel < 3 ) {
3306  htmlStr += quoteFontTag[actQuoteLevel];
3307  } else {
3308  htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3309  }
3310  }
3311  }
3312  currQuoteLevel = actQuoteLevel;
3313  }
3314  curHidden = actHidden;
3315 
3316 
3317  if ( !actHidden )
3318  {
3319  // don't write empty <div ...></div> blocks (they have zero height)
3320  // ignore ^M DOS linebreaks
3321  if( !line.remove( QLatin1Char('\015') ).isEmpty() )
3322  {
3323  if ( startNewPara )
3324  paraIsRTL = line.isRightToLeft();
3325  htmlStr += QString::fromLatin1( "<div dir=\"%1\">" ).arg( paraIsRTL ? QLatin1String("rtl") : QLatin1String("ltr") );
3326  htmlStr += LinkLocator::convertToHtml( line, convertFlags );
3327  htmlStr += QLatin1String( "</div>" );
3328  startNewPara = looksLikeParaBreak( s, pos );
3329  }
3330  else
3331  {
3332  htmlStr += QLatin1String("<br/>");
3333  // after an empty line, always start a new paragraph
3334  startNewPara = true;
3335  }
3336  }
3337  } /* while() */
3338 
3339  /* really finish the last quotelevel */
3340  if (currQuoteLevel == -1) {
3341  htmlStr.append( normalEndTag );
3342  } else {
3343  htmlStr.append( quoteEnd );
3344  }
3345 
3346  //kDebug() << "========================================\n"
3347  // << htmlStr
3348  // << "\n======================================\n";
3349  return htmlStr;
3350 }
3351 
3352 
3353 
3354 const QTextCodec * ObjectTreeParser::codecFor( KMime::Content * node ) const
3355 {
3356  assert( node );
3357  if ( mSource->overrideCodec() )
3358  return mSource->overrideCodec();
3359  return mNodeHelper->codec( node );
3360 }
3361 
3362 // Guesstimate if the newline at newLinePos actually separates paragraphs in the text s
3363 // We use several heuristics:
3364 // 1. If newLinePos points after or before (=at the very beginning of) text, it is not between paragraphs
3365 // 2. If the previous line was longer than the wrap size, we want to consider it a paragraph on its own
3366 // (some clients, notably Outlook, send each para as a line in the plain-text version).
3367 // 3. Otherwise, we check if the newline could have been inserted for wrapping around; if this
3368 // was the case, then the previous line will be shorter than the wrap size (which we already
3369 // know because of item 2 above), but adding the first word from the next line will make it
3370 // longer than the wrap size.
3371 bool ObjectTreeParser::looksLikeParaBreak( const QString& s, unsigned int newLinePos ) const
3372 {
3373  const unsigned int WRAP_COL = 78;
3374 
3375  unsigned int length = s.length();
3376  // 1. Is newLinePos at an end of the text?
3377  if ( newLinePos >= length-1 || newLinePos == 0 ) {
3378  return false;
3379  }
3380 
3381  // 2. Is the previous line really a paragraph -- longer than the wrap size?
3382 
3383  // First char of prev line -- works also for first line
3384  unsigned prevStart = s.lastIndexOf( QLatin1Char('\n'), newLinePos - 1 ) + 1;
3385  unsigned prevLineLength = newLinePos - prevStart;
3386  if ( prevLineLength > WRAP_COL ) {
3387  return true;
3388  }
3389 
3390  // find next line to delimit search for first word
3391  unsigned int nextStart = newLinePos + 1;
3392  int nextEnd = s.indexOf( QLatin1Char('\n'), nextStart );
3393  if ( nextEnd == -1 ) {
3394  nextEnd = length;
3395  }
3396  QString nextLine = s.mid( nextStart, nextEnd - nextStart );
3397  length = nextLine.length();
3398  // search for first word in next line
3399  unsigned int wordStart;
3400  bool found = false;
3401  for ( wordStart = 0; !found && wordStart < length; wordStart++ ) {
3402  switch ( nextLine[wordStart].toLatin1() ) {
3403  case '>':
3404  case '|':
3405  case ' ': // spaces, tabs and quote markers don't count
3406  case '\t':
3407  case '\r':
3408  break;
3409  default:
3410  found = true;
3411  break;
3412  }
3413  } /* for() */
3414 
3415  if ( !found ) {
3416  // next line is essentially empty, it seems -- empty lines are
3417  // para separators
3418  return true;
3419  }
3420  //Find end of first word.
3421  //Note: flowText (in kmmessage.cpp) separates words for wrap by
3422  //spaces only. This should be consistent, which calls for some
3423  //refactoring.
3424  int wordEnd = nextLine.indexOf( QLatin1Char(' '), wordStart );
3425  if ( wordEnd == (-1) ) {
3426  wordEnd = length;
3427  }
3428  int wordLength = wordEnd - wordStart;
3429 
3430  // 3. If adding a space and the first word to the prev line don't
3431  // make it reach the wrap column, then the break was probably
3432  // meaningful
3433  return prevLineLength + wordLength + 1 < WRAP_COL;
3434 }
3435 
3436 #ifdef MARCS_DEBUG
3437 void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
3438  size_t len ) {
3439  assert( filename );
3440 
3441  QFile f( filename );
3442  if ( f.open( QIODevice::WriteOnly ) ) {
3443  if ( start ) {
3444  QDataStream ds( &f );
3445  ds.writeRawData( start, len );
3446  }
3447  f.close(); // If data is 0 we just create a zero length file.
3448  }
3449 }
3450 #endif // !NDEBUG
3451 
3452 
3453 KMime::Content* ObjectTreeParser::findType( KMime::Content* content, const QByteArray& mimeType, bool deep, bool wide )
3454 {
3455  if( ( !content->contentType()->isEmpty() )
3456  && ( mimeType.isEmpty() || ( mimeType == content->contentType()->mimeType() ) ) )
3457  return content;
3458  KMime::Content *child = MessageCore::NodeHelper::firstChild( content );
3459  if ( child && deep ) //first child
3460  return findType( child, mimeType, deep, wide );
3461 
3462  KMime::Content *next = MessageCore::NodeHelper::nextSibling( content );
3463  if (next && wide ) //next on the same level
3464  return findType( next, mimeType, deep, wide );
3465 
3466  return 0;
3467 }
3468 
3469 KMime::Content* ObjectTreeParser::findType( KMime::Content* content, const QByteArray& mediaType, const QByteArray& subType, bool deep, bool wide )
3470 {
3471  if ( !content->contentType()->isEmpty() ) {
3472  if ( ( mediaType.isEmpty() || mediaType == content->contentType()->mediaType() )
3473  && ( subType.isEmpty() || subType == content->contentType()->subType() ) )
3474  return content;
3475  }
3476  KMime::Content *child = MessageCore::NodeHelper::firstChild( content );
3477  if ( child && deep ) //first child
3478  return findType( child, mediaType, subType, deep, wide );
3479 
3480  KMime::Content *next = MessageCore::NodeHelper::nextSibling( content );
3481  if (next && wide ) //next on the same level
3482  return findType( next, mediaType, subType, deep, wide );
3483 
3484  return 0;
3485 }
3486 
3487 KMime::Content* ObjectTreeParser::findTypeNot( KMime::Content * content, const QByteArray& mediaType, const QByteArray& subType, bool deep, bool wide )
3488 {
3489  if( ( !content->contentType()->isEmpty() )
3490  && ( mediaType.isEmpty() || content->contentType()->mediaType() != mediaType )
3491  && ( subType.isEmpty() || content->contentType()->subType() != subType )
3492  )
3493  return content;
3494  KMime::Content *child = MessageCore::NodeHelper::firstChild( content );
3495  if ( child && deep )
3496  return findTypeNot( child, mediaType, subType, deep, wide );
3497 
3498  KMime::Content *next = MessageCore::NodeHelper::nextSibling( content );
3499  if ( next && wide )
3500  return findTypeNot( next, mediaType, subType, deep, wide );
3501  return 0;
3502 }
3503 
3504 QString ObjectTreeParser::convertedTextContent() const
3505 {
3506  QString plainTextContent = mPlainTextContent;
3507  if( plainTextContent.isEmpty() ) {
3508 #ifdef KDEPIM_NO_WEBKIT
3509  QTextDocument doc;
3510  doc.setHtml( mHtmlContent );
3511  plainTextContent = doc.toPlainText();
3512 #else
3513  QWebPage doc;
3514  doc.mainFrame()->setHtml( mHtmlContent );
3515  plainTextContent = doc.mainFrame()->toPlainText();
3516 #endif
3517  }
3518  return plainTextContent.append(QLatin1Char('\n'));
3519 }
3520 
3521 QString ObjectTreeParser::convertedHtmlContent() const
3522 {
3523  QString htmlContent = mHtmlContent;
3524  if( htmlContent.isEmpty() ) {
3525  QString convertedHtml = Qt::escape( mPlainTextContent );
3526  convertedHtml.append(QLatin1String("</body></html>"));
3527  convertedHtml.prepend(QLatin1String("<html><head></head><body>"));
3528  htmlContent = convertedHtml.replace( QRegExp( QLatin1String("\n" )), QLatin1String("<br />") );
3529  }
3530  return htmlContent.append(QLatin1Char('\n'));
3531 }
MessageViewer::ObjectTreeParser::processMessageRfc822Subtype
bool processMessageRfc822Subtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1767
MessageViewer::VerifyOpaqueBodyPartMemento::verifyResult
const GpgME::VerificationResult & verifyResult() const
Definition: objecttreeparser_p.h:180
MessageViewer::VerifyDetachedBodyPartMemento::verifyResult
const GpgME::VerificationResult & verifyResult() const
Definition: objecttreeparser_p.h:141
MessageViewer::ObjectTreeParser::decryptChiasmus
bool decryptChiasmus(const QByteArray &data, QByteArray &bodyDecoded, QString &errorText)
Definition: objecttreeparser.cpp:2058
attachmentstrategy.h
MessageViewer::ObjectTreeSourceIf::htmlLoadExternal
virtual bool htmlLoadExternal()=0
Return true if external sources should be loaded in a html mail.
globalsettings.h
MessageViewer::PartMetaData
Definition: partmetadata.h:31
MessageViewer::ObjectTreeParser::showOnlyOneMimePart
bool showOnlyOneMimePart() const
Definition: objecttreeparser.h:374
MessageViewer::CSSHelperBase::nonQuotedFontTag
QString nonQuotedFontTag() const
Definition: csshelperbase.cpp:201
MessageViewer::NodeHelper::attachExtraContent
void attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content)
Attach an extra node to an existing node.
Definition: nodehelper.cpp:797
MessageViewer::PartMetaData::auditLog
QString auditLog
Definition: partmetadata.h:55
MessageViewer::Interface::BodyPartFormatter
Definition: interfaces/bodypartformatter.h:48
iconnamecache.h
MessageViewer::ObjectTreeParser::writePartIcon
void writePartIcon(KMime::Content *msgPart, bool inlineImage=false)
Definition: objecttreeparser.cpp:2188
MessageViewer::BodyPartFormatter
Definition: viewer/bodypartformatter.h:47
MessageViewer::ObjectTreeParser::writeBodyString
void writeBodyString(const QByteArray &bodyString, const QString &fromAddress, const QTextCodec *codec, ProcessResult &result, bool decorate)
Definition: objecttreeparser.cpp:2173
MessageViewer::PartNodeBodyPart
an implementation of the BodyPart interface using KMime::Content's
Definition: partnodebodypart.h:55
MessageViewer::Interface::BodyPartFormatter::NeedContent
Definition: interfaces/bodypartformatter.h:58
MessageViewer::BodyPartFormatter::createFor
static const BodyPartFormatter * createFor(const char *type, const char *subtype)
Definition: bodypartformatter.cpp:293
MessageViewer::Util::MultipartPlain
A multipart/alternative message, the plain text part is currently displayed.
Definition: util.h:76
MessageViewer::NodeHelper::decryptedNodeForContent
KMime::Content * decryptedNodeForContent(KMime::Content *content) const
Definition: nodehelper.cpp:925
MessageViewer::ObjectTreeParser::processTextPlainSubtype
bool processTextPlainSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1395
MessageViewer::VerifyDetachedBodyPartMemento
Definition: objecttreeparser_p.h:127
MessageViewer::NodeHelper::partMetaData
PartMetaData partMetaData(KMime::Content *node)
Definition: nodehelper.cpp:206
MessageViewer::VerifyDetachedBodyPartMemento::signingKey
const GpgME::Key & signingKey() const
Definition: objecttreeparser_p.h:142
MessageViewer::PartMetaData::isDecryptable
bool isDecryptable
Definition: partmetadata.h:60
MessageViewer::PartMetaData::signClass
QString signClass
Definition: partmetadata.h:45
MessageViewer::ObjectTreeSourceIf::sourceObject
virtual QObject * sourceObject()=0
The source object behind the interface.
MessageViewer::ProcessResult::neverDisplayInline
bool neverDisplayInline() const
Definition: objecttreeparser.h:95
MessageViewer::ObjectTreeParser::processMultiPartEncryptedSubtype
bool processMultiPartEncryptedSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1619
MessageViewer::ObjectTreeParser::processMultiPartAlternativeSubtype
bool processMultiPartAlternativeSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1499
MessageViewer::KMMsgSignatureState
KMMsgSignatureState
Flags for the signature state.
Definition: nodehelper.h:58
objecttreesourceif.h
MessageViewer::DecryptVerifyBodyPartMemento::verifyResult
const GpgME::VerificationResult & verifyResult() const
Definition: objecttreeparser_p.h:105
partmetadata.h
htmlstatusbar.h
MessageViewer::Interface::BodyPartFormatter::Failed
Definition: interfaces/bodypartformatter.h:58
MessageViewer::ObjectTreeParser::plainTextContentCharset
QByteArray plainTextContentCharset() const
The original charset of MIME part the plain text was extracted from.
Definition: objecttreeparser.h:364
MessageViewer::AttachmentStrategy::defaultDisplay
virtual Display defaultDisplay(KMime::Content *node) const =0
SIG_FRAME_COL_UNDEF
static const int SIG_FRAME_COL_UNDEF
Definition: objecttreeparser.cpp:2229
MessageViewer::KMMsgPartiallySigned
Definition: nodehelper.h:62
MessageViewer::ObjectTreeParser::sigStatusToString
QString sigStatusToString(const Kleo::CryptoBackend::Protocol *cryptProto, int status_code, GpgME::Signature::Summary summary, int &frameColor, bool &showKeyInfos)
Definition: objecttreeparser.cpp:2233
MessageViewer::Viewer::UpdateMode
UpdateMode
The display update mode: Force updates the display immediately, Delayed updates after some time (150m...
Definition: viewer.h:132
MessageViewer::PartMetaData::status_code
int status_code
Definition: partmetadata.h:51
MessageViewer::DecryptVerifyBodyPartMemento::start
bool start()
MessageViewer::PartMetaData::auditLogError
GpgME::Error auditLogError
Definition: partmetadata.h:56
nodehelper.h
MessageViewer::ProcessResult::setNeverDisplayInline
void setNeverDisplayInline(bool display)
Definition: objecttreeparser.h:96
MessageViewer::ObjectTreeParser::setCryptoProtocol
void setCryptoProtocol(const Kleo::CryptoBackend::Protocol *protocol)
Definition: objecttreeparser.h:367
partnodebodypart.h
MessageViewer::KMMsgPartiallyEncrypted
Definition: nodehelper.h:52
QObject
MessageViewer::ProcessResult
Definition: objecttreeparser.h:69
kleojobexecutor.h
MessageViewer::ObjectTreeParser::includeSignatures
bool includeSignatures() const
Definition: objecttreeparser.h:384
MessageViewer::ObjectTreeParser::writeAttachmentMarkFooter
void writeAttachmentMarkFooter()
Definition: objecttreeparser.cpp:2922
objecttreeparser_p.h
MessageViewer::PartMetaData::creationTime
QDateTime creationTime
Definition: partmetadata.h:53
MessageViewer::ObjectTreeParser::writeAttachmentMarkHeader
void writeAttachmentMarkHeader(KMime::Content *node)
Definition: objecttreeparser.cpp:2912
SIG_FRAME_COL_GREEN
#define SIG_FRAME_COL_GREEN
Definition: objecttreeparser.cpp:2232
MessageViewer::ObjectTreeParser::defaultToltecReplacementText
static QString defaultToltecReplacementText()
Default text for processToltecMail(), which is used in messageviewer.kcfg, therefore it needs to be s...
Definition: objecttreeparser.cpp:1466
MessageViewer::Interface::BodyPartFormatter::Ok
Definition: interfaces/bodypartformatter.h:58
MessageViewer::ObjectTreeParser::findTypeNot
static KMime::Content * findTypeNot(KMime::Content *content, const QByteArray &mediaType, const QByteArray &subType, bool deep=true, bool wide=true)
Definition: objecttreeparser.cpp:3487
MessageViewer::AttachmentStrategy::None
Definition: attachmentstrategy.h:79
MessageViewer::ObjectTreeParser::processApplicationOctetStreamSubtype
bool processApplicationOctetStreamSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1817
MessageViewer::CryptoBodyPartMemento::isRunning
bool isRunning() const
Definition: objecttreeparser_p.h:67
MessageViewer::ObjectTreeParser::keepEncryptions
bool keepEncryptions() const
Definition: objecttreeparser.h:379
MessageViewer::KleoJobExecutor::exec
GpgME::VerificationResult exec(Kleo::VerifyDetachedJob *job, const QByteArray &signature, const QByteArray &signedData)
Definition: kleojobexecutor.cpp:47
MessageViewer::HTMLQuoteColorer::process
QString process(const QString &htmlSource, QString &extraHead)
Do the work and add nice colors to the HTML.
Definition: htmlquotecolorer.cpp:37
MessageViewer::PartMetaData::isEncapsulatedRfc822Message
bool isEncapsulatedRfc822Message
Definition: partmetadata.h:63
MessageViewer::KMMsgNotSigned
Definition: nodehelper.h:61
MessageViewer::KMMsgFullySigned
Definition: nodehelper.h:63
MessageViewer::ObjectTreeParser::processApplicationPkcs7MimeSubtype
bool processApplicationPkcs7MimeSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1899
viewer_p.h
MessageViewer::DecryptVerifyBodyPartMemento
Definition: objecttreeparser_p.h:92
htmlwriter.h
MessageViewer::ObjectTreeParser::allowAsync
bool allowAsync() const
Definition: objecttreeparser.h:310
MessageViewer::PartMetaData::isEncrypted
bool isEncrypted
Definition: partmetadata.h:59
MessageViewer::IconNameCache::instance
static IconNameCache * instance()
Definition: iconnamecache.cpp:28
MessageViewer::AutoQPointer
A QPointer which when destructed, deletes the object it points to.
Definition: autoqpointer.h:38
MessageViewer::PartMetaData::technicalProblem
bool technicalProblem
Definition: partmetadata.h:62
MessageViewer::ProcessResult::setInlineEncryptionState
void setInlineEncryptionState(KMMsgEncryptionState state)
Definition: objecttreeparser.h:91
MessageViewer::VerifyOpaqueBodyPartMemento::exec
void exec()
autoqpointer.h
MessageViewer::ProcessResult::adjustCryptoStatesOfNode
void adjustCryptoStatesOfNode(KMime::Content *node) const
Definition: objecttreeparser.cpp:449
MessageViewer::ObjectTreeSourceIf::showSignatureDetails
virtual bool showSignatureDetails()=0
Return true to include the signature details in the generated html.
MessageViewer::ChiasmusKeySelector::options
QString options() const
Definition: chiasmuskeyselector.cpp:67
MessageViewer::KMMsgNotEncrypted
Definition: nodehelper.h:51
MessageViewer::ObjectTreeSourceIf::overrideCodec
virtual const QTextCodec * overrideCodec()=0
The override codec that should be used for the mail.
MessageViewer::ObjectTreeParser::processApplicationChiasmusTextSubtype
bool processApplicationChiasmusTextSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:2135
MessageViewer::GlobalSettings::self
static GlobalSettings * self()
Definition: globalsettings.cpp:34
MessageViewer::ObjectTreeParser::writeBodyStr
void writeBodyStr(const QByteArray &bodyString, const QTextCodec *aCodec, const QString &fromAddress, KMMsgSignatureState &inlineSignatureState, KMMsgEncryptionState &inlineEncryptionState, bool decorate)
Definition: objecttreeparser.cpp:2942
MessageViewer::ObjectTreeParser::htmlContentCharset
QByteArray htmlContentCharset() const
Definition: objecttreeparser.h:365
MessageViewer::ObjectTreeParser::writeSigstatFooter
QString writeSigstatFooter(PartMetaData &part)
Definition: objecttreeparser.cpp:2880
MessageViewer::ObjectTreeParser::nodeHelper
NodeHelper * nodeHelper() const
Definition: objecttreeparser.h:406
MessageViewer::ObjectTreeSourceIf
Interface for object tree sources.
Definition: objecttreesourceif.h:43
MessageViewer::HTMLQuoteColorer
Little helper class that takes a HTML source as input and finds all lines that are quoted with '>' or...
Definition: htmlquotecolorer.h:31
MessageViewer::BodyPartFormatterFactory::instance
static const BodyPartFormatterFactory * instance()
Definition: bodypartformatterfactory.cpp:73
MessageViewer::ObjectTreeParser::parseObjectTree
void parseObjectTree(KMime::Content *node)
Parse beginning at a given node and recursively parsing the children of that node and it's next sibli...
Definition: objecttreeparser.cpp:263
chiasmuskeyselector.h
MessageViewer::HtmlWriter::queue
virtual void queue(const QString &str)=0
MessageViewer::ChiasmusKeySelector
Definition: chiasmuskeyselector.h:13
MessageViewer::NodeHelper::nodeProcessed
bool nodeProcessed(KMime::Content *node) const
Definition: nodehelper.cpp:140
MessageViewer::ObjectTreeParser::processMultiPartMixedSubtype
bool processMultiPartMixedSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1484
MessageViewer::DecryptVerifyBodyPartMemento::plainText
const QByteArray & plainText() const
Definition: objecttreeparser_p.h:103
MessageViewer::VerifyOpaqueBodyPartMemento::signingKey
const GpgME::Key & signingKey() const
Definition: objecttreeparser_p.h:181
util.h
MessageViewer::ObjectTreeParser::rawDecryptedBody
KDE_DEPRECATED QByteArray rawDecryptedBody() const
The origin and purpose of this function is unknown, the ancient wisdom about it got lost during the c...
Definition: objecttreeparser.h:330
MessageViewer::HtmlWriter::extraHead
virtual void extraHead(const QString &str)=0
MessageViewer::NodeHelper::codecForName
static const QTextCodec * codecForName(const QByteArray &_str)
Return a QTextCodec for the specified charset.
Definition: nodehelper.cpp:577
MessageViewer::ProcessResult::inlineSignatureState
KMMsgSignatureState inlineSignatureState() const
Definition: objecttreeparser.h:81
MessageViewer::Util::Html
A HTML message, non-multipart.
Definition: util.h:75
MessageViewer::ObjectTreeParser::attachmentStrategy
const AttachmentStrategy * attachmentStrategy() const
Definition: objecttreeparser.h:398
MessageViewer::IconNameCache::iconPath
QString iconPath(const QString &name, int size) const
Definition: iconnamecache.cpp:42
MessageViewer::HTMLQuoteColorer::setQuoteColor
void setQuoteColor(unsigned int level, const QColor &color)
Sets the quote color of the specific leve.
Definition: htmlquotecolorer.cpp:146
MessageViewer::NodeHelper::asHREF
QString asHREF(const KMime::Content *node, const QString &place)
Definition: nodehelper.cpp:683
MessageViewer::ObjectTreeSourceIf::levelQuote
virtual int levelQuote()=0
MessageViewer::BodyPartFormatter::process
virtual bool process(ObjectTreeParser *, KMime::Content *, ProcessResult &) const =0
MessageViewer::NodeHelper::setBodyPartMemento
void setBodyPartMemento(KMime::Content *node, const QByteArray &which, Interface::BodyPartMemento *memento)
Definition: nodehelper.cpp:610
MessageViewer::Interface::BodyPartFormatter::Result
Result
Definition: interfaces/bodypartformatter.h:58
MessageViewer::CSSHelperBase::quoteFontTag
QString quoteFontTag(int level) const
Definition: csshelperbase.cpp:188
beginVerboseSigstatHeader
static QString beginVerboseSigstatHeader()
Definition: objecttreeparser.cpp:2439
MessageViewer::ObjectTreeParser::htmlContent
QString htmlContent() const
Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part...
Definition: objecttreeparser.h:345
MessageViewer::HtmlWriter::embedPart
virtual void embedPart(const QByteArray &contentId, const QString &url)=0
Embed a part with Content-ID contentId, using url url.
MessageViewer::Interface::BodyPart::Display
Display
Definition: bodypart.h:161
MessageViewer::AttachmentStrategy::Inline
Definition: attachmentstrategy.h:79
MessageViewer::PartMetaData::inProgress
bool inProgress
Definition: partmetadata.h:61
MessageViewer::NodeHelper::setSignatureState
void setSignatureState(KMime::Content *node, const KMMsgSignatureState state)
Definition: nodehelper.cpp:196
MessageViewer::NodeHelper::writeNodeToTempFile
QString writeNodeToTempFile(KMime::Content *node)
Writes the given message part to a temporary file and returns the name of this file or QString() if w...
Definition: nodehelper.cpp:216
MessageViewer::NodeHelper::bodyPartMemento
Interface::BodyPartMemento * bodyPartMemento(KMime::Content *node, const QByteArray &which) const
Definition: nodehelper.cpp:597
MessageViewer::VerifyOpaqueBodyPartMemento::start
bool start()
MessageViewer::ObjectTreeParser::writeSigstatHeader
QString writeSigstatHeader(PartMetaData &part, const Kleo::CryptoBackend::Protocol *cryptProto, const QString &fromAddress, KMime::Content *node=0)
Definition: objecttreeparser.cpp:2485
MessageViewer::PartMetaData::sigSummary
GpgME::Signature::Summary sigSummary
Definition: partmetadata.h:44
MessageViewer::NodeHelper::charset
static QByteArray charset(KMime::Content *node)
Returns the charset for the given node.
Definition: nodehelper.cpp:366
MessageViewer::NodeHelper
Definition: nodehelper.h:71
MessageViewer::ObjectTreeParser::~ObjectTreeParser
virtual ~ObjectTreeParser()
Definition: objecttreeparser.cpp:215
MessageViewer::NodeHelper::setPartMetaData
void setPartMetaData(KMime::Content *node, const PartMetaData &metaData)
Definition: nodehelper.cpp:211
MessageViewer::AttachmentStrategy
Definition: attachmentstrategy.h:46
makeShowAuditLogLink
static QString makeShowAuditLogLink(const GpgME::Error &err, const QString &auditLog)
Definition: objecttreeparser.cpp:2444
MessageViewer::NodeHelper::fromAsString
static QString fromAsString(KMime::Content *node)
Definition: nodehelper.cpp:789
MessageViewer::NodeHelper::isToltecMessage
static bool isToltecMessage(KMime::Content *node)
Definition: nodehelper.cpp:319
writeSimpleSigstatHeader
static QString writeSimpleSigstatHeader(const PartMetaData &block)
Definition: objecttreeparser.cpp:2402
htmlquotecolorer.h
MessageViewer::VerifyOpaqueBodyPartMemento::plainText
const QByteArray & plainText() const
Definition: objecttreeparser_p.h:179
MessageViewer::NodeHelper::setNodeDisplayedHidden
void setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden)
Definition: nodehelper.cpp:651
MessageViewer::NodeHelper::setNodeDisplayedEmbedded
void setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded)
Definition: nodehelper.cpp:637
signatureToStatus
static int signatureToStatus(const GpgME::Signature &sig)
Definition: objecttreeparser.cpp:461
MessageViewer::PartMetaData::isSigned
bool isSigned
Definition: partmetadata.h:57
MessageViewer::KMMsgFullyEncrypted
Definition: nodehelper.h:53
MessageViewer::ProcessResult::inlineEncryptionState
KMMsgEncryptionState inlineEncryptionState() const
Definition: objecttreeparser.h:88
MessageViewer::ObjectTreeParser::htmlWriter
HtmlWriter * htmlWriter() const
Definition: objecttreeparser.h:402
MessageViewer::ObjectTreeParser::cssHelper
CSSHelper * cssHelper() const
Definition: objecttreeparser.h:404
MessageViewer::ObjectTreeParser::cryptoProtocol
const Kleo::CryptoBackend::Protocol * cryptoProtocol() const
Definition: objecttreeparser.h:370
endVerboseSigstatHeader
static QString endVerboseSigstatHeader(const PartMetaData &pmd)
Definition: objecttreeparser.cpp:2472
MessageViewer::KMMsgEncryptionState
KMMsgEncryptionState
Flags for the encryption state.
Definition: nodehelper.h:48
MessageViewer::ObjectTreeParser::findType
static KMime::Content * findType(KMime::Content *content, const QByteArray &mimeType, bool deep, bool wide)
Definition: objecttreeparser.cpp:3453
bodypartformatter.h
bodypartformatterfactory.h
MessageViewer::ObjectTreeSourceIf::attachmentStrategy
virtual const AttachmentStrategy * attachmentStrategy()=0
Return the wanted attachment startegy.
MessageViewer::ObjectTreeSourceIf::htmlMail
virtual bool htmlMail()=0
Return true if the mail should be parsed as a html mail.
MessageViewer::DecryptVerifyBodyPartMemento::decryptResult
const GpgME::DecryptionResult & decryptResult() const
Definition: objecttreeparser_p.h:104
MessageViewer::ObjectTreeParser::processMultiPartDigestSubtype
bool processMultiPartDigestSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1557
MessageViewer::NodeHelper::iconName
static QString iconName(KMime::Content *node, int size=KIconLoader::Desktop)
Definition: nodehelper.cpp:469
MessageViewer::ObjectTreeParser::convertedTextContent
QString convertedTextContent() const
Returns a plain text version of the content, which is either plainTextContent() if that exists...
Definition: objecttreeparser.cpp:3504
MessageViewer::NodeHelper::setNodeUnprocessed
void setNodeUnprocessed(KMime::Content *node, bool recurse)
Definition: nodehelper.cpp:111
MessageViewer::NodeHelper::setNodeProcessed
void setNodeProcessed(KMime::Content *node, bool recurse)
Definition: nodehelper.cpp:95
MessageViewer::ObjectTreeSourceIf::createMessageHeader
virtual QString createMessageHeader(KMime::Message *message)=0
MessageViewer::ObjectTreeParser
Parses messages and generates HTML display code out of them.
Definition: objecttreeparser.h:287
objecttreeparser.h
MessageViewer::Interface::BodyPartFormatter::AsIcon
Definition: interfaces/bodypartformatter.h:58
MessageViewer::NodeHelper::codec
const QTextCodec * codec(KMime::Content *node)
Get a QTextCodec suitable for this message part.
Definition: nodehelper.cpp:554
MessageViewer::PartMetaData::signerMailAddresses
QStringList signerMailAddresses
Definition: partmetadata.h:47
MessageViewer::KleoJobExecutor
Helper class for synchronous execution of Kleo crypto jobs.
Definition: kleojobexecutor.h:44
MessageViewer::ObjectTreeParser::processTextHtmlSubtype
bool processTextHtmlSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1202
MessageViewer::ProcessResult::setInlineSignatureState
void setInlineSignatureState(KMMsgSignatureState state)
Definition: objecttreeparser.h:84
MessageViewer::VerifyDetachedBodyPartMemento::start
bool start()
MessageViewer::PartMetaData::keyId
QByteArray keyId
Definition: partmetadata.h:48
MessageViewer::ObjectTreeParser::isMailmanMessage
bool isMailmanMessage(KMime::Content *curNode)
Definition: objecttreeparser.cpp:1271
MessageViewer::ObjectTreeSourceIf::decryptMessage
virtual bool decryptMessage()=0
Return true if an encrypted mail should be decrypted.
MessageViewer::ObjectTreeParser::processMultiPartSignedSubtype
bool processMultiPartSignedSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1565
MessageViewer::PartMetaData::isGoodSignature
bool isGoodSignature
Definition: partmetadata.h:58
MessageViewer::AttachmentStrategy::AsIcon
Definition: attachmentstrategy.h:79
MessageViewer::ObjectTreeParser::convertedHtmlContent
QString convertedHtmlContent() const
Returns a HTML version of the plain text mail.
Definition: objecttreeparser.cpp:3521
MessageViewer::PartMetaData::status
QString status
Definition: partmetadata.h:50
MessageViewer::PartMetaData::keyTrust
Kpgp::Validity keyTrust
Definition: partmetadata.h:49
MessageViewer::PartMetaData::errorText
QString errorText
Definition: partmetadata.h:52
MessageViewer::ChiasmusKeySelector::key
QString key() const
Definition: chiasmuskeyselector.cpp:58
MessageViewer::ObjectTreeSourceIf::setHtmlMode
virtual void setHtmlMode(MessageViewer::Util::HtmlMode mode)=0
Sets the type of mail that is currently displayed.
MessageViewer::PartMetaData::signer
QString signer
Definition: partmetadata.h:46
MessageViewer::VerifyDetachedBodyPartMemento::exec
void exec()
SIG_FRAME_COL_RED
#define SIG_FRAME_COL_RED
Definition: objecttreeparser.cpp:2230
MessageViewer::DecryptVerifyBodyPartMemento::exec
void exec()
MessageViewer::CryptoBodyPartMemento::auditLogAsHtml
const QString & auditLogAsHtml() const
Definition: objecttreeparser_p.h:69
MessageViewer::ProcessResult::isImage
bool isImage() const
Definition: objecttreeparser.h:100
csshelper.h
MessageViewer::NodeHelper::setEncryptionState
void setEncryptionState(KMime::Content *node, const KMMsgEncryptionState state)
Definition: nodehelper.cpp:186
MessageViewer::NodeHelper::fileName
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header...
Definition: nodehelper.cpp:586
MessageViewer::NodeHelper::magicSetType
void magicSetType(KMime::Content *node, bool autoDecode=true)
Set the 'Content-Type' by mime-magic from the contents of the body.
Definition: nodehelper.cpp:484
MessageViewer::Util::MultipartHtml
A multipart/altervative message, the HTML part is currently displayed.
Definition: util.h:77
MessageViewer::ObjectTreeParser::plainTextContent
QString plainTextContent() const
The text of the message, ie.
Definition: objecttreeparser.h:337
SIG_FRAME_COL_YELLOW
#define SIG_FRAME_COL_YELLOW
Definition: objecttreeparser.cpp:2231
MessageViewer::ObjectTreeParser::processMultiPartParallelSubtype
bool processMultiPartParallelSubtype(KMime::Content *node, ProcessResult &result)
Definition: objecttreeparser.cpp:1561
MessageViewer::CryptoBodyPartMemento::auditLogError
GpgME::Error auditLogError() const
Definition: objecttreeparser_p.h:70
MessageViewer::VerifyOpaqueBodyPartMemento
Definition: objecttreeparser_p.h:166
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:55:57 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

messageviewer

Skip menu "messageviewer"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal