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