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