kmail Library API Documentation

objecttreeparser.cpp

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