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