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                                       bool& actuallyEncrypted,
00625                                       QString& aErrorText )
00626 {
00627   passphraseError = false;
00628   wrongKeyUsage = false;
00629   aErrorText = QString::null;
00630   bool bDecryptionOk = false;
00631   enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
00632     cryptPlugError = NO_PLUGIN;
00633 
00634   CryptPlugWrapper* cryptPlug = cryptPlugWrapper();
00635   if ( !cryptPlug )
00636     cryptPlug = CryptPlugFactory::instance()->active();
00637 
00638   QString cryptPlugLibName;
00639   if ( cryptPlug )
00640     cryptPlugLibName = cryptPlug->libName();
00641 
00642   // check whether the crypto plug-in is usable
00643   if ( cryptPlug ) {
00644     if ( cryptPlug->initStatus( 0 ) != CryptPlugWrapper::InitStatus_Ok ) {
00645       cryptPlugError = NOT_INITIALIZED;
00646       cryptPlug = 0;
00647     }
00648     else if ( !cryptPlug->hasFeature( Feature_DecryptMessages ) ) {
00649       cryptPlugError = CANT_DECRYPT;
00650       cryptPlug = 0;
00651     }
00652   }
00653 
00654   if ( cryptPlug && !kmkernel->contextMenuShown() ) {
00655     QByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00656     QCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00657     bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
00658                           (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
00659                           (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
00660     int cipherLen = ciphertext.size();
00661 
00662     dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
00663 
00664 #ifdef MARCS_DEBUG
00665     QCString deb;
00666     deb =  "\n\nE N C R Y P T E D    D A T A = ";
00667     if ( cipherIsBinary )
00668       deb += "[binary data]";
00669     else {
00670       deb += "\"";
00671       deb += cipherStr;
00672       deb += "\"";
00673     }
00674     deb += "\n\n";
00675     kdDebug(5006) << deb << endl;
00676 #endif
00677 
00678 
00679 
00680     char* cleartext = 0;
00681     const char* certificate = 0;
00682 
00683     kdDebug(5006) << "ObjectTreeParser::decryptMIME: going to call CRYPTPLUG "
00684                   << cryptPlugLibName << endl;
00685     int errId = 0;
00686     char* errTxt = 0;
00687     if ( mReader )
00688       emit mReader->noDrag(); // in case pineentry pops up, don't let kmheaders start a drag afterwards
00689     bDecryptionOk = cryptPlug->decryptAndCheckMessage( ciphertext.data(),
00690                                                        cipherIsBinary,
00691                                                        cipherLen,
00692                                                        &cleartext,
00693                                                        certificate,
00694                                                        &signatureFound,
00695                                                        &sigMeta,
00696                                                        &errId,
00697                                                        &errTxt );
00698     kdDebug(5006) << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG"
00699                   << endl;
00700     actuallyEncrypted = gpg_err_code( errId ) != GPG_ERR_NO_DATA;
00701     if ( bDecryptionOk && errId == CRYPTPLUG_ERR_WRONG_KEY_USAGE ){
00702       errId = 0;
00703       wrongKeyUsage = true;
00704     }
00705     aErrorText = CryptPlugWrapper::errorIdToText( errId, passphraseError );
00706     if ( bDecryptionOk )
00707       decryptedData = cleartext;
00708     else if ( mReader && showWarning ) {
00709       decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00710                       "padding:20pt;\">"
00711                     + i18n("Encrypted data not shown.").utf8()
00712                     + "</div>";
00713       if ( !passphraseError )
00714         aErrorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.")
00715                        .arg( cryptPlugLibName )
00716                    + "<br />"
00717                    + i18n("Error: %1").arg( aErrorText );
00718     }
00719     if ( errTxt )
00720       free( errTxt );
00721     if ( cleartext )
00722       free( cleartext );
00723   }
00724   else if ( !cryptPlug ) {
00725     decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
00726                   + i18n("Encrypted data not shown.").utf8()
00727                   + "</div>";
00728     switch ( cryptPlugError ) {
00729     case NOT_INITIALIZED:
00730       aErrorText = i18n( "Crypto plug-in \"%1\" is not initialized." )
00731                      .arg( cryptPlugLibName );
00732       break;
00733     case CANT_DECRYPT:
00734       aErrorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages." )
00735                      .arg( cryptPlugLibName );
00736       break;
00737     case NO_PLUGIN:
00738       aErrorText = i18n( "No appropriate crypto plug-in was found." );
00739       break;
00740     }
00741   }
00742   else {
00743     // ### Workaround for bug 56693 (kmail freeze with the complete desktop
00744     // ### while pinentry-qt appears)
00745     QByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00746     QCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
00747     bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
00748                           (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
00749                           (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
00750     if ( !cipherIsBinary ) {
00751       decryptedData = cipherStr;
00752     }
00753     else {
00754       decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00755                       "padding:20pt;\">"
00756                     + i18n("Encrypted data not shown.").utf8()
00757                     + "</div>";
00758     }
00759   }
00760 
00761   dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
00762 
00763   return bDecryptionOk;
00764 }
00765 
00766   bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
00767     QCString cstr( curNode->msgPart().bodyDecoded() );
00768 
00769     mRawReplyString = cstr;
00770     if ( curNode->isFirstTextPart() ) {
00771       mTextualContent += curNode->msgPart().bodyToUnicode();
00772       mTextualContentCharset = curNode->msgPart().charset();
00773     }
00774 
00775     if ( !mReader )
00776       return true;
00777 
00778     if ( curNode->isFirstTextPart() ||
00779          attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
00780          showOnlyOneMimePart() )
00781     {
00782       if ( mReader->htmlMail() ) {
00783         // ---Sven's strip </BODY> and </HTML> from end of attachment start-
00784         // We must fo this, or else we will see only 1st inlined html
00785         // attachment.  It is IMHO enough to search only for </BODY> and
00786         // put \0 there.
00787         int i = cstr.findRev("</body>", -1, false); //case insensitive
00788         if ( 0 <= i )
00789           cstr.truncate(i);
00790         else // just in case - search for </html>
00791         {
00792           i = cstr.findRev("</html>", -1, false); //case insensitive
00793           if ( 0 <= i ) cstr.truncate(i);
00794         }
00795         // ---Sven's strip </BODY> and </HTML> from end of attachment end-
00796       } else {
00797         htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
00798         htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
00799                                   "security reasons, only the raw HTML code "
00800                                   "is shown. If you trust the sender of this "
00801                                   "message then you can activate formatted "
00802                                   "HTML display for this message "
00803                                   "<a href=\"kmail:showHTML\">by clicking here</a>.") );
00804         htmlWriter()->queue( "</div><br><br>" );
00805       }
00806       htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr : KMMessage::html2source( cstr )));
00807       mReader->mColorBar->setHtmlMode();
00808       return true;
00809     }
00810     return false;
00811   }
00812 } // namespace KMail
00813 
00814 static bool isMailmanMessage( partNode * curNode ) {
00815   if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
00816     return false;
00817   DwHeaders & headers = curNode->dwPart()->Headers();
00818   if ( headers.HasField("X-Mailman-Version") )
00819     return true;
00820   if ( headers.HasField("X-Mailer") &&
00821        0 == QCString( headers.FieldBody("X-Mailer").AsString().c_str() )
00822        .find("MAILMAN", 0, false) )
00823     return true;
00824   return false;
00825 }
00826 
00827 namespace KMail {
00828 
00829   bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
00830     const QCString cstr = curNode->msgPart().bodyDecoded();
00831 
00832     //###
00833     const QCString delim1( "--__--__--\n\nMessage:");
00834     const QCString delim2( "--__--__--\r\n\r\nMessage:");
00835     const QCString delimZ2("--__--__--\n\n_____________");
00836     const QCString delimZ1("--__--__--\r\n\r\n_____________");
00837     QCString partStr, digestHeaderStr;
00838     int thisDelim = cstr.find(delim1, 0, false);
00839     if ( thisDelim == -1 )
00840       thisDelim = cstr.find(delim2, 0, false);
00841     if ( thisDelim == -1 ) {
00842       kdDebug(5006) << "        Sorry: Old style Mailman message but no delimiter found." << endl;
00843       return false;
00844     }
00845 
00846     int nextDelim = cstr.find(delim1, thisDelim+1, false);
00847     if ( -1 == nextDelim )
00848       nextDelim = cstr.find(delim2, thisDelim+1, false);
00849     if ( -1 == nextDelim )
00850       nextDelim = cstr.find(delimZ1, thisDelim+1, false);
00851     if ( -1 == nextDelim )
00852       nextDelim = cstr.find(delimZ2, thisDelim+1, false);
00853     if ( nextDelim < 0)
00854       return false;
00855 
00856     kdDebug(5006) << "        processing old style Mailman digest" << endl;
00857     //if ( curNode->mRoot )
00858     //  curNode = curNode->mRoot;
00859 
00860     // at least one message found: build a mime tree
00861     digestHeaderStr = "Content-Type=text/plain\nContent-Description=digest header\n\n";
00862     digestHeaderStr += cstr.mid( 0, thisDelim );
00863     insertAndParseNewChildNode( *curNode,
00864                                 &*digestHeaderStr,
00865                                 "Digest Header", true );
00866     //mReader->queueHtml("<br><hr><br>");
00867     // temporarily change curent node's Content-Type
00868     // to get our embedded RfC822 messages properly inserted
00869     curNode->setType(    DwMime::kTypeMultipart );
00870     curNode->setSubType( DwMime::kSubtypeDigest );
00871     while( -1 < nextDelim ){
00872       int thisEoL = cstr.find("\nMessage:", thisDelim, false);
00873       if ( -1 < thisEoL )
00874         thisDelim = thisEoL+1;
00875       else{
00876         thisEoL = cstr.find("\n_____________", thisDelim, false);
00877         if ( -1 < thisEoL )
00878           thisDelim = thisEoL+1;
00879       }
00880       thisEoL = cstr.find('\n', thisDelim);
00881       if ( -1 < thisEoL )
00882         thisDelim = thisEoL+1;
00883       else
00884         thisDelim = thisDelim+1;
00885       //while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] )
00886       //  ++thisDelim;
00887 
00888       partStr = "Content-Type=message/rfc822\nContent-Description=embedded message\n";
00889       partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
00890       QCString subject("embedded message");
00891       QCString subSearch("\nSubject:");
00892       int subPos = partStr.find(subSearch, 0, false);
00893       if ( -1 < subPos ){
00894         subject = partStr.mid(subPos+subSearch.length());
00895         thisEoL = subject.find('\n');
00896         if ( -1 < thisEoL )
00897           subject.truncate( thisEoL );
00898       }
00899       kdDebug(5006) << "        embedded message found: \"" << subject << "\"" << endl;
00900       insertAndParseNewChildNode( *curNode,
00901                                   &*partStr,
00902                                   subject, true );
00903       //mReader->queueHtml("<br><hr><br>");
00904       thisDelim = nextDelim+1;
00905       nextDelim = cstr.find(delim1, thisDelim, false);
00906       if ( -1 == nextDelim )
00907         nextDelim = cstr.find(delim2, thisDelim, false);
00908       if ( -1 == nextDelim )
00909         nextDelim = cstr.find(delimZ1, thisDelim, false);
00910       if ( -1 == nextDelim )
00911         nextDelim = cstr.find(delimZ2, thisDelim, false);
00912     }
00913     // reset curent node's Content-Type
00914     curNode->setType(    DwMime::kTypeText );
00915     curNode->setSubType( DwMime::kSubtypePlain );
00916     int thisEoL = cstr.find("_____________", thisDelim);
00917     if ( -1 < thisEoL ){
00918       thisDelim = thisEoL;
00919       thisEoL = cstr.find('\n', thisDelim);
00920       if ( -1 < thisEoL )
00921         thisDelim = thisEoL+1;
00922     }
00923     else
00924       thisDelim = thisDelim+1;
00925     partStr = "Content-Type=text/plain\nContent-Description=digest footer\n\n";
00926     partStr += cstr.mid( thisDelim );
00927     insertAndParseNewChildNode( *curNode,
00928                                 &*partStr,
00929                                 "Digest Footer", true );
00930     return true;
00931   }
00932 
00933   bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
00934     const QCString cstr = curNode->msgPart().bodyDecoded();
00935     if ( !mReader ) {
00936       mRawReplyString = cstr;
00937       if ( curNode->isFirstTextPart() ) {
00938     mTextualContent += curNode->msgPart().bodyToUnicode();
00939     mTextualContentCharset = curNode->msgPart().charset();
00940       }
00941       return true;
00942     }
00943 
00944     //resultingRawData += cstr;
00945     if ( !curNode->isFirstTextPart() &&
00946          attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
00947          !showOnlyOneMimePart() )
00948       return false;
00949 
00950     mRawReplyString = cstr;
00951     if ( curNode->isFirstTextPart() ) {
00952       mTextualContent += curNode->msgPart().bodyToUnicode();
00953       mTextualContentCharset = curNode->msgPart().charset();
00954     }
00955 
00956     QString label = curNode->msgPart().fileName().stripWhiteSpace();
00957     if ( label.isEmpty() )
00958       label = curNode->msgPart().name().stripWhiteSpace();
00959 
00960     const bool bDrawFrame = !curNode->isFirstTextPart()
00961                           && !showOnlyOneMimePart()
00962                           && !label.isEmpty();
00963     if ( bDrawFrame ) {
00964       label = KMMessage::quoteHtmlChars( label, true );
00965 
00966       const QString comment =
00967         KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(), true );
00968 
00969       const QString fileName =
00970         mReader->writeMessagePartToTempFile( &curNode->msgPart(),
00971                                              curNode->nodeId() );
00972 
00973       const QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00974 
00975       QString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
00976                  "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
00977       if ( !fileName.isEmpty() )
00978         htmlStr += "<a href=\"" + QString("file:")
00979           + KURL::encode_string( fileName ) + "\">"
00980           + label + "</a>";
00981       else
00982         htmlStr += label;
00983       if ( !comment.isEmpty() )
00984         htmlStr += "<br>" + comment;
00985       htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
00986 
00987       htmlWriter()->queue( htmlStr );
00988     }
00989     // process old style not-multipart Mailman messages to
00990     // enable verification of the embedded messages' signatures
00991     if ( !isMailmanMessage( curNode ) ||
00992          !processMailmanMessage( curNode ) ) {
00993         writeBodyString( cstr, curNode->trueFromAddress(),
00994                          codecFor( curNode ), result );
00995     }
00996     if ( bDrawFrame )
00997       htmlWriter()->queue( "</td></tr></table>" );
00998 
00999     return true;
01000   }
01001 
01002   void ObjectTreeParser::stdChildHandling( partNode * child ) {
01003     if ( !child )
01004       return;
01005 
01006     ObjectTreeParser otp( *this );
01007     otp.setShowOnlyOneMimePart( false );
01008     otp.parseObjectTree( child );
01009     mRawReplyString += otp.rawReplyString();
01010     mTextualContent += otp.textualContent();
01011     if ( !otp.textualContentCharset().isEmpty() )
01012       mTextualContentCharset = otp.textualContentCharset();
01013   }
01014 
01015   bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01016     partNode * child = node->firstChild();
01017     if ( !child )
01018       return false;
01019 
01020     // normal treatment of the parts in the mp/mixed container
01021     stdChildHandling( child );
01022     return true;
01023   }
01024 
01025   bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01026     partNode * child = node->firstChild();
01027     if ( !child )
01028       return false;
01029 
01030     partNode * dataHtml = child->findType( DwMime::kTypeText,
01031                                            DwMime::kSubtypeHtml, false, true );
01032     partNode * dataPlain = child->findType( DwMime::kTypeText,
01033                                             DwMime::kSubtypePlain, false, true );
01034 
01035     if ( (mReader && mReader->htmlMail() && dataHtml) ||
01036          (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01037       if ( dataPlain )
01038         dataPlain->setProcessed( true, false );
01039       stdChildHandling( dataHtml );
01040       return true;
01041     }
01042 
01043     if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01044       if ( dataHtml )
01045         dataHtml->setProcessed( true, false );
01046       stdChildHandling( dataPlain );
01047       return true;
01048     }
01049 
01050     stdChildHandling( child );
01051     return true;
01052   }
01053 
01054   bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01055     return processMultiPartMixedSubtype( node, result );
01056   }
01057 
01058   bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01059     return processMultiPartMixedSubtype( node, result );
01060   }
01061 
01062   bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01063     if ( node->childCount() != 2 ) {
01064       kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
01065                     << "processing as multipart/mixed" << endl;
01066       if ( node->firstChild() )
01067         stdChildHandling( node->firstChild() );
01068       return node->firstChild();
01069     }
01070 
01071     partNode * signedData = node->firstChild();
01072     assert( signedData );
01073 
01074     partNode * signature = signedData->nextSibling();
01075     assert( signature );
01076 
01077     signature->setProcessed( true, true );
01078 
01079     if ( !includeSignatures() ) {
01080       stdChildHandling( signedData );
01081       return true;
01082     }
01083 
01084     // FIXME(marc) check here that the protocol parameter matches the
01085     // mimetype of "signature" (not required by the RFC, but practised
01086     // by all implementaions of security multiparts
01087 
01088     CryptPlugWrapper * cpw =
01089       CryptPlugFactory::instance()->createForProtocol( node->contentTypeParameter( "protocol" ) );
01090 
01091     if ( !cpw ) {
01092       signature->setProcessed( true, true );
01093       stdChildHandling( signedData );
01094       return true;
01095     }
01096 
01097     CryptPlugWrapperSaver saver( this, cpw );
01098 
01099     node->setSignatureState( KMMsgFullySigned );
01100     writeOpaqueOrMultipartSignedData( signedData, *signature,
01101                                       node->trueFromAddress() );
01102     return true;
01103   }
01104 
01105   bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01106     partNode * child = node->firstChild();
01107     if ( !child )
01108       return false;
01109 
01110     if ( keepEncryptions() ) {
01111       node->setEncryptionState( KMMsgFullyEncrypted );
01112       const QCString cstr = node->msgPart().bodyDecoded();
01113       if ( mReader )
01114         writeBodyString( cstr, node->trueFromAddress(),
01115                          codecFor( node ), result );
01116       mRawReplyString += cstr;
01117       return true;
01118     }
01119 
01120     CryptPlugWrapper * useThisCryptPlug = 0;
01121 
01122     /*
01123       ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
01124     */
01125     partNode * data = child->findType( DwMime::kTypeApplication,
01126                                        DwMime::kSubtypeOctetStream, false, true );
01127     if ( data ) {
01128       useThisCryptPlug = KMail::CryptPlugFactory::instance()->openpgp();
01129     }
01130     if ( !data ) {
01131       data = child->findType( DwMime::kTypeApplication,
01132                               DwMime::kSubtypePkcs7Mime, false, true );
01133       if ( data ) {
01134         useThisCryptPlug = KMail::CryptPlugFactory::instance()->smime();
01135       }
01136     }
01137     /*
01138       ---------------------------------------------------------------------------------------------------------------
01139     */
01140 
01141     if ( !data ) {
01142       stdChildHandling( child );
01143       return true;
01144     }
01145 
01146     CryptPlugWrapperSaver cpws( this, useThisCryptPlug );
01147 
01148     if ( partNode * dataChild = data->firstChild() ) {
01149       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01150       stdChildHandling( dataChild );
01151       kdDebug(5006) << "\n----->  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01152       return true;
01153     }
01154 
01155     kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01156     PartMetaData messagePart;
01157     node->setEncryptionState( KMMsgFullyEncrypted );
01158     QCString decryptedData;
01159     bool signatureFound;
01160     CryptPlug::SignatureMetaData sigMeta;
01161     sigMeta.status              = 0;
01162     sigMeta.extended_info       = 0;
01163     sigMeta.extended_info_count = 0;
01164     bool passphraseError;
01165     bool wrongKeyUsage;
01166     bool actuallyEncrypted = true;
01167 
01168     bool bOkDecrypt = okDecryptMIME( *data,
01169                                      decryptedData,
01170                                      signatureFound,
01171                                      sigMeta,
01172                                      true,
01173                                      passphraseError,
01174                                      wrongKeyUsage,
01175                                      actuallyEncrypted,
01176                                      messagePart.errorText );
01177 
01178     // paint the frame
01179     if ( mReader ) {
01180       messagePart.isDecryptable = bOkDecrypt;
01181       messagePart.isEncrypted = true;
01182       messagePart.isSigned = false;
01183       messagePart.isWrongKeyUsage = wrongKeyUsage;
01184       htmlWriter()->queue( writeSigstatHeader( messagePart,
01185                                                cryptPlugWrapper(),
01186                                                node->trueFromAddress() ) );
01187     }
01188 
01189     if ( bOkDecrypt ) {
01190       // Note: Multipart/Encrypted might also be signed
01191       //       without encapsulating a nicely formatted
01192       //       ~~~~~~~                 Multipart/Signed part.
01193       //                               (see RFC 3156 --> 6.2)
01194       // In this case we paint a _2nd_ frame inside the
01195       // encryption frame, but we do _not_ show a respective
01196       // encapsulated MIME part in the Mime Tree Viewer
01197       // since we do want to show the _true_ structure of the
01198       // message there - not the structure that the sender's
01199       // MUA 'should' have sent.  :-D       (khz, 12.09.2002)
01200       //
01201       if ( signatureFound ) {
01202         writeOpaqueOrMultipartSignedData( 0,
01203                                           *node,
01204                                           node->trueFromAddress(),
01205                                           false,
01206                                           &decryptedData,
01207                                           &sigMeta,
01208                                           false );
01209         node->setSignatureState( KMMsgFullySigned );
01210       } else {
01211         insertAndParseNewChildNode( *node,
01212                                     &*decryptedData,
01213                                     "encrypted data" );
01214       }
01215     } else {
01216       mRawReplyString += decryptedData;
01217       if ( mReader ) {
01218         // print the error message that was returned in decryptedData
01219         // (utf8-encoded)
01220         htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01221       }
01222     }
01223 
01224     if ( mReader )
01225       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01226     data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
01227     return true;
01228   }
01229 
01230 
01231   bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01232     if ( mReader
01233          && !attachmentStrategy()->inlineNestedMessages()
01234          && !showOnlyOneMimePart() )
01235       return false;
01236 
01237     if ( partNode * child = node->firstChild() ) {
01238       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01239       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01240       otp.parseObjectTree( child );
01241       mRawReplyString += otp.rawReplyString();
01242       mTextualContent += otp.textualContent();
01243       if ( !otp.textualContentCharset().isEmpty() )
01244     mTextualContentCharset = otp.textualContentCharset();
01245       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01246       return true;
01247     }
01248     kdDebug(5006) << "\n----->  Initially processing data of embedded RfC 822 message\n" << endl;
01249     // paint the frame
01250     PartMetaData messagePart;
01251     if ( mReader ) {
01252       messagePart.isEncrypted = false;
01253       messagePart.isSigned = false;
01254       messagePart.isEncapsulatedRfc822Message = true;
01255       QString filename =
01256         mReader->writeMessagePartToTempFile( &node->msgPart(),
01257                                             node->nodeId() );
01258       htmlWriter()->queue( writeSigstatHeader( messagePart,
01259                                                cryptPlugWrapper(),
01260                                                node->trueFromAddress(),
01261                                                filename ) );
01262     }
01263     QCString rfc822messageStr( node->msgPart().bodyDecoded() );
01264     // display the headers of the encapsulated message
01265     DwMessage* rfc822DwMessage = new DwMessage(); // will be deleted by c'tor of rfc822headers
01266     rfc822DwMessage->FromString( rfc822messageStr );
01267     rfc822DwMessage->Parse();
01268     KMMessage rfc822message( rfc822DwMessage );
01269     node->setFromAddress( rfc822message.from() );
01270     kdDebug(5006) << "\n----->  Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl;
01271     if ( mReader )
01272       htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01273       //mReader->parseMsgHeader( &rfc822message );
01274     // display the body of the encapsulated message
01275     insertAndParseNewChildNode( *node,
01276                                 &*rfc822messageStr,
01277                                 "encapsulated message" );
01278     if ( mReader )
01279       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01280     return true;
01281   }
01282 
01283 
01284 bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01285     if ( partNode * child = node->firstChild() ) {
01286       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01287       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01288       otp.parseObjectTree( child );
01289       mRawReplyString += otp.rawReplyString();
01290       mTextualContent += otp.textualContent();
01291       if ( !otp.textualContentCharset().isEmpty() )
01292     mTextualContentCharset = otp.textualContentCharset();
01293       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01294       return true;
01295     }
01296 
01297     CryptPlugWrapper* oldUseThisCryptPlug = cryptPlugWrapper();
01298     if (    node->parentNode()
01299             && DwMime::kTypeMultipart    == node->parentNode()->type()
01300             && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01301       kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01302       node->setEncryptionState( KMMsgFullyEncrypted );
01303       if ( keepEncryptions() ) {
01304         const QCString cstr = node->msgPart().bodyDecoded();
01305         if ( mReader )
01306           writeBodyString( cstr, node->trueFromAddress(),
01307                            codecFor( node ), result );
01308         mRawReplyString += cstr;
01309       } else {
01310         /*
01311           ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
01312         */
01313         PartMetaData messagePart;
01314         setCryptPlugWrapper( KMail::CryptPlugFactory::instance()->openpgp() );
01315         QCString decryptedData;
01316         bool signatureFound;
01317         CryptPlug::SignatureMetaData sigMeta;
01318         sigMeta.status              = 0;
01319         sigMeta.extended_info       = 0;
01320         sigMeta.extended_info_count = 0;
01321         bool passphraseError;
01322         bool wrongKeyUsage;
01323         bool actuallyEncrypted = true;
01324 
01325         bool bOkDecrypt = okDecryptMIME( *node,
01326                                          decryptedData,
01327                                          signatureFound,
01328                                          sigMeta,
01329                                          true,
01330                                          passphraseError,
01331                                          wrongKeyUsage,
01332                                          actuallyEncrypted,
01333                                          messagePart.errorText );
01334 
01335         // paint the frame
01336         if ( mReader ) {
01337           messagePart.isDecryptable = bOkDecrypt;
01338           messagePart.isEncrypted = true;
01339           messagePart.isSigned = false;
01340           messagePart.isWrongKeyUsage = wrongKeyUsage;
01341           htmlWriter()->queue( writeSigstatHeader( messagePart,
01342                                                    cryptPlugWrapper(),
01343                                                    node->trueFromAddress() ) );
01344         }
01345 
01346         if ( bOkDecrypt ) {
01347           // fixing the missing attachments bug #1090-b
01348           insertAndParseNewChildNode( *node,
01349                                       &*decryptedData,
01350                                       "encrypted data" );
01351         } else {
01352           mRawReplyString += decryptedData;
01353           if ( mReader ) {
01354             // print the error message that was returned in decryptedData
01355             // (utf8-encoded)
01356             htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01357           }
01358         }
01359 
01360         if ( mReader )
01361           htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01362       }
01363       return true;
01364     }
01365     setCryptPlugWrapper( oldUseThisCryptPlug );
01366     return false;
01367 }
01368 
01369 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01370     if ( partNode * child = node->firstChild() ) {
01371       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01372       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01373       otp.parseObjectTree( child );
01374       mRawReplyString += otp.rawReplyString();
01375       mTextualContent += otp.textualContent();
01376       if ( !otp.textualContentCharset().isEmpty() )
01377     mTextualContentCharset = otp.textualContentCharset();
01378       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01379       return true;
01380     }
01381 
01382     kdDebug(5006) << "\n----->  Initially processing signed and/or encrypted data\n" << endl;
01383     if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01384       return false;
01385 
01386     CryptPlugWrapper * smimeCrypto = CryptPlugFactory::instance()->smime();
01387 
01388     const QString smimeType = node->contentTypeParameter("smime-type").lower();
01389 
01390     if ( smimeType == "certs-only" ) {
01391       result.setNeverDisplayInline( true );
01392       if ( !smimeCrypto )
01393         return false;
01394 
01395       const KConfigGroup reader( KMKernel::config(), "Reader" );
01396       if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
01397         return false;
01398 
01399       const QByteArray certData = node->msgPart().bodyDecodedBinary();
01400 
01401       const GpgME::ImportResult res
01402         = smimeCrypto->importCertificate( certData.data(), certData.size() );
01403       if ( res.error() ) {
01404         htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
01405                                    "Reason: %1").arg( QString::fromLocal8Bit( res.error().asString() ) ) );
01406         return true;
01407       }
01408 
01409       const int nImp = res.numImported();
01410       const int nUnc = res.numUnchanged();
01411       const int nSKImp = res.numSecretKeysImported();
01412       const int nSKUnc = res.numSecretKeysUnchanged();
01413       if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01414         htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
01415         return true;
01416       }
01417       QString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br>&nbsp;<br>";
01418       if ( nImp )
01419         comment += i18n( "1 new certificate was imported.",
01420                          "%n new certificates were imported.", nImp ) + "<br>";
01421       if ( nUnc )
01422         comment += i18n( "1 certificate was unchanged.",
01423                          "%n certificates were unchanged.", nUnc ) + "<br>";
01424       if ( nSKImp )
01425         comment += i18n( "1 new secret key was imported.",
01426                          "%n new secret keys were imported.", nSKImp ) + "<br>";
01427       if ( nSKUnc )
01428         comment += i18n( "1 secret key was unchanged.",
01429                          "%n secret keys were unchanged.", nSKUnc ) + "<br>";
01430       comment += "&nbsp;<br>";
01431       htmlWriter()->queue( comment );
01432       if ( !nImp && !nSKImp ) {
01433         htmlWriter()->queue( "<hr>" );
01434         return true;
01435       }
01436       const std::vector<GpgME::Import> imports = res.imports();
01437       if ( imports.empty() ) {
01438         htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
01439         return true;
01440       }
01441       htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
01442       for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01443         if ( (*it).error() )
01444           htmlWriter()->queue( i18n( "Failed: %1 (%2)" ).arg( (*it).fingerprint() )
01445                                .arg( QString::fromLocal8Bit( (*it).error().asString() ) ) );
01446         else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey )
01447           if ( (*it).status() & GpgME::Import::ContainedSecretKey )
01448             htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01449           else
01450             htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
01451         htmlWriter()->queue( "<br>" );
01452       }
01453 
01454       htmlWriter()->queue( "<hr>" );
01455       return true;
01456     }
01457 
01458     if ( !smimeCrypto )
01459       return false;
01460     CryptPlugWrapperSaver cpws( this, smimeCrypto );
01461 
01462     bool isSigned      = smimeType == "signed-data";
01463     bool isEncrypted   = smimeType == "enveloped-data";
01464 
01465     // Analyze "signTestNode" node to find/verify a signature.
01466     // If zero this verification was successfully done after
01467     // decrypting via recursion by insertAndParseNewChildNode().
01468     partNode* signTestNode = isEncrypted ? 0 : node;
01469 
01470 
01471     // We try decrypting the content
01472     // if we either *know* that it is an encrypted message part
01473     // or there is neither signed nor encrypted parameter.
01474     if ( !isSigned ) {
01475       if ( isEncrypted )
01476         kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data" << endl;
01477       else
01478         kdDebug(5006) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?" << endl;
01479       QCString decryptedData;
01480       PartMetaData messagePart;
01481       messagePart.isEncrypted = true;
01482       messagePart.isSigned = false;
01483       bool signatureFound;
01484       CryptPlug::SignatureMetaData sigMeta;
01485       sigMeta.status              = 0;
01486       sigMeta.extended_info       = 0;
01487       sigMeta.extended_info_count = 0;
01488       bool passphraseError;
01489       bool wrongKeyUsage;
01490       bool actuallyEncrypted = true;
01491 
01492       if ( okDecryptMIME( *node,
01493                           decryptedData,
01494                           signatureFound,
01495                           sigMeta,
01496                           false,
01497                           passphraseError,
01498                           wrongKeyUsage,
01499                           actuallyEncrypted,
01500                           messagePart.errorText ) ) {
01501         kdDebug(5006) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !" << endl;
01502         isEncrypted = true;
01503         node->setEncryptionState( KMMsgFullyEncrypted );
01504         signTestNode = 0;
01505         // paint the frame
01506         messagePart.isDecryptable = true;
01507         messagePart.isWrongKeyUsage = wrongKeyUsage;
01508         if ( mReader )
01509           htmlWriter()->queue( writeSigstatHeader( messagePart,
01510                                                    cryptPlugWrapper(),
01511                                                    node->trueFromAddress() ) );
01512         insertAndParseNewChildNode( *node,
01513                                     &*decryptedData,
01514                                     "encrypted data" );
01515         if ( mReader )
01516           htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01517       } else {
01518           // decryption failed, which could be because the part was encrypted but
01519           // decryption failed, or because we didn't know if it was encrypted, tried,
01520           // and failed. If the message was not actually encrypted, we continue
01521           // assuming it's signed
01522         if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
01523           isEncrypted = true;
01524           signTestNode = 0;
01525         }
01526 
01527         if ( isEncrypted ) {
01528           kdDebug(5006) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !" << endl;
01529           // paint the frame
01530           messagePart.isDecryptable = false;
01531           if ( mReader ) {
01532             htmlWriter()->queue( writeSigstatHeader( messagePart,
01533                                                      cryptPlugWrapper(),
01534                                                      node->trueFromAddress() ) );
01535             writePartIcon( &node->msgPart(), node->nodeId() );
01536             htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01537           }
01538         } else {
01539           kdDebug(5006) << "pkcs7 mime  -  NO encryption found" << endl;
01540         }
01541       }
01542       if ( isEncrypted )
01543         node->setEncryptionState( KMMsgFullyEncrypted );
01544     }
01545 
01546     // We now try signature verification if necessarry.
01547     if ( signTestNode ) {
01548       if ( isSigned )
01549         kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data" << endl;
01550       else
01551         kdDebug(5006) << "pkcs7 mime  -  type unknown  -  opaque signed data ?" << endl;
01552 
01553       bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01554                                                         *signTestNode,
01555                                                         node->trueFromAddress(),
01556                                                         true,
01557                                                         0,
01558                                                         0,
01559                                                         isEncrypted );
01560       if ( sigFound ) {
01561         if ( !isSigned ) {
01562           kdDebug(5006) << "pkcs7 mime  -  signature found  -  opaque signed data !" << endl;
01563           isSigned = true;
01564         }
01565         signTestNode->setSignatureState( KMMsgFullySigned );
01566         if ( signTestNode != node )
01567           node->setSignatureState( KMMsgFullySigned );
01568       } else {
01569         kdDebug(5006) << "pkcs7 mime  -  NO signature found   :-(" << endl;
01570       }
01571     }
01572 
01573     return isSigned || isEncrypted;
01574 }
01575 
01576 #ifdef KLEO_CHIASMUS
01577 bool ObjectTreeParser::decryptChiasmus( const QByteArray& data, QByteArray& bodyDecoded, QString& errorText )
01578 {
01579   const Kleo::CryptoBackend::Protocol * chiasmus =
01580     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
01581   Q_ASSERT( chiasmus );
01582   if ( !chiasmus )
01583     return false;
01584 
01585   const std::auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
01586   if ( !listjob.get() ) {
01587     errorText = i18n( "Chiasmus backend does not offer the "
01588                       "\"x-obtain-keys\" function. Please report this bug." );
01589     return false;
01590   }
01591 
01592   if ( listjob->exec() ) {
01593     errorText = i18n( "Chiasmus Backend Error" );
01594     return false;
01595   }
01596 
01597   const QVariant result = listjob->property( "result" );
01598   if ( result.type() != QVariant::StringList ) {
01599     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01600                       "The \"x-obtain-keys\" function did not return a "
01601                       "string list. Please report this bug." );
01602     return false;
01603   }
01604 
01605   const QStringList keys = result.toStringList();
01606   if ( keys.empty() ) {
01607     errorText = i18n( "No keys have been found. Please check that a "
01608                       "valid key path has been set in the Chiasmus "
01609                       "configuration." );
01610     return false;
01611   }
01612 
01613   emit mReader->noDrag();
01614   ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
01615                                    keys, mReader->mChiasmusKey, mReader->mChiasmusOptions );
01616   if ( selectorDlg.exec() != QDialog::Accepted )
01617     return false;
01618 
01619   mReader->mChiasmusOptions = selectorDlg.options();
01620   mReader->mChiasmusKey = selectorDlg.key();
01621   assert( !mReader->mChiasmusKey.isEmpty() );
01622 
01623   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
01624   if ( !job ) {
01625     errorText = i18n( "Chiasmus backend does not offer the "
01626                       "\"x-decrypt\" function. Please report this bug." );
01627     return false;
01628   }
01629 
01630   if ( !job->setProperty( "key", mReader->mChiasmusKey ) ||
01631        !job->setProperty( "options", mReader->mChiasmusOptions ) ||
01632        !job->setProperty( "input", data ) ) {
01633     errorText = i18n( "The \"x-decrypt\" function does not accept "
01634                       "the expected parameters. Please report this bug." );
01635     return false;
01636   }
01637 
01638   if ( job->exec() ) {
01639     errorText = i18n( "Chiasmus Decryption Error" );
01640     return false;
01641   }
01642 
01643   const QVariant resultData = job->property( "result" );
01644   if ( resultData.type() != QVariant::ByteArray ) {
01645     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01646                       "The \"x-decrypt\" function did not return a "
01647                       "byte array. Please report this bug." );
01648     return false;
01649   }
01650   bodyDecoded = resultData.toByteArray();
01651   return true;
01652 }
01653 
01654 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
01655 {
01656   if ( !mReader ) {
01657     mRawReplyString = curNode->msgPart().bodyDecoded();
01658     mTextualContent += curNode->msgPart().bodyToUnicode();
01659     mTextualContentCharset = curNode->msgPart().charset();
01660     return true;
01661   }
01662 
01663   QByteArray decryptedBody;
01664   QString errorText;
01665   const QByteArray data = curNode->msgPart().bodyDecodedBinary();
01666   bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
01667   PartMetaData messagePart;
01668   messagePart.isDecryptable = bOkDecrypt;
01669   messagePart.isEncrypted = true;
01670   messagePart.isSigned = false;
01671   messagePart.errorText = errorText;
01672   if ( mReader )
01673     htmlWriter()->queue( writeSigstatHeader( messagePart,
01674                                              0, //cryptPlugWrapper(),
01675                                              curNode->trueFromAddress() ) );
01676   const QByteArray body = bOkDecrypt ? decryptedBody : data;
01677   const QString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
01678   const QTextCodec* aCodec = chiasmusCharset.isEmpty()
01679     ? codecFor( curNode )
01680     : KMMsgBase::codecForName( chiasmusCharset.ascii() );
01681   htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ) ) );
01682   result.setInlineEncryptionState( KMMsgFullyEncrypted );
01683   if ( mReader )
01684     htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01685   return true;
01686 }
01687 #endif
01688 
01689 void ObjectTreeParser::writeBodyString( const QCString & bodyString,
01690                                         const QString & fromAddress,
01691                                         const QTextCodec * codec,
01692                                         ProcessResult & result ) {
01693   assert( mReader ); assert( codec );
01694   KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
01695   KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
01696   writeBodyStr( bodyString, codec, fromAddress,
01697                 inlineSignatureState, inlineEncryptionState );
01698   result.setInlineSignatureState( inlineSignatureState );
01699   result.setInlineEncryptionState( inlineEncryptionState );
01700 }
01701 
01702 void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
01703   if ( !mReader || !msgPart )
01704     return;
01705 
01706   kdDebug(5006) << "writePartIcon: PartNum: " << partNum << endl;
01707 
01708   QString label = msgPart->fileName();
01709   if( label.isEmpty() )
01710     label = msgPart->name();
01711   if( label.isEmpty() )
01712     label = "unnamed";
01713   label = KMMessage::quoteHtmlChars( label, true );
01714 
01715   QString comment = msgPart->contentDescription();
01716   comment = KMMessage::quoteHtmlChars( comment, true );
01717 
01718   QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
01719 
01720   QString href = fileName.isEmpty() ?
01721     "part://" + QString::number( partNum + 1 ) :
01722     "file:" + KURL::encode_string( fileName ) ;
01723 
01724   QString iconName;
01725   if( inlineImage )
01726     iconName = href;
01727   else {
01728     iconName = msgPart->iconName();
01729     if( iconName.right( 14 ) == "mime_empty.png" ) {
01730       msgPart->magicSetType();
01731       iconName = msgPart->iconName();
01732     }
01733   }
01734 
01735   if( inlineImage )
01736     // show the filename of the image below the embedded image
01737     htmlWriter()->queue( "<div><a href=\"" + href + "\">"
01738                          "<img src=\"" + iconName + "\" border=\"0\"></a>"
01739                          "</div>"
01740                          "<div><a href=\"" + href + "\">" + label + "</a>"
01741                          "</div>"
01742                          "<div>" + comment + "</div><br>" );
01743   else
01744     // show the filename next to the image
01745     htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
01746                          iconName + "\" border=\"0\">" + label +
01747                          "</a></div>"
01748                          "<div>" + comment + "</div><br>" );
01749 }
01750 
01751 #define SIG_FRAME_COL_UNDEF  99
01752 #define SIG_FRAME_COL_RED    -1
01753 #define SIG_FRAME_COL_YELLOW  0
01754 #define SIG_FRAME_COL_GREEN   1
01755 QString ObjectTreeParser::sigStatusToString( CryptPlugWrapper* cryptPlug,
01756                                         int status_code,
01757                                         CryptPlugWrapper::SigStatusFlags statusFlags,
01758                                         int& frameColor,
01759                                         bool& showKeyInfos )
01760 {
01761     // note: At the moment frameColor and showKeyInfos are
01762     //       used for CMS only but not for PGP signatures
01763     // pending(khz): Implement usage of these for PGP sigs as well.
01764     showKeyInfos = true;
01765     QString result;
01766     if( cryptPlug ) {
01767         if( cryptPlug->protocol().lower() == "openpgp" ) {
01768             // process enum according to it's definition to be read in
01769             // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
01770             switch( status_code ) {
01771             case 0: // GPGME_SIG_STAT_NONE
01772                 result = i18n("Error: Signature not verified");
01773                 break;
01774             case 1: // GPGME_SIG_STAT_GOOD
01775                 result = i18n("Good signature");
01776                 break;
01777             case 2: // GPGME_SIG_STAT_BAD
01778                 result = i18n("<b>Bad</b> signature");
01779                 break;
01780             case 3: // GPGME_SIG_STAT_NOKEY
01781                 result = i18n("No public key to verify the signature");
01782                 break;
01783             case 4: // GPGME_SIG_STAT_NOSIG
01784                 result = i18n("No signature found");
01785                 break;
01786             case 5: // GPGME_SIG_STAT_ERROR
01787                 result = i18n("Error verifying the signature");
01788                 break;
01789             case 6: // GPGME_SIG_STAT_DIFF
01790                 result = i18n("Different results for signatures");
01791                 break;
01792             /* PENDING(khz) Verify exact meaning of the following values:
01793             case 7: // GPGME_SIG_STAT_GOOD_EXP
01794                 return i18n("Signature certificate is expired");
01795             break;
01796             case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
01797                 return i18n("One of the certificate's keys is expired");
01798             break;
01799             */
01800             default:
01801                 result = "";   // do *not* return a default text here !
01802                 break;
01803             }
01804         }
01805         else if ( cryptPlug->protocol().lower() == "smime" ) {
01806             // process status bits according to SigStatus_...
01807             // definitions in kdenetwork/libkdenetwork/cryptplug.h
01808 
01809             if( CryptPlugWrapper::SigStatus_UNKNOWN == statusFlags ) {
01810                 result = i18n("No status information available.");
01811                 frameColor = SIG_FRAME_COL_YELLOW;
01812                 showKeyInfos = false;
01813                 return result;
01814             }
01815 
01816             if( CryptPlugWrapper::SigStatus_VALID & statusFlags ) {
01817                 result = i18n("Good signature.");
01818                 // Note:
01819                 // Here we are work differently than KMail did before!
01820                 //
01821                 // The GOOD case ( == sig matching and the complete
01822                 // certificate chain was verified and is valid today )
01823                 // by definition does *not* show any key
01824                 // information but just states that things are OK.
01825                 //           (khz, according to LinuxTag 2002 meeting)
01826                 frameColor = SIG_FRAME_COL_GREEN;
01827                 showKeyInfos = false;
01828                 return result;
01829             }
01830 
01831             // we are still there?  OK, let's test the different cases:
01832 
01833             // we assume green, test for yellow or red (in this order!)
01834             frameColor = SIG_FRAME_COL_GREEN;
01835             QString result2;
01836             if( CryptPlugWrapper::SigStatus_KEY_EXPIRED & statusFlags ){
01837                 // still is green!
01838                 result2 += i18n("One key has expired.");
01839             }
01840             if( CryptPlugWrapper::SigStatus_SIG_EXPIRED & statusFlags ){
01841                 // and still is green!
01842                 result2 += i18n("The signature has expired.");
01843             }
01844 
01845             // test for yellow:
01846             if( CryptPlugWrapper::SigStatus_KEY_MISSING & statusFlags ) {
01847                 result2 += i18n("Unable to verify: key missing.");
01848                 // if the signature certificate is missing
01849                 // we cannot show infos on it
01850                 showKeyInfos = false;
01851                 frameColor = SIG_FRAME_COL_YELLOW;
01852             }
01853             if( CryptPlugWrapper::SigStatus_CRL_MISSING & statusFlags ){
01854                 result2 += i18n("CRL not available.");
01855                 frameColor = SIG_FRAME_COL_YELLOW;
01856             }
01857             if( CryptPlugWrapper::SigStatus_CRL_TOO_OLD & statusFlags ){
01858                 result2 += i18n("Available CRL is too old.");
01859                 frameColor = SIG_FRAME_COL_YELLOW;
01860             }
01861             if( CryptPlugWrapper::SigStatus_BAD_POLICY & statusFlags ){
01862                 result2 += i18n("A policy was not met.");
01863                 frameColor = SIG_FRAME_COL_YELLOW;
01864             }
01865             if( CryptPlugWrapper::SigStatus_SYS_ERROR & statusFlags ){
01866                 result2 += i18n("A system error occurred.");
01867                 // if a system error occurred
01868                 // we cannot trust any information
01869                 // that was given back by the plug-in
01870                 showKeyInfos = false;
01871                 frameColor = SIG_FRAME_COL_YELLOW;
01872             }
01873             if( CryptPlugWrapper::SigStatus_NUMERICAL_CODE & statusFlags ) {
01874                 result2 += i18n("Internal system error #%1 occurred.")
01875                         .arg( statusFlags - CryptPlugWrapper::SigStatus_NUMERICAL_CODE );
01876                 // if an unsupported internal error occurred
01877                 // we cannot trust any information
01878                 // that was given back by the plug-in
01879                 showKeyInfos = false;
01880                 frameColor = SIG_FRAME_COL_YELLOW;
01881             }
01882 
01883             // test for red:
01884             if( CryptPlugWrapper::SigStatus_KEY_REVOKED & statusFlags ){
01885                 // this is red!
01886                 result2 += i18n("One key has been revoked.");
01887                 frameColor = SIG_FRAME_COL_RED;
01888             }
01889             if( CryptPlugWrapper::SigStatus_RED & statusFlags ) {
01890                 if( result2.isEmpty() )
01891                     // Note:
01892                     // Here we are work differently than KMail did before!
01893                     //
01894                     // The BAD case ( == sig *not* matching )
01895                     // by definition does *not* show any key
01896                     // information but just states that things are BAD.
01897                     //
01898                     // The reason for this: In this case ALL information
01899                     // might be falsificated, we can NOT trust the data
01900                     // in the body NOT the signature - so we don't show
01901                     // any key/signature information at all!
01902                     //         (khz, according to LinuxTag 2002 meeting)
01903                     showKeyInfos = false;
01904                 frameColor = SIG_FRAME_COL_RED;
01905             }
01906             else
01907                 result = "";
01908 
01909             if( SIG_FRAME_COL_GREEN == frameColor ) {
01910                 result = i18n("Good signature.");
01911             } else if( SIG_FRAME_COL_RED == frameColor ) {
01912                 result = i18n("<b>Bad</b> signature.");
01913             } else
01914                 result = "";
01915 
01916             if( !result2.isEmpty() ) {
01917                 if( !result.isEmpty() )
01918                     result.append("<br />");
01919                 result.append( result2 );
01920             }
01921         }
01922         /*
01923         // add i18n support for 3rd party plug-ins here:
01924         else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
01925 
01926         }
01927         */
01928     }
01929     return result;
01930 }
01931 
01932 
01933 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
01934                                               CryptPlugWrapper * cryptPlug,
01935                                               const QString & fromAddress,
01936                                               const QString & filename )
01937 {
01938     bool isSMIME = cryptPlug && cryptPlug->protocol().lower() == "smime";
01939     QString signer = block.signer;
01940 
01941     QString htmlStr;
01942     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01943     QString cellPadding("cellpadding=\"1\"");
01944 
01945     if( block.isEncapsulatedRfc822Message )
01946     {
01947         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
01948             "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
01949         if( !filename.isEmpty() )
01950             htmlStr += "<a href=\"" + QString("file:")
01951                      + KURL::encode_string( filename ) + "\">"
01952                      + i18n("Encapsulated message") + "</a>";
01953         else
01954             htmlStr += i18n("Encapsulated message");
01955         htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
01956     }
01957 
01958     if( block.isEncrypted )
01959     {
01960         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
01961             "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
01962         if( block.isDecryptable )
01963             htmlStr += i18n("Encrypted message");
01964         else {
01965             htmlStr += i18n("Encrypted message (decryption not possible)");
01966             if( !block.errorText.isEmpty() )
01967                 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
01968         }
01969         if( block.isWrongKeyUsage )
01970           htmlStr += "<br /><br /><b>"
01971                   + i18n("Warning: Message was encrypted with an only-for-signing key.")
01972                   + "</b>";
01973         htmlStr += "</td></tr><tr class=\"encrB\"><td>";
01974     }
01975 
01976     if( block.isSigned ) {
01977         QStringList& blockAddrs( block.signerMailAddresses );
01978         // note: At the moment frameColor and showKeyInfos are
01979         //       used for CMS only but not for PGP signatures
01980         // pending(khz): Implement usage of these for PGP sigs as well.
01981         int frameColor = SIG_FRAME_COL_UNDEF;
01982         bool showKeyInfos;
01983         bool onlyShowKeyURL = false;
01984         bool cannotCheckSignature = true;
01985         QString statusStr = sigStatusToString( cryptPlug,
01986                                                block.status_code,
01987                                                block.sigStatusFlags,
01988                                                frameColor,
01989                                                showKeyInfos );
01990         // if needed fallback to english status text
01991         // that was reported by the plugin
01992         if( statusStr.isEmpty() )
01993             statusStr = block.status;
01994         if( block.technicalProblem )
01995             frameColor = SIG_FRAME_COL_YELLOW;
01996 
01997         switch( frameColor ){
01998             case SIG_FRAME_COL_RED:
01999                 cannotCheckSignature = false;
02000                 break;
02001             case SIG_FRAME_COL_YELLOW:
02002                 cannotCheckSignature = true;
02003                 break;
02004             case SIG_FRAME_COL_GREEN:
02005                 cannotCheckSignature = false;
02006                 break;
02007         }
02008 
02009         // compose the string for displaying the key ID
02010         // either as URL or not linked (for PGP)
02011         // note: Once we can start PGP key manager programs
02012         //       from within KMail we could change this and
02013         //       always show the URL.    (khz, 2002/06/27)
02014         QString startKeyHREF;
02015         if( isSMIME )
02016             startKeyHREF =
02017                 QString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02018                 .arg( cryptPlug->displayName() )
02019                 .arg( cryptPlug->libName() )
02020                 .arg( block.keyId );
02021         QString keyWithWithoutURL
02022             = isSMIME
02023             ? QString("%1%2</a>")
02024                 .arg( startKeyHREF )
02025                 .arg( cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02026             : "0x" + QString::fromUtf8( block.keyId );
02027 
02028 
02029         // temporary hack: always show key infos!
02030         showKeyInfos = true;
02031 
02032         // Sorry for using 'black' as null color but .isValid()
02033         // checking with QColor default c'tor did not work for
02034         // some reason.
02035         if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02036 
02037             // new frame settings for CMS:
02038             // beautify the status string
02039             if( !statusStr.isEmpty() ) {
02040                 statusStr.prepend("<i>");
02041                 statusStr.append( "</i>");
02042             }
02043 
02044             // special color handling: S/MIME uses only green/yellow/red.
02045             switch( frameColor ) {
02046                 case SIG_FRAME_COL_RED:
02047                     block.signClass = "signErr";//"signCMSRed";
02048                     onlyShowKeyURL = true;
02049                     break;
02050                 case SIG_FRAME_COL_YELLOW:
02051                     if( block.technicalProblem )
02052                         block.signClass = "signWarn";
02053                     else
02054                         block.signClass = "signOkKeyBad";//"signCMSYellow";
02055                     break;
02056                 case SIG_FRAME_COL_GREEN:
02057                     block.signClass = "signOkKeyOk";//"signCMSGreen";
02058                     // extra hint for green case
02059                     // that email addresses in DN do not match fromAddress
02060                     QString greenCaseWarning;
02061                     QString msgFrom( KPIM::getEmailAddr(fromAddress) );
02062                     QString certificate;
02063                     if( block.keyId.isEmpty() )
02064                         certificate = "certificate";
02065                     else
02066                         certificate = QString("%1%2</a>")
02067                                       .arg( startKeyHREF )
02068                                       .arg( "certificate" );
02069                     if( !blockAddrs.empty() ){
02070                         if( blockAddrs.grep(
02071                                 msgFrom,
02072                                 false ).isEmpty() ) {
02073                             greenCaseWarning =
02074                                 "<u>" +
02075                                 i18n("Warning:") +
02076                                 "</u> " +
02077                                 i18n("Sender's mail address is not stored "
02078                                      "in the %1 used for signing.").arg(certificate) +
02079                                 "<br />" +
02080                                 i18n("sender: ") +
02081                                 msgFrom +
02082                                 "<br />" +
02083                                 i18n("stored: ");
02084                             // We cannot use Qt's join() function here but
02085                             // have to join the addresses manually to
02086                             // extract the mail addresses (without '<''>')
02087                             // before including it into our string:
02088                             bool bStart = true;
02089                             for(QStringList::ConstIterator it = blockAddrs.begin();
02090                                 it != blockAddrs.end(); ++it ){
02091                                 if( !bStart )
02092                                     greenCaseWarning.append(", <br />&nbsp; &nbsp;");
02093                                 bStart = false;
02094                                 greenCaseWarning.append( KPIM::getEmailAddr(*it) );
02095                             }
02096                         }
02097                     } else {
02098                         greenCaseWarning =
02099                             "<u>" +
02100                             i18n("Warning:") +
02101                             "</u> " +
02102                             i18n("No mail address is stored in the %1 used for signing, "
02103                                  "so we cannot compare it to the sender's address %2.")
02104                             .arg(certificate)
02105                             .arg(msgFrom);
02106                     }
02107                     if( !greenCaseWarning.isEmpty() ) {
02108                         if( !statusStr.isEmpty() )
02109                             statusStr.append("<br />&nbsp;<br />");
02110                         statusStr.append( greenCaseWarning );
02111                     }
02112                     break;
02113             }
02114 
02115             htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02116                 "class=\"" + block.signClass + "\">"
02117                 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02118             if( block.technicalProblem ) {
02119                 htmlStr += block.errorText;
02120             }
02121             else if( showKeyInfos ) {
02122                 if( cannotCheckSignature ) {
02123                     htmlStr += i18n( "Not enough information to check "
02124                                      "signature. %1" )
02125                                 .arg( keyWithWithoutURL );
02126                 }
02127                 else {
02128 
02129                     if (block.signer.isEmpty())
02130                         signer = "";
02131                     else {
02132                         if( !blockAddrs.empty() ){
02133                             QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02134                             signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02135                         }
02136                     }
02137 
02138                     if( block.keyId.isEmpty() ) {
02139                         if( signer.isEmpty() || onlyShowKeyURL )
02140                             htmlStr += i18n( "Message was signed with unknown key." );
02141                         else
02142                             htmlStr += i18n( "Message was signed by %1." )
02143                                     .arg( signer );
02144                     } else {
02145                         bool dateOK = ( 0 < block.creationTime.tm_year &&
02146                                         block.creationTime.tm_year < 3000 );
02147                         QDateTime created;
02148                         if ( dateOK )
02149                           created.setTime_t( mktime(&block.creationTime) );
02150                         if( dateOK && created.isValid() ) {
02151                             if( signer.isEmpty() ) {
02152                                 if( onlyShowKeyURL )
02153                                     htmlStr += i18n( "Message was signed with key %1." )
02154                                                 .arg( keyWithWithoutURL );
02155                                 else
02156                                     htmlStr += i18n( "Message was signed on %1 with key %2." )
02157                                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02158                                                 .arg( keyWithWithoutURL );
02159                             }
02160                             else {
02161                                 if( onlyShowKeyURL )
02162                                     htmlStr += i18n( "Message was signed with key %1." )
02163                                             .arg( keyWithWithoutURL );
02164                                 else
02165                                     htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02166                                             .arg( KGlobal::locale()->formatDateTime( created ) )
02167                                             .arg( keyWithWithoutURL )
02168                                             .arg( signer );
02169                             }
02170                         }
02171                         else {
02172                             if( signer.isEmpty() || onlyShowKeyURL )
02173                                 htmlStr += i18n( "Message was signed with key %1." )
02174                                         .arg( keyWithWithoutURL );
02175                             else
02176                                 htmlStr += i18n( "Message was signed by %2 with key %1." )
02177                                         .arg( keyWithWithoutURL )
02178                                         .arg( signer );
02179                         }
02180                     }
02181                 }
02182                 htmlStr += "<br />";
02183                 if( !statusStr.isEmpty() ) {
02184                     htmlStr += "&nbsp;<br />";
02185                     htmlStr += i18n( "Status: " );
02186                     htmlStr += statusStr;
02187                 }
02188             } else {
02189                 htmlStr += statusStr;
02190             }
02191             htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02192 
02193         } else {
02194 
02195             // old frame settings for PGP:
02196 
02197             if( block.signer.isEmpty() || block.technicalProblem ) {
02198                 block.signClass = "signWarn";
02199                 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02200                     "class=\"" + block.signClass + "\">"
02201                     "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02202                 if( block.technicalProblem ) {
02203                     htmlStr += block.errorText;
02204                 }
02205                 else {
02206                   if( !block.keyId.isEmpty() ) {
02207                     bool dateOK = ( 0 < block.creationTime.tm_year &&
02208                                     block.creationTime.tm_year < 3000 );
02209                     QDateTime created;
02210                     if ( dateOK )
02211                       created.setTime_t( mktime(&block.creationTime) );
02212                     if( dateOK && created.isValid() )
02213                         htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02214                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02215                                 .arg( keyWithWithoutURL );
02216                     else
02217                         htmlStr += i18n( "Message was signed with unknown key %1." )
02218                                 .arg( keyWithWithoutURL );
02219                   }
02220                   else
02221                     htmlStr += i18n( "Message was signed with unknown key." );
02222                   htmlStr += "<br />";
02223                   htmlStr += i18n( "The validity of the signature cannot be "
02224                                    "verified." );
02225                   if( !statusStr.isEmpty() ) {
02226                     htmlStr += "<br />";
02227                     htmlStr += i18n( "Status: " );
02228                     htmlStr += "<i>";
02229                     htmlStr += statusStr;
02230                     htmlStr += "</i>";
02231                   }
02232                 }
02233                 htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02234             }
02235             else
02236             {
02237                 // HTMLize the signer's user id and create mailto: link
02238                 signer = KMMessage::quoteHtmlChars( signer, true );
02239                 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02240 
02241                 if (block.isGoodSignature) {
02242                     if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02243                         block.signClass = "signOkKeyBad";
02244                     else
02245                         block.signClass = "signOkKeyOk";
02246                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02247                         "class=\"" + block.signClass + "\">"
02248                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02249                     if( !block.keyId.isEmpty() )
02250                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02251                                    .arg( keyWithWithoutURL )
02252                                    .arg( signer );
02253                     else
02254                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02255                     htmlStr += "<br />";
02256 
02257                     switch( block.keyTrust )
02258                     {
02259                         case Kpgp::KPGP_VALIDITY_UNKNOWN:
02260                         htmlStr += i18n( "The signature is valid, but the key's "
02261                                 "validity is unknown." );
02262                         break;
02263                         case Kpgp::KPGP_VALIDITY_MARGINAL:
02264                         htmlStr += i18n( "The signature is valid and the key is "
02265                                 "marginally trusted." );
02266                         break;
02267                         case Kpgp::KPGP_VALIDITY_FULL:
02268                         htmlStr += i18n( "The signature is valid and the key is "
02269                                 "fully trusted." );
02270                         break;
02271                         case Kpgp::KPGP_VALIDITY_ULTIMATE:
02272                         htmlStr += i18n( "The signature is valid and the key is "
02273                                 "ultimately trusted." );
02274                         break;
02275                         default:
02276                         htmlStr += i18n( "The signature is valid, but the key is "
02277                                 "untrusted." );
02278                     }
02279                     htmlStr += "</td></tr>"
02280                         "<tr class=\"" + block.signClass + "B\"><td>";
02281                 }
02282                 else
02283                 {
02284                     block.signClass = "signErr";
02285                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02286                         "class=\"" + block.signClass + "\">"
02287                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02288                     if( !block.keyId.isEmpty() )
02289                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02290                         .arg( keyWithWithoutURL )
02291                         .arg( signer );
02292                     else
02293                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02294                     htmlStr += "<br />";
02295                     htmlStr += i18n("Warning: The signature is bad.");
02296                     htmlStr += "</td></tr>"
02297                         "<tr class=\"" + block.signClass + "B\"><td>";
02298                 }
02299             }
02300         }
02301     }
02302 
02303     return htmlStr;
02304 }
02305 
02306 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02307 {
02308     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02309 
02310     QString htmlStr;
02311 
02312     if (block.isSigned) {
02313         htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02314         htmlStr += "<td dir=\"" + dir + "\">" +
02315             i18n( "End of signed message" ) +
02316             "</td></tr></table>";
02317     }
02318 
02319     if (block.isEncrypted) {
02320         htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02321                 i18n( "End of encrypted message" ) +
02322             "</td></tr></table>";
02323     }
02324 
02325     if( block.isEncapsulatedRfc822Message )
02326     {
02327         htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02328             i18n( "End of encapsulated message" ) +
02329             "</td></tr></table>";
02330     }
02331 
02332     return htmlStr;
02333 }
02334 
02335 //-----------------------------------------------------------------------------
02336 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02337                                 const QString& fromAddress )
02338 {
02339   KMMsgSignatureState dummy1;
02340   KMMsgEncryptionState dummy2;
02341   writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2 );
02342 }
02343 
02344 //-----------------------------------------------------------------------------
02345 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02346                                 const QString& fromAddress,
02347                                 KMMsgSignatureState&  inlineSignatureState,
02348                                 KMMsgEncryptionState& inlineEncryptionState )
02349 {
02350   bool goodSignature = false;
02351   Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02352   assert(pgp != 0);
02353   bool isPgpMessage = false; // true if the message contains at least one
02354                              // PGP MESSAGE or one PGP SIGNED MESSAGE block
02355   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02356   QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02357 
02358   inlineSignatureState  = KMMsgNotSigned;
02359   inlineEncryptionState = KMMsgNotEncrypted;
02360   QPtrList<Kpgp::Block> pgpBlocks;
02361   QStrList nonPgpBlocks;
02362   if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02363   {
02364       bool isEncrypted = false, isSigned = false;
02365       bool fullySignedOrEncrypted = true;
02366       bool firstNonPgpBlock = true;
02367       bool couldDecrypt = false;
02368       QString signer;
02369       QCString keyId;
02370       QString decryptionError;
02371       Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02372 
02373       QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02374 
02375       QStrListIterator npbit( nonPgpBlocks );
02376 
02377       QString htmlStr;
02378       for( ; *pbit != 0; ++pbit, ++npbit )
02379       {
02380           // insert the next Non-OpenPGP block
02381           QCString str( *npbit );
02382           if( !str.isEmpty() ) {
02383             htmlStr += quotedHTML( aCodec->toUnicode( str ) );
02384             kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
02385                             << "'" << endl;
02386             // treat messages with empty lines before the first clearsigned
02387             // block as fully signed/encrypted
02388             if( firstNonPgpBlock ) {
02389               // check whether str only consists of \n
02390               for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02391                 if( *c != '\n' ) {
02392                   fullySignedOrEncrypted = false;
02393                   break;
02394                 }
02395               }
02396             }
02397             else {
02398               fullySignedOrEncrypted = false;
02399             }
02400           }
02401           firstNonPgpBlock = false;
02402 
02403           //htmlStr += "<br>";
02404 
02405           Kpgp::Block* block = *pbit;
02406           if( ( block->type() == Kpgp::PgpMessageBlock &&
02407                 // ### Workaround for bug 56693
02408                 !kmkernel->contextMenuShown() ) ||
02409               ( block->type() == Kpgp::ClearsignedBlock ) )
02410           {
02411               isPgpMessage = true;
02412               if( block->type() == Kpgp::PgpMessageBlock )
02413               {
02414                 if ( mReader )
02415                   emit mReader->noDrag();
02416                 // try to decrypt this OpenPGP block
02417                 couldDecrypt = block->decrypt();
02418                 isEncrypted = block->isEncrypted();
02419                 if (!couldDecrypt) {
02420                   decryptionError = pgp->lastErrorMsg();
02421                 }
02422               }
02423               else
02424               {
02425                   // try to verify this OpenPGP block
02426                   block->verify();
02427               }
02428 
02429               isSigned = block->isSigned();
02430               if( isSigned )
02431               {
02432                   keyId = block->signatureKeyId();
02433                   signer = block->signatureUserId();
02434                   if( !signer.isEmpty() )
02435                   {
02436                       goodSignature = block->goodSignature();
02437 
02438                       if( !keyId.isEmpty() ) {
02439                         keyTrust = pgp->keyTrust( keyId );
02440                         Kpgp::Key* key = pgp->publicKey( keyId );
02441                         if ( key ) {
02442                           // Use the user ID from the key because this one
02443                           // is charset safe.
02444                           signer = key->primaryUserID();
02445                         }
02446                       }
02447                       else
02448                         // This is needed for the PGP 6 support because PGP 6 doesn't
02449                         // print the key id of the signing key if the key is known.
02450                         keyTrust = pgp->keyTrust( signer );
02451                   }
02452               }
02453 
02454               if( isSigned )
02455                 inlineSignatureState = KMMsgPartiallySigned;
02456               if( isEncrypted )
02457                 inlineEncryptionState = KMMsgPartiallyEncrypted;
02458 
02459               PartMetaData messagePart;
02460 
02461               messagePart.isSigned = isSigned;
02462               messagePart.technicalProblem = false;
02463               messagePart.isGoodSignature = goodSignature;
02464               messagePart.isEncrypted = isEncrypted;
02465               messagePart.isDecryptable = couldDecrypt;
02466               messagePart.decryptionError = decryptionError;
02467               messagePart.signer = signer;
02468               messagePart.keyId = keyId;
02469               messagePart.keyTrust = keyTrust;
02470 
02471               htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02472 
02473               htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) );
02474               htmlStr += writeSigstatFooter( messagePart );
02475           }
02476           else // block is neither message block nor clearsigned block
02477             htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) );
02478       }
02479 
02480       // add the last Non-OpenPGP block
02481       QCString str( nonPgpBlocks.last() );
02482       if( !str.isEmpty() ) {
02483         htmlStr += quotedHTML( aCodec->toUnicode( str ) );
02484         // Even if the trailing Non-OpenPGP block isn't empty we still
02485         // consider the message part fully signed/encrypted because else
02486         // all inline signed mailing list messages would only be partially
02487         // signed because of the footer which is often added by the mailing
02488         // list software. IK, 2003-02-15
02489       }
02490       if( fullySignedOrEncrypted ) {
02491         if( inlineSignatureState == KMMsgPartiallySigned )
02492           inlineSignatureState = KMMsgFullySigned;
02493         if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02494           inlineEncryptionState = KMMsgFullyEncrypted;
02495       }
02496       htmlWriter()->queue( htmlStr );
02497   }
02498   else
02499     htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ) ) );
02500 }
02501 
02502 QString ObjectTreeParser::quotedHTML(const QString& s)
02503 {
02504   assert( mReader );
02505   assert( cssHelper() );
02506 
02507   QString htmlStr;
02508   const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02509   QString quoteFontTag[3];
02510   for ( int i = 0 ; i < 3 ; ++i )
02511     quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02512   const QString normalEndTag = "</div>";
02513   const QString quoteEnd = "</div>";
02514 
02515   unsigned int pos, beg;
02516   const unsigned int length = s.length();
02517 
02518   // skip leading empty lines
02519   for ( pos = 0; pos < length && s[pos] <= ' '; pos++ );
02520   while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02521   beg = pos;
02522 
02523   int currQuoteLevel = -2; // -2 == no previous lines
02524 
02525   while (beg<length)
02526   {
02527     QString line;
02528 
02529     /* search next occurrence of '\n' */
02530     pos = s.find('\n', beg, FALSE);
02531     if (pos == (unsigned int)(-1))
02532         pos = length;
02533 
02534     line = s.mid(beg,pos-beg);
02535     beg = pos+1;
02536 
02537     /* calculate line's current quoting depth */
02538     int actQuoteLevel = -1;
02539     for (unsigned int p=0; p<line.length(); p++) {
02540       switch (line[p].latin1()) {
02541         case '>':
02542         case '|':
02543           actQuoteLevel++;
02544           break;
02545         case ' ':  // spaces and tabs are allowed between the quote markers
02546         case '\t':
02547         case '\r':
02548           break;
02549         default:  // stop quoting depth calculation
02550           p = line.length();
02551           break;
02552       }
02553     } /* for() */
02554 
02555     if ( actQuoteLevel != currQuoteLevel ) {
02556       /* finish last quotelevel */
02557       if (currQuoteLevel == -1)
02558         htmlStr.append( normalEndTag );
02559       else if (currQuoteLevel >= 0)
02560         htmlStr.append( quoteEnd );
02561 
02562       /* start new quotelevel */
02563       currQuoteLevel = actQuoteLevel;
02564       if (actQuoteLevel == -1)
02565         htmlStr += normalStartTag;
02566       else
02567         htmlStr += quoteFontTag[currQuoteLevel%3];
02568     }
02569 
02570     // don't write empty <div ...></div> blocks (they have zero height)
02571     // ignore ^M DOS linebreaks
02572     if( !line.replace('\015', "").isEmpty() )
02573     {
02574       if( line.isRightToLeft() )
02575         htmlStr += QString( "<div dir=\"rtl\">" );
02576       else
02577         htmlStr += QString( "<div dir=\"ltr\">" );
02578       htmlStr += LinkLocator::convertToHtml( line, true /* preserve blanks */);
02579       htmlStr += QString( "</div>" );
02580     }
02581     else
02582       htmlStr += "<br>";
02583   } /* while() */
02584 
02585   /* really finish the last quotelevel */
02586   if (currQuoteLevel == -1)
02587      htmlStr.append( normalEndTag );
02588   else
02589      htmlStr.append( quoteEnd );
02590 
02591   //kdDebug(5006) << "KMReaderWin::quotedHTML:\n"
02592   //              << "========================================\n"
02593   //              << htmlStr
02594   //              << "\n======================================\n";
02595   return htmlStr;
02596 }
02597 
02598 
02599 
02600   const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
02601     assert( node );
02602     if ( mReader && mReader->overrideCodec() )
02603       return mReader->overrideCodec();
02604     return node->msgPart().codec();
02605   }
02606 
02607 #ifdef MARCS_DEBUG
02608   void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
02609                                      size_t len ) {
02610     assert( filename );
02611 
02612     QFile f( filename );
02613     if ( f.open( IO_WriteOnly ) ) {
02614       if ( start ) {
02615         QDataStream ds( &f );
02616         ds.writeRawBytes( start, len );
02617       }
02618       f.close();  // If data is 0 we just create a zero length file.
02619     }
02620   }
02621 #endif // !NDEBUG
02622 
02623 
02624 } // 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 Fri Dec 21 14:25:04 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003