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