kmail

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