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