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