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