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