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