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