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