kmail

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