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
00038
00039
#include "kmreaderwin.h"
00040
#include "partNode.h"
00041
#include "kmgroupware.h"
00042
#include "kmkernel.h"
00043
#include <libkdepim/kfileio.h>
00044
#include <libkdepim/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
00055
#include "cryptplugwrapperlist.h"
00056
#include "cryptplugfactory.h"
00057
00058
00059
#include <mimelib/enum.h>
00060
#include <mimelib/bodypart.h>
00061
#include <mimelib/string.h>
00062
#include <mimelib/text.h>
00063
00064
#include <gpgmepp/importresult.h>
00065
00066
#include <kpgpblock.h>
00067
#include <kpgp.h>
00068
#include <linklocator.h>
00069
00070
00071
#include <kdebug.h>
00072
#include <klocale.h>
00073
#include <kglobal.h>
00074
#include <khtml_part.h>
00075
#include <ktempfile.h>
00076
#include <kstandarddirs.h>
00077
#include <kapplication.h>
00078
#include <kmessagebox.h>
00079
00080
00081
#include <qtextcodec.h>
00082
#include <qfile.h>
00083
#include <qapplication.h>
00084
00085
00086
#include <sys/stat.h>
00087
#include <sys/types.h>
00088
#include <unistd.h>
00089
00090
00091
namespace KMail {
00092
00093
00094
class ObjectTreeParser::CryptPlugWrapperSaver {
00095 ObjectTreeParser * otp;
00096 CryptPlugWrapper * wrapper;
00097
public:
00098 CryptPlugWrapperSaver( ObjectTreeParser * _otp, CryptPlugWrapper * _w )
00099 : otp( _otp ), wrapper( _otp ? _otp->cryptPlugWrapper() : 0 )
00100 {
00101
if ( otp )
00102 otp->setCryptPlugWrapper( _w );
00103 }
00104
00105 ~CryptPlugWrapperSaver() {
00106
if ( otp )
00107 otp->setCryptPlugWrapper( wrapper );
00108 }
00109 };
00110
00111
00112 ObjectTreeParser::ObjectTreeParser( KMReaderWin * reader, CryptPlugWrapper * wrapper,
00113
bool showOnlyOneMimePart,
bool keepEncryptions,
00114
bool includeSignatures,
00115
const AttachmentStrategy * strategy,
00116 HtmlWriter * htmlWriter,
00117 CSSHelper * cssHelper )
00118 : mReader( reader ),
00119 mCryptPlugWrapper( wrapper ),
00120 mShowOnlyOneMimePart( showOnlyOneMimePart ),
00121 mKeepEncryptions( keepEncryptions ),
00122 mIncludeSignatures( includeSignatures ),
00123 mAttachmentStrategy( strategy ),
00124 mHtmlWriter( htmlWriter ),
00125 mCSSHelper( cssHelper )
00126 {
00127
if ( !attachmentStrategy() )
00128 mAttachmentStrategy = reader ? reader->attachmentStrategy()
00129 : AttachmentStrategy::smart();
00130
if ( reader && !this->htmlWriter() )
00131 mHtmlWriter = reader->htmlWriter();
00132
if ( reader && !this->cssHelper() )
00133 mCSSHelper = reader->mCSSHelper;
00134 }
00135
00136 ObjectTreeParser::ObjectTreeParser(
const ObjectTreeParser & other )
00137 : mReader( other.mReader ),
00138 mCryptPlugWrapper( other.cryptPlugWrapper() ),
00139 mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
00140 mKeepEncryptions( other.keepEncryptions() ),
00141 mIncludeSignatures( other.includeSignatures() ),
00142 mAttachmentStrategy( other.attachmentStrategy() ),
00143 mHtmlWriter( other.htmlWriter() ),
00144 mCSSHelper( other.cssHelper() )
00145 {
00146
00147 }
00148
00149 ObjectTreeParser::~ObjectTreeParser() {}
00150
00151
void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
00152
const char* content,
00153
const char* cntDesc,
00154
bool append )
00155 {
00156
00157 DwBodyPart* myBody =
new DwBodyPart( DwString( content ), 0 );
00158 myBody->Parse();
00159
00160
if ( myBody->hasHeaders() ) {
00161 DwText& desc = myBody->Headers().ContentDescription();
00162 desc.FromString( cntDesc );
00163 desc.SetModified();
00164
00165 myBody->Headers().Parse();
00166 }
00167
00168
if ( ( !myBody->Body().FirstBodyPart() ||
00169 myBody->Body().AsString().length() == 0 ) &&
00170 startNode.dwPart() &&
00171 startNode.dwPart()->Body().Message() &&
00172 startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
00173 {
00174
00175
00176 kdDebug(5006) <<
"copy parts" << endl;
00177 myBody->Body().AddBodyPart(
00178 startNode.dwPart()->Body().Message()->Body().FirstBodyPart() );
00179 myBody->Body().FromString(
00180 startNode.dwPart()->Body().Message()->Body().FirstBodyPart()->Body().AsString() );
00181 }
00182
00183 partNode* parentNode = &startNode;
00184 partNode* newNode =
new partNode(
false, myBody);
00185
if ( append && parentNode->firstChild() ) {
00186 parentNode = parentNode->firstChild();
00187
while( parentNode->nextSibling() )
00188 parentNode = parentNode->nextSibling();
00189 parentNode->setNext( newNode );
00190 }
else
00191 parentNode->setFirstChild( newNode );
00192
00193 newNode->buildObjectTree(
false );
00194
00195
if ( startNode.mimePartTreeItem() ) {
00196 kdDebug(5006) <<
"\n -----> Inserting items into MimePartTree\n" << endl;
00197 newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
00198 QString::null, QString::null, QString::null, 0,
00199 append );
00200 kdDebug(5006) <<
"\n <----- Finished inserting items into MimePartTree\n" << endl;
00201 }
else {
00202 kdDebug(5006) <<
"\n ------ Sorry, node.mimePartTreeItem() returns ZERO so"
00203 <<
"\n we cannot insert new lines into MimePartTree. :-(\n" << endl;
00204 }
00205 kdDebug(5006) <<
"\n -----> Now parsing the MimePartTree\n" << endl;
00206 ObjectTreeParser otp( mReader, cryptPlugWrapper() );
00207 otp.parseObjectTree( newNode );
00208 mRawReplyString += otp.rawReplyString();
00209 mTextualContent += otp.textualContent();
00210
if ( !otp.textualContentCharset().isEmpty() )
00211 mTextualContentCharset = otp.textualContentCharset();
00212 kdDebug(5006) <<
"\n <----- Finished parsing the MimePartTree in insertAndParseNewChildNode()\n" << endl;
00213 }
00214
00215
00216
00217
00218
void ObjectTreeParser::parseObjectTree( partNode * node ) {
00219 kdDebug(5006) <<
"ObjectTreeParser::parseObjectTree( "
00220 << (node ?
"node OK, " :
"no node, ")
00221 <<
"showOnlyOneMimePart: " << (showOnlyOneMimePart() ?
"TRUE" :
"FALSE")
00222 <<
" )" << endl;
00223
00224
if ( !node )
00225
return;
00226
00227
00228
if ( showOnlyOneMimePart() ) {
00229
00230 node->setProcessed(
false,
false );
00231
if ( partNode * child = node->firstChild() )
00232 child->setProcessed(
false,
true );
00233 }
else if ( mReader && !node->parentNode() ) {
00234
00235 node->setProcessed(
false,
true );
00236 }
00237
00238
for ( ; node ; node = node->nextSibling() ) {
00239
if ( node->processed() )
00240
continue;
00241
00242 ProcessResult processResult;
00243
00244
if (
const Interface::BodyPartFormatter * formatter
00245 = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
00246 PartNodeBodyPart part( *node, codecFor( node ) );
00247
const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter() );
00248
if ( mReader && node->bodyPartMemento() )
00249
if ( Interface::Observable * obs = node->bodyPartMemento()->asObservable() )
00250 obs->attach( mReader );
00251
switch ( result ) {
00252
case Interface::BodyPartFormatter::AsIcon:
00253 processResult.setNeverDisplayInline(
true );
00254
00255
case Interface::BodyPartFormatter::Failed:
00256 defaultHandling( node, processResult );
00257
break;
00258
case Interface::BodyPartFormatter::Ok:
00259
case Interface::BodyPartFormatter::NeedContent:
00260
00261 ;
00262 }
00263 }
else {
00264
const BodyPartFormatter * bpf
00265 = BodyPartFormatter::createFor( node->type(), node->subType() );
00266 kdFatal( !bpf, 5006 ) <<
"THIS SHOULD NO LONGER HAPPEN ("
00267 << node->typeString() <<
'/' << node->subTypeString()
00268 <<
')' << endl;
00269
00270
if ( !bpf->process(
this, node, processResult ) )
00271 defaultHandling( node, processResult );
00272 }
00273 node->setProcessed(
true,
false );
00274
00275
00276 processResult.adjustCryptoStatesOfNode( node );
00277
00278
if ( showOnlyOneMimePart() )
00279
break;
00280 }
00281 }
00282
00283
void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
00284
00285
00286
if ( !mReader )
00287
return;
00288
if ( attachmentStrategy() == AttachmentStrategy::hidden() &&
00289 !showOnlyOneMimePart() &&
00290 node->parentNode() )
00291
return;
00292
00293
bool asIcon =
true;
00294
if ( showOnlyOneMimePart() )
00295
00296
00297
00298 asIcon = !node->hasContentDispositionInline();
00299
else if ( !result.neverDisplayInline() )
00300
if (
const AttachmentStrategy * as = attachmentStrategy() )
00301 asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
00302
00303
if ( !result.isImage()
00304 && node->type() != DwMime::kTypeText )
00305 asIcon =
true;
00306
if ( asIcon ) {
00307
if ( attachmentStrategy() != AttachmentStrategy::hidden()
00308 || showOnlyOneMimePart() )
00309 writePartIcon( &node->msgPart(), node->nodeId() );
00310 }
else if ( result.isImage() )
00311 writePartIcon( &node->msgPart(), node->nodeId(),
true );
00312
else
00313 writeBodyString( node->msgPart().bodyDecoded(),
00314 node->trueFromAddress(),
00315 codecFor( node ), result );
00316
00317 }
00318
00319
void ProcessResult::adjustCryptoStatesOfNode( partNode * node )
const {
00320
if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
00321 ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
00322 node->setSignatureState( inlineSignatureState() );
00323 node->setEncryptionState( inlineEncryptionState() );
00324 }
00325 }
00326
00330
00331
bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
00332 partNode& sign,
00333
const QString& fromAddress,
00334
bool doCheck,
00335
QCString* cleartextData,
00336 CryptPlug::SignatureMetaData* paramSigMeta,
00337
bool hideErrors )
00338 {
00339
bool bIsOpaqueSigned =
false;
00340
enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
00341 cryptPlugError = NO_PLUGIN;
00342
00343 CryptPlugWrapper* cryptPlug = cryptPlugWrapper();
00344
if ( !cryptPlug )
00345 cryptPlug = CryptPlugFactory::instance()->active();
00346
00347
QString cryptPlugLibName;
00348
QString cryptPlugDisplayName;
00349
if ( cryptPlug ) {
00350 cryptPlugLibName = cryptPlug->libName();
00351
if ( cryptPlug == CryptPlugFactory::instance()->openpgp() )
00352 cryptPlugDisplayName =
"OpenPGP";
00353
else if ( cryptPlug == CryptPlugFactory::instance()->smime() )
00354 cryptPlugDisplayName =
"S/MIME";
00355 }
00356
00357
#ifndef NDEBUG
00358
if ( !doCheck )
00359 kdDebug(5006) <<
"ObjectTreeParser::writeOpaqueOrMultipartSignedData: showing OpenPGP (Encrypted+Signed) data" << endl;
00360
else
00361
if ( data )
00362 kdDebug(5006) <<
"ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Multipart Signed data" << endl;
00363
else
00364 kdDebug(5006) <<
"ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl;
00365
#endif
00366
00367
if ( doCheck && cryptPlug ) {
00368 kdDebug(5006) <<
"ObjectTreeParser::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG "
00369 << cryptPlugLibName << endl;
00370
00371
00372
if ( cryptPlug->initStatus( 0 ) != CryptPlugWrapper::InitStatus_Ok ) {
00373 cryptPlugError = NOT_INITIALIZED;
00374 cryptPlug = 0;
00375 }
00376
else if ( !cryptPlug->hasFeature( Feature_VerifySignatures ) ) {
00377 cryptPlugError = CANT_VERIFY_SIGNATURES;
00378 cryptPlug = 0;
00379 }
00380 }
00381
00382
QCString cleartext;
00383
char* new_cleartext = 0;
00384
QByteArray signaturetext;
00385
bool signatureIsBinary =
false;
00386
int signatureLen = 0;
00387
00388
if ( doCheck && cryptPlug ) {
00389
if ( data ) {
00390 cleartext = data->dwPart()->AsString().c_str();
00391
00392 dumpToFile(
"dat_01_reader_signedtext_before_canonicalization",
00393 cleartext.data(), cleartext.length() );
00394
00395
00396
00397 kdDebug(5006) <<
"Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
00398 cleartext = KMMessage::lf2crlf( cleartext );
00399 kdDebug(5006) <<
" done." << endl;
00400 }
00401
00402 dumpToFile(
"dat_02_reader_signedtext_after_canonicalization",
00403 cleartext.data(), cleartext.length() );
00404
00405 signaturetext = sign.msgPart().bodyDecodedBinary();
00406
QCString signatureStr( signaturetext, signaturetext.size() + 1 );
00407 signatureIsBinary = (-1 == signatureStr.find(
"BEGIN SIGNED MESSAGE", 0,
false) ) &&
00408 (-1 == signatureStr.find(
"BEGIN PGP SIGNED MESSAGE", 0,
false) ) &&
00409 (-1 == signatureStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
00410 signatureLen = signaturetext.size();
00411
00412 dumpToFile(
"dat_03_reader.sig", signaturetext.data(),
00413 signaturetext.size() );
00414 }
00415
00416 CryptPlug::SignatureMetaData localSigMeta;
00417
if ( doCheck ){
00418 localSigMeta.status = 0;
00419 localSigMeta.extended_info = 0;
00420 localSigMeta.extended_info_count = 0;
00421 }
00422 CryptPlug::SignatureMetaData* sigMeta = doCheck ? &localSigMeta : paramSigMeta;
00423
00424
const char* cleartextP = cleartext;
00425 PartMetaData messagePart;
00426 messagePart.isSigned =
true;
00427 messagePart.technicalProblem = ( cryptPlug == 0 );
00428 messagePart.isGoodSignature =
false;
00429 messagePart.isEncrypted =
false;
00430 messagePart.isDecryptable =
false;
00431 messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
00432 messagePart.status = i18n(
"Wrong Crypto Plug-In.");
00433
00434
if ( !doCheck ||
00435 ( cryptPlug &&
00436 cryptPlug->checkMessageSignature( data
00437 ? const_cast<char**>(&cleartextP)
00438 : &new_cleartext,
00439 signaturetext,
00440 signatureIsBinary,
00441 signatureLen,
00442 sigMeta ) ) ) {
00443 messagePart.isGoodSignature =
true;
00444 }
00445
00446
if ( doCheck )
00447 kdDebug(5006) <<
"\nObjectTreeParser::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl;
00448
00449
if ( sigMeta->status && *sigMeta->status )
00450 messagePart.status = QString::fromUtf8( sigMeta->status );
00451 messagePart.status_code = sigMeta->status_code;
00452
00453
00454
if ( sigMeta->extended_info_count != 0 ) {
00455 kdDebug(5006) <<
"\nObjectTreeParser::writeOpaqueOrMultipartSignedData: found extended sigMeta info" << endl;
00456
00457 CryptPlug::SignatureMetaDataExtendedInfo& ext = sigMeta->extended_info[0];
00458
00459
00460 messagePart.sigStatusFlags = ext.sigStatusFlags;
00461
00462
if ( messagePart.status.isEmpty()
00463 && ext.status_text
00464 && *ext.status_text )
00465 messagePart.status = QString::fromUtf8( ext.status_text );
00466
if ( ext.keyid && *ext.keyid )
00467 messagePart.keyId = ext.keyid;
00468
if ( messagePart.keyId.isEmpty() )
00469 messagePart.keyId = ext.fingerprint;
00470
00471 messagePart.keyTrust = (Kpgp::Validity)ext.validity;
00472
if ( ext.userid && *ext.userid )
00473 messagePart.signer = QString::fromUtf8( ext.userid );
00474
for(
int iMail = 0; iMail < ext.emailCount; ++iMail )
00475
00476
00477
if ( ext.emailList[ iMail ] && *ext.emailList[ iMail ] ) {
00478
QString email = QString::fromUtf8( ext.emailList[ iMail ] );
00479
00480
00481
if ( email.startsWith(
"<" ) && email.endsWith(
">" ) )
00482 email = email.mid( 1, email.length() - 2 );
00483 messagePart.signerMailAddresses.append( email );
00484 }
00485
if ( ext.creation_time )
00486 messagePart.creationTime = *ext.creation_time;
00487
if ( 70 > messagePart.creationTime.tm_year
00488 || 200 < messagePart.creationTime.tm_year
00489 || 0 > messagePart.creationTime.tm_mon
00490 || 12 < messagePart.creationTime.tm_mon
00491 || 1 > messagePart.creationTime.tm_mday
00492 || 31 < messagePart.creationTime.tm_mday ) {
00493 messagePart.creationTime.tm_year = 0;
00494 messagePart.creationTime.tm_mon = 1;
00495 messagePart.creationTime.tm_mday = 1;
00496 }
00497
if ( messagePart.signer.isEmpty() ) {
00498
if ( ext.name && *ext.name )
00499 messagePart.signer = QString::fromUtf8( ext.name );
00500
if ( !messagePart.signerMailAddresses.empty() ) {
00501
if ( messagePart.signer.isEmpty() )
00502 messagePart.signer = messagePart.signerMailAddresses.front();
00503
else
00504 messagePart.signer +=
" <" + messagePart.signerMailAddresses.front() +
'>';
00505 }
00506 }
00507
00508 kdDebug(5006) <<
"\n key id: " << messagePart.keyId
00509 <<
"\n key trust: " << messagePart.keyTrust
00510 <<
"\n signer: " << messagePart.signer << endl;
00511
00512 }
else {
00513 messagePart.creationTime.tm_year = 0;
00514 messagePart.creationTime.tm_mon = 1;
00515 messagePart.creationTime.tm_mday = 1;
00516 }
00517
00518
if ( !doCheck || !data ){
00519
if ( cleartextData || new_cleartext ) {
00520
if ( mReader )
00521 htmlWriter()->queue( writeSigstatHeader( messagePart,
00522 cryptPlug,
00523 fromAddress ) );
00524 bIsOpaqueSigned =
true;
00525
00526 CryptPlugWrapperSaver cpws(
this, cryptPlug );
00527 insertAndParseNewChildNode( sign,
00528 doCheck ? new_cleartext
00529 : cleartextData->data(),
00530
"opaqued signed data" );
00531
if ( doCheck )
00532 free( new_cleartext );
00533
00534
if ( mReader )
00535 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00536
00537 }
00538
else if ( !hideErrors ) {
00539
QString txt;
00540 txt =
"<hr><b><h2>";
00541 txt.append( i18n(
"The crypto engine returned no cleartext data." ) );
00542 txt.append(
"</h2></b>" );
00543 txt.append(
"<br> <br>" );
00544 txt.append( i18n(
"Status: " ) );
00545
if ( sigMeta->status && *sigMeta->status ) {
00546 txt.append(
"<i>" );
00547 txt.append( sigMeta->status );
00548 txt.append(
"</i>" );
00549 }
00550
else
00551 txt.append( i18n(
"(unknown)") );
00552
if ( mReader )
00553 htmlWriter()->queue(txt);
00554 }
00555 }
00556
else {
00557
if ( mReader ) {
00558
if ( !cryptPlug ) {
00559
QString errorMsg;
00560
switch ( cryptPlugError ) {
00561
case NOT_INITIALIZED:
00562 errorMsg = i18n(
"Crypto plug-in \"%1\" is not initialized." )
00563 .arg( cryptPlugLibName );
00564
break;
00565
case CANT_VERIFY_SIGNATURES:
00566 errorMsg = i18n(
"Crypto plug-in \"%1\" cannot verify signatures." )
00567 .arg( cryptPlugLibName );
00568
break;
00569
case NO_PLUGIN:
00570
if ( cryptPlugDisplayName.isEmpty() )
00571 errorMsg = i18n(
"No appropriate crypto plug-in was found." );
00572
else
00573 errorMsg = i18n(
"%1 is either 'OpenPGP' or 'S/MIME'",
00574
"No %1 plug-in was found." )
00575 .arg( cryptPlugDisplayName );
00576
break;
00577 }
00578 messagePart.errorText = i18n(
"The message is signed, but the "
00579
"validity of the signature cannot be "
00580
"verified.<br />"
00581
"Reason: %1" )
00582 .arg( errorMsg );
00583 }
00584
00585 htmlWriter()->queue( writeSigstatHeader( messagePart,
00586 cryptPlug,
00587 fromAddress ) );
00588 }
00589
00590 ObjectTreeParser otp( mReader, cryptPlug,
true );
00591 otp.parseObjectTree( data );
00592 mRawReplyString += otp.rawReplyString();
00593 mTextualContent += otp.textualContent();
00594
if ( !otp.textualContentCharset().isEmpty() )
00595 mTextualContentCharset = otp.textualContentCharset();
00596
00597
if ( mReader )
00598 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00599 }
00600
00601
if ( cryptPlug )
00602 cryptPlug->freeSignatureMetaData( sigMeta );
00603
00604 kdDebug(5006) <<
"\nObjectTreeParser::writeOpaqueOrMultipartSignedData: done, returning "
00605 << ( bIsOpaqueSigned ?
"TRUE" :
"FALSE" ) << endl;
00606
return bIsOpaqueSigned;
00607 }
00608
00609
00610
bool ObjectTreeParser::okDecryptMIME( partNode& data,
00611
QCString& decryptedData,
00612
bool& signatureFound,
00613 CryptPlug::SignatureMetaData& sigMeta,
00614
bool showWarning,
00615
bool& passphraseError,
00616
QString& aErrorText )
00617 {
00618 passphraseError =
false;
00619 aErrorText = QString::null;
00620
bool bDecryptionOk =
false;
00621
enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
00622 cryptPlugError = NO_PLUGIN;
00623
00624 CryptPlugWrapper* cryptPlug = cryptPlugWrapper();
00625
if ( !cryptPlug )
00626 cryptPlug = CryptPlugFactory::instance()->active();
00627
00628
QString cryptPlugLibName;
00629
if ( cryptPlug )
00630 cryptPlugLibName = cryptPlug->libName();
00631
00632
00633
if ( cryptPlug ) {
00634
if ( cryptPlug->initStatus( 0 ) != CryptPlugWrapper::InitStatus_Ok ) {
00635 cryptPlugError = NOT_INITIALIZED;
00636 cryptPlug = 0;
00637 }
00638
else if ( !cryptPlug->hasFeature( Feature_DecryptMessages ) ) {
00639 cryptPlugError = CANT_DECRYPT;
00640 cryptPlug = 0;
00641 }
00642 }
00643
00644
if ( cryptPlug && !kmkernel->contextMenuShown() ) {
00645
QByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00646
QCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00647
bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
00648 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
00649 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
00650
int cipherLen = ciphertext.size();
00651
00652 dumpToFile(
"dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
00653
00654
#ifdef MARCS_DEBUG
00655
QCString deb;
00656 deb =
"\n\nE N C R Y P T E D D A T A = ";
00657
if ( cipherIsBinary )
00658 deb +=
"[binary data]";
00659
else {
00660 deb +=
"\"";
00661 deb += cipherStr;
00662 deb +=
"\"";
00663 }
00664 deb +=
"\n\n";
00665 kdDebug(5006) << deb << endl;
00666
#endif
00667
00668
00669
00670
char* cleartext = 0;
00671
const char* certificate = 0;
00672
00673 kdDebug(5006) <<
"ObjectTreeParser::decryptMIME: going to call CRYPTPLUG "
00674 << cryptPlugLibName << endl;
00675
int errId = 0;
00676
char* errTxt = 0;
00677 bDecryptionOk = cryptPlug->decryptAndCheckMessage( ciphertext.data(),
00678 cipherIsBinary,
00679 cipherLen,
00680 &cleartext,
00681 certificate,
00682 &signatureFound,
00683 &sigMeta,
00684 &errId,
00685 &errTxt );
00686 kdDebug(5006) <<
"ObjectTreeParser::decryptMIME: returned from CRYPTPLUG"
00687 << endl;
00688 aErrorText = CryptPlugWrapper::errorIdToText( errId, passphraseError );
00689
if ( bDecryptionOk )
00690 decryptedData = cleartext;
00691
else if ( mReader && showWarning ) {
00692 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
00693
"padding:20pt;\">"
00694 + i18n(
"Encrypted data not shown.").utf8()
00695 +
"</div>";
00696
if ( !passphraseError )
00697 aErrorText = i18n(
"Crypto plug-in \"%1\" could not decrypt the data.")
00698 .arg( cryptPlugLibName )
00699 +
"<br />"
00700 + i18n(
"Error: %1").arg( aErrorText );
00701 }
00702
if ( errTxt )
00703 free( errTxt );
00704
if ( cleartext )
00705 free( cleartext );
00706 }
00707
else if ( !cryptPlug ) {
00708 decryptedData =
"<div style=\"text-align:center; padding:20pt;\">"
00709 + i18n(
"Encrypted data not shown.").utf8()
00710 +
"</div>";
00711
switch ( cryptPlugError ) {
00712
case NOT_INITIALIZED:
00713 aErrorText = i18n(
"Crypto plug-in \"%1\" is not initialized." )
00714 .arg( cryptPlugLibName );
00715
break;
00716
case CANT_DECRYPT:
00717 aErrorText = i18n(
"Crypto plug-in \"%1\" cannot decrypt messages." )
00718 .arg( cryptPlugLibName );
00719
break;
00720
case NO_PLUGIN:
00721 aErrorText = i18n(
"No appropriate crypto plug-in was found." );
00722
break;
00723 }
00724 }
00725
else {
00726
00727
00728
QByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00729
QCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00730
bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
00731 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
00732 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
00733
if ( !cipherIsBinary ) {
00734 decryptedData = cipherStr;
00735 }
00736
else {
00737 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
00738
"padding:20pt;\">"
00739 + i18n(
"Encrypted data not shown.").utf8()
00740 +
"</div>";
00741 }
00742 }
00743
00744 dumpToFile(
"dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
00745
00746
return bDecryptionOk;
00747 }
00748
00749
QString ObjectTreeParser::byteArrayToTempFile( KMReaderWin* reader,
00750
const QString& dirExt,
00751
const QString& orgName,
00752
const QByteArray& theBody )
00753 {
00754 KTempFile *tempFile =
new KTempFile( QString::null,
"." + dirExt );
00755 tempFile->setAutoDelete(
true);
00756
QString fname = tempFile->name();
00757
delete tempFile;
00758
00759
bool bOk =
true;
00760
00761
if (access(QFile::encodeName(fname), W_OK) != 0)
00762
if (mkdir(QFile::encodeName(fname), 0) != 0
00763 || chmod (QFile::encodeName(fname), S_IRWXU) != 0)
00764 bOk =
false;
00765
00766
if ( bOk )
00767 {
00768
QString fileName( orgName );
00769
if ( reader )
00770 reader->mTempDirs.append(fname);
00771
00772
00773
int slashPos = fileName.findRev(
'/' );
00774
if ( -1 != slashPos )
00775 fileName = fileName.mid( slashPos + 1 );
00776
if (fileName.isEmpty()) fileName =
"unnamed";
00777 fname +=
"/" + fileName;
00778
00779
if (!KPIM::kByteArrayToFile(theBody, fname,
false,
false,
false))
00780 bOk =
false;
00781
if ( reader )
00782 reader->mTempFiles.append(fname);
00783 }
00784
return bOk ? fname : QString::null;
00785 }
00786
00787
bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
00788
QCString cstr( curNode->msgPart().bodyDecoded() );
00789
00790 mRawReplyString = cstr;
00791
if ( curNode->isFirstTextPart() ) {
00792 mTextualContent += curNode->msgPart().bodyToUnicode();
00793 mTextualContentCharset = curNode->msgPart().charset();
00794 }
00795
00796
if ( !mReader )
00797
return true;
00798
00799
if ( curNode->isFirstTextPart() ||
00800 attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
00801 showOnlyOneMimePart() )
00802 {
00803
if ( mReader->htmlMail() ) {
00804
00805
00806
00807
00808
int i = cstr.findRev(
"</body>", -1,
false);
00809
if ( 0 <= i )
00810 cstr.truncate(i);
00811
else
00812 {
00813 i = cstr.findRev(
"</html>", -1,
false);
00814
if ( 0 <= i ) cstr.truncate(i);
00815 }
00816
00817 }
else {
00818 htmlWriter()->queue(
"<div class=\"htmlWarn\">\n" );
00819 htmlWriter()->queue( i18n(
"<b>Note:</b> This is an HTML message. For "
00820
"security reasons, only the raw HTML code "
00821
"is shown. If you trust the sender of this "
00822
"message then you can activate formatted "
00823
"HTML display for this message "
00824
"<a href=\"kmail:showHTML\">by clicking here</a>.") );
00825 htmlWriter()->queue(
"</div><br><br>" );
00826 }
00827 htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr : KMMessage::html2source( cstr )));
00828 mReader->mColorBar->setHtmlMode();
00829
return true;
00830 }
00831
return false;
00832 }
00833
00834
bool ObjectTreeParser::processTextVCalSubtype( partNode * curNode,
00835 ProcessResult & result ) {
00836
00837 result.setNeverDisplayInline(
true );
00838
00839
00840
if ( !mReader )
00841
return false;
00842
00843
QString iCal = curNode->msgPart().bodyToUnicode();
00844
QString html = kmkernel->groupware().vPartToHTML( iCal );
00845
if( !html.isEmpty() )
00846 htmlWriter()->queue( html );
00847 htmlWriter()->queue( quotedHTML( iCal ) );
00848
00849
return true;
00850 }
00851
00852 }
00853
00854
static bool isMailmanMessage( partNode * curNode ) {
00855
if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
00856
return false;
00857 DwHeaders & headers = curNode->dwPart()->Headers();
00858
if ( headers.HasField(
"X-Mailman-Version") )
00859
return true;
00860
if ( headers.HasField(
"X-Mailer") &&
00861 0 ==
QCString( headers.FieldBody(
"X-Mailer").AsString().c_str() )
00862 .find(
"MAILMAN", 0,
false) )
00863
return true;
00864
return false;
00865 }
00866
00867
namespace KMail {
00868
00869
bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
00870
const QCString cstr = curNode->msgPart().bodyDecoded();
00871
00872
00873
const QCString delim1(
"--__--__--\n\nMessage:");
00874
const QCString delim2(
"--__--__--\r\n\r\nMessage:");
00875
const QCString delimZ2(
"--__--__--\n\n_____________");
00876
const QCString delimZ1(
"--__--__--\r\n\r\n_____________");
00877
QCString partStr, digestHeaderStr;
00878
int thisDelim = cstr.find(delim1, 0,
false);
00879
if ( thisDelim == -1 )
00880 thisDelim = cstr.find(delim2, 0,
false);
00881
if ( thisDelim == -1 ) {
00882 kdDebug(5006) <<
" Sorry: Old style Mailman message but no delimiter found." << endl;
00883
return false;
00884 }
00885
00886
int nextDelim = cstr.find(delim1, thisDelim+1,
false);
00887
if ( -1 == nextDelim )
00888 nextDelim = cstr.find(delim2, thisDelim+1,
false);
00889
if ( -1 == nextDelim )
00890 nextDelim = cstr.find(delimZ1, thisDelim+1,
false);
00891
if ( -1 == nextDelim )
00892 nextDelim = cstr.find(delimZ2, thisDelim+1,
false);
00893
if ( nextDelim < 0)
00894
return false;
00895
00896 kdDebug(5006) <<
" processing old style Mailman digest" << endl;
00897
00898
00899
00900
00901 digestHeaderStr =
"Content-Type=text/plain\nContent-Description=digest header\n\n";
00902 digestHeaderStr += cstr.mid( 0, thisDelim );
00903 insertAndParseNewChildNode( *curNode,
00904 &*digestHeaderStr,
00905
"Digest Header",
true );
00906
00907
00908
00909 curNode->setType( DwMime::kTypeMultipart );
00910 curNode->setSubType( DwMime::kSubtypeDigest );
00911
while( -1 < nextDelim ){
00912
int thisEoL = cstr.find(
"\nMessage:", thisDelim,
false);
00913
if ( -1 < thisEoL )
00914 thisDelim = thisEoL+1;
00915
else{
00916 thisEoL = cstr.find(
"\n_____________", thisDelim,
false);
00917
if ( -1 < thisEoL )
00918 thisDelim = thisEoL+1;
00919 }
00920 thisEoL = cstr.find(
'\n', thisDelim);
00921
if ( -1 < thisEoL )
00922 thisDelim = thisEoL+1;
00923
else
00924 thisDelim = thisDelim+1;
00925
00926
00927
00928 partStr =
"Content-Type=message/rfc822\nContent-Description=embedded message\n";
00929 partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
00930
QCString subject(
"embedded message");
00931
QCString subSearch(
"\nSubject:");
00932
int subPos = partStr.find(subSearch, 0,
false);
00933
if ( -1 < subPos ){
00934 subject = partStr.mid(subPos+subSearch.length());
00935 thisEoL = subject.find(
'\n');
00936
if ( -1 < thisEoL )
00937 subject.truncate( thisEoL );
00938 }
00939 kdDebug(5006) <<
" embedded message found: \"" << subject <<
"\"" << endl;
00940 insertAndParseNewChildNode( *curNode,
00941 &*partStr,
00942 subject,
true );
00943
00944 thisDelim = nextDelim+1;
00945 nextDelim = cstr.find(delim1, thisDelim,
false);
00946
if ( -1 == nextDelim )
00947 nextDelim = cstr.find(delim2, thisDelim,
false);
00948
if ( -1 == nextDelim )
00949 nextDelim = cstr.find(delimZ1, thisDelim,
false);
00950
if ( -1 == nextDelim )
00951 nextDelim = cstr.find(delimZ2, thisDelim,
false);
00952 }
00953
00954 curNode->setType( DwMime::kTypeText );
00955 curNode->setSubType( DwMime::kSubtypePlain );
00956
int thisEoL = cstr.find(
"_____________", thisDelim);
00957
if ( -1 < thisEoL ){
00958 thisDelim = thisEoL;
00959 thisEoL = cstr.find(
'\n', thisDelim);
00960
if ( -1 < thisEoL )
00961 thisDelim = thisEoL+1;
00962 }
00963
else
00964 thisDelim = thisDelim+1;
00965 partStr =
"Content-Type=text/plain\nContent-Description=digest footer\n\n";
00966 partStr += cstr.mid( thisDelim );
00967 insertAndParseNewChildNode( *curNode,
00968 &*partStr,
00969
"Digest Footer",
true );
00970
return true;
00971 }
00972
00973
bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
00974
const QCString cstr = curNode->msgPart().bodyDecoded();
00975
if ( !mReader ) {
00976 mRawReplyString = cstr;
00977
if ( curNode->isFirstTextPart() ) {
00978 mTextualContent += curNode->msgPart().bodyToUnicode();
00979 mTextualContentCharset = curNode->msgPart().charset();
00980 }
00981
return true;
00982 }
00983
00984
00985
if ( !curNode->isFirstTextPart() &&
00986 attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
00987 !showOnlyOneMimePart() )
00988
return false;
00989
00990 mRawReplyString = cstr;
00991
if ( curNode->isFirstTextPart() ) {
00992 mTextualContent += curNode->msgPart().bodyToUnicode();
00993 mTextualContentCharset = curNode->msgPart().charset();
00994 }
00995
00996
QString label = curNode->msgPart().fileName().stripWhiteSpace();
00997
if ( label.isEmpty() )
00998 label = curNode->msgPart().name().stripWhiteSpace();
00999
01000
const bool bDrawFrame = !curNode->isFirstTextPart()
01001 && !showOnlyOneMimePart()
01002 && !label.isEmpty();
01003
if ( bDrawFrame ) {
01004 label = KMMessage::quoteHtmlChars( label,
true );
01005
01006
const QString comment =
01007 KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(),
true );
01008
01009
const QString fileName =
01010 mReader->writeMessagePartToTempFile( &curNode->msgPart(),
01011 curNode->nodeId() );
01012
01013
const QString dir = QApplication::reverseLayout() ?
"rtl" :
"ltr" ;
01014
01015
QString htmlStr =
"<table cellspacing=\"1\" class=\"textAtm\">"
01016
"<tr class=\"textAtmH\"><td dir=\"" + dir +
"\">";
01017
if ( !fileName.isEmpty() )
01018 htmlStr +=
"<a href=\"" +
QString(
"file:")
01019 + KURL::encode_string( fileName ) +
"\">"
01020 + label +
"</a>";
01021
else
01022 htmlStr += label;
01023
if ( !comment.isEmpty() )
01024 htmlStr +=
"<br>" + comment;
01025 htmlStr +=
"</td></tr><tr class=\"textAtmB\"><td>";
01026
01027 htmlWriter()->queue( htmlStr );
01028 }
01029
01030
01031
if ( !isMailmanMessage( curNode ) ||
01032 !processMailmanMessage( curNode ) )
01033 writeBodyString( cstr, curNode->trueFromAddress(),
01034 codecFor( curNode ), result );
01035
if ( bDrawFrame )
01036 htmlWriter()->queue(
"</td></tr></table>" );
01037
01038
return true;
01039 }
01040
01041
void ObjectTreeParser::stdChildHandling( partNode * child ) {
01042
if ( !child )
01043
return;
01044
01045 ObjectTreeParser otp( *
this );
01046 otp.setShowOnlyOneMimePart(
false );
01047 otp.parseObjectTree( child );
01048 mRawReplyString += otp.rawReplyString();
01049 mTextualContent += otp.textualContent();
01050
if ( !otp.textualContentCharset().isEmpty() )
01051 mTextualContentCharset = otp.textualContentCharset();
01052 }
01053
01054
bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01055 partNode * child = node->firstChild();
01056
if ( !child )
01057
return false;
01058
01059
#if 0
01060
01061
01062 partNode * dataPlain = child->findType( DwMime::kTypeText,
01063 DwMime::kSubtypePlain,
false,
true );
01064
01065
01066 partNode * dataCal = child->findType( DwMime::kTypeText,
01067 DwMime::kSubtypeVCal,
false,
true );
01068
if ( dataCal ) {
01069 ProcessResult dummy;
01070
if ( processTextVCalSubtype( dataCal, dummy ) ) {
01071
01072
01073
if ( dataPlain )
01074 dataPlain->setProcessed(
true,
false );
01075
return true;
01076 }
01077 }
01078
#endif
01079
01080
#if 0
01081
01082 partNode * dataTNEF = child->findType( DwMime::kTypeApplication,
01083 DwMime::kSubtypeMsTNEF,
false,
true );
01084
if ( dataTNEF ) {
01085 ProcessResult dummy;
01086
if ( processApplicationMsTnefSubtype( dataTNEF, dummy ) ) {
01087
01088
01089
if ( dataPlain )
01090 dataPlain->setProcessed(
true,
false );
01091
return true;
01092 }
01093 }
01094
#endif
01095
01096
01097 stdChildHandling( child );
01098
return true;
01099 }
01100
01101
bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01102 partNode * child = node->firstChild();
01103
if ( !child )
01104
return false;
01105
01106 partNode * dataHtml = child->findType( DwMime::kTypeText,
01107 DwMime::kSubtypeHtml,
false,
true );
01108 partNode * dataPlain = child->findType( DwMime::kTypeText,
01109 DwMime::kSubtypePlain,
false,
true );
01110
01111
if ( (mReader && mReader->htmlMail() && dataHtml) ||
01112 (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01113
if ( dataPlain )
01114 dataPlain->setProcessed(
true,
false );
01115 stdChildHandling( dataHtml );
01116
return true;
01117 }
01118
01119
if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01120
if ( dataHtml )
01121 dataHtml->setProcessed(
true,
false );
01122 stdChildHandling( dataPlain );
01123
return true;
01124 }
01125
01126 stdChildHandling( child );
01127
return true;
01128 }
01129
01130
bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01131
return processMultiPartMixedSubtype( node, result );
01132 }
01133
01134
bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01135
return processMultiPartMixedSubtype( node, result );
01136 }
01137
01138
bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01139
if ( node->childCount() != 2 ) {
01140 kdDebug(5006) <<
"mulitpart/signed must have exactly two child parts!" << endl
01141 <<
"processing as multipart/mixed" << endl;
01142
if ( node->firstChild() )
01143 stdChildHandling( node->firstChild() );
01144
return node->firstChild();
01145 }
01146
01147 partNode * signedData = node->firstChild();
01148 assert( signedData );
01149
01150 partNode * signature = signedData->nextSibling();
01151 assert( signature );
01152
01153 signature->setProcessed(
true,
true );
01154
01155
if ( !includeSignatures() ) {
01156 stdChildHandling( signedData );
01157
return true;
01158 }
01159
01160
01161
01162
01163
01164 CryptPlugWrapper * cpw =
01165 CryptPlugFactory::instance()->createForProtocol( node->contentTypeParameter(
"protocol" ) );
01166
01167
if ( !cpw ) {
01168 signature->setProcessed(
true,
true );
01169 stdChildHandling( signedData );
01170
return true;
01171 }
01172
01173 CryptPlugWrapperSaver saver(
this, cpw );
01174
01175 node->setSignatureState( KMMsgFullySigned );
01176 writeOpaqueOrMultipartSignedData( signedData, *signature,
01177 node->trueFromAddress() );
01178
return true;
01179 }
01180
01181
bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01182 partNode * child = node->firstChild();
01183
if ( !child )
01184
return false;
01185
01186
if ( keepEncryptions() ) {
01187 node->setEncryptionState( KMMsgFullyEncrypted );
01188
const QCString cstr = node->msgPart().bodyDecoded();
01189
if ( mReader )
01190 writeBodyString( cstr, node->trueFromAddress(),
01191 codecFor( node ), result );
01192 mRawReplyString += cstr;
01193
return true;
01194 }
01195
01196 CryptPlugWrapper * useThisCryptPlug = 0;
01197
01198
01199
01200
01201 partNode * data = child->findType( DwMime::kTypeApplication,
01202 DwMime::kSubtypeOctetStream,
false,
true );
01203
if ( data ) {
01204 useThisCryptPlug = KMail::CryptPlugFactory::instance()->openpgp();
01205 }
01206
if ( !data ) {
01207 data = child->findType( DwMime::kTypeApplication,
01208 DwMime::kSubtypePkcs7Mime,
false,
true );
01209
if ( data ) {
01210 useThisCryptPlug = KMail::CryptPlugFactory::instance()->smime();
01211 }
01212 }
01213
01214
01215
01216
01217
if ( !data ) {
01218 stdChildHandling( child );
01219
return true;
01220 }
01221
01222 CryptPlugWrapperSaver cpws(
this, useThisCryptPlug );
01223
01224
if ( partNode * dataChild = data->firstChild() ) {
01225 kdDebug(5006) <<
"\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
01226 stdChildHandling( dataChild );
01227 kdDebug(5006) <<
"\n-----> Returning from parseObjectTree( curNode->mChild )\n" << endl;
01228
return true;
01229 }
01230
01231 kdDebug(5006) <<
"\n-----> Initially processing encrypted data\n" << endl;
01232 PartMetaData messagePart;
01233 node->setEncryptionState( KMMsgFullyEncrypted );
01234
QCString decryptedData;
01235
bool signatureFound;
01236 CryptPlug::SignatureMetaData sigMeta;
01237 sigMeta.status = 0;
01238 sigMeta.extended_info = 0;
01239 sigMeta.extended_info_count = 0;
01240
bool passphraseError;
01241
01242
bool bOkDecrypt = okDecryptMIME( *data,
01243 decryptedData,
01244 signatureFound,
01245 sigMeta,
01246
true,
01247 passphraseError,
01248 messagePart.errorText );
01249
01250
01251
if ( mReader ) {
01252 messagePart.isDecryptable = bOkDecrypt;
01253 messagePart.isEncrypted =
true;
01254 messagePart.isSigned =
false;
01255 htmlWriter()->queue( writeSigstatHeader( messagePart,
01256 cryptPlugWrapper(),
01257 node->trueFromAddress() ) );
01258 }
01259
01260
if ( bOkDecrypt ) {
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
if ( signatureFound ) {
01273 writeOpaqueOrMultipartSignedData( 0,
01274 *node,
01275 node->trueFromAddress(),
01276
false,
01277 &decryptedData,
01278 &sigMeta,
01279
false );
01280 node->setSignatureState( KMMsgFullySigned );
01281 }
else {
01282 insertAndParseNewChildNode( *node,
01283 &*decryptedData,
01284
"encrypted data" );
01285 }
01286 }
else {
01287 mRawReplyString += decryptedData;
01288
if ( mReader ) {
01289
01290
01291 htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01292 }
01293 }
01294
01295
if ( mReader )
01296 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01297 data->setProcessed(
true,
false );
01298
return true;
01299 }
01300
01301
01302
bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01303
if ( mReader
01304 && !attachmentStrategy()->inlineNestedMessages()
01305 && !showOnlyOneMimePart() )
01306
return false;
01307
01308
if ( partNode * child = node->firstChild() ) {
01309 kdDebug(5006) <<
"\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
01310 ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01311 otp.parseObjectTree( child );
01312 mRawReplyString += otp.rawReplyString();
01313 mTextualContent += otp.textualContent();
01314
if ( !otp.textualContentCharset().isEmpty() )
01315 mTextualContentCharset = otp.textualContentCharset();
01316 kdDebug(5006) <<
"\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
01317
return true;
01318 }
01319 kdDebug(5006) <<
"\n-----> Initially processing data of embedded RfC 822 message\n" << endl;
01320
01321 PartMetaData messagePart;
01322
if ( mReader ) {
01323 messagePart.isEncrypted =
false;
01324 messagePart.isSigned =
false;
01325 messagePart.isEncapsulatedRfc822Message =
true;
01326
QString filename =
01327 mReader->writeMessagePartToTempFile( &node->msgPart(),
01328 node->nodeId() );
01329 htmlWriter()->queue( writeSigstatHeader( messagePart,
01330 cryptPlugWrapper(),
01331 node->trueFromAddress(),
01332 filename ) );
01333 }
01334
QCString rfc822messageStr( node->msgPart().bodyDecoded() );
01335
01336 DwMessage* rfc822DwMessage = 0;
01337
if ( node->dwPart()->Body().Message() )
01338 rfc822DwMessage =
new DwMessage( *(node->dwPart()->Body().Message()) );
01339
else
01340 {
01341 rfc822DwMessage =
new DwMessage();
01342 rfc822DwMessage->FromString( rfc822messageStr );
01343 rfc822DwMessage->Parse();
01344 }
01345 KMMessage rfc822message( rfc822DwMessage );
01346 node->setFromAddress( rfc822message.from() );
01347 kdDebug(5006) <<
"\n-----> Store RfC 822 message header \"From: " << rfc822message.from() <<
"\"\n" << endl;
01348
if ( mReader )
01349 htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01350
01351
01352 insertAndParseNewChildNode( *node,
01353 &*rfc822messageStr,
01354
"encapsulated message" );
01355
if ( mReader )
01356 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01357
return true;
01358 }
01359
01360
01361
bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01362
if ( partNode * child = node->firstChild() ) {
01363 kdDebug(5006) <<
"\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
01364 ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01365 otp.parseObjectTree( child );
01366 mRawReplyString += otp.rawReplyString();
01367 mTextualContent += otp.textualContent();
01368
if ( !otp.textualContentCharset().isEmpty() )
01369 mTextualContentCharset = otp.textualContentCharset();
01370 kdDebug(5006) <<
"\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
01371
return true;
01372 }
01373
01374 CryptPlugWrapper* oldUseThisCryptPlug = cryptPlugWrapper();
01375
if ( node->parentNode()
01376 && DwMime::kTypeMultipart == node->parentNode()->type()
01377 && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01378 kdDebug(5006) <<
"\n-----> Initially processing encrypted data\n" << endl;
01379 node->setEncryptionState( KMMsgFullyEncrypted );
01380
if ( keepEncryptions() ) {
01381
const QCString cstr = node->msgPart().bodyDecoded();
01382
if ( mReader )
01383 writeBodyString( cstr, node->trueFromAddress(),
01384 codecFor( node ), result );
01385 mRawReplyString += cstr;
01386 }
else {
01387
01388
01389
01390 PartMetaData messagePart;
01391 setCryptPlugWrapper( KMail::CryptPlugFactory::instance()->openpgp() );
01392
QCString decryptedData;
01393
bool signatureFound;
01394 CryptPlug::SignatureMetaData sigMeta;
01395 sigMeta.status = 0;
01396 sigMeta.extended_info = 0;
01397 sigMeta.extended_info_count = 0;
01398
bool passphraseError;
01399
01400
bool bOkDecrypt = okDecryptMIME( *node,
01401 decryptedData,
01402 signatureFound,
01403 sigMeta,
01404
true,
01405 passphraseError,
01406 messagePart.errorText );
01407
01408
01409
if ( mReader ) {
01410 messagePart.isDecryptable = bOkDecrypt;
01411 messagePart.isEncrypted =
true;
01412 messagePart.isSigned =
false;
01413 htmlWriter()->queue( writeSigstatHeader( messagePart,
01414 cryptPlugWrapper(),
01415 node->trueFromAddress() ) );
01416 }
01417
01418
if ( bOkDecrypt ) {
01419
01420 insertAndParseNewChildNode( *node,
01421 &*decryptedData,
01422
"encrypted data" );
01423 }
else {
01424 mRawReplyString += decryptedData;
01425
if ( mReader ) {
01426
01427
01428 htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01429 }
01430 }
01431
01432
if ( mReader )
01433 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01434 }
01435
return true;
01436 }
01437 setCryptPlugWrapper( oldUseThisCryptPlug );
01438
return false;
01439 }
01440
01441
bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01442
if ( partNode * child = node->firstChild() ) {
01443 kdDebug(5006) <<
"\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
01444 ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01445 otp.parseObjectTree( child );
01446 mRawReplyString += otp.rawReplyString();
01447 mTextualContent += otp.textualContent();
01448
if ( !otp.textualContentCharset().isEmpty() )
01449 mTextualContentCharset = otp.textualContentCharset();
01450 kdDebug(5006) <<
"\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
01451
return true;
01452 }
01453
01454 kdDebug(5006) <<
"\n-----> Initially processing signed and/or encrypted data\n" << endl;
01455
if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01456
return false;
01457
01458 CryptPlugWrapper * smimeCrypto = CryptPlugFactory::instance()->smime();
01459
01460
const QString smimeType = node->contentTypeParameter(
"smime-type").lower();
01461
01462
if ( smimeType ==
"certs-only" ) {
01463 result.setNeverDisplayInline(
true );
01464
if ( !smimeCrypto )
01465
return false;
01466
01467
const KConfigGroup reader( KMKernel::config(),
"Reader" );
01468
if ( !reader.readBoolEntry(
"AutoImportKeys",
false ) )
01469
return false;
01470
01471
const QByteArray certData = node->msgPart().bodyDecodedBinary();
01472
01473
const GpgME::ImportResult res
01474 = smimeCrypto->importCertificate( certData.data(), certData.size() );
01475
if ( res.error() ) {
01476 htmlWriter()->queue( i18n(
"Sorry, certificate could not be imported.<br>"
01477
"Reason: %1").arg( QString::fromLocal8Bit( res.error().asString() ) ) );
01478
return true;
01479 }
01480
01481
const int nImp = res.numImported();
01482
const int nUnc = res.numUnchanged();
01483
const int nSKImp = res.numSecretKeysImported();
01484
const int nSKUnc = res.numSecretKeysUnchanged();
01485
if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01486 htmlWriter()->queue( i18n(
"Sorry, no certificates were found in this message." ) );
01487
return true;
01488 }
01489
QString comment =
"<b>" + i18n(
"Certificate import status:" ) +
"</b><br> <br>";
01490
if ( nImp )
01491 comment += i18n(
"1 new certificate was imported.",
01492
"%n new certificates were imported.", nImp ) +
"<br>";
01493
if ( nUnc )
01494 comment += i18n(
"1 certificate was unchanged.",
01495
"%n certificates were unchanged.", nUnc ) +
"<br>";
01496
if ( nSKImp )
01497 comment += i18n(
"1 new secret key was imported.",
01498
"%n new secret keys were imported.", nSKImp ) +
"<br>";
01499
if ( nSKUnc )
01500 comment += i18n(
"1 secret key was unchanged.",
01501
"%n secret keys were unchanged.", nSKUnc ) +
"<br>";
01502 comment +=
" <br>";
01503 htmlWriter()->queue( comment );
01504
if ( !nImp && !nSKImp ) {
01505 htmlWriter()->queue(
"<hr>" );
01506
return true;
01507 }
01508
const std::vector<GpgME::Import> imports = res.imports();
01509
if ( imports.empty() ) {
01510 htmlWriter()->queue( i18n(
"Sorry, no details on certificate import available." ) +
"<hr>" );
01511
return true;
01512 }
01513 htmlWriter()->queue(
"<b>" + i18n(
"Certificate import details:" ) +
"</b><br>" );
01514
for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01515
if ( (*it).error() )
01516 htmlWriter()->queue( i18n(
"Failed: %1 (%2)" ).arg( (*it).fingerprint() )
01517 .arg( QString::fromLocal8Bit( (*it).error().asString() ) ) );
01518
else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey )
01519
if ( (*it).status() & GpgME::Import::ContainedSecretKey )
01520 htmlWriter()->queue( i18n(
"New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01521
else
01522 htmlWriter()->queue( i18n(
"New or changed: %1" ).arg( (*it).fingerprint() ) );
01523 htmlWriter()->queue(
"<br>" );
01524 }
01525
01526 htmlWriter()->queue(
"<hr>" );
01527
return true;
01528 }
01529
01530
if ( !smimeCrypto )
01531
return false;
01532 CryptPlugWrapperSaver cpws(
this, smimeCrypto );
01533
01534
bool isSigned = smimeType ==
"signed-data";
01535
bool isEncrypted = smimeType ==
"enveloped-data";
01536
01537
01538
01539
01540 partNode* signTestNode = isEncrypted ? 0 : node;
01541
01542
01543
01544
01545
01546
if ( !isSigned ) {
01547
if ( isEncrypted )
01548 kdDebug(5006) <<
"pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl;
01549
else
01550 kdDebug(5006) <<
"pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl;
01551
QCString decryptedData;
01552 PartMetaData messagePart;
01553 messagePart.isEncrypted =
true;
01554 messagePart.isSigned =
false;
01555
bool signatureFound;
01556 CryptPlug::SignatureMetaData sigMeta;
01557 sigMeta.status = 0;
01558 sigMeta.extended_info = 0;
01559 sigMeta.extended_info_count = 0;
01560
bool passphraseError;
01561
01562
if ( okDecryptMIME( *node,
01563 decryptedData,
01564 signatureFound,
01565 sigMeta,
01566
false,
01567 passphraseError,
01568 messagePart.errorText ) ) {
01569 kdDebug(5006) <<
"pkcs7 mime - encryption found - enveloped (encrypted) data !" << endl;
01570 isEncrypted =
true;
01571 node->setEncryptionState( KMMsgFullyEncrypted );
01572 signTestNode = 0;
01573
01574 messagePart.isDecryptable =
true;
01575
if ( mReader )
01576 htmlWriter()->queue( writeSigstatHeader( messagePart,
01577 cryptPlugWrapper(),
01578 node->trueFromAddress() ) );
01579 insertAndParseNewChildNode( *node,
01580 &*decryptedData,
01581
"encrypted data" );
01582
if ( mReader )
01583 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01584 }
else {
01585
01586
if ( passphraseError ) {
01587 isEncrypted =
true;
01588 signTestNode = 0;
01589 }
01590
01591
if ( isEncrypted ) {
01592 kdDebug(5006) <<
"pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !" << endl;
01593
01594 messagePart.isDecryptable =
false;
01595
if ( mReader ) {
01596 htmlWriter()->queue( writeSigstatHeader( messagePart,
01597 cryptPlugWrapper(),
01598 node->trueFromAddress() ) );
01599 writePartIcon( &node->msgPart(), node->nodeId() );
01600 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01601 }
01602 }
else {
01603 kdDebug(5006) <<
"pkcs7 mime - NO encryption found" << endl;
01604 }
01605 }
01606
if ( isEncrypted )
01607 node->setEncryptionState( KMMsgFullyEncrypted );
01608 }
01609
01610
01611
if ( signTestNode ) {
01612
if ( isSigned )
01613 kdDebug(5006) <<
"pkcs7 mime == S/MIME TYPE: opaque signed data" << endl;
01614
else
01615 kdDebug(5006) <<
"pkcs7 mime - type unknown - opaque signed data ?" << endl;
01616
01617
bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01618 *signTestNode,
01619 node->trueFromAddress(),
01620
true,
01621 0,
01622 0,
01623 isEncrypted );
01624
if ( sigFound ) {
01625
if ( !isSigned ) {
01626 kdDebug(5006) <<
"pkcs7 mime - signature found - opaque signed data !" << endl;
01627 isSigned =
true;
01628 }
01629 signTestNode->setSignatureState( KMMsgFullySigned );
01630
if ( signTestNode != node )
01631 node->setSignatureState( KMMsgFullySigned );
01632 }
else {
01633 kdDebug(5006) <<
"pkcs7 mime - NO signature found :-(" << endl;
01634 }
01635 }
01636
01637
return isSigned || isEncrypted;
01638 }
01639
01640
bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode * curNode,
01641 ProcessResult & )
01642 {
01643
01644
if ( !mReader )
01645
return false;
01646
01647
QByteArray theBody = curNode->msgPart().bodyDecodedBinary();
01648
QString html = kmkernel->groupware().msTNEFToHTML( theBody );
01649
if( !html.isEmpty() && theBody.size() > 0 )
01650 htmlWriter()->queue( html );
01651
else
01652
return false;
01653
01654
return true;
01655 }
01656
01657
01658
void ObjectTreeParser::writeBodyString(
const QCString & bodyString,
01659
const QString & fromAddress,
01660
const QTextCodec * codec,
01661 ProcessResult & result ) {
01662 assert( mReader ); assert( codec );
01663 KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
01664 KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
01665 writeBodyStr( bodyString, codec, fromAddress,
01666 inlineSignatureState, inlineEncryptionState );
01667 result.setInlineSignatureState( inlineSignatureState );
01668 result.setInlineEncryptionState( inlineEncryptionState );
01669 }
01670
01671
void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart,
int partNum,
bool inlineImage ) {
01672
if ( !mReader || !msgPart )
01673
return;
01674
01675 kdDebug(5006) <<
"writePartIcon: PartNum: " << partNum << endl;
01676
01677
QString label = msgPart->fileName();
01678
if( label.isEmpty() )
01679 label = msgPart->name();
01680
if( label.isEmpty() )
01681 label =
"unnamed";
01682 label = KMMessage::quoteHtmlChars( label,
true );
01683
01684
QString comment = msgPart->contentDescription();
01685 comment = KMMessage::quoteHtmlChars( comment,
true );
01686
01687
QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
01688
01689
QString href = fileName.isEmpty() ?
01690
"part://" + QString::number( partNum + 1 ) :
01691
"file:" + KURL::encode_string( fileName ) ;
01692
01693
QString iconName;
01694
if( inlineImage )
01695 iconName = href;
01696
else {
01697 iconName = msgPart->iconName();
01698
if( iconName.right( 14 ) ==
"mime_empty.png" ) {
01699 msgPart->magicSetType();
01700 iconName = msgPart->iconName();
01701 }
01702 }
01703
01704
if( inlineImage )
01705
01706 htmlWriter()->queue(
"<div><a href=\"" + href +
"\">"
01707
"<img src=\"" + iconName +
"\" border=\"0\"></a>"
01708
"</div>"
01709
"<div><a href=\"" + href +
"\">" + label +
"</a>"
01710
"</div>"
01711
"<div>" + comment +
"</div><br>" );
01712
else
01713
01714 htmlWriter()->queue(
"<div><a href=\"" + href +
"\"><img src=\"" +
01715 iconName +
"\" border=\"0\">" + label +
01716
"</a></div>"
01717
"<div>" + comment +
"</div><br>" );
01718 }
01719
01720
#define SIG_FRAME_COL_UNDEF 99
01721
#define SIG_FRAME_COL_RED -1
01722
#define SIG_FRAME_COL_YELLOW 0
01723
#define SIG_FRAME_COL_GREEN 1
01724
QString ObjectTreeParser::sigStatusToString( CryptPlugWrapper* cryptPlug,
01725
int status_code,
01726 CryptPlugWrapper::SigStatusFlags statusFlags,
01727
int& frameColor,
01728
bool& showKeyInfos )
01729 {
01730
01731
01732
01733 showKeyInfos =
true;
01734
QString result;
01735
if( cryptPlug ) {
01736
if( cryptPlug->protocol() ==
"openpgp" ) {
01737
01738
01739
switch( status_code ) {
01740
case 0:
01741 result = i18n(
"Error: Signature not verified");
01742
break;
01743
case 1:
01744 result = i18n(
"Good signature");
01745
break;
01746
case 2:
01747 result = i18n(
"<b>Bad</b> signature");
01748
break;
01749
case 3:
01750 result = i18n(
"No public key to verify the signature");
01751
break;
01752
case 4:
01753 result = i18n(
"No signature found");
01754
break;
01755
case 5:
01756 result = i18n(
"Error verifying the signature");
01757
break;
01758
case 6:
01759 result = i18n(
"Different results for signatures");
01760
break;
01761
01762
01763
01764
01765
01766
01767
01768
01769
default:
01770 result =
"";
01771
break;
01772 }
01773 }
01774
else if( cryptPlug->protocol() ==
"smime" ) {
01775
01776
01777
01778
if( CryptPlugWrapper::SigStatus_UNKNOWN == statusFlags ) {
01779 result = i18n(
"No status information available.");
01780 frameColor = SIG_FRAME_COL_YELLOW;
01781 showKeyInfos =
false;
01782
return result;
01783 }
01784
01785
if( CryptPlugWrapper::SigStatus_VALID & statusFlags ) {
01786 result = i18n(
"Good signature.");
01787
01788
01789
01790
01791
01792
01793
01794
01795 frameColor = SIG_FRAME_COL_GREEN;
01796 showKeyInfos =
false;
01797
return result;
01798 }
01799
01800
01801
01802
01803 frameColor = SIG_FRAME_COL_GREEN;
01804
QString result2;
01805
if( CryptPlugWrapper::SigStatus_KEY_EXPIRED & statusFlags ){
01806
01807 result2 += i18n(
"One key has expired.");
01808 }
01809
if( CryptPlugWrapper::SigStatus_SIG_EXPIRED & statusFlags ){
01810
01811 result2 += i18n(
"The signature has expired.");
01812 }
01813
01814
01815
if( CryptPlugWrapper::SigStatus_KEY_MISSING & statusFlags ) {
01816 result2 += i18n(
"Unable to verify: key missing.");
01817
01818
01819 showKeyInfos =
false;
01820 frameColor = SIG_FRAME_COL_YELLOW;
01821 }
01822
if( CryptPlugWrapper::SigStatus_CRL_MISSING & statusFlags ){
01823 result2 += i18n(
"CRL not available.");
01824 frameColor = SIG_FRAME_COL_YELLOW;
01825 }
01826
if( CryptPlugWrapper::SigStatus_CRL_TOO_OLD & statusFlags ){
01827 result2 += i18n(
"Available CRL is too old.");
01828 frameColor = SIG_FRAME_COL_YELLOW;
01829 }
01830
if( CryptPlugWrapper::SigStatus_BAD_POLICY & statusFlags ){
01831 result2 += i18n(
"A policy was not met.");
01832 frameColor = SIG_FRAME_COL_YELLOW;
01833 }
01834
if( CryptPlugWrapper::SigStatus_SYS_ERROR & statusFlags ){
01835 result2 += i18n(
"A system error occurred.");
01836
01837
01838
01839 showKeyInfos =
false;
01840 frameColor = SIG_FRAME_COL_YELLOW;
01841 }
01842
if( CryptPlugWrapper::SigStatus_NUMERICAL_CODE & statusFlags ) {
01843 result2 += i18n(
"Internal system error #%1 occurred.")
01844 .arg( statusFlags - CryptPlugWrapper::SigStatus_NUMERICAL_CODE );
01845
01846
01847
01848 showKeyInfos =
false;
01849 frameColor = SIG_FRAME_COL_YELLOW;
01850 }
01851
01852
01853
if( CryptPlugWrapper::SigStatus_KEY_REVOKED & statusFlags ){
01854
01855 result2 += i18n(
"One key has been revoked.");
01856 frameColor = SIG_FRAME_COL_RED;
01857 }
01858
if( CryptPlugWrapper::SigStatus_RED & statusFlags ) {
01859
if( result2.isEmpty() )
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872 showKeyInfos =
false;
01873 frameColor = SIG_FRAME_COL_RED;
01874 }
01875
else
01876 result =
"";
01877
01878
if( SIG_FRAME_COL_GREEN == frameColor ) {
01879 result = i18n(
"Good signature.");
01880 }
else if( SIG_FRAME_COL_RED == frameColor ) {
01881 result = i18n(
"<b>Bad</b> signature.");
01882 }
else
01883 result =
"";
01884
01885
if( !result2.isEmpty() ) {
01886
if( !result.isEmpty() )
01887 result.append(
"<br />");
01888 result.append( result2 );
01889 }
01890 }
01891
01892
01893
01894
01895
01896
01897 }
01898
return result;
01899 }
01900
01901
01902
QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
01903 CryptPlugWrapper * cryptPlug,
01904
const QString & fromAddress,
01905
const QString & filename )
01906 {
01907
bool isSMIME = cryptPlug && cryptPlug->protocol() ==
"smime";
01908
QString signer = block.signer;
01909
01910
QString htmlStr;
01911
QString dir = ( QApplication::reverseLayout() ?
"rtl" :
"ltr" );
01912
QString cellPadding(
"cellpadding=\"1\"");
01913
01914
if( block.isEncapsulatedRfc822Message )
01915 {
01916 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"rfc822\">"
01917
"<tr class=\"rfc822H\"><td dir=\"" + dir +
"\">";
01918
if( !filename.isEmpty() )
01919 htmlStr +=
"<a href=\"" +
QString(
"file:")
01920 + KURL::encode_string( filename ) +
"\">"
01921 + i18n(
"Encapsulated message") +
"</a>";
01922
else
01923 htmlStr += i18n(
"Encapsulated message");
01924 htmlStr +=
"</td></tr><tr class=\"rfc822B\"><td>";
01925 }
01926
01927
if( block.isEncrypted )
01928 {
01929 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"encr\">"
01930
"<tr class=\"encrH\"><td dir=\"" + dir +
"\">";
01931
if( block.isDecryptable )
01932 htmlStr += i18n(
"Encrypted message");
01933
else {
01934 htmlStr += i18n(
"Encrypted message (decryption not possible)");
01935
if( !block.errorText.isEmpty() )
01936 htmlStr +=
"<br />" + i18n(
"Reason: %1").arg( block.errorText );
01937 }
01938 htmlStr +=
"</td></tr><tr class=\"encrB\"><td>";
01939 }
01940
01941
if( block.isSigned ) {
01942
QStringList& blockAddrs( block.signerMailAddresses );
01943
01944
01945
01946
int frameColor = SIG_FRAME_COL_UNDEF;
01947
bool showKeyInfos;
01948
bool onlyShowKeyURL =
false;
01949
bool cannotCheckSignature =
true;
01950
QString statusStr = sigStatusToString( cryptPlug,
01951 block.status_code,
01952 block.sigStatusFlags,
01953 frameColor,
01954 showKeyInfos );
01955
01956
01957
if( statusStr.isEmpty() )
01958 statusStr = block.status;
01959
if( block.technicalProblem )
01960 frameColor = SIG_FRAME_COL_YELLOW;
01961
01962
switch( frameColor ){
01963
case SIG_FRAME_COL_RED:
01964 cannotCheckSignature =
false;
01965
break;
01966
case SIG_FRAME_COL_YELLOW:
01967 cannotCheckSignature =
true;
01968
break;
01969
case SIG_FRAME_COL_GREEN:
01970 cannotCheckSignature =
false;
01971
break;
01972 }
01973
01974
01975
01976
01977
01978
01979
QString startKeyHREF;
01980
if( isSMIME )
01981 startKeyHREF =
01982
QString(
"<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
01983 .arg( cryptPlug->displayName() )
01984 .arg( cryptPlug->libName() )
01985 .arg( block.keyId );
01986
QString keyWithWithoutURL
01987 = isSMIME
01988 ?
QString(
"%1%2</a>")
01989 .arg( startKeyHREF )
01990 .arg( cannotCheckSignature ? i18n(
"[Details]") : (
"0x" + block.keyId) )
01991 :
"0x" +
QString::fromUtf8( block.keyId );
01992
01993
01994
01995 showKeyInfos =
true;
01996
01997
01998
01999
02000
if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02001
02002
02003
02004
if( !statusStr.isEmpty() ) {
02005 statusStr.prepend(
"<i>");
02006 statusStr.append(
"</i>");
02007 }
02008
02009
02010
switch( frameColor ) {
02011
case SIG_FRAME_COL_RED:
02012 block.signClass =
"signErr";
02013 onlyShowKeyURL =
true;
02014
break;
02015
case SIG_FRAME_COL_YELLOW:
02016
if( block.technicalProblem )
02017 block.signClass =
"signWarn";
02018
else
02019 block.signClass =
"signOkKeyBad";
02020
break;
02021
case SIG_FRAME_COL_GREEN:
02022 block.signClass =
"signOkKeyOk";
02023
02024
02025
QString greenCaseWarning;
02026
QString msgFrom( KPIM::getEmailAddr(fromAddress) );
02027
QString certificate;
02028
if( block.keyId.isEmpty() )
02029 certificate =
"certificate";
02030
else
02031 certificate =
QString(
"%1%2</a>")
02032 .arg( startKeyHREF )
02033 .arg(
"certificate" );
02034
if( !blockAddrs.empty() ){
02035
if( blockAddrs.grep(
02036 msgFrom,
02037
false ).isEmpty() ) {
02038 greenCaseWarning =
02039
"<u>" +
02040 i18n(
"Warning:") +
02041
"</u> " +
02042 i18n(
"Sender's mail address is not stored "
02043
"in the %1 used for signing.").arg(certificate) +
02044
"<br />" +
02045 i18n(
"sender: ") +
02046 msgFrom +
02047
"<br />" +
02048 i18n(
"stored: ");
02049
02050
02051
02052
02053
bool bStart =
true;
02054
for(QStringList::ConstIterator it = blockAddrs.begin();
02055 it != blockAddrs.end(); ++it ){
02056
if( !bStart )
02057 greenCaseWarning.append(
", <br /> ");
02058 bStart =
false;
02059 greenCaseWarning.append( KPIM::getEmailAddr(*it) );
02060 }
02061 }
02062 }
else {
02063 greenCaseWarning =
02064
"<u>" +
02065 i18n(
"Warning:") +
02066
"</u> " +
02067 i18n(
"No mail address is stored in the %1 used for signing, "
02068
"so we cannot compare it to the sender's address %2.")
02069 .arg(certificate)
02070 .arg(msgFrom);
02071 }
02072
if( !greenCaseWarning.isEmpty() ) {
02073
if( !statusStr.isEmpty() )
02074 statusStr.append(
"<br /> <br />");
02075 statusStr.append( greenCaseWarning );
02076 }
02077
break;
02078 }
02079
02080 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" "
02081
"class=\"" + block.signClass +
"\">"
02082
"<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
02083
if( block.technicalProblem ) {
02084 htmlStr += block.errorText;
02085 }
02086
else if( showKeyInfos ) {
02087
if( cannotCheckSignature ) {
02088 htmlStr += i18n(
"Not enough information to check "
02089
"signature. %1" )
02090 .arg( keyWithWithoutURL );
02091 }
02092
else {
02093
02094
if (block.signer.isEmpty())
02095 signer =
"";
02096
else {
02097
if( !blockAddrs.empty() ){
02098
QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02099 signer =
"<a href=\"mailto:" + address +
"\">" + signer +
"</a>";
02100 }
02101 }
02102
02103
if( block.keyId.isEmpty() ) {
02104
if( signer.isEmpty() || onlyShowKeyURL )
02105 htmlStr += i18n(
"Message was signed with unknown key." );
02106
else
02107 htmlStr += i18n(
"Message was signed by %1." )
02108 .arg( signer );
02109 }
else {
02110
bool dateOK = ( 0 < block.creationTime.tm_year &&
02111 block.creationTime.tm_year < 3000 );
02112
QDateTime created;
02113
if ( dateOK )
02114 created.setTime_t( mktime(&block.creationTime) );
02115
if( dateOK && created.isValid() ) {
02116
if( signer.isEmpty() ) {
02117
if( onlyShowKeyURL )
02118 htmlStr += i18n(
"Message was signed with key %1." )
02119 .arg( keyWithWithoutURL );
02120
else
02121 htmlStr += i18n(
"Message was signed on %1 with key %2." )
02122 .arg( KGlobal::locale()->formatDateTime( created ) )
02123 .arg( keyWithWithoutURL );
02124 }
02125
else {
02126
if( onlyShowKeyURL )
02127 htmlStr += i18n(
"Message was signed with key %1." )
02128 .arg( keyWithWithoutURL );
02129
else
02130 htmlStr += i18n(
"Message was signed by %3 on %1 with key %2" )
02131 .arg( KGlobal::locale()->formatDateTime( created ) )
02132 .arg( keyWithWithoutURL )
02133 .arg( signer );
02134 }
02135 }
02136
else {
02137
if( signer.isEmpty() || onlyShowKeyURL )
02138 htmlStr += i18n(
"Message was signed with key %1." )
02139 .arg( keyWithWithoutURL );
02140
else
02141 htmlStr += i18n(
"Message was signed by %2 with key %1." )
02142 .arg( keyWithWithoutURL )
02143 .arg( signer );
02144 }
02145 }
02146 }
02147 htmlStr +=
"<br />";
02148
if( !statusStr.isEmpty() ) {
02149 htmlStr +=
" <br />";
02150 htmlStr += i18n(
"Status: " );
02151 htmlStr += statusStr;
02152 }
02153 }
else {
02154 htmlStr += statusStr;
02155 }
02156 htmlStr +=
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
02157
02158 }
else {
02159
02160
02161
02162
if( block.signer.isEmpty() || block.technicalProblem ) {
02163 block.signClass =
"signWarn";
02164 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" "
02165
"class=\"" + block.signClass +
"\">"
02166
"<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
02167
if( block.technicalProblem ) {
02168 htmlStr += block.errorText;
02169 }
02170
else {
02171
if( !block.keyId.isEmpty() ) {
02172
bool dateOK = ( 0 < block.creationTime.tm_year &&
02173 block.creationTime.tm_year < 3000 );
02174
QDateTime created;
02175
if ( dateOK )
02176 created.setTime_t( mktime(&block.creationTime) );
02177
if( dateOK && created.isValid() )
02178 htmlStr += i18n(
"Message was signed on %1 with unknown key %2." )
02179 .arg( KGlobal::locale()->formatDateTime( created ) )
02180 .arg( keyWithWithoutURL );
02181
else
02182 htmlStr += i18n(
"Message was signed with unknown key %1." )
02183 .arg( keyWithWithoutURL );
02184 }
02185
else
02186 htmlStr += i18n(
"Message was signed with unknown key." );
02187 htmlStr +=
"<br />";
02188 htmlStr += i18n(
"The validity of the signature cannot be "
02189
"verified." );
02190
if( !statusStr.isEmpty() ) {
02191 htmlStr +=
"<br />";
02192 htmlStr += i18n(
"Status: " );
02193 htmlStr +=
"<i>";
02194 htmlStr += statusStr;
02195 htmlStr +=
"</i>";
02196 }
02197 }
02198 htmlStr +=
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
02199 }
02200
else
02201 {
02202
02203 signer = KMMessage::quoteHtmlChars( signer,
true );
02204 signer =
"<a href=\"mailto:" + signer +
"\">" + signer +
"</a>";
02205
02206
if (block.isGoodSignature) {
02207
if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02208 block.signClass =
"signOkKeyBad";
02209
else
02210 block.signClass =
"signOkKeyOk";
02211 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" "
02212
"class=\"" + block.signClass +
"\">"
02213
"<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
02214
if( !block.keyId.isEmpty() )
02215 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
02216 .arg( keyWithWithoutURL )
02217 .arg( signer );
02218
else
02219 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
02220 htmlStr +=
"<br />";
02221
02222
switch( block.keyTrust )
02223 {
02224
case Kpgp::KPGP_VALIDITY_UNKNOWN:
02225 htmlStr += i18n(
"The signature is valid, but the key's "
02226
"validity is unknown." );
02227
break;
02228
case Kpgp::KPGP_VALIDITY_MARGINAL:
02229 htmlStr += i18n(
"The signature is valid and the key is "
02230
"marginally trusted." );
02231
break;
02232
case Kpgp::KPGP_VALIDITY_FULL:
02233 htmlStr += i18n(
"The signature is valid and the key is "
02234
"fully trusted." );
02235
break;
02236
case Kpgp::KPGP_VALIDITY_ULTIMATE:
02237 htmlStr += i18n(
"The signature is valid and the key is "
02238
"ultimately trusted." );
02239
break;
02240
default:
02241 htmlStr += i18n(
"The signature is valid, but the key is "
02242
"untrusted." );
02243 }
02244 htmlStr +=
"</td></tr>"
02245
"<tr class=\"" + block.signClass +
"B\"><td>";
02246 }
02247
else
02248 {
02249 block.signClass =
"signErr";
02250 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" "
02251
"class=\"" + block.signClass +
"\">"
02252
"<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
02253
if( !block.keyId.isEmpty() )
02254 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
02255 .arg( keyWithWithoutURL )
02256 .arg( signer );
02257
else
02258 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
02259 htmlStr +=
"<br />";
02260 htmlStr += i18n(
"Warning: The signature is bad.");
02261 htmlStr +=
"</td></tr>"
02262
"<tr class=\"" + block.signClass +
"B\"><td>";
02263 }
02264 }
02265 }
02266 }
02267
02268
return htmlStr;
02269 }
02270
02271
QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02272 {
02273
QString dir = ( QApplication::reverseLayout() ?
"rtl" :
"ltr" );
02274
02275
QString htmlStr;
02276
02277
if (block.isSigned) {
02278 htmlStr +=
"</td></tr><tr class=\"" + block.signClass +
"H\">";
02279 htmlStr +=
"<td dir=\"" + dir +
"\">" +
02280 i18n(
"End of signed message" ) +
02281
"</td></tr></table>";
02282 }
02283
02284
if (block.isEncrypted) {
02285 htmlStr +=
"</td></tr><tr class=\"encrH\"><td dir=\"" + dir +
"\">" +
02286 i18n(
"End of encrypted message" ) +
02287
"</td></tr></table>";
02288 }
02289
02290
if( block.isEncapsulatedRfc822Message )
02291 {
02292 htmlStr +=
"</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir +
"\">" +
02293 i18n(
"End of encapsulated message" ) +
02294
"</td></tr></table>";
02295 }
02296
02297
return htmlStr;
02298 }
02299
02300
02301
void ObjectTreeParser::writeBodyStr(
const QCString& aStr,
const QTextCodec *aCodec,
02302
const QString& fromAddress )
02303 {
02304 KMMsgSignatureState dummy1;
02305 KMMsgEncryptionState dummy2;
02306 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2 );
02307 }
02308
02309
02310
void ObjectTreeParser::writeBodyStr(
const QCString& aStr,
const QTextCodec *aCodec,
02311
const QString& fromAddress,
02312 KMMsgSignatureState& inlineSignatureState,
02313 KMMsgEncryptionState& inlineEncryptionState )
02314 {
02315
bool goodSignature =
false;
02316 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02317 assert(pgp != 0);
02318
bool isPgpMessage =
false;
02319
02320
QString dir = ( QApplication::reverseLayout() ?
"rtl" :
"ltr" );
02321
QString headerStr =
QString(
"<div dir=\"%1\">").arg(dir);
02322
02323 inlineSignatureState = KMMsgNotSigned;
02324 inlineEncryptionState = KMMsgNotEncrypted;
02325
QPtrList<Kpgp::Block> pgpBlocks;
02326
QStrList nonPgpBlocks;
02327
if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02328 {
02329
bool isEncrypted =
false, isSigned =
false;
02330
bool fullySignedOrEncrypted =
true;
02331
bool firstNonPgpBlock =
true;
02332
bool couldDecrypt =
false;
02333
QString signer;
02334
QCString keyId;
02335
QString decryptionError;
02336 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02337
02338
QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02339
02340
QStrListIterator npbit( nonPgpBlocks );
02341
02342
QString htmlStr;
02343
for( ; *pbit != 0; ++pbit, ++npbit )
02344 {
02345
02346
QCString str( *npbit );
02347
if( !str.isEmpty() ) {
02348 htmlStr += quotedHTML( aCodec->toUnicode( str ) );
02349 kdDebug( 5006 ) <<
"Non-empty Non-OpenPGP block found: '" << str
02350 <<
"'" << endl;
02351
02352
02353
if( firstNonPgpBlock ) {
02354
02355
for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02356
if( *c !=
'\n' ) {
02357 fullySignedOrEncrypted =
false;
02358
break;
02359 }
02360 }
02361 }
02362
else {
02363 fullySignedOrEncrypted =
false;
02364 }
02365 }
02366 firstNonPgpBlock =
false;
02367
02368
02369
02370 Kpgp::Block* block = *pbit;
02371
if( ( block->type() == Kpgp::PgpMessageBlock &&
02372
02373 !kmkernel->contextMenuShown() ) ||
02374 ( block->type() == Kpgp::ClearsignedBlock ) )
02375 {
02376 isPgpMessage =
true;
02377
if( block->type() == Kpgp::PgpMessageBlock )
02378 {
02379
if ( mReader )
02380 emit mReader->noDrag();
02381
02382 couldDecrypt = block->decrypt();
02383 isEncrypted = block->isEncrypted();
02384
if (!couldDecrypt) {
02385 decryptionError = pgp->lastErrorMsg();
02386 }
02387 }
02388
else
02389 {
02390
02391 block->verify();
02392 }
02393
02394 isSigned = block->isSigned();
02395
if( isSigned )
02396 {
02397 keyId = block->signatureKeyId();
02398 signer = block->signatureUserId();
02399
if( !signer.isEmpty() )
02400 {
02401 goodSignature = block->goodSignature();
02402
02403
if( !keyId.isEmpty() ) {
02404 keyTrust = pgp->keyTrust( keyId );
02405 Kpgp::Key* key = pgp->publicKey( keyId );
02406
if ( key ) {
02407
02408
02409 signer = key->primaryUserID();
02410 }
02411 }
02412
else
02413
02414
02415 keyTrust = pgp->keyTrust( signer );
02416 }
02417 }
02418
02419
if( isSigned )
02420 inlineSignatureState = KMMsgPartiallySigned;
02421
if( isEncrypted )
02422 inlineEncryptionState = KMMsgPartiallyEncrypted;
02423
02424 PartMetaData messagePart;
02425
02426 messagePart.isSigned = isSigned;
02427 messagePart.technicalProblem =
false;
02428 messagePart.isGoodSignature = goodSignature;
02429 messagePart.isEncrypted = isEncrypted;
02430 messagePart.isDecryptable = couldDecrypt;
02431 messagePart.decryptionError = decryptionError;
02432 messagePart.signer = signer;
02433 messagePart.keyId = keyId;
02434 messagePart.keyTrust = keyTrust;
02435
02436 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02437
02438 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) );
02439 htmlStr += writeSigstatFooter( messagePart );
02440 }
02441
else
02442 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) );
02443 }
02444
02445
02446
QCString str( nonPgpBlocks.last() );
02447
if( !str.isEmpty() ) {
02448 htmlStr += quotedHTML( aCodec->toUnicode( str ) );
02449
02450
02451
02452
02453
02454 }
02455
if( fullySignedOrEncrypted ) {
02456
if( inlineSignatureState == KMMsgPartiallySigned )
02457 inlineSignatureState = KMMsgFullySigned;
02458
if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02459 inlineEncryptionState = KMMsgFullyEncrypted;
02460 }
02461 htmlWriter()->queue( htmlStr );
02462 }
02463
else
02464 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ) ) );
02465 }
02466
02467
QString ObjectTreeParser::quotedHTML(
const QString& s)
02468 {
02469 assert( mReader );
02470 assert( cssHelper() );
02471
02472
QString htmlStr;
02473
const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02474
QString quoteFontTag[3];
02475
for (
int i = 0 ; i < 3 ; ++i )
02476 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02477
const QString normalEndTag =
"</div>";
02478
const QString quoteEnd =
"</div>";
02479
02480
unsigned int pos, beg;
02481
const unsigned int length = s.length();
02482
02483
02484
for ( pos = 0; pos < length && s[pos] <=
' '; pos++ );
02485
while (pos > 0 && (s[pos-1] ==
' ' || s[pos-1] ==
'\t')) pos--;
02486 beg = pos;
02487
02488
int currQuoteLevel = -2;
02489
02490
while (beg<length)
02491 {
02492
QString line;
02493
02494
02495 pos = s.find(
'\n', beg, FALSE);
02496
if (pos == (
unsigned int)(-1))
02497 pos = length;
02498
02499 line = s.mid(beg,pos-beg);
02500 beg = pos+1;
02501
02502
02503
int actQuoteLevel = -1;
02504
for (
unsigned int p=0; p<line.length(); p++) {
02505
switch (line[p].latin1()) {
02506
case '>':
02507
case '|':
02508 actQuoteLevel++;
02509
break;
02510
case ' ':
02511
case '\t':
02512
case '\r':
02513
break;
02514
default:
02515 p = line.length();
02516
break;
02517 }
02518 }
02519
02520
if ( actQuoteLevel != currQuoteLevel ) {
02521
02522
if (currQuoteLevel == -1)
02523 htmlStr.append( normalEndTag );
02524
else if (currQuoteLevel >= 0)
02525 htmlStr.append( quoteEnd );
02526
02527
02528 currQuoteLevel = actQuoteLevel;
02529
if (actQuoteLevel == -1)
02530 htmlStr += normalStartTag;
02531
else
02532 htmlStr += quoteFontTag[currQuoteLevel%3];
02533 }
02534
02535
02536
02537
if( !line.replace(
'\015',
"").isEmpty() )
02538 {
02539
if( line.isRightToLeft() )
02540 htmlStr +=
QString(
"<div dir=\"rtl\">" );
02541
else
02542 htmlStr +=
QString(
"<div dir=\"ltr\">" );
02543 htmlStr += LinkLocator::convertToHtml( line,
true );
02544 htmlStr +=
QString(
"</div>" );
02545 }
02546
else
02547 htmlStr +=
"<br>";
02548 }
02549
02550
02551
if (currQuoteLevel == -1)
02552 htmlStr.append( normalEndTag );
02553
else
02554 htmlStr.append( quoteEnd );
02555
02556
02557
02558
02559
02560
return htmlStr;
02561 }
02562
02563
02564
02565
const QTextCodec * ObjectTreeParser::codecFor( partNode * node )
const {
02566 assert( node );
02567
if ( mReader && mReader->overrideCodec() )
02568
return mReader->overrideCodec();
02569
return node->msgPart().codec();
02570 }
02571
02572
#ifdef MARCS_DEBUG
02573
void ObjectTreeParser::dumpToFile(
const char * filename,
const char * start,
02574 size_t len ) {
02575 assert( filename );
02576
02577
QFile f( filename );
02578
if ( f.open( IO_WriteOnly ) ) {
02579
if ( start ) {
02580
QDataStream ds( &f );
02581 ds.writeRawBytes( start, len );
02582 }
02583 f.close();
02584 }
02585 }
02586
#endif // !NDEBUG
02587
02588
02589 }