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