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