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