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