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