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