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