00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <config.h>
00034
00035
00036 #include "objecttreeparser.h"
00037 #include "objecttreeparser_p.h"
00038
00039
00040 #include "kmkernel.h"
00041 #include "kmreaderwin.h"
00042 #include "partNode.h"
00043 #include <libkdepim/kfileio.h>
00044 #include <libemailfunctions/email.h>
00045 #include "partmetadata.h"
00046 #include "attachmentstrategy.h"
00047 #include "interfaces/htmlwriter.h"
00048 #include "htmlstatusbar.h"
00049 #include "csshelper.h"
00050 #include "bodypartformatter.h"
00051 #include "bodypartformatterfactory.h"
00052 #include "partnodebodypart.h"
00053 #include "interfaces/bodypartformatter.h"
00054 #include "globalsettings.h"
00055 #include "util.h"
00056 #include "callback.h"
00057
00058
00059 #include <mimelib/enum.h>
00060 #include <mimelib/bodypart.h>
00061 #include <mimelib/string.h>
00062 #include <mimelib/text.h>
00063
00064 #include <kleo/specialjob.h>
00065 #include <kleo/cryptobackendfactory.h>
00066 #include <kleo/decryptverifyjob.h>
00067 #include <kleo/verifydetachedjob.h>
00068 #include <kleo/verifyopaquejob.h>
00069 #include <kleo/keylistjob.h>
00070 #include <kleo/importjob.h>
00071 #include <kleo/dn.h>
00072
00073 #include <gpgmepp/importresult.h>
00074 #include <gpgmepp/decryptionresult.h>
00075 #include <gpgmepp/key.h>
00076 #include <gpgmepp/keylistresult.h>
00077 #include <gpgme.h>
00078
00079 #include <kpgpblock.h>
00080 #include <kpgp.h>
00081 #include <linklocator.h>
00082
00083 #include <ktnef/ktnefparser.h>
00084 #include <ktnef/ktnefmessage.h>
00085 #include <ktnef/ktnefattach.h>
00086
00087
00088 #include <kdebug.h>
00089 #include <klocale.h>
00090 #include <kmimetype.h>
00091 #include <kglobal.h>
00092 #include <khtml_part.h>
00093 #include <ktempfile.h>
00094 #include <kstandarddirs.h>
00095 #include <kapplication.h>
00096 #include <kmessagebox.h>
00097 #include <kiconloader.h>
00098 #include <kmdcodec.h>
00099
00100
00101 #include <tqtextcodec.h>
00102 #include <tqdir.h>
00103 #include <tqfile.h>
00104 #include <tqapplication.h>
00105 #include <kstyle.h>
00106 #include <tqbuffer.h>
00107 #include <tqpixmap.h>
00108 #include <tqpainter.h>
00109 #include <tqregexp.h>
00110
00111
00112 #include <memory>
00113 #include <sstream>
00114 #include <sys/stat.h>
00115 #include <sys/types.h>
00116 #include <unistd.h>
00117 #include <cassert>
00118 #include "chiasmuskeyselector.h"
00119
00120 namespace KMail {
00121
00122
00123 class ObjectTreeParser::CryptoProtocolSaver {
00124 ObjectTreeParser * otp;
00125 const Kleo::CryptoBackend::Protocol * protocol;
00126 public:
00127 CryptoProtocolSaver( ObjectTreeParser * _otp, const Kleo::CryptoBackend::Protocol* _w )
00128 : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
00129 {
00130 if ( otp )
00131 otp->setCryptoProtocol( _w );
00132 }
00133
00134 ~CryptoProtocolSaver() {
00135 if ( otp )
00136 otp->setCryptoProtocol( protocol );
00137 }
00138 };
00139
00140
00141 ObjectTreeParser::ObjectTreeParser( KMReaderWin * reader, const Kleo::CryptoBackend::Protocol * protocol,
00142 bool showOnlyOneMimePart, bool keepEncryptions,
00143 bool includeSignatures,
00144 const AttachmentStrategy * strategy,
00145 HtmlWriter * htmlWriter,
00146 CSSHelper * cssHelper )
00147 : mReader( reader ),
00148 mCryptoProtocol( protocol ),
00149 mShowOnlyOneMimePart( showOnlyOneMimePart ),
00150 mKeepEncryptions( keepEncryptions ),
00151 mIncludeSignatures( includeSignatures ),
00152 mHasPendingAsyncJobs( false ),
00153 mAllowAsync( false ),
00154 mShowRawToltecMail( false ),
00155 mAttachmentStrategy( strategy ),
00156 mHtmlWriter( htmlWriter ),
00157 mCSSHelper( cssHelper )
00158 {
00159 if ( !attachmentStrategy() )
00160 mAttachmentStrategy = reader ? reader->attachmentStrategy()
00161 : AttachmentStrategy::smart();
00162 if ( reader && !this->htmlWriter() )
00163 mHtmlWriter = reader->htmlWriter();
00164 if ( reader && !this->cssHelper() )
00165 mCSSHelper = reader->mCSSHelper;
00166 }
00167
00168 ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser & other )
00169 : mReader( other.mReader ),
00170 mCryptoProtocol( other.cryptoProtocol() ),
00171 mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
00172 mKeepEncryptions( other.keepEncryptions() ),
00173 mIncludeSignatures( other.includeSignatures() ),
00174 mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ),
00175 mAllowAsync( other.allowAsync() ),
00176 mAttachmentStrategy( other.attachmentStrategy() ),
00177 mHtmlWriter( other.htmlWriter() ),
00178 mCSSHelper( other.cssHelper() )
00179 {
00180
00181 }
00182
00183 ObjectTreeParser::~ObjectTreeParser() {}
00184
00185 void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
00186 const char* content,
00187 const char* cntDesc,
00188 bool append, bool addToTextualContent )
00189 {
00190 DwBodyPart* myBody = new DwBodyPart( DwString( content ), 0 );
00191 myBody->Parse();
00192
00193 if ( ( !myBody->Body().FirstBodyPart() ||
00194 myBody->Body().AsString().length() == 0 ) &&
00195 startNode.dwPart() &&
00196 startNode.dwPart()->Body().Message() &&
00197 startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
00198 {
00199
00200
00201 myBody = new DwBodyPart( *(startNode.dwPart()->Body().Message()) );
00202 }
00203
00204 if ( myBody->hasHeaders() ) {
00205 DwText& desc = myBody->Headers().ContentDescription();
00206 desc.FromString( cntDesc );
00207 desc.SetModified();
00208 myBody->Headers().Parse();
00209 }
00210
00211 partNode* parentNode = &startNode;
00212 partNode* newNode = new partNode(false, myBody);
00213
00214
00215
00216 newNode->buildObjectTree( false );
00217
00218 if ( append && parentNode->firstChild() ) {
00219 parentNode = parentNode->firstChild();
00220 while( parentNode->nextSibling() )
00221 parentNode = parentNode->nextSibling();
00222 parentNode->setNext( newNode );
00223 } else
00224 parentNode->setFirstChild( newNode );
00225
00226 if ( startNode.mimePartTreeItem() ) {
00227 newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
00228 TQString(), TQString(), TQString(), 0,
00229 append );
00230 } else {
00231 }
00232 ObjectTreeParser otp( mReader, cryptoProtocol() );
00233 otp.parseObjectTree( newNode );
00234 if ( addToTextualContent ) {
00235 mRawReplyString += otp.rawReplyString();
00236 mTextualContent += otp.textualContent();
00237 if ( !otp.textualContentCharset().isEmpty() )
00238 mTextualContentCharset = otp.textualContentCharset();
00239 }
00240 }
00241
00242
00243
00244
00245 void ObjectTreeParser::parseObjectTree( partNode * node ) {
00246
00247
00248
00249
00250
00251 if ( !node )
00252 return;
00253
00254
00255 mHasPendingAsyncJobs = false;
00256
00257
00258 if ( showOnlyOneMimePart() ) {
00259
00260 node->setProcessed( false, false );
00261 if ( partNode * child = node->firstChild() )
00262 child->setProcessed( false, true );
00263 } else if ( mReader && !node->parentNode() ) {
00264
00265 node->setProcessed( false, true );
00266 }
00267
00268 for ( ; node ; node = node->nextSibling() ) {
00269 if ( node->processed() )
00270 continue;
00271
00272 ProcessResult processResult;
00273
00274 if ( mReader ) {
00275 htmlWriter()->queue( TQString::fromLatin1("<a name=\"att%1\"/>").arg( node->nodeId() ) );
00276 }
00277
00278 if ( const Interface::BodyPartFormatter * formatter
00279 = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
00280
00281
00282
00283 if ( mReader ) {
00284 PartNodeBodyPart part( *node, codecFor( node ) );
00285
00286
00287 part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
00288
00289 writeAttachmentMarkHeader( node );
00290 node->setDisplayedEmbedded( true );
00291 Callback callback( mReader->message(), mReader );
00292 const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), callback );
00293 writeAttachmentMarkFooter();
00294 switch ( result ) {
00295 case Interface::BodyPartFormatter::AsIcon:
00296 processResult.setNeverDisplayInline( true );
00297
00298 case Interface::BodyPartFormatter::Failed:
00299 defaultHandling( node, processResult );
00300 break;
00301 case Interface::BodyPartFormatter::Ok:
00302 case Interface::BodyPartFormatter::NeedContent:
00303
00304 ;
00305 }
00306 }
00307 } else {
00308 const BodyPartFormatter * bpf
00309 = BodyPartFormatter::createFor( node->type(), node->subType() );
00310 kdFatal( !bpf, 5006 ) << "THIS SHOULD NO LONGER HAPPEN ("
00311 << node->typeString() << '/' << node->subTypeString()
00312 << ')' << endl;
00313
00314 writeAttachmentMarkHeader( node );
00315 if ( bpf && !bpf->process( this, node, processResult ) ) {
00316 defaultHandling( node, processResult );
00317 }
00318 writeAttachmentMarkFooter();
00319 }
00320
00321 node->setProcessed( true, false );
00322
00323
00324 processResult.adjustCryptoStatesOfNode( node );
00325
00326 if ( showOnlyOneMimePart() )
00327 break;
00328 }
00329 }
00330
00331 void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
00332
00333
00334 if ( !mReader )
00335 return;
00336
00337
00338 const AttachmentStrategy * as = attachmentStrategy();
00339 if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None &&
00340 !showOnlyOneMimePart() &&
00341 node->parentNode() ) {
00342 node->setDisplayedHidden( true );
00343 return;
00344 }
00345
00346 bool asIcon = true;
00347 if ( showOnlyOneMimePart() )
00348
00349
00350
00351 asIcon = !node->hasContentDispositionInline();
00352 else if ( !result.neverDisplayInline() )
00353 if ( as )
00354 asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
00355
00356 if ( !result.isImage() && node->type() != DwMime::kTypeText )
00357 asIcon = true;
00358
00359 if ( result.isImage() && !node->msgPart().isComplete() )
00360 asIcon = true;
00361 if ( asIcon ) {
00362 if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) ||
00363 showOnlyOneMimePart() ) {
00364 writePartIcon( &node->msgPart(), node->nodeId() );
00365 }
00366 else {
00367 node->setDisplayedHidden( true );
00368 }
00369 } else if ( result.isImage() ) {
00370 node->setDisplayedEmbedded( true );
00371 writePartIcon( &node->msgPart(), node->nodeId(), true );
00372 }
00373 else {
00374 node->setDisplayedEmbedded( true );
00375 writeBodyString( node->msgPart().bodyDecoded(),
00376 node->trueFromAddress(),
00377 codecFor( node ), result, false );
00378 }
00379
00380 }
00381
00382 void ProcessResult::adjustCryptoStatesOfNode( partNode * node ) const {
00383 if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
00384 ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
00385 node->setSignatureState( inlineSignatureState() );
00386 node->setEncryptionState( inlineEncryptionState() );
00387 }
00388 }
00389
00393
00394 static int signatureToStatus( const GpgME::Signature &sig )
00395 {
00396 switch ( sig.status().code() ) {
00397 case GPG_ERR_NO_ERROR:
00398 return GPGME_SIG_STAT_GOOD;
00399 case GPG_ERR_BAD_SIGNATURE:
00400 return GPGME_SIG_STAT_BAD;
00401 case GPG_ERR_NO_PUBKEY:
00402 return GPGME_SIG_STAT_NOKEY;
00403 case GPG_ERR_NO_DATA:
00404 return GPGME_SIG_STAT_NOSIG;
00405 case GPG_ERR_SIG_EXPIRED:
00406 return GPGME_SIG_STAT_GOOD_EXP;
00407 case GPG_ERR_KEY_EXPIRED:
00408 return GPGME_SIG_STAT_GOOD_EXPKEY;
00409 default:
00410 return GPGME_SIG_STAT_ERROR;
00411 }
00412 }
00413
00414 bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
00415 partNode& sign,
00416 const TQString& fromAddress,
00417 bool doCheck,
00418 TQCString* cleartextData,
00419 const std::vector<GpgME::Signature> & paramSignatures,
00420 bool hideErrors )
00421 {
00422 bool bIsOpaqueSigned = false;
00423 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
00424 cryptPlugError = NO_PLUGIN;
00425
00426 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
00427
00428 TQString cryptPlugLibName;
00429 TQString cryptPlugDisplayName;
00430 if ( cryptProto ) {
00431 cryptPlugLibName = cryptProto->name();
00432 cryptPlugDisplayName = cryptProto->displayName();
00433 }
00434
00435 #ifndef NDEBUG
00436 if ( !doCheck ) {
00437
00438 }
00439 else {
00440 if ( data ) {
00441
00442 }
00443 else {
00444
00445 }
00446 }
00447 #endif
00448
00449 if ( doCheck && cryptProto ) {
00450
00451
00452 }
00453
00454 TQCString cleartext;
00455 TQByteArray signaturetext;
00456
00457 if ( doCheck && cryptProto ) {
00458 if ( data ) {
00459 cleartext = KMail::Util::CString( data->dwPart()->AsString() );
00460
00461 dumpToFile( "dat_01_reader_signedtext_before_canonicalization",
00462 cleartext.data(), cleartext.length() );
00463
00464
00465
00466
00467 cleartext = Util::lf2crlf( cleartext );
00468
00469 }
00470
00471 dumpToFile( "dat_02_reader_signedtext_after_canonicalization",
00472 cleartext.data(), cleartext.length() );
00473
00474 signaturetext = sign.msgPart().bodyDecodedBinary();
00475 dumpToFile( "dat_03_reader.sig", signaturetext.data(),
00476 signaturetext.size() );
00477 }
00478
00479 std::vector<GpgME::Signature> signatures;
00480 if ( !doCheck )
00481 signatures = paramSignatures;
00482
00483 PartMetaData messagePart;
00484 messagePart.isSigned = true;
00485 messagePart.technicalProblem = ( cryptProto == 0 );
00486 messagePart.isGoodSignature = false;
00487 messagePart.isEncrypted = false;
00488 messagePart.isDecryptable = false;
00489 messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
00490 messagePart.status = i18n("Wrong Crypto Plug-In.");
00491 messagePart.status_code = GPGME_SIG_STAT_NONE;
00492
00493 GpgME::Key key;
00494
00495 if ( doCheck && cryptProto ) {
00496 GpgME::VerificationResult result;
00497 if ( data ) {
00498 const VerifyDetachedBodyPartMemento * m
00499 = dynamic_cast<VerifyDetachedBodyPartMemento*>( sign.bodyPartMemento( "verifydetached" ) );
00500 if ( !m ) {
00501 Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
00502 if ( !job ) {
00503 cryptPlugError = CANT_VERIFY_SIGNATURES;
00504
00505 } else {
00506 TQByteArray plainData = cleartext;
00507 plainData.resize( cleartext.size() - 1 );
00508 VerifyDetachedBodyPartMemento * newM
00509 = new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
00510 if ( allowAsync() ) {
00511 if ( newM->start() ) {
00512 messagePart.inProgress = true;
00513 mHasPendingAsyncJobs = true;
00514 } else {
00515 m = newM;
00516 }
00517 } else {
00518 newM->exec();
00519 m = newM;
00520 }
00521 sign.setBodyPartMemento( "verifydetached", newM );
00522 }
00523 } else if ( m->isRunning() ) {
00524 messagePart.inProgress = true;
00525 mHasPendingAsyncJobs = true;
00526 m = 0;
00527 }
00528
00529 if ( m ) {
00530 result = m->verifyResult();
00531 messagePart.auditLogError = m->auditLogError();
00532 messagePart.auditLog = m->auditLogAsHtml();
00533 key = m->signingKey();
00534 }
00535 } else {
00536 const VerifyOpaqueBodyPartMemento * m
00537 = dynamic_cast<VerifyOpaqueBodyPartMemento*>( sign.bodyPartMemento( "verifyopaque" ) );
00538 if ( !m ) {
00539 Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
00540 if ( !job ) {
00541 cryptPlugError = CANT_VERIFY_SIGNATURES;
00542
00543 } else {
00544 VerifyOpaqueBodyPartMemento * newM
00545 = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
00546 if ( allowAsync() ) {
00547 if ( newM->start() ) {
00548 messagePart.inProgress = true;
00549 mHasPendingAsyncJobs = true;
00550 } else {
00551 m = newM;
00552 }
00553 } else {
00554 newM->exec();
00555 m = newM;
00556 }
00557 sign.setBodyPartMemento( "verifyopaque", newM );
00558 }
00559 } else if ( m->isRunning() ) {
00560 messagePart.inProgress = true;
00561 mHasPendingAsyncJobs = true;
00562 m = 0;
00563 }
00564
00565 if ( m ) {
00566 result = m->verifyResult();
00567 const TQByteArray & plainData = m->plainText();
00568 cleartext = TQCString( plainData.data(), plainData.size() + 1 );
00569 messagePart.auditLogError = m->auditLogError();
00570 messagePart.auditLog = m->auditLogAsHtml();
00571 key = m->signingKey();
00572 }
00573 }
00574 std::stringstream ss;
00575 ss << result;
00576
00577 signatures = result.signatures();
00578 }
00579
00580 if ( doCheck ) {
00581
00582 }
00583
00584
00585 if ( signatures.size() > 0 ) {
00586
00587 GpgME::Signature signature = signatures[0];
00588
00589 messagePart.status_code = signatureToStatus( signature );
00590 messagePart.status = TQString::fromUtf8( signature.status().asString() );
00591 for ( uint i = 1; i < signatures.size(); ++i ) {
00592 if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
00593 messagePart.status_code = GPGME_SIG_STAT_DIFF;
00594 messagePart.status = i18n("Different results for signatures");
00595 }
00596 }
00597 if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
00598 messagePart.isGoodSignature = true;
00599
00600
00601 messagePart.sigSummary = signature.summary();
00602
00603 if ( key.keyID() )
00604 messagePart.keyId = key.keyID();
00605 if ( messagePart.keyId.isEmpty() )
00606 messagePart.keyId = signature.fingerprint();
00607
00608 messagePart.keyTrust = (Kpgp::Validity)signature.validity();
00609 if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
00610 messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
00611 for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
00612
00613
00614 if ( key.userID( iMail ).email() ) {
00615 TQString email = TQString::fromUtf8( key.userID( iMail ).email() );
00616
00617
00618 if ( email.startsWith( "<" ) && email.endsWith( ">" ) )
00619 email = email.mid( 1, email.length() - 2 );
00620 if ( !email.isEmpty() )
00621 messagePart.signerMailAddresses.append( email );
00622 }
00623 }
00624
00625 if ( signature.creationTime() )
00626 messagePart.creationTime.setTime_t( signature.creationTime() );
00627 else
00628 messagePart.creationTime = TQDateTime();
00629 if ( messagePart.signer.isEmpty() ) {
00630 if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
00631 messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
00632 if ( !messagePart.signerMailAddresses.empty() ) {
00633 if ( messagePart.signer.isEmpty() )
00634 messagePart.signer = messagePart.signerMailAddresses.front();
00635 else
00636 messagePart.signer += " <" + messagePart.signerMailAddresses.front() + '>';
00637 }
00638 }
00639
00640
00641
00642
00643
00644 } else {
00645 messagePart.creationTime = TQDateTime();
00646 }
00647
00648 if ( !doCheck || !data ){
00649 if ( cleartextData || !cleartext.isEmpty() ) {
00650 if ( mReader )
00651 htmlWriter()->queue( writeSigstatHeader( messagePart,
00652 cryptProto,
00653 fromAddress ) );
00654 bIsOpaqueSigned = true;
00655
00656 CryptoProtocolSaver cpws( this, cryptProto );
00657 insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
00658 "opaqued signed data" );
00659
00660 if ( mReader )
00661 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00662
00663 }
00664 else if ( !hideErrors ) {
00665 TQString txt;
00666 txt = "<hr><b><h2>";
00667 txt.append( i18n( "The crypto engine returned no cleartext data." ) );
00668 txt.append( "</h2></b>" );
00669 txt.append( "<br> <br>" );
00670 txt.append( i18n( "Status: " ) );
00671 if ( !messagePart.status.isEmpty() ) {
00672 txt.append( "<i>" );
00673 txt.append( messagePart.status );
00674 txt.append( "</i>" );
00675 }
00676 else
00677 txt.append( i18n("(unknown)") );
00678 if ( mReader )
00679 htmlWriter()->queue(txt);
00680 }
00681 }
00682 else {
00683 if ( mReader ) {
00684 if ( !cryptProto ) {
00685 TQString errorMsg;
00686 switch ( cryptPlugError ) {
00687 case NOT_INITIALIZED:
00688 errorMsg = i18n( "Crypto plug-in \"%1\" is not initialized." )
00689 .arg( cryptPlugLibName );
00690 break;
00691 case CANT_VERIFY_SIGNATURES:
00692 errorMsg = i18n( "Crypto plug-in \"%1\" cannot verify signatures." )
00693 .arg( cryptPlugLibName );
00694 break;
00695 case NO_PLUGIN:
00696 if ( cryptPlugDisplayName.isEmpty() )
00697 errorMsg = i18n( "No appropriate crypto plug-in was found." );
00698 else
00699 errorMsg = i18n( "%1 is either 'OpenPGP' or 'S/MIME'",
00700 "No %1 plug-in was found." )
00701 .arg( cryptPlugDisplayName );
00702 break;
00703 }
00704 messagePart.errorText = i18n( "The message is signed, but the "
00705 "validity of the signature cannot be "
00706 "verified.<br />"
00707 "Reason: %1" )
00708 .arg( errorMsg );
00709 }
00710
00711 if ( mReader )
00712 htmlWriter()->queue( writeSigstatHeader( messagePart,
00713 cryptProto,
00714 fromAddress ) );
00715 }
00716
00717 ObjectTreeParser otp( mReader, cryptProto, true );
00718 otp.parseObjectTree( data );
00719 mRawReplyString += otp.rawReplyString();
00720 mTextualContent += otp.textualContent();
00721 if ( !otp.textualContentCharset().isEmpty() )
00722 mTextualContentCharset = otp.textualContentCharset();
00723
00724 if ( mReader )
00725 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00726 }
00727
00728
00729
00730 return bIsOpaqueSigned;
00731 }
00732
00733 void ObjectTreeParser::writeDecryptionInProgressBlock() {
00734 assert( mReader );
00735
00736
00737 const TQString decryptedData = i18n("Encrypted data not shown");
00738 PartMetaData messagePart;
00739 messagePart.isDecryptable = true;
00740 messagePart.isEncrypted = true;
00741 messagePart.isSigned = false;
00742 messagePart.inProgress = true;
00743 htmlWriter()->queue( writeSigstatHeader( messagePart,
00744 cryptoProtocol(),
00745 TQString() ) );
00746
00747 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00748 }
00749
00750 void ObjectTreeParser::writeDeferredDecryptionBlock() {
00751 assert( mReader );
00752 const TQString iconName = KGlobal::instance()->iconLoader()->iconPath( "decrypted", KIcon::Small );
00753 const TQString decryptedData =
00754 "<div style=\"font-size:large; text-align:center;padding-top:20pt;\">" +
00755 i18n("This message is encrypted.") +
00756 "</div>"
00757 "<div style=\"text-align:center; padding-bottom:20pt;\">"
00758 "<a href=\"kmail:decryptMessage\">"
00759 "<img src=\"" + iconName + "\"/>" +
00760 i18n("Decrypt Message") +
00761 "</a></div>";
00762 PartMetaData messagePart;
00763 messagePart.isDecryptable = true;
00764 messagePart.isEncrypted = true;
00765 messagePart.isSigned = false;
00766 mRawReplyString += decryptedData.utf8();
00767 htmlWriter()->queue( writeSigstatHeader( messagePart,
00768 cryptoProtocol(),
00769 TQString() ) );
00770 htmlWriter()->queue( decryptedData );
00771 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00772 }
00773
00774 bool ObjectTreeParser::okDecryptMIME( partNode& data,
00775 TQCString& decryptedData,
00776 bool& signatureFound,
00777 std::vector<GpgME::Signature> &signatures,
00778 bool showWarning,
00779 bool& passphraseError,
00780 bool& actuallyEncrypted,
00781 bool& decryptionStarted,
00782 TQString& aErrorText,
00783 GpgME::Error & auditLogError,
00784 TQString& auditLog )
00785 {
00786 passphraseError = false;
00787 decryptionStarted = false;
00788 aErrorText = TQString();
00789 auditLogError = GpgME::Error();
00790 auditLog = TQString();
00791 bool bDecryptionOk = false;
00792 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
00793 cryptPlugError = NO_PLUGIN;
00794
00795 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
00796
00797 TQString cryptPlugLibName;
00798 if ( cryptProto )
00799 cryptPlugLibName = cryptProto->name();
00800
00801 assert( !mReader || mReader->decryptMessage() );
00802
00803 if ( cryptProto && !kmkernel->contextMenuShown() ) {
00804 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00805 #ifdef MARCS_DEBUG
00806 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00807 bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
00808 (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
00809 (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
00810
00811 dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
00812
00813 TQCString deb;
00814 deb = "\n\nE N C R Y P T E D D A T A = ";
00815 if ( cipherIsBinary )
00816 deb += "[binary data]";
00817 else {
00818 deb += "\"";
00819 deb += cipherStr;
00820 deb += "\"";
00821 }
00822 deb += "\n\n";
00823 kdDebug(5006) << deb << endl;
00824 #endif
00825
00826
00827
00828
00829 if ( mReader )
00830 emit mReader->noDrag();
00831
00832
00833 const DecryptVerifyBodyPartMemento * m
00834 = dynamic_cast<DecryptVerifyBodyPartMemento*>( data.bodyPartMemento( "decryptverify" ) );
00835 if ( !m ) {
00836 Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
00837 if ( !job ) {
00838 cryptPlugError = CANT_DECRYPT;
00839 cryptProto = 0;
00840 } else {
00841 DecryptVerifyBodyPartMemento * newM
00842 = new DecryptVerifyBodyPartMemento( job, ciphertext );
00843 if ( allowAsync() ) {
00844 if ( newM->start() ) {
00845 decryptionStarted = true;
00846 mHasPendingAsyncJobs = true;
00847 } else {
00848 m = newM;
00849 }
00850 } else {
00851 newM->exec();
00852 m = newM;
00853 }
00854 data.setBodyPartMemento( "decryptverify", newM );
00855 }
00856 } else if ( m->isRunning() ) {
00857 decryptionStarted = true;
00858 mHasPendingAsyncJobs = true;
00859 m = 0;
00860 }
00861
00862 if ( m ) {
00863 const TQByteArray & plainText = m->plainText();
00864 const GpgME::DecryptionResult & decryptResult = m->decryptResult();
00865 const GpgME::VerificationResult & verifyResult = m->verifyResult();
00866 std::stringstream ss;
00867 ss << decryptResult << '\n' << verifyResult;
00868
00869 signatureFound = verifyResult.signatures().size() > 0;
00870 signatures = verifyResult.signatures();
00871 bDecryptionOk = !decryptResult.error();
00872 passphraseError = decryptResult.error().isCanceled()
00873 || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
00874 actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
00875 aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() );
00876 auditLogError = m->auditLogError();
00877 auditLog = m->auditLogAsHtml();
00878
00879
00880
00881 if ( bDecryptionOk )
00882 decryptedData = TQCString( plainText.data(), plainText.size() + 1 );
00883 else if ( mReader && showWarning ) {
00884 decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00885 "padding:20pt;\">"
00886 + i18n("Encrypted data not shown.").utf8()
00887 + "</div>";
00888 if ( !passphraseError )
00889 aErrorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.")
00890 .arg( cryptPlugLibName )
00891 + "<br />"
00892 + i18n("Error: %1").arg( aErrorText );
00893 }
00894 }
00895 }
00896
00897 if ( !cryptProto ) {
00898 decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
00899 + i18n("Encrypted data not shown.").utf8()
00900 + "</div>";
00901 switch ( cryptPlugError ) {
00902 case NOT_INITIALIZED:
00903 aErrorText = i18n( "Crypto plug-in \"%1\" is not initialized." )
00904 .arg( cryptPlugLibName );
00905 break;
00906 case CANT_DECRYPT:
00907 aErrorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages." )
00908 .arg( cryptPlugLibName );
00909 break;
00910 case NO_PLUGIN:
00911 aErrorText = i18n( "No appropriate crypto plug-in was found." );
00912 break;
00913 }
00914 } else if ( kmkernel->contextMenuShown() ) {
00915
00916
00917 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00918 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00919 bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
00920 (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
00921 (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
00922 if ( !cipherIsBinary ) {
00923 decryptedData = cipherStr;
00924 }
00925 else {
00926 decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00927 "padding:20pt;\">"
00928 + i18n("Encrypted data not shown.").utf8()
00929 + "</div>";
00930 }
00931 }
00932
00933 dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
00934
00935 return bDecryptionOk;
00936 }
00937
00938
00939 bool ObjectTreeParser::containsExternalReferences( const TQCString & str )
00940 {
00941 TQRegExp httpRegExp("(\\\"|\\\'|url\\s*\\(\\s*)http[s]?:");
00942 int httpPos = str.find( httpRegExp, 0 );
00943
00944 while ( httpPos >= 0 ) {
00945
00946 if ( httpPos > 5 ) {
00947 int hrefPos = str.findRev( "href", httpPos - 5, true );
00948
00949
00950
00951 if ( ( hrefPos == -1 ) || ( httpPos - hrefPos > 7 ) )
00952 return true;
00953 }
00954
00955 httpPos = str.find( httpRegExp, httpPos + 6 );
00956 }
00957 return false;
00958 }
00959
00960 bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
00961 TQCString cstr( curNode->msgPart().bodyDecoded() );
00962
00963 mRawReplyString = cstr;
00964 if ( curNode->isFirstTextPart() ) {
00965 mTextualContent += curNode->msgPart().bodyToUnicode();
00966 mTextualContentCharset = curNode->msgPart().charset();
00967 }
00968
00969 if ( !mReader )
00970 return true;
00971
00972 if ( curNode->isFirstTextPart() ||
00973 attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
00974 showOnlyOneMimePart() )
00975 {
00976 if ( mReader->htmlMail() ) {
00977 curNode->setDisplayedEmbedded( true );
00978
00979
00980
00981
00982 int i = cstr.findRev("</body>", -1, false);
00983 if ( 0 <= i )
00984 cstr.truncate(i);
00985 else
00986 {
00987 i = cstr.findRev("</html>", -1, false);
00988 if ( 0 <= i ) cstr.truncate(i);
00989 }
00990
00991
00992
00993
00994
00995
00996
00997 if ( !mReader->htmlLoadExternal() &&
00998 containsExternalReferences( cstr ) ) {
00999 htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
01000 htmlWriter()->queue( i18n("<b>Note:</b> This HTML message may contain external "
01001 "references to images etc. For security/privacy reasons "
01002 "external references are not loaded. If you trust the "
01003 "sender of this message then you can load the external "
01004 "references for this message "
01005 "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
01006 htmlWriter()->queue( "</div><br><br>" );
01007 }
01008 } else {
01009 htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
01010 htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
01011 "security reasons, only the raw HTML code "
01012 "is shown. If you trust the sender of this "
01013 "message then you can activate formatted "
01014 "HTML display for this message "
01015 "<a href=\"kmail:showHTML\">by clicking here</a>.") );
01016 htmlWriter()->queue( "</div><br><br>" );
01017 }
01018 htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr : KMMessage::html2source( cstr )));
01019 mReader->mColorBar->setHtmlMode();
01020 return true;
01021 }
01022 return false;
01023 }
01024 }
01025
01026 static bool isMailmanMessage( partNode * curNode ) {
01027 if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
01028 return false;
01029 DwHeaders & headers = curNode->dwPart()->Headers();
01030 if ( headers.HasField("X-Mailman-Version") )
01031 return true;
01032 if ( headers.HasField("X-Mailer") &&
01033 0 == TQCString( headers.FieldBody("X-Mailer").AsString().c_str() )
01034 .find("MAILMAN", 0, false) )
01035 return true;
01036 return false;
01037 }
01038
01039 namespace KMail {
01040
01041 bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
01042 const TQCString cstr = curNode->msgPart().bodyDecoded();
01043
01044
01045 const TQCString delim1( "--__--__--\n\nMessage:");
01046 const TQCString delim2( "--__--__--\r\n\r\nMessage:");
01047 const TQCString delimZ2("--__--__--\n\n_____________");
01048 const TQCString delimZ1("--__--__--\r\n\r\n_____________");
01049 TQCString partStr, digestHeaderStr;
01050 int thisDelim = cstr.find(delim1.data(), 0, false);
01051 if ( thisDelim == -1 )
01052 thisDelim = cstr.find(delim2.data(), 0, false);
01053 if ( thisDelim == -1 ) {
01054 kdDebug(5006) << " Sorry: Old style Mailman message but no delimiter found." << endl;
01055 return false;
01056 }
01057
01058 int nextDelim = cstr.find(delim1.data(), thisDelim+1, false);
01059 if ( -1 == nextDelim )
01060 nextDelim = cstr.find(delim2.data(), thisDelim+1, false);
01061 if ( -1 == nextDelim )
01062 nextDelim = cstr.find(delimZ1.data(), thisDelim+1, false);
01063 if ( -1 == nextDelim )
01064 nextDelim = cstr.find(delimZ2.data(), thisDelim+1, false);
01065 if ( nextDelim < 0)
01066 return false;
01067
01068
01069
01070
01071
01072
01073 digestHeaderStr = "Content-Type=text/plain\nContent-Description=digest header\n\n";
01074 digestHeaderStr += cstr.mid( 0, thisDelim );
01075 insertAndParseNewChildNode( *curNode,
01076 &*digestHeaderStr,
01077 "Digest Header", true );
01078
01079
01080
01081 curNode->setType( DwMime::kTypeMultipart );
01082 curNode->setSubType( DwMime::kSubtypeDigest );
01083 while( -1 < nextDelim ){
01084 int thisEoL = cstr.find("\nMessage:", thisDelim, false);
01085 if ( -1 < thisEoL )
01086 thisDelim = thisEoL+1;
01087 else{
01088 thisEoL = cstr.find("\n_____________", thisDelim, false);
01089 if ( -1 < thisEoL )
01090 thisDelim = thisEoL+1;
01091 }
01092 thisEoL = cstr.find('\n', thisDelim);
01093 if ( -1 < thisEoL )
01094 thisDelim = thisEoL+1;
01095 else
01096 thisDelim = thisDelim+1;
01097
01098
01099
01100 partStr = "Content-Type=message/rfc822\nContent-Description=embedded message\n";
01101 partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
01102 TQCString subject("embedded message");
01103 TQCString subSearch("\nSubject:");
01104 int subPos = partStr.find(subSearch.data(), 0, false);
01105 if ( -1 < subPos ){
01106 subject = partStr.mid(subPos+subSearch.length());
01107 thisEoL = subject.find('\n');
01108 if ( -1 < thisEoL )
01109 subject.truncate( thisEoL );
01110 }
01111
01112 insertAndParseNewChildNode( *curNode,
01113 &*partStr,
01114 subject, true );
01115
01116 thisDelim = nextDelim+1;
01117 nextDelim = cstr.find(delim1.data(), thisDelim, false);
01118 if ( -1 == nextDelim )
01119 nextDelim = cstr.find(delim2.data(), thisDelim, false);
01120 if ( -1 == nextDelim )
01121 nextDelim = cstr.find(delimZ1.data(), thisDelim, false);
01122 if ( -1 == nextDelim )
01123 nextDelim = cstr.find(delimZ2.data(), thisDelim, false);
01124 }
01125
01126 curNode->setType( DwMime::kTypeText );
01127 curNode->setSubType( DwMime::kSubtypePlain );
01128 int thisEoL = cstr.find("_____________", thisDelim);
01129 if ( -1 < thisEoL ){
01130 thisDelim = thisEoL;
01131 thisEoL = cstr.find('\n', thisDelim);
01132 if ( -1 < thisEoL )
01133 thisDelim = thisEoL+1;
01134 }
01135 else
01136 thisDelim = thisDelim+1;
01137 partStr = "Content-Type=text/plain\nContent-Description=digest footer\n\n";
01138 partStr += cstr.mid( thisDelim );
01139 insertAndParseNewChildNode( *curNode,
01140 &*partStr,
01141 "Digest Footer", true );
01142 return true;
01143 }
01144
01145 bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
01146 if ( !mReader ) {
01147 mRawReplyString = curNode->msgPart().bodyDecoded();
01148 if ( curNode->isFirstTextPart() ) {
01149 mTextualContent += curNode->msgPart().bodyToUnicode();
01150 mTextualContentCharset = curNode->msgPart().charset();
01151 }
01152 return true;
01153 }
01154
01155 if ( !curNode->isFirstTextPart() &&
01156 attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
01157 !showOnlyOneMimePart() )
01158 return false;
01159
01160 mRawReplyString = curNode->msgPart().bodyDecoded();
01161 if ( curNode->isFirstTextPart() ) {
01162 mTextualContent += curNode->msgPart().bodyToUnicode();
01163 mTextualContentCharset = curNode->msgPart().charset();
01164 }
01165
01166 TQString label = curNode->msgPart().fileName().stripWhiteSpace();
01167 if ( label.isEmpty() )
01168 label = curNode->msgPart().name().stripWhiteSpace();
01169
01170 const bool bDrawFrame = !curNode->isFirstTextPart()
01171 && !showOnlyOneMimePart()
01172 && !label.isEmpty();
01173 if ( bDrawFrame ) {
01174 label = KMMessage::quoteHtmlChars( label, true );
01175
01176 const TQString comment =
01177 KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(), true );
01178
01179 const TQString fileName =
01180 mReader->writeMessagePartToTempFile( &curNode->msgPart(),
01181 curNode->nodeId() );
01182
01183 const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
01184
01185 TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
01186 "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
01187 if ( !fileName.isEmpty() )
01188 htmlStr += "<a href=\"" + curNode->asHREF( "body" ) + "\">"
01189 + label + "</a>";
01190 else
01191 htmlStr += label;
01192 if ( !comment.isEmpty() )
01193 htmlStr += "<br>" + comment;
01194 htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
01195
01196 htmlWriter()->queue( htmlStr );
01197 }
01198
01199
01200 if ( !isMailmanMessage( curNode ) ||
01201 !processMailmanMessage( curNode ) ) {
01202 writeBodyString( mRawReplyString, curNode->trueFromAddress(),
01203 codecFor( curNode ), result, !bDrawFrame );
01204 curNode->setDisplayedEmbedded( true );
01205 }
01206 if ( bDrawFrame )
01207 htmlWriter()->queue( "</td></tr></table>" );
01208
01209 return true;
01210 }
01211
01212 void ObjectTreeParser::stdChildHandling( partNode * child ) {
01213 if ( !child )
01214 return;
01215
01216 ObjectTreeParser otp( *this );
01217 otp.setShowOnlyOneMimePart( false );
01218 otp.parseObjectTree( child );
01219 mRawReplyString += otp.rawReplyString();
01220 mTextualContent += otp.textualContent();
01221 if ( !otp.textualContentCharset().isEmpty() )
01222 mTextualContentCharset = otp.textualContentCharset();
01223 }
01224
01225 TQString ObjectTreeParser::defaultToltecReplacementText()
01226 {
01227 return i18n( "This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
01228 "Microsoft Outlook in combination with the Toltec connector." );
01229 }
01230
01231 bool ObjectTreeParser::processToltecMail( partNode *node )
01232 {
01233 if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
01234 !node->isToltecMessage() || mShowRawToltecMail )
01235 return false;
01236
01237 htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
01238 htmlWriter()->queue( "<br><br><a href=\"kmail:showRawToltecMail\">" +
01239 i18n( "Show Raw Message" ) + "</a>" );
01240 return true;
01241 }
01242
01243 bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01244
01245 if ( processToltecMail( node ) ) {
01246 return true;
01247 }
01248
01249 partNode * child = node->firstChild();
01250 if ( !child )
01251 return false;
01252
01253
01254 stdChildHandling( child );
01255 return true;
01256 }
01257
01258 bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01259 partNode * child = node->firstChild();
01260 if ( !child )
01261 return false;
01262
01263 partNode * dataHtml = child->findType( DwMime::kTypeText,
01264 DwMime::kSubtypeHtml, false, true );
01265 partNode * dataPlain = child->findType( DwMime::kTypeText,
01266 DwMime::kSubtypePlain, false, true );
01267
01268 if ( (mReader && mReader->htmlMail() && dataHtml) ||
01269 (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01270 if ( dataPlain )
01271 dataPlain->setProcessed( true, false );
01272 stdChildHandling( dataHtml );
01273 return true;
01274 }
01275
01276 if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01277 if ( dataHtml )
01278 dataHtml->setProcessed( true, false );
01279 stdChildHandling( dataPlain );
01280 return true;
01281 }
01282
01283 stdChildHandling( child );
01284 return true;
01285 }
01286
01287 bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01288 return processMultiPartMixedSubtype( node, result );
01289 }
01290
01291 bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01292 return processMultiPartMixedSubtype( node, result );
01293 }
01294
01295 bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01296 if ( node->childCount() != 2 ) {
01297 kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
01298 << "processing as multipart/mixed" << endl;
01299 if ( node->firstChild() )
01300 stdChildHandling( node->firstChild() );
01301 return node->firstChild();
01302 }
01303
01304 partNode * signedData = node->firstChild();
01305 assert( signedData );
01306
01307 partNode * signature = signedData->nextSibling();
01308 assert( signature );
01309
01310 signature->setProcessed( true, true );
01311
01312 if ( !includeSignatures() ) {
01313 stdChildHandling( signedData );
01314 return true;
01315 }
01316
01317
01318
01319
01320
01321 const TQString contentType = node->contentTypeParameter( "protocol" ).lower();
01322 const Kleo::CryptoBackend::Protocol *protocol = 0;
01323 if ( contentType == "application/pkcs7-signature" || contentType == "application/x-pkcs7-signature" )
01324 protocol = Kleo::CryptoBackendFactory::instance()->smime();
01325 else if ( contentType == "application/pgp-signature" || contentType == "application/x-pgp-signature" )
01326 protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
01327
01328 if ( !protocol ) {
01329 signature->setProcessed( true, true );
01330 stdChildHandling( signedData );
01331 return true;
01332 }
01333
01334 CryptoProtocolSaver saver( this, protocol );
01335
01336 node->setSignatureState( KMMsgFullySigned );
01337 writeOpaqueOrMultipartSignedData( signedData, *signature,
01338 node->trueFromAddress() );
01339 return true;
01340 }
01341
01342 bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01343 partNode * child = node->firstChild();
01344 if ( !child )
01345 return false;
01346
01347 if ( keepEncryptions() ) {
01348 node->setEncryptionState( KMMsgFullyEncrypted );
01349 const TQCString cstr = node->msgPart().bodyDecoded();
01350 if ( mReader )
01351 writeBodyString( cstr, node->trueFromAddress(),
01352 codecFor( node ), result, false );
01353 mRawReplyString += cstr;
01354 return true;
01355 }
01356
01357 const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
01358
01359
01360
01361
01362 partNode * data = child->findType( DwMime::kTypeApplication,
01363 DwMime::kSubtypeOctetStream, false, true );
01364 if ( data ) {
01365 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
01366 }
01367 if ( !data ) {
01368 data = child->findType( DwMime::kTypeApplication,
01369 DwMime::kSubtypePkcs7Mime, false, true );
01370 if ( data ) {
01371 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
01372 }
01373 }
01374
01375
01376
01377
01378 if ( !data ) {
01379 stdChildHandling( child );
01380 return true;
01381 }
01382
01383 CryptoProtocolSaver cpws( this, useThisCryptProto );
01384
01385 if ( partNode * dataChild = data->firstChild() ) {
01386
01387 stdChildHandling( dataChild );
01388
01389 return true;
01390 }
01391
01392 node->setEncryptionState( KMMsgFullyEncrypted );
01393
01394 if ( mReader && !mReader->decryptMessage() ) {
01395 writeDeferredDecryptionBlock();
01396 data->setProcessed( true, false );
01397 return true;
01398 }
01399
01400
01401 PartMetaData messagePart;
01402 TQCString decryptedData;
01403 bool signatureFound;
01404 std::vector<GpgME::Signature> signatures;
01405 bool passphraseError;
01406 bool actuallyEncrypted = true;
01407 bool decryptionStarted;
01408
01409 bool bOkDecrypt = okDecryptMIME( *data,
01410 decryptedData,
01411 signatureFound,
01412 signatures,
01413 true,
01414 passphraseError,
01415 actuallyEncrypted,
01416 decryptionStarted,
01417 messagePart.errorText,
01418 messagePart.auditLogError,
01419 messagePart.auditLog );
01420
01421 if ( decryptionStarted ) {
01422 writeDecryptionInProgressBlock();
01423 return true;
01424 }
01425
01426
01427 if ( mReader ) {
01428 messagePart.isDecryptable = bOkDecrypt;
01429 messagePart.isEncrypted = true;
01430 messagePart.isSigned = false;
01431 htmlWriter()->queue( writeSigstatHeader( messagePart,
01432 cryptoProtocol(),
01433 node->trueFromAddress() ) );
01434 }
01435
01436 if ( bOkDecrypt ) {
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448 if ( signatureFound ) {
01449 writeOpaqueOrMultipartSignedData( 0,
01450 *node,
01451 node->trueFromAddress(),
01452 false,
01453 &decryptedData,
01454 signatures,
01455 false );
01456 node->setSignatureState( KMMsgFullySigned );
01457 } else {
01458 insertAndParseNewChildNode( *node,
01459 &*decryptedData,
01460 "encrypted data" );
01461 }
01462 } else {
01463 mRawReplyString += decryptedData;
01464 if ( mReader ) {
01465
01466
01467 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
01468 }
01469 }
01470
01471 if ( mReader )
01472 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01473 data->setProcessed( true, false );
01474 return true;
01475 }
01476
01477
01478 bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01479 if ( mReader
01480 && !attachmentStrategy()->inlineNestedMessages()
01481 && !showOnlyOneMimePart() )
01482 return false;
01483
01484 if ( partNode * child = node->firstChild() ) {
01485
01486 ObjectTreeParser otp( mReader, cryptoProtocol() );
01487 otp.parseObjectTree( child );
01488 mRawReplyString += otp.rawReplyString();
01489 mTextualContent += otp.textualContent();
01490 if ( !otp.textualContentCharset().isEmpty() )
01491 mTextualContentCharset = otp.textualContentCharset();
01492
01493 return true;
01494 }
01495
01496
01497 PartMetaData messagePart;
01498 if ( mReader ) {
01499 messagePart.isEncrypted = false;
01500 messagePart.isSigned = false;
01501 messagePart.isEncapsulatedRfc822Message = true;
01502 TQString filename =
01503 mReader->writeMessagePartToTempFile( &node->msgPart(),
01504 node->nodeId() );
01505 htmlWriter()->queue( writeSigstatHeader( messagePart,
01506 cryptoProtocol(),
01507 node->trueFromAddress(),
01508 node ) );
01509 }
01510 TQCString rfc822messageStr( node->msgPart().bodyDecoded() );
01511
01512 DwMessage* rfc822DwMessage = new DwMessage();
01513 rfc822DwMessage->FromString( rfc822messageStr );
01514 rfc822DwMessage->Parse();
01515 KMMessage rfc822message( rfc822DwMessage );
01516 node->setFromAddress( rfc822message.from() );
01517
01518 if ( mReader )
01519 htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01520
01521
01522 insertAndParseNewChildNode( *node,
01523 &*rfc822messageStr,
01524 "encapsulated message", false ,
01525 false );
01526 node->setDisplayedEmbedded( true );
01527 if ( mReader )
01528 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01529 return true;
01530 }
01531
01532
01533 bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01534 if ( partNode * child = node->firstChild() ) {
01535
01536 ObjectTreeParser otp( mReader, cryptoProtocol() );
01537 otp.parseObjectTree( child );
01538 mRawReplyString += otp.rawReplyString();
01539 mTextualContent += otp.textualContent();
01540 if ( !otp.textualContentCharset().isEmpty() )
01541 mTextualContentCharset = otp.textualContentCharset();
01542
01543 return true;
01544 }
01545
01546 const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
01547 if ( node->parentNode()
01548 && DwMime::kTypeMultipart == node->parentNode()->type()
01549 && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01550
01551 node->setEncryptionState( KMMsgFullyEncrypted );
01552 if ( keepEncryptions() ) {
01553 const TQCString cstr = node->msgPart().bodyDecoded();
01554 if ( mReader )
01555 writeBodyString( cstr, node->trueFromAddress(),
01556 codecFor( node ), result, false );
01557 mRawReplyString += cstr;
01558 } else if ( mReader && !mReader->decryptMessage() ) {
01559 writeDeferredDecryptionBlock();
01560 } else {
01561
01562
01563
01564 PartMetaData messagePart;
01565 setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
01566 TQCString decryptedData;
01567 bool signatureFound;
01568 std::vector<GpgME::Signature> signatures;
01569 bool passphraseError;
01570 bool actuallyEncrypted = true;
01571 bool decryptionStarted;
01572
01573 bool bOkDecrypt = okDecryptMIME( *node,
01574 decryptedData,
01575 signatureFound,
01576 signatures,
01577 true,
01578 passphraseError,
01579 actuallyEncrypted,
01580 decryptionStarted,
01581 messagePart.errorText,
01582 messagePart.auditLogError,
01583 messagePart.auditLog );
01584
01585 if ( decryptionStarted ) {
01586 writeDecryptionInProgressBlock();
01587 return true;
01588 }
01589
01590
01591 if ( mReader ) {
01592 messagePart.isDecryptable = bOkDecrypt;
01593 messagePart.isEncrypted = true;
01594 messagePart.isSigned = false;
01595 htmlWriter()->queue( writeSigstatHeader( messagePart,
01596 cryptoProtocol(),
01597 node->trueFromAddress() ) );
01598 }
01599
01600 if ( bOkDecrypt ) {
01601
01602 insertAndParseNewChildNode( *node,
01603 &*decryptedData,
01604 "encrypted data" );
01605 } else {
01606 mRawReplyString += decryptedData;
01607 if ( mReader ) {
01608
01609
01610 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
01611 }
01612 }
01613
01614 if ( mReader )
01615 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01616 }
01617 return true;
01618 }
01619 setCryptoProtocol( oldUseThisCryptPlug );
01620 return false;
01621 }
01622
01623 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01624 if ( partNode * child = node->firstChild() ) {
01625
01626 ObjectTreeParser otp( mReader, cryptoProtocol() );
01627 otp.parseObjectTree( child );
01628 mRawReplyString += otp.rawReplyString();
01629 mTextualContent += otp.textualContent();
01630 if ( !otp.textualContentCharset().isEmpty() )
01631 mTextualContentCharset = otp.textualContentCharset();
01632
01633 return true;
01634 }
01635
01636
01637 if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01638 return false;
01639
01640 const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
01641
01642 const TQString smimeType = node->contentTypeParameter("smime-type").lower();
01643
01644 if ( smimeType == "certs-only" ) {
01645 result.setNeverDisplayInline( true );
01646 if ( !smimeCrypto || !mReader )
01647 return false;
01648
01649 const KConfigGroup reader( KMKernel::config(), "Reader" );
01650 if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
01651 return false;
01652
01653 const TQByteArray certData = node->msgPart().bodyDecodedBinary();
01654
01655 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::ImportJob> import( smimeCrypto->importJob() );
01656 const GpgME::ImportResult res = import->exec( certData );
01657 if ( res.error() ) {
01658 htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
01659 "Reason: %1").arg( TQString::fromLocal8Bit( res.error().asString() ) ) );
01660 return true;
01661 }
01662
01663 const int nImp = res.numImported();
01664 const int nUnc = res.numUnchanged();
01665 const int nSKImp = res.numSecretKeysImported();
01666 const int nSKUnc = res.numSecretKeysUnchanged();
01667 if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01668 htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
01669 return true;
01670 }
01671 TQString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br> <br>";
01672 if ( nImp )
01673 comment += i18n( "1 new certificate was imported.",
01674 "%n new certificates were imported.", nImp ) + "<br>";
01675 if ( nUnc )
01676 comment += i18n( "1 certificate was unchanged.",
01677 "%n certificates were unchanged.", nUnc ) + "<br>";
01678 if ( nSKImp )
01679 comment += i18n( "1 new secret key was imported.",
01680 "%n new secret keys were imported.", nSKImp ) + "<br>";
01681 if ( nSKUnc )
01682 comment += i18n( "1 secret key was unchanged.",
01683 "%n secret keys were unchanged.", nSKUnc ) + "<br>";
01684 comment += " <br>";
01685 htmlWriter()->queue( comment );
01686 if ( !nImp && !nSKImp ) {
01687 htmlWriter()->queue( "<hr>" );
01688 return true;
01689 }
01690 const std::vector<GpgME::Import> imports = res.imports();
01691 if ( imports.empty() ) {
01692 htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
01693 return true;
01694 }
01695 htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
01696 for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01697 if ( (*it).error() )
01698 htmlWriter()->queue( i18n( "Failed: %1 (%2)" )
01699 .arg( (*it).fingerprint(),
01700 TQString::fromLocal8Bit( (*it).error().asString() ) ) );
01701 else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
01702 if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
01703 htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01704 }
01705 else {
01706 htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
01707 }
01708 }
01709 htmlWriter()->queue( "<br>" );
01710 }
01711
01712 htmlWriter()->queue( "<hr>" );
01713 return true;
01714 }
01715
01716 if ( !smimeCrypto )
01717 return false;
01718 CryptoProtocolSaver cpws( this, smimeCrypto );
01719
01720 bool isSigned = smimeType == "signed-data";
01721 bool isEncrypted = smimeType == "enveloped-data";
01722
01723
01724
01725
01726 partNode* signTestNode = isEncrypted ? 0 : node;
01727
01728
01729
01730
01731
01732 if ( !isSigned ) {
01733 if ( isEncrypted ) {
01734
01735 }
01736 else {
01737
01738 }
01739 TQCString decryptedData;
01740 PartMetaData messagePart;
01741 messagePart.isEncrypted = true;
01742 messagePart.isSigned = false;
01743 bool signatureFound;
01744 std::vector<GpgME::Signature> signatures;
01745 bool passphraseError;
01746 bool actuallyEncrypted = true;
01747 bool decryptionStarted;
01748
01749 if ( mReader && !mReader->decryptMessage() ) {
01750 writeDeferredDecryptionBlock();
01751 isEncrypted = true;
01752 signTestNode = 0;
01753 } else {
01754 const bool bOkDecrypt = okDecryptMIME( *node,
01755 decryptedData,
01756 signatureFound,
01757 signatures,
01758 false,
01759 passphraseError,
01760 actuallyEncrypted,
01761 decryptionStarted,
01762 messagePart.errorText,
01763 messagePart.auditLogError,
01764 messagePart.auditLog );
01765 if ( decryptionStarted ) {
01766 writeDecryptionInProgressBlock();
01767 return true;
01768 }
01769 if ( bOkDecrypt ) {
01770
01771 isEncrypted = true;
01772 node->setEncryptionState( KMMsgFullyEncrypted );
01773 signTestNode = 0;
01774
01775 messagePart.isDecryptable = true;
01776 if ( mReader )
01777 htmlWriter()->queue( writeSigstatHeader( messagePart,
01778 cryptoProtocol(),
01779 node->trueFromAddress() ) );
01780 insertAndParseNewChildNode( *node,
01781 &*decryptedData,
01782 "encrypted data" );
01783 if ( mReader )
01784 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01785 } else {
01786
01787
01788
01789
01790 if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
01791 isEncrypted = true;
01792 signTestNode = 0;
01793 }
01794
01795 if ( isEncrypted ) {
01796
01797
01798 messagePart.isDecryptable = false;
01799 if ( mReader ) {
01800 htmlWriter()->queue( writeSigstatHeader( messagePart,
01801 cryptoProtocol(),
01802 node->trueFromAddress() ) );
01803 assert( mReader->decryptMessage() );
01804 writePartIcon( &node->msgPart(), node->nodeId() );
01805 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01806 }
01807 } else {
01808
01809 }
01810 }
01811 }
01812 if ( isEncrypted )
01813 node->setEncryptionState( KMMsgFullyEncrypted );
01814 }
01815
01816
01817 if ( signTestNode ) {
01818 if ( isSigned ) {
01819
01820 }
01821 else {
01822
01823 }
01824
01825 bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01826 *signTestNode,
01827 node->trueFromAddress(),
01828 true,
01829 0,
01830 std::vector<GpgME::Signature>(),
01831 isEncrypted );
01832 if ( sigFound ) {
01833 if ( !isSigned ) {
01834
01835 isSigned = true;
01836 }
01837 signTestNode->setSignatureState( KMMsgFullySigned );
01838 if ( signTestNode != node )
01839 node->setSignatureState( KMMsgFullySigned );
01840 } else {
01841
01842 }
01843 }
01844
01845 return isSigned || isEncrypted;
01846 }
01847
01848 bool ObjectTreeParser::decryptChiasmus( const TQByteArray& data, TQByteArray& bodyDecoded, TQString& errorText )
01849 {
01850 const Kleo::CryptoBackend::Protocol * chiasmus =
01851 Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
01852 Q_ASSERT( chiasmus );
01853 if ( !chiasmus )
01854 return false;
01855
01856 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQMap<TQString,TQVariant>() ) );
01857 if ( !listjob.get() ) {
01858 errorText = i18n( "Chiasmus backend does not offer the "
01859 "\"x-obtain-keys\" function. Please report this bug." );
01860 return false;
01861 }
01862
01863 if ( listjob->exec() ) {
01864 errorText = i18n( "Chiasmus Backend Error" );
01865 return false;
01866 }
01867
01868 const TQVariant result = listjob->property( "result" );
01869 if ( result.type() != TQVariant::StringList ) {
01870 errorText = i18n( "Unexpected return value from Chiasmus backend: "
01871 "The \"x-obtain-keys\" function did not return a "
01872 "string list. Please report this bug." );
01873 return false;
01874 }
01875
01876 const TQStringList keys = result.toStringList();
01877 if ( keys.empty() ) {
01878 errorText = i18n( "No keys have been found. Please check that a "
01879 "valid key path has been set in the Chiasmus "
01880 "configuration." );
01881 return false;
01882 }
01883
01884 emit mReader->noDrag();
01885 ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
01886 keys, GlobalSettings::chiasmusDecryptionKey(),
01887 GlobalSettings::chiasmusDecryptionOptions() );
01888 if ( selectorDlg.exec() != TQDialog::Accepted )
01889 return false;
01890
01891 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
01892 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
01893 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
01894
01895 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-decrypt", TQMap<TQString,TQVariant>() ) );
01896 if ( !job.get() ) {
01897 errorText = i18n( "Chiasmus backend does not offer the "
01898 "\"x-decrypt\" function. Please report this bug." );
01899 return false;
01900 }
01901
01902 if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
01903 !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
01904 !job->setProperty( "input", data ) ) {
01905 errorText = i18n( "The \"x-decrypt\" function does not accept "
01906 "the expected parameters. Please report this bug." );
01907 return false;
01908 }
01909
01910 if ( job->exec() ) {
01911 errorText = i18n( "Chiasmus Decryption Error" );
01912 return false;
01913 }
01914
01915 const TQVariant resultData = job->property( "result" );
01916 if ( resultData.type() != TQVariant::ByteArray ) {
01917 errorText = i18n( "Unexpected return value from Chiasmus backend: "
01918 "The \"x-decrypt\" function did not return a "
01919 "byte array. Please report this bug." );
01920 return false;
01921 }
01922 bodyDecoded = resultData.toByteArray();
01923 return true;
01924 }
01925
01926 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
01927 {
01928 if ( !mReader ) {
01929 mRawReplyString = curNode->msgPart().bodyDecoded();
01930 mTextualContent += curNode->msgPart().bodyToUnicode();
01931 mTextualContentCharset = curNode->msgPart().charset();
01932 return true;
01933 }
01934
01935 TQByteArray decryptedBody;
01936 TQString errorText;
01937 const TQByteArray data = curNode->msgPart().bodyDecodedBinary();
01938 bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
01939 PartMetaData messagePart;
01940 messagePart.isDecryptable = bOkDecrypt;
01941 messagePart.isEncrypted = true;
01942 messagePart.isSigned = false;
01943 messagePart.errorText = errorText;
01944 if ( mReader )
01945 htmlWriter()->queue( writeSigstatHeader( messagePart,
01946 0,
01947 curNode->trueFromAddress() ) );
01948 const TQByteArray body = bOkDecrypt ? decryptedBody : data;
01949 const TQString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
01950 const TQTextCodec* aCodec = chiasmusCharset.isEmpty()
01951 ? codecFor( curNode )
01952 : KMMsgBase::codecForName( chiasmusCharset.ascii() );
01953 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false ) );
01954 result.setInlineEncryptionState( KMMsgFullyEncrypted );
01955 if ( mReader )
01956 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01957 return true;
01958 }
01959
01960 bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
01961 {
01962 Q_UNUSED( result );
01963 if ( !mReader )
01964 return false;
01965
01966 const TQString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
01967 KTNEFParser parser;
01968 if ( !parser.openFile( fileName ) || !parser.message()) {
01969 kdDebug() << k_funcinfo << "Could not parse " << fileName << endl;
01970 return false;
01971 }
01972
01973 TQPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
01974 if ( tnefatts.isEmpty() ) {
01975 kdDebug() << k_funcinfo << "No attachments found in " << fileName << endl;
01976 return false;
01977 }
01978
01979 if ( !showOnlyOneMimePart() ) {
01980 TQString label = node->msgPart().fileName().stripWhiteSpace();
01981 if ( label.isEmpty() )
01982 label = node->msgPart().name().stripWhiteSpace();
01983 label = KMMessage::quoteHtmlChars( label, true );
01984 const TQString comment = KMMessage::quoteHtmlChars( node->msgPart().contentDescription(), true );
01985 const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
01986
01987 TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
01988 "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
01989 if ( !fileName.isEmpty() )
01990 htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
01991 + label + "</a>";
01992 else
01993 htmlStr += label;
01994 if ( !comment.isEmpty() )
01995 htmlStr += "<br>" + comment;
01996 htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
01997 htmlWriter()->queue( htmlStr );
01998 }
01999
02000 for ( uint i = 0; i < tnefatts.count(); ++i ) {
02001 KTNEFAttach *att = tnefatts.at( i );
02002 TQString label = att->displayName();
02003 if( label.isEmpty() )
02004 label = att->name();
02005 label = KMMessage::quoteHtmlChars( label, true );
02006
02007 TQString dir = mReader->createTempDir( "ktnef-" + TQString::number( i ) );
02008 parser.extractFileTo( att->name(), dir );
02009 mReader->mTempFiles.append( dir + TQDir::separator() + att->name() );
02010 TQString href = "file:" + KURL::encode_string( dir + TQDir::separator() + att->name() );
02011
02012 KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
02013 TQString iconName = KGlobal::instance()->iconLoader()->iconPath( mimeType->icon( TQString(), false ), KIcon::Desktop );
02014
02015 htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
02016 iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
02017 "</a></div><br>" );
02018 }
02019
02020 if ( !showOnlyOneMimePart() )
02021 htmlWriter()->queue( "</td></tr></table>" );
02022
02023 return true;
02024 }
02025
02026 void ObjectTreeParser::writeBodyString( const TQCString & bodyString,
02027 const TQString & fromAddress,
02028 const TQTextCodec * codec,
02029 ProcessResult & result,
02030 bool decorate ) {
02031 assert( mReader ); assert( codec );
02032 KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
02033 KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
02034 writeBodyStr( bodyString, codec, fromAddress,
02035 inlineSignatureState, inlineEncryptionState, decorate );
02036 result.setInlineSignatureState( inlineSignatureState );
02037 result.setInlineEncryptionState( inlineEncryptionState );
02038 }
02039
02040 void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
02041 if ( !mReader || !msgPart )
02042 return;
02043
02044 TQString label = msgPart->fileName();
02045 if( label.isEmpty() )
02046 label = msgPart->name();
02047 if( label.isEmpty() )
02048 label = "unnamed";
02049 label = KMMessage::quoteHtmlChars( label, true );
02050
02051 TQString comment = msgPart->contentDescription();
02052 comment = KMMessage::quoteHtmlChars( comment, true );
02053 if ( label == comment ) comment = TQString();
02054
02055 TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
02056
02057 TQString href = TQString( "attachment:%1?place=body" ).arg( partNum );
02058
02059 TQString iconName;
02060 if( inlineImage )
02061 iconName = href;
02062 else {
02063 iconName = msgPart->iconName();
02064 if( iconName.right( 14 ) == "mime_empty.png" ) {
02065 msgPart->magicSetType();
02066 iconName = msgPart->iconName();
02067 }
02068 }
02069
02070 TQCString contentId = msgPart->contentId();
02071 if ( !contentId.isEmpty() ) {
02072 htmlWriter()->embedPart( contentId, href );
02073 }
02074
02075 if( inlineImage )
02076
02077 htmlWriter()->queue( "<div><a href=\"" + href + "\">"
02078 "<img src=\"" + fileName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
02079 "</div>"
02080 "<div><a href=\"" + href + "\">" + label + "</a>"
02081 "</div>"
02082 "<div>" + comment + "</div><br>" );
02083 else
02084
02085 htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
02086 iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
02087 "</a></div>"
02088 "<div>" + comment + "</div><br>" );
02089 }
02090
02091 #define SIG_FRAME_COL_UNDEF 99
02092 #define SIG_FRAME_COL_RED -1
02093 #define SIG_FRAME_COL_YELLOW 0
02094 #define SIG_FRAME_COL_GREEN 1
02095 TQString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
02096 int status_code,
02097 GpgME::Signature::Summary summary,
02098 int& frameColor,
02099 bool& showKeyInfos )
02100 {
02101
02102
02103
02104 showKeyInfos = true;
02105 TQString result;
02106 if( cryptProto ) {
02107 if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
02108
02109
02110 switch( status_code ) {
02111 case 0:
02112 result = i18n("Error: Signature not verified");
02113 break;
02114 case 1:
02115 result = i18n("Good signature");
02116 break;
02117 case 2:
02118 result = i18n("<b>Bad</b> signature");
02119 break;
02120 case 3:
02121 result = i18n("No public key to verify the signature");
02122 break;
02123 case 4:
02124 result = i18n("No signature found");
02125 break;
02126 case 5:
02127 result = i18n("Error verifying the signature");
02128 break;
02129 case 6:
02130 result = i18n("Different results for signatures");
02131 break;
02132
02133
02134
02135
02136
02137
02138
02139
02140 default:
02141 result = "";
02142 break;
02143 }
02144 }
02145 else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
02146
02147
02148
02149 if( summary == GpgME::Signature::None ) {
02150 result = i18n("No status information available.");
02151 frameColor = SIG_FRAME_COL_YELLOW;
02152 showKeyInfos = false;
02153 return result;
02154 }
02155
02156 if( summary & GpgME::Signature::Valid ) {
02157 result = i18n("Good signature.");
02158
02159
02160
02161
02162
02163
02164
02165
02166 frameColor = SIG_FRAME_COL_GREEN;
02167 showKeyInfos = false;
02168 return result;
02169 }
02170
02171
02172
02173
02174 frameColor = SIG_FRAME_COL_GREEN;
02175 TQString result2;
02176 if( summary & GpgME::Signature::KeyExpired ){
02177
02178 result2 += i18n("One key has expired.");
02179 }
02180 if( summary & GpgME::Signature::SigExpired ){
02181
02182 result2 += i18n("The signature has expired.");
02183 }
02184
02185
02186 if( summary & GpgME::Signature::KeyMissing ) {
02187 result2 += i18n("Unable to verify: key missing.");
02188
02189
02190 showKeyInfos = false;
02191 frameColor = SIG_FRAME_COL_YELLOW;
02192 }
02193 if( summary & GpgME::Signature::CrlMissing ){
02194 result2 += i18n("CRL not available.");
02195 frameColor = SIG_FRAME_COL_YELLOW;
02196 }
02197 if( summary & GpgME::Signature::CrlTooOld ){
02198 result2 += i18n("Available CRL is too old.");
02199 frameColor = SIG_FRAME_COL_YELLOW;
02200 }
02201 if( summary & GpgME::Signature::BadPolicy ){
02202 result2 += i18n("A policy was not met.");
02203 frameColor = SIG_FRAME_COL_YELLOW;
02204 }
02205 if( summary & GpgME::Signature::SysError ){
02206 result2 += i18n("A system error occurred.");
02207
02208
02209
02210 showKeyInfos = false;
02211 frameColor = SIG_FRAME_COL_YELLOW;
02212 }
02213
02214
02215 if( summary & GpgME::Signature::KeyRevoked ){
02216
02217 result2 += i18n("One key has been revoked.");
02218 frameColor = SIG_FRAME_COL_RED;
02219 }
02220 if( summary & GpgME::Signature::Red ) {
02221 if( result2.isEmpty() )
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234 showKeyInfos = false;
02235 frameColor = SIG_FRAME_COL_RED;
02236 }
02237 else
02238 result = "";
02239
02240 if( SIG_FRAME_COL_GREEN == frameColor ) {
02241 result = i18n("Good signature.");
02242 } else if( SIG_FRAME_COL_RED == frameColor ) {
02243 result = i18n("<b>Bad</b> signature.");
02244 } else
02245 result = "";
02246
02247 if( !result2.isEmpty() ) {
02248 if( !result.isEmpty() )
02249 result.append("<br />");
02250 result.append( result2 );
02251 }
02252 }
02253
02254
02255
02256
02257
02258
02259 }
02260 return result;
02261 }
02262
02263
02264 static TQString writeSimpleSigstatHeader( const PartMetaData &block )
02265 {
02266 TQString html;
02267 html += "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
02268
02269 if ( block.signClass == "signErr" ) {
02270 html += i18n( "Invalid signature." );
02271 } else if ( block.signClass == "signOkKeyBad" || block.signClass == "signWarn" ) {
02272 html += i18n( "Not enough information to check signature validity." );
02273 } else if ( block.signClass == "signOkKeyOk" ) {
02274 TQString addr;
02275 if ( !block.signerMailAddresses.isEmpty() )
02276 addr = block.signerMailAddresses.first();
02277 TQString name = addr;
02278 if ( name.isEmpty() )
02279 name = block.signer;
02280 if ( addr.isEmpty() ) {
02281 html += i18n( "Signature is valid." );
02282 } else {
02283 html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
02284 }
02285 } else {
02286
02287 html += i18n( "Unknown signature state" );
02288 }
02289 html += "</td><td align=\"right\">";
02290 html += "<a href=\"kmail:showSignatureDetails\">";
02291 html += i18n( "Show Details" );
02292 html += "</a></td></tr></table>";
02293 return html;
02294 }
02295
02296 static TQString beginVerboseSigstatHeader()
02297 {
02298 return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
02299 }
02300
02301 static TQString makeShowAuditLogLink( const GpgME::Error & err, const TQString & auditLog ) {
02302 if ( const unsigned int code = err.code() ) {
02303 if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
02304
02305 return TQString();
02306 } else if ( code == GPG_ERR_NO_DATA ) {
02307
02308 return i18n("No Audit Log available");
02309 } else {
02310 return i18n("Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) );
02311 }
02312 }
02313
02314 if ( !auditLog.isEmpty() ) {
02315 KURL url;
02316 url.setProtocol( "kmail" );
02317 url.setPath( "showAuditLog" );
02318 url.addQueryItem( "log", auditLog );
02319
02320 return "<a href=\"" + url.htmlURL() + "\">" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + "</a>";
02321 }
02322
02323 return TQString();
02324 }
02325
02326 static TQString endVerboseSigstatHeader( const PartMetaData & pmd )
02327 {
02328 TQString html;
02329 html += "</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
02330 html += "<a href=\"kmail:hideSignatureDetails\">";
02331 html += i18n( "Hide Details" );
02332 html += "</a></td></tr>";
02333 html += "<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
02334 html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
02335 html += "</td></tr></table>";
02336 return html;
02337 }
02338
02339 TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
02340 const Kleo::CryptoBackend::Protocol * cryptProto,
02341 const TQString & fromAddress,
02342 partNode *node )
02343 {
02344 const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
02345 TQString signer = block.signer;
02346
02347 TQString htmlStr, simpleHtmlStr;
02348 TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
02349 TQString cellPadding("cellpadding=\"1\"");
02350
02351 if( block.isEncapsulatedRfc822Message )
02352 {
02353 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
02354 "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
02355 if ( node )
02356 htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
02357 + i18n("Encapsulated message") + "</a>";
02358 else
02359 htmlStr += i18n("Encapsulated message");
02360 htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
02361 }
02362
02363 if( block.isEncrypted )
02364 {
02365 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
02366 "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
02367 if ( block.inProgress )
02368 htmlStr += i18n("Please wait while the message is being decrypted...");
02369 else if ( block.isDecryptable )
02370 htmlStr += i18n("Encrypted message");
02371 else {
02372 htmlStr += i18n("Encrypted message (decryption not possible)");
02373 if( !block.errorText.isEmpty() )
02374 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
02375 }
02376 htmlStr += "</td></tr><tr class=\"encrB\"><td>";
02377 }
02378
02379 if ( block.isSigned && block.inProgress )
02380 {
02381 block.signClass = "signInProgress";
02382 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"signInProgress\">"
02383 "<tr class=\"signInProgressH\"><td dir=\"" + dir + "\">";
02384 htmlStr += i18n("Please wait while the signature is being verified...");
02385 htmlStr += "</td></tr><tr class=\"signInProgressB\"><td>";
02386 }
02387 simpleHtmlStr = htmlStr;
02388
02389 if ( block.isSigned && !block.inProgress ) {
02390 TQStringList& blockAddrs( block.signerMailAddresses );
02391
02392
02393
02394 int frameColor = SIG_FRAME_COL_UNDEF;
02395 bool showKeyInfos;
02396 bool onlyShowKeyURL = false;
02397 bool cannotCheckSignature = true;
02398 TQString statusStr = sigStatusToString( cryptProto,
02399 block.status_code,
02400 block.sigSummary,
02401 frameColor,
02402 showKeyInfos );
02403
02404
02405 if( statusStr.isEmpty() )
02406 statusStr = block.status;
02407 if( block.technicalProblem )
02408 frameColor = SIG_FRAME_COL_YELLOW;
02409
02410 switch( frameColor ){
02411 case SIG_FRAME_COL_RED:
02412 cannotCheckSignature = false;
02413 break;
02414 case SIG_FRAME_COL_YELLOW:
02415 cannotCheckSignature = true;
02416 break;
02417 case SIG_FRAME_COL_GREEN:
02418 cannotCheckSignature = false;
02419 break;
02420 }
02421
02422
02423
02424
02425
02426
02427 TQString startKeyHREF;
02428 if( isSMIME )
02429 startKeyHREF =
02430 TQString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02431 .arg( cryptProto->displayName(),
02432 cryptProto->name(),
02433 block.keyId );
02434 TQString keyWithWithoutURL
02435 = isSMIME
02436 ? TQString("%1%2</a>")
02437 .arg( startKeyHREF,
02438 cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02439 : "0x" + TQString::fromUtf8( block.keyId );
02440
02441
02442
02443 showKeyInfos = true;
02444
02445
02446
02447
02448 if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02449
02450
02451
02452 if( !statusStr.isEmpty() ) {
02453 statusStr.prepend("<i>");
02454 statusStr.append( "</i>");
02455 }
02456
02457
02458 switch( frameColor ) {
02459 case SIG_FRAME_COL_RED:
02460 block.signClass = "signErr";
02461 onlyShowKeyURL = true;
02462 break;
02463 case SIG_FRAME_COL_YELLOW:
02464 if( block.technicalProblem )
02465 block.signClass = "signWarn";
02466 else
02467 block.signClass = "signOkKeyBad";
02468 break;
02469 case SIG_FRAME_COL_GREEN:
02470 block.signClass = "signOkKeyOk";
02471
02472
02473 TQString greenCaseWarning;
02474 TQString msgFrom( KPIM::getEmailAddress(fromAddress) );
02475 TQString certificate;
02476 if( block.keyId.isEmpty() )
02477 certificate = i18n("certificate");
02478 else
02479 certificate = startKeyHREF + i18n("certificate") + "</a>";
02480 if( !blockAddrs.empty() ){
02481 if( blockAddrs.grep(
02482 msgFrom,
02483 false ).isEmpty() ) {
02484 greenCaseWarning =
02485 "<u>" +
02486 i18n("Warning:") +
02487 "</u> " +
02488 i18n("Sender's mail address is not stored "
02489 "in the %1 used for signing.").arg(certificate) +
02490 "<br />" +
02491 i18n("sender: ") +
02492 msgFrom +
02493 "<br />" +
02494 i18n("stored: ");
02495
02496
02497
02498
02499 bool bStart = true;
02500 for(TQStringList::ConstIterator it = blockAddrs.begin();
02501 it != blockAddrs.end(); ++it ){
02502 if( !bStart )
02503 greenCaseWarning.append(", <br /> ");
02504 bStart = false;
02505 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02506 }
02507 }
02508 } else {
02509 greenCaseWarning =
02510 "<u>" +
02511 i18n("Warning:") +
02512 "</u> " +
02513 i18n("No mail address is stored in the %1 used for signing, "
02514 "so we cannot compare it to the sender's address %2.")
02515 .arg(certificate,msgFrom);
02516 }
02517 if( !greenCaseWarning.isEmpty() ) {
02518 if( !statusStr.isEmpty() )
02519 statusStr.append("<br /> <br />");
02520 statusStr.append( greenCaseWarning );
02521 }
02522 break;
02523 }
02524
02525 TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02526 "class=\"" + block.signClass + "\">"
02527 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02528 htmlStr += frame + beginVerboseSigstatHeader();
02529 simpleHtmlStr += frame;
02530 simpleHtmlStr += writeSimpleSigstatHeader( block );
02531 if( block.technicalProblem ) {
02532 htmlStr += block.errorText;
02533 }
02534 else if( showKeyInfos ) {
02535 if( cannotCheckSignature ) {
02536 htmlStr += i18n( "Not enough information to check "
02537 "signature. %1" )
02538 .arg( keyWithWithoutURL );
02539 }
02540 else {
02541
02542 if (block.signer.isEmpty())
02543 signer = "";
02544 else {
02545 if( !blockAddrs.empty() ){
02546 TQString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02547 signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02548 }
02549 }
02550
02551 if( block.keyId.isEmpty() ) {
02552 if( signer.isEmpty() || onlyShowKeyURL )
02553 htmlStr += i18n( "Message was signed with unknown key." );
02554 else
02555 htmlStr += i18n( "Message was signed by %1." )
02556 .arg( signer );
02557 } else {
02558 TQDateTime created = block.creationTime;
02559 if( created.isValid() ) {
02560 if( signer.isEmpty() ) {
02561 if( onlyShowKeyURL )
02562 htmlStr += i18n( "Message was signed with key %1." )
02563 .arg( keyWithWithoutURL );
02564 else
02565 htmlStr += i18n( "Message was signed on %1 with key %2." )
02566 .arg( KGlobal::locale()->formatDateTime( created ),
02567 keyWithWithoutURL );
02568 }
02569 else {
02570 if( onlyShowKeyURL )
02571 htmlStr += i18n( "Message was signed with key %1." )
02572 .arg( keyWithWithoutURL );
02573 else
02574 htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02575 .arg( KGlobal::locale()->formatDateTime( created ),
02576 keyWithWithoutURL,
02577 signer );
02578 }
02579 }
02580 else {
02581 if( signer.isEmpty() || onlyShowKeyURL )
02582 htmlStr += i18n( "Message was signed with key %1." )
02583 .arg( keyWithWithoutURL );
02584 else
02585 htmlStr += i18n( "Message was signed by %2 with key %1." )
02586 .arg( keyWithWithoutURL,
02587 signer );
02588 }
02589 }
02590 }
02591 htmlStr += "<br />";
02592 if( !statusStr.isEmpty() ) {
02593 htmlStr += " <br />";
02594 htmlStr += i18n( "Status: " );
02595 htmlStr += statusStr;
02596 }
02597 } else {
02598 htmlStr += statusStr;
02599 }
02600 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02601 htmlStr += endVerboseSigstatHeader( block ) + frame;
02602 simpleHtmlStr += frame;
02603
02604 } else {
02605
02606
02607
02608 if( block.signer.isEmpty() || block.technicalProblem ) {
02609 block.signClass = "signWarn";
02610 TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02611 "class=\"" + block.signClass + "\">"
02612 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02613 htmlStr += frame + beginVerboseSigstatHeader();
02614 simpleHtmlStr += frame;
02615 simpleHtmlStr += writeSimpleSigstatHeader( block );
02616 if( block.technicalProblem ) {
02617 htmlStr += block.errorText;
02618 }
02619 else {
02620 if( !block.keyId.isEmpty() ) {
02621 TQDateTime created = block.creationTime;
02622 if ( created.isValid() )
02623 htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02624 .arg( KGlobal::locale()->formatDateTime( created ),
02625 keyWithWithoutURL );
02626 else
02627 htmlStr += i18n( "Message was signed with unknown key %1." )
02628 .arg( keyWithWithoutURL );
02629 }
02630 else
02631 htmlStr += i18n( "Message was signed with unknown key." );
02632 htmlStr += "<br />";
02633 htmlStr += i18n( "The validity of the signature cannot be "
02634 "verified." );
02635 if( !statusStr.isEmpty() ) {
02636 htmlStr += "<br />";
02637 htmlStr += i18n( "Status: " );
02638 htmlStr += "<i>";
02639 htmlStr += statusStr;
02640 htmlStr += "</i>";
02641 }
02642 }
02643 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02644 htmlStr += endVerboseSigstatHeader( block ) + frame;
02645 simpleHtmlStr += frame;
02646 }
02647 else
02648 {
02649
02650 signer = KMMessage::quoteHtmlChars( signer, true );
02651 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02652
02653 if (block.isGoodSignature) {
02654 if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02655 block.signClass = "signOkKeyBad";
02656 else
02657 block.signClass = "signOkKeyOk";
02658 TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02659 "class=\"" + block.signClass + "\">"
02660 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02661 htmlStr += frame + beginVerboseSigstatHeader();
02662 simpleHtmlStr += frame;
02663 simpleHtmlStr += writeSimpleSigstatHeader( block );
02664 if( !block.keyId.isEmpty() )
02665 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02666 .arg( keyWithWithoutURL,
02667 signer );
02668 else
02669 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02670 htmlStr += "<br />";
02671
02672 switch( block.keyTrust )
02673 {
02674 case Kpgp::KPGP_VALIDITY_UNKNOWN:
02675 htmlStr += i18n( "The signature is valid, but the key's "
02676 "validity is unknown." );
02677 break;
02678 case Kpgp::KPGP_VALIDITY_MARGINAL:
02679 htmlStr += i18n( "The signature is valid and the key is "
02680 "marginally trusted." );
02681 break;
02682 case Kpgp::KPGP_VALIDITY_FULL:
02683 htmlStr += i18n( "The signature is valid and the key is "
02684 "fully trusted." );
02685 break;
02686 case Kpgp::KPGP_VALIDITY_ULTIMATE:
02687 htmlStr += i18n( "The signature is valid and the key is "
02688 "ultimately trusted." );
02689 break;
02690 default:
02691 htmlStr += i18n( "The signature is valid, but the key is "
02692 "untrusted." );
02693 }
02694 frame = "</td></tr>"
02695 "<tr class=\"" + block.signClass + "B\"><td>";
02696 htmlStr += endVerboseSigstatHeader( block ) + frame;
02697 simpleHtmlStr += frame;
02698 }
02699 else
02700 {
02701 block.signClass = "signErr";
02702 TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02703 "class=\"" + block.signClass + "\">"
02704 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02705 htmlStr += frame + beginVerboseSigstatHeader();
02706 simpleHtmlStr += frame;
02707 simpleHtmlStr += writeSimpleSigstatHeader( block );
02708 if( !block.keyId.isEmpty() )
02709 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02710 .arg( keyWithWithoutURL,
02711 signer );
02712 else
02713 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02714 htmlStr += "<br />";
02715 htmlStr += i18n("Warning: The signature is bad.");
02716 frame = "</td></tr>"
02717 "<tr class=\"" + block.signClass + "B\"><td>";
02718 htmlStr += endVerboseSigstatHeader( block ) + frame;
02719 simpleHtmlStr += frame;
02720 }
02721 }
02722 }
02723 }
02724
02725 if ( mReader->showSignatureDetails() )
02726 return htmlStr;
02727 return simpleHtmlStr;
02728 }
02729
02730 TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02731 {
02732 TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
02733
02734 TQString htmlStr;
02735
02736 if (block.isSigned) {
02737 htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02738 htmlStr += "<td dir=\"" + dir + "\">" +
02739 i18n( "End of signed message" ) +
02740 "</td></tr></table>";
02741 }
02742
02743 if (block.isEncrypted) {
02744 htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02745 i18n( "End of encrypted message" ) +
02746 "</td></tr></table>";
02747 }
02748
02749 if( block.isEncapsulatedRfc822Message )
02750 {
02751 htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02752 i18n( "End of encapsulated message" ) +
02753 "</td></tr></table>";
02754 }
02755
02756 return htmlStr;
02757 }
02758
02759
02760
02761 void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
02762 {
02763 if ( !mReader )
02764 return;
02765
02766 htmlWriter()->queue( TQString( "<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
02767 }
02768
02769
02770
02771 void ObjectTreeParser::writeAttachmentMarkFooter()
02772 {
02773 if ( !mReader )
02774 return;
02775
02776 htmlWriter()->queue( TQString( "</div>" ) );
02777 }
02778
02779
02780 void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
02781 const TQString& fromAddress )
02782 {
02783 KMMsgSignatureState dummy1;
02784 KMMsgEncryptionState dummy2;
02785 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02786 }
02787
02788
02789 void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
02790 const TQString& fromAddress,
02791 KMMsgSignatureState& inlineSignatureState,
02792 KMMsgEncryptionState& inlineEncryptionState,
02793 bool decorate )
02794 {
02795 bool goodSignature = false;
02796 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02797 assert(pgp != 0);
02798 bool isPgpMessage = false;
02799
02800 TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
02801 TQString headerStr = TQString("<div dir=\"%1\">").arg(dir);
02802
02803 inlineSignatureState = KMMsgNotSigned;
02804 inlineEncryptionState = KMMsgNotEncrypted;
02805 TQPtrList<Kpgp::Block> pgpBlocks;
02806 TQStrList nonPgpBlocks;
02807 if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02808 {
02809 bool isEncrypted = false, isSigned = false;
02810 bool fullySignedOrEncrypted = true;
02811 bool firstNonPgpBlock = true;
02812 bool couldDecrypt = false;
02813 TQString signer;
02814 TQCString keyId;
02815 TQString decryptionError;
02816 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02817
02818 TQPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02819
02820 TQStrListIterator npbit( nonPgpBlocks );
02821
02822 TQString htmlStr;
02823 for( ; *pbit != 0; ++pbit, ++npbit )
02824 {
02825
02826 TQCString str( *npbit );
02827 if( !str.isEmpty() ) {
02828 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02829
02830
02831
02832
02833 if( firstNonPgpBlock ) {
02834
02835 for( TQCString::ConstIterator c = str.begin(); *c; ++c ) {
02836 if( *c != '\n' ) {
02837 fullySignedOrEncrypted = false;
02838 break;
02839 }
02840 }
02841 }
02842 else {
02843 fullySignedOrEncrypted = false;
02844 }
02845 }
02846 firstNonPgpBlock = false;
02847
02848
02849
02850 Kpgp::Block* block = *pbit;
02851 if( ( block->type() == Kpgp::PgpMessageBlock &&
02852
02853 !kmkernel->contextMenuShown() ) ||
02854 ( block->type() == Kpgp::ClearsignedBlock ) )
02855 {
02856 isPgpMessage = true;
02857 if( block->type() == Kpgp::PgpMessageBlock )
02858 {
02859 if ( mReader )
02860 emit mReader->noDrag();
02861
02862 couldDecrypt = block->decrypt();
02863 isEncrypted = block->isEncrypted();
02864 if (!couldDecrypt) {
02865 decryptionError = pgp->lastErrorMsg();
02866 }
02867 }
02868 else
02869 {
02870
02871 block->verify();
02872 }
02873
02874 isSigned = block->isSigned();
02875 if( isSigned )
02876 {
02877 keyId = block->signatureKeyId();
02878 signer = block->signatureUserId();
02879 if( !signer.isEmpty() )
02880 {
02881 goodSignature = block->goodSignature();
02882
02883 if( !keyId.isEmpty() ) {
02884 keyTrust = pgp->keyTrust( keyId );
02885 Kpgp::Key* key = pgp->publicKey( keyId );
02886 if ( key ) {
02887
02888
02889 signer = key->primaryUserID();
02890 }
02891 }
02892 else
02893
02894
02895 keyTrust = pgp->keyTrust( signer );
02896 }
02897 }
02898
02899 if( isSigned )
02900 inlineSignatureState = KMMsgPartiallySigned;
02901 if( isEncrypted )
02902 inlineEncryptionState = KMMsgPartiallyEncrypted;
02903
02904 PartMetaData messagePart;
02905
02906 messagePart.isSigned = isSigned;
02907 messagePart.technicalProblem = false;
02908 messagePart.isGoodSignature = goodSignature;
02909 messagePart.isEncrypted = isEncrypted;
02910 messagePart.isDecryptable = couldDecrypt;
02911 messagePart.decryptionError = decryptionError;
02912 messagePart.signer = signer;
02913 messagePart.keyId = keyId;
02914 messagePart.keyTrust = keyTrust;
02915
02916 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02917
02918 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02919 htmlStr += writeSigstatFooter( messagePart );
02920 }
02921 else
02922 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02923 decorate );
02924 }
02925
02926
02927 TQCString str( nonPgpBlocks.last() );
02928 if( !str.isEmpty() ) {
02929 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02930
02931
02932
02933
02934
02935 }
02936 if( fullySignedOrEncrypted ) {
02937 if( inlineSignatureState == KMMsgPartiallySigned )
02938 inlineSignatureState = KMMsgFullySigned;
02939 if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02940 inlineEncryptionState = KMMsgFullyEncrypted;
02941 }
02942 htmlWriter()->queue( htmlStr );
02943 }
02944 else
02945 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02946 }
02947
02948
02949 TQString ObjectTreeParser::quotedHTML( const TQString& s, bool decorate )
02950 {
02951 assert( mReader );
02952 assert( cssHelper() );
02953
02954 int convertFlags = LinkLocator::PreserveSpaces;
02955 if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02956 convertFlags |= LinkLocator::ReplaceSmileys;
02957 }
02958 TQString htmlStr;
02959 const TQString normalStartTag = cssHelper()->nonQuotedFontTag();
02960 TQString quoteFontTag[3];
02961 TQString deepQuoteFontTag[3];
02962 for ( int i = 0 ; i < 3 ; ++i ) {
02963 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02964 deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02965 }
02966 const TQString normalEndTag = "</div>";
02967 const TQString quoteEnd = "</div>";
02968
02969 unsigned int pos, beg;
02970 const unsigned int length = s.length();
02971
02972
02973 for ( pos = 0; pos < length && s.at(pos) <= TQChar(' '); pos++ ) { ; }
02974 while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02975 beg = pos;
02976
02977 int currQuoteLevel = -2;
02978 bool curHidden = false;
02979
02980 while (beg<length)
02981 {
02982 TQString line;
02983
02984
02985 pos = s.find('\n', beg, FALSE);
02986 if (pos == (unsigned int)(-1))
02987 pos = length;
02988
02989 line = s.mid(beg,pos-beg);
02990 beg = pos+1;
02991
02992
02993 int actQuoteLevel = -1;
02994
02995 if ( GlobalSettings::self()->showExpandQuotesMark() )
02996 {
02997
02998 if ( mCollapseIcon.isEmpty() ) {
02999 mCollapseIcon= LinkLocator::pngToDataUrl(
03000 KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
03001 }
03002 if ( mExpandIcon.isEmpty() )
03003 mExpandIcon= LinkLocator::pngToDataUrl(
03004 KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
03005 }
03006
03007 for (unsigned int p=0; p<line.length(); p++) {
03008 switch (line[p].latin1()) {
03009 case '>':
03010 case '|':
03011 actQuoteLevel++;
03012 break;
03013 case ' ':
03014 case '\t':
03015 case '\r':
03016 break;
03017 default:
03018 p = line.length();
03019 break;
03020 }
03021 }
03022
03023 bool actHidden = false;
03024 TQString textExpand;
03025
03026
03027 if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
03028 && mReader->mLevelQuote <= ( actQuoteLevel ) )
03029 actHidden = true;
03030
03031 if ( actQuoteLevel != currQuoteLevel ) {
03032
03033 if (currQuoteLevel == -1)
03034 htmlStr.append( normalEndTag );
03035 else if ( currQuoteLevel >= 0 && !curHidden )
03036 htmlStr.append( quoteEnd );
03037
03038
03039 if (actQuoteLevel == -1)
03040 htmlStr += normalStartTag;
03041 else
03042 {
03043 if ( GlobalSettings::self()->showExpandQuotesMark() )
03044 {
03045 if ( actHidden )
03046 {
03047
03048 if ( !curHidden )
03049 {
03050
03051 htmlStr += "<div class=\"quotelevelmark\" >" ;
03052 htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
03053 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03054 .arg(-1)
03055 .arg( mExpandIcon );
03056 htmlStr += "</div><br/>";
03057 htmlStr += quoteEnd;
03058 }
03059 }else {
03060 htmlStr += "<div class=\"quotelevelmark\" >" ;
03061 htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
03062 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03063 .arg(actQuoteLevel)
03064 .arg( mCollapseIcon);
03065 htmlStr += "</div>";
03066 if ( actQuoteLevel < 3 )
03067 htmlStr += quoteFontTag[actQuoteLevel];
03068 else
03069 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03070 }
03071 } else
03072 if ( actQuoteLevel < 3 )
03073 htmlStr += quoteFontTag[actQuoteLevel];
03074 else
03075 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03076 }
03077 currQuoteLevel = actQuoteLevel;
03078 }
03079 curHidden = actHidden;
03080
03081
03082 if ( !actHidden )
03083 {
03084
03085
03086 if( !line.replace('\015', "").isEmpty() )
03087 {
03088 htmlStr +=TQString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
03089 htmlStr += LinkLocator::convertToHtml( line, convertFlags );
03090 htmlStr += TQString( "</div>" );
03091 }
03092 else
03093 htmlStr += "<br>";
03094 }
03095 }
03096
03097
03098 if (currQuoteLevel == -1)
03099 htmlStr.append( normalEndTag );
03100 else
03101 htmlStr.append( quoteEnd );
03102
03103 return htmlStr;
03104 }
03105
03106
03107
03108 const TQTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
03109 assert( node );
03110 if ( mReader && mReader->overrideCodec() )
03111 return mReader->overrideCodec();
03112 return node->msgPart().codec();
03113 }
03114
03115 #ifdef MARCS_DEBUG
03116 void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
03117 size_t len ) {
03118 assert( filename );
03119
03120 TQFile f( filename );
03121 if ( f.open( IO_WriteOnly ) ) {
03122 if ( start ) {
03123 TQDataStream ds( &f );
03124 ds.writeRawBytes( start, len );
03125 }
03126 f.close();
03127 }
03128 }
03129 #endif // !NDEBUG
03130
03131
03132 }