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