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