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