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