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