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