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