kmail Library API Documentation

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