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 QString ObjectTreeParser::defaultToltecReplacementText()
01214 {
01215 return i18n( "This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
01216 "Microsoft Outlook in combination with the Toltec connector." );
01217 }
01218
01219 bool ObjectTreeParser::processToltecMail( partNode *node )
01220 {
01221 if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
01222 !node->isToltecMessage() || mShowRawToltecMail )
01223 return false;
01224
01225 htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
01226 htmlWriter()->queue( "<br><br><a href=\"kmail:showRawToltecMail\">" +
01227 i18n( "Show Raw Message" ) + "</a>" );
01228 return true;
01229 }
01230
01231 bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01232
01233 if ( processToltecMail( node ) ) {
01234 return true;
01235 }
01236
01237 partNode * child = node->firstChild();
01238 if ( !child )
01239 return false;
01240
01241
01242 stdChildHandling( child );
01243 return true;
01244 }
01245
01246 bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01247 partNode * child = node->firstChild();
01248 if ( !child )
01249 return false;
01250
01251 partNode * dataHtml = child->findType( DwMime::kTypeText,
01252 DwMime::kSubtypeHtml, false, true );
01253 partNode * dataPlain = child->findType( DwMime::kTypeText,
01254 DwMime::kSubtypePlain, false, true );
01255
01256 if ( (mReader && mReader->htmlMail() && dataHtml) ||
01257 (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01258 if ( dataPlain )
01259 dataPlain->setProcessed( true, false );
01260 stdChildHandling( dataHtml );
01261 return true;
01262 }
01263
01264 if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01265 if ( dataHtml )
01266 dataHtml->setProcessed( true, false );
01267 stdChildHandling( dataPlain );
01268 return true;
01269 }
01270
01271 stdChildHandling( child );
01272 return true;
01273 }
01274
01275 bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01276 return processMultiPartMixedSubtype( node, result );
01277 }
01278
01279 bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01280 return processMultiPartMixedSubtype( node, result );
01281 }
01282
01283 bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01284 if ( node->childCount() != 2 ) {
01285 kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
01286 << "processing as multipart/mixed" << endl;
01287 if ( node->firstChild() )
01288 stdChildHandling( node->firstChild() );
01289 return node->firstChild();
01290 }
01291
01292 partNode * signedData = node->firstChild();
01293 assert( signedData );
01294
01295 partNode * signature = signedData->nextSibling();
01296 assert( signature );
01297
01298 signature->setProcessed( true, true );
01299
01300 if ( !includeSignatures() ) {
01301 stdChildHandling( signedData );
01302 return true;
01303 }
01304
01305
01306
01307
01308
01309 const QString contentType = node->contentTypeParameter( "protocol" ).lower();
01310 const Kleo::CryptoBackend::Protocol *protocol = 0;
01311 if ( contentType == "application/pkcs7-signature" || contentType == "application/x-pkcs7-signature" )
01312 protocol = Kleo::CryptoBackendFactory::instance()->smime();
01313 else if ( contentType == "application/pgp-signature" || contentType == "application/x-pgp-signature" )
01314 protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
01315
01316 if ( !protocol ) {
01317 signature->setProcessed( true, true );
01318 stdChildHandling( signedData );
01319 return true;
01320 }
01321
01322 CryptoProtocolSaver saver( this, protocol );
01323
01324 node->setSignatureState( KMMsgFullySigned );
01325 writeOpaqueOrMultipartSignedData( signedData, *signature,
01326 node->trueFromAddress() );
01327 return true;
01328 }
01329
01330 bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01331 partNode * child = node->firstChild();
01332 if ( !child )
01333 return false;
01334
01335 if ( keepEncryptions() ) {
01336 node->setEncryptionState( KMMsgFullyEncrypted );
01337 const QCString cstr = node->msgPart().bodyDecoded();
01338 if ( mReader )
01339 writeBodyString( cstr, node->trueFromAddress(),
01340 codecFor( node ), result, false );
01341 mRawReplyString += cstr;
01342 return true;
01343 }
01344
01345 const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
01346
01347
01348
01349
01350 partNode * data = child->findType( DwMime::kTypeApplication,
01351 DwMime::kSubtypeOctetStream, false, true );
01352 if ( data ) {
01353 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
01354 }
01355 if ( !data ) {
01356 data = child->findType( DwMime::kTypeApplication,
01357 DwMime::kSubtypePkcs7Mime, false, true );
01358 if ( data ) {
01359 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
01360 }
01361 }
01362
01363
01364
01365
01366 if ( !data ) {
01367 stdChildHandling( child );
01368 return true;
01369 }
01370
01371 CryptoProtocolSaver cpws( this, useThisCryptProto );
01372
01373 if ( partNode * dataChild = data->firstChild() ) {
01374
01375 stdChildHandling( dataChild );
01376
01377 return true;
01378 }
01379
01380 node->setEncryptionState( KMMsgFullyEncrypted );
01381
01382 if ( mReader && !mReader->decryptMessage() ) {
01383 writeDeferredDecryptionBlock();
01384 data->setProcessed( true, false );
01385 return true;
01386 }
01387
01388
01389 PartMetaData messagePart;
01390 QCString decryptedData;
01391 bool signatureFound;
01392 std::vector<GpgME::Signature> signatures;
01393 bool passphraseError;
01394 bool actuallyEncrypted = true;
01395 bool decryptionStarted;
01396
01397 bool bOkDecrypt = okDecryptMIME( *data,
01398 decryptedData,
01399 signatureFound,
01400 signatures,
01401 true,
01402 passphraseError,
01403 actuallyEncrypted,
01404 decryptionStarted,
01405 messagePart.errorText,
01406 messagePart.auditLogError,
01407 messagePart.auditLog );
01408
01409 if ( decryptionStarted ) {
01410 writeDecryptionInProgressBlock();
01411 return true;
01412 }
01413
01414
01415 if ( mReader ) {
01416 messagePart.isDecryptable = bOkDecrypt;
01417 messagePart.isEncrypted = true;
01418 messagePart.isSigned = false;
01419 htmlWriter()->queue( writeSigstatHeader( messagePart,
01420 cryptoProtocol(),
01421 node->trueFromAddress() ) );
01422 }
01423
01424 if ( bOkDecrypt ) {
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436 if ( signatureFound ) {
01437 writeOpaqueOrMultipartSignedData( 0,
01438 *node,
01439 node->trueFromAddress(),
01440 false,
01441 &decryptedData,
01442 signatures,
01443 false );
01444 node->setSignatureState( KMMsgFullySigned );
01445 } else {
01446 insertAndParseNewChildNode( *node,
01447 &*decryptedData,
01448 "encrypted data" );
01449 }
01450 } else {
01451 mRawReplyString += decryptedData;
01452 if ( mReader ) {
01453
01454
01455 htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01456 }
01457 }
01458
01459 if ( mReader )
01460 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01461 data->setProcessed( true, false );
01462 return true;
01463 }
01464
01465
01466 bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01467 if ( mReader
01468 && !attachmentStrategy()->inlineNestedMessages()
01469 && !showOnlyOneMimePart() )
01470 return false;
01471
01472 if ( partNode * child = node->firstChild() ) {
01473
01474 ObjectTreeParser otp( mReader, cryptoProtocol() );
01475 otp.parseObjectTree( child );
01476 mRawReplyString += otp.rawReplyString();
01477 mTextualContent += otp.textualContent();
01478 if ( !otp.textualContentCharset().isEmpty() )
01479 mTextualContentCharset = otp.textualContentCharset();
01480
01481 return true;
01482 }
01483
01484
01485 PartMetaData messagePart;
01486 if ( mReader ) {
01487 messagePart.isEncrypted = false;
01488 messagePart.isSigned = false;
01489 messagePart.isEncapsulatedRfc822Message = true;
01490 QString filename =
01491 mReader->writeMessagePartToTempFile( &node->msgPart(),
01492 node->nodeId() );
01493 htmlWriter()->queue( writeSigstatHeader( messagePart,
01494 cryptoProtocol(),
01495 node->trueFromAddress(),
01496 node ) );
01497 }
01498 QCString rfc822messageStr( node->msgPart().bodyDecoded() );
01499
01500 DwMessage* rfc822DwMessage = new DwMessage();
01501 rfc822DwMessage->FromString( rfc822messageStr );
01502 rfc822DwMessage->Parse();
01503 KMMessage rfc822message( rfc822DwMessage );
01504 node->setFromAddress( rfc822message.from() );
01505
01506 if ( mReader )
01507 htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01508
01509
01510 insertAndParseNewChildNode( *node,
01511 &*rfc822messageStr,
01512 "encapsulated message", false ,
01513 false );
01514 node->setDisplayedEmbedded( true );
01515 if ( mReader )
01516 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01517 return true;
01518 }
01519
01520
01521 bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01522 if ( partNode * child = node->firstChild() ) {
01523
01524 ObjectTreeParser otp( mReader, cryptoProtocol() );
01525 otp.parseObjectTree( child );
01526 mRawReplyString += otp.rawReplyString();
01527 mTextualContent += otp.textualContent();
01528 if ( !otp.textualContentCharset().isEmpty() )
01529 mTextualContentCharset = otp.textualContentCharset();
01530
01531 return true;
01532 }
01533
01534 const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
01535 if ( node->parentNode()
01536 && DwMime::kTypeMultipart == node->parentNode()->type()
01537 && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01538
01539 node->setEncryptionState( KMMsgFullyEncrypted );
01540 if ( keepEncryptions() ) {
01541 const QCString cstr = node->msgPart().bodyDecoded();
01542 if ( mReader )
01543 writeBodyString( cstr, node->trueFromAddress(),
01544 codecFor( node ), result, false );
01545 mRawReplyString += cstr;
01546 } else if ( mReader && !mReader->decryptMessage() ) {
01547 writeDeferredDecryptionBlock();
01548 } else {
01549
01550
01551
01552 PartMetaData messagePart;
01553 setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
01554 QCString decryptedData;
01555 bool signatureFound;
01556 std::vector<GpgME::Signature> signatures;
01557 bool passphraseError;
01558 bool actuallyEncrypted = true;
01559 bool decryptionStarted;
01560
01561 bool bOkDecrypt = okDecryptMIME( *node,
01562 decryptedData,
01563 signatureFound,
01564 signatures,
01565 true,
01566 passphraseError,
01567 actuallyEncrypted,
01568 decryptionStarted,
01569 messagePart.errorText,
01570 messagePart.auditLogError,
01571 messagePart.auditLog );
01572
01573 if ( decryptionStarted ) {
01574 writeDecryptionInProgressBlock();
01575 return true;
01576 }
01577
01578
01579 if ( mReader ) {
01580 messagePart.isDecryptable = bOkDecrypt;
01581 messagePart.isEncrypted = true;
01582 messagePart.isSigned = false;
01583 htmlWriter()->queue( writeSigstatHeader( messagePart,
01584 cryptoProtocol(),
01585 node->trueFromAddress() ) );
01586 }
01587
01588 if ( bOkDecrypt ) {
01589
01590 insertAndParseNewChildNode( *node,
01591 &*decryptedData,
01592 "encrypted data" );
01593 } else {
01594 mRawReplyString += decryptedData;
01595 if ( mReader ) {
01596
01597
01598 htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01599 }
01600 }
01601
01602 if ( mReader )
01603 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01604 }
01605 return true;
01606 }
01607 setCryptoProtocol( oldUseThisCryptPlug );
01608 return false;
01609 }
01610
01611 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01612 if ( partNode * child = node->firstChild() ) {
01613
01614 ObjectTreeParser otp( mReader, cryptoProtocol() );
01615 otp.parseObjectTree( child );
01616 mRawReplyString += otp.rawReplyString();
01617 mTextualContent += otp.textualContent();
01618 if ( !otp.textualContentCharset().isEmpty() )
01619 mTextualContentCharset = otp.textualContentCharset();
01620
01621 return true;
01622 }
01623
01624
01625 if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01626 return false;
01627
01628 const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
01629
01630 const QString smimeType = node->contentTypeParameter("smime-type").lower();
01631
01632 if ( smimeType == "certs-only" ) {
01633 result.setNeverDisplayInline( true );
01634 if ( !smimeCrypto || !mReader )
01635 return false;
01636
01637 const KConfigGroup reader( KMKernel::config(), "Reader" );
01638 if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
01639 return false;
01640
01641 const QByteArray certData = node->msgPart().bodyDecodedBinary();
01642
01643 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::ImportJob> import( smimeCrypto->importJob() );
01644 const GpgME::ImportResult res = import->exec( certData );
01645 if ( res.error() ) {
01646 htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
01647 "Reason: %1").arg( QString::fromLocal8Bit( res.error().asString() ) ) );
01648 return true;
01649 }
01650
01651 const int nImp = res.numImported();
01652 const int nUnc = res.numUnchanged();
01653 const int nSKImp = res.numSecretKeysImported();
01654 const int nSKUnc = res.numSecretKeysUnchanged();
01655 if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01656 htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
01657 return true;
01658 }
01659 QString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br> <br>";
01660 if ( nImp )
01661 comment += i18n( "1 new certificate was imported.",
01662 "%n new certificates were imported.", nImp ) + "<br>";
01663 if ( nUnc )
01664 comment += i18n( "1 certificate was unchanged.",
01665 "%n certificates were unchanged.", nUnc ) + "<br>";
01666 if ( nSKImp )
01667 comment += i18n( "1 new secret key was imported.",
01668 "%n new secret keys were imported.", nSKImp ) + "<br>";
01669 if ( nSKUnc )
01670 comment += i18n( "1 secret key was unchanged.",
01671 "%n secret keys were unchanged.", nSKUnc ) + "<br>";
01672 comment += " <br>";
01673 htmlWriter()->queue( comment );
01674 if ( !nImp && !nSKImp ) {
01675 htmlWriter()->queue( "<hr>" );
01676 return true;
01677 }
01678 const std::vector<GpgME::Import> imports = res.imports();
01679 if ( imports.empty() ) {
01680 htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
01681 return true;
01682 }
01683 htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
01684 for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01685 if ( (*it).error() )
01686 htmlWriter()->queue( i18n( "Failed: %1 (%2)" )
01687 .arg( (*it).fingerprint(),
01688 QString::fromLocal8Bit( (*it).error().asString() ) ) );
01689 else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
01690 if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
01691 htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01692 }
01693 else {
01694 htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
01695 }
01696 }
01697 htmlWriter()->queue( "<br>" );
01698 }
01699
01700 htmlWriter()->queue( "<hr>" );
01701 return true;
01702 }
01703
01704 if ( !smimeCrypto )
01705 return false;
01706 CryptoProtocolSaver cpws( this, smimeCrypto );
01707
01708 bool isSigned = smimeType == "signed-data";
01709 bool isEncrypted = smimeType == "enveloped-data";
01710
01711
01712
01713
01714 partNode* signTestNode = isEncrypted ? 0 : node;
01715
01716
01717
01718
01719
01720 if ( !isSigned ) {
01721 if ( isEncrypted ) {
01722
01723 }
01724 else {
01725
01726 }
01727 QCString decryptedData;
01728 PartMetaData messagePart;
01729 messagePart.isEncrypted = true;
01730 messagePart.isSigned = false;
01731 bool signatureFound;
01732 std::vector<GpgME::Signature> signatures;
01733 bool passphraseError;
01734 bool actuallyEncrypted = true;
01735 bool decryptionStarted;
01736
01737 if ( mReader && !mReader->decryptMessage() ) {
01738 writeDeferredDecryptionBlock();
01739 isEncrypted = true;
01740 signTestNode = 0;
01741 } else {
01742 const bool bOkDecrypt = okDecryptMIME( *node,
01743 decryptedData,
01744 signatureFound,
01745 signatures,
01746 false,
01747 passphraseError,
01748 actuallyEncrypted,
01749 decryptionStarted,
01750 messagePart.errorText,
01751 messagePart.auditLogError,
01752 messagePart.auditLog );
01753 if ( decryptionStarted ) {
01754 writeDecryptionInProgressBlock();
01755 return true;
01756 }
01757 if ( bOkDecrypt ) {
01758
01759 isEncrypted = true;
01760 node->setEncryptionState( KMMsgFullyEncrypted );
01761 signTestNode = 0;
01762
01763 messagePart.isDecryptable = true;
01764 if ( mReader )
01765 htmlWriter()->queue( writeSigstatHeader( messagePart,
01766 cryptoProtocol(),
01767 node->trueFromAddress() ) );
01768 insertAndParseNewChildNode( *node,
01769 &*decryptedData,
01770 "encrypted data" );
01771 if ( mReader )
01772 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01773 } else {
01774
01775
01776
01777
01778 if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
01779 isEncrypted = true;
01780 signTestNode = 0;
01781 }
01782
01783 if ( isEncrypted ) {
01784
01785
01786 messagePart.isDecryptable = false;
01787 if ( mReader ) {
01788 htmlWriter()->queue( writeSigstatHeader( messagePart,
01789 cryptoProtocol(),
01790 node->trueFromAddress() ) );
01791 assert( mReader->decryptMessage() );
01792 writePartIcon( &node->msgPart(), node->nodeId() );
01793 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01794 }
01795 } else {
01796
01797 }
01798 }
01799 }
01800 if ( isEncrypted )
01801 node->setEncryptionState( KMMsgFullyEncrypted );
01802 }
01803
01804
01805 if ( signTestNode ) {
01806 if ( isSigned ) {
01807
01808 }
01809 else {
01810
01811 }
01812
01813 bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01814 *signTestNode,
01815 node->trueFromAddress(),
01816 true,
01817 0,
01818 std::vector<GpgME::Signature>(),
01819 isEncrypted );
01820 if ( sigFound ) {
01821 if ( !isSigned ) {
01822
01823 isSigned = true;
01824 }
01825 signTestNode->setSignatureState( KMMsgFullySigned );
01826 if ( signTestNode != node )
01827 node->setSignatureState( KMMsgFullySigned );
01828 } else {
01829
01830 }
01831 }
01832
01833 return isSigned || isEncrypted;
01834 }
01835
01836 bool ObjectTreeParser::decryptChiasmus( const QByteArray& data, QByteArray& bodyDecoded, QString& errorText )
01837 {
01838 const Kleo::CryptoBackend::Protocol * chiasmus =
01839 Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
01840 Q_ASSERT( chiasmus );
01841 if ( !chiasmus )
01842 return false;
01843
01844 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
01845 if ( !listjob.get() ) {
01846 errorText = i18n( "Chiasmus backend does not offer the "
01847 "\"x-obtain-keys\" function. Please report this bug." );
01848 return false;
01849 }
01850
01851 if ( listjob->exec() ) {
01852 errorText = i18n( "Chiasmus Backend Error" );
01853 return false;
01854 }
01855
01856 const QVariant result = listjob->property( "result" );
01857 if ( result.type() != QVariant::StringList ) {
01858 errorText = i18n( "Unexpected return value from Chiasmus backend: "
01859 "The \"x-obtain-keys\" function did not return a "
01860 "string list. Please report this bug." );
01861 return false;
01862 }
01863
01864 const QStringList keys = result.toStringList();
01865 if ( keys.empty() ) {
01866 errorText = i18n( "No keys have been found. Please check that a "
01867 "valid key path has been set in the Chiasmus "
01868 "configuration." );
01869 return false;
01870 }
01871
01872 emit mReader->noDrag();
01873 ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
01874 keys, GlobalSettings::chiasmusDecryptionKey(),
01875 GlobalSettings::chiasmusDecryptionOptions() );
01876 if ( selectorDlg.exec() != QDialog::Accepted )
01877 return false;
01878
01879 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
01880 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
01881 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
01882
01883 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() ) );
01884 if ( !job.get() ) {
01885 errorText = i18n( "Chiasmus backend does not offer the "
01886 "\"x-decrypt\" function. Please report this bug." );
01887 return false;
01888 }
01889
01890 if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
01891 !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
01892 !job->setProperty( "input", data ) ) {
01893 errorText = i18n( "The \"x-decrypt\" function does not accept "
01894 "the expected parameters. Please report this bug." );
01895 return false;
01896 }
01897
01898 if ( job->exec() ) {
01899 errorText = i18n( "Chiasmus Decryption Error" );
01900 return false;
01901 }
01902
01903 const QVariant resultData = job->property( "result" );
01904 if ( resultData.type() != QVariant::ByteArray ) {
01905 errorText = i18n( "Unexpected return value from Chiasmus backend: "
01906 "The \"x-decrypt\" function did not return a "
01907 "byte array. Please report this bug." );
01908 return false;
01909 }
01910 bodyDecoded = resultData.toByteArray();
01911 return true;
01912 }
01913
01914 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
01915 {
01916 if ( !mReader ) {
01917 mRawReplyString = curNode->msgPart().bodyDecoded();
01918 mTextualContent += curNode->msgPart().bodyToUnicode();
01919 mTextualContentCharset = curNode->msgPart().charset();
01920 return true;
01921 }
01922
01923 QByteArray decryptedBody;
01924 QString errorText;
01925 const QByteArray data = curNode->msgPart().bodyDecodedBinary();
01926 bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
01927 PartMetaData messagePart;
01928 messagePart.isDecryptable = bOkDecrypt;
01929 messagePart.isEncrypted = true;
01930 messagePart.isSigned = false;
01931 messagePart.errorText = errorText;
01932 if ( mReader )
01933 htmlWriter()->queue( writeSigstatHeader( messagePart,
01934 0,
01935 curNode->trueFromAddress() ) );
01936 const QByteArray body = bOkDecrypt ? decryptedBody : data;
01937 const QString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
01938 const QTextCodec* aCodec = chiasmusCharset.isEmpty()
01939 ? codecFor( curNode )
01940 : KMMsgBase::codecForName( chiasmusCharset.ascii() );
01941 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false ) );
01942 result.setInlineEncryptionState( KMMsgFullyEncrypted );
01943 if ( mReader )
01944 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01945 return true;
01946 }
01947
01948 bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
01949 {
01950 Q_UNUSED( result );
01951 if ( !mReader )
01952 return false;
01953
01954 const QString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
01955 KTNEFParser parser;
01956 if ( !parser.openFile( fileName ) || !parser.message()) {
01957 kdDebug() << k_funcinfo << "Could not parse " << fileName << endl;
01958 return false;
01959 }
01960
01961 QPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
01962 if ( tnefatts.isEmpty() ) {
01963 kdDebug() << k_funcinfo << "No attachments found in " << fileName << endl;
01964 return false;
01965 }
01966
01967 if ( !showOnlyOneMimePart() ) {
01968 QString label = node->msgPart().fileName().stripWhiteSpace();
01969 if ( label.isEmpty() )
01970 label = node->msgPart().name().stripWhiteSpace();
01971 label = KMMessage::quoteHtmlChars( label, true );
01972 const QString comment = KMMessage::quoteHtmlChars( node->msgPart().contentDescription(), true );
01973 const QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
01974
01975 QString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
01976 "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
01977 if ( !fileName.isEmpty() )
01978 htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
01979 + label + "</a>";
01980 else
01981 htmlStr += label;
01982 if ( !comment.isEmpty() )
01983 htmlStr += "<br>" + comment;
01984 htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
01985 htmlWriter()->queue( htmlStr );
01986 }
01987
01988 for ( uint i = 0; i < tnefatts.count(); ++i ) {
01989 KTNEFAttach *att = tnefatts.at( i );
01990 QString label = att->displayName();
01991 if( label.isEmpty() )
01992 label = att->name();
01993 label = KMMessage::quoteHtmlChars( label, true );
01994
01995 QString dir = mReader->createTempDir( "ktnef-" + QString::number( i ) );
01996 parser.extractFileTo( att->name(), dir );
01997 mReader->mTempFiles.append( dir + QDir::separator() + att->name() );
01998 QString href = "file:" + KURL::encode_string( dir + QDir::separator() + att->name() );
01999
02000 KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
02001 QString iconName = KGlobal::instance()->iconLoader()->iconPath( mimeType->icon( QString(), false ), KIcon::Desktop );
02002
02003 htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
02004 iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
02005 "</a></div><br>" );
02006 }
02007
02008 if ( !showOnlyOneMimePart() )
02009 htmlWriter()->queue( "</td></tr></table>" );
02010
02011 return true;
02012 }
02013
02014 void ObjectTreeParser::writeBodyString( const QCString & bodyString,
02015 const QString & fromAddress,
02016 const QTextCodec * codec,
02017 ProcessResult & result,
02018 bool decorate ) {
02019 assert( mReader ); assert( codec );
02020 KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
02021 KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
02022 writeBodyStr( bodyString, codec, fromAddress,
02023 inlineSignatureState, inlineEncryptionState, decorate );
02024 result.setInlineSignatureState( inlineSignatureState );
02025 result.setInlineEncryptionState( inlineEncryptionState );
02026 }
02027
02028 void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
02029 if ( !mReader || !msgPart )
02030 return;
02031
02032 QString label = msgPart->fileName();
02033 if( label.isEmpty() )
02034 label = msgPart->name();
02035 if( label.isEmpty() )
02036 label = "unnamed";
02037 label = KMMessage::quoteHtmlChars( label, true );
02038
02039 QString comment = msgPart->contentDescription();
02040 comment = KMMessage::quoteHtmlChars( comment, true );
02041 if ( label == comment ) comment = QString::null;
02042
02043 QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
02044
02045 QString href = QString( "attachment:%1?place=body" ).arg( partNum );
02046
02047 QString iconName;
02048 if( inlineImage )
02049 iconName = href;
02050 else {
02051 iconName = msgPart->iconName();
02052 if( iconName.right( 14 ) == "mime_empty.png" ) {
02053 msgPart->magicSetType();
02054 iconName = msgPart->iconName();
02055 }
02056 }
02057
02058 QCString contentId = msgPart->contentId();
02059 if ( !contentId.isEmpty() ) {
02060 htmlWriter()->embedPart( contentId, href );
02061 }
02062
02063 if( inlineImage )
02064
02065 htmlWriter()->queue( "<div><a href=\"" + href + "\">"
02066 "<img src=\"" + fileName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
02067 "</div>"
02068 "<div><a href=\"" + href + "\">" + label + "</a>"
02069 "</div>"
02070 "<div>" + comment + "</div><br>" );
02071 else
02072
02073 htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
02074 iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
02075 "</a></div>"
02076 "<div>" + comment + "</div><br>" );
02077 }
02078
02079 #define SIG_FRAME_COL_UNDEF 99
02080 #define SIG_FRAME_COL_RED -1
02081 #define SIG_FRAME_COL_YELLOW 0
02082 #define SIG_FRAME_COL_GREEN 1
02083 QString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
02084 int status_code,
02085 GpgME::Signature::Summary summary,
02086 int& frameColor,
02087 bool& showKeyInfos )
02088 {
02089
02090
02091
02092 showKeyInfos = true;
02093 QString result;
02094 if( cryptProto ) {
02095 if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
02096
02097
02098 switch( status_code ) {
02099 case 0:
02100 result = i18n("Error: Signature not verified");
02101 break;
02102 case 1:
02103 result = i18n("Good signature");
02104 break;
02105 case 2:
02106 result = i18n("<b>Bad</b> signature");
02107 break;
02108 case 3:
02109 result = i18n("No public key to verify the signature");
02110 break;
02111 case 4:
02112 result = i18n("No signature found");
02113 break;
02114 case 5:
02115 result = i18n("Error verifying the signature");
02116 break;
02117 case 6:
02118 result = i18n("Different results for signatures");
02119 break;
02120
02121
02122
02123
02124
02125
02126
02127
02128 default:
02129 result = "";
02130 break;
02131 }
02132 }
02133 else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
02134
02135
02136
02137 if( summary == GpgME::Signature::None ) {
02138 result = i18n("No status information available.");
02139 frameColor = SIG_FRAME_COL_YELLOW;
02140 showKeyInfos = false;
02141 return result;
02142 }
02143
02144 if( summary & GpgME::Signature::Valid ) {
02145 result = i18n("Good signature.");
02146
02147
02148
02149
02150
02151
02152
02153
02154 frameColor = SIG_FRAME_COL_GREEN;
02155 showKeyInfos = false;
02156 return result;
02157 }
02158
02159
02160
02161
02162 frameColor = SIG_FRAME_COL_GREEN;
02163 QString result2;
02164 if( summary & GpgME::Signature::KeyExpired ){
02165
02166 result2 += i18n("One key has expired.");
02167 }
02168 if( summary & GpgME::Signature::SigExpired ){
02169
02170 result2 += i18n("The signature has expired.");
02171 }
02172
02173
02174 if( summary & GpgME::Signature::KeyMissing ) {
02175 result2 += i18n("Unable to verify: key missing.");
02176
02177
02178 showKeyInfos = false;
02179 frameColor = SIG_FRAME_COL_YELLOW;
02180 }
02181 if( summary & GpgME::Signature::CrlMissing ){
02182 result2 += i18n("CRL not available.");
02183 frameColor = SIG_FRAME_COL_YELLOW;
02184 }
02185 if( summary & GpgME::Signature::CrlTooOld ){
02186 result2 += i18n("Available CRL is too old.");
02187 frameColor = SIG_FRAME_COL_YELLOW;
02188 }
02189 if( summary & GpgME::Signature::BadPolicy ){
02190 result2 += i18n("A policy was not met.");
02191 frameColor = SIG_FRAME_COL_YELLOW;
02192 }
02193 if( summary & GpgME::Signature::SysError ){
02194 result2 += i18n("A system error occurred.");
02195
02196
02197
02198 showKeyInfos = false;
02199 frameColor = SIG_FRAME_COL_YELLOW;
02200 }
02201
02202
02203 if( summary & GpgME::Signature::KeyRevoked ){
02204
02205 result2 += i18n("One key has been revoked.");
02206 frameColor = SIG_FRAME_COL_RED;
02207 }
02208 if( summary & GpgME::Signature::Red ) {
02209 if( result2.isEmpty() )
02210
02211
02212
02213
02214
02215
02216
02217
02218
02219
02220
02221
02222 showKeyInfos = false;
02223 frameColor = SIG_FRAME_COL_RED;
02224 }
02225 else
02226 result = "";
02227
02228 if( SIG_FRAME_COL_GREEN == frameColor ) {
02229 result = i18n("Good signature.");
02230 } else if( SIG_FRAME_COL_RED == frameColor ) {
02231 result = i18n("<b>Bad</b> signature.");
02232 } else
02233 result = "";
02234
02235 if( !result2.isEmpty() ) {
02236 if( !result.isEmpty() )
02237 result.append("<br />");
02238 result.append( result2 );
02239 }
02240 }
02241
02242
02243
02244
02245
02246
02247 }
02248 return result;
02249 }
02250
02251
02252 static QString writeSimpleSigstatHeader( const PartMetaData &block )
02253 {
02254 QString html;
02255 html += "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
02256
02257 if ( block.signClass == "signErr" ) {
02258 html += i18n( "Invalid signature." );
02259 } else if ( block.signClass == "signOkKeyBad" || block.signClass == "signWarn" ) {
02260 html += i18n( "Not enough information to check signature validity." );
02261 } else if ( block.signClass == "signOkKeyOk" ) {
02262 QString addr;
02263 if ( !block.signerMailAddresses.isEmpty() )
02264 addr = block.signerMailAddresses.first();
02265 QString name = addr;
02266 if ( name.isEmpty() )
02267 name = block.signer;
02268 if ( addr.isEmpty() ) {
02269 html += i18n( "Signature is valid." );
02270 } else {
02271 html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
02272 }
02273 } else {
02274
02275 html += i18n( "Unknown signature state" );
02276 }
02277 html += "</td><td align=\"right\">";
02278 html += "<a href=\"kmail:showSignatureDetails\">";
02279 html += i18n( "Show Details" );
02280 html += "</a></td></tr></table>";
02281 return html;
02282 }
02283
02284 static QString beginVerboseSigstatHeader()
02285 {
02286 return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
02287 }
02288
02289 static QString makeShowAuditLogLink( const GpgME::Error & err, const QString & auditLog ) {
02290 if ( const unsigned int code = err.code() ) {
02291 if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
02292
02293 return QString();
02294 } else if ( code == GPG_ERR_NO_DATA ) {
02295
02296 return i18n("No Audit Log available");
02297 } else {
02298 return i18n("Error Retrieving Audit Log: %1").arg( QString::fromLocal8Bit( err.asString() ) );
02299 }
02300 }
02301
02302 if ( !auditLog.isEmpty() ) {
02303 KURL url;
02304 url.setProtocol( "kmail" );
02305 url.setPath( "showAuditLog" );
02306 url.addQueryItem( "log", auditLog );
02307
02308 return "<a href=\"" + url.htmlURL() + "\">" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + "</a>";
02309 }
02310
02311 return QString::null;
02312 }
02313
02314 static QString endVerboseSigstatHeader( const PartMetaData & pmd )
02315 {
02316 QString html;
02317 html += "</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
02318 html += "<a href=\"kmail:hideSignatureDetails\">";
02319 html += i18n( "Hide Details" );
02320 html += "</a></td></tr>";
02321 html += "<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
02322 html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
02323 html += "</td></tr></table>";
02324 return html;
02325 }
02326
02327 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
02328 const Kleo::CryptoBackend::Protocol * cryptProto,
02329 const QString & fromAddress,
02330 partNode *node )
02331 {
02332 const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
02333 QString signer = block.signer;
02334
02335 QString htmlStr, simpleHtmlStr;
02336 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02337 QString cellPadding("cellpadding=\"1\"");
02338
02339 if( block.isEncapsulatedRfc822Message )
02340 {
02341 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
02342 "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
02343 if ( node )
02344 htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
02345 + i18n("Encapsulated message") + "</a>";
02346 else
02347 htmlStr += i18n("Encapsulated message");
02348 htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
02349 }
02350
02351 if( block.isEncrypted )
02352 {
02353 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
02354 "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
02355 if ( block.inProgress )
02356 htmlStr += i18n("Please wait while the message is being decrypted...");
02357 else if ( block.isDecryptable )
02358 htmlStr += i18n("Encrypted message");
02359 else {
02360 htmlStr += i18n("Encrypted message (decryption not possible)");
02361 if( !block.errorText.isEmpty() )
02362 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
02363 }
02364 htmlStr += "</td></tr><tr class=\"encrB\"><td>";
02365 }
02366
02367 if ( block.isSigned && block.inProgress )
02368 {
02369 block.signClass = "signInProgress";
02370 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"signInProgress\">"
02371 "<tr class=\"signInProgressH\"><td dir=\"" + dir + "\">";
02372 htmlStr += i18n("Please wait while the signature is being verified...");
02373 htmlStr += "</td></tr><tr class=\"signInProgressB\"><td>";
02374 }
02375 simpleHtmlStr = htmlStr;
02376
02377 if ( block.isSigned && !block.inProgress ) {
02378 QStringList& blockAddrs( block.signerMailAddresses );
02379
02380
02381
02382 int frameColor = SIG_FRAME_COL_UNDEF;
02383 bool showKeyInfos;
02384 bool onlyShowKeyURL = false;
02385 bool cannotCheckSignature = true;
02386 QString statusStr = sigStatusToString( cryptProto,
02387 block.status_code,
02388 block.sigSummary,
02389 frameColor,
02390 showKeyInfos );
02391
02392
02393 if( statusStr.isEmpty() )
02394 statusStr = block.status;
02395 if( block.technicalProblem )
02396 frameColor = SIG_FRAME_COL_YELLOW;
02397
02398 switch( frameColor ){
02399 case SIG_FRAME_COL_RED:
02400 cannotCheckSignature = false;
02401 break;
02402 case SIG_FRAME_COL_YELLOW:
02403 cannotCheckSignature = true;
02404 break;
02405 case SIG_FRAME_COL_GREEN:
02406 cannotCheckSignature = false;
02407 break;
02408 }
02409
02410
02411
02412
02413
02414
02415 QString startKeyHREF;
02416 if( isSMIME )
02417 startKeyHREF =
02418 QString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02419 .arg( cryptProto->displayName(),
02420 cryptProto->name(),
02421 block.keyId );
02422 QString keyWithWithoutURL
02423 = isSMIME
02424 ? QString("%1%2</a>")
02425 .arg( startKeyHREF,
02426 cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02427 : "0x" + QString::fromUtf8( block.keyId );
02428
02429
02430
02431 showKeyInfos = true;
02432
02433
02434
02435
02436 if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02437
02438
02439
02440 if( !statusStr.isEmpty() ) {
02441 statusStr.prepend("<i>");
02442 statusStr.append( "</i>");
02443 }
02444
02445
02446 switch( frameColor ) {
02447 case SIG_FRAME_COL_RED:
02448 block.signClass = "signErr";
02449 onlyShowKeyURL = true;
02450 break;
02451 case SIG_FRAME_COL_YELLOW:
02452 if( block.technicalProblem )
02453 block.signClass = "signWarn";
02454 else
02455 block.signClass = "signOkKeyBad";
02456 break;
02457 case SIG_FRAME_COL_GREEN:
02458 block.signClass = "signOkKeyOk";
02459
02460
02461 QString greenCaseWarning;
02462 QString msgFrom( KPIM::getEmailAddress(fromAddress) );
02463 QString certificate;
02464 if( block.keyId.isEmpty() )
02465 certificate = i18n("certificate");
02466 else
02467 certificate = startKeyHREF + i18n("certificate") + "</a>";
02468 if( !blockAddrs.empty() ){
02469 if( blockAddrs.grep(
02470 msgFrom,
02471 false ).isEmpty() ) {
02472 greenCaseWarning =
02473 "<u>" +
02474 i18n("Warning:") +
02475 "</u> " +
02476 i18n("Sender's mail address is not stored "
02477 "in the %1 used for signing.").arg(certificate) +
02478 "<br />" +
02479 i18n("sender: ") +
02480 msgFrom +
02481 "<br />" +
02482 i18n("stored: ");
02483
02484
02485
02486
02487 bool bStart = true;
02488 for(QStringList::ConstIterator it = blockAddrs.begin();
02489 it != blockAddrs.end(); ++it ){
02490 if( !bStart )
02491 greenCaseWarning.append(", <br /> ");
02492 bStart = false;
02493 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02494 }
02495 }
02496 } else {
02497 greenCaseWarning =
02498 "<u>" +
02499 i18n("Warning:") +
02500 "</u> " +
02501 i18n("No mail address is stored in the %1 used for signing, "
02502 "so we cannot compare it to the sender's address %2.")
02503 .arg(certificate,msgFrom);
02504 }
02505 if( !greenCaseWarning.isEmpty() ) {
02506 if( !statusStr.isEmpty() )
02507 statusStr.append("<br /> <br />");
02508 statusStr.append( greenCaseWarning );
02509 }
02510 break;
02511 }
02512
02513 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02514 "class=\"" + block.signClass + "\">"
02515 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02516 htmlStr += frame + beginVerboseSigstatHeader();
02517 simpleHtmlStr += frame;
02518 simpleHtmlStr += writeSimpleSigstatHeader( block );
02519 if( block.technicalProblem ) {
02520 htmlStr += block.errorText;
02521 }
02522 else if( showKeyInfos ) {
02523 if( cannotCheckSignature ) {
02524 htmlStr += i18n( "Not enough information to check "
02525 "signature. %1" )
02526 .arg( keyWithWithoutURL );
02527 }
02528 else {
02529
02530 if (block.signer.isEmpty())
02531 signer = "";
02532 else {
02533 if( !blockAddrs.empty() ){
02534 QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02535 signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02536 }
02537 }
02538
02539 if( block.keyId.isEmpty() ) {
02540 if( signer.isEmpty() || onlyShowKeyURL )
02541 htmlStr += i18n( "Message was signed with unknown key." );
02542 else
02543 htmlStr += i18n( "Message was signed by %1." )
02544 .arg( signer );
02545 } else {
02546 QDateTime created = block.creationTime;
02547 if( created.isValid() ) {
02548 if( signer.isEmpty() ) {
02549 if( onlyShowKeyURL )
02550 htmlStr += i18n( "Message was signed with key %1." )
02551 .arg( keyWithWithoutURL );
02552 else
02553 htmlStr += i18n( "Message was signed on %1 with key %2." )
02554 .arg( KGlobal::locale()->formatDateTime( created ),
02555 keyWithWithoutURL );
02556 }
02557 else {
02558 if( onlyShowKeyURL )
02559 htmlStr += i18n( "Message was signed with key %1." )
02560 .arg( keyWithWithoutURL );
02561 else
02562 htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02563 .arg( KGlobal::locale()->formatDateTime( created ),
02564 keyWithWithoutURL,
02565 signer );
02566 }
02567 }
02568 else {
02569 if( signer.isEmpty() || onlyShowKeyURL )
02570 htmlStr += i18n( "Message was signed with key %1." )
02571 .arg( keyWithWithoutURL );
02572 else
02573 htmlStr += i18n( "Message was signed by %2 with key %1." )
02574 .arg( keyWithWithoutURL,
02575 signer );
02576 }
02577 }
02578 }
02579 htmlStr += "<br />";
02580 if( !statusStr.isEmpty() ) {
02581 htmlStr += " <br />";
02582 htmlStr += i18n( "Status: " );
02583 htmlStr += statusStr;
02584 }
02585 } else {
02586 htmlStr += statusStr;
02587 }
02588 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02589 htmlStr += endVerboseSigstatHeader( block ) + frame;
02590 simpleHtmlStr += frame;
02591
02592 } else {
02593
02594
02595
02596 if( block.signer.isEmpty() || block.technicalProblem ) {
02597 block.signClass = "signWarn";
02598 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02599 "class=\"" + block.signClass + "\">"
02600 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02601 htmlStr += frame + beginVerboseSigstatHeader();
02602 simpleHtmlStr += frame;
02603 simpleHtmlStr += writeSimpleSigstatHeader( block );
02604 if( block.technicalProblem ) {
02605 htmlStr += block.errorText;
02606 }
02607 else {
02608 if( !block.keyId.isEmpty() ) {
02609 QDateTime created = block.creationTime;
02610 if ( created.isValid() )
02611 htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02612 .arg( KGlobal::locale()->formatDateTime( created ),
02613 keyWithWithoutURL );
02614 else
02615 htmlStr += i18n( "Message was signed with unknown key %1." )
02616 .arg( keyWithWithoutURL );
02617 }
02618 else
02619 htmlStr += i18n( "Message was signed with unknown key." );
02620 htmlStr += "<br />";
02621 htmlStr += i18n( "The validity of the signature cannot be "
02622 "verified." );
02623 if( !statusStr.isEmpty() ) {
02624 htmlStr += "<br />";
02625 htmlStr += i18n( "Status: " );
02626 htmlStr += "<i>";
02627 htmlStr += statusStr;
02628 htmlStr += "</i>";
02629 }
02630 }
02631 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02632 htmlStr += endVerboseSigstatHeader( block ) + frame;
02633 simpleHtmlStr += frame;
02634 }
02635 else
02636 {
02637
02638 signer = KMMessage::quoteHtmlChars( signer, true );
02639 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02640
02641 if (block.isGoodSignature) {
02642 if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02643 block.signClass = "signOkKeyBad";
02644 else
02645 block.signClass = "signOkKeyOk";
02646 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02647 "class=\"" + block.signClass + "\">"
02648 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02649 htmlStr += frame + beginVerboseSigstatHeader();
02650 simpleHtmlStr += frame;
02651 simpleHtmlStr += writeSimpleSigstatHeader( block );
02652 if( !block.keyId.isEmpty() )
02653 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02654 .arg( keyWithWithoutURL,
02655 signer );
02656 else
02657 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02658 htmlStr += "<br />";
02659
02660 switch( block.keyTrust )
02661 {
02662 case Kpgp::KPGP_VALIDITY_UNKNOWN:
02663 htmlStr += i18n( "The signature is valid, but the key's "
02664 "validity is unknown." );
02665 break;
02666 case Kpgp::KPGP_VALIDITY_MARGINAL:
02667 htmlStr += i18n( "The signature is valid and the key is "
02668 "marginally trusted." );
02669 break;
02670 case Kpgp::KPGP_VALIDITY_FULL:
02671 htmlStr += i18n( "The signature is valid and the key is "
02672 "fully trusted." );
02673 break;
02674 case Kpgp::KPGP_VALIDITY_ULTIMATE:
02675 htmlStr += i18n( "The signature is valid and the key is "
02676 "ultimately trusted." );
02677 break;
02678 default:
02679 htmlStr += i18n( "The signature is valid, but the key is "
02680 "untrusted." );
02681 }
02682 frame = "</td></tr>"
02683 "<tr class=\"" + block.signClass + "B\"><td>";
02684 htmlStr += endVerboseSigstatHeader( block ) + frame;
02685 simpleHtmlStr += frame;
02686 }
02687 else
02688 {
02689 block.signClass = "signErr";
02690 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02691 "class=\"" + block.signClass + "\">"
02692 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02693 htmlStr += frame + beginVerboseSigstatHeader();
02694 simpleHtmlStr += frame;
02695 simpleHtmlStr += writeSimpleSigstatHeader( block );
02696 if( !block.keyId.isEmpty() )
02697 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02698 .arg( keyWithWithoutURL,
02699 signer );
02700 else
02701 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02702 htmlStr += "<br />";
02703 htmlStr += i18n("Warning: The signature is bad.");
02704 frame = "</td></tr>"
02705 "<tr class=\"" + block.signClass + "B\"><td>";
02706 htmlStr += endVerboseSigstatHeader( block ) + frame;
02707 simpleHtmlStr += frame;
02708 }
02709 }
02710 }
02711 }
02712
02713 if ( mReader->showSignatureDetails() )
02714 return htmlStr;
02715 return simpleHtmlStr;
02716 }
02717
02718 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02719 {
02720 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02721
02722 QString htmlStr;
02723
02724 if (block.isSigned) {
02725 htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02726 htmlStr += "<td dir=\"" + dir + "\">" +
02727 i18n( "End of signed message" ) +
02728 "</td></tr></table>";
02729 }
02730
02731 if (block.isEncrypted) {
02732 htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02733 i18n( "End of encrypted message" ) +
02734 "</td></tr></table>";
02735 }
02736
02737 if( block.isEncapsulatedRfc822Message )
02738 {
02739 htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02740 i18n( "End of encapsulated message" ) +
02741 "</td></tr></table>";
02742 }
02743
02744 return htmlStr;
02745 }
02746
02747
02748
02749 void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
02750 {
02751 if ( !mReader )
02752 return;
02753
02754 htmlWriter()->queue( QString( "<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
02755 }
02756
02757
02758
02759 void ObjectTreeParser::writeAttachmentMarkFooter()
02760 {
02761 if ( !mReader )
02762 return;
02763
02764 htmlWriter()->queue( QString( "</div>" ) );
02765 }
02766
02767
02768 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02769 const QString& fromAddress )
02770 {
02771 KMMsgSignatureState dummy1;
02772 KMMsgEncryptionState dummy2;
02773 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02774 }
02775
02776
02777 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02778 const QString& fromAddress,
02779 KMMsgSignatureState& inlineSignatureState,
02780 KMMsgEncryptionState& inlineEncryptionState,
02781 bool decorate )
02782 {
02783 bool goodSignature = false;
02784 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02785 assert(pgp != 0);
02786 bool isPgpMessage = false;
02787
02788 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02789 QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02790
02791 inlineSignatureState = KMMsgNotSigned;
02792 inlineEncryptionState = KMMsgNotEncrypted;
02793 QPtrList<Kpgp::Block> pgpBlocks;
02794 QStrList nonPgpBlocks;
02795 if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02796 {
02797 bool isEncrypted = false, isSigned = false;
02798 bool fullySignedOrEncrypted = true;
02799 bool firstNonPgpBlock = true;
02800 bool couldDecrypt = false;
02801 QString signer;
02802 QCString keyId;
02803 QString decryptionError;
02804 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02805
02806 QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02807
02808 QStrListIterator npbit( nonPgpBlocks );
02809
02810 QString htmlStr;
02811 for( ; *pbit != 0; ++pbit, ++npbit )
02812 {
02813
02814 QCString str( *npbit );
02815 if( !str.isEmpty() ) {
02816 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02817
02818
02819
02820
02821 if( firstNonPgpBlock ) {
02822
02823 for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02824 if( *c != '\n' ) {
02825 fullySignedOrEncrypted = false;
02826 break;
02827 }
02828 }
02829 }
02830 else {
02831 fullySignedOrEncrypted = false;
02832 }
02833 }
02834 firstNonPgpBlock = false;
02835
02836
02837
02838 Kpgp::Block* block = *pbit;
02839 if( ( block->type() == Kpgp::PgpMessageBlock &&
02840
02841 !kmkernel->contextMenuShown() ) ||
02842 ( block->type() == Kpgp::ClearsignedBlock ) )
02843 {
02844 isPgpMessage = true;
02845 if( block->type() == Kpgp::PgpMessageBlock )
02846 {
02847 if ( mReader )
02848 emit mReader->noDrag();
02849
02850 couldDecrypt = block->decrypt();
02851 isEncrypted = block->isEncrypted();
02852 if (!couldDecrypt) {
02853 decryptionError = pgp->lastErrorMsg();
02854 }
02855 }
02856 else
02857 {
02858
02859 block->verify();
02860 }
02861
02862 isSigned = block->isSigned();
02863 if( isSigned )
02864 {
02865 keyId = block->signatureKeyId();
02866 signer = block->signatureUserId();
02867 if( !signer.isEmpty() )
02868 {
02869 goodSignature = block->goodSignature();
02870
02871 if( !keyId.isEmpty() ) {
02872 keyTrust = pgp->keyTrust( keyId );
02873 Kpgp::Key* key = pgp->publicKey( keyId );
02874 if ( key ) {
02875
02876
02877 signer = key->primaryUserID();
02878 }
02879 }
02880 else
02881
02882
02883 keyTrust = pgp->keyTrust( signer );
02884 }
02885 }
02886
02887 if( isSigned )
02888 inlineSignatureState = KMMsgPartiallySigned;
02889 if( isEncrypted )
02890 inlineEncryptionState = KMMsgPartiallyEncrypted;
02891
02892 PartMetaData messagePart;
02893
02894 messagePart.isSigned = isSigned;
02895 messagePart.technicalProblem = false;
02896 messagePart.isGoodSignature = goodSignature;
02897 messagePart.isEncrypted = isEncrypted;
02898 messagePart.isDecryptable = couldDecrypt;
02899 messagePart.decryptionError = decryptionError;
02900 messagePart.signer = signer;
02901 messagePart.keyId = keyId;
02902 messagePart.keyTrust = keyTrust;
02903
02904 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02905
02906 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02907 htmlStr += writeSigstatFooter( messagePart );
02908 }
02909 else
02910 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02911 decorate );
02912 }
02913
02914
02915 QCString str( nonPgpBlocks.last() );
02916 if( !str.isEmpty() ) {
02917 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02918
02919
02920
02921
02922
02923 }
02924 if( fullySignedOrEncrypted ) {
02925 if( inlineSignatureState == KMMsgPartiallySigned )
02926 inlineSignatureState = KMMsgFullySigned;
02927 if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02928 inlineEncryptionState = KMMsgFullyEncrypted;
02929 }
02930 htmlWriter()->queue( htmlStr );
02931 }
02932 else
02933 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02934 }
02935
02936
02937 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
02938 {
02939 assert( mReader );
02940 assert( cssHelper() );
02941
02942 int convertFlags = LinkLocator::PreserveSpaces;
02943 if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02944 convertFlags |= LinkLocator::ReplaceSmileys;
02945 }
02946 QString htmlStr;
02947 const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02948 QString quoteFontTag[3];
02949 QString deepQuoteFontTag[3];
02950 for ( int i = 0 ; i < 3 ; ++i ) {
02951 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02952 deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02953 }
02954 const QString normalEndTag = "</div>";
02955 const QString quoteEnd = "</div>";
02956
02957 unsigned int pos, beg;
02958 const unsigned int length = s.length();
02959
02960
02961 for ( pos = 0; pos < length && s[pos] <= ' '; pos++ ) { ; }
02962 while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02963 beg = pos;
02964
02965 int currQuoteLevel = -2;
02966 bool curHidden = false;
02967
02968 while (beg<length)
02969 {
02970 QString line;
02971
02972
02973 pos = s.find('\n', beg, FALSE);
02974 if (pos == (unsigned int)(-1))
02975 pos = length;
02976
02977 line = s.mid(beg,pos-beg);
02978 beg = pos+1;
02979
02980
02981 int actQuoteLevel = -1;
02982
02983 if ( GlobalSettings::self()->showExpandQuotesMark() )
02984 {
02985
02986 if ( mCollapseIcon.isEmpty() ) {
02987 mCollapseIcon= LinkLocator::pngToDataUrl(
02988 KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
02989 }
02990 if ( mExpandIcon.isEmpty() )
02991 mExpandIcon= LinkLocator::pngToDataUrl(
02992 KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
02993 }
02994
02995 for (unsigned int p=0; p<line.length(); p++) {
02996 switch (line[p].latin1()) {
02997 case '>':
02998 case '|':
02999 actQuoteLevel++;
03000 break;
03001 case ' ':
03002 case '\t':
03003 case '\r':
03004 break;
03005 default:
03006 p = line.length();
03007 break;
03008 }
03009 }
03010
03011 bool actHidden = false;
03012 QString textExpand;
03013
03014
03015 if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
03016 && mReader->mLevelQuote <= ( actQuoteLevel ) )
03017 actHidden = true;
03018
03019 if ( actQuoteLevel != currQuoteLevel ) {
03020
03021 if (currQuoteLevel == -1)
03022 htmlStr.append( normalEndTag );
03023 else if ( currQuoteLevel >= 0 && !curHidden )
03024 htmlStr.append( quoteEnd );
03025
03026
03027 if (actQuoteLevel == -1)
03028 htmlStr += normalStartTag;
03029 else
03030 {
03031 if ( GlobalSettings::self()->showExpandQuotesMark() )
03032 {
03033 if ( actHidden )
03034 {
03035
03036 if ( !curHidden )
03037 {
03038
03039 htmlStr += "<div class=\"quotelevelmark\" >" ;
03040 htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
03041 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03042 .arg(-1)
03043 .arg( mExpandIcon );
03044 htmlStr += "</div><br/>";
03045 htmlStr += quoteEnd;
03046 }
03047 }else {
03048 htmlStr += "<div class=\"quotelevelmark\" >" ;
03049 htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
03050 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03051 .arg(actQuoteLevel)
03052 .arg( mCollapseIcon);
03053 htmlStr += "</div>";
03054 if ( actQuoteLevel < 3 )
03055 htmlStr += quoteFontTag[actQuoteLevel];
03056 else
03057 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03058 }
03059 } else
03060 if ( actQuoteLevel < 3 )
03061 htmlStr += quoteFontTag[actQuoteLevel];
03062 else
03063 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03064 }
03065 currQuoteLevel = actQuoteLevel;
03066 }
03067 curHidden = actHidden;
03068
03069
03070 if ( !actHidden )
03071 {
03072
03073
03074 if( !line.replace('\015', "").isEmpty() )
03075 {
03076 htmlStr +=QString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
03077 htmlStr += LinkLocator::convertToHtml( line, convertFlags );
03078 htmlStr += QString( "</div>" );
03079 }
03080 else
03081 htmlStr += "<br>";
03082 }
03083 }
03084
03085
03086 if (currQuoteLevel == -1)
03087 htmlStr.append( normalEndTag );
03088 else
03089 htmlStr.append( quoteEnd );
03090
03091 return htmlStr;
03092 }
03093
03094
03095
03096 const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
03097 assert( node );
03098 if ( mReader && mReader->overrideCodec() )
03099 return mReader->overrideCodec();
03100 return node->msgPart().codec();
03101 }
03102
03103 #ifdef MARCS_DEBUG
03104 void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
03105 size_t len ) {
03106 assert( filename );
03107
03108 QFile f( filename );
03109 if ( f.open( IO_WriteOnly ) ) {
03110 if ( start ) {
03111 QDataStream ds( &f );
03112 ds.writeRawBytes( start, len );
03113 }
03114 f.close();
03115 }
03116 }
03117 #endif // !NDEBUG
03118
03119
03120 }