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