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 = data->dwPart()->AsString().c_str();
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     const QCString cstr = curNode->msgPart().bodyDecoded();
00974     if ( !mReader ) {
00975       mRawReplyString = cstr;
00976       if ( curNode->isFirstTextPart() ) {
00977         mTextualContent += curNode->msgPart().bodyToUnicode();
00978         mTextualContentCharset = curNode->msgPart().charset();
00979       }
00980       return true;
00981     }
00982 
00983     //resultingRawData += cstr;
00984     if ( !curNode->isFirstTextPart() &&
00985          attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
00986          !showOnlyOneMimePart() )
00987       return false;
00988 
00989     mRawReplyString = cstr;
00990     if ( curNode->isFirstTextPart() ) {
00991       mTextualContent += curNode->msgPart().bodyToUnicode();
00992       mTextualContentCharset = curNode->msgPart().charset();
00993     }
00994 
00995     QString label = curNode->msgPart().fileName().stripWhiteSpace();
00996     if ( label.isEmpty() )
00997       label = curNode->msgPart().name().stripWhiteSpace();
00998 
00999     const bool bDrawFrame = !curNode->isFirstTextPart()
01000                           && !showOnlyOneMimePart()
01001                           && !label.isEmpty();
01002     if ( bDrawFrame ) {
01003       label = KMMessage::quoteHtmlChars( label, true );
01004 
01005       const QString comment =
01006         KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(), true );
01007 
01008       const QString fileName =
01009         mReader->writeMessagePartToTempFile( &curNode->msgPart(),
01010                                              curNode->nodeId() );
01011 
01012       const QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
01013 
01014       QString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
01015                  "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
01016       if ( !fileName.isEmpty() )
01017         htmlStr += "<a href=\"" + QString("file:")
01018           + KURL::encode_string( fileName ) + "\">"
01019           + label + "</a>";
01020       else
01021         htmlStr += label;
01022       if ( !comment.isEmpty() )
01023         htmlStr += "<br>" + comment;
01024       htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
01025 
01026       htmlWriter()->queue( htmlStr );
01027     }
01028     // process old style not-multipart Mailman messages to
01029     // enable verification of the embedded messages' signatures
01030     if ( !isMailmanMessage( curNode ) ||
01031          !processMailmanMessage( curNode ) )
01032       writeBodyString( cstr, curNode->trueFromAddress(),
01033                        codecFor( curNode ), result, !bDrawFrame );
01034     if ( bDrawFrame )
01035       htmlWriter()->queue( "</td></tr></table>" );
01036 
01037     return true;
01038   }
01039 
01040   void ObjectTreeParser::stdChildHandling( partNode * child ) {
01041     if ( !child )
01042       return;
01043 
01044     ObjectTreeParser otp( *this );
01045     otp.setShowOnlyOneMimePart( false );
01046     otp.parseObjectTree( child );
01047     mRawReplyString += otp.rawReplyString();
01048     mTextualContent += otp.textualContent();
01049     if ( !otp.textualContentCharset().isEmpty() )
01050       mTextualContentCharset = otp.textualContentCharset();
01051   }
01052 
01053   bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01054     partNode * child = node->firstChild();
01055     if ( !child )
01056       return false;
01057 
01058     // normal treatment of the parts in the mp/mixed container
01059     stdChildHandling( child );
01060     return true;
01061   }
01062 
01063   bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01064     partNode * child = node->firstChild();
01065     if ( !child )
01066       return false;
01067 
01068     partNode * dataHtml = child->findType( DwMime::kTypeText,
01069                                            DwMime::kSubtypeHtml, false, true );
01070     partNode * dataPlain = child->findType( DwMime::kTypeText,
01071                                             DwMime::kSubtypePlain, false, true );
01072 
01073     if ( (mReader && mReader->htmlMail() && dataHtml) ||
01074          (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01075       if ( dataPlain )
01076         dataPlain->setProcessed( true, false );
01077       stdChildHandling( dataHtml );
01078       return true;
01079     }
01080 
01081     if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01082       if ( dataHtml )
01083         dataHtml->setProcessed( true, false );
01084       stdChildHandling( dataPlain );
01085       return true;
01086     }
01087 
01088     stdChildHandling( child );
01089     return true;
01090   }
01091 
01092   bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01093     return processMultiPartMixedSubtype( node, result );
01094   }
01095 
01096   bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01097     return processMultiPartMixedSubtype( node, result );
01098   }
01099 
01100   bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01101     if ( node->childCount() != 2 ) {
01102       kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
01103                     << "processing as multipart/mixed" << endl;
01104       if ( node->firstChild() )
01105         stdChildHandling( node->firstChild() );
01106       return node->firstChild();
01107     }
01108 
01109     partNode * signedData = node->firstChild();
01110     assert( signedData );
01111 
01112     partNode * signature = signedData->nextSibling();
01113     assert( signature );
01114 
01115     signature->setProcessed( true, true );
01116 
01117     if ( !includeSignatures() ) {
01118       stdChildHandling( signedData );
01119       return true;
01120     }
01121 
01122     // FIXME(marc) check here that the protocol parameter matches the
01123     // mimetype of "signature" (not required by the RFC, but practised
01124     // by all implementaions of security multiparts
01125 
01126     CryptPlugWrapper * cpw =
01127       CryptPlugFactory::instance()->createForProtocol( node->contentTypeParameter( "protocol" ) );
01128 
01129     if ( !cpw ) {
01130       signature->setProcessed( true, true );
01131       stdChildHandling( signedData );
01132       return true;
01133     }
01134 
01135     CryptPlugWrapperSaver saver( this, cpw );
01136 
01137     node->setSignatureState( KMMsgFullySigned );
01138     writeOpaqueOrMultipartSignedData( signedData, *signature,
01139                                       node->trueFromAddress() );
01140     return true;
01141   }
01142 
01143   bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01144     partNode * child = node->firstChild();
01145     if ( !child )
01146       return false;
01147 
01148     if ( keepEncryptions() ) {
01149       node->setEncryptionState( KMMsgFullyEncrypted );
01150       const QCString cstr = node->msgPart().bodyDecoded();
01151       if ( mReader )
01152         writeBodyString( cstr, node->trueFromAddress(),
01153                          codecFor( node ), result, false );
01154       mRawReplyString += cstr;
01155       return true;
01156     }
01157 
01158     CryptPlugWrapper * useThisCryptPlug = 0;
01159 
01160     /*
01161       ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
01162     */
01163     partNode * data = child->findType( DwMime::kTypeApplication,
01164                                        DwMime::kSubtypeOctetStream, false, true );
01165     if ( data ) {
01166       useThisCryptPlug = KMail::CryptPlugFactory::instance()->openpgp();
01167     }
01168     if ( !data ) {
01169       data = child->findType( DwMime::kTypeApplication,
01170                               DwMime::kSubtypePkcs7Mime, false, true );
01171       if ( data ) {
01172         useThisCryptPlug = KMail::CryptPlugFactory::instance()->smime();
01173       }
01174     }
01175     /*
01176       ---------------------------------------------------------------------------------------------------------------
01177     */
01178 
01179     if ( !data ) {
01180       stdChildHandling( child );
01181       return true;
01182     }
01183 
01184     CryptPlugWrapperSaver cpws( this, useThisCryptPlug );
01185 
01186     if ( partNode * dataChild = data->firstChild() ) {
01187       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01188       stdChildHandling( dataChild );
01189       kdDebug(5006) << "\n----->  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01190       return true;
01191     }
01192 
01193     kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01194     PartMetaData messagePart;
01195     node->setEncryptionState( KMMsgFullyEncrypted );
01196     QCString decryptedData;
01197     bool signatureFound;
01198     CryptPlug::SignatureMetaData sigMeta;
01199     sigMeta.status              = 0;
01200     sigMeta.extended_info       = 0;
01201     sigMeta.extended_info_count = 0;
01202     bool passphraseError;
01203 
01204     bool bOkDecrypt = okDecryptMIME( *data,
01205                                      decryptedData,
01206                                      signatureFound,
01207                                      sigMeta,
01208                                      true,
01209                                      passphraseError,
01210                                      messagePart.errorText );
01211 
01212     // paint the frame
01213     if ( mReader ) {
01214       messagePart.isDecryptable = bOkDecrypt;
01215       messagePart.isEncrypted = true;
01216       messagePart.isSigned = false;
01217       htmlWriter()->queue( writeSigstatHeader( messagePart,
01218                                                cryptPlugWrapper(),
01219                                                node->trueFromAddress() ) );
01220     }
01221 
01222     if ( bOkDecrypt ) {
01223       // Note: Multipart/Encrypted might also be signed
01224       //       without encapsulating a nicely formatted
01225       //       ~~~~~~~                 Multipart/Signed part.
01226       //                               (see RFC 3156 --> 6.2)
01227       // In this case we paint a _2nd_ frame inside the
01228       // encryption frame, but we do _not_ show a respective
01229       // encapsulated MIME part in the Mime Tree Viewer
01230       // since we do want to show the _true_ structure of the
01231       // message there - not the structure that the sender's
01232       // MUA 'should' have sent.  :-D       (khz, 12.09.2002)
01233       //
01234       if ( signatureFound ) {
01235         writeOpaqueOrMultipartSignedData( 0,
01236                                           *node,
01237                                           node->trueFromAddress(),
01238                                           false,
01239                                           &decryptedData,
01240                                           &sigMeta,
01241                                           false );
01242         node->setSignatureState( KMMsgFullySigned );
01243       } else {
01244         insertAndParseNewChildNode( *node,
01245                                     &*decryptedData,
01246                                     "encrypted data" );
01247       }
01248     } else {
01249       mRawReplyString += decryptedData;
01250       if ( mReader ) {
01251         // print the error message that was returned in decryptedData
01252         // (utf8-encoded)
01253         htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01254       }
01255     }
01256 
01257     if ( mReader )
01258       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01259     data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
01260     return true;
01261   }
01262 
01263 
01264   bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01265     if ( mReader
01266          && !attachmentStrategy()->inlineNestedMessages()
01267          && !showOnlyOneMimePart() )
01268       return false;
01269 
01270     if ( partNode * child = node->firstChild() ) {
01271       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01272       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01273       otp.parseObjectTree( child );
01274       mRawReplyString += otp.rawReplyString();
01275       mTextualContent += otp.textualContent();
01276       if ( !otp.textualContentCharset().isEmpty() )
01277         mTextualContentCharset = otp.textualContentCharset();
01278       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01279       return true;
01280     }
01281     kdDebug(5006) << "\n----->  Initially processing data of embedded RfC 822 message\n" << endl;
01282     // paint the frame
01283     PartMetaData messagePart;
01284     if ( mReader ) {
01285       messagePart.isEncrypted = false;
01286       messagePart.isSigned = false;
01287       messagePart.isEncapsulatedRfc822Message = true;
01288       QString filename =
01289         mReader->writeMessagePartToTempFile( &node->msgPart(),
01290                                             node->nodeId() );
01291       htmlWriter()->queue( writeSigstatHeader( messagePart,
01292                                                cryptPlugWrapper(),
01293                                                node->trueFromAddress(),
01294                                                filename ) );
01295     }
01296     QCString rfc822messageStr( node->msgPart().bodyDecoded() );
01297     // display the headers of the encapsulated message
01298     DwMessage* rfc822DwMessage = new DwMessage(); // will be deleted by c'tor of rfc822headers
01299     rfc822DwMessage->FromString( rfc822messageStr );
01300     rfc822DwMessage->Parse();
01301     KMMessage rfc822message( rfc822DwMessage );
01302     node->setFromAddress( rfc822message.from() );
01303     kdDebug(5006) << "\n----->  Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl;
01304     if ( mReader )
01305       htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01306       //mReader->parseMsgHeader( &rfc822message );
01307     // display the body of the encapsulated message
01308     insertAndParseNewChildNode( *node,
01309                                 &*rfc822messageStr,
01310                                 "encapsulated message" );
01311     if ( mReader )
01312       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01313     return true;
01314   }
01315 
01316 
01317   bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01318     if ( partNode * child = node->firstChild() ) {
01319       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01320       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01321       otp.parseObjectTree( child );
01322       mRawReplyString += otp.rawReplyString();
01323       mTextualContent += otp.textualContent();
01324       if ( !otp.textualContentCharset().isEmpty() )
01325         mTextualContentCharset = otp.textualContentCharset();
01326       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01327       return true;
01328     }
01329 
01330     CryptPlugWrapper* oldUseThisCryptPlug = cryptPlugWrapper();
01331     if (    node->parentNode()
01332             && DwMime::kTypeMultipart    == node->parentNode()->type()
01333             && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01334       kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01335       node->setEncryptionState( KMMsgFullyEncrypted );
01336       if ( keepEncryptions() ) {
01337         const QCString cstr = node->msgPart().bodyDecoded();
01338         if ( mReader )
01339           writeBodyString( cstr, node->trueFromAddress(),
01340                            codecFor( node ), result, false );
01341         mRawReplyString += cstr;
01342       } else {
01343         /*
01344           ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
01345         */
01346         PartMetaData messagePart;
01347         setCryptPlugWrapper( KMail::CryptPlugFactory::instance()->openpgp() );
01348         QCString decryptedData;
01349         bool signatureFound;
01350         CryptPlug::SignatureMetaData sigMeta;
01351         sigMeta.status              = 0;
01352         sigMeta.extended_info       = 0;
01353         sigMeta.extended_info_count = 0;
01354         bool passphraseError;
01355 
01356         bool bOkDecrypt = okDecryptMIME( *node,
01357                                          decryptedData,
01358                                          signatureFound,
01359                                          sigMeta,
01360                                          true,
01361                                          passphraseError,
01362                                          messagePart.errorText );
01363 
01364         // paint the frame
01365         if ( mReader ) {
01366           messagePart.isDecryptable = bOkDecrypt;
01367           messagePart.isEncrypted = true;
01368           messagePart.isSigned = false;
01369           htmlWriter()->queue( writeSigstatHeader( messagePart,
01370                                                    cryptPlugWrapper(),
01371                                                    node->trueFromAddress() ) );
01372         }
01373 
01374         if ( bOkDecrypt ) {
01375           // fixing the missing attachments bug #1090-b
01376           insertAndParseNewChildNode( *node,
01377                                       &*decryptedData,
01378                                       "encrypted data" );
01379         } else {
01380           mRawReplyString += decryptedData;
01381           if ( mReader ) {
01382             // print the error message that was returned in decryptedData
01383             // (utf8-encoded)
01384             htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01385           }
01386         }
01387 
01388         if ( mReader )
01389           htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01390       }
01391       return true;
01392     }
01393     setCryptPlugWrapper( oldUseThisCryptPlug );
01394     return false;
01395   }
01396 
01397   bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01398     if ( partNode * child = node->firstChild() ) {
01399       kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01400       ObjectTreeParser otp( mReader, cryptPlugWrapper() );
01401       otp.parseObjectTree( child );
01402       mRawReplyString += otp.rawReplyString();
01403       mTextualContent += otp.textualContent();
01404       if ( !otp.textualContentCharset().isEmpty() )
01405         mTextualContentCharset = otp.textualContentCharset();
01406       kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01407       return true;
01408     }
01409 
01410     kdDebug(5006) << "\n----->  Initially processing signed and/or encrypted data\n" << endl;
01411     if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01412       return false;
01413 
01414     CryptPlugWrapper * smimeCrypto = CryptPlugFactory::instance()->smime();
01415 
01416     const QString smimeType = node->contentTypeParameter("smime-type").lower();
01417 
01418     if ( smimeType == "certs-only" ) {
01419       result.setNeverDisplayInline( true );
01420       if ( !smimeCrypto || !mReader )
01421         return false;
01422 
01423       const KConfigGroup reader( KMKernel::config(), "Reader" );
01424       if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
01425         return false;
01426 
01427       const QByteArray certData = node->msgPart().bodyDecodedBinary();
01428 
01429       const GpgME::ImportResult res
01430         = smimeCrypto->importCertificate( certData.data(), certData.size() );
01431       if ( res.error() ) {
01432         htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
01433                                    "Reason: %1").arg( QString::fromLocal8Bit( res.error().asString() ) ) );
01434         return true;
01435       }
01436 
01437       const int nImp = res.numImported();
01438       const int nUnc = res.numUnchanged();
01439       const int nSKImp = res.numSecretKeysImported();
01440       const int nSKUnc = res.numSecretKeysUnchanged();
01441       if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01442         htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
01443         return true;
01444       }
01445       QString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br>&nbsp;<br>";
01446       if ( nImp )
01447         comment += i18n( "1 new certificate was imported.",
01448                          "%n new certificates were imported.", nImp ) + "<br>";
01449       if ( nUnc )
01450         comment += i18n( "1 certificate was unchanged.",
01451                          "%n certificates were unchanged.", nUnc ) + "<br>";
01452       if ( nSKImp )
01453         comment += i18n( "1 new secret key was imported.",
01454                          "%n new secret keys were imported.", nSKImp ) + "<br>";
01455       if ( nSKUnc )
01456         comment += i18n( "1 secret key was unchanged.",
01457                          "%n secret keys were unchanged.", nSKUnc ) + "<br>";
01458       comment += "&nbsp;<br>";
01459       htmlWriter()->queue( comment );
01460       if ( !nImp && !nSKImp ) {
01461         htmlWriter()->queue( "<hr>" );
01462         return true;
01463       }
01464       const std::vector<GpgME::Import> imports = res.imports();
01465       if ( imports.empty() ) {
01466         htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
01467         return true;
01468       }
01469       htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
01470       for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01471         if ( (*it).error() )
01472           htmlWriter()->queue( i18n( "Failed: %1 (%2)" ).arg( (*it).fingerprint() )
01473                                .arg( QString::fromLocal8Bit( (*it).error().asString() ) ) );
01474         else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey )
01475           if ( (*it).status() & GpgME::Import::ContainedSecretKey )
01476             htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01477           else
01478             htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
01479         htmlWriter()->queue( "<br>" );
01480       }
01481 
01482       htmlWriter()->queue( "<hr>" );
01483       return true;
01484     }
01485 
01486     if ( !smimeCrypto )
01487       return false;
01488     CryptPlugWrapperSaver cpws( this, smimeCrypto );
01489 
01490     bool isSigned      = smimeType == "signed-data";
01491     bool isEncrypted   = smimeType == "enveloped-data";
01492 
01493     // Analyze "signTestNode" node to find/verify a signature.
01494     // If zero this verification was successfully done after
01495     // decrypting via recursion by insertAndParseNewChildNode().
01496     partNode* signTestNode = isEncrypted ? 0 : node;
01497 
01498 
01499     // We try decrypting the content
01500     // if we either *know* that it is an encrypted message part
01501     // or there is neither signed nor encrypted parameter.
01502     if ( !isSigned ) {
01503       if ( isEncrypted )
01504         kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data" << endl;
01505       else
01506         kdDebug(5006) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?" << endl;
01507       QCString decryptedData;
01508       PartMetaData messagePart;
01509       messagePart.isEncrypted = true;
01510       messagePart.isSigned = false;
01511       bool signatureFound;
01512       CryptPlug::SignatureMetaData sigMeta;
01513       sigMeta.status              = 0;
01514       sigMeta.extended_info       = 0;
01515       sigMeta.extended_info_count = 0;
01516       bool passphraseError;
01517 
01518       if ( okDecryptMIME( *node,
01519                           decryptedData,
01520                           signatureFound,
01521                           sigMeta,
01522                           false,
01523                           passphraseError,
01524                           messagePart.errorText ) ) {
01525         kdDebug(5006) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !" << endl;
01526         isEncrypted = true;
01527         node->setEncryptionState( KMMsgFullyEncrypted );
01528         signTestNode = 0;
01529         // paint the frame
01530         messagePart.isDecryptable = true;
01531         if ( mReader )
01532           htmlWriter()->queue( writeSigstatHeader( messagePart,
01533                                                    cryptPlugWrapper(),
01534                                                    node->trueFromAddress() ) );
01535         insertAndParseNewChildNode( *node,
01536                                     &*decryptedData,
01537                                     "encrypted data" );
01538         if ( mReader )
01539           htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01540       } else {
01541 
01542         if ( passphraseError ) {
01543           isEncrypted = true;
01544           signTestNode = 0;
01545         }
01546 
01547         if ( isEncrypted ) {
01548           kdDebug(5006) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !" << endl;
01549           // paint the frame
01550           messagePart.isDecryptable = false;
01551           if ( mReader ) {
01552             htmlWriter()->queue( writeSigstatHeader( messagePart,
01553                                                      cryptPlugWrapper(),
01554                                                      node->trueFromAddress() ) );
01555             writePartIcon( &node->msgPart(), node->nodeId() );
01556             htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01557           }
01558         } else {
01559           kdDebug(5006) << "pkcs7 mime  -  NO encryption found" << endl;
01560         }
01561       }
01562       if ( isEncrypted )
01563         node->setEncryptionState( KMMsgFullyEncrypted );
01564     }
01565 
01566     // We now try signature verification if necessarry.
01567     if ( signTestNode ) {
01568       if ( isSigned )
01569         kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data" << endl;
01570       else
01571         kdDebug(5006) << "pkcs7 mime  -  type unknown  -  opaque signed data ?" << endl;
01572 
01573       bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01574                                                         *signTestNode,
01575                                                         node->trueFromAddress(),
01576                                                         true,
01577                                                         0,
01578                                                         0,
01579                                                         isEncrypted );
01580       if ( sigFound ) {
01581         if ( !isSigned ) {
01582           kdDebug(5006) << "pkcs7 mime  -  signature found  -  opaque signed data !" << endl;
01583           isSigned = true;
01584         }
01585         signTestNode->setSignatureState( KMMsgFullySigned );
01586         if ( signTestNode != node )
01587           node->setSignatureState( KMMsgFullySigned );
01588       } else {
01589         kdDebug(5006) << "pkcs7 mime  -  NO signature found   :-(" << endl;
01590       }
01591     }
01592 
01593     return isSigned || isEncrypted;
01594 }
01595 
01596 bool ObjectTreeParser::decryptChiasmus( const QByteArray& data, QByteArray& bodyDecoded, QString& errorText )
01597 {
01598   const Kleo::CryptoBackend::Protocol * chiasmus =
01599     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
01600   Q_ASSERT( chiasmus );
01601   if ( !chiasmus )
01602     return false;
01603 
01604   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
01605   if ( !listjob.get() ) {
01606     errorText = i18n( "Chiasmus backend does not offer the "
01607                       "\"x-obtain-keys\" function. Please report this bug." );
01608     return false;
01609   }
01610 
01611   if ( listjob->exec() ) {
01612     errorText = i18n( "Chiasmus Backend Error" );
01613     return false;
01614   }
01615 
01616   const QVariant result = listjob->property( "result" );
01617   if ( result.type() != QVariant::StringList ) {
01618     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01619                       "The \"x-obtain-keys\" function did not return a "
01620                       "string list. Please report this bug." );
01621     return false;
01622   }
01623 
01624   const QStringList keys = result.toStringList();
01625   if ( keys.empty() ) {
01626     errorText = i18n( "No keys have been found. Please check that a "
01627                       "valid key path has been set in the Chiasmus "
01628                       "configuration." );
01629     return false;
01630   }
01631 
01632   emit mReader->noDrag();
01633   ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
01634                                    keys, GlobalSettings::chiasmusDecryptionKey(),
01635                                    GlobalSettings::chiasmusDecryptionOptions() );
01636   if ( selectorDlg.exec() != QDialog::Accepted )
01637     return false;
01638 
01639   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
01640   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
01641   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
01642 
01643   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
01644   if ( !job ) {
01645     errorText = i18n( "Chiasmus backend does not offer the "
01646                       "\"x-decrypt\" function. Please report this bug." );
01647     return false;
01648   }
01649 
01650   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
01651        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
01652        !job->setProperty( "input", data ) ) {
01653     errorText = i18n( "The \"x-decrypt\" function does not accept "
01654                       "the expected parameters. Please report this bug." );
01655     return false;
01656   }
01657 
01658   if ( job->exec() ) {
01659     errorText = i18n( "Chiasmus Decryption Error" );
01660     return false;
01661   }
01662 
01663   const QVariant resultData = job->property( "result" );
01664   if ( resultData.type() != QVariant::ByteArray ) {
01665     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01666                       "The \"x-decrypt\" function did not return a "
01667                       "byte array. Please report this bug." );
01668     return false;
01669   }
01670   bodyDecoded = resultData.toByteArray();
01671   return true;
01672 }
01673 
01674 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
01675 {
01676   if ( !mReader ) {
01677     mRawReplyString = curNode->msgPart().bodyDecoded();
01678     mTextualContent += curNode->msgPart().bodyToUnicode();
01679     mTextualContentCharset = curNode->msgPart().charset();
01680     return true;
01681   }
01682 
01683   QByteArray decryptedBody;
01684   QString errorText;
01685   const QByteArray data = curNode->msgPart().bodyDecodedBinary();
01686   bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
01687   PartMetaData messagePart;
01688   messagePart.isDecryptable = bOkDecrypt;
01689   messagePart.isEncrypted = true;
01690   messagePart.isSigned = false;
01691   messagePart.errorText = errorText;
01692   if ( mReader )
01693     htmlWriter()->queue( writeSigstatHeader( messagePart,
01694                                              0, //cryptPlugWrapper(),
01695                                              curNode->trueFromAddress() ) );
01696   const QByteArray body = bOkDecrypt ? decryptedBody : data;
01697   const QString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
01698   const QTextCodec* aCodec = chiasmusCharset.isEmpty()
01699     ? codecFor( curNode )
01700     : KMMsgBase::codecForName( chiasmusCharset.ascii() );
01701   htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false /*decorate*/ ) );
01702   result.setInlineEncryptionState( KMMsgFullyEncrypted );
01703   if ( mReader )
01704     htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01705   return true;
01706 }
01707 
01708   void ObjectTreeParser::writeBodyString( const QCString & bodyString,
01709                                           const QString & fromAddress,
01710                                           const QTextCodec * codec,
01711                                           ProcessResult & result,
01712                                           bool decorate ) {
01713     assert( mReader ); assert( codec );
01714     KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
01715     KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
01716     writeBodyStr( bodyString, codec, fromAddress,
01717                   inlineSignatureState, inlineEncryptionState, decorate );
01718     result.setInlineSignatureState( inlineSignatureState );
01719     result.setInlineEncryptionState( inlineEncryptionState );
01720   }
01721 
01722   void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
01723     if ( !mReader || !msgPart )
01724       return;
01725 
01726     kdDebug(5006) << "writePartIcon: PartNum: " << partNum << endl;
01727 
01728     QString label = msgPart->fileName();
01729     if( label.isEmpty() )
01730       label = msgPart->name();
01731     if( label.isEmpty() )
01732       label = "unnamed";
01733     label = KMMessage::quoteHtmlChars( label, true );
01734 
01735     QString comment = msgPart->contentDescription();
01736     comment = KMMessage::quoteHtmlChars( comment, true );
01737 
01738     QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
01739 
01740     QString href = fileName.isEmpty() ?
01741       "part://" + QString::number( partNum + 1 ) :
01742       "file:" + KURL::encode_string( fileName ) ;
01743 
01744     QString iconName;
01745     if( inlineImage )
01746       iconName = href;
01747     else {
01748       iconName = msgPart->iconName();
01749       if( iconName.right( 14 ) == "mime_empty.png" ) {
01750         msgPart->magicSetType();
01751         iconName = msgPart->iconName();
01752       }
01753     }
01754 
01755     QCString contentId = msgPart->contentId();
01756     if ( !contentId.isEmpty() ) {
01757       htmlWriter()->embedPart( contentId, href );
01758     }
01759 
01760     if( inlineImage )
01761       // show the filename of the image below the embedded image
01762       htmlWriter()->queue( "<div><a href=\"" + href + "\">"
01763                            "<img src=\"" + iconName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
01764                            "</div>"
01765                            "<div><a href=\"" + href + "\">" + label + "</a>"
01766                            "</div>"
01767                            "<div>" + comment + "</div><br>" );
01768     else
01769       // show the filename next to the image
01770       htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
01771                            iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
01772                            "</a></div>"
01773                            "<div>" + comment + "</div><br>" );
01774   }
01775 
01776 #define SIG_FRAME_COL_UNDEF  99
01777 #define SIG_FRAME_COL_RED    -1
01778 #define SIG_FRAME_COL_YELLOW  0
01779 #define SIG_FRAME_COL_GREEN   1
01780 QString ObjectTreeParser::sigStatusToString( CryptPlugWrapper* cryptPlug,
01781                                         int status_code,
01782                                         CryptPlugWrapper::SigStatusFlags statusFlags,
01783                                         int& frameColor,
01784                                         bool& showKeyInfos )
01785 {
01786     // note: At the moment frameColor and showKeyInfos are
01787     //       used for CMS only but not for PGP signatures
01788     // pending(khz): Implement usage of these for PGP sigs as well.
01789     showKeyInfos = true;
01790     QString result;
01791     if( cryptPlug ) {
01792         if( cryptPlug->protocol().lower() == "openpgp" ) {
01793             // process enum according to it's definition to be read in
01794             // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
01795             switch( status_code ) {
01796             case 0: // GPGME_SIG_STAT_NONE
01797                 result = i18n("Error: Signature not verified");
01798                 break;
01799             case 1: // GPGME_SIG_STAT_GOOD
01800                 result = i18n("Good signature");
01801                 break;
01802             case 2: // GPGME_SIG_STAT_BAD
01803                 result = i18n("<b>Bad</b> signature");
01804                 break;
01805             case 3: // GPGME_SIG_STAT_NOKEY
01806                 result = i18n("No public key to verify the signature");
01807                 break;
01808             case 4: // GPGME_SIG_STAT_NOSIG
01809                 result = i18n("No signature found");
01810                 break;
01811             case 5: // GPGME_SIG_STAT_ERROR
01812                 result = i18n("Error verifying the signature");
01813                 break;
01814             case 6: // GPGME_SIG_STAT_DIFF
01815                 result = i18n("Different results for signatures");
01816                 break;
01817             /* PENDING(khz) Verify exact meaning of the following values:
01818             case 7: // GPGME_SIG_STAT_GOOD_EXP
01819                 return i18n("Signature certificate is expired");
01820             break;
01821             case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
01822                 return i18n("One of the certificate's keys is expired");
01823             break;
01824             */
01825             default:
01826                 result = "";   // do *not* return a default text here !
01827                 break;
01828             }
01829         }
01830         else if ( cryptPlug->protocol().lower() == "smime" ) {
01831             // process status bits according to SigStatus_...
01832             // definitions in kdenetwork/libkdenetwork/cryptplug.h
01833 
01834             if( CryptPlugWrapper::SigStatus_UNKNOWN == statusFlags ) {
01835                 result = i18n("No status information available.");
01836                 frameColor = SIG_FRAME_COL_YELLOW;
01837                 showKeyInfos = false;
01838                 return result;
01839             }
01840 
01841             if( CryptPlugWrapper::SigStatus_VALID & statusFlags ) {
01842                 result = i18n("Good signature.");
01843                 // Note:
01844                 // Here we are work differently than KMail did before!
01845                 //
01846                 // The GOOD case ( == sig matching and the complete
01847                 // certificate chain was verified and is valid today )
01848                 // by definition does *not* show any key
01849                 // information but just states that things are OK.
01850                 //           (khz, according to LinuxTag 2002 meeting)
01851                 frameColor = SIG_FRAME_COL_GREEN;
01852                 showKeyInfos = false;
01853                 return result;
01854             }
01855 
01856             // we are still there?  OK, let's test the different cases:
01857 
01858             // we assume green, test for yellow or red (in this order!)
01859             frameColor = SIG_FRAME_COL_GREEN;
01860             QString result2;
01861             if( CryptPlugWrapper::SigStatus_KEY_EXPIRED & statusFlags ){
01862                 // still is green!
01863                 result2 += i18n("One key has expired.");
01864             }
01865             if( CryptPlugWrapper::SigStatus_SIG_EXPIRED & statusFlags ){
01866                 // and still is green!
01867                 result2 += i18n("The signature has expired.");
01868             }
01869 
01870             // test for yellow:
01871             if( CryptPlugWrapper::SigStatus_KEY_MISSING & statusFlags ) {
01872                 result2 += i18n("Unable to verify: key missing.");
01873                 // if the signature certificate is missing
01874                 // we cannot show infos on it
01875                 showKeyInfos = false;
01876                 frameColor = SIG_FRAME_COL_YELLOW;
01877             }
01878             if( CryptPlugWrapper::SigStatus_CRL_MISSING & statusFlags ){
01879                 result2 += i18n("CRL not available.");
01880                 frameColor = SIG_FRAME_COL_YELLOW;
01881             }
01882             if( CryptPlugWrapper::SigStatus_CRL_TOO_OLD & statusFlags ){
01883                 result2 += i18n("Available CRL is too old.");
01884                 frameColor = SIG_FRAME_COL_YELLOW;
01885             }
01886             if( CryptPlugWrapper::SigStatus_BAD_POLICY & statusFlags ){
01887                 result2 += i18n("A policy was not met.");
01888                 frameColor = SIG_FRAME_COL_YELLOW;
01889             }
01890             if( CryptPlugWrapper::SigStatus_SYS_ERROR & statusFlags ){
01891                 result2 += i18n("A system error occurred.");
01892                 // if a system error occurred
01893                 // we cannot trust any information
01894                 // that was given back by the plug-in
01895                 showKeyInfos = false;
01896                 frameColor = SIG_FRAME_COL_YELLOW;
01897             }
01898             if( CryptPlugWrapper::SigStatus_NUMERICAL_CODE & statusFlags ) {
01899                 result2 += i18n("Internal system error #%1 occurred.")
01900                         .arg( statusFlags - CryptPlugWrapper::SigStatus_NUMERICAL_CODE );
01901                 // if an unsupported internal error occurred
01902                 // we cannot trust any information
01903                 // that was given back by the plug-in
01904                 showKeyInfos = false;
01905                 frameColor = SIG_FRAME_COL_YELLOW;
01906             }
01907 
01908             // test for red:
01909             if( CryptPlugWrapper::SigStatus_KEY_REVOKED & statusFlags ){
01910                 // this is red!
01911                 result2 += i18n("One key has been revoked.");
01912                 frameColor = SIG_FRAME_COL_RED;
01913             }
01914             if( CryptPlugWrapper::SigStatus_RED & statusFlags ) {
01915                 if( result2.isEmpty() )
01916                     // Note:
01917                     // Here we are work differently than KMail did before!
01918                     //
01919                     // The BAD case ( == sig *not* matching )
01920                     // by definition does *not* show any key
01921                     // information but just states that things are BAD.
01922                     //
01923                     // The reason for this: In this case ALL information
01924                     // might be falsificated, we can NOT trust the data
01925                     // in the body NOT the signature - so we don't show
01926                     // any key/signature information at all!
01927                     //         (khz, according to LinuxTag 2002 meeting)
01928                     showKeyInfos = false;
01929                 frameColor = SIG_FRAME_COL_RED;
01930             }
01931             else
01932                 result = "";
01933 
01934             if( SIG_FRAME_COL_GREEN == frameColor ) {
01935                 result = i18n("Good signature.");
01936             } else if( SIG_FRAME_COL_RED == frameColor ) {
01937                 result = i18n("<b>Bad</b> signature.");
01938             } else
01939                 result = "";
01940 
01941             if( !result2.isEmpty() ) {
01942                 if( !result.isEmpty() )
01943                     result.append("<br />");
01944                 result.append( result2 );
01945             }
01946         }
01947         /*
01948         // add i18n support for 3rd party plug-ins here:
01949         else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
01950 
01951         }
01952         */
01953     }
01954     return result;
01955 }
01956 
01957 
01958 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
01959                                               CryptPlugWrapper * cryptPlug,
01960                                               const QString & fromAddress,
01961                                               const QString & filename )
01962 {
01963     bool isSMIME = cryptPlug && cryptPlug->protocol().lower() == "smime";
01964     QString signer = block.signer;
01965 
01966     QString htmlStr;
01967     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01968     QString cellPadding("cellpadding=\"1\"");
01969 
01970     if( block.isEncapsulatedRfc822Message )
01971     {
01972         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
01973             "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
01974         if( !filename.isEmpty() )
01975             htmlStr += "<a href=\"" + QString("file:")
01976                      + KURL::encode_string( filename ) + "\">"
01977                      + i18n("Encapsulated message") + "</a>";
01978         else
01979             htmlStr += i18n("Encapsulated message");
01980         htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
01981     }
01982 
01983     if( block.isEncrypted )
01984     {
01985         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
01986             "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
01987         if( block.isDecryptable )
01988             htmlStr += i18n("Encrypted message");
01989         else {
01990             htmlStr += i18n("Encrypted message (decryption not possible)");
01991             if( !block.errorText.isEmpty() )
01992                 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
01993         }
01994         htmlStr += "</td></tr><tr class=\"encrB\"><td>";
01995     }
01996 
01997     if( block.isSigned ) {
01998         QStringList& blockAddrs( block.signerMailAddresses );
01999         // note: At the moment frameColor and showKeyInfos are
02000         //       used for CMS only but not for PGP signatures
02001         // pending(khz): Implement usage of these for PGP sigs as well.
02002         int frameColor = SIG_FRAME_COL_UNDEF;
02003         bool showKeyInfos;
02004         bool onlyShowKeyURL = false;
02005         bool cannotCheckSignature = true;
02006         QString statusStr = sigStatusToString( cryptPlug,
02007                                                block.status_code,
02008                                                block.sigStatusFlags,
02009                                                frameColor,
02010                                                showKeyInfos );
02011         // if needed fallback to english status text
02012         // that was reported by the plugin
02013         if( statusStr.isEmpty() )
02014             statusStr = block.status;
02015         if( block.technicalProblem )
02016             frameColor = SIG_FRAME_COL_YELLOW;
02017 
02018         switch( frameColor ){
02019             case SIG_FRAME_COL_RED:
02020                 cannotCheckSignature = false;
02021                 break;
02022             case SIG_FRAME_COL_YELLOW:
02023                 cannotCheckSignature = true;
02024                 break;
02025             case SIG_FRAME_COL_GREEN:
02026                 cannotCheckSignature = false;
02027                 break;
02028         }
02029 
02030         // compose the string for displaying the key ID
02031         // either as URL or not linked (for PGP)
02032         // note: Once we can start PGP key manager programs
02033         //       from within KMail we could change this and
02034         //       always show the URL.    (khz, 2002/06/27)
02035         QString startKeyHREF;
02036         if( isSMIME )
02037             startKeyHREF =
02038                 QString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02039                 .arg( cryptPlug->displayName() )
02040                 .arg( cryptPlug->libName() )
02041                 .arg( block.keyId );
02042         QString keyWithWithoutURL
02043             = isSMIME
02044             ? QString("%1%2</a>")
02045                 .arg( startKeyHREF )
02046                 .arg( cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02047             : "0x" + QString::fromUtf8( block.keyId );
02048 
02049 
02050         // temporary hack: always show key infos!
02051         showKeyInfos = true;
02052 
02053         // Sorry for using 'black' as null color but .isValid()
02054         // checking with QColor default c'tor did not work for
02055         // some reason.
02056         if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02057 
02058             // new frame settings for CMS:
02059             // beautify the status string
02060             if( !statusStr.isEmpty() ) {
02061                 statusStr.prepend("<i>");
02062                 statusStr.append( "</i>");
02063             }
02064 
02065             // special color handling: S/MIME uses only green/yellow/red.
02066             switch( frameColor ) {
02067                 case SIG_FRAME_COL_RED:
02068                     block.signClass = "signErr";//"signCMSRed";
02069                     onlyShowKeyURL = true;
02070                     break;
02071                 case SIG_FRAME_COL_YELLOW:
02072                     if( block.technicalProblem )
02073                         block.signClass = "signWarn";
02074                     else
02075                         block.signClass = "signOkKeyBad";//"signCMSYellow";
02076                     break;
02077                 case SIG_FRAME_COL_GREEN:
02078                     block.signClass = "signOkKeyOk";//"signCMSGreen";
02079                     // extra hint for green case
02080                     // that email addresses in DN do not match fromAddress
02081                     QString greenCaseWarning;
02082                     QString msgFrom( KPIM::getEmailAddress(fromAddress) );
02083                     QString certificate;
02084                     if( block.keyId.isEmpty() )
02085                         certificate = "certificate";
02086                     else
02087                         certificate = QString("%1%2</a>")
02088                                       .arg( startKeyHREF )
02089                                       .arg( "certificate" );
02090                     if( !blockAddrs.empty() ){
02091                         if( blockAddrs.grep(
02092                                 msgFrom,
02093                                 false ).isEmpty() ) {
02094                             greenCaseWarning =
02095                                 "<u>" +
02096                                 i18n("Warning:") +
02097                                 "</u> " +
02098                                 i18n("Sender's mail address is not stored "
02099                                      "in the %1 used for signing.").arg(certificate) +
02100                                 "<br />" +
02101                                 i18n("sender: ") +
02102                                 msgFrom +
02103                                 "<br />" +
02104                                 i18n("stored: ");
02105                             // We cannot use Qt's join() function here but
02106                             // have to join the addresses manually to
02107                             // extract the mail addresses (without '<''>')
02108                             // before including it into our string:
02109                             bool bStart = true;
02110                             for(QStringList::ConstIterator it = blockAddrs.begin();
02111                                 it != blockAddrs.end(); ++it ){
02112                                 if( !bStart )
02113                                     greenCaseWarning.append(", <br />&nbsp; &nbsp;");
02114                                 bStart = false;
02115                                 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02116                             }
02117                         }
02118                     } else {
02119                         greenCaseWarning =
02120                             "<u>" +
02121                             i18n("Warning:") +
02122                             "</u> " +
02123                             i18n("No mail address is stored in the %1 used for signing, "
02124                                  "so we cannot compare it to the sender's address %2.")
02125                             .arg(certificate)
02126                             .arg(msgFrom);
02127                     }
02128                     if( !greenCaseWarning.isEmpty() ) {
02129                         if( !statusStr.isEmpty() )
02130                             statusStr.append("<br />&nbsp;<br />");
02131                         statusStr.append( greenCaseWarning );
02132                     }
02133                     break;
02134             }
02135 
02136             htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02137                 "class=\"" + block.signClass + "\">"
02138                 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02139             if( block.technicalProblem ) {
02140                 htmlStr += block.errorText;
02141             }
02142             else if( showKeyInfos ) {
02143                 if( cannotCheckSignature ) {
02144                     htmlStr += i18n( "Not enough information to check "
02145                                      "signature. %1" )
02146                                 .arg( keyWithWithoutURL );
02147                 }
02148                 else {
02149 
02150                     if (block.signer.isEmpty())
02151                         signer = "";
02152                     else {
02153                         if( !blockAddrs.empty() ){
02154                             QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02155                             signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02156                         }
02157                     }
02158 
02159                     if( block.keyId.isEmpty() ) {
02160                         if( signer.isEmpty() || onlyShowKeyURL )
02161                             htmlStr += i18n( "Message was signed with unknown key." );
02162                         else
02163                             htmlStr += i18n( "Message was signed by %1." )
02164                                     .arg( signer );
02165                     } else {
02166                         bool dateOK = ( 0 < block.creationTime.tm_year &&
02167                                         block.creationTime.tm_year < 3000 );
02168                         QDateTime created;
02169                         if ( dateOK )
02170                           created.setTime_t( mktime(&block.creationTime) );
02171                         if( dateOK && created.isValid() ) {
02172                             if( signer.isEmpty() ) {
02173                                 if( onlyShowKeyURL )
02174                                     htmlStr += i18n( "Message was signed with key %1." )
02175                                                 .arg( keyWithWithoutURL );
02176                                 else
02177                                     htmlStr += i18n( "Message was signed on %1 with key %2." )
02178                                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02179                                                 .arg( keyWithWithoutURL );
02180                             }
02181                             else {
02182                                 if( onlyShowKeyURL )
02183                                     htmlStr += i18n( "Message was signed with key %1." )
02184                                             .arg( keyWithWithoutURL );
02185                                 else
02186                                     htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02187                                             .arg( KGlobal::locale()->formatDateTime( created ) )
02188                                             .arg( keyWithWithoutURL )
02189                                             .arg( signer );
02190                             }
02191                         }
02192                         else {
02193                             if( signer.isEmpty() || onlyShowKeyURL )
02194                                 htmlStr += i18n( "Message was signed with key %1." )
02195                                         .arg( keyWithWithoutURL );
02196                             else
02197                                 htmlStr += i18n( "Message was signed by %2 with key %1." )
02198                                         .arg( keyWithWithoutURL )
02199                                         .arg( signer );
02200                         }
02201                     }
02202                 }
02203                 htmlStr += "<br />";
02204                 if( !statusStr.isEmpty() ) {
02205                     htmlStr += "&nbsp;<br />";
02206                     htmlStr += i18n( "Status: " );
02207                     htmlStr += statusStr;
02208                 }
02209             } else {
02210                 htmlStr += statusStr;
02211             }
02212             htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02213 
02214         } else {
02215 
02216             // old frame settings for PGP:
02217 
02218             if( block.signer.isEmpty() || block.technicalProblem ) {
02219                 block.signClass = "signWarn";
02220                 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02221                     "class=\"" + block.signClass + "\">"
02222                     "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02223                 if( block.technicalProblem ) {
02224                     htmlStr += block.errorText;
02225                 }
02226                 else {
02227                   if( !block.keyId.isEmpty() ) {
02228                     bool dateOK = ( 0 < block.creationTime.tm_year &&
02229                                     block.creationTime.tm_year < 3000 );
02230                     QDateTime created;
02231                     if ( dateOK )
02232                       created.setTime_t( mktime(&block.creationTime) );
02233                     if( dateOK && created.isValid() )
02234                         htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02235                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02236                                 .arg( keyWithWithoutURL );
02237                     else
02238                         htmlStr += i18n( "Message was signed with unknown key %1." )
02239                                 .arg( keyWithWithoutURL );
02240                   }
02241                   else
02242                     htmlStr += i18n( "Message was signed with unknown key." );
02243                   htmlStr += "<br />";
02244                   htmlStr += i18n( "The validity of the signature cannot be "
02245                                    "verified." );
02246                   if( !statusStr.isEmpty() ) {
02247                     htmlStr += "<br />";
02248                     htmlStr += i18n( "Status: " );
02249                     htmlStr += "<i>";
02250                     htmlStr += statusStr;
02251                     htmlStr += "</i>";
02252                   }
02253                 }
02254                 htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02255             }
02256             else
02257             {
02258                 // HTMLize the signer's user id and create mailto: link
02259                 signer = KMMessage::quoteHtmlChars( signer, true );
02260                 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02261 
02262                 if (block.isGoodSignature) {
02263                     if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02264                         block.signClass = "signOkKeyBad";
02265                     else
02266                         block.signClass = "signOkKeyOk";
02267                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02268                         "class=\"" + block.signClass + "\">"
02269                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02270                     if( !block.keyId.isEmpty() )
02271                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02272                                    .arg( keyWithWithoutURL )
02273                                    .arg( signer );
02274                     else
02275                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02276                     htmlStr += "<br />";
02277 
02278                     switch( block.keyTrust )
02279                     {
02280                         case Kpgp::KPGP_VALIDITY_UNKNOWN:
02281                         htmlStr += i18n( "The signature is valid, but the key's "
02282                                 "validity is unknown." );
02283                         break;
02284                         case Kpgp::KPGP_VALIDITY_MARGINAL:
02285                         htmlStr += i18n( "The signature is valid and the key is "
02286                                 "marginally trusted." );
02287                         break;
02288                         case Kpgp::KPGP_VALIDITY_FULL:
02289                         htmlStr += i18n( "The signature is valid and the key is "
02290                                 "fully trusted." );
02291                         break;
02292                         case Kpgp::KPGP_VALIDITY_ULTIMATE:
02293                         htmlStr += i18n( "The signature is valid and the key is "
02294                                 "ultimately trusted." );
02295                         break;
02296                         default:
02297                         htmlStr += i18n( "The signature is valid, but the key is "
02298                                 "untrusted." );
02299                     }
02300                     htmlStr += "</td></tr>"
02301                         "<tr class=\"" + block.signClass + "B\"><td>";
02302                 }
02303                 else
02304                 {
02305                     block.signClass = "signErr";
02306                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02307                         "class=\"" + block.signClass + "\">"
02308                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02309                     if( !block.keyId.isEmpty() )
02310                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02311                         .arg( keyWithWithoutURL )
02312                         .arg( signer );
02313                     else
02314                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02315                     htmlStr += "<br />";
02316                     htmlStr += i18n("Warning: The signature is bad.");
02317                     htmlStr += "</td></tr>"
02318                         "<tr class=\"" + block.signClass + "B\"><td>";
02319                 }
02320             }
02321         }
02322     }
02323 
02324     return htmlStr;
02325 }
02326 
02327 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02328 {
02329     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02330 
02331     QString htmlStr;
02332 
02333     if (block.isSigned) {
02334         htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02335         htmlStr += "<td dir=\"" + dir + "\">" +
02336             i18n( "End of signed message" ) +
02337             "</td></tr></table>";
02338     }
02339 
02340     if (block.isEncrypted) {
02341         htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02342                 i18n( "End of encrypted message" ) +
02343             "</td></tr></table>";
02344     }
02345 
02346     if( block.isEncapsulatedRfc822Message )
02347     {
02348         htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02349             i18n( "End of encapsulated message" ) +
02350             "</td></tr></table>";
02351     }
02352 
02353     return htmlStr;
02354 }
02355 
02356 //-----------------------------------------------------------------------------
02357 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02358                                 const QString& fromAddress )
02359 {
02360   KMMsgSignatureState dummy1;
02361   KMMsgEncryptionState dummy2;
02362   writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02363 }
02364 
02365 //-----------------------------------------------------------------------------
02366 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02367                                 const QString& fromAddress,
02368                                 KMMsgSignatureState&  inlineSignatureState,
02369                                 KMMsgEncryptionState& inlineEncryptionState,
02370                                 bool decorate )
02371 {
02372   bool goodSignature = false;
02373   Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02374   assert(pgp != 0);
02375   bool isPgpMessage = false; // true if the message contains at least one
02376                              // PGP MESSAGE or one PGP SIGNED MESSAGE block
02377   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02378   QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02379 
02380   inlineSignatureState  = KMMsgNotSigned;
02381   inlineEncryptionState = KMMsgNotEncrypted;
02382   QPtrList<Kpgp::Block> pgpBlocks;
02383   QStrList nonPgpBlocks;
02384   if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02385   {
02386       bool isEncrypted = false, isSigned = false;
02387       bool fullySignedOrEncrypted = true;
02388       bool firstNonPgpBlock = true;
02389       bool couldDecrypt = false;
02390       QString signer;
02391       QCString keyId;
02392       QString decryptionError;
02393       Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02394 
02395       QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02396 
02397       QStrListIterator npbit( nonPgpBlocks );
02398 
02399       QString htmlStr;
02400       for( ; *pbit != 0; ++pbit, ++npbit )
02401       {
02402           // insert the next Non-OpenPGP block
02403           QCString str( *npbit );
02404           if( !str.isEmpty() ) {
02405             htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02406             kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
02407                             << "'" << endl;
02408             // treat messages with empty lines before the first clearsigned
02409             // block as fully signed/encrypted
02410             if( firstNonPgpBlock ) {
02411               // check whether str only consists of \n
02412               for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02413                 if( *c != '\n' ) {
02414                   fullySignedOrEncrypted = false;
02415                   break;
02416                 }
02417               }
02418             }
02419             else {
02420               fullySignedOrEncrypted = false;
02421             }
02422           }
02423           firstNonPgpBlock = false;
02424 
02425           //htmlStr += "<br>";
02426 
02427           Kpgp::Block* block = *pbit;
02428           if( ( block->type() == Kpgp::PgpMessageBlock &&
02429                 // ### Workaround for bug 56693
02430                 !kmkernel->contextMenuShown() ) ||
02431               ( block->type() == Kpgp::ClearsignedBlock ) )
02432           {
02433               isPgpMessage = true;
02434               if( block->type() == Kpgp::PgpMessageBlock )
02435               {
02436                 if ( mReader )
02437                   emit mReader->noDrag();
02438                 // try to decrypt this OpenPGP block
02439                 couldDecrypt = block->decrypt();
02440                 isEncrypted = block->isEncrypted();
02441                 if (!couldDecrypt) {
02442                   decryptionError = pgp->lastErrorMsg();
02443                 }
02444               }
02445               else
02446               {
02447                   // try to verify this OpenPGP block
02448                   block->verify();
02449               }
02450 
02451               isSigned = block->isSigned();
02452               if( isSigned )
02453               {
02454                   keyId = block->signatureKeyId();
02455                   signer = block->signatureUserId();
02456                   if( !signer.isEmpty() )
02457                   {
02458                       goodSignature = block->goodSignature();
02459 
02460                       if( !keyId.isEmpty() ) {
02461                         keyTrust = pgp->keyTrust( keyId );
02462                         Kpgp::Key* key = pgp->publicKey( keyId );
02463                         if ( key ) {
02464                           // Use the user ID from the key because this one
02465                           // is charset safe.
02466                           signer = key->primaryUserID();
02467                         }
02468                       }
02469                       else
02470                         // This is needed for the PGP 6 support because PGP 6 doesn't
02471                         // print the key id of the signing key if the key is known.
02472                         keyTrust = pgp->keyTrust( signer );
02473                   }
02474               }
02475 
02476               if( isSigned )
02477                 inlineSignatureState = KMMsgPartiallySigned;
02478               if( isEncrypted )
02479                 inlineEncryptionState = KMMsgPartiallyEncrypted;
02480 
02481               PartMetaData messagePart;
02482 
02483               messagePart.isSigned = isSigned;
02484               messagePart.technicalProblem = false;
02485               messagePart.isGoodSignature = goodSignature;
02486               messagePart.isEncrypted = isEncrypted;
02487               messagePart.isDecryptable = couldDecrypt;
02488               messagePart.decryptionError = decryptionError;
02489               messagePart.signer = signer;
02490               messagePart.keyId = keyId;
02491               messagePart.keyTrust = keyTrust;
02492 
02493               htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02494 
02495               htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02496               htmlStr += writeSigstatFooter( messagePart );
02497           }
02498           else // block is neither message block nor clearsigned block
02499             htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02500                                    decorate );
02501       }
02502 
02503       // add the last Non-OpenPGP block
02504       QCString str( nonPgpBlocks.last() );
02505       if( !str.isEmpty() ) {
02506         htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02507         // Even if the trailing Non-OpenPGP block isn't empty we still
02508         // consider the message part fully signed/encrypted because else
02509         // all inline signed mailing list messages would only be partially
02510         // signed because of the footer which is often added by the mailing
02511         // list software. IK, 2003-02-15
02512       }
02513       if( fullySignedOrEncrypted ) {
02514         if( inlineSignatureState == KMMsgPartiallySigned )
02515           inlineSignatureState = KMMsgFullySigned;
02516         if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02517           inlineEncryptionState = KMMsgFullyEncrypted;
02518       }
02519       htmlWriter()->queue( htmlStr );
02520   }
02521   else
02522     htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02523 }
02524 
02525 
02526 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
02527 {
02528   assert( mReader );
02529   assert( cssHelper() );
02530 
02531   int convertFlags = LinkLocator::PreserveSpaces;
02532   if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02533     convertFlags |= LinkLocator::ReplaceSmileys;
02534   }
02535   QString htmlStr;
02536   const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02537   QString quoteFontTag[3];
02538   QString deepQuoteFontTag[3];
02539   for ( int i = 0 ; i < 3 ; ++i ) {
02540     quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02541     deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02542   }
02543   const QString normalEndTag = "</div>";
02544   const QString quoteEnd = "</div>";
02545 
02546   unsigned int pos, beg;
02547   const unsigned int length = s.length();
02548 
02549   // skip leading empty lines
02550   for ( pos = 0; pos < length && s[pos] <= ' '; pos++ );
02551   while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02552   beg = pos;
02553 
02554   int currQuoteLevel = -2; // -2 == no previous lines
02555   bool curHidden = false; // no hide any block
02556 
02557   while (beg<length)
02558   {
02559     QString line;
02560 
02561     /* search next occurrence of '\n' */
02562     pos = s.find('\n', beg, FALSE);
02563     if (pos == (unsigned int)(-1))
02564         pos = length;
02565 
02566     line = s.mid(beg,pos-beg);
02567     beg = pos+1;
02568 
02569     /* calculate line's current quoting depth */
02570     int actQuoteLevel = -1;
02571 
02572     if ( GlobalSettings::self()->showExpandQuotesMark() )
02573     {
02574       // Cache Icons
02575       if ( mCollapseIcon.isEmpty() ) {
02576         mCollapseIcon= LinkLocator::pngToDataUrl(
02577             KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
02578       }
02579       if ( mExpandIcon.isEmpty() )
02580         mExpandIcon= LinkLocator::pngToDataUrl(
02581             KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
02582     }
02583 
02584     for (unsigned int p=0; p<line.length(); p++) {
02585       switch (line[p].latin1()) {
02586         case '>':
02587         case '|':
02588           actQuoteLevel++;
02589           break;
02590         case ' ':  // spaces and tabs are allowed between the quote markers
02591         case '\t':
02592         case '\r':
02593           break;
02594         default:  // stop quoting depth calculation
02595           p = line.length();
02596           break;
02597       }
02598     } /* for() */
02599 
02600     bool actHidden = false;
02601     QString textExpand;
02602 
02603     // This quoted line needs be hiden
02604     if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
02605         && mReader->mLevelQuote <= ( actQuoteLevel ) )
02606       actHidden = true;
02607 
02608     if ( actQuoteLevel != currQuoteLevel ) {
02609       /* finish last quotelevel */
02610       if (currQuoteLevel == -1)
02611         htmlStr.append( normalEndTag );
02612       else if ( currQuoteLevel >= 0 && !curHidden )
02613         htmlStr.append( quoteEnd );
02614 
02615       /* start new quotelevel */
02616       if (actQuoteLevel == -1)
02617         htmlStr += normalStartTag;
02618       else
02619       {
02620         if ( GlobalSettings::self()->showExpandQuotesMark() )
02621         {
02622           if (  actHidden )
02623           {
02624             //only show the QuoteMark when is the first line of the level hidden
02625             if ( !curHidden )
02626             {
02627               //Expand all quotes
02628               htmlStr += "<div class=\"quotelevelmark\" >" ;
02629               htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02630                   "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02631                 .arg(-1)
02632                 .arg( mExpandIcon );
02633               htmlStr += "</div><br/>";
02634               htmlStr += quoteEnd;
02635             }
02636           }else {
02637             htmlStr += "<div class=\"quotelevelmark\" >" ;
02638             htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02639                 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02640               .arg(actQuoteLevel)
02641               .arg( mCollapseIcon);
02642             htmlStr += "</div>";
02643             if ( actQuoteLevel < 3 )
02644               htmlStr += quoteFontTag[actQuoteLevel];
02645             else
02646               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02647           }
02648         } else
02649             if ( actQuoteLevel < 3 )
02650               htmlStr += quoteFontTag[actQuoteLevel];
02651             else
02652               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02653       }
02654       currQuoteLevel = actQuoteLevel;
02655     }
02656     curHidden = actHidden;
02657 
02658 
02659     if ( !actHidden )
02660     {
02661       // don't write empty <div ...></div> blocks (they have zero height)
02662       // ignore ^M DOS linebreaks
02663       if( !line.replace('\015', "").isEmpty() )
02664       {
02665          htmlStr +=QString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
02666          htmlStr += LinkLocator::convertToHtml( line, convertFlags );
02667          htmlStr += QString( "</div>" );
02668       }
02669       else
02670         htmlStr += "<br>";
02671     }
02672   } /* while() */
02673 
02674   /* really finish the last quotelevel */
02675   if (currQuoteLevel == -1)
02676      htmlStr.append( normalEndTag );
02677   else
02678      htmlStr.append( quoteEnd );
02679 
02680   //kdDebug(5006) << "KMReaderWin::quotedHTML:\n"
02681   //              << "========================================\n"
02682   //              << htmlStr
02683   //              << "\n======================================\n";
02684   return htmlStr;
02685 }
02686 
02687 
02688 
02689   const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
02690     assert( node );
02691     if ( mReader && mReader->overrideCodec() )
02692       return mReader->overrideCodec();
02693     return node->msgPart().codec();
02694   }
02695 
02696 #ifdef MARCS_DEBUG
02697   void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
02698                                      size_t len ) {
02699     assert( filename );
02700 
02701     QFile f( filename );
02702     if ( f.open( IO_WriteOnly ) ) {
02703       if ( start ) {
02704         QDataStream ds( &f );
02705         ds.writeRawBytes( start, len );
02706       }
02707       f.close();  // If data is 0 we just create a zero length file.
02708     }
02709   }
02710 #endif // !NDEBUG
02711 
02712 
02713 } // namespace KMail
KDE Home | KDE Accessibility Home | Description of Access Keys