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