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

messageviewer

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

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