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("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 = "certificate";
02234 else
02235 certificate = QString("%1%2</a>")
02236 .arg( startKeyHREF,
02237 "certificate" );
02238 if( !blockAddrs.empty() ){
02239 if( blockAddrs.grep(
02240 msgFrom,
02241 false ).isEmpty() ) {
02242 greenCaseWarning =
02243 "<u>" +
02244 i18n("Warning:") +
02245 "</u> " +
02246 i18n("Sender's mail address is not stored "
02247 "in the %1 used for signing.").arg(certificate) +
02248 "<br />" +
02249 i18n("sender: ") +
02250 msgFrom +
02251 "<br />" +
02252 i18n("stored: ");
02253
02254
02255
02256
02257 bool bStart = true;
02258 for(QStringList::ConstIterator it = blockAddrs.begin();
02259 it != blockAddrs.end(); ++it ){
02260 if( !bStart )
02261 greenCaseWarning.append(", <br /> ");
02262 bStart = false;
02263 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02264 }
02265 }
02266 } else {
02267 greenCaseWarning =
02268 "<u>" +
02269 i18n("Warning:") +
02270 "</u> " +
02271 i18n("No mail address is stored in the %1 used for signing, "
02272 "so we cannot compare it to the sender's address %2.")
02273 .arg(certificate,msgFrom);
02274 }
02275 if( !greenCaseWarning.isEmpty() ) {
02276 if( !statusStr.isEmpty() )
02277 statusStr.append("<br /> <br />");
02278 statusStr.append( greenCaseWarning );
02279 }
02280 break;
02281 }
02282
02283 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02284 "class=\"" + block.signClass + "\">"
02285 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02286 htmlStr += frame + beginVerboseSigstatHeader();
02287 simpleHtmlStr += frame;
02288 simpleHtmlStr += writeSimpleSigstatHeader( block );
02289 if( block.technicalProblem ) {
02290 htmlStr += block.errorText;
02291 }
02292 else if( showKeyInfos ) {
02293 if( cannotCheckSignature ) {
02294 htmlStr += i18n( "Not enough information to check "
02295 "signature. %1" )
02296 .arg( keyWithWithoutURL );
02297 }
02298 else {
02299
02300 if (block.signer.isEmpty())
02301 signer = "";
02302 else {
02303 if( !blockAddrs.empty() ){
02304 QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02305 signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02306 }
02307 }
02308
02309 if( block.keyId.isEmpty() ) {
02310 if( signer.isEmpty() || onlyShowKeyURL )
02311 htmlStr += i18n( "Message was signed with unknown key." );
02312 else
02313 htmlStr += i18n( "Message was signed by %1." )
02314 .arg( signer );
02315 } else {
02316 QDateTime created = block.creationTime;
02317 if( created.isValid() ) {
02318 if( signer.isEmpty() ) {
02319 if( onlyShowKeyURL )
02320 htmlStr += i18n( "Message was signed with key %1." )
02321 .arg( keyWithWithoutURL );
02322 else
02323 htmlStr += i18n( "Message was signed on %1 with key %2." )
02324 .arg( KGlobal::locale()->formatDateTime( created ),
02325 keyWithWithoutURL );
02326 }
02327 else {
02328 if( onlyShowKeyURL )
02329 htmlStr += i18n( "Message was signed with key %1." )
02330 .arg( keyWithWithoutURL );
02331 else
02332 htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02333 .arg( KGlobal::locale()->formatDateTime( created ),
02334 keyWithWithoutURL,
02335 signer );
02336 }
02337 }
02338 else {
02339 if( signer.isEmpty() || onlyShowKeyURL )
02340 htmlStr += i18n( "Message was signed with key %1." )
02341 .arg( keyWithWithoutURL );
02342 else
02343 htmlStr += i18n( "Message was signed by %2 with key %1." )
02344 .arg( keyWithWithoutURL,
02345 signer );
02346 }
02347 }
02348 }
02349 htmlStr += "<br />";
02350 if( !statusStr.isEmpty() ) {
02351 htmlStr += " <br />";
02352 htmlStr += i18n( "Status: " );
02353 htmlStr += statusStr;
02354 }
02355 } else {
02356 htmlStr += statusStr;
02357 }
02358 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02359 htmlStr += endVerboseSigstatHeader( block ) + frame;
02360 simpleHtmlStr += frame;
02361
02362 } else {
02363
02364
02365
02366 if( block.signer.isEmpty() || block.technicalProblem ) {
02367 block.signClass = "signWarn";
02368 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02369 "class=\"" + block.signClass + "\">"
02370 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02371 htmlStr += frame + beginVerboseSigstatHeader();
02372 simpleHtmlStr += frame;
02373 simpleHtmlStr += writeSimpleSigstatHeader( block );
02374 if( block.technicalProblem ) {
02375 htmlStr += block.errorText;
02376 }
02377 else {
02378 if( !block.keyId.isEmpty() ) {
02379 QDateTime created = block.creationTime;
02380 if ( created.isValid() )
02381 htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02382 .arg( KGlobal::locale()->formatDateTime( created ),
02383 keyWithWithoutURL );
02384 else
02385 htmlStr += i18n( "Message was signed with unknown key %1." )
02386 .arg( keyWithWithoutURL );
02387 }
02388 else
02389 htmlStr += i18n( "Message was signed with unknown key." );
02390 htmlStr += "<br />";
02391 htmlStr += i18n( "The validity of the signature cannot be "
02392 "verified." );
02393 if( !statusStr.isEmpty() ) {
02394 htmlStr += "<br />";
02395 htmlStr += i18n( "Status: " );
02396 htmlStr += "<i>";
02397 htmlStr += statusStr;
02398 htmlStr += "</i>";
02399 }
02400 }
02401 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02402 htmlStr += endVerboseSigstatHeader( block ) + frame;
02403 simpleHtmlStr += frame;
02404 }
02405 else
02406 {
02407
02408 signer = KMMessage::quoteHtmlChars( signer, true );
02409 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02410
02411 if (block.isGoodSignature) {
02412 if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02413 block.signClass = "signOkKeyBad";
02414 else
02415 block.signClass = "signOkKeyOk";
02416 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02417 "class=\"" + block.signClass + "\">"
02418 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02419 htmlStr += frame + beginVerboseSigstatHeader();
02420 simpleHtmlStr += frame;
02421 simpleHtmlStr += writeSimpleSigstatHeader( block );
02422 if( !block.keyId.isEmpty() )
02423 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02424 .arg( keyWithWithoutURL,
02425 signer );
02426 else
02427 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02428 htmlStr += "<br />";
02429
02430 switch( block.keyTrust )
02431 {
02432 case Kpgp::KPGP_VALIDITY_UNKNOWN:
02433 htmlStr += i18n( "The signature is valid, but the key's "
02434 "validity is unknown." );
02435 break;
02436 case Kpgp::KPGP_VALIDITY_MARGINAL:
02437 htmlStr += i18n( "The signature is valid and the key is "
02438 "marginally trusted." );
02439 break;
02440 case Kpgp::KPGP_VALIDITY_FULL:
02441 htmlStr += i18n( "The signature is valid and the key is "
02442 "fully trusted." );
02443 break;
02444 case Kpgp::KPGP_VALIDITY_ULTIMATE:
02445 htmlStr += i18n( "The signature is valid and the key is "
02446 "ultimately trusted." );
02447 break;
02448 default:
02449 htmlStr += i18n( "The signature is valid, but the key is "
02450 "untrusted." );
02451 }
02452 frame = "</td></tr>"
02453 "<tr class=\"" + block.signClass + "B\"><td>";
02454 htmlStr += endVerboseSigstatHeader( block ) + frame;
02455 simpleHtmlStr += frame;
02456 }
02457 else
02458 {
02459 block.signClass = "signErr";
02460 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02461 "class=\"" + block.signClass + "\">"
02462 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02463 htmlStr += frame + beginVerboseSigstatHeader();
02464 simpleHtmlStr += frame;
02465 simpleHtmlStr += writeSimpleSigstatHeader( block );
02466 if( !block.keyId.isEmpty() )
02467 htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02468 .arg( keyWithWithoutURL,
02469 signer );
02470 else
02471 htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02472 htmlStr += "<br />";
02473 htmlStr += i18n("Warning: The signature is bad.");
02474 frame = "</td></tr>"
02475 "<tr class=\"" + block.signClass + "B\"><td>";
02476 htmlStr += endVerboseSigstatHeader( block ) + frame;
02477 simpleHtmlStr += frame;
02478 }
02479 }
02480 }
02481 }
02482
02483 if ( mReader->showSignatureDetails() )
02484 return htmlStr;
02485 return simpleHtmlStr;
02486 }
02487
02488 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02489 {
02490 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02491
02492 QString htmlStr;
02493
02494 if (block.isSigned) {
02495 htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02496 htmlStr += "<td dir=\"" + dir + "\">" +
02497 i18n( "End of signed message" ) +
02498 "</td></tr></table>";
02499 }
02500
02501 if (block.isEncrypted) {
02502 htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02503 i18n( "End of encrypted message" ) +
02504 "</td></tr></table>";
02505 }
02506
02507 if( block.isEncapsulatedRfc822Message )
02508 {
02509 htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02510 i18n( "End of encapsulated message" ) +
02511 "</td></tr></table>";
02512 }
02513
02514 return htmlStr;
02515 }
02516
02517
02518 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02519 const QString& fromAddress )
02520 {
02521 KMMsgSignatureState dummy1;
02522 KMMsgEncryptionState dummy2;
02523 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02524 }
02525
02526
02527 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02528 const QString& fromAddress,
02529 KMMsgSignatureState& inlineSignatureState,
02530 KMMsgEncryptionState& inlineEncryptionState,
02531 bool decorate )
02532 {
02533 bool goodSignature = false;
02534 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02535 assert(pgp != 0);
02536 bool isPgpMessage = false;
02537
02538 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02539 QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02540
02541 inlineSignatureState = KMMsgNotSigned;
02542 inlineEncryptionState = KMMsgNotEncrypted;
02543 QPtrList<Kpgp::Block> pgpBlocks;
02544 QStrList nonPgpBlocks;
02545 if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02546 {
02547 bool isEncrypted = false, isSigned = false;
02548 bool fullySignedOrEncrypted = true;
02549 bool firstNonPgpBlock = true;
02550 bool couldDecrypt = false;
02551 QString signer;
02552 QCString keyId;
02553 QString decryptionError;
02554 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02555
02556 QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02557
02558 QStrListIterator npbit( nonPgpBlocks );
02559
02560 QString htmlStr;
02561 for( ; *pbit != 0; ++pbit, ++npbit )
02562 {
02563
02564 QCString str( *npbit );
02565 if( !str.isEmpty() ) {
02566 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02567 kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
02568 << "'" << endl;
02569
02570
02571 if( firstNonPgpBlock ) {
02572
02573 for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02574 if( *c != '\n' ) {
02575 fullySignedOrEncrypted = false;
02576 break;
02577 }
02578 }
02579 }
02580 else {
02581 fullySignedOrEncrypted = false;
02582 }
02583 }
02584 firstNonPgpBlock = false;
02585
02586
02587
02588 Kpgp::Block* block = *pbit;
02589 if( ( block->type() == Kpgp::PgpMessageBlock &&
02590
02591 !kmkernel->contextMenuShown() ) ||
02592 ( block->type() == Kpgp::ClearsignedBlock ) )
02593 {
02594 isPgpMessage = true;
02595 if( block->type() == Kpgp::PgpMessageBlock )
02596 {
02597 if ( mReader )
02598 emit mReader->noDrag();
02599
02600 couldDecrypt = block->decrypt();
02601 isEncrypted = block->isEncrypted();
02602 if (!couldDecrypt) {
02603 decryptionError = pgp->lastErrorMsg();
02604 }
02605 }
02606 else
02607 {
02608
02609 block->verify();
02610 }
02611
02612 isSigned = block->isSigned();
02613 if( isSigned )
02614 {
02615 keyId = block->signatureKeyId();
02616 signer = block->signatureUserId();
02617 if( !signer.isEmpty() )
02618 {
02619 goodSignature = block->goodSignature();
02620
02621 if( !keyId.isEmpty() ) {
02622 keyTrust = pgp->keyTrust( keyId );
02623 Kpgp::Key* key = pgp->publicKey( keyId );
02624 if ( key ) {
02625
02626
02627 signer = key->primaryUserID();
02628 }
02629 }
02630 else
02631
02632
02633 keyTrust = pgp->keyTrust( signer );
02634 }
02635 }
02636
02637 if( isSigned )
02638 inlineSignatureState = KMMsgPartiallySigned;
02639 if( isEncrypted )
02640 inlineEncryptionState = KMMsgPartiallyEncrypted;
02641
02642 PartMetaData messagePart;
02643
02644 messagePart.isSigned = isSigned;
02645 messagePart.technicalProblem = false;
02646 messagePart.isGoodSignature = goodSignature;
02647 messagePart.isEncrypted = isEncrypted;
02648 messagePart.isDecryptable = couldDecrypt;
02649 messagePart.decryptionError = decryptionError;
02650 messagePart.signer = signer;
02651 messagePart.keyId = keyId;
02652 messagePart.keyTrust = keyTrust;
02653
02654 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02655
02656 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02657 htmlStr += writeSigstatFooter( messagePart );
02658 }
02659 else
02660 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02661 decorate );
02662 }
02663
02664
02665 QCString str( nonPgpBlocks.last() );
02666 if( !str.isEmpty() ) {
02667 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02668
02669
02670
02671
02672
02673 }
02674 if( fullySignedOrEncrypted ) {
02675 if( inlineSignatureState == KMMsgPartiallySigned )
02676 inlineSignatureState = KMMsgFullySigned;
02677 if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02678 inlineEncryptionState = KMMsgFullyEncrypted;
02679 }
02680 htmlWriter()->queue( htmlStr );
02681 }
02682 else
02683 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02684 }
02685
02686
02687 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
02688 {
02689 assert( mReader );
02690 assert( cssHelper() );
02691
02692 int convertFlags = LinkLocator::PreserveSpaces;
02693 if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02694 convertFlags |= LinkLocator::ReplaceSmileys;
02695 }
02696 QString htmlStr;
02697 const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02698 QString quoteFontTag[3];
02699 QString deepQuoteFontTag[3];
02700 for ( int i = 0 ; i < 3 ; ++i ) {
02701 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02702 deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02703 }
02704 const QString normalEndTag = "</div>";
02705 const QString quoteEnd = "</div>";
02706
02707 unsigned int pos, beg;
02708 const unsigned int length = s.length();
02709
02710
02711 for ( pos = 0; pos < length && s[pos] <= ' '; pos++ );
02712 while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02713 beg = pos;
02714
02715 int currQuoteLevel = -2;
02716 bool curHidden = false;
02717
02718 while (beg<length)
02719 {
02720 QString line;
02721
02722
02723 pos = s.find('\n', beg, FALSE);
02724 if (pos == (unsigned int)(-1))
02725 pos = length;
02726
02727 line = s.mid(beg,pos-beg);
02728 beg = pos+1;
02729
02730
02731 int actQuoteLevel = -1;
02732
02733 if ( GlobalSettings::self()->showExpandQuotesMark() )
02734 {
02735
02736 if ( mCollapseIcon.isEmpty() ) {
02737 mCollapseIcon= LinkLocator::pngToDataUrl(
02738 KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
02739 }
02740 if ( mExpandIcon.isEmpty() )
02741 mExpandIcon= LinkLocator::pngToDataUrl(
02742 KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
02743 }
02744
02745 for (unsigned int p=0; p<line.length(); p++) {
02746 switch (line[p].latin1()) {
02747 case '>':
02748 case '|':
02749 actQuoteLevel++;
02750 break;
02751 case ' ':
02752 case '\t':
02753 case '\r':
02754 break;
02755 default:
02756 p = line.length();
02757 break;
02758 }
02759 }
02760
02761 bool actHidden = false;
02762 QString textExpand;
02763
02764
02765 if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
02766 && mReader->mLevelQuote <= ( actQuoteLevel ) )
02767 actHidden = true;
02768
02769 if ( actQuoteLevel != currQuoteLevel ) {
02770
02771 if (currQuoteLevel == -1)
02772 htmlStr.append( normalEndTag );
02773 else if ( currQuoteLevel >= 0 && !curHidden )
02774 htmlStr.append( quoteEnd );
02775
02776
02777 if (actQuoteLevel == -1)
02778 htmlStr += normalStartTag;
02779 else
02780 {
02781 if ( GlobalSettings::self()->showExpandQuotesMark() )
02782 {
02783 if ( actHidden )
02784 {
02785
02786 if ( !curHidden )
02787 {
02788
02789 htmlStr += "<div class=\"quotelevelmark\" >" ;
02790 htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02791 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02792 .arg(-1)
02793 .arg( mExpandIcon );
02794 htmlStr += "</div><br/>";
02795 htmlStr += quoteEnd;
02796 }
02797 }else {
02798 htmlStr += "<div class=\"quotelevelmark\" >" ;
02799 htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02800 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02801 .arg(actQuoteLevel)
02802 .arg( mCollapseIcon);
02803 htmlStr += "</div>";
02804 if ( actQuoteLevel < 3 )
02805 htmlStr += quoteFontTag[actQuoteLevel];
02806 else
02807 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02808 }
02809 } else
02810 if ( actQuoteLevel < 3 )
02811 htmlStr += quoteFontTag[actQuoteLevel];
02812 else
02813 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02814 }
02815 currQuoteLevel = actQuoteLevel;
02816 }
02817 curHidden = actHidden;
02818
02819
02820 if ( !actHidden )
02821 {
02822
02823
02824 if( !line.replace('\015', "").isEmpty() )
02825 {
02826 htmlStr +=QString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
02827 htmlStr += LinkLocator::convertToHtml( line, convertFlags );
02828 htmlStr += QString( "</div>" );
02829 }
02830 else
02831 htmlStr += "<br>";
02832 }
02833 }
02834
02835
02836 if (currQuoteLevel == -1)
02837 htmlStr.append( normalEndTag );
02838 else
02839 htmlStr.append( quoteEnd );
02840
02841
02842
02843
02844
02845 return htmlStr;
02846 }
02847
02848
02849
02850 const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
02851 assert( node );
02852 if ( mReader && mReader->overrideCodec() )
02853 return mReader->overrideCodec();
02854 return node->msgPart().codec();
02855 }
02856
02857 #ifdef MARCS_DEBUG
02858 void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
02859 size_t len ) {
02860 assert( filename );
02861
02862 QFile f( filename );
02863 if ( f.open( IO_WriteOnly ) ) {
02864 if ( start ) {
02865 QDataStream ds( &f );
02866 ds.writeRawBytes( start, len );
02867 }
02868 f.close();
02869 }
02870 }
02871 #endif // !NDEBUG
02872
02873
02874 }