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